Writing a storage backend (30 minutes)¶
Storage holds the audit log, the snapshot and named files (page, style, corpus, request log). Per-machine state (cursors, drafts, inbox queue) is not storage and stays on local disk.
The contract¶
miseenplace.storage.base.Storage:
| Method | Meaning |
|---|---|
append(entry) |
add one log entry, durably |
read_entries(after=0) |
entries with seq > after, in order |
load_snapshot() / save_snapshot(seq, items) |
the cache; None until saved |
read_file(name) / write_file(name, text) / append_file(name, text) |
named text files; read_file returns None if missing |
refresh() |
called before reads; pull remote changes |
sync(message) |
called after writes; publish them |
Reject unsafe names with check_name(name): it raises StorageError for .., absolute paths and empty names. Raise StorageError for any failure. Never swallow one.
Steps¶
- Copy
src/miseenplace/storage/_template.py; implement the methods (15 min).tests/memory_storage.pyis a complete example in 40 lines. - Constructor signature:
(root: Path, settings: dict[str, object]);settingsis the[storage]table. - Load it by name:
toml [storage] backend = "my_package.module:ClassName"or publish an entry point in themiseenplace.storagegroup and use the short name. - Run the contract suite against it (10 min): add your factory to
BACKENDSintests/contract/test_storage_contract.py. It checks ordering,after, persistence across reopening, snapshot, files and name safety.
Sketches (prose only)¶
- SQLite: one table of entries keyed by
seq, one for files;appendis an INSERT inside a transaction;refreshandsyncdo nothing. - S3 or object storage: keep the log as numbered objects (
log/000001.json), because objects are not appendable;refreshlists new keys;syncdoes nothing because writes are already remote. - Notion or a database: entries become rows; guard against two writers with a unique constraint on
seq, and raiseStorageErroron conflict so the caller retries.