Skip to content
shelf logo

shelf

Fast in-memory access with automatic disk persistence for the BEAM.

v1.0.1 on Hex

Reads come from ETS at memory speed; writes persist to DETS on disk. shelf wraps the classic Erlang persistence pattern in a type-safe Gleam API, with decoders validating every entry loaded from disk before it reaches your code.

import gleam/dynamic/decode
import shelf/set
// Open a persistent set — existing data loads from disk, through your decoders
let assert Ok(table) =
set.open(
name: "users",
path: "data/users.dets",
base_directory: "/app/data",
key: decode.string,
value: decode.int,
)
// Writes and reads hit ETS at memory speed
let assert Ok(Nil) = set.insert(into: table, key: "alice", value: 42)
let assert Ok(42) = set.lookup(from: table, key: "alice")
// Persist to disk when ready; close auto-saves
let assert Ok(Nil) = set.save(table)

Gleam's ETS ecosystem already covers the pieces individually — shelf exists because most projects need both at once.

  • bravo wraps ETS. Fast, in-memory only; nothing survives a restart.
  • slate wraps DETS. Persistent, but every read and write touches disk.
  • Mnesia ships with OTP and gives you both, plus distribution, transactions, and a schema — more than most single-node apps need.
  • shelf combines ETS-speed reads with DETS-backed persistence, without a distributed database to run.
ETS speed, DETS persistence
Microsecond reads from memory, durable storage on disk — no database process to run.
Runtime type safety
Decoder-gated loading catches corrupted or mistyped data at the storage boundary, not in production.
Two write modes
WriteBack for high-throughput batching, or WriteThrough for immediate durability on every write.
Set, bag, and duplicate bag
All three ETS table types, each with persistent backing — pick the data model your use case needs.
Safe resource management
The with_table callback closes tables even when the body panics or returns an error.
Atomic counters
Lock-free increments via update_counter — no serialising through an actor.
Cross-process reads
Tables are protected: one owner process writes while any process reads concurrently.