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:

  1. Ter escopo transacional.

  2. 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:

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.