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

# Bulk Recorder

> Use o Bulk Recorder para agrupar mensagens do RabbitMQ em inserções em massa no PostgreSQL, reduzindo idas e vindas ao banco de dados e aumentando o throughput em cargas de trabalho de alto volume.

## Por que isso importa

***

Toda transação no Midaz cria operações de saldo que o Midaz deve persistir. No modo síncrono padrão, o Midaz as grava diretamente no banco de dados durante o ciclo da requisição. Isso funciona bem para volumes moderados. Em escala (milhares de transações por segundo), isso se torna o gargalo.

O Bulk Recorder acumula mensagens e as grava em lotes. Ele reduz as idas e vindas ao PostgreSQL, diminui a disputa por locks e aumenta o throughput. As cargas de trabalho de alto volume ganham mais: pagamentos em massa, liquidações em lote e processamento de pagamentos em tempo real.

Para estratégias mais amplas de escalar o Midaz, veja [Estratégias de escalabilidade](/pt/products/midaz/scalability-strategies).

## Como funciona

***

O Bulk Recorder fica entre o consumidor do RabbitMQ e a camada de banco de dados. Ele não insere cada mensagem de uma vez. Em vez disso, ele coleta mensagens em um buffer e as descarrega sob duas condições:

1. **Tamanho de lote atingido**: o buffer atinge o tamanho configurado.
2. **Timeout esgotado**: o timeout de descarga configurado expira, mesmo que o buffer não esteja cheio.

A condição que ocorrer primeiro dispara a descarga. Isso gera lotes grandes sob carga e baixa latência em períodos de baixa atividade.

<Frame caption="Figura 1. Fluxo ponta a ponta do Bulk Recorder entre o RabbitMQ e o PostgreSQL.">
  <img src="https://mintcdn.com/lerian-49cb71fc/vdBt8wfgjsNRO1rf/images/pt/d2/bulk-recorder-flow.svg?fit=max&auto=format&n=vdBt8wfgjsNRO1rf&q=85&s=46b40a3f202397caee93c7cdae37312b" alt="Diagrama de sequência mostrando o RabbitMQ entregando mensagens ao BulkCollector, que as armazena em buffer até que o tamanho do lote ou o timeout seja atingido, então envia um INSERT em massa fragmentado ao PostgreSQL e confirma cada mensagem de volta ao RabbitMQ." className="mx-auto" style={{ width:"80%" }} width="711" height="994" data-path="images/pt/d2/bulk-recorder-flow.svg" />
</Frame>

Aqui está o fluxo completo, passo a passo:

1. **O RabbitMQ entrega mensagens** ao BulkCollector uma de cada vez: Mensagem 1, Mensagem 2, até a Mensagem N.

2. **O BulkCollector as mantém em memória** em vez de uma gravação por mensagem. Ele coleta até o lote se encher ou o timeout de descarga expirar.

3. **O BulkCollector envia um INSERT em massa fragmentado ao PostgreSQL.** O Midaz divide lotes grandes em fragmentos que respeitam os limites de parâmetro do PostgreSQL. Cada fragmento usa `ON CONFLICT (id) DO NOTHING`, então novas tentativas e entregas duplicadas permanecem seguras.

4. **O PostgreSQL confirma a gravação** e armazena os dados.

5. **O BulkCollector confirma cada mensagem** de volta ao RabbitMQ após a gravação. Ele as confirma uma de cada vez, não em uma única confirmação em massa. Isso evita que um canal compartilhado confirme mensagens que outros workers ainda estão processando.

## Habilitando o Bulk Recorder

***

O modo em massa exige o modo assíncrono. A configuração explícita do Bulk Recorder é opcional porque o padrão já é habilitado:

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

O modo assíncrono é obrigatório. Com `RABBITMQ_TRANSACTION_ASYNC=true`, o Bulk Recorder fica habilitado por padrão. Defina `BULK_RECORDER_ENABLED=false` para processar as mensagens enfileiradas individualmente. Sem o modo assíncrono, o Midaz persiste as transações diretamente em vez de consumir mensagens enfileiradas.

<Tip>
  `BULK_RECORDER_ENABLED` assume `true` por padrão quando você não define a variável de ambiente. Se você já roda com `RABBITMQ_TRANSACTION_ASYNC=true`, o modo em massa provavelmente já está ativo. Para confirmar, procure `Bulk mode is ACTIVE` nos logs da aplicação na inicialização.
</Tip>

## Configuração

***

| Variável                         | Descrição                                                                             | Padrão           |
| :------------------------------- | :------------------------------------------------------------------------------------ | :--------------- |
| `BULK_RECORDER_ENABLED`          | Habilita ou desabilita o modo em massa.                                               | `true`           |
| `BULK_RECORDER_SIZE`             | Número de mensagens a acumular antes de descarregar. `0` = calculado automaticamente. | `0` (automático) |
| `BULK_RECORDER_FLUSH_TIMEOUT_MS` | Tempo máximo (ms) de espera antes de descarregar um lote incompleto.                  | `100`            |

### Tamanho de lote calculado automaticamente

Quando `BULK_RECORDER_SIZE` é definido como `0` (o padrão), o Midaz calcula o tamanho do lote:

```
batch size = RABBITMQ_NUMBERS_OF_WORKERS × RABBITMQ_NUMBERS_OF_PREFETCH
```

Isso alinha a capacidade do coletor com o fluxo real de mensagens do RabbitMQ. Isso evita descargas parciais e pressão de memória.

<Warning>
  Se você definir `BULK_RECORDER_SIZE` manualmente, mantenha-o alinhado com suas configurações de prefetch. Um tamanho muito maior que `workers × prefetch` raramente enche o coletor. O coletor então descarrega principalmente pelo timeout.
</Warning>

## Ajustando para sua carga de trabalho

***

Os dois principais parâmetros são **tamanho do lote** e **timeout de descarga**. O equilíbrio certo depende da sua prioridade: latência ou throughput.

### Baixa latência (processamento em tempo real)

Mantenha os lotes pequenos e os timeouts curtos. O Midaz persiste as mensagens rapidamente, mesmo quando os lotes não estão cheios.

<CodeGroup>
  ```bash Low-latency configuration theme={null}
  RABBITMQ_NUMBERS_OF_WORKERS=5
  RABBITMQ_NUMBERS_OF_PREFETCH=10
  BULK_RECORDER_SIZE=0          # auto: 5 × 10 = 50
  BULK_RECORDER_FLUSH_TIMEOUT_MS=50
  ```
</CodeGroup>

### Alto throughput (operações em lote)

Lotes maiores e timeouts mais longos maximizam a eficiência do banco de dados. Use isso para pagamentos em massa, liquidações de fim de dia ou cargas de trabalho de migração.

<CodeGroup>
  ```bash High-throughput configuration theme={null}
  RABBITMQ_NUMBERS_OF_WORKERS=10
  RABBITMQ_NUMBERS_OF_PREFETCH=20
  BULK_RECORDER_SIZE=0          # auto: 10 × 20 = 200
  BULK_RECORDER_FLUSH_TIMEOUT_MS=300
  ```
</CodeGroup>

<Tip>
  Comece com os padrões e ajuste a partir do que você observar. Para confirmar suas configurações, procure o log `Bulk mode configured for consumer` na inicialização.
</Tip>

## Garantias de segurança

***

Estes mecanismos mantêm o Bulk Recorder seguro:

### Idempotência

Toda inserção em massa usa `ON CONFLICT (id) DO NOTHING`. Se uma mensagem chegar duas vezes (por uma nova tentativa, uma reentrega ou uma falha de rede), o PostgreSQL descarta a duplicata. Você não tem corrupção de dados nem violações de restrição.

### Prevenção de deadlock

Antes de cada inserção em massa, o Midaz ordena os IDs dos registros. Todos os gravadores concorrentes então adquirem locks na mesma ordem. Isso remove a fonte mais comum de deadlocks do PostgreSQL sob alta concorrência.

### Fragmentação interna

O Midaz divide lotes grandes em fragmentos que cabem dentro do limite de 65.535 parâmetros por consulta do PostgreSQL:

| Tipo de registro | Colunas por linha | Linhas por fragmento | Parâmetros por fragmento |
| :--------------- | :---------------- | :------------------- | :----------------------- |
| Transação        | 18                | 1.000                | 18.000                   |
| Operação         | 31                | 1.000                | 31.000                   |

O Midaz cuida dessa fragmentação para você. Cada `INSERT` carrega no máximo 1.000 linhas. `BULK_RECORDER_MAX_ROWS_PER_INSERT` não é aplicado atualmente ao tamanho de fragmento do PostgreSQL.

## Quando usar

***

**Use o Bulk Recorder quando:**

* Você processa altos volumes de transações (centenas ou milhares por segundo).
* Sua carga de trabalho inclui operações em lote como pagamentos em massa, liquidações ou migrações de dados.
* Você já usa processamento assíncrono de transações (`RABBITMQ_TRANSACTION_ASYNC=true`).
* Você quer reduzir a carga do banco de dados e a pressão de conexões.

**Mantenha-o desabilitado quando:**

* Seu volume é baixo o suficiente para que inserções individuais não sejam um gargalo.
* Você precisa de ordenação estrita por mensagem, que o processamento em lote quebraria.
* Você está depurando o processamento de transações e quer um fluxo mais simples, mensagem por mensagem.
