Add ZMQ hashblock subscriber for sub-second chain refresh
New internal/zmqmon package subscribes to bitcoind's hashblock ZMQ topic and emits TipEvents. Uses github.com/go-zeromq/zmq4 (pure Go, builds with CGO_ENABLED=0). Exponential backoff on connection failure so bitcoind restarts don't kill the subscriber permanently; event channel drops rather than blocks if the consumer is slow (signals are advisory, not logs). Aggregator.Run now takes a <-chan TipEvent; when a tip arrives it fires an immediate refresh() outside the normal ticker cadence. With a 5s poll interval and 0.5-1s ZMQ latency from bitcoind, dashboards now reflect new tips roughly 4x faster. Endpoint comes from BITCOIN_ZMQ_BLOCK — empty disables ZMQ entirely and the aggregator just runs on the ticker alone.
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/kamadopool/kamado-api/internal/bitcoind"
|
||||
"github.com/kamadopool/kamado-api/internal/ckpool"
|
||||
"github.com/kamadopool/kamado-api/internal/store"
|
||||
"github.com/kamadopool/kamado-api/internal/zmqmon"
|
||||
)
|
||||
|
||||
// Snapshot is the merged view served to the UI. All fields are safe to
|
||||
@@ -81,8 +82,9 @@ func New(ck *ckpool.Client, rpc *bitcoind.RPC, interval time.Duration, log *slog
|
||||
// Run blocks until ctx is cancelled, refreshing the snapshot every Interval.
|
||||
// It runs one immediate refresh at startup so readers don't see an empty
|
||||
// snapshot after ctx launches the goroutine. Persisted block history is
|
||||
// loaded from the store before the first refresh.
|
||||
func (a *Aggregator) Run(ctx context.Context) {
|
||||
// loaded from the store before the first refresh. If tipEvents is non-nil,
|
||||
// each received tip triggers an immediate refresh outside the poll cadence.
|
||||
func (a *Aggregator) Run(ctx context.Context, tipEvents <-chan zmqmon.TipEvent) {
|
||||
a.loadPersistedBlocks()
|
||||
a.refresh(ctx)
|
||||
t := time.NewTicker(a.Interval)
|
||||
@@ -93,6 +95,13 @@ func (a *Aggregator) Run(ctx context.Context) {
|
||||
return
|
||||
case <-t.C:
|
||||
a.refresh(ctx)
|
||||
case ev, ok := <-tipEvents:
|
||||
if !ok {
|
||||
tipEvents = nil
|
||||
continue
|
||||
}
|
||||
a.Log.Debug("zmq tip, refreshing", "hash", ev.Hash)
|
||||
a.refresh(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user