Skip to main content

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

Figura 1. Fluxo ponta a ponta do Bulk Recorder entre o RabbitMQ e o PostgreSQL.

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

Configuração


Tamanho de lote calculado automaticamente

Quando BULK_RECORDER_SIZE é definido como 0 (o padrão), o Midaz calcula o tamanho do lote:
Isso alinha a capacidade do coletor com o fluxo real de mensagens do RabbitMQ. Isso evita descargas parciais e pressão de memória.
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.

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

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