{"id":12471,"date":"2021-11-15T10:34:56","date_gmt":"2021-11-15T18:34:56","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=12471"},"modified":"2025-06-13T22:39:19","modified_gmt":"2025-06-14T05:39:19","slug":"couchbase-eventing-handling-errors-and-retries","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/","title":{"rendered":"Couchbase Eventing Handling Errors and Retries"},"content":{"rendered":"<p><span style=\"font-weight: 400\">The <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-overview.html\"><span style=\"font-weight: 400\">Couchbase Eventing Service<\/span><\/a><span style=\"font-weight: 400\"> allows you to promptly act on mutations (or changes) to your data. All actions in <a href=\"https:\/\/www.couchbase.com\/products\/eventing\/\">Eventing<\/a> are accomplished by executing a lambda, a small piece of business logic written in JavaScript.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Common use cases include data enrichment, document archiving, and integration with external REST services. See more details <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-examples.html\"><span style=\"font-weight: 400\">here<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/p>\n<p><span style=\"font-weight: 400\">In the following blog, we will outline how errors during the event listener execution can be handled. By using a retry mechanism we ensure that the anticipated action is performed, even if the event listener fails during execution.<\/span><\/p>\n<p><b><i>Sample application<\/i><\/b><\/p>\n<p><span style=\"font-weight: 400\">As an example, we implement part of an e-commerce application that stores the customer orders in a Couchbase collection. As soon as the status of an order is changed to <\/span><b>paid<\/b><span style=\"font-weight: 400\">, we want to send an order confirmation to the customer.<\/span><\/p>\n<p><span style=\"font-weight: 400\">To accomplish this we integrate a Couchbase event listener with an external <\/span><i><span style=\"font-weight: 400\">email service<\/span><\/i><span style=\"font-weight: 400\">. The Couchbase event listener will pick up any changes to the order document, verify that the order was paid and then call out to the email service to trigger the confirmation message.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The email service is a standalone microservice providing a REST endpoint. We use the cURL support built directly into Couchbase Eventing to call the microservice from the event listener.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12458 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-1024x352.png\" alt=\"\" width=\"900\" height=\"309\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-1024x352.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-300x103.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-768x264.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-1536x529.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-2048x705.png 2048w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-20x7.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.37.22-1320x454.png 1320w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><i><span style=\"font-weight: 400\">Conceptual flow: As the orders are updated in Couchbase, events are triggered and picked up by an event listener. The event listener then calls out to the external Email Service.<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400\">\u00a0<\/span><b>Email service returning an error<\/b><\/p>\n<p><span style=\"font-weight: 400\">The described scenario works very well if the <\/span><i><span style=\"font-weight: 400\">email service<\/span><\/i><span style=\"font-weight: 400\"> is operational. However, what happens if the <\/span><i><span style=\"font-weight: 400\">email service<\/span><\/i><span style=\"font-weight: 400\"> is returning an error? Requests from the event listener to the email service will fail and therefore no confirmation message is sent to the customer. Since at the time of failure the Couchbase document change event has already been processed, no new event for the same document is triggered unless there is another change to it. In order to ensure that the confirmation is sent we need to handle the error and implement a retry mechanism. By doing this we can work around any temporary issue of the external service and at the same time guarantee that the confirmation is sent.<\/span><\/p>\n<p><span style=\"font-weight: 400\">\u00a0<\/span><span style=\"font-weight: 400\">There are different ways to approach this. In my example below I choose to create a new collection called \u2018retry\u2019, that will store references to the documents for which the event listener execution failed.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12459 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-1024x435.png\" alt=\"\" width=\"900\" height=\"382\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-1024x435.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-300x127.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-768x326.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-1536x652.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-2048x869.png 2048w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.39.25-1320x560.png 1320w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400\">\u00a0<\/span><span style=\"font-weight: 400\">The event listener will pick up changes to the order documents (step #1) and then call the Email Service (step #2). If the call to the email service succeeds the event listener then updates the confirmation message status in the order document (step #3). However, in case of failure a retry document is created and put into the \u2018retry\u2019 collection (step #3*).<\/span><\/p>\n<p><span style=\"font-weight: 400\">Keeping a reference to the documents allows us to identify all failed updates and enables us to rerun them later. This could be either by manual intervention by an operator or automatic retry using Couchbase eventing timers.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12460 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-1024x435.png\" alt=\"\" width=\"900\" height=\"382\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-1024x435.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-300x127.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-768x326.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-1536x652.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-2048x869.png 2048w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/Screen-Shot-2021-11-10-at-22.40.24-1320x560.png 1320w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">We initiate the retry process by adding a document with a specified document id into the retry collection. A recurring timer is created given a provided timer interval.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">On timer execution all documents older than a small time quanta in the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection are updated. By adding an attribute like <\/span><i><span style=\"font-weight: 400\">fireRetry = true<\/span><\/i><span style=\"font-weight: 400\"> to the retry documents we trigger another update event that is picked up by the event listener to execute the retry mechanism. This gives us a recursive mutation that lights up all the documents in the retry collection in parallel. The retry function is now executed using all available worker threads in parallel.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">A document update event is triggered for each retry document individually.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The corresponding order document is retrieved from the inbound collection<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Now the email service is called.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">If the call to the email service succeeds the event listener then updates the confirmation message status in the order document and removes the retry document\u00a0<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">In case of failure the retry document is updated and put into the \u2018retry\u2019 collection.<\/span><\/li>\n<\/ol>\n<p><b>Code Review<\/b><\/p>\n<p><span style=\"font-weight: 400\">\u00a0<\/span><span style=\"font-weight: 400\">Now that we have established the conceptual design, let\u2019s have a look at the sample implementation:<\/span><\/p>\n<p><b>Prerequisites<\/b><span style=\"font-weight: 400\">:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Couchbase 7 Enterprise Edition. I run Couchbase as a single node cluster on Docker on my local machine. (https:\/\/docs.couchbase.com\/server\/current\/install\/getting-started-docker.html)<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">For development purposes we create a single node Couchbase Cluster running the following services:<\/span>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Index, Query, Eventing &amp; Data Service<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">Please note that a single node install is not recommended for production usage.<\/span><\/p>\n<p><b>Preparation<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create a bucket named <\/span><b><i>orders<\/i><\/b><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create two collections in the <\/span><b><i>orders<\/i><\/b><span style=\"font-weight: 400\"> buckets _default scope:<\/span>\n<ul>\n<li style=\"font-weight: 400\"><b>inbound<\/b><span style=\"font-weight: 400\"> (this will contain all the incoming orders)<\/span><\/li>\n<li style=\"font-weight: 400\"><b>retry<\/b><span style=\"font-weight: 400\"> (this will contain the retry documents referencing to the orders that have failed)<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12461 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/eventing1-1024x331.png\" alt=\"\" width=\"900\" height=\"291\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-1024x331.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-300x97.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-768x248.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-1536x496.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-20x6.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1-1320x427.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing1.png 1600w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create bucket <\/span><b>\u2018metadata\u2019<\/b><span style=\"font-weight: 400\">. We will use the _default scope and _default collection. The metadata bucket is used for the Eventing metadata.<\/span><\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12462 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/eventing2-1024x226.png\" alt=\"\" width=\"900\" height=\"199\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-1024x226.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-300x66.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-768x169.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-1536x339.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-20x4.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2-1320x291.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/eventing2.png 1600w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create an index on the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection. The retry listener will query any documents contained in the collection using N1QL, thus an index needs to be in place for the query to be executed.<\/span><\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">CREATE PRIMARY INDEX idx_default_primary ON orders._default.retry USING GSI;<\/pre>\n<p><span style=\"font-weight: 400\">\u00a0<\/span><b><i>Data model order document<\/i><\/b><\/p>\n<p><span style=\"font-weight: 400\">For the sake of this sample application we use a lightweight data model for the Order document, only containing the relevant fields. Many other fields that you typically would expect in an order document are omitted.<\/span><\/p>\n<pre class=\"lang:default decode:true\">{\r\n\u00a0\u00a0\"email\": \"customer_email\",\r\n\u00a0\u00a0\"paymentStatus\": \"initiated\",\r\n\u00a0\u00a0\"confirmationEmailSent\": false,\r\n\u00a0\u00a0\"items\": [\r\n\u00a0\u00a0\u00a0\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"name\": \"Swedish Meatballs 500g\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"amount\": 2,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"unitPrice\": 9.95\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0]\r\n}<\/pre>\n<p><b><i>Data model retry document<\/i><\/b><\/p>\n<p><span style=\"font-weight: 400\">The retry document contains a few basic attributes such as the document id of the order document, an attempt counter and a timestamp. The <\/span><i><span style=\"font-weight: 400\">type <\/span><\/i><span style=\"font-weight: 400\">\u00a0attribute is not necessary in our application, but it can be useful to determine the type of email notification in case the application is extended to also send shipment and delivery updates.<\/span><\/p>\n<pre class=\"lang:default decode:true\">{\r\n\u00a0\u00a0\"type\": \"confirmation\",\r\n\u00a0\u00a0\"docId\": \"order_140\",\r\n\u00a0\u00a0\"attempt\": 1,\r\n\u00a0\u00a0\"ts\": 1632775908319\r\n}<\/pre>\n<p><b><i>Email Service MOCK\u00a0<\/i><\/b><\/p>\n<p><span style=\"font-weight: 400\">We will mock the Email Service using a simple Python script running a local web server. The script will randomly respond with HTTP 200 OK, or with HTTP 406 to indicate a failure.<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Update the IP address to the IP address of your local machine in line 31<\/span><em>server = ThreadedHTTPServer((&#8216;replace with your IP&#8217;, 9080), Handler)<\/em><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Start the script by running: python http.py<\/span><\/li>\n<\/ol>\n<p><b>Event Listeners<\/b><\/p>\n<p><span style=\"font-weight: 400\">Now with all the preparations in place we can go ahead and add the two Event listeners:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><b>evt_send_confirmation_email<\/b><span style=\"font-weight: 400\"> &#8211; provides the integration with the Email Service<\/span><\/li>\n<li style=\"font-weight: 400\"><b>evt_send_confirmation_email_retry<\/b><span style=\"font-weight: 400\"> &#8211; contains the retry logic<\/span><\/li>\n<\/ul>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The listeners are available here: https:\/\/github.com\/puhhma\/cb_eventing_retry_sample<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Import the listeners (<\/span><i><span style=\"font-weight: 400\">json<\/span><\/i><span style=\"font-weight: 400\"> file) into the Couchbase Eventing service.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400\">Please note that for the listeners to work you need to follow the naming conventions used in this article.\u00a0<\/span><\/p>\n<p><b>Review <\/b><b><i>evt_send_confirmation_email<\/i><\/b><b> listener<\/b><\/p>\n<p><span style=\"font-weight: 400\">Event listener configuration:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12465 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/function-1-602x1024.png\" alt=\"\" width=\"602\" height=\"1024\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-602x1024.png 602w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-176x300.png 176w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-768x1307.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-902x1536.png 902w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-300x511.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1-12x20.png 12w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-1.png 940w\" sizes=\"auto, (max-width: 602px) 100vw, 602px\" \/><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The event listener is listening to the <\/span><i><span style=\"font-weight: 400\">inbound<\/span><\/i><span style=\"font-weight: 400\"> collection in the <\/span><i><span style=\"font-weight: 400\">orders<\/span><\/i><span style=\"font-weight: 400\"> bucket.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The <\/span><i><span style=\"font-weight: 400\">metadata<\/span><\/i><span style=\"font-weight: 400\"> bucket is used to store the listeners metadata<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The bucket aliases <\/span><i><span style=\"font-weight: 400\">bkt_order_inbound<\/span><\/i><span style=\"font-weight: 400\"> and <\/span><i><span style=\"font-weight: 400\">bkt_order_retry<\/span><\/i><span style=\"font-weight: 400\"> reference the corresponding <\/span><i><span style=\"font-weight: 400\">inbound<\/span><\/i><span style=\"font-weight: 400\"> and <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection in the <\/span><i><span style=\"font-weight: 400\">order<\/span><\/i><span style=\"font-weight: 400\"> bucket<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The <\/span><i><span style=\"font-weight: 400\">curlEmailServiceHost<\/span><\/i><span style=\"font-weight: 400\"> specifies the URL alias to the mock EmailService. Please make sure to update with your ip address<\/span><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<pre class=\"lang:default decode:true\">\/\/ OnUpdate is invoked for all documents created\/updated in the 'inbound' bucket\r\nfunction OnUpdate(doc, meta) {\r\n\u00a0\u00a0\u00a0\u00a0\/\/ determine if document status is 'paid' &amp; confirmation email was not previously sent\r\n\u00a0\u00a0\u00a0\u00a0if( doc.paymentStatus === \"paid\" &amp;&amp; !doc.confirmationEmailSent ) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SendConfirmationMail(doc, meta.id);\r\n\u00a0\u00a0\u00a0\u00a0} else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 1)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"Nothing to do for: \" + meta.id);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n}\r\n\r\nfunction SendConfirmationMail(doc, docId) {\r\n\u00a0\u00a0try {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ build the request to the EmailService\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var request = {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0path: 'sendConfirmation',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0body: doc\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0};\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0 perform the cURL request using the URL alias 'curlEmailServiceHost' from the settings\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var response = curl('POST', curlEmailServiceHost, request);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (response.status != 200) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ this did not work as expected\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 1) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"docId\", docId, \"cURL POST failed response.status:\",response.status);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ create retry document referencing the documentId and store in 'retry' bucket\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0bkt_order_retry[docId] = {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"docId\": docId,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"attempt\": 1,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"ts\": Date.now()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 5) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"cURL POST success, sent\",docId,\"response.body:\",response.body);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ update confirmationEmailSent status\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0doc.confirmationEmailSent = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0bkt_order_inbound[docId] = doc;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0} catch (e) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"ERROR cURL request had an exception:\",e)\r\n\u00a0\u00a0}\r\n}<\/pre>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Please see the inline comments for details<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The <\/span><i><span style=\"font-weight: 400\">OnUpdate <\/span><\/i><span style=\"font-weight: 400\">function is triggered once an order document is updated or created<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The request is constructed and the HTTP POST request sent to the EmailService using cURL.<\/span><\/li>\n<li style=\"font-weight: 400\">The result is evaluated. In case that the HTTP response is not successful a retry document is constructed and added to the <i>retry<\/i> collection.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12464 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2021\/11\/function-564x1024.png\" alt=\"\" width=\"564\" height=\"1024\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-564x1024.png 564w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-165x300.png 165w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-768x1393.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-847x1536.png 847w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-300x544.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function-11x20.png 11w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/function.png 882w\" sizes=\"auto, (max-width: 564px) 100vw, 564px\" \/><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The event listener is listening to the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection in the <\/span><i><span style=\"font-weight: 400\">orders<\/span><\/i><span style=\"font-weight: 400\"> bucket.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The <\/span><i><span style=\"font-weight: 400\">metadata<\/span><\/i><span style=\"font-weight: 400\"> bucket is used to store the listeners metadata<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The bucket aliases <\/span><i><span style=\"font-weight: 400\">bkt_order_inbound<\/span><\/i><span style=\"font-weight: 400\"> and <\/span><i><span style=\"font-weight: 400\">bkt_order_retry<\/span><\/i><span style=\"font-weight: 400\"> reference the corresponding <\/span><i><span style=\"font-weight: 400\">inbound<\/span><\/i><span style=\"font-weight: 400\"> and <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection in the order bucket<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The <\/span><i><span style=\"font-weight: 400\">curlEmailServiceHost<\/span><\/i><span style=\"font-weight: 400\"> specifies the URL alias to the mock EmailService<\/span><\/li>\n<li style=\"font-weight: 400\">The <i>retryTimerIntervall<\/i> specifies the timer interval in seconds.<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">function OnUpdate(doc, meta) {\r\n\u00a0\u00a0\u00a0\u00a0if (meta.id === \"allow_retrys\") {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ the timer is initialized by creating document with id = 'allow_retrys'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0CreateRetryTimer({\"id\": meta.id, \"mode\": \"initial\"});\r\n\u00a0\u00a0\u00a0\u00a0} else if (doc.fireRetry) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ process retry documents\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SendConfirmationMail(doc, meta.id);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n}\r\n\r\nfunction CreateRetryTimer(context) {\r\n\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 2) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log('From CreateRetryTimer: creating timer', context.mode, context.id);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\/\/ Create a timestamp 'retryTimerInterval' seconds (from the settings) from now\r\n\u00a0\u00a0\u00a0\u00a0var timerStartTime = new Date();\r\n\u00a0\u00a0\u00a0\u00a0\/\/ Get current time &amp; add 'retryTimerInterval' sec. to it.\r\n\u00a0\u00a0\u00a0\u00a0timerStartTime.setSeconds(timerStartTime.getSeconds() + retryTimerInterval);\r\n\u00a0\u00a0\u00a0\u00a0\/\/ Create a document to use as out for our context\r\n\u00a0\u00a0\u00a0\u00a0createTimer(RetryTimerCallback, timerStartTime, context.id, context);\r\n}\r\n\r\nfunction RetryTimerCallback(context) {\r\n\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 2) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log('From RetryTimerCallback: timer fired', context);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\/\/ rearm the timer ASAP, to ensure timer keeps running in the event\r\n\u00a0\u00a0\u00a0\u00a0\/\/ of later\u00a0 errors or script timeouts in later \"recurring work\".\r\n\u00a0\u00a0\u00a0\u00a0CreateRetryTimer({ \"id\": context.id, \"mode\": \"via_callback\" });\r\n\u00a0\u00a0\u00a0\u00a0\/\/ Update all retry documents in the 'retry' bucket. Exclude the 'allow_retys' document\r\n\u00a0\u00a0\u00a0\u00a0\/\/ and any documents that were created more than 15 seconds ago, in order to avoid retry 'to early'.\r\n\u00a0\u00a0\u00a0\u00a0N1QL(\"UPDATE orders._default.retry SET fireRetry = true WHERE meta().id != 'allow_retrys' AND ts &lt; DATE_ADD_MILLIS(NOW_MILLIS(), -15, 'second')\");\r\n}\r\n\r\nfunction SendConfirmationMail(retryDoc, docId) {\r\n\u00a0\u00a0try {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ resolve order document by id\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var doc = bkt_order_inbound[docId];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ build the request\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var request = {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0path: 'sendConfirmation',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0body: doc\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0};\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/\u00a0 perform the cURL request using the URL alias from the settings\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var response = curl('POST', curlEmailServiceHost, request);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (response.status != 200) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ this did not work as expected\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 1) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"docId\", docId, \"cURL POST failed response.status:\",response.status);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ increment attempt count in retry document\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0retryDoc.attempt = ++retryDoc.attempt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Set fireRetry = false, to avoid retry execution with this document change\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0retryDoc.fireRetry = false;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0retryDoc.ts = Date.now();\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ update retry document\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0bkt_order_retry[docId] = retryDoc;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (debug_level &gt; 5) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"cURL POST success, sent\",docId,\"response.body:\",response.body);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0doc.confirmationEmailSent = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0bkt_order_inbound[docId] = doc;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ delete the retry document\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0delete bkt_order_retry[docId];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\u00a0 } catch (e) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0log(\"ERROR cURL request had an exception:\",e)\r\n\u00a0\u00a0}\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The timer is initiated by adding a document with the id <\/span><b>allow_retrys<\/b><span style=\"font-weight: 400\"> to the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The timer is then initialized and the RetryTimerCallback function associated with the timer<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Once the timer is executed the RetryTimerCallback function is called<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Before proceeding with the retry mechanism a new timer is created as the first step to ensure this it keeps running in case of later errors<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">A N1QL query is used to update all <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> documents in the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection by adding a <\/span><i><span style=\"font-weight: 400\">fireRetry<\/span><\/i><span style=\"font-weight: 400\"> attribute to the document<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Each document change results in a document update event and the retry mechanism is executed<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">The order document is resolved from the <\/span><i><span style=\"font-weight: 400\">inbound<\/span><\/i><span style=\"font-weight: 400\"> collection &amp; the EmailService is called via cURL<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">In case of failure the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> document is updated and the <\/span><i><span style=\"font-weight: 400\">attempt<\/span><\/i><span style=\"font-weight: 400\"> counter increased<\/span><\/li>\n<\/ul>\n<p><b>Test the sample application<\/b><\/p>\n<p><span style=\"font-weight: 400\">Now it\u2019s time to finally test the sample application:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Make sure the mock EmailService is up and running<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Start the <\/span><b><i>evt_send_confirmation_email <\/i><\/b><span style=\"font-weight: 400\">event listener, but keep the <\/span><b><i>evt_send_confirmation_email_retry <\/i><\/b><span style=\"font-weight: 400\">listener stopped for now.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create a sample order document (see the data model above) in the Couchbase console<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">In case of a success response the <\/span><span style=\"font-weight: 400\">confirmationEmailSent <\/span><span style=\"font-weight: 400\">attribute is updated to true in the <\/span><i><span style=\"font-weight: 400\">order<\/span><\/i><span style=\"font-weight: 400\"> document.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">In case of failure a retry document is created in the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection. Since the EmailService will randomly reply with an error, please repeat step #3 until an error occurs.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Now that we have captured an error lets even start the retry event listener <\/span><b><i>evt_send_confirmation_email_retry<\/i><\/b><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Create a document with the id \u2018allow_retrys\u2019. This will initialize retry mechanism.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">After a short while the listener will become active and starts to process the documents in the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Please observe that the \u2018attempt\u2019 attribute is increased with every failed update to the Email Service. In case of a success the order document is updated and the corresponding <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> document removed from the <\/span><i><span style=\"font-weight: 400\">retry<\/span><\/i><span style=\"font-weight: 400\"> collection.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400\">Since the response from the Email Service Mock is random you may need to repeat the above steps to be able to observe the anticipated behaviour.<\/span><\/p>\n<p><b>Conclusion<\/b><\/p>\n<p><span style=\"font-weight: 400\">In this article I outline a retry mechanism to handle error conditions when integrating Couchbase Eventing with an external REST service. This or similar solutions can be used to guarantee that anticipated actions are performed even if the external service is temporarily malfunctioning.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400\">When considering a retry mechanism various factors need to be considered, such as the volume of retries, the available worker threads for Couchbase Eventing &amp; the requests the external service can handle.<\/span><\/p>\n<p><span style=\"font-weight: 400\">You can find more information about the internals of Couchbase Eventing here: <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-overview.html\"><span style=\"font-weight: 400\">https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-overview.html<\/span><\/a><\/p>\n<p><span style=\"font-weight: 400\">Many thanks to Jon Strabala (Principal Product Manager, Couchbase) for the technical insight and support with this article.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Couchbase Eventing Service allows you to promptly act on mutations (or changes) to your data. All actions in Eventing are accomplished by executing a lambda, a small piece of business logic written in JavaScript. Common use cases include data [&hellip;]<\/p>\n","protected":false},"author":77950,"featured_media":12473,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[2273],"tags":[],"ppma_author":[9323],"class_list":["post-12471","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-eventing"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Couchbase Eventing Handling Errors and Retries w\/ Examples<\/title>\n<meta name=\"description\" content=\"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Couchbase Eventing Handling Errors and Retries\" \/>\n<meta property=\"og:description\" content=\"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-11-15T18:34:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T05:39:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash-1024x683.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"683\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Marian Puhl, Solutions Engineer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Marian Puhl, Solutions Engineer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\"},\"author\":{\"name\":\"Marian Puhl, Solutions Engineer\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/b2fc479528e2819b50082a425cf381e3\"},\"headline\":\"Couchbase Eventing Handling Errors and Retries\",\"datePublished\":\"2021-11-15T18:34:56+00:00\",\"dateModified\":\"2025-06-14T05:39:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\"},\"wordCount\":1751,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg\",\"articleSection\":[\"Eventing\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\",\"name\":\"Couchbase Eventing Handling Errors and Retries w\/ Examples\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg\",\"datePublished\":\"2021-11-15T18:34:56+00:00\",\"dateModified\":\"2025-06-14T05:39:19+00:00\",\"description\":\"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg\",\"width\":7952,\"height\":5304,\"caption\":\"Creating JavaScript UDFs to traverse hierarchy in Couchbase SQL++\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Couchbase Eventing Handling Errors and Retries\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/b2fc479528e2819b50082a425cf381e3\",\"name\":\"Marian Puhl, Solutions Engineer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/1f65549252c18bb3651eaa3a78e46169\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg\",\"caption\":\"Marian Puhl, Solutions Engineer\"},\"description\":\"Marian Puhl is a Solutions Engineer at Couchbase in the Nordic region.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/marian-puhl\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Couchbase Eventing Handling Errors and Retries w\/ Examples","description":"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/","og_locale":"en_US","og_type":"article","og_title":"Couchbase Eventing Handling Errors and Retries","og_description":"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.","og_url":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/","og_site_name":"The Couchbase Blog","article_published_time":"2021-11-15T18:34:56+00:00","article_modified_time":"2025-06-14T05:39:19+00:00","og_image":[{"width":1024,"height":683,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash-1024x683.jpg","type":"image\/jpeg"}],"author":"Marian Puhl, Solutions Engineer","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Marian Puhl, Solutions Engineer","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/"},"author":{"name":"Marian Puhl, Solutions Engineer","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/b2fc479528e2819b50082a425cf381e3"},"headline":"Couchbase Eventing Handling Errors and Retries","datePublished":"2021-11-15T18:34:56+00:00","dateModified":"2025-06-14T05:39:19+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/"},"wordCount":1751,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg","articleSection":["Eventing"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/","url":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/","name":"Couchbase Eventing Handling Errors and Retries w\/ Examples","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg","datePublished":"2021-11-15T18:34:56+00:00","dateModified":"2025-06-14T05:39:19+00:00","description":"This post will outline how errors during the event listener execution can be handled. By using a retry mechanism ensure the anticipated action is performed.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/11\/thisisengineering-raeng-64YrPKiguAE-unsplash.jpg","width":7952,"height":5304,"caption":"Creating JavaScript UDFs to traverse hierarchy in Couchbase SQL++"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-eventing-handling-errors-and-retries\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Couchbase Eventing Handling Errors and Retries"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/b2fc479528e2819b50082a425cf381e3","name":"Marian Puhl, Solutions Engineer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/1f65549252c18bb3651eaa3a78e46169","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg","caption":"Marian Puhl, Solutions Engineer"},"description":"Marian Puhl is a Solutions Engineer at Couchbase in the Nordic region.","url":"https:\/\/www.couchbase.com\/blog\/author\/marian-puhl\/"}]}},"authors":[{"term_id":9323,"user_id":77950,"is_guest":0,"slug":"marian-puhl","display_name":"Marian Puhl, Solutions Engineer","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2021\/07\/marian-puhl-couchbase-engineering.jpeg"},"author_category":"","last_name":"Puhl","first_name":"Marian","job_title":"","user_url":"","description":"Marian Puhl is a Solutions Engineer at Couchbase in the Nordic region. "}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/12471","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/77950"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=12471"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/12471\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/12473"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=12471"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=12471"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=12471"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=12471"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}