Skip to content

[v25.3.x] admin/controller: expose raft0 cancel_reconfiguration - #31198

Merged
joe-redpanda merged 5 commits into
v25.3.xfrom
manual-backport-30918-v25.3.x-1
Jul 22, 2026
Merged

[v25.3.x] admin/controller: expose raft0 cancel_reconfiguration#31198
joe-redpanda merged 5 commits into
v25.3.xfrom
manual-backport-30918-v25.3.x-1

Conversation

@joe-redpanda

Copy link
Copy Markdown
Contributor

Backport of PR #30918
Also includes both raft gate-correctness fixes from #31022 (raft: hold gate across cancel/force, raft: hold gate in refresh_commit_index) — same root issue; the v26.1.x backport (#30945) carried the first of these as well.

Release Notes

Improvements

  • adds stuck raft0 escape hatches

Backport adaptations:

  • tests/rptest/tests/throttled_raft0_test.py: RedpandaService.remove_from_started_nodes has no reason parameter on this branch — dropped the reason arguments (kept as comments).
  • tests/rptest/services/admin.py: dev's MaybeNode alias doesn't exist here — used ClusterNode | None (this branch's existing convention) in the force_set_partition_replicas signature; rest of the typed signature kept verbatim.

Verified locally: bazel build of //src/v/redpanda/admin:admin, //src/v/raft, //src/v/cluster succeeds on this branch; branch-pinned bazel run //tools:clang_format produces no changes; ruff format --check (CI-pinned 0.12.10) passes on the touched python files.

bharathv and others added 5 commits July 20, 2026 14:23
Exploses an api which allows the caller to cancel the current raft0
reconfiguration. This operation preserves any additional enqueued
reconfigurations.

This is to be used as an emergency escape hatch if a raft0
reconfiguration is stuck such as the case of a dead learner.

(cherry picked from commit 9570661)
... via evil_mode

Adds an evil_mode parameter which allows partition_force_reconfiguration
to operate against the controller.

It replicates a fresh raft0 configuration whose revision is past the
current log tail, superseding every change currently enqueued in the
members_manager raft0 queue (the members backend drops them as stale).
If the new configuration removes the processing leader from the voter
set, it steps down so a survivor can take over.

This is a last ditch fix for controller raft issues

(cherry picked from commit aa2f903)
Adds ducktape coverage for the controller (raft0) reconfiguration admin
APIs exposed in this series:

- cancel_reconfiguration unwedges a stuck raft0 learner and drains the
  adds queued behind it, without stranding queued membership changes.
- successive cancels clear a whole backlog of dead learner adds.
- evil_mode force reconfiguration nukes the enqueued raft0 changes and
  removes stuck learners or a healthy voter; forcing away from the
  current leader steps it down and transfers leadership to a survivor.
- a force-removed voter is then cleaned out of the members table by a
  follow-up decommission.

(cherry picked from commit b2c3ba8)
If you're holding the lock, you should probably also hold the gate.
Both of these are top level lock requiring apis that were not taking the
gate leaving them vulnerable to shutdown races.

Solution: take the gate
(cherry picked from commit e44a8c2)
refresh_commit_index grabs the lock and yields while not holding a gate
holder. consensus can be torn down underneath

if you're holding a lock you should probably hold a gate

(cherry picked from commit dd3a5b8)
@joe-redpanda joe-redpanda added this to the v25.3.x-next milestone Jul 20, 2026
@joe-redpanda joe-redpanda added the kind/backport PRs targeting a stable branch label Jul 20, 2026
@joe-redpanda
joe-redpanda marked this pull request as ready for review July 21, 2026 17:08
@joe-redpanda
joe-redpanda requested a review from bharathv July 21, 2026 17:08
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#87486
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FAIL MasterTestSuite test_remote_partition_read_cached_index unit https://buildkite.com/redpanda/redpanda/builds/87486#019f85a6-b797-4868-ad1d-5f37960ba5d5 0/1
FLAKY(PASS) TxKafkaCompatTest test_concurrent_producer_ids {"metadata_quorum": "COMBINED_KRAFT"} integration https://buildkite.com/redpanda/redpanda/builds/87486#019f85cc-5dbb-4e0c-bd5d-77d3aacbd31a 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0000, 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=TxKafkaCompatTest&test_method=test_concurrent_producer_ids
FLAKY(PASS) ConsumerGroupBalancingTest test_coordinator_nodes_balance null integration https://buildkite.com/redpanda/redpanda/builds/87486#019f85be-3a66-4105-8c40-a3ebf95ec794 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0000, 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=ConsumerGroupBalancingTest&test_method=test_coordinator_nodes_balance

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

Backports and extends admin/raft changes to provide operational “escape hatches” for a wedged controller (raft0), including admin-triggered cancel/force reconfiguration, plus consensus lifecycle (gate) correctness fixes to prevent teardown during async operations.

Changes:

  • Extend admin partition endpoints to allow canceling raft0 reconfiguration and (with evil_mode=true) forcing raft0 replica set replacement.
  • Add consensus gate-holding around cancellation/refresh paths and introduce a replicated force-replace configuration API.
  • Expand raft0-throttling rptest coverage with new scenarios for cancel/force behavior and queued-add semantics.

Reviewed changes

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

Show a summary per file
File Description
tests/rptest/tests/throttled_raft0_test.py Refactors/expands raft0 wedge tests; adds new cancel/force scenarios and helper methods.
tests/rptest/services/admin.py Adds typed evil_mode support to force_set_partition_replicas via query params.
src/v/redpanda/admin/server.cc Maps raft::errc::invalid_configuration_update to HTTP 400.
src/v/redpanda/admin/partition.cc Enables controller (raft0) cancel and gated “evil mode” force reconfiguration paths.
src/v/raft/consensus.h Declares replicated force-replace configuration and op-lock acquisition helper using std::expected.
src/v/raft/consensus.cc Implements replicated force-replace; holds gate across cancel/refresh to avoid teardown races.
src/v/cluster/controller.h Exposes controller APIs for canceling and forcing raft0 reconfiguration.
src/v/cluster/controller.cc Implements raft0 cancel/force operations, including revision bumping for force-replace.

Comment on lines +174 to +176
def _joiner_in_brokers(self) -> bool:
"""True if the joiner appears in any started node's broker list"""
return self._node_in_brokers(self.JOINER_NODE_ID)
Comment on lines +384 to +386
self.redpanda._admin.patch_cluster_config(
upsert={"raft_learner_recovery_rate": 100 * 1024 * 1024}
)
Comment on lines +466 to +489
# Cancel the in-flight controller reconfiguration via the admin API.
# The request is routed to the raft0 leader rather than rejected.
def cancel_controller_reconfiguration() -> bool:
controller = self.redpanda.controller()
if controller is None:
return False
try:
self.redpanda._admin.cancel_partition_move(
namespace="redpanda",
topic="controller",
partition=0,
node=controller,
)
return True
except Exception as e:
self.logger.debug(f"cancel reconfiguration not yet accepted: {e}")
return False

wait_until(
cancel_controller_reconfiguration,
timeout_sec=MEDIUM_TIMEOUT.timeout_s,
backoff_sec=MEDIUM_TIMEOUT.backoff_s,
err_msg="controller reconfiguration cancel was never accepted",
)
Comment on lines +488 to +510
if (ntp == model::controller_ntp) {
// Reconfiguring the controller group is dangerous and gated behind an
// explicit evil_mode flag. It forcibly replaces raft0's configuration,
// blowing away any in-flight or enqueued raft0 reconfiguration.
if (!admin::get_boolean_query_param(*req, "evil_mode")) {
throw ss::httpd::bad_request_exception(
"Refusing to reconfigure the controller; pass evil_mode=true to "
"force a raft0 reconfiguration");
}
std::vector<model::node_id> nodes;
nodes.reserve(replicas.size());
for (const auto& bs : replicas) {
nodes.push_back(bs.node_id);
}
vlog(
adminlog.warn,
"evil_mode: forcing controller (raft0) reconfiguration to {}",
nodes);
auto err = co_await _controller->force_raft0_reconfiguration(
std::move(nodes));
co_await throw_on_error(*req, err, model::controller_ntp);
co_return ss::json::json_void();
}
@joe-redpanda
joe-redpanda merged commit e8f1962 into v25.3.x Jul 22, 2026
19 checks passed
@joe-redpanda
joe-redpanda deleted the manual-backport-30918-v25.3.x-1 branch July 22, 2026 18:34
@tyson-redpanda tyson-redpanda modified the milestones: v25.3.x-next, v25.3.16 Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/redpanda kind/backport PRs targeting a stable branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants