payments/migration1: wire KV→SQL migration in the main pkg

Hook the payments KV→SQL migration into the SQL migration config.
The migration is still only available when building with the build tag
"test_native_sql".

Moreover a tombstone protection similar to the invoice migration is added
to prevent re-runningi with the KV backend  once migration completes.
This commit is contained in:
ziggie committed 2026-02-25 18:52:33 +01:00
1 parent 4b30bed0ac
commit f174b60b94
4 files changed
+145

No files matched your search

+58
View File
@@ -51,6 +51,8 @@ import (
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/msgmux"
paymentsdb "github.com/lightningnetwork/lnd/payments/db"
paymentsmig1 "github.com/lightningnetwork/lnd/payments/db/migration1"
paymentsmig1sqlc "github.com/lightningnetwork/lnd/payments/db/migration1/sqlc"
"github.com/lightningnetwork/lnd/rpcperms"
"github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/sqldb"
@@ -76,6 +78,10 @@ const (
// graphMigration is the version number for the graph migration
// that migrates the KV graph to the native SQL schema.
graphMigration = 10
// paymentMigration is the version number for the payments migration
// that migrates KV payments to the native SQL schema.
paymentMigration = 14
)
// GrpcRegistrar is an interface that must be satisfied by an external subserver
@@ -1153,6 +1159,31 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
return nil
}
paymentMig := func(tx *sqlc.Queries) error {
err := paymentsmig1.MigratePaymentsKVToSQL(
ctx,
dbs.ChanStateDB.Backend,
paymentsmig1sqlc.New(tx.GetTx()),
&paymentsmig1.SQLStoreConfig{
QueryCfg: queryCfg,
},
)
if err != nil {
return fmt.Errorf("failed to migrate "+
"payments to SQL: %w", err)
}
// Set the payments bucket tombstone to
// indicate that the migration has been
// completed.
d.logger.Debugf("Setting payments bucket " +
"tombstone")
return paymentsdb.SetPaymentsBucketTombstone(
dbs.ChanStateDB.Backend,
)
}
// Make sure we attach the custom migration function to
// the correct migration version.
for i := 0; i < len(migrations); i++ {
@@ -1162,11 +1193,17 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
migrations[i].MigrationFn = invoiceMig
continue
case graphMigration:
migrations[i].MigrationFn = graphMig
continue
case paymentMigration:
migrations[i].MigrationFn = paymentMig
continue
default:
}
@@ -1265,6 +1302,27 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
return nil, nil, err
}
// Check if the payments bucket tombstone is set. If it is, we
// need to return and ask the user switch back to using the
// native SQL store.
ripPayments, err := paymentsdb.GetPaymentsBucketTombstone(
dbs.ChanStateDB.Backend,
)
if err != nil {
err = fmt.Errorf("unable to check payments bucket "+
"tombstone: %w", err)
d.logger.Error(err)
return nil, nil, err
}
if ripPayments {
err = fmt.Errorf("payments bucket tombstoned, please " +
"switch back to native SQL")
d.logger.Error(err)
return nil, nil, err
}
dbs.InvoiceDB = dbs.ChanStateDB
graphStore, err = graphdb.NewKVStore(
+71
View File
@@ -0,0 +1,71 @@
package paymentsdb
import (
"fmt"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
// paymentsBucketTombstone is the key used to mark the payments bucket
// as permanently closed after a successful migration.
paymentsBucketTombstone = []byte("payments-tombstone")
)
// SetPaymentsBucketTombstone sets the tombstone key in the payments bucket to
// mark the bucket as permanently closed. This prevents it from being reopened
// in the future.
func SetPaymentsBucketTombstone(db kvdb.Backend) error {
return kvdb.Update(db, func(tx kvdb.RwTx) error {
// Access the top-level payments bucket.
payments := tx.ReadWriteBucket(paymentsRootBucket)
// In case the bucket doesn't exist, because we start
// immediately with the native SQL schema, we create it as well
// to make sure the user cannot switch back to the KV store.
if payments == nil {
var err error
payments, err = tx.CreateTopLevelBucket(
paymentsRootBucket,
)
if err != nil {
return fmt.Errorf("failed to create payments "+
"bucket: %w", err)
}
}
// Add the tombstone key to the payments bucket.
err := payments.Put(paymentsBucketTombstone, []byte("1"))
if err != nil {
return fmt.Errorf("failed to set tombstone: %w", err)
}
return nil
}, func() {})
}
// GetPaymentsBucketTombstone checks if the tombstone key exists in the payments
// bucket. It returns true if the tombstone is present and false otherwise.
func GetPaymentsBucketTombstone(db kvdb.Backend) (bool, error) {
var tombstoneExists bool
err := kvdb.View(db, func(tx kvdb.RTx) error {
// Access the top-level payments bucket.
payments := tx.ReadBucket(paymentsRootBucket)
if payments == nil {
tombstoneExists = false
return nil
}
// Check if the tombstone key exists.
tombstone := payments.Get(paymentsBucketTombstone)
tombstoneExists = tombstone != nil
return nil
}, func() {})
if err != nil {
return false, err
}
return tombstoneExists, nil
}
+2
View File
@@ -3,6 +3,7 @@ package paymentsdb
import (
"github.com/btcsuite/btclog/v2"
"github.com/lightningnetwork/lnd/build"
paymentsmig1 "github.com/lightningnetwork/lnd/payments/db/migration1"
)
// log is a logger that is initialized with no output filters. This
@@ -29,4 +30,5 @@ func DisableLog() {
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
paymentsmig1.UseLogger(logger)
}
+14
View File
@@ -8,4 +8,18 @@ var migrationAdditions = []MigrationConfig{
Version: 12,
SchemaVersion: 10,
},
{
Name: "000011_payment_duplicates",
Version: 13,
SchemaVersion: 11,
},
{
Name: "kv_payments_migration",
Version: 14,
SchemaVersion: 11,
// A migration function may be attached to this
// migration to migrate KV payments to the native SQL
// schema. This is optional and can be disabled by the
// user if necessary.
},
}