> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Flash Memory Store

> Tiered storage with LMDB cold tier + in-memory hot tier

# Flash Memory Store

## What it solves

For long-term agent memory (user preferences, learned facts, historical decisions) the access pattern is power-law:

* \~1% of keys are read constantly (current user's preferences, recent decisions).
* \~99% are read rarely (historical facts, archived sessions).

Keeping everything in memory wastes RAM. Storing everything on disk wastes latency.

`FlashMemoryStore` is a tiered `StorageDriver`:

* **Hot tier:** in-process Map with LFU eviction. Fast.
* **Cold tier:** LMDB on disk. Cheap, ACID, \~50,000 ops/sec on SSDs.

Inspired by [Redis Iris / Flex](https://redis.io/iris) which uses the same pattern at petabyte scale.

## Architecture

```
                  ┌──────────────────────────────────────┐
                  │           FlashMemoryStore           │
                  │                                      │
   get(ns, key) ──┼──▶  Hot Map (LFU, hotCacheSize cap)  │
                  │                                      │
                  └──────────────┬───────────────────────┘
                                 │ miss
                                 ▼
                  ┌──────────────────────────────────────┐
                  │       LMDB (cold, on disk)            │
                  └──────────────────────────────────────┘
```

On a hot miss, the cold value is promoted to hot. On hot-cache overflow, the least-frequently-used entry is evicted (it's still in cold, so no data loss).

## Quick start

```typescript theme={null}
import { FlashMemoryStore } from "@agentium/core";

const store = new FlashMemoryStore({
  path: "./.agentium/memory",   // LMDB directory
  hotCacheSize: 10_000,
  mapSize: 2 * 1024 * 1024 * 1024, // 2 GB max cold size
});

await store.initialize();

await store.set("users", "user-123", { name: "Alice", signups: 7 });
const u = await store.get<{ name: string }>("users", "user-123");
console.log(u?.name); // "Alice"
```

## Configuration

```typescript theme={null}
interface FlashMemoryStoreConfig {
  path?: string;          // LMDB dir; omit for pure in-memory
  mapSize?: number;       // LMDB max size in bytes; default 2 GiB
  hotCacheSize?: number;  // Max entries in hot tier; default 10_000
}
```

### `path`

When omitted, **falls back to pure in-memory operation**. Useful for tests and serverless environments where you can't write to disk.

When set, `lmdb` (optional peer dep) is loaded lazily. If the package isn't installed, a warning is printed and the store also falls back to in-memory:

```
[agentium/flash-memory] `lmdb` not installed - falling back to pure in-memory storage. Install with: npm install lmdb
```

### `mapSize`

LMDB's underlying memory-mapped file size cap. **Once the file grows past `mapSize`, writes fail with `MDB_MAP_FULL`**. Set it generously — LMDB only consumes actual disk space, not the full mapped size. 2 GiB default is fine for most agent memory; raise to 100+ GiB for large knowledge corpuses.

### `hotCacheSize`

Bounds the hot tier. Too low → constant promotion churn. Too high → wastes RAM. Rule of thumb: set to 10× the expected working set (concurrent active users × per-user keys).

## Eviction policy (LFU)

The hot tier evicts the entry with the lowest **hit count**, with ties broken by oldest **last access**. This means:

* Recently written + never read → evicted soon.
* Old but constantly read → kept.
* Bursty access pattern → bursts survive while the burst is hot.

The hit count is monotonically increasing — there's no decay. For decay-based LFU (FreshLFU), wrap this in your own decorator.

## API (`StorageDriver`)

`FlashMemoryStore` implements the full `StorageDriver` interface, so it's a drop-in replacement anywhere a `StorageDriver` is accepted:

```typescript theme={null}
interface StorageDriver {
  initialize?(): Promise<void>;
  get<T>(namespace: string, key: string): Promise<T | null>;
  set<T>(namespace: string, key: string, value: T): Promise<void>;
  delete(namespace: string, key: string): Promise<void>;
  list<T>(namespace: string, prefix?: string): Promise<Array<{ key: string; value: T }>>;
  close(): Promise<void>;
}
```

### `get(namespace, key)`

Lookup order: hot map → cold LMDB. A cold hit is promoted to hot, evicting the least-used entry if hot is full.

### `set(namespace, key, value)`

Writes to BOTH tiers immediately. The hot copy makes the next read instant; the cold copy survives process restart.

If `path` is omitted (in-memory mode), only the hot map is written.

### `delete(namespace, key)`

Removes from both tiers. Idempotent.

### `list(namespace, prefix?)`

Returns all entries whose key (within `namespace`) starts with `prefix`. Implementation:

1. Range-scan the cold LMDB starting at `<namespace>\u0000<prefix>`.
2. Add any hot-only entries (e.g. set in memory before a cold flush).

Returns `Array<{ key, value }>` where `key` is the inner key (no namespace prefix).

**Performance:** Cold range scans are O(log N) seek + O(K) scan. Don't `list` over millions of keys per request — paginate with prefixes.

### `close()`

Closes the LMDB env and clears the hot map. **Always call this at process shutdown** to flush LMDB cleanly.

## Use as agent memory backend

```typescript theme={null}
import { Agent, FlashMemoryStore, openai } from "@agentium/core";

const storage = new FlashMemoryStore({ path: "./.agentium/memory" });
await storage.initialize();

const agent = new Agent({
  name: "long-memory-bot",
  model: openai("gpt-4o"),
  memory: { storage },
});

// All session history, learned facts, summaries persist to LMDB.
```

## Combine with `ScopedStorage`

For multi-tenant deployments, wrap it:

```typescript theme={null}
import { FlashMemoryStore, ScopedStorage } from "@agentium/core";

const flash = new FlashMemoryStore({ path: "./.agentium/memory" });
await flash.initialize();

function storageFor(tenantId: string) {
  return new ScopedStorage(flash, { tenantId });
}
```

Each tenant's keys are namespaced; the hot cache is still shared, so hot entries from heavy tenants stay hot.

## Performance characteristics

Benchmarks on M2 Mac, 5K entries, JSON values \~1KB each:

| Op                                 | Hot hit                   | Cold hit | Cold miss           |
| ---------------------------------- | ------------------------- | -------- | ------------------- |
| `get`                              | \< 1 μs                   | \~25 μs  | \~10 μs (LMDB miss) |
| `set`                              | \~50 μs (LMDB sync write) | n/a      | n/a                 |
| `list("ns", "pfx-")` (100 entries) | n/a                       | \~2 ms   | n/a                 |

Disk usage is roughly 1.2× the sum of serialized values (LMDB B+tree overhead).

## When to use

* Persistent agent memory across process restarts
* Long-running multi-day agent sessions
* High-volume workloads where the default `InMemoryStorage` won't survive
* Edge / Raspberry Pi deployments (LMDB has tiny RAM overhead)

## When NOT to use

* Multi-process deployments — LMDB allows multiple readers but only one writer process at a time. Use Postgres / Mongo / DynamoDB instead.
* Serverless functions — the LMDB file isn't shared across cold starts; use a network driver.
* You only need an in-memory cache — `Map` is simpler. Use this if you want the persistence too.

## See also

* [Storage Drivers](/storage/overview) — comparison of all built-in drivers
* [Multi-Tenant](/features/multi-tenant) — combine with ScopedStorage
* [Sessions](/agents/sessions) — how the session manager uses the storage driver
