fix(replication): propagate per-event expiresAt to record writes - #640
Conversation
Three issues prevented replicated records from being evicted: 1. The `options` object passed to `_writeUpdate` for each replicated event did not include `expiresAt`. In a multi-record transaction batch every event after the first uses the first event as `context`, so `context.expiresAt` would silently apply the wrong expiration (or none) to subsequent records. 2. `_writeUpdate` read `expiresAt` only from `context`, so per-event options were ignored. Now: `options?.expiresAt ?? context?.expiresAt ?? fallback`. 3. `scheduleCleanup()` was only armed when `context.expiresAt` was truthy, missing replicated writes that carry expiration via options. Changed to `if (expiresAt >= 0)` to cover both paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| if (options?.residencyId != undefined) residencyId = options.residencyId; | ||
| const expiresAt: number = context?.expiresAt ?? (expirationMs ? expirationMs + Date.now() : -1); | ||
| const expiresAt: number = | ||
| options?.expiresAt ?? context?.expiresAt ?? (expirationMs ? expirationMs + Date.now() : -1); |
There was a problem hiding this comment.
Missing test for the new options.expiresAt path
What: options?.expiresAt ?? is a new first-priority branch. In the replication loop it's populated from event.expiresAt (line 331); for local writes the third arg to the static put call flows into context.expiresAt via the transactional() wrapper, so every existing test in caching.test.js exercises the context?.expiresAt fallback, not this new leg.
Why it matters: A regression to this path — e.g. silently reverting the resolution order — would not be caught by any existing test. This is specifically the path the PR claims to fix.
Suggested fix: Add a test that calls _writeUpdate with options.expiresAt set but no context.expiresAt, and asserts the stored entry carries the correct expiresAt value (and that the cleanup scanner is armed). The "Handles eviction-only config without expiration" shape in caching.test.js is a reasonable model.
|
1 blocker: the new |
Problem
Records received via replication were not being evicted by the cleanup scanner even when they carried an
expiresAtvalue. Three issues (same as #639 which backports this tov5.0):1.
expiresAtmissing from options passed to_writeUpdateIn a multi-record transaction batch, every event after the first is processed with
txnInProgress(the first event) ascontext. Subsequent events had noexpiresAtin theiroptions, so_writeUpdatesawcontext.expiresAtfrom the first record applied to all records — or saw nothing if the first record had no expiration.2.
_writeUpdatedidn't readexpiresAtfrom options3.
scheduleCleanup()not armed for replicated writesSee also
v5.0: fix(replication): propagate per-event expiresAt to record writes #639Signed-off by Claude