Chapter 13 โ ChangeStream Backpressure and Overflow
Chapter objective: After this chapter, a developer can choose ChangeStream capacity, origins, and overflow behavior explicitly, and make drops, blocking, and callback timeouts observable.
Learning objectives
- Separate collection convergence from ChangeStream delivery.
- Explain bounded buffering as a stability boundary.
- Compare
DROP_OLDEST,DROP_NEWEST,BLOCK, andERROR. - Select origins and failure behavior for a consumer's purpose.
Prerequisites
- The Register change-stream experiment in Chapter 5 is complete.
- Delta and repair from Chapters 11โ12 are understood.
- ChangeStream is not treated as a durable business-event log.
Case progress
Metrics Adapter sends route-hints changes to a slowing
diagnostics dashboard. The collection continues updating while the
consumer falls behind. The team decides whether to drop older or newer
observations, block briefly, or report an explicit error.
Choosing behavior when the buffer is full
ChangeStreamOptions defaults to local and remote events,
capacity 1024, DROP_OLDEST, a one-second block limit, and a
five-second subscriber callback limit. The defaults bound resource use;
they do not replace a business choice.
| Policy | Full-buffer behavior | Suitable use | Main cost |
|---|---|---|---|
DROP_OLDEST |
discard oldest, admit newest | UI refresh and current-state hints | incomplete intermediate changes |
DROP_NEWEST |
keep queued events, reject newest | short FIFO work preserving its prefix | increasingly stale current view |
BLOCK |
wait for capacity until timeout | controlled consumer tolerating short jitter | producer waits; timeout can still drop |
ERROR |
raise a structured overflow error | validation/control path forbidding silent loss | caller handles failure |
These policies change the observation stream, not the committed
collection value. After a stream drops an event,
register.get(key) can still return the latest value. A
current-state consumer rereads the collection instead of assuming event
completeness.
Origin filtering is part of the contract
Event origin distinguishes a local operation from a remote delta. Local-only observation can confirm actions on one node; local plus remote helps diagnose convergence. Disabling both has no meaning and the constructor rejects it.
For the running case, the diagnostics dashboard labels both origins. A local publishing aid may watch local events only, while remaining separate from the transaction audit source. Repair validation combines events with a final collection read.
A slow callback cannot hold the publisher indefinitely
A push subscriber callback also has a timeout and reports excess
duration through onError. A stable design keeps the
callback to a fast handoff, places slow IO in a separate bounded
executor, and records low-cardinality overflow and callback-timeout
metrics.
Counterexample and fault injection
Set capacity to one and publish two changes without consuming.
DROP_OLDEST retains the second, DROP_NEWEST
retains the first, BLOCK waits and then records overflow on
timeout, and ERROR raises a structured error. The
consumer's need for current view, ordered prefix, or explicit failure
determines the policy.
Experiment
cd submodule/dsm
mvn -q -pl dsm-runtime -am \
-Dtest=BufferedChangeStreamTest,InMemoryDsmRegisterTest \
-Dsurefire.failIfNoSpecifiedTests=false testComplete overflow-cases.json with the retained event,
producer outcome, and required metric for all four policies.
Experiment acceptance card
| Field | Content |
|---|---|
| Command | The focused Maven tests above;
node --test tests/chapter-assets.test.mjs |
| Input or fault | capacity one, two events, slow consumer, and timed-out callback |
| Observable result | the four policies retain/reject differently; overflow and callback timeout are visible |
| Evidence level | E2: collection and buffer behavior tests |
| This experiment does not prove | Durable ChangeStream history, cross-process exactly-once, or sufficient production capacity |
Review
- Correct collection state does not imply that every subscriber saw every event.
- Bounded capacity has an explicit overflow policy.
- Blocking is time-bounded behavior, not unlimited reliability.
- After observation loss, the consumer rereads state or uses a durable event system according to its purpose.
Next
Chapter 14 combines the failure windows: during a partition both nodes can return local success, and repair cannot revoke business commitments already made.