CM-14 · Resiliencia operacional#
En una frase, para cualquiera: los sistemas se caen. La diferencia entre un susto y un desastre es si alguien había ensayado qué hacer, y si el sistema sabe pararse solo antes de hacer daño.
Estado real: 🟠 prototype — hay código y escenarios que se ejecutan, sin verificación en un entorno real · Módulo: crates/sandbox-markets/src/cases/resilience.rs
Por qué se realiza este caso#
En un sistema financiero, seguir funcionando mal es peor que detenerse. Un motor de órdenes con precios erróneos ejecuta operaciones reales a precios que no existen, y deshacerlas después es caro, lento y a veces imposible.
| Incidente | Por qué es peligroso seguir |
|---|---|
| Base de datos caída | Se opera sobre un estado que no se puede persistir |
| Mensajes duplicados | La misma orden se ejecuta dos veces |
| Latencia alta | Se opera con precios de hace medio minuto |
| Motor de órdenes desconectado | Nadie sabe qué se ejecutó y qué no |
| Precios erróneos | Se ejecuta a precios inventados |
| Custodio indisponible | Se prometen entregas que no se pueden cumplir |
| Credenciales comprometidas | Cada segundo cuenta |
| Despliegue defectuoso | El fallo se multiplica por el volumen |
La idea que enseña, y que ningún otro caso enseña#
Detenerse a tiempo es una función del sistema. El kill switch no es un botón de emergencia para humanos: es un control con condiciones definidas de antemano —qué lo dispara, qué se detiene, qué sigue funcionando— y que se ensaya.
Y junto a él, la degradación controlada: cuando algo falla, el sistema no elige entre «todo» y «nada», sino que apaga por partes en un orden decidido antes.
Casos de uso reales#
- Ensayo de continuidad operacional exigido a una entidad supervisada.
- Un equipo que quiere saber si su kill switch funciona de verdad.
- Post mortem de un incidente real, reproducido.
- Formación: por qué degradar es mejor que caerse entero.
Cómo funcionará#
flowchart LR
I["⚡ Incidente inyectado"] --> D["👁️ Detección"]
D --> K{"🛑 ¿Se cumplen las<br/>condiciones del kill switch?"}
K -- sí --> S["🔴 Detener lo afectado"]
K -- no --> G["🟡 Degradación controlada"]
S & G --> R["🔧 Recuperación"]
R --> RP["⏪ Replay de<br/>lo que quedó a medias"]
RP --> RC["🔍 Reconciliación"]
RC --> CO["📣 Comunicación"]
CO --> PM["📚 Post mortem"]
flowchart TB
A["Incidente"] --> B{"¿Afecta a la<br/>integridad de los datos?"}
B -- sí --> C["🛑 KILL SWITCH inmediato"]
B -- no --> D{"¿Afecta a la<br/>calidad de la ejecución?"}
D -- sí --> E["🟡 Suspender solo lo afectado"]
D -- no --> F["🟢 Seguir, con alerta"]
C & E --> G["⏱️ Medir: tiempo hasta detectar<br/>y tiempo hasta detener"]
Esos dos tiempos —detectar y detener— son las métricas del caso. Todo lo demás se deriva de ellas.
Esquemas#
{
"incident": {
"kind": "stale-prices",
"injectedAt": 1200,
"affects": ["order-engine"],
"severity": "alta"
}
}
{
"response": {
"detectedAtMs": 340,
"killSwitchTriggeredAtMs": 380,
"stopped": ["nuevas órdenes en ACME-SIM"],
"keptRunning": ["consulta de saldos", "cancelaciones"],
"replayed": 128,
"reconciliationFindings": [],
"dataIntegrityPreserved": true
}
}
durante un incidente. Impedir cancelar mientras el mercado se mueve es atrapar a los clientes en sus posiciones.
Software necesario#
| Componente | Para qué |
|---|---|
| Rust 1.75+ | Inyección de incidentes, kill switch y replay |
| Node.js 20+ / pnpm 9+ | Panel de estado durante el incidente (recomendado) |
Sin jaula ni Linux: los incidentes se inyectan en un sistema simulado, no se provocan fallos reales en el equipo.
Instalación#
cargo build --release
Procesos que se crearán#
sandboxctl markets resilience --incident precios-obsoletos --seed 7
│
└─ un proceso determinista, sin red
├─ reloj simulado con inyección en un instante exacto
├─ registro append-only para poder hacer replay
└─ conciliación posterior contra CM-03
La semilla y el reloj simulado hacen que el mismo incidente se reproduzca igual, que es lo que permite comparar respuestas entre versiones del sistema.
Tiempo de carga estimado#
| Operación | Coste esperado |
|---|---|
| Un escenario de incidente completo | < 1 s |
| Replay de 100 000 eventos | segundos |
| Conciliación posterior | milisegundos |
Qué hace falta para construirlo#
- Inyección de los ocho incidentes listados, en instantes exactos.
- Kill switch con condiciones declarativas y ensayables.
- Degradación controlada por partes, con orden definido.
- Replay desde el registro append-only.
- Conciliación posterior contra CM-03.
- Post mortem generado con la línea de tiempo y las dos métricas.
Si algo falla#
El caso ya tiene código y escenarios que se ejecutan. Lo que sigue son sus fallos con la causa y la salida:
| Situación | Causa | Cómo se resuelve |
|---|---|---|
| El kill switch no se dispara | Las condiciones estaban mal definidas | Se declaran de antemano y se ensayan con incidentes inyectados. Un kill switch que nunca se ha probado no se sabe si funciona |
| Se detiene todo cuando bastaba con degradar | Respuesta desproporcionada | Se apaga por partes, en un orden decidido antes. keptRunning importa tanto como stopped |
| Las cancelaciones se bloquean durante el incidente | Error de diseño frecuente | Las cancelaciones siguen vivas siempre: impedir cancelar mientras el mercado se mueve atrapa a los clientes en sus posiciones |
| El replay duplica operaciones | Falta idempotencia | El libro es idempotente por operación. Repetir un evento del registro no lo aplica dos veces |
| El mismo incidente da resultados distintos | Falta la semilla o el reloj simulado | Con semilla y reloj simulado el incidente se reproduce igual, que es lo que permite comparar la respuesta entre versiones |
Los fallos que afectan a cualquier caso —la compilación, el catálogo, la evidencia— están resueltos uno a uno en Cuando algo falla.
Esta familia no necesita aislamiento del sistema: no ejecuta código ajeno, sino reglas de negocio deterministas. Por eso casi ningún fallo suyo viene del entorno, y casi todos vienen de los datos.
Cómo se comprueba#
cargo run -p sandboxctl -- markets check --case CM-14
Ejecuta los escenarios de este caso y compara cada uno con lo que declara de antemano que debe salir. Corre en cada commit: si el caso deja de detectar lo que dice detectar, la integración continua se pone roja.
cargo test -p sandbox-markets resilience
Los invariantes del módulo, incluidos los que ningún escenario de arriba cubre.
Sigue enprototype, no enfunctional. Los escenarios se ejecutan y pasan, pero el caso no emite evidencia firmada por ejecución ni se ha usado contra datos que no sean los suyos. La regla completa está en el ROADMAP.
Ver también: Catálogo completo · CM-10 · liquidación · CM-16 · datos de mercado