shelf/duplicate_bag
Types
An open persistent duplicate bag table with typed keys and values.
pub opaque type PDuplicateBag(k, v)
Values
pub fn close(
table: PDuplicateBag(k, v),
) -> Result(Nil, shelf.ShelfError)
Close the table, saving all data to disk.
On Ok(Nil), the handle must not be used again. If the final save
fails with a retryable persistence error, close() returns
Error(...) and leaves the table open so the caller can retry.
If close fails terminally, shelf still releases resources and future
operations on the handle return Error(TableClosed).
pub fn delete_all(
from table: PDuplicateBag(k, v),
) -> Result(Nil, shelf.ShelfError)
Delete all entries from the table.
The table remains open and usable after this call — only the data
is removed. To release the table entirely, use close.
pub fn delete_key(
from table: PDuplicateBag(k, v),
key key: k,
) -> Result(Nil, shelf.ShelfError)
Delete all values for the given key.
pub fn delete_object(
from table: PDuplicateBag(k, v),
key key: k,
value value: v,
) -> Result(Nil, shelf.ShelfError)
Delete a specific key-value pair.
Only the exact matching pair is removed. Other values for the same key are preserved.
pub fn fold(
over table: PDuplicateBag(k, v),
from initial: acc,
with fun: fn(acc, k, v) -> acc,
) -> Result(acc, shelf.ShelfError)
Fold over all entries. Order is unspecified.
pub fn insert(
into table: PDuplicateBag(k, v),
key key: k,
value value: v,
) -> Result(Nil, shelf.ShelfError)
Insert a key-value pair. Duplicates are preserved.
pub fn insert_list(
into table: PDuplicateBag(k, v),
entries entries: List(#(k, v)),
) -> Result(Nil, shelf.ShelfError)
Insert multiple key-value pairs.
pub fn lookup(
from table: PDuplicateBag(k, v),
key key: k,
) -> Result(List(v), shelf.ShelfError)
Look up all values for a key.
Returns Error(NotFound) if the key does not exist.
pub fn member(
of table: PDuplicateBag(k, v),
key key: k,
) -> Result(Bool, shelf.ShelfError)
Check if a key exists without returning the values.
pub fn open(
name name: String,
path path: String,
base_directory base_directory: String,
key key_decoder: decode.Decoder(k),
value value_decoder: decode.Decoder(v),
) -> Result(PDuplicateBag(k, v), shelf.ShelfError)
Open a persistent duplicate bag table with defaults (WriteBack mode).
let assert Ok(table) =
duplicate_bag.open(name: "events", path: "events.dets",
base_directory: "/app/data",
key: decode.string, value: decode.string)
pub fn open_config(
config config: shelf.Config,
key key_decoder: decode.Decoder(k),
value value_decoder: decode.Decoder(v),
) -> Result(PDuplicateBag(k, v), shelf.ShelfError)
Open a persistent duplicate bag table with full configuration.
If the DETS file exists, its contents are loaded into a fresh ETS table after validating each entry through the provided decoders. If no file exists, both tables start empty.
The DETS file path is validated against the configured base directory.
let config =
shelf.config(name: "events", path: "events.dets",
base_directory: "/app/data")
|> shelf.write_mode(shelf.WriteThrough)
let assert Ok(table) =
duplicate_bag.open_config(config,
key: decode.string, value: decode.string)
pub fn reload(
table: PDuplicateBag(k, v),
) -> Result(Nil, shelf.ShelfError)
Discard unsaved ETS changes and reload from DETS.
Clears the ETS table, re-reads all DETS entries, validates them through the stored decoders, and loads valid entries into ETS. Only useful in WriteBack mode — in WriteThrough mode, ETS and DETS are always in sync.
pub fn save(
table: PDuplicateBag(k, v),
) -> Result(Nil, shelf.ShelfError)
Snapshot the current ETS contents to DETS.
Uses an atomic save strategy: data is written to a temporary file first, then atomically renamed over the original DETS file. This prevents data loss if the process is killed mid-save (the original file remains intact until the rename succeeds).
See the Durability story for the full layer-by-layer breakdown of which crashes this protects against.
pub fn size(
of table: PDuplicateBag(k, v),
) -> Result(Int, shelf.ShelfError)
Return the number of entries in the table.
pub fn sync(
table: PDuplicateBag(k, v),
) -> Result(Nil, shelf.ShelfError)
Drain the DETS in-memory write buffer into the open DETS file.
Calls dets:sync/1. Most useful in WriteThrough mode after a critical
write, to make sure pending DETS writes are reflected in the file on
disk. Note: this does not call fsync(2) on the file descriptor — see
the Durability story
for what sync() does and does not guarantee.
pub fn to_list(
from table: PDuplicateBag(k, v),
) -> Result(List(#(k, v)), shelf.ShelfError)
Return all key-value pairs as a list.
Warning: loads entire table into memory.
pub fn with_table(
name name: String,
path path: String,
base_directory base_directory: String,
key key_decoder: decode.Decoder(k),
value value_decoder: decode.Decoder(v),
fun fun: fn(PDuplicateBag(k, v)) -> Result(a, shelf.ShelfError),
) -> Result(a, shelf.ShelfError)
Use a table within a callback, ensuring it is closed afterward.
If the final save fails during close, with_table force-cleans the
table to release resources. If the callback succeeded, the close
error is returned; if both the callback and close fail, the callback
error is preserved.
use table <- duplicate_bag.with_table("events", "events.dets",
base_directory: "/app/data",
key: decode.string, value: decode.string)
duplicate_bag.insert(into: table, key: "click", value: "btn_1")