From b3d8ab77191e914807a771bc96a4b5aea917dc00 Mon Sep 17 00:00:00 2001 From: Colin Adler Date: Mon, 17 Oct 2022 18:31:29 -0500 Subject: [PATCH] fix: agent stats websocket blocking until next interval For `httpapi.Heartbeat` to work properly, a call to `wsjson.Read` must always be in flight, otherwise pings can't be exchanged. This caused the ping loop to hang until the next stat interval. Since the loop was blocking until the next interval it would also never exit until it fired. --- coderd/workspaceagents.go | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index fb7f765cc7519..250b4c73ae3f9 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -757,18 +757,30 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques // Allow overriding the stat interval for debugging and testing purposes. timer := time.NewTicker(api.AgentStatsRefreshInterval) - for { - err := wsjson.Write(ctx, conn, codersdk.AgentStatsReportRequest{}) - if err != nil { - api.Logger.Debug(ctx, "write report request", slog.Error(err)) - conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("write report request: %s", err)) - return + defer timer.Stop() + + go func() { + for { + err := wsjson.Write(ctx, conn, codersdk.AgentStatsReportRequest{}) + if err != nil { + conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("write report request: %s", err)) + return + } + + select { + case <-timer.C: + continue + case <-ctx.Done(): + conn.Close(websocket.StatusNormalClosure, "") + return + } } - var rep codersdk.AgentStatsReportResponse + }() + for { + var rep codersdk.AgentStatsReportResponse err = wsjson.Read(ctx, conn, &rep) if err != nil { - api.Logger.Debug(ctx, "read report response", slog.Error(err)) conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("read report response: %s", err)) return } @@ -827,14 +839,6 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques return } } - - select { - case <-timer.C: - continue - case <-ctx.Done(): - conn.Close(websocket.StatusNormalClosure, "") - return - } } }