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

Bibliothèque Ticketing Java / Kotlin

GitHub Maven Central

Un seul TicketBroker propose trois styles d'appel ensemble — les coroutines Kotlin utilisent acquire, l'asynchrone Java utilise acquireAsync (CompletableFuture), et le mode bloquant Java utilise acquireBlocking. Quel que soit celui utilisé, c'est le même broker et les mêmes connexions.

Dépôt

xml
kts

Exemple

Exemple simple

Créez un broker au démarrage et partagez-le. wait=0 est une tentative immédiate sans file, au maximum 255 s ; lease vaut 1–250 s. Le minimum-work budget final est obligatoire et peut valoir zéro. Les fractions de seconde sont arrondies vers le haut et les valeurs hors plage ou un budget supérieur au bail normalisé sont rejetés avant envoi, jamais clampés.

Kotlin (coroutines):

kotlin
val broker = TicketBroker.connect("127.0.0.1:5225")
broker.waitReady(Duration.ofSeconds(5))

val ticket = broker.acquire("key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))
val token = ticket.token
// In the same DB transaction: verify/update token high-water and perform the business write.
ticket.release()

Java (blocking):

java
TicketBroker broker = TicketBroker.connect("127.0.0.1:5225");
broker.waitReadyBlocking(Duration.ofSeconds(5));

try (Ticket ticket = broker.acquireBlocking(
        "key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))) {
    long token = ticket.getToken();
    // In the same DB transaction: verify/update token high-water and perform the business write.
}

Le close/drop automatique est un release borné best-effort. Utilisez l'API de release explicite si le résultat compte. Le token doit protéger l'écriture DB dans la même transaction.

Bon à savoir (comportement)

  • Dès qu'un byte a pu être envoyé, la perte de la réponse définitive produit Indeterminate. Aucun renvoi automatique de A avec le même owner ; le caller n'entre pas en section critique.
  • Cancel avant envoi est unsent ; après possible-send il ferme la session. Si un grant token a été parsé en parallèle, un release compensatoire exact-token borné est tenté.
  • M, tous les E et les réponses malformed/oversized/unknown sont session-fatal. Un acquire possible-send non résolu devient Indeterminate.
  • B est un refus certain de capacity, rendu immédiatement. Aucun retry interne ; le caller peut démarrer un nouvel acquire avec nouvel owner et backoff applicatif.
  • Les releases explicites/compensatoires ne retentent l'exact token que pendant 5 secondes absolues depuis call/enqueue. R réussit, N signifie absent ou non courant ; sans réponse finale, erreur, jamais succès supposé.
  • Ticket n'est rendu qu'avec un temps conservateur positif suffisant pour le work budget. Un travail de plus de 250 s est unsupported avant envoi.
  • Le fencing DB par token est obligatoire : dans la même transaction, rejeter token <= stored_high_water, mettre à jour high-water et effectuer le business write avant commit/rollback puis release.

Options de sécurité (Token · TLS)

Toutes les options sont facultatives. token doit correspondre au client_tokens du serveur, et TLS propose quatre modes : désactivé / magasin de confiance système / CA spécifiée / vérification ignorée (test uniquement).

kotlin
val broker = TicketBroker.builder()
    .addrs("10.0.0.1:5225", "10.0.0.2:5225", "10.0.0.3:5225")
    .token("123")
    .tls(TlsMode.SystemRoots)
    // .tls(TlsMode.Ca("ca.crt"))
    // .tls(TlsMode.InsecureSkipVerify)
    .connect()

En Java, utilisez les fabriques statiques TlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify().

Vous pouvez générer le jeton, le TLS et la configuration de cluster côté serveur sur la page Déploiement du serveur Ticketing.

Threads virtuels Spring

Si Spring MVC tourne sur des threads virtuels, utilisez les appels *Blocking, en Java comme en Kotlin. Un contrôleur non suspendu ne peut pas appeler acquire : en Kotlin aussi, acquireBlocking est donc la voie normale. Ils ne passent pas par les coroutines et ne se garent que sur la réponse, le thread porteur reste donc libre.

kotlin
@RestController
class OrderController(private val broker: TicketBroker) {

    @PostMapping("/orders/{id}")
    fun place(@PathVariable id: String): String {
        broker.acquireBlocking("order-$id", Duration.ofSeconds(5), Duration.ofSeconds(30)).use {
            // section critique
        }
        return "ok"
    }
}

La libération en arrière-plan de close() (try-with-resources / use) s'exécute par défaut sur un exécuteur à threads virtuels : elle ne s'accumule jamais derrière un pool de taille fixe. Passez votre propre exécuteur pour utiliser celui de Spring.

kotlin
TicketBroker.builder()
    .addrs("127.0.0.1:5225")
    .executor(applicationTaskExecutor)
    .connect()

Avec WebFlux ou des contrôleurs à coroutines, continuez d'utiliser les appels suspend.