| name | caching-patterns |
| summary | Couchbase as a cache โ TTL/expiry, cache-aside pattern, write-through, get_and_touch, ephemeral buckets, session storage, rate limiting with atomic counters, replacing Redis/Memcached with Couchbase |
| description | Couchbase as a cache โ TTL/expiry, cache-aside pattern, write-through, get_and_touch, ephemeral buckets, session storage, rate limiting with atomic counters, replacing Redis/Memcached with Couchbase |
| compatibility | Language-agnostic concept skill. Code examples use Node.js, Python, and Java for illustration. |
| metadata | {"last_verified":"2026-05","min_server_version":"5.0","handoff":[{"condition":"user asks about CAS, bulk ops, sub-document, or SDK patterns","type":"variant","skill":"sdk-patterns-nodejs"},{"condition":"user asks about data modeling for cached data","skill":"server-data-modeling"},{"condition":"user asks about Couchbase fundamentals or core concepts","skill":"getting-started"}]} |
Caching Patterns
Couchbase works as a cache natively โ every document can have a TTL, and the KV API is sub-millisecond. No separate Redis instance needed.
TTL / Expiry
Set expiry on any KV operation. The document is automatically deleted when it expires.
await collection.upsert('session::abc123', { userId: 'alice', cart: [] },
{ expiry: 3600 }
);
await collection.insert('rate::ip::1.2.3.4', { count: 0 },
{ expiry: 60 }
);
from datetime import timedelta
collection.upsert('session::abc123', {'userId': 'alice'},
UpsertOptions(expiry=timedelta(hours=1)))
collection.upsert("session::abc123", JsonObject.create().put("userId", "alice"),
UpsertOptions.upsertOptions().expiry(Duration.ofHours(1)));
Cache-aside (lazy loading)
The most common pattern: read from cache, fall back to source of truth on miss, populate cache.
async function getUser(userId) {
const cacheKey = `user::${userId}`;
try {
const cached = await collection.get(cacheKey);
return cached.content;
} catch (e) {
if (!(e instanceof couchbase.DocumentNotFoundError)) throw e;
}
const user = await db.users.findById(userId);
if (!user) return null;
await collection.upsert(cacheKey, user, { expiry: 300 });
return user;
}
Write-through
Write to cache and source of truth together. Cache is always warm.
async function updateUser(userId, updates) {
const user = await db.users.update(userId, updates);
await collection.upsert(`user::${userId}`, user, { expiry: 300 });
return user;
}
async function deleteUser(userId) {
await db.users.delete(userId);
try {
await collection.remove(`user::${userId}`);
} catch (e) {
if (!(e instanceof couchbase.DocumentNotFoundError)) throw e;
}
}
get_and_touch โ reset TTL on read
Extend a document's TTL each time it's accessed (sliding expiry โ useful for sessions).
const result = await collection.getAndTouch('session::abc123', 3600);
const session = result.content;
result = collection.get_and_touch('session::abc123', timedelta(hours=1))
session = result.content_as[dict]
GetResult result = collection.getAndTouch("session::abc123", Duration.ofHours(1));
Session storage
async function createSession(userId) {
const sessionId = crypto.randomUUID();
await collection.insert(`session::${sessionId}`, {
userId,
createdAt: Date.now(),
data: {}
}, { expiry: 86400 });
return sessionId;
}
async function getSession(sessionId) {
try {
const result = await collection.getAndTouch(
`session::${sessionId}`,
86400
);
return result.content;
} catch (e) {
if (e instanceof couchbase.DocumentNotFoundError) return null;
throw e;
}
}
async function destroySession(sessionId) {
try {
await collection.remove(`session::${sessionId}`);
} catch (e) {
if (!(e instanceof couchbase.DocumentNotFoundError)) throw e;
}
}
Rate limiting with atomic counters
async function checkRateLimit(ip, limitPerMinute = 100) {
const key = `rate::${ip}::${Math.floor(Date.now() / 60000)}`;
try {
const result = await collection.binary.increment(key, {
delta: 1,
initial: 1,
expiry: 60,
});
return result.value <= limitPerMinute;
} catch (e) {
console.error('Rate limit check failed:', e);
return true;
}
}
Ephemeral buckets
For pure caching workloads (no persistence needed), use an Ephemeral bucket:
- Data lives in RAM only โ no disk I/O
- Faster than Couchbase buckets for cache-only use cases
- Eviction policies:
noEviction (reject writes when full) or nruEviction (evict least-recently-used)
Create via UI: Buckets โ Add Bucket โ Bucket Type: Ephemeral
Or via REST:
curl -u Administrator:"$CB_ADMIN_PASSWORD" \
-X POST http://localhost:8091/pools/default/buckets \
-d name=cache \
-d bucketType=ephemeral \
-d ramQuota=512 \
-d evictionPolicy=nruEviction
Connect to an ephemeral bucket the same way as a regular bucket โ the SDK API is identical.
Key design for cache entries
session::{session-id} # user sessions
user::{user-id}:profile # cached user profiles
product::{sku}:detail # product detail pages
rate::{ip}::{minute-bucket} # rate limit counters
lock::{resource-id} # distributed locks (use getAndLock)
Couchbase vs Redis
| Feature | Redis | Couchbase |
|---|
| TTL per key | โ
| โ
|
| Atomic increment | โ
| โ
|
| Sub-ms KV | โ
| โ
|
| Sliding expiry | โ
GETEX | โ
getAndTouch |
| Persistence | Optional | Built-in |
| SQL queries over cache | โ | โ
SQL++ |
| Full-text / vector search | โ | โ
|
| Transactions | Limited | โ
ACID |
| Cluster replication | โ
| โ
|
| Ephemeral (RAM-only) mode | โ
| โ
Ephemeral bucket |