GH GambleHub

Exactly-once semántica

Qué es exactly-once en realidad

A menudo se entiende por «exactly-once» dos cosas diferentes:
  • Entrega: el mensaje será entregado al usuario exactamente una vez.
  • Tratamiento: el efecto secundario final (registro en la DB, cambio de balance, emisión de otro evento) ocurrirá exactamente una vez, incluso si las entregas o los intentos fueron mayores.

En sistemas distribuidos es más fiable hablar de semántica de procesamiento. La entrega es exactamente una vez difícil de asegurar (duplicados y repeticiones son inevitables), pero se puede hacer que el estado final sea equivalente a un solo procesamiento.


Cuándo se necesita EOS y cuándo no

EOS es necesario si:
  • Transacciones monetarias y balances: no se permite el doble cargo.
  • Contabilidad de licencias/cuotas, contadores de facturación.
  • Llamadas externas irreversibles (por ejemplo, activación de clave única).
Puede prescindir de la idempotencia al-least-once + si:
  • Los efectos son reversibles o compensables (sagas, devoluciones).
  • Se permiten duplicados temporales en las vitrinas/logs.
  • Es más barato proporcionar un sink idempotente que arrastrar transacciones a través de todo el trayecto.

Modelo: end-to-end vs. hop-by-hop

Hop-by-hop EOS: cada sección (fuente → procesador → receptor) asegura que su acción se aplica exactamente una vez.
End-to-end EOS: toda la cadena asegura que, desde el «hecho» hasta el «efecto lateral», el resultado es equivalente a un solo procesamiento.

En la práctica, el fin a fin se logra mediante una combinación de transacciones y/o idempotencia en cada hop.


Bloques básicos de construcción

1. Operaciones idempotentes

La repetición de la misma consulta sobre la clave de operación produce el mismo resultado.

Ключи: `idempotency_key`/`event_id`/`operation_id`.
Implementación: tabla de operaciones «vistas» con TTL ≥ retocar el registro de entrada.

2. Transacciones «leyendo-procesando-escribiendo» (read-process-write)

En una unidad atómica de trabajo, tanto el efecto secundario como el progreso de la lectura (offsets/posición) se registran. Esto elimina los «fantasmas» al caer entre los pasos.

3. Versificación/SEQUENCE

Para el agregado, se almacena la versión/contador; los cambios sólo se aplican si 'expected _ version' coincide. Las repeticiones del mismo evento no aumentan la versión → el efecto una vez.

4. Deduplikatsiya

Índice por '(consumer_id, event_id)' o por 'business _ id' natural de la operación.


Patrones de implementación

1) Registro transaccional + sink transaccional con fijación offset

Ideal para el procesamiento en streaming.

Leemos del registro (sólo los registros confirmados).
Realizamos el procesamiento.

En una transacción:
  • a) Registramos el efecto en sink (DB/tabla),
  • b) fijamos «leído antes del offset N» (en este mismo DB).
  • Commite. En el restart, o todo está fermentado (y el offset se moverá) o nada.

Propiedades: volver a ejecutar no perjudica; «exactamente una vez» por efecto, aunque el mensaje se haya leído dos veces.

2) Outbox + consumer idempotente

Para servicios de producción transaccional.

En una transacción de BD: cambiamos la entrada de dominio y escribimos el evento en outbox.
El duplicador entrega el evento al bus con el mismo 'event _ id'.
Los consumidores aplican los eventos de forma idempotente (dedoop por 'event _ id').

Propiedades: el productor garantiza que el hecho no se pierda; los consumidores garantizan exactamente un efecto.

3) EOS en sistemas similares a Kafka/Flink (conceptualmente)

Productor idempotente: protege contra las tomas en los retratos de envío.
Transacciones del productor: el grupo de registros en topics + el cambio de consumer se combinan atomicamente; los lectores utilizan el aislamiento 'read _ committed'.
El lado del procesador almacena el estado (state store) y lo comite junto con la transacción.

Propiedades: reiniciar el sctor/tasca no produce un efecto doble; duplicados «no visibles» downstream.

4) Idempotent «shiki» (sinks) vía upsert/merge

Sink acepta 'operation _ id '/' event _ id' y ejecuta 'UPSERT... WHERE NOT EXISTS`.
El efecto secundario (por ejemplo, la acumulación) se ejecuta atomicamente con la comprobación de «no se ha aplicado ya».

Propiedades: método EOS barato en el límite de almacenamiento, sin transacciones distribuidas.


Detalles clave de implementación

Identificadores de operación

Deben determinarse las repeticiones (no genere un nuevo UUID cuando se retrase).
Tener un área de visibilidad estable (por consumidor/por unidad/por sistema).

Tabla de deduplicación

Колонки: `consumer_id`, `operation_id`, `applied_at`, `ttl_expires_at`.
Índices por '(consumer_id, operation_id)'.
TTL ≥ la ventana de repetición máxima (retén de registro + retardos potenciales).

Competencia optimista

En el modelo write, almacene la versión del agregado.
Al aplicar un evento/comando, utilice 'WHERE version =: expected'; duplicado no aumentará la versión.

Orden/Orden

EOS no es igual a «exactamente el mismo orden». Proporcione consistencia a través de la llave del lote (todos los eventos del agregado → un lote) y/o la comparación 'sequence'.

Llamadas externas idempotentes

Para métodos inseguros (por ejemplo, webhooks HTTP a un servicio de terceros), agregue 'Idempotency-Key' y exija que el socio lo mantenga.


Trampas frecuentes

EOS en un solo lugar: si el sink es idempotente, pero usted emite eventos secundarios sin idempotencia, consiga «exactamente muchas veces» downstream.
Dos commitas: primero en el DB, luego el commit offset en el broker - la caída entre ellos crea efectos duplicados.
CDC crudos hacia fuera: el cambio del esquema del DB rompe la idempotencia de los consumidores.
Claves inestables: 'operation _ id' depende del tiempo/random y cambia cuando se retrasa.


Costos y compromisos

Latencia: transacciones/lecturas aisladas → crecimiento p95/p99.
Almacenamiento overhead: tablas de dedup, state stores, registros de transacciones.
Dificultad de operación: tiempos de transacción, rebalance de flujos, sesiones «vertidas».
Diagnóstico: más condiciones ("en kamita", "visto como read_committed", "retrocedido").

Seleccione EOS puntualmente: para agregados y efectos críticos; cubrir el resto con idempotencia y compensaciones.


Pruebas exactly-once

1. Fault-injection: la caída del proceso entre los pasos «registró el efecto» y «fijó el offset».
2. Duplicados: bombee el mismo mensaje 2-5 veces, asegúrese de que tiene el mismo efecto.
3. Restarts y rebalance: detener/reiniciar los workers, comprobando que no hay doble procesamiento.
4. Flappies de red: temporizadores en la mitad de la transacción, repetición del commit.
5. Pruebas de carga: el crecimiento de las colas → si no hay degradación a «para siempre en la transacción».


Mini plantillas (pseudo)

Sink idempotente con fijación offset

pseudo begin tx if not exists(select 1 from dedup where consumer_id=:c and op_id=:id)
then apply_effect(...)    -- upsert / merge / add_one_time_action insert into dedup(c, id, applied_at) values(:c,:id, now)
end if update offsets set pos=:pos where consumer_id=:c commit

Comando con la versión agregada

pseudo begin tx update account set balance = balance +:delta,
version = version + 1 where id=:account_id and version=:expected_version;
if row_count=0 then error CONCURRENT_MODIFICATION commit

Seguridad y cumplimiento

PII/PCI en tablas de dedup: almacenar un mínimo, usar tokens en lugar de datos «crudos».
Auditoría: lógica 'operation _ id', 'trace _ id', éxodo (APPLIED/ALREADY_APPLIED).
Política de retención: TTL en tablas de dedoup, archiving offset/logs.


Anti-patterny

«Real exactly-once delivery»: un intento de eliminar tomas a nivel de protocolo de transporte sin idempotencia de efecto.
Transacciones globales distribuidas para todo: XA/2PC a través de todos los servicios - frágil y lento.
Mezcla de subproductos no idempotentes (por ejemplo, se envía un correo electrónico antes de commit offset).
Ausencia de claves de operación: confiar en la «singularidad» de la carga útil.


Lista de verificación de producción

  • Hay una clave idempotente en cada efecto crítico.
  • El offset/posición de lectura se fija en una sola transacción con efecto.
  • Las tablas de dedoop están indexadas; TTL ≥ retoque de registro.
  • Se incluye competencia optimista para las unidades (versión/sequence).
  • Los flujos/topics se leen en el modo «Sólo en orden» (si está disponible).
  • Las pruebas de duplicados y caídas están presentes en el CI/CD.
  • Dashboards: proporción de repeticiones, transacciones fallidas, tiempos de bloqueo, lags.
  • Documentación para integradores sobre 'Idempotency-Keu '/repeticiones/timautas.

FAQ

¿Es posible proporcionar EOS sin transacciones?
A menudo sí - a través de la idempotencia de sink's (upsert/merge) y la versificación de agregados. Las transacciones simplifican las garantías, pero aumentan el costo.

¿Necesita «exactly-once» todo el mundo?
No. Es caro. Aplicar puntualmente donde la compensación no es posible/carretera.

¿Cómo vincular correos/webhooks con EOS?
Almacene en búfer la notificación antes del commit, envíela después de confirmar el efecto; almacene 'notification _ id' y haga el envío idempotente.

¿Qué es más importante: envío o manipulación?
Procesamiento. Los envíos pueden repetirse; el estado final debe ser correcto y el único.


Resultado

Exactly-once es sobre la corrección del efecto, no sobre la ausencia de tomas en el cableado. Se logra mediante una combinación de idempotencia, fijación atómica del efecto y progreso de la lectura, particionamiento inteligente y disciplina de versionamiento. Aplique EOS cuando el costo del error sea inaceptable y compruebe su realidad con pruebas de caídas y tomas - no por fe en el transporte.

Contact

Póngase en contacto

Escríbanos ante cualquier duda o necesidad de soporte.¡Siempre estamos listos para ayudarle!

Iniciar integración

El Email es obligatorio. Telegram o WhatsApp — opcionales.

Su nombre opcional
Email opcional
Asunto opcional
Mensaje opcional
Telegram opcional
@
Si indica Telegram, también le responderemos allí además del Email.
WhatsApp opcional
Formato: +código de país y número (por ejemplo, +34XXXXXXXXX).

Al hacer clic en el botón, usted acepta el tratamiento de sus datos.