Skip to content

fix(inventory): add diagnostics for negative resource accounting#414

Open
chalabi2 wants to merge 1 commit into
mainfrom
inventory-negative-state-clean
Open

fix(inventory): add diagnostics for negative resource accounting#414
chalabi2 wants to merge 1 commit into
mainfrom
inventory-negative-state-clean

Conversation

@chalabi2

@chalabi2 chalabi2 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces #379 with a diagnostic fix for akash-network/support#429.

This keeps raw operator inventory intact so negative resource accounting remains visible, while bid, metrics, and provider status consume a sanitized non-negative view.

It also logs forensic data when accounting goes negative: pod/PV identity, inventory_before, and the applied change. Rancher storage accounting now uses signed resource quantities instead of uint64 arithmetic so underflows stay visible instead of becoming huge positive values.

Fixes akash-network/support#429.

Validation

  • GOWORK=off go test ./operator/inventory -count=1
  • GOWORK=off go test ./cluster/... ./operator/inventory -count=1
  • git diff --check

Keep raw operator inventory intact so negative resource accounting remains visible
for debugging, while status, metrics, and bidding consume a sanitized view.

Add diagnostics around pod and storage subtraction to log the previous
inventory state and the applied change when accounting goes negative.

Replaces #379.
Closes akash-network/support#429

Signed-off-by: Joseph Chalabi <chalabi.joseph@gmail.com>
@chalabi2 chalabi2 requested a review from a team as a code owner June 23, 2026 17:09
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR introduces structured diagnostic logging for negative resource quantities in inventory management, replaces silent NLZ-clamping with log-and-keep subtraction helpers across node, Ceph, and Rancher storage operators, adds a sanitized safe inventory copy to the kube client that gates Adjust/Snapshot/Metrics, and sanitizes cluster snapshots returned by the provider status API.

Changes

Negative inventory quantity sanitization and diagnostic logging

Layer / File(s) Summary
Diagnostics infrastructure
operator/inventory/diagnostics.go, operator/inventory/diagnostics_test.go
New file defines error sentinels, JSON-serializable structs for allocation change records and negative resource entries, subStorageAllocatedResource (subtracts and logs when result is negative), logNegativeClusterInventory (scans nodes/storage and logs negative quantities), Ceph/Rancher log formatters, and two unit tests validating logging behavior and field contents.
Node pod resource subtraction with logging
operator/inventory/node-discovery.go, operator/inventory/node-discovery_test.go
Replaces subAllocatedNLZ clamping with subAllocatedResource that logs errNegativeAllocatedResource and preserves the negative value. Introduces podKey for stable namespace/name Pod keying. EmptyDir memory uses the new helper. Table-driven tests cover CPU, GPU, and memory cases.
Ceph/Rancher storage allocation using diagnostic subtraction
operator/inventory/ceph.go, operator/inventory/rancher.go
rancherStorage.allocated changes from uint64 to resource.Quantity. Add/modify/backfill paths use Quantity.Add. Delete paths in Ceph and Rancher call subStorageAllocatedResource; Ceph adds a missing-storage-class guard. Metrics emission uses params.allocated.Value().
Cluster state wires logNegativeClusterInventory
operator/inventory/state.go
Derives a named logr logger in clusterState.run() and calls logNegativeClusterInventory on each signal event before publishing retained cluster inventory.
Safe sanitized inventory state in kube client
cluster/kube/operators/clients/inventory/client.go, cluster/kube/operators/clients/inventory/inventory.go, cluster/kube/operators/clients/inventory/client_test.go
Adds safe inventoryV1.Cluster field to inventory struct. safeDup() copies both Cluster and safe. Sanitization helpers clamp negatives into safe. Adjust clones from safeDup() and commits to inv.safe; Snapshot() and Metrics() operate on safe. Tests verify raw state stays negative while public methods return sanitized values.
Provider status API sanitizes cluster snapshot
cluster/inventory.go, cluster/inventory_test.go
getStatusV1 sanitizes the cluster snapshot via sanitizeStatusCluster before returning the provider.Inventory. New sanitization helpers clamp negative resource.Quantity fields to zero. Tests verify all affected fields are zeroed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • troian

🐇 A rabbit hops through the cluster one day,
Spotted some numbers gone negative — nay!
With a clamp and a log and a safe little store,
No allocations below zero anymore.
The ledger stays honest, the pod watchers cheer,
Sanitized quantities: nothing to fear! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description is directly related to the changeset, explaining the diagnostic fix approach and providing context about issue #429.
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.
Title check ✅ Passed The title clearly summarizes the main change: adding diagnostic logging for negative resource accounting in inventory systems.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch inventory-negative-state-clean

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.

@chalabi2 chalabi2 changed the title fix(inventory): preserve raw negative state fix(inventory): add diagnostics for negative resource accounting Jun 23, 2026

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
operator/inventory/node-discovery.go (1)

782-805: 🎯 Functional Correctness | 🟠 Major

Move EmptyDir accounting out of the container loop in both functions.

pod.Spec.Volumes is pod-scoped, but the volumes loop is nested inside the container loop. This causes memory accounting to be processed once per container instead of once per pod. Multi-container pods will incorrectly add or subtract memory multiple times, creating false accounting.

This bug exists in both addPodAllocatedResources (line 735-741) and subPodAllocatedResources (line 799-805). Move both volumes loops outside their respective container loops so they execute once per pod.

🤖 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 `@operator/inventory/node-discovery.go` around lines 782 - 805, The volumes
loop that handles EmptyDir memory accounting is incorrectly nested inside the
container loop in both the subPodAllocatedResources and addPodAllocatedResources
functions. Since pod.Spec.Volumes is pod-scoped and not container-scoped, this
causes the memory accounting to be processed once per container instead of once
per pod, leading to incorrect totals for multi-container pods. Move the entire
volumes loop (the for loop iterating over pod.Spec.Volumes) outside and after
the container loop by unindenting it one level, so it executes once per pod
rather than once per container, in both functions.
🧹 Nitpick comments (2)
operator/inventory/node-discovery_test.go (1)

76-204: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression case for memory.emptydir with multi-container pods.

This table currently validates cpu/gpu/memory requests but does not exercise the new EmptyDir subtraction path. A dedicated multi-container case will lock in correct pod-scoped EmptyDir accounting.

🤖 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 `@operator/inventory/node-discovery_test.go` around lines 76 - 204, Add a new
test case to the tests table in TestSubPodAllocatedResourcesLogsAndKeepsNegative
function to validate the EmptyDir subtraction path with multi-container pods.
Create a test case entry with appropriate fields (name, resourceName,
requestQuantity, changeResource, before, subtracted, after, setAllocated,
allocated callbacks) that exercises memory.emptydir resource handling
specifically with a pod containing multiple containers in the Spec.Containers
field to ensure correct pod-scoped EmptyDir accounting.
operator/inventory/state.go (1)

84-84: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid re-logging identical negative snapshots on every signal.

This branch can emit the same large inventory/negative_resources payload repeatedly while negatives persist. Consider deduping by last logged fingerprint or throttling to reduce log churn.

🤖 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 `@operator/inventory/state.go` at line 84, The logNegativeClusterInventory
function call is logging identical negative resource snapshots repeatedly
whenever negative inventory persists, causing excessive log output. To fix this,
implement deduplication logic in the logNegativeClusterInventory function by
either tracking the last logged fingerprint of the negative resources and only
logging when the fingerprint changes, or by implementing a throttling mechanism
to limit how frequently the same negative snapshot is logged. This will reduce
log churn while still capturing important state changes.
🤖 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.

Outside diff comments:
In `@operator/inventory/node-discovery.go`:
- Around line 782-805: The volumes loop that handles EmptyDir memory accounting
is incorrectly nested inside the container loop in both the
subPodAllocatedResources and addPodAllocatedResources functions. Since
pod.Spec.Volumes is pod-scoped and not container-scoped, this causes the memory
accounting to be processed once per container instead of once per pod, leading
to incorrect totals for multi-container pods. Move the entire volumes loop (the
for loop iterating over pod.Spec.Volumes) outside and after the container loop
by unindenting it one level, so it executes once per pod rather than once per
container, in both functions.

---

Nitpick comments:
In `@operator/inventory/node-discovery_test.go`:
- Around line 76-204: Add a new test case to the tests table in
TestSubPodAllocatedResourcesLogsAndKeepsNegative function to validate the
EmptyDir subtraction path with multi-container pods. Create a test case entry
with appropriate fields (name, resourceName, requestQuantity, changeResource,
before, subtracted, after, setAllocated, allocated callbacks) that exercises
memory.emptydir resource handling specifically with a pod containing multiple
containers in the Spec.Containers field to ensure correct pod-scoped EmptyDir
accounting.

In `@operator/inventory/state.go`:
- Line 84: The logNegativeClusterInventory function call is logging identical
negative resource snapshots repeatedly whenever negative inventory persists,
causing excessive log output. To fix this, implement deduplication logic in the
logNegativeClusterInventory function by either tracking the last logged
fingerprint of the negative resources and only logging when the fingerprint
changes, or by implementing a throttling mechanism to limit how frequently the
same negative snapshot is logged. This will reduce log churn while still
capturing important state changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1adbed34-6fd1-4f4d-8933-4e24331e3bef

📥 Commits

Reviewing files that changed from the base of the PR and between 0e0fd8a and e8d8f36.

📒 Files selected for processing (12)
  • cluster/inventory.go
  • cluster/inventory_test.go
  • cluster/kube/operators/clients/inventory/client.go
  • cluster/kube/operators/clients/inventory/client_test.go
  • cluster/kube/operators/clients/inventory/inventory.go
  • operator/inventory/ceph.go
  • operator/inventory/diagnostics.go
  • operator/inventory/diagnostics_test.go
  • operator/inventory/node-discovery.go
  • operator/inventory/node-discovery_test.go
  • operator/inventory/rancher.go
  • operator/inventory/state.go

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.

Inventory operator reports absurd GPU counts

1 participant