Skip to content

fix(node): re-anchor cNIGHT observation window near tip on from-genesis sync#1836

Draft
rsporny wants to merge 4 commits into
mainfrom
worktree-rspo-fix-1835-cnight-window
Draft

fix(node): re-anchor cNIGHT observation window near tip on from-genesis sync#1836
rsporny wants to merge 4 commits into
mainfrom
worktree-rspo-fix-1835-cnight-window

Conversation

@rsporny

@rsporny rsporny commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Overview

A validator that syncs from genesis ends up holding an oversized in-memory cNIGHT observation window that never narrows, unlike a restart-with-state node.

The observation sliding window trims to a lookback behind the follower in steady state. That lookback was a large fixed window. On a restart with state the cache starts behind the runtime and re-anchors narrowly near tip; but a from-genesis node advances the follower contiguously the whole way, never takes that narrow re-anchor, and so trails the tip by the entire window forever. It ends up holding a ~100k-block window (hundreds of MB) where a restarted peer holds a few thousand, with the retained width depending on how the node happened to sync rather than on the chain.

Fix

Re-anchor the window to a small reorg-safety margin behind the follower — the Cardano security parameter plus stability margin (the maximum reorg depth), which the node already computes. The runtime only ever reads at or ahead of the follower, so anything further back is never served from cache; the only reason to keep any is a reorg, and reorgs can't run deeper than that margin (deeper re-reads fall back to the live source). A from-genesis node now converges to the same near-tip window as a restart-with-state one.

This makes the fixed-size window knob redundant, so cnight_observation_window_size (and DEFAULT_WINDOW_SIZE) are removed — the reorg margin the node already tracks is the right lookback, and there's nothing to tune. Existing configs that still carry the key keep loading (it's ignored; no deny_unknown_fields).

No consensus/correctness impact: the window is node-local and out-of-window reads fall back to the live db-sync source.

Scope / note on the latency figures in #1835

The per-refresh db-sync pull is already incremental ([old_end + 1, target_end], clamped to the stable tip), so the oversized window cost memory, not steady-state query time — a caught-up node already queries only a few blocks near tip. The wide/slow refreshes in the issue were captured during a concurrent db-sync incident (the issue disclaims those numbers). So this change fixes the window-tracking and memory behavior and makes from-genesis match restart; it is not, on its own, a steady-state latency fix.

🗹 TODO before merging

  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason:
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • Updated AGENTS.md if build commands, architecture, or workflows changed
  • No new todos introduced

🧪 Testing Evidence

cargo test -p midnight-primitives-mainchain-follower --lib passes, including a regression test (plan_refresh_from_genesis_steady_state_tracks_follower_not_genesis) that a caught-up from-genesis node trims to the reorg-margin lookback rather than staying anchored at genesis. cargo check -p midnight-node, cargo fmt --check, and cargo clippy --tests on both changed crates are clean.

  • Additional tests are provided (if possible)

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other:
  • N/A

Links

Closes #1835

…sis syncs narrow

The cNIGHT observation sliding window trims to `follower - window_size`
during steady operation, so `window_size` is a reorg-safety lookback
behind the follower, not a general cache size. A validator that syncs
from genesis advances the follower contiguously and never takes
`plan_refresh`'s narrow jump re-anchor, so it keeps `window_size` blocks
behind the follower forever. At the old 100_000 default (100x the
runtime's own 1000-block observation window) that pinned it at a
~100k-wide window; every refresh then queried a Cardano range far wider
than a healthy peer's, overrunning the 6s slot and starving block
authoring until a manual restart.

Lower DEFAULT_WINDOW_SIZE to 10_000: 10x the runtime window and well
above Cardano's security parameter k (~2160), so it still covers the
deepest reorg while converging a from-genesis node to roughly the narrow
window a restart-with-state node already gets. No consensus impact — the
window is node-local and out-of-window reads fall back to the live db
source. Also corrects the misleading config/const docs and adds
regression tests.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Radosław Sporny <404@rspo.dev>
@rsporny rsporny requested a review from a team as a code owner July 6, 2026 21:35
@rsporny rsporny added the bot:ai-assisted Authored or substantially edited by an AI agent label Jul 6, 2026
@datadog-official

This comment has been minimized.

PR: #1836

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Radosław Sporny <404@rspo.dev>
@rsporny rsporny changed the title fix(node): shrink cNIGHT observation window default to keep from-genesis syncs narrow fix(node): re-anchor cNIGHT observation window near tip on from-genesis sync Jul 7, 2026
@rsporny rsporny marked this pull request as draft July 7, 2026 09:32

@m2ux m2ux left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review Summary

PR: #1836 — fix(node): re-anchor cNIGHT observation window near tip on from-genesis sync
Reviewers: Code Review Agent · Test Suite Review Agent · Validation Agent · Strategic Review Agent
Reports: Prior Feedback Triage · Code Review · Test Suite Review · Strategic Review
Branch / Commit reviewed: worktree-rspo-fix-1835-cnight-window @ ee928b30 (merge-base b246fb5a)
Date: 2026-07-07

Executive Summary

This draft PR addresses #1835: from-genesis validators can keep a ~100k-block cNIGHT observation lookback in steady state, making background refreshes heavy enough to starve block authoring. The branch at ee928b30 lowers DEFAULT_WINDOW_SIZE to 10_000, clarifies that window_size is a reorg lookback (not general cache size), and adds two solid plan_refresh regression tests. The mechanism is sound — contiguous steady-state trim already sets width to window_size; the bug was the default magnitude.

Important: The PR description describes a further refactor (re-anchor via computed reorg margin, remove the config knob) that is not in this diff. Treat current commits as an interim constant mitigation; align description or push follow-on before marking ready.

Overall Rating: Comment Only (draft; no correctness blocker on current commits; process/doc alignment needed before merge)


Prior Feedback Triage

# Prior Comment Author Disposition Reasoning
1 CI Changes Check failed on 858fd4c datadog-official Superseded Minimized/outdated; check-changes passes on latest head
2 Thumbs-up reaction chatgpt-codex-connector Refuted Not actionable
Finding Details

No human review threads or blocker-class comments. Rating cap unset.


Code Review Findings

# Finding Severity
1 PR body promises reorg-margin re-anchor + config removal — not in diff at ee928b30 High
2 REFRESH_THRESHOLD (10k) now equals DEFAULT_WINDOW_SIZE — confirm lookahead intent Medium
3 Constant-only fix vs issue "Expected" logic re-anchor — interim mitigation Medium
4 Change file title ("Shrink default") vs PR title ("re-anchor") Low
Finding Details

CR-1. PR description vs branch (High — process)

PR body: re-anchor to security parameter + stability margin; remove cnight_observation_window_size. Branch: DEFAULT_WINDOW_SIZE 100k→10k, docs, unit tests only.
Suggestion: Push follow-on commits or revise PR/change file to match landed behaviour before ready-for-review.

CR-2. REFRESH_THRESHOLD coupling (Medium)

Both constants are 10_000. Refresh target uses tip + REFRESH_THRESHOLD + stability_margin. Pre-existing but now coupled to lookback size.
Suggestion: Document relationship or derive threshold from window size.

CR-3. Fix class (Medium)

Issue #1835 expected re-anchor near tip (logic). Branch implements default reduction (mitigation #2). Regression test proves trim at 10k width, not production slot timing.
Suggestion: State interim intent in PR; optional follow-on using stability_margin as lookback.

CR-4. Title mismatch (Low)

Change file heading vs PR title — editorial only.

Verified correct: plan_refresh trim logic, reorg bound [2160, 20000], backward-compatible config, no consensus impact.


Test Review Findings

# Gap Severity
1 No test on RefreshContext::refresh / bulk_pull span after steady-state trim Medium
Finding Details

TR-1. Integration gap (Medium)

Unit tests cover plan_refresh only. Operational failure is wide bulk_pull during refresh.
Suggestion: Test recording pull block range on simulated caught-up from-genesis refreshes.

Strengths: plan_refresh_from_genesis_steady_state_tracks_follower_not_genesis, default_window_size_is_a_modest_reorg_lookback; 15/15 lib tests pass locally.


Validation Findings

# Check Severity
1 cargo test -p midnight-primitives-mainchain-follower --lib — 15/15 pass (local)
2 cargo fmt --check on touched crates — pass (local)
Finding Details

VF-1. Local validation (Pass)

All 15 unit tests in midnight-primitives-mainchain-follower pass; plan_refresh suite including new regressions green. Matches PR testing evidence.

CI on latest head: check-changes pass; several jobs still pending at review time (draft). No validation failures observed.


Branch Hygiene

# Item Severity
1 Branch 1 commit behind origin/main Warning
Finding Details

BH-1. Branch freshness (Warning)

One commit behind main. Rebase before merge and re-run CI.


Action Items

Must Address (Blocking):

  • None on current code correctness.

Should Address (Recommended):

  • Align PR description with branch diff or push the described reorg-margin refactor (CR-1)
  • Mark PR ready only after description/commits match (CR-1)
  • Rebase on main before merge (BH-1)

Could Address (Suggested):

  • Add integration test for refresh bulk_pull span (TR-1)
  • Document REFRESH_THRESHOLD vs DEFAULT_WINDOW_SIZE relationship (CR-2)
  • Align change file heading with PR title (CR-4)

Nice to Have (Optional):

  • Note interim vs complete fix relative to issue #1835 Expected (CR-3)

Review conducted via work-package workflow (review mode). Artifacts under .engineering/artifacts/planning/2026-07-07-review-midnight-node-pr-1836-cnight-window/.

…is sync

The sliding window trimmed to a large fixed lookback behind the follower. A
from-genesis node advances the follower contiguously and never takes the narrow
re-anchor a restart-with-state node does, so it trailed the tip by the whole
window forever instead of tracking it — holding a ~100k-block in-memory window
(hundreds of MB) where a restarted peer holds a few thousand.

Re-anchor to a small reorg-safety margin (cardano_security_parameter +
block_stability_margin) behind the follower instead. The runtime only reads at
or ahead of the follower, so anything further back is never served from cache;
the only reason to keep any is a reorg, which can't run deeper than that margin,
and deeper re-reads fall back to the live source. From-genesis now converges to
the same near-tip window as a restart.

Removes the now-redundant cnight_observation_window_size knob and
DEFAULT_WINDOW_SIZE; existing configs that still set the key keep loading (it is
ignored). No consensus impact — the window is node-local.

The per-refresh db-sync pull is already incremental, so the oversized window
cost memory, not steady-state query time; the slow refreshes in the linked
issue were during a concurrent db-sync incident.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Radosław Sporny <404@rspo.dev>
@rsporny rsporny force-pushed the worktree-rspo-fix-1835-cnight-window branch from 46f4895 to 5094975 Compare July 7, 2026 10:09
Rewrite the change file as a concise why/what/removed explanation, and tighten
the stability_margin and plan_refresh doc comments.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Radosław Sporny <404@rspo.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ai-assisted Authored or substantially edited by an AI agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cNIGHT observation sliding window stays at max width (100k) after a from-genesis sync — degraded block authoring

2 participants