Skip to content

feat: optional fail recovery when post-failover hooks fail (#110) - #115

Merged
renecannao merged 2 commits into
masterfrom
feat/issue-110-post-failover-hook-failure
Jul 28, 2026
Merged

feat: optional fail recovery when post-failover hooks fail (#110)#115
renecannao merged 2 commits into
masterfrom
feat/issue-110-post-failover-hook-failure

Conversation

@renecannao

@renecannao renecannao commented Jul 27, 2026

Copy link
Copy Markdown

Summary

Implements #110.

  • New config: FailRecoveryOnFailedPostFailoverProcesses (default false)
  • When true, failed successful-path post hooks mark recovery unsuccessful:
    • PostFailoverProcesses
    • PostMasterFailoverProcesses
    • PostIntermediateMasterFailoverProcesses
    • PostGracefulTakeoverProcesses
  • Persists IsSuccessful=false, returns error to caller, emits recovery metrics after hooks
  • Does not undo topology changes already performed
  • Async hooks (command &) still ignore exit codes
  • Default false keeps legacy “hooks best-effort” behavior

Tests

  • go test ./go/logic/ — policy on/off, persist error, executeProcesses failOnError
  • go test ./go/config/ — default false

Docs / samples

  • docs/configuration-recovery.md
  • sample conf JSON files

Test plan

  • Unit tests above
  • CI full suite
  • Optional manual: set flag true, PostFailoverProcesses: ["false"], trigger recovery → UI shows failed

Summary by CodeRabbit

  • New Features
    • Added FailRecoveryOnFailedPostFailoverProcesses option (default false) to mark recovery as unsuccessful when synchronous post-failover/post-success hooks fail.
    • Recovery now returns an error and records failed recovery metrics without rolling back already-applied topology changes.
    • Asynchronous hook commands (... &) still run independently and their failures are ignored.
  • Documentation
    • Updated recovery configuration docs to describe the new option and behaviors.
  • Tests
    • Added tests covering defaults and hook-failure outcomes across post-failover and graceful takeover flows.

Add FailRecoveryOnFailedPostFailoverProcesses (default false). When
enabled, non-zero exits from PostFailoverProcesses,
PostMasterFailoverProcesses, PostIntermediateMasterFailoverProcesses,
and PostGracefulTakeoverProcesses mark the recovery unsuccessful,
persist that state, return an error, and count metrics as failed.

Topology changes are not rolled back. Async hooks (&) remain best-effort.
Legacy behavior unchanged when the flag is false.

Includes unit tests for executeProcesses failOnError and the policy
wrapper, sample config, and docs.
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4707adb6-0ace-4474-880e-56cac0f971f7

📥 Commits

Reviewing files that changed from the base of the PR and between f69c942 and 63ed903.

📒 Files selected for processing (2)
  • go/config/config.go
  • go/logic/topology_recovery.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/config/config.go
  • go/logic/topology_recovery.go

📝 Walkthrough

Walkthrough

Adds a disabled-by-default configuration option that controls whether failed synchronous post-failover hooks mark recovery unsuccessful. Centralized handling persists the updated state, propagates errors, updates metrics, and is integrated across failover and graceful takeover paths.

Changes

Post-failover hook failure handling

Layer / File(s) Summary
Configuration contract and defaults
go/config/config.go, conf/orchestrator-sample*.conf.json, docs/configuration-recovery.md, go/config/config_post_failover_test.go
Adds FailRecoveryOnFailedPostFailoverProcesses, defaults it to false, documents synchronous and asynchronous hook behavior, and tests the default.
Centralized hook failure processing
go/logic/topology_recovery.go, go/logic/post_failover_hooks_test.go
Adds configurable failure detection, unsuccessful-state marking, persistence, audit stubbing, and tests for policy, execution, persistence errors, and successful hooks.
Recovery flow integration
go/logic/topology_recovery.go, go/logic/topology_recovery_postgresql.go
Uses centralized post-success hook handling across failover, PostgreSQL promotion, graceful takeover, and metric emission paths.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • ProxySQL/orchestrator#99: Updates post-success hook error propagation in the PostgreSQL graceful primary switchover flow.

Poem

A rabbit hops where failover hooks run,
Errors now mark the recovery done—
Unsuccessful, persisted, clear,
While async troubles disappear.
“Config decides!” the bunny sings,
As metrics follow changed outcomes’ wings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 The title accurately summarizes the main change: optional failure of recovery when post-failover hooks fail.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-110-post-failover-hook-failure

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.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
go/logic/topology_recovery_postgresql.go (1)

96-113: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Remove the duplicate PostFailoverProcesses call from checkAndRecoverDeadPrimary.

PostgreSQL/DeadPrimary recovery is dispatched through executeCheckAndRecoverFunction, which already runs config.Config.PostFailoverProcesses once for any successful recovery with SuccessorKey set. checkAndRecoverDeadPrimary should only run the type-specific PostMasterFailoverProcesses here, matching the MySQL recovery paths. Otherwise enabled hooks run twice, including duplicate persistResolvedRecoveryFn/state flips on hook failure.

🤖 Prompt for 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.

In `@go/logic/topology_recovery_postgresql.go` around lines 96 - 113, Remove the
PostFailoverProcesses invocation from checkAndRecoverDeadPrimary’s successful
promotedInstance branch. Keep assigning topologyRecovery.SuccessorKey and
running the type-specific PostMasterFailoverProcesses hook there, while leaving
the unsuccessful recovery handling unchanged so executeCheckAndRecoverFunction
can run PostFailoverProcesses once.
🧹 Nitpick comments (1)
go/logic/topology_recovery.go (1)

2002-2036: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Metrics-after-hooks reordering is correct; combined-hook-failure error swallowing needs a fix.

The reordering so topologyRecovery.IsSuccessful reflects hook failures before metrics emission is correct. Two secondary notes:

  1. At line 2020-2024, err = hookErr only fires if err == nil, so if the recovery-type-specific hook (e.g. PostMasterFailoverProcesses) already failed and set err, a second, independent PostFailoverProcesses failure is silently dropped from the returned error (see consolidated comment).
  2. duration now includes the time spent running post-failover hooks (previously it likely only measured the topology recovery itself). If external hooks are slow, RecoveryDurationSeconds will no longer represent pure recovery time — worth a one-line doc/metric-description update if this is intentional.
🤖 Prompt for 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.

In `@go/logic/topology_recovery.go` around lines 2002 - 2036, Update the
PostFailoverProcesses error handling in the topology recovery flow so a hookErr
is not silently discarded when err already contains a recovery-specific hook
failure; combine or otherwise preserve both errors in the returned result. Keep
the existing assignment behavior when no earlier error exists, and update the
RecoveryDurationSeconds metric description to state that post-failover hook time
is included.
🤖 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.

Outside diff comments:
In `@go/logic/topology_recovery_postgresql.go`:
- Around line 96-113: Remove the PostFailoverProcesses invocation from
checkAndRecoverDeadPrimary’s successful promotedInstance branch. Keep assigning
topologyRecovery.SuccessorKey and running the type-specific
PostMasterFailoverProcesses hook there, while leaving the unsuccessful recovery
handling unchanged so executeCheckAndRecoverFunction can run
PostFailoverProcesses once.

---

Nitpick comments:
In `@go/logic/topology_recovery.go`:
- Around line 2002-2036: Update the PostFailoverProcesses error handling in the
topology recovery flow so a hookErr is not silently discarded when err already
contains a recovery-specific hook failure; combine or otherwise preserve both
errors in the returned result. Keep the existing assignment behavior when no
earlier error exists, and update the RecoveryDurationSeconds metric description
to state that post-failover hook time is included.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6dc0a1e9-cd1b-4563-a819-641209408521

📥 Commits

Reviewing files that changed from the base of the PR and between bea29e5 and f69c942.

📒 Files selected for processing (8)
  • conf/orchestrator-sample-sqlite.conf.json
  • conf/orchestrator-sample.conf.json
  • docs/configuration-recovery.md
  • go/config/config.go
  • go/config/config_post_failover_test.go
  • go/logic/post_failover_hooks_test.go
  • go/logic/topology_recovery.go
  • go/logic/topology_recovery_postgresql.go

@renecannao
renecannao merged commit 80ac2f9 into master Jul 28, 2026
22 checks passed
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