first commit
This commit is contained in:
76
docs/foundation/flow/idempotency/src/store/redis-store.md
Normal file
76
docs/foundation/flow/idempotency/src/store/redis-store.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# `redis-store`
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting a generic `RedisClientAdapter` interface rather than a concrete library, this module integrates with `ioredis`, `redis`, Upstash Redis, or any compatible client without coupling to a specific driver.
|
||||
|
||||
## Features
|
||||
|
||||
- JSON-serialised records stored as Redis string values
|
||||
- Automatic TTL set on every `set()` call based on `record.expiresAt`
|
||||
- Fallback TTL (`defaultTtlSeconds`) for records whose expiry is in the past
|
||||
- Configurable key prefix for namespace isolation (multi-tenant)
|
||||
- `cleanup()` scans expired records server-side for manual purging
|
||||
- Generic `<T>` type parameter for typed payloads
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { RedisIdempotencyStore } from "@suaveplan/idempotency/store";
|
||||
import type { RedisClientAdapter } from "@suaveplan/idempotency/store";
|
||||
|
||||
declare const redisAdapter: RedisClientAdapter; // your ioredis wrapper
|
||||
|
||||
const store = new RedisIdempotencyStore({
|
||||
client: redisAdapter,
|
||||
prefix: "app:idempotency:",
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { RedisIdempotencyStore } from "@suaveplan/idempotency/store";
|
||||
|
||||
const store = new RedisIdempotencyStore({
|
||||
client: redisAdapter,
|
||||
prefix: `tenant:${tenantId}:idempotency:`,
|
||||
defaultTtlSeconds: 3600, // 1 hour fallback
|
||||
});
|
||||
|
||||
// Manual cleanup (most records expire via Redis TTL automatically)
|
||||
const removed = await store.cleanup(Date.now());
|
||||
console.log(`Manually removed ${removed} stale records`);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new RedisIdempotencyStore<T>(config: RedisIdempotencyStoreConfig)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `client` | `RedisClientAdapter` | required | Redis client adapter |
|
||||
| `prefix` | `string` | `"idempotency:"` | Key namespace prefix |
|
||||
| `defaultTtlSeconds` | `number` | `86400` | Fallback TTL when expiresAt is past |
|
||||
|
||||
### `get(key): Promise<IdempotencyRecord<T> | undefined>`
|
||||
|
||||
Fetches and JSON-parses the record. Returns `undefined` if not found (key expired or never set).
|
||||
|
||||
### `set(key, record): Promise<void>`
|
||||
|
||||
Serialises the record as JSON and stores it with a TTL computed from `record.expiresAt - Date.now()`.
|
||||
|
||||
### `delete(key): Promise<boolean>`
|
||||
|
||||
Calls `DEL` on the prefixed key; returns `true` if a key was removed.
|
||||
|
||||
### `cleanup(beforeTimestamp): Promise<number>`
|
||||
|
||||
Scans all keys under the prefix, parses each, and deletes those with `expiresAt < beforeTimestamp`. Returns the count removed.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Redis TTL handles the common expiration case automatically; `cleanup` is only needed for records that were not expired by Redis (e.g. records with very long TTLs where early manual deletion is desired).
|
||||
- `scan` is used rather than `KEYS` to avoid blocking the Redis event loop in production.
|
||||
- JSON serialisation assumes records are fully serialisable; `Date` objects should be stored as timestamps (numbers).
|
||||
Reference in New Issue
Block a user