Skip to content

Commit 327eb8d

Browse files
authored
Merge pull request #9438 from bhandras/invoice-bucket-tombstone
channeldb+lnd: set invoice bucket tombstone after migration
2 parents e403243 + 3f6f6c1 commit 327eb8d

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

channeldb/invoice_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,28 @@ func TestEncodeDecodeAmpInvoiceState(t *testing.T) {
103103
// The two states should match.
104104
require.Equal(t, ampState, ampState2)
105105
}
106+
107+
// TestInvoiceBucketTombstone tests the behavior of setting and checking the
108+
// invoice bucket tombstone. It verifies that the tombstone can be set correctly
109+
// and detected when present in the database.
110+
func TestInvoiceBucketTombstone(t *testing.T) {
111+
t.Parallel()
112+
113+
// Initialize a test database.
114+
db, err := MakeTestDB(t)
115+
require.NoError(t, err, "unable to initialize db")
116+
117+
// Ensure the tombstone doesn't exist initially.
118+
tombstoneExists, err := db.GetInvoiceBucketTombstone()
119+
require.NoError(t, err)
120+
require.False(t, tombstoneExists)
121+
122+
// Set the tombstone.
123+
err = db.SetInvoiceBucketTombstone()
124+
require.NoError(t, err)
125+
126+
// Verify that the tombstone exists after setting it.
127+
tombstoneExists, err = db.GetInvoiceBucketTombstone()
128+
require.NoError(t, err)
129+
require.True(t, tombstoneExists)
130+
}

channeldb/invoices.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ var (
8080
//
8181
// settleIndexNo => invoiceKey
8282
settleIndexBucket = []byte("invoice-settle-index")
83+
84+
// invoiceBucketTombstone is a special key that indicates the invoice
85+
// bucket has been permanently closed. Its purpose is to prevent the
86+
// invoice bucket from being reopened in the future. A key use case for
87+
// the tombstone is to ensure users cannot switch back to the KV invoice
88+
// database after migrating to the native SQL database.
89+
invoiceBucketTombstone = []byte("invoice-tombstone")
8390
)
8491

8592
const (
@@ -2400,3 +2407,49 @@ func (d *DB) DeleteInvoice(_ context.Context,
24002407

24012408
return err
24022409
}
2410+
2411+
// SetInvoiceBucketTombstone sets the tombstone key in the invoice bucket to
2412+
// mark the bucket as permanently closed. This prevents it from being reopened
2413+
// in the future.
2414+
func (d *DB) SetInvoiceBucketTombstone() error {
2415+
return kvdb.Update(d, func(tx kvdb.RwTx) error {
2416+
// Access the top-level invoice bucket.
2417+
invoices := tx.ReadWriteBucket(invoiceBucket)
2418+
if invoices == nil {
2419+
return fmt.Errorf("invoice bucket does not exist")
2420+
}
2421+
2422+
// Add the tombstone key to the invoice bucket.
2423+
err := invoices.Put(invoiceBucketTombstone, []byte("1"))
2424+
if err != nil {
2425+
return fmt.Errorf("failed to set tombstone: %w", err)
2426+
}
2427+
2428+
return nil
2429+
}, func() {})
2430+
}
2431+
2432+
// GetInvoiceBucketTombstone checks if the tombstone key exists in the invoice
2433+
// bucket. It returns true if the tombstone is present and false otherwise.
2434+
func (d *DB) GetInvoiceBucketTombstone() (bool, error) {
2435+
var tombstoneExists bool
2436+
2437+
err := kvdb.View(d, func(tx kvdb.RTx) error {
2438+
// Access the top-level invoice bucket.
2439+
invoices := tx.ReadBucket(invoiceBucket)
2440+
if invoices == nil {
2441+
return fmt.Errorf("invoice bucket does not exist")
2442+
}
2443+
2444+
// Check if the tombstone key exists.
2445+
tombstone := invoices.Get(invoiceBucketTombstone)
2446+
tombstoneExists = tombstone != nil
2447+
2448+
return nil
2449+
}, func() {})
2450+
if err != nil {
2451+
return false, err
2452+
}
2453+
2454+
return tombstoneExists, nil
2455+
}

config_builder.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,11 +1101,22 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
11011101
// regardless of the flag.
11021102
if !d.cfg.DB.SkipSQLInvoiceMigration {
11031103
migrationFn := func(tx *sqlc.Queries) error {
1104-
return invoices.MigrateInvoicesToSQL(
1104+
err := invoices.MigrateInvoicesToSQL(
11051105
ctx, dbs.ChanStateDB.Backend,
11061106
dbs.ChanStateDB, tx,
11071107
invoiceMigrationBatchSize,
11081108
)
1109+
if err != nil {
1110+
return fmt.Errorf("failed to migrate "+
1111+
"invoices to SQL: %w", err)
1112+
}
1113+
1114+
// Set the invoice bucket tombstone to indicate
1115+
// that the migration has been completed.
1116+
d.logger.Debugf("Setting invoice bucket " +
1117+
"tombstone")
1118+
1119+
return dbs.ChanStateDB.SetInvoiceBucketTombstone() //nolint:ll
11091120
}
11101121

11111122
// Make sure we attach the custom migration function to
@@ -1147,6 +1158,28 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
11471158

11481159
dbs.InvoiceDB = sqlInvoiceDB
11491160
} else {
1161+
// Check if the invoice bucket tombstone is set. If it is, we
1162+
// need to return and ask the user switch back to using the
1163+
// native SQL store.
1164+
ripInvoices, err := dbs.ChanStateDB.GetInvoiceBucketTombstone()
1165+
d.logger.Debugf("Invoice bucket tombstone set to: %v",
1166+
ripInvoices)
1167+
1168+
if err != nil {
1169+
err = fmt.Errorf("unable to check invoice bucket "+
1170+
"tombstone: %w", err)
1171+
d.logger.Error(err)
1172+
1173+
return nil, nil, err
1174+
}
1175+
if ripInvoices {
1176+
err = fmt.Errorf("invoices bucket tombstoned, please " +
1177+
"switch back to native SQL")
1178+
d.logger.Error(err)
1179+
1180+
return nil, nil, err
1181+
}
1182+
11501183
dbs.InvoiceDB = dbs.ChanStateDB
11511184
}
11521185

docs/release-notes/release-notes-0.19.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ The underlying functionality between those two options remain the same.
299299
SQL](https://github.com/lightningnetwork/lnd/pull/8831) as part of a larger
300300
effort to support SQL databases natively in LND.
301301

302+
* [Set invoice bucket
303+
](https://github.com/lightningnetwork/lnd/pull/9438) tombstone after native
304+
SQL migration.
302305

303306
## Code Health
304307

itest/lnd_invoice_migration_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ func testInvoiceMigration(ht *lntest.HarnessTest) {
298298
}
299299
}
300300

301+
// Now restart Bob without the --db.use-native-sql flag so we can check
302+
// that the KV tombstone was set and that Bob will fail to start.
303+
require.NoError(ht, bob.Stop())
304+
bob.SetExtraArgs(nil)
305+
306+
// Bob should now fail to start due to the tombstone being set.
307+
require.NoError(ht, bob.StartLndCmd(ht.Context()))
308+
require.Error(ht, bob.WaitForProcessExit())
309+
301310
// Start Bob again so the test can complete.
311+
bob.SetExtraArgs([]string{"--db.use-native-sql"})
302312
require.NoError(ht, bob.Start(ht.Context()))
303313
}

0 commit comments

Comments
 (0)