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.
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:
- Tamanho de lote atingido: o buffer atinge o tamanho configurado.
- Timeout esgotado: o timeout de descarga configurado expira, mesmo que o buffer não esteja cheio.
Figura 1. Fluxo ponta a ponta do Bulk Recorder entre o RabbitMQ e o PostgreSQL.
- O RabbitMQ entrega mensagens ao BulkCollector uma de cada vez: Mensagem 1, Mensagem 2, até a Mensagem N.
- 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.
-
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. - O PostgreSQL confirma a gravação e armazena os dados.
- 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:
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.
Configuração
Tamanho de lote calculado automaticamente
QuandoBULK_RECORDER_SIZE é definido como 0 (o padrão), o Midaz calcula o tamanho do lote:
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.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.Garantias de segurança
Estes mecanismos mantêm o Bulk Recorder seguro:
Idempotência
Toda inserção em massa usaON 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:
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.
- 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.

