> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Processamento assíncrono de transações

> Processe transações de forma assíncrona via RabbitMQ para reduzir a latência da API e sustentar maior throughput em escala.

## 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](/pt/midaz/scalability-strategies).

## 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.

<Frame caption="Figura 1. Fluxo de transação síncrono — o cliente espera até que a gravação no banco de dados seja confirmada.">
  <img src="https://mintcdn.com/lerian-49cb71fc/gxkISQZzCtlgORao/images/pt/d2/sync-transaction-flow.svg?fit=max&auto=format&n=gxkISQZzCtlgORao&q=85&s=872835ae94ba931c25a5222d168d51d1" alt="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." className="mx-auto" style={{ width:"80%" }} width="713" height="830" data-path="images/pt/d2/sync-transaction-flow.svg" />
</Frame>

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.

<Note>
  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](#sincronização-de-saldos)).
</Note>

### 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.

<Frame caption="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.">
  <img src="https://mintcdn.com/lerian-49cb71fc/ZrZBZTM4DWnrahSd/images/pt/d2/async-transaction-flow.svg?fit=max&auto=format&n=ZrZBZTM4DWnrahSd&q=85&s=45263b0fbbc0a5cb6f00b4cbfd592326" alt="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." className="mx-auto" style={{ width:"80%" }} width="1263" height="912" data-path="images/pt/d2/async-transaction-flow.svg" />
</Frame>

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.

<Tip>
  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.
</Tip>

## 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.

<Warning>
  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.
</Warning>

## Habilitando o modo assíncrono

***

Configure uma variável de ambiente na aplicação do ledger:

<CodeGroup>
  ```bash Variável de ambiente theme={null}
  RABBITMQ_TRANSACTION_ASYNC=true
  ```
</CodeGroup>

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):

| Variável                                          | Descrição                                     | Padrão                                               |
| :------------------------------------------------ | :-------------------------------------------- | :--------------------------------------------------- |
| `RABBITMQ_TRANSACTION_ASYNC`                      | Habilita o processamento assíncrono.          | `false`                                              |
| `RABBITMQ_HOST`                                   | Hostname do servidor RabbitMQ.                | `midaz-rabbitmq`                                     |
| `RABBITMQ_PORT_HOST`                              | Porta da API de gerenciamento.                | `3003`                                               |
| `RABBITMQ_PORT_AMQP`                              | Porta do protocolo AMQP.                      | `3004`                                               |
| `RABBITMQ_DEFAULT_USER`                           | Credenciais do produtor (usuário).            | `transaction`                                        |
| `RABBITMQ_DEFAULT_PASS`                           | Credenciais do produtor (senha).              | —                                                    |
| `RABBITMQ_CONSUMER_USER`                          | Credenciais do consumidor (usuário).          | `consumer`                                           |
| `RABBITMQ_CONSUMER_PASS`                          | Credenciais do consumidor (senha).            | —                                                    |
| `RABBITMQ_NUMBERS_OF_WORKERS`                     | Número de goroutines worker do consumidor.    | `5`                                                  |
| `RABBITMQ_NUMBERS_OF_PREFETCH`                    | Mensagens prefetched por worker.              | `10`                                                 |
| `RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE` | Nome do exchange para mensagens de transação. | `transaction.transaction_balance_operation.exchange` |
| `RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY`      | Routing key.                                  | `transaction.transaction_balance_operation.key`      |
| `RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE`    | Nome da fila.                                 | `transaction.transaction_balance_operation.queue`    |

<Tip>
  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.
</Tip>

## 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.

| Variável                        | Descrição                                                         | Padrão |
| :------------------------------ | :---------------------------------------------------------------- | :----- |
| `BALANCE_SYNC_BATCH_SIZE`       | Número de atualizações de saldo a agrupar antes do flush.         | `50`   |
| `BALANCE_SYNC_FLUSH_TIMEOUT_MS` | Tempo máximo de espera (ms) antes de esvaziar um lote incompleto. | `500`  |
| `BALANCE_SYNC_POLL_INTERVAL_MS` | Frequência (ms) com que o worker verifica atualizações pendentes. | `50`   |

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

| Variável                                         | Descrição                                                                                       | Padrão |
| :----------------------------------------------- | :---------------------------------------------------------------------------------------------- | :----- |
| `RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES`  | Falhas consecutivas antes de o circuito abrir.                                                  | `15`   |
| `RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO`         | Percentual de falhas (0–100) que dispara o estado aberto.                                       | `50`   |
| `RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS`          | Requisições mínimas antes de avaliar a proporção de falhas.                                     | `10`   |
| `RABBITMQ_CIRCUIT_BREAKER_INTERVAL`              | Janela de tempo (segundos) para contar falhas. Os contadores resetam após cada intervalo.       | `120`  |
| `RABBITMQ_CIRCUIT_BREAKER_TIMEOUT`               | Por quanto tempo (segundos) o circuito permanece aberto antes de transicionar para semi-aberto. | `30`   |
| `RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS`          | Requisições permitidas no estado semi-aberto para sondar a recuperação.                         | `3`    |
| `RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL` | Frequência (segundos) com que o health checker em segundo plano faz ping no RabbitMQ.           | `30`   |
| `RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT`  | Timeout (segundos) para cada ping de health check.                                              | `10`   |

<Note>
  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.
</Note>

<Tip>
  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.
</Tip>

## Como o modo assíncrono se conecta ao Bulk Recorder

***

O modo assíncrono e o [Bulk Recorder](/pt/midaz/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.

| Configuração                        | Comportamento de processamento                                                |
| :---------------------------------- | :---------------------------------------------------------------------------- |
| Async `false`                       | Escrita direta no banco de dados por transação (síncrono)                     |
| Async `true`, Bulk Recorder `false` | Baseado em fila, uma mensagem processada por vez                              |
| Async `true`, Bulk Recorder `true`  | Baseado em fila, mensagens agrupadas para inserções em lote (10×+ throughput) |

## 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.

<Tip>
  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.
</Tip>
