fix: lower the consumed cycles gauge on a full refund - #11274
Merged
Conversation
`SystemState::observe_consumed_cycles_with_use_case()` maintains two kinds of metrics: the gauges (`consumed_cycles` and `consumed_cycles_by_use_cases`), which are raised by the prepayment and lowered by the refund; and the monotonic counters (`consumed_cycles_by_use_cases_as_counters`), which for the refundable use cases (`Instructions` and `RequestAndResponseTransmission`) are only bumped at refund time, by `prepayment - refund`. The early return conflated the two: for a refund equal to its prepayment it skipped the update altogether, so the gauges kept the prepayment even though nothing was consumed in the end. The inflated value also outlives the canister, as `CanisterManager::delete_canister()` folds `consumed_cycles_by_use_cases` into the subnet level metrics. Skip only if there is nothing to record at all, i.e. if both the prepayment and the refund are zero. Non-refundable use cases are unaffected, as a zero prepayment implies a zero refund for them, and `NominalCycles` saturates on subtraction, so a refund against a freshly created entry cannot underflow. Tests: the new `full_refund_resets_consumed_cycles` refunds a prepayment in full and checks that the balance and both gauges are back to where they started and that the counter did not move. It covers both refundable use cases under both cost schedules. The gauges are kept in nominal cycles, which the cost schedule does not affect, so a full refund under `Free` hits the same skipped update; it is in fact the more interesting case, as `Free` waives the real amount and hence leaves the metrics as the only observable of the prepayment. The assertion on the balance after the prepayment (which drops by the real amount under `Normal` and stays put under `Free`) pins down that difference. Each of the four combinations fails without the fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes consumed-cycle gauges remaining inflated after a full refund.
Changes:
- Updates metric handling to process equal prepayment/refund amounts.
- Adds coverage for both refundable use cases and cost schedules.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
rs/replicated_state/src/canister_state/system_state.rs |
Corrects the metric-update early return. |
rs/replicated_state/src/canister_state/tests.rs |
Tests full-refund balance, gauges, and counters. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
✅ No security or compliance issues detected. Reviewed everything up to 594a004. Security Overview
Detected Code Changes
|
eichhorl
approved these changes
Aug 24, 2026
…cles Address review feedback: prepay for the same use case once more without ever refunding it, so the full refund is expected to bring the gauges back down to that outstanding prepayment rather than all the way to zero. This pins down that the refund subtracts the refunded amount instead of dropping or clearing the use case entry, which a zero baseline cannot distinguish. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SystemState::observe_consumed_cycles_with_use_case()maintains two kinds of metrics: the gauges (consumed_cyclesandconsumed_cycles_by_use_cases), which are raised by the prepayment and lowered by the refund; and the monotonic counters (consumed_cycles_by_use_cases_as_counters), which for the refundable use cases (InstructionsandRequestAndResponseTransmission) are only bumped at refund time, byprepayment - refund.The early return conflated the two: for a refund equal to its prepayment it skipped the update altogether, so the gauges kept the prepayment even though nothing was consumed in the end. The inflated value also outlives the canister, as
CanisterManager::delete_canister()foldsconsumed_cycles_by_use_casesinto the subnet level metrics.Skip only if there is nothing to record at all, i.e. if both the prepayment and the refund are zero. Non-refundable use cases are unaffected, as a zero prepayment implies a zero refund for them, and
NominalCyclessaturates on subtraction, so a refund against a freshly created entry cannot underflow.Tests: the new
full_refund_resets_consumed_cyclesrefunds a prepayment in full and checks that the balance and both gauges are back to where they started and that the counter did not move. It covers both refundable use cases under both cost schedules. The gauges are kept in nominal cycles, which the cost schedule does not affect, so a full refund underFreehits the same skipped update; it is in fact the more interesting case, asFreewaives the real amount and hence leaves the metrics as the only observable of the prepayment. The assertion on the balance after the prepayment (which drops by the real amount underNormaland stays put underFree) pins down that difference. Each of the four combinations fails without the fix.