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

# Cache Configuration

> Configuring BentoCache stores and drivers

Sonamu provides powerful caching capabilities based on [BentoCache](https://bentocache.dev/). This guide explains how to configure caching in `sonamu.config.ts`.

## What is BentoCache?

BentoCache is a TypeScript cache library that supports multi-tier caching.

**Key Features**:

* **L1/L2 Layers**: Memory (fast) + Persistent storage (slower but shareable)
* **Various Drivers**: Memory, Redis, File, Knex support
* **Bus System**: Distributed cache invalidation
* **Tag-based Invalidation**: Manage multiple caches as groups
* **TTL & Grace Period**: Expiration and Stale-While-Revalidate

## Basic Configuration

### sonamu.config.ts

Add cache configuration to the `server.cache` field in `sonamu.config.ts`:

```typescript theme={null}
import { drivers, store, type SonamuConfig } from "sonamu";

export const config: SonamuConfig = {
  // ... other settings
  server: {
    cache: {
      default: "main",
      stores: {
        main: store()
          .useL1Layer(drivers.memory({ maxSize: "100mb" }))
      },
    },
  },
};
```

**Required Fields**:

* `default`: Name of the default store to use
* `stores`: Store configuration object (at least 1 required)

## Driver Types

Sonamu provides 5 types of drivers:

<CardGroup cols={2}>
  <Card title="memory" icon="microchip">
    Memory-based cache
    (Fast but cleared on process restart)
  </Card>

  <Card title="redis" icon="database">
    Redis-based cache
    (Shareable across multiple processes)
  </Card>

  <Card title="file" icon="file">
    File system-based cache
    (Persistent storage)
  </Card>

  <Card title="knex" icon="table">
    Database-based cache
    (Uses existing DB)
  </Card>

  <Card title="redisBus" icon="tower-broadcast">
    Distributed cache invalidation bus
    (Synchronization across multiple servers)
  </Card>
</CardGroup>

### Import Methods

```typescript theme={null}
// 1. Using drivers object (recommended)
import { drivers } from "sonamu/cache";
drivers.memory({ maxSize: "100mb" });
drivers.redis({ connection: {...} });

// 2. Individual imports
import { memoryDriver, redisDriver } from "sonamu/cache";
memoryDriver({ maxSize: "100mb" });
redisDriver({ connection: {...} });
```

## Store Configuration

### L1 Layer (Memory Cache)

L1 is stored in local memory and is **the fastest**.

```typescript theme={null}
store().useL1Layer(drivers.memory({
  maxSize: "100mb",  // Maximum size
  maxItems: 1000,    // Maximum number of items
}))
```

**Characteristics**:

* Valid only within the process
* Cleared on server restart
* No network I/O (fastest)

### L2 Layer (Persistent Storage)

L2 is **shareable storage** across multiple processes/servers.

<Tabs>
  <Tab title="Redis" icon="database">
    ```typescript theme={null}
    import { drivers, store } from "sonamu/cache";
    import Redis from "ioredis";

    const redis = new Redis({
      host: "localhost",
      port: 6379,
    });

    store()
      .useL1Layer(drivers.memory({ maxSize: "100mb" }))
      .useL2Layer(drivers.redis({ connection: redis }))
    ```

    **Redis Advantages**:

    * Share cache across multiple servers
    * Persistent storage (retained after restart)
    * Fast network access
  </Tab>

  <Tab title="File" icon="file">
    ```typescript theme={null}
    store()
      .useL1Layer(drivers.memory({ maxSize: "100mb" }))
      .useL2Layer(drivers.file({ directory: ".cache" }))
    ```

    **File Advantages**:

    * No separate server required
    * Persistent storage
    * Simple configuration

    **File Disadvantages**:

    * Difficult to share across servers
    * Relatively slow
  </Tab>

  <Tab title="Knex (DB)" icon="table">
    ```typescript theme={null}
    import { DB } from "sonamu";

    store()
      .useL1Layer(drivers.memory({ maxSize: "100mb" }))
      .useL2Layer(drivers.knex({
        connection: DB.getDB("w"),
        tableName: "cache_items"
      }))
    ```

    **Knex Advantages**:

    * Uses existing DB
    * No additional infrastructure required

    **Knex Disadvantages**:

    * May increase DB load
    * Relatively slow
  </Tab>
</Tabs>

### Bus Layer (Distributed Invalidation)

When you have multiple servers, deleting cache on one server **notifies the others**.

```typescript theme={null}
import Redis from "ioredis";

const redis = new Redis({ host: "localhost", port: 6379 });

store()
  .useL1Layer(drivers.memory({ maxSize: "100mb" }))
  .useL2Layer(drivers.redis({ connection: redis }))
  .useBus(drivers.redisBus({ connection: redis }))
```

**Without Bus**:

```
Server 1: deleteByTag("product") → L1/L2 deleted
Server 2: Still using stale cache ❌
```

**With Bus**:

```
Server 1: deleteByTag("product") → L1/L2 deleted + Bus notification
Server 2: Receives Bus message → L1 deleted ✅
```

## Multi-Store Configuration

You can use multiple stores for different purposes:

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    cache: {
      default: "api",  // Default store
      stores: {
        // API response caching (short TTL, memory only)
        api: store()
          .useL1Layer(drivers.memory({ maxSize: "200mb" })),

        // Database query caching (long TTL, Redis shared)
        database: store()
          .useL1Layer(drivers.memory({ maxSize: "100mb" }))
          .useL2Layer(drivers.redis({ connection: redis }))
          .useBus(drivers.redisBus({ connection: redis })),

        // Static config caching (permanent, file storage)
        config: store()
          .useL1Layer(drivers.memory({ maxItems: 100 }))
          .useL2Layer(drivers.file({ directory: ".config-cache" })),
      },
    },
  },
};
```

### Using Stores

```typescript theme={null}
// Use default store
await Sonamu.cache.set({ key: "user:1", value: {...} });

// Use specific store
await Sonamu.cache.use("database").set({ key: "query:1", value: {...} });
await Sonamu.cache.use("config").set({ key: "settings", value: {...} });
```

You can also specify stores in decorators:

```typescript theme={null}
class UserModelClass extends BaseModelClass {
  // Use database store
  @cache({ store: "database", ttl: "1h" })
  @api()
  async findById(id: number) {
    return this.findOne(['id', id]);
  }

  // Use config store
  @cache({ store: "config", ttl: "forever" })
  async getSettings() {
    return this.findOne(['key', 'settings']);
  }
}
```

## Practical Examples

### 1. Single Server (Memory Only)

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    cache: {
      default: "main",
      stores: {
        main: store().useL1Layer(
          drivers.memory({
            maxSize: "500mb",
            maxItems: 10000,
          })
        ),
      },
    },
  },
};
```

**Suitable Cases**:

* Single server operation
* Fast cache regeneration
* Infrequent server restarts

### 2. Multiple Servers (Redis Shared)

```typescript theme={null}
import Redis from "ioredis";

const redis = new Redis({
  host: process.env.REDIS_HOST ?? "localhost",
  port: Number(process.env.REDIS_PORT) ?? 6379,
  password: process.env.REDIS_PASSWORD,
});

export const config: SonamuConfig = {
  server: {
    cache: {
      default: "main",
      stores: {
        main: store()
          .useL1Layer(drivers.memory({ maxSize: "200mb" }))
          .useL2Layer(drivers.redis({ connection: redis }))
          .useBus(drivers.redisBus({ connection: redis })),
      },
    },
  },
};
```

**Suitable Cases**:

* Load balancer + multiple servers
* Need to share cache across servers
* Maintain cache after server restart

### 3. Tier-Based Configuration (Performance Optimization)

```typescript theme={null}
import Redis from "ioredis";

const redis = new Redis({ /* ... */ });

export const config: SonamuConfig = {
  server: {
    cache: {
      default: "main",
      stores: {
        // Fast lookup (API responses)
        main: store()
          .useL1Layer(drivers.memory({ maxSize: "300mb" })),

        // Sharing required (user sessions)
        shared: store()
          .useL1Layer(drivers.memory({ maxSize: "100mb" }))
          .useL2Layer(drivers.redis({ connection: redis }))
          .useBus(drivers.redisBus({ connection: redis })),

        // Permanent storage (settings, templates)
        persistent: store()
          .useL1Layer(drivers.memory({ maxItems: 100 }))
          .useL2Layer(drivers.file({ directory: ".persistent-cache" })),
      },
    },
  },
};
```

## Driver Options in Detail

### Memory Driver

```typescript theme={null}
drivers.memory({
  maxSize: "100mb",     // Maximum size (string or bytes)
  maxItems: 1000,       // Maximum number of items
  maxItemSize: "10mb",  // Maximum size per item
})
```

**Size Units**: `"10kb"`, `"5mb"`, `"1gb"` or bytes (`1024`)

### Redis Driver

```typescript theme={null}
import Redis from "ioredis";

const redis = new Redis({
  host: "localhost",
  port: 6379,
  password: "secret",
  db: 0,
});

drivers.redis({
  connection: redis,
  keyPrefix: "cache:",  // Key prefix (optional)
})
```

### File Driver

```typescript theme={null}
drivers.file({
  directory: ".cache",  // Storage directory
  prefix: "sonamu",     // Filename prefix (optional)
})
```

### Knex Driver

```typescript theme={null}
import { DB } from "sonamu";

drivers.knex({
  connection: DB.getDB("w"),
  tableName: "cache_items",  // Table name (default)
})
```

**Table Schema** (auto-created):

```sql theme={null}
CREATE TABLE cache_items (
  key TEXT PRIMARY KEY,
  value TEXT,
  expires_at INTEGER
);
```

### Redis Bus Driver

```typescript theme={null}
drivers.redisBus({
  connection: redis,
  channelPrefix: "cache:",  // Channel prefix (optional)
})
```

## Test Environment

In test environments, the memory driver is automatically used:

```typescript theme={null}
// No separate configuration needed for tests
import { Sonamu } from "sonamu";

test("cache test", async () => {
  // Automatically uses memory cache
  await Sonamu.cache.set({ key: "test", value: "value" });

  const result = await Sonamu.cache.get({ key: "test" });
  expect(result).toBe("value");
});
```

**Internal Implementation**:

```typescript theme={null}
// sonamu/src/api/sonamu.ts
private async initializeCache(config: CacheConfig | undefined, forTesting: boolean) {
  if (forTesting) {
    const { createTestCacheManager } = await import("../cache/cache-manager");
    this._cache = createTestCacheManager();  // Memory driver
    return;
  }
  // ...
}
```

## Cautions

<Warning>
  **Cache Configuration Cautions**:

  1. **Memory Limits**: Setting `maxSize` too large can cause OOM (Out of Memory)
     ```typescript theme={null}
     // ❌ Bad example
     drivers.memory({ maxSize: "10gb" })  // Larger than server memory

     // ✅ Good example
     drivers.memory({ maxSize: "500mb" })  // Appropriate size
     ```

  2. **Redis Connection Sharing**: Use the **same instance** for driver and bus
     ```typescript theme={null}
     // ✅ Good example
     const redis = new Redis({...});
     store()
       .useL2Layer(drivers.redis({ connection: redis }))
       .useBus(drivers.redisBus({ connection: redis }))
     ```

  3. **Store Name Matching**: The `store` option in decorators must match names defined in configuration
     ```typescript theme={null}
     // sonamu.config.ts
     stores: {
       myStore: store()...
     }

     // ❌ Bad example
     @cache({ store: "wrongName" })  // Error

     // ✅ Good example
     @cache({ store: "myStore" })
     ```

  4. **Using L2 Without Bus**: If multiple servers use only L2 (Redis), L1 won't be synchronized
     ```typescript theme={null}
     // ❌ Risky: L1 inconsistency possible between servers
     store()
       .useL1Layer(drivers.memory({ maxSize: "100mb" }))
       .useL2Layer(drivers.redis({ connection: redis }))
     // No Bus → L1 on other servers remains unchanged during invalidation

     // ✅ Safe: L1 synchronized via Bus
     store()
       .useL1Layer(drivers.memory({ maxSize: "100mb" }))
       .useL2Layer(drivers.redis({ connection: redis }))
       .useBus(drivers.redisBus({ connection: redis }))
     ```
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="@cache Decorator" icon="at" href="/en/advanced-features/caching/cache-decorator">
    Apply caching to methods
  </Card>

  <Card title="Cache Invalidation" icon="trash" href="/en/advanced-features/caching/cache-invalidation">
    Tag-based cache invalidation
  </Card>

  <Card title="Cache Strategies" icon="chess" href="/en/advanced-features/caching/cache-strategies">
    TTL, Grace, Namespace usage
  </Card>
</CardGroup>
