A store consists conceptually of an on-disk database (SQLite), and a collection of in-memory metadata derived from the database. This metadata includes the symbolic and numeric schema, mappings from idents to entids and back again, a table of part numbers, a current transaction counter, and more: e.g., we will likely add a last-schema-change-tx-id to invalidate schema-dependent pre-work.
Writes are (at least conceptually) executed sequentially, and are queued for convenience.
Writes that change the schema/metadata are uncommon.
Reads can occur in parallel.
Reads and writes involve some interpretation — e.g., mapping idents to entids according to the schema — and that interpretation necessarily must seem to occur against the schema at the moment of their execution.
Reads and writes are isolated. That implies that the results of a read (which are themselves interpreted according to metadata) are interpreted against the database upon which the read ran, regardless of a simultaneous write that might have separately changed the schema.
So:
- A conn maintains a reference to the current DB and the writer. The writer must be able to replace the current DB with a new DB. Multiple threads and readers can maintain read-only references to an individual DB in order to interpret query results or consistently make API calls.
- A query requires only a SQLite connection and a read-only reference to a DB. The DB’s schema is used to interpret queries and results.
- Reads use a (perhaps read-only) SQLite connection, likely drawn from a pool or reserved for private use.
- A write requires the writer SQLite connection and a DB (used to interpret the next write), optionally returning a new derived DB.
- Writes are queued. A single writer owns a SQLite connection, and is associated with a thread.
- The initial DB is built by issuing database reads (fetching parts, idents, schema) within a transaction.
- => DB instances can be immutable. They’re shared across threads, with varying lifetimes, and so must be Arc, but they’re never modified.
- => A conn is effectively a thread with which one communicates by message passing.
- => A conn’s current-db can be read from multiple threads — to get an immutable DB ref that we can use for querying — and written by one, the writer. That implies a RwLock. Only the writer will ever take the write lock.
- => In order to avoid cycles, the writer loop itself can’t hold a reference to the conn!