Skip to content

Latest commit

 

History

History
228 lines (187 loc) · 6.48 KB

File metadata and controls

228 lines (187 loc) · 6.48 KB
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 min_server_version handoff
2026-05
5.0
condition type skill
user asks about CAS, bulk ops, sub-document, or SDK patterns
variant
sdk-patterns-nodejs
condition skill
user asks about data modeling for cached data
server-data-modeling
condition skill
user asks about Couchbase fundamentals or core concepts
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.

// Node.js — expiry in seconds
await collection.upsert('session::abc123', { userId: 'alice', cart: [] },
  { expiry: 3600 }  // 1 hour
);

// Insert with expiry (fails if key exists)
await collection.insert('rate::ip::1.2.3.4', { count: 0 },
  { expiry: 60 }  // 1 minute window
);
# Python
from datetime import timedelta
collection.upsert('session::abc123', {'userId': 'alice'},
    UpsertOptions(expiry=timedelta(hours=1)))
// Java
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}`;

  // 1. Try cache
  try {
    const cached = await collection.get(cacheKey);
    return cached.content;
  } catch (e) {
    if (!(e instanceof couchbase.DocumentNotFoundError)) throw e;
  }

  // 2. Cache miss — fetch from database
  const user = await db.users.findById(userId);
  if (!user) return null;

  // 3. Populate cache with TTL
  await collection.upsert(cacheKey, user, { expiry: 300 });  // 5 min
  return user;
}

Write-through

Write to cache and source of truth together. Cache is always warm.

async function updateUser(userId, updates) {
  // 1. Write to source of truth
  const user = await db.users.update(userId, updates);

  // 2. Update cache immediately
  await collection.upsert(`user::${userId}`, user, { expiry: 300 });

  return user;
}

async function deleteUser(userId) {
  await db.users.delete(userId);
  // Invalidate cache
  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).

// Node.js — getAndTouch resets TTL and returns content atomically
const result = await collection.getAndTouch('session::abc123', 3600);
const session = result.content;
# Python
result = collection.get_and_touch('session::abc123', timedelta(hours=1))
session = result.content_as[dict]
// Java
GetResult result = collection.getAndTouch("session::abc123", Duration.ofHours(1));

Session storage

// Create session
async function createSession(userId) {
  const sessionId = crypto.randomUUID();
  await collection.insert(`session::${sessionId}`, {
    userId,
    createdAt: Date.now(),
    data: {}
  }, { expiry: 86400 });  // 24 hours
  return sessionId;
}

// Read + extend session (sliding expiry)
async function getSession(sessionId) {
  try {
    const result = await collection.getAndTouch(
      `session::${sessionId}`,
      86400  // reset to 24h on each access
    );
    return result.content;
  } catch (e) {
    if (e instanceof couchbase.DocumentNotFoundError) return null;
    throw e;
  }
}

// Destroy session
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)}`;  // per-minute bucket

  try {
    const result = await collection.binary.increment(key, {
      delta: 1,
      initial: 1,
      expiry: 60,  // auto-expire after 1 minute
    });
    return result.value <= limitPerMinute;
  } catch (e) {
    // Fail open on errors — don't block legitimate traffic
    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