The issue: “Stateless” doesn’t imply “setup-free”
Couchbase Eventing is deliberately constructed like a short-running, stateless lambda: react to a mutation, do some work, exit. That mannequin is clear – till your eventing perform wants one-time housekeeping earlier than it might probably safely course of the primary mutation.
Traditionally, builders labored round this by doing a “first mutation warmup” inside OnUpdate, usually with CAS-safe writes or counters so just one thread would load a shared state. That strategy can work, nevertheless it forces you to rely on a mutation simply to determine stipulations, and it pushes setup considerations into the most popular a part of your code path: mutation processing.
That is precisely why there was a necessity for a mutationless initialization idea in Eventing – an specific setup part that may run with out an preliminary mutation and make the perform prepared earlier than any actual work begins.
The thought: a pre-flight guidelines
OnDeploy is a brand new Eventing handler that runs as soon as when an Eventing perform is:
- deployed, or
- resumed (after being paused)
And, importantly, it runs earlier than any mutations are processed.
When you consider your Eventing perform like an plane:
- OnUpdate / OnDelete are the flight operations.
- OnDeploy is the pre-flight test.
- If the pre-flight test fails, the airplane doesn’t go away the runway.
That “gatekeeper” conduct is the important thing: if OnDeploy fails, the perform reverts to the earlier state – defending you from “misconfigured logic goes reside” eventualities.
The way it works: OnDeploy(motion)
When you outline an OnDeploy handler in your eventing perform code, it will likely be invoked as soon as with an motion argument.
|
perform OnDeploy(motion) { // your setup code } |
The place:
- motion.cause is one among:
- “deploy”: a contemporary eventing perform deploy
- “resume”: resuming after a pause
- motion.delay is:
- 0 on “deploy”
- the efficient paused length (milliseconds) on “resume”
Two vital security rails
- Timeout: OnDeploy should end throughout the configured OnDeploy Timeout (default: 60 seconds). If it exceeds the timeout, the perform deployment shouldn’t be allowed to proceed.
- Fail-fast semantics: If OnDeploy throws an error or fails, no mutations are processed and the perform stays in its earlier state.
Why this issues
OnDeploy isn’t simply “good to have.” It’s a shift in how one can design Eventing capabilities:
- Correctness first: Guarantee stipulations exist earlier than any write logic runs.
- Much less boilerplate: Take away thread coordination hacks from OnUpdate.
- Quicker time-to-first-correct-mutation: Heat caches as soon as, not per thread.
| Facet | With out OnDeploy | With OnDeploy |
| Initialization | In OnUpdate (race-prone) | As soon as, earlier than mutations |
| Thread coordination | Required | Not wanted |
| Setup assure | Greatest effort | Fail-fast assure |
| Deployment security | Silent failures attainable | Blocked if setup fails |
Use instances
- You want a lookup desk (costs, trade charges, configs) earlier than any mutation is allowed to behave on incomplete knowledge.
- You need a recurring job (as soon as a day, as soon as an hour) however you don’t wish to “pretend” a mutation simply to begin a timer.
- You need the perform to refuse to run if stipulations are lacking – as a result of it’s higher to dam deployment than to silently produce mistaken writes.
Instance 1: Heat a lookup desk as soon as, then schedule a each day refresh
State of affairs
You enrich paperwork with end-of-day inventory costs. Costs are fetched each day from an exterior REST API, saved in a KV doc, and utilized by each mutation afterward.
With OnDeploy, you are able to do the warmup as soon as, assured, earlier than any mutation is processed.
Code
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// Timer Callback: refresh costs each day perform RefreshPricesCallback(context) { log(“Refreshing inventory costs from API”); var response = curl(“GET”, stock_api_binding, {path: ‘/api/eod-prices’}); cache_bucket[“lookup::eod_prices”] = response.physique;
// Re-schedule for tomorrow var tomorrow = new Date(); tomorrow.setSeconds(tomorrow.getSeconds() + 86400); // 24 hours createTimer(RefreshPricesCallback, tomorrow, “refresh_prices”, {}); }
// OnDeploy: initialize value cache as soon as perform OnDeploy(motion) { log(“OnDeploy: cause=” + motion.cause);
// Fetch preliminary costs from exterior API var response = curl(“GET”, stock_api_binding, {path: ‘/api/eod-prices’}); cache_bucket[“lookup::eod_prices”] = response.physique;
// Schedule each day refresh timer var tomorrow = new Date(); tomorrow.setSeconds(tomorrow.getSeconds() + 86400); createTimer(RefreshPricesCallback, tomorrow, “refresh_prices”, {});
log(“Worth cache initialized and refresh timer scheduled”); }
// OnUpdate: enrich paperwork with cached costs perform OnUpdate(doc, meta) { var costs = cache_bucket[“lookup::eod_prices”];
doc.stockPrice = costs[doc.symbol]; doc.enrichedAt = new Date().toISOString();
dst_bucket[meta.id] = doc; } |
The benefits with this logic:
- No “first mutation does setup” shock.
- No cross-thread complexity inside OnUpdate.
- Each mutation begins with the stipulations in place.
Instance 2: Deal with deploy and resume in a different way with OnDeploy
State of affairs
Typically the job isn’t to “schedule work” – it’s to make your perform secure to begin (or secure to restart).
For instance, your Eventing perform would possibly rely on:
- exterior knowledge (could be fetched by way of cURL bindings at first deployment), and
- configuration saved in Couchbase (that needs to be reloaded after a pause)
This can be a nice match for OnDeploy as a result of it enables you to department conduct primarily based on lifecycle and guarantee your perform is prepared earlier than processing any mutations.
Code
|
perform OnDeploy(motion) { log(‘OnDeploy triggered. Purpose:’, motion.cause); change (motion.cause) { case ‘deploy’: // First-time deployment: fetch preliminary knowledge wanted by the perform. log(‘Bootstrapping: Performing first-time setup…’); bootstrap_external_data(); break; case ‘resume’: // Perform was paused and resumed: refresh any cached settings. log(‘Configuration modified. Reloading settings…’); reload_config_from_bucket(); break; } } |
This sample retains lifecycle logic specific and clear:
- Deploy turns into the second you do one-time bootstrap work.
- Resume turns into the second you reconcile/refresh config after pausing (motion.delay tells you ways lengthy you had been paused).
- Your mutation handlers keep centered on enterprise logic as a result of they’ll assume that the stipulations are already happy.
Design steering: what not to do in OnDeploy
OnDeploy is highly effective, however it’s intentionally constrained:
- Preserve it quick and deterministic.
- Keep away from long-running loops that would delay deployment.
- Deal with it like infrastructure code: validate stipulations, initialize shared state, schedule work, then exit.
Conclusion
OnDeploy brings a elementary shift to Couchbase Eventing: the power to ensure stipulations are met earlier than a single mutation is processed. As a substitute of scattering initialization logic throughout mutation handlers or counting on race-prone warmup hacks, you now have a single, specific pre-flight test that runs as soon as and fails quick if something goes mistaken.
The consequence? Cleaner code, safer deployments, and confidence that your Eventing capabilities begin in a known-good state each time.
Able to strive it? OnDeploy is accessible from Couchbase Server 8.0 and above. Try the documentation beneath to get began, and take into account which of your present Eventing capabilities may gain advantage from this sample.
References
