Por que isso importa
Um cliente envia uma transação. O Midaz deve fazer duas coisas: validá-la e persistir o resultado. No modo síncrono, as duas etapas rodam na mesma requisição. O cliente espera cada gravação chegar ao banco de dados antes de receber uma resposta. Esse modelo é simples e previsível, mas tem um teto. Em alto volume, as gravações no banco de dados se tornam o gargalo. Cada transação ocupa uma conexão, espera por locks e disputa I/O. O modo assíncrono rompe essa dependência. O Midaz valida a transação, retorna a resposta de imediato e persiste os dados em segundo plano através do RabbitMQ. O cliente recebe respostas mais rápidas. Com o processamento assíncrono habilitado, o Bulk Recorder agrupa inserções por padrão. Defina
BULK_RECORDER_ENABLED=false para persistir as mensagens enfileiradas individualmente.
Para orientações mais amplas de escalabilidade, veja Estratégias de escalabilidade.
Como funciona
Modo síncrono (padrão)
O Midaz valida a transação e a grava diretamente no PostgreSQL no mesmo ciclo de requisição. A API envia a resposta apenas depois que toda operação no banco de dados é concluída.Figura 1. Fluxo de transação síncrono. O cliente espera até a gravação no banco de dados ser confirmada.
-
O cliente envia um
POST /transactionpara a API do Midaz. - A API valida a requisição. Ela executa aqui a validação do formato da requisição, as checagens de saldo e a aplicação de limites.
- A API grava no PostgreSQL. Ela persiste a transação e as operações dela no mesmo ciclo de requisição.
- O PostgreSQL confirma a gravação. Ele faz o commit de todos os registros.
-
A API retorna
201 Createdao cliente com a transação criada. A resposta apenas sai do servidor depois que o banco de dados confirma tudo. A resposta carrega o status transitórioCREATED. O Midaz promove a transação paraAPPROVEDde forma assíncrona depois do processamento de saldo. Não trate o201como aprovação final. Espere o status chegar aAPPROVEDantes de tratar a transação como liquidada.
- O tempo de resposta inclui a latência de gravação no banco de dados.
- Cada transação é uma operação independente no banco de dados.
- Mais simples de entender. A resposta mostra exatamente o que o Midaz persiste.
Mesmo no modo síncrono, o Midaz atualiza os saldos de forma atômica no Redis durante a requisição. O Redis é a fonte de verdade para saldos. A gravação acima persiste a transação e as operações dela, não as linhas de saldo do Postgres. O worker de sincronização de saldo, que roda continuamente, reconcilia essas linhas (veja Sincronização de saldo).
Modo assíncrono
O Midaz valida a transação da mesma forma. Em vez de uma gravação direta 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.Figura 2. Fluxo de transação assíncrono. O cliente recebe uma resposta assim que a mensagem é publicada, e a persistência acontece em segundo plano.
-
O cliente envia um
POST /transactionpara a API do Midaz. - A API valida a requisição. Ela executa a validação do formato da requisição, as checagens de saldo e a aplicação de limites exatamente como no modo síncrono.
- A API publica o payload da transação no RabbitMQ em vez de gravar diretamente no banco de dados.
-
A API retorna
201 Createdao cliente assim que a fila aceita a mensagem. O cliente não espera pela persistência no banco de dados. A resposta carrega o status transitórioCREATED. O Midaz promove a transação paraAPPROVEDde forma assíncrona depois do processamento de saldo. Não trate o201como aprovação final. Espere o status chegar aAPPROVEDantes de tratar a transação como liquidada. - O RabbitMQ entrega a mensagem a um consumidor em segundo plano, desacoplado da requisição da API.
- O consumidor grava no PostgreSQL. Ele persiste a transação e as operações dela a partir da mensagem enfileirada. O worker de sincronização de saldo coordena as atualizações de saldo e mantém os saldos consistentes em ambos os modos (veja a seção Sincronização de saldo).
- O tempo de resposta exclui a latência de gravação no banco de dados. O cliente espera apenas pela validação e pela publicação na fila.
- O Midaz serializa as mensagens com MessagePack para um transporte compacto e eficiente.
- Os consumidores em segundo plano gravam no banco de dados no próprio ritmo, com novas tentativas. As inserções em lote exigem o Bulk Recorder habilitado.
Resiliência embutida
Se o RabbitMQ estiver indisponível quando o modo assíncrono tenta publicar uma mensagem, o Midaz tenta uma gravação direta no banco de dados. Se essa gravação falhar, o Midaz retorna o erro do banco de dados. Isso significa:
- Durante uma indisponibilidade da fila, o Midaz tenta gravar diretamente no banco de dados.
- O cliente pode receber um erro se a gravação de fallback no banco de dados falhar.
- O Midaz registra em log a falha da fila e, se a gravação direta também falhar, a falha da gravação de fallback, para que a sua equipe de operações possa investigá-las.
Habilitando o modo assíncrono
Defina uma variável de ambiente na aplicação do ledger:
false (o padrão), todas as transações usam o processamento síncrono e persistem diretamente no PostgreSQL. O bootstrap atual do Ledger ainda inicializa o RabbitMQ e conecta o consumidor dele.
Com true, o ledger publica os payloads de transação no exchange do RabbitMQ configurado. Um consumidor em segundo plano então trata a persistência.
Configuração do RabbitMQ
O modo assíncrono usa as seguintes configurações do RabbitMQ (todas no
.env do ledger):
Sincronização de saldo
Um worker dedicado de sincronização de saldo coordena as atualizações de saldo. Ele usa o Redis como camada de coordenação. Esse worker roda tanto no modo síncrono quanto no assíncrono. Ele mantém os saldos consistentes mesmo quando vários consumidores processam mensagens ao mesmo tempo.
O worker de sincronização de saldo roda automaticamente nos dois modos. Você não precisa de nenhuma configuração extra além de uma instância do Redis disponível.
Circuit breaker do RabbitMQ
Quando você habilita o modo assíncrono, o Midaz depende do RabbitMQ para a persistência das transações. Um circuit breaker embutido protege contra indisponibilidades do broker. Ele monitora a saúde da conexão com o RabbitMQ e falha rápido quando o broker cai. Isso evita acúmulo de requisições e falhas em cascata. O circuit breaker fica ativo no caminho RabbitMQ single-tenant. O RabbitMQ multi-tenant usa gestão de conexão por tenant em vez disso. O circuit breaker segue o modelo padrão de três estados:
- Closed (normal): as requisições fluem normalmente para o RabbitMQ. O breaker conta as falhas.
- Open (acionado): o breaker não contata o RabbitMQ. O Midaz contorna o broker e tenta uma gravação direta no banco de dados para cada transação assíncrona. Se essa gravação falhar, o Midaz retorna o erro do banco de dados. Um health checker em segundo plano monitora o broker e tenta a recuperação.
- Half-open (testando): o breaker deixa passar um número limitado de requisições para testar a recuperação do RabbitMQ. Se elas tiverem sucesso, o circuito fecha. Se falharem, ele reabre.
- O número de falhas consecutivas atinge o limite, OU
- A proporção de falhas ultrapassa o percentual configurado dentro da janela de contagem
Configuração do circuit breaker
Quando o circuito está open, o Midaz tenta gravações diretas no banco de dados para transações assíncronas. Se uma gravação direta falhar, o Midaz retorna o erro do banco de dados. O fallback não garante a entrega da transação durante indisponibilidades do broker.
Como o modo assíncrono se conecta ao Bulk Recorder
O modo assíncrono e o Bulk Recorder trabalham juntos:
- O 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.
- O Bulk Recorder otimiza como o consumidor grava essas mensagens no banco de dados. Ele agrupa várias mensagens em inserções em lote únicas.
BULK_RECORDER_ENABLED=false. Sem o modo assíncrono, a persistência das transações usa gravações diretas no banco de dados e não há fila de transações para agrupar.
Quando usar o modo assíncrono
Use o modo assíncrono quando:
- Você precisa de tempos de resposta mais baixos da API para a criação de transações.
- Sua carga de trabalho envolve altos volumes de transações (centenas ou mais por segundo).
- Você executa operações em lote, como repasses em massa ou liquidações.
- Você quer desacoplar a camada de API do desempenho do banco de dados.
- Você precisa de persistência de transação direta e vinculada à requisição.
- O volume de transações é baixo a moderado.
- Você quer que uma resposta de sucesso da API signifique que o Midaz já persistiu os dados.
- Você trabalha em um ambiente de desenvolvimento ou teste, onde a simplicidade importa mais que o throughput.

