barrel_att_store_blob (barrel_docdb v1.0.0)

View Source

BlobDB storage backend for attachments

Uses a separate RocksDB instance with BlobDB enabled for storing attachment binary data. This avoids compaction issues from mixing small documents with large blobs.

Large attachments (>= chunk_threshold) are stored as chunks for streaming support. Small attachments are stored as single values.

This is the default attachment backend, selected by barrel_att_store when no other backend is configured.

Summary

Functions

Abort a put stream and clean up any written chunks

Feed entries since an HLC (exclusive). See barrel_att_feed.

Hard-link snapshot into Path (timeline forks); the target must not exist.

Close the attachment store

Close a stream (currently a no-op, but included for API completeness)

Delete an attachment Handles both single-value and chunked attachments.

Delete with options (origin_hlc for replicated deletes: the last-write-wins guard applies, and a tombstone lands even when the attachment was never seen locally).

Delete all attachments for a document

Finish a put stream: verify the digest, run the LWW guard, and commit metadata + feed row in one batch. Nothing is visible until this batch lands (chunks without metadata are unreadable), so a digest mismatch or a lost LWW race leaves nothing committed.

Fold over all attachments for a document

Retrieve an attachment Automatically handles both single-value and chunked attachments.

Get attachment info/metadata without reading the data

Open a stream for reading an attachment

Open an attachment store with BlobDB enabled

Store an attachment (async by default) Small attachments are stored as single values. Large attachments (>= chunk_threshold) are stored as chunks.

Store an attachment with options Options: - sync: boolean() - if true, sync to disk before returning (default: false) Options: - sync: sync to disk before returning - content_type: override the name-derived content type (replication preserves the source's) - origin_hlc: replicated write; the last-write-wins guard may answer {ok, ignored} - expected_digest: verify the data against a digest before committing anything

Open a stream for writing an attachment Returns a stream handle that can be used with write_chunk/2 and finish_stream/1

Read the next chunk from a stream

Maintenance escape hatch: synthesize feed rows for attachments written before the feed existed (format-break stance: they do not sync otherwise). Rebuilt entries carry the MINIMUM origin so any real write, local or remote, wins the LWW race against them; two rebuilt replicas converge by digest tie-break. Safe to re-run.

Write data to a put stream Data is buffered and written in chunks

Types

att_info/0

-type att_info() ::
          #{name := binary(),
            content_type := binary(),
            length := non_neg_integer(),
            digest := binary(),
            chunked => boolean(),
            chunk_size => pos_integer(),
            chunk_count => pos_integer()}.

att_ref/0

-type att_ref() ::
          #{ref := rocksdb:db_handle(),
            path := string(),
            chunk_threshold => pos_integer(),
            chunk_size => pos_integer(),
            env => rocksdb:env_handle()}.

att_stream/0

-type att_stream() ::
          #{att_ref := att_ref(),
            db_name := binary(),
            doc_id := binary(),
            att_name := binary(),
            info := att_info(),
            chunk_index := non_neg_integer(),
            chunk_count := non_neg_integer()}.

change/0

-type change() :: map().

db_config/0

-type db_config() :: #{path => string(), store => module(), atom() => term()}.

db_name/0

-type db_name() :: binary().

db_ref/0

-type db_ref() :: pid() | atom().

doc/0

-type doc() :: #{binary() => term()}.

doc_info/0

-type doc_info() :: #{id := docid(), rev := revid(), deleted := boolean(), revtree := revtree()}.

docid/0

-type docid() :: binary().

endpoint/0

-type endpoint() :: db_name() | {node(), db_name()} | {module(), term()}.

rep_options/0

-type rep_options() ::
          #{continuous => boolean(),
            since => seq_string(),
            filter => fun((doc()) -> boolean()),
            atom() => term()}.

rev_info/0

-type rev_info() ::
          #{id := revid(),
            parent := revid() | undefined,
            deleted := boolean(),
            attachments => #{binary() => att_info()}}.

revid/0

-type revid() :: binary().

revtree/0

-type revtree() :: #{revid() => rev_info()}.

seq/0

-type seq() :: barrel_hlc:timestamp().

seq_string/0

-type seq_string() :: binary().

view_name/0

-type view_name() :: binary().

view_result/0

-type view_result() :: #{key := term(), value := term(), id := docid()}.

Functions

abort_stream(_)

-spec abort_stream(map()) -> ok.

Abort a put stream and clean up any written chunks

att_changes(_, DbName, Since, Opts)

-spec att_changes(att_ref(), db_name(), barrel_hlc:timestamp() | first, map()) ->
                     {ok, [barrel_att_feed:entry()], barrel_hlc:timestamp() | first}.

Feed entries since an HLC (exclusive). See barrel_att_feed.

att_floor(_, DbName)

-spec att_floor(att_ref(), db_name()) -> barrel_hlc:timestamp() | undefined.

checkpoint(_, Path)

-spec checkpoint(att_ref(), string()) -> ok | {error, term()}.

Hard-link snapshot into Path (timeline forks); the target must not exist.

close(_)

-spec close(att_ref()) -> ok.

Close the attachment store

close_stream(Stream)

-spec close_stream(att_stream()) -> ok.

Close a stream (currently a no-op, but included for API completeness)

delete(AttRef, DbName, DocId, AttName)

-spec delete(att_ref(), db_name(), docid(), binary()) -> ok | {error, term()}.

Delete an attachment Handles both single-value and chunked attachments.

delete(_, DbName, DocId, AttName, Opts)

-spec delete(att_ref(), db_name(), docid(), binary(), map()) -> ok | {error, term()}.

Delete with options (origin_hlc for replicated deletes: the last-write-wins guard applies, and a tombstone lands even when the attachment was never seen locally).

delete_all(AttRef, DbName, DocId)

-spec delete_all(att_ref(), db_name(), docid()) -> ok | {error, term()}.

Delete all attachments for a document

finish_stream(_)

-spec finish_stream(map()) -> {ok, att_info()} | {ok, ignored} | {error, term()}.

Finish a put stream: verify the digest, run the LWW guard, and commit metadata + feed row in one batch. Nothing is visible until this batch lands (chunks without metadata are unreadable), so a digest mismatch or a lost LWW race leaves nothing committed.

fold(_, DbName, DocId, Fun, Acc)

-spec fold(att_ref(), db_name(), docid(), fun(), term()) -> term().

Fold over all attachments for a document

get(AttRef, DbName, DocId, AttName)

-spec get(att_ref(), db_name(), docid(), binary()) -> {ok, binary()} | not_found | {error, term()}.

Retrieve an attachment Automatically handles both single-value and chunked attachments.

get_info(_, DbName, DocId, AttName)

-spec get_info(att_ref(), db_name(), docid(), binary()) -> {ok, att_info()} | {error, term()}.

Get attachment info/metadata without reading the data

get_stream(AttRef, DbName, DocId, AttName)

-spec get_stream(att_ref(), db_name(), docid(), binary()) -> {ok, att_stream()} | {error, term()}.

Open a stream for reading an attachment

open(Path, Options)

-spec open(string(), map()) -> {ok, att_ref()} | {error, term()}.

Open an attachment store with BlobDB enabled

put(AttRef, DbName, DocId, AttName, Data)

-spec put(att_ref(), db_name(), docid(), binary(), binary()) -> {ok, att_info()} | {error, term()}.

Store an attachment (async by default) Small attachments are stored as single values. Large attachments (>= chunk_threshold) are stored as chunks.

put(AttRef, DbName, DocId, AttName, Data, Opts)

-spec put(att_ref(), db_name(), docid(), binary(), binary(), map()) ->
             {ok, att_info()} | {ok, ignored} | {error, term()}.

Store an attachment with options Options: - sync: boolean() - if true, sync to disk before returning (default: false) Options: - sync: sync to disk before returning - content_type: override the name-derived content type (replication preserves the source's) - origin_hlc: replicated write; the last-write-wins guard may answer {ok, ignored} - expected_digest: verify the data against a digest before committing anything

put_stream(AttRef, DbName, DocId, AttName, ContentType)

-spec put_stream(att_ref(), db_name(), docid(), binary(), binary()) -> {ok, map()} | {error, term()}.

Open a stream for writing an attachment Returns a stream handle that can be used with write_chunk/2 and finish_stream/1

put_stream(AttRef, DbName, DocId, AttName, ContentType, Opts)

-spec put_stream(att_ref(), db_name(), docid(), binary(), binary(), map()) ->
                    {ok, map()} | {error, term()}.

read_chunk(Stream)

-spec read_chunk(att_stream()) -> {ok, binary(), att_stream()} | eof | {error, term()}.

Read the next chunk from a stream

rebuild_feed(AttRef, DbName)

-spec rebuild_feed(att_ref(), db_name()) -> {ok, #{rows := non_neg_integer()}}.

Maintenance escape hatch: synthesize feed rows for attachments written before the feed existed (format-break stance: they do not sync otherwise). Rebuilt entries carry the MINIMUM origin so any real write, local or remote, wins the LWW race against them; two rebuilt replicas converge by digest tie-break. Safe to re-run.

sweep_att_feed(_, DbName, Cutoff)

-spec sweep_att_feed(att_ref(), db_name(), barrel_hlc:timestamp()) ->
                        {ok, #{tombstones_swept := non_neg_integer()}}.

write_chunk(Stream, Data)

-spec write_chunk(map(), binary()) -> {ok, map()} | {error, term()}.

Write data to a put stream Data is buffered and written in chunks