Streaming Reads
The dcb/read.go implementation streams events end-to-end without buffering all results in memory. It uses a k-way merge over multiple FoundationDB range iterators to produce a single globally ordered event stream.
Why K-Way Merge?
A single Query can expand into multiple FDB ranges — for example, one range per event type, or one range per tag combination. Each individual range is already ordered by versionstamp (since versionstamp is the last element in the key). The merge combines these sorted streams in real time to produce a single ordered result.
Components
rangeIterator
Wraps an fdb.RangeIterator. Keeps track of:
- The current FDB key/value
- The current extracted versionstamp
vsHeap
A min-heap ordered by versionstamp. When two iterators have equal versionstamps, ordering is broken by the original iterator index for determinism.
readEvents
The main driver: builds all ranges, initializes iterators, runs the merge loop, and yields events one at a time via iter.Seq2.
Algorithm
for each range in buildQueryRanges(query):
iterator = initRangeIterator(range)
if not exhausted: push iterator into min-heap
lastEmitted = nil
while heap not empty:
iterator = heap.pop_min() // smallest versionstamp
vs = iterator.current_versionstamp
if vs == lastEmitted: // deduplication
iterator.advance()
if not exhausted: heap.push(iterator)
continue
event = fetchEvent(primaryStore, vs) // lookup from /e/<vs>
yield event
lastEmitted = vs
iterator.advance()
if not exhausted: heap.push(iterator)
Memory Profile
The merge loop never buffers all results. It holds:
- One current item per active range iterator
- The heap (one entry per active iterator)
Memory usage is O(number of ranges), not O(number of events). A query with 3 types across 2 tag combinations → at most 6 iterators in memory simultaneously, regardless of result set size.
Ordering Guarantees
- Events are always yielded in ascending versionstamp order.
- Equal versionstamps (which cannot occur for distinct events but may appear across ranges pointing to the same event) are deduplicated — each event is emitted exactly once.
- Deduplication is stable: if multiple ranges point to the same versionstamp, the first one wins.
Early Exit
The merge terminates cleanly when:
- The caller stops the iterator (returns
falsefrom its range handler) - The configured
LimitinReadOptionsis reached
No additional FDB ranges are scanned after the stop condition is met.
Go Iterator Integration
Results are yielded via Go 1.23's iter.Seq2[StoredEvent, error]:
for event, err := range store.Read(ctx, query, nil) {
if err != nil {
return err
}
// process event
}
The caller controls iteration pace. The store does not pre-fetch or buffer beyond what is needed for the current merge step.