Skip to content

ADR-005: Durable Objects for State Management

Status: Accepted
Date: 2026-02-01
Deciders: Abdisamed Mohamed
Related ADRs: ADR-001 (Single DO Class), ADR-002 (Edge-First Architecture)

Context

CepatEdge requires persistent state management at the edge for:

  • User sessions (authentication state)
  • Data caching (frequent query results)
  • Usage metrics (API call tracking)

Traditional approaches include:

  • External databases (latency, cost)
  • Cloudflare KV (no transactions, eventual consistency)
  • In-memory caching (not persistent, single-instance)

Durable Objects provide persistent, transactional storage at the edge with strong consistency guarantees.

Decision

Use Durable Objects for all edge state management with a single DO class handling multiple namespaces.

Implementation

DO Class Architecture

typescript
export class CepatEdgeCache {
  private state: DurableObjectState;

  async fetch(request: Request) {
    const url = new URL(request.url);
    const pathParts = url.pathname.split('/').filter(Boolean);

    // Route by namespace
    if (pathParts[0] === 'sessions') {
      return this.handleSessionOperations(request, pathParts.slice(1));
    } else if (pathParts[0] === 'cache') {
      return this.handleCacheOperations(request, pathParts.slice(1));
    } else if (pathParts[0] === 'usage') {
      return this.handleUsageOperations(request, pathParts.slice(1));
    }
  }

  // Session management methods
  async handleSessionOperations(request: Request, path: string[]) {
    const sessionId = path[0];
    // Session CRUD operations
  }

  // Cache management methods
  async handleCacheOperations(request: Request, path: string[]) {
    const cacheKey = path[0];
    // Cache operations with TTL
  }

  // Usage tracking methods
  async handleUsageOperations(request: Request, path: string[]) {
    // Metrics collection and aggregation
  }
}

Namespace Strategy

typescript
// Different DO instances for different data types
const sessions = env.CEPATEDGE_CACHE.get(env.CEPATEDGE_CACHE.idFromName('sessions'));
const userCache = env.CEPATEDGE_CACHE.get(env.CEPATEDGE_CACHE.idFromName('cache:users'));
const apiUsage = env.CEPATEDGE_CACHE.get(env.CEPATEDGE_CACHE.idFromName('usage:api'));

// Usage in services
await sessions.fetch('/sessions/user-123', { method: 'GET' });
await userCache.fetch('/cache/profile-456', { method: 'PUT', body: data });
await apiUsage.fetch('/usage', { method: 'POST', body: metrics });

Data Persistence

  • Automatic Persistence: DO state survives worker restarts
  • Transactional: All operations are ACID compliant
  • Consistent: Strong consistency across all operations
  • Durable: Data survives infrastructure failures

Consequences

Positive

  • Edge Performance: Data stored and processed at the edge with multiple instances
  • Strong Consistency: ACID transactions for critical operations
  • Faster Reads: Optimized for cache hits with timestamp and index-based instances
  • Automatic Scaling: DOs scale automatically with usage
  • Developer Experience: Familiar async/await API
  • Cost Effective: Included in Workers free tier

Negative

  • Storage Limits: 1GB per DO instance
  • Complexity: Additional abstraction layer
  • Cold Starts: Initial requests may have higher latency
  • Debugging: Distributed state harder to inspect

Mitigation

  • Intelligent Sharding: Multiple DO instances for different data types
  • Efficient Storage: Compact data structures, automatic cleanup
  • Caching Strategy: Warm DOs with frequently accessed data
  • Monitoring: Built-in usage tracking and performance metrics

Performance Characteristics

Latency

  • Hot DO: <10ms response time
  • Warm DO: <50ms response time
  • Cold DO: <200ms response time (first request)

Throughput

  • Read Operations: 1000+ ops/second per DO
  • Write Operations: 500+ ops/second per DO
  • Concurrent Connections: 1000+ simultaneous connections

Scalability

  • Horizontal Scaling: Automatic across Cloudflare network
  • Storage Scaling: Multiple DO instances for different data
  • Global Distribution: Data available at all edge locations

Alternatives Considered

Cloudflare KV

  • Pros: Simple API, high throughput, global consistency
  • Cons: Eventual consistency, no transactions, higher latency

External Database (Neon, PlanetScale)

  • Pros: Familiar SQL interface, advanced querying
  • Cons: Network latency, additional cost, connection limits

Redis/Upstash

  • Pros: High performance, rich data structures
  • Cons: Additional cost (~$10/month), external dependency

In-Memory Caching

  • Pros: Fastest possible performance
  • Cons: Not persistent, single-instance, data loss on restarts

Monitoring & Observability

Built-in Metrics

typescript
// Usage tracking in DO
async getUsageStats() {
  const logs = await this.state.storage.get('usage:logs');
  return this.aggregateMetrics(logs);
}

// Cache performance metrics
async getCacheStats() {
  const keys = await this.state.storage.list({ prefix: 'cache:' });
  return this.calculateHitRates(keys);
}

Operational Monitoring

  • Usage Patterns: Track which data is frequently accessed
  • Performance Metrics: Response times, error rates
  • Storage Usage: Monitor storage consumption
  • Cache Hit Rates: Optimize caching strategies

References

  • Cloudflare Durable Objects Documentation
  • CepatEdge Performance Benchmarks
  • Edge Computing Patterns
  • Distributed Systems Best Practices