{"id":8489,"date":"2020-05-22T10:50:02","date_gmt":"2020-05-22T17:50:02","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=8489"},"modified":"2025-06-13T22:40:23","modified_gmt":"2025-06-14T05:40:23","slug":"implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/","title":{"rendered":"Implementaci\u00f3n de un programador a trav\u00e9s de eventos Couchbase (Parte 1)"},"content":{"rendered":"<p>Esta es la primera de una serie de varias partes para aprovechar la <a href=\"https:\/\/www.couchbase.com\/blog\/es\/products\/eventing\/\">Servicio de eventos Couchbase<\/a> para ejecutar m\u00faltiples tareas programadas a intervalos recurrentes espec\u00edficos en un <em>cron<\/em> completamente dentro de la base de datos sin necesidad de infraestructura adicional a trav\u00e9s de una \u00fanica funci\u00f3n de eventos de uso general.<\/p>\n<p>En esta entrega, nos centraremos en la ejecuci\u00f3n de rutinas fijas de usuario, funciones JavaScript definidas dentro de una funci\u00f3n de eventos.<\/p>\n<p>En art\u00edculos posteriores ampliaremos la <em>cron<\/em> como Eventing Function para programar y ejecutar sentencias N1QL din\u00e1micas manejadas por la base de datos y finalmente exploraremos la programaci\u00f3n de funciones JavaScript din\u00e1micas manejadas por la base de datos.<\/p>\n<h3>Fondo<\/h3>\n<p>El servicio de eventos de Couchbase proporciona un marco para escribir tus propias rutinas, simples funciones JavaScript, para procesar los cambios en los documentos.  Este servicio proporciona toda la infraestructura necesaria para crear funciones escalables y robustas basadas en la nube, permiti\u00e9ndote centrarte en desarrollar pura l\u00f3gica de negocio para interactuar casi en tiempo real con los cambios en tus datos. Tus funciones pueden acceder al servicio de datos de Couchbase (KV), al servicio de consultas de Couchbase (N1QL) y a puntos finales REST externos al sistema Couchbase.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-8490\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-1024x405.png\" alt=\"Eventing Life Cycle 6.5 I\/O\" width=\"900\" height=\"356\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-1024x405.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-300x119.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-768x304.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-1536x608.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5-1320x522.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/cron_implementation_1_image_0_input-output-overview-6.5.png 1870w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p>En <a href=\"https:\/\/www.json.org\/\">JSON<\/a> modelo de datos de Couchbase procede de <a href=\"https:\/\/en.wikipedia.org\/wiki\/JavaScript\">JavaScript<\/a>Por lo tanto, es natural que el servicio de eventos ofrezca la posibilidad de escribir c\u00f3digo JavaScript para analizar y manipular documentos JSON en cualquier tipo de evento de cambio, incluyendo inserciones, actualizaciones, fusiones y eliminaciones (denominadas conjuntamente mutaciones).<\/p>\n<p>Las funciones de eventos normalmente le permiten desplegar y ejecutar fragmentos de c\u00f3digo personalizados para que reaccionen a miles e incluso millones de mutaciones por segundo en sus documentos. Algunos casos de uso t\u00edpicos son <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/eventing\/eventing-overview.html\">documentado<\/a> para desarrollar Funciones Eventing de alta velocidad a escala que respondan a mutaciones de documentos Couchbase.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8739 size-large\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-1024x398.png\" alt=\"Eventing Life Cycle 6.5\" width=\"900\" height=\"350\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-1024x398.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-300x117.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-768x298.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-1536x597.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b-1320x513.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/cron_implementation_1_image_0_lifecycle_overview_notitle_b.png 1825w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p>Este art\u00edculo se centrar\u00e1 m\u00e1s bien en un caso de uso de muy baja velocidad del Servicio de Eventos construyendo un crontab distribuido fiable \"en base de datos\", permiti\u00e9ndote ejecutar funciones JavaScript que interact\u00faan con servicios Couchbase en un horario peri\u00f3dico regular.<\/p>\n<h3>Programaci\u00f3n de la l\u00f3gica empresarial para que se ejecute en una fecha u hora determinadas<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\">Cron<\/a>que lleva el nombre de \"<em>Chronos<\/em>,\" la palabra griega para el tiempo es una de las utilidades m\u00e1s \u00fatiles en un sistema Linux. En Linux el <em>cron<\/em> est\u00e1 controlada por un archivo crontab (tabla cron), un archivo de configuraci\u00f3n que especifica los comandos del shell que se ejecutar\u00e1n peri\u00f3dicamente en un horario determinado.<\/p>\n<p>Uno de los inconvenientes <em>cron<\/em> es que no est\u00e1 dise\u00f1ado para ser un servicio distribuido; se ejecuta en una sola caja, por lo que presenta un \u00fanico punto de fallo. Si el sistema est\u00e1 fuera de l\u00ednea durante varias horas, se pierden todas las tareas programadas.<\/p>\n<p>S\u00ed, hay algunos distribuidos <em>cron<\/em> como Google's Cloud Service, AWS' Scheduled Tasks, y Azure Functions \/ Time Triggers. Pero las ofertas de cada proveedor de nube tienen sus propios modismos que no son directamente portables.<\/p>\n<p>Adem\u00e1s, es necesario asegurar la metodolog\u00eda de configuraci\u00f3n y control, por ejemplo si se controla una <em>cron<\/em> a trav\u00e9s de una API REST sobre HTTP\/S, deber\u00e1 tenerlo en cuenta en su plan de seguridad.<\/p>\n<h3>Utilizar el propio Couchbase para ejecutar comandos peri\u00f3dicos<\/h3>\n<p>Con una peque\u00f1a cantidad de c\u00f3digo y planificaci\u00f3n, puede aprovechar el servicio Eventing de Couchbase para proporcionar <em>cron<\/em> para las operaciones o el mantenimiento programados de la base de datos. La integraci\u00f3n del programador en la base de datos permite obtener las siguientes ventajas:<\/p>\n<ul>\n<li>Portabilidad a trav\u00e9s de proveedores Cloud, si reubicas tu cluster de Couchbase tu planificador no se ve afectado.<\/li>\n<li>Soporte, si utiliza Couchbase tiene un \u00fanico proveedor para proporcionarle soporte y otros servicios.<\/li>\n<li>Distribuido, no hay un \u00fanico punto de fallo y todos los servicios de Couchbase admiten r\u00e9plicas distribuidas.<\/li>\n<li>Ejecuci\u00f3n garantizada, su tarea se ejecuta incluso despu\u00e9s de recuperarse de un fallo de nodo.<\/li>\n<\/ul>\n<h3>Programaci\u00f3n de Couchbase, los temporizadores son la salsa secreta<\/h3>\n<p>Los temporizadores son construcciones del servicio de eventos de Couchbase mediante las cuales los desarrolladores pueden especificar una rutina (l\u00f3gica de negocio) que se activar\u00e1 en un momento futuro. Utilizaremos esta funcionalidad para implementar un Couchbase configurable puro. <em>crontab<\/em> que le permite activar tareas repetitivas como parte de sus flujos de trabajo, tanto si necesita ejecutar una simple consulta N1QL como construir un complejo motor de reglas.<\/p>\n<p>En todos los dise\u00f1os posteriores limitaremos nuestra <em>cron<\/em> a una resoluci\u00f3n de 15 segundos o m\u00e1s. Tenemos esta limitaci\u00f3n porque, aunque los temporizadores se escalan a millones y se garantiza que se disparan y ejecutan, no son precisos como un reloj de pared y actualmente tienen un retardo de estado estacionario limitado a menos de 14 segundos. <a id=\"footnote_1\"><\/a><sup>[1]<\/sup>.<\/p>\n<p>Por supuesto, si necesita una programaci\u00f3n m\u00e1s ajustada, es decir, menos de 15 segundos, entonces simplemente debe procesar la mutaci\u00f3n en s\u00ed en la l\u00f3gica de eventos sin el uso de una construcci\u00f3n de temporizador para programar una llamada de vuelta en el futuro.<\/p>\n<p>En el momento de escribir estas l\u00edneas, la versi\u00f3n actual de Couchbase es la 6.5.1 que <em><strong>dos limitaciones con las que debemos trabajar<\/strong><\/em> al hacer un robusto <em>cron<\/em> sistema.<\/p>\n<ol>\n<li>En las versiones 5.5.x, 6.0.x y 6.5.x, una funci\u00f3n invocada por una llamada de retorno de un temporizador no puede crear un nuevo temporizador de forma fiable (se puede solucionar mediante una segunda funci\u00f3n cooperativa).<\/li>\n<li>En las versiones 6.5.x, la creaci\u00f3n de temporizadores en el futuro (como en una hora o m\u00e1s) en un sistema por lo dem\u00e1s inactivo puede dar lugar a un n\u00famero creciente de operaciones de cubo de metadatos que eventualmente pueden bloquear las mutaciones para una funci\u00f3n de Eventing dada (en 6.5.X se puede lograr una soluci\u00f3n en el espacio de usuario a trav\u00e9s de una segunda funci\u00f3n cooperativa). La gravedad se rige por:\n<ul>\n<li>El n\u00famero de vBuckets que mantienen un temporizador activo. Por lo tanto, si solo hay unos pocos temporizadores en el futuro, es posible que el problema no se note o no se materialice.\u00a0 <em>Este es el caso con s\u00f3lo unos pocos cron horarios, pero para completar en caso de a\u00f1adir la funcionalidad de la fecha puse en una soluci\u00f3n para este problema para el c\u00f3digo suministrado en este art\u00edculo<\/em>.<\/li>\n<li>Si un temporizador de Eventing se ha disparado recientemente en un vBucket (lo que soluciona el problema para el vBucket en cuesti\u00f3n por funci\u00f3n). Por lo tanto, los sistemas con mucha actividad de temporizadores a corto plazo no experimentar\u00e1n este problema aunque los temporizadores est\u00e9n programados para un futuro lejano.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>Afortunadamente, en la versi\u00f3n 6.6.0 ambos problemas o restricciones se han eliminado y se puede crear un programador en una \u00fanica y sencilla Funci\u00f3n de Eventos unificada.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8730\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/ev_cr_1u_design_choices-1.png\" alt=\"Eventing cron update\" width=\"740\" height=\"325\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1.png 1479w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1-300x132.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1-1024x449.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1-768x337.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1-20x9.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_design_choices-1-1320x579.png 1320w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/p>\n<h3>Requisitos previos<\/h3>\n<p>En este art\u00edculo utilizaremos la \u00faltima versi\u00f3n de GA, es decir, la versi\u00f3n 6.5.1 de Couchbase (puede que tengas que hacer algunos cambios en las funciones de eventos descritas para versiones anteriores de Couchbase). El ejemplo de este art\u00edculo se ejecutar\u00e1 contra el conjunto de datos travel-sample que se entrega con el servidor Couchbase.<\/p>\n<p style=\"border: 1px solid black;padding: 8px\"><strong>CONSEJO PROFESIONAL<\/strong>: <u>S\u00f3lo para usuarios avanzados<\/u>, <em>si est\u00e1s familiarizado con Couchbase Eventing y tambi\u00e9n con nuestras herramientas CLI \/ REST puedes saltarte la mayor parte de este blog y descargar un archivo ZIP para configurar y ejecutar r\u00e1pidamente el sistema programador que se presenta a continuaci\u00f3n.  Haz clic con el bot\u00f3n derecho en el siguiente enlace y selecciona <strong>Guardar enlace como<\/strong>\u00a0para descargar el archivo<\/em> \u00a0<a href=\"https:\/\/github.com\/couchbaselabs\/blog-source-code\/raw\/master\/Strabala\/CronFiles\/cron_impl_2func_CLI.zip\"><strong>cron_impl_2func_CLI.zip<\/strong><\/a>, <em>mu\u00e9valo a un nodo Eventing, extraiga el archivo ZIP y consulte el archivo README.txt extra\u00eddo.<\/em><\/p>\n<p>Sin embargo, si usted no est\u00e1 familiarizado con Couchbase o el servicio Eventing por favor camine a trav\u00e9s de GET STARTED y un ejemplo Eventing espec\u00edficamente se refieren a los siguientes:<\/p>\n<ul>\n<li>Configure un servidor Couchbase 6.5.1 que funcione seg\u00fan las instrucciones de <a href=\"https:\/\/docs.couchbase.com\/server\/current\/getting-started\/start-here.html\">Comience aqu\u00ed<\/a><\/li>\n<li>Aseg\u00farese de que puede ejecutar una consulta N1QL en la base de datos <strong>viaje-muestra<\/strong> seg\u00fan las instrucciones de <a href=\"https:\/\/docs.couchbase.com\/server\/current\/getting-started\/try-a-query.html\">Ejecute su primera consulta N1QL<\/a>.<\/li>\n<li>Comprender c\u00f3mo desplegar una funci\u00f3n b\u00e1sica de Eventing seg\u00fan las instrucciones de la secci\u00f3n <a href=\"https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-examples-docarchive.html\">Archivo de documentos<\/a> que tambi\u00e9n utiliza <strong>viaje-muestra<\/strong> conjunto de datos.<\/li>\n<li>Aseg\u00farese de tener la <strong>viaje-muestra<\/strong> en la vista de cubos de la interfaz de usuario.<\/li>\n<li>Aseg\u00farese de que un cubo llamado <strong>metadatos<\/strong> en la vista Buckets de la interfaz de usuario debe tener un tama\u00f1o m\u00ednimo de 200 MB.<\/li>\n<li>En la vista Buckets de la interfaz de usuario, cree un bucket llamado <strong>crondata<\/strong> con un tama\u00f1o m\u00ednimo de 200 MB. Para obtener informaci\u00f3n detallada sobre c\u00f3mo crear cubos, consulte <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/manage\/manage-buckets\/create-bucket.html\">Crear un cubo<\/a>.<\/li>\n<li>Establecer <em>allow_interbucket_recursion<\/em> a <em>verdadero<\/em> para permitir que dos (2) funciones de Eventing modifiquen el mismo documento KV <a id=\"footnote_2\"><\/a><sup>[2]<\/sup>.\n<pre class=\"striped:false wrap-toggle:false lang:sh highlight:0 decode:true\">curl -X POST -u \"$CB_USERNAME:$CB_PASSWORD\" 'https:\/\/localhost:8091\/_p\/event\/api\/v1\/config' -d '{ \"allow_interbucket_recursion\":true }'\r\n<\/pre>\n<\/li>\n<\/ul>\n<h3>Implementaci\u00f3n #1, programaci\u00f3n tipo \"cron\" codificada.<\/h3>\n<p>Para nuestra primera implementaci\u00f3n, por ejemplo la Parte 1 de la serie, dise\u00f1aremos una estructura de control simple que es simplemente un documento KV JSON y tambi\u00e9n dos (2) Funciones Eventing que responder\u00e1n y actuar\u00e1n sobre la informaci\u00f3n en la estructura de control.<\/p>\n<p>A continuaci\u00f3n se muestra un dise\u00f1o de un documento JSON, o estructura de control, que nos permitir\u00e1 tener m\u00faltiples \"eventos\" programados. Cada evento programado tendr\u00e1 su propio documento de control con una CLAVE \u00fanica como recurring_event::1, recurring_event::1, ... recurring_event::N. La propia estructura JSON contiene informaci\u00f3n para \"reconstituir la clave\", ya que nuestro sistema de programaci\u00f3n responder\u00e1 a los cambios o actualizaciones (mutaciones) de los documentos de control, como alternar el estado \"activo\" para activar o desactivar la acci\u00f3n o cambiar el campo \"verbose\" que controla la cantidad y el estilo del registro.<\/p>\n<p>A continuaci\u00f3n se muestra un ejemplo de documento de control con CLAVE <strong>evento_recurrente::1<\/strong> que ejecutar\u00e1 la funci\u00f3n JavaScript <strong>doCronActionA<\/strong> a las 14:54 (14:30) todos los d\u00edas.<\/p>\n<table style=\"width: 100%;border: 1px solid #456789;padding: 1px;font-size: 80%;margin-bottom: 26px\">\n<tbody>\n<tr>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px;width: 33%\">Registro de control JSON<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">Descripci\u00f3n<\/th>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">{<\/td>\n<td style=\"padding: 4px\"><\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\"type\": \"recurring_event\",<\/td>\n<td style=\"padding: 4px\">La CLAVE ser\u00e1 &lt;&gt;::&lt;&gt;.<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\"id\":1,<\/td>\n<td style=\"padding: 4px\"><\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\"hora\":14,<\/td>\n<td style=\"padding: 4px\">La hora del d\u00eda 0-23, *, *2X, *4X para activar<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\"min\":54,<\/td>\n<td style=\"padding: 4px\">El minuto en la hora 0-59, *, *2X, *4X para activar<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0\u00a0 \"acci\u00f3n\": \"doCronActionA\",<\/td>\n<td style=\"padding: 4px\">Funci\u00f3n JavaScript que se ejecutar\u00e1 cuando se active el temporizador<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\"activo\":true,<\/td>\n<td style=\"padding: 4px\">Bandera para activar o desactivar este horario<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0\u00a0 \"verbose\": {<\/td>\n<td style=\"padding: 4px\">[OPCIONAL] control de registro<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"user_func\":2,<\/td>\n<td style=\"padding: 4px\">Nivel de registro para la l\u00f3gica de acci\u00f3n : 0=ninguno, etc. etc.<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"programador\":3<\/td>\n<td style=\"padding: 4px\">Nivel de registro para la l\u00f3gica cron : 0=ninguno, etc. etc.<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0\u00a0 },<\/td>\n<td style=\"padding: 4px\"><\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0\u00a0 \"din\u00e1mico\": {<\/td>\n<td style=\"padding: 4px\">control y estad\u00edsticas del sistema [DIN\u00c1MICO<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"estado\": \"brazo\",<\/td>\n<td style=\"padding: 4px\">\"armar\"|\"rearmar\"|\"pendiente\" cualquier valor != \"pendiente\" iniciar una programaci\u00f3n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"next_sched\": 0,<\/td>\n<td style=\"padding: 4px\">N\u00famero de segundos transcurridos desde la \u00e9poca hasta el siguiente horario deseado<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"prev_sched\": 0,<\/td>\n<td style=\"padding: 4px\">N\u00famero de segundos transcurridos desde la fecha de la programaci\u00f3n anterior<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"prev_etime\": 0,<\/td>\n<td style=\"padding: 4px\">N\u00famero de segundos transcurridos desde la \u00e9poca para el tiempo de ejecuci\u00f3n real del programa anterior<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"prev_delay\": 0,<\/td>\n<td style=\"padding: 4px\">N\u00famero de segundos que el temporizador se ha retrasado con respecto a la programaci\u00f3n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0 \u00a0\u00a0 \u00a0\"prev_atime\": 0<\/td>\n<td style=\"padding: 4px\">N\u00famero de segundos que ha tardado el usuario 'acci\u00f3n'<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">\u00a0\u00a0 }<\/td>\n<td style=\"padding: 4px\"><\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">}<\/td>\n<td style=\"padding: 4px\"><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Como Linux tradicional <em>crontab<\/em> puede establecer la hora y el minuto en n\u00fameros enteros legales, y tambi\u00e9n puede establecer <em>hora<\/em> a \"*\" para procesar todas las horas o ajuste <em>min<\/em> a \"*\" para procesar todos los minutos.<\/p>\n<p>Aunque no apoyaremos la <em>crontab<\/em> sintaxis admitimos dos configuraciones no est\u00e1ndar, a saber, si <u>fijar ambos<\/u> <em>hora<\/em> y <em>min<\/em> a \"*4X\" ejecutaremos y rearmaremos cuatro (4) veces por minuto y si los configuras ambos a \"*2X\" ejecutaremos y rearmaremos dos (2) veces por minuto.  A continuaci\u00f3n se muestra una tabla de horarios soportados con su descripci\u00f3n:<\/p>\n<table style=\"width: 100%;border: 1px solid #456789;padding: 1px;font-size: 80%;margin-bottom: 26px\">\n<tbody>\n<tr>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"60\">hora<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"72\">min<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"510\">Los valores pueden ser n\u00fameros o cadenas<\/th>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">13<\/td>\n<td style=\"padding: 4px\" width=\"72\">32<\/td>\n<td style=\"padding: 4px\" width=\"510\">Corre a las 13:32 (o 13:32)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*<\/td>\n<td style=\"padding: 4px\" width=\"72\">15<\/td>\n<td style=\"padding: 4px\" width=\"510\">Se ejecuta cada hora a los 15 minutos<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">8<\/td>\n<td style=\"padding: 4px\" width=\"72\">12<\/td>\n<td style=\"padding: 4px\" width=\"510\">Corre una vez al d\u00eda a las 8:32 (o 8:32 am)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*<\/td>\n<td style=\"padding: 4px\" width=\"72\">*<\/td>\n<td style=\"padding: 4px\" width=\"510\">Ejecutar una vez por minuto<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*2X<\/td>\n<td style=\"padding: 4px\" width=\"72\">*2X<\/td>\n<td style=\"padding: 4px\" width=\"510\">Se ejecuta dos veces por minuto - requiere que tanto la hora como el minuto est\u00e9n ajustados a \"*2X\".<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*4X<\/td>\n<td style=\"padding: 4px\" width=\"72\">*4X<\/td>\n<td style=\"padding: 4px\" width=\"510\">Funciona cuatro veces por minuto - requiere que la hora y el minuto est\u00e9n ajustados a \"*2X\".<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Eventualmente utilizaremos el Query Workbench para insertar el <em>cron<\/em> documentos de control, todos los cuales deben tener una CLAVE \u00fanica de <strong>evento_recurrente::#<\/strong> a una hora de ejecuci\u00f3n programada de 14:54 ( 2:54 pm), para la acci\u00f3n doCronActionA, podr\u00edamos utilizar la siguiente sentencia N1QL.<\/p>\n<p><u>No te preocupes por ejecutar ninguna sentencia N1QL en este momento,<\/u> realizaremos las sentencias N1QL m\u00e1s tarde despu\u00e9s de haber construido y desplegado nuestra Funci\u00f3n de Eventos.<\/p>\n<p>Puede crear un registro (o registros) de control en el cubo <strong>viaje-muestra<\/strong>y luego listarlo, armarlo, desarmarlo, ajustar el horario que sigue, cambiar el nivel de verbosidad para el registro, o borrarlo de la siguiente manera:<\/p>\n<table style=\"width: 100%;border: 1px solid #456789;padding: 1px;font-size: 80%;margin-bottom: 26px\">\n<tbody>\n<tr>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px;width: 30%\">Acci\u00f3n<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">Declaraci\u00f3n N1QL<\/th>\n<\/tr>\n<tr>\n<td style=\"padding: 4px;vertical-align: top\">Crear un calendario<\/td>\n<td style=\"padding: 4px\">INSERT INTO `travel-sample` (KEY,VALUE) VALUES (<br \/>\n\"recurring_event::1\", {<br \/>\n\"type\": \"recurring_event\",<br \/>\n\"id\":1,<br \/>\n\"hora\": \"14\u2033,<br \/>\n\"min\": \"54\u2033,<br \/>\n\"acci\u00f3n\": \"doCronActionA\",<br \/>\n\"activo\":true<br \/>\n}<br \/>\n);<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Crear un \u00edndice para consultar datos sin especificar claves<\/td>\n<td style=\"padding: 4px\">CREATE primary INDEX on `crondata` ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Mostrar todos los horarios ordenados por id<\/td>\n<td style=\"padding: 4px\">SELECT * FROM `crondata` WHERE type=&#8221;recurring_event&#8221; order by id ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Mostrar horario espec\u00edfico<\/td>\n<td style=\"padding: 4px\">SELECT * FROM `crondata` WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Armar o activar<\/td>\n<td style=\"padding: 4px\">UPDATE `crondata` SET active = true WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Desarmar o desactivar<\/td>\n<td style=\"padding: 4px\">UPDATE `crondata` SET active = false WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Ajustar el tiempo de activaci\u00f3n<\/td>\n<td style=\"padding: 4px\">UPDATE `crondata` SET hour = 11, min = 30 WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Ajustar el registro de la \"acci\u00f3n<\/td>\n<td style=\"padding: 4px\">UPDATE `crondata` SET verbose.user_data = 0 WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Ajustar el registro de la l\u00f3gica del programador<\/td>\n<td style=\"padding: 4px\">UPDATE `crondata` SET verbose.scheduler = 0 WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">Borrar el horario<\/td>\n<td style=\"padding: 4px\">DELETE FROM `crondata` WHERE type=&#8221;recurring_event&#8221; AND id=1 ;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Supongamos que tenemos cuatro (4) horarios activos, la ejecuci\u00f3n de la primera sentencia N1QL, por encima de la lista de todos ellos, por ejemplo.<\/p>\n<pre class=\"striped:false wrap-toggle:false lang:sh highlight:0 decode:true\">SELECT active, action, hour, min, type, id, verbose.user_func, verbose.scheduler\r\nFROM `crondata` where type=\"recurring_event\" order by id ;<\/pre>\n<p>Devolver\u00eda algo como la siguiente salida (vista de tabla en el Query Workbench):<\/p>\n<table style=\"width: 100%;border: 1px solid #456789;padding: 1px;font-size: 80%;margin-bottom: 26px\">\n<tbody>\n<tr>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">activo<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px;width: 20%\">acci\u00f3n<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">hora<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">id<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">min<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">programador<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px;width: 20%\">tipo<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\">usuario_func<\/th>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">verdadero<\/td>\n<td style=\"padding: 4px\">\"doCronActionA\"<\/td>\n<td style=\"padding: 4px\">14<\/td>\n<td style=\"padding: 4px\">1<\/td>\n<td style=\"padding: 4px\">54<\/td>\n<td style=\"padding: 4px\">1<\/td>\n<td style=\"padding: 4px\">\"evento_recurrente\"<\/td>\n<td style=\"padding: 4px\">2<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">verdadero<\/td>\n<td style=\"padding: 4px\">\"doCronActionB\"<\/td>\n<td style=\"padding: 4px\">*<\/td>\n<td style=\"padding: 4px\">2<\/td>\n<td style=\"padding: 4px\">*<\/td>\n<td style=\"padding: 4px\">1<\/td>\n<td style=\"padding: 4px\">\"evento_recurrente\"<\/td>\n<td style=\"padding: 4px\">1<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">verdadero<\/td>\n<td style=\"padding: 4px\">\"doCronActionC\"<\/td>\n<td style=\"padding: 4px\">*2X<\/td>\n<td style=\"padding: 4px\">3<\/td>\n<td style=\"padding: 4px\">*2X<\/td>\n<td style=\"padding: 4px\">4<\/td>\n<td style=\"padding: 4px\">\"evento_recurrente\"<\/td>\n<td style=\"padding: 4px\">4<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\">verdadero<\/td>\n<td style=\"padding: 4px\">\"doCronActionD\"<\/td>\n<td style=\"padding: 4px\">*<\/td>\n<td style=\"padding: 4px\">4<\/td>\n<td style=\"padding: 4px\">0<\/td>\n<td style=\"padding: 4px\">0<\/td>\n<td style=\"padding: 4px\">\"evento_recurrente\"<\/td>\n<td style=\"padding: 4px\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>En la tabla anterior tenemos cuatro acciones: la primera se ejecuta una vez al d\u00eda, la segunda cada minuto, la tercera cada 30 segundos y la cuarta cada hora.  En una pr\u00f3xima entrega de esta serie a\u00f1adiremos la funci\u00f3n \"d\u00eda de la semana\".<\/p>\n<p>El objeto anidado \"<em>verbose<\/em>\"si no se suministra ser\u00e1 por defecto { \"user_func\":1, \"scheduler\":1 } indicando un nivel de registro bajo o escueto para la funci\u00f3n de acci\u00f3n y tambi\u00e9n para la l\u00f3gica de programaci\u00f3n.  Un valor de 0 suprimir\u00e1 todos los mensajes de registro, es decir, doCronActionD, mientras que valores mayores ser\u00e1n m\u00e1s verbosos, es decir, como se define en doCronActionC.<\/p>\n<p>El objeto anidado \"<em>din\u00e1mico<\/em>\" si normalmente nunca se suministra y ser\u00e1 por defecto { \"state\": \"arm\", \"next_sched\": 0, \"prev_sched\": 0, \"prev_etime\": 0, \"prev_delay\": 0, \"prev_atime\": 0 } este es un bloc de notas para la programaci\u00f3n l\u00f3gica de eventos en ejecuci\u00f3n y tambi\u00e9n proporciona estad\u00edsticas \u00fatiles sobre los tiempos de ejecuci\u00f3n, por lo que debe ser tratado como de s\u00f3lo lectura.<\/p>\n<p>En este punto tenemos un dise\u00f1o de control de alto nivel, pero necesitamos l\u00f3gica para procesar nuestras estructuras de control, aqu\u00ed es donde el Servicio de Eventos de Couchbase, espec\u00edficamente una Funci\u00f3n de Eventos entra en juego.<\/p>\n<h3>Funciones del concurso<\/h3>\n<p>Este dise\u00f1o requiere dos (2) funciones de Eventing: una funci\u00f3n JavaScript principal \"cron_impl_2func_651\" y una peque\u00f1a funci\u00f3n JavaScript de ayuda \"cron_impl_2func_651_help\". Discutiremos cada secci\u00f3n de las funciones JavaScript que comprende la implementaci\u00f3n inicial combinada de c\u00f3digo JavaScript de casi 610 l\u00edneas (con aproximadamente 44% de las l\u00edneas son comentarios y espacios en blanco)<\/p>\n<p><u>No te preocupes por hacer un cortar y pegar en este momento<\/u>Como m\u00e1s tarde voy a proporcionar un enlace para descargar (para la importaci\u00f3n) las dos funciones de eventos necesarios y todos los ajustes necesarios en dos archivos llamados \"cron_impl_2func_651.json\" \"cron_impl_2func_651_help.json\" y tambi\u00e9n si lo prefiere las dos funciones unificadas completo que se puede cortar y pegar directamente.<\/p>\n<p>Nuestra funci\u00f3n principal de eventos \"cron_impl_2func_651\" estar\u00e1 compuesta por nueve (9) funciones JavaScript<\/p>\n<ul>\n<li>Tres (3) funciones de l\u00f3gica empresarial (dos de las cuales est\u00e1n vac\u00edas).\n<ul>\n<li>doCronActionA(doc) - una acci\u00f3n de usuario de ejemplo N1QL a ejecutar<\/li>\n<li>doCronActionB(doc) - un int\u00e9rprete de comandos de acci\u00f3n de usuario vac\u00edo para experimentos<\/li>\n<li>doCronActionC(doc) - un int\u00e9rprete de comandos de acci\u00f3n de usuario vac\u00edo para experimentos<\/li>\n<\/ul>\n<\/li>\n<li>Un (1) punto de entrada para concursos completos.\n<ul>\n<li>OnUpdate(doc, meta) - el punto de entrada est\u00e1ndar de Eventing para Inserciones o Actualizaciones<\/li>\n<\/ul>\n<\/li>\n<li>Uno (1) <em>cron<\/em> analizador sint\u00e1ctico para generar la siguiente programaci\u00f3n.\n<ul>\n<li>getNextRecurringDate(hour_str, min_str) - l\u00f3gica cron para encontrar la siguiente Fecha programada<\/li>\n<\/ul>\n<\/li>\n<li>Tres (3) funciones de apoyo para comprobar que la l\u00f3gica de negocio existe o formatear resultados.\n<ul>\n<li>verifyFunctionExistsViaEval(curDoc, id) - asegurarse de que tenemos una funci\u00f3n que ejecutar<\/li>\n<li>toNumericFixed(number, precision) - formatea un flotante a un estilo compacto<\/li>\n<li>toLocalISOTime(d) - formatea una fecha a un estilo compacto<\/li>\n<\/ul>\n<\/li>\n<li>Una (1) funci\u00f3n callback cuando se ejecutan los temporizadores.\n<ul>\n<li>Callback(doc) - una funci\u00f3n de devoluci\u00f3n de llamada para temporizadores programados<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Nuestra funci\u00f3n de ayuda para eventos \"cron_impl_2func_651_help\" estar\u00e1 compuesta por una (1) funci\u00f3n JavaScript<\/p>\n<ul>\n<li>Un (1) punto de entrada para concursos completos.\n<ul>\n<li>OnUpdate(doc, meta) - el punto de entrada est\u00e1ndar de Eventing para Inserciones o Actualizaciones<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>En las secciones siguientes recorreremos cada una de las funciones JavaScript anteriores.<\/p>\n<h3>Necesitamos una funci\u00f3n JavaScript, por ejemplo, la l\u00f3gica de negocio para ejecutar en un horario peri\u00f3dico.<\/h3>\n<p>Lo primero que queremos es una rutina o funci\u00f3n que contenga nuestra l\u00f3gica de negocio que ejecutaremos bas\u00e1ndonos en nuestras reglas crontab.  Llamaremos al m\u00e9todo JavaScript <strong>doCronActionA(doc)<\/strong>sin embargo, puede llamarse como se quiera, por ejemplo <em>doPeriodicLedgerBalance(doc),<\/em> los \u00fanicos requisitos para nuestras funciones de \"acci\u00f3n\" que implementan nuestra l\u00f3gica de negocio programada son los siguientes:<\/p>\n<ul>\n<li>Tiene un par\u00e1metro: doc, un documento de control como el descrito anteriormente de type=\"recurring_event\".<\/li>\n<li>El nombre real de JavaScript coincide con el campo \"acci\u00f3n\" del documento de control.<\/li>\n<li>Devuelve <strong><em>verdadero<\/em><\/strong> sobre el \u00e9xito y <em><strong>falso<\/strong><\/em> sobre el fracaso<\/li>\n<li>Utiliza <em><strong>doc.verbose.user_func<\/strong><\/em> para controlar el registro si 0 es silencioso, si 1 emite una sola l\u00ednea, si 2 emite cualquier informaci\u00f3n de registro que se necesite para depurar la funci\u00f3n, etc. etc..<\/li>\n<\/ul>\n<p>Escribiremos nuestra funci\u00f3n <strong>doCronActionA(doc)<\/strong>para ejecutar una consulta N1QL incrustada ) para combinar los recuentos de l\u00edneas a\u00e9reas por pa\u00eds y, a continuaci\u00f3n, crear un \u00fanico documento KV con los datos calculados.<\/p>\n<pre class=\"striped:false wrap-toggle:false lang:sh highlight:0 decode:true\">SELECT country, count( * ) AS cnt FROM `travel-sample` WHERE `type` = 'airline' GROUP BY country;<\/pre>\n<p>En mi sistema de prueba un peque\u00f1o nodo \u00fanico no<a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/learn\/services-and-indexes\/services\/services.html#services-and-multi-dimensional-scaling\">MDS<\/a> (ejecutando todos los servicios de Couchbase) el N1QL anterior tarda unos 20 ms. (para mayor claridad fingir que es supercomplejo tarda 10 segundos en completarse).<\/p>\n<p>La idea aqu\u00ed es que el documento KV final calculado y resumido pueda ser cargado r\u00e1pidamente por 100K (o un mill\u00f3n) de mutaciones Eventing por segundo sin la sobrecarga adicional de comunicaci\u00f3n con los nodos del servicio de Consulta y de procesar sentencias N1QL en cada mutaci\u00f3n.<\/p>\n<p>Deber\u00eda ser obvio que el objetivo de esta l\u00f3gica de negocio en particular, <strong>doCronActionA(doc)<\/strong>es crear una cach\u00e9 semiest\u00e1tica que se actualice peri\u00f3dicamente seg\u00fan un calendario.<\/p>\n<p>Todo lo que estamos haciendo realmente (y es bastante r\u00e1pido) es obtener un recuento de aerol\u00edneas por pa\u00eds a partir del conjunto de documentos de la muestra de viajes.  A medida que usamos N1QL construimos un documento y eventualmente lo escribimos en KV como un documento resumido.   El punto clave a destacar aqu\u00ed es que no queremos repetir el mismo trabajo para millones de mutaciones cada uno, sobre todo porque algunos c\u00e1lculos podr\u00edan tomar 10 segundos de tiempo de c\u00e1lculo del servicio de consulta cada vez que iniciamos una consulta N1QL incrustada desde una funci\u00f3n Eventing.<\/p>\n<p>A continuaci\u00f3n mostramos la funci\u00f3n JavaScript que queremos que se ejecute una vez al d\u00eda (o quiz\u00e1s una vez cada hora, etc.).  Observa que el nombre de la funci\u00f3n coincide con el nombre del campo de acci\u00f3n de la estructura de control.  Para m\u00e1s detalles sobre la terminolog\u00eda de Eventing y las construcciones del lenguaje, consulta los documentos y ejemplos de Couchbase en <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/eventing\/eventing-overview.html\">Servicio de Eventing: Fundamentos<\/a>.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function doCronActionA(doc) {\r\n  try {\r\n    \/\/ Check that doc has desired values\r\n    if (!doc.type || doc.type !== \"recurring_event\" || !doc.active || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ this is a 6.5 N1QL query (feature not available in GA prior to 6.5)\r\n    \/\/ Create an embedded N1QL iterator by issuing a SELECT statement to get the\r\n    \/\/ counts of airlines by country.  Make a new document and write it out to KV \r\n\r\n    \/\/ We will use the iterator to create a KV document representing the results of a\r\n    \/\/ HARD lengthy embedded N1QL query and write it back to KV, the idea is to keep\r\n    \/\/ a calculation up to date once a day such that it that can be read 'quickly' \r\n    \/\/ by other Eventing Functions, other Couchbase services or SDKs.   \r\n\r\n    \/\/ Consider if we had 1 million docs in a minute do we really want to use N1QL\r\n    \/\/ to recalculate something that is almost static for all 1 million documents, of \r\n    \/\/ course not, so we make an intermediate value that can be read into Eventing\r\n    \/\/ and used via a single 'light weight' KV read.\r\n\r\n    var q_iter = SELECT country,\r\n      count( * ) cnt\r\n    FROM `travel-sample`\r\n    WHERE `type` = 'airline'\r\n    GROUP BY country;\r\n\r\n    \/\/ loop through the result set and update the map 'accumulate'\r\n    var accumulate = {};\r\n    var idx = 0;\r\n    for (var val of q_iter) {\r\n      if (doc.verbose.user_func &gt;= 2)\r\n        log(doc.action + ' N1QL idx ' + idx + ', country ' + val.country + \" cnt \" + val.cnt);\r\n      accumulate[val.country] = val.cnt;\r\n      idx++;\r\n    }\r\n    \/\/ close out embedded N1QL iterator\r\n    q_iter.close();\r\n\r\n    \/\/ Now let\u2019s make a cached KV document representing a HARD length embedded N1QL\r\n    \/\/ query and write it back to KV, we need a KEY and a type and id and then we \r\n    \/\/ upsert it into the `travel-sample` bucket.\r\n\r\n    var cachedoc = {};\r\n    cachedoc.type = \"cron_cache\";\r\n    cachedoc.id = \"airlines_by_country\";\r\n    cachedoc.date = new Date();\r\n    cachedoc.data = accumulate;\r\n    var ckey = cachedoc.type + '::' + cachedoc.id;\r\n    ts_bkt[ckey] = cachedoc;\r\n    if (doc.verbose.user_func &gt;= 2) {\r\n      log(doc.action + ' upsert to KV with KEY ' + ckey + ' cachedoc ', cachedoc);\r\n    }\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}<\/pre>\n<p>La funci\u00f3n anterior simplemente 1) consulta el cubo de muestras de viajes para extraer datos, en este caso el recuento de aerol\u00edneas para cada pa\u00eds, 2) crea un nuevo documento KV y una nueva clave y lo escribe en el cubo de muestras de viajes para su uso posterior.<\/p>\n<p>Adem\u00e1s, como parte de este ejemplo hemos construido un registro que responde a un ajuste num\u00e9rico de verbosidad que a) registra una sola l\u00ednea si el documento de control tiene un valor para doc.verbose.user_func == 1 o b) emite m\u00e1s informaci\u00f3n si el valor de doc.verbose.user_func &gt;= 2.<\/p>\n<p>Se trata de un marco gen\u00e9rico que puede ejecutar una (1) <em>cron<\/em> acci\u00f3n o incluso mil (1000) de <em>cron<\/em> acciones. Como tal, he proporcionado dos funciones adicionales \"vac\u00edas\" - como se se\u00f1al\u00f3 antes, podr\u00edan haber sido nombradas de cualquier manera.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function doCronActionB(doc) {\r\n  try {\r\n    \/\/ check that doc has desired values\r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ YOUR LOGIC HERE\r\n\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}<\/pre>\n<p>y<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function doCronActionC(doc) {\r\n  try {\r\n    \/\/ check that doc has desired values\r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ YOUR LOGIC HERE\r\n\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}<\/pre>\n<p>Estas funciones doCronActionB y doCronActionC son triviales, ya que simplemente registran informaci\u00f3n en el registro de la aplicaci\u00f3n de eventos de la funci\u00f3n de eventos.  Consulte <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/eventing\/eventing-debugging-and-diagnosability.html#logging-functions\">Funciones de registro<\/a> para m\u00e1s detalles. Por supuesto, necesita un documento de control de type=\"recurring_event\" con active=true y una acci\u00f3n como action = \"doCronActionB\" para habilitarlos y ejecutarlos realmente.<\/p>\n<h3>Necesitamos un punto de entrada o manejador de eventos<\/h3>\n<p>A partir de la versi\u00f3n 6.5, el servicio de eventos admite dos puntos de entrada o controladores <strong>OnUpdate(doc, meta)<\/strong> y <strong>OnDelete(meta)<\/strong> s\u00f3lo nos interesa el <strong>OnUpdate(doc,meta)<\/strong> para este ejemplo.<\/p>\n<p>En <strong>OnUpdate(doc,meta)<\/strong> es llamado cuando se crea o modifica (muta) cualquier documento del bucket de origen y filtra inmediatamente los documentos que no interesan. <a id=\"footnote_3\"><\/a><sup>[3]<\/sup><\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function OnUpdate(doc, meta) {\r\n  \/\/ fix for 6.5.X growing bucket ops\r\n  if (doc.type === \"_tmp_vbs\") genNoopTimers(doc, meta, 30);\r\n  if (!cron_bkt[\"fix_timer_scan_issue::1\"]) {\r\n      cron_bkt[\"fix_timer_scan_issue::1\"] = {};\r\n  }\r\n    \r\n  try {\r\n    \/\/ Check if further analysis is needed we only trigger on an active recurring_event \r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n\r\n    var update_doc = false;\r\n    if (!doc.dynamic) {\r\n      \/\/ Add if missing doc.dynamic with defaults\r\n      doc.dynamic = {\r\n        \"state\": \"arm\",\r\n        \"next_sched\": 0,\r\n        \"prev_sched\": 0,\r\n        \"prev_etime\": 0,\r\n        \"prev_delay\": 0,\r\n        \"prev_atime\": 0\r\n      };\r\n      \/\/ we need to update the document once we have the next schedule\r\n      update_doc = true;\r\n    }\r\n    if (!doc.verbose) {\r\n      \/\/ Add if missing doc.dynamic with defaults\r\n      doc.verbose = {\r\n        \"user_func\": 1,\r\n        \"scheduler\": 1\r\n      };\r\n      \/\/ we need to update the document once we have the next schedule\r\n      update_doc = true;\r\n    }\r\n    \/\/ Do not process dynamic.state pending\r\n    if (!doc.dynamic || !doc.dynamic.state || doc.dynamic.state === \"pending\") return;\r\n\r\n    var mid = doc.type + \"::\" + doc.id; \/\/ this is the same as meta.id or the KEY\r\n    var hour = doc.hour;\r\n    var min = doc.min;\r\n\r\n    \/\/ Do an eval check the JavaScript function exists. The eval occurs in a common \r\n    \/\/ utility function shared with RecurringCallback\r\n    if (!verifyFunctionExistsViaEval(doc, mid)) {\r\n      \/\/ doc.action did not exist, we have already logged the issue\r\n      return;\r\n    }\r\n\r\n    \/\/ Get the next valid execution time\r\n    var date_timer = getNextRecurringDate(hour, min);\r\n    var next_sched = Math.round(date_timer.getTime() \/ 1000);\r\n    if (!update_doc &amp;&amp; next_sched !== doc.dynamic.next_sched) {\r\n      \/\/ the next_sched should be the same as the setting from the helper application, however\r\n      \/\/ if we undeploy\/deploy or pause\/resume we might haver to reschedule to the next time slot\r\n      log('OnUpdate U ' + mid + ' calculated next_sched !== doc.dynamic.next_sched, delta ' +\r\n        (next_sched - doc.dynamic.next_sched) + ', reschedule');\r\n      update_doc = true;\r\n    }\r\n\r\n    if (update_doc) {\r\n      \/\/ this mutation is recursive and will be suppressed, we ensure we have a dynamic structure\r\n      doc.dynamic.next_sched = next_sched;\r\n\r\n      \/\/ rather then the call a function, to trap and retry if there is a resource issue\r\n      \/\/ cron_bkt[mid] = doc;\r\n      if (!tryBucketKvWriteWithLog('OnUpdate F', mid, doc)) {\r\n        \/\/ Failed to write doc to cron_bkt[key] the error has been logged\r\n        \/\/ and there is nothing more we can do.\r\n        return;\r\n      }\r\n    }\r\n\r\n    \/\/ Schedule an Eventing timer\r\n    var timer_id = createTimer(Callback, date_timer, null, doc);\r\n    if (doc.verbose.scheduler &gt;= 1) {\r\n      log('OnUpdate A ' + mid + ' rcv mutation (initial or rearm) schedule timer at ' +\r\n        toLocalISOTime(date_timer));\r\n    }\r\n    if (doc.verbose.scheduler &gt;= 2) {\r\n      log('OnUpdate B ' + mid + ' recurring timer was created, timer_id ' + timer_id);\r\n    }\r\n  } catch (e) {\r\n    log('OnUpdate E ' + meta.id + ', Error exception:', e);\r\n  }\r\n}<\/pre>\n<p>La clave aqu\u00ed es que el <em>cron<\/em> en nuestro manejador s\u00f3lo se preocupa por los documentos que tienen doc.type de \"recurring_event y tambi\u00e9n un doc.active de true.  Adem\u00e1s, en este ejemplo hemos creado un rastreo para el m\u00e9todo <em>cron<\/em> l\u00f3gica de mantenimiento que s\u00f3lo se registra en el registro de la aplicaci\u00f3n si el documento de control tiene un valor para doc.verbose &gt;= 3.<\/p>\n<p>Si s\u00f3lo ejecuta unas pocas programaciones, puede desactivar el trabajo en el espacio de usuario o \"<em>arreglo para 6.5.X cubo creciente ops<\/em>\" comentando cuatro l\u00edneas de c\u00f3digo en el bloque OnUpdate anterior para \"cron_impl_2func_651\" como sigue:<\/p>\n<pre class=\"toolbar-overlay:false lang:js decode:true\">function OnUpdate(doc, meta) {\r\n  \/\/ fix for 6.5.X growing bucket ops\r\n  \/\/ if (doc.type === \"_tmp_vbs\") genNoopTimers(doc, meta, 30);\r\n  \/\/ if (!cron_bkt[\"fix_timer_scan_issue::1\"]) {\r\n  \/\/     cron_bkt[\"fix_timer_scan_issue::1\"] = {};\r\n  \/\/ }\r\n<\/pre>\n<h3>Necesitamos c\u00f3digo para solucionar posibles operaciones de crecimiento de cubos para 6.5.X<\/h3>\n<p>A partir de la versi\u00f3n 6.5.X necesitamos un \"<em>arreglo para 6.5.X cubo creciente ops<\/em>\"que ocurre en sistemas inactivos con muchos temporizadores programados en el futuro.  Este c\u00f3digo garantiza que un temporizador de Eventing se ha disparado recientemente en un vBucket (lo que soluciona el problema para el vBucket dado en funci\u00f3n de cada funci\u00f3n).<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">\/\/ FIXUP: ADDIN FUNCTON\r\nfunction noopTimer(context) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        if (context.type === \"_tmp_vbs\" &amp;&amp; context.vb === 0) { \r\n            \/\/ log(\"noopTimer timers firing, printing only for vBucket 0\");\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in callback noopTimer:\", e);\r\n    }\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\nfunction rearmTimer(context) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        if (context.type === \"_tmp_vbs\" &amp;&amp; context.vb === 0) { \r\n            \/\/ Update\/touch all docs in the helper_bucket the helper function will then\r\n            \/\/ mutate all 1024 of type == vbs_seed (64 on MacOS) to create a recuring cycle.\r\n            \/\/ log(\"noopTimer timer fired all 1024 vBuckets, logging only vb 0\", context);\r\n            \r\n            \/\/ generate a mutation to re-arm the HELPER function: fix_scan_issue\r\n            \/\/ which will in turn make new mutations for this Function\r\n            var cur = cron_bkt[context.key];\r\n            if (cur &amp;&amp; cur.ts_millis === context.ts_millis) {\r\n                \/\/ log(\"rearmTimer update fix_timer_scan_issue::1 in helper_bucket alias only for vBucket 0\");\r\n                var now = new Date();\r\n                cron_bkt[\"fix_timer_scan_issue::1\"] = { \"last_update\": now };\r\n            } else {\r\n                \/\/ NOOP we had multiple timer cycles, just let this one quietly stop.\r\n            }\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in callback rearmTimer:\", e);\r\n    }\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\nfunction genNoopTimers(doc, meta, seconds) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        \/\/ redundant but play it safe\r\n        if (doc.type === \"_tmp_vbs\") {\r\n            \/\/ Since we are using an different function a timer on all our vBuckets do immeadiately (can take up to 15 seconds)\r\n            \/\/ If we used cross bucket recursion to rearm all the timers in a recurring fashion we would add a delay of at least 40 seconds.\r\n            createTimer(noopTimer, new Date(), null, doc);\r\n            if (doc.vb === 0) { \r\n                \/\/ Update\/touch all docs in the helper_bucket the helper function will then\r\n                \/\/ mutate all 1024 of type == vbs_seed (64 on MacOS) to create a recuring cycle.\r\n                \/\/ log(\"noopTimer timer fired all 1024 vBuckets, logging only vb 0\", context);\r\n            \r\n                \/\/ generate a mutation to re-arm the HELPER function: fix_scan_issue\r\n                \/\/ which will in turn make new mutations for this Function\r\n                \r\n                \/\/ log(\"genNoopTimers make timer to rearm fix_timer_scan_issue::1\");\r\n                createTimer(rearmTimer, new Date(new Date().getTime() + seconds * 1000), null, doc);\r\n            }\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in genNoopTimers:\", e);\r\n    }\r\n}<\/pre>\n<h3>\u00a0Necesitamos una utilidad para calcular la siguiente hora en el horario<\/h3>\n<p>La siguiente funci\u00f3n getNextRecurringDate(hour, min) determinar\u00e1 una hora para ejecutar la acci\u00f3n definida como parte de nuestra programaci\u00f3n.  Esto no es una implementaci\u00f3n completa de <em>cron<\/em>En lugar de ello, contiene las caracter\u00edsticas est\u00e1ndar clave para ejecutarse una vez al d\u00eda, una vez por hora y una vez por minuto.  Tambi\u00e9n contiene alguna sintaxis no est\u00e1ndar para proporcionar la capacidad de ejecutar dos veces por minuto o cuatro veces por minuto.<\/p>\n<p>Como se ha descrito anteriormente, la funci\u00f3n <strong>getNextRecurringDate(hora, min)<\/strong> permite lo siguiente (la tabla se duplica a continuaci\u00f3n), siendo las dos \u00faltimas no est\u00e1ndar.<a id=\"footnote_4\"><\/a><sup>[4]<\/sup><\/p>\n<table style=\"width: 100%;border: 1px solid #456789;padding: 1px;font-size: 80%;margin-bottom: 26px\">\n<tbody>\n<tr>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"60\">hora<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"72\">min<\/th>\n<th style=\"padding: 4px;font-weight: bold;border-bottom: solid 1px\" width=\"510\">Los valores pueden ser n\u00fameros o cadenas<\/th>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">13<\/td>\n<td style=\"padding: 4px\" width=\"72\">32<\/td>\n<td style=\"padding: 4px\" width=\"510\">Corre a las 13:32 (o 13:32)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*<\/td>\n<td style=\"padding: 4px\" width=\"72\">15<\/td>\n<td style=\"padding: 4px\" width=\"510\">Se ejecuta cada hora a los 15 minutos<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">8<\/td>\n<td style=\"padding: 4px\" width=\"72\">12<\/td>\n<td style=\"padding: 4px\" width=\"510\">Corre una vez al d\u00eda a las 8:32 (o 8:32 am)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*<\/td>\n<td style=\"padding: 4px\" width=\"72\">*<\/td>\n<td style=\"padding: 4px\" width=\"510\">Ejecutar una vez por minuto<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*2X<\/td>\n<td style=\"padding: 4px\" width=\"72\">*2X<\/td>\n<td style=\"padding: 4px\" width=\"510\">Se ejecuta dos veces por minuto - requiere que tanto la hora como el minuto est\u00e9n ajustados a \"*2X\".<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 4px\" width=\"60\">*4X<\/td>\n<td style=\"padding: 4px\" width=\"72\">*4X<\/td>\n<td style=\"padding: 4px\" width=\"510\">Funciona cuatro veces por minuto - requiere que la hora y el minuto est\u00e9n ajustados a \"*2X\".<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A continuaci\u00f3n se muestra una implementaci\u00f3n de la l\u00f3gica necesaria para determinar la pr\u00f3xima vez que debe activarse un temporizador de Eventing en nuestro programa, en el caso de que la l\u00f3gica de usuario de nuestro primer ejemplo <strong>doCronActionA(doc) <\/strong>no se completa a tiempo, por ejemplo, si se sobrepasa el tiempo real, se seleccionar\u00e1 el siguiente cuanto de la programaci\u00f3n.  Tenga en cuenta tanto los Temporizadores como sus Funciones Padre. As\u00ed, si una Funci\u00f3n de Eventos tiene un tiempo de espera de ejecuci\u00f3n por defecto de 60 segundos, si es necesario esta configuraci\u00f3n puede ser ajustada o aumentada.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function getNextRecurringDate(hour_str, min_str) {\r\n  \/\/ Note Javascript Dates are in milliseconds\r\n  var date_now = new Date();\r\n  var date_ret = new Date();\r\n  var hour;\r\n  var min;\r\n\r\n  try {\r\n    hour = parseInt(hour_str);\r\n  } catch (e) {}\r\n  try {\r\n    min = parseInt(min_str);\r\n  } catch (e) {}\r\n\r\n  \/\/ Note, this is only a simplistic partial 'crontab' syntax with some slight extensions\r\n  \/\/ it allows once a day, once an hour, once a minute.  It also contains some non-standard \r\n  \/\/ syntax to provide the ability to execute twice a minute or four times a minute.\r\n\r\n  if (hour_str === '*4X' &amp;&amp; min_str === '*4X') {\r\n    \/\/ once every 15 seconds or four times a minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(15);\r\n    while (date_ret.getTime() &lt; date_now.getTime()) {\r\n      date_ret.setSeconds(date_ret.getSeconds() + 15);\r\n    }\r\n    return date_ret;\r\n  } else\r\n  if (hour_str === '*2X' &amp;&amp; min_str === '*2X') {\r\n    \/\/ once every 30 seconds or twice a minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(30);\r\n    while (date_ret.getTime() &lt; date_now.getTime()) {\r\n      date_ret.setSeconds(date_ret.getSeconds() + 30);\r\n    }\r\n    return date_ret;\r\n  } else\r\n  if (hour_str === '*' &amp;&amp; min_str === '*') {\r\n    \/\/ once a minute \r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(date_ret.getMinutes() + 1);\r\n  } else\r\n  if (hour_str !== '*' &amp;&amp; isNaN(hour) === false &amp;&amp; min_str === '*') {\r\n    \/\/ once a minute only for a given hour\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(date_ret.getMinutes() + 1);\r\n    if (date_ret.getTime() &lt; date_now.getTime()) { date_ret.setHours(hour); } if (date_ret.getTime() &gt; date_now.getTime()) {\r\n      date_ret.setDate(date_ret.getDate() + 1);\r\n      date_ret.setSeconds(0);\r\n      date_ret.setMinutes(0);\r\n      date_ret.setHours(hour);\r\n    }\r\n  } else\r\n  if (hour_str === '*' &amp;&amp; min_str !== '*' &amp;&amp; isNaN(min) === false) {\r\n    \/\/ once a hour at a given minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(min);\r\n    \/\/ schedule for next hour\r\n    date_ret.setHours(date_ret.getHours() + 1);\r\n  } else\r\n  if (isNaN(hour) === false &amp;&amp; isNaN(min) === false) {\r\n    \/\/ once a day for a given hour and a given minute \r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(min);\r\n    date_ret.setHours(hour);\r\n    if (date_ret.getTime() &lt; date_now.getTime()) {\r\n      \/\/ schedule for tomorrow\r\n      date_ret.setDate(date_ret.getDate() + 1);\r\n    }\r\n  } else {\r\n    log('getNextRecurringDate illegal input hour_str &lt;' + hour_str + '&gt; min_str &lt;' + min_str + '&gt;');\r\n    throw new Error('getNextRecurringDate illegal input hour_str &lt;' + hour_str + '&gt; min_str &lt;' + min_str + '&gt;');\r\n    return null;\r\n  }\r\n  return date_ret;\r\n}<\/pre>\n<h3>Necesitamos algunas peque\u00f1as utilidades<\/h3>\n<p>La funci\u00f3n de utilidad com\u00fan que simplemente comprueba si nuestro JavaScript existe utilizado por ambos <strong>OnUpdate(doc,meta), <\/strong>mostrado arriba, y el temporizador <strong>Devoluci\u00f3n de llamada(doc), <\/strong>que se muestra m\u00e1s adelante.  A continuaci\u00f3n <strong>verifyFunctionExistsViaEval(curDoc, id) <\/strong>que recibe dos argumentos: un documento de control JSON y la CLAVE de dicho documento.<\/p>\n<p>Esto nos permite saber inmediatamente, en el despliegue, si hubo un problema con una falta de coincidencia de nombres entre el documento de registro de control JSON y el nombre real de la funci\u00f3n de l\u00f3gica de negocio en el c\u00f3digo JavaScript.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function verifyFunctionExistsViaEval(curDoc, id) {\r\n  var result = false;\r\n  try {\r\n    \/\/ check for function if missing this is invalid return result\r\n    result = eval(\"typeof \" + curDoc.action + \" === 'function';\");\r\n    if (result === false) {\r\n      if (curDoc.verbose.scheduler &gt;= 1)\r\n        log(\"Warn\/Disable (No Action and No Re-Arm), because required 'action' of \" +\r\n          curDoc.action + \"(doc) does not exist, id is\", id);\r\n      return result;\r\n    }\r\n  } catch (e) {\r\n    log('verifyFunctionExistsViaEval Error exception:', e);\r\n  }\r\n  return result;\r\n}<\/pre>\n<p>Tenga en cuenta que si se intenta ejecutar una funci\u00f3n inexistente, el usuario final recibir\u00e1 una advertencia en el registro de la aplicaci\u00f3n. <em>cron_impl_2func_651.log<\/em> \u00a0para corregir el problema.<\/p>\n<p style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%\">2020-04-22T16:20:38.725-07:00 [INFO] \"Advertir\/Desactivar (Sin Acci\u00f3n y Sin Rearmar), porque la 'acci\u00f3n' requerida de doCronMyNewFunction(doc) no existe, id es\" \"recurring_event::1\"<\/p>\n<p>Esta correcci\u00f3n se puede hacer a trav\u00e9s de una Pausa\/Reanudar a\u00f1adiendo la funci\u00f3n y luego ajustando el documento de control con el id especificado o CLAVE (a trav\u00e9s de un toggle activo a falso y luego verdadero) -o- ajustando el documento de control para apuntar a una funci\u00f3n existente en su manejador.<\/p>\n<p>A continuaci\u00f3n, la utilidad <strong>toNumericFixed(n\u00famero, precisi\u00f3n)<\/strong> s\u00f3lo permite un bonito formato compacto de flotantes para nuestros mensajes de registro.<\/p>\n<pre class=\"\">function toNumericFixed(number, precision) {\r\n  var multi = Math.pow(10, precision);\r\n  return Math.round((number * multi).toFixed(precision + 1)) \/ multi;\r\n}<\/pre>\n<p>Por \u00faltimo, la utilidad <strong>toLocalISOTime(d)<\/strong> s\u00f3lo permite un formato compacto de fechas para nuestros mensajes de registro.<\/p>\n<pre class=\"\">function toLocalISOTime(d) {\r\n  var tzoffset = (new Date()).getTimezoneOffset() * 60000; \/\/offset in milliseconds\r\n  return (new Date(d.getTime() - tzoffset)).toISOString().slice(0, -1);\r\n}<\/pre>\n<h3>Necesitamos una llamada de retorno del temporizador para ejecutar la l\u00f3gica de usuario y volver a activar el temporizador<\/h3>\n<p>La \u00faltima funci\u00f3n JavaScript de \"cron_impl_2func_651\" es la llamada de retorno del temporizador, a la que se llama cuando se activa el temporizador programado. La funci\u00f3n de devoluci\u00f3n de llamada debe ser una funci\u00f3n de nivel superior que reciba un \u00fanico argumento, el contexto.<\/p>\n<p>En este caso en nuestro manejador OnUpdate referenciamos una funci\u00f3n JavaScript de <strong>Devoluci\u00f3n de llamada(doc)<\/strong> con un contexto de doc (nuestro documento de control del planificador activo de type=\"recurring_event\")<\/p>\n<p>En la versi\u00f3n 6.6 podemos crear otro temporizador dentro de un temporizador pero para todas las versiones anteriores necesitaremos lanzar una mutaci\u00f3n a una funci\u00f3n \"helper\" (evitamos cuidadosamente la recursi\u00f3n infinita).  En la 6.6 la funci\u00f3n helper no es necesaria y la l\u00f3gica se ha simplificado sustancialmente.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function Callback(doc) {\r\n  try {\r\n    var fired_at = new Date();\r\n\r\n    \/\/ Check if further analysis is needed we only trigger on a recurring_event that is active\r\n    if (doc.type !== \"recurring_event\") return;\r\n    \/\/ doc must have 'action', 'dynamic {}', verbose {}, dynamic.state\r\n    if (!doc.action || !doc.dynamic || !doc.verbose || !doc.dynamic.state) return;\r\n    \/\/ process any doc.dynamic.state BUT pending \r\n    if (doc.dynamic.state === \"pending\") return;\r\n\r\n    \/\/ ==================\r\n    \/\/ Check if still active\r\n\r\n    \/\/ We make sure that in KV the 'doc' still exists and that it is still active if not just \r\n    \/\/ return thus skipping the action and not Re-arming the timer. Note `travel-sample` is \r\n    \/\/ aliased to the map 'cron_bkt\r\n\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    var curDoc = null;\r\n    try {\r\n      \/\/ read the current version of doc from KV, e.g. curDoc\r\n      curDoc = cron_bkt[mid];\r\n    } catch (e) {} \/\/ needed for pre 6.5, note pure 6.5+ deployment returns null sans exception\r\n\r\n    var reason = null;\r\n    if (!curDoc || curDoc === null) {\r\n      reason = \"cron document is missing\";\r\n    } else\r\n    if (!curDoc.active) {\r\n      reason = \"cron document has active = false\";\r\n    } else\r\n    if (!curDoc.dynamic.state || curDoc.dynamic.state !== doc.dynamic.state) {\r\n      reason = \"cron document wrong dynamic.state expected \" + doc.dynamic.state;\r\n    } else\r\n    if (crc64(doc) !== crc64(curDoc)) {\r\n      reason = \"cron document changed\";\r\n    }\r\n\r\n    if (reason !== null) {\r\n      if (!curDoc || curDoc === null || curDoc.verbose.scheduler &gt;= 1) {\r\n        log('Callback X ' + mid + \" ignore\/stop this timer's schedule because \" + reason);\r\n      }\r\n      if (!curDoc || curDoc === null || curDoc.verbose.scheduler &gt;= 4) {\r\n        log('Callback Y ' + mid + ' timer doc', doc);\r\n        log('Callback Z ' + mid + ' KV curDoc', curDoc);\r\n      }\r\n      return;\r\n    }\r\n\r\n    \/\/ ==================\r\n    \/\/ Verify user routine exists and if so eval it \r\n\r\n    \/\/ Assume curDoc.action contains something like \"doCronActionA\" and we have a function in \r\n    \/\/ this handler like \"doCronActionA(doc)\". Below we use curDoc as the end user should be \r\n    \/\/ able to alter the eval'd JavaScript function.  We will execute two (2) evals.\r\n\r\n    \/\/ First eval check the JavaScript function exists.  The eval occurs in a common \r\n    \/\/ utility function shared with Callback\r\n    if (!verifyFunctionExistsViaEval(curDoc, mid)) {\r\n      \/\/ curDoc.action did not exist, we have already logged the issue\r\n      return;\r\n    }\r\n\r\n    \/\/ Second eval execute and process the user function we execute the defined function \r\n    \/\/ with an argument of curDoc\r\n    var beg_act = new Date();\r\n    var result = null;\r\n    eval(\"result = \" + curDoc.action + \"(curDoc);\");\r\n    var end_act = new Date();\r\n    var atime_ms = end_act.getTime() - beg_act.getTime();\r\n\r\n    if (curDoc.verbose.scheduler &gt;= 2)\r\n      log('Callback R ' + mid + ' action took ' + toNumericFixed((atime_ms \/ 1000), 3) +\r\n        ' sec., returned ' + result);\r\n\r\n    \/\/ ==================\r\n    \/\/ Calculate next time and mutate the control document for our our helper function\r\n    \/\/ which will create another mutation such that OnUpdate of this function will pick\r\n    \/\/ it up and generate the timer (avoids the MB-38554 issue).\r\n\r\n    var hour = curDoc.hour;\r\n    var min = curDoc.min;\r\n    var date_timer = getNextRecurringDate(hour, min);\r\n\r\n    curDoc.dynamic.prev_delay =\r\n      toNumericFixed(((fired_at.getTime() \/ 1000) - curDoc.dynamic.next_sched), 3);\r\n    curDoc.dynamic.prev_sched = curDoc.dynamic.next_sched;\r\n    curDoc.dynamic.prev_etime = Math.round(fired_at.getTime() \/ 1000);\r\n    curDoc.dynamic.prev_atime = toNumericFixed((atime_ms \/ 1000), 3);\r\n\r\n    curDoc.dynamic.state = \"pending\";\r\n    curDoc.dynamic.next_sched = Math.round(date_timer.getTime() \/ 1000);\r\n    \r\n    try {\r\n      cron_bkt[mid] = curDoc;\r\n    } catch (e) {\r\n      log('Callback help: F ' + mid + ' FATAL could not update KV cron cycle ' + curDoc.action);\r\n      return;\r\n    }\r\n\r\n    if (curDoc.verbose.scheduler &gt;= 1) {\r\n      log('Callback A ' + mid + ' gen mutation #1 to doc to force schedule rearm at ' +\r\n        toLocalISOTime(date_timer));\r\n    }\r\n    if (curDoc.verbose.scheduler &gt;= 2) {\r\n      log('Callback B ' + mid + ' sched ' + curDoc.dynamic.prev_sched +\r\n        ', actual ' + curDoc.dynamic.prev_etime +\r\n        ', delay ' + curDoc.dynamic.prev_delay +\r\n        ', took ' + curDoc.dynamic.prev_atime);\r\n    }\r\n    if (curDoc.verbose.scheduler &gt;= 3) {\r\n      log('Callback C ' + mid + ' curDoc', curDoc);\r\n    }\r\n  } catch (e) {\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    log('Callback E ' + mid + ' Error exception:', e);\r\n  }\r\n}<\/pre>\n<h3>Necesitamos una funci\u00f3n de ayuda para desencadenar una nueva mutaci\u00f3n<\/h3>\n<p>Dado que antes de la versi\u00f3n 6.6 (que a\u00fan no se ha publicado) no se puede crear un temporizador desde la llamada de retorno de un temporizador en ejecuci\u00f3n, necesitamos una segunda funci\u00f3n de eventos (junto con \"allow_interbucket_recursion\":true) para activar una mutaci\u00f3n de forma que podamos generar todos nuestros temporizadores en el punto de entrada OnUpdate(doc,meta) de la funci\u00f3n de eventos principal.  Hacemos esto de la siguiente manera:<\/p>\n<ol>\n<li>cron_impl_2func_651 <strong>OnUpdate(doc,meta)<\/strong> recibe una mutaci\u00f3n, programa un temporizador<\/li>\n<li>cron_impl_2func_651 Despu\u00e9s de una cantidad de tiempo cuando el temporizador madura el <strong>Devoluci\u00f3n de llamada(doc)<\/strong> se ejecuta la rutina, primero ejecuta la acci\u00f3n de usuario deseada y luego crea una mutaci\u00f3n #1 en el documento de control (que no es vista por la Funci\u00f3n creadora para evitar la recursividad)<\/li>\n<li>cron_impl_2func_651_help <strong>OnUpdate(doc,meta)<\/strong> recibe una mutaci\u00f3n, hace otra mutaci\u00f3n #2 en el documento de control esto desencadena 1. anterior en un ciclo sin fin.<\/li>\n<\/ol>\n<p>Nota, en la versi\u00f3n 6.6 de Couchbase no necesitamos una funci\u00f3n de ayuda en absoluto porque se te permite crear un temporizador desde dentro de un temporizador en ejecuci\u00f3n.  Esto simplifica enormemente la l\u00f3gica necesaria para hacer un <em>cron<\/em> sistema<a id=\"footnote_2\"><\/a><sup>[2]<\/sup>.<\/p>\n<p>La \u00fanica funci\u00f3n JavaScript en \"cron_impl_2func_651_help\" <strong>OnUpdate(doc,meta)<\/strong> se muestra a continuaci\u00f3n.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:default decode:true\">function OnUpdate(doc, meta) {\r\n  \/\/ fix for 6.5.X growing bucket ops\r\n  if (meta.id.startsWith(\"fix_timer_scan_issue:\")) upsertOneDocPerBucket(doc, meta);\r\n  \r\n  try {\r\n    \/\/ Check that doc has desired values\r\n    if (!doc.type || doc.type !== \"recurring_event\" || !doc.active || doc.active != true) return;\r\n    \/\/ doc must have 'action', 'dynamic {}', verbose {}, dynamic.state\r\n    if (!doc.action || !doc.dynamic || !doc.verbose || !doc.dynamic.state) return;\r\n    \/\/ Only process state pending this will only exist for a 'breif' time\r\n    if (doc.dynamic.state !== \"pending\") return;\r\n\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    var newdoc = null;\r\n    try {\r\n      \/\/ read the current version of doc from KV, e.g. curDoc\r\n      newdoc = cron_bkt[mid];\r\n    } catch (e) {} \/\/ needed for pre 6.5, note pure 6.5+ deployment returns null sans exception\r\n    \r\n    var reason = null;\r\n    if (!newdoc || newdoc == null) {\r\n      reason = \"cron document is missing\";\r\n    } else\r\n    if (!newdoc.active) {\r\n      reason = \"cron document has active = false\";\r\n    } else\r\n    if (!newdoc.dynamic.state || newdoc.dynamic.state !== doc.dynamic.state) {\r\n      reason = \"cron document wrong dynamic.state expected \" + doc.dynamic.state;\r\n    } else\r\n    if (crc64(doc) !== crc64(newdoc)) {\r\n      reason = \"cron document changed\";\r\n    }\r\n    if (reason != null) {\r\n      if (!newdoc || newdoc == null || newdoc.verbose.scheduler &gt;= 1) {\r\n        log('OnUpdate help: X stopping schedule because ' + reason + ',', newdoc)\r\n        return;\r\n      }\r\n    }\r\n\r\n    newdoc.dynamic.state = \"rearm\";\r\n    \/\/ cron_bkt[mid] = newdoc;\r\n    if (!tryBucketKvWriteWithLog('OnUpdate help: F', mid, newdoc)) {\r\n      \/\/ Failed to write newdoc to cron_bkt[key] the error has been logged\r\n      \/\/ and there is nothing more we can do.\r\n      return;\r\n    }\r\n\r\n    if (newdoc.verbose.scheduler &gt;= 1) {\r\n      log('OnUpdate help: A ' + mid + ' mutation #2 to doc to force schedule rearm');\r\n    }\r\n    if (newdoc.verbose.scheduler &gt;= 3) {\r\n      log('OnUpdate help: B ' + mid + ',', newdoc);\r\n    }\r\n  } catch (e) {\r\n    log('OnUpdate help: E ' + meta.id + ', Error exception:', e);\r\n  }\r\n}\r\n\r\nfunction tryBucketKvWriteWithLog(tag, key, doc) {\r\n  var success = false;\r\n  var tries = 0;\r\n  while (tries &lt; 10) {\r\n    tries++;\r\n    try {\r\n      \/\/ critical that the below succeeds, because if it doesn't the cron cycle will break\r\n      cron_bkt[key] = doc;\r\n      success = true;\r\n      break;\r\n    } catch (e) {\r\n      log(tag + ' ' + key + ' WARN failed to update KV tries ' + tries, e);\r\n    }\r\n  }\r\n  if (!success) {\r\n    log(tag + ' ' + +key + ' FATAL could not update KV cron cycle, tried ' + tries + ', stoping ' + curDoc.action);\r\n  }\r\n  return success;\r\n}<\/pre>\n<h3>La funci\u00f3n helper necesita algunas utilidades<\/h3>\n<p>Estas utilidades proporcionan un <em>arreglo para 6.5.X cubo creciente ops<\/em> asegur\u00e1ndose de que se dispara un temporizador de eventos en cada vBucket en el momento oportuno.<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction upsertOneDocPerBucket(doc, meta) {\r\n\r\n    var crcTable = makeCRC32Table();\r\n\r\n    \/\/ make one doc per bucket\r\n    var isVerbose = 0;\r\n    var isMacOS = false; \/\/ would be nice if this was an exposed constant in Eventing\r\n    var numvbs = 1024;   \/\/ default is linux\/PC\r\n    if (isMacOS) {\r\n        numvbs = 64;\r\n    }\r\n\r\n    var beg = (new Date).getTime();\r\n    var result = getKeysToCoverAllPartitions(crcTable, \"_tmp_vbs:\", numvbs);\r\n\r\n    for (var vb=0; vb&lt;numvbs; vb++) {\r\n        \/\/ brute force to fit a key prefix into a vBucket\r\n         var tst = result[vb];\r\n        if (isVerbose &gt; 1  || (isVerbose == 1) &amp;&amp; (vb &lt; 3 || vb &gt; numvbs -4)) {\r\n            log(\"KEY: \" + tst);\r\n        } else {\r\n            if (vb == 5) console.log(\"\\t*\\n\\t*\\n\\t*\");\r\n        }\r\n        \/\/ update the items to trigger a mutation for our PRIMARY fucntion\r\n        cron_bkt[tst] = { \"type\": \"_tmp_vbs\", \"vb\": vb, \"ts_millis\": beg, \"key\": tst };\r\n    }\r\n    var end = (new Date).getTime();\r\n    log(\"seeding one doc to each vBucket in primary_bucket alias (took \" + (end - beg) + \" mililis)\");\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction showHex(n) {\r\n    return n.toString(16);\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction makeCRC32Table() {\r\n    var crcTable = [];\r\n    var c;\r\n    for(var n =0; n &lt; 256; n++){\r\n        c = n;\r\n        for(var k =0; k &lt; 8; k++){\r\n            c = ((c&amp;1) ? (0xEDB88320 ^ (c &gt;&gt;&gt; 1)) : (c &gt;&gt;&gt; 1));\r\n        }\r\n        crcTable[n] = c;\r\n    }\r\n    return crcTable;\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction crc32(crcTable,str) {\r\n    var crc = 0 ^ (-1);\r\n    for (var i = 0; i &lt; str.length; i++ ) {\r\n        crc = (crc &gt;&gt;&gt; 8) ^ crcTable[(crc ^ str.charCodeAt(i)) &amp; 0xFF];\r\n    }\r\n    return (crc ^ (-1)) &gt;&gt;&gt; 0;\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction getKeysToCoverAllPartitions(crcTable,keyPrefix,partitionCount) {\r\n    var result = [];\r\n    var remaining = partitionCount;\r\n    for (var i = 0; remaining &gt; 0; i++) {\r\n      var key = keyPrefix + i;\r\n      var rv = (crc32(crcTable,key) &gt;&gt; 16) &amp; 0x7fff;\r\n      var actualPartition = rv &amp; partitionCount - 1;\r\n      if (!result[actualPartition] || result[actualPartition] === undefined) {\r\n        result[actualPartition] = key;\r\n        remaining--;\r\n      }\r\n    }\r\n    return result;\r\n}\r\n<\/pre>\n<h3>Ahora vamos a desplegar las dos Funciones Eventing<\/h3>\n<p>Hemos revisado mucho c\u00f3digo y el dise\u00f1o del planificador inicial, ahora es el momento de ver c\u00f3mo funciona todo junto.<\/p>\n<p>Recuerde que en este ejemplo hay tres cubos <strong>muestra-viaje (<\/strong>un ejemplo de conjunto de datos por defecto), <strong>metadatos<\/strong>(el cubo de metadatos es un bloc de notas para Eventing y puede compartirse con otras funciones de Eventing) y, por \u00faltimo, la funci\u00f3n <strong>crondata<\/strong> (que contiene nuestros cronogramas). La direcci\u00f3n <strong>viaje-muestra<\/strong> tiene un tama\u00f1o de 100 MB y los otros dos cubos <strong>metadatos<\/strong> y <strong>crondata<\/strong> deben tener un tama\u00f1o de 200 MB y existir ya seg\u00fan las instrucciones de \"Requisitos previos\".<\/p>\n<ul>\n<li>Verifique la configuraci\u00f3n actual de su cubo accediendo a la p\u00e1gina <strong>Consola Web de Couchbase &gt; Cubos<\/strong> p\u00e1gina:<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8703 size-full\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_01_00_buckets.png\" alt=\"Eventing cron update\" width=\"2040\" height=\"650\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-300x96.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-1024x326.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-768x245.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-1536x489.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-20x6.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_00_buckets-1320x421.png 1320w\" sizes=\"auto, (max-width: 2040px) 100vw, 2040px\" \/><\/li>\n<\/ul>\n<p>Para desplegar la funci\u00f3n Eventing \"<em>cron_impl_2func_651<\/em>\"puede seguir uno de estos dos m\u00e9todos:<\/p>\n<ul>\n<li>Complejidad b\u00e1sica, M\u00e9todo #1 Descargar\/Importar<\/li>\n<li>Complejidad media, M\u00e9todo #2 A\u00f1adir funci\u00f3n manualmente, Cortar y pegar JavaScript<\/li>\n<\/ul>\n<h3>M\u00e9todo #1 Descargar\/Importar<\/h3>\n<h4>Importar la 1\u00aa funci\u00f3n \"cron_impl_2func_651\"<\/h4>\n<p>Descargue la primera funci\u00f3n Eventing con todos los ajustes necesarios, haga clic con el bot\u00f3n derecho del rat\u00f3n en el siguiente enlace y seleccione\u00a0<strong>Guardar enlace como<\/strong>\u00a0para descargar el archivo <a href=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Strabala\/CronFiles\/cron_impl_2func_651.json\"><strong>cron_impl_2func_651.json<\/strong><\/a> en tu sistema de archivos local.<\/p>\n<p>Desde el <strong>Consola Web de Couchbase &gt; Eventos<\/strong> haga clic en <strong>IMPORTAR<\/strong>navega hasta el archivo <em>cron_impl_2func_651.json<\/em>selecci\u00f3nelo y \u00e1bralo. La p\u00e1gina <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> aparece el cuadro de di\u00e1logo.<\/p>\n<p>En el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> para cada uno de los elementos de la funci\u00f3n, facilite la siguiente informaci\u00f3n. Tenga en cuenta el archivo JSON <em>cron_impl_2func_651.json<\/em> preconfigurar\u00e1 todos los ajustes correctamente para este ejemplo:<\/p>\n<ul>\n<li>Para el <strong>Cubo de origen<\/strong> y compruebe que est\u00e1 en <strong>crondata<\/strong>.<\/li>\n<li>Para el <strong>Cubo de metadatos<\/strong> y compruebe que est\u00e1 en <strong>metadatos<\/strong>.<\/li>\n<li>Compruebe que <strong>cron_impl_2func_651<\/strong>\u00a0es el nombre de la funci\u00f3n que est\u00e1 creando en el campo <strong>Nombre de la funci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>[Paso opcional] Introduzca el texto <b>Un programador cron parte 1<\/b>en el <strong>Descripci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>Para el <strong>Ajustes<\/strong> utilice los valores por defecto.<\/li>\n<li>Para el <strong>Fijaciones<\/strong> verifique que existen dos enlaces.<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>cron_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>crondata<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>ts_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>viaje-muestra<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>La configuraci\u00f3n en el cuadro de di\u00e1logo debe ser como la siguiente:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8704\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_01_add_function.png\" alt=\"Eventing cron update\" width=\"643\" height=\"640\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function.png 1286w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-300x299.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-1024x1019.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-150x150.png 150w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-768x764.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-65x65.png 65w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-50x50.png 50w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-20x20.png 20w\" sizes=\"auto, (max-width: 643px) 100vw, 643px\" \/><\/li>\n<li>Despu\u00e9s de verificar toda la informaci\u00f3n requerida en el cuadro de di\u00e1logo A\u00d1ADIR FUNCI\u00d3N, haga clic en Siguiente: A\u00f1adir C\u00f3digo. Aparecer\u00e1 la p\u00e1gina <strong>cron_impl_2func_651<\/strong> (con el c\u00f3digo JavaScript precargado).<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8711 size-full\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_03_code_editor_done.png\" alt=\"Eventing cron update\" width=\"2040\" height=\"1542\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-300x227.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1024x774.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-768x581.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1536x1161.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-20x15.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1320x998.png 1320w\" sizes=\"auto, (max-width: 2040px) 100vw, 2040px\" \/><\/li>\n<li>Para volver a la pantalla de Eventos, pulse el bot\u00f3n '<strong>&lt; volver a Concurso completo<\/strong>(debajo del editor) o haga clic en el enlace <strong>Eventos<\/strong> ficha.<\/li>\n<\/ul>\n<h4>Importar la 2\u00aa Funci\u00f3n \"cron_impl_2func_651_help\"<\/h4>\n<p>Descargue la segunda funci\u00f3n Eventing con todos los ajustes necesarios, haga clic con el bot\u00f3n derecho del rat\u00f3n en el siguiente enlace y seleccione <strong>Guardar enlace como<\/strong>\u00a0para descargar el archivo <a href=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Strabala\/CronFiles\/cron_impl_2func_651_help.json\"><strong>cron_impl_2func_651_help.json<\/strong><\/a> en tu sistema de archivos local.<\/p>\n<p>Desde el <strong>Consola Web de Couchbase &gt; Eventos<\/strong> haga clic en <strong>IMPORTAR<\/strong>navega hasta el archivo <em>cron_impl_2func_651_help.json<\/em>selecci\u00f3nelo y \u00e1bralo. La p\u00e1gina <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> aparece el cuadro de di\u00e1logo.<\/p>\n<p>En el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> para cada uno de los elementos de la funci\u00f3n, facilite la siguiente informaci\u00f3n. Tenga en cuenta el archivo JSON <em>cron_impl_2func_651_help.json<\/em> preconfigurar\u00e1 todos los ajustes correctamente para este ejemplo:<\/p>\n<ul>\n<li>Para el <strong>Cubo de origen<\/strong> y compruebe que est\u00e1 en <strong>crondata<\/strong>.<\/li>\n<li>Para el <strong>Cubo de metadatos<\/strong> y compruebe que est\u00e1 en <strong>metadatos<\/strong>.<\/li>\n<li>Compruebe que <strong>cron_impl_2func_651_help<\/strong>\u00a0es el nombre de la funci\u00f3n que est\u00e1 creando en el campo <strong>Nombre de la funci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>[Paso opcional] Introduzca el texto <b>Un ayudante cron como programador parte 1<\/b>en el <strong>Descripci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>Para el <strong>Ajustes<\/strong> utilice los valores por defecto.<\/li>\n<li>Para el <strong>Fijaciones<\/strong> verifique que s\u00f3lo existe un enlace.<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>cron_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>crondata<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>La configuraci\u00f3n en el cuadro de di\u00e1logo debe ser como la siguiente:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8706\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_01_add_function_help.png\" alt=\"Eventing cron update\" width=\"643\" height=\"640\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help.png 1286w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-300x299.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-1024x1019.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-150x150.png 150w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-768x764.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-65x65.png 65w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-50x50.png 50w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-20x20.png 20w\" sizes=\"auto, (max-width: 643px) 100vw, 643px\" \/><\/li>\n<li>Despu\u00e9s de verificar toda la informaci\u00f3n requerida en el cuadro de di\u00e1logo A\u00d1ADIR FUNCI\u00d3N, haga clic en Siguiente: A\u00f1adir C\u00f3digo. Aparecer\u00e1 la p\u00e1gina <strong>cron_impl_2func_651_help<\/strong> (con el c\u00f3digo JavaScript precargado).<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8729\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/ev_cr_1u_02_code_editor_done_help.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"770\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help.png 2044w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-300x227.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1024x774.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-768x580.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1536x1160.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-20x15.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1320x997.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Para volver a la pantalla de Eventos, pulse el bot\u00f3n '<strong>&lt; volver a Concurso completo<\/strong>(debajo del editor) o haga clic en el enlace <strong>Eventos<\/strong> ficha.<\/li>\n<\/ul>\n<h3>M\u00e9todo #2 A\u00f1adir funci\u00f3n manualmente, cortar y pegar JavaScript<\/h3>\n<h4>Crear manualmente \"cron_impl_2func_651\"<\/h4>\n<p>Para a\u00f1adir la primera funci\u00f3n de Eventing desde <strong>Consola Web de Couchbase &gt; Eventos<\/strong> haga clic en <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong>para a\u00f1adir una nueva funci\u00f3n. La direcci\u00f3n <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> aparece el cuadro de di\u00e1logo.<\/p>\n<p>En el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> para cada elemento de la funci\u00f3n, facilite la siguiente informaci\u00f3n:<\/p>\n<ul>\n<li>Para el <strong>Cubo de origen<\/strong> seleccione <strong>crondata<\/strong>.<\/li>\n<li>Para el <strong>Cubo de metadatos<\/strong> seleccione <strong>metadatos<\/strong>.<\/li>\n<li>Visite <strong>cron_impl_2func_651<\/strong>\u00a0es el nombre de la funci\u00f3n que est\u00e1 creando en el campo <strong>Nombre de la funci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>[Paso opcional] Introduzca el texto <b>Un programador cron parte 1<\/b>en el <strong>Descripci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>Para el <strong>Ajustes<\/strong> utilice los valores por defecto.<\/li>\n<li>Para el <strong>Fijaciones<\/strong> crear dos enlaces:<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>cron_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>crondata<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>ts_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>viaje-muestra<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>Una vez configurados los ajustes, el cuadro de di\u00e1logo deber\u00eda tener este aspecto:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8704\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_01_add_function.png\" alt=\"Eventing cron update\" width=\"643\" height=\"640\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function.png 1286w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-300x299.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-1024x1019.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-150x150.png 150w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-768x764.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-65x65.png 65w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-50x50.png 50w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function-20x20.png 20w\" sizes=\"auto, (max-width: 643px) 100vw, 643px\" \/><\/li>\n<li>Tras facilitar toda la informaci\u00f3n requerida en el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> pulse <strong>Siguiente A\u00f1adir c\u00f3digo<\/strong>. En <strong>cron_impl_2func_651<\/strong> aparece el cuadro de di\u00e1logo. La p\u00e1gina <strong>cron_impl_2func_651<\/strong>\u00a0contiene inicialmente un bloque de c\u00f3digo. Usted sustituir\u00e1 su <strong>cron_impl_2func_651<\/strong>\u00a0en este bloque.<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8707\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_02_code_editor.png\" alt=\"Eventing cron update\" width=\"800\" height=\"186\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor.png 2042w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-300x70.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-1024x239.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-768x179.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-1536x358.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-20x5.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor-1320x308.png 1320w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/li>\n<li style=\"list-style-type: none\"><\/li>\n<li>Copie el siguiente c\u00f3digo fuente JavaScript de Eventing Function (618 l\u00edneas) y p\u00e9guelo en el bloque de c\u00f3digo del marcador de posici\u00f3n de <strong>cron_impl_2func_651<\/strong>\n<pre class=\"height-set:true height:800 toolbar-overlay:false nums:false lang:js decode:true\">\/* \r\nFunction \"cron_impl_2func_651\" also requires \"cron_impl_2func_651_help\"\r\n\r\nCreate a basic cron system using Eventing allows a recurring function to execute activity at a \r\nspecified time every day, hour, min, 30 sec., and 15 sec.  We use a bucket called 'crondata' \r\naliased to 'cron_bkt' which can hold one or more control documents of type = \"recurring_event\".\r\n\r\nThe following uses of timers do not work reliably in Couchbase versions 6.5 and 6.5.1\r\n  a) scheduling an Eventing timer within a timer's callback \r\n  b) overwriting an existing timer by id\r\n  \r\nIn addition the ability to cancel a timer does not exist in Couchbase versions 6.5 and 6.5.1\r\n  \r\nFor this example, we supply one real user function that builds a recurring 'static' cache document\r\nfrom bucket `travel-sample` via an N1QL query and save the result back to `travel-sample` via\r\nthe alais 'ts_bkt'.  This JavaScript function is doCronActionA(), we also provide two placeholders\r\ndoCronActionB() and doCronActionC() for additional experimentation.\r\n\r\nTest Doc:\r\n   {\r\n        \"type\":\"recurring_event\",   \/\/ The KEY will be &lt;&lt;type&gt;&gt;::&lt;&lt;id&gt;&gt;\r\n        \"id\":1,                     \/\/\r\n        \"hour\":14,                  \/\/ The hour of the day 0-23, *, *2X, *4X to trigger\r\n        \"min\":54,                   \/\/ The minute in the hour 0-59, *, *2X, *4X to trigger\r\n        \"action\":\"doCronActionA\",   \/\/ What function to run on the trigger\r\n        \"active\":false,             \/\/ Flag to arm or disable this schedule\r\n        \"verbose\" : {\r\n          \"user_func\":2,            \/\/ Logging level for the action logic : 0=none, etc. etc.\r\n          \"scheduler\":3             \/\/ Logging level for the cron logic   : 0=none, etc. etc.\r\n        },\r\n        \"dynamic\" : {\r\n          \"state\":\"arm\",            \/\/ States \"arm\"|\"rearm\"|\"pending\" if any value but \"pending\" start a schedule\r\n          \"next_sched\": 0,          \/\/ Number of seconds since epoch to next desired schedule\r\n          \"prev_sched\": 0,          \/\/ Number of seconds since epoch for previous schedule\r\n          \"prev_etime\": 0,          \/\/ Number of seconds since epoch for previous schedule actual exec time\r\n          \"prev_delay\": 0,          \/\/ Number of seconds that the timer was delayed from the schedule\r\n          \"prev_atime\": 0           \/\/ Number of seconds taken by the user 'action'\r\n        }\r\n    }\r\n    \r\n    INSERT INTO `crondata` (KEY,VALUE) VALUES (\"recurring_event::1\", \r\n    {\r\n        \"type\":\"recurring_event\",\r\n        \"id\":1,\r\n        \"hour\":14,\r\n        \"min\":54,\r\n        \"action\":\"doCronActionA\",\r\n    \t\"verbose\" : {\r\n          \"user_func\":2,\r\n          \"scheduler\":3\r\n        },\r\n        \"active\":false,\r\n\t    \"dynamic\" : {\r\n          \"state\": \"arm\",\r\n          \"next_sched\": 0,\r\n          \"prev_sched\": 0,\r\n          \"prev_etime\": 0,\r\n          \"prev_delay\": 0,\r\n          \"prev_atime\": 0\r\n\t    }\r\n    } \r\n    );\r\n\r\nNote, you can omit verbose{} and dynamic{} as they will be auto-created by this main Eventing \r\nFunction \"cron_impl_2func_651\". If verbose{} is missing the logging levels will default to \r\nverbose\" : {  \"user_func\":1, \"scheduler\":1 }\r\n\r\n    INSERT INTO `crondata` (KEY,VALUE) VALUES (\"recurring_event::1\", \r\n    {\r\n        \"type\":\"recurring_event\",\r\n        \"id\":1,\r\n        \"hour\":14,\r\n        \"min\":54,\r\n        \"action\":\"doCronActionA\",\r\n        \"active\":false\r\n    } \r\n    );\r\n\r\nN1QL : Make an index to query data without specifying keys\r\n    CREATE primary INDEX on `crondata` ;\r\n\r\nN1QL : Verify or inspect settings in schedule\t        \r\n    SELECT * FROM `crondata` WHERE type=\"recurring_event\";\r\n\r\nN1QL : Arm or set active\t    \r\n    UPDATE `crondata` SET active = true WHERE type=\"recurring_event\" AND id=1 ;\r\n    \r\nN1QL : Disarm or set inactive\t\r\n    UPDATE `crondata` SET active = false WHERE type=\"recurring_event\" AND id=1 ;\r\n    \r\nN1QL : Adjust time of trigger\t\r\n    UPDATE `crondata` SET hour = 11, min = 30 WHERE type=\"recurring_event\" AND id=1 ;\r\n\r\nN1QL : Adjust logging\t        \r\n    UPDATE `crondata` SET verbose.user_func = 1,  verbose.scheduler = 0 WHERE type=\"recurring_event\" AND id=1 ;\r\n\r\nN1QL : Delete the schedule\t    \r\n    DELETE FROM `crondata` WHERE type=\"recurring_event\" AND id=1 ;\r\n         \r\nThe action field is important it 'should' exist in this Eventing Function note it could be any \r\nJavaScript name e.g. MyFunc and you must implement like the example doCronActionA(doc) where\r\ndoc will be the currently active item of type = 'recurring_event' read from the alias bucket\r\n\u2018cron_bkt\u2019 when the timer is fired.  The action JavaScript function should return  either true\r\nor false used for logging purposes.  If the action does not exist it is an error and a warning\r\nis logged and the timer is disabled.\r\n\r\nIn Couchbase version 6.5+ to add a new cron like daily function just pause the active handler \r\ninsert your new function doCronActionB(doc) {...} then Resume the eventing handler.  The nice \r\nthing is if a timer was to be fired will the function was paused it will NOT be lost, when you \r\nresume the function it will be processed at the next available time slot.  \r\n\r\nAny change to a control structure will create a new recurring schedule or timer and cancel the \r\ncurrent previous schedule this includes changing the verbosity level. The previous timer will \r\ncontinue to run however when executed it will do a Checksum on the current control structure \r\nfrom KV against it\u2019s passed context and if different the Callback will ignore the old schedule.\r\nThis logic could be altered to process immediately if the schedule has expired search for the \r\nstring \"OnUpdate U\" in the code below.\r\n*\/\r\n\r\n\/\/ ==================\r\n\/* BEG USER FUNCTIONS TO RUN ONCE A DAY, HOUR, OR MINUTE - ANYTHING YOU WANT BELOW *\/\r\nfunction doCronActionA(doc) {\r\n  try {\r\n    \/\/ Check that doc has desired values\r\n    if (!doc.type || doc.type !== \"recurring_event\" || !doc.active || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ this is a 6.5 N1QL query (feature not available in GA prior to 6.5)\r\n    \/\/ Create an embedded N1QL iterator by issuing a SELECT statement to get the\r\n    \/\/ counts of airlines by country.  Make a new document and write it out to KV \r\n\r\n    \/\/ We will use the iterator to create a KV document representing the results of a\r\n    \/\/ HARD lengthy embedded N1QL query and write it back to KV, the idea is to keep\r\n    \/\/ a calculation up to date once a day such that it that can be read 'quickly' \r\n    \/\/ by other Eventing Functions, other Couchbase services or SDKs.   \r\n\r\n    \/\/ Consider if we had 1 million docs in a minute do we really want to use N1QL\r\n    \/\/ to recalculate something that is almost static for all 1 million documents, of \r\n    \/\/ course not, so we make an intermediate value that can be read into Eventing\r\n    \/\/ and used via a single 'light weight' KV read.\r\n\r\n    var q_iter = SELECT country,\r\n      count( * ) cnt\r\n    FROM `travel-sample`\r\n    WHERE `type` = 'airline'\r\n    GROUP BY country;\r\n\r\n    \/\/ loop through the result set and update the map 'accumulate'\r\n    var accumulate = {};\r\n    var idx = 0;\r\n    for (var val of q_iter) {\r\n      if (doc.verbose.user_func &gt;= 2)\r\n        log(doc.action + ' N1QL idx ' + idx + ', country ' + val.country + \" cnt \" + val.cnt);\r\n      accumulate[val.country] = val.cnt;\r\n      idx++;\r\n    }\r\n    \/\/ close out embedded N1QL iterator\r\n    q_iter.close();\r\n\r\n    \/\/ Now let\u2019s make a cached KV document representing a HARD length embedded N1QL\r\n    \/\/ query and write it back to KV, we need a KEY and a type and id and then we \r\n    \/\/ upsert it into the `travel-sample` bucket.\r\n\r\n    var cachedoc = {};\r\n    cachedoc.type = \"cron_cache\";\r\n    cachedoc.id = \"airlines_by_country\";\r\n    cachedoc.date = new Date();\r\n    cachedoc.data = accumulate;\r\n    var ckey = cachedoc.type + '::' + cachedoc.id;\r\n    ts_bkt[ckey] = cachedoc;\r\n    if (doc.verbose.user_func &gt;= 2) {\r\n      log(doc.action + ' upsert to KV with KEY ' + ckey + ' cachedoc ', cachedoc);\r\n    }\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}\r\n\r\nfunction doCronActionB(doc) {\r\n  try {\r\n    \/\/ check that doc has desired values\r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ YOUR LOGIC HERE\r\n\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}\r\n\r\nfunction doCronActionC(doc) {\r\n  try {\r\n    \/\/ check that doc has desired values\r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ YOUR LOGIC HERE\r\n\r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}\r\n\r\n\/* END USER FUNCTIONS TO RUN ONCE A DAY, HOUR, OR MINUTE - ANYTHING YOU WANT ABOVE *\/\r\n\/\/ ==================\r\n\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\nfunction noopTimer(context) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        if (context.type === \"_tmp_vbs\" &amp;&amp; context.vb === 0) { \r\n            \/\/ log(\"noopTimer timers firing, printing only for vBucket 0\");\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in callback noopTimer:\", e);\r\n    }\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\nfunction rearmTimer(context) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        if (context.type === \"_tmp_vbs\" &amp;&amp; context.vb === 0) { \r\n            \/\/ Update\/touch all docs in the helper_bucket the helper function will then\r\n            \/\/ mutate all 1024 of type == vbs_seed (64 on MacOS) to create a recuring cycle.\r\n            \/\/ log(\"noopTimer timer fired all 1024 vBuckets, logging only vb 0\", context);\r\n            \r\n            \/\/ generate a mutation to re-arm the HELPER function: fix_scan_issue\r\n            \/\/ which will in turn make new mutations for this Function\r\n            var cur = cron_bkt[context.key];\r\n            if (cur &amp;&amp; cur.ts_millis === context.ts_millis) {\r\n                \/\/ log(\"rearmTimer update fix_timer_scan_issue::1 in helper_bucket alias only for vBucket 0\");\r\n                var now = new Date();\r\n                cron_bkt[\"fix_timer_scan_issue::1\"] = { \"last_update\": now };\r\n            } else {\r\n                \/\/ NOOP we had multiple timer cycles, just let this one quietly stop.\r\n            }\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in callback rearmTimer:\", e);\r\n    }\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\nfunction genNoopTimers(doc, meta, seconds) {\r\n    \/\/ fix for 6.5.X growing bucket ops\r\n    try {\r\n        \/\/ redundant but play it safe\r\n        if (doc.type === \"_tmp_vbs\") {\r\n            \/\/ Since we are using an different function a timer on all our vBuckets do immeadiately (can take up to 15 seconds)\r\n            \/\/ If we used cross bucket recursion to rearm all the timers in a recurring fashion we would add a delay of at least 40 seconds.\r\n            createTimer(noopTimer, new Date(), null, doc);\r\n            if (doc.vb === 0) { \r\n                \/\/ Update\/touch all docs in the helper_bucket the helper function will then\r\n                \/\/ mutate all 1024 of type == vbs_seed (64 on MacOS) to create a recuring cycle.\r\n                \/\/ log(\"noopTimer timer fired all 1024 vBuckets, logging only vb 0\", context);\r\n            \r\n                \/\/ generate a mutation to re-arm the HELPER function: fix_scan_issue\r\n                \/\/ which will in turn make new mutations for this Function\r\n                \r\n                \/\/ log(\"genNoopTimers make timer to rearm fix_timer_scan_issue::1\");\r\n                createTimer(rearmTimer, new Date(new Date().getTime() + seconds * 1000), null, doc);\r\n            }\r\n        }\r\n    } catch (e) {\r\n        log(\"OnUpdate Exception in genNoopTimers:\", e);\r\n    }\r\n}\r\n\r\nfunction OnUpdate(doc, meta) {\r\n  \/\/ fix for 6.5.X growing bucket ops\r\n  if (doc.type === \"_tmp_vbs\") genNoopTimers(doc, meta, 30);\r\n  if (!cron_bkt[\"fix_timer_scan_issue::1\"]) {\r\n      cron_bkt[\"fix_timer_scan_issue::1\"] = {};\r\n  }\r\n    \r\n  try {\r\n    \/\/ Check if further analysis is needed we only trigger on an active recurring_event \r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n\r\n    var update_doc = false;\r\n    if (!doc.dynamic) {\r\n      \/\/ Add if missing doc.dynamic with defaults\r\n      doc.dynamic = {\r\n        \"state\": \"arm\",\r\n        \"next_sched\": 0,\r\n        \"prev_sched\": 0,\r\n        \"prev_etime\": 0,\r\n        \"prev_delay\": 0,\r\n        \"prev_atime\": 0\r\n      };\r\n      \/\/ we need to update the document once we have the next schedule\r\n      update_doc = true;\r\n    }\r\n    if (!doc.verbose) {\r\n      \/\/ Add if missing doc.dynamic with defaults\r\n      doc.verbose = {\r\n        \"user_func\": 1,\r\n        \"scheduler\": 1\r\n      };\r\n      \/\/ we need to update the document once we have the next schedule\r\n      update_doc = true;\r\n    }\r\n    \/\/ Do not process dynamic.state pending\r\n    if (!doc.dynamic || !doc.dynamic.state || doc.dynamic.state === \"pending\") return;\r\n\r\n    var mid = doc.type + \"::\" + doc.id; \/\/ this is the same as meta.id or the KEY\r\n    var hour = doc.hour;\r\n    var min = doc.min;\r\n\r\n    \/\/ Do an eval check the JavaScript function exists. The eval occurs in a common \r\n    \/\/ utility function shared with RecurringCallback\r\n    if (!verifyFunctionExistsViaEval(doc, mid)) {\r\n      \/\/ doc.action did not exist, we have already logged the issue\r\n      return;\r\n    }\r\n\r\n    \/\/ Get the next valid execution time\r\n    var date_timer = getNextRecurringDate(hour, min);\r\n    var next_sched = Math.round(date_timer.getTime() \/ 1000);\r\n    if (!update_doc &amp;&amp; next_sched !== doc.dynamic.next_sched) {\r\n      \/\/ the next_sched should be the same as the setting from the helper application, however\r\n      \/\/ if we undeploy\/deploy or pause\/resume we might haver to reschedule to the next time slot\r\n      log('OnUpdate U ' + mid + ' calculated next_sched !== doc.dynamic.next_sched, delta ' +\r\n        (next_sched - doc.dynamic.next_sched) + ', reschedule');\r\n      update_doc = true;\r\n    }\r\n\r\n    if (update_doc) {\r\n      \/\/ this mutation is recursive and will be suppressed, we ensure we have a dynamic structure\r\n      doc.dynamic.next_sched = next_sched;\r\n\r\n      \/\/ rather then the call a function, to trap and retry if there is a resource issue\r\n      \/\/ cron_bkt[mid] = doc;\r\n      if (!tryBucketKvWriteWithLog('OnUpdate F', mid, doc)) {\r\n        \/\/ Failed to write doc to cron_bkt[key] the error has been logged\r\n        \/\/ and there is nothing more we can do.\r\n        return;\r\n      }\r\n    }\r\n\r\n    \/\/ Schedule an Eventing timer\r\n    var timer_id = createTimer(Callback, date_timer, null, doc);\r\n    if (doc.verbose.scheduler &gt;= 1) {\r\n      log('OnUpdate A ' + mid + ' rcv mutation (initial or rearm) schedule timer at ' +\r\n        toLocalISOTime(date_timer));\r\n    }\r\n    if (doc.verbose.scheduler &gt;= 2) {\r\n      log('OnUpdate B ' + mid + ' recurring timer was created, timer_id ' + timer_id);\r\n    }\r\n  } catch (e) {\r\n    log('OnUpdate E ' + meta.id + ', Error exception:', e);\r\n  }\r\n}\r\n\r\nfunction getNextRecurringDate(hour_str, min_str) {\r\n  \/\/ Note Javascript Dates are in milliseconds\r\n  var date_now = new Date();\r\n  var date_ret = new Date();\r\n  var hour;\r\n  var min;\r\n\r\n  try {\r\n    hour = parseInt(hour_str);\r\n  } catch (e) {}\r\n  try {\r\n    min = parseInt(min_str);\r\n  } catch (e) {}\r\n\r\n  \/\/ Note, this is only a simplistic partial 'crontab' syntax with some slight extensions\r\n  \/\/ it allows once a day, once an hour, once a minute.  It also contains some non-standard \r\n  \/\/ syntax to provide the ability to execute twice a minute or four times a minute.\r\n\r\n  if (hour_str === '*4X' &amp;&amp; min_str === '*4X') {\r\n    \/\/ once every 15 seconds or four times a minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(15);\r\n    while (date_ret.getTime() &lt; date_now.getTime()) {\r\n      date_ret.setSeconds(date_ret.getSeconds() + 15);\r\n    }\r\n    return date_ret;\r\n  } else\r\n  if (hour_str === '*2X' &amp;&amp; min_str === '*2X') {\r\n    \/\/ once every 30 seconds or twice a minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(30);\r\n    while (date_ret.getTime() &lt; date_now.getTime()) {\r\n      date_ret.setSeconds(date_ret.getSeconds() + 30);\r\n    }\r\n    return date_ret;\r\n  } else\r\n  if (hour_str === '*' &amp;&amp; min_str === '*') {\r\n    \/\/ once a minute \r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(date_ret.getMinutes() + 1);\r\n  } else\r\n  if (hour_str !== '*' &amp;&amp; isNaN(hour) === false &amp;&amp; min_str === '*') {\r\n    \/\/ once a minute only for a given hour\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(date_ret.getMinutes() + 1);\r\n    if (date_ret.getTime() &lt; date_now.getTime()) {\r\n      date_ret.setHours(hour);\r\n    }\r\n    if (date_ret.getTime() &gt; date_now.getTime()) {\r\n      date_ret.setDate(date_ret.getDate() + 1);\r\n      date_ret.setSeconds(0);\r\n      date_ret.setMinutes(0);\r\n      date_ret.setHours(hour);\r\n    }\r\n  } else\r\n  if (hour_str === '*' &amp;&amp; min_str !== '*' &amp;&amp; isNaN(min) === false) {\r\n    \/\/ once a hour at a given minute\r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(min);\r\n    \/\/ schedule for next hour\r\n    date_ret.setHours(date_ret.getHours() + 1);\r\n  } else\r\n  if (isNaN(hour) === false &amp;&amp; isNaN(min) === false) {\r\n    \/\/ once a day for a given hour and a given minute \r\n    date_ret.setMilliseconds(0);\r\n    date_ret.setSeconds(0);\r\n    date_ret.setMinutes(min);\r\n    date_ret.setHours(hour);\r\n    if (date_ret.getTime() &lt; date_now.getTime()) {\r\n      \/\/ schedule for tomorrow\r\n      date_ret.setDate(date_ret.getDate() + 1);\r\n    }\r\n  } else {\r\n    log('getNextRecurringDate illegal input hour_str &lt;' +\r\n      hour_str + '&gt; min_str &lt;' + min_str + '&gt;');\r\n    throw new Error('getNextRecurringDate illegal input hour_str &lt;' +\r\n      hour_str + '&gt; min_str &lt;' + min_str + '&gt;');\r\n    return null;\r\n  }\r\n  return date_ret;\r\n}\r\n\r\nfunction verifyFunctionExistsViaEval(curDoc, id) {\r\n  var result = false;\r\n  try {\r\n    \/\/ check for function if missing this is invalid return result\r\n    result = eval(\"typeof \" + curDoc.action + \" === 'function';\");\r\n    if (result === false) {\r\n      if (curDoc.verbose.scheduler &gt;= 1)\r\n        log(\"Warn\/Disable (No Action and No Re-Arm), because required 'action' of \" +\r\n          curDoc.action + \"(doc) does not exist, id is\", id);\r\n      return result;\r\n    }\r\n  } catch (e) {\r\n    log('verifyFunctionExistsViaEval Error exception:', e);\r\n  }\r\n  return result;\r\n}\r\n\r\nfunction toNumericFixed(number, precision) {\r\n  var multi = Math.pow(10, precision);\r\n  return Math.round((number * multi).toFixed(precision + 1)) \/ multi;\r\n}\r\n\r\nfunction toLocalISOTime(d) {\r\n  var tzoffset = (new Date()).getTimezoneOffset() * 60000; \/\/offset in milliseconds\r\n  return (new Date(d.getTime() - tzoffset)).toISOString().slice(0, -1);\r\n}\r\n\r\nfunction tryBucketKvWriteWithLog(tag, key, doc) {\r\n  var success = false;\r\n  var tries = 0;\r\n  while (tries &lt; 10) {\r\n    tries++;\r\n    try {\r\n      \/\/ critical that the below succeeds, because if it doesn't the cron cycle will break\r\n      cron_bkt[key] = doc;\r\n      success = true;\r\n      break;\r\n    } catch (e) {\r\n      log(tag + ' ' + key + ' WARN failed to update KV tries ' + tries, e);\r\n    }\r\n  }\r\n  if (!success) {\r\n    log(tag + ' ' + +key + ' FATAL could not update KV cron cycle, tried ' + tries + ', stoping ' + curDoc.action);\r\n  }\r\n  return success;\r\n}\r\n\r\nfunction Callback(doc) {\r\n  try {\r\n    var fired_at = new Date();\r\n\r\n    \/\/ Check if further analysis is needed we only trigger on a recurring_event that is active\r\n    if (doc.type !== \"recurring_event\") return;\r\n    \/\/ doc must have 'action', 'dynamic {}', verbose {}, dynamic.state\r\n    if (!doc.action || !doc.dynamic || !doc.verbose || !doc.dynamic.state) return;\r\n    \/\/ process any doc.dynamic.state BUT pending \r\n    if (doc.dynamic.state === \"pending\") return;\r\n\r\n    \/\/ ==================\r\n    \/\/ Check if still active\r\n\r\n    \/\/ We make sure that in KV the 'doc' still exists and that it is still active if not just \r\n    \/\/ return thus skipping the action and not Re-arming the timer. Note `travel-sample` is \r\n    \/\/ aliased to the map 'cron_bkt\r\n\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    var curDoc = null;\r\n    try {\r\n      \/\/ read the current version of doc from KV, e.g. curDoc\r\n      curDoc = cron_bkt[mid];\r\n    } catch (e) {} \/\/ needed for pre 6.5, note pure 6.5+ deployment returns null sans exception\r\n\r\n    var reason = null;\r\n    if (!curDoc || curDoc === null) {\r\n      reason = \"cron document is missing\";\r\n    } else\r\n    if (!curDoc.active) {\r\n      reason = \"cron document has active = false\";\r\n    } else\r\n    if (!curDoc.dynamic.state || curDoc.dynamic.state !== doc.dynamic.state) {\r\n      reason = \"cron document wrong dynamic.state expected \" + doc.dynamic.state;\r\n    } else\r\n    if (crc64(doc) !== crc64(curDoc)) {\r\n      reason = \"cron document changed\";\r\n    }\r\n\r\n    if (reason !== null) {\r\n      if (!curDoc || curDoc === null || curDoc.verbose.scheduler &gt;= 1) {\r\n        log('Callback X ' + mid + \" ignore\/stop this timer's schedule because \" + reason);\r\n      }\r\n      if (!curDoc || curDoc === null || curDoc.verbose.scheduler &gt;= 4) {\r\n        log('Callback Y ' + mid + ' timer doc', doc);\r\n        log('Callback Z ' + mid + ' KV curDoc', curDoc);\r\n      }\r\n      return;\r\n    }\r\n\r\n    \/\/ ==================\r\n    \/\/ Verify user routine exists and if so eval it \r\n\r\n    \/\/ Assume curDoc.action contains something like \"doCronActionA\" and we have a function in \r\n    \/\/ this handler like \"doCronActionA(doc)\". Below we use curDoc as the end user should be \r\n    \/\/ able to alter the eval'd JavaScript function.  We will execute two (2) evals.\r\n\r\n    \/\/ First eval check the JavaScript function exists.  The eval occurs in a common \r\n    \/\/ utility function shared with RecurringCallback\r\n    if (!verifyFunctionExistsViaEval(curDoc, mid)) {\r\n      \/\/ curDoc.action did not exist, we have already logged the issue\r\n      return;\r\n    }\r\n\r\n    \/\/ Second eval execute and process the user function we execute the defined function \r\n    \/\/ with an argument of curDoc\r\n    var beg_act = new Date();\r\n    var result = null;\r\n    eval(\"result = \" + curDoc.action + \"(curDoc);\");\r\n    var end_act = new Date();\r\n    var atime_ms = end_act.getTime() - beg_act.getTime();\r\n\r\n    if (curDoc.verbose.scheduler &gt;= 2)\r\n      log('Callback R ' + mid + ' action took ' + toNumericFixed((atime_ms \/ 1000), 3) +\r\n        ' sec., returned ' + result);\r\n\r\n    \/\/ ==================\r\n    \/\/ Calculate next time and mutate the control document for our our helper function\r\n    \/\/ which will create another mutation such that OnUpdate of this function will pick\r\n    \/\/ it up and generate the timer (avoids the MB-38554 issue).\r\n\r\n    var hour = curDoc.hour;\r\n    var min = curDoc.min;\r\n    var date_timer = getNextRecurringDate(hour, min);\r\n\r\n    curDoc.dynamic.prev_delay =\r\n      toNumericFixed(((fired_at.getTime() \/ 1000) - curDoc.dynamic.next_sched), 3);\r\n    curDoc.dynamic.prev_sched = curDoc.dynamic.next_sched;\r\n    curDoc.dynamic.prev_etime = Math.round(fired_at.getTime() \/ 1000);\r\n    curDoc.dynamic.prev_atime = toNumericFixed((atime_ms \/ 1000), 3);\r\n\r\n    curDoc.dynamic.state = \"pending\";\r\n    curDoc.dynamic.next_sched = Math.round(date_timer.getTime() \/ 1000);\r\n\r\n    \/\/ rather then the call a function, to trap and retry if there is a resource issue\r\n    \/\/ cron_bkt[mid] = curDoc;\r\n    if (!tryBucketKvWriteWithLog('Callback F', mid, curDoc)) {\r\n      \/\/ Failed to write curDoc to cron_bkt[key] the error has been logged\r\n      \/\/ and there is nothing more we can do.\r\n      return;\r\n    }\r\n\r\n    if (curDoc.verbose.scheduler &gt;= 1) {\r\n      log('Callback A ' + mid + ' gen mutation #1 to doc to force schedule rearm at ' +\r\n        toLocalISOTime(date_timer));\r\n    }\r\n    if (curDoc.verbose.scheduler &gt;= 2) {\r\n      log('Callback B ' + mid + ' sched ' + curDoc.dynamic.prev_sched +\r\n        ', actual ' + curDoc.dynamic.prev_etime +\r\n        ', delay ' + curDoc.dynamic.prev_delay +\r\n        ', took ' + curDoc.dynamic.prev_atime);\r\n    }\r\n    if (curDoc.verbose.scheduler &gt;= 3) {\r\n      log('Callback C ' + mid + ' curDoc', curDoc);\r\n    }\r\n  } catch (e) {\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    log('Callback E ' + mid + ' Error exception:', e);\r\n  }\r\n}<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Despu\u00e9s de pegar, aparece la pantalla que se muestra a continuaci\u00f3n:<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8711\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_03_code_editor_done.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"771\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-300x227.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1024x774.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-768x581.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1536x1161.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-20x15.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_03_code_editor_done-1320x998.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Haga clic en <strong>Guardar<\/strong>.<\/li>\n<li>Para volver a la pantalla de Eventos, pulse el bot\u00f3n '<strong>&lt; volver a Concurso completo<\/strong>(debajo del editor) o haga clic en el enlace <strong>Eventos<\/strong><\/li>\n<\/ul>\n<h4>Crear manualmente \"cron_impl_2func_651_help\"<\/h4>\n<p>Para a\u00f1adir la segunda funci\u00f3n de Eventing desde <strong>Consola Web de Couchbase &gt; Eventos<\/strong> haga clic en <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong>para a\u00f1adir una nueva funci\u00f3n. La direcci\u00f3n <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> aparece el cuadro de di\u00e1logo.<\/p>\n<p>En el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> para cada elemento de la funci\u00f3n, facilite la siguiente informaci\u00f3n:<\/p>\n<ul>\n<li>Para el <strong>Cubo de origen<\/strong> seleccione <strong>crondata<\/strong>.<\/li>\n<li>Para el <strong>Cubo de metadatos<\/strong> seleccione <strong>metadatos<\/strong>.<\/li>\n<li>Visite <strong>cron_impl_2func_651_help<\/strong>\u00a0es el nombre de la funci\u00f3n que est\u00e1 creando en el campo <strong>Nombre de la funci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>[Paso opcional] Introduzca el texto <b>Un ayudante cron como programador parte 1<\/b>en el <strong>Descripci\u00f3n<\/strong> cuadro de texto.<\/li>\n<li>Para el <strong>Ajustes<\/strong> utilice los valores por defecto.<\/li>\n<li>Para el <strong>Fijaciones<\/strong> crear un enlace:<\/li>\n<li>Para la vinculaci\u00f3n, el \"alias de cubo\", especifica <strong>cron_bkt<\/strong> como \"nombre alias\" del cubo, y seleccione<br \/>\n<strong>crondata<\/strong> como cubo asociado, y el modo debe ser \"lectura y escritura\".<\/li>\n<li>Una vez configurados los ajustes, el cuadro de di\u00e1logo deber\u00eda tener este aspecto:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8706\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_01_add_function_help.png\" alt=\"Eventing cron update\" width=\"643\" height=\"640\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help.png 1286w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-300x299.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-1024x1019.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-150x150.png 150w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-768x764.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-65x65.png 65w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-50x50.png 50w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_01_add_function_help-20x20.png 20w\" sizes=\"auto, (max-width: 643px) 100vw, 643px\" \/><\/li>\n<li>Tras facilitar toda la informaci\u00f3n requerida en el <strong>A\u00d1ADIR FUNCI\u00d3N<\/strong> pulse <strong>Siguiente A\u00f1adir c\u00f3digo<\/strong>. En <strong>cron_impl_2func_651_help<\/strong> aparece el cuadro de di\u00e1logo. La p\u00e1gina <strong>cron_impl_2func_651_help<\/strong>\u00a0contiene inicialmente un bloque de c\u00f3digo. Usted sustituir\u00e1 su <strong>cron_impl_2func_651_help<\/strong>\u00a0en este bloque.<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8710\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_02_code_editor_help.png\" alt=\"Eventing cron update\" width=\"800\" height=\"186\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help.png 2042w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-300x70.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-1024x239.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-768x179.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-1536x358.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-20x5.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_02_code_editor_help-1320x308.png 1320w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/li>\n<li>Copie el siguiente c\u00f3digo fuente JavaScript de Eventing Function (187 l\u00edneas) y p\u00e9guelo en el bloque de c\u00f3digo del marcador de posici\u00f3n de <strong>cron_impl_2func_651_help<\/strong>\n<pre class=\"height-set:true height:800 toolbar-overlay:false nums:false lang:js decode:true\">\/*\r\nFunction \"cron_impl_2func_651_help\" also requires \"cron_impl_2func_651\"\r\n\r\nTest Doc:\r\n   {\r\n        \"type\":\"recurring_event\",   \/\/ The KEY will be &lt;&lt;type&gt;&gt;::&lt;&lt;id&gt;&gt;\r\n        \"id\":1,                     \/\/\r\n        \"hour\":14,                  \/\/ The hour of the day 0-23, *, *2X, *4X to trigger\r\n        \"min\":54,                   \/\/ The minute in the hour 0-59, *, *2X, *4X to trigger\r\n        \"action\":\"doCronActionA\",   \/\/ What function to run on the trigger\r\n        \"active\":false,             \/\/ Flag to arm or disable this schedule\r\n        \"verbose\" : {\r\n          \"user_func\":2,            \/\/ Logging level for the action logic : 0=none, etc. etc.\r\n          \"scheduler\":3             \/\/ Logging level for the cron logic   : 0=none, etc. etc.\r\n        },\r\n        \"dynamic\" : {\r\n          \"state\":\"arm\",            \/\/ States \"arm\"|\"rearm\"|\"pending\" if any value but \"pending\" start a schedule\r\n          \"next_sched\": 0,          \/\/ Number of seconds since epoch to next desired schedule\r\n          \"prev_sched\": 0,          \/\/ Number of seconds since epoch for previous schedule\r\n          \"prev_etime\": 0,          \/\/ Number of seconds since epoch for previous schedule actual exec time\r\n          \"prev_delay\": 0,          \/\/ Number of seconds that the timer was delayed from the schedule\r\n          \"prev_atime\": 0           \/\/ Number of seconds taken by the user 'action'\r\n        }\r\n    }\r\n    \r\nNote, you can omit verbose{} and dynamic{} as they will be autocreated by the main Eventing \r\nFunction \"cron_impl_2func_651\". If verbose{} is missing the logging levels will default to \r\nverbose\" : {  \"user_func\":1, \"scheduler\":1 }\r\n*\/\r\n\r\nfunction tryBucketKvWriteWithLog(tag, key, doc) {\r\n  var success = false;\r\n  var tries = 0;\r\n  while (tries &lt; 10) {\r\n    tries++;\r\n    try {\r\n      \/\/ critical that the below succeeds, because if it doesn't the cron cycle will break\r\n      cron_bkt[key] = doc;\r\n      success = true;\r\n      break;\r\n    } catch (e) {\r\n      log(tag + ' ' + key + ' WARN failed to update KV tries ' + tries, e);\r\n    }\r\n  }\r\n  if (!success) {\r\n    log(tag + ' ' + +key + ' FATAL could not update KV cron cycle, tried ' + tries + ', stoping ' + curDoc.action);\r\n  }\r\n  return success;\r\n}\r\n\r\nfunction OnUpdate(doc, meta) {\r\n  \/\/ fix for 6.5.X growing bucket ops\r\n  if (meta.id.startsWith(\"fix_timer_scan_issue:\")) upsertOneDocPerBucket(doc, meta);\r\n  \r\n  try {\r\n    \/\/ Check that doc has desired values\r\n    if (!doc.type || doc.type !== \"recurring_event\" || !doc.active || doc.active != true) return;\r\n    \/\/ doc must have 'action', 'dynamic {}', verbose {}, dynamic.state\r\n    if (!doc.action || !doc.dynamic || !doc.verbose || !doc.dynamic.state) return;\r\n    \/\/ Only process state pending this will only exist for a 'breif' time\r\n    if (doc.dynamic.state !== \"pending\") return;\r\n\r\n    var mid = doc.type + '::' + doc.id; \/\/ make our KEY\r\n    var newdoc = null;\r\n    try {\r\n      \/\/ read the current version of doc from KV, e.g. curDoc\r\n      newdoc = cron_bkt[mid];\r\n    } catch (e) {} \/\/ needed for pre 6.5, note pure 6.5+ deployment returns null sans exception\r\n    \r\n    var reason = null;\r\n    if (!newdoc || newdoc == null) {\r\n      reason = \"cron document is missing\";\r\n    } else\r\n    if (!newdoc.active) {\r\n      reason = \"cron document has active = false\";\r\n    } else\r\n    if (!newdoc.dynamic.state || newdoc.dynamic.state !== doc.dynamic.state) {\r\n      reason = \"cron document wrong dynamic.state expected \" + doc.dynamic.state;\r\n    } else\r\n    if (crc64(doc) !== crc64(newdoc)) {\r\n      reason = \"cron document changed\";\r\n    }\r\n    if (reason != null) {\r\n      if (!newdoc || newdoc == null || newdoc.verbose.scheduler &gt;= 1) {\r\n        log('OnUpdate help: X stopping schedule because ' + reason + ',', newdoc)\r\n        return;\r\n      }\r\n    }\r\n\r\n    newdoc.dynamic.state = \"rearm\";\r\n    \/\/ cron_bkt[mid] = newdoc;\r\n    if (!tryBucketKvWriteWithLog('OnUpdate help: F', mid, newdoc)) {\r\n      \/\/ Failed to write newdoc to cron_bkt[key] the error has been logged\r\n      \/\/ and there is nothing more we can do.\r\n      return;\r\n    }\r\n\r\n    if (newdoc.verbose.scheduler &gt;= 1) {\r\n      log('OnUpdate help: A ' + mid + ' mutation #2 to doc to force schedule rearm');\r\n    }\r\n    if (newdoc.verbose.scheduler &gt;= 3) {\r\n      log('OnUpdate help: B ' + mid + ',', newdoc);\r\n    }\r\n  } catch (e) {\r\n    log('OnUpdate help: E ' + meta.id + ', Error exception:', e);\r\n  }\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction upsertOneDocPerBucket(doc, meta) {\r\n\r\n    var crcTable = makeCRC32Table();\r\n\r\n    \/\/ make one doc per bucket\r\n    var isVerbose = 0;\r\n    var isMacOS = false; \/\/ would be nice if this was an exposed constant in Eventing\r\n    var numvbs = 1024;   \/\/ default is linux\/PC\r\n    if (isMacOS) {\r\n        numvbs = 64;\r\n    }\r\n\r\n    var beg = (new Date).getTime();\r\n    var result = getKeysToCoverAllPartitions(crcTable, \"_tmp_vbs:\", numvbs);\r\n\r\n    for (var vb=0; vb&lt;numvbs; vb++) {\r\n        \/\/ brute force to fit a key prefix into a vBucket\r\n         var tst = result[vb];\r\n        if (isVerbose &gt; 1  || (isVerbose == 1) &amp;&amp; (vb &lt; 3 || vb &gt; numvbs -4)) {\r\n            log(\"KEY: \" + tst);\r\n        } else {\r\n            if (vb == 5) console.log(\"\\t*\\n\\t*\\n\\t*\");\r\n        }\r\n        \/\/ update the items to trigger a mutation for our PRIMARY fucntion\r\n        cron_bkt[tst] = { \"type\": \"_tmp_vbs\", \"vb\": vb, \"ts_millis\": beg, \"key\": tst };\r\n    }\r\n    var end = (new Date).getTime();\r\n    log(\"seeding one doc to each vBucket in primary_bucket alias (took \" + (end - beg) + \" mililis)\");\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction showHex(n) {\r\n    return n.toString(16);\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction makeCRC32Table() {\r\n    var crcTable = [];\r\n    var c;\r\n    for(var n =0; n &lt; 256; n++){\r\n        c = n;\r\n        for(var k =0; k &lt; 8; k++){\r\n            c = ((c&amp;1) ? (0xEDB88320 ^ (c &gt;&gt;&gt; 1)) : (c &gt;&gt;&gt; 1));\r\n        }\r\n        crcTable[n] = c;\r\n    }\r\n    return crcTable;\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction crc32(crcTable,str) {\r\n    var crc = 0 ^ (-1);\r\n    for (var i = 0; i &lt; str.length; i++ ) {\r\n        crc = (crc &gt;&gt;&gt; 8) ^ crcTable[(crc ^ str.charCodeAt(i)) &amp; 0xFF];\r\n    }\r\n    return (crc ^ (-1)) &gt;&gt;&gt; 0;\r\n}\r\n\r\n\/\/ FIXUP: ADDIN FUNCTON\r\n\/\/ fix for 6.5.X growing bucket ops\r\nfunction getKeysToCoverAllPartitions(crcTable,keyPrefix,partitionCount) {\r\n    var result = [];\r\n    var remaining = partitionCount;\r\n    for (var i = 0; remaining &gt; 0; i++) {\r\n      var key = keyPrefix + i;\r\n      var rv = (crc32(crcTable,key) &gt;&gt; 16) &amp; 0x7fff;\r\n      var actualPartition = rv &amp; partitionCount - 1;\r\n      if (!result[actualPartition] || result[actualPartition] === undefined) {\r\n        result[actualPartition] = key;\r\n        remaining--;\r\n      }\r\n    }\r\n    return result;\r\n}<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Despu\u00e9s de pegar, aparece la pantalla que se muestra a continuaci\u00f3n:<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8729\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/ev_cr_1u_02_code_editor_done_help.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"770\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help.png 2044w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-300x227.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1024x774.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-768x580.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1536x1160.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-20x15.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1u_02_code_editor_done_help-1320x997.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Guardar<\/strong>.<\/li>\n<li>Para volver a la pantalla de Eventos, pulse el bot\u00f3n '<strong>&lt; volver a Concurso completo<\/strong>(debajo del editor) o haga clic en el enlace <strong>Eventos<\/strong><\/li>\n<\/ul>\n<h3>Despliegue de las dos funciones<\/h3>\n<p>Ahora estamos listos para iniciar las funciones de Eventing. Desde el <strong>Consola Web de Couchbase &gt; Eventos<\/strong>\u00a0pantalla:<\/p>\n<ul>\n<li>Haga clic en el nombre de la funci\u00f3n <strong>cron_impl_2func_651_help<\/strong>\u00a0para expandir y exponer los controles de Funci\u00f3n.<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8713\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_04_functon_ctl_help.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"280\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help.png 2042w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-300x82.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-1024x281.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-768x211.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-1536x421.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-20x5.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl_help-1320x362.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Despliegue<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de despliegue<\/strong> seleccione \"<strong>A partir de ahora\"<\/strong> de la opci\u00f3n L\u00edmite de alimentaci\u00f3n.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8535\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/ev_cr_1_05_functon_deploy.png\" alt=\"\" width=\"382\" height=\"263\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy.png 764w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy-300x207.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy-20x14.png 20w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><\/li>\n<\/ul>\n<p>Empecemos con la otra funci\u00f3n de Eventing. Desde el <strong>Consola Web de Couchbase &gt; Eventos<\/strong>\u00a0pantalla:<\/p>\n<ul>\n<li>Haga clic en el nombre de la funci\u00f3n <strong>cron_impl_2func_651<\/strong>\u00a0para expandir y exponer los controles de Funci\u00f3n.<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8712\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_04_functon_ctl.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"365\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl.png 2042w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-300x107.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-1024x366.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-768x275.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-1536x549.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-20x7.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_04_functon_ctl-1320x472.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Haga clic en <strong>Despliegue<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de despliegue<\/strong> seleccione \"<strong>A partir de ahora\"<\/strong> de la opci\u00f3n L\u00edmite de alimentaci\u00f3n.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8535\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/04\/ev_cr_1_05_functon_deploy.png\" alt=\"\" width=\"382\" height=\"263\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy.png 764w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy-300x207.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/ev_cr_1_05_functon_deploy-20x14.png 20w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><\/li>\n<\/ul>\n<h3>Configurar un <em>cron<\/em> tarea a ejecutar cuatro (4) veces por minuto<\/h3>\n<p>En este punto nuestra funci\u00f3n Eventing est\u00e1 esperando una mutaci\u00f3n espec\u00edficamente cualquier documento de type=\"recurring_event\" que tenga un campo active=true.<\/p>\n<p>Desde el\u00a0<strong>Consola Web Couchbase &gt; Consulta<\/strong>\u00a0utilizaremos N1QL para crear una nueva tarea programada en el cubo \"muestra-viaje\":<\/p>\n<ul>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:default decode:true\">INSERT INTO `crondata` (KEY,VALUE) VALUES (\r\n  \"recurring_event::1\", \r\n  {\r\n    \"type\": \"recurring_event\",\r\n    \"id\":1,\r\n    \"hour\":\"*\",\r\n    \"min\":\"0\",\r\n    \"action\": \"doCronActionA\",\r\n    \"verbose\": {\r\n      \"user_func\": 2,\r\n      \"scheduler\": 3\r\n    },\r\n    \"active\": false\r\n  }\r\n);<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Ejecute<br \/>\n<\/strong><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8715\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_06_n1ql_add_sched.png\" alt=\"Eventing cron update\" width=\"817\" height=\"582\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched.png 1634w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-300x214.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-1024x729.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-768x547.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-1536x1094.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-20x14.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_06_n1ql_add_sched-1320x940.png 1320w\" sizes=\"auto, (max-width: 817px) 100vw, 817px\" \/><\/li>\n<\/ul>\n<h3>Activar nuestro primer <em>cron<\/em> tarea<\/h3>\n<p>El documento de control que hicimos anteriormente no se activ\u00f3 porque especificamos \"active\":false, adem\u00e1s la programaci\u00f3n anterior s\u00f3lo se ejecutar\u00e1 una vez por hora, pero queremos probar las cosas y ver que funcionen en un futuro pr\u00f3ximo.<\/p>\n<p>En primer lugar necesitamos un \u00edndice para poder manipular nuestros documentos de control en N1QL esto s\u00f3lo tiene que hacerse una vez<\/p>\n<ul>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">CREATE primary INDEX on `crondata` ;<\/pre>\n<\/li>\n<\/ul>\n<p>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>Ahora activaremos la tarea, pero ajustaremos el horario de repetici\u00f3n a cada 15 segundos para ver exactamente c\u00f3mo se comporta el sistema.  Para ello modificaremos el documento de control con la CLAVE recurring_event::1<\/p>\n<ul>\n<li>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">UPDATE `crondata`\r\nSET active=TRUE, hour=\"*4X\", min=\"*4X\"\r\nWHERE type=\"recurring_event\" AND id=1 ;<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Ejecute<br \/>\n<\/strong><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8716\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_07_n1ql_activate.png\" alt=\"Eventing cron update\" width=\"790\" height=\"420\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate.png 1580w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-300x159.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-1024x544.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-768x408.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-1536x817.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-818x434.png 818w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-20x11.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_07_n1ql_activate-1320x702.png 1320w\" sizes=\"auto, (max-width: 790px) 100vw, 790px\" \/><\/li>\n<\/ul>\n<p>Utilizamos la sintaxis no est\u00e1ndar de =\"*4X\" para programar un elemento recurrente cuatro veces por minuto podemos ver nuestra funci\u00f3n de trabajo <strong>doCronActionA<\/strong> ejecut\u00e1ndose y tambi\u00e9n la l\u00f3gica de mantenimiento para programar la funci\u00f3n a trav\u00e9s de sentencias de registro porque hemos establecido verbose=3.<\/p>\n<p>El programador se ejecuta ahora cuatro veces por minuto. Puede ver la actividad en las estad\u00edsticas y en los archivos de registro de la aplicaci\u00f3n para las funciones de eventos. <strong>cron_impl_2func_651 <\/strong>y <strong>cron_impl_2func_651_help<\/strong>.<\/p>\n<ul>\n<li>Acceder a la\u00a0<strong>Consola Web de Couchbase &gt; Panel de control<\/strong> ver\u00e1s una r\u00e1faga de actividad cada 15 segundos:<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8717\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_08_dashboard_activity.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"532\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-300x156.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-1024x534.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-768x401.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-1536x801.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-20x10.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_08_dashboard_activity-1320x688.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Acceder a la <strong>Consola Web de Couchbase &gt; Eventos<\/strong> y haga clic en el bot\u00f3n <strong>Registro<\/strong> enlace del desplegado <strong>cron_impl_2func_651<\/strong>\u00a0Funci\u00f3n de registro de eventos. Este cuadro de di\u00e1logo Registro de funciones enumera las sentencias de registro en orden inverso (los elementos m\u00e1s nuevos primero). La salida inicial deber\u00eda ser similar a la siguiente:\n<div style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%;margin-top: 12px;margin-bottom: 12px\">2020-05-20T18:34:33.340-07:00 [INFO] \"OnUpdate B recurring_event::1 temporizador recurrente fue creado, timer_id 570927555481258455388\"<br \/>\n2020-05-20T18:34:33.340-07:00 [INFO] \"OnUpdate A recurring_event::1 rcv mutation (initial or rearm) schedule timer at 2020-05-20T18:34:45.000\"<br \/>\n2020-05-20T18:34:33.233-07:00 [INFO]\"doCronActionA upsert to KV with KEY cron_cache::airlines_by_country cachedoc \" {\"type\": \"cron_cache\", \"id\": \"airlines_by_country\", \"date\": \"2020-05-21T01:34:33.232Z\", \"data\":{\"Estados Unidos\":127, \"Reino Unido\":39, \"Francia\":21}}<br \/>\n2020-05-20T18:34:33.233-07:00 [INFO] \"Callback R recurring_event::1 action took 0.013 sec., returned true\"<br \/>\n2020-05-20T18:34:33.233-07:00 [INFO] \"Callback C recurring_event::1 curDoc\" {\"action\": \"doCronActionA\", \"active\":true, \"hour\": \"*4X\", \"id\":1, \"min\": \"*4X\", \"type\": \"recurring_event\", \"verbose\":{\"scheduler\":3, \"user_func\":2}, \"dynamic\":{\"state\": \"pending\", \"next_sched\":1590024885, \"prev_sched\":1590024870, \"prev_etime\":1590024873, \"prev_delay\":3.218,\u201dprev_atime\u201d:0.013}}<br \/>\n2020-05-20T18:34:33.233-07:00 [INFO] \"Callback B recurring_event::1 sched 1590024870, actual 1590024873, delay 3.218, took 0.013\"<br \/>\n2020-05-20T18:34:33.233-07:00 [INFO] \"Callback A recurring_event::1 gen mutation #1 to doc to force schedule rearm at 2020-05-20T18:34:45.000\"<br \/>\n2020-05-20T18:34:33.232-07:00 [INFO] \"doCronActionA N1QL idx 2, country France cnt 21\"<br \/>\n2020-05-20T18:34:33.232-07:00 [INFO] \"doCronActionA N1QL idx 1, country Reino Unido cnt 39\"<br \/>\n2020-05-20T18:34:33.232-07:00 [INFO] \"doCronActionA N1QL idx 0, country Estados Unidos cnt 127\"<br \/>\n2020-05-20T18:34:33.220-07:00 [INFO] \"doCronActionA acci\u00f3n de usuario controlada por recurring_event::1\"<br \/>\n2020-05-20T18:34:19.340-07:00 [INFO] \"OnUpdate B recurring_event::1 temporizador recurrente fue creado, timer_id 381384185845112994486\"<br \/>\n2020-05-20T18:34:19.340-07:00 [INFO] \"OnUpdate A recurring_event::1 rcv mutation (initial or rearm) schedule timer at 2020-05-20T18:34:30.000\"<\/div>\n<p>La l\u00ednea m\u00e1s antigua en la parte inferior es la mutaci\u00f3n que inici\u00f3 el programa (o rearm\u00f3 el programa), por ejemplo, el mensaje OnUpdate, y vemos las dos primeras ejecuciones completas de la l\u00f3gica de negocio que codificamos en <strong>doCronActionA<\/strong><\/li>\n<li>Tambi\u00e9n habr\u00e1 algunos mensajes relacionados con el \"<em>arreglo para 6.5.X cubo creciente ops<\/em>\"pero se registrar\u00e1 en <strong>cron_impl_2func_651_help<\/strong> ver\u00e1 mensajes como el siguiente cada 30 segundos aproximadamente:\n<div style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%;margin-top: 12px;margin-bottom: 12px\">2020-05-20T18:34::49.185-07:00 [INFO] \"seeding one doc to each vBucket in primary_bucket alias (took 221 mililis)\"<\/div>\n<\/li>\n<\/ul>\n<p>Vamos a ajustar tanto la frecuencia como la verbosidad de esta tarea concreta.  Utilizaremos el est\u00e1ndar <em>cron<\/em> sintaxis de \"*\" para ambos <em>hora<\/em> y <em>min<\/em> para obtener una programaci\u00f3n recurrente de una vez por minuto 4X m\u00e1s lenta que la frecuencia actual.   Adem\u00e1s, bajaremos el nivel de verbosidad de la l\u00f3gica del planificador a cero y la funci\u00f3n de usuario a 1 para que s\u00f3lo veamos un mensaje por invocaci\u00f3n.<\/p>\n<p>Desde el\u00a0<strong>Consola Web Couchbase &gt; Consulta<\/strong>\u00a0utilizaremos N1QL para crear una nueva tarea programada en el cubo \"muestra-viaje\":<\/p>\n<ul>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:default decode:true\">UPDATE `crondata` \r\nSET verbose.scheduler = 0, verbose.user_func = 1, \r\n    active=true, hour=\"*\", min=\"*\" \r\nWHERE type=\"recurring_event\" AND id=1 ;<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Ejecute<\/strong><\/li>\n<\/ul>\n<p>Despu\u00e9s de un acceso de 2 o 3 minutos el <strong>Consola Web de Couchbase &gt; Eventos<\/strong> y haga clic en el bot\u00f3n <strong>Registro<\/strong> enlace del desplegado <strong>cron_impl_2func_651<\/strong>\u00a0Funci\u00f3n Eventing.<\/p>\n<ul>\n<li>La programaci\u00f3n recurrente es ahora de un minuto y mucho menos verbosa. S\u00f3lo se emite un mensaje o l\u00ednea de registro por cada ejecuci\u00f3n de funci\u00f3n (una vez m\u00e1s en orden temporal inverso).\n<div style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%;margin-top: 12px;margin-bottom: 12px\">2020-05-20T18:43:04.231-07:00 [INFO] \"doCronActionA acci\u00f3n de usuario controlada por recurring_event::1\"<br \/>\n2020-05-20T18:42:08.233-07:00 [INFO] \"doCronActionA acci\u00f3n de usuario controlada por recurring_event::1\"<\/div>\n<p>S\u00f3lo se emite un mensaje o l\u00ednea de registro por cada ejecuci\u00f3n de funci\u00f3n de usuario programada, es decir. <strong>doCronActionA<\/strong> (de nuevo en orden cronol\u00f3gico inverso).<\/li>\n<\/ul>\n<h3>Veamos el trabajo que se realiza<\/h3>\n<p>Este c\u00f3digo proporciona un marco pr\u00e1ctico para ejecutar cualquier funci\u00f3n JavaScript en un horario recurrente y nuestra funci\u00f3n <strong>doCronActionA<\/strong> es upserting (insertar o actualizar) un documento KV de cach\u00e9 calculado una vez por minuto.<\/p>\n<p>Para comprobar los resultados de la funci\u00f3n de eventos desplegada, acceda a la pantalla <strong>Consola Web de Couchbase &gt; Cubos<\/strong> y haga clic en el enlace Documentos de la p\u00e1gina <strong>viaje-muestra<\/strong> cubo.<\/p>\n<ul>\n<li>En el cuadro de texto \"<strong>N1QL DONDE<\/strong>\" pasado el siguiente texto.\n<pre class=\"toolbar-overlay:false nums:false lang:default decode:true\">type=\"cron_cache\"<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Recuperar documentos<br \/>\n<\/strong>Ahora deber\u00eda ver un documento de cach\u00e9 cron_cache::airlines_by_country que se actualiza una vez por minuto mediante la funci\u00f3n programada <strong>doCronActionA<\/strong>.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8719\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"426\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-300x125.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-1024x428.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-768x321.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-1536x642.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_10_look_at_kv_cache_doc-1320x551.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Haga clic en el identificador \"<strong>cron_cache::airlines_by_country<\/strong>\"ver\u00e1 su documento en cach\u00e9 que est\u00e1 siendo actualizado por la l\u00f3gica de negocio de <strong>doCronActionA<\/strong>.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8720\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc.png\" alt=\"Eventing cron update\" width=\"644\" height=\"377\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc.png 1288w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc-300x176.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc-1024x599.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc-768x450.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_11_look_at_kv_cache_doc-20x12.png 20w\" sizes=\"auto, (max-width: 644px) 100vw, 644px\" \/><br \/>\nPor supuesto, los datos de origen son \"est\u00e1ticos\" en este momento, por lo que los recuentos seguir\u00e1n siendo los mismos.<\/li>\n<\/ul>\n<h3>Veamos el documento de control<\/h3>\n<p>Este c\u00f3digo proporciona un marco para mantener un poco de estad\u00edsticas sobre cada programa en ejecuci\u00f3n.<\/p>\n<p>Para comprobar las estad\u00edsticas de la funci\u00f3n de eventos desplegada, acceda a la pantalla <strong>Consola Web de Couchbase &gt; Cubos<\/strong> y haga clic en el enlace Documentos de la p\u00e1gina <strong>crondata<\/strong> cubo.<\/p>\n<ul>\n<li>En el cuadro de texto \"<strong>N1QL DONDE<\/strong>\" pasado el siguiente texto.\n<pre class=\"toolbar-overlay:false nums:false lang:default decode:true\">type=\"recuring_event\"<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li>Haga clic en <strong>Recuperar documentos<br \/>\n<\/strong>Ahora deber\u00eda ver un documento de su documento de control recurring_event::1 que est\u00e1 controlando la funci\u00f3n programada <strong>doCronActionA<\/strong>.<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8721\" style=\"border: #000 1px solid;margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_12_look_at_control_doc.png\" alt=\"Eventing cron update\" width=\"1020\" height=\"426\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc.png 2040w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-300x125.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-1024x428.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-768x321.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-1536x642.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_12_look_at_control_doc-1320x551.png 1320w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/li>\n<li>Haga clic en el identificador \"<strong>evento_recurrente::1<\/strong>\" ver\u00e1 su documento de control que est\u00e1 siendo actualizado por la l\u00f3gica del planificador con algunas estad\u00edsticas en la din\u00e1mica de objetos JSON.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8722\" style=\"margin-top: 12px\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/05\/ev_cr_1u_13_look_at_control_doc.png\" alt=\"Eventing cron update\" width=\"646\" height=\"473\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_13_look_at_control_doc.png 1292w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_13_look_at_control_doc-300x220.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_13_look_at_control_doc-1024x750.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_13_look_at_control_doc-768x562.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/05\/ev_cr_1u_13_look_at_control_doc-20x15.png 20w\" sizes=\"auto, (max-width: 646px) 100vw, 646px\" \/><br \/>\nLa parte \"din\u00e1mica\" del documento que se a\u00f1ade autom\u00e1ticamente conserva algunas estad\u00edsticas de depuraci\u00f3n:<\/li>\n<li>prev_sched: es la marca de tiempo UNIX anterior de la \u00faltima planificaci\u00f3n ejecutada<\/li>\n<li>prev_etime: es la marca de tiempo UNIX real de la \u00faltima vez que se ejecut\u00f3 el programa<\/li>\n<li>prev_delay: es el retraso desde el prev_sched hasta el prev_etime<\/li>\n<li>prev_atime: es el tiempo que tard\u00f3 en ejecutarse esta acci\u00f3n, es decir, doCronActionA en ejecutarse.<\/li>\n<li>next_sched: es la siguiente ejecuci\u00f3n programada para esta acci\u00f3n<\/li>\n<\/ul>\n<p>Estas estad\u00edsticas guardadas en el subobjeto din\u00e1mico JSON para cada programaci\u00f3n son \u00fatiles para determinar que su sistema de programaci\u00f3n est\u00e1 en buen estado y que la acci\u00f3n que se est\u00e1 ejecutando est\u00e1 terminando a tiempo.<\/p>\n<h3>Comprobar que la cach\u00e9 se actualiza cuando cambian los datos<\/h3>\n<p>Todo el prop\u00f3sito de <strong>doCronActionA<\/strong> es realizar el trabajo a una hora programada o cerca de ella y actualizar un documento de cach\u00e9 con la CLAVE \"cron_cache::airlines_by_country\".<\/p>\n<p>Hagamos algunas validaciones en el Query Monitor de que nuestra cach\u00e9 se est\u00e1 actualizando 1) mirando el documento de la cach\u00e9, 2) eliminando algunas aerol\u00edneas del conjunto de documentos de la muestra de viaje, y 3) verificando que el documento de la cach\u00e9 se actualiza mediante la funci\u00f3n <strong>doCronActionA<\/strong>.<\/p>\n<p>Desde el\u00a0<strong>Consola Web Couchbase &gt; Consulta<\/strong>\u00a0utilizaremos N1QL para ver y manipular los datos del cubo \"muestra-viaje\":<\/p>\n<ul>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo<\/li>\n<li><strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">SELECT data FROM `travel-sample`\r\nWHERE `type` = 'cron_cache' AND id== 'airlines_by_country';<\/pre>\n<\/li>\n<li>Haga clic en <strong>Ejecute<br \/>\n<\/strong>En la vista JSON del Query Workbench deber\u00edas ver:<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">[\r\n  {\r\n    \"data\": {\r\n      \"France\": 21,\r\n      \"United Kingdom\": 39,\r\n      \"United States\": 127\r\n    }\r\n  }\r\n]<\/pre>\n<\/li>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">DELETE FROM `travel-sample`\r\nWHERE `type` = 'airline' AND callsign LIKE 'U%'<\/pre>\n<\/li>\n<li>Haga clic en <strong>Ejecute<br \/>\n<\/strong>En la vista JSON del Query Workbench deber\u00edas ver (acabamos de borrar algunos datos)<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">{\r\n  \"results\": []\r\n}<\/pre>\n<\/li>\n<li>Espere un poco m\u00e1s de un minuto<\/li>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<\/strong>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">SELECT data FROM `travel-sample`\r\nWHERE `type` = 'cron_cache' AND id== 'airlines_by_country';<\/pre>\n<\/li>\n<li><strong><span style=\"font-weight: 400\">Haga clic en <\/span>Ejecute<br \/>\n<\/strong>En la vista JSON del Query Workbench deber\u00eda ver que cuatro (4) compa\u00f1\u00edas a\u00e9reas ya no est\u00e1n presentes<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">[\r\n  {\r\n    \"data\": {\r\n      \"France\": 21,\r\n      \"United Kingdom\": 39,\r\n      \"United States\": 123\r\n    }\r\n  }\r\n]<\/pre>\n<\/li>\n<\/ul>\n<h3>Iniciar una segunda tarea programada<\/h3>\n<p>Este c\u00f3digo proporciona un marco pr\u00e1ctico para ejecutar de 1 a N funciones JavaScript en horarios recurrentes.<\/p>\n<p>Dejaremos que la funci\u00f3n <strong>doCronActionA <\/strong>seguir\u00e1n funcionando en un horario de un minuto, pero ahora habilitaremos <strong>doCronActionB <\/strong>en un horario de 30 segundos (dos veces por minuto).   Esta funci\u00f3n es una shell vac\u00eda y s\u00f3lo registrar\u00e1 que fue invocada.<\/p>\n<p>Desde el\u00a0<strong>Consola Web Couchbase &gt; Consulta<\/strong> utilizaremos N1QL para ver y manipular datos en el directorio '<strong>crondata<\/strong>' cubo:<\/p>\n<ul>\n<li>Corta y pega la siguiente sentencia N1QL en el archivo <strong>Editor de consultas<br \/>\n<\/strong><\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">INSERT INTO `crondata` (KEY,VALUE) VALUES (\r\n  \"recurring_event::2\", \r\n  {\r\n    \"type\":\"recurring_event\",\r\n    \"id\":2,\r\n    \"hour\":\"*2X\",\r\n    \"min\":\"*2X\",\r\n    \"action\":\"doCronActionB\",\r\n    \"verbose\": {\r\n      \"user_func\": 1,\r\n      \"scheduler\": 0\r\n    },\r\n    \"active\": true\r\n  }\r\n);<\/pre>\n<\/li>\n<li>Haga clic en <strong>Ejecute<\/strong><\/li>\n<\/ul>\n<p>En este punto est\u00e1 ejecutando dos (2) tareas diferentes cada una en dos (2) horarios diferentes para verificar espere dos o tres minutos e inspeccione los archivos de registro de nuevo<\/p>\n<ul>\n<li>Acceder a la <strong>Consola Web de Couchbase &gt; Eventos<\/strong> y haga clic en el bot\u00f3n <strong>Registro<\/strong> enlace del desplegado <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong>\u00a0Funci\u00f3n Eventing.<br \/>\nS\u00f3lo se emite un mensaje o l\u00ednea de registro por cada ejecuci\u00f3n de funci\u00f3n (una vez m\u00e1s en orden temporal inverso).  Vemos que <strong>doCronActionA<\/strong> dispara una vez por minuto mientras<strong> doCronActionB <\/strong>dispara el doble, por ejemplo, una vez cada 30 segundos.<\/p>\n<div style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%;margin-top: 12px;margin-bottom: 12px\">2020-05-20T19:16:05.259-07:00 [INFO] \"doCronActionA acci\u00f3n de usuario controlada por recurring_event::1\"<br \/>\n2020-05-20T19:16:05.255-07:00 [INFO] \"doCronActionB acci\u00f3n de usuario controlada por recurring_event::2\"<br \/>\n2020-05-20T19:15:37.253-07:00 [INFO] \"doCronActionB acci\u00f3n de usuario controlada por recurring_event::2\"<br \/>\n2020-05-20T19:15:09.250-07:00 [INFO] \"doCronActionA acci\u00f3n de usuario controlada por recurring_event::1\"<br \/>\n2020-05-20T19:15:09.249-07:00 [INFO] \"doCronActionB acci\u00f3n de usuario controlada por recurring_event::2\"<br \/>\n2020-05-20T19:14:34.255-07:00 [INFO] \"doCronActionB acci\u00f3n de usuario controlada por recurring_event::2\"<\/div>\n<\/li>\n<\/ul>\n<h3>[OPCIONAL] Pausa \/ Editar JavaScript \/ Reanudar<\/h3>\n<p>Esencialmente hemos terminado con esta entrega, si\u00e9ntete libre de experimentar y modificar cosas y experimentar por ejemplo:<\/p>\n<p>Desde el\u00a0<strong>Consola Web de Couchbase &gt; Eventos<\/strong>\u00a0pantalla:<\/p>\n<ul>\n<li>Haga clic en el nombre de la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong>\u00a0para expandir y exponer los controles de Funci\u00f3n.<\/li>\n<li>Haga clic en <strong>Pausa<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de pausa<\/strong> seleccione \"<strong>Funci\u00f3n de pausa\"<\/strong>.<\/li>\n<li>Pulse \"<strong>Editar JavaScript<\/strong>\"<\/li>\n<li>Si se siente seguro, modifique el <strong>doCronActionB <\/strong>para realizar algunas operaciones KV o integrarse con cURL.<br \/>\nSi s\u00f3lo quieres ver un cambio a\u00f1ade algo simple a la funci\u00f3n prueba algo como lo de abajo:<\/p>\n<pre class=\"toolbar-overlay:false nums:false lang:js decode:true\">function doCronActionB(doc) {\r\n  try {\r\n    \/\/ check that doc has desired values\r\n    if (doc.type !== \"recurring_event\" || doc.active !== true) return;\r\n    if (doc.verbose.user_func &gt;= 1)\r\n      log(doc.action + ' user action controlled by ' + doc.type + '::' + doc.id);\r\n\r\n    \/\/ YOUR LOGIC HERE\r\n    var a = 1 + 7; \r\n    log('this is my logic, a = 1 +7 = ' + a);\r\n    \r\n  } catch (e) {\r\n    log(doc.action + ' Error exception:', e);\r\n    return false;\r\n  }\r\n  return true;\r\n}<\/pre>\n<\/li>\n<li>Haga clic en <strong>Guardar<\/strong>.<\/li>\n<li>Para volver a la pantalla de Eventos, pulse el bot\u00f3n '<strong>&lt; volver a Concurso completo<\/strong>(debajo del editor) o haga clic en el enlace <strong>Eventos<\/strong><\/li>\n<li>Haga clic en <strong>Curr\u00edculum<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de reanudaci\u00f3n<\/strong> seleccione \"<strong>Reanudar la funci\u00f3n\"<\/strong>.<\/li>\n<li>Espere aproximadamente un minuto y para la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong>\u00a0desplegar<\/li>\n<li>Haga clic en el bot\u00f3n <strong>Registro<\/strong> enlace del desplegado <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong>\u00a0Funci\u00f3n Eventing.\n<div style=\"border: 1px solid black;max-width: 1000px;padding: 4px;font-size: 60%;margin-top: 12px;margin-bottom: 12px\">2020-05-20T19:20:41.343-07:00 [INFO] \"esta es mi l\u00f3gica, a = 1 +7 = 8\"<br \/>\n2020-05-20T19:20:41.343-07:00 [INFO] \"doCronActionB acci\u00f3n de usuario controlada por recurring_event::2\"<\/div>\n<\/li>\n<\/ul>\n<h3>Limpieza<\/h3>\n<p>La limpieza consiste en desinstalar y eliminar la funci\u00f3n y, a continuaci\u00f3n, eliminar los dos buckets creados. Con esto concluye el Ejemplo.<\/p>\n<h4>Eliminar funciones<\/h4>\n<p>Desde el <strong>Consola Web de Couchbase &gt; Eventos<\/strong>\u00a0pantalla:<\/p>\n<ul>\n<li>Haga clic en el nombre de la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong>\u00a0para expandir y exponer los controles de Funci\u00f3n.<\/li>\n<li>Haga clic en <strong>Despliegue<\/strong>.<\/li>\n<li>En el <strong>Confirmar Funci\u00f3n de Despliegue<\/strong> seleccione \"<strong>Funci\u00f3n Despliegue\"<\/strong>.<\/li>\n<li>Esperar a la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong><strong>\u00a0<\/strong>para deshacer el despliegue.<\/li>\n<li>Haga clic en <strong>Borrar<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de borrado<\/strong> seleccione \"<strong>Borrar funci\u00f3n\"<\/strong>.<\/li>\n<\/ul>\n<p>Desde el\u00a0<strong>Consola Web de Couchbase &gt; Eventos<\/strong>\u00a0pantalla:<\/p>\n<ul>\n<li>Haga clic en el nombre de la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651_help<\/span><\/strong>\u00a0para expandir y exponer los controles de Funci\u00f3n.<\/li>\n<li>Haga clic en <strong>Despliegue<\/strong>.<\/li>\n<li>En el <strong>Confirmar Funci\u00f3n de Despliegue<\/strong> seleccione \"<strong>Funci\u00f3n Despliegue\"<\/strong>.<\/li>\n<li>Esperar a la funci\u00f3n <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651_help<\/span><\/strong><strong>\u00a0<\/strong>para deshacer el despliegue.<\/li>\n<li>Haga clic en <strong>Borrar<\/strong>.<\/li>\n<li>En el <strong>Confirmar funci\u00f3n de borrado<\/strong> seleccione \"<strong>Borrar funci\u00f3n\"<\/strong>.<\/li>\n<\/ul>\n<h4>Retirar cubos<\/h4>\n<p>Siguiente Suelta los cubos '<strong>metadatos<\/strong>' '<strong>crondata<\/strong>y<strong>viaje-muestra<\/strong>(siempre se pueden volver a crear).<\/p>\n<p>Desde el <strong>Consola Web de Couchbase &gt; Cubos<\/strong> y haga clic en el enlace Documentos de la p\u00e1gina <strong>viaje-muestra<\/strong> cubo.<\/p>\n<ul>\n<li><strong>Haga clic en<\/strong> en el nombre del cubo \"<strong>metadatos<\/strong>\" para expandir y exponer los controles<\/li>\n<li>Haga clic en <strong>Borrar <\/strong><\/li>\n<li>En el <strong>Confirmar Borrar Cubo<\/strong> seleccione \"<strong>Borrar cubo\"<\/strong>.<\/li>\n<li><strong>Haga clic en<\/strong> en el nombre del cubo \"<strong>crondata<\/strong>\" para expandir y exponer los controles<\/li>\n<li>Haga clic en <strong>Borrar <\/strong><\/li>\n<li>En el <strong>Confirmar Borrar Cubo<\/strong> seleccione \"<strong>Borrar cubo\"<\/strong>.<\/li>\n<li><strong>Haga clic en<\/strong> en el nombre del cubo \"<strong>viaje-muestra<\/strong>\" para expandir y exponer los controles<\/li>\n<li>Haga clic en <strong>Borrar <\/strong><\/li>\n<li>En el <strong>Confirmar Borrar Cubo<\/strong> seleccione \"<strong>Borrar cubo\"<\/strong>.<\/li>\n<\/ul>\n<h3>Reflexiones finales<\/h3>\n<p>Espero que hayas encontrado este tutorial educativo y hayas desarrollado una mayor apreciaci\u00f3n del Servicio de Eventos Couchbase en su conjunto.<\/p>\n<p>Anteriormente se\u00f1al\u00e9 que Eventing est\u00e1 dise\u00f1ado para procesar mutaciones de alta velocidad (en millones por segundo) desde el flujo DCP del bucket de origen asociado a la funci\u00f3n.  Esta funci\u00f3n de Eventing o planificador s\u00f3lo necesita reaccionar a cambios m\u00ednimos en los documentos del planificador.<\/p>\n<p>He iniciado 5.000 programaciones con este c\u00f3digo sin cambios, simplemente a\u00f1adiendo documentos de control. Incluso he ejecutado 120.000 programaciones cada minuto s\u00f3lo para probar esta implementaci\u00f3n (s\u00ed, 120.000 es una cantidad bastante rid\u00edcula de programaciones independientes). <em>cron<\/em> horarios a ejecutar).  Adem\u00e1s puse 120.000 sc3hedules 2 d\u00edas en el futuro para asegurarme de que no ten\u00edamos problemas de recursos degenerados.<\/p>\n<p>Al verse obligado a utilizar dos Funciones Vespertinas para crear el sistema de programaci\u00f3n, la funci\u00f3n principal <strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651<\/span><\/strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\"> y un simple ayudante <\/span><strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\">cron_impl_2func_651_help<\/span><\/strong><span class=\"cbui-table-cell cbui-tablerow-title ng-binding\"> no era tan elegante como esperaba.  Adem\u00e1s, tener que trabajar alrededor de una fuga op cubo a trav\u00e9s de disparar un temporizador en cada vBucket fue decepcionante por decir lo menos. Afortunadamente, debido a este esfuerzo se implementaron cambios y en la pr\u00f3xima versi\u00f3n 6.6.0 podr\u00e9 hacer un planificador m\u00e1s limpio utilizando una \u00fanica Evening Function.   Las mejoras clave en 6.6 son 1) la capacidad de generar un nuevo temporizador desde dentro de un callback de temporizador y 2) la capacidad de cancelar o sobrescribir un temporizador existente por referencia, y 3) la eliminaci\u00f3n del uso creciente de recursos en sistemas inactivos con temporizadores programados en el futuro.<\/span><\/p>\n<p>Utilic\u00e9 un cubo independiente '<strong>crondata<\/strong>' para mantener el horario o los documentos de control (no cuento el uso de '<strong>metadatos<\/strong>ya que es un bloc de notas del sistema necesario para Eventing y reservado para Eventing en s\u00ed) para ofrecer la m\u00e1xima flexibilidad a la hora de manipular datos en otros buckets.    Si hubiera colocado los documentos de control en otro bucket, no podr\u00eda realizar operaciones N1QL en ese bucket (ya que es un bucket de origen para la funci\u00f3n Eventing) y me habr\u00eda limitado \u00fanicamente a operaciones KV para manipular datos en el bucket colocado.<\/p>\n<p>Te reto a que pruebes otros casos de uso de cron y tambi\u00e9n a que pienses en otras formas de aprovechar un servicio de programaci\u00f3n:<\/p>\n<ul>\n<li>Comprobaci\u00f3n de un recuento de elementos en un gran conjunto de datos durante las \"horas valle\" y realizaci\u00f3n de una purga incremental.<\/li>\n<li>Realizaci\u00f3n del enriquecimiento programado de documentos a trav\u00e9s de N1QL.<\/li>\n<li>Recalcular peri\u00f3dicamente las carteras de valores.<\/li>\n<li>Gesti\u00f3n de TTL o tiempos de expiraci\u00f3n a trav\u00e9s de N1QL en un horario recurrente, consulte <a href=\"https:\/\/www.couchbase.com\/blog\/es\/how-to-manage-ttl-with-couchbase-n1ql\/\">C\u00f3mo gestionar documentos Time-To-Live (TTL) con Couchbase N1QL.<\/a><\/li>\n<li>Integraci\u00f3n con puntos finales REST externos de forma repetida, consulte <a href=\"https:\/\/www.couchbase.com\/blog\/es\/using-curl-eventing-service-update\/\">Uso de cURL con el servicio Eventing: Actualizaci\u00f3n<\/a>.<\/li>\n<li>Actualizar el JavaScript de <strong>cron_impl_2func_651<\/strong> para a\u00f1adir un nuevo campo \"prev_astatus\" al objeto din\u00e1mico JSON para guardar el indicador de resultado verdadero\/falso devuelto por la acci\u00f3n de usuario ejecutada anteriormente.<\/li>\n<\/ul>\n<h3>Actualizaciones<\/h3>\n<p>Este blog se actualiz\u00f3 el 24 de julio de 2020 para a\u00f1adir una soluci\u00f3n para las versiones 6.5.x que tienen un n\u00famero creciente de operaciones de cubo de metadatos que eventualmente pueden bloquear las mutaciones para una funci\u00f3n de Eventing dada al crear temporizadores en el futuro (como en una hora+) en un sistema por lo dem\u00e1s inactivo.<\/p>\n<h3>Pr\u00f3ximos pasos<\/h3>\n<p>En unas semanas \"<em>Implementaci\u00f3n de un cron port\u00e1til robusto como programador a trav\u00e9s de Couchbase Eventing (Parte 2)<\/em>\"donde exploraremos la ejecuci\u00f3n de una secuencia de sentencias N1QL din\u00e1micas controladas por la base de datos sin necesidad de editar la funci\u00f3n de eventos o definir un script de \"acci\u00f3n\" codificado dentro de la funci\u00f3n de eventos.<\/p>\n<h3>Recursos<\/h3>\n<ul>\n<li><em>Descargar: <\/em><a href=\"https:\/\/www.couchbase.com\/blog\/es\/downloads\/?family=server&amp;product=couchbase-server-developer\">Descargar Couchbase Server 6.5.1<\/a><\/li>\n<li><em>Funci\u00f3n Eventing: <\/em><a href=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Strabala\/CronFiles\/cron_impl_2func_651.json\">cron_impl_2func_651.json<\/a><\/li>\n<li><em>Funci\u00f3n Eventing Helper:<\/em> <a href=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Strabala\/CronFiles\/cron_impl_2func_651_help.json\">cron_impl_2func_651_help.json<\/a><\/li>\n<\/ul>\n<h3>Referencias<\/h3>\n<ul>\n<li>Documentaci\u00f3n de Couchbase Eventing:<br \/>\n<a href=\"https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-overview.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/docs.couchbase.com\/server\/current\/eventing\/eventing-overview.html<\/a><\/li>\n<li>Couchbase Server 6.5 Novedades:<br \/>\n<a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/introduction\/whats-new.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/docs.couchbase.com\/server\/6.5\/introduction\/whats-new.html<\/a><\/li>\n<li>Blogs de Couchbase sobre Eventing:<br \/>\n<a href=\"https:\/\/www.couchbase.com\/blog\/es\/tag\/eventing\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.couchbase.com\/blog\/tag\/eventing\/<\/a><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">Nos encantar\u00eda saber qu\u00e9 te han parecido las caracter\u00edsticas de la versi\u00f3n 6.5 y c\u00f3mo beneficiar\u00e1n a tu negocio en el futuro. Por favor, comparte tu opini\u00f3n a trav\u00e9s de los comentarios o en el Couchbase <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/es\/forums\/\"><span style=\"font-weight: 400\">foro<\/span><\/a><span style=\"font-weight: 400\">.\u00a0<\/span><\/p>\n<h3>Notas a pie de p\u00e1gina<\/h3>\n<div style=\"font-size: 75%\">\n<p><a href=\"#footnote_1\"><sup>[1]<\/sup><\/a> La implementaci\u00f3n del temporizador en el servicio Eventing est\u00e1 dise\u00f1ada para manejar grandes cantidades de temporizadores distribuidos en millones a alta velocidad. Un solo nodo Eventing puede manejar m\u00e1s de 100K temporizadores por segundo y la \u00fanica promesa es ejecutar los temporizadores tan pronto como sea posible, por ejemplo, no hay temporizadores perdidos. Tenga en cuenta que el intervalo de escaneo actual es de siete (7) segundos para recoger los temporizadores que est\u00e1n listos para disparar, por lo que debe esperar algunos retrasos. Para m\u00e1s detalles sobre la programaci\u00f3n de temporizadores consulte <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/eventing\/eventing-timers.html#wall-clock-accuracy\">Temporizadores: Precisi\u00f3n de reloj de pared<\/a> en la documentaci\u00f3n de Couchbase.<\/p>\n<p><a href=\"#footnote_2\"><sup>[2]<\/sup><\/a> Ajustando <em>allow_interbucket_recursion<\/em> a <em>verdadero<\/em> est\u00e1s eliminando las protecciones que se colocaron en el servidor Couchbase para proteger contra la l\u00f3gica Eventing accidental que puede iniciar bucles recursivos infinitos.  No hay nada malo con esto pero es f\u00e1cil cometer un error cuando se aprovecha la recursividad.  En las versiones 6.6 de Couchbase ajustando la l\u00f3gica Eventing puede ser colapsada de dos (2) Funciones Eventing en una (1) Funci\u00f3n Eventing simplificada sin necesidad de ajustar la funci\u00f3n <em>allow_interbucket_recursion<\/em> ajuste.<\/p>\n<p><a href=\"#footnote_3\"><sup>[3]<\/sup><\/a> Existen dos limitaciones importantes. En primer lugar, si un documento se modifica varias veces en poco tiempo, las llamadas pueden unirse en un \u00fanico evento debido a la deduplicaci\u00f3n. En segundo lugar, no es posible discernir entre las operaciones de creaci\u00f3n y las de actualizaci\u00f3n.  Para las propuestas de un <em>cron<\/em> funci\u00f3n ninguna de las dos limitaciones presenta un problema.<\/p>\n<p><a href=\"#footnote_4\"><sup>[4]<\/sup><\/a> Por qu\u00e9 no implement\u00e9 la sem\u00e1ntica exacta de crontab, podr\u00eda haberlo hecho pero la cantidad de c\u00f3digo es excesiva - te remito a explorar el <a href=\"https:\/\/github.com\/kelektiv\/node-cron\">https:\/\/github.com\/kelektiv\/node-cron<\/a> junto con sus dependencias moment y moment-timezone (todos ellos paquetes muy grandes).  El getNextRecurringDate(hour_str, min_str) puede no ser tan flexible pero es simple y cubre nuestro caso de uso.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>This is the first of a multi-part series to leverage the Couchbase Eventing Service to run multiple scheduled tasks at specific recurring intervals in a cron like fashion completely inside the database without requiring additional infrastructure via a single general-purpose [&hellip;]<\/p>","protected":false},"author":42711,"featured_media":8494,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1815,1821,2225,1816,1819,2273,2389,1812],"tags":[],"ppma_author":[9113],"class_list":["post-8489","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-best-practices-and-tutorials","category-couchbase-architecture","category-cloud","category-couchbase-server","category-data-modeling","category-eventing","category-solutions","category-n1ql-query"],"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>Implementing a Scheduler Via Couchbase Eventing (Part 1)<\/title>\n<meta name=\"description\" content=\"In this tutorial, you&#039;ll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.\" \/>\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\/es\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Implementing a Scheduler Via Couchbase Eventing (Part 1)\" \/>\n<meta property=\"og:description\" content=\"In this tutorial, you&#039;ll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T17:50:02+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T05:40:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1920\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Jon Strabala, Principal Product Manager, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jon Strabala, Principal Product Manager, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"73 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\"},\"author\":{\"name\":\"Jon Strabala, Principal Product Manager, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c991579f88217edee79ffedb6fc914cc\"},\"headline\":\"Implementing a Scheduler Via Couchbase Eventing (Part 1)\",\"datePublished\":\"2020-05-22T17:50:02+00:00\",\"dateModified\":\"2025-06-14T05:40:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\"},\"wordCount\":8614,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg\",\"articleSection\":[\"Application Design\",\"Best Practices and Tutorials\",\"Couchbase Architecture\",\"Couchbase Capella\",\"Couchbase Server\",\"Data Modeling\",\"Eventing\",\"Solutions\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\",\"name\":\"Implementing a Scheduler Via Couchbase Eventing (Part 1)\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg\",\"datePublished\":\"2020-05-22T17:50:02+00:00\",\"dateModified\":\"2025-06-14T05:40:23+00:00\",\"description\":\"In this tutorial, you'll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg\",\"width\":2560,\"height\":1920,\"caption\":\"Prague Astronomical Clock\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Implementing a Scheduler Via Couchbase Eventing (Part 1)\"}]},{\"@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\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@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\/c991579f88217edee79ffedb6fc914cc\",\"name\":\"Jon Strabala, Principal Product Manager, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/9c6045b0c2f7b07b0ee10f94ad748a25\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/db52a9f6d84faba430dd38106cdbc16ff02c2066b103b5f6b4cfcde40e83c683?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/db52a9f6d84faba430dd38106cdbc16ff02c2066b103b5f6b4cfcde40e83c683?s=96&d=mm&r=g\",\"caption\":\"Jon Strabala, Principal Product Manager, Couchbase\"},\"description\":\"Jon Strabala is a Principal Product Manager, responsible for the Couchbase Eventing Service. Before joining Couchbase, he spent more than 20 years building software products across various domains, starting with EDA in aerospace then transitioning to building enterprise software focused on what today is coined \u201cIoT\u201d and \u201cat-scale data.\u201d Jon worked for several small software consultancies until eventually starting and managing his own firm. He has extensive experience in NoSQL\/NewSQL, both in contributing and commercializing new technologies such as compressed bitmaps and column stores. Jon holds a bachelor\u2019s degree in electrical engineering and a master's in computer engineering, both from the University of Southern California, and an MBA from the University of California at Irvine.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/es\/author\/jon-strabala\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Implementaci\u00f3n de un programador a trav\u00e9s de eventos Couchbase (Parte 1)","description":"In this tutorial, you'll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.","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\/es\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/","og_locale":"es_MX","og_type":"article","og_title":"Implementing a Scheduler Via Couchbase Eventing (Part 1)","og_description":"In this tutorial, you'll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.","og_url":"https:\/\/www.couchbase.com\/blog\/es\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/","og_site_name":"The Couchbase Blog","article_published_time":"2020-05-22T17:50:02+00:00","article_modified_time":"2025-06-14T05:40:23+00:00","og_image":[{"width":2560,"height":1920,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg","type":"image\/jpeg"}],"author":"Jon Strabala, Principal Product Manager, Couchbase","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jon Strabala, Principal Product Manager, Couchbase","Est. reading time":"73 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/"},"author":{"name":"Jon Strabala, Principal Product Manager, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c991579f88217edee79ffedb6fc914cc"},"headline":"Implementing a Scheduler Via Couchbase Eventing (Part 1)","datePublished":"2020-05-22T17:50:02+00:00","dateModified":"2025-06-14T05:40:23+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/"},"wordCount":8614,"commentCount":3,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg","articleSection":["Application Design","Best Practices and Tutorials","Couchbase Architecture","Couchbase Capella","Couchbase Server","Data Modeling","Eventing","Solutions","SQL++ \/ N1QL Query"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/","url":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/","name":"Implementaci\u00f3n de un programador a trav\u00e9s de eventos Couchbase (Parte 1)","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg","datePublished":"2020-05-22T17:50:02+00:00","dateModified":"2025-06-14T05:40:23+00:00","description":"In this tutorial, you'll learn about running fixed user routines and JavaScript functions defined inside an Eventing Function.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/04\/Schema_Orloj_pragueorlojhzenilc-scaled.jpg","width":2560,"height":1920,"caption":"Prague Astronomical Clock"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/implementing-a-robust-portable-cron-like-scheduler-via-couchbase-eventing-part-1\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Implementing a Scheduler Via Couchbase Eventing (Part 1)"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","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":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@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\/c991579f88217edee79ffedb6fc914cc","name":"Jon Strabala, Director Principal de Producto, Couchbase","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/9c6045b0c2f7b07b0ee10f94ad748a25","url":"https:\/\/secure.gravatar.com\/avatar\/db52a9f6d84faba430dd38106cdbc16ff02c2066b103b5f6b4cfcde40e83c683?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/db52a9f6d84faba430dd38106cdbc16ff02c2066b103b5f6b4cfcde40e83c683?s=96&d=mm&r=g","caption":"Jon Strabala, Principal Product Manager, Couchbase"},"description":"Jon Strabala es Gerente Principal de Producto, responsable del Servicio de Eventos de Couchbase. Antes de unirse a Couchbase, pas\u00f3 m\u00e1s de 20 a\u00f1os construyendo productos de software a trav\u00e9s de diversos dominios, comenzando con EDA en el sector aeroespacial y luego la transici\u00f3n a la construcci\u00f3n de software empresarial centrado en lo que hoy se conoce como \"IoT\" y \"datos a escala\". Jon trabaj\u00f3 para varias peque\u00f1as consultoras de software hasta que finalmente fund\u00f3 y dirigi\u00f3 su propia empresa. Tiene una amplia experiencia en NoSQL\/NewSQL, tanto en la contribuci\u00f3n como en la comercializaci\u00f3n de nuevas tecnolog\u00edas como los mapas de bits comprimidos y los almacenes de columnas. Jon es licenciado en ingenier\u00eda el\u00e9ctrica y tiene un m\u00e1ster en ingenier\u00eda inform\u00e1tica, ambos por la Universidad del Sur de California, y un MBA por la Universidad de California en Irvine.","url":"https:\/\/www.couchbase.com\/blog\/es\/author\/jon-strabala\/"}]}},"authors":[{"term_id":9113,"user_id":42711,"is_guest":0,"slug":"jon-strabala","display_name":"Jon Strabala, Principal Product Manager, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/db52a9f6d84faba430dd38106cdbc16ff02c2066b103b5f6b4cfcde40e83c683?s=96&d=mm&r=g","author_category":"","last_name":"Strabala, Principal Product Manager, Couchbase","first_name":"Jon","job_title":"","user_url":"","description":"Jon Strabala es Gerente Principal de Producto, responsable del Servicio de Eventos de Couchbase. Antes de unirse a Couchbase, pas\u00f3 m\u00e1s de 20 a\u00f1os construyendo productos de software a trav\u00e9s de diversos dominios, comenzando con EDA en el sector aeroespacial y luego la transici\u00f3n a la construcci\u00f3n de software empresarial centrado en lo que hoy se conoce como \"IoT\" y \"datos a escala\". Jon trabaj\u00f3 para varias peque\u00f1as consultoras de software hasta que finalmente fund\u00f3 y dirigi\u00f3 su propia empresa. Tiene una amplia experiencia en NoSQL\/NewSQL, tanto en la contribuci\u00f3n como en la comercializaci\u00f3n de nuevas tecnolog\u00edas como los mapas de bits comprimidos y los almacenes de columnas. Jon es licenciado en ingenier\u00eda el\u00e9ctrica y tiene un m\u00e1ster en ingenier\u00eda inform\u00e1tica, ambos por la Universidad del Sur de California, y un MBA por la Universidad de California en Irvine."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/8489","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/42711"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=8489"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/8489\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/8494"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=8489"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=8489"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=8489"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=8489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}