Work Intent: fix frozen NetIO/BlockIO in dockerContainerStats subscription
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 113
- Forks
- 22
- Avg merge
- 10h 40m
- Merged PRs (30d)
- 13
Description
Overview
Fix #2007: the dockerContainerStats GraphQL subscription emits CPU updates correctly but the cumulative NetIO and BlockIO fields stay frozen at the first sample for the lifetime of the subscription. Consumers that derive per-second rates from consecutive emissions always read 0 B/s — verified against a live Unraid 7.3 box with an actively downloading qBittorrent container (docker stats --no-stream returned the same 40.1GB / 220GB across 12 s while /containers/<id>/stats on the Docker socket showed +109 MB rx in 3 s = 36 MB/s).
Root cause: DockerStatsService.startStatsStream() spawns execa('docker', ['stats', '--format', ..., '--no-trunc']) and parses each output line. The docker CLI's "live" mode keeps the cumulative counters from its initial snapshot — they don't refresh across output ticks the way the per-container /stats socket endpoint does.
Technical Approach
Rewrite DockerStatsService to stream from the Docker daemon socket directly using dockerode (already a dependency, used by DockerEventService):
startStatsStream()callsdocker.listContainers()and opens onecontainer.stats({ stream: true })socket per running container. It also subscribes todocker.getEvents({ filters: { type: ['container'] } })so streams are added onstartand torn down ondie/stop/kill/destroy.- Each stats chunk is parsed: CPU% via the standard
((cpu_delta / system_delta) × online_cpus × 100)formula, memory used asusage − stats.cache, sums overnetworksandblkio_stats.io_service_bytes_recursive, formatted with the existing binary units (KiB / MiB / GiB) so the GraphQL response shape is unchanged. stopStatsStream()destroys every active socket plus the events stream soOnModuleDestroyreleases all resources.- Reuses the existing
getDockerClient()helper so the socket path stays in one place.
The change is internal to the service — the DockerContainerStats model, the GraphQL schema, the resolver and the pubsub channel are all untouched. Consumers see fresh values without any client-side change.
Implementation already prepared on jandrop/api:fix/docker-stats-cli-cache for reference:
- 196 lines added / 76 removed in
docker-stats.service.ts - 22 vitest cases in a new
docker-stats.service.spec.tscovering CPU formula + edge cases, memory minus cache, network sum, blkio reads/writes, docker events (start adds, die/stop/kill/destroy remove), malformed JSON resilience, idempotentstartStatsStream,OnModuleDestroycleanup pnpm lint,pnpm type-check,pnpm testall clean (1991 tests pass)
Scope
- API
- Plugin
- Web UI
- Build/Deploy Process
- Documentation
Timeline & Impact
- Estimated time: implementation already done locally; ~1 day for review iteration + any reviewer-requested changes.
- Impact: behaviour fix only —
DockerContainerStatsGraphQL response shape is identical, just with non-frozen values. No new dependencies (dockerodeis already used byDockerEventServiceon the same socket). - Risk: low. Falls back to the existing fail-on-error path; events stream and per-container streams are independently recoverable.
Pre-submission Checklist
- I have searched for similar work/issues
- I understand this needs approval before starting
- I am willing to make adjustments based on feedback
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with DockerStatsService.startStatsStream() and the existing getDockerClient() helper, then compare dockerode usage in DockerEventService. Review docker-stats.service.spec.ts and run the listed pnpm checks. Done means fresh NetIO and BlockIO values, correct CPU and memory calculations, container event handling, cleanup, and an unchanged GraphQL response shape.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100