Expand description
Durable-mode auto-flush-on-exit.
A durable Db stages id-index updates in an in-memory WAL (write_buf) and
only makes them durable in flush_all() — normally driven by the manifest
ticker or by Drop. But Drop “only fires once every owning handle is gone”,
and a ticker thread (or a server that blocks forever in serve()) holds an
Arc<Db> for the whole process lifetime — so on a hard exit (Ctrl+C,
SIGTERM from an orchestrator, kill) Drop NEVER runs and the writes staged
since the last tick are lost. That is the gap this module closes.
Db::install_exit_flush registers a durable database to be flushed when the
process receives SIGINT or SIGTERM. It flushes exactly once, on the way
out — NOT on every put — so the hot write path stays hot.
§Design
-
Opt-in. A library that unilaterally seizes signal handlers would trample a host application’s own shutdown logic, so the core never installs this implicitly. The napi (
nedb-node) and pyo3 (nedb-py)open()paths call it for durable databases — so Node and Python embedders get flush-on-exit for free — and Rust applications call it explicitly.nedbdkeeps its own tokio graceful-shutdown handler and does not use this. -
Async-signal-safe. The installed handler does exactly one thing: a non-blocking
write(2)of the signal number to a self-pipe (writeis on the POSIX async-signal-safe list). A dedicated reader thread blocks on the read end; when woken it runsflush_all()on every registered database from a normal thread context (locks, allocation and file I/O are all safe there), restores the signal’s default disposition, and re-raises it so the process terminates with the correct128 + signumstatus. -
Idempotent. The handler and reader thread are installed once per process; each subsequent call just registers another database. In-memory (
:memory:) databases are ignored — there is nothing to flush. -
Weak references. The registry holds
Weak<Db>, so a registered database that is otherwise dropped is not kept alive (no leak) and is pruned on the next flush pass.