fix(meta): keep reader temp info alive with a session lease - #266
fix(meta): keep reader temp info alive with a session lease#266tinswzy wants to merge 2 commits into
Conversation
Reader temp info was created with a fixed 60s lease that nothing ever renewed (an etcd Put with WithLease re-binds the key but does not refresh the lease TTL), so every reader older than 60s silently lost its metadata and every subsequent UpdateReaderTempInfo failed with "reader temp info not found" forever. The metadata is what protects truncated segments still needed by lagging readers from cleanup, so that protection silently switched off for exactly the readers it exists for. Key changes: - CreateReaderTempInfo now guards the key with a concurrency.Session (automatic keepalive at ~TTL/3, mirroring the writer-lock pattern), so liveness is independent of read progress; a crashed process still auto-expires within the TTL. - UpdateReaderTempInfo is now an unconditional upsert rebuilt from the reader's cached open position, gated on the in-process session entry: an owned reader self-heals its key after lease loss, while a closed reader can never resurrect it (a resurrected key with a live keepalive would pin the cleanup low-watermark forever). - DeleteReaderTempInfo and provider Close() close the session (revoking the lease deletes the key immediately), keeping the explicit etcd delete as a fallback. - Regression tests: idle reader outlives the lease TTL, update self-heals after lease revocation, update after delete does not resurrect the key. Fixes #265 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: tinswzy <zhenyuan.wei@zilliz.com>
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (73.10%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## master #266 +/- ##
==========================================
- Coverage 83.69% 83.64% -0.06%
==========================================
Files 182 182
Lines 25458 25544 +86
==========================================
+ Hits 21307 21366 +59
- Misses 3172 3194 +22
- Partials 979 984 +5
🚀 New features to boost your workflow:
|
|
You've hit your session limit · resets 11:20am (America/Los_Angeles) |
Re-review of
|
Address the adversarial review findings on #266: - Do not rotate the session when a Put fails for a reason unrelated to the lease (caller cancelled, etcd slow): closing a session revokes its lease and etcd deletes every key attached to it, so the old code deleted the live reader's own temp info on any transient write error. Rotate only when the lease is actually gone (ErrLeaseNotFound, or the session's Done channel is closed). - Close the TOCTOU window between UpdateReaderTempInfo's map lookup and DeleteReaderTempInfo: entries are now marked closed under their mutex whenever they are retired (delete, same-name reopen, provider close), and updates re-validate ownership after acquiring the lock, so a late update can no longer resurrect a deleted key on an untracked session. - Bound session creation by the caller's deadline: grant the lease with the request-scoped context, then attach the session to that lease. Passing the request context to concurrency.NewSession itself would kill the keepalive when the request ends, silently reintroducing the original expiry bug. - Test hygiene: the idle-reader test honours testing.Short() and uses a 5s TTL for more margin on loaded runners. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: tinswzy <zhenyuan.wei@zilliz.com>
|
@czs007 Replying to the re-review of 97c15df802df: All four findings confirmed and addressed in c2874da, each with a regression test that was verified failing on the previous commit first:
Verified: Known boundary unchanged: a concurrent same-name reopen while an update is mid-flight can still transiently drop the key until the next update (real reader names are unique per open, so this is not reachable in supported usage); it is narrowed but not eliminated by the closed-flag gate. |
Summary
Reader temp info was created with a fixed 60s lease that nothing ever renewed — an etcd
PutwithWithLeasere-binds the key but does not refresh the lease TTL — so every reader older than 60s silently lost its metadata, and every subsequentUpdateReaderTempInfo(a strict get-then-put) failed withreader temp info not foundforever. Since this metadata is what protects truncated segments still needed by lagging readers from physical cleanup, the protection silently switched off for exactly the readers it exists for. See #265 for the full analysis; observed in the field as part of milvus-io/milvus#52341.Key changes
CreateReaderTempInfonow guards the key with aconcurrency.Session(automatic keepalive at ~TTL/3, mirroring the existing writer-lock pattern). Liveness is decoupled from read progress, so idle/slow readers keep their metadata; a crashed process still auto-expires within the TTL. Reopening the same reader name swaps in a fresh session and closes the stale one.UpdateReaderTempInfois now an unconditional upsert rebuilt from the reader's cached open position, gated on the in-process session entry:reader temp info not founderror — blind adoption would resurrect a key with a live keepalive after close and pin the cleanup low-watermark forever.DeleteReaderTempInfo/ providerClose()close the session first (revoking the lease deletes the key immediately), keeping the explicit etcd delete as a fallback for keys not owned by this process.readerTempInfoSessionTTLSeconds(default unchanged at 60) so tests can shrink it.No key layout or value format changes; mixed-version deployments keep working (keys written by old clients keep the old 60s-expiry behavior).
Testing
Written test-first (both regressions verified failing on master before the fix):
test reader temp info outlives lease ttl while idle— an idle reader's metadata survives 2.5× the lease TTL and stays attached to a live lease (fails on master: the key expires).test update reader temp info self heals after lease loss— after the lease is revoked and the key removed, the next update recreates the key, preserves the open position, and re-attaches a live lease (fails on master:reader temp info not found).test update after delete does not resurrect reader temp info— a late update after close is rejected and the key stays deleted (guards the deliberate non-resurrection gate).testUpdateReaderTempInfoWithoutLeaserenamed totestUpdateReaderTempInfoWithoutSessionwith inverted semantics to pin the new gate.go test ./meta/andgo test ./woodpecker/log/pass;golangci-lint run ./meta/...reports 0 issues.Known boundaries (deliberately out of scope): the stuck-retry behavior on
no record extractfrom milvus-io/milvus#52341 is a separate defect; "process crash → key expires within TTL" relies on etcd session semantics and is not separately unit-tested.Fixes #265
🤖 Generated with Claude Code