Understanding SonicJS Three-Tiered Caching Strategy
Learn how SonicJS implements a three-tiered caching strategy using memory, Cloudflare KV, and D1 to deliver sub-15ms response times globally.

Understanding SonicJS Three-Tiered Caching Strategy
TL;DR β SonicJS uses a three-tiered caching system: Memory (sub-1ms), KV (5-15ms), and D1 (20-50ms). Content is checked in order from fastest to slowest, with automatic population of faster tiers on cache misses.
Key Stats:
- Memory cache: Under 1ms access time
- KV cache: 5-15ms globally distributed
- 90-95% cache hit rate for well-configured sites
- Only 5-10% of requests hit the D1 database
Performance is at the heart of SonicJS. To deliver consistent sub-15ms response times globally, SonicJS implements a sophisticated three-tiered caching strategy. This guide explains how it works and how to configure it for your needs.
The Three Tiers
SonicJS caching operates across three layers:
Request β Memory Cache β KV Cache β D1 Database
β β β
<1ms 5-15ms 20-50ms
Tier 1: Memory Cache
The fastest layer, storing frequently accessed content in V8 isolate memory.
Characteristics:
- Speed: Sub-millisecond access
- Scope: Per-isolate (not shared across data centers)
- Lifetime: Duration of the isolate (typically minutes)
- Size: Limited by isolate memory
Tier 2: KV Cache
Cloudflare KV provides globally distributed, persistent caching.
Characteristics:
- Speed: 5-15ms globally
- Scope: Shared across all edge locations
- Lifetime: Configurable TTL
- Size: Up to 25MB per value
Tier 3: D1 Database
The source of truth, providing SQLite at the edge.
Characteristics:
- Speed: 20-50ms
- Scope: Replicated globally
- Lifetime: Persistent
- Size: 2GB+ per database
How Caching Works
Read Path
When a request comes in:
async function getContent(key: string) {
// 1. Check memory cache first
const memoryResult = memoryCache.get(key)
if (memoryResult) {
return memoryResult // <1ms
}
// 2. Check KV cache
const kvResult = await env.CACHE.get(key, 'json')
if (kvResult) {
memoryCache.set(key, kvResult) // Populate memory
return kvResult // 5-15ms
}
// 3. Query D1 database
const dbResult = await db.query(...)
if (dbResult) {
await env.CACHE.put(key, JSON.stringify(dbResult), { ttl })
memoryCache.set(key, dbResult)
return dbResult // 20-50ms
}
return null
}
Write Path
When content is updated:
async function updateContent(key: string, data: any) {
// 1. Update D1 database
await db.update(...)
// 2. Invalidate KV cache
await env.CACHE.delete(key)
// 3. Memory cache expires naturally
// (or explicitly invalidated if supported)
}
Configuration Options
Basic Setup
import { createSonicJS } from '@sonicjs-cms/core'
import { cachePlugin } from '@sonicjs-cms/core/plugins'
const cms = createSonicJS({
plugins: [
cachePlugin({
defaultTTL: 3600, // 1 hour default
}),
],
})
Route-Specific TTLs
cachePlugin({
defaultTTL: 3600,
patterns: {
// Frequently updated content
'/api/content/posts': { ttl: 60 },
// Rarely changed content
'/api/content/categories': { ttl: 86400 },
// Individual posts can cache longer
'/api/content/posts/*': { ttl: 3600 },
// Never cache user-specific data
'/api/users/*': { ttl: 0 },
},
})
Cache Headers
SonicJS automatically sets appropriate cache headers:
cachePlugin({
headers: {
'Cache-Control': 'public, max-age=3600, s-maxage=86400',
'CDN-Cache-Control': 'max-age=86400',
},
})
Cache Invalidation Strategies
Time-Based (TTL)
The simplest approach - content expires after a set time:
{ ttl: 300 } // Cache for 5 minutes
Tag-Based
Invalidate groups of related content:
// When saving a post
await cache.tag(postId, ['posts', `category:${categoryId}`])
// Invalidate all posts in a category
await cache.invalidateTag(`category:${categoryId}`)
Event-Based
Invalidate on specific events:
cms.on('content:afterSave', async (content) => {
await cache.invalidate(`posts:${content.id}`)
await cache.invalidate('posts:list')
})
Performance Benchmarks
Real-world performance across cache tiers:
| Scenario | Memory Hit | KV Hit | D1 Query |
|---|---|---|---|
| Simple content | 0.5ms | 8ms | 25ms |
| Complex query | 1ms | 12ms | 45ms |
| With relations | 1.5ms | 15ms | 60ms |
Cache Hit Rates
Typical hit rates for well-configured caching:
- Memory: 60-80% for hot content
- KV: 90-95% for all cached content
- D1: Only ~5-10% of requests
Best Practices
1. Set Appropriate TTLs
// Static content: Long TTL
'/api/content/pages': { ttl: 86400 }, // 24 hours
// Dynamic listings: Short TTL
'/api/content/posts': { ttl: 60 }, // 1 minute
// Real-time data: No caching
'/api/analytics': { ttl: 0 },
2. Use Stale-While-Revalidate
Serve stale content while refreshing in the background:
cachePlugin({
staleWhileRevalidate: true,
staleTime: 60, // Serve stale for up to 60s while revalidating
})
3. Warm Critical Caches
Pre-populate caches for critical content:
// Warm cache on deploy
async function warmCache() {
const posts = await db.getAllPublishedPosts()
for (const post of posts) {
await cache.set(`post:${post.slug}`, post)
}
}
4. Monitor Cache Performance
Track cache effectiveness:
cachePlugin({
metrics: true,
onCacheHit: (key, tier) => {
console.log(`Cache hit: ${key} from ${tier}`)
},
onCacheMiss: (key) => {
console.log(`Cache miss: ${key}`)
},
})
Debugging Cache Issues
Check Cache Status
API responses include cache metadata:
{
"data": [...],
"meta": {
"cache": {
"hit": true,
"source": "kv",
"age": 45,
"ttl": 3600
}
}
}
Force Cache Bypass
For debugging, bypass the cache:
curl -H "Cache-Control: no-cache" https://api.example.com/content/posts
View Cache Keys
List cached keys:
wrangler kv:key list --namespace-id YOUR_KV_ID
Advanced Patterns
Regional Caching
Adjust TTLs based on region:
cachePlugin({
ttlByRegion: {
'North America': 3600,
'Europe': 3600,
'Asia': 1800, // Lower TTL for faster propagation
},
})
Content-Aware Caching
Cache based on content type:
cms.on('content:beforeCache', (content, options) => {
if (content.type === 'breaking-news') {
options.ttl = 30 // Very short TTL
}
})
Key Takeaways
- Three-tiered caching provides sub-15ms response times
- Memory cache is fastest but per-isolate
- KV cache provides global persistence
- D1 is the source of truth, accessed only on cache miss
- Configure TTLs based on content update frequency
- Use tag-based invalidation for related content
- Monitor cache hit rates for optimization
Related Resources
Optimize your SonicJS deployment with intelligent caching!
Related Articles

Why Edge-First CMS is the Future of Content Management
Discover why edge-first content management systems like SonicJS are revolutionizing how we build and deliver digital experiences with unprecedented speed and reliability.

Best Open Source Project to Practice AI Coding Tools (Claude Code, Cursor, Copilot)
Looking for an open source project to practice AI coding tools like Claude Code, Cursor, or GitHub Copilot? SonicJS is the perfect TypeScript codebase for learning AI-assisted development.

How to Use SonicJS with Astro: Complete Integration Guide
Learn how to integrate SonicJS headless CMS with Astro for blazing-fast static and server-rendered websites. Step-by-step guide covering setup, content fetching, dynamic routing, and deployment.