@comic-vine/in-memory-store
Version:
In-memory store implementations for Comic Vine client caching, deduplication, and rate limiting
416 lines (319 loc) • 12.7 kB
Markdown
In-memory store implementations for Comic Vine client caching, deduplication, and rate limiting.
```bash
npm install @comic-vine/in-memory-store @comic-vine/client
```
```typescript
import ComicVine from '@comic-vine/client';
import {
InMemoryCacheStore,
InMemoryDedupeStore,
InMemoryRateLimitStore,
AdaptiveRateLimitStore, // NEW: Intelligent rate limiting
} from '@comic-vine/in-memory-store';
const client = new ComicVine({
apiKey: 'your-api-key',
stores: {
cache: new InMemoryCacheStore(),
dedupe: new InMemoryDedupeStore(),
rateLimit: new AdaptiveRateLimitStore(), // Recommended for optimal API usage
},
});
// Use client normally - data is cached in memory with intelligent rate limiting
const issue = await client.issue.retrieve(1, { priority: 'user' });
```
- **High Performance**: All operations are in-memory for maximum speed
- **Object-based Configuration**: Clean, type-safe constructor parameters
- **Zero Dependencies**: No external storage requirements
- **Type Safety**: Full TypeScript support with exported option interfaces
Provides in-memory caching with TTL support.
```typescript
const cacheStore = new InMemoryCacheStore({
maxSize: 1000, // Maximum number of entries
ttl: 300000, // 5 minutes TTL
cleanupIntervalMs: 60000, // Cleanup expired items every minute
});
```
**Features:**
- LRU eviction when max size is reached
- TTL-based expiration with automatic cleanup
- Fast O(1) get/set operations
- Memory-efficient storage
### InMemoryDedupeStore
Prevents duplicate concurrent requests using in-memory coordination.
```typescript
import type { InMemoryDedupeStoreOptions } from '@comic-vine/in-memory-store';
const dedupeStore = new InMemoryDedupeStore({
jobTimeoutMs: 300_000, // 5 minute timeout for jobs (default)
cleanupIntervalMs: 60_000, // Cleanup interval (default: 1 minute)
});
```
**Features:**
- Promise-based deduplication
- Automatic cleanup of expired jobs
- Error state handling and propagation
- Memory-efficient job tracking
- Configurable job timeouts and cleanup intervals
Implements sliding window rate limiting with in-memory tracking.
```typescript
import type { InMemoryRateLimitStoreOptions } from '@comic-vine/in-memory-store';
const rateLimitStore = new InMemoryRateLimitStore({
defaultConfig: { limit: 100, windowMs: 60_000 }, // Default: 100 req/min
resourceConfigs: new Map([
['issues', { limit: 50, windowMs: 60_000 }], // Custom per-resource limits
['characters', { limit: 200, windowMs: 60_000 }],
]),
});
```
**Features:**
- Sliding window algorithm
- Per-resource configuration
- Automatic cleanup of expired records
- Memory-efficient timestamp tracking
- Flexible default and per-resource configurations
Advanced rate limiting with intelligent priority-based capacity allocation that adapts to real-time user activity patterns.
```typescript
import type { AdaptiveRateLimitStoreOptions } from '@comic-vine/in-memory-store';
const adaptiveStore = new AdaptiveRateLimitStore({
adaptiveConfig: {
// Activity detection
highActivityThreshold: 10, // Requests/15min to trigger priority mode
moderateActivityThreshold: 3, // Requests/15min for moderate activity
// Timing behavior
monitoringWindowMs: 15 * 60 * 1000, // 15 minutes monitoring window
sustainedInactivityThresholdMs: 30 * 60 * 1000, // 30min for full background scale-up
recalculationIntervalMs: 30000, // Recalculate every 30 seconds
// Capacity limits
maxUserScaling: 2.0, // Maximum user capacity multiplier
minUserReserved: 5, // Minimum guaranteed user requests
backgroundPauseOnIncreasingTrend: true, // Pause background on user surge
},
});
```
**Features:**
- **Real-time adaptation**: Dynamically allocates capacity based on user activity patterns
- **Priority-aware**: Distinguishes between user requests and background operations
- **Four adaptive strategies**: Night mode, low activity, moderate activity, high activity
- **Zero configuration**: Works perfectly with intelligent defaults
- **Memory efficient**: In-memory activity tracking with automatic cleanup
- **Trend detection**: Recognizes increasing, stable, decreasing, or no activity trends
**How It Works:**
```typescript
// Night time (zero user activity for 30+ minutes)
// → Background gets 100% capacity (200/200 requests)
// Low activity (1-2 user requests per 15 minutes)
// → Users: 30% reserved, Background: 70% capacity
// Moderate activity (3-9 user requests per 15 minutes)
// → Dynamic scaling based on trends and activity level
// High activity (10+ user requests per 15 minutes)
// → Users: 90% priority capacity, Background: paused during increasing trends
```
**Usage with Priority:**
```typescript
import ComicVine from '@comic-vine/client';
import { AdaptiveRateLimitStore } from '@comic-vine/in-memory-store';
const client = new ComicVine({
apiKey: 'your-api-key',
stores: {
rateLimit: new AdaptiveRateLimitStore(), // Zero config needed!
},
});
// User-facing requests get priority during high activity
const character = await client.character.retrieve(1443, {
priority: 'user',
});
// Background operations get remaining capacity
const volumes = await client.volume.list({
priority: 'background',
});
// Check adaptive status
const status = await client.stores.rateLimit.getStatus('characters');
console.log(status.adaptive);
// {
// userReserved: 120,
// backgroundMax: 80,
// backgroundPaused: false,
// recentUserActivity: 4,
// reason: "Moderate user activity - dynamic scaling (1.2x user capacity)"
// }
```
The three store types work in sequence to optimize API requests:
1. **Cache Store**: Checks for existing response (immediate return if found)
2. **Dedupe Store**: Prevents duplicate concurrent requests (waits for in-progress requests)
3. **Rate Limit Store**: Enforces API limits (waits or throws if limit exceeded)
```typescript
// Example: Multiple concurrent requests for the same resource
const [a, b, c] = await Promise.all([
client.issue.retrieve(1), // Cache miss → dedupe register → rate limit check → API call
client.issue.retrieve(1), // Cache miss → dedupe wait (shares result from first call)
client.issue.retrieve(1), // Cache miss → dedupe wait (shares result from first call)
]);
// Subsequent calls return from cache
const d = await client.issue.retrieve(1); // Cache hit → immediate return
```
Each store manages its own lifecycle and cleanup:
- **Cache**: Expires entries based on TTL, evicts LRU items when full
- **Dedupe**: Times out jobs, cleans up completed requests
- **Rate Limit**: Slides time windows, removes expired request records
All stores are designed for efficient memory usage:
- **Cache**: LRU eviction with configurable memory limits
- **Dedupe**: Automatic cleanup of completed/failed jobs
- **Rate Limit**: Sliding window removes old timestamps automatically
```typescript
import ComicVine from '@comic-vine/client';
import {
InMemoryCacheStore,
InMemoryDedupeStore,
AdaptiveRateLimitStore, // Recommended for intelligent rate limiting
} from '@comic-vine/in-memory-store';
const client = new ComicVine({
apiKey: 'your-api-key',
stores: {
cache: new InMemoryCacheStore(),
dedupe: new InMemoryDedupeStore(),
rateLimit: new AdaptiveRateLimitStore(), // Zero configuration needed!
},
});
```
**With Traditional Rate Limiting:**
```typescript
const client = new ComicVine({
apiKey: 'your-api-key',
stores: {
cache: new InMemoryCacheStore({
maxSize: 5000,
ttl: 600_000, // 10 minutes
cleanupIntervalMs: 120_000, // 2 minutes
}),
dedupe: new InMemoryDedupeStore({
jobTimeoutMs: 600_000, // 10 minutes
cleanupIntervalMs: 120_000, // 2 minutes
}),
rateLimit: new InMemoryRateLimitStore({
defaultConfig: { limit: 200, windowMs: 60_000 }, // 200 requests per minute
resourceConfigs: new Map([
['issues', { limit: 100, windowMs: 60_000 }],
['characters', { limit: 300, windowMs: 60_000 }],
]),
}),
},
});
```
**With Adaptive Rate Limiting (Recommended):**
```typescript
const client = new ComicVine({
apiKey: 'your-api-key',
stores: {
cache: new InMemoryCacheStore({
maxSize: 5000,
ttl: 600_000, // 10 minutes
cleanupIntervalMs: 120_000, // 2 minutes
}),
dedupe: new InMemoryDedupeStore({
jobTimeoutMs: 600_000, // 10 minutes
cleanupIntervalMs: 120_000, // 2 minutes
}),
rateLimit: new AdaptiveRateLimitStore({
adaptiveConfig: {
// Fine-tune behavior for your use case
highActivityThreshold: 15, // Requests/15min to trigger priority mode
sustainedInactivityThresholdMs: 45 * 60 * 1000, // 45min before full background mode
maxUserScaling: 1.5, // Conservative user scaling
minUserReserved: 10, // Higher minimum for users
},
}),
},
});
```
All stores export their option interfaces for type safety:
```typescript
import type {
InMemoryCacheStoreOptions,
InMemoryDedupeStoreOptions,
InMemoryRateLimitStoreOptions,
AdaptiveRateLimitStoreOptions, // NEW: Adaptive rate limiting options
} from '@comic-vine/in-memory-store';
// Type-safe configuration for traditional rate limiting
const rateLimitOptions: InMemoryRateLimitStoreOptions = {
defaultConfig: { limit: 200, windowMs: 3600000 },
resourceConfigs: new Map([['issues', { limit: 100, windowMs: 3600000 }]]),
};
// Type-safe configuration for adaptive rate limiting
const adaptiveOptions: AdaptiveRateLimitStoreOptions = {
adaptiveConfig: {
highActivityThreshold: 15,
sustainedInactivityThresholdMs: 45 * 60 * 1000,
maxUserScaling: 1.5,
},
};
const adaptiveStore = new AdaptiveRateLimitStore(adaptiveOptions);
```
- **Cache**: O(n) where n is the number of cached entries
- **Dedupe**: O(m) where m is the number of active jobs
- **Rate Limit**: O(r × t) where r is resources and t is tracking window
- **Cache Get/Set**: O(1) average case
- **Dedupe Check**: O(1) lookup
- **Rate Limit Check**: O(log n) where n is requests in window
| Feature | Traditional Rate Limiting | Adaptive Rate Limiting |
| -------------------------- | ------------------------- | --------------------------- |
| **Configuration Required** | Yes (limits per resource) | No (intelligent defaults) |
| **API Utilization** | Fixed allocation | Dynamic (up to 100%) |
| **User Experience** | First-come-first-served | Priority during activity |
| **Background Processing** | Competes with users | Scales up when idle |
| **Activity Awareness** | No | Real-time monitoring |
| **Trend Detection** | No | Yes (increasing/decreasing) |
| **Night/Off-hours** | Wasted capacity | Full background utilization |
**When to use Traditional Rate Limiting:**
- Simple, predictable workloads
- No distinction between user/background requests
- Fixed capacity requirements
**When to use Adaptive Rate Limiting (Recommended):**
- Applications with varying user activity
- Mix of interactive and background operations
- Want to maximize API utilization
- Need responsive user experience
### In-Memory vs SQLite Stores
| Feature | In-Memory | SQLite |
| ----------------- | ----------- | -------------------- |
| **Performance** | Fastest | Fast |
| **Persistence** | No | Yes |
| **Memory Usage** | Higher | Lower |
| **Cross-Process** | No | Yes |
| **Setup** | Zero config | Requires file system |
Choose in-memory stores for:
- Single-process applications
- Maximum performance requirements
- Development and testing
- Stateless deployments
Choose SQLite stores for:
- Multi-process applications
- Persistent caching across restarts
- Production deployments
- Resource-constrained environments
## API Reference
All stores implement the same interfaces as the SQLite versions:
- `CacheStore`
- `DedupeStore`
- `RateLimitStore`
## License
MIT