El coste oculto de la orquestación con estado
SchemaBridge Team · 2026-02-02 · Distributed Systems, Immutable Infrastructure, Orchestration, Anti-Patterns, Reliability
Por qué tus herramientas internas "sencillas" pueden ser tu mayor riesgo de fiabilidad y cómo aplicar patrones de infraestructura inmutable a la lógica de aplicación puede salvarte.
En el mundo de los microservicios estamos obsesionados con el desacoplamiento. Usamos colas, adoptamos arquitecturas orientadas a eventos y rompemos monolitos. Sin embargo, dentro de nuestros motores de orquestación solemos cometer el pecado capital de los sistemas distribuidos: escribimos bucles con estado.
En SchemaBridge hemos renovado recientemente nuestros workflows de sistema principales, los que dirigen nuestros propios despliegues graduales. Al hacerlo, nos encontramos frente a los mismos antipatrones que afectan a muchas organizaciones de ingeniería. Esto no es solo una historia sobre nuestras notas de versión; es un ejercicio de aplicación de los principios de la infraestructura inmutable a la capa de aplicación.
El antipatrón del "bucle"
Considera el clásico "poller":
while not deployment.is_ready():
sleep(60)
check_status()
Este código presupone un universo estable. Presupone que el proceso de polling vivirá para siempre. En realidad, es un artefacto de un monolito distribuido. Un proceso que mantiene su estado en memoria durante una larga espera síncrona es frágil: cuando su host se reinicia o una llamada se cuelga, pierde ese estado o se queda como un "zombi" que nadie vigila, y sus modos de fallo se vuelven impredecibles.
La solución: encadenamiento recursivo
Pasamos de un bucle while interno a un modelo de ejecución recursivo, similar al Continuation Passing Style de la programación funcional.
- Patrón: Cada ronda lanza un workflow hijo de monitorización que espera con un paso
DELAYduradero en lugar desleep(); a continuación, un pasoSPAWNERinicia la siguiente ronda como workflow hijo: el workflow de despliegue lanza otra ejecución de sí mismo. - Resultado: El padre sigue esperando a que su hijo le informe, pero espera de forma duradera: la espera es una fila en PostgreSQL (un merge
WAIT_ALLque aguarda el callback del hijo), no un hilo ni un bucle en memoria. Si un worker muere a mitad del despliegue, no se pierde nada; el estado de la base de datos sigue siendo la fuente de verdad.
Lógica inmutable: versionado con hashing canónico
El versionado es notoriamente difícil. Cuando actualizas la definición de un workflow, ¿qué pasa con las ejecuciones que ya están en curso?
- El antipatrón: "Actualizaciones in situ". Despliegas código nuevo y las ejecuciones existentes empiezan de repente a comportarse de otra manera.
- El estándar del sector: Infraestructura inmutable. Igual que no entramos por SSH en los servidores para parchearlos (los reemplazamos), no deberíamos parchear definiciones de workflow en ejecución.
Las versiones de workflow en SchemaBridge son inmutables: un despliegue crea una versión nueva y cada ejecución queda ligada a la versión con la que empezó. Lo que hemos añadido es el hashing canónico del DSL: cada definición de workflow importada se serializa a una forma JSON canónica y se le calcula un hash, que se almacena con el workflow (dslHash).
Ese hash responde a una pregunta que antes era sorprendentemente difícil: ¿ha cambiado realmente la lógica? Cuando una importación de configuración o nuestro propio bootstrap de workflows de sistema se encuentra con una definición cuyo hash coincide con lo que ya está en producción, planifica un no-op en lugar de redesplegar, y un workflow en producción que se ha desviado de su definición de origen se puede detectar comparando hashes.
Bloquear el "modo dios"
Por último, abordamos la seguridad de las herramientas internas. Es habitual dar acceso root a los scripts internos de "limpieza". Esto viola el principio de mínimo privilegio.
Descubrimos que nuestra propia herramienta interna de "Gradual Rollout" localizaba los workflows sobre los que operaba mediante IDs lógicos codificados a mano, lo que facilitaba apuntar al equivocado y sobrescribirlo. Lo corregimos de dos maneras:
- Resolver por rol, no por nombre: El despliegue encuentra ahora su punto de entrada de destino mediante etiquetas explícitas, validadas para que sean únicas, en lugar de búsquedas por IDs lógicos codificados a mano.
- Una credencial de mínimo privilegio: Los pasos Gateway de nuestros workflows de despliegue se autentican con su propia API key con nombre, a la que deliberadamente le falta
config:manage, el permiso para reescribir la configuración del tenant de sistema. La automatización que se ejecuta desde el lugar menos fiable ya no puede cambiar las reglas bajo las que se ejecuta.
Conclusión
Si tu orquestación depende de sleep(), estás luchando contra la nube.
Al adoptar la recursión, la inmutabilidad y el mínimo privilegio, pasamos de una orquestación "basada en la esperanza" a una orquestación cuyo estado puedes inspeccionar y cuyo radio de impacto puedes acotar.
Lecturas recomendadas: