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

Qu'est-ce que Ticketing ?

Ticketing est un serveur de verrous dédié, conçu pour une seule tâche — le verrouillage distribué. C'est un unique binaire serveur écrit en Rust, associé à des clients officiels par langage qui implémentent tous le même protocole binaire, au bit près. Seulement deux opérations — acquérir et libérer — une file FIFO par clé, des jetons de fencing, et un cluster Raft optionnel que vous activez quand vous en avez besoin. Tout ce dont un verrou distribué a besoin, et rien de plus.

En quoi est-ce différent des verrous construits sur un magasin de données ?

La plupart des verrous distribués en production s'appuient sur un magasin clé-valeur — un script Lua ou Redlock au-dessus de Redis, ou un verrou construit sur les sessions et les baux de ZooKeeper ou etcd. Redis est un excellent magasin de données, mais c'est un magasin clé-valeur détourné pour le verrouillage distribué, pas un système de verrous conçu pour cela — et cela se traduit par un surcoût structurel : le coût d'analyse d'un protocole généraliste, et le surcoût de structures de données généralistes, sont toujours de la partie.

Ticketing a été conçu exclusivement pour le verrouillage distribué dès le premier jour, si bien que chaque couche est pensée autour des verrous.

  • Protocole — des champs binaires à largeur fixe, ni du texte ni un format de sérialisation généraliste. Les valeurs sont lues directement par décalage d'octets sans analyse syntaxique, et le pipelining permet d'envoyer la requête suivante sans attendre la réponse précédente.
  • Interne au serveur — l'état complet tient dans une file FIFO par clé plus un minuteur de bail (lease). Le chemin de traitement des requêtes n'alloue même pas sur le tas.
  • Exploitation — démarrez un binaire serveur (ou un conteneur), configurez-le avec quelques variables d'environnement, et c'est terminé. Aucune conception de schéma, aucun magasin de données séparé, aucune connaissance opérationnelle sans rapport avec le verrouillage n'est requise.

Performance

  • Traite plus d'un million d'opérations par seconde en utilisant moins de 10 Mo de mémoire
Comparaison de vitesse avec Redisson (Redis), le système de verrou distribué le plus utilisé.
Clients TicketingRedis (Redisson RLock)
JavaScript
210.2 ms · 190,295 ops/s
210.2 ms
Rust
225.9 ms · 177,069 ops/s
225.9 ms
Go
228.4 ms · 175,131 ops/s
228.4 ms
C#
286.9 ms · 139,421 ops/s
286.9 ms
Kotlin
443.5 ms · 90,192 ops/s
443.5 ms
Java
457.8 ms · 87,374 ops/s
457.8 ms
C/C++
659.7 ms · 60,634 ops/s
659.7 ms
Python
739.4 ms · 54,098 ops/s
739.4 ms
Ruby
1,682.4 ms · 23,776 ops/s
1,682.4 ms
Redisréférence
4,909.1 ms · 8,148 ops/s
4,909.1 ms
La longueur de la barre correspond au temps total (ms) de l’exécution — plus court est plus rapide.
32 contended keys × 1,250 ops · concurrency 64 · mac mini m4 2024 basic (10 core), single local server, acknowledged release, 1s min-work budget

Pourquoi Ticketing

🚦 Intégrité de l'ordre — file FIFO avec remise directe

Premier arrivé, premier servi. Quand le détenteur libère le verrou, celui-ci est remis directement à la tête de la file, sans nouvelle mise en concurrence. Aucune famine causée par un verrouillage en polling-et-nouvelle-tentative, et aucune tempête de nouvelles tentatives.

🛟 Un client mort ne laisse pas le verrou bloqué — le bail (lease)

Si le processus détenant le verrou meurt ou que sa connexion tombe, le serveur récupère automatiquement le verrou une fois la durée de lease définie à l'acquisition écoulée, et le remet au prochain en attente. Oublier de libérer le verrou ne bloque pas tout le système.

🔑 Empêcher un travail effectué dans le désordre — les jetons de fencing

Il existe une défaillance qu'un verrou seul ne peut pas empêcher : un détenteur qui se réveille en retard — ignorant que son lease a déjà expiré — et demande quand même à terminer son travail. Ticketing émet un jeton de fencing strictement croissant à chaque acquisition. Tant que la ressource protégée applique une règle unique — « rejeter tout jeton inférieur au dernier vu » — même cette dernière brèche est comblée. Voir Comment ça marche pour l'explication complète.

🗳️ Un cluster sans interruption bâti sur Raft

Un serveur unique suffit à lui seul, mais quand vous avez besoin d'une disponibilité continue, formez un cluster de 3 (ou 5) nœuds. Chaque changement d'état de verrou n'est finalisé qu'après l'accord d'une majorité de nœuds via le consensus Raft, si bien que le même verrou n'est jamais délivré deux fois à la fois, même si le leader meurt ou que le réseau se partitionne. Si le leader tombe en panne, un nouveau est élu en environ une seconde et les clients basculent automatiquement. Le service continue de fonctionner même quand les nœuds tombent un par un — par exemple lors de redémarrages d'instances cloud ou d'une maintenance progressive.

🔒 Authentification et TLS intégrés

Chaque connexion passe par une authentification par jeton de type challenge-réponse immédiatement après la connexion, et le jeton n'est jamais envoyé en clair sur le réseau. Chiffrez toute la connexion avec TLS dès que vous en avez besoin.

🌐 Le même protocole, au bit près, dans tous les langages majeurs

Rust, Java/Kotlin, JavaScript/TypeScript, Python, C#, Go, Ruby, C/C++ — chaque client officiel implémente le même protocole binaire, avec une API qui s'intègre naturellement à l'écosystème de chaque langage (asynchrone ou bloquante). Un verrou acquis depuis n'importe quel langage fait la queue équitablement avec des clients écrits dans n'importe quel autre. Voir Bibliothèques pour les instructions d'installation et les exemples.

Seulement deux opérations

Fidèle à un serveur exclusivement dédié aux verrous, la surface d'API est elle aussi minimale.

  1. Attachez une clé à la ressource que vous voulez protéger — par ex. order-1234, account-77, daily-batch.
  2. Acquérez cette clé avant de faire le travail — si quelqu'un d'autre la détient, vous attendez votre tour dans la file FIFO.
  3. Libérez-la une fois le travail terminé — le prochain en attente prend immédiatement le relais sans nouvelle mise en concurrence.

Les seules options qui s'ajoutent à ces deux opérations sont lease (récupération automatique) et waitTimeout (abandon de l'attente) — c'est là toute l'API.

À lire ensuite