Skip to Content
AdvancedCaching Adapter

Caching Adapter

Add intelligent caching to your sync server for 10-100x performance improvement.

Overview

The Caching Adapter sits between your application and database adapter, transparently caching queries and invalidating on writes.

Quick Start

import { SyncServer } from '@gluonic/server' import { PrismaAdapter } from '@gluonic/server-prisma' import { CachingAdapter, RedisCache } from '@gluonic/caching-redis' // 1. Create database adapter const dbAdapter = PrismaAdapter({ prisma }) // 2. Create cache const cache = RedisCache({ url: 'redis://localhost:6379' }) // 3. Wrap with caching const database = CachingAdapter({ adapter: dbAdapter, cache, strategies: { bootstrap: { ttl: 600 }, // Cache 10 minutes records: { ttl: 60 }, // Cache 1 minute syncActions: { ttl: 30 } // Cache 30 seconds } }) // 4. Use cached adapter const server = SyncServer({ database, // Caching transparent! ✓ auth })

Cache Implementations

Redis Cache (Production)

import { createRedisCache } from '@gluonic/server/adapters' const cache = createRedisCache('redis://localhost:6379', { keyPrefix: 'gluonic:', maxRetries: 3, retryDelay: 100 })

Memory Cache (Development)

import { createMemoryCache } from '@gluonic/server/adapters' const cache = createMemoryCache({ maxSize: 1000, // Max entries ttl: 300 // Default TTL (seconds) })

No-Op Cache (Testing)

import { createNoOpCache } from '@gluonic/server/adapters' const cache = createNoOpCache() // Pass-through, no caching

Caching Strategies

Bootstrap Caching

strategies: { bootstrap: { ttl: 600, // 10 minutes keyPattern: 'bootstrap:{orgId}', invalidateOn: ['mutation'] // Clear on any mutation } }

Record Caching

strategies: { records: { ttl: 60, // 1 minute keyPattern: '{model}:{id}', invalidateOn: ['update', 'delete'] } }

Query Caching

strategies: { queries: { ttl: 30, // 30 seconds keyPattern: 'query:{model}:{hash}', invalidateOn: ['mutation'] } }

Invalidation

Automatic Invalidation

// When mutation happens: await adapter.applyMutations(orgId, userId, [ { type: 'post', id: '123', op: 'u', patch: { title: 'New' } } ]) // Adapter automatically invalidates: cache.invalidate('post:123') // Record cache cache.invalidate('bootstrap:*') // Bootstrap caches cache.invalidate('query:post:*') // Post queries // Next request gets fresh data ✓

Manual Invalidation

// Clear specific key await cache.invalidate('post:123') // Clear pattern await cache.invalidate('post:*') // Clear all await cache.invalidateAll()

Performance Impact

Without Caching

GET /sync/v1/bootstrap → Query database (1200ms) → Serialize (100ms) → Total: 1300ms 😔

With Caching

GET /sync/v1/bootstrap (first) → Query database (1200ms) → Serialize (100ms) → Cache result → Total: 1300ms GET /sync/v1/bootstrap (cached) → Read from Redis (15ms) → Total: 15ms ✓ Improvement: 86x faster! 🎉

Configuration

Cache Interface

interface CachingAdapter { get(key: string): Promise<any | null> set(key: string, value: any, ttl?: number): Promise<void> invalidate(pattern: string): Promise<void> wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T> }

Wrap Helper

// Automatic caching with wrap() const data = await cache.wrap( 'bootstrap:org-123', async () => { // Only called if cache miss return await adapter.bootstrap('org-123') }, 600 // TTL: 10 minutes )

Monitoring

Cache Metrics

const metrics = await cache.getMetrics() console.log({ hits: metrics.hits, // Cache hits misses: metrics.misses, // Cache misses hitRate: metrics.hitRate, // Hit rate % evictions: metrics.evictions // Evicted entries })

Performance Tracking

const cacheAdapter = createCachingAdapter({ adapter: dbAdapter, cache, onCacheHit: (key, duration) => { console.log(`Cache hit: ${key} (${duration}ms saved)`) }, onCacheMiss: (key) => { console.log(`Cache miss: ${key}`) } })

Best Practices

DO ✅

  • Cache expensive queries (bootstrap, batch fetch)
  • Use short TTLs for frequently changing data
  • Use longer TTLs for stable data
  • Monitor cache hit rates
  • Invalidate on mutations

DON’T ❌

  • Cache with infinite TTL
  • Cache sensitive data without encryption
  • Over-cache (wastes memory)
  • Forget to invalidate on writes

Next Steps

Last updated on