Concorrência
Introdução
Concorrência, no contexto de microsserviços, refere-se ao processamento simultâneo de múltiplas execuções de funcionalidade, seja da mesma ou de distintas funcionalidades. Isso pode ocorrer, por exemplo, quando há um grande volume de requisições _webservice.
O tratamento de concorrência é crucial para garantir a consistência dos dados. Ainda que as
funcionalidades do microsserviço tenham escopo transacional de banco de dados relacional
(como @Transactional
), a concorrência pode resultar em estados inconsistentes do banco de dados.
É necessário que se analise as regras e o fluxo da(s) funcionalidade(s) para identificar
esse tipo de risco.
Havendo risco de inconsistência em razão de concorrência, uma solução possível é aplicar um bloqueio exclusivo no conjunto das requisições que não* podem ser executadas simultaneamente.
Importante: Considerando que a abordagem de desenvolvimento orientado a microsserviços tem
como premissa a possibilidade de escalabilidade horizontal ou balanceamento de carga entre
várias instâncias de determinado microsserviço, tem-se que o uso de
bloqueios exclusivos no escopo da JVM (Java), como locks e instruções serializable
,
podem não garantir o sequenciamento das todas as requisições webservice. O bloqueio em
nível de JVM ainda pode ser considerado para ganho de desempenho ou outras finalidades, porém
não como solução completa de bloqueio exclusivo.
Nesse contexto, sugere-se a utilização de uma solução de bloqueio exclusivo que permeie todas as instâncias do microsserviço. Soluções possíveis podem incluir protocolos disponíveis no ZooKeeper ou via cluster Redis.
Entretanto, considerando que, tipicamente, o microsserviço está vinculado a um banco de dados relacional, pode-se considerar a utilização de locks exclusivos de SGBD (Sistema Gerenciador de Banco de Dados), conforme explanado a seguir.
Bloqueios Exclusivos via SGBD
SGBD relacionais modernos oferecem funcionalidade de bloqueio exclusivo através do que é chamado de “lock de aplicação”. Esse tipo de lock não está associado a tabelas relacionais ou a dados pré-existentes no banco de dados. O lock de aplicação é gerado para um identificador genérico, informado pela aplicação, podendo ser um número ou string conforme a implementação do SGBD. O identificador deve contemplar todos os dados necessários para evitar a concorrência entre as requisições que interferem entre si. Ele poderia ser formado, por exemplo, pelo hash composto de:
-
URL relativa do endpoint (sem query string).
-
Parâmetros da requisição.
-
Token ou login de autenticação, caso a concorrência entre usuários distintos não implique risco de inconsistência de dados.
É importante que o lock de aplicação oferecido pelo SGBD atenda aos seguintes requisitos:
-
Ter escopo transacional.
-
Haver a garantia, pelo SGBD, que o lock será liberado ao final da transação, seja ela findada por commit ou por rollback.
Caso os itens acima não sejam contemplados pelo SGBD e não houver tratamentos especializados na aplicação para compensar esses critérios, haverá o risco de leak de locks ou starvation, ou seja, falhas de aplicação ou de infraestrutura em que locks ficariam órfãos no banco de dados impedindo indevidamente a execução de novas requisições.
Exemplos de soluções de lock de aplicação:
-
PostgreSQL: instrução pg_advisory_xact_lock ou pg_try_advisory_xact_lock.
-
SQL Server: instrução sp_getapplock com escopo de transação.
Bloqueios Exclusivos via Redis
Orienta-se no sentido de utilizar o Redis, via jedis
ou Redisson
, para implementação de
lock exclusivo. Neste caso, é fundamental a implantação de ambiente Redis clusterizado
de alta disponibilidade.
Próximos Passos
A próxima leitura sugerida é a seção especial de Transações Distribuídas, que apresenta orientações para tratamento de operações que envolvem vários microsserviços.