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

> Usa el Bulk Recorder para agrupar mensajes de RabbitMQ en inserciones masivas en PostgreSQL, reduciendo los viajes de ida y vuelta a la base de datos y aumentando el rendimiento en cargas de alto volumen.

## Por qué esto es importante

***

Cada transacción en Midaz crea operaciones de saldo que Midaz debe persistir. En el modo síncrono predeterminado, Midaz las escribe directamente en la base de datos durante el ciclo de la solicitud. Esto es aceptable para volúmenes moderados. A escala — miles de transacciones por segundo — se convierte en el cuello de botella.

El Bulk Recorder acumula mensajes y los escribe en lotes. Hace menos viajes de ida y vuelta a PostgreSQL, reduce la contención de bloqueos y aumenta el rendimiento. Las cargas de alto volumen son las que más ganan: pagos masivos, liquidaciones por lotes y procesamiento de pagos en tiempo real.

Para estrategias más amplias para escalar Midaz, consulta [Estrategias de escalabilidad](/es/midaz/scalability-strategies).

## Cómo funciona

***

El Bulk Recorder se sitúa entre el consumidor de RabbitMQ y la capa de base de datos. No inserta cada mensaje de inmediato. En su lugar, recopila los mensajes en un búfer y los descarga bajo dos condiciones:

1. **Tamaño de lote alcanzado** — el búfer se llena hasta el tamaño configurado.
2. **Tiempo de espera agotado** — el tiempo de espera de descarga configurado expira, incluso si el búfer no está lleno.

La primera condición que ocurra desencadena la descarga. Esto da lotes grandes bajo carga y baja latencia durante los períodos de poca actividad.

<Frame caption="Figura 1. Flujo de extremo a extremo del Bulk Recorder entre RabbitMQ y PostgreSQL.">
  <img src="https://mintcdn.com/lerian-49cb71fc/Cb_meREQqa6luqbJ/images/es/d2/bulk-recorder-flow.svg?fit=max&auto=format&n=Cb_meREQqa6luqbJ&q=85&s=6c164e141416ac9d483bc0d09b27f140" alt="Diagrama de secuencia que muestra a RabbitMQ entregando mensajes al BulkCollector, el cual los almacena en un búfer hasta alcanzar el tamaño de lote o expirar el tiempo de espera, y luego envía un INSERT en lote fragmentado a PostgreSQL y confirma cada mensaje de vuelta a RabbitMQ." className="mx-auto" style={{ width:"80%" }} width="775" height="994" data-path="images/es/d2/bulk-recorder-flow.svg" />
</Frame>

Aquí está el flujo completo, paso a paso:

1. **RabbitMQ entrega los mensajes** al BulkCollector uno a la vez — Mensaje 1, Mensaje 2, hasta el Mensaje N.

2. **El BulkCollector los mantiene en memoria** en lugar de una escritura por mensaje. Recopila hasta que el lote se llena o el tiempo de espera de descarga expira.

3. **El BulkCollector envía un INSERT en lote fragmentado a PostgreSQL.** Midaz divide los lotes grandes en fragmentos que respetan los límites de parámetros de PostgreSQL. Cada fragmento usa `ON CONFLICT (id) DO NOTHING`, por lo que los reintentos y las entregas duplicadas se mantienen seguros.

4. **PostgreSQL confirma la escritura** y almacena los datos.

5. **El BulkCollector confirma cada mensaje** de vuelta a RabbitMQ después de la escritura. Los confirma uno a la vez, no en un único ACK conjunto. Esto evita que un canal compartido confirme mensajes que otros workers aún procesan.

## Habilitar el Bulk Recorder

***

El modo en lote requiere el modo asíncrono. La configuración explícita del Bulk Recorder es opcional porque está habilitada de forma predeterminada:

<CodeGroup>
  ```bash Variables de entorno theme={null}
  RABBITMQ_TRANSACTION_ASYNC=true
  BULK_RECORDER_ENABLED=true
  ```
</CodeGroup>

El modo asíncrono es obligatorio. Con `RABBITMQ_TRANSACTION_ASYNC=true`, el Bulk Recorder está habilitado de forma predeterminada; configura `BULK_RECORDER_ENABLED=false` para procesar los mensajes en cola individualmente. Sin modo asíncrono, Midaz persiste las transacciones directamente en lugar de consumir mensajes en cola.

<Tip>
  `BULK_RECORDER_ENABLED` tiene el valor predeterminado `true` cuando no configuras la variable de entorno. Si ya estás ejecutando con `RABBITMQ_TRANSACTION_ASYNC=true`, es probable que el modo en lote esté activo. Para confirmarlo, revisa los logs de tu aplicación buscando `Bulk mode is ACTIVE` al inicio.
</Tip>

## Configuración

***

| Variable                         | Descripción                                                                        | Predeterminado   |
| :------------------------------- | :--------------------------------------------------------------------------------- | :--------------- |
| `BULK_RECORDER_ENABLED`          | Habilita o deshabilita el modo en lote.                                            | `true`           |
| `BULK_RECORDER_SIZE`             | Número de mensajes a acumular antes de descargar. `0` = calculado automáticamente. | `0` (automático) |
| `BULK_RECORDER_FLUSH_TIMEOUT_MS` | Tiempo máximo (ms) de espera antes de descargar un lote incompleto.                | `100`            |

### Tamaño de lote calculado automáticamente

Cuando `BULK_RECORDER_SIZE` se establece en `0` (el valor predeterminado), Midaz deriva el tamaño del lote:

```
tamaño de lote = RABBITMQ_NUMBERS_OF_WORKERS × RABBITMQ_NUMBERS_OF_PREFETCH
```

Esto alinea la capacidad del recopilador con el flujo real de mensajes desde RabbitMQ. Evita descargas parciales y presión de memoria.

<Warning>
  Si configuras `BULK_RECORDER_SIZE` a mano, mantenlo alineado con tu configuración de prefetch. Un tamaño mucho mayor que `workers × prefetch` rara vez llena el recopilador. Entonces el recopilador descarga principalmente por el tiempo de espera.
</Warning>

## Ajuste para tu carga de trabajo

***

Las dos palancas principales son el **tamaño del lote** y el **tiempo de espera de descarga**. El equilibrio correcto depende de tu prioridad: latencia o rendimiento.

### Baja latencia (procesamiento en tiempo real)

Mantén los lotes pequeños y los tiempos de espera cortos. Midaz persiste los mensajes rápidamente, incluso cuando los lotes no están llenos.

<CodeGroup>
  ```bash Configuración de baja latencia theme={null}
  RABBITMQ_NUMBERS_OF_WORKERS=5
  RABBITMQ_NUMBERS_OF_PREFETCH=10
  BULK_RECORDER_SIZE=0          # automático: 5 × 10 = 50
  BULK_RECORDER_FLUSH_TIMEOUT_MS=50
  ```
</CodeGroup>

### Alto rendimiento (operaciones por lotes)

Lotes más grandes y tiempos de espera más largos maximizan la eficiencia de la base de datos. Usa esto para pagos masivos, liquidaciones de fin de día o cargas de trabajo de migración.

<CodeGroup>
  ```bash Configuración de alto rendimiento theme={null}
  RABBITMQ_NUMBERS_OF_WORKERS=10
  RABBITMQ_NUMBERS_OF_PREFETCH=20
  BULK_RECORDER_SIZE=0          # automático: 10 × 20 = 200
  BULK_RECORDER_FLUSH_TIMEOUT_MS=300
  ```
</CodeGroup>

<Tip>
  Comienza con los valores predeterminados y ajusta a partir de lo que observes. Para confirmar tu configuración, observa el log `Bulk mode configured for consumer` al inicio.
</Tip>

## Garantías de seguridad

***

El Bulk Recorder se mantiene seguro en toda condición:

### Idempotencia

Cada inserción en lote usa `ON CONFLICT (id) DO NOTHING`. Si un mensaje llega dos veces — por un reintento, una re-entrega o un fallo de red — PostgreSQL descarta el duplicado. No obtienes corrupción de datos ni violaciones de restricciones.

### Prevención de deadlocks

Antes de cada inserción en lote, Midaz ordena los IDs de los registros. Todos los escritores concurrentes adquieren entonces los bloqueos en el mismo orden. Esto elimina la fuente más común de deadlocks de PostgreSQL en alta concurrencia.

### Fragmentación interna

Midaz divide los lotes grandes en fragmentos que caben dentro del límite de 65.535 parámetros por consulta de PostgreSQL:

| Tipo de registro | Columnas por fila | Filas por fragmento | Parámetros por fragmento |
| :--------------- | :---------------- | :------------------ | :----------------------- |
| Transaction      | 18                | 1.000               | 18.000                   |
| Operation        | 31                | 1.000               | 31.000                   |

Midaz maneja esta fragmentación por ti. Cada `INSERT` incluye como máximo 1.000 filas. `BULK_RECORDER_MAX_ROWS_PER_INSERT` no se aplica actualmente al tamaño del fragmento de PostgreSQL.

## Cuándo usar

***

**Usa el Bulk Recorder cuando:**

* Procesas altos volúmenes de transacciones (cientos o miles por segundo).
* Tu carga de trabajo incluye operaciones por lotes como pagos masivos, liquidaciones o migraciones de datos.
* Ya usas procesamiento asíncrono de transacciones (`RABBITMQ_TRANSACTION_ASYNC=true`).
* Quieres reducir la carga de la base de datos y la presión de conexión.

**Mantenlo deshabilitado cuando:**

* Tu volumen es lo suficientemente bajo como para que las inserciones individuales no sean un cuello de botella.
* Necesitas garantías estrictas de ordenamiento por mensaje que el procesamiento por lotes rompería.
* Depuras el procesamiento de transacciones y quieres un flujo más simple, mensaje por mensaje.
