Kill ckpool on bitcoind failure so miners can failover
When bitcoind is unreachable for 3+ consecutive polls and no block submission is pending, SIGTERM the ckpool process so miners disconnect and failover to backup pools. Adds a red dashboard banner when Bitcoin Core is down.
This commit is contained in:
@@ -190,6 +190,12 @@ type Aggregator struct {
|
||||
// refresh. Used by the WebSocket hub to push updates to clients.
|
||||
OnRefresh func(Snapshot)
|
||||
|
||||
// KillCKPool, if set, is called when bitcoind has been unreachable
|
||||
// for several consecutive polls and no block submission is pending.
|
||||
// Killing ckpool disconnects miners so they can failover to other
|
||||
// pools instead of mining stale work.
|
||||
KillCKPool func() error
|
||||
|
||||
mu sync.RWMutex
|
||||
snap Snapshot
|
||||
blocks []BlockRecord
|
||||
@@ -231,6 +237,12 @@ type Aggregator struct {
|
||||
// escalate to WARN.
|
||||
ckFailStreak int
|
||||
|
||||
// btcFailStreak counts consecutive refreshes where bitcoind was
|
||||
// unreachable. After a threshold, if no block submission is pending,
|
||||
// we kill the ckpool process so miners can failover to other pools.
|
||||
btcFailStreak int
|
||||
ckpoolKilled bool // true after we sent SIGTERM, reset on bitcoind recovery
|
||||
|
||||
// readyOnce + ready closes the Ready() channel exactly once after
|
||||
// the first refresh completes. main blocks briefly on this so the
|
||||
// HTTP server doesn't serve a never-refreshed (all-zeros) snapshot.
|
||||
@@ -424,6 +436,11 @@ func (a *Aggregator) refresh(ctx context.Context) {
|
||||
if bi, err := a.RPC.GetBlockchainInfo(ctx); err == nil {
|
||||
next.Chain = bi
|
||||
next.BitcoinOK = true
|
||||
if a.btcFailStreak > 0 {
|
||||
a.Log.Info("bitcoind recovered", "after_failures", a.btcFailStreak)
|
||||
}
|
||||
a.btcFailStreak = 0
|
||||
a.ckpoolKilled = false
|
||||
// Track when the tip height last changed.
|
||||
if bi.Blocks != a.lastTipHeight {
|
||||
a.lastTipHeight = bi.Blocks
|
||||
@@ -436,7 +453,8 @@ func (a *Aggregator) refresh(ctx context.Context) {
|
||||
next.NetworkHashrateHs = nh
|
||||
}
|
||||
} else {
|
||||
a.Log.Warn("bitcoind getblockchaininfo failed", "err", err)
|
||||
a.btcFailStreak++
|
||||
a.Log.Warn("bitcoind getblockchaininfo failed", "err", err, "streak", a.btcFailStreak)
|
||||
if next.LastError == "" {
|
||||
next.LastError = err.Error()
|
||||
}
|
||||
@@ -675,6 +693,26 @@ func (a *Aggregator) refresh(ctx context.Context) {
|
||||
cb(pushed)
|
||||
}
|
||||
a.markReady()
|
||||
|
||||
// Kill ckpool when bitcoind is unreachable so miners can failover.
|
||||
// Conditions: 3+ consecutive failures, no pending block submission,
|
||||
// haven't already killed it, and a kill callback is configured.
|
||||
const btcFailThreshold = 3
|
||||
submitGap := next.BlockSubmitAttempts - next.BlockSubmitsConfirmed
|
||||
if a.btcFailStreak >= btcFailThreshold && !a.ckpoolKilled && a.KillCKPool != nil {
|
||||
if submitGap > 0 {
|
||||
a.Log.Warn("bitcoind down but block submission pending, keeping ckpool alive",
|
||||
"submit_gap", submitGap, "streak", a.btcFailStreak)
|
||||
} else {
|
||||
a.Log.Warn("bitcoind unreachable, killing ckpool so miners can failover",
|
||||
"streak", a.btcFailStreak)
|
||||
if err := a.KillCKPool(); err != nil {
|
||||
a.Log.Error("failed to kill ckpool", "err", err)
|
||||
} else {
|
||||
a.ckpoolKilled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AckBestDiff records the current best_diff as acknowledged so the UI
|
||||
|
||||
Reference in New Issue
Block a user