Skip to content

Latest commit

 

History

History
183 lines (132 loc) · 4.83 KB

File metadata and controls

183 lines (132 loc) · 4.83 KB
title ICacheService Contract
description Reference for the Cache Service contract — key-value caching with TTL support

ICacheService Contract

The Cache Service provides a key-value caching layer used throughout ObjectStack for metadata caching, query result caching, session storage, and rate limiting. Implementations can range from in-memory Maps to Redis clusters.

**Source:** `packages/spec/src/contracts/cache-service.ts` **Service name:** `cache` — resolve with `kernel.getService('cache')`

Interface Definition

export interface ICacheService {
  // Core Operations
  get<T = unknown>(key: string): Promise<T | undefined>;
  set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>;
  delete(key: string): Promise<boolean>;
  has(key: string): Promise<boolean>;
  clear(): Promise<void>;

  // Observability
  stats(): Promise<CacheStats>;
}

CacheStats is returned by stats() for monitoring:

export interface CacheStats {
  hits: number;          // Total number of cache hits
  misses: number;        // Total number of cache misses
  keyCount: number;      // Number of keys currently stored
  memoryUsage?: number;  // Memory usage in bytes (if available)
}

Core Operations

get

Retrieves a cached value by key. Returns undefined if the key does not exist or has expired.

const user = await cacheService.get<AuthUser>('user:usr_01HQ3V5K8N');
if (user) {
  console.log(user.name); // "Jane Smith"
}

set

Stores a value with an optional TTL (time-to-live), passed as a positional argument in seconds.

// Cache for 5 minutes
await cacheService.set('user:usr_01HQ3V5K8N', userData, 300);

// Cache indefinitely (omit the ttl argument)
await cacheService.set('config:feature_flags', flags);

delete

Removes a key from the cache. Returns true if the key existed.

const deleted = await cacheService.delete('user:usr_01HQ3V5K8N');
// true if the key was found and removed

has

Checks if a key exists without retrieving the value.

const exists = await cacheService.has('user:usr_01HQ3V5K8N');
// true or false

clear

Removes all entries from the cache. clear() takes no arguments — it flushes the entire store.

// Clear every key in the cache
await cacheService.clear();
The contract has no pattern-based clearing, TTL inspection, bulk, atomic, or namespace operations. To scope keys, prefix them yourself (e.g. `meta:object:task`) and track which keys to invalidate at the call site.

Observability

stats

Returns counters for monitoring cache effectiveness.

const { hits, misses, keyCount } = await cacheService.stats();
const hitRate = hits / (hits + misses || 1);
console.log(`Cache hit rate: ${(hitRate * 100).toFixed(1)}% (${keyCount} keys)`);

Common Cache Patterns

Cache-Aside (Lazy Loading)

async function getObjectDefinition(name: string): Promise<ObjectDefinition> {
  const cacheKey = `meta:object:${name}`;

  // Try cache first
  const cached = await cacheService.get<ObjectDefinition>(cacheKey);
  if (cached) return cached;

  // Load from source
  const definition = await metadataService.getObject(name);
  if (!definition) throw new Error(`Object not found: ${name}`);

  // Store in cache
  await cacheService.set(cacheKey, definition, 3600);

  return definition;
}

Write-Through

async function updateObjectDefinition(
  name: string,
  changes: Partial<ObjectDefinition>
): Promise<ObjectDefinition> {
  // Update source of truth — register() saves the full definition
  const current = await metadataService.getObject(name);
  const updated = { ...(current as ObjectDefinition), ...changes };
  await metadataService.register('object', name, updated);

  // Update cache immediately
  await cacheService.set(`meta:object:${name}`, updated, 3600);

  return updated;
}

Cache Invalidation on Events

// watch() is optional on IMetadataService — check before subscribing
const handle = metadataService.watch?.('object', (event) => {
  // Invalidate the affected key when metadata changes.
  // The contract deletes individual keys — track related keys
  // (e.g. cached queries) yourself to invalidate them.
  cacheService.delete(`meta:object:${event.name}`);
});

Implementations

Two adapters ship in @objectstack/service-cache, both implementing ICacheService:

  • MemoryCacheAdapter — Map-backed in-process store with TTL expiry and optional LRU-style eviction (maxSize). Suitable for development, testing, and single-process deployments.
  • RedisCacheAdapter — a skeleton placeholder for future Redis integration; its methods currently throw RedisCacheAdapter not yet implemented.