Módulo 3: Gradual Rollout
Dwell time: cuánto esperar antes de confiar en una etapa
Descripción
Las lecciones anteriores dieron a cada etapa de la rampa un tamaño correcto (lección 3), un criterio de avance correcto (lección 4), y una población bien elegida (lección 6). Falta una pieza: el tiempo. Un guardrail que "se ve bien" apenas cinco minutos después de encender una etapa no es todavía una medición confiable — es una foto tomada demasiado pronto. Esta lección introduce el dwell time: el tiempo mínimo que una etapa necesita permanecer activa antes de que sus datos sirvan para decidir algo.
Conexión con el módulo. Esta lección agrega una segunda condición al criterio de avance que la lección 4 formalizó: no basta con que el guardrail esté dentro del techo — también hace falta haber esperado lo suficiente, y haber acumulado suficiente volumen, antes de confiar en esa medición. Es la última pieza de la rampa antes del mini-proyecto de la lección 8, que va a juntar las seis piezas del módulo sobre el rollout completo de recommendations.
Una analogía: probar la sopa antes de que termine de hervir
Nadie prueba si una sopa quedó bien salada a los treinta segundos de haber echado la sal — el sabor todavía no se distribuyó por todo el líquido, y una cucharada en ese momento puede salir insípida o, por el contrario, directamente salada, según qué tan cerca haya caído del punto exacto donde cayó la sal. Hace falta esperar a que hierva un rato, a que todo se mezcle, antes de que probar una cucharada diga algo confiable sobre el sabor de toda la olla. Probar demasiado pronto no es solo poco útil — puede llevar a una conclusión activamente equivocada, en cualquier dirección.
El guardrail de una etapa de rollout tiene el mismo problema. Cinco minutos después de subir el canary al 1%, es posible que apenas un puñado de usuarios hayan pasado por el código nuevo — la medición de p95Latency en ese momento puede estar dominada por una sola petición lenta, o por pura casualidad estar libre de cualquier petición lenta, sin que ninguno de los dos casos diga nada confiable sobre cómo se va a comportar esa etapa una vez que de verdad "hirvió" el tiempo suficiente.
Ejemplo trabajado: stageDwellCheck() — tiempo y volumen, los dos a la vez
Un chequeo de dwell time necesita dos condiciones al mismo tiempo: suficiente tiempo transcurrido, y suficiente volumen de datos acumulado. Ninguna de las dos, por sí sola, alcanza:
// stageDwellCheck: antes de avanzar, hacen falta DOS cosas -- tiempo minimo
// transcurrido (minDwellHours) Y volumen minimo de datos (minSampleSize). Un
// guardrail que "se ve bien" con muy pocos datos, o con muy poco tiempo, no es
// todavia una senal confiable -- es ruido con suerte.
function stageDwellCheck({ stage, elapsedHours, minDwellHours, exposedUsers, minSampleSize }) {
const enoughTime = elapsedHours >= minDwellHours;
const enoughSample = exposedUsers >= minSampleSize;
const ready = enoughTime && enoughSample;
return { stage, elapsedHours, minDwellHours, enoughTime, exposedUsers, minSampleSize, enoughSample, ready };
}
const checks = [
{ stage: 'canary 1%', elapsedHours: 6, minDwellHours: 4, exposedUsers: 2500, minSampleSize: 1000 },
{ stage: 'rollout 10%', elapsedHours: 1, minDwellHours: 12, exposedUsers: 25000, minSampleSize: 5000 },
{ stage: 'rollout 50%', elapsedHours: 18, minDwellHours: 24, exposedUsers: 125000, minSampleSize: 20000 },
];
console.log('=== stageDwellCheck: tiempo Y muestra minimos antes de avanzar ===\n');
checks.forEach((c) => {
const r = stageDwellCheck(c);
console.log(r.stage.padEnd(14) + 'elapsed=' + String(r.elapsedHours).padStart(2) + 'h/' + String(r.minDwellHours).padStart(2) + 'h' +
' sample=' + r.exposedUsers.toLocaleString('en-US').padStart(7) + '/' + r.minSampleSize.toLocaleString('en-US').padStart(6) +
' ready=' + r.ready);
});
const notReady = checks.map(stageDwellCheck).filter((r) => !r.ready);
console.log('\nEtapas que NO estan listas para avanzar todavia: ' + notReady.map((r) => r.stage).join(', ') + '.');
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== stageDwellCheck: tiempo Y muestra minimos antes de avanzar ===
canary 1% elapsed= 6h/ 4h sample= 2,500/ 1,000 ready=true
rollout 10% elapsed= 1h/12h sample= 25,000/ 5,000 ready=false
rollout 50% elapsed=18h/24h sample=125,000/20,000 ready=false
Etapas que NO estan listas para avanzar todavia: rollout 10%, rollout 50%.
Mira con atención la fila rollout 10%: tiene 25,000 usuarios expuestos, cinco veces más que el mínimo de muestra requerido (5,000) — el volumen de datos sobra. Y aun así, ready: false, porque solo pasó 1 hora de las 12 requeridas. Esta fila es exactamente el caso que justifica por qué el modelo exige las dos condiciones a la vez: si stageDwellCheck() solo mirara el volumen, esta etapa habría dado ready: true con apenas una hora de datos — tiempo suficiente para que, por ejemplo, un pico de tráfico de una sola hora del día (la hora de almuerzo, un evento puntual) distorsione por completo la medición de p95Latency, sin que ese sesgo se note todavía.
Por qué el volumen, solo, no basta — y por qué el tiempo, solo, tampoco
El ejemplo de hoy deja ver algo que vale la pena nombrar explícitamente: volumen y tiempo capturan riesgos distintos, y ninguno cubre el hueco del otro. El volumen (exposedUsers >= minSampleSize) protege contra el ruido estadístico — muy pocos eventos, y cualquier resultado es más suerte que señal, exactamente el problema que viste en la lección 3 con un canary demasiado chico. El tiempo (elapsedHours >= minDwellHours) protege contra un riesgo distinto: la variación por momento del día o del ciclo semanal. Un sistema puede comportarse de forma completamente distinta un lunes a las 9am (tráfico alto, jobs de fin de semana recién terminando) que un sábado a las 3am (tráfico mínimo) — y una hora de datos, sin importar cuántos usuarios haya en esa hora, nunca puede capturar esa variación.
rollout 50% en el ejemplo muestra el caso contrario: casi cumple el tiempo (18 de 24 horas) y tiene volumen de sobra (125,000 contra un mínimo de 20,000) — pero ready sigue siendo false, porque falta esa última condición de tiempo. No hay atajos: las dos condiciones tienen que cumplirse a la vez, no basta con que una compense a la otra.
Errores comunes
Dwell time cero: avanzar antes de que los datos lleguen, apenas se enciende la etapa. Qué pasa: alguien revisa el guardrail a los pocos minutos de subir una nueva etapa, lo ve dentro del techo, y avanza de inmediato a la siguiente. Por qué pasa: la ansiedad de avanzar rápido —sobre todo si las etapas anteriores ya pasaron sin problema— hace que una primera mirada "limpia" se sienta como suficiente confirmación. Cómo detectarlo: como en la fila rollout 10% del ejemplo, el tiempo transcurrido es una fracción chica del mínimo requerido, aunque el guardrail se vea bien en ese instante. Cómo corregirlo: fija un minDwellHours explícito para cada etapa, antes de encenderla —no lo decidas sobre la marcha mirando qué tan bien se ve el número en ese momento—, y respétalo aunque el guardrail luzca perfecto desde el primer minuto.
Usar el mismo dwell time fijo para todas las etapas, sin ajustar según su volumen esperado de tráfico. Qué pasa: el equipo define "esperamos 4 horas en cada etapa" como regla general, sin distinguir que el canary al 1% tarda mucho más en acumular el mismo volumen de datos que el 50%. Por qué pasa: una regla fija y simple es más fácil de recordar y comunicar que un cálculo distinto para cada etapa. Cómo detectarlo: compara cuánto tiempo le toma a cada etapa alcanzar su minSampleSize — si una etapa con mucho menos tráfico (el canary) necesita mucho más tiempo que una etapa con más tráfico (el 50%) para juntar el mismo volumen, una regla de tiempo fija está sobre-esperando en una etapa y sub-esperando en otra. Cómo corregirlo: como hace stageDwellCheck(), define el dwell time mínimo pensando en cuánto tráfico necesitas acumular en esa etapa específica, no como un número redondo aplicado a todas por igual.
Confundir "el guardrail pasó una vez, en un instante" con "el guardrail es estable". Qué pasa: se mira el dashboard una sola vez, en un momento puntual, se ve p95Latency bajo el techo, y se concluye que la etapa "pasó" — sin haber observado el comportamiento a lo largo de todo el dwell time. Por qué pasa: una sola foto limpia se siente como evidencia suficiente, especialmente si confirma lo que el equipo ya esperaba ver. Cómo detectarlo: nadie puede describir cómo se comportó el guardrail a lo largo del dwell time completo —solo puede citar el último número visto—. Cómo corregirlo: el dwell time no es solo "esperar un rato y mirar una vez al final" — es acumular suficientes mediciones a lo largo de ese período para confirmar que el guardrail se mantuvo dentro del techo de forma sostenida, no solo en el instante en que alguien decidió mirar.
Ejercicios
Ejercicio 1 — Calcula si una etapa está lista. El equipo revisa rollout 10% de nuevo, seis horas después del chequeo del ejemplo: ahora elapsedHours: 12, con exposedUsers: 30000 (el resto de los valores iguales: minDwellHours: 12, minSampleSize: 5000). ¿Está lista para avanzar? Usa stageDwellCheck() mentalmente.
Ver solución
enoughTime: 12 >= 12 → true. enoughSample: 30000 >= 5000 → true. ready: true. La etapa ya está lista — cumple exactamente el mínimo de tiempo (ni un minuto de más) y sobra volumen de muestra. Vale la pena notar que "exactamente el mínimo" es un caso límite: en la práctica, muchos equipos prefieren un pequeño margen adicional sobre el mínimo estricto antes de avanzar, especialmente si la etapa siguiente es una de las que expone a un salto grande de gente nueva (lección 5).
Ejercicio 2 — Diseña el dwell time de una etapa nueva. Si Mercado agrega la etapa intermedia del 5% (como en el ejercicio 1 de la lección 5), y quiere que su minSampleSize sea proporcional a su tamaño relativo a rollout 10% (que tiene minSampleSize: 5000 con 25,000 usuarios esperados), ¿qué minSampleSize sería razonable para la etapa del 5% (que expone a 12,500 usuarios)?
Ver solución
Manteniendo la misma proporción (minSampleSize / exposedUsers = 5000 / 25000 = 0.2, es decir, un mínimo de muestra equivalente al 20% del volumen esperado de la etapa), la etapa del 5% necesitaría minSampleSize: round(12500 * 0.2) = 2500. No es la única forma razonable de calcularlo —también se podría fijar un mínimo absoluto igual para todas las etapas, como hace el umbral de señal de la lección 3—, pero escalar el mínimo según el tamaño de cada etapa evita pedir, sin necesidad, el mismo volumen absoluto a una etapa mucho más chica que otra.
Ejercicio 3 — Explica el error con tus propias palabras. Un compañero de equipo dice: "Tenemos 25,000 usuarios en la etapa del 10%, que es muchísima gente — no hace falta esperar más, ya tenemos suficiente dato." Usando lo que viste en esta lección, explica en 2-3 frases por qué esa afirmación, aunque cierta sobre el volumen, no es suficiente para decidir avanzar.
Ver solución
La afirmación tiene razón sobre el volumen —25,000 es, de hecho, cinco veces el mínimo de muestra de esa etapa— pero volumen y tiempo protegen contra riesgos distintos: el volumen asegura que hay suficientes eventos para que la señal no sea puro ruido estadístico (lección 3), mientras que el tiempo asegura que esos eventos cubren la variación normal de un sistema real —distintas horas del día, distintos días de la semana—. Veinticinco mil usuarios juntados en una sola hora pico no dicen nada sobre cómo se comporta el sistema durante la madrugada, o un fin de semana — por eso stageDwellCheck() exige las dos condiciones a la vez, y sobrar en una no compensa faltar en la otra.
Resumen y siguiente paso
En esta lección agregaste la última pieza de la rampa: el dwell time. Con stageDwellCheck() viste que una etapa necesita cumplir dos condiciones a la vez —tiempo mínimo transcurrido y volumen mínimo de muestra— antes de que su medición del guardrail sea confiable, y que un volumen grande en poco tiempo (como los 25,000 usuarios de rollout 10% en apenas 1 hora) no compensa la falta de tiempo suficiente para capturar la variación normal de un sistema real.
Antes de avanzar deberías poder: explicar por qué tiempo y volumen protegen contra riesgos distintos; y calcular si una etapa está lista para avanzar dado su tiempo transcurrido, volumen acumulado, y sus mínimos requeridos.
Con esto se completan las seis piezas de la rampa: el tamaño del canary (lección 3), el criterio de avance (lección 4), el radio de impacto creciente (lección 5), los rings (lección 6), y el dwell time (esta lección) — todas construidas sobre la estructura de rolloutPlan() de la lección 2. La lección 8, el mini-proyecto, te pide juntar las seis piezas y simular la rampa completa de recommendations, de principio a fin.
Recursos
- Martin Fowler (bliki), "CanaryRelease" — martinfowler.com/bliki/CanaryRelease.html. Contrasta la duración típica de un canary —minutos u horas— contra la de un experimento A/B con significancia estadística —que puede tardar días—, un matiz útil para calibrar qué tan largo debería ser el dwell time de cada etapa según su propósito. En inglés.
- Google SRE, "Reliable Product Launches at Scale" — sre.google/resources/book-update/reliable-product-launches-at-scale. El capítulo del libro de SRE de Google sobre cómo la organización coordina lanzamientos confiables a gran escala, incluyendo la revisión sostenida de un lanzamiento en el tiempo, no solo en el momento de encenderlo. En inglés.