Com o lançamento do Couchbase 6.6, o Eventing Service apresenta grandes aprimoramentos de funcionalidade.
Introduzimos novos temporizadores de eventos que podem ser cancelados usando a função cancelTimer() ou criando um novo temporizador com o mesmo identificador de referência de um temporizador existente. Os temporizadores recorrentes também são totalmente compatíveis e podem ser facilmente usados para criar lógica repetitiva usando um retorno de chamada do temporizador para criar novos temporizadores. O agendamento de temporizadores permite que eles sejam criados para dias, semanas ou anos no futuro, sem impacto adverso no desempenho. O manipulador OnDelete agora indica se um documento foi excluído ou expirou usando o novo parâmetro "options". As principais estatísticas de eventos na interface do usuário agora estão co-localizadas com cada controle de ciclo de vida do Functions.
Juntos, esses aprimoramentos simplificam o esforço e o código necessários para criar uma lógica comercial robusta.
Pré-requisitos
Neste artigo, apresentaremos os principais Eventos melhorias adicionadas à versão mais recente do GA, ou seja, a versão 6.6.0 do Couchbase, e para cada item fornecemos um exemplo básico funcional. No entanto, entenda que nenhuma das Eventing Functions fornecidas neste artigo funcionará "como está" em versões anteriores do servidor Couchbase sem alterações significativas e soluções complexas.
Se você não estiver familiarizado com o Couchbase ou com o serviço Eventing, consulte o GET STARTED e um exemplo de Eventing, especificamente o seguinte:
- Configure um servidor Couchbase 6.6.0 funcional de acordo com as instruções em Comece aqui!
- Compreender os conceitos básicos de Eventing e como implantar uma função básica de Eventing de acordo com as instruções do Arquivamento de documentos exemplo.
Os cronômetros de eventos agora podem ser cancelados
Com a adição da função cancelTimer() ou com a criação de um novo Eventing Timer com o mesmo identificador de referência de um timer existente, os timers ativos que ainda não foram acionados podem ser cancelados. Esse aprimoramento simplifica o código necessário para criar uma lógica comercial robusta.
Os desenvolvedores não são mais obrigados a adicionar campos e lógica e a fazer verificações adicionais para garantir que um Timer que está sendo disparado não seja "obsoleto" e substituído por um Timer mais recente.
Exemplo:
- Crie um compartimento de "origem" e um compartimento de "metadados" para Eventing.
- Implemente essa função (código abaixo).
- Crie um documento no bucket de origem com:
1CHAVE placar_do_usuário::1 e DADOS {"tipo": "user_scoreboard", "id": 1} - Inspecione os registros após um minuto.
- Exclua o documento com a chave "user_scoreboard::1".
- Inspecione os registros após um minuto.
- Cancelar a implantação dessa função.
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 |
função UserInactivityCallback(contexto) { registro('O usuário ficou inativo por 10 minutos', contexto.docId); /* * tomar algumas medidas ... */ } função Sobre a atualização(doc, meta) { se (doc.tipo != 'user_scoreboard') retorno; // Criar um registro de data e hora 600 segundos a partir de agora var dezMinutosDeAgora = novo Data(); // Obtenha a hora atual e adicione 600 segundos a ela. dezMinutosDeAgora.setSeconds(dezMinutosDeAgora.getSeconds() + 600); // Toda mutação no placar do usuário resultará em um novo Timer, mas // os cronômetros antigos serão cancelados, pois têm o mesmo identificador, meta.id. createTimer(UserInactivityCallback, dezMinutosDeAgora, meta.id, { "docId": meta.id }); } função OnDelete(meta, opções) { se (!(meta.id.startsWith("user_scoreboard:"))) retorno; registro('o placar do usuário foi excluído por', meta.id); // remover o cronômetro, pois não há mais nenhum documento relacionado. cancelTimer(UserInactivityCallback,meta.id); /* * tomar algumas medidas ... */ } |
Os temporizadores de eventos recorrentes agora são totalmente compatíveis
Os temporizadores recorrentes são totalmente compatíveis, ou seja, uma função invocada por uma chamada de retorno de temporizador pode criar novos temporizadores de forma confiável. Essa atualização permite que uma única Eventing Function implemente de forma confiável eventos recorrentes complexos, criando novos timers a partir da chamada de retorno de outro timer.
Anteriormente, era necessária uma cofunção para criar de forma confiável uma série recorrente de temporizadores (imagem à esquerda). A versão 6.6 simplifica o código necessário para implementar a lógica comercial recorrente (ou programada) (imagem à direita).
Exemplo:
- Crie um compartimento de "origem" e um compartimento de "metadados" para Eventing.
- Crie um alias de bucket na configuração da função como "src_bkt" para o bucket de origem no modo leitura+gravação.
- Implemente essa função (código abaixo).
- Crie um documento no bucket de origem com:
1CHAVE recurring_timer::1 e DADOS {"tipo": "recurring_timer", "id": 1, "ativo": verdadeiro} - Inspecione os registros após um minuto.
- Inspecione as toras após alguns minutos.
- Altere o documento com a chave "recurring_timer::1" e mude o campo "active" para false da seguinte forma:
-
1CHAVE recurring_timer::1 e DADOS {"tipo": "recurring_timer", "id": 1, "ativo": falso}
- Inspecione os registros após um minuto.
- Cancelar a implantação dessa função.
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 |
função CreateRecurringTimer(contexto) { registro('De CreateRecurringTimer: criando cronômetro', contexto.modo, contexto.id); // Criar um registro de data e hora 30 segundos a partir de agora var trinta segundos a partir de agora = novo Data(); // Obter a hora atual e adicionar 30 segundos a ela. trinta segundos a partir de agora.setSeconds(trinta segundos a partir de agora.getSeconds() + 30); // Criar um documento para usar como saída para nosso contexto createTimer(RecurringTimerCallback, trinta segundos a partir de agora, contexto.id, contexto); } função RecurringTimerCallback(contexto) { registro('From RecurringTimerCallback: timer fired', contexto); /* * Faça qualquer tipo de trabalho recorrente aqui, apenas atualize um date_stamp em um documento * gravado de volta no bucket de origem (mas com o ID prefixado com "cur_") */ src_bkt["cur_" + contexto.id] = { "last_update": novo Data() }; // rearmar o cronômetro CreateRecurringTimer({ "id": contexto.id, "mode" (modo): "via_callback" }) } função Sobre a atualização(doc, meta) { // Normalmente, você filtraria as mutações de interesse se (doc.tipo !== 'recurring_timer') retorno; se (doc.ativo === falso) { se (cancelTimer(RecurringTimerCallback, meta.id)) { registro('From OnUpdate: canceled active Timer, doc.active', doc.ativo, meta.id); } mais { registro('From OnUpdate: no active Timer to cancel, doc.active', doc.ativo, meta.id); } } mais { registro('From OnUpdate: create/overwrite doc.active', doc.ativo, meta.id); CreateRecurringTimer({ "id": meta.id, "mode" (modo): "via_onupdate" }); } } |
Os cronômetros podem ser criados para dias/semanas/anos futuros
Um ou um milhão de temporizadores podem ser criados sem impacto adverso no desempenho de um sistema Eventing que, de outra forma, estaria ocioso. Essa capacidade abre casos de uso para notificações de longo prazo e programas de reengajamento de clientes.
Nas versões 6.5.X, a criação de alguns milhares de temporizadores no futuro (como em uma hora ou mais) em um sistema ocioso resultava em um número crescente de operações de bucket de metadados que afetavam o desempenho e podiam eventualmente bloquear mutações para uma determinada Eventing Function.[1]
Observe que, em uma operação cancelTimer() ou ao substituir um Timer existente por referência, haverá documentos temporários no compartimento de "metadados" do Eventing.[2]
Exemplo:
Demonstrar a criação de vários Eventing Timers (neste caso, 50.000) e programá-los para um futuro distante (96 horas) e, em seguida, cancelar todo o conjunto (ou permitir que todos disparem a rotina de retorno de chamada) Retorno do cronômetro).
- Crie um compartimento de "origem" e um compartimento de "metadados" para Eventing.
- Implemente essa função (código abaixo).
- Crie um documento no bucket de origem com quaisquer DADOS, como veremos apenas.
1CHAVE spawn_50k_timers::1 - Inspecione as toras após um ou dois minutos.
- Exclua o documento com a chave "spawn_50k_timers::1".
- Inspecione as toras após um ou dois minutos.
- Cancelar a implantação dessa função.
Para ver os 50K Eventing Timers sendo criados e disparados (em vez de serem cancelados):
- Edite a função e altere delayMinutes para 1 (você não quer esperar 4 dias).
- Implemente a função modificada (código abaixo).
- Crie um documento no bucket de origem com quaisquer DADOS, já que só analisamos a CHAVE.
1CHAVE spawn_50k_timers::1 - Inspecione os registros após dois minutos.
- Exclua o documento com a chave "spawn_50k_timers::1".
- Inspecione as toras após um ou dois minutos.
- Cancelar a implantação dessa função.
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 |
função Retorno do cronômetro(contexto) { se (contexto.timer_num == 1 || contexto.timer_num == 50000) registro('TimerCallback: disparou o cronômetro #', contexto.timer_num); /* * tomar algumas medidas ... */ } função Sobre a atualização(doc, meta) { se (meta.id != 'spawn_50k_timers::1') retorno; // Criar um registro de data e hora 96 horas a partir de agora (4 dias) var delayMinutes = 60*96; var futureTime = novo Data(); futureTime.setMinutes(futureTime.getMinutes() + delayMinutes); // criar cronômetros de 50 mil para (var i=1; i<= 50000; i++) { var timer_id = "tmr_" + i; se (i == 1 || i == 50000) registro('OnUpdate: criar cronômetro # ', i, timer_id); tentar { createTimer(Retorno do cronômetro, futureTime, timer_id, { "docId": meta.id, "timer_num": i}); } captura (e) { registro('OnUpdate: create #', i, " erro ", e); } } } função OnDelete(meta, opções) { se (meta.id != 'spawn_50k_timers::1') retorno; /* * Remova o cronômetro de 50K, pois não há mais nenhum documento de controle relacionado. * * Observe que, em um cancelamento ou sobrescrita, haverá documentos que serão * Limpo em a) programação inicial de disparo dos temporizadores, b) desdobramento * da função, ou c) um processo de limpeza preguiçoso pelo Eventing Service. */ para (var i=1; i<= 50000; i++) { var timer_id = "tmr_" + i; se (i == 1 || i == 50000) registro('OnDelete: limpar o cronômetro # ', i, timer_id); tentar { cancelTimer(Retorno do cronômetro, timer_id); } captura (e) { registro('OnDelete: limpar o cronômetro #', i, " erro ", e); } } } |
As estatísticas de eventos agora estão localizadas juntamente com os controles de ciclo de vida
Quatro (4) estatísticas importantes de Eventing na interface do usuário agora estão co-localizadas com cada controle de ciclo de vida das funções. Esses aprimoramentos simplificam o esforço, o código e o diagnóstico das Eventing Functions para criar rapidamente uma lógica comercial robusta.
O desenvolvedor ou administrador pode determinar imediatamente que uma função está se comportando mal sem precisar navegar para fora dos controles do ciclo de vida da função.
Exemplo:
- Crie um compartimento de "origem" e um compartimento de "metadados" para Eventing.
- Faça alguns documentos com qualquer KEY e qualquer DATA no bucket de origem.
- Implemente a função modificada (código abaixo), que contém um erro de sintaxe, esquecendo-se de colocar var na frente de uma variável.
1 2 3 4 5 |
função Sobre a atualização(doc, meta) { registro('docId', meta.id); // isso é um erro, faltando 'var' a = 2; } |
- Quando a função Eventing é implementada, há um feedback imediato de que algo deu errado para o desenvolvedor.
- O usuário responde ao feed back, ou seja, às falhas "vermelhas" e pode inspecionar a função para identificar a origem do erro.
- Cancelar a implantação dessa função de eventos.
- Corrija o erro nesse caso na linha 4: "var a=2;".
- Implemente essa Eventing Function novamente.
- Depois que o código for corrigido, o desenvolvedor poderá ver o comportamento e o progresso corretos à medida que o contador de sucesso for aumentando.
O manipulador OnDelete agora indica a exclusão ou a expiração
O manipulador OnDelete agora indica se um documento foi excluído ou expirou por meio de um novo parâmetro "options". Esse recurso frequentemente solicitado permite que diferentes lógicas sejam executadas, dependendo do tipo de remoção.
Exemplo:
- Criar um bucket de "origem" e um bucket de "metadados" para Eventing
- Implemente essa função (código abaixo)
- Crie um documento no bucket de origem com quaisquer DADOS, pois só examinamos a CHAVE
1CHAVE doc_to_delete::1 - Inspecione os registros após alguns segundos
- Excluir o documento com a chave "doc_to_delete::1"
- Inspecione os registros após um minuto
- Cancelar a implantação dessa função
1 2 3 4 5 6 7 8 9 10 11 12 13 |
função OnDelete(meta, opções) { se (opções.expirado) { registro("doc expired:",meta.id); /* * tomar alguma medida ... */ } mais { registro("doc deleted:",meta.id); /* * Tomar alguma outra medida... */ } } |
Explore os recursos do Couchbase Server 6.6
Referências
- Documentação do Couchbase Eventing:
https://docs.couchbase.com/server/current/eventing/eventing-overview.html - Exemplos de eventos do Couchbase:
https://docs.couchbase.com/server/current/eventing/eventing-examples.html - Couchbase Server 6.6 O que há de novo:
https://docs.couchbase.com/server/6.6/introduction/whats-new.html - Blogs do Couchbase sobre eventos:
https://www.couchbase.com/blog/tag/eventing/
Gostaríamos muito de saber se você gostou dos recursos da versão 6.6 e como ela beneficiará seus negócios daqui para frente. Compartilhe seu feedback nos comentários ou na seção Couchbase fórum.
Notas de rodapé
[1] A gravidade é determinada por: a) O número de vBuckets com um timer ativo. Portanto, se houver apenas alguns temporizadores no futuro, o problema pode não ser perceptível ou não se materializar. e b) Se um temporizador Eventing foi disparado recentemente em um vBucket (o que elimina o problema para o vBucket em questão com base em cada função). Portanto, os sistemas 6.5 com muita atividade de temporizador de curto prazo não terão esse problema, mesmo que os temporizadores estejam programados para um futuro distante. Isso não é um problema na versão 6.6.
[2] Em uma operação cancelTimer() ou sobrescrevendo um Timer existente por referência, haverá um documento temporário no compartimento de "metadados" do Eventing que será limpo: a) na programação inicial de disparo dos Timers cancelados (ou sobrescritos) ou b) em uma não implantação da função. Esse comportamento é diferente do disparo de um Timer no horário programado, no qual todos os metadados de Eventing associados são imediatamente limpos do compartimento de "metadados" de Eventing após o disparo de um Timer.