Por que isso importa
Cada transação no Midaz gera operações de saldo que precisam ser persistidas. No modo padrão (síncrono), cada mensagem dispara uma inserção individual no banco de dados. Isso é aceitável para volumes moderados, mas em escala — milhares de transações por segundo — torna-se o gargalo. O Bulk Recorder muda isso acumulando mensagens e gravando-as em lotes. Menos idas e vindas ao PostgreSQL, menos contenção de lock e throughput significativamente maior. Para operações de alto volume como pagamentos em massa, liquidações em lote ou processamento de pagamentos em tempo real, essa é a diferença entre um sistema que acompanha o ritmo e um que não acompanha. Para estratégias mais amplas sobre como escalar o Midaz, veja Estratégias de escalabilidade.
Como funciona
O Bulk Recorder fica entre o consumidor do RabbitMQ e a camada de banco de dados. Em vez de inserir cada mensagem imediatamente, ele as coleta em um buffer e descarrega sob duas condições:
- Tamanho do lote atingido — o buffer enche até o tamanho configurado.
- Timeout expirado — o timeout de flush configurado expira, mesmo se o buffer não estiver cheio.

- O RabbitMQ entrega as mensagens ao BulkCollector uma de cada vez — Mensagem 1, Mensagem 2, e assim por diante até a Mensagem N.
- O BulkCollector as mantém em memória em vez de gravar cada uma imediatamente. Ele continua coletando até que o tamanho do lote seja atingido ou o timeout de flush expire.
- O BulkCollector envia um INSERT em lote em chunks ao PostgreSQL. Lotes grandes são automaticamente divididos em chunks que respeitam os limites de parâmetros do PostgreSQL. Cada chunk usa
ON CONFLICT (id) DO NOTHING, então retries e entregas duplicadas são tratados com segurança. - O PostgreSQL confirma a gravação, garantindo que os dados foram persistidos.
- O BulkCollector confirma todas as mensagens de volta ao RabbitMQ em um único ACK, liberando-as da fila em conjunto.
Habilitando o Bulk Recorder
O modo bulk requer que duas condições estejam ativas simultaneamente:
Configuração
| Variável | Descrição | Padrão |
|---|---|---|
BULK_RECORDER_ENABLED | Habilita ou desabilita o modo bulk. | true |
BULK_RECORDER_SIZE | Número de mensagens a acumular antes do flush. 0 = calculado automaticamente. | 0 (auto) |
BULK_RECORDER_FLUSH_TIMEOUT_MS | Tempo máximo (ms) de espera antes de descarregar um lote incompleto. | 100 |
BULK_RECORDER_MAX_ROWS_PER_INSERT | Máximo de linhas por instrução INSERT enviada ao PostgreSQL. | 1000 |
Tamanho de lote calculado automaticamente
QuandoBULK_RECORDER_SIZE é definido como 0 (o padrão), o tamanho do lote é derivado automaticamente:
Ajustando para sua carga de trabalho
As duas principais alavancas são o tamanho do lote e o timeout de flush. O equilíbrio certo depende de priorizar latência ou throughput.
Baixa latência (processamento em tempo real)
Mantenha lotes pequenos e timeouts curtos. Mensagens são persistidas rapidamente, mesmo que os lotes não estejam cheios.Alto throughput (operações em lote)
Lotes maiores e timeouts mais longos maximizam a eficiência do banco de dados. Ideal para pagamentos em massa, liquidações de fim de dia ou cargas de trabalho de migração.Garantias de segurança
O Bulk Recorder foi projetado para ser seguro em todas as condições:
Idempotência
Cada inserção em lote usaON CONFLICT (id) DO NOTHING. Se uma mensagem for entregue duas vezes — devido a um retry, redelivery ou falha de rede — a duplicata é descartada silenciosamente. Sem corrupção de dados, sem violações de constraint.
Prevenção de deadlock
Antes de cada inserção em lote, os IDs dos registros são ordenados. Isso garante que todos os escritores concorrentes adquiram locks na mesma ordem, eliminando a fonte mais comum de deadlocks do PostgreSQL em cenários de alta concorrência.Chunking interno
Lotes grandes são automaticamente divididos em chunks que cabem dentro do limite de 65.535 parâmetros por query do PostgreSQL:| Tipo de registro | Colunas por linha | Linhas por chunk | Parâmetros por chunk |
|---|---|---|---|
| Transaction | 15 | 1.000 | 15.000 |
| Operation | 30 | 1.000 | 30.000 |
BULK_RECORDER_MAX_ROWS_PER_INSERT se quiser ajustar o tamanho do chunk — o padrão de 1.000 linhas é ideal para a maioria dos deployments.
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á está usando 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.
- Seu volume é baixo o suficiente para que inserções individuais não sejam um gargalo.
- Você precisa de garantias estritas de ordenação 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.

