Skip to content

Add fenced Raft member replacement operation#1122

Open
bootjp wants to merge 6 commits into
mainfrom
feature/raft-member-replacement
Open

Add fenced Raft member replacement operation#1122
bootjp wants to merge 6 commits into
mainfrom
feature/raft-member-replacement

Conversation

@bootjp

@bootjp bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a resumable, explicitly fenced same-ID voter replacement command
  • expose the durable configuration index and pending change state through RaftAdmin status
  • enforce surviving quorum, old-owner fencing, learner catch-up, promotion, normal restart, and application write/read verification
  • document the single-group operational contract and failure handling

This is stacked on #1118 because the replacement deploy uses its fresh learner join mode.

Safety

  • no automatic repair from CANDIDATE or health-check state
  • no no-quorum or multi-group recovery
  • every membership RPC uses previous_index
  • archive is the default; deletion is explicit
  • final application verification is mandatory

Verification

  • go test -count=1 . -run TestRaftMemberReplaceScript
  • go test -race -count=1 . ./cmd/raftadmin ./internal/raftadmin -run "TestRaftMemberReplaceScript|TestExecuteCommandLeaderAndStateOutput|TestServerMapsEngineAdminMethods"
  • go test -count=1 ./internal/raftengine/etcd
  • golangci-lint run ./... --timeout=5m
  • make -C proto gen (includes breaking-change check)
  • full parallel go test ./... compiled all packages but hit an etcd test failure under concurrent package load; the etcd package passed when rerun alone

Author: bootjp

Summary by CodeRabbit

  • 新機能

    • raftadmin status に構成インデックスと構成変更保留状態を表示する項目を追加。
    • Raft メンバーを安全に置換できる、ドライラン・フェンス・再開に対応した運用スクリプトを追加。
  • ドキュメント

    • Raft メンバー置換の手順、安全条件、障害時の再開方法を dokument化。
  • テスト

    • メンバー置換の正常系、失敗時の停止、フェンス、再開処理を検証するテストを追加。

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@bootjp, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ebcedd1-61a8-4178-bbf1-5308ae246c17

📥 Commits

Reviewing files that changed from the base of the PR and between df27d70 and 4d54f11.

📒 Files selected for processing (8)
  • docs/design/2026_07_18_implemented_fenced_raft_member_replacement.md
  • internal/raftadmin/server.go
  • internal/raftadmin/server_test.go
  • internal/raftengine/engine.go
  • internal/raftengine/etcd/engine.go
  • internal/raftengine/etcd/engine_test.go
  • raft_member_replace_script_test.go
  • scripts/raft-member-replace.sh
📝 Walkthrough

Walkthrough

Raftの設定インデックスと保留中の構成変更をステータスAPI・CLIへ追加し、フェンス付き同一IDメンバー置換を再開可能な状態機械として実装しました。関連する安全検証、障害復旧テスト、運用ドキュメントも追加されています。

Changes

Raftステータスとメンバー置換

Layer / File(s) Summary
ステータスフィールドの伝播
proto/service.proto, internal/raftengine/..., internal/raftadmin/..., cmd/raftadmin/...
configuration_indexpending_conf_change をエンジンから管理API、CLI出力まで伝播し、各段階のテストを追加しました。
置換ワークフローの実装
scripts/raft-member-replace.sh
引数検証、フェンス、投票者削除、データ処理、learner追加・追いつき・昇格、再デプロイ、安定性検証を状態ファイル付きの段階処理として実装しました。
検証と運用手順
raft_member_replace_script_test.go, docs/design/..., docs/raft_learner_operations.md, scripts/rolling-update.env.example
通常完了、フェンス、クォーラム不足、構成変更、応答喪失後の再開、ロック保護をテストし、実行条件と運用手順を文書化しました。

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant ReplacementScript as raft-member-replace.sh
  participant RaftAdmin as raftadmin
  participant RollingUpdate as rolling-update.sh
  Operator->>ReplacementScript: --execute with target and fence controls
  ReplacementScript->>RaftAdmin: read status and configuration
  ReplacementScript->>RaftAdmin: remove voter and add learner
  ReplacementScript->>RollingUpdate: deploy learner
  ReplacementScript->>RaftAdmin: wait for catch-up and promote learner
  ReplacementScript->>RollingUpdate: redeploy promoted voter
  ReplacementScript-->>Operator: report stability and application verification
Loading

Possibly related PRs

  • bootjp/elastickv#532: raftengine.Status の追加ステータス項目を扱う同じステータス報告領域の変更です。
  • bootjp/elastickv#972: raftadmin status のCLI経路を変更する関連PRです。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、フェンス付きのRaftメンバー置換オペレーション追加という主変更を適切に要約しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bootjp

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Implemented the fenced same-ID replacement state machine with quorum, identity, fencing, configuration-index, catch-up, restart, and application verification gates.

Verification on 9bfc113:

  • replacement lifecycle, response-loss resume, candidate exclusion, and lock ownership tests pass
  • RaftAdmin server/CLI and engine status propagation tests pass
  • affected tests pass under race
  • internal/raftengine/etcd passes in full when run alone
  • golangci-lint reports 0 issues
  • proto generation and breaking-change check pass

@codex review

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces a new script, scripts/raft-member-replace.sh, along with design documentation and tests to automate safe, fenced, and resumable same-ID voter replacement in a Raft group. To support this workflow, the Raft status response has been extended with configuration_index and pending_conf_change fields across the proto, engine, and admin CLI. Feedback on the changes identifies a high-severity issue in the remote SSH execution block of the replacement script, where a standalone sudo -n true check under set -e can cause premature script termination if passwordless sudo is not configured. A suggestion is provided to safely evaluate the sudo check within the conditional statement.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread scripts/raft-member-replace.sh
@bootjp
bootjp force-pushed the feature/raft-member-replacement branch from 9bfc113 to a23368c Compare July 18, 2026 15:03
@bootjp

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Updated the signed head to a23368c after an additional shell audit.

  • ShellCheck now passes with no findings
  • replaced ambiguous boolean chains with explicit validation branches
  • operator commands use one-shot environment assignments without mutating parent control state
  • replacement lifecycle tests and golangci-lint still pass

Previous reviews are stale against the amended head.

@codex review

@bootjp

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Addressed the applicable high-severity remote sudo finding in signed commit 55d863c.

Verification:

  • ShellCheck: no findings
  • replacement lifecycle/resume/quorum/lock tests: pass
  • commit hook golangci-lint: 0 issues

@codex review

@bootjp

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55d863c1e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh Outdated
@bootjp

bootjp commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

Addressed all latest-head P1/P2 findings in signed commit bc84f3e.\n\nChanges:\n- force execute rollouts even when deployment inventory contains DRY_RUN=true\n- persist and compare the complete membership signature plus configuration index across Remove/Add/Promote\n- ignore replacement controls sourced from deploy.env and require fresh invocation-level authorization\n- reject completed state reuse\n- normalize DATA_DIR and reject filesystem root\n\nVerification:\n- ShellCheck: no findings\n- replacement lifecycle, response-loss resume, config-drift rejection, invocation authorization, quorum, and lock tests: pass\n- affected root tests under race: pass\n- RaftAdmin and etcd engine packages under race: pass\n- golangci-lint commit hook: 0 issues\n- git diff --check: pass\n- the concurrent full local suite hit unrelated adapter/store election-resource failures while other design worktrees were compiling; current-head CI is the authoritative full-suite rerun\n\n@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc84f3e587

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/raftengine/etcd/engine.go
Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh
Base automatically changed from feature/raft-learner-join-members to main July 19, 2026 05:56

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Addressed all current-head P2 findings in signed commit df27d70.

Changes:

  • route RemoveServer through the shared pending configuration fence
  • canonicalize and persist DATA_DIR, rejecting resume drift
  • require a verified process stop after external fencing and before membership removal
  • make stage-4 learner rollout response loss resumable by idempotent target-only redeploy

Verification:

  • shellcheck scripts/raft-member-replace.sh
  • bash -n scripts/raft-member-replace.sh
  • go test -race -count=1 . -run TestRaftMemberReplaceScript
  • go test -race -count=1 ./internal/raftengine/etcd ./cmd/raftadmin ./internal/raftadmin
  • git diff --check
  • golangci-lint commit hook: 0 issues
  • semantic caller audit: add, promote, and remove membership proposals now share proposeMembershipChange and its pending fence

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/raft-member-replace.sh`:
- Line 345: bash 4.3 以前の set -u 環境で空配列展開が失敗しないよう、scripts/raft-member-replace.sh
の345行では NODE_IDS の要素数が0より大きい場合 בלבד contains_value を実行し、768-771行では L788/L793 の
sudo_cmd 配列展開を未設定・空配列対応の形式に置き換える。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd8a6fb2-0233-4c51-b57c-9480bdf574b3

📥 Commits

Reviewing files that changed from the base of the PR and between 6d8ee63 and df27d70.

⛔ Files ignored due to path filters (1)
  • proto/service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (13)
  • cmd/raftadmin/main.go
  • cmd/raftadmin/main_test.go
  • docs/design/2026_07_18_implemented_fenced_raft_member_replacement.md
  • docs/raft_learner_operations.md
  • internal/raftadmin/server.go
  • internal/raftadmin/server_test.go
  • internal/raftengine/engine.go
  • internal/raftengine/etcd/engine.go
  • internal/raftengine/etcd/engine_test.go
  • proto/service.proto
  • raft_member_replace_script_test.go
  • scripts/raft-member-replace.sh
  • scripts/rolling-update.env.example

Comment thread scripts/raft-member-replace.sh Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df27d7089e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh Outdated
Comment thread internal/raftengine/etcd/engine.go
@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Addressed every latest-head P1/P2 and compatibility finding in signed commit b1e6e5a.

Changes:

  • require successful Docker inspection before and after process stop
  • accept the metadata-before-stage resume window only when the complete membership signature and index match
  • measure the fence interval from the first failed probe without pre-crediting a poll
  • reject every add/promote/remove proposal while a configuration change is pending
  • guard empty Bash arrays on Bash 3.2/4.3

Verification:

  • shellcheck scripts/raft-member-replace.sh
  • /bin/bash -n and bash -n
  • /bin/bash 3.2 dry-run
  • go test -race -count=1 . -run TestRaftMemberReplaceScript
  • go test -race -count=1 ./internal/raftengine/etcd ./cmd/raftadmin ./internal/raftadmin
  • semantic caller audit: AddVoter, AddLearner, PromoteLearner, and RemoveServer all share the pending-conf rejection
  • golangci-lint commit hook: 0 issues
  • git diff --check

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1e6e5a40e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/raft-member-replace.sh Outdated
Comment thread scripts/raft-member-replace.sh
Comment thread internal/raftengine/etcd/engine.go
Comment thread internal/raftengine/etcd/engine.go
Comment thread scripts/raft-member-replace.sh
Comment thread scripts/raft-member-replace.sh Outdated
@bootjp

bootjp commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Addressed all six latest-head P2 findings in signed commit 4d54f11.

Changes:

  • require exact configuration-index equality for metadata-only resume, while retaining strict advancement for pre-change adoption
  • measure the final stability window from the first healthy observation
  • reconcile lost or overwritten pending configuration proposals without clearing unstable or persisted configuration entries
  • serialize membership RPCs and reject pending changes before encryption pre-registration
  • adopt the verified new leader's configuration token after transfer when the membership signature is unchanged
  • directly validate follower-reported leader endpoints before accepting discovery

Verification:

  • go test -count=1 ./internal/raftadmin ./internal/raftengine/etcd ./cmd/raftadmin
  • go test -count=1 . -run TestRaftMemberReplaceScript
  • go test -race -count=1 ./internal/raftadmin ./internal/raftengine/etcd ./cmd/raftadmin
  • go test -race -count=1 . -run TestRaftMemberReplaceScript
  • targeted golangci-lint: 0 issues
  • commit hook golangci-lint: 0 issues
  • git diff --check
  • caller audit: AddVoter, AddLearner, PromoteLearner, and RemoveServer converge on the fenced membership proposal path; runtime RPC entry points are serialized in RaftAdmin

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 4d54f118ff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

1 participant