Defer dashboard RPCs until after ckpool notifier completes on tip change

On ZMQ hashblock events, wait for the ckpool "Block update latency"
log line before firing dashboard RPC calls (getblockchaininfo,
getnetworkhashps, etc). This avoids competing with ckpool's
latency-critical getblocktemplate call for bitcoind RPC threads,
which matters when many services flood bitcoind on every new block.
This commit is contained in:
satoshi
2026-05-18 17:53:50 +03:00
parent 1157f3501a
commit c6f0ba5f4d
2 changed files with 34 additions and 6 deletions
+23 -1
View File
@@ -270,6 +270,13 @@ type Aggregator struct {
latencyLastMs int64 // most recent observation latencyLastMs int64 // most recent observation
staleWorkHashes float64 // cumulative wasted hashes = sum(latency_s * hashrate_at_event) staleWorkHashes float64 // cumulative wasted hashes = sum(latency_s * hashrate_at_event)
// notifierDone is signalled by IngestLatencyEvents when a latency
// event arrives, meaning ckpool's notifier has finished its
// getblocktemplate + mining.notify cycle. The Run loop uses this to
// defer dashboard RPC calls until after the notifier is done,
// avoiding resource contention on bitcoind.
notifierDone chan struct{}
// High-water mark for the best share difficulty ever seen. // High-water mark for the best share difficulty ever seen.
// Persisted to kv so it survives restarts and transient ckpool gaps. // Persisted to kv so it survives restarts and transient ckpool gaps.
bestDiff float64 bestDiff float64
@@ -303,6 +310,7 @@ func New(ck *ckpool.Client, rpc *bitcoind.RPC, interval time.Duration, log *slog
sessionRejectReasons: make(map[string]int64), sessionRejectReasons: make(map[string]int64),
alltimeRejectReasons: make(map[string]int64), alltimeRejectReasons: make(map[string]int64),
ready: make(chan struct{}), ready: make(chan struct{}),
notifierDone: make(chan struct{}, 1),
} }
} }
@@ -345,7 +353,21 @@ func (a *Aggregator) Run(ctx context.Context, tipEvents <-chan zmqmon.TipEvent)
a.mu.Lock() a.mu.Lock()
a.lastZMQEventTime = ev.SeenAt a.lastZMQEventTime = ev.SeenAt
a.mu.Unlock() a.mu.Unlock()
a.Log.Debug("zmq tip, refreshing", "hash", ev.Hash) // Wait for ckpool's notifier to finish (getblocktemplate +
// mining.notify) before firing our dashboard RPCs, so we
// don't compete for bitcoind RPC resources during the
// latency-critical window. The latency log line signals
// completion. Timeout after 5s in case the log line is
// missed or ckpool is slow.
a.Log.Debug("zmq tip, waiting for notifier", "hash", ev.Hash)
select {
case <-a.notifierDone:
a.Log.Debug("notifier done, refreshing dashboard")
case <-time.After(5 * time.Second):
a.Log.Debug("notifier wait timed out, refreshing anyway")
case <-ctx.Done():
return
}
a.refresh(ctx) a.refresh(ctx)
} }
} }
+6
View File
@@ -103,6 +103,12 @@ func (a *Aggregator) IngestLatencyEvents(ctx context.Context, events <-chan logm
a.staleWorkHashes += (float64(ev.LatencyMs) / 1000.0) * hs5m a.staleWorkHashes += (float64(ev.LatencyMs) / 1000.0) * hs5m
count, sum, last, stale := a.latencyCount, a.latencySumMs, a.latencyLastMs, a.staleWorkHashes count, sum, last, stale := a.latencyCount, a.latencySumMs, a.latencyLastMs, a.staleWorkHashes
a.mu.Unlock() a.mu.Unlock()
// Signal the Run loop that ckpool's notifier is done so
// dashboard RPC calls can proceed without competing.
select {
case a.notifierDone <- struct{}{}:
default:
}
a.Log.Info("logmon: block update latency", "ms", ev.LatencyMs, "count", count) a.Log.Info("logmon: block update latency", "ms", ev.LatencyMs, "count", count)
// Blocks are infrequent (~10min); persist every event. // Blocks are infrequent (~10min); persist every event.
if a.Store != nil { if a.Store != nil {