Introduction
Un ticket ne doit pas être clôturé tant que sa note de résolution n’est pas enregistrée. Vous utiliserez un lot préparé D1 pour regrouper ces écritures, puis vous transmettrez un repère de l’API Sessions entre les requêtes afin que les lectures ultérieures puissent voir les modifications déjà validées.
Cet exercice distingue l’annulation atomique de la cohérence séquentielle des sessions. Il utilise une base de données et un Worker indépendants, sans nécessiter l’activation de réplicas en lecture ni la reproduction d’une course de réplication.
Utilisez votre propre compte d’apprentissage et une VM neuve. La configuration prépare d’abord Node.js 22.22.0, puis exécute npm install pour installer Wrangler 4.131.1 au niveau du projet ainsi que les dépendances d’évaluation dans /home/labex/project/ticket-database. Les versions des dépendances directes sont figées ; l’installation crée son propre fichier de verrouillage. La configuration ne nécessite aucune connexion au cloud et n’exécute aucune opération évaluée sur la base de données. Sur une machine personnelle, installez la même version de Wrangler avec npm install --save-dev wrangler@4.131.1 dans votre projet.
Cet exercice utilise de petits enregistrements synthétiques compris dans les allocations gratuites de D1. L’utilisation existante de votre compte est comptabilisée dans ces allocations. Aucun domaine acheté n’est nécessaire. Conservez cette VM jusqu’à ce que la suppression des ressources et la déconnexion aient toutes deux été vérifiées.
Autoriser cette VM et sélectionner le compte
Dans cette étape, connectez ce terminal neuf à votre propre compte d’apprentissage. Une simple connexion au Dashboard n’autorise pas la VM. L’autorisation D1 permet de créer, modifier et supprimer des bases de données ; l’autorisation Workers permet le déploiement ; l’autorisation KV permet à Wrangler d’inventorier les ressources lors du nettoyage. Consultez attentivement la page de consentement réelle, notamment la section Background Access, avant d’autoriser l’accès.
Ouvrez le projet préparé et vérifiez la version de l’interface en ligne de commande :
cd /home/labex/project/ticket-database
npx wrangler --version
La version attendue est 4.131.1. Lancez l’autorisation par appareil ; --device affiche un code à saisir dans le navigateur et --browser=false vous laisse choisir vous-même l’ouverture du navigateur :
npx wrangler login --device --browser=false --scopes account:read user:read d1:write workers_scripts:write workers_kv:write
Ouvrez l’URL affichée dans votre navigateur, saisissez le code actuel, vérifiez votre compte d’apprentissage et les autorisations, puis autorisez l’accès. Attendez que le terminal confirme la réussite. Ne collez jamais de mots de passe ou de jetons dans les fichiers du projet.
npx wrangler whoami --json
Vérifiez loggedIn: true, puis lisez le name et l’id du compte, même si un seul compte est répertorié. Copiez l’ID voulu dans la configuration ci-dessous. La variable shell suivante utilise 6 octets aléatoires, soit 12 caractères hexadécimaux, afin d’éviter les collisions entre apprenants. Un document here-document écrit le JSON entre les lignes JSON ; $RUN est développé à l’intérieur.
La barre oblique inverse devant $schema conserve cette clé JSON littérale ; $RUN est toujours remplacé par le nom unique de cette exécution.
RUN=labex-c04-d05-$(openssl rand -hex 6)
cat > wrangler.jsonc <<JSON
{
"\$schema": "./node_modules/wrangler/config-schema.json",
"name": "$RUN",
"account_id": "YOUR_ACCOUNT_ID",
"main": "src/index.js",
"compatibility_date": "2026-09-15",
"workers_dev": true,
"preview_urls": false
}
JSON
Remplacez YOUR_ACCOUNT_ID avant d’exécuter ce bloc. Laissez ce terminal ouvert afin que RUN reste disponible. name identifie cette exécution ; account_id sélectionne le compte utilisé pour les opérations cloud. Le fichier est un fichier JSON ordinaire, également valide en JSONC. Sa création ne déploie aucun Worker.
Préparer les tickets et les notes de résolution
Dans cette étape, vous préparez deux tables liées. La clôture d’un ticket doit également enregistrer sa note de résolution. Si une seule écriture réussit, le personnel peut voir un ticket clôturé sans explication. La configuration fournit le schéma et le routeur HTTP ; vous allez implémenter les opérations SQL associées.
Créez une base de données cloud temporaire. --binding DB fournit au code de l’application un nom court, --update-config enregistre son nom réel et son UUID dans wrangler.jsonc, et --use-remote=false conserve le développement en local :
npx wrangler d1 create "$RUN-db" --binding DB --update-config --use-remote=false
Lisez le nom et l’ID de la base créée, puis inspectez la liaison enregistrée :
cat wrangler.jsonc
L’entrée DB doit désigner la base de données de cette exécution. Une liaison est une connexion configurée entre le code et une ressource. Son UUID identifie la base de données cloud, tandis que --local utilise une base SQLite distincte dans cette VM. Incluez toujours --local ou --remote dans les commandes SQL.
cat schema.sql
npx wrangler d1 execute DB --local --file schema.sql
npx wrangler d1 execute DB --remote --file schema.sql
Le ticket 1 est ouvert et ne possède aucune résolution. Le ticket 2 est clôturé et possède l’événement de résolution 1. Cet ID d’événement déjà utilisé fournit un cas d’échec contrôlé : l’insertion d’un autre événement 1 doit violer la clé primaire.
Garantir l’atomicité des écritures et la séquentialité des lectures
Dans cette étape, vous résolvez deux problèmes de cohérence distincts. L’atomicité signifie que les deux écritures liées réussissent ensemble ou échouent toutes les deux. D1 batch() exécute les instructions préparées dans le cadre d’une transaction : en cas d’échec, tout le lot est annulé. Deux écritures attendues séparément ne fournissent pas cette garantie.
Une session suit l’état de la base de données observé par une séquence de requêtes. Le routeur fourni appelle env.DB.withSession(...), en commençant par first-primary lorsqu’aucun repère n’est fourni par le client. Il renvoie le résultat de getBookmark() dans l’en-tête x-d1-bookmark. Une requête ultérieure peut envoyer ce repère pour continuer à partir d’un état au moins aussi récent de la base. Il s’agit d’une cohérence séquentielle, et non d’une transaction tout-ou-rien entre plusieurs requêtes HTTP.
Implémentez les deux fonctions en utilisant la session transmise par le routeur :
cat > src/store.js <<'JS'
export async function closeTicket(session, id, eventId, note) {
await session.batch([
session.prepare("UPDATE tickets SET status = 'closed' WHERE id = ?").bind(id),
session.prepare('INSERT INTO resolutions(event_id, ticket_id, note) VALUES (?, ?, ?)').bind(eventId, id, note)
]);
}
export async function readTicket(session, id) {
const ticket = await session.prepare('SELECT id, subject, status FROM tickets WHERE id = ?').bind(id).first();
if (!ticket) return null;
const { results } = await session.prepare('SELECT event_id, note FROM resolutions WHERE ticket_id = ? ORDER BY event_id').bind(id).all();
return { ...ticket, resolutions: results };
}
JS
Lisez src/index.js pour repérer withSession, l’en-tête contenant le repère entrant et le repère renvoyé. Toutes les opérations de base de données de la requête utilisent cette session. Un repère est une position opaque : renvoyez-le tel quel au lieu d’essayer de l’analyser ou d’en inventer un.
cat src/index.js
npx wrangler dev --ip 0.0.0.0 > dev.log 2>&1 &
cat dev.log
Attendez le message indiquant que le service écoute en local. La simulation locale permet de tester l’annulation d’un lot, mais elle ne démontre ni une réplication distante réelle ni un repère cloud.
Observer l’annulation avant une clôture réussie
Dans cette étape, envoyez volontairement l’ID d’événement déjà utilisé. La première instruction du lot tente de clôturer le ticket 1, mais la seconde échoue. Lisez le résultat après l’échec :
curl -i http://localhost:8787/tickets/1/close -H 'Content-Type: application/json' -d '{"event_id":1,"note":"Must roll back"}'
curl -i http://localhost:8787/tickets/1
Attendez-vous à recevoir 409 event_conflict, puis à voir le ticket 1 toujours open avec un tableau resolutions vide. Le code 409 seul ne suffit pas : la lecture suivante prouve qu’aucune mise à jour partielle n’a été conservée.
Utilisez maintenant l’ID d’événement libre 2 :
curl -i http://localhost:8787/tickets/1/close -H 'Content-Type: application/json' -d '{"event_id":2,"note":"Access restored"}'
curl -i http://localhost:8787/tickets/1
Vous devez obtenir HTTP 200, le ticket 1 à l’état closed et l’événement de résolution 2 avec la note Access restored. Les deux enregistrements concordent désormais. Ne réinitialisez pas les données locales et ne modifiez pas la base distante pour provoquer artificiellement un retard de réplication.
Poursuivre une session distante avec son repère
Dans cette étape, vous exécutez le même lot sur D1 et transmettez un repère réel entre les requêtes. Les données de test distantes sont toujours dans leur état initial.
npx wrangler deploy
Copiez l’URL réelle de votre déploiement. Répétez d’abord le lot en échec et vérifiez l’annulation :
URL='YOUR_DEPLOYED_HTTPS_URL'
curl -i "$URL/tickets/1/close" -H 'Content-Type: application/json' -d '{"event_id":1,"note":"Must roll back"}'
curl -i "$URL/tickets/1"
Attendez-vous à recevoir 409, puis à voir un ticket ouvert sans résolution. Si le déploiement est encore en cours de propagation, réessayez la lecture pendant une minute au maximum ; ne confondez pas une page d’erreur de la plateforme avec le contrat JSON de l’application.
Envoyez la clôture réussie :
curl -i "$URL/tickets/1/close" -H 'Content-Type: application/json' -d '{"event_id":2,"note":"Access restored"}'
Vous devez voir le ticket clôturé et sa note. Copiez l’en-tête de réponse x-d1-bookmark non vide, sans espace supplémentaire, dans la variable suivante :
BOOKMARK='YOUR_RESPONSE_BOOKMARK'
curl -i "$URL/tickets/1" -H "x-d1-bookmark: $BOOKMARK"
La requête suivante doit observer le ticket clôturé et l’événement de résolution 2. Un repère limite l’ancienneté maximale d’une lecture ; ce n’est pas un jeton d’authentification. Ce flux fonctionne sans nécessiter une lecture obsolète ni l’activation de la réplication en lecture. Nous vérifions le contrat de session, sans affirmer qu’une course entre réplicas s’est produite.
Ouvrez le Worker exact dans le Dashboard et vérifiez que sa liaison DB pointe vers la base de données de cette exécution. Terminez la vérification fonctionnelle avant le nettoyage.

Cet exemple montre la liaison DB du Worker vers sa base D1. Le préfixe généré identifie cette exécution d’exemple ; vos noms seront différents. La capture confirme uniquement la liaison. Les vérifications HTTP ci-dessus établissent l’annulation atomique, la mise à jour réussie et la poursuite de la lecture avec le signet.
Supprimer les ressources temporaires
Dans cette étape, supprimez uniquement les ressources de cet exercice pendant que la VM est encore autorisée. Terminez d’abord toutes les vérifications fonctionnelles. Conservez la configuration jusqu’à la fin de la vérification de suppression.
npx wrangler delete
Vérifiez que seul le nom du Worker de la configuration de cette exécution est concerné.
npx wrangler d1 delete DB
Examinez l’invite et confirmez qu’il s’agit uniquement de la base de données de cette exécution. Répertoriez ensuite les bases de données :
npx wrangler d1 list --json
Le nom et l’UUID de la base que vous avez enregistrés doivent être absents d’une réponse réussie. D’autres ressources peuvent rester présentes. Une erreur d’authentification ou de réseau ne permet pas de conclure : rétablissez l’accès et répétez la lecture avant de continuer. Effectuez la vérification de cette étape tant que vous êtes encore connecté.
Arrêtez également le processus de développement local. Répertoriez les tâches et terminez uniquement la tâche wrangler dev que vous avez lancée ; remplacez %1 si son numéro de tâche est différent :
jobs
kill %1
Mettre fin à l’autorisation de cette VM
Dans cette étape, mettez fin à l’autorisation uniquement après la réussite de la vérification indépendante de la suppression. La déconnexion supprime l’autorisation Wrangler stockée sur cette VM ; fermer simplement une VM ne constitue pas un nettoyage cloud.
npx wrangler logout
npx wrangler whoami --json
Vous devez obtenir loggedIn: false. Cette requête non authentifiée peut se terminer avec un code différent de zéro ; cela est attendu uniquement si la réponse structurée indique explicitement que vous êtes déconnecté. Terminez la vérification, puis fermez l’environnement de laboratoire.
Résumé
Vous avez appris à maintenir la cohérence de mises à jour liées à des tickets. Vous avez vérifié les résultats observables dans la base de données, gardé explicites le compte sélectionné et l’état local, puis supprimé les ressources temporaires avant de vous déconnecter.



