Fix memory journal returns the last remote sequence instead of the next - #7034
Conversation
🦋 Changeset detectedLatest commit: e65b8e3 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.
Important
This PR adds a focused regression test but does not yet include the memory-journal implementation fix. Please add the fix before merging.
Reviewed changes
- Added a regression test in
packages/effect/test/unstable/eventlog/EventJournal.test.tsasserting thatnextRemoteSequencereturns1after importing a remote entry withremoteSequence: 0.
The test correctly fails against the current memory implementation (packages/effect/src/unstable/eventlog/EventJournal.ts:481 returns remote.sequence unchanged, while the SQL and IndexedDB implementations both add one). I ran the focused reproduction command and confirmed the failure: AssertionError: expected +0 to equal 1.
Before this can land:
- Apply the memory-journal fix: maintain
remote.sequenceas the next unused sequence by storingremoteEntry.remoteSequence + 1inwriteFromRemote(both the duplicate and uncommitted branches), sonextRemoteSequencecan return it unchanged. - Update the
nextRemoteSequenceJSDoc (packages/effect/src/unstable/eventlog/EventJournal.ts:89) from "last known sequence number" to "first unused sequence number". - Expand the test to cover the initial
0state and a non-zero sequence.
@v0 or keep the SHA fresh with Dependabot | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Applied the memory-journal implementation fix:
writeFromRemotenow storesremoteEntry.remoteSequence + 1in both the duplicate and uncommitted branches, soremote.sequencealways tracks the first unused remote sequence. - Updated the
nextRemoteSequenceJSDoc to describe the returned value as the first unused sequence. - Expanded the regression test to assert the initial empty state returns
0, aremoteSequence: 0import returns1, and a non-zero import (5) returns6. - Added a patch changeset for
effect.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
d7602af to
1b4c444
Compare
1b4c444 to
e65b8e3
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Since the prior pullfrog review at d7602af, the branch was rebased onto a newer main and the regression coverage was restructured.
- Extracted the sequence assertions into a dedicated
it.effect("returns the next unused remote sequence")that asserts the empty state returns0, aremoteSequence: 0import returns1, and a duplicateremoteSequence: 5import returns6. - Verified the memory-journal implementation fix (
remoteEntry.remoteSequence + 1in both duplicate and uncommitted branches), thenextRemoteSequenceJSDoc update, and the patch changeset remain unchanged after the rebase.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Summary
After importing remote sequence
n, the memory journal returnsnas the next synchronization start instead ofn + 1. Sequence zero is especially indistinguishable from the initial empty state.Important
This PR includes focused regression coverage and the memory-journal implementation fix.
Memory journal returns the last remote sequence instead of the next
Module:
effect/unstable/eventlog/EventJournalAudit ID:
effect-2f6a0458baf5d1feSeverity / confidence: high / high
What happens
After importing remote sequence
n, the memory journal returnsnas the next synchronization start instead ofn + 1. Sequence zero is especially indistinguishable from the initial empty state.Why it happens
Each memory remote stores a
sequenceinitialized to zero; imports update it to the greatest observedremoteSequence, andnextRemoteSequencereturns that field unchanged. The SQL implementation computesMAX(sequence) + 1and the IndexedDB implementation reads the greatest sequence and adds one, confirming that this state is a cursor for the next request rather than the last value itself.Expected behavior
nextRemoteSequence(remoteId)must return the first unused sequence for that remote: zero when no remote entries are recorded, otherwise one greater than the maximum recorded sequence.Relevant implementation
These links and excerpts are pinned to audit base
17f0b91a243ccfe4a38d27debdc983adf434e738.packages/effect/src/unstable/eventlog/EventJournal.ts:417-481View problematic code at
packages/effect/src/unstable/eventlog/EventJournal.ts:417-466View exact lines on GitHub
Excerpt truncated. Open the complete packages/effect/src/unstable/eventlog/EventJournal.ts:417-481 range.
Reproduction
pnpm test --run packages/effect/test/unstable/eventlog/EventJournal.test.tsValidation: The focused contract assertion fails against 17f0b91 and passes with this fix.
Implementation
The memory journal now stores the first unused remote sequence after both new and duplicate remote imports. The regression test covers the empty state, sequence zero, and a non-zero duplicate sequence.
Validated with:
pnpm test --run packages/effect/test/unstable/eventlog/EventJournal.test.tsAudit provenance
17f0b91a243ccfe4a38d27debdc983adf434e73817f0b91a243ccfe4a38d27debdc983adf434e738effect-2f6a0458baf5d1feCloses EFF-516