sendBlocking si blocca fino al timeout

Sintomo

Hai cambiato un'operazione in modalità bloccante con BlockingMessageGateway, e ora la coroutine non riprende mai in caso di errore — si blocca fino a quando non genera un'eccezione TimeoutCancellationException. Le operazioni riuscite funzionano correttamente; solo i fallimenti si bloccano. Un login o un pagamento che dovrebbe restituire un errore invece si limita a scadere per timeout.

Causa

sendBlocking riprende quando arriva un messaggio del tipo esatto che hai chiesto di attendere. Se attendi il sottotipo di successo (ad esempio SuccessRetailerLoginResponse) e l'operazione fallisce, il fallimento arriva come un tipo diverso (ErrorRetailerLoginResponse) — che non corrisponde mai a ciò che stai aspettando. Il gateway continua ad aspettare e la tua coroutine si blocca fino al timeout.

La risposta di errore è stata consegnata. Semplicemente non stavi ascoltando per quel tipo.

Soluzione

Attendi il super-tipo sigillato, non un sottotipo, quindi ramifica con when:

// SBAGLIATO — riprende solo in caso di successo; ogni fallimento diventa un timeout
val r: SuccessRetailerLoginResponse = gateway.sendBlocking { clientSDK.sendLoginRequest() }

// CORRETTO — riprende sia in caso di successo che di errore
val r: RetailerLoginResponse = gateway.sendBlocking(timeoutMillis = 30000) {
    clientSDK.sendLoginRequest()
}
when (r) {
    is SuccessRetailerLoginResponse -> { /* ok */ }
    is ErrorRetailerLoginResponse   -> { /* gestisci il fallimento */ }
}

La stessa regola si applica a ogni operazione: attendi RetailerPaymentResponse, RetailerReversalResponse, ecc. — il super-tipo — e ramifica dopo. Il super-tipo copre tutti gli esiti, quindi sia successo che fallimento fanno riprendere la tua coroutine.

Ricorda che il pagamento ha un terzo esito, PartialRetailerPaymentResponse. Attendere RetailerPaymentResponse lo cattura; attendere solo il sottotipo di successo perderebbe sia l'errore che i casi parziali.

Ancora timeout dopo aver corretto il tipo?

Se attendi il super-tipo e ancora si verifica un timeout, la risposta non sta realmente arrivando — verifica che il gateway riceva ogni messaggio (gateway.onNewMessage(message) nel tuo interceptor), che tu abbia una sessione attiva (vedi Session and login lifecycle) e che il timeout sia sufficientemente lungo per l'operazione. Gestisci sempre TimeoutCancellationException comunque — un vero timeout è una condizione a cui la tua app deve reagire.

Correlati

  • Come funziona il modello dei messaggi — la spiegazione completa del pattern del super-tipo.
  • Ciclo di vita della sessione e del login — una sessione mancante produce anch'essa nessuna risposta.