Étude de cas · En production

Attribution concurrente d'identifiants

Plusieurs opérateurs créent des fiches en même temps, depuis des postes différents. Chacune doit recevoir un numéro unique et consécutif. Sans base de données, sans serveur de transactions : seulement des fichiers et un verrou.

Rôle
Conception, développement et déploiement — seul
Durée
Juillet 2026 — en service depuis
Volume
1 966 lignes (PHP, Python, JavaScript)
Données
3 943 fiches synchronisées depuis l'ERP
Stack
PHP 8 · Python 3 · JavaScript · SMTP
Cœur du sujet
Exclusion mutuelle et libération automatique des ressources

Le problème

Un « moyen de programme » est identifié par un numéro séquentiel. Quand un opérateur crée une fiche, l'application doit lui attribuer le prochain numéro libre. Les fiches existantes viennent de l'ERP ; les nouvelles n'y remontent qu'après validation, avec plusieurs heures de décalage.

Le numéro doit donc être réservé pendant que l'opérateur remplit le formulaire — sinon deux personnes qui ouvrent le formulaire en même temps repartent avec le même numéro, et la collision n'est découverte qu'à l'intégration dans l'ERP, bien trop tard pour être corrigée simplement.

La condition de course

L'approche naïve — lire le dernier numéro, ajouter un — échoue dès que deux requêtes s'intercalent. C'est le scénario que le système doit rendre impossible :

Condition de course sans verrou Deux postes lisent le dernier numéro utilisé au même instant, obtiennent la même valeur, et créent tous deux la fiche numéro 3944. La collision n'est détectée qu'à l'intégration dans l'ERP. POSTE A POSTE B FICHIER lit → 3943 lit → 3943 les deux postes ont la même valeur crée 3944 crée 3944 collision
Sans réservation, la collision n'apparaît qu'à l'intégration — plusieurs heures plus tard.

La solution : des baux à expiration

À l'ouverture du formulaire, le serveur ne se contente pas de calculer le prochain numéro : il le réserve dans un fichier de verrous, avec une date d'expiration à 15 secondes. Tant que ce bail est valide, aucun autre poste ne peut obtenir ce numéro.

La sélection parcourt les numéros croissants et retient le premier qui n'est ni déjà utilisé dans l'ERP, ni sous bail actif. L'opération complète — nettoyage des baux expirés, recherche, pose du nouveau bail — s'exécute à l'intérieur d'un unique verrou exclusif sur le fichier. Deux requêtes simultanées sont donc sérialisées : la seconde voit la réservation de la première.

Cycle de vie d'un bail Le bail est posé à l'ouverture du formulaire pour quinze secondes, renouvelé tant que le formulaire reste ouvert, puis libéré immédiatement à la validation ou à l'annulation. S'il n'est ni renouvelé ni libéré, il expire de lui-même. Ouverture bail = T + 15 s Formulaire actif bail renouvelé Validation bail libéré Annulation bail libéré Onglet fermé bail expire seul Numéro libéré réutilisable heartbeat
Un bail non libéré expire de lui-même : aucun numéro ne peut rester bloqué indéfiniment.
Liste des programmes synchronisés depuis l'ERP, avec recherche, bouton de création et onglet des fiches en attente d'intégration.
Onglet « Base ERP » et onglet « En attente d'intégration ». Données anonymisées pour cette capture.

Décisions techniques

Un bail à expiration, plutôt qu'un verrou explicite

Verrou libéré par le client

Un verrou que seul le client peut libérer est un verrou qui finit bloqué : onglet fermé, navigateur planté, poste éteint, coupure réseau. Le numéro serait alors perdu définitivement.

Avec un bail daté, l'absence de nouvelle est un cas normal, pas une panne. Le nettoyage n'a besoin d'aucune tâche de fond : toute requête qui consulte le fichier purge au passage les baux périmés. Le système se répare tout seul.

15 secondes, renouvelées tant que le formulaire est ouvert

Bail long (5 minutes)

La durée arbitre entre deux gênes. Trop court, le numéro est repris sous les doigts d'un opérateur qui réfléchit. Trop long, un formulaire abandonné gèle un numéro pendant des minutes, et l'opérateur suivant voit un trou inexpliqué dans la numérotation.

15 secondes avec renouvellement périodique donne le meilleur des deux : un formulaire réellement ouvert conserve son numéro indéfiniment, un formulaire abandonné le rend en quelques secondes.

Toute écriture passe par une fonction de mutation sous verrou

Lire, modifier, réécrire à chaque appel

Le fichier de verrous est modifié depuis cinq endroits différents. Chacun appelle le même helper, qui prend une fonction transformant l'état et garantit que lecture et écriture ont lieu dans le même verrou. Sans cette factorisation, il suffirait qu'un seul appelant relise le fichier hors du verrou pour rouvrir la condition de course — et ce serait invisible en relecture de code.

Difficultés rencontrées

Le bail peut expirer pendant la saisie

Un opérateur peut laisser le formulaire ouvert et partir. À la validation, le bail posé à l'ouverture a pu expirer — et être repris par quelqu'un d'autre. Valider aveuglément réintroduirait exactement la collision que le système est censé empêcher.

La validation revérifie donc la disponibilité du numéro au moment de l'écriture, sous verrou, plutôt que de faire confiance à la réservation initiale. La réservation est une optimisation du confort ; la garantie, elle, est posée à l'écriture.

Libérer le numéro quand l'utilisateur renonce

Fermer le formulaire sans valider devait rendre le numéro immédiatement, et non attendre l'expiration : sinon un opérateur qui hésite crée des trous visibles dans la numérotation. Chaque chemin de sortie — bouton « Annuler », clic en dehors, touche d'échappement — déclenche une libération explicite. L'expiration reste le filet de sécurité pour les cas qu'on ne contrôle pas.

Résultats

3 943

fiches synchronisées depuis l'ERP

0

collision de numéro constatée

15 s

durée d'un bail, renouvelable

0

serveur de base de données

L'application tourne depuis juillet 2026 sur le réseau de l'atelier. Chaque création déclenche un e-mail au responsable, dont chaque étape — connexion, STARTTLS, authentification, envoi — est tracée dans un journal quotidien : en cas d'échec, on sait immédiatement où ça a cassé, sans reproduire le problème.

Ce que je referais autrement

Le renouvellement du bail dépend du navigateur. Si l'onglet est mis en arrière-plan, le navigateur peut ralentir les minuteurs et laisser le bail expirer alors que l'utilisateur est toujours là. La revérification à l'écriture évite la collision, mais l'opérateur peut voir son numéro changer. Utiliser l'API de visibilité de page pour reprendre le renouvellement au retour au premier plan corrigerait ce cas.

Aucune authentification. C'est un choix assumé — le service est confiné au réseau local et la règle de pare-feu est limitée au profil privé — mais cela signifie qu'aucune action n'est attribuable à une personne. Pour un outil qui déclenche des e-mails au nom de l'entreprise, une identification même minimale serait plus saine.