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

> O processamento assíncrono de transações valida a chamada rapidamente e então persiste a gravação através do RabbitMQ, para menor latência de API e maior throughput.

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

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

<Frame caption="Figura 1. Fluxo de transação síncrono. O cliente espera até a gravação no banco de dados ser confirmada.">
  <img src="https://mintcdn.com/lerian-49cb71fc/TGLv2g3qhXqf0dqb/images/pt/d2/sync-transaction-flow.svg?fit=max&auto=format&n=TGLv2g3qhXqf0dqb&q=85&s=da4f152afedd028f1ed4945688ef5d6d" alt="Diagrama de sequência mostrando o cliente enviando um POST /transaction para a API do Midaz, que a valida, grava a transação e as operações no PostgreSQL, espera a confirmação e apenas então retorna 201 Created ao cliente. A resposta 201 Created carrega o status transitório CREATED, não a aprovação final." className="mx-auto" style={{ width:"80%" }} width="867" height="830" data-path="images/pt/d2/sync-transaction-flow.svg" />
</Frame>

Veja 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.** Ela executa aqui a validação do formato da requisição, as checagens de saldo e a aplicação de limites.

3. **A API grava no PostgreSQL.** Ela persiste a transação e as operações dela no mesmo ciclo de requisição.

4. **O PostgreSQL confirma a gravação.** Ele faz o commit de todos os registros.

5. **A API retorna `201 Created`** ao 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ório `CREATED`. O Midaz promove a transação para `APPROVED` de forma assíncrona depois do processamento de saldo. Não trate o `201` como aprovação final. Espere o status chegar a `APPROVED` antes de tratar a transação como liquidada.

**Características:**

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

<Note>
  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](#balance-synchronization)).
</Note>

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

<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/vdBt8wfgjsNRO1rf/images/pt/d2/async-transaction-flow.svg?fit=max&auto=format&n=vdBt8wfgjsNRO1rf&q=85&s=0a7b249ea617df59677f5bcefb7632b7" alt="Diagrama de sequência mostrando o cliente enviando um POST /transaction para a API do Midaz, que a valida, publica o payload no RabbitMQ e retorna imediatamente 201 Created ao cliente. A resposta 201 Created carrega o status transitório CREATED, não a aprovação final. 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 saldo." className="mx-auto" style={{ width:"80%" }} width="1262" height="912" data-path="images/pt/d2/async-transaction-flow.svg" />
</Frame>

Veja 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.** 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.

3. **A API publica o payload da transação no RabbitMQ** em vez de gravar diretamente no banco de dados.

4. **A API retorna `201 Created`** ao 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ório `CREATED`. O Midaz promove a transação para `APPROVED` de forma assíncrona depois do processamento de saldo. Não trate o `201` como aprovação final. Espere o status chegar a `APPROVED` antes de tratar a transação como liquidada.

5. **O RabbitMQ entrega a mensagem** a um consumidor em segundo plano, desacoplado da requisição da API.

6. **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**).

**Características:**

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

<Tip>
  A etapa de validação é idêntica nos dois modos. As checagens de saldo, a validação do formato da requisição e a aplicação de limites acontecem antes de a API responder, independentemente do modo de processamento. A diferença está apenas em *quando* os dados chegam ao banco de dados.
</Tip>

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

<Warning>
  Durante uma indisponibilidade da fila, a latência pode aumentar porque as gravações vão direto para o banco de dados. Monitore a saúde do seu RabbitMQ para manter o modo assíncrono ativo.
</Warning>

## Habilitando o modo assíncrono

***

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

<CodeGroup>
  ```bash Environment variable theme={null}
  RABBITMQ_TRANSACTION_ASYNC=true
  ```
</CodeGroup>

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

| 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 do protocolo AMQP.                      | `3003`                                               |
| `RABBITMQ_PORT_AMQP`                              | Porta da API de gerenciamento.                | `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 de worker do consumidor. | `5`                                                  |
| `RABBITMQ_NUMBERS_OF_PREFETCH`                    | Mensagens pré-buscadas 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`      | Chave de roteamento.                          | `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`) das do produtor. Isso segue o princípio do menor privilégio. O consumidor precisa apenas de acesso de leitura à fila.
</Tip>

<h2 id="balance-synchronization">
  Sincronização de saldo
</h2>

***

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.

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

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 circuito abre quando qualquer uma das condições abaixo é verdadeira:

* 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

| 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 falha (0–100) que aciona o estado open.                                    | `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 reiniciam a cada intervalo. | `120`  |
| `RABBITMQ_CIRCUIT_BREAKER_TIMEOUT`               | Por quanto tempo (segundos) o circuito fica open antes de transicionar para half-open.   | `30`   |
| `RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS`          | Requisições permitidas no estado half-open para testar a recuperação.                    | `3`    |
| `RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL` | Com que frequência (segundos) 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á 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.
</Note>

<Tip>
  Para a maioria dos deploys de produção, os padrões funcionam bem. Ajuste `CONSECUTIVE_FAILURES` e `TIMEOUT` se o seu cluster RabbitMQ tiver padrões de recuperação conhecidos. Por exemplo, reduza o timeout se o seu broker se recupera em segundos. Aumente as falhas consecutivas se você observar oscilaçõ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/products/midaz/bulk-recorder) trabalham juntos:

1. **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.

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

O Bulk Recorder fica ativo com o modo assíncrono, a menos que você defina explicitamente `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.

| Configuração                                    | Comportamento de processamento                              |
| :---------------------------------------------- | :---------------------------------------------------------- |
| Async `false`                                   | Gravação 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 habilitado (padrão) | Baseado em fila, mensagens agrupadas para inserções em lote |

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

**Mantenha o modo síncrono quando:**

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

<Tip>
  Você pode alternar entre os modos a qualquer momento. Altere `RABBITMQ_TRANSACTION_ASYNC` e reinicie a aplicação do ledger. Você não precisa de nenhuma migração de dados, porque o formato da transação é o mesmo nos dois caminhos.
</Tip>
