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:
@@ -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
|
||||||
@@ -296,13 +303,14 @@ type Aggregator struct {
|
|||||||
|
|
||||||
func New(ck *ckpool.Client, rpc *bitcoind.RPC, interval time.Duration, log *slog.Logger) *Aggregator {
|
func New(ck *ckpool.Client, rpc *bitcoind.RPC, interval time.Duration, log *slog.Logger) *Aggregator {
|
||||||
return &Aggregator{
|
return &Aggregator{
|
||||||
CK: ck,
|
CK: ck,
|
||||||
RPC: rpc,
|
RPC: rpc,
|
||||||
Interval: interval,
|
Interval: interval,
|
||||||
Log: log,
|
Log: log,
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user