Con el lanzamiento de Couchbase 6.6 el Servicio de Eventos tiene importantes mejoras en su funcionalidad.
Hemos introducido nuevos temporizadores de eventos que pueden cancelarse utilizando la función cancelTimer(), o creando un nuevo temporizador con el mismo identificador de referencia que un temporizador existente. Los temporizadores recurrentes también son totalmente compatibles, y se pueden utilizar fácilmente para crear lógica repetitiva mediante el uso de una devolución de llamada del temporizador para crear temporizadores nuevos. La programación de temporizadores permite crear temporizadores para días, semanas o años en el futuro sin que ello afecte negativamente al rendimiento. El manejador OnDelete ahora indica si un documento fue borrado o expiró usando el nuevo parámetro "options". Las estadísticas de eventos clave en la interfaz de usuario se encuentran ahora junto a cada control del ciclo de vida de las funciones.
Juntas, estas mejoras simplifican el esfuerzo y el código necesarios para crear una lógica empresarial sólida.
Requisitos previos
En este artículo presentaremos las claves Eventos mejoras añadidas a la última versión de GA, es decir, la versión 6.6.0 de Couchbase, y para cada elemento proporcionamos un ejemplo básico que funciona. Sin embargo, por favor, comprenda que ninguna de las funciones de eventos proporcionadas en este artículo funcionará "tal cual" en versiones anteriores del servidor Couchbase sin cambios significativos y complejas soluciones..
Si usted no está familiarizado con Couchbase o el servicio de Eventing por favor camine a través de GET STARTED y un ejemplo de Eventing específicamente consulte lo siguiente:
- Configure un servidor Couchbase 6.6.0 que funcione según las instrucciones de Comience aquí
- Comprender tanto los conceptos básicos de Eventing como la forma de implementar una función básica de Eventing siguiendo las instrucciones del manual Archivo de documentos ejemplo.
Los temporizadores de eventos ahora se pueden cancelar
Con la adición de la función cancelTimer(), o mediante la creación de un nuevo temporizador de eventos con el mismo identificador de referencia que un temporizador existente, se pueden cancelar los temporizadores activos que aún no se han disparado. Esta mejora simplifica el código necesario para realizar una lógica de negocio robusta.
Los desarrolladores ya no se ven obligados a añadir campos y lógica y hacer comprobaciones adicionales para asegurarse de que un temporizador que se dispara no es "obsoleto" y sustituido por un temporizador más reciente.
Por ejemplo:
- Cree un bucket "fuente" y un bucket "metadatos" para Eventing.
- Despliegue esta función (código más abajo).
- Crear un documento en el cubo de origen con:
1CLAVE marcador_usuario::1 y DATOS {"tipo": "marcador_usuario", "id": 1} - Inspeccione los troncos al cabo de un minuto.
- Elimina el documento con la clave "user_scoreboard::1".
- Inspeccione los troncos al cabo de un minuto.
- Despliega esta función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
función UserInactivityCallback(contexto) { registro(usuario inactivo durante 10 minutos, contexto.docId); /* * tomar algunas medidas ... */ } función OnUpdate(doc, meta) { si (doc.tipo != 'marcador_usuario') devolver; // Crear una marca de tiempo de 600 segundos a partir de ahora var ADiezMinutosDeAhora = nuevo Fecha(); // Obtiene la hora actual y le añade 600 seg. ADiezMinutosDeAhora.setSeconds(ADiezMinutosDeAhora.getSeconds() + 600); // Cada mutación al marcador del usuario resultará en un nuevo Temporizador pero // los temporizadores antiguos se cancelarán ya que tienen el mismo identificador, meta.id. crearTiempo(UserInactivityCallback, ADiezMinutosDeAhora, meta.id, { "docId": meta.id }); } función OnDelete(meta, opciones) { si (!(meta.id.empiezaCon("user_scoreboard:"))) devolver; registro('el marcador de usuario fue borrado por', meta.id); // eliminar el temporizador puesto que ya no existe ningún documento relacionado. cancelTimer(UserInactivityCallback,meta.id); /* * tomar algunas medidas ... */ } |
Los temporizadores de eventos recurrentes son ahora totalmente compatibles
Los temporizadores recurrentes son totalmente compatibles, es decir, una función invocada por una llamada de retorno de un temporizador puede crear nuevos temporizadores de forma fiable. Esta actualización permite que una única función de eventos implemente de forma fiable eventos recurrentes complejos creando nuevos temporizadores desde la llamada de retorno de otro temporizador.
Anteriormente se requería una co-función para crear de forma fiable una serie recurrente de Temporizadores (imagen izquierda), la versión 6.6 simplifica el código necesario para implementar la lógica de negocio recurrente (o programada) (imagen derecha).

Por ejemplo:
- Cree un bucket "fuente" y un bucket "metadatos" para Eventing.
- Haga un alias de cubo en la configuración de la función como "src_bkt" para el cubo de origen en modo lectura+escritura.
- Despliegue esta función (código más abajo).
- Crear un documento en el cubo de origen con:
1CLAVE temporizador_recurrente::1 y DATOS {"tipo": "temporizador_recurrente", "id": 1, "activo": verdadero} - Inspeccione los troncos al cabo de un minuto.
- Inspeccione los troncos después de varios minutos.
- Altere el documento con clave "recurring_timer::1" y cambie el campo "active" a false de la siguiente manera:
-
1CLAVE temporizador_recurrente::1 y DATOS {"tipo": "temporizador_recurrente", "id": 1, "activo": falso}
- Inspeccione los troncos al cabo de un minuto.
- Despliega esta función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
función CrearRecurringTimer(contexto) { registro('De CreateRecurringTimer: creando temporizador', contexto.modo, contexto.id); // Crear una marca de tiempo 30 segundos a partir de ahora var treintaSegundosDesdeAhora = nuevo Fecha(); // Obtener la hora actual y añadirle 30 seg. treintaSegundosDesdeAhora.setSeconds(treintaSegundosDesdeAhora.getSeconds() + 30); // Crear un documento para usar como out para nuestro contexto crearTiempo(RecurringTimerCallback, treintaSegundosDesdeAhora, contexto.id, contexto); } función RecurringTimerCallback(contexto) { registro('De RecurringTimerCallback: temporizador disparado', contexto); /* * hacer cualquier tipo de trabajo recurrente aquí, sólo actualizar un date_stamp en un documento * se vuelve a escribir en el cubo de origen (pero con el ID prefijado con "cur_") */ src_bkt["cur_" + contexto.id] = { "última_actualización": nuevo Fecha() }; // rearmar el temporizador CrearRecurringTimer({ "id": contexto.id, "mode": "via_callback" }) } función OnUpdate(doc, meta) { // Lo normal sería filtrar las mutaciones de interés si (doc.tipo !== temporizador_recurrente) devolver; si (doc.activo === falso) { si (cancelTimer(RecurringTimerCallback, meta.id)) { registro('Desde OnUpdate: cancelado temporizador activo, doc.activo', doc.activo, meta.id); } si no { registro('De OnUpdate: no hay temporizador activo para cancelar, doc.activo', doc.activo, meta.id); } } si no { registro('Desde OnUpdate: crear/sobrescribir doc.active', doc.activo, meta.id); CrearRecurringTimer({ "id": meta.id, "mode": "via_onupdate" }); } } |
Se pueden crear temporizadores con días/semanas/años de antelación
Se puede crear un temporizador o un millón de temporizadores sin que ello repercuta negativamente en el rendimiento de un sistema de Eventing que, de otro modo, estaría inactivo. Esta capacidad abre casos de uso para notificaciones a largo plazo y programas de captación de clientes.
En las versiones 6.5.X, la creación de varios miles de temporizadores en el futuro (como en una hora o más) en un sistema inactivo daba lugar a un número creciente de operaciones de cubo de metadatos que afectaban al rendimiento y podían llegar a bloquear las mutaciones para una función de eventos determinada.[1]
Tenga en cuenta que en una operación cancelTimer() o sobrescribiendo un Temporizador existente por referencia habrá documentos temporales en el cubo de "metadatos" de Eventing, eventualmente estos documentos serán purgados.[2]
Por ejemplo:
Demostrar la creación de una gran cantidad de temporizadores de eventos (en este caso 50.000) y programarlos bien en el futuro (por 96 horas) y luego cancelar todo el conjunto (o permitió que todos ellos para disparar la rutina de devolución de llamada TimerCallback).
- Cree un bucket "fuente" y un bucket "metadatos" para Eventing.
- Despliegue esta función (código más abajo).
- Crear un documento en el cubo de origen con cualquier DATOS como sólo nos fijamos en.
1CLAVE spawn_50k_timers::1 - Inspeccione los troncos después de uno o dos minutos.
- Elimina el documento con la clave "spawn_50k_timers::1".
- Inspeccione los troncos después de uno o dos minutos.
- Despliega esta función.
Para ver cómo se crean y disparan los cronómetros de las pruebas de 50K (en lugar de cancelarse):
- Edita la función y cambia delayMinutes a 1 (no quieres esperar 4 días).
- Despliegue la función modificada (código siguiente).
- Cree un documento en el bucket de origen con cualquier DATO ya que sólo miramos la CLAVE.
1CLAVE spawn_50k_timers::1 - Inspeccione los troncos después de dos minutos.
- Elimina el documento con la clave "spawn_50k_timers::1".
- Inspeccione los troncos después de uno o dos minutos.
- Despliega esta función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
función TimerCallback(contexto) { si (contexto.timer_num == 1 || contexto.timer_num == 50000) registro('TimerCallback: temporizador disparado #', contexto.timer_num); /* * tomar algunas medidas ... */ } función OnUpdate(doc, meta) { si (meta.id != 'spawn_50k_timers::1') devolver; // Crear una marca de tiempo dentro de 96 horas (4 días) var delayMinutes = 60*96; var futureTime = nuevo Fecha(); futureTime.setMinutes(futureTime.getMinutes() + delayMinutes); // hacer temporizadores 50K para (var i=1; i<= 50000; i++) { var timer_id = "tmr_" + i; si (i == 1 || i == 50000) registro('OnUpdate: crear temporizador # ', i, timer_id); pruebe { crearTiempo(TimerCallback, futureTime, timer_id, { "docId": meta.id, "timer_num": i}); } captura (e) { registro('OnUpdate: crear #', i, " error ", e); } } } función OnDelete(meta, opciones) { si (meta.id != 'spawn_50k_timers::1') devolver; /* * Eliminar el temporizador 50K ya que no existe ningún documento de control relacionado. * * Tenga en cuenta en una cancelación o sobrescritura habrá documentos que se * limpiado en a) el programa de disparo inicial de los temporizadores, b) el despliegue * de la función, o c) un proceso de limpieza perezoso por parte del Servicio de Eventing. */ para (var i=1; i<= 50000; i++) { var timer_id = "tmr_" + i; si (i == 1 || i == 50000) registro('OnDelete: borrar temporizador # ', i, timer_id); pruebe { cancelTimer(TimerCallback, timer_id); } captura (e) { registro('OnDelete: borrar temporizador #', i, " error ", e); } } } |
Las estadísticas de eventos están ahora ubicadas junto con los controles del ciclo de vida.
Cuatro (4) estadísticas clave de Eventing en la UI están ahora co-localizadas con cada control del Ciclo de Vida de las Funciones. Estas mejoras simplifican el esfuerzo y el código y diagnosticar Funciones Eventing para hacer rápidamente la lógica de negocio robusto.
El desarrollador o administrador puede determinar inmediatamente que una función se está comportando mal sin tener que navegar fuera de los controles del ciclo de vida de la función.
Por ejemplo:
- Cree un bucket "fuente" y un bucket "metadatos" para Eventing.
- Haga unos documentos cualquier CLAVE y cualquier DATOS en el cubo de origen.
- Despliega la función modificada (código de abajo) contiene un error de sintaxis, olvidando poner var delante de una variable.
|
1 2 3 4 5 |
función OnUpdate(doc, meta) { registro(docId, meta.id); // esto es un error falta 'var' a = 2; } |
- Cuando se despliega la función de eventos, el desarrollador recibe inmediatamente la señal de que algo ha ido mal.

- El usuario responde al feed back, es decir, a los fallos "rojos" y puede inspeccionar la Función en busca del origen del error.

- Despliegue esta función de eventos.
- Corregir el error en este caso hacer la línea 4: "var a=2;".
- Despliegue de nuevo esta función de eventos.
- Una vez que el código se fija el desarrollador puede ver el comportamiento correcto y el progreso como contador de éxito ahora se incrementa.

El manejador OnDelete ahora indica Borrado o Expiración
El manejador OnDelete ahora indica si un documento fue eliminado o expiró a través de un nuevo parámetro "opciones". Esta capacidad, solicitada a menudo, permite ejecutar una lógica diferente en función del tipo de eliminación.
Por ejemplo:
- Crear un bucket "fuente" y un bucket "metadatos" para Eventing
- Despliegue esta función (código siguiente)
- Cree un documento en el bucket de origen con cualquier DATO ya que sólo miramos la CLAVE
1CLAVE doc_to_delete::1 - Inspeccione los registros después de unos segundos
- Elimina el documento con clave "doc_to_delete::1"
- Inspeccione los troncos después de un minuto
- Despliegue de esta función
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
función OnDelete(meta, opciones) { si (opciones.caducado) { registro("doc caducado:",meta.id); /* * tomar alguna acción ... */ } si no { registro("doc borrado:",meta.id); /* * tomar alguna otra acción ... */ } } |
Explorar los recursos de Couchbase Server 6.6
Referencias
- Documentación de Couchbase Eventing:
https://docs.couchbase.com/server/current/eventing/eventing-overview.html - Ejemplos de Couchbase Eventing:
https://docs.couchbase.com/server/current/eventing/eventing-examples.html - Couchbase Server 6.6 Novedades:
https://docs.couchbase.com/server/6.6/introduction/whats-new.html - Blogs de Couchbase sobre Eventing:
https://www.couchbase.com/blog/tag/eventing/
Nos encantaría saber qué te han parecido las características de la versión 6.6 y cómo beneficiarán a tu negocio en el futuro. Por favor, comparte tu opinión a través de los comentarios o en la sección de Couchbase foro.
Notas a pie de página
[1] La gravedad se rige por: a) El número de vBuckets que tienen un temporizador activo. Por lo tanto, si sólo hay unos pocos temporizadores en el futuro, el problema puede no ser perceptible o materializarse. y b) Si un temporizador Eventing se ha disparado recientemente en un vBucket (que elimina el problema para el vBucket dado en una base por función). Por lo tanto, los sistemas 6.5 con mucha actividad de temporizadores a corto plazo no experimentarán este problema, incluso si los temporizadores están programados para un futuro lejano. Esto no es un problema en 6.6.
[2] En una operación cancelTimer() o sobreescribiendo un temporizador existente por referencia, habrá documentos temporales en el cubo de metadatos de eventos que se limpiarán a) en el programa inicial de disparo de los temporizadores cancelados (o sobreescritos), o b) al desinstalar la función. Este comportamiento es diferente al de un temporizador que se dispara a la hora programada, en el que todos los metadatos de eventos asociados se limpian inmediatamente del cubo de metadatos de eventos al dispararse el temporizador.