Skip to content

Commit-blind cache reconciliation: local cache event listener ignores LedgerCommitPublished #1381

Description

@bplatz

Severity: medium

Component: fluree-db-api local cache event listener / ledger reconciliation

Summary

spawn_local_cache_event_listener (fluree-db-api/src/lib.rs:1197) reconciles the in-process ledger cache in response to nameservice events, but it only acts on LedgerIndexPublished and LedgerRetracted. LedgerCommitPublished falls through the catch-all Ok(_) => {} arm (fluree-db-api/src/lib.rs:1241) and is silently ignored.

The net effect: in-process cache reconciliation is index-driven only. The cache catches up when a new index root is published (asynchronous, rate-limited by reindex_min_bytes, ~100KB default), not when a commit is published.

Impact

  • Harmless for a strict single-process writer: the committer advances its own cached handle atomically under the write lock (finalize_commitwrite_guard.replace), so it never depends on the event listener to see its own writes.
  • Load-dependent read staleness for passively-synced readers: any peer/proxy/SSE-follower — or any component reading a handle it did not itself write — only reflects committed-but-unindexed data after the next index publish. Under high write rates the window between a commit and the next index-published reconcile widens, so a read can transiently observe a behind-head cached t.

This is the same class of gap previously root-caused as the Raft follower-cache CI flake (the follower cache ignored LedgerCommitPublished). It is also the seam that a min-t read masks today: await_query_min_t_requirementsrefresh()notify() forces a catch-up that the listener would otherwise only perform on index publish.

Current code

fluree-db-api/src/lib.rs:1207-1241:

Ok(NameServiceEvent::LedgerIndexPublished { ledger_id, .. }) => { /* ledger_manager.notify(...) */ }
Ok(NameServiceEvent::LedgerRetracted { ledger_id })          => { /* ledger_manager.disconnect(...) */ }
Ok(_) => {}   // <-- LedgerCommitPublished lands here and is dropped

The event exists and is emitted on every commit publish:
NameServiceEvent::LedgerCommitPublished { ledger_id, commit_id, commit_t }
(fluree-db-nameservice/src/lib.rs:831; emitted at fluree-db-nameservice/src/notifying.rs:181 and :286).

Proposed fix

Add a LedgerCommitPublished arm that calls ledger_manager.notify(NsNotify { ledger_id, record: None }), routing through the CommitCatchUp path in LedgerManager::notify / UpdatePlan, so the cache reconciles on commits rather than only on index publishes.

UpdatePlan::plan is already novelty-protective (no-op when local t ≥ nameservice head; IndexOnly/CommitCatchUp only when behind), so this is safe for the writer's own handle and only advances lagging readers.

Notes / context

  • Found while triaging a (separate, misdiagnosed) DELETE-WHERE staleness report. On a strict single in-process node the write path is read-your-writes-correct and this listener gap does not affect it; the gap matters specifically for the peer/proxy/follower read paths, which is why it is filed here alongside the Raft work.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions