---
title: "Storage Layer"
description: "Transaction storage interface and implementations: MemoryStorage, RedisStorage, MongoStorage"
---

## Storage Interface

All storage implementations must implement the `ITransactionStorage` interface:

```typescript
interface ITransactionStorage {
    // Lifecycle
    close?(): Promise<void>;

    // Distributed lock
    acquireLock(key: string, ttl: number): Promise<string | null>;
    releaseLock(key: string, token: string): Promise<boolean>;

    // Transaction log
    saveTransaction(tx: TransactionLog): Promise<void>;
    getTransaction(id: string): Promise<TransactionLog | null>;
    updateTransactionState(id: string, state: TransactionState): Promise<void>;
    updateOperationState(txId: string, opIndex: number, state: string, error?: string): Promise<void>;
    getPendingTransactions(serverId?: string): Promise<TransactionLog[]>;
    deleteTransaction(id: string): Promise<void>;

    // Data operations
    get<T>(key: string): Promise<T | null>;
    set<T>(key: string, value: T, ttl?: number): Promise<void>;
    delete(key: string): Promise<boolean>;
}
```

## MemoryStorage

In-memory storage, suitable for development and testing.

```typescript
import { MemoryStorage } from '@esengine/transaction';

const storage = new MemoryStorage({
    maxTransactions: 1000,  // Maximum transaction log count
});

const manager = new TransactionManager({ storage });
```

### Characteristics

- ✅ No external dependencies
- ✅ Fast, good for debugging
- ❌ Data only stored in memory
- ❌ No true distributed locking
- ❌ Data lost on restart

### Test Helpers

```typescript
// Clear all data
storage.clear();

// Get transaction count
console.log(storage.transactionCount);
```

## RedisStorage

Redis storage, suitable for production distributed systems. Uses factory pattern with lazy connection.

```typescript
import Redis from 'ioredis';
import { RedisStorage } from '@esengine/transaction';

// Factory pattern: lazy connection, connects on first operation
const storage = new RedisStorage({
    factory: () => new Redis('redis://localhost:6379'),
    prefix: 'tx:',              // Key prefix
    transactionTTL: 86400,      // Transaction log TTL (seconds)
});

const manager = new TransactionManager({ storage });

// Close connection when done
await storage.close();

// Or use await using for automatic cleanup (TypeScript 5.2+)
await using storage = new RedisStorage({
    factory: () => new Redis('redis://localhost:6379')
});
// Automatically closed when scope ends
```

### Characteristics

- ✅ High-performance distributed locking
- ✅ Fast read/write
- ✅ Supports TTL auto-expiration
- ✅ Suitable for high concurrency
- ❌ Requires Redis server

### Distributed Lock Implementation

Uses Redis `SET NX EX` for distributed locking:

```typescript
// Acquire lock (atomic operation)
SET tx:lock:player:123 <token> NX EX 10

// Release lock (Lua script for atomicity)
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
```

### Key Structure

```
tx:lock:{key}           - Distributed locks
tx:tx:{id}              - Transaction logs
tx:server:{id}:txs      - Server transaction index
tx:data:{key}           - Business data
```

## MongoStorage

MongoDB storage, suitable for scenarios requiring persistence and complex queries. Uses shared connection from `@esengine/database-drivers`.

```typescript
import { createMongoConnection } from '@esengine/database-drivers';
import { createMongoStorage, TransactionManager } from '@esengine/transaction';

// Create shared connection
const mongo = createMongoConnection({
    uri: 'mongodb://localhost:27017',
    database: 'game'
});
await mongo.connect();

// Create storage using shared connection
const storage = createMongoStorage(mongo, {
    transactionCollection: 'transactions',  // Transaction log collection (optional)
    dataCollection: 'transaction_data',     // Business data collection (optional)
    lockCollection: 'transaction_locks',    // Lock collection (optional)
});

// Create indexes (run on first startup)
await storage.ensureIndexes();

const manager = new TransactionManager({ storage });

// Close storage (does not close shared connection)
await storage.close();

// Shared connection can continue to be used by other modules
const userRepo = new UserRepository(mongo);  // @esengine/database

// Finally close the shared connection
await mongo.disconnect();
```

### Characteristics

- ✅ Persistent storage
- ✅ Supports complex queries
- ✅ Transaction logs are traceable
- ✅ Suitable for audit requirements
- ❌ Slightly lower performance than Redis
- ❌ Requires MongoDB server

### Index Structure

```javascript
// transactions collection
{ state: 1 }
{ 'metadata.serverId': 1 }
{ createdAt: 1 }

// transaction_locks collection
{ expireAt: 1 }  // TTL index

// transaction_data collection
{ expireAt: 1 }  // TTL index
```

### Distributed Lock Implementation

Uses MongoDB unique index for distributed locking:

```typescript
// Acquire lock
db.transaction_locks.insertOne({
    _id: 'player:123',
    token: '<token>',
    expireAt: new Date(Date.now() + 10000)
});

// If key exists, check if expired
db.transaction_locks.updateOne(
    { _id: 'player:123', expireAt: { $lt: new Date() } },
    { $set: { token: '<token>', expireAt: new Date(Date.now() + 10000) } }
);
```

## Storage Selection Guide

| Scenario | Recommended Storage | Reason |
|----------|---------------------|--------|
| Development/Testing | MemoryStorage | No dependencies, fast startup |
| Single-machine Production | RedisStorage | High performance, simple |
| Distributed System | RedisStorage | True distributed locking |
| Audit Required | MongoStorage | Persistent logs |
| Mixed Requirements | Redis + Mongo | Redis for locks, Mongo for logs |

## Custom Storage

Implement `ITransactionStorage` interface to create custom storage:

```typescript
import { ITransactionStorage, TransactionLog, TransactionState } from '@esengine/transaction';

class MyCustomStorage implements ITransactionStorage {
    async acquireLock(key: string, ttl: number): Promise<string | null> {
        // Implement distributed lock acquisition
    }

    async releaseLock(key: string, token: string): Promise<boolean> {
        // Implement distributed lock release
    }

    async saveTransaction(tx: TransactionLog): Promise<void> {
        // Save transaction log
    }

    // ... implement other methods
}
```
