Skip to content

Commit 39e521e

Browse files
committed
graph/db+sqldb: implement ChanUpdatesInHorizon
Add `ChanUpdatesInHorizon` method to the SQLStore. This lets us run `TestChanUpdatesInHorizon` against our SQL backends.
1 parent 45c1564 commit 39e521e

File tree

5 files changed

+413
-10
lines changed

5 files changed

+413
-10
lines changed

graph/db/graph_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1937,7 +1937,7 @@ func TestChanUpdatesInHorizon(t *testing.T) {
19371937
t.Parallel()
19381938
ctx := context.Background()
19391939

1940-
graph := MakeTestGraph(t)
1940+
graph := MakeTestGraphNew(t)
19411941

19421942
// If we issue an arbitrary query before any channel updates are
19431943
// inserted in the database, we should get zero results.

graph/db/sql_store.go

Lines changed: 183 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type SQLQueries interface {
9090
GetChannelFeaturesAndExtras(ctx context.Context, channelID int64) ([]sqlc.GetChannelFeaturesAndExtrasRow, error)
9191
HighestSCID(ctx context.Context, version int16) ([]byte, error)
9292
ListChannelsByNodeID(ctx context.Context, arg sqlc.ListChannelsByNodeIDParams) ([]sqlc.ListChannelsByNodeIDRow, error)
93+
GetChannelsByPolicyLastUpdateRange(ctx context.Context, arg sqlc.GetChannelsByPolicyLastUpdateRangeParams) ([]sqlc.GetChannelsByPolicyLastUpdateRangeRow, error)
9394

9495
CreateChannelExtraType(ctx context.Context, arg sqlc.CreateChannelExtraTypeParams) error
9596
InsertChannelFeature(ctx context.Context, arg sqlc.InsertChannelFeatureParams) error
@@ -924,6 +925,125 @@ func (s *SQLStore) ForEachNodeChannel(nodePub route.Vertex,
924925
}, sqldb.NoOpReset)
925926
}
926927

928+
// ChanUpdatesInHorizon returns all the known channel edges which have at least
929+
// one edge that has an update timestamp within the specified horizon.
930+
//
931+
// NOTE: This is part of the V1Store interface.
932+
func (s *SQLStore) ChanUpdatesInHorizon(startTime,
933+
endTime time.Time) ([]ChannelEdge, error) {
934+
935+
s.cacheMu.Lock()
936+
defer s.cacheMu.Unlock()
937+
938+
var (
939+
ctx = context.TODO()
940+
// To ensure we don't return duplicate ChannelEdges, we'll use
941+
// an additional map to keep track of the edges already seen to
942+
// prevent re-adding it.
943+
edgesSeen = make(map[uint64]struct{})
944+
edgesToCache = make(map[uint64]ChannelEdge)
945+
edges []ChannelEdge
946+
hits int
947+
)
948+
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
949+
rows, err := db.GetChannelsByPolicyLastUpdateRange(
950+
ctx, sqlc.GetChannelsByPolicyLastUpdateRangeParams{
951+
Version: int16(ProtocolV1),
952+
StartTime: sqldb.SQLInt64(startTime.Unix()),
953+
EndTime: sqldb.SQLInt64(endTime.Unix()),
954+
},
955+
)
956+
if err != nil {
957+
return err
958+
}
959+
960+
for _, row := range rows {
961+
// If we've already retrieved the info and policies for
962+
// this edge, then we can skip it as we don't need to do
963+
// so again.
964+
chanIDInt := byteOrder.Uint64(row.Channel.Scid)
965+
if _, ok := edgesSeen[chanIDInt]; ok {
966+
continue
967+
}
968+
969+
if channel, ok := s.chanCache.get(chanIDInt); ok {
970+
hits++
971+
edgesSeen[chanIDInt] = struct{}{}
972+
edges = append(edges, channel)
973+
974+
continue
975+
}
976+
977+
node1, node2, err := buildNodes(
978+
ctx, db, row.Node, row.Node_2,
979+
)
980+
if err != nil {
981+
return err
982+
}
983+
984+
channel, err := getAndBuildEdgeInfo(
985+
ctx, db, s.cfg.ChainHash, row.Channel.ID,
986+
row.Channel, node1.PubKeyBytes,
987+
node2.PubKeyBytes,
988+
)
989+
if err != nil {
990+
return fmt.Errorf("unable to build channel "+
991+
"info: %w", err)
992+
}
993+
994+
dbPol1, dbPol2, err := extractChannelPolicies(row)
995+
if err != nil {
996+
return fmt.Errorf("unable to extract channel "+
997+
"policies: %w", err)
998+
}
999+
1000+
p1, p2, err := getAndBuildChanPolicies(
1001+
ctx, db, dbPol1, dbPol2, channel.ChannelID,
1002+
node1.PubKeyBytes, node2.PubKeyBytes,
1003+
)
1004+
if err != nil {
1005+
return fmt.Errorf("unable to build channel "+
1006+
"policies: %w", err)
1007+
}
1008+
1009+
edgesSeen[chanIDInt] = struct{}{}
1010+
chanEdge := ChannelEdge{
1011+
Info: channel,
1012+
Policy1: p1,
1013+
Policy2: p2,
1014+
Node1: node1,
1015+
Node2: node2,
1016+
}
1017+
edges = append(edges, chanEdge)
1018+
edgesToCache[chanIDInt] = chanEdge
1019+
}
1020+
1021+
return nil
1022+
}, func() {
1023+
edgesSeen = make(map[uint64]struct{})
1024+
edgesToCache = make(map[uint64]ChannelEdge)
1025+
edges = nil
1026+
})
1027+
if err != nil {
1028+
return nil, fmt.Errorf("unable to fetch channels: %w", err)
1029+
}
1030+
1031+
// Insert any edges loaded from disk into the cache.
1032+
for chanid, channel := range edgesToCache {
1033+
s.chanCache.insert(chanid, channel)
1034+
}
1035+
1036+
if len(edges) > 0 {
1037+
log.Debugf("ChanUpdatesInHorizon hit percentage: %f (%d/%d)",
1038+
float64(hits)/float64(len(edges)), hits, len(edges))
1039+
} else {
1040+
log.Debugf("ChanUpdatesInHorizon returned no edges in "+
1041+
"horizon (%s, %s)", startTime, endTime)
1042+
}
1043+
1044+
return edges, nil
1045+
}
1046+
9271047
// forEachNodeDirectedChannel iterates through all channels of a given
9281048
// node, executing the passed callback on the directed edge representing the
9291049
// channel and its incoming policy. If the node is not found, no error is
@@ -977,12 +1097,7 @@ func forEachNodeDirectedChannel(ctx context.Context, db SQLQueries,
9771097
err)
9781098
}
9791099

980-
edge, err := buildCacheableChannelInfo(
981-
row.Channel, node1, node2,
982-
)
983-
if err != nil {
984-
return err
985-
}
1100+
edge := buildCacheableChannelInfo(row.Channel, node1, node2)
9861101

9871102
dbPol1, dbPol2, err := extractChannelPolicies(row)
9881103
if err != nil {
@@ -1286,14 +1401,14 @@ func getNodeByPubKey(ctx context.Context, db SQLQueries,
12861401
// provided database channel row and the public keys of the two nodes
12871402
// involved in the channel.
12881403
func buildCacheableChannelInfo(dbChan sqlc.Channel, node1Pub,
1289-
node2Pub route.Vertex) (*models.CachedEdgeInfo, error) {
1404+
node2Pub route.Vertex) *models.CachedEdgeInfo {
12901405

12911406
return &models.CachedEdgeInfo{
12921407
ChannelID: byteOrder.Uint64(dbChan.Scid),
12931408
NodeKey1Bytes: node1Pub,
12941409
NodeKey2Bytes: node2Pub,
12951410
Capacity: btcutil.Amount(dbChan.Capacity.Int64),
1296-
}, nil
1411+
}
12971412
}
12981413

12991414
// buildNode constructs a LightningNode instance from the given database node
@@ -2302,17 +2417,76 @@ func buildChanPolicy(dbPolicy sqlc.ChannelPolicy, channelID uint64,
23022417
}, nil
23032418
}
23042419

2420+
// buildNodes builds the models.LightningNode instances for the
2421+
// given row which is expected to be a sqlc type that contains node information.
2422+
func buildNodes(ctx context.Context, db SQLQueries, dbNode1,
2423+
dbNode2 sqlc.Node) (*models.LightningNode, *models.LightningNode,
2424+
error) {
2425+
2426+
node1, err := buildNode(ctx, db, &dbNode1)
2427+
if err != nil {
2428+
return nil, nil, err
2429+
}
2430+
2431+
node2, err := buildNode(ctx, db, &dbNode2)
2432+
if err != nil {
2433+
return nil, nil, err
2434+
}
2435+
2436+
return node1, node2, nil
2437+
}
2438+
23052439
// extractChannelPolicies extracts the sqlc.ChannelPolicy records from the give
23062440
// row which is expected to be a sqlc type that contains channel policy
23072441
// information. It returns two policies, which may be nil if the policy
23082442
// information is not present in the row.
23092443
//
2310-
//nolint:ll
2444+
//nolint:ll,dupl
23112445
func extractChannelPolicies(row any) (*sqlc.ChannelPolicy, *sqlc.ChannelPolicy,
23122446
error) {
23132447

23142448
var policy1, policy2 *sqlc.ChannelPolicy
23152449
switch r := row.(type) {
2450+
case sqlc.GetChannelsByPolicyLastUpdateRangeRow:
2451+
if r.Policy1ID.Valid {
2452+
policy1 = &sqlc.ChannelPolicy{
2453+
ID: r.Policy1ID.Int64,
2454+
Version: r.Policy1Version.Int16,
2455+
ChannelID: r.Channel.ID,
2456+
NodeID: r.Policy1NodeID.Int64,
2457+
Timelock: r.Policy1Timelock.Int32,
2458+
FeePpm: r.Policy1FeePpm.Int64,
2459+
BaseFeeMsat: r.Policy1BaseFeeMsat.Int64,
2460+
MinHtlcMsat: r.Policy1MinHtlcMsat.Int64,
2461+
MaxHtlcMsat: r.Policy1MaxHtlcMsat,
2462+
LastUpdate: r.Policy1LastUpdate,
2463+
InboundBaseFeeMsat: r.Policy1InboundBaseFeeMsat,
2464+
InboundFeeRateMilliMsat: r.Policy1InboundFeeRateMilliMsat,
2465+
Disabled: r.Policy1Disabled,
2466+
Signature: r.Policy1Signature,
2467+
}
2468+
}
2469+
if r.Policy2ID.Valid {
2470+
policy2 = &sqlc.ChannelPolicy{
2471+
ID: r.Policy2ID.Int64,
2472+
Version: r.Policy2Version.Int16,
2473+
ChannelID: r.Channel.ID,
2474+
NodeID: r.Policy2NodeID.Int64,
2475+
Timelock: r.Policy2Timelock.Int32,
2476+
FeePpm: r.Policy2FeePpm.Int64,
2477+
BaseFeeMsat: r.Policy2BaseFeeMsat.Int64,
2478+
MinHtlcMsat: r.Policy2MinHtlcMsat.Int64,
2479+
MaxHtlcMsat: r.Policy2MaxHtlcMsat,
2480+
LastUpdate: r.Policy2LastUpdate,
2481+
InboundBaseFeeMsat: r.Policy2InboundBaseFeeMsat,
2482+
InboundFeeRateMilliMsat: r.Policy2InboundFeeRateMilliMsat,
2483+
Disabled: r.Policy2Disabled,
2484+
Signature: r.Policy2Signature,
2485+
}
2486+
}
2487+
2488+
return policy1, policy2, nil
2489+
23162490
case sqlc.ListChannelsByNodeIDRow:
23172491
if r.Policy1ID.Valid {
23182492
policy1 = &sqlc.ChannelPolicy{

0 commit comments

Comments
 (0)