Skip to content

CBG-5951: make changeCache.initialSequence atomic - #8854

Merged
torcolvin merged 1 commit into
mainfrom
CBG-5951
Oct 6, 2026
Merged

torcolvin merged 1 commit into
mainfrom
CBG-5951

Conversation

@torcolvin

Copy link
Copy Markdown
Collaborator

The caching DCP feed starts before changeCache.Start sets initialSequence, so the feed is running while Start writes the field. DocChanged and processPrincipalDoc read initialSequence without the cache lock. A feed event that arrived while the database was starting raced with the write in Start. TestPushReplicationAPIUpdateDatabase (CBG-1903) hits this by reloading a database while documents are being written.

Store initialSequence as an atomic.Uint64. An event that arrives before Start can still read zero and pass the startup filter, but processEntry waits for the cache lock and then drops it as a duplicate of a sequence below nextSequence.

Add TestDocChangedBeforeStart, which delivers feed events between Init and Start. Without the fix, it fails under -race with the same data race.

Testing:

  • go test -race ./db/... ./rest/... (EE) passes.
  • TestPushReplicationAPIUpdateDatabase from CBG-1903, applied on top of this change, passed 5 of 5 runs with -race.

🤖 Generated with Claude Code

The caching DCP feed starts before changeCache.Start sets initialSequence,
so the feed is running while Start writes the field. DocChanged and
processPrincipalDoc read initialSequence without the cache lock. A feed event
that arrived while the database was starting raced with the write in Start.
TestPushReplicationAPIUpdateDatabase hits this by reloading a database while
documents are being written.

Store initialSequence as an atomic.Uint64. An event that arrives before Start
can still read zero and pass the startup filter, but processEntry waits for the
cache lock and then drops it as a duplicate of a sequence below nextSequence.

Add TestDocChangedBeforeStart, which delivers feed events between Init and
Start.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@torcolvin
torcolvin requested review from a team and a balanced review from Copilot October 5, 2026 20:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Copilot was unable to run its full agentic suite in this review.

Copilot review overview

Review effort: Lite
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds coverage for DCP feed events arriving during startup and makes changeCache.initialSequence safe for concurrent reads/writes.

Changes:

  • Added a new test to validate DocChanged behavior when events arrive between Init and Start.
  • Switched initialSequence to an atomic uint64 and updated reads/writes to use Load/Store.
File Description
db/​changecachetest/​change_cache_test.go Adds a startup-timing regression test for DocChanged/cache filtering.
db/​change_cache.go Makes initialSequence atomic and updates comparison/set sites accordingly.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread db/changecachetest/change_cache_test.go
Comment thread db/change_cache.go
logsDisabled bool // If true, ignore incoming tap changes
nextSequence uint64 // Next consecutive sequence number to add. State variable for sequence buffering tracking. Should use getNextSequence() rather than accessing directly.
initialSequence uint64 // DB's current sequence at startup time.
initialSequence atomic.Uint64 // DB's current sequence at startup time. Atomic because DocChanged reads it without the lock, and the feed starts before Start sets it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine DocChanged may not flag as a sequence below initial sequence on startup when initial sequence is 0 (not yet set) but procesEntry duplicate checking will filter it out and by the time any goroutine acquires change cache lock this value must be set.

Comment on lines +3094 to +3102
var wg sync.WaitGroup
for _, event := range events {
wg.Go(func() {
changeCache.DocChanged(event, db.DocTypeDocument)
})
}

require.NoError(t, changeCache.Start(initialSequence))
wg.Wait()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goroutines are needed here otherwise will deadlock. I agree though that this doesn't guarantee that the events are delivered before Start is called. But we already have testing around that handling so no need to worry about the ordering here.

@gregns1 gregns1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@torcolvin
torcolvin merged commit c665a2f into main Oct 6, 2026
71 of 110 checks passed
@torcolvin
torcolvin deleted the CBG-5951 branch October 6, 2026 16:39
torcolvin added a commit that referenced this pull request Oct 12, 2026
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants