Skip to content

Container state is only refreshed by a 20s poll; the agent never pushes Docker events #162

Description

@rodriguecyber

Difficulty: Advanced · You'll need: Go, TypeScript · Size: ~200 lines · Candidate for v0.2.0 — filed for the decision

What's going on

Container state in the dashboard is refreshed by polling, not by push:

  • apps/web/src/hooks/useContainers.tsrefetchInterval: 20_000
  • each poll calls GET /hosts/:id/containersAgentsGateway.requestContainerList → a fresh container.list over the socket → the agent runs docker ps and replies

Why it matters

  1. State is stale for up to 20 seconds. A container that crash-loops, OOMs or exits shows as running until the next poll. For a monitoring product that is the number the operator most needs to be current.
  2. It costs a docker ps per host per 20s, forever, whether or not anything changed and whether or not anyone is looking. With N hosts open in a dashboard tab, that is N calls every 20 seconds indefinitely.
  3. The platform already holds a live bidirectional socket to every host. Polling over a push-capable channel is the thing the WebSocket decision in docs/architecture.md was made to avoid — the same argument the "Why WebSocket?" table uses to reject agent-polls-REST.

What Docker offers

GET /events is a long-lived stream of container lifecycle events — start, die, stop, pause, unpause, destroy, health_status. The agent already knows how to consume a long-lived Docker stream: internal/logs/stream.go does exactly this for logs, including a decode goroutine, batching at 50 entries / 200ms, and idempotent teardown.

Sketch

  • Agent: subscribe to Docker events at startup, filtered to type=container. Debounce and batch, following logs/stream.go. Push a new container.event (or a re-sent container.listed) when the set changes.
  • Protocol: a new message type. Per design commitment 2 in docs/roadmap.md, new capability arrives as new type values — container.listed stays untouched, so an older agent that only answers polls keeps working.
  • Server: update ContainerInventoryService on receipt. Push to the browser over the existing SSE machinery, or keep polling the server's now-fresh cache — the second is a much smaller change and removes the per-poll socket round trip and docker ps.
  • Web: drop or lengthen refetchInterval once the cache is authoritative.

Decisions to settle first

  1. New message, or re-send container.listed? Re-sending the full list is simpler and idempotent; a delta event is cheaper on a host with many containers.
  2. How far does the push go? Agent→server is the valuable half. Server→browser can stay polled at first, since the server's cache would then be current.
  3. Does polling stay as a fallback? An agent that fails to open the events stream should still be listable. Keeping container.list as the fallback path is probably non-negotiable.
  4. Event storms. docker compose up on a 40-container stack emits hundreds of events in a second. Debouncing is required, not optional.

Why this is filed as a decision rather than a task

Everything container-related in v0.1.0 works; this changes how it stays current. It is the largest remaining gap in container management, but it is a design change to the refresh model, not a missing feature — so it likely belongs to v0.2.0. Filed so the trade-off is recorded rather than rediscovered.

Done when

  • A decision is recorded on all four questions above
  • Milestone agreed (v0.1.0 or v0.2.0)
  • If accepted: container state reflects a crash within ~1s, and the steady-state docker ps poll is gone

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:agentThe DockSight Agent (apps/agent)area:serverThe NestJS platform server (apps/server)area:webThe React dashboard (apps/web)enhancementNew feature or requestneeds-decisionBlocked on a project decision, not on code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions