Skip to content

feat(nns): tolerate XRC failures when updating maturity modulation#10210

Open
jasonz-dfinity wants to merge 4 commits into
masterfrom
handle-failure-of-retrieving-rates
Open

feat(nns): tolerate XRC failures when updating maturity modulation#10210
jasonz-dfinity wants to merge 4 commits into
masterfrom
handle-failure-of-retrieving-rates

Conversation

@jasonz-dfinity
Copy link
Copy Markdown
Contributor

Why

XRC sometimes fails to return a rate for the current day, possibly for
several consecutive days — most recently between 2026-05-07 and 2026-05-10,
when no rate was returned for four consecutive days even though XRC had
succeeded on every one of the preceding 365 days. The previous
implementation got stuck retrying the same failing day every 60s and
refused to update maturity modulation until the full 365-day window was
filled, so a single persistent gap stalled the whole feature.

NNS1-4319: https://dfinity.atlassian.net/browse/NNS1-4319

What

  • compute_average_icp_xdr_rate now uses Last Observation Carried Forward:
    for each day in the window, fall back to the most recent prior day's rate
    if that day is missing. The fallback is compute-only — nothing is written
    back to the price history.
  • Add an in-memory attempted_through_day cursor on the timer task. Failed
    fetches advance the cursor so the next tick moves on instead of looping
    on the failing day; missing days are retried on subsequent midnight runs
    until they fall out of the lookback window.
  • update_rates_buffer no longer evicts by size. A new evict_stale_rates
    runs at the top of each tick: it drops entries before the lookback
    window, but keeps the single most-recent pre-window entry as an LOCF
    seed so leading gaps in the window can still be filled.

Testing

  • LOCF unit tests for compute_average_icp_xdr_rate covering middle gaps,
    trailing gaps, multi-day walk-back, no-prior-rate skip, and full windows.
  • Eviction unit tests for evict_stale_rates covering seed retention,
    sparse-within-window preservation, and lone-seed cases.
  • execute() async tests chaining ticks to verify cursor advancement on
    failure (failed day stays absent in the buffer — fallback is not
    persisted) and cursor reset after a completed round.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the NNS governance timer task that backfills ICP/XDR rates (via XRC) and computes maturity modulation so that transient/multi-day XRC gaps no longer stall maturity modulation updates.

Changes:

  • Switch compute_average_icp_xdr_rate to Last Observation Carried Forward (LOCF) so averages remain usable across missing days.
  • Add an in-memory attempted_through_day cursor so repeated failures advance to later missing days instead of retrying the same day every tick.
  • Replace size-based buffer eviction with timestamp-based eviction (evict_stale_rates) that retains one pre-window “seed” entry for LOCF.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
rs/nns/governance/src/timer_tasks/update_icp_xdr_rate_related_data.rs Implements LOCF averaging, cursor-based retry progression, and timestamp-anchored eviction to keep maturity modulation progressing despite XRC gaps.
rs/nns/governance/src/timer_tasks/update_icp_xdr_rate_related_data_tests.rs Adds/updates unit + async tests for LOCF behavior, eviction rules, and cursor advancement/reset across ticks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rs/nns/governance/src/timer_tasks/update_icp_xdr_rate_related_data.rs Outdated
Comment thread rs/nns/governance/src/timer_tasks/update_icp_xdr_rate_related_data_tests.rs Outdated
@jasonz-dfinity jasonz-dfinity requested a review from Copilot May 12, 2026 23:30
@jasonz-dfinity jasonz-dfinity marked this pull request as ready for review May 12, 2026 23:31
@jasonz-dfinity jasonz-dfinity requested a review from a team as a code owner May 12, 2026 23:31
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

This pull request changes code owned by the Governance team. Therefore, make sure that
you have considered the following (for Governance-owned code):

  1. Update unreleased_changelog.md (if there are behavior changes, even if they are
    non-breaking).

  2. Are there BREAKING changes?

  3. Is a data migration needed?

  4. Security review?

How to Satisfy This Automatic Review

  1. Go to the bottom of the pull request page.

  2. Look for where it says this bot is requesting changes.

  3. Click the three dots to the right.

  4. Select "Dismiss review".

  5. In the text entry box, respond to each of the numbered items in the previous
    section, declare one of the following:

  • Done.

  • $REASON_WHY_NO_NEED. E.g. for unreleased_changelog.md, "No
    canister behavior changes.", or for item 2, "Existing APIs
    behave as before.".

Brief Guide to "Externally Visible" Changes

"Externally visible behavior change" is very often due to some NEW canister API.

Changes to EXISTING APIs are more likely to be "breaking".

If these changes are breaking, make sure that clients know how to migrate, how to
maintain their continuity of operations.

If your changes are behind a feature flag, then, do NOT add entrie(s) to
unreleased_changelog.md in this PR! But rather, add entrie(s) later, in the PR
that enables these changes in production.

Reference(s)

For a more comprehensive checklist, see here.

GOVERNANCE_CHECKLIST_REMINDER_DEDUP

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines +336 to +337
fn test_compute_average_icp_xdr_rate_locf_fills_middle_gap() {
// 7-day window ending at day 100. Days 94-99 present (50_000), day 100 missing.
Comment on lines +207 to +215
let current_day = 1_000_u64;
let oldest_kept_day = current_day - (MAX_RATES_BUFFER_SIZE as u64 - 1); // 636
let mut rates: Vec<SampledPrice> = (0..=635)
.map(|d| SampledPrice {
timestamp_seconds: d * ONE_DAY_SECONDS,
xdr_permyriad_per_icp: 50_000,
})
.collect();
// Plus one entry at the boundary inside the window.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants