From 8e611840d16c1788f67b1ce1543b0b0cf1c6a8a9 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 2 Apr 2026 18:49:04 +0500 Subject: [PATCH] fix: add 30s timeout to PB HTTP client; halve heartbeat tick interval - storage/pocketbase.go: replace http.DefaultClient (no timeout) with a dedicated pbHTTPClient{Timeout: 30s} so a slow/hung PocketBase cannot stall the backend or runner indefinitely - runner/asynq_runner.go: heartbeat ticker was firing at StaleTaskThreshold (2 min) == the Docker healthcheck deadline, so a single missed tick would mark the container unhealthy; halved to StaleTaskThreshold/2 (1 min) --- backend/internal/runner/asynq_runner.go | 2 +- backend/internal/storage/pocketbase.go | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/internal/runner/asynq_runner.go b/backend/internal/runner/asynq_runner.go index 5609e51..115d3b6 100644 --- a/backend/internal/runner/asynq_runner.go +++ b/backend/internal/runner/asynq_runner.go @@ -78,7 +78,7 @@ func (r *Runner) runAsynq(ctx context.Context) error { // Write /tmp/runner.alive every 30s so Docker healthcheck passes in asynq mode. // This mirrors the heartbeat file behavior from the poll() loop. go func() { - heartbeatTick := time.NewTicker(r.cfg.StaleTaskThreshold) + heartbeatTick := time.NewTicker(r.cfg.StaleTaskThreshold / 2) defer heartbeatTick.Stop() for { select { diff --git a/backend/internal/storage/pocketbase.go b/backend/internal/storage/pocketbase.go index 53d724a..789462a 100644 --- a/backend/internal/storage/pocketbase.go +++ b/backend/internal/storage/pocketbase.go @@ -26,6 +26,11 @@ import ( // ErrNotFound is returned by single-record lookups when no record exists. var ErrNotFound = errors.New("storage: record not found") +// pbHTTPClient is a shared HTTP client with a 30 s timeout so that a slow or +// hung PocketBase never stalls the backend/runner process indefinitely. +// http.DefaultClient has no timeout and must not be used for PocketBase calls. +var pbHTTPClient = &http.Client{Timeout: 30 * time.Second} + // pbClient is the internal PocketBase REST admin client. type pbClient struct { baseURL string @@ -66,7 +71,7 @@ func (c *pbClient) authToken(ctx context.Context) (string, error) { } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := pbHTTPClient.Do(req) if err != nil { return "", fmt.Errorf("pb auth: %w", err) } @@ -104,7 +109,7 @@ func (c *pbClient) do(ctx context.Context, method, path string, body io.Reader) req.Header.Set("Content-Type", "application/json") } - resp, err := http.DefaultClient.Do(req) + resp, err := pbHTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("pb: %s %s: %w", method, path, err) }