SQLite and RocksDBintermediate
Production Considerations
Running embedded databases in production — backups, upgrades, observability and the failure modes that have no operator to notice them.
An embedded database has no separate process to monitor, no DBA and no operator watching it. Its health is your application's responsibility, and its failures surface as application bugs.
Storage is the whole game
Also confirm:
- The filesystem honours
fsync. Some virtualised and network-backed storage acknowledges writes before they are durable, which converts a clean crash into corruption. - There is free space for maintenance:
VACUUMneeds roughly the database size; RocksDB compaction needs headroom above the dataset. - File permissions match the process user, and the database is not world-readable if it holds anything sensitive.
Backups
There is no backup daemon, so the application or its deployment must take them.
- SQLite — use the backup API or
.backup, never a plain file copy. See File Management. - RocksDB — use its checkpoint or backup engine APIs, which produce a consistent snapshot; copying the directory while the process is running does not.
Copy the result off the host. An embedded database on a single machine has the same failure domain as that machine.
Upgrades
Both formats are stable, but the interaction with your application is not:
- Migrations must run before the application uses the new schema, and both are in the same process — so a failed migration is a failed startup. Make migrations idempotent and safe to retry.
- A downgrade may not be possible. If a new version writes a format an older build cannot read, rolling back the application means restoring a backup.
- Test the upgrade against a copy of a real database, including one that has been in use long enough to accumulate the shapes that only appear in production.
Observability
Nothing reports on an embedded database unless the application does. At minimum, emit:
- Database file size and its growth rate.
- Query duration percentiles from the application's own instrumentation.
- Lock contention:
SQLITE_BUSYcounts, or RocksDB write stall time. - Integrity check results from a scheduled job.
- Backup success and age.
# A periodic health check worth running.
row = conn.execute("PRAGMA quick_check").fetchone()
if row[0] != "ok":
metrics.increment("sqlite.integrity_failure")Concurrency assumptions
- SQLite — one writer at a time. Serialise writes through a single connection or a queue in the
application, set
busy_timeout, and useBEGIN IMMEDIATEfor read-then-write transactions. See Concurrency and WAL. - RocksDB — concurrent writers are supported, but write stalls are the mechanism by which it pushes back. Handle them as backpressure rather than as an error.