Surfaced while fixing the use-club verify probe (#173). Every Dispatcher call goes out on Client.default with no timeout of its own, so against a host that accepts nothing (a black-holed IP, a wedged box, a wrong api_url) the CLI sits on zio-http's 30s connect default before reporting anything.
Measured with a stub that failed to accept:
$ ccas club remove devon-chess
error: cannot reach http://127.0.0.1:8099 — start a server with 'ccas server up'.
(connection timed out after 30000 ms: /127.0.0.1:8099)
The message is good; the 30s wait before it is not. A refused connection is instant (the common "server is down" case), so this only bites a reachable-but-not-accepting host — but that is exactly the misconfigured-api_url case a new user hits.
Note the non-obvious part
use-club's probe now bounds itself, and getting that right needed more than adding .timeout:
.provide(Client.default)
.disconnect // plain `timeout` AWAITS the interrupted fiber
.timeout(VerifyTimeout) // and must sit OUTSIDE `provide` to cover layer acquire/release
Without disconnect the "2s" bound measured 31s, because NettyConnectionPool.createChannel is uninterruptible with a 30s connect default and timeout waits for that region to unwind. Any fix here needs the same treatment — a naive .timeout on the Dispatcher calls will not do what it looks like it does.
Suggested shape
A connect/response bound configured once where the client is built, rather than sprinkled per call site — Dispatcher.dispatch already has the single .provide(Client.default) chokepoint. Consider zio.http.ZClient.Config connection timeout instead of a ZIO-level timeout, which would bound the connect natively and avoid the uninterruptible-unwind problem entirely. Long-running streaming follows (ccas logs, job following) must be exempt — they are meant to stay open for the life of a job, so any bound belongs on connect, not on the overall request.
Surfaced while fixing the
use-clubverify probe (#173). EveryDispatchercall goes out onClient.defaultwith no timeout of its own, so against a host that accepts nothing (a black-holed IP, a wedged box, a wrongapi_url) the CLI sits on zio-http's 30s connect default before reporting anything.Measured with a stub that failed to accept:
The message is good; the 30s wait before it is not. A refused connection is instant (the common "server is down" case), so this only bites a reachable-but-not-accepting host — but that is exactly the misconfigured-
api_urlcase a new user hits.Note the non-obvious part
use-club's probe now bounds itself, and getting that right needed more than adding.timeout:Without
disconnectthe "2s" bound measured 31s, becauseNettyConnectionPool.createChannelis uninterruptible with a 30s connect default andtimeoutwaits for that region to unwind. Any fix here needs the same treatment — a naive.timeouton the Dispatcher calls will not do what it looks like it does.Suggested shape
A connect/response bound configured once where the client is built, rather than sprinkled per call site —
Dispatcher.dispatchalready has the single.provide(Client.default)chokepoint. Considerzio.http.ZClient.Configconnection timeout instead of a ZIO-leveltimeout, which would bound the connect natively and avoid the uninterruptible-unwind problem entirely. Long-running streaming follows (ccas logs, job following) must be exempt — they are meant to stay open for the life of a job, so any bound belongs on connect, not on the overall request.