Le temps est une dépendance. Traitez-le comme telle, sinon il vous traitera comme un amateur. Si vous avez déjà couru après un pic de latence “aléatoire”,
une erreur TLS intermittente ou une bascule de base de données qui “n’aurait pas dû arriver”, vous avez déjà rencontré la dérive du temps sur le parking après le travail.
En 1991, une toute petite erreur de temps dans le système Patriot s’est accumulée pendant une longue période de fonctionnement et a contribué à l’échec d’interception d’un missile entrant.
En termes de production : le système a tourné trop longtemps, le budget d’erreur s’est épuisé, et la réalité s’est fichée que les calculs soient “assez proches”.
Ce qui s’est passé (et pourquoi les « petites erreurs » ne sont pas petites)
Le système Patriot suit les cibles en prédisant où elles seront, pas seulement où elles sont. Cette prédiction dépend du temps.
Si votre estimation temporelle est fausse, votre position prédite est fausse. Si la cible va assez vite, « faux » devient « manqué ».
Le schéma d’échec est douloureusement familier à quiconque gère des systèmes longue durée :
un calcul implique une constante arrondie ; l’erreur d’arrondi est minuscule ; le système est conçu en pensant à une durée de fonctionnement typique ;
puis les opérations prolongent ce fonctionnement parce que l’environnement l’exige ; l’erreur s’accumule ; un seuil bascule ; le système se comporte mal au pire moment.
Voici la vérité opérationnelle inconfortable : les bugs qui dépendent d’un long uptime ne sont pas « rares ». Ils sont « programmés ».
L’horloge compte littéralement les secondes avant votre panne.
Une citation qui mérite sa place sur un mur d’incident
« L’espoir n’est pas une stratégie. » — idée paraphrasée, souvent citée dans les cercles fiabilité/ops
Si vous concevez ou exploitez des systèmes qui doivent tenir en cas de stress, traitez le temps comme un sous-système critique.
Mesurez-le. Surveillez-le. Budgétez ses modes de défaillance. Et ne supposez jamais que « l’horloge va bien » sans preuve.
Faits historiques importants pour les ingénieurs
- Le Patriot était initialement conçu pour l’aviation, puis adapté à la défense antimissile balistique. L’environnement opérationnel a changé plus vite que la culture logicielle.
- L’échec s’est produit en 1991 pendant la guerre du Golfe, en conditions de combat réel avec des opérations prolongées et des enjeux élevés.
- Le problème clé impliquait une conversion de temps : convertir un compteur (ticks) en secondes en utilisant de l’arithmétique en point fixe et une constante arrondie.
- L’erreur d’arrondi était minuscule par conversion, mais elle s’est accumulée avec le temps de fonctionnement. De petites erreurs par événement deviennent grandes sur de longues périodes.
- Le suivi utilisait la prédiction, ce qui fait que l’erreur de temps se transforme en erreur de position. La prédiction amplifie les erreurs temporelles.
- Un fonctionnement continu plus long que prévu a fait dépasser la dérive un seuil tolérable. Le système fonctionnait « bien » jusqu’à ce que ce ne soit plus le cas.
- Une mise à jour logicielle existait apparemment pour atténuer le problème, mais déployer des changements en temps de guerre est difficile, lent et parfois politiquement délicat.
- L’incident est devenu un cas d’école utilisé dans les cours d’ingénierie logicielle sur la précision numérique, la dérive des exigences et les hypothèses opérationnelles.
Remarquez combien de ces faits ne sont pas des « faits mathématiques ». La plupart sont des faits opérationnels :
pour quoi c’était conçu, comment ça a été utilisé et combien de temps ça a tourné. C’est le thème.
Mécanique du bug : temps en point fixe, arrondis et accumulation de dérive
Parlons mécanique sans transformer ceci en séminaire d’analyse numérique.
Le système Patriot utilisait une horloge interne qui comptait des dixièmes de seconde (ou une unité de tick similaire ; l’essentiel est : un compteur, pas une horloge flottante).
Pour prédire la position de la cible, le logiciel avait besoin du temps en secondes.
Convertir des ticks en secondes est conceptuellement simple :
- ticks = compteur entier
- seconds = ticks × 0.1
Le piège est la façon dont vous représentez 0.1 dans un ordinateur qui préfère le binaire. En binaire, beaucoup de fractions décimales sont des fractions répétées.
0.1 ne peut pas être représenté exactement avec un nombre fini de chiffres binaires. Vous l’approximez donc.
Arithmétique en point fixe : le compromis des ingénieurs embarqués
Dans les systèmes contraints (historiquement surtout), le point flottant pouvait être coûteux ou indisponible, donc les ingénieurs utilisent le point fixe :
représenter les réels comme des entiers avec une échelle implicite. Exemple : stocker des secondes en unités de 2^-N, ou stocker « 0.1 » comme un ratio entier.
Ce compromis a une facture : vous devez choisir combien de bits de précision vous conservez, et quand vous arrondissez.
Arrondir une fois va ; arrondir répétitivement dans une boucle qui tourne pendant des heures est un incident au ralenti.
Forme spécifique de l’échec : « erreur par tick » × « ticks depuis le démarrage »
La dérive se comporte ainsi :
- Vous approximez un facteur de conversion (comme 0.1 seconde par tick) avec une précision limitée.
- Chaque conversion introduit une petite erreur (souvent une fraction de tick).
- Sur de nombreux ticks, cette erreur fractionnaire s’accumule en un décalage temporel mesurable.
- Le décalage temporel devient un décalage de position via la vitesse : position_error ≈ velocity × time_error.
Cette dernière ligne est celle qui devrait vous donner un haut-le-cœur. Si une cible se déplace vite, même des dizaines de millisecondes comptent.
Pourquoi ce bug échappe aux tests
Ce n’est pas parce que les ingénieurs sont stupides. C’est parce que les tests sont généralement bornés :
- Les exécutions de test courtes n’accumulent pas assez de dérive.
- Les conditions de laboratoire ne correspondent pas aux cycles d’utilisation en production.
- Les critères d’acceptation se focalisent sur « ça marche maintenant », pas sur « ça marche après 100 heures ».
- Le temps est souvent simulé dans les tests, ce qui est nécessaire mais peut cacher des réalités d’intégration.
Les bugs liés au long uptime exigent des tests longue durée, ou au moins un raisonnement formel et une surveillance qui prennent explicitement en compte l’accumulation.
Si vous ne pouvez pas exécuter le test pendant 100 heures, simulez 100 heures avec des compteurs accélérés et vérifiez les maths à grande échelle.
Comment la dérive a mené à une interception manquée
Le radar du Patriot observe la position d’une cible, puis le système prédit où elle sera au moment où l’intercepteur pourra agir.
La prédiction utilise le temps. Si le temps interne est légèrement faux, la position prédite est fausse.
Une certaine quantité d’erreur est tolérable ; les filtres de suivi peuvent absorber le bruit. Mais la dérive accumulée n’est pas du « bruit ».
C’est un biais. Le biais vous pousse systématiquement dans la mauvaise direction.
En termes opérationnels, l’échec ressemble à ceci :
- Opérations normales : le système tourne, la dérive grandit lentement, personne ne remarque.
- Approche de l’échec : la qualité du suivi se dégrade subtilement ; le système devient plus susceptible de perdre ou de mal associer des pistes.
- Moment critique : une cible rapide apparaît ; la fenêtre de prédiction est serrée ; le biais compte ; le système ne parvient pas à aligner correctement la piste pour l’engagement.
L’essentiel ici est que la « dérive du temps » n’est pas une préoccupation secondaire. Elle contribue directement à une décision en temps réel.
Si vous exploitez quoi que ce soit de sensible au temps — paiements, authentification, stockage distribué, pipelines de télémétrie — le temps fait partie de votre plan de contrôle, que vous l’admettiez ou non.
Blague n°1 : la dérive du temps est le seul bug qui empire pendant que vous dormez, ce qui est impoli car c’est aussi le seul moment où vous ne vous envoyez pas de pages.
Les vraies leçons (pour SRE, ingénieurs embarqués et managers)
1) L’uptime n’est pas une vertu en soi
La culture des “cinq neuf” dégénère parfois en « ne redémarrez jamais rien ». C’est une religion, pas de l’ingénierie.
Un long uptime augmente l’exposition aux fuites, aux rollovers de compteurs, à la perte lente de précision et aux états étranges qui n’existent qu’après des jours de fonctionnement continu.
La bonne posture : concevoir pour un long uptime, mais opérer avec des fenêtres de maintenance planifiées.
Si un système est critique pour la sécurité ou la mission, vous devez savoir exactement quel état s’accumule avec le temps et comment il est borné.
2) La dérive des exigences est une vraie source de bugs
Le Patriot a été adapté à un nouvel environnement de menace. Ce n’est pas rare. Ce qui est inhabituel, c’est de croire que les hypothèses initiales tiennent encore.
Dans les systèmes d’entreprise, la « dérive des exigences » se déguise souvent en « juste un changement de configuration » ou « augmente juste le timeout ».
Quand le profil opérationnel change — trafic, latence, uptime, vitesse des cibles — révalidez les calculs.
Surtout les calculs impliquant le temps, les compteurs et les conversions numériques.
3) Le temps est une dépendance de systèmes distribués même sur une seule machine
Même un nœud unique a plusieurs horloges :
- Horloge murale (
CLOCK_REALTIME) : peut sauter à cause de corrections NTP ou de changements manuels. - Horloge monotone (
CLOCK_MONOTONIC) : stable, mais pas liée au temps civil. - Réalités matérielles TSC/HPET : dérive, variation de fréquence, artefacts de virtualisation.
Utilisez le temps monotone pour mesurer des intervalles et programmer des timeouts internes.
Utilisez le temps réel pour les horodatages destinés aux humains et l’interopérabilité. Les mélanger à la légère, c’est se retrouver avec des bugs qui paraissent surnaturels.
4) La précision doit être une décision de conception explicite
Si votre système utilise le point fixe, documentez :
- le facteur d’échelle
- la valeur maximale représentable avant roulage
- la stratégie d’arrondi
- l’erreur accumulée pire cas sur l’uptime maximal
Si personne ne peut répondre à « quel est le décalage maximal après 72 heures », vous n’avez pas conçu la gestion du temps. Vous y avez juste espéré.
5) La supervision doit inclure la qualité du temps, pas seulement la valeur
Beaucoup de tableaux de bord affichent « NTP activé » comme une case de conformité. Inutile.
Ce dont vous avez besoin : offset, correction de fréquence, jitter et atteignabilité. Et des alertes qui déclenchent avant que votre offset ne devienne opérationnellement significatif.
Mode opératoire de diagnostic rapide : dérive du temps et pannes liées au temps
Quand quelque chose sent le « temporel » (échecs d’auth intermittents, invalidations étranges de cache, basculements de verrous distribués, télémétrie hors ordre),
ne vous éparpillez pas. Exécutez une boucle serrée.
Première étape : vérifiez que vous ne vous mentez pas sur l’heure courante
- Vérifiez l’état de l’horloge locale : est-elle synchronisée ? NTP/chrony la contrôle-t-elle réellement ?
- Mesurez l’ampleur de l’offset : êtes-vous décalé de millisecondes, secondes, minutes ?
- Cherchez des événements de step : l’horloge a-t-elle sauté récemment ?
Deuxième étape : confirmez la source temporelle et le chemin réseau
- Qui est le serveur de temps ? Local, serveurs stratum de l’entreprise, ou sources publiques ?
- UDP/123 est-il joignable ? Ou êtes-vous en train de « synchroniser » sur rien ?
- L’hyperviseur interfère-t-il ? Le temps virtuel peut être « créatif ».
Troisième étape : mappez les symptômes au type d’horloge
- Bugs d’intervalle (timeouts, retries, rate limiting) : suspectez un mauvais usage du monotonic ou des blocages de boucle d’événements.
- Bugs d’horodatage (validité JWT, TLS, ordre des logs) : suspectez des sauts ou une dérive du temps réel.
- Incohérences entre hôtes : suspectez un offset entre hôtes ou une partition de sources temporelles.
Quatrième étape : bornez le rayon d’impact
- Arrêtez l’hémorragie : épinglez des instances, désactivez temporairement les vérifications temporelles « strictes » si c’est sûr (par ex., élargissez la dérive autorisée) pendant que vous restaurez la synchronisation.
- Réduisez l’accumulation d’état : redémarrez les services avec caches sensibles au temps si nécessaire.
- Empêchez la récidive : corrigez la cause racine, puis ajoutez des alertes sur offset/jitter/atteignabilité.
Blague n°2 : si vous trouvez « timekeeping » sous « non-functional requirements », félicitations — vous avez découvert une exigence fonctionnelle avec un meilleur marketing.
Tâches pratiques avec commandes : détecter, quantifier et décider
Ci-dessous se trouvent de vraies tâches que vous pouvez exécuter sur des flottes Linux typiques. Chacune inclut :
la commande, un exemple de sortie, ce que cela signifie et la décision à prendre.
Utilisez-les comme manuel de terrain, pas comme carte au trésor.
Task 1: Check if the system thinks it’s synchronized
cr0x@server:~$ timedatectl
Local time: Mon 2026-01-22 14:10:03 UTC
Universal time: Mon 2026-01-22 14:10:03 UTC
RTC time: Mon 2026-01-22 14:10:02
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Ce que ça signifie : « System clock synchronized: yes » indique qu’un service de synchronisation a discipliné l’horloge.
Décision : Si c’est no, considérez le temps comme suspect et passez aux vérifications NTP/chrony avant de dépanner autre chose.
Task 2: Identify whether chrony is healthy (offset, jitter, stratum)
cr0x@server:~$ chronyc tracking
Reference ID : 192.0.2.10 (ntp-a.internal)
Stratum : 3
Ref time (UTC) : Mon Jan 22 14:09:55 2026
System time : 0.000021345 seconds slow of NTP time
Last offset : -0.000012311 seconds
RMS offset : 0.000034112 seconds
Frequency : 12.345 ppm fast
Residual freq : -0.021 ppm
Skew : 0.120 ppm
Root delay : 0.003210 seconds
Root dispersion : 0.001102 seconds
Update interval : 64.0 seconds
Leap status : Normal
Ce que ça signifie : Offset d’environ 21µs lent ; excellent. La correction de fréquence est faible, le jitter est bas.
Décision : Si vous voyez des offsets en millisecondes/secondes, ou « Leap status: Not synchronised », arrêtez et corrigez la synchronisation temporelle d’abord.
Task 3: See which NTP sources are reachable and preferred
cr0x@server:~$ chronyc sources -v
210 Number of sources = 3
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
|| .- xxxx [ yyyy ] +/- zzzz
|| Reachability register (octal) -. | xxxx = adjusted offset,
|| Log2(Polling interval) --. | | yyyy = measured offset,
|| \ | | zzzz = estimated error.
|| | | \
^* ntp-a.internal 377 6 -21us[ -33us] +/- 312us
^+ ntp-b.internal 377 6 -10us[ -18us] +/- 401us
^? ntp-c.internal 0 6 +0ns[ +0ns] +/- 0ns
Ce que ça signifie : Deux sources saines ; une inaccessible (^?, reach 0).
Décision : Si la plupart des sources sont inaccessibles, vérifiez le pare-feu/le routage. Si la « meilleure » source bascule fréquemment, suspectez du jitter réseau ou un serveur défaillant.
Task 4: Confirm UDP/123 is reachable to your time server
cr0x@server:~$ nc -uvz ntp-a.internal 123
Connection to ntp-a.internal 123 port [udp/ntp] succeeded!
Ce que ça signifie : La joignabilité de base existe. Ce n’est pas une preuve d’un bon temps, mais ça élimine rapidement le blocage NTP.
Décision : Si ça échoue, coordonnez avec le réseau/la sécurité. Ne « contournez » pas en désactivant la validation temporelle dans les apps comme solution permanente.
Task 5: Verify kernel time discipline status
cr0x@server:~$ timedatectl timesync-status
Server: 192.0.2.10 (ntp-a.internal)
Poll interval: 1min 4s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 3
Reference: 9B1A2C3D
Precision: 1us (-24)
Root distance: 1.5ms
Offset: -17us
Delay: 310us
Jitter: 52us
Packet count: 128
Frequency: +12.345ppm
Ce que ça signifie : Offset et root distance sont infimes ; le système est discipliné.
Décision : Si la root distance est grande (centaines de ms+), la qualité du temps est mauvaise même si « synchronized: yes ». Envisagez de meilleures sources ou des serveurs de stratum plus proches.
Task 6: Detect whether the clock stepped (jumped) recently
cr0x@server:~$ journalctl -u chrony --since "2 hours ago" | tail -n 10
Jan 22 13:02:11 server chronyd[612]: Selected source 192.0.2.10
Jan 22 13:02:11 server chronyd[612]: System clock wrong by -0.742314 seconds
Jan 22 13:02:11 server chronyd[612]: System clock was stepped by -0.742314 seconds
Jan 22 13:02:12 server chronyd[612]: Frequency 12.345 ppm
Jan 22 13:03:16 server chronyd[612]: Source 192.0.2.11 replaced with 192.0.2.12
Ce que ça signifie : Un step d’environ ~742ms a eu lieu. Cela peut casser des systèmes qui supposent que le temps ne recule jamais ou ne saute pas.
Décision : Si vous voyez des steps, recherchez la cause : démarrage à froid, suspend/reprise de VM, ou perte de synchronisation. Envisagez la configuration en slew-only pour les applis sensibles (avec prudence).
Task 7: Check for VM or host time anomalies (dmesg clues)
cr0x@server:~$ dmesg | egrep -i "clocksource|tsc|timekeeping" | tail -n 8
[ 0.000000] tsc: Detected 2294.687 MHz processor
[ 0.000000] clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x211f0b6d85a, max_idle_ns: 440795223908 ns
[ 0.125432] clocksource: Switched to clocksource tsc
[ 831.441100] timekeeping: Marking clocksource 'tsc' as unstable because the skew is too large
[ 831.441105] clocksource: Switched to clocksource hpet
Ce que ça signifie : Le noyau a détecté un TSC instable et a changé de clocksource. Cela peut corréler avec de la dérive et des comportements temporels étranges, surtout en VM.
Décision : Si vous voyez « unstable », impliquez les équipes plateforme/virtualisation. Envisagez d’épingler la clocksource ou de corriger les réglages hôtes ; ne redémarrez pas simplement l’appli indéfiniment.
Task 8: Compare time between hosts (quick skew check)
cr0x@server:~$ for h in app01 app02 db01; do echo -n "$h "; ssh $h "date -u +%s.%N"; done
app01 1769091003.123456789
app02 1769091003.123991234
db01 1769091002.997000111
Ce que ça signifie : db01 a ~126ms de retard sur app01. C’est suffisant pour casser des vérifications de dérive strictes et réordonner des événements.
Décision : Si la dérive > la tolérance de votre système (souvent 50–200ms selon le protocole), corrigez la synchronisation avant de déboguer les « mystères » applicatifs.
Task 9: Verify monotonic clock behavior (no backwards jumps)
cr0x@server:~$ python3 - <<'PY'
import time
a=time.monotonic()
time.sleep(0.2)
b=time.monotonic()
print("delta_ms", (b-a)*1000)
PY
delta_ms 200.312614
Ce que ça signifie : Le temps monotone augmente de façon régulière. Si votre appli utilise le wall clock pour des intervalles, elle peut casser lors de steps ; le monotonic évite cette classe d’erreurs.
Décision : Si vous trouvez du code qui utilise l’horloge murale pour mesurer des intervalles, planifiez une correction. Ce n’est pas optionnel ; c’est un incident futur.
Task 10: Quantify drift rate (ppm) over time using chrony
cr0x@server:~$ chronyc sourcestats -v
210 Number of sources = 2
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
ntp-a.internal 20 12 18m +12.345 0.120 -21us 52us
ntp-b.internal 18 10 18m +11.998 0.200 -10us 60us
Ce que ça signifie : La correction de fréquence est d’environ 12 ppm rapide. C’est normal pour des horloges grand public ; chrony compense.
Décision : Si la fréquence est extrême ou instable, suspectez des problèmes matériels, des soucis thermiques, la planification VM ou des sources de temps défaillantes.
Task 11: Inspect whether NTP is configured to use a local “lies-to-you” source
cr0x@server:~$ grep -R "server\|pool\|local" /etc/chrony/chrony.conf
server ntp-a.internal iburst
server ntp-b.internal iburst
# local stratum 10
Ce que ça signifie : Des serveurs réels sont configurés ; le fallback « local clock » est commenté. Bien.
Décision : Si vous voyez local stratum activé sans raison solide, revoyez cela. Le fallback local peut masquer une panne amont et diverger silencieusement.
Task 12: Detect log timestamp anomalies (time went backwards)
cr0x@server:~$ journalctl --since "1 hour ago" | awk '
$1 ~ /^[A-Z][a-z]{2}$/ {
ts=$1" "$2" "$3;
if (prev != "" && ts < prev) { print "time went backwards:", prev, "->", ts }
prev=ts
}' | head
Ce que ça signifie : Si une sortie apparaît, vous avez des anomalies d’ordre (souvent dues à des steps d’horloge ou à un mélange d’ingestion de logs entre hôtes).
Décision : Si des « backwards » apparaissent, considérez toute corrélation temporelle durant l’incident comme suspecte ; priorisez la restauration de la synchronisation et l’utilisation d’un ordonnancement monotone quand c’est possible.
Task 13: Validate TLS failures might be clock-related (certificate validity)
cr0x@server:~$ openssl x509 -in /etc/ssl/certs/ca-certificates.crt -noout -dates 2>/dev/null | head -n 2
notBefore=Jan 1 00:00:00 2025 GMT
notAfter=Dec 31 23:59:59 2030 GMT
Ce que ça signifie : Les dates des certificats sont OK, mais si votre horloge système est en retard par rapport à notBefore, les handshakes TLS échouent d’une manière qui ressemble à un problème réseau.
Décision : Si vous observez des échecs TLS soudains sur un sous-ensemble d’hôtes, vérifiez l’offset avant de paniqueusement faire tourner des certificats.
Task 14: Confirm application containers inherit sane time (host vs container)
cr0x@server:~$ docker exec -it api-1 date -u
Mon Jan 22 14:10:05 UTC 2026
Ce que ça signifie : Les conteneurs utilisent typiquement l’horloge de l’hôte ; si l’hôte est erroné, tous les conteneurs sont erronés en même temps.
Décision : Si seulement certains nœuds sont erronés, concentrez-vous sur la NTP au niveau du nœud ; si tous sont erronés, regardez la source amont ou des changements de politique réseau.
Trois mini-récits d’entreprise (mauvaise hypothèse, optimisation qui se retourne, pratique ennuyeuse)
Mini-récit 1 : La mauvaise hypothèse (« le temps est suffisamment stable »)
Une entreprise fintech de taille moyenne exploitait un bus d’événements interne utilisé par des processeurs de paiements et des scores de fraude. Rien d’exotique : les producteurs horodatent les messages,
les consommateurs utilisent une fenêtre de « fraîcheur » pour rejeter tout ce qui a plus de 30 secondes, et le système antifraude ignorait agressivement les signaux « périmés » pour garder la latence faible.
Lors d’une fenêtre de maintenance réseau, un sous-ensemble de nœuds applicatifs a perdu l’accès aux serveurs NTP internes. Chrony a continué de servir le temps, mais il était désormais en libre-cours.
Sur plusieurs heures, ces nœuds ont dérivé. Pas de minutes — quelques centaines de millisecondes par-ci, une seconde par-là. Les tableaux de bord indiquaient toujours « service healthy ».
Bien sûr qu’ils l’affichaient. La plupart des dashboards ne mesurent pas la qualité du temps.
Le pipeline antifraude a commencé à rejeter des événements comme « vieux ». Pas constamment. Juste assez pour que ça compte.
Les analystes ont ensuite constaté que les décisions se faisaient avec moins de contexte : moins de signaux récents d’appareil, moins de marqueurs de session, moins d’indices « cette carte vient d’être utilisée ».
Les faux positifs ont augmenté. Le support client s’est fait entendre. Les ingénieurs ont bricolé la pire des solutions : ils ont élargi la fenêtre de fraîcheur.
Cette « correction » a empiré les choses. Désormais, des événements réellement périmés étaient acceptés, ce qui a changé la sémantique des fonctionnalités antifraude.
Les sorties du modèle ont dévié, et personne ne pouvait expliquer pourquoi. L’incident s’est terminé quand quelqu’un a vérifié les offsets chrony sur les hôtes en dérive et a rétabli la reachabilité NTP.
Le problème racine était une hypothèse erronée : que la dérive serait négligeable et que les horloges étaient « essentiellement correctes ».
La vraie correction n’était pas d’ajuster la fenêtre. C’était :
(1) alerter sur l’offset/la reachabilité, et
(2) utiliser le temps monotone pour les calculs de fraîcheur au sein d’un hôte, tout en utilisant des IDs de séquence d’événements entre hôtes.
Mini-récit 2 : L’optimisation qui s’est retournée (économie CPU en coupant la précision)
Une équipe stockage maintenait un service d’ingestion à haut débit qui horodaté chaque écriture avec un timestamp logique utilisé pour l’ordonnancement et les décisions de compactage.
Sous charge, le profiling montrait que la conversion et le formatage du temps étaient sur le chemin chaud. Quelqu’un a proposé une optimisation propre : remplacer un timestamp haute résolution
par un compteur de ticks moins cher et un facteur de conversion pré-calculé, gardé en point fixe. Cela économisait du CPU et semblait propre.
L’optimisation a été déployée. La latence s’est améliorée. Tout le monde s’est félicité. Puis, des semaines plus tard, un changement opérationnel a prolongé les intervalles de maintenance.
Les nœuds sont restés plus longtemps en fonctionnement. Les compactages ont commencé à se comporter bizarrement : certains segments étaient « dans le futur », d’autres étaient considérés comme déjà expirés,
et le compactor a commencé à osciller. Le système n’était pas arrêté, mais il brûlait des IOPS et faisait augmenter les latences tail.
Le coupable n’était pas le compteur de ticks. C’était le comportement d’arrondi dans le facteur de conversion et le fait que la conversion était faite à plusieurs endroits.
Différents chemins de code utilisaient des échelles légèrement différentes. Sur un long uptime, le biais est devenu visible dans les décisions d’ordonnancement.
Pire, parce que c’était du stockage, les effets ont persisté : un mauvais ordonnancement crée de mauvaises merges, et de mauvaises merges créent plus de mauvaises merges.
Le postmortem a abouti à trois changements :
(1) une bibliothèque partagée de conversion temporelle avec une précision explicite et des tests simulant un long uptime,
(2) basculer les calculs d’intervalle internes en nanosecondes monotones,
et (3) ajouter une vérification de cohérence : si les deltas d’ordonnancement dépassent les bornes attendues, le nœud refuse de prendre des décisions de compactage et fait intervenir des humains.
L’optimisation est permise. Une optimisation non mesurée et non bornée est la façon d’obtenir une défaillance lente qui ressemble à de « l’entropie ».
Mini-récit 3 : La pratique ennuyeuse qui a sauvé la situation (maintenance + SLOs temporels)
Une plateforme santé exploitait une flotte de serveurs API et de processeurs de messages dans plusieurs datacenters.
Leur équipe fiabilité avait une politique qui paraissait douloureusement conservatrice : chaque nœud reçoit un redémarrage programmé dans un intervalle défini,
et chaque environnement possède un SLO de « qualité temporelle » (offset, reachability et seuils de jitter).
Les ingénieurs se plaignaient. Les redémarrages sont pénibles. Ils perturbent les caches. Ils cassent les sessions de debug longue durée.
Les SREs ont tenu bon, parce qu’ils avaient vu ce qui arrive quand « ne jamais redémarrer » devient une doctrine.
Une nuit, un changement réseau a partiellement bloqué UDP/123 entre un segment et les serveurs temps internes.
Les alertes SLO temporelles ont déclenché en quelques minutes : offset en hausse sur un sous-ensemble de nœuds, reachability en baisse.
L’astreignant n’a pas eu à inférer quoi que ce soit à partir de symptômes ; la télémétrie pointait directement vers l’horloge.
La réponse a été ennuyeuse et efficace :
rerouter le NTP, confirmer la convergence des offsets, faire tourner les nœuds affectés par redémarrage pour nettoyer l’état sensible au temps, puis valider les systèmes aval (JWT, TLS, planificateurs).
Les clients ont à peine remarqué. Le rapport d’incident a été court. La partie la plus controversée a été qui devait remplir le ticket de changement de pare-feu.
Les pratiques ennuyeuses sont souvent juste « le bon compromis », répétées jusqu’à ce que les gens oublient pourquoi elles existent.
Gardez la politique. Documentez le pourquoi. Et ne négociez pas avec la physique.
Erreurs courantes : symptôme → cause racine → correctif
-
Symptôme : Échecs TLS aléatoires (« certificate not yet valid ») sur un sous-ensemble d’hôtes
Cause racine : horloge hôte en retard ; NTP inaccessible ou step en arrière après une reprise
Correctif : restaurer la reachabilité NTP, vérifier l’offset viachronyc tracking, puis redémarrer les clients qui mettent en cache des sessions si nécessaire -
Symptôme : Verrous distribués qui oscillent ; leaders réélus constamment
Cause racine : baux temporels basés sur l’horloge murale ; des steps d’horloge provoquent des expirations ou des extensions erronées
Correctif : utiliser le temps monotone pour les durées de bail ; assurer la synchronisation des horodatages ; alerter sur les steps d’horloge -
Symptôme : Métriques ou logs hors ordre entre hôtes ; traces en spaghetti
Cause racine : skew entre hôtes ; un segment a perdu NTP et a dérivé ; le pipeline d’ingestion fait confiance aveuglément aux horodatages
Correctif : imposer des SLOs de synchronisation temporelle ; ajouter une logique d’ingestion tolérante à une dérive bornée ; inclure des IDs de séquence ou un ordonnancement monotone par hôte -
Symptôme : Le rate limiting se comporte mal (« soudain tout le monde dépasse » ou « personne ne dépasse »)
Cause racine : compteurs basés sur des buckets temporels utilisant l’horloge murale ; un saut change les bornes des buckets
Correctif : baser les buckets sur le temps monotone ou utiliser une époque générée par le serveur depuis une source de confiance ; éviter le bucketing wall-clock en processus -
Symptôme : Jobs planifiés qui s’exécutent deux fois ou pas du tout après une correction NTP
Cause racine : scheduler qui utilise l’horloge murale et ne gère pas les steps ; l’heure système a été stepée pour corriger l’offset
Correctif : configurer la synchronisation temporelle pour slewer quand c’est possible ; utiliser des timers monotones ; ajouter des clés d’idempotence pour les jobs -
Symptôme : « Ça marche pendant des jours puis se dégrade » dans le tracking, le streaming ou les boucles de contrôle
Cause racine : erreur d’arrondi accumulée, rollover de compteur ou dérive interagissant avec des hypothèses d’uptime long
Correctif : calculer l’erreur pire cas sur l’uptime max ; augmenter la précision ; réinitialiser l’état en toute sécurité pendant une maintenance planifiée -
Symptôme : Seules les VMs dérivent ; le bare metal est OK
Cause racine : TSC instable, oversubscription de l’hôte, artefacts suspend/reprise, mauvaise configuration de l’horloge paravirtuelle
Correctif : coordonner avec l’équipe de virtualisation ; vérifier la stabilité de la clocksource noyau ; s’assurer que chrony est configuré pour les environnements VM
Listes de contrôle / plan étape par étape
Checklist A: Prévenir le bug en forme « Patriot » dans vos propres systèmes
- Inventoriez les dépendances temporelles : tokens d’auth, caches, planificateurs, ordonnancement, baux, compactage, protection contre rejouage.
- Déclarez l’uptime maximal supporté pour les composants avec état cumulatif ; testez-le.
- Utilisez le temps monotone pour les intervalles (timeouts, retries, backoff, rate limiting), et le temps mur pour la présentation/interopérabilité.
- Définissez un SLO de qualité temporelle : offset max, jitter max, sources minimales atteignables.
- Alertez sur la reachabilité et les tendances d’offset, pas seulement sur « NTP en cours d’exécution ».
- Testez le comportement longue durée : compteurs accélérés, tests de soak, ou analyse formelle des bornes.
- Centralisez les conversions temporelles : une bibliothèque, une échelle, une politique d’arrondi, testée.
- Planifiez des redémarrages sûrs : fenêtres de maintenance, redémarrages rolling, réhydratation d’état, idempotence.
Checklist B: Pendant la réponse à incident quand le temps est suspecté
- Vérifiez l’offset maintenant sur les hôtes affectés (
chronyc tracking/timedatectl). - Vérifiez la reachabilité des sources temporelles (
chronyc sources -v, chemin UDP/123). - Cherchez des steps dans les logs (
journalctl -u chrony). - Comparez rapidement la dérive entre hôtes (boucle SSH avec
date -u). - Atténuez le rayon d’impact : élargissez temporairement les tolérances de skew là où c’est sûr, épinglez les leaders, pausez les transitions d’état sensibles au temps.
- Restaurez la discipline temporelle : corrigez le réseau/la politique, assurez plusieurs sources, vérifiez la convergence.
- Nettoyez l’état : redémarrez les services avec caches sensibles au temps ; réélisez les leaders proprement ; relancez les jobs échoués de manière idempotente.
- Verrouillez la prévention : ajoutez des alertes SLO temporelles, mettez en place la gestion des changements pour NTP/règles de pare-feu, et tests post-incident.
FAQ
1) Le bug Patriot était-il « juste du floating point » ?
Non. Le problème central est la précision et l’accumulation. L’arithmétique en point fixe avec arrondi peut être parfaitement valide,
mais il faut borner l’erreur sur l’uptime maximal et les cas d’usage. L’incident Patriot est l’exemple type de « petit biais × long temps = grosse erreur ».
2) Pourquoi 0.1 pose-t-il problème en binaire ?
Parce que beaucoup de fractions décimales sont répétées en binaire, comme 1/3 est répétée en décimal. Si vous stockez 0.1 avec un nombre fini de chiffres binaires, vous l’approximez.
L’approximation est acceptable ; l’accumulation non prise en compte ne l’est pas.
3) La supervision aurait-elle pu le détecter ?
Oui — si le système surveillait la croissance de l’erreur temporelle comme signal de premier ordre et définissait un uptime maximal sécurisé ou un seuil de dérive.
Dans beaucoup de systèmes, l’absence d’une telle supervision est moins une limitation technique qu’un choix organisationnel.
4) Redémarrer est-il une mitigation valide pour les bugs d’accumulation temporelle ?
Parfois, oui. Redémarrer réinitialise l’état accumulé, y compris les dérives ou l’accumulation d’erreurs. Mais redémarrer comme stratégie n’est acceptable que si :
vous pouvez le faire en sécurité, de façon prévisible, et avec un intervalle maximal clair lié à des bornes d’erreur connues.
5) Quelle quantité de skew est « trop » dans les systèmes d’entreprise ?
Ça dépend. Pour la validation JWT et certains flux d’auth, des dizaines de secondes peuvent être tolérées avec marge, mais c’est un compromis de sécurité.
Pour le traçage distribué et l’ordonnancement, des dizaines de millisecondes peuvent déjà nuire. Pour des boucles de contrôle temps réel, encore moins.
La réponse doit venir de vos exigences, pas d’impressions vagues.
6) NTP vs chrony : est-ce important ?
Les deux peuvent fonctionner. Chrony est souvent préféré sur Linux moderne, surtout en environnements VM, car il gère bien des conditions réseau variables.
Ce qui compte plus que le démon, c’est que vous :
(1) ayez plusieurs sources sensées,
(2) puissiez y accéder de manière fiable,
et (3) alertiez sur offset/jitter/atteignabilité.
7) Pourquoi ne pas simplement utiliser le GPS partout ?
Le GPS peut être une excellente référence, mais il introduit ses propres modes de défaillance : problèmes d’antenne, perte de signal, spoofing/jamming et complexité opérationnelle.
Beaucoup d’organisations utilisent des serveurs stratum-1 adossés au GPS en interne, puis distribuent le temps via NTP/PTP sur des réseaux contrôlés.
8) Quelle est la différence pratique entre temps monotone et horloge murale ?
Le temps monotone sert à mesurer des durées ; il ne doit pas reculer quand le système corrige l’horloge murale.
L’horloge murale est pour « quelle heure est-il ». Utilisez le mauvais et vous obtenez des retries qui bloquent, des tokens qui expirent trop tôt ou des planificateurs qui voyagent dans le temps.
9) Si les steps sont dangereux, doit-on les interdire ?
Pas catégoriquement. Les steps peuvent être nécessaires au démarrage ou quand l’offset est énorme. Mais vous devez comprendre quelles applis cassent sur les steps,
préférer le slewing en état stable et concevoir les composants critiques avec des intervalles monotones et une logique idempotente.
10) Quel est le point opérationnel à retenir de l’incident Patriot ?
Ne laissez pas le « profil d’exploitation attendu » vivre seulement dans la tête de quelqu’un ou dans une hypothèse vieille de plusieurs décennies.
Encodez-le : dans les tests, les moniteurs, la politique de redémarrage et les bornes explicites sur l’erreur numérique.
Prochaines étapes réalisables cette semaine
Si vous exploitez des systèmes en production, le bug Patriot n’est pas une leçon d’histoire. C’est un rappel que le temps fait partie de votre surface de fiabilité.
Voici ce que faire ensuite, dans l’ordre, sans héroïsme.
- Ajoutez des métriques de qualité temporelle à votre monitoring : offset chrony, jitter, reachability et événements de step.
- Fixez un budget de skew explicite par sous-système (auth, ordonnancement stockage, planificateurs). Mettez le chiffre par écrit.
- Auditez le code pour les intervalles basés sur l’horloge murale. Remplacez par des timers monotones quand applicable.
- Faites une expérience contrôlée : bloquez le NTP sur un segment de staging et observez ce qui casse. Corrigez ce qui casse.
- Définissez un uptime maximal sûr pour les composants avec risques d’accumulation connus ; mettez en place des redémarrages rolling si nécessaire.
- Centralisez les conversions temporelles et ajoutez des tests longue durée (compteurs accélérés) pour que les régressions de précision soient détectées avant la prod.
L’incident Patriot est célèbre parce que les conséquences étaient visibles et immédiates. La plupart des défaillances temporelles dans les systèmes métiers sont plus silencieuses :
quelques événements perdus, quelques décisions erronées, une semaine de « on dirait que c’est plus lent ces derniers temps ». Les pannes silencieuses coûtent aussi de l’argent et de la confiance — juste à tempérament.