Couchbase Eventing doesn’t just react to data changes; it actively uses those changes as its primary trigger for executing application logic.
Let’s see it in action. Imagine we have a users bucket with documents like this:
{
"name": "Alice",
"email": "alice@example.com",
"status": "pending"
}
We want to automatically send a welcome email when a user’s status changes to "active". Here’s how Couchbase Eventing makes that happen.
First, we define a Function. This is the core of Eventing. A Function monitors a specific bucket (our users bucket) for document mutations. When a mutation occurs, it can decide whether to run its logic.
function OnUpdate(doc, meta) {
// Check if the status field was updated and is now "active"
if (doc.status === "active" && meta.new.status === "active" && meta.old.status !== "active") {
// Call an external service to send the email
// This is a placeholder for actual email sending logic
log("User " + meta.id + " activated. Sending welcome email.");
// Example: Call an external webhook or microservice
// sendEmailService.sendWelcome(doc.email);
}
}
The OnUpdate function is automatically invoked by the Eventing service when a document is created or updated in the users bucket. doc represents the current state of the document, and meta contains metadata about the change, including meta.new (the new state) and meta.old (the previous state). We specifically check if the status field changed to "active".
To make this work, we need to deploy this Function to the Couchbase Eventing service. This involves configuring the Eventing service itself, setting up the Function, and ensuring it has access to any external services it might need to call.
The Eventing service runs as a separate component within Couchbase. It maintains its own internal state, tracking which mutations it has processed and which Functions it needs to execute. When a document in the users bucket is modified, the Couchbase data service sends a notification to the Eventing service. The Eventing service then looks at its deployed Functions to see if any of them are configured to respond to changes in the users bucket. If a Function’s WHERE clause (or its internal logic, as in our OnUpdate example) matches the mutation, the Function’s code is executed.
The critical levers you control are:
- The Function Code: This is where you define the logic that runs in response to data changes. You can filter mutations based on document content, metadata, or even perform complex transformations.
- Mutation Type: Functions can be triggered by
INSERT,UPDATE, orDELETEoperations. - Bucket Scope: You specify which bucket(s) a Function monitors.
- External Integrations: Functions can call external HTTP endpoints, publish messages to Kafka, or interact with other services, enabling complex workflows.
What most people don’t immediately grasp is how Eventing guarantees exactly-once processing without relying on external transaction managers or complex retry mechanisms for basic use cases. When an Eventing Function successfully completes its execution (e.g., the OnUpdate function returns without errors), the Eventing service marks that specific mutation as processed for that Function. If the Eventing service crashes or restarts mid-execution, it will resume processing from the last successfully acknowledged mutation upon restart, ensuring no changes are lost and no logic is executed more than once for a given mutation.
The next step is often to trigger another data change based on your Eventing Function’s logic, creating a chain of reactions.