Skip to content

tests/rptest: exercise the fetch_controller_snapshot_rpc gate#31063

Open
dotnwat wants to merge 1 commit into
redpanda-data:devfrom
dotnwat:noah/exercise-fetch-controller-snapshot-gate
Open

tests/rptest: exercise the fetch_controller_snapshot_rpc gate#31063
dotnwat wants to merge 1 commit into
redpanda-data:devfrom
dotnwat:noah/exercise-fetch-controller-snapshot-gate

Conversation

@dotnwat

@dotnwat dotnwat commented Jul 9, 2026

Copy link
Copy Markdown
Member

What

fetch_controller_snapshot_rpc (v26.2) was the one v26.2-gated feature in
ManualFinalizationUpgradeTest marked as needing no perturbation coverage, on
the rationale that a gated broker's bootstrap is "indistinguishable from prior
behavior, with no distinctive signal to assert."

Re-examining the feature (added in #30396, "the great config bootstrap fix"):
the gate is the client-side is_active() check in
application::bootstrap_controller_view. While the upgrade is unfinalized the
active version is held below the feature's require_version (v26_2_1), so the
gate is provably closed and a restarting broker falls through to its stale local
config cache — writing no new state, which is what keeps the unfinalized window
downgrade-safe. That behavior is distinguishable, and there is a signal: the
info-level Fetching controller snapshot from log, and the broker's
restart-required status.

Changes

  • Move fetch_controller_snapshot_rpc from PERTURB_ACKNOWLEDGED_FEATURES
    PERTURB_EXERCISED_FEATURES.
  • _exercise_fetch_controller_snapshot_rpc (unfinalized): bring one broker
    down, change a restart-requiring, non-feature-gated property
    (kafka_qdc_idle_depth) it will miss, then restart only that broker with the
    controller leader still up. Assert the gated outcome — no bootstrap fetch
    attempted, and the broker boots on the stale value still reporting
    restart-required. Keeping the leader reachable is what makes this a gate
    test: a wrongly-open gate would fetch the fresh value and flip restart to
    False.
  • _verify_fetch_controller_snapshot_rpc_working (post-finalize): confirm the
    gate opens (feature auto-activates). The active-state fetch mechanics are
    covered by ClusterConfigMultiNodeBootstrapTest.test_node_delayed_restart.

Note: the whole-cluster in-place restart already in _perturb_common cannot
exercise this — restarting every broker at once leaves no leader to fetch from
and changes no config, so it can't tell the gated path from the active one.

Testing

RP_GIT_RELEASED_VERSIONS="v26.1.10:v26.1.9" tools/dt run --stamp tests/rptest/tests/cluster_features_test.py::ManualFinalizationUpgradeTest.test_downgrade_before_finalize

  • Green (1 passed, 0 failed) against a real v26.1.10 ⇄ v26.2-dev upgrade.
  • Fault injection: inverting the restart is True assertion fails cleanly with
    restart-required was True, confirming the gated broker really does boot on
    stale config and that the check has teeth (and that the no-fetch log assertion
    passed right before it).

🤖 Generated with Claude Code

Copilot AI 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.

Pull request overview

Adds explicit perturbation coverage for the fetch_controller_snapshot_rpc feature gate during the unfinalized v26.1 → v26.2 upgrade path, ensuring a restarting broker does not perform the bootstrap controller-snapshot fetch until after finalization.

Changes:

  • Move fetch_controller_snapshot_rpc from acknowledged to exercised feature coverage.
  • Add _exercise_fetch_controller_snapshot_rpc() to validate the gate remains closed during the unfinalized window using a restart-required config change and a single-node restart while the controller leader remains reachable.
  • Add _verify_fetch_controller_snapshot_rpc_working() to confirm the feature activates after finalization.

# survives, so the change commits and a controller leader stays up.
self.redpanda.stop_node(node)
try:
prev = self.admin.get_cluster_config_status()[0]["config_version"]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch that this guard wasn't doing its job — though the root cause is a bit different from a stale [0]. config_frontend::patch unconditionally writes a new delta and returns an incremented version, so new_version > prev is effectively always true and cannot detect a value no-op even with a max() baseline (new_version = V+1 > max_applied = V). In this test the cleanup removes the property every cycle, so NEW_VALUE is always a real change; and if it somehow weren't, the restart is True assertion below would fail anyway. So I've dropped the ineffective guard (keeping new_version, which the catch-up wait_until still needs) rather than switching to max().

try:
self.admin.patch_cluster_config(remove=[PROPERTY])
except Exception as e:
self.logger.warn(f"cleanup: failed to reset {PROPERTY}: {e}")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done — switched to logger.warning.

@dotnwat dotnwat force-pushed the noah/exercise-fetch-controller-snapshot-gate branch from 964cfb8 to f8480f2 Compare July 9, 2026 22:12
It was previously acknowledged as needing no perturbation coverage, on
the rationale that a gated broker's bootstrap is indistinguishable from
prior behavior. It is distinguishable: exercise the gate in the
unfinalized state by bringing one broker down, changing a
restart-requiring property it misses, then restarting only that broker
with the controller leader still up. While gated the broker boots on its
stale local cache, reports restart-required, and attempts no
controller-snapshot fetch -- exactly the pre-upgrade behavior, which is
what keeps the unfinalized state downgrade-safe.

The whole-cluster restart in _perturb_common cannot exercise this: with
every broker down there is no leader to fetch from and no config delta to
reconcile. Also verify the gate opens post-finalize; the active-state
fetch mechanics are covered by
ClusterConfigMultiNodeBootstrapTest.test_node_delayed_restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dotnwat dotnwat force-pushed the noah/exercise-fetch-controller-snapshot-gate branch from f8480f2 to 0464bff Compare July 9, 2026 22:20
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

Retry command for Build#86943

please wait until all jobs are finished before running the slash command

/ci-repeat 1
skip-redpanda-build
skip-units
skip-rebase
tests/rptest/tests/cluster_features_test.py::ManualFinalizationUpgradeTest.test_downgrade_before_finalize

@vbotbuildovich

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#86943
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(FAIL) ManualFinalizationUpgradeTest test_downgrade_before_finalize null integration https://buildkite.com/redpanda/redpanda/builds/86943#019f48fd-1aff-4981-a71a-6dc019fe827d 8/11 Test FAILS after retries.Significant increase in flaky rate(baseline=0.0000, p0=0.0000, reject_threshold=0.0100) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ManualFinalizationUpgradeTest&test_method=test_downgrade_before_finalize
FLAKY(PASS) SimpleEndToEndTest test_relaxed_acks {"write_caching": false} integration https://buildkite.com/redpanda/redpanda/builds/86943#019f48fd-1b02-4d88-90a6-10e27683d9c5 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0048, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=SimpleEndToEndTest&test_method=test_relaxed_acks

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.

3 participants