Pular para o conteúdo principal

Por que isso importa


Quando um cliente envia uma transação, duas coisas precisam acontecer: validá-la e persistir os resultados. No modo síncrono, ambas ocorrem na mesma requisição — o cliente espera até que tudo seja escrito no banco de dados antes de receber uma resposta. Isso é simples e previsível, mas tem um teto. Em altos volumes, as escritas no banco de dados se tornam o gargalo. Cada transação segura uma conexão, espera por locks e concorre por I/O. O modo assíncrono quebra essa dependência. A transação é validada, a resposta é retornada imediatamente e a persistência acontece em segundo plano através do RabbitMQ. O cliente recebe respostas mais rápidas. O banco de dados recebe escritas em lotes controlados e otimizados. O sistema lida com mais usando menos. Para orientação mais ampla sobre escalabilidade, veja Estratégias de escalabilidade.

Como funciona


Modo síncrono (padrão)

A transação é validada e escrita diretamente no PostgreSQL dentro do mesmo ciclo de requisição. A resposta da API só é enviada depois que todas as operações de banco de dados forem concluídas.
Diagrama de sequência mostrando o cliente enviando um POST /transaction para a API do Midaz, que valida, grava a transação e as operações no PostgreSQL, espera a confirmação e só então retorna 200 OK ao cliente.
Aqui está o fluxo completo, passo a passo:
  1. O cliente envia um POST /transaction para a API do Midaz.
  2. A API valida a requisição — parsing do DSL, verificações de saldo e aplicação de limites acontecem aqui.
  3. A API grava no PostgreSQL — a transação e suas operações são persistidas dentro do mesmo ciclo de requisição.
  4. O PostgreSQL confirma a gravação, sinalizando que todos os registros foram commitados.
  5. A API retorna 200 OK ao cliente com a transação criada. A resposta só sai do servidor depois que o banco de dados confirmou tudo.
Características:
  • O tempo de resposta inclui a latência de escrita no banco de dados.
  • Cada transação é uma operação independente de banco de dados.
  • Mais simples de raciocinar — o que você vê na resposta é o que está persistido.
Mesmo no modo síncrono, os saldos são atualizados atomicamente no Redis — que é a fonte autoritativa — durante a requisição; a gravação acima persiste a transação e suas operações, não as linhas de saldo no Postgres. Essas linhas são reconciliadas pelo worker de sincronização de saldos, sempre ativo (veja Sincronização de saldos).

Modo assíncrono

A transação é validada da mesma forma, mas em vez de escrever no banco de dados, o Midaz publica uma mensagem no RabbitMQ. Um consumidor em segundo plano pega a mensagem e trata a persistência separadamente.
Diagrama de sequência mostrando o cliente enviando um POST /transaction para a API do Midaz, que valida, publica o payload no RabbitMQ e retorna imediatamente 200 OK ao cliente. Em paralelo, o RabbitMQ entrega a mensagem a um consumidor em segundo plano, que grava a transação e as operações no PostgreSQL; os saldos são tratados pelo worker dedicado de sincronização de saldos.
Aqui está o fluxo completo, passo a passo:
  1. O cliente envia um POST /transaction para a API do Midaz.
  2. A API valida a requisição — parsing do DSL, verificações de saldo e aplicação de limites acontecem exatamente como no modo síncrono.
  3. A API publica o payload da transação no RabbitMQ em vez de gravar diretamente no banco de dados.
  4. A API retorna 200 OK ao cliente imediatamente depois que a mensagem é aceita pela fila — o cliente não espera pela persistência no banco de dados.
  5. O RabbitMQ entrega a mensagem a um consumidor em segundo plano, desacoplado da requisição da API.
  6. O consumidor grava no PostgreSQL — a transação e suas operações são persistidas a partir da mensagem enfileirada. As atualizações de saldo são coordenadas pelo worker dedicado de sincronização de saldos (veja a seção Sincronização de saldos), que mantém os saldos consistentes em ambos os modos.
Características:
  • O tempo de resposta exclui a latência de escrita no banco de dados — o cliente só espera pela validação e pela publicação na fila.
  • As mensagens são serializadas com MessagePack para um transporte compacto e eficiente.
  • Os consumidores em segundo plano escrevem no banco de dados no próprio ritmo, com capacidades de batching e retries.
O passo de validação é idêntico em ambos os modos. Verificações de saldo, parsing do DSL, aplicação de limites — tudo isso acontece antes da API responder, independentemente do modo de processamento. A diferença está apenas em quando os dados chegam ao banco de dados.

Resiliência integrada


Se o RabbitMQ estiver indisponível quando o modo assíncrono tentar publicar uma mensagem, o Midaz não falha a transação. Em vez disso, ele recorre automaticamente a uma escrita direta no banco de dados — o mesmo caminho do modo síncrono. Isso significa:
  • Nenhuma transação é perdida por causa de uma indisponibilidade da fila.
  • O cliente ainda recebe uma resposta de sucesso.
  • O fallback é registrado em log para que seu time de operações possa investigar o problema da fila.
O fallback automático garante a segurança dos dados, mas também significa que a latência vai disparar durante uma indisponibilidade da fila (já que as escritas vão direto ao banco de dados). Monitore a saúde do seu RabbitMQ para manter o modo assíncrono operando como o esperado.

Habilitando o modo assíncrono


Configure uma variável de ambiente na aplicação do ledger:
Quando definida como false (o padrão), todas as transações usam processamento síncrono. Nenhum consumidor do RabbitMQ é necessário. Quando definida como true, o ledger publica os payloads da transação no exchange configurado do RabbitMQ e um consumidor em segundo plano trata a persistência.

Configuração do RabbitMQ


O modo assíncrono utiliza as seguintes configurações do RabbitMQ (todas no .env do ledger):
O consumidor usa credenciais separadas (RABBITMQ_CONSUMER_USER / RABBITMQ_CONSUMER_PASS) do produtor. Isso segue o princípio do menor privilégio — o consumidor só precisa de acesso de leitura à fila.

Sincronização de saldos


As atualizações de saldo são coordenadas através de um worker dedicado de sincronização que utiliza o Redis como camada de coordenação. Esse worker roda tanto no modo síncrono quanto no assíncrono e mantém os saldos consistentes mesmo quando múltiplos consumidores processam mensagens concorrentemente. O worker de sincronização de saldos roda automaticamente tanto no modo síncrono quanto no assíncrono. Nenhuma configuração adicional é necessária além de ter o Redis disponível.

Circuit breaker do RabbitMQ


Quando o modo assíncrono está habilitado, o Midaz depende do RabbitMQ para a persistência das transações. Para proteger contra indisponibilidades do broker, o Midaz inclui um circuit breaker integrado que monitora a saúde da conexão com o RabbitMQ e falha rápido quando o broker está indisponível — evitando acúmulo de requisições e falhas em cascata. O circuit breaker está sempre ativo quando o RabbitMQ está em uso. Ele segue o modelo padrão de três estados:
  • Fechado (normal): as requisições fluem normalmente para o RabbitMQ. As falhas são contabilizadas.
  • Aberto (disparado): as requisições são rejeitadas imediatamente sem contatar o RabbitMQ. Um health checker em segundo plano monitora o broker e tenta a recuperação.
  • Semi-aberto (sondagem): um número limitado de requisições é permitido para testar se o RabbitMQ se recuperou. Se tiverem sucesso, o circuito se fecha. Se falharem, ele reabre.
O circuito abre quando qualquer uma destas condições é atendida:
  • O número de falhas consecutivas atinge o limite, OU
  • A proporção de falhas excede o percentual configurado dentro da janela de contagem

Configuração do circuit breaker

Quando o circuito está aberto, as transações assíncronas fazem fallback para escritas síncronas diretas no banco de dados — a transação não é perdida. Esse fallback garante a integridade dos dados mesmo durante indisponibilidades do broker.
Para a maioria dos deployments em produção, os valores padrão funcionam bem. Ajuste CONSECUTIVE_FAILURES e TIMEOUT se seu cluster do RabbitMQ tiver padrões de recuperação conhecidos — por exemplo, diminua o timeout se seu broker normalmente se recupera em segundos, ou aumente as falhas consecutivas se você enfrenta flutuações transitórias de rede.

Como o modo assíncrono se conecta ao Bulk Recorder


O modo assíncrono e o Bulk Recorder são funcionalidades complementares que trabalham juntas:
  1. Modo assíncrono desacopla a resposta da API da persistência — as transações vão para o RabbitMQ em vez de diretamente para o PostgreSQL.
  2. Bulk Recorder otimiza como o consumidor escreve essas mensagens no banco de dados — agrupando múltiplas mensagens em inserções em lote únicas.
O Bulk Recorder é ativado apenas quando o modo assíncrono está habilitado (RABBITMQ_TRANSACTION_ASYNC=true E BULK_RECORDER_ENABLED=true). Sem o modo assíncrono, não há fila de mensagens — e sem fila, não há nada para agrupar.

Quando usar o modo assíncrono


Use o modo assíncrono quando:
  • Você precisa de tempos de resposta de API mais baixos para a criação de transações.
  • Sua carga de trabalho envolve altos volumes de transações (centenas+ por segundo).
  • Você está rodando operações em lote como pagamentos em massa ou liquidações.
  • Você quer desacoplar sua camada de API do desempenho do banco de dados.
Mantenha o modo síncrono quando:
  • Você precisa da configuração mais simples possível (sem dependência do RabbitMQ).
  • O volume de transações é baixo a moderado.
  • Você quer a garantia de que uma resposta de sucesso da API significa que os dados já estão persistidos.
  • Você está em um ambiente de desenvolvimento ou teste onde a simplicidade importa mais do que o throughput.
Você pode alternar entre os modos a qualquer momento alterando RABBITMQ_TRANSACTION_ASYNC e reiniciando a aplicação do ledger. Nenhuma migração de dados é necessária — o formato da transação é o mesmo em ambos os caminhos.