Skip to content

Commit c8e69f9

Browse files
committed
lndmon: cache closedChannels response
1 parent f7d3fdf commit c8e69f9

File tree

3 files changed

+72
-16
lines changed

3 files changed

+72
-16
lines changed

collectors/channels_collector.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import (
44
"context"
55
"fmt"
66
"strconv"
7+
"sync"
8+
"time"
79

810
"github.com/btcsuite/btcd/btcutil"
911
"github.com/lightninglabs/lndclient"
1012
"github.com/lightningnetwork/lnd/routing/route"
1113
"github.com/prometheus/client_golang/prometheus"
1214
)
1315

16+
// Cache refresh interval magic number.
17+
const cacheRefreshInterval = 10 * time.Minute
18+
1419
// ChannelsCollector is a collector that keeps track of channel information.
1520
type ChannelsCollector struct {
1621
channelBalanceDesc *prometheus.Desc
@@ -51,17 +56,25 @@ type ChannelsCollector struct {
5156
// errChan is a channel that we send any errors that we encounter into.
5257
// This channel should be buffered so that it does not block sends.
5358
errChan chan<- error
59+
60+
// quit is a channel that we use to signal for graceful shutdown.
61+
quit chan struct{}
62+
63+
// cache is for storing results from a ticker to reduce grpc server
64+
// load on lnd.
65+
closedChannelsCache []lndclient.ClosedChannel
66+
cacheMutex sync.RWMutex
5467
}
5568

5669
// NewChannelsCollector returns a new instance of the ChannelsCollector for the
5770
// target lnd client.
5871
func NewChannelsCollector(lnd lndclient.LightningClient, errChan chan<- error,
59-
cfg *MonitoringConfig) *ChannelsCollector {
72+
quitChan chan struct{}, cfg *MonitoringConfig) *ChannelsCollector {
6073

6174
// Our set of labels, status should either be active or inactive. The
6275
// initiator is "true" if we are the initiator, and "false" otherwise.
6376
labels := []string{"chan_id", "status", "initiator", "peer"}
64-
return &ChannelsCollector{
77+
collector := &ChannelsCollector{
6578
channelBalanceDesc: prometheus.NewDesc(
6679
"lnd_channels_open_balance_sat",
6780
"total balance of channels in satoshis",
@@ -174,10 +187,49 @@ func NewChannelsCollector(lnd lndclient.LightningClient, errChan chan<- error,
174187
[]string{"amount"}, nil,
175188
),
176189

177-
lnd: lnd,
178-
primaryNode: cfg.PrimaryNode,
179-
errChan: errChan,
190+
lnd: lnd,
191+
primaryNode: cfg.PrimaryNode,
192+
closedChannelsCache: nil,
193+
errChan: errChan,
194+
quit: quitChan,
180195
}
196+
197+
// Start a ticker to update the cache once per 10m
198+
go func() {
199+
ticker := time.NewTicker(cacheRefreshInterval)
200+
defer ticker.Stop()
201+
202+
for {
203+
err := collector.refreshClosedChannelsCache()
204+
if err != nil {
205+
errChan <- err
206+
}
207+
208+
select {
209+
case <-ticker.C:
210+
continue
211+
212+
case <-collector.quit:
213+
return
214+
}
215+
}
216+
}()
217+
218+
return collector
219+
}
220+
221+
// refreshClosedChannelsCache acquires a mutex write lock to update
222+
// the closedChannelsCache.
223+
func (c *ChannelsCollector) refreshClosedChannelsCache() error {
224+
data, err := c.lnd.ClosedChannels(context.Background())
225+
if err != nil {
226+
return err
227+
}
228+
c.cacheMutex.Lock()
229+
c.closedChannelsCache = data
230+
c.cacheMutex.Unlock()
231+
232+
return nil
181233
}
182234

183235
// Describe sends the super-set of all possible descriptors of metrics
@@ -452,12 +504,9 @@ func (c *ChannelsCollector) Collect(ch chan<- prometheus.Metric) {
452504
)
453505

454506
// Get the list of closed channels.
455-
closedChannelsResp, err := c.lnd.ClosedChannels(context.Background())
456-
if err != nil {
457-
c.errChan <- fmt.Errorf("ChannelsCollector ClosedChannels "+
458-
"failed with: %v", err)
459-
return
460-
}
507+
c.cacheMutex.RLock()
508+
closedChannelsResp := c.closedChannelsCache
509+
c.cacheMutex.RUnlock()
461510
closeCounts := make(map[string]int)
462511
for _, channel := range closedChannelsResp {
463512
typeString, ok := closeTypeLabelMap[channel.CloseType]

collectors/prometheus.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func DefaultConfig() *PrometheusConfig {
8484
// NewPrometheusExporter makes a new instance of the PrometheusExporter given
8585
// the address to listen for Prometheus on and an lnd gRPC client.
8686
func NewPrometheusExporter(cfg *PrometheusConfig, lnd *lndclient.LndServices,
87-
monitoringCfg *MonitoringConfig) *PrometheusExporter {
87+
monitoringCfg *MonitoringConfig, quitChan chan struct{}) *PrometheusExporter {
8888

8989
// We have six collectors and a htlc monitor running, so we buffer our
9090
// error channel by 7 so that we do not need to consume all errors from
@@ -94,11 +94,16 @@ func NewPrometheusExporter(cfg *PrometheusConfig, lnd *lndclient.LndServices,
9494

9595
htlcMonitor := newHtlcMonitor(lnd.Router, errChan)
9696

97+
chanCollector := NewChannelsCollector(
98+
lnd.Client, errChan, quitChan, monitoringCfg,
99+
)
100+
go func() {
101+
close(chanCollector.quit)
102+
}()
103+
97104
collectors := []prometheus.Collector{
98105
NewChainCollector(lnd.Client, errChan),
99-
NewChannelsCollector(
100-
lnd.Client, errChan, monitoringCfg,
101-
),
106+
chanCollector,
102107
NewWalletCollector(lnd, errChan),
103108
NewPeerCollector(lnd.Client, errChan),
104109
NewInfoCollector(lnd.Client, errChan),

lndmon.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func start() error {
3131
return err
3232
}
3333

34+
quit := make(chan struct{})
3435
interceptor, err := signal.Intercept()
3536
if err != nil {
3637
return fmt.Errorf("could not intercept signal: %v", err)
@@ -72,7 +73,7 @@ func start() error {
7273
// Start our Prometheus exporter. This exporter spawns a goroutine
7374
// that pulls metrics from our lnd client on a set interval.
7475
exporter := collectors.NewPrometheusExporter(
75-
cfg.Prometheus, &lnd.LndServices, &monitoringCfg,
76+
cfg.Prometheus, &lnd.LndServices, &monitoringCfg, quit,
7677
)
7778
if err := exporter.Start(); err != nil {
7879
return err
@@ -83,6 +84,7 @@ func start() error {
8384
var stopErr error
8485
select {
8586
case <-interceptor.ShutdownChannel():
87+
close(quit)
8688
fmt.Println("Exiting lndmon.")
8789

8890
case stopErr = <-exporter.Errors():

0 commit comments

Comments
 (0)