mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-01 02:02:10 +02:00
Merge pull request #9438 from bhandras/invoice-bucket-tombstone
channeldb+lnd: set invoice bucket tombstone after migration
This commit is contained in:
@@ -103,3 +103,28 @@ func TestEncodeDecodeAmpInvoiceState(t *testing.T) {
|
|||||||
// The two states should match.
|
// The two states should match.
|
||||||
require.Equal(t, ampState, ampState2)
|
require.Equal(t, ampState, ampState2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestInvoiceBucketTombstone tests the behavior of setting and checking the
|
||||||
|
// invoice bucket tombstone. It verifies that the tombstone can be set correctly
|
||||||
|
// and detected when present in the database.
|
||||||
|
func TestInvoiceBucketTombstone(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Initialize a test database.
|
||||||
|
db, err := MakeTestDB(t)
|
||||||
|
require.NoError(t, err, "unable to initialize db")
|
||||||
|
|
||||||
|
// Ensure the tombstone doesn't exist initially.
|
||||||
|
tombstoneExists, err := db.GetInvoiceBucketTombstone()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, tombstoneExists)
|
||||||
|
|
||||||
|
// Set the tombstone.
|
||||||
|
err = db.SetInvoiceBucketTombstone()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that the tombstone exists after setting it.
|
||||||
|
tombstoneExists, err = db.GetInvoiceBucketTombstone()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, tombstoneExists)
|
||||||
|
}
|
||||||
|
@@ -80,6 +80,13 @@ var (
|
|||||||
//
|
//
|
||||||
// settleIndexNo => invoiceKey
|
// settleIndexNo => invoiceKey
|
||||||
settleIndexBucket = []byte("invoice-settle-index")
|
settleIndexBucket = []byte("invoice-settle-index")
|
||||||
|
|
||||||
|
// invoiceBucketTombstone is a special key that indicates the invoice
|
||||||
|
// bucket has been permanently closed. Its purpose is to prevent the
|
||||||
|
// invoice bucket from being reopened in the future. A key use case for
|
||||||
|
// the tombstone is to ensure users cannot switch back to the KV invoice
|
||||||
|
// database after migrating to the native SQL database.
|
||||||
|
invoiceBucketTombstone = []byte("invoice-tombstone")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -2400,3 +2407,49 @@ func (d *DB) DeleteInvoice(_ context.Context,
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetInvoiceBucketTombstone sets the tombstone key in the invoice bucket to
|
||||||
|
// mark the bucket as permanently closed. This prevents it from being reopened
|
||||||
|
// in the future.
|
||||||
|
func (d *DB) SetInvoiceBucketTombstone() error {
|
||||||
|
return kvdb.Update(d, func(tx kvdb.RwTx) error {
|
||||||
|
// Access the top-level invoice bucket.
|
||||||
|
invoices := tx.ReadWriteBucket(invoiceBucket)
|
||||||
|
if invoices == nil {
|
||||||
|
return fmt.Errorf("invoice bucket does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the tombstone key to the invoice bucket.
|
||||||
|
err := invoices.Put(invoiceBucketTombstone, []byte("1"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set tombstone: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvoiceBucketTombstone checks if the tombstone key exists in the invoice
|
||||||
|
// bucket. It returns true if the tombstone is present and false otherwise.
|
||||||
|
func (d *DB) GetInvoiceBucketTombstone() (bool, error) {
|
||||||
|
var tombstoneExists bool
|
||||||
|
|
||||||
|
err := kvdb.View(d, func(tx kvdb.RTx) error {
|
||||||
|
// Access the top-level invoice bucket.
|
||||||
|
invoices := tx.ReadBucket(invoiceBucket)
|
||||||
|
if invoices == nil {
|
||||||
|
return fmt.Errorf("invoice bucket does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the tombstone key exists.
|
||||||
|
tombstone := invoices.Get(invoiceBucketTombstone)
|
||||||
|
tombstoneExists = tombstone != nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, func() {})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tombstoneExists, nil
|
||||||
|
}
|
||||||
|
@@ -1101,11 +1101,22 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
|
|||||||
// regardless of the flag.
|
// regardless of the flag.
|
||||||
if !d.cfg.DB.SkipSQLInvoiceMigration {
|
if !d.cfg.DB.SkipSQLInvoiceMigration {
|
||||||
migrationFn := func(tx *sqlc.Queries) error {
|
migrationFn := func(tx *sqlc.Queries) error {
|
||||||
return invoices.MigrateInvoicesToSQL(
|
err := invoices.MigrateInvoicesToSQL(
|
||||||
ctx, dbs.ChanStateDB.Backend,
|
ctx, dbs.ChanStateDB.Backend,
|
||||||
dbs.ChanStateDB, tx,
|
dbs.ChanStateDB, tx,
|
||||||
invoiceMigrationBatchSize,
|
invoiceMigrationBatchSize,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to migrate "+
|
||||||
|
"invoices to SQL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the invoice bucket tombstone to indicate
|
||||||
|
// that the migration has been completed.
|
||||||
|
d.logger.Debugf("Setting invoice bucket " +
|
||||||
|
"tombstone")
|
||||||
|
|
||||||
|
return dbs.ChanStateDB.SetInvoiceBucketTombstone() //nolint:ll
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we attach the custom migration function to
|
// Make sure we attach the custom migration function to
|
||||||
@@ -1147,6 +1158,28 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
|
|||||||
|
|
||||||
dbs.InvoiceDB = sqlInvoiceDB
|
dbs.InvoiceDB = sqlInvoiceDB
|
||||||
} else {
|
} else {
|
||||||
|
// Check if the invoice bucket tombstone is set. If it is, we
|
||||||
|
// need to return and ask the user switch back to using the
|
||||||
|
// native SQL store.
|
||||||
|
ripInvoices, err := dbs.ChanStateDB.GetInvoiceBucketTombstone()
|
||||||
|
d.logger.Debugf("Invoice bucket tombstone set to: %v",
|
||||||
|
ripInvoices)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to check invoice bucket "+
|
||||||
|
"tombstone: %w", err)
|
||||||
|
d.logger.Error(err)
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if ripInvoices {
|
||||||
|
err = fmt.Errorf("invoices bucket tombstoned, please " +
|
||||||
|
"switch back to native SQL")
|
||||||
|
d.logger.Error(err)
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
dbs.InvoiceDB = dbs.ChanStateDB
|
dbs.InvoiceDB = dbs.ChanStateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -299,6 +299,9 @@ The underlying functionality between those two options remain the same.
|
|||||||
SQL](https://github.com/lightningnetwork/lnd/pull/8831) as part of a larger
|
SQL](https://github.com/lightningnetwork/lnd/pull/8831) as part of a larger
|
||||||
effort to support SQL databases natively in LND.
|
effort to support SQL databases natively in LND.
|
||||||
|
|
||||||
|
* [Set invoice bucket
|
||||||
|
](https://github.com/lightningnetwork/lnd/pull/9438) tombstone after native
|
||||||
|
SQL migration.
|
||||||
|
|
||||||
## Code Health
|
## Code Health
|
||||||
|
|
||||||
|
@@ -298,6 +298,16 @@ func testInvoiceMigration(ht *lntest.HarnessTest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now restart Bob without the --db.use-native-sql flag so we can check
|
||||||
|
// that the KV tombstone was set and that Bob will fail to start.
|
||||||
|
require.NoError(ht, bob.Stop())
|
||||||
|
bob.SetExtraArgs(nil)
|
||||||
|
|
||||||
|
// Bob should now fail to start due to the tombstone being set.
|
||||||
|
require.NoError(ht, bob.StartLndCmd(ht.Context()))
|
||||||
|
require.Error(ht, bob.WaitForProcessExit())
|
||||||
|
|
||||||
// Start Bob again so the test can complete.
|
// Start Bob again so the test can complete.
|
||||||
|
bob.SetExtraArgs([]string{"--db.use-native-sql"})
|
||||||
require.NoError(ht, bob.Start(ht.Context()))
|
require.NoError(ht, bob.Start(ht.Context()))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user