@aurora/runtime-projection-state (0.3.2)
Published 2025-12-27 10:26:35 +00:00 by vlad
Installation
@aurora:registry=npm install @aurora/runtime-projection-state@0.3.2"@aurora/runtime-projection-state": "0.3.2"About this package
@aurora/runtime-storage-lmdbx
LMDBX-based storage adapter for Aurora projections.
Overview
This package provides a high-performance, embedded key-value store implementation using LMDB (Lightning Memory-Mapped Database). LMDBX is optimized for read-heavy workloads and provides ACID guarantees with zero-copy reads via memory-mapped files.
Use cases:
- Hot projection data (current state)
- Read-optimized caching layers
- Fast lookups with sub-millisecond latency
Not suitable for:
- Write-heavy workloads (use RocksDB instead)
- Large values (>1MB)
- Event history (use RocksDB instead)
Installation
bun add @aurora/runtime-storage-lmdbx
Usage
Basic Example
import { createLMDBXRepository } from "@aurora/runtime-storage-lmdbx";
// Create repository
const repo = createLMDBXRepository<string, { name: string; email: string }>({
path: "./data/users",
encoding: "msgpack",
});
// Put a value
await repo.put("tenant1", "user:123", { name: "Alice", email: "alice@example.com" });
// Get a value
const user = await repo.get("tenant1", "user:123");
console.log(user); // { name: "Alice", email: "alice@example.com" }
// Delete a value
await repo.delete("tenant1", "user:123");
// Check if key exists
const exists = await repo.has("tenant1", "user:123");
console.log(exists); // false (just deleted)
// List all keys for a tenant
const keys = await repo.list("tenant1");
console.log(keys); // ["user:456", "user:789"]
// Close database
await repo.close();
Configuration
interface LMDBXConfig {
/** Path to the LMDBX database directory */
path: string;
/** Database name (for multiple databases in same environment) */
name?: string;
/** Enable compression (default: false) */
compression?: boolean;
/** Encoding format (default: "msgpack") */
encoding?: "msgpack" | "json" | "string";
/** Maximum database size in bytes (default: auto) */
mapSize?: number;
}
Encoding options:
msgpack(default): Binary encoding, compact, fastjson: Human-readable, larger sizestring: For string-only values
Multi-Tenant Isolation
Keys are automatically scoped by tenant ID:
// Different tenants, same key
await repo.put("tenant1", "counter", { value: 10 });
await repo.put("tenant2", "counter", { value: 20 });
const t1 = await repo.get("tenant1", "counter"); // { value: 10 }
const t2 = await repo.get("tenant2", "counter"); // { value: 20 }
Complex Keys
Keys can be any JSON-serializable type:
type TimeSeriesKey = { userId: string; timestamp: number };
type Metric = { value: number; tags: string[] };
const repo = new LMDBXRepository<TimeSeriesKey, Metric>({
path: "./data/metrics",
});
await repo.put(
"tenant1",
{ userId: "user123", timestamp: Date.now() },
{ value: 42, tags: ["cpu"] }
);
Architecture
Key Encoding
Keys are encoded as tenantId:JSON(key) to provide tenant isolation and support arbitrary key types.
Example:
- Input:
tenantId="acme",key="user:123" - Stored as:
acme:"user:123"
Storage Layout
data/
└── users/ # LMDBX database directory
├── data.mdb # Memory-mapped data file
└── lock.mdb # Lock file
Performance Characteristics
- Reads: O(log n) with memory-mapped access (sub-millisecond)
- Writes: O(log n) with ACID guarantees
- Space: Compact binary encoding (msgpack)
- Concurrency: Multiple readers, single writer (MVCC)
Memory-Mapped I/O
LMDBX uses memory-mapped files for zero-copy reads:
- OS manages caching automatically
- No serialization overhead on reads
- Writes are still transactional and durable
Integration with Aurora
Projection Storage
import { createLMDBXRepository } from "@aurora/runtime-storage-lmdbx";
import type { Repository } from "@aurora/runtime-storage";
// Hot projection data
const ordersRepo: Repository<string, Order> = new LMDBXRepository({
path: "./data/projections/orders",
encoding: "msgpack",
});
// Use in projection workers
async function updateProjection(event: OrderCreated) {
const order = {
id: event.payload.orderId,
status: "pending",
createdAt: event.timestamp,
};
await ordersRepo.put(
event.payload.tenantId,
event.payload.orderId,
order
);
}
Comparison with RocksDB
| Feature | LMDBX | RocksDB |
|---|---|---|
| Read performance | ⚡️ Faster (mmap) | Fast (cached) |
| Write performance | Good | ⚡️ Faster (LSM) |
| Space efficiency | Good | ⚡️ Better (compression) |
| Use case | Hot projections | Event history, write-heavy |
Rule of thumb:
- LMDBX: Current state, frequent reads
- RocksDB: Historical data, frequent writes
Limitations
- Single writer: Only one process can write at a time (multiple readers allowed)
- Database size: Must be pre-allocated or will auto-grow (configurable via
mapSize) - Value size: Best for values < 1MB (no hard limit, but performance degrades)
- Not distributed: Embedded storage only (use NATS JetStream for replication)
Testing
bun test
Tests use a temporary database directory that's cleaned up after each test.
License
MIT
Dependencies
Dependencies
| ID | Version |
|---|---|
| @aurora/runtime-effect | 0.3.2 |
| @aurora/runtime-observability | 0.3.2 |
| @aurora/runtime-storage | 0.3.2 |
| lmdb | ^3.2.2 |
Details
2025-12-27 10:26:35 +00:00
Assets (1)
Versions (3)
View all
npm
4
5.6 KiB