Atualização de dados paralelos na API da Web do ASP.NET

Quero contar como organizamos uma atualização de dados em segundo plano durante uma solicitação para um serviço REST.

A tarefa é a seguinte: o sistema armazena dados do usuário. O serviço funciona isoladamente e não tem acesso direto aos bancos de dados com esses dados. Para que o serviço funcione, é necessário ter os nomes e sobrenomes dos usuários em seu banco de dados interno. Eles podem ser obtidos na Identidade do usuário atual no momento da solicitação. Necessário para adicionar ou atualizar nomes durante cada solicitação. É aconselhável implementar isso em um encadeamento separado, para que este trabalho não afete o tempo de execução da solicitação principal.

Refinamento de tarefas


No banco de dados do serviço, armazenamos os nomes e sobrenomes dos usuários. Os clientes precisam deles para obter informações sobre quem criou ou modificou o recurso.

Esses dados não são sistemicamente significativos: se de repente no banco de dados não houver registros necessários, nada de ruim acontecerá. Portanto, não queremos registrar nosso trabalho em segundo plano no ASP usando QueueBackgroundWorkItem, para complicar a sobrecarga do domínio do aplicativo.
É aconselhável resolver o problema o mais simples possível.

Para aqueles que desejam aprender mais sobre tarefas em segundo plano no ASP.NET, aconselho a ler um bom artigo sobre o assunto.

Solução


Temos uma classe DbRefresher que adiciona ou altera dados do usuário no método RefreshAsync.

Nossos controladores usam o atributo Autorizar. Adicione nosso descendente a essa classe e substitua o método OnAuthorization:

public override void OnAuthorization(HttpActionContext actionContext) { base.OnAuthorization(actionContext); if (IsAuthorized(actionContext)) DbRefresher.RefreshAsync(actionContext.RequestContext.Principal) .ContinueWith(t => { LogFactory.For<AuthorizeAndRefreshUserAttribute>() .ErrorException("Error occured", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted); } 

DbRefresher.RefreshAsync é um método assíncrono que retorna um objeto de tarefa que continua a ser executado em outro thread. O método OnAuthorize sai imediatamente sem aguardar a conclusão da tarefa. No caso de uma falha, uma mensagem de erro será adicionada ao log.

Isso é tudo: resta apenas substituir o atributo Autorizar nos controladores pelo nome do nosso novo atributo. O novo atributo retorna o controle imediatamente após a verificação dos direitos do usuário atual, após o qual o controlador inicia seu trabalho. O banco de dados será atualizado em paralelo com a preparação da resposta pelo controlador.

Teste


Um teste que executa várias solicitações de serviço simultâneas ajudará a identificar possíveis problemas:

 [TestMethod] public void ConcurrentTest() { const int threadCount = 10; var tasks = new Task[threadCount]; for (int i = 0; i < threadCount; i++) { // DoOperations contains several CRUD operations on resources tasks[i] = Task.Factory.StartNew(DoOperations); } Task.WaitAll(tasks); } 

Os problemas


Se para a operação de alguns métodos de ação do controlador for necessário que o banco de dados contenha obviamente os dados do usuário atual, essa abordagem não é adequada. Você precisará usar uma chamada explícita para DbRefresher.RefreshAsync no corpo do método.

Pode haver problemas ao adicionar um novo usuário ao banco de dados com várias solicitações simultâneas. Se for feita uma tentativa de adicionar um usuário a uma tabela com uma chave existente, você deverá capturar a exceção de violação da chave primária e parar de trabalhar. Todo o trabalho de atualização de dados do usuário será realizado por apenas um encadeamento.

Conclusão


Essa abordagem trabalha com sucesso em um de nossos serviços em Konfermit há mais de um ano.
Parece-me simples e elegante. Seria interessante conhecer a opinião da comunidade.

Source: https://habr.com/ru/post/pt439628/


All Articles