Skip to content

Expose container block I/O counters in stats (utilization.disk_io)#198

Open
flxmrk wants to merge 1 commit into
pelican-dev:mainfrom
flxmrk:feat/container-disk-io-stats
Open

Expose container block I/O counters in stats (utilization.disk_io)#198
flxmrk wants to merge 1 commit into
pelican-dev:mainfrom
flxmrk:feat/container-disk-io-stats

Conversation

@flxmrk

@flxmrk flxmrk commented Jul 13, 2026

Copy link
Copy Markdown

What

Adds cumulative container block-I/O counters to the stats payload:

"utilization": {
  "network": { "rx_bytes": 27353515, "tx_bytes": 132234063 },
  "disk_io": { "read_bytes": 0, "write_bytes": 81649664 }
}

Why

Docker's stats stream (which pollResources already 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 NetworkStats shape:

  • environment/stats.go: new DiskIoStats{read_bytes, write_bytes} on Stats as disk_io
  • environment/docker/stats.go: sum BlkioStats.IoServiceBytesRecursive into it; ops matched case-insensitively since cgroup v1 reports Read/Write while v2 (io.stat) reports read/write
  • server/resources.go: zero the counters in Reset()

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_bytes by ~47 MB in one reading; steamcmd install traffic and zero-read steady state (fully page-cached world) both report as expected. Websocket stats events carry the field as well since they serialize the same struct.

Summary by CodeRabbit

  • New Features

    • Added cumulative disk I/O metrics to resource usage data, including read and write byte totals.
  • Bug Fixes

    • Disk I/O counters now reset correctly when resource usage is reset, preventing stale values from carrying over.

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).
@flxmrk
flxmrk requested a review from a team as a code owner July 13, 2026 02:15
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Docker statistics now expose cumulative disk read/write bytes, populate them from Docker block I/O data, and clear them when resource usage resets.

Changes

Disk I/O statistics

Layer / File(s) Summary
Disk I/O data contract and collection
environment/stats.go, environment/docker/stats.go
Adds JSON-serialized disk read/write counters and accumulates Docker block I/O entries using case-insensitive operation matching.
Disk I/O reset handling
server/resources.go
Resets disk read and write counters alongside existing resource usage values.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a rabbit counting bytes in a burrow so bright,
Read hops by day and write hops by night.
Docker brings counters, neatly in line,
Reset clears the slate when the server resigns.
Carrots for stats—what a wonderful design!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: exposing container block I/O counters in stats under utilization.disk_io.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
server/resources.go (1)

71-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add 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 win

Add regression coverage for operation casing and aggregation.

Test both Read/Write and read/write entries, including multiple device entries, and assert the resulting disk_io.read_bytes and disk_io.write_bytes totals.

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2070e and 61352fe.

📒 Files selected for processing (3)
  • environment/docker/stats.go
  • environment/stats.go
  • server/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!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant