Actuellement en test : le code GitHub sera ouvert une fois terminé.

Comment ça marche

Cette page suit le flux réel des messages entre un client et le serveur (cluster) pour expliquer le fonctionnement de Ticketing. La spécification exacte, au bit près, de ce qui transite sur le réseau se trouve dans Protocole (API) et Protocole (Cluster).


Structure générale

Les services qui reçoivent les requêtes utilisateur (par exemple, les serveurs d'événements gérant l'ouverture d'une vente de billets) font la queue pour la même clé auprès du cluster Ticketing, et seul le serveur dont c'est le tour poursuit le travail, comme décrémenter un stock ou attribuer un siège.

groups
Utilisateurs
Votre service (p. ex. serveurs d’événements)
dns Serveur d’événements 1
dns Serveur d’événements 2
dns Serveur d’événements 3
Cluster Ticketing
confirmation_number ticketing-server 1
confirmation_number ticketing-server 2
confirmation_number ticketing-server 3

Acquérir et libérer un verrou

Le flux de base. Une clé non détenue est accordée immédiatement ; une clé détenue attend son tour dans la file FIFO. Quand le détenteur la libère, le verrou est remis directement à la tête de la file, sans nouvelle mise en concurrence — le prochain en attente prend immédiatement le relais avec un nouveau jeton, si bien qu'il n'y a jamais de moment où le verrou est vacant, et personne ne peut passer devant les autres.

Client X
Client Y
Serveur
A · acquisition (order-1234)
A · acquis (token 41)
A · acquisition (order-1234)
détenu → mis en file, réponse différée
R · libération
R · libéré
A · remise directe (token 42)
requêteréponse
  • lease est un filet de sécurité. Si un client meurt ou oublie de libérer le verrou, une fois la durée de lease écoulée, le serveur récupère la clé et la remet au prochain en attente. Définissez-la largement — plus longue que le pire temps que pourrait prendre votre section critique. La v1 n'accepte que 1 à 250 secondes ; les travaux plus longs sont explicitement non pris en charge et rejetés.
  • wait est la borne supérieure du temps d'attente d'un client. Si son tour n'arrive pas dans ce délai, le serveur répond par T (timeout) au lieu d'accorder le verrou, si bien qu'aucun verrou ne fuit jamais. 0 signifie une seule tentative immédiate, sans entrée dans la file. Il n'existe pas d'attente infinie.
  • Si le même client tente de racquérir une clé qu'il détient déjà, il fait la queue comme n'importe quel autre client en attente — ce n'est pas un verrou réentrant.

Voir la Matrice requête × état de la clé pour les règles complètes du comportement du serveur selon l'état de la clé, et les Contraintes de champs pour les plages de valeurs des champs.

Jetons de fencing

Même un serveur de verrous parfait ne peut pas contrôler l'horloge d'un client. Ticketing seul ne peut pas empêcher le cas du détenteur périmé : un client qui a gelé un moment sous l'effet de pauses GC ou d'une surcharge alors qu'il détenait le verrou, puis se réveille — ignorant que son lease a déjà expiré — et poursuit son travail. C'est pourquoi le token de chaque réponse d'acquisition est un entier u64 garanti croissant à chaque octroi. Pour une base financière ou persistante, la comparaison et la mise à jour du high-water de fencing par clé doivent être atomiques avec l'écriture métier réelle, dans la même transaction DB ou une seule écriture conditionnelle. Mettre d'abord à jour uniquement la ligne de fencing puis écrire plus tard n'est pas sûr.

Client X
Client Y
Serveur
Ressource protégée
A · acquisition
A · acquis (token 7)
A · acquisition → en attente
gel par GC / surcharge
lease expiré → récupéré
A · remise directe (token 8)
écriture (token 8)
enregistre le token 8 → accepté
se réveille en retard
écriture (token 7)
7 < 8 → rejeté
requêteréponse

La monotonie est également garantie à l'échelle du cluster — le compteur de jetons lui-même est répliqué via le consensus, si bien que même après un changement de leader, le nouveau leader poursuit toujours à partir d'un nombre supérieur au précédent. Voir Jeton de fencing (spécification) pour le format exact du champ.

Cluster — sans interruption via le consensus Raft

En mode cluster, les nœuds partagent un état de verrou unique via le consensus Raft. Seul le leader traite les requêtes des clients, et chaque changement d'état de verrou (acquisition, libération, expiration) n'est finalisé qu'une fois enregistré par une majorité de nœuds. Se connecter à un nœud qui n'est pas leader renvoie une réponse M (redirigé) pointant vers le leader.

Client
Suiveur B
Leader A
Suiveur C
A · acquisition
M · redirection (leader)
A · acquisition
proposition de réplication
proposition de réplication
accusé de réception
accusé de réception
majorité atteinte → commit
A · acquis (token)
requêteréponseRPC de consensus (Raft)
  • Si le leader meurt, les réglages actuels élisent un nouveau leader en environ 2,3 à 2,5 secondes. Le même acquire logique ne peut continuer dans le budget wait restant que si aucun octet de requête n'a été envoyé. Un acquire envoyé sans réponse confirmée est Indeterminate et n'est jamais renvoyé automatiquement.
  • Même l'expiration du lease passe par le consensus. Un verrou ne disparaît qu'une fois que le leader a validé la commande d'expiration, si bien que de légers écarts d'horloge entre nœuds ne font jamais diverger l'état des verrous.
  • Si le cluster perd sa majorité (par ex. 2 nœuds sur 3 en panne), il cesse d'accepter les écritures plutôt que de risquer d'émettre un verrou invalide (sécurité avant tout). Si l'état volatile du processus est perdu sur 2 nœuds sur 3 (ou 3 sur 5), ce domaine cluster/fencing ne doit ni être récupéré ni être automatiquement bootstrapé sous la même identité.

Comme seule la majorité compte, seuls 3 ou 5 nœuds sont autorisés. Un nombre pair de nœuds n'ajoute que du coût sans améliorer la tolérance aux pannes, si bien que le serveur refuse de démarrer avec un tel nombre.

Nombre de nœudsSans interruptionPannes simultanées toléréesRemarques
1Monotonie du token garantie seulement pendant la durée de vie du processus ; non pris en charge pour protéger une DB persistante redémarrable
31La configuration standard sans interruption. Suffisante dans la plupart des cas
52La redondance tient même pendant qu'un nœud est en maintenance

Voir Protocole (Cluster) pour les règles de trame et de port des RPC entre pairs, et le remplacement sans interruption par un learner au nouveau NodeId pour remplacer les nœuds un par un.

Comportement du client

Les clients officiels partagent le comportement suivant (voir les Bibliothèques pour les API spécifiques à chaque langage).

  • Maintient une connexion persistante par adresse en arrière-plan. Avec une seule adresse fournie, il maintient deux connexions vers ce même nœud, si bien qu'une brève coupure sur un socket n'interrompt pas le service.
  • Choisit une connexion en priorisant le leader, puis en round-robin. Il se souvient du leader vers lequel il a été redirigé pour la dernière fois via M et envoie directement les requêtes suivantes vers lui.
  • Les requêtes sont pipelinées. La requête suivante peut être envoyée sans attendre de réponse — voir les Règles de correspondance des réponses pour savoir comment les réponses sont associées aux requêtes.
  • Une connexion perdue continue de tenter de se reconnecter avec un backoff exponentiel (0,1 s jusqu'à un plafond de 3,2 s). Une connexion morte ne casse pas l'objet broker — il se rétablit de lui-même.
  • Le même acquire logique n'est réessayé en interne que si aucun octet de requête n'a été envoyé. Un B corrélé est un résultat Busy/non-acquis certain, renvoyé immédiatement à l'appelant sans nouvel essai interne. M n'est qu'un leader hint pour la prochaine connexion ; sans owner/key, il ne justifie pas le renvoi d'une requête pending déjà envoyée. Un acquire envoyé sans réponse confirmée est signalé Indeterminate et interdit l'entrée en section critique.
  • La libération explicite et la compensation d'un token connu exigent la correspondance exacte owner/token/key et une file dédiée bornée. Les essais cessent à une échéance absolue de 5 secondes depuis request/enqueue, jamais à la fin du lease restant. Sans réponse de succès, une libération explicite n'est ni signalée ni supposée réussie ; le lease serveur reste le filet final.

Connexion et sécurité

Chaque connexion (client ou pair) s'établit dans l'ordre TCP → (TLS) → authentification challenge-réponse. Le client hache le nonce envoyé par le serveur avec son jeton pour répondre, si bien que le jeton n'est jamais envoyé en clair sur le réseau, et un nonce différent à chaque connexion exclut toute attaque par rejeu (replay). Voir la Poignée de main d'authentification pour la spécification exacte.

Récapitulatif sur la cohérence

  • L'exclusion mutuelle est garantie par le consensus. Comme chaque octroi passe par un commit majoritaire, la même clé n'est jamais délivrée à deux détenteurs à la fois, même pendant une partition réseau ou un changement de leader.
  • Les bases persistantes doivent imposer le fencing. Seule une condition high-water par clé, exécutée atomiquement avec l'écriture protégée dans la même transaction/écriture conditionnelle, arrête un client réveillé après l'expiration de son lease. Le verrou ordonne le travail ; le fencing DB bloque l'ultime écriture obsolète.