Inicio / Varios agentes y verificación adversa
Exitoso, fallido, interrumpido, sin rastro
Cuando un corte detiene una cola de tareas, un registro llevado fuera del proceso, escrito antes y después de cada tarea, separa cuatro estados: exitoso, fallido, interrumpido a mitad de camino y sin rastro de intento; confundirlos hace relanzar lo que puede haber actuado ya, o declarar fallido lo que nadie ha examinado.
Tomemos un caso típico. Una cadena de verificaciones procesa una cola de tareas una tras otra. Un tope de uso cae en mitad de la cola y detiene el proceso supervisor con los agentes que dirigía. Al reiniciar, el supervisor marca como fallidas todas las tareas sin resultado, incluidas aquellas que ningún agente había empezado.
Cuatro estados, porque un corte crea dos
Un sistema que solo conoce exitoso y fallido mete en fallido dos casos que produce el corte. La tarea interrumpida ha empezado: ha podido actuar en parte, y se inspecciona antes de relanzarla. La tarea sin rastro se relanza, una vez verificado que ningún otro proceso la ha tomado. Escribir la línea «en curso» antes de empezar es lo que las separa: sin ella, una tarea interrumpida se parece a una tarea nunca abordada.
La ausencia de línea establece una cosa acotada, como recuerda la lección sobre lo que una verificación rigurosa no ve: este registro no lleva ningún rastro de intento, y no dice nada de otro proceso que haya podido tomar la tarea.
La demostración
Los dos scripts se crean en una carpeta desechable. El primero escribe seis tareas en cola.json y luego simula un corte durante la cuarta. El segundo confronta esa lista de partida con el registro: es ella la que da a conocer las tareas sin rastro.
mkdir -p demo-cola && cd demo-cola
cat > procesar-cola.js << 'EOF'
// Fabrica la cola de partida y luego la procesa registrando antes y después de cada tarea.
const fs = require('fs');
const cola = ['tarea-1', 'tarea-2', 'tarea-3', 'tarea-4', 'tarea-5', 'tarea-6'];
fs.writeFileSync('cola.json', JSON.stringify(cola));
fs.writeFileSync('registro.jsonl', '');
const resultados = { 'tarea-1': 'exitoso', 'tarea-2': 'fallido', 'tarea-3': 'exitoso' };
for (const id of cola) {
fs.appendFileSync('registro.jsonl', JSON.stringify({ id, estado: 'en curso' }) + '\n');
if (id === 'tarea-4') process.exit(1); // corte simulado durante la cuarta tarea
fs.appendFileSync('registro.jsonl', JSON.stringify({ id, estado: resultados[id] }) + '\n');
}
EOF
cat > leer-registro.js << 'EOF'
// Relee la cola de partida y el registro, y clasifica cada tarea en cuatro estados.
const fs = require('fs');
const cola = JSON.parse(fs.readFileSync('cola.json', 'utf8'));
const ultimo = {};
for (const linea of fs.readFileSync('registro.jsonl', 'utf8').split('\n')) {
if (linea) { const e = JSON.parse(linea); ultimo[e.id] = e.estado; }
}
const clases = { 'exitoso': [], 'fallido': [], 'interrumpido': [], 'sin rastro': [] };
for (const id of cola) {
const estado = ultimo[id];
if (estado === undefined) clases['sin rastro'].push(id);
else if (estado === 'en curso') clases['interrumpido'].push(id);
else clases[estado].push(id);
}
for (const [nombre, ids] of Object.entries(clases)) console.log(nombre + ' : ' + (ids.join(', ') || '-'));
EOF
node procesar-cola.js
echo "código de salida : $?"
cat registro.jsonl
node leer-registro.js
código de salida : 1
{"id":"tarea-1","estado":"en curso"}
{"id":"tarea-1","estado":"exitoso"}
{"id":"tarea-2","estado":"en curso"}
{"id":"tarea-2","estado":"fallido"}
{"id":"tarea-3","estado":"en curso"}
{"id":"tarea-3","estado":"exitoso"}
{"id":"tarea-4","estado":"en curso"}
exitoso : tarea-1, tarea-3
fallido : tarea-2
interrumpido : tarea-4
sin rastro : tarea-5, tarea-6
El código de salida 1 señala la parada anormal. La tarea 4 lleva «en curso» sin resultado: interrumpida, no fallida.
Un rastro que sobrevive al proceso
Un balance guardado en la memoria del supervisor desaparece con él. El registro vive, por tanto, fuera del proceso: un archivo como aquí, una tabla de base de datos o una cola de mensajes externa también sirven. appendFileSync termina la escritura de la línea antes de pasar a lo siguiente: si el proceso se mata, las líneas ya escritas permanecen en el archivo. Un corte de corriente es otro caso, porque el sistema puede guardar las últimas líneas en memoria antes de ponerlas en el disco; una sincronización explícita, fs.fsyncSync en Node, fuerza esa escritura.
La lección sobre los tres estados de fallo separa, entre las tareas que han devuelto un resultado, el fallo recuperable que se reintentará y el fallo definitivo que sube a una persona; esta trata las tareas que el corte ha dejado sin resultado.
Lo que el registro anota, tarea por tarea
Cuatro estados, cuatro gestos
| Estado | Rastro en el registro | Gesto siguiente |
|---|---|---|
| Exitoso | «en curso» y luego «exitoso» | Ninguno |
| Fallido | «en curso» y luego «fallido» | Tratar según el tipo de fallo |
| Interrumpido | «en curso» y nada después | Inspeccionar los efectos parciales y luego relanzar |
| Sin rastro | Ninguna línea, tarea presente en la lista de partida | Verificar que ningún otro proceso la ha tomado y luego relanzar |
Un proceso recorre una cola de seis tareas listadas en un archivo de partida. Escribe en un registro en disco una línea «en curso» antes de cada tarea y una línea de resultado después. Un tope de uso detiene el proceso. Releído tras la parada, el registro contiene para la tarea 1 «en curso» y luego «exitoso», para la tarea 2 «en curso» y luego «fallido», para la tarea 3 «en curso» y luego «exitoso», y para la tarea 4 «en curso». Las tareas 5 y 6 figuran en el archivo de partida.
Escribe en una frase lo que esta situación establece, y en una frase lo que no establece.
Lo que esto establece: Establece que las tareas 1 y 3 tuvieron éxito, que la tarea 2 falló, que la tarea 4 empezó y no tiene resultado registrado, y que el registro no lleva ningún rastro de intento para las tareas 5 y 6.
Lo que esto no establece: No establece que la tarea 4 quedara sin efecto, puesto que pudo actuar en parte antes de la parada, ni que ningún otro proceso haya tratado las tareas 5 y 6: el registro solo describe este proceso.
Los tres calibrados falsos más frecuentes
- Demasiado amplio Las tareas 4, 5 y 6 fallaron a causa del tope de uso y se relanzan las tres de la misma manera.
- Demasiado estrecho El registro establece el estado de las tareas 1 a 3; para las tareas 4, 5 y 6, no permite distinguir nada.
- Fuera de tema La situación establece que el tope de uso estaba fijado demasiado bajo para el tamaño de esta cola.
- Una tarea interrumpida ha empezado y ha podido actuar en parte, por lo que se inspecciona antes de relanzarla.
- Una tarea sin rastro se reconoce confrontando la lista de partida con el registro, y no con el registro solo.
- La línea escrita antes de empezar es lo que separa una tarea interrumpida de una tarea que el proceso no ha abordado.
- Un registro útil vive fuera del proceso que lo escribe: archivo, base de datos o cola de mensajes externa.
Toma un script que procese una lista de elementos, haz que escriba una línea «en curso» antes de cada elemento y una línea de resultado después, deténlo a mano en pleno medio y comprueba después que recuperas los cuatro estados confrontando su lista de partida con el registro.