Les systèmes logiciels aiment imaginer le temps comme une valeur qui avance parfaitement. En production, les horloges dérivent, les jobs partent en retard, les messages arrivent dans le désordre, les retries doublent des effets et les déploiements arrêtent des workers. Un système fiable ne suppose pas un temps idéal. Il définit quelle horloge fait autorité, quelle incertitude est acceptable et quoi faire lorsqu’une opération arrive trop tôt, trop tard ou plusieurs fois.

Choisissez l’horloge selon la question

Le wall clock répond “quand cela s’est-il produit dans le monde partagé?”. L’horloge monotone répond “combien de temps cela a-t-il duré dans ce processus?”. Utiliser le wall clock pour un timeout peut échouer si l’horloge se corrige en arrière.

Le code devrait séparer ces sources. Les deadlines basées sur une durée utilisent une horloge monotone. Les événements persistés utilisent un instant UTC documenté.

L’expiration est une politique

now > expires_at paraît simple jusqu’à ce que des services aient quelques secondes d’écart. Un token peut tolérer un petit skew, tandis qu’une réservation doit être appliquée par transaction. La frontière inclusive ou exclusive doit être claire.

Centraliser cette règle évite que deux endpoints interprètent différemment la même expiration. Les logs devraient indiquer l’horloge, le timestamp et la décision prise.

Les jobs planifiés partent parfois en retard

Un scheduler peut rendre un job disponible à une heure donnée, sans garantir l’exécution immédiate. File d’attente saturée, worker arrêté, dépendance indisponible ou déploiement peuvent retarder le traitement. Le job doit savoir si une exécution tardive est encore utile.

Les jobs récurrents ont besoin d’une politique de rattrapage. Exécuter toutes les occurrences manquées peut surcharger; les ignorer peut perdre des données. C’est une décision métier.

L’idempotence évite les dégâts

Les queues et schedulers peuvent livrer deux fois. Un retry peut se produire après une première exécution réussie mais non confirmée. Sans idempotence, on envoie deux e-mails, crée deux factures ou applique deux transferts.

Operation ID stable, constraint unique ou état enregistré rendent la répétition sûre. Exactly-once entre systèmes est difficile; safe retry est plus réaliste.

Un timestamp ne prouve pas l’ordre causal

Deux services peuvent avoir des horloges différentes. Un message ancien peut arriver après un nouveau. Trier seulement par timestamp produit une histoire plausible, mais pas forcément vraie. Pour la concurrence, il faut séquence, version de domaine, transaction ou horloge logique.

Le timestamp reste utile pour l’observation. Il ne devrait pas devenir un mécanisme caché de contrôle de concurrence.

Injectez le temps dans la logique

Un code qui appelle l’horloge réelle partout est difficile à tester. Passer une horloge ou un instant courant comme dépendance permet de tester expirations, changements d’heure et retries sans sleep. Les tests deviennent rapides et déterministes.

Une horloge fake doit avancer par décision du test. Attendre réellement dans les tests cache les cas limites et ralentit la suite.

Les frontières critiques demandent atomicité

Pour une réservation, un lease ou une promotion limitée, vérifier l’heure dans l’application puis mettre à jour plus tard crée une course. La base ou le système autoritatif doit comparer deadline et changer l’état dans une opération atomique.

Les effets externes demandent encore idempotence et reconciliation. La transaction protège l’état interne, pas le fournisseur d’e-mail ou de paiement.

Reconciliation répare les hypothèses cassées

Même un bon scheduler a besoin de jobs qui trouvent les éléments en retard, bloqués ou incohérents. La reconciliation transforme une panne temporaire en retard récupérable. Elle doit elle-même être idempotente.

La concevoir dès le départ est souvent plus simple que prouver que chaque chemin temporel ne peut jamais échouer.

Mesurez le retard, pas seulement la profondeur

La profondeur d’une queue ne dit pas combien de temps un job attend. Mesurez schedule lag, queue wait, duration, âge du plus vieil item et taux de deadlines manquées. Chaque métrique pointe vers une cause différente.

Les retries doivent connaître le deadline métier

Un retry utile pour une synchronisation peut être dangereux pour une action qui n’a de sens que jusqu’à une heure limite. Chaque tentative doit vérifier le deadline métier, pas seulement le moment de mise en queue. Backoff, nombre maximum d’essais et dead-letter font partie de la sémantique temporelle.

Cette vérification évite d’exécuter tard une opération qui aurait dû expirer, comme une réservation, une autorisation temporaire ou une notification sensible au contexte.

Les décisions temporelles doivent être auditables

Lorsqu’un accès expire, qu’un job est ignoré ou qu’une opération est rejetée pour retard, le log doit contenir les instants pertinents et la règle appliquée. Sans cela, l’équipe ne peut pas savoir si le bug vient de l’horloge, du scheduler, de la queue ou du domaine.

Les tableaux de bord devraient montrer les retards par type de travail. Un retard acceptable pour un rapport de nuit peut être critique pour une autorisation courte.

Un système temporel fiable n’est pas celui qui n’est jamais en retard. C’est celui qui sait qu’il l’est, décide si l’action reste valable et récupère sans dupliquer les effets.