• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13518235753

25 Feb 2025 09:44AM UTC coverage: 58.823% (+0.008%) from 58.815%
13518235753

Pull #9551

github

ellemouton
graph/db: move cache writes for Prune methods

This commit moves the cache writes for PruneGraphNodes and PruneGraph
from the KVStore to the ChannelGraph.
Pull Request #9551: graph: extract cache from CRUD [4]

92 of 115 new or added lines in 2 files covered. (80.0%)

263 existing lines in 12 files now uncovered.

136378 of 231846 relevant lines covered (58.82%)

19356.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/graph/db/graph.go
1
package graphdb
2

3
import (
4
        "time"
5

6
        "github.com/btcsuite/btcd/chaincfg/chainhash"
7
        "github.com/btcsuite/btcd/wire"
8
        "github.com/lightningnetwork/lnd/graph/db/models"
9
        "github.com/lightningnetwork/lnd/kvdb"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
        "github.com/lightningnetwork/lnd/routing/route"
12
)
13

14
// Config is a struct that holds all the necessary dependencies for a
15
// ChannelGraph.
16
type Config struct {
17
        // KVDB is the kvdb.Backend that will be used for initializing the
18
        // KVStore CRUD layer.
19
        KVDB kvdb.Backend
20

21
        // KVStoreOpts is a list of functional options that will be used when
22
        // initializing the KVStore.
23
        KVStoreOpts []KVStoreOptionModifier
24
}
25

26
// ChannelGraph is a layer above the graph's CRUD layer.
27
//
28
// NOTE: currently, this is purely a pass-through layer directly to the backing
29
// KVStore. Upcoming commits will move the graph cache out of the KVStore and
30
// into this layer so that the KVStore is only responsible for CRUD operations.
31
type ChannelGraph struct {
32
        graphCache *GraphCache
33

34
        *KVStore
35
}
36

37
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
38
func NewChannelGraph(cfg *Config, options ...ChanGraphOption) (*ChannelGraph,
39
        error) {
176✔
40

176✔
41
        opts := defaultChanGraphOptions()
176✔
42
        for _, o := range options {
281✔
43
                o(opts)
105✔
44
        }
105✔
45

46
        store, err := NewKVStore(cfg.KVDB, cfg.KVStoreOpts...)
176✔
47
        if err != nil {
176✔
48
                return nil, err
×
49
        }
×
50

51
        if !opts.useGraphCache {
212✔
52
                return &ChannelGraph{
36✔
53
                        KVStore: store,
36✔
54
                }, nil
36✔
55
        }
36✔
56

57
        // The graph cache can be turned off (e.g. for mobile users) for a
58
        // speed/memory usage tradeoff.
59
        graphCache := NewGraphCache(opts.preAllocCacheNumNodes)
143✔
60
        startTime := time.Now()
143✔
61
        log.Debugf("Populating in-memory channel graph, this might take a " +
143✔
62
                "while...")
143✔
63

143✔
64
        err = store.ForEachNodeCacheable(func(node route.Vertex,
143✔
65
                features *lnwire.FeatureVector) error {
246✔
66

103✔
67
                graphCache.AddNodeFeatures(node, features)
103✔
68

103✔
69
                return nil
103✔
70
        })
103✔
71
        if err != nil {
143✔
72
                return nil, err
×
73
        }
×
74

75
        err = store.ForEachChannel(func(info *models.ChannelEdgeInfo,
143✔
76
                policy1, policy2 *models.ChannelEdgePolicy) error {
542✔
77

399✔
78
                graphCache.AddChannel(info, policy1, policy2)
399✔
79

399✔
80
                return nil
399✔
81
        })
399✔
82
        if err != nil {
143✔
83
                return nil, err
×
84
        }
×
85

86
        log.Debugf("Finished populating in-memory channel graph (took %v, %s)",
143✔
87
                time.Since(startTime), graphCache.Stats())
143✔
88

143✔
89
        store.setGraphCache(graphCache)
143✔
90

143✔
91
        return &ChannelGraph{
143✔
92
                KVStore:    store,
143✔
93
                graphCache: graphCache,
143✔
94
        }, nil
143✔
95
}
96

97
// DeleteChannelEdges removes edges with the given channel IDs from the
98
// database and marks them as zombies. This ensures that we're unable to re-add
99
// it to our database once again. If an edge does not exist within the
100
// database, then ErrEdgeNotFound will be returned. If strictZombiePruning is
101
// true, then when we mark these edges as zombies, we'll set up the keys such
102
// that we require the node that failed to send the fresh update to be the one
103
// that resurrects the channel from its zombie state. The markZombie bool
104
// denotes whether to mark the channel as a zombie.
105
func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning, markZombie bool,
106
        chanIDs ...uint64) error {
152✔
107

152✔
108
        infos, err := c.KVStore.DeleteChannelEdges(
152✔
109
                strictZombiePruning, markZombie, chanIDs...,
152✔
110
        )
152✔
111
        if err != nil {
209✔
112
                return err
57✔
113
        }
57✔
114

115
        if c.graphCache != nil {
190✔
116
                for _, info := range infos {
123✔
117
                        c.graphCache.RemoveChannel(
28✔
118
                                info.NodeKey1Bytes, info.NodeKey2Bytes,
28✔
119
                                info.ChannelID,
28✔
120
                        )
28✔
121
                }
28✔
122
        }
123

124
        return err
95✔
125
}
126

127
// DisconnectBlockAtHeight is used to indicate that the block specified
128
// by the passed height has been disconnected from the main chain. This
129
// will "rewind" the graph back to the height below, deleting channels
130
// that are no longer confirmed from the graph. The prune log will be
131
// set to the last prune height valid for the remaining chain.
132
// Channels that were removed from the graph resulting from the
133
// disconnected block are returned.
134
func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) (
135
        []*models.ChannelEdgeInfo, error) {
163✔
136

163✔
137
        edges, err := c.KVStore.DisconnectBlockAtHeight(height)
163✔
138
        if err != nil {
163✔
NEW
139
                return nil, err
×
NEW
140
        }
×
141

142
        if c.graphCache != nil {
326✔
143
                for _, edge := range edges {
245✔
144
                        c.graphCache.RemoveChannel(
82✔
145
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
82✔
146
                                edge.ChannelID,
82✔
147
                        )
82✔
148
                }
82✔
149
        }
150

151
        return edges, nil
163✔
152
}
153

154
// PruneGraph prunes newly closed channels from the channel graph in response
155
// to a new block being solved on the network. Any transactions which spend the
156
// funding output of any known channels within he graph will be deleted.
157
// Additionally, the "prune tip", or the last block which has been used to
158
// prune the graph is stored so callers can ensure the graph is fully in sync
159
// with the current UTXO state. A slice of channels that have been closed by
160
// the target block are returned if the function succeeds without error.
161
func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
162
        blockHash *chainhash.Hash, blockHeight uint32) (
163
        []*models.ChannelEdgeInfo, error) {
238✔
164

238✔
165
        edges, err := c.KVStore.PruneGraph(spentOutputs, blockHash, blockHeight)
238✔
166
        if err != nil {
238✔
NEW
167
                return nil, err
×
NEW
168
        }
×
169

170
        // Now that the graph has been pruned, we'll also attempt to
171
        // prune any nodes that have had a channel closed within the
172
        // latest block.
173
        nodes, err := c.KVStore.PruneGraphNodes()
238✔
174
        if err != nil {
238✔
NEW
175
                return nil, err
×
NEW
176
        }
×
177

178
        if c.graphCache != nil {
476✔
179
                for _, edge := range edges {
266✔
180
                        c.graphCache.RemoveChannel(
28✔
181
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
28✔
182
                                edge.ChannelID,
28✔
183
                        )
28✔
184
                }
28✔
185

186
                for _, node := range nodes {
291✔
187
                        c.graphCache.RemoveNode(node)
53✔
188
                }
53✔
189

190
                log.Debugf("Pruned graph, cache now has %s",
238✔
191
                        c.graphCache.Stats())
238✔
192
        }
193

194
        return edges, nil
238✔
195
}
196

197
// PruneGraphNodes is a garbage collection method which attempts to prune out
198
// any nodes from the channel graph that are currently unconnected. This ensure
199
// that we only maintain a graph of reachable nodes. In the event that a pruned
200
// node gains more channels, it will be re-added back to the graph.
201
func (c *ChannelGraph) PruneGraphNodes() error {
26✔
202
        nodes, err := c.KVStore.PruneGraphNodes()
26✔
203
        if err != nil {
26✔
NEW
204
                return err
×
NEW
205
        }
×
206

207
        if c.graphCache != nil {
52✔
208
                for _, node := range nodes {
33✔
209
                        c.graphCache.RemoveNode(node)
7✔
210
                }
7✔
211
        }
212

213
        return nil
26✔
214
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc