Skip to main content

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

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 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

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

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:
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

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:
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:
OpHot hitCold hitCold miss
get< 1 μs~25 μs~10 μs (LMDB miss)
set~50 μs (LMDB sync write)n/an/a
list("ns", "pfx-") (100 entries)n/a~2 msn/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