Fix PubSub replay value retention - #6805
Conversation
🦋 Changeset detectedLatest commit: 9837b66 The changes in this PR will be included in the next version bump. This PR includes changesets to release 30 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
ReplayWindowImplrefactored to use bounded arrays — snapshots replay values at construction time and releases references on drain/unsubscribe, breaking the unbounded retention chain.ReplayBuffer.slide()stores slid-out values — a bounded ring buffer feeds replay windows during sliding PubSub operation without holding the growing linked list.- PubSub
slide()methods pass slid-out values — all four implementations now capture and forward the slid-out value, andslide()is called only when a value actually slides out. - Three new deterministic regression tests — object-graph reachability checks for drained and undrained windows, plus replay order preservation across multiple slides.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
6a40234 to
8a9f103
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Index-based replay ordering —
ReplayBuffer.offer()returns a publisher index; each PubSub implementation stores per-slot replay indices;slideValuesentries carry{value, index}pairs soReplayWindowImpl.sync()deduplicates by index rather than rebuilding the window. close()promoted to theReplayWindowinterface — removes theinstanceof ReplayWindowImplcheck inunsubscribe.- Simplified
sync()logic — single unified loop with index comparison replaces the two-branch partial-rotation / full-rebuild approach. - New sliding order test — verifies monotonic ordering for a lagging subscriber in a scoped sliding pubsub.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
|
Benchmark rerun for Harness:
That is a measured reduction of 0.85 ns/op (4.6%) in this fresh run. The absolute timings are lower than the earlier local experiment, but all paired runs consistently favored the stable node shape. Validation:
|

Summary
Root cause
ReplayWindowImplretained a node fromReplayBuffer's linked list. Future publications extended that same list, so a live subscription could keep every later value reachable even after its replay window was drained. A never-draining subscription retained the same unbounded suffix from its original replay node.The replay window cannot simply derive every take from the current buffer head: existing replay semantics require a subscription to retain its original replay snapshot, while sliding PubSubs replace only values that are explicitly slid out. This change keeps those semantics while bounding all replay-owned references.
Validation
pnpm lint-fixpnpm test --run packages/effect/test/PubSub.test.ts packages/effect/test/SubscriptionRef.test.ts(38 tests)pnpm checkCloses EFF-262
Closes EFF-264
Closes #6804