Un client prélevé deux fois
Un point de reprise est une trace du travail déjà fini. Grâce à lui, une longue tâche peut s’arrêter puis repartir sans tout refaire. Les agents tournent des minutes ou des heures, et les processus meurent.
Une tâche a prélevé cinq commandes. Elle a planté à la troisième et repris tout au début. Deux clients ont payé deux fois.
À la fin de cette leçon
Remarque
Je peux rendre un long travail sûr à arrêter et à reprendre.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Point de reprise | une trace enregistrée du travail fini | |
| Idempotent | sûr à lancer deux fois, pour un seul effet | |
| Reprendre | continuer depuis le dernier point de reprise | |
| Effet de bord | un changement hors du programme | un prélèvement |
| Journal d’événements | la liste de ce qui s’est passé, dans l’ordre |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un point de reprise note l’avancement
Après chaque étape, écrivez son nom dans un fichier ou une table. Au démarrage, lisez cette trace et sautez ce qui est déjà fini.
Écrivez une entrée début avant une étape et une entrée fini après. Un redémarrage distingue alors une étape jamais commencée d’une étape coupée en route.
Exemple travaillé : où une panne peut tomber
| La panne tombe | La trace dit | À la reprise |
|---|---|---|
| Avant le début de l’étape | Rien à son sujet | La lancer |
| Dans l’étape, avant son effet | Début | La réessayer |
| Après son effet, avant fini | Début | La réessayer, donc l’effet se répète |
| Après fini | Fini | La sauter |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Écrire la tâche reprenable, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const REPRISE = \"demo/reprise.json\";' 'const JOURNAL = \"demo/prelevements.log\";' 'const commandes = [\"C1\", \"C2\", \"C3\", \"C4\", \"C5\"];' 'const panneA = process.argv[2];' 'fs.mkdirSync(\"demo\", { recursive: true });' 'function charger() {' ' try { return JSON.parse(fs.readFileSync(REPRISE, \"utf8\")); } catch (erreur) { return { finies: [], commencee: null }; }' '}' 'function sauver(etat) { fs.writeFileSync(REPRISE, JSON.stringify(etat)); }' 'function prelever(id) {' ' const vus = fs.existsSync(JOURNAL) ? fs.readFileSync(JOURNAL, \"utf8\").split(\"\n\") : [];' ' if (vus.indexOf(id) >= 0) { console.log(id + \" : déjà prélevée, pas de second prélèvement\"); return; }' ' fs.appendFileSync(JOURNAL, id + \"\n\");' '}' > lot1.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire la tâche reprenable, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const etat = charger();' 'console.log(\"Déjà finies : \" + (etat.finies.join(\",\") || \"aucune\") + \". Interrompue à : \" + etat.commencee);' 'for (const id of commandes) {' ' if (etat.finies.indexOf(id) >= 0) { console.log(id + \" : sautée, déjà finie\"); continue; }' ' etat.commencee = id;' ' sauver(etat);' ' prelever(id);' ' if (id === panneA) { console.log(\"Panne pendant le travail sur \" + id); process.exit(1); }' ' etat.finies.push(id);' ' etat.commencee = null;' ' sauver(etat);' ' console.log(id + \" : finie\");' '}' 'console.log(\"Terminé. Prélèvements : \" + fs.readFileSync(JOURNAL, \"utf8\").trim().split(\"\n\").length);' >> lot1.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer à neuf et planter à C3
docker exec -w /home/node/project agent sh -c "rm -rf demo; node lot1.js C3"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Relancer et reprendre
docker exec -w /home/node/project agent node lot1.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une sortie possible
| Run | Ce qu’il montre |
|---|---|
| Premier | C1 finie, C2 finie, puis Panne pendant le travail sur C3 |
| Second | Interrompue à C3, C1 et C2 sautées |
| C3 au second run | déjà prélevée, pas de second prélèvement, puis finie |
| Dernière ligne | Terminé. Prélèvements : 5, un par commande |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Rendre chaque étape sûre à répéter
Une étape est idempotente quand la lancer deux fois a l’effet de la lancer une fois. Donnez une clé à chaque effet, par exemple le numéro de commande. Vérifiez si la clé a déjà servi avant d’agir.
Quand un service extérieur le permet, envoyez la clé avec la demande : le service peut alors refuser une répétition.
Une erreur fréquente
Attention
Beaucoup pensent qu’un point de reprise rend un nouvel essai sûr. Un point de reprise n’enregistre que ce qui est fini. Une étape coupée après son effet est relancée, donc l’étape doit pouvoir se répéter sans danger. Utilisez les deux : une trace et une étape idempotente.