Optional ReadonlySharedWhether other processes read and write the same storage — true for Redis, where every
server in a fleet shares one keyspace; false/omitted for per-process or per-browser
stores (in-memory, IndexedDB, localStorage, MMKV).
LocalCacheManager keeps its registry (per-process eviction accounting) out of shared
storage: persisting it there made every server overwrite one key with its own view and
publish the whole registry to every peer on each write.
Optional ReadonlySharesWhether this provider stores and returns live object references rather than serialized copies.
true — no serialization boundary. SetItem(k, v) retains v itself and
GetItem(k) hands the very same object back, so the store and every caller
share memory (e.g. the Map-based in-memory providers).false — a serialization / structured-clone boundary isolates stored data from
live objects in both directions (IndexedDB, localStorage, Redis, MMKV).Why callers care: ILocalStorageProvider implementations are
interchangeable, but this one property changes the ownership semantics of
everything stored. LocalCacheManager reads it to decide whether it must
deep-freeze row data at write time — without a freeze, a reference-sharing
provider lets any consumer that mutates a returned row silently corrupt the
process-wide cache. (That is a bug that actually shipped: a GraphQL resolver
renamed __mj_CreatedAt to its transport alias in place, rewriting the live
cache for every later reader.) Serializing providers were never exposed to this,
so they opt out and keep handing back freely-mutable copies.
Optional, but always declare it. Omitting it is not a way to opt out of the
contract: when it is undefined, LocalCacheManager MEASURES the provider at
initialization instead — it stores a sentinel object, reads it back, and compares
identity. Declaring the value skips that probe and documents intent at the
implementation site, which is why every in-repo provider declares it. It is
optional purely so that adding this contract does not break existing external
implementations at compile time.
Optional ReadonlySupportsWhether a value written here can be read back by another process, or by this one after a restart.
true — the store outlives the process that wrote to it (Redis, or a browser's
localStorage / IndexedDB surviving a reload).false — an in-process store whose contents die with the process.ProviderBase reads this before saving its metadata snapshot. That save serializes, gzips and
base64-encodes the whole metadata graph, which on a large tenant is expensive enough to
exhaust the heap, and an in-process store can only ever hand the result back to the heap that
already holds those objects — so it is skipped. Declaring false therefore removes real work;
declaring it wrongly on a persistent store would leave the cold-start cache unpopulated.
Optional, but every in-repo provider declares it. undefined is treated as persistent, which
is the safer default of the two: a pointless save wastes work, while a skipped necessary one
breaks the cache. It is optional only so that adding this contract did not break external
implementations at compile time.
OptionalClearClears all items in a specific category. If no category is specified, clears the default category only.
The category to clear
OptionalGetGets all keys in a specific category.
The category to list keys from
OptionalGetGets the keys in a category that were written with the given LocalStorageWriteOptions.IndexGroup and still exist.
Implemented by providers whose storage is shared between processes (Redis): a process needs it to find entries another process wrote. A process-local provider does not need it, because the caller's own in-memory index already knows every key it wrote.
The category the keys were written to
The index group (for the RunView cache, the entity name)
Retrieves a value from storage. The implementation is responsible for any deserialization required by the underlying medium:
Returns null for missing keys or corrupt entries.
Expected type of the stored value. Caller-controlled — the provider does
not validate the runtime shape against this type. Falls back to unknown.
The key to retrieve
Optionalcategory: stringOptional category for key isolation (e.g., 'RunViewCache', 'Metadata')
Batched retrieval — reads N values for N keys in one logical operation.
Returns a Map keyed by the input key strings. Missing keys map to null.
The map preserves the original key set so callers can index by key without
relying on array-position alignment.
Why batch? IndexedDB serializes transactions on the same object store —
Promise.all([...N GetItem calls]) looks parallel but pays per-transaction
setup cost (~3–10ms each) for every key. A single transaction with N
get() calls amortizes that overhead. Redis can use MGET/pipelines.
In-memory implementations have no real win but implement consistently for
a uniform API.
Implementations are free to fall back to per-key reads internally if the
underlying medium doesn't support batching — the contract is just "read
all of these as efficiently as you can". An empty keys array returns
an empty map without touching the storage backend.
Expected type of all stored values. Caller-controlled.
The keys to retrieve. Duplicates are deduplicated; the returned map has one entry per unique key.
Optionalcategory: stringOptional category for key isolation (applies to all keys)
OptionalReleaseEnds a lease this process claimed with TryAcquireLease before it expires, so the next process waiting for it can proceed. Does nothing for a lease held by someone else.
Removes an item from storage.
The key to remove
Optionalcategory: stringOptional category for key isolation
Stores a value. Callers should pass plain data (objects/arrays/primitives/Date/etc). Implementations handle any serialization required by the medium:
Class instances lose their prototype on retrieval — store the underlying data
(e.g. via entity.GetAll()) and reconstruct on read if needed.
Type of the value being stored. Caller-controlled.
The key to store under
The value to store
Optionalcategory: stringOptional category for key isolation
Optionaloptions: LocalStorageWriteOptionsOptional expiry and index hints; a provider ignores what it cannot honour.
OptionalTryClaims a named lease for ttlMs if no other process holds it. The lease is never released
early; it simply expires. Implemented by shared providers (Redis) so one server in a fleet
does a periodic job per interval. A provider that does not implement it is private to its
process, so the caller treats the lease as held.
The lease name
How long the lease lasts
True when this call claimed the lease
OptionalWithRuns work while holding an exclusive lock on one key across every process that shares this
storage. Implemented by shared providers (Redis): a read-modify-write of one entry from two
servers at once otherwise loses one of the writes. Rejects when the lock cannot be acquired
in time, so the caller can fall back (LocalCacheManager invalidates the entry instead).
The entry being rewritten
Its category
The read-modify-write to run under the lock
Interface for local storage providers. Abstracts storage operations to support different storage backends (e.g., browser localStorage, IndexedDB, file system).
Implementations should handle the optional category parameter as follows:
mj:RunViewCache)[mj]:[category]:[key]When category is not provided, use a default category (e.g., 'default' or 'general').