fix: resolve ERR_INSUFFICIENT_RESOURCES connection exhaustion

- Add concurrency limiter (max 4 GET requests) to API layer, leaving
  slots for SSE and health checks. Write ops bypass the limiter.
- Add AbortController to ContainerStats, project detail page, and
  dashboard to cancel in-flight requests on navigation/unmount.
- Move global SSE connection from layout to events page (only consumer).
- Add 30s heartbeat to SSE endpoint to detect zombie connections.
- Serialize dashboard project fetches to avoid parallel burst.
- Rebuild frontend in dev-server.sh so go:embed stays in sync.
This commit is contained in:
2026-04-13 00:12:14 +03:00
parent 791cd4d6af
commit 96fd910603
7 changed files with 233 additions and 87 deletions
+9
View File
@@ -7,6 +7,7 @@ import (
"log/slog"
"net/http"
"strings"
"time"
"github.com/go-chi/chi/v5"
@@ -158,6 +159,10 @@ func (s *Server) streamEvents(w http.ResponseWriter, r *http.Request) {
})
defer s.eventBus.Unsubscribe(sub)
// Periodic heartbeat so the browser detects dead connections.
heartbeat := time.NewTicker(30 * time.Second)
defer heartbeat.Stop()
ctx := r.Context()
for {
select {
@@ -168,6 +173,10 @@ func (s *Server) streamEvents(w http.ResponseWriter, r *http.Request) {
return
}
writeSSE(w, flusher, evt)
case <-heartbeat.C:
// SSE comment line — keeps the connection alive without triggering onmessage.
fmt.Fprintf(w, ": heartbeat\n\n")
flusher.Flush()
}
}
}