現在テスト中です: 完了後にGitHubのコードを公開する予定です。

Ticketing Java / Kotlinライブラリ

GitHub Maven Central

1つのTicketBrokerが3つの呼び出しスタイルをまとめて提供します — Kotlinのコルーチンでは acquireを、Javaの非同期ではacquireAsync(CompletableFuture)を、Javaのブロッキングでは acquireBlockingを使います。どれを使っても、同じブローカー・同じ接続です。

リポジトリ

xml
kts

基本的な例

brokerはapplication起動時に1つ作って共有します。wait=0はqueueなしの即時試行1回で最大255秒、leaseは1〜250秒です。最後のminimum-work budgetは必須で0も可能です。秒未満は切り上げ、範囲外または正規化lease超過budgetは送信前に拒否し、clampしません。

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.
}

自動close/dropはbounded best-effort releaseです。結果が必要ならexplicit release APIを使います。tokenは同じDB transactionで保護対象writeをfenceしなければなりません。

知っておきたい挙動

  • request byteを1つでも送った可能性があり確定応答を失うとIndeterminateです。同じownerでAを自動再送せず、callerはcritical sectionへ入りません。
  • 送信前cancelはunsent、possible-send後cancelはsessionを閉じます。同時にgrant tokenをparseした場合はboundedなexact-token補償releaseを試みます。
  • M、全てのE、malformed/oversized/unknown responseはsession-fatalです。未確定possible-send acquireはIndeterminateです。
  • Bはcapacityによる確定拒否で即時返却します。内部retryはなく、callerがapplication backoff後に新ownerで新acquireを開始できます。
  • explicit/compensating releaseはcall/enqueueから絶対5秒だけexact tokenを再試行します。Rは成功、Nは既にないかcurrent tokenでない状態、最終応答なしは成功と推定せずerrorです。
  • 正の保守的残り時間がありrequired work budgetを満たす場合だけTicketを返します。250秒超workは送信前にunsupportedです。
  • tokenによるDB fencingは必須です。同じDB transactionでtoken <= stored_high_waterを拒否し、high-water更新とbusiness writeを行ってcommit/rollback後にreleaseします。

セキュリティオプション(トークン · TLS)

すべてのオプションは任意です。tokenはサーバーのclient_tokensに合わせ、TLSはオフ / システム 信頼ストア / CA指定 / 検証省略(テスト専用)の4モードがあります。

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()

Javaでは、静的ファクトリのTlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify()を使います。

サーバー側のトークン・TLS・クラスタ設定はTicketingサーバーデプロイ ページで生成できます。

Spring 仮想スレッド

Spring MVCを仮想スレッドで動かす場合は、JavaでもKotlinでも*Blocking関数を使います。 非suspendのコントローラーからはacquire(suspend)を呼べないため、Kotlinでも acquireBlockingが通常の経路です。コルーチンを経由せず応答でのみparkするので、 キャリアスレッドを占有しません。

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 {
            // クリティカルセクション
        }
        return "ok"
    }
}

close()(try-with-resources / use)によるバックグラウンド返却は既定で仮想スレッド 実行機で動くため、固定サイズのプールで詰まりません。Spring管理の実行機を使うには 次のように渡します。

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

WebFluxやコルーチンコントローラーでは、これまで通りsuspend関数を使ってください。