From 013624cf3d35423dfd7afbb0439f20888537c8d7 Mon Sep 17 00:00:00 2001 From: krynju Date: Fri, 3 Jul 2026 16:45:44 +0200 Subject: [PATCH] fix(client): abort-on-close watcher busy-spins while events are pending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The watcher added in #98 waited with `wait(stream_to); yield()` in a loop. `wait(::Channel)` returns as soon as data is *available*, so whenever an event sits in the channel before the consumer takes it, the loop spins hot — measured at 100% of a core in system time on a k8s watch stream. Poll `isopen` with a 250ms sleep instead; the watcher only exists to abort the read when the consumer walks away, so the added abort latency is irrelevant. Bumps to 0.2.6. Co-Authored-By: Claude Fable 5 --- Project.toml | 2 +- src/client/httplibs/juliaweb_http.jl | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 2cad120..4fda915 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,7 @@ keywords = ["Swagger", "OpenAPI", "REST"] license = "MIT" desc = "OpenAPI server and client helper for Julia" authors = ["JuliaHub Inc."] -version = "0.2.5" +version = "0.2.6" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/src/client/httplibs/juliaweb_http.jl b/src/client/httplibs/juliaweb_http.jl index a8e4975..c1b4e4c 100644 --- a/src/client/httplibs/juliaweb_http.jl +++ b/src/client/httplibs/juliaweb_http.jl @@ -373,9 +373,14 @@ function _http_streaming_request(ctx, method, url, headers, body, timeout, bytes # read-idle timeout. Mirrors the `:downloads` backend, which interrupts # its download task on channel close. try + # Block until the consumer closes the channel. Do NOT use + # `wait(stream_to)`: it returns as soon as data is AVAILABLE, so + # while an event sits in the channel not yet consumed, a + # wait+yield loop degenerates into a hot spin that burns a full + # core in scheduler/syscall overhead. Poll `isopen` instead; + # 250ms of extra abort latency is irrelevant here. while isopen(stream_to) - wait(stream_to) - yield() + sleep(0.25) end catch ex isa(ex, InvalidStateException) || rethrow(ex)