From 5bef729c52f6c81e98f3aceb9a56731f43a077e2 Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Tue, 26 May 2026 07:17:49 -0700 Subject: [PATCH] fix: bump cloud search timeout to 3s and map gRPC deadline to 504 (#4748) Co-authored-by: Claude Opus 4.7 (1M context) --- internal/web/cloud_search.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/web/cloud_search.go b/internal/web/cloud_search.go index 35725edc..babfab81 100644 --- a/internal/web/cloud_search.go +++ b/internal/web/cloud_search.go @@ -10,11 +10,15 @@ import ( "github.com/amir20/dozzle/internal/cloud" "github.com/rs/zerolog/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // cloudSearchTimeout caps the round-trip to Doligence Cloud. Search is on -// the keystroke path; we'd rather show "no results" than block typing. -const cloudSearchTimeout = 500 * time.Millisecond +// the keystroke path, but a cold Cloud-side query plus network RTT routinely +// blows past half a second, so 500ms produced spurious timeouts. 3s is still +// short enough that the (debounced) UI stays responsive. +const cloudSearchTimeout = 3 * time.Second // cloudSearchLogs proxies a search query to Doligence Cloud over the existing // authenticated gRPC connection. Identity is derived server-side from the @@ -85,7 +89,11 @@ func (h *handler) cloudSearchLogs(w http.ResponseWriter, r *http.Request) { writeError(w, http.StatusServiceUnavailable, "cloud not configured") return } - if errors.Is(err, context.DeadlineExceeded) { + // A blown deadline can surface two ways: as context.DeadlineExceeded + // when our own ctx fires first, or as a gRPC status with code + // DeadlineExceeded when the server-side deadline trips. status.Code + // walks the %w wrap chain, so both map to a 504. + if errors.Is(err, context.DeadlineExceeded) || status.Code(err) == codes.DeadlineExceeded { writeError(w, http.StatusGatewayTimeout, "cloud search timed out") return }