barrel_att_feed (barrel_docdb v1.1.1)
View SourceAttachment change feed: the tracking layer that lets attachments replicate. Lives INSIDE the attachments RocksDB and is committed atomically with the blob write (one WriteBatch).
Keys are tagged 0xFF, provably disjoint from blob keys (their first byte is the high byte of a 16-bit db-name length, always 0x00):
feed <<16#FF, $C, DbNameLen:16, DbName, LocalHlc:12>>
index <<16#FF, $A, DbNameLen:16, DbName, DocIdLen:16, DocId, $:, Name>>
meta <<16#FF, $M, DbNameLen:16, DbName, "att_floor">>Invariant: ONE feed row per (DocId, AttName), moved on every write (doc-feed style, not history style), so a full resync is one bounded feed walk. Put rows are never age-swept; delete tombstones sweep past the retention window, recorded by the floor.
Convergence: attachments have no version vectors, so bidirectional sync uses last-write-wins on an ORIGIN HLC that travels with the data (fresh local HLC for local writes, preserved from the source feed entry for replicated ones). Feed keys always use fresh local HLCs so the feed stays monotone; the origin rides in the values.
Summary
Functions
Fold the feed since an HLC (exclusive), in write order. Options: limit. Returns the entries and the last visited HLC.
Oldest HLC the feed is complete from (undefined = never swept).
Last-write-wins guard for writes carrying an origin HLC. Deterministic across replicas: older origin loses; equal origins tie-break on digest byte order (equal digest = redelivery).
Current index entry for one attachment (delete tombstones are entries with op = delete).
Batch ops recording one applied write: move the (DocId, Name) feed row to a fresh local HLC and rewrite the index row. Info carries digest/length/content_type (empty for deletes).
Sweep delete tombstones older than the cutoff and advance the floor. Put rows are the live feed and are never swept.
Types
-type att_info() :: #{name := binary(), content_type := binary(), length := non_neg_integer(), digest := binary(), chunked => boolean(), chunk_size => pos_integer(), chunk_count => pos_integer()}.
-type change() :: map().
-type db_name() :: binary().
-type docid() :: binary().
-type entry() :: #{seq := barrel_hlc:timestamp(), origin := barrel_hlc:timestamp(), op := op(), id := docid(), name := binary(), digest := binary(), length := non_neg_integer(), content_type := binary()}.
-type op() :: put | delete.
-type revid() :: binary().
-type seq() :: barrel_hlc:timestamp().
-type seq_string() :: binary().
-type view_name() :: binary().
Functions
-spec att_changes(rocksdb:db_handle(), db_name(), barrel_hlc:timestamp() | first, map()) -> {ok, [entry()], barrel_hlc:timestamp() | first}.
Fold the feed since an HLC (exclusive), in write order. Options: limit. Returns the entries and the last visited HLC.
-spec att_floor(rocksdb:db_handle(), db_name()) -> barrel_hlc:timestamp() | undefined.
Oldest HLC the feed is complete from (undefined = never swept).
-spec check(rocksdb:db_handle(), db_name(), docid(), binary(), barrel_hlc:timestamp(), binary()) -> apply | ignored.
Last-write-wins guard for writes carrying an origin HLC. Deterministic across replicas: older origin loses; equal origins tie-break on digest byte order (equal digest = redelivery).
-spec decode_feed(binary(), barrel_hlc:timestamp()) -> entry().
-spec index_get(rocksdb:db_handle(), db_name(), docid(), binary()) -> {ok, map()} | not_found.
Current index entry for one attachment (delete tombstones are entries with op = delete).
-spec ops(rocksdb:db_handle(), db_name(), docid(), binary(), op(), barrel_hlc:timestamp(), map()) -> [term()].
Batch ops recording one applied write: move the (DocId, Name) feed row to a fresh local HLC and rewrite the index row. Info carries digest/length/content_type (empty for deletes).
-spec sweep(rocksdb:db_handle(), db_name(), barrel_hlc:timestamp()) -> {ok, #{tombstones_swept := non_neg_integer()}}.
Sweep delete tombstones older than the cutoff and advance the floor. Put rows are the live feed and are never swept.