Pinned event-mongodb resume-token persistence and pending acknowledgment boundaries
Parent: Change Data Capture Patterns · Published reference · snapshot 2026-11-02
↓ Facts as markdownall context files
Source-anchored research for Pinned event-mongodb resume-token persistence and pending acknowledgment boundaries. Run status: COMPLETED.
These notes link each claim to its source. Some citations identify archived research reports that are no longer hosted on this site. A published reference means the content is available; it does not certify independent review or accuracy.Read the editorial policy and follow the sources before relying on a claim.
Structure and components
- buildMessage installs a callback that calls Ack on the acknowledgment store only for nil or errors matching eventlib.ErrAck and only when an ack store is configured. The callback uses a background context with detachedTimeout, logs Ack errors and returns them. This callback contains no resume-token update or rollback. (low confidence) [source]
How it works
- Initial startup can save immediately: when no existing token is loaded and startup is not from the past, TryNext either processes an event before saving its token or saves the available cursor token without an event. On main-loop exit, the watcher attempts to flush a non-nil pendingToken. saveResumeToken uses a timeout on context.Background and returns Save errors; these paths are attempts, not a guarantee that persistence succeeds. (low confidence) [source]
- The Ack method on MongoAckStore updates only a matching event whose acked_at is time.Time{} and decrements pending only when ModifiedCount is positive. The fixture invokes Ack twice for the stored event, then for an event never stored, and asserts the local count remains zero. It checks document-level repeated acknowledgment, not completion of every subscriber. (medium confidence) [source 1] [source 2]
Problems, failure modes and limitations
- For a decoded change, processChange returns nil when an empty update is discarded or the built payload is nil, returns a payload-building error when buildPayload fails, and otherwise returns the result of storeAndPublish. A successful return therefore does not by itself establish downstream handler completion. (low confidence) [source]
- storeAndPublish calls Store on the configured acknowledgment store before channel publication and returns immediately if Store fails. It attempts publication to every registered string event name and joins publication errors. With no registered event names it can return nil after only logging a warning; this success does not establish delivery to a subscriber. (low confidence) [source]
- In the main change-stream loop, an error from t.processChange(ctx, cs) continues the loop before token handling. After a successful call, the loop assigns cs.ResumeToken() to pendingToken when a resume-token store exists. It calls t.saveResumeToken when elapsed time reaches resumeTokenSaveInterval, clearing pendingToken and updating lastTokenSave only after the save succeeds. The interval is 5 * time.Second, checked during successful event iterations. The separate acknowledgment callback does not control this token-save branch. (low confidence) [source]
- The Store method on MongoAckStore inserts an acknowledgment document. A duplicate-key error returns nil without incrementing the local pending counter; a successful new insert increments it. The duplicate-store fixture calls Store twice for the same event and asserts one pending entry in the local counter. These are store semantics, not a transport-level suppression of publication. (medium confidence) [source 1] [source 2] [source 3]
- A counter limitation follows from the implementation: if a new MongoAckStore instance acknowledges a pending document inserted by another instance, a successful modification decrements its own initially zero counter. No ownership check or lower bound appears in Ack. Thus process-local PendingCount must not be assumed equal to persistent Count across store instances. This is a conditional inference from the shown constructor and Ack implementation. (low confidence) [source]
Facts and statements
- The processChange body propagates decoding and payload-building errors and, after constructing a non-nil payload and message, returns the storeAndPublish result. Its caller continues the main stream loop before token handling on a non-nil return. A later successful event can overwrite pendingToken with cs.ResumeToken() and persist that later position. The loop records no barrier for the failed event. Consequently, skipping the failed iteration’s token save alone does not establish replayability after later progress is saved. This is an inference from the shown caller and method return paths. (low confidence) [source]
- PendingCount reads a local atomic counter; Count executes CountDocuments with buildAckFilter, whose pending status selects acked_at equal to time.Time{}. The constructor initializes no counter from the database, and the initial-count fixture asserts zero on a fresh store. The local counter therefore does not reconstruct persistent pending state after a restart. (medium confidence) [source 1] [source 2]
- The resume-persistence fixture waits only for a nonempty stored token, closes the first bus, inserts a document during downtime, and opens another transport with the same resume-token ID. Its resumed-event assertion checks OperationInsert, without checking that event's document identity. Neither transport in this fixture configures WithAckStore. This source fixture does not demonstrate abrupt-crash recovery, replay of pending acknowledgments, or an end-to-end handler acknowledgment boundary. These statements describe the fixture source. (low confidence) [source]
Children
- No children recorded.