Volver a Insights
LinkedInX
Categoría: ingenieria2026-02-177 min

Deriva de estado: el modo de falla silencioso en sistemas de apuestas en tiempo real

Cuando el estado diverge entre las capas de ingesta, trading y riesgo, los sportsbooks pierden determinismo sin notarlo.

SB
Autor
SmartBet Engineering
Escribimos sobre arquitectura, trading systems, riesgo e infraestructura en tiempo real para sportsbooks.

Contexto: por qué la deriva de estado es un fallo “silencioso”

En un sportsbook en tiempo real, el estado operativo se materializa en varias capas: ingesta (feeds), pricing/trading, riesgo, y finalmente liquidación/contabilidad. La plataforma asume implícitamente que todas “ven” el mismo mundo con el mismo orden causal.

La deriva de estado aparece cuando dos o más capas mantienen versiones distintas del mismo hecho (marcador, reloj, suspensiones, límites, exposición, elegibilidad) y esas diferencias no disparan alarmas. El sistema sigue aceptando apuestas y cotizando, pero pierde:

  • Determinismo (misma entrada ≠ misma decisión).
  • Reproducibilidad (no se puede reconstruir por qué se aceptó/rechazó algo).
  • Auditabilidad (complicación regulatoria y disputas).

Esto se conecta directamente con el marco de determinismo en sistemas regulados (ver /es/insights/ingenieria/el-determinismo-como-ventaja-competitiva-en-trading-regulado) y con patrones que “parecen” escalar pero degradan coherencia en producción (ver /es/insights/ingenieria/la-ilusion-de-escalabilidad-en-arquitecturas-de-sportsbooks).

Anatomía del estado en un sportsbook (infraestructura-first)

Capas típicas y sus fuentes de verdad de facto

  • Ingesta
    • Fuentes: proveedor A/B, scraping, oficial, trading desks.
    • Estado: eventos, clock, scores, flags (suspensión, VAR, lesión), calidad de feed.
  • Trading / Pricing
    • Estado: parámetros del modelo, snapshot del evento, offsets, latencia percibida, guardrails.
  • Riesgo
    • Estado: exposición por mercado/selección/cliente, límites, reglas AML, KYC, correlaciones, “cooldowns”.
  • Execution / Bet placement
    • Estado: reglas de aceptación, idempotencia, locking de precio, expiraciones.
  • Ledger
    • Estado: balances, reservas, settlement, reconciliación.

En la práctica, cada capa termina manteniendo caches, proyecciones y materializaciones. El fallo no es tener copias; el fallo es no tener un protocolo explícito de convergencia.

“Estado” no es solo datos: es orden y tiempo

Para trading en vivo, dos propiedades son críticas:

  • Orden de eventos: qué update ocurrió antes (y bajo qué reloj).
  • Validez temporal: hasta cuándo un precio o un estado era válido.

Si cada servicio reinterpreta orden/tiempo (por latencia, reintentos, clocks desalineados), la deriva es inevitable.

Modos de deriva de estado (los que no suelen alertar)

1) Divergencia por orden (out-of-order) y replays parciales

Síntoma: ingesta corrige un score/clock; trading ya repreció; riesgo aún calcula exposición con el estado anterior.

Causas infra:

  • Kafka/streaming sin claves de orden por entidad (eventId/marketId).
  • Retries que reinsertan mensajes antiguos con timestamps “nuevos”.
  • Consumers con commits adelantados o lag variable por partición.

Impacto:

  • Precios ofertados basados en un estado que riesgo considera inválido.
  • Aceptación de apuestas en ventanas que deberían estar suspendidas.

2) Divergencia por “caches inteligentes” (TTL) y fallbacks

Síntoma: trading usa cache local para resiliencia; ingesta cambia estado; riesgo consulta otro store con TTL distinto.

Causas infra:

  • TTL heterogéneos por servicio.
  • Circuit breakers que activan un fallback a un snapshot antiguo.
  • “Stale-while-revalidate” sin límites de staleness por mercado.

Impacto:

  • El sistema se mantiene “verde” (latencia baja), pero actúa sobre datos obsoletos.

3) Divergencia por semántica distinta del mismo flag

Síntoma: “suspendido” significa cosas distintas (por ejemplo, suspendido para aceptar vs suspendido para cotizar).

Causas:

  • Contratos API débiles (booleanos ambiguos).
  • Evolución de schema sin versionado semántico.
  • Normalización incompleta entre proveedores.

Impacto:

  • Aceptación de apuestas con pricing “congelado”.
  • Rechazos intermitentes difíciles de explicar al cliente.

4) Divergencia por identidad (IDs) y mapeos inconsistentes

Síntoma: el mismo partido tiene distintos eventIds según feed; trading y riesgo agregan exposición en entidades diferentes.

Causas:

  • Servicio de entity resolution no determinista.
  • Mapeos eventual-consistent sin “hard locks” para eventos live.
  • Merge/split de entidades durante el evento.

Impacto:

  • Subestimación de exposición real.
  • Límites que no se aplican donde deberían.

5) Divergencia por particiones de red: “split-brain” funcional

Síntoma: dos regiones/az activas aceptan apuestas con estados distintos; reconcilian después.

Causas infra:

  • Multi-region activo/activo sin consenso estricto para el plano de control.
  • Stores con consistencia eventual en rutas críticas (p.ej., límites y suspensiones).
  • Falta de fencing tokens en writers.

Impacto:

  • Pérdida de atomicidad entre aceptación y reserva de riesgo.
  • Incidentes de “void” y disputas de settlement.

Señales operativas: cómo se ve en métricas (cuando nadie mira lo correcto)

Métricas que suelen estar “bien” aunque haya deriva

  • CPU/memoria normales.
  • Lag medio aceptable.
  • Error rate HTTP bajo.
  • P99 de colocación bajo por uso de caches.

Métricas que sí delatan deriva

  • Discrepancias de versión por entidad (eventVersion/marketVersion) entre servicios.
  • Tasa de decisiones no reproducibles: misma requestId → distinta decisión al reejecutar con el mismo snapshot.
  • Drift budget consumido: porcentaje de operaciones ejecutadas con staleness > X ms.
  • Mismatch de suspensiones: apuestas aceptadas durante ventanas suspendidas según ingesta.
  • Reconciliación de exposición: delta entre exposición online vs ledger/settlement.

Contención: limitar el blast radius sin “apagar el mundo”

Guardrails deterministas en el plano de ejecución

  • Price locking con versión: un bet accept debe incluir (eventId, marketId, version) y fallar si el writer ve una versión distinta.
  • Fencing para writers de estado crítico (suspensiones, límites): tokens monotónicos para evitar split-brain.
  • Idempotencia end-to-end: requestId + invariantes (stake, selection, price, version).

“Fail closed” selectivo, no global

  • Cerrar solo mercados/eventos con drift > umbral.
  • Degradar a “view-only” cuando la ingesta es incierta.
  • Aplicar circuit breakers por entidad, no por servicio.

Presupuestos de staleness por mercado

No todos los mercados toleran lo mismo:

  • Moneyline live: staleness permitido muy bajo.
  • Props de baja frecuencia: tolerancia mayor.

Hacerlo explícito evita que caches “optimicen” donde no deben.

Prevención estructural: arquitectura para convergencia, no solo para throughput

Estado como stream con versiones monotónicas

  • Versionado por entidad (event/market) con monotonicidad garantizada.
  • Consumers deben rechazar updates con version <= lastSeen.
  • Correcciones (amendments) como eventos explícitos, no overwrites silenciosos.

Un plano de control único para suspensiones y elegibilidad

Separar:

  • Data plane: precios, snapshots, cotización.
  • Control plane: suspender/activar, límites, reglas de aceptación.

El control plane requiere consistencia más fuerte que el data plane.

Reconciliación continua (online) con invariantes

Ejemplos de invariantes:

  • No aceptar si marketState != OPEN.
  • Exposición por marketId debe ser >= suma de reservas de bets aceptadas.
  • Settlement solo si eventState terminal y versión final confirmada.

La reconciliación debe correr como servicio de producción, no como batch post-mortem.

Observabilidad orientada a estado (no a servicios)

  • Traces con state vector (versions, timestamps, source).
  • Logs estructurados con causalidad: parent updateId → decisionId.
  • Dashboards por entidad (top eventos por drift), no por microservicio.

Para una taxonomía amplia de ingeniería/operación, ver /es/insights.

Caso típico de incidente (patrón)

Cadena de eventos

  1. Feed A envía gol con timestamp T; feed B lo envía 2s después.
  2. Ingesta publica update v105 (gol), luego recibe corrección y publica v106 (anulado).
  3. Trading consume v105, reprice; su cache TTL mantiene pricing 3s.
  4. Riesgo consume v106 a tiempo y marca mercado suspendido.
  5. Execution acepta apuestas porque valida contra cache de trading (OPEN) y no contra control plane (SUSPENDED).

Por qué “no se ve”

  • No hay errores: todo responde.
  • Latencia es baja: caches funcionan.
  • El drift ocurre en una ventana pequeña, pero suficiente para pérdidas.

Key takeaways

  • La deriva de estado no es “inconsistencia eventual”; es pérdida de determinismo en decisiones de aceptación/pricing/riesgo.
  • Medir salud por servicios oculta el problema; medir versiones y staleness por entidad lo revela.
  • La contención efectiva combina price locking con versión, fencing y fail-closed selectivo.
  • La prevención exige un control plane consistente y reconciliación continua con invariantes.

Related