El trabajo que cobró dos veces
Un punto de control es un registro guardado del trabajo ya terminado. Gracias a él, un trabajo largo puede parar y volver a empezar sin repetirlo. Los agentes funcionan minutos u horas, y los procesos mueren.
Un trabajo cobró cinco pedidos. Falló en el tercero y arrancó de nuevo desde el principio. Dos clientes pagaron dos veces.
Al final de esta lección
Nota
Puedo hacer que un trabajo largo se pueda parar y reanudar sin riesgo.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Punto de control | un registro guardado del trabajo terminado | |
| Idempotente | seguro de ejecutar dos veces, con un solo efecto | |
| Reanudar | seguir desde el último punto de control | |
| Efecto secundario | un cambio fuera del programa | un cobro |
| Registro de eventos | una lista de lo ocurrido, en orden |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un punto de control dice qué terminó
Después de cada paso, escribe su nombre en un archivo o en una tabla. Al arrancar, lee ese registro y salta lo que ya está hecho.
Escribe una entrada iniciado antes de un paso y una entrada hecho después. Así un reinicio distingue un paso que nunca empezó de uno cortado a medias.
Ejemplo resuelto: dónde puede caer un fallo
| El fallo cae | El registro dice | Al reanudar |
|---|---|---|
| Antes de empezar el paso | Nada sobre él | Ejecutarlo |
| En el paso, antes de su efecto | Iniciado | Reintentarlo |
| Tras su efecto, antes de hecho | Iniciado | Reintentarlo, y el efecto se repite |
| Después de hecho | Hecho | Saltarlo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Escribir el trabajo reanudable, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const CHECK = \"demo/checkpoint.json\";' 'const LOG = \"demo/charges.log\";' 'const orders = [\"P1\", \"P2\", \"P3\", \"P4\", \"P5\"];' 'const crashAt = process.argv[2];' 'fs.mkdirSync(\"demo\", { recursive: true });' 'function load() {' ' try { return JSON.parse(fs.readFileSync(CHECK, \"utf8\")); } catch (error) { return { done: [], started: null }; }' '}' 'function save(state) { fs.writeFileSync(CHECK, JSON.stringify(state)); }' 'function charge(id) {' ' const seen = fs.existsSync(LOG) ? fs.readFileSync(LOG, \"utf8\").split(\"\n\") : [];' ' if (seen.indexOf(id) >= 0) { console.log(id + \": ya cobrado, no se cobra otra vez\"); return; }' ' fs.appendFileSync(LOG, id + \"\n\");' '}' > batch1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el trabajo reanudable, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const state = load();' 'console.log(\"Hechos hasta ahora: \" + (state.done.join(\",\") || \"ninguno\") + \". Interrumpido en: \" + state.started);' 'for (const id of orders) {' ' if (state.done.indexOf(id) >= 0) { console.log(id + \": saltado, ya hecho\"); continue; }' ' state.started = id;' ' save(state);' ' charge(id);' ' if (id === crashAt) { console.log(\"Fallo mientras se trabajaba en \" + id); process.exit(1); }' ' state.done.push(id);' ' state.started = null;' ' save(state);' ' console.log(id + \": hecho\");' '}' 'console.log(\"Terminado. Cobros: \" + fs.readFileSync(LOG, \"utf8\").trim().split(\"\n\").length);' >> batch1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecutar desde cero y fallar en P3
docker exec -w /home/node/project agent sh -c "rm -rf demo; node batch1.js P3"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecutar otra vez y reanudar
docker exec -w /home/node/project agent node batch1.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Ejecución | Qué muestra |
|---|---|
| Primera | P1 hecho, P2 hecho, luego Fallo mientras se trabajaba en P3 |
| Segunda | Interrumpido en P3, P1 y P2 saltados |
| P3 en la segunda | ya cobrado, no se cobra otra vez, y luego hecho |
| Última línea | Terminado. Cobros: 5, uno por pedido |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Haz cada paso seguro de repetir
Un paso es idempotente cuando ejecutarlo dos veces tiene el efecto de ejecutarlo una. Da a cada efecto una clave, como el número de pedido. Comprueba si la clave ya se usó antes de actuar.
Cuando un servicio externo lo permite, envía la clave con la petición para que el servicio pueda rechazar una repetición.
Un error frecuente
Advertencia
Mucha gente cree que un punto de control hace seguro un reintento. Un punto de control solo registra lo que terminó. Un paso cortado tras su efecto se vuelve a ejecutar, así que el paso debe ser seguro de repetir. Usa los dos: un registro y un paso idempotente.