eventing
Couchbase Eventing Service — write, deploy, and troubleshoot Eventing Functions that react to document mutations in real time
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Couchbase Eventing Service — write, deploy, and troubleshoot Eventing Functions that react to document mutations in real time
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | eventing |
| summary | Couchbase Eventing Service — write, deploy, and troubleshoot Eventing Functions that react to document mutations in real time |
| description | Couchbase Eventing Service — write, deploy, and troubleshoot Eventing Functions that react to document mutations in real time |
| compatibility | Requires Couchbase Server 6.5+ with Eventing Service enabled. Functions are JavaScript (ES5 subset via V8). |
| metadata | {"last_verified":"2026-05","min_server_version":"6.5","handoff":[{"condition":"user asks about SQL++ queries","type":"variant","skill":"server-querying-python"},{"condition":"user asks about streaming to Kafka","skill":"kafka"},{"condition":"user asks about Couchbase fundamentals or core concepts","skill":"getting-started"},{"condition":"user asks about atomic multi-document operations or whether to use Eventing vs Transactions","type":"variant","skill":"transactions-python"},{"condition":"user asks about full-text search or vector search from Eventing Functions","type":"variant","skill":"search-python"}]} |
JavaScript functions that trigger on document mutations — create, update, delete, and expiry.
Template: templates/eventing-function.js — copy this skeleton as a starting point for new Eventing Functions.
function OnUpdate(doc, meta) {
// doc — document body; meta.id, meta.cas, meta.expiration
if (doc.type !== "order") return;
dst[meta.id] = Object.assign({}, doc, { processed: true });
}
function OnDelete(meta, options) {
// options.is_expiry — true if deleted by TTL
delete dst[meta.id];
}
Bind collections to global variables in function settings. Bound collections act as maps:
dst[meta.id] = { ...doc, enriched: true }; // upsert
var existing = dst[meta.id]; // get
delete dst["stale_" + meta.id]; // delete
Alias modes: read-only (GET only) or read+write (GET, SET, DELETE).
var customerId = doc.customerId;
var rows = SELECT name, email FROM `myapp`._default.customers WHERE id = $customerId;
for (var row of rows) {
dst[meta.id] = Object.assign({}, doc, { customerName: row.name });
break;
}
rows.close(); // always close — releases query resources
Rules: use $varName for substitution (assign meta.id to a local var first); always call rows.close(); use /* */ not // in multi-line statements; DML cannot target the source collection.
function OnUpdate(doc, meta) {
if (doc.enriched) return;
var pid = doc.productId;
var rows = SELECT name, category FROM `myapp`.catalog.products WHERE META().id = $pid;
for (var p of rows) { dst[meta.id] = Object.assign({}, doc, { productName: p.name, enriched: true }); break; }
rows.close();
}
function OnDelete(meta, options) {
var userId = meta.id;
var rows = SELECT META().id AS oid FROM `myapp`._default.orders WHERE userId = $userId;
for (var row of rows) { delete orders[row.oid]; }
rows.close();
}
function OnUpdate(doc, meta) {
if (doc.type !== "subscription") return;
createTimer(onExpiry, new Date(doc.expiresAt), meta.id, { userId: doc.userId });
}
function onExpiry(ctx) {
notifications[ctx.userId] = { type: "expired", at: new Date().toISOString() };
}
function OnDelete(meta, options) {
cancelTimer(onExpiry, meta.id); // cancel if subscription deleted
}
createTimer(callback, fireAt, reference, context) — reference is unique per function+callback; calling again with the same reference replaces the timer. Timers fire at-least-once — make callbacks idempotent.
function OnUpdate(doc, meta) {
if (doc.status !== "shipped") return;
var r = curl("POST", "https://hooks.example.com/shipped", {
headers: { "Content-Type": "application/json", "Authorization": "Bearer " + TOKEN },
body: JSON.stringify({ orderId: meta.id }),
timeout: 5000
});
if (r.status !== 200) log("Webhook failed:", r.status);
}
curl(method, url, {headers, body, timeout, encoding}) → {status, headers, body}. Use Constant bindings for secrets — never hardcode tokens.
Use when bindings use wildcard scope/collection, or when you need CAS/expiry:
var result = couchbase.get(src, meta);
if (result.success) log("CAS:", result.meta.cas);
couchbase.upsert(dst, { id: meta.id }, doc);
couchbase.replace(dst, { id: meta.id, cas: result.meta.cas }, doc);
couchbase.insert(dst, { id: "new_key" }, { field: "value" });
couchbase.delete(dst, { id: meta.id });
Meta argument: id (required), cas (optimistic locking), expiry (TTL seconds).
couchbase.query() is the preferred way to run parameterized SQL++ queries from Eventing when you need explicit control over query options (consistency, timeout, named parameters). It is cleaner than inline SQL++ for complex or reusable queries.
function OnUpdate(doc, meta) {
if (doc.type !== "order") return;
var customerId = doc.customerId;
var result = couchbase.query(
"SELECT name, email, tier FROM `myapp`._default.customers WHERE META().id = $cid",
{ namedParams: { cid: customerId }, consistency: "request_plus" }
);
if (result.success) {
var rows = result.results;
if (rows.length > 0) {
dst[meta.id] = Object.assign({}, doc, {
customerName: rows[0].name,
customerTier: rows[0].tier,
enriched: true
});
}
} else {
log("query failed:", result.error);
}
}
Options object fields:
namedParams — object of $name: value substitutionspositionalParams — array for $1, $2 positional parametersconsistency — "none" (default, fastest) or "request_plus" (read-your-writes)timeout — milliseconds (default: function timeout)Return value: { success: bool, results: [...rows], error: string }
vs inline SQL++:
var rows = SELECT ...) is simpler for one-off queries but has no error handling and limited option controlcouchbase.query() returns a result object you can inspect for errors and is easier to testasync/awaitvar rows = couchbase.analyticsQuery(
"SELECT * FROM `myapp`._default.orders WHERE status = $status LIMIT $limit;",
{ status: doc.filterStatus, limit: 10 }
);
var results = [];
for (var row of rows) results.push(row);
dst[meta.id] = Object.assign({}, doc, { results: results });
Use instead of inline SQL++ for heavy aggregations or Analytics dataset queries.
When a function writes to a collection it also reads from, use crc64 to skip unchanged documents:
function OnUpdate(doc, meta) {
if (meta.id.startsWith("_sync")) return;
var curr = crc64(doc);
if (checksums[meta.id] === curr) return;
checksums[meta.id] = curr;
dst[meta.id] = processDoc(doc);
}
| Supported | Not Supported |
|---|---|
| ES5 JavaScript (V8) | Global variables (no state between calls) |
Inline SQL++, curl(), Timers | async/await, Promises |
log(), crc64() | setTimeout, setInterval, require() |
No global state — persist all state to a bound collection.
Feed boundary controls which mutations trigger the function:
Everything — existing documents + new mutations (backfill)From now — only new mutations after deploymentcurl -X POST http://localhost:8096/api/v1/functions/my-function/deploy -u Administrator:"$CB_ADMIN_PASSWORD"
curl -X POST http://localhost:8096/api/v1/functions/my-function/undeploy -u Administrator:"$CB_ADMIN_PASSWORD"
Not triggering: verify source collection and that the function is deployed (not just saved). From now won't process existing documents.
Resource exhaustion: missing rows.close(). Always close result sets, even in error paths.
Double mutations: use crc64 checksums or a processed flag.
Slow function: inline SQL++ without an index causes full scans per mutation. Add a GSI.
Logs: Eventing UI → function → Logs, or GET http://localhost:8096/api/v1/functions/my-function/applog.
Couchbase SDK error handling — UnambiguousTimeoutException vs AmbiguousTimeoutException, DocumentNotFoundException, CasMismatchException, DurabilityImpossibleException, SDK debug logging, sdk-doctor connectivity diagnostics, retryable vs non-retryable errors, circuit breaker, error best practices
Couchbase SDK patterns for Rust — CAS optimistic locking, retry on CasMismatch, tokio bulk operations with join_all/FuturesUnordered, atomic counters, sub-document (MutateInSpec/LookupInSpec), array operations, exists, replica reads, touch/getAndTouch, preserve_expiry, error handling
Configure and optimize Couchbase SDK connections in Rust — async/tokio cluster setup, singleton pattern, wait_until_ready, timeouts, KV CRUD (upsert/insert/get/replace/remove), expiry, durability, sub-document operations
SQL++ queries with the Couchbase Rust SDK — scope.query, cluster.query, positional and named parameters, scan consistency, deserializing rows into structs, DML (INSERT/UPDATE/DELETE/UPSERT), MutationState RYOW
Testing Couchbase Rust applications — unit testing with mockall, integration testing with testcontainers-rs, scope/collection isolation
Distributed ACID transactions are not supported by the Couchbase Rust SDK 1.x — alternatives and workarounds