Timers

Timers are asynchronous compute, which offers Eventing Functions the ability to execute in reference to wall-clock events. Timers also measure and track the amount of elapsed time and can be used while archiving of expired documents at a preconfigured time.

When using timers, it is required that all nodes of the cluster are synchronized at computer startup, and periodically afterwards using a clock synchronization tool like NTP.

A few important aspects related to timers are listed below:

  • Timers follow the same timeout semantics as their Parent Functions. So, if an Eventing Function has an execution timeout of 60 seconds, each of the timers created from the Eventing Function inherits the same execution timeout value of 60 seconds.

  • Timers may run on a different node than the one on which it was created.

  • One execution of timers is guaranteed despite node failures and cluster rebalances.

  • During Function backlogs, timers get eventually executed.

  • The metadata bucket stores information about timers and its association with an Eventing Function’s handler.

  • Ensure that the metadata bucket is not deleted or flushed, or the keys in metadata bucket get updated.

  • Each active timer an additional amount of space between 832 and 1856 bytes (832 bytes + sizeof(context)) is needed. Where the context can not be larger than 1024 bytes.

  • With an increase in the usage of timers, the metadata bucket’s space assignment must also be increased to accomadate all active timers and any potential backlog. If the use-case mandates large numbers of timers in the system, it is required that the space assigned to the metadata bucket be suitably high as well.

  • Due to runtime or programmatic errors in the function handler code, if triggering of a timer fails, then timer execution may get permanently blocked.

  • Bindings for bucket-aliases can be reused in timers. Bucket-aliases, created during the Eventing Function definition, can be accessed by the timer constructs in the handler code.

  • Timers get deleted when the associated Function is deleted or undeployed.

Language Constructs

The Timers language construct is added to support requirements of Couchbase Eventing Functions.

To create a timer use the below syntax:

createTimer(callback, date, reference, context)

In the createTimer syntax:

  • callback - This function is called when the timer fires. The callback function must be a top-level function that takes a single argument, the context (see below).

  • date - This is a JavaScript Date object representing the time for the timer to fire. The date of a timer must always be in future when the timer is created, otherwise the behavior is unspecified.

  • reference - This is a unique string that must be passed in to help identify the timer that is being created. References are always scoped to the function and callback they are used with and need to be unique only within this scope. The call returns the reference string if timer was created successfully. If multiple timers are created with the same unique reference, old timers with the same unique reference are implicitly cancelled. If the reference parameter is set to JavaScript null value, a unique reference will be generated.

  • context - This is any JavaScript object that can be serialized. The context specified when a timer is created is passed to the callback function when the timer fires. The default maximum size for Context objects is 1kB. Larger objects would typically be stored as bucket objects, and document key can be passed as context.

  • return value - If the reference parameter was null, this call returns the generated unique reference. Otherwise, the passed in reference parameter is the return value.

  • Exceptions Thrown - The createTimer() function throws an exception if the timer creation fails for an unexpected reason, such as an error writing to the metadata bucket.

A sample createTimer language construct is provided for reference.

createTimer(DocTimerCallback, oneMinuteFromNow, meta.id, context)

In the sample construct:

  • DocTimerCallback is the name of the function used in the Function handler code.

  • oneMinuteFromNow is a JavaScript Date object.

  • meta.id is a generic reference string that can be used in the Couchbase cluster.

  • context is the JavaScript object that is used in the Function handler code.

Using the construct above here is a working Eventing Function that creates a timer that is scheduled to be executed 60 seconds from its creation time where the callback will create a document in the alias 'dest_bucket' which must be a 'read and write' bbinding to an existing bucket.

function DocTimerCallback(context) {
   log('From DocTimerCallback', context);

   // create a new document as per our received context
   dest_bucket[context.docId] = context.random_text; // upsert a portion of the context
}

function OnUpdate(doc,meta) {
   // you would typically  filter to mutations of interest perhaps by type and a needed condition
   // if (doc.type != 'type_of_interest' || doc.needed_condition != true) return;

   // create a timestamp 60 seconds from now
   var oneMinuteFromNow = new Date(); //get current time
   oneMinuteFromNow.setSeconds(oneMinuteFromNow.getSeconds() + 60); //add 60 seconds to it

   // create a document to use as out for our context
   var context = {docId : meta.id, random_text : "arbitrary text"};
   createTimer(DocTimerCallback, oneMinuteFromNow, meta.id, context);
}

Sharding of Timers

Timers get automatically sharded across Eventing nodes and therefore are elastically scalable. Triggering of timers at or after a specified time interval is guaranteed. However, triggering of timers may either be on the same node (where the timer was created), or on a different node. Relative ordering between two specific timers cannot be maintained.

Debugging and Logs

Timers cannot be debugged using the Visual Debugger. For debugging, Couchbase recommends enclosing of timers in a try-catch block. When logging is enabled, timer related logs get captured as part of the Eventing Function’s application logs.

Elapsed Timestamps

During runtime, when a Function handler code contains a timestamp in the past (elapsed timestamp), the system executes the code in the next available time window, as soon as the required resources are available.

Handling Delays

During Function backlogs, execution of timers may be delayed. To handle these delays, you need to program additional time window in your code. If your business logic is time-sensitive after this additional time window the code should refrain from its Function execution.

The following is a sample code snippet, which performs a timestamp check (my_deadline) before code execution.

func callback(context) {
  //context.my_deadline is the parameter in the timer payload
  if (new Date().getTime() > context.my_deadline) {
     // timestamp is back-dated, do not execute the rest of the timer
     return;
  }
}

Examples

The Eventing Examples section provides two examples that show the use of Timers. The first example Document Expiry and second example is Document Archive.