2.7 KiB
2.7 KiB
ADR-005: Storage Strategy
Status
Accepted
Context
The system needs to store: structured data (users, documents, comments), document content, file attachments, and search indexes. Requirements include: zero external dependencies, backup simplicity, and resistance to data corruption.
Decision
libsql (SQLite) for structured data + content-addressed filesystem for files
Implementation
-
Database (libsql/SQLite):
- Single file:
data/db.sqlite - WAL mode for concurrent reads during writes
- Embedded — no separate process needed
- Backup:
cp db.sqlite db.sqlite.backup(atomic with WAL checkpoint)
- Single file:
-
File Storage (Content-Addressed):
- Directory:
data/files/ - Layout:
data/files/ab/cd/abcdef1234567890abcdef1234567890abcdef12 - SHA-256 hash as filename (hex encoded)
- First two characters as subdirectory (for filesystem performance)
- Immutable: write-once, never modify
- Directory:
-
Document Mapping:
- Database stores:
document_path → current_hash - History table stores all previous hashes
- Attachment metadata stores original filename + content hash
- Database stores:
Consequences
Positive
- SQLite is the most tested database in the world (billions of deployments)
- libsql adds replication option without changing the API
- Content-addressed files are naturally deduplicated
- Filesystem storage is trivial to backup (rsync, tar)
- No network dependencies, works offline
- Single directory contains all state — easy to migrate
Negative
- SQLite has write concurrency limits (mitigated by WAL + short transactions)
- Filesystem storage doesn't scale horizontally without shared storage
- No built-in file expiration/garbage collection (must implement orphan cleanup)
- Large attachments can bloat filesystem (mitigated by quotas)
Alternatives Considered
PostgreSQL
- Pros: Excellent concurrency, rich feature set, horizontal scaling
- Cons: Requires separate process, more complex backup, network dependency
- Rejected: Overkill for anticipated scale; SQLite is simpler and faster for read-heavy workloads
Object Storage (S3/MinIO)
- Pros: Unlimited scale, CDN-friendly
- Cons: Network dependency, vendor lock-in, complex local development
- Rejected: Violates zero-dependency and simple backup requirements
SQLite for Everything (BLOBs)
- Pros: Single file for everything
- Cons: Large BLOBs slow down database, harder to serve directly, backup size bloat
- Rejected: Separation of concerns — database for metadata, filesystem for content
References
- SQLite WAL mode: https://sqlite.org/wal.html
- Content-addressable storage: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
- libsql: https://github.com/tursodatabase/libsql