Notificações de serviços confiáveis
As notificações permitem que os clientes acompanhem as alterações que estão sendo feitas em um objeto no qual estão interessados. Dois tipos de objetos suportam notificações: Reliable State Manager e Reliable Dictionary.
Os motivos mais comuns para a utilização de notificações são:
- Criação de exibições materializadas, como índices secundários ou exibições filtradas agregadas do estado da réplica. Um exemplo é um índice ordenado de todas as chaves no Reliable Dictionary.
- Envio de dados de monitoramento, como o número de usuários adicionados na última hora.
As notificações são disparadas como parte das operações de aplicação. Em uma réplica primária, as operações são aplicadas após a confirmação do quórum como parte do transaction.CommitAsync()
ou this.StateManager.GetOrAddAsync()
. Em réplicas secundárias, as operações são aplicadas no processamento de dados da fila de replicação. Por isso, as notificações devem ser tratadas o mais rápido possível e os eventos síncronos não devem incluir operações caras. Caso contrário, isso pode afetar negativamente o tempo de processamento da transação, bem como o acúmulo de réplicas.
Notificações confiáveis do State Manager
O Reliable State Manager fornece notificações para os seguintes eventos:
- Transação
- Consolidação
- Gestor do Estado
- Reconstruir
- Adição de um estado confiável
- Remoção de um estado confiável
O Reliable State Manager rastreia as transações atuais a bordo. A única alteração no estado da transação que faz com que uma notificação seja acionada é uma transação que está sendo confirmada.
O Reliable State Manager mantém uma coleção de estados confiáveis como Reliable Dictionary e Reliable Queue. O Reliable State Manager dispara notificações quando essa coleção é alterada: um estado confiável é adicionado ou removido ou toda a coleção é reconstruída. A coleção Reliable State Manager é reconstruída em três casos:
- Recuperação: quando uma réplica é iniciada, ela recupera seu estado anterior do disco. No final da recuperação, ele usa NotifyStateManagerChangedEventArgs para disparar um evento que contém o conjunto de estados confiáveis recuperados.
- Cópia completa: antes que uma réplica possa ingressar no conjunto de configurações, ela deve ser criada. Às vezes, isso requer uma cópia completa do estado do Reliable State Manager da réplica primária a ser aplicada à réplica secundária ociosa. O Reliable State Manager na réplica secundária usa NotifyStateManagerChangedEventArgs para disparar um evento que contém o conjunto de estados confiáveis que ele adquiriu da réplica primária.
- Restauração: em cenários de recuperação de desastres, o estado da réplica pode ser restaurado a partir de um backup por meio do RestoreAsync. Nesses casos, o Reliable State Manager na réplica primária usa NotifyStateManagerChangedEventArgs para disparar um evento que contém o conjunto de estados confiáveis restaurados a partir do backup.
Para se registrar para notificações de transação e/ou notificações do gerenciador de estado, você precisa se registrar com os eventos TransactionChanged ou StateManagerChanged no Reliable State Manager. Um lugar comum para se registrar com esses manipuladores de eventos é o construtor do seu serviço stateful. Quando você se registra no construtor, você não perderá nenhuma notificação causada por uma alteração durante o tempo de vida do IReliableStateManager.
public MyService(StatefulServiceContext context)
: base(MyService.EndpointName, context, CreateReliableStateManager(context))
{
this.StateManager.TransactionChanged += this.OnTransactionChangedHandler;
this.StateManager.StateManagerChanged += this.OnStateManagerChangedHandler;
}
O manipulador de eventos TransactionChanged usa NotifyTransactionChangedEventArgs para fornecer detalhes sobre o evento. Ele contém a propriedade action (por exemplo, NotifyTransactionChangedAction.Commit) que especifica o tipo de alteração. Ele também contém a propriedade transaction que fornece uma referência à transação que foi alterada.
Nota
Hoje, os eventos TransactionChanged são gerados somente se a transação for confirmada. A ação é então igual a NotifyTransactionChangedAction.Commit. Mas, no futuro, eventos podem ser gerados para outros tipos de mudanças de estado de transação. Recomendamos verificar a ação e processar o evento somente se ele for o esperado.
A seguir está um exemplo de manipulador de eventos TransactionChanged .
private void OnTransactionChangedHandler(object sender, NotifyTransactionChangedEventArgs e)
{
if (e.Action == NotifyTransactionChangedAction.Commit)
{
this.lastCommitLsn = e.Transaction.CommitSequenceNumber;
this.lastTransactionId = e.Transaction.TransactionId;
this.lastCommittedTransactionList.Add(e.Transaction.TransactionId);
}
}
O manipulador de eventos StateManagerChanged usa NotifyStateManagerChangedEventArgs para fornecer detalhes sobre o evento. NotifyStateManagerChangedEventArgs tem duas subclasses: NotifyStateManagerRebuildEventArgs e NotifyStateManagerSingleEntityChangedEventArgs. Use a propriedade action em NotifyStateManagerChangedEventArgs para converter NotifyStateManagerChangedEventArgs para a subclasse correta:
- NotifyStateManagerChangedAction.Rebuild: NotifyStateManagerRebuildEventArgs
- NotifyStateManagerChangedAction.Add e NotifyStateManagerChangedAction.Remover: NotifyStateManagerSingleEntityChangedEventArgs
A seguir está um exemplo de manipulador de notificação StateManagerChanged .
public void OnStateManagerChangedHandler(object sender, NotifyStateManagerChangedEventArgs e)
{
if (e.Action == NotifyStateManagerChangedAction.Rebuild)
{
this.ProcessStateManagerRebuildNotification(e);
return;
}
this.ProcessStateManagerSingleEntityNotification(e);
}
Notificações de dicionário confiáveis
O Reliable Dictionary fornece notificações para os seguintes eventos:
- Rebuild: Chamado quando ReliableDictionary se recuperou para um estado de recuperação intermediário legível de um estado local ou backup recuperado ou copiado. Um registro de transações desde a criação desse estado de recuperação será aplicado antes que a reconstrução seja concluída. A aplicação desses registros fornecerá notificações claras, adicionadas, atualizadas e/ou removidas.
- Clear: Chamado quando o estado de ReliableDictionary foi limpo através do método ClearAsync .
- Add: Chamado quando um item foi adicionado ao ReliableDictionary.
- Update: Chamado quando um item no IReliableDictionary foi atualizado.
- Remover: Chamado quando um item no IReliableDictionary foi excluído.
Para obter notificações do Reliable Dictionary, você precisa se registrar no manipulador de eventos DictionaryChanged no IReliableDictionary. Um local comum para se registrar com esses manipuladores de eventos é na notificação de adição ReliableStateManager.StateManagerChanged . Registrar-se quando IReliableDictionary é adicionado ao IReliableStateManager garante que você não perderá nenhuma notificação.
private void ProcessStateManagerSingleEntityNotification(NotifyStateManagerChangedEventArgs e)
{
var operation = e as NotifyStateManagerSingleEntityChangedEventArgs;
if (operation.Action == NotifyStateManagerChangedAction.Add)
{
if (operation.ReliableState is IReliableDictionary<TKey, TValue>)
{
var dictionary = (IReliableDictionary<TKey, TValue>)operation.ReliableState;
dictionary.RebuildNotificationAsyncCallback = this.OnDictionaryRebuildNotificationHandlerAsync;
dictionary.DictionaryChanged += this.OnDictionaryChangedHandler;
}
}
}
Nota
ProcessStateManagerSingleEntityNotification é o método de exemplo que o exemplo OnStateManagerChangedHandler anterior chama.
O código anterior define a interface IReliableNotificationAsyncCallback , juntamente com DictionaryChanged. Como NotifyDictionaryRebuildEventArgs contém uma interface IAsyncEnumerable - que precisa ser enumerada de forma assíncrona -, as notificações de reconstrução são disparadas por meio de RebuildNotificationAsyncCallback em vez de OnDictionaryChangedHandler.
public async Task OnDictionaryRebuildNotificationHandlerAsync(
IReliableDictionary<TKey, TValue> origin,
NotifyDictionaryRebuildEventArgs<TKey, TValue> rebuildNotification)
{
this.secondaryIndex.Clear();
var enumerator = e.State.GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(CancellationToken.None))
{
this.secondaryIndex.Add(enumerator.Current.Key, enumerator.Current.Value);
}
}
Nota
No código anterior, como parte do processamento da notificação de reconstrução, primeiro o estado agregado mantido é limpo. Como a coleção confiável está sendo reconstruída com um novo estado, todas as notificações anteriores são irrelevantes.
O manipulador de eventos DictionaryChanged usa NotifyDictionaryChangedEventArgs para fornecer detalhes sobre o evento. NotifyDictionaryChangedEventArgs tem cinco subclasses. Use a propriedade action em NotifyDictionaryChangedEventArgs para converter NotifyDictionaryChangedEventArgs para a subclasse correta:
- NotifyDictionaryChangedAction.Rebuild: NotifyDictionaryRebuildEventArgs
- NotifyDictionaryChangedAction.Clear: NotifyDictionaryClearEventArgs
- NotifyDictionaryChangedAction.Add: NotifyDictionaryItemAddedEventArgs
- NotifyDictionaryChangedAction.Update: NotifyDictionaryItemUpdatedEventArgs
- NotifyDictionaryChangedAction.Remover: NotifyDictionaryItemRemovedEventArgs
public void OnDictionaryChangedHandler(object sender, NotifyDictionaryChangedEventArgs<TKey, TValue> e)
{
switch (e.Action)
{
case NotifyDictionaryChangedAction.Clear:
var clearEvent = e as NotifyDictionaryClearEventArgs<TKey, TValue>;
this.ProcessClearNotification(clearEvent);
return;
case NotifyDictionaryChangedAction.Add:
var addEvent = e as NotifyDictionaryItemAddedEventArgs<TKey, TValue>;
this.ProcessAddNotification(addEvent);
return;
case NotifyDictionaryChangedAction.Update:
var updateEvent = e as NotifyDictionaryItemUpdatedEventArgs<TKey, TValue>;
this.ProcessUpdateNotification(updateEvent);
return;
case NotifyDictionaryChangedAction.Remove:
var deleteEvent = e as NotifyDictionaryItemRemovedEventArgs<TKey, TValue>;
this.ProcessRemoveNotification(deleteEvent);
return;
default:
break;
}
}
Recomendações
- Faça eventos de notificação completos o mais rápido possível.
- Não execute operações dispendiosas (por exemplo, operações de E/S ) como parte de eventos síncronos.
- Verifique o tipo de ação antes de processar o evento. Novos tipos de ação podem ser adicionados no futuro.
Aqui estão algumas coisas a ter em mente:
- As notificações são disparadas como parte da execução de uma operação. Por exemplo, uma notificação de restauração é acionada como uma etapa de uma operação de restauração. Uma restauração não continuará até que o evento de notificação seja processado.
- Como as notificações são disparadas como parte das operações de aplicação, os clientes veem apenas notificações para operações confirmadas localmente. E como as operações são garantidas apenas para serem comprometidas localmente (em outras palavras, registradas), elas podem ou não ser desfeitas no futuro.
- No caminho de refazer, uma única notificação é disparada para cada operação aplicada. Isso significa que, se a transação T1 incluir Create(X), Delete(X) e Create(X), você receberá uma notificação para a criação de X, uma para a exclusão e uma para a criação novamente, nessa ordem.
- Para transações que contêm várias operações, as operações são aplicadas na ordem em que foram recebidas na réplica primária do usuário.
- Como parte do processamento de falsos progressos, algumas operações podem ser desfeitas em réplicas secundárias. As notificações são geradas para essas operações de desfazer, rolando o estado da réplica de volta para um ponto estável. Uma diferença importante das notificações de desfazer é que os eventos com chaves duplicadas são agregados. Por exemplo, se a transação T1 estiver sendo desfeita, você verá uma única notificação para Delete(X).
Próximos passos
- Reliable Collections
- Início rápido de serviços confiáveis
- Backup e restauração de serviços confiáveis (recuperação de desastres)
- Referência do desenvolvedor para coleções confiáveis
Problemas Conhecidos
- Em situações específicas, algumas notificações de transação podem ser ignoradas durante uma reconstrução. Neste caso, o valor correto ainda está presente e ainda pode ser lido ou iterado; falta apenas a notificação. Para recuperar o estado na memória, coleções confiáveis usam um log de gravação antecipada que é periodicamente condensado em um arquivo de ponto de verificação. Durante a restauração, primeiro o estado base é carregado a partir de arquivos de ponto de verificação que dispara uma notificação de reconstrução. Em seguida, as transações salvas no log são aplicadas, cada uma acionando sua própria notificação, limpar, adicionar, atualizar ou remover. Esse problema pode surgir de uma condição de corrida em que um novo ponto de verificação é tomado rapidamente após uma restauração. Se o ponto de verificação for concluído antes da aplicação do log, o estado na memória será definido como o estado do novo ponto de verificação. Embora isso resulte no estado correto, significa que as transações no log que ainda não foram aplicadas não enviarão notificações.