Fix/ds proxy via proxy - #4661
Gauravsingh096 wants to merge 3 commits into
Conversation
Signed-off-by: Gaurav Singh <singhgaurav07122004@gmail.com>
Signed-off-by: Gaurav Singh <singhgaurav07122004@gmail.com>
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
🚀 Keploy Performance Test ResultsMulti-Run Validation: Tests run 3 times, pipeline fails only if 2+ runs show regression.
Thresholds: P50 < 5ms, P90 < 15ms, P99 < 70ms, RPS >= 100 (±1% tolerance), Error Rate < 1% ✅ Result: PASSED - Only 0 out of 3 runs failed (threshold: 2) P50, P90, and P99 percentiles naturally filter out outliers |
Gauravsingh096
left a comment
There was a problem hiding this comment.
Staff review — keploy/keploy#4661
Reviewed with the three companion PRs on keploy/enterprise#2659 (enterprise#2676, k8s-proxy#1002, integrations#305).
Verdict: no Blocker or Major found.
This is the smallest of the four but has the widest blast radius: preferCurrentTestWindow sits in HTTP mock selection, which every replay uses, so a reorder that fires when it should not would hand wrong mocks to unrelated suites. The claim is that nothing changes without an active window. I checked it rather than took it, and the guards are complete:
len(mocks) < 2 || mockDb == nil→ returnsmocksuntouched;winStart.IsZero() || winEnd.IsZero()→ untouched, so no active window means no reorder;len(inside) == 0 || len(rest) == 0→ untouched, so all-inside or all-outside keeps the original order.
Only a genuine mix reorders, and the partition is stable — both groups are appended in their original relative order, so within a group the previous choice is preserved. The boundary check is inclusive on both ends (!reqAt.Before(winStart) && !reqAt.After(winEnd)), and a zero ReqTimestampMock is treated as outside rather than accidentally inside.
One implementation detail worth noting because getting it wrong would be a silent corruption: inside is a fresh slice (make([]*models.Mock, 0, len(mocks))), so append(inside, rest...) cannot alias and overwrite the caller's backing array. Had inside been a re-slice of mocks, this would have scrambled the input. It is not.
The OnTestWindow hook (C2) is additive and declared structurally, so integrations still builds against releases without it — which is what keeps keploy/integrations#305 independently mergeable.
Scope and honesty note. These four PRs total ~5,900 added lines. I did not read every line; I prioritised by risk and verified the specific things most likely to be Blocker/Major, listed above. Per Gaurav's instruction this review carries Blocker and Major only — smaller observations are omitted deliberately, not because none exist. Gates not run: this machine is out of disk and the Go build cannot complete, so I have no local test result for any of these branches and am not implying one. Everything above is from reading the code at the head shown.
🤖 Generated with Claude Code
Describe the changes that are made
Two replay fixes found while making DaemonSet proxy-via-proxy record/replay green (a k8s-proxy with the DaemonSet agent recording another k8s-proxy, plus a plain Go control app).
1.
fix(http): prefer the mock recorded in the current test window (C1)pkg/agent/proxy/integrations/http/match.goPOST /v1/authorizefor a valid PAT, then for a bad one. The two recorded mocks have the same URL and body and differ only in theAuthorizationvalue, which is marked as noise.DeriveLifetimelax-promotes tagged HTTP mocks into the session pool (pkg/models/lifetime.go), so neither mock is consumed or window-filtered, andExactBodyMatchreturns the first byte-identical body. The bad-token test gets the valid token's200instead of its recorded401.preferCurrentTestWindowstable-reorders the schema-matched candidates so that those whoseReqTimestampMocklies insideCurrentTestWindow()come first — the rulemysql/replayeralready applies. Only a recorded timestamp inside the window moves a mock. With no active window, a single candidate, or no candidate inside the window, the order and the chosen mock are exactly as before.2.
feat(proxy): add anOnTestWindowhook for parsers (C2)pkg/agent/proxy/mockmanager.go,pkg/agent/proxy/scoped_mockdb.go,pkg/agent/proxy/integrations/integrations.gointegrations.TestWindowListener.OnTestWindow(fn)registers a callback that runs each time a real test window is published:SetMocksWithWindowpast itsBaseTimestaging call, or a non-zeroSetCurrentTestWindow. It runs synchronously on the setter's goroutine, after every MockManager lock is released and before the setter returns, so whatever the callback writes reaches the app before the replayer sends that test's request.SetMocksWithWindowis now a thin wrapper around the unchanged body (renamedsetMocksWithWindow, which reports whether it published a window).scopedMockDbforwardsOnTestWindow, the same way it forwardsRevision.Links & References
Closes: NA — part of the DaemonSet proxy-via-proxy record/replay work.
🔗 Related PRs
go.keploy.io/server/v3bump to the release carrying this PR🐞 Related Issues
📄 Related Documents
What type of PR is this? (check all applicable)
Added e2e test pipeline?
Added comments for hard-to-understand areas?
Added to documentation?
Are there any sample code or steps to test the changes?
TestMatch_IdenticalSessionMocksPreferCurrentTestWindowdrivesmatch()with the two authorize mocks: each test window gets its own reply; with no window, or a window matching neither, the first recorded mock still wins (unchanged behaviour).TestOnTestWindow_RunsWithNoLockHeldreads the pool back from inside the listener (GetPerTestMocksInWindowtakesswapMu), so a listener run under a MockManager lock fails the test instead of deadlocking silently../pkg/agent/proxy/and./pkg/agent/proxy/integrations/http/suites pass;go vetclean;golangci-lintv2.13.1--new-from-rev: 0 issues.Self Review done?
Any relevant screenshots, recordings or logs?
DaemonSet rig (kind): the recorder k8s-proxy records target k8s-proxy A and a Go control app, then auto-replays. Images were built from k8s-proxy#1002, enterprise#2676 and integrations#305 with this PR's two commits on v3.6.79 (
932fa2d1f,63b147253— before themainmergedf5461f20, which changes none of this PR's files). Two runs of each:401) passes because of C1.readyReplicas) and the control app's step 11 (informer pod count) pass because of C2 + integrations#305.Follow-ups (after this PR)
/proxy/update/status) answers differently from the recording. Not addressed here; next.🧠 Semantics for PR Title & Branch Name
Please ensure your PR title and branch name follow the Keploy semantics:
📌 PR Semantics Guide
📌 Branch Semantics Guide
Additional checklist:
🤖 Generated with Claude Code