Skip to content

Storage primitives

IStorage is the atomic-operation interface every backend implements and every protocol handler ultimately calls. Each method's atomicity boundary is the keys it touches; concurrent calls on different keys need not synchronise (and don't, when fronted by ShardedStorage).

The interface

class IStorage {
public:
    virtual std::expected<GetResult, StorageError> Get(string_view key, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> Set(string_view key, vector<byte> value, uint32_t flags, TimePoint expiry) = 0;
    virtual std::expected<CasToken, StorageError> Add(string_view key, vector<byte> value, uint32_t flags, TimePoint expiry, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> Replace(string_view key, vector<byte> value, uint32_t flags, TimePoint expiry, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> Append(string_view key, span<byte const> suffix, CasToken expected, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> Prepend(string_view key, span<byte const> prefix, CasToken expected, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> CompareAndSwap(string_view key, CasToken expected, vector<byte> value, uint32_t flags, TimePoint expiry, TimePoint now) = 0;
    // magnitude is unsigned; `decrement` picks the direction (a signed delta
    // could not represent magnitudes >= 2^63).
    virtual std::expected<IncrResult, StorageError> IncrementOrInitialize(string_view key, uint64_t magnitude, bool decrement, TimePoint now) = 0;
    virtual std::expected<void, StorageError> Delete(string_view key, TimePoint now) = 0;
    virtual std::expected<CasToken, StorageError> Touch(string_view key, TimePoint newExpiry, TimePoint now) = 0;

    // Non-mutating read: like Get but does not touch lastAccess, LRU position, or hit/miss stats.
    virtual std::expected<GetResult, StorageError> Peek(string_view key, TimePoint now) = 0;
    // Mark the entry stale (meta `md I` / `ms I`) without removing it; optionally refresh expiry.
    virtual std::expected<CasToken, StorageError> MarkStale(string_view key, optional<TimePoint> newExpiry, TimePoint now) = 0;
    // Atomic get-and-touch (memcached gat); defaults to Touch + Get, lock-owning backends override.
    virtual std::expected<GetResult, StorageError> GetAndTouch(string_view key, TimePoint newExpiry, TimePoint now);
    // Atomic delete-if-CAS-matches (meta `md C(token)`); defaults to Peek + Delete.
    virtual std::expected<void, StorageError> CompareAndDelete(string_view key, CasToken expected, TimePoint now);

    virtual void FlushWithGeneration(TimePoint effectiveAt) = 0;
    virtual std::size_t PurgeExpired(TimePoint now) = 0;
    virtual void Resize(std::size_t newMaxBytes) = 0;
    virtual StorageStats Snapshot() const noexcept = 0;
};

Implementations

Class Purpose
InMemoryLruStorage In-memory LRU with a soft byte budget. Single-threaded by contract.
CowTreeStorage Persistent copy-on-write B-tree backing with an in-memory LRU mirror for eviction accounting.
LayeredStorage Two-tier composition: L1 = InMemoryLruStorage, L2 = any IStorage. Reads hit L1 first, writes are write-through.
ShardedStorage Hash-based sharding across N inner storages. Each shard holds a std::shared_mutex for concurrency.
TracingStorage Decorator that emits one Trace log line per call.

Wire → primitive mapping

This is the table the protocol handlers follow. For the full per- operation mapping across all four protocols, see Coverage matrix.

Operation Primitive
Store unconditionally Set
Store if absent Add
Store if present Replace
Append / prepend Append / Prepend
Compare-and-swap CompareAndSwap
Increment / decrement IncrementOrInitialize
Delete Delete
Compare-and-delete (meta md C) CompareAndDelete
Refresh TTL Touch
Get-and-touch (gat / gats) GetAndTouch
Mark stale (meta md I / ms I) MarkStale
Drop all entries FlushWithGeneration
Rebudget Resize
Snapshot stats Snapshot

CacheEntry

Each stored value has the metadata recorded in CacheEntry:

Field Purpose
value Payload bytes
flags 32-bit opaque, returned on get
cas 64-bit monotonically increasing CAS token
expiry Absolute steady-clock deadline (or TimePoint::max)
generation For flush_all — entries older than the storage's live generation are invisible
lastAccess Updated on every successful Get; surfaced via meta l flag
stale Set by meta md I / ms I; surfaced via meta X response flag
fetched Set once the entry has been returned by a successful Get. Drives the evicted_unfetched / expired_unfetched stats (entries discarded before any client read them). Reset on insertion and on every value-rewriting mutation.

StorageStats

The counter set returned by Snapshot(). See stats command for the wire exposure.