Saltar al contenido
Payment Emulator LabDocumentaciónEntrar a la consola
En esta página

Arquitectura

ADR 011-transporte-de-webhooks-intercambiable

La entrega de webhooks corría sobre BullMQ y Redis, y sobre nada más. Eso nuncafue tanto una decisión sobre Redis como una herencia: la cola llegó antes que eloutbox transaccional.

3 min de lectura

Índice / Index · English

Traducción de docs/en/architecture/decisions/ADR-011-pluggable-webhook-transport.md, que es el original. Si discrepan, manda el inglés.

Estado: Aceptado

Sustituye la suposición de Redis del ADR 005; la regla que ese ADR enuncia no cambia, y ahora vale para todos los transportes.

#Contexto

La entrega de webhooks corría sobre BullMQ y Redis, y sobre nada más. Eso nunca fue tanto una decisión sobre Redis como una herencia: la cola llegó antes que el outbox transaccional.

Una vez que el outbox existió, la cola dejó de ser donde vive la obligación. Una fila de webhook_events se escribe en la misma transacción que el pago que la debe; un trabajo perdido se vuelve a crear desde la fila. La cola pasó a ser transporte.

Dos cosas convirtieron entonces un transporte fijo en un coste en vez de en una simplificación. Redis no tiene una compilación propia para Windows, así que ahí significa WSL — una máquina virtual reservando gigabytes para alojar un proceso que necesita megabytes. Y PostgreSQL, que ya es obligatorio, había desarrollado para entonces casi una cola entera dentro del outbox: reclamo con FOR UPDATE SKIP LOCKED, por lotes, y una ventana de rancidez que es un arrendamiento con otro nombre.

#Decisión

El worker toma su transporte de WEBHOOK_QUEUE_DRIVER, detrás de un puerto de tres métodos —enqueue, consume, close— más dos declaraciones: qué driver es, y si el despachador del outbox tiene que alimentarlo.

Driver Guarda los trabajos en Necesita en marcha
postgres el propio webhook_events nada más
mongo un documento de webhook_jobs mongod, suelto
bullmq Redis Redis

postgres es el valor por defecto, para que un clon entregue webhooks sin instalar nada más allá de la base de datos que la API ya exigía. docker-compose nombra bullmq explícitamente, porque la pila compuesta es la que enseña la forma que esto tiene en producción y un defecto silencioso la representaría mal.

Tres reglas atan a todos los drivers:

  1. Ninguna verdad de pago en un driver. Guarda un id de evento, un contador de intentos y una hora. La fila es el compromiso y webhook_attempts es el historial; perder el almacén de un driver entero cuesta un retraso, nunca un hecho.
  2. Una sola política de reintentos, declarada en RETRY y aplicada por los tres — BullMQ por dentro, los otros dos a mano.
  3. Una sola suite de contrato, corrida contra cada driver disponible. Sin ella no son alternativas, sino tres cosas que se parecen; la afirmación que más peso lleva es que dos consumidores nunca reciben el mismo trabajo.

#Consecuencias

El worker arranca en una máquina que sólo tenga PostgreSQL, que es el caso normal del desarrollo local en Windows.

La política de reintentos queda expresada de hecho dos veces —como opciones de BullMQ y como aritmética en los dos drivers escritos a mano— y sólo la suite de contrato las mantiene iguales. Ése es el precio de la costura, y se paga a sabiendas.

mongo sondea en vez de mirar un change stream, porque los change streams exigen un replica set y pedir rs.initiate() antes de que nadie pueda desarrollar es peor impuesto que una consulta cada quinto de segundo. Nada de lo que hace necesita una transacción multi-documento: un reclamo es un findOneAndUpdate sobre un documento.

Un tercer almacén es una tercera cosa que mantener viva. Si Mongo deja de merecer su sitio, el driver se borra en vez de dejarlo pudrirse.

Payment Emulator Lab · RonuSoftwareMIT