reckon_db_dcb (reckon_db v3.1.1)
View SourceDCB conditional-append primitive.
Implements append_if_no_tag_matches/4 as a khepri:transaction/2 body. The transaction:
1. Verifies the tag-filter context ({context_changed, _} on conflict). 2. For integrity-enabled stores: verifies the seq counter and chain-tip still match what the caller observed when pre-stamping MACs. On mismatch aborts with {dcb_state_changed, _} and the outer loop retries with a fresh snapshot. 3. Writes events under ?DCB_STREAM_PATH ++ [SeqKey] plus one tag-index entry per tag at ?BY_TAG_PATH ++ [Tag, SeqKey]. 4. Updates the seq counter (and chain-tip if integrity is enabled).
The whole sequence happens inside one Ra log entry; atomic across the cluster. Either everything commits, or an abort comes back and nothing changed.
Integrity (reckon-db 3.2.0+)
On integrity-enabled stores, DCB events carry prev_event_hash + mac like any other integrity-bearing event. Because Khepri's Horus extractor rejects code containing crypto:* calls (even on unreachable branches; extraction sees the whole function), MAC chains are pre-computed OUTSIDE the transaction. Inside the transaction we verify the chain-tip + counter are still what we saw, then write the pre-stamped records.
Two retry vectors: - {context_changed, _}: tag-filter conflict. Callers (e.g., evoq_decision_runtime) retry with fresh context. - {dcb_state_changed, _}: counter or chain-tip moved between our pre-stamp read and the transaction. The retry loop in this module handles it transparently, bounded by ?INTEGRITY_RETRY_BUDGET.
Summary
Functions
-spec append_if_no_tag_matches(StoreId :: atom() | binary(), TagFilter :: reckon_gater_types:tag_filter(), SeqCutoff :: reckon_gater_types:seq_cutoff(), Events :: [map()]) -> {ok, LastSeq :: non_neg_integer()} | {error, {context_changed, non_neg_integer()}} | {error, no_events} | {error, dcb_concurrent_writer_exhausted} | {error, term()}.