Atualmente em testes: o código do GitHub será aberto quando concluído.

Biblioteca Java / Kotlin do Ticketing

GitHub Maven Central

Um único TicketBroker oferece três estilos de chamada juntos — as corrotinas do Kotlin usam acquire, o async do Java usa acquireAsync (CompletableFuture), e o bloqueante do Java usa acquireBlocking. Não importa qual você use, é o mesmo broker e as mesmas conexões.

Repositório

xml
kts

Exemplo

Exemplo básico

Crie um broker ao iniciar a aplicação e compartilhe-o. wait=0 é uma tentativa imediata sem fila, máximo 255 s; lease é 1–250 s. O minimum-work budget final é obrigatório e pode ser zero. Frações de segundo são arredondadas para cima e valores fora do intervalo ou budget acima do lease normalizado são rejeitados antes do envio, nunca clampados.

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 automático é release bounded best-effort. Use a API de release explícito se o resultado importar. O token deve aplicar fencing ao write DB protegido na mesma transaction.

Bom saber (comportamento)

  • Se um byte puder ter sido enviado, perder a resposta definitiva produz Indeterminate. Não há reenvio automático de A com o mesmo owner; o caller não entra na seção crítica.
  • Cancel antes de send é unsent; após possible-send fecha a sessão. Se grant token foi parseado em paralelo, tenta release compensatório exact-token limitado.
  • M, todo E e respostas malformed/oversized/unknown são session-fatal. Acquires possible-send não resolvidos ficam Indeterminate.
  • B é rejeição definitiva de capacity e retorna imediatamente. Sem retry interno; caller pode iniciar acquire novo com owner novo e backoff da aplicação.
  • Releases explícitos/compensatórios tentam exact token só por 5 segundos absolutos desde call/enqueue. R sucesso, N ausente/não atual; sem resposta final é erro, nunca sucesso presumido.
  • Ticket só é entregue com tempo conservador positivo suficiente para work budget. Trabalho acima de 250 s é unsupported antes de enviar.
  • Fencing DB com token é obrigatório: na mesma transaction rejeite token <= stored_high_water, atualize high-water e faça business write antes de commit/rollback e release.

Opções de segurança (Token · TLS)

Toda opção é opcional. token deve corresponder ao client_tokens do servidor, e o TLS tem quatro modos: desligado / repositório de confiança do sistema / uma CA especificada / pular verificação (apenas para testes).

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

Em Java, use as fábricas estáticas TlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify().

Você pode gerar a configuração de token, TLS e cluster do lado do servidor na página Implantação do Servidor Ticketing.

Threads virtuais do Spring

Ao rodar o Spring MVC em threads virtuais, use as chamadas *Blocking, tanto em Java quanto em Kotlin. Um controller não suspenso não pode chamar acquire, então acquireBlocking também é o caminho normal no Kotlin. Elas não passam por corrotinas e só estacionam na resposta, mantendo a thread portadora livre.

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 {
            // seção crítica
        }
        return "ok"
    }
}

A liberação em segundo plano de close() (try-with-resources / use) roda por padrão em um executor de threads virtuais, então nunca fica na fila atrás de um pool de tamanho fixo. Passe seu próprio executor para usar o do Spring.

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

Com WebFlux ou controllers de corrotinas, continue usando as chamadas suspend.