AcornMemoryWorker Storage

In-memory content-addressable storage with nanosecond-scale synchronous reads and optional LFU eviction.

.package(url: "https://github.com/treehauslabs/AcornMemoryWorker.git", from: "1.0.0")

MemoryCASWorker

actor AcornCASWorker

public actor MemoryCASWorker: AcornCASWorker {
    public init(
        capacity: Int? = nil,
        maxBytes: Int? = nil,
        halfLife: Duration = .seconds(300),
        sampleSize: Int = 5,
        timeout: Duration? = nil
    )
}

Parameters

capacity Int? Maximum number of entries. When exceeded, LFU eviction kicks in. Nil for unbounded.
maxBytes Int? Maximum total bytes in memory. Nil for unbounded.
halfLife Duration Time for LFU scores to decay to 50%. Default 300 seconds.
sampleSize Int Number of random candidates to sample during eviction. Default 5.
timeout Duration? Timeout for chain traversal operations. Nil for no timeout.
Dual API
MemoryCASWorker provides two ways to access data: async methods (via the actor, ~7.5µs) and sync methods (via internal lock, 3–28ns). Both access the same underlying state and are safe to use concurrently.

Sync API

Marked nonisolated — bypasses the actor executor and accesses the internal OSAllocatedUnfairLock directly. Use these on hot paths.

MethodReturnLatency
syncHas(cid:) Bool 3–11 ns
syncGet(cid:) Data? 6–28 ns
syncStore(cid:data:) Void 142 ns (733 ns with eviction)
syncDelete(cid:) Void 78 ns
let worker = MemoryCASWorker(capacity: 10_000)

// No async/await needed
worker.syncStore(cid: cid, data: payload)

if let data = worker.syncGet(cid: cid) {
    process(data)
}

if worker.syncHas(cid: cid) {
    // exists
}

Async API

Standard AcornCASWorker protocol methods. These go through the actor executor (~7µs overhead) but compose naturally with the chain.

MethodBehavior
has(cid:) async Check existence. Same logic as syncHas.
getLocal(cid:) async Retrieve data from local store. Updates LFU scores and metrics.
storeLocal(cid:data:) async Store data locally. Handles eviction if at capacity.
delete(cid:) Remove an entry. Updates metrics, byte tracking, and LFU cache.
get(cid:) async Protocol default. Walks chain: near → local → backfill.
store(cid:data:) async Protocol default. Stores locally, propagates to near.

Properties

PropertyTypeDescription
metricsCASMetricsHit/miss/store/eviction/deletion counts
totalBytesIntCurrent total bytes stored in memory
timeoutDuration?Timeout for chain operations
near(any AcornCASWorker)?Faster adjacent worker in chain
far(any AcornCASWorker)?Slower adjacent worker in chain

CASMetrics

public struct CASMetrics: Sendable, Equatable {
    public var hits: Int
    public var misses: Int
    public var stores: Int
    public var evictions: Int
    public var deletions: Int
}

Eviction Behavior

When either capacity or maxBytes is exceeded during a store:

  1. The LFU cache samples sampleSize random entries
  2. The entry with the lowest time-decayed score is evicted
  3. Eviction repeats until limits are satisfied
  4. metrics.evictions is incremented for each eviction
Unbounded mode
Passing nil for both capacity and maxBytes creates an unbounded cache with no eviction overhead. Useful when you know your dataset fits in memory.

Performance

Benchmarked on Apple Silicon in release mode:

OperationSyncAsync (Actor)Ratio
Get hit (17B)28 ns7.5 µs268x
Get miss6 ns7.4 µs1233x
Has (hit)11 ns7.3 µs664x
Has (miss)3 ns
Store142 ns7.6 µs54x
Store + evict733 ns7.6 µs10x
Delete78 ns7.2 µs92x

The ~7µs actor overhead is fixed regardless of data size. For the sync API, data size is irrelevant for gets (dictionary stores references, not copies).