reckon_db_ccc_filter (reckon_db v5.5.1)

View Source

CCC (Command Context Consistency) filter evaluation inside Khepri transactions.

Evaluates a tag_filter() against the full set of CCC indexes — tags, event types, single-field payload, and composite payload hash — and returns either the set of matching seqs or an answer to "does any matching event have seq above Cutoff?".

This module supersedes reckon_db_dcb_filter (removed in 5.4.0). CCC is a superset of DCB: all tag/event_type filters still work; payload_match and payload_hash_match add CCC payload predicate support.

Two layers: - match_seqs/2: pure set algebra over seqs. Side effects are confined to the seqs-provider function, which the caller supplies. Test with a hardcoded mock provider. - match_any_above_cutoff/2: wraps match_seqs with the cutoff comparison and the production seqs-provider that reads from Khepri via khepri_tx:get_many/1. Must be called from inside a khepri:transaction/2 body.

The tag_filter() type is canonical in reckon_gater_types.hrl (reckon-gater 3.5.0+). Use it via the include; do NOT redefine here.

Summary

Functions

"Does any matching event have seq > Cutoff?" using the production Khepri-backed seqs provider. Call from inside khepri:transaction/2.

Pure-testable variant: the caller supplies the seqs provider.

Set of seqs whose events match the filter, per-event semantics.

Rewrite a tag_filter to replace {payload_hash_match, Keys, Values} with {payload_hash_match_pre, Hash}, where Hash is the pre-computed SHA-256 of the sorted [{Key,Value}] pair list.

Look up seqs by event type. Reads the event-type index inside a transaction. Returns the list of seqs for EventType. Empty list if none.

Look up seqs by single payload field value. Reads the payload index [by_payload, Key, Value, *] inside a transaction.

Look up seqs by pre-computed composite payload hash. Reads the payload hash index [by_payload_hash, Hash, *] inside a transaction.

Look up seqs by tag. Reads the tag index inside a transaction. Returns the list of seqs indexed under Tag. Empty list if no entries.

Types

match_result/0

-type match_result() :: false | {true, MaxSeq :: non_neg_integer()}.

seqs_provider/0

-type seqs_provider() ::
          fun((binary() |
               {event_type, binary()} |
               {payload, Key :: binary(), Value :: binary()} |
               {payload_hash_pre, Hash :: binary()}) ->
                  [non_neg_integer()]).

Functions

match_any_above_cutoff(Filter, Cutoff)

-spec match_any_above_cutoff(reckon_gater_types:tag_filter(), reckon_gater_types:seq_cutoff()) ->
                                match_result().

"Does any matching event have seq > Cutoff?" using the production Khepri-backed seqs provider. Call from inside khepri:transaction/2.

Cutoff = -1 means "I saw nothing yet" — ANY matching event triggers a conflict. Cutoff = N (>= 0) means "I saw events through seq N" — only seqs > N trigger a conflict.

match_any_above_cutoff(Filter, Cutoff, Provider)

-spec match_any_above_cutoff(reckon_gater_types:tag_filter(),
                             reckon_gater_types:seq_cutoff(),
                             seqs_provider()) ->
                                match_result().

Pure-testable variant: the caller supplies the seqs provider.

match_seqs(_, Provider)

-spec match_seqs(reckon_gater_types:tag_filter(), seqs_provider()) -> sets:set(non_neg_integer()).

Set of seqs whose events match the filter, per-event semantics.

Algebra: any_of(Tags) = union(seqs_for_tag(T) | T in Tags) all_of(Tags) = intersection(seqs_for_tag(T) | T in Tags) event_type(Type) = seqs_for_event_type(Type) payload_match(K, V) = seqs_for_payload(K, V) payload_hash_match_pre(H)= seqs_for_payload_hash_pre(H) or_(Filters) = union(match_seqs(F) | F in Filters) and_(Filters) = intersection(match_seqs(F) | F in Filters)

Empty tag/filter lists yield the empty set (no event matches "nothing").

preprocess_filter(Other)

-spec preprocess_filter(reckon_gater_types:tag_filter()) -> reckon_gater_types:tag_filter().

Rewrite a tag_filter to replace {payload_hash_match, Keys, Values} with {payload_hash_match_pre, Hash}, where Hash is the pre-computed SHA-256 of the sorted [{Key,Value}] pair list.

MUST be called OUTSIDE a Khepri transaction. The hash computation calls crypto:hash/2 (a NIF); Horus cannot extract NIF calls into the portable transaction form that Ra replicates to all cluster members.

Filters without payload_hash_match are returned unchanged.

seqs_for_event_type(EventType)

-spec seqs_for_event_type(binary()) -> [non_neg_integer()].

Look up seqs by event type. Reads the event-type index inside a transaction. Returns the list of seqs for EventType. Empty list if none.

seqs_for_payload(Key, Value)

-spec seqs_for_payload(binary(), binary()) -> [non_neg_integer()].

Look up seqs by single payload field value. Reads the payload index [by_payload, Key, Value, *] inside a transaction.

seqs_for_payload_hash_pre(Hash)

-spec seqs_for_payload_hash_pre(binary()) -> [non_neg_integer()].

Look up seqs by pre-computed composite payload hash. Reads the payload hash index [by_payload_hash, Hash, *] inside a transaction.

The Hash must be pre-computed OUTSIDE the transaction via reckon_db_ccc_paths:payload_combo_hash/2 — this function is called inside the transaction and must not perform any crypto calls.

seqs_for_tag(Tag)

-spec seqs_for_tag(binary()) -> [non_neg_integer()].

Look up seqs by tag. Reads the tag index inside a transaction. Returns the list of seqs indexed under Tag. Empty list if no entries.