Skip to content

What is shelf?

shelf coordinates an ETS table for in-memory access and a DETS file for persistence. You work with a typed table handle in Gleam; shelf handles loading, validation, and snapshots through OTP's built-in storage APIs.

When you call open(), shelf opens the DETS file and creates an ETS table. If the file exists, shelf checks each saved key and value with your decoders before loading them into ETS. If the file does not exist, you start with an empty table. You must create the parent directory before opening the file.

The decoders let you detect stored data that does not match your application's types. If a new application version changes those types, you may need a schema migration before reopening the table.

After loading, reads use ETS without DETS I/O. Writes follow the mode you choose when you open the table:

  • WriteBack, the default: update ETS and call save() to snapshot the changes to DETS.
  • WriteThrough: update DETS first, then ETS, on each write.

WriteThrough sends changes to DETS before returning, but DETS can buffer them in memory. sync() flushes that buffer to the file. Neither mode provides fsync(2)-level protection against OS crashes or power loss. The write-mode guide explains the choice and links to the full durability guarantees.

The process that opens a table owns it. Other processes can read through the handle, but only the owner can write, save, reload, sync, or close. For a long-running application, open the table in a supervised actor or server and send mutation requests to that owner.

When the owner exits, ETS disappears and unsaved changes are lost. The next owner can reopen the DETS file and load its saved contents. See process ownership for the read/write split and crash behavior.

save() copies the ETS contents to a temporary DETS file, then renames that file over the database path. close() attempts a final save before releasing the resources. With with_table, shelf also releases resources if your callback returns an error or panics; a failed final save can still lose unsaved changes.

Start with the quick start to open, save, and reopen your first table. Use persistence operations when you need to choose between saving, reloading, syncing, and closing.