CacheDBProviderclass
new CacheDBProvider<I, T>(source: DBProvider<I, T>, cache: MemoryDBProvider<I, T> = new MemoryDBProvider<I, T>())
| Param | Type | |
|---|---|---|
source | DBProvider<I, T> | Provider with a fully asynchronous interface for database access. required |
cache | MemoryDBProvider<I, T> | In-memory provider to use as the cache (a fresh MemoryDBProvider by default). Defaults to new MemoryDBProvider<I, T>() |
| Return | |
|---|---|
CacheDBProvider<I, T> | Database provider that keeps a copy of asynchronous remote data in a local synchronous cache. |
| Property | Type | |
|---|---|---|
.memory | MemoryDBProvider<I, T> | The in-memory provider holding the local synchronous cache of source data. required readonly |
Database provider that keeps a copy of asynchronous remote data in a local synchronous cache.
- Wraps a
sourceprovider and mirrors every read and write into an in-memoryMemoryDBProvider, so subsequent reads can be served synchronously and live subscriptions stay seeded. - Reads fetch from
source, then refresh the cache; writes hitsource, then mirror the change into the cache. - Fetch-first item writes:
updateItem()anddeleteItem()fetch the item first (caching it) and skip the source write when it doesn't exist. Query writes are inherited two-step, resolving through this provider's owngetQuery()— so the matched items are cached, and each per-item write mirrors exactly. The fetch and the writes are separate steps, so wrap them intransact()when they must be atomic. - Transactions run on
sourceviatransact()with a transaction-scoped mirror — only a committed transaction's writes reach the cache. - Discover the cache from a wrapping layer with
getSource(CacheDBProvider, provider)to seed stores from.memory.
A wrapping provider that keeps an in-memory mirror in sync with a remote source. CacheDBProvider extends ThroughDBProvider, holds a MemoryDBProvider, and populates it as data is read and written, so subsequent reads are synchronous — the basis of synchronous first renders in the React integration.
The constructor takes the source provider and an optional MemoryDBProvider to use as the mirror (one is created by default).
Usage
import { CacheDBProvider, ValidationDBProvider, MemoryDBProvider } from "shelving/db";
const provider = new CacheDBProvider(
new ValidationDBProvider(new MemoryDBProvider())
);
await provider.getItem(POSTS, "abc"); // fetches from source, populates the mirror
await provider.getItem(POSTS, "abc"); // served synchronously from the mirrorDBCache finds the CacheDBProvider in a chain automatically and reuses its mirror to seed reactive stores.
Fetch-first writes
Writes that depend on existing data fetch before they write, so every write leaves the touched items cached and the cache mirrors exactly what changed:
CacheDBProvider.updateItem()andCacheDBProvider.deleteItem()fetch the item first (caching it) and skip the source write when it doesn't exist.- Query writes (
setQuery,updateQuery,deleteQuery) are inherited two-step and resolve through the cache's owngetQuery(), so the matched items are fetched and cached first, then each per-item write mirrors exactly — never a query replayed against the cache's subset.
The fetch and the writes are separate operations against source, so a concurrent change can slip between them — wrap the call in transact() when that matters, because inside a transaction both steps run in the source transaction and are atomic.
Transactions
CacheDBProvider.transact() runs the transaction on source and hands the callback this cache over the source's transaction, backed by a transaction-scoped mirror — so reads, fetch-first writes, and query resolution behave exactly as they do outside a transaction. The callback's operations are recorded with RecordingDBProvider, and once the source commits, the recorded writes are committed into the real cache with RecordingDBProvider.replayWrites().
Uncommitted data never touches the cache — a thrown callback commits nothing, and if the backend retries the callback only the committed attempt's writes are mirrored. Update writes commit as deltas, so they refresh cached items and skip uncached ones; an item only read inside a transaction stays uncached until its next read. (The recording also holds the reads, so a future refinement could commit those with RecordingDBProvider.replay().)