Retour au carnet
IngénierieJonas K. · 12 min

Sync PMS sous 100 ms : architecture du registre temps réel.

Un état chambre faux pendant trente secondes, c'est un agent envoyé dans une chambre occupée. La latence n'est pas un confort d'ingénieur ici : c'est la différence entre un planning juste et un incident client.

Publié le

Le problème : le drift d'état chambre

Chaque PMS — Hostaway, Beds24, Smoobu, Cloudbeds, Mews — détient sa propre vérité sur les arrivées, départs et prolongations. Si Hostik n'apprend un départ tardif que lors du prochain polling, l'assignation du ménage part sur une donnée périmée. Le symptôme visible (un agent devant une porte qui ne s'ouvre pas) a une cause invisible : un écart de quelques secondes entre deux systèmes.

L'objectif n'était donc pas « synchroniser vite pour la beauté du geste », mais réduire la fenêtre pendant laquelle nos deux mondes divergent au point qu'un humain agit sur du faux.

Un port PMS, plusieurs transports

On ne voulait pas d'un connecteur Hostaway, d'un connecteur Beds24, d'un connecteur Smoobu — chacun avec sa propre logique de retry et ses propres bugs. On a défini un seul port (un contrat : `fetchEvents`, `drainPages`, gestion des jetons) et autant d'adaptateurs fins que de fournisseurs. Le cœur du registre ne sait pas quel PMS lui parle ; il ne voit que des événements normalisés.

Bénéfice concret : un webhook entrant et un backfill paginé traversent exactement le même chemin de code une fois normalisés. Le jour où l'on ajoute un fournisseur, on écrit un adaptateur de transport, pas une seconde moitié d'application.

L'idempotence avant la vitesse

Les webhooks PMS arrivent en double, dans le désordre, et parfois après un backfill qui contient déjà le même événement. Aller vite sur un flux non idempotent, c'est juste se tromper plus rapidement. Chaque événement porte une clé stable ; un événement déjà appliqué est ignoré, un événement plus ancien que l'état courant ne le réécrit pas.

Ce n'est qu'une fois cette propriété garantie qu'on a pu se permettre d'être agressif sur la propagation : un changement validé est poussé aux clients connectés immédiatement, parce qu'on sait qu'un doublon n'abîmera rien.

Les pièges qu'on a payés une fois

Le backfill qui écrase le temps réel : au premier branchement d'une propriété, la pagination historique peut arriver après des événements live plus récents. La règle « on n'applique jamais un état plus ancien » règle ça — mais il a fallu un incident pour l'écrire.

Les jetons qui expirent en silence : un adaptateur dont le jeton est périmé échoue poliment et laisse l'état figé. On a centralisé la gestion des jetons dans le port plutôt que dans chaque adaptateur, pour que l'échec soit visible et réparable au même endroit.

Les régressions qu'on a évitées

On a refusé d'exposer la latence comme une promesse marketing avant d'avoir un test qui la mesure de bout en bout. « Sous 100 ms » n'est pas un slogan : c'est la cible que le chemin webhook → normalisation → propagation doit tenir, et qu'une régression fait échouer le build plutôt que la production.

La vraie leçon d'architecture n'est pas dans la milliseconde gagnée. Elle est dans l'ordre des priorités : un contrat unique d'abord, l'idempotence ensuite, la vitesse en dernier. Inversez l'ordre et vous obtenez un système rapide qui ment.