Expose container block I/O counters in stats (utilization.disk_io)#198
Expose container block I/O counters in stats (utilization.disk_io)#198flxmrk wants to merge 1 commit into
Conversation
Docker's stats stream already carries cgroup block I/O counters, but wings
drops them when building environment.Stats. Surface them as cumulative
utilization.disk_io.{read_bytes,write_bytes}, mirroring the network
counters, so panels can derive per-server disk throughput from deltas the
same way they do for rx/tx. Ops are matched case-insensitively to cover
cgroup v1 (Read/Write) and v2 (read/write from io.stat).
📝 WalkthroughWalkthroughDocker statistics now expose cumulative disk read/write bytes, populate them from Docker block I/O data, and clear them when resource usage resets. ChangesDisk I/O statistics
Estimated code review effort: 2 (Simple) | ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
server/resources.go (1)
71-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a reset regression test for the new counters.
Initialize nonzero disk counters, call
ResourceUsage.Reset, and verify both values return to zero.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/resources.go` around lines 71 - 72, Add a regression test for ResourceUsage.Reset that initializes DiskIo.ReadBytes and DiskIo.WriteBytes to nonzero values, invokes Reset, and asserts both counters are reset to zero.environment/docker/stats.go (1)
94-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd regression coverage for operation casing and aggregation.
Test both
Read/Writeandread/writeentries, including multiple device entries, and assert the resultingdisk_io.read_bytesanddisk_io.write_bytestotals.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@environment/docker/stats.go` around lines 94 - 103, Add regression coverage for the Docker block I/O aggregation handled in the stats collection path: provide both uppercase “Read”/“Write” and lowercase “read”/“write” operations across multiple device entries, then assert that disk_io.read_bytes and disk_io.write_bytes contain the summed totals.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@environment/docker/stats.go`:
- Around line 94-103: Add regression coverage for the Docker block I/O
aggregation handled in the stats collection path: provide both uppercase
“Read”/“Write” and lowercase “read”/“write” operations across multiple device
entries, then assert that disk_io.read_bytes and disk_io.write_bytes contain the
summed totals.
In `@server/resources.go`:
- Around line 71-72: Add a regression test for ResourceUsage.Reset that
initializes DiskIo.ReadBytes and DiskIo.WriteBytes to nonzero values, invokes
Reset, and asserts both counters are reset to zero.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 413b4f12-c9ea-47a5-a5d1-6aa57bf8cea0
📒 Files selected for processing (3)
environment/docker/stats.goenvironment/stats.goserver/resources.go
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-02T13:53:08.995Z
Learnt from: parkervcp
Repo: pelican-dev/wings PR: 171
File: server/power.go:190-203
Timestamp: 2026-03-02T13:53:08.995Z
Learning: In the server package, when quotas are enabled via config.Get().System.Quotas.Enabled, the disk space check using used >= s.DiskSpace() does not require a special guard for unlimited-disk scenarios (DiskSpace() <= 0). The filesystem handles such cases, so the existing check is sufficient. Apply this pattern to similar quota-related disk checks in the server package and ensure tests/docs reflect that unlimited-disk behavior is governed by the filesystem, not by an extra guard in code.
Applied to files:
server/resources.go
🔇 Additional comments (2)
environment/stats.go (1)
23-27: LGTM!Also applies to: 36-40
environment/docker/stats.go (1)
7-7: LGTM!
What
Adds cumulative container block-I/O counters to the stats payload:
Why
Docker's stats stream (which
pollResourcesalready consumes) carries the cgroup block-I/O counters, but wings currently drops them. Surfacing them lets panels and API consumers show per-server disk throughput exactly the way they already derive network rates — deltas between readings of a cumulative counter. Disk I/O is often the first thing saturating a node packed with game servers (world autosaves, chunk generation, steamcmd updates), and today there's no per-server visibility into it.How
+24 lines, additive only — mirrors the existing
NetworkStatsshape:environment/stats.go: newDiskIoStats{read_bytes, write_bytes}onStatsasdisk_ioenvironment/docker/stats.go: sumBlkioStats.IoServiceBytesRecursiveinto it; ops matched case-insensitively since cgroup v1 reportsRead/Writewhile v2 (io.stat) reportsread/writeserver/resources.go: zero the counters inReset()Existing consumers are unaffected (unknown JSON fields are ignored by the panel).
Testing
Running in production on a live node (built from v1.0.0-beta26 + this patch, cgroup v2, Docker 28). Counters verified against real workloads: a forced Palworld world save moved
write_bytesby ~47 MB in one reading; steamcmd install traffic and zero-read steady state (fully page-cached world) both report as expected. Websocketstatsevents carry the field as well since they serialize the same struct.Summary by CodeRabbit
New Features
Bug Fixes