routing: perform path finding inside a single DB transaction
This commit modifies the path finding logic such that all path finding is done inside a _single_ database transaction. With this change, we ensure that we don’t end up possibly creating hundreds of database transactions slowing down the path finding and payment sending process all together.
This commit is contained in:
parent
5eb3406b19
commit
646f79f566
@ -369,9 +369,19 @@ func edgeWeight(e *channeldb.ChannelEdgePolicy) float64 {
|
|||||||
// time-lock+fee costs along a particular edge. If a path is found, this
|
// time-lock+fee costs along a particular edge. If a path is found, this
|
||||||
// function returns a slice of ChannelHop structs which encoded the chosen path
|
// function returns a slice of ChannelHop structs which encoded the chosen path
|
||||||
// from the target to the source.
|
// from the target to the source.
|
||||||
func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode,
|
func findPath(tx *bolt.Tx, graph *channeldb.ChannelGraph,
|
||||||
target *btcec.PublicKey, ignoredNodes map[vertex]struct{},
|
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
|
||||||
ignoredEdges map[uint64]struct{}, amt lnwire.MilliSatoshi) ([]*ChannelHop, error) {
|
ignoredNodes map[vertex]struct{}, ignoredEdges map[uint64]struct{},
|
||||||
|
amt lnwire.MilliSatoshi) ([]*ChannelHop, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if tx == nil {
|
||||||
|
tx, err = graph.Database().Begin(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
}
|
||||||
|
|
||||||
// First we'll initialize an empty heap which'll help us to quickly
|
// First we'll initialize an empty heap which'll help us to quickly
|
||||||
// locate the next edge we should visit next during our graph
|
// locate the next edge we should visit next during our graph
|
||||||
@ -394,6 +404,9 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): also add path caching
|
||||||
|
// * similar to route caching, but doesn't factor in the amount
|
||||||
|
|
||||||
// To start, we add the source of our path finding attempt to the
|
// To start, we add the source of our path finding attempt to the
|
||||||
// distance map with with a distance of 0. This indicates our starting
|
// distance map with with a distance of 0. This indicates our starting
|
||||||
// point in the graph traversal.
|
// point in the graph traversal.
|
||||||
@ -428,13 +441,13 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
|
|||||||
// examine all the outgoing edge (channels) from this node to
|
// examine all the outgoing edge (channels) from this node to
|
||||||
// further our graph traversal.
|
// further our graph traversal.
|
||||||
pivot := newVertex(bestNode.PubKey)
|
pivot := newVertex(bestNode.PubKey)
|
||||||
err := bestNode.ForEachChannel(nil, func(tx *bolt.Tx,
|
err := bestNode.ForEachChannel(tx, func(tx *bolt.Tx,
|
||||||
edgeInfo *channeldb.ChannelEdgeInfo,
|
edgeInfo *channeldb.ChannelEdgeInfo,
|
||||||
outEdge, inEdge *channeldb.ChannelEdgePolicy) error {
|
outEdge, inEdge *channeldb.ChannelEdgePolicy) error {
|
||||||
|
|
||||||
v := newVertex(outEdge.Node.PubKey)
|
v := newVertex(outEdge.Node.PubKey)
|
||||||
|
|
||||||
// TODO(roasbeef): skip if disabled
|
// TODO(roasbeef): skip if chan disabled
|
||||||
|
|
||||||
// If this vertex or edge has been black listed, then
|
// If this vertex or edge has been black listed, then
|
||||||
// we'll skip exploring this edge during this
|
// we'll skip exploring this edge during this
|
||||||
@ -494,6 +507,7 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
|
|||||||
// to further explore down this edge.
|
// to further explore down this edge.
|
||||||
heap.Push(&nodeHeap, distance[v])
|
heap.Push(&nodeHeap, distance[v])
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -555,8 +569,11 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
|
|||||||
// make our inner path finding algorithm aware of our k-shortest paths
|
// make our inner path finding algorithm aware of our k-shortest paths
|
||||||
// algorithm, rather than attempting to use an unmodified path finding
|
// algorithm, rather than attempting to use an unmodified path finding
|
||||||
// algorithm in a block box manner.
|
// algorithm in a block box manner.
|
||||||
func findPaths(graph *channeldb.ChannelGraph, source *channeldb.LightningNode,
|
func findPaths(tx *bolt.Tx, graph *channeldb.ChannelGraph,
|
||||||
target *btcec.PublicKey, amt lnwire.MilliSatoshi) ([][]*ChannelHop, error) {
|
source *channeldb.LightningNode, target *btcec.PublicKey,
|
||||||
|
amt lnwire.MilliSatoshi) ([][]*ChannelHop, error) {
|
||||||
|
|
||||||
|
// TODO(roasbeef): take in db tx
|
||||||
|
|
||||||
ignoredEdges := make(map[uint64]struct{})
|
ignoredEdges := make(map[uint64]struct{})
|
||||||
ignoredVertexes := make(map[vertex]struct{})
|
ignoredVertexes := make(map[vertex]struct{})
|
||||||
@ -571,7 +588,7 @@ func findPaths(graph *channeldb.ChannelGraph, source *channeldb.LightningNode,
|
|||||||
// First we'll find a single shortest path from the source (our
|
// First we'll find a single shortest path from the source (our
|
||||||
// selfNode) to the target destination that's capable of carrying amt
|
// selfNode) to the target destination that's capable of carrying amt
|
||||||
// satoshis along the path before fees are calculated.
|
// satoshis along the path before fees are calculated.
|
||||||
startingPath, err := findPath(graph, source, target,
|
startingPath, err := findPath(tx, graph, source, target,
|
||||||
ignoredVertexes, ignoredEdges, amt)
|
ignoredVertexes, ignoredEdges, amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to find path: %v", err)
|
log.Errorf("Unable to find path: %v", err)
|
||||||
@ -643,7 +660,7 @@ func findPaths(graph *channeldb.ChannelGraph, source *channeldb.LightningNode,
|
|||||||
// the vertexes (other than the spur path) within the
|
// the vertexes (other than the spur path) within the
|
||||||
// root path removed, we'll attempt to find another
|
// root path removed, we'll attempt to find another
|
||||||
// shortest path from the spur node to the destination.
|
// shortest path from the spur node to the destination.
|
||||||
spurPath, err := findPath(graph, spurNode, target,
|
spurPath, err := findPath(tx, graph, spurNode, target,
|
||||||
ignoredVertexes, ignoredEdges, amt)
|
ignoredVertexes, ignoredEdges, amt)
|
||||||
|
|
||||||
// If we weren't able to find a path, we'll continue to
|
// If we weren't able to find a path, we'll continue to
|
||||||
|
@ -316,7 +316,7 @@ func TestBasicGraphPathFinding(t *testing.T) {
|
|||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
target := aliases["sophon"]
|
target := aliases["sophon"]
|
||||||
path, err := findPath(graph, sourceNode, target, ignoredVertexes,
|
path, err := findPath(nil, graph, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt)
|
ignoredEdges, paymentAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find path: %v", err)
|
t.Fatalf("unable to find path: %v", err)
|
||||||
@ -412,7 +412,7 @@ func TestBasicGraphPathFinding(t *testing.T) {
|
|||||||
// exist two possible paths in the graph, but the shorter (1 hop) path
|
// exist two possible paths in the graph, but the shorter (1 hop) path
|
||||||
// should be selected.
|
// should be selected.
|
||||||
target = aliases["luoji"]
|
target = aliases["luoji"]
|
||||||
path, err = findPath(graph, sourceNode, target, ignoredVertexes,
|
path, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt)
|
ignoredEdges, paymentAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find route: %v", err)
|
t.Fatalf("unable to find route: %v", err)
|
||||||
@ -469,7 +469,7 @@ func TestKShortestPathFinding(t *testing.T) {
|
|||||||
|
|
||||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||||
target := aliases["luoji"]
|
target := aliases["luoji"]
|
||||||
paths, err := findPaths(graph, sourceNode, target, paymentAmt)
|
paths, err := findPaths(nil, graph, sourceNode, target, paymentAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find paths between roasbeef and "+
|
t.Fatalf("unable to find paths between roasbeef and "+
|
||||||
"luo ji: %v", err)
|
"luo ji: %v", err)
|
||||||
@ -530,7 +530,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
// We start by confirminig that routing a payment 20 hops away is possible.
|
// We start by confirminig that routing a payment 20 hops away is possible.
|
||||||
// Alice should be able to find a valid route to ursula.
|
// Alice should be able to find a valid route to ursula.
|
||||||
target := aliases["ursula"]
|
target := aliases["ursula"]
|
||||||
_, err = findPath(graph, sourceNode, target, ignoredVertexes,
|
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt)
|
ignoredEdges, paymentAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("path should have been found")
|
t.Fatalf("path should have been found")
|
||||||
@ -539,7 +539,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
|
|||||||
// Vincent is 21 hops away from Alice, and thus no valid route should be
|
// Vincent is 21 hops away from Alice, and thus no valid route should be
|
||||||
// presented to Alice.
|
// presented to Alice.
|
||||||
target = aliases["vincent"]
|
target = aliases["vincent"]
|
||||||
path, err := findPath(graph, sourceNode, target, ignoredVertexes,
|
path, err := findPath(nil, graph, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, paymentAmt)
|
ignoredEdges, paymentAmt)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("should not have been able to find path, supposed to be "+
|
t.Fatalf("should not have been able to find path, supposed to be "+
|
||||||
@ -579,7 +579,7 @@ func TestPathNotAvailable(t *testing.T) {
|
|||||||
t.Fatalf("unable to parse pubkey: %v", err)
|
t.Fatalf("unable to parse pubkey: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = findPath(graph, sourceNode, unknownNode, ignoredVertexes,
|
_, err = findPath(nil, graph, sourceNode, unknownNode, ignoredVertexes,
|
||||||
ignoredEdges, 100)
|
ignoredEdges, 100)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("path shouldn't have been found: %v", err)
|
t.Fatalf("path shouldn't have been found: %v", err)
|
||||||
@ -613,7 +613,7 @@ func TestPathInsufficientCapacity(t *testing.T) {
|
|||||||
target := aliases["sophon"]
|
target := aliases["sophon"]
|
||||||
|
|
||||||
const payAmt = btcutil.SatoshiPerBitcoin
|
const payAmt = btcutil.SatoshiPerBitcoin
|
||||||
_, err = findPath(graph, sourceNode, target, ignoredVertexes,
|
_, err = findPath(nil, graph, sourceNode, target, ignoredVertexes,
|
||||||
ignoredEdges, payAmt)
|
ignoredEdges, payAmt)
|
||||||
if !IsError(err, ErrNoPathFound) {
|
if !IsError(err, ErrNoPathFound) {
|
||||||
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
t.Fatalf("graph shouldn't be able to support payment: %v", err)
|
||||||
|
@ -978,14 +978,24 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx, err := r.cfg.Graph.Database().Begin(false)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we know the destination is reachable within the graph,
|
// Now that we know the destination is reachable within the graph,
|
||||||
// we'll execute our KSP algorithm to find the k-shortest paths from
|
// we'll execute our KSP algorithm to find the k-shortest paths from
|
||||||
// our source to the destination.
|
// our source to the destination.
|
||||||
shortestPaths, err := findPaths(r.cfg.Graph, r.selfNode, target, amt)
|
shortestPaths, err := findPaths(tx, r.cfg.Graph, r.selfNode, target,
|
||||||
|
amt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx.Rollback()
|
||||||
|
|
||||||
// Now that we have a set of paths, we'll need to turn them into
|
// Now that we have a set of paths, we'll need to turn them into
|
||||||
// *routes* by computing the required time-lock and fee information for
|
// *routes* by computing the required time-lock and fee information for
|
||||||
// each path. During this process, some paths may be discarded if they
|
// each path. During this process, some paths may be discarded if they
|
||||||
@ -1032,7 +1042,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
|
|||||||
return validRoutes[i].TotalFees < validRoutes[j].TotalFees
|
return validRoutes[i].TotalFees < validRoutes[j].TotalFees
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Tracef("Obtained %v paths sending %v to %x: %v", len(validRoutes),
|
go log.Tracef("Obtained %v paths sending %v to %x: %v", len(validRoutes),
|
||||||
amt, dest, newLogClosure(func() string {
|
amt, dest, newLogClosure(func() string {
|
||||||
return spew.Sdump(validRoutes)
|
return spew.Sdump(validRoutes)
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user