graph: add ReadOnlyGraph interface to GraphSource interface

In this commit, we take the existing graphsession.ReadyOnlyGraph
interface and remove its usage of a kvdb.RTx and replace it with a more
abstract `RTx` interface type.

The new GraphSource interface is expanded to include the
graphsession.ReadOnlyGraph interface and the implementation of it,
DBSource, is expanded to include the new methods. It converts the
given RTx to the underlying kvdb read transaction where needed.
This commit is contained in:
Elle Mouton 2024-11-11 16:24:46 +02:00
parent 6c008ff8fb
commit aa2480464b
No known key found for this signature in database
GPG Key ID: D7D916376026F177
6 changed files with 135 additions and 15 deletions

View File

@ -4,7 +4,6 @@ import (
"fmt"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
@ -53,7 +52,7 @@ var _ routing.GraphSessionFactory = (*Factory)(nil)
// access the backing channel graph.
type session struct {
graph graph
tx kvdb.RTx
tx RTx
}
// NewRoutingGraph constructs a session that which does not first start a
@ -72,7 +71,7 @@ func (g *session) close() error {
return nil
}
err := g.tx.Rollback()
err := g.tx.Close()
if err != nil {
return fmt.Errorf("error closing db tx: %w", err)
}
@ -109,7 +108,7 @@ type ReadOnlyGraph interface {
// NewPathFindTx returns a new read transaction that can be used for a
// single path finding session. Will return nil if the graph cache is
// enabled.
NewPathFindTx() (kvdb.RTx, error)
NewPathFindTx() (RTx, error)
graph
}
@ -128,7 +127,7 @@ type graph interface {
//
// NOTE: if a nil tx is provided, then it is expected that the
// implementation create a read only tx.
ForEachNodeDirectedChannel(tx kvdb.RTx, node route.Vertex,
ForEachNodeDirectedChannel(tx RTx, node route.Vertex,
cb func(channel *graphdb.DirectedChannel) error) error
// FetchNodeFeatures returns the features of a given node. If no
@ -136,10 +135,6 @@ type graph interface {
//
// NOTE: if a nil tx is provided, then it is expected that the
// implementation create a read only tx.
FetchNodeFeatures(tx kvdb.RTx, node route.Vertex) (
*lnwire.FeatureVector, error)
FetchNodeFeatures(tx RTx, node route.Vertex) (*lnwire.FeatureVector,
error)
}
// A compile-time check to ensure that *channeldb.ChannelGraph implements the
// graph interface.
var _ graph = (*graphdb.ChannelGraph)(nil)

14
graph/session/read_tx.go Normal file
View File

@ -0,0 +1,14 @@
package session
// RTx represents a read-only transaction that can only be used for graph
// reads during a path-finding session.
type RTx interface {
// Close closes the transaction.
Close() error
// MustImplementRTx is a helper method that ensures that the RTx
// interface is implemented by the underlying type. This is useful since
// the other methods in the interface are quite generic and so many
// types will satisfy the interface if it only contains those methods.
MustImplementRTx()
}

View File

@ -1,6 +1,14 @@
package sources
import graphdb "github.com/lightningnetwork/lnd/graph/db"
import (
"fmt"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/session"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
// DBSource is an implementation of the GraphSource interface backed by a local
// persistence layer holding graph related data.
@ -19,3 +27,102 @@ func NewDBGSource(db *graphdb.ChannelGraph) *DBSource {
db: db,
}
}
// NewPathFindTx returns a new read transaction that can be used for a single
// path finding session. Will return nil if the graph cache is enabled for the
// underlying graphdb.ChannelGraph.
//
// NOTE: this is part of the graphsession.ReadOnlyGraph interface.
func (s *DBSource) NewPathFindTx() (session.RTx, error) {
tx, err := s.db.NewPathFindTx()
if err != nil {
return nil, err
}
return newKVDBRTx(tx), nil
}
// ForEachNodeDirectedChannel iterates through all channels of a given node,
// executing the passed callback on the directed edge representing the channel
// and its incoming policy. If the callback returns an error, then the
// iteration is halted with the error propagated back up to the caller. An
// optional read transaction may be provided. If it is, then it will be cast
// into a kvdb.RTx and passed into the callback.
//
// Unknown policies are passed into the callback as nil values.
//
// NOTE: this is part of the graphsession.ReadOnlyGraph interface.
func (s *DBSource) ForEachNodeDirectedChannel(tx session.RTx,
node route.Vertex,
cb func(channel *graphdb.DirectedChannel) error) error {
kvdbTx, err := extractKVDBRTx(tx)
if err != nil {
return err
}
return s.db.ForEachNodeDirectedChannel(kvdbTx, node, cb)
}
// FetchNodeFeatures returns the features of a given node. If no features are
// known for the node, an empty feature vector is returned. An optional read
// transaction may be provided. If it is, then it will be cast into a kvdb.RTx
// and passed into the callback.
//
// NOTE: this is part of the graphsession.ReadOnlyGraph interface.
func (s *DBSource) FetchNodeFeatures(tx session.RTx,
node route.Vertex) (*lnwire.FeatureVector, error) {
kvdbTx, err := extractKVDBRTx(tx)
if err != nil {
return nil, err
}
return s.db.FetchNodeFeatures(kvdbTx, node)
}
// kvdbRTx is an implementation of graphdb.RTx backed by a KVDB database read
// transaction.
type kvdbRTx struct {
kvdb.RTx
}
// newKVDBRTx constructs a kvdbRTx instance backed by the given kvdb.RTx.
func newKVDBRTx(tx kvdb.RTx) *kvdbRTx {
return &kvdbRTx{tx}
}
// Close closes the underlying transaction.
//
// NOTE: this is part of the graphdb.RTx interface.
func (t *kvdbRTx) Close() error {
if t.RTx == nil {
return nil
}
return t.RTx.Rollback()
}
// MustImplementRTx is a helper method that ensures that the kvdbRTx type
// implements the RTx interface.
//
// NOTE: this is part of the graphdb.RTx interface.
func (t *kvdbRTx) MustImplementRTx() {}
// A compile-time assertion to ensure that kvdbRTx implements the RTx interface.
var _ session.RTx = (*kvdbRTx)(nil)
// extractKVDBRTx is a helper function that casts an RTx into a kvdbRTx and
// errors if the cast fails.
func extractKVDBRTx(tx session.RTx) (kvdb.RTx, error) {
if tx == nil {
return nil, nil
}
kvdbTx, ok := tx.(*kvdbRTx)
if !ok {
return nil, fmt.Errorf("expected a graphdb.kvdbRTx, got %T", tx)
}
return kvdbTx, nil
}

View File

@ -1,6 +1,9 @@
package sources
import "github.com/lightningnetwork/lnd/graph/session"
// GraphSource defines the read-only graph interface required by LND for graph
// related queries.
type GraphSource interface {
session.ReadOnlyGraph
}

View File

@ -695,6 +695,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
return err
}
graph := s.graphDB
graphSource := s.graphSource
routerBackend := &routerrpc.RouterBackend{
SelfNode: selfNode.PubKeyBytes,
@ -711,7 +712,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
return routing.FetchAmountPairCapacity(
graphsession.NewRoutingGraph(graph),
graphsession.NewRoutingGraph(graphSource),
selfNode.PubKeyBytes, nodeFrom, nodeTo, amount,
)
},

View File

@ -1026,7 +1026,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
}
paymentSessionSource := &routing.SessionSource{
GraphSessionFactory: graphsession.NewGraphSessionFactory(
dbs.GraphDB,
graphSource,
),
SourceNode: sourceNode,
MissionControl: s.defaultMC,
@ -1060,7 +1060,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
s.chanRouter, err = routing.New(routing.Config{
SelfNode: selfNode.PubKeyBytes,
RoutingGraph: graphsession.NewRoutingGraph(dbs.GraphDB),
RoutingGraph: graphsession.NewRoutingGraph(graphSource),
Chain: cc.ChainIO,
Payer: s.htlcSwitch,
Control: s.controlTower,