Skip to content

Commit 45c1564

Browse files
authored
Merge pull request #9945 from ziggie1984/optional-migration
Decayed log optional migration
2 parents 09df589 + 8208eb6 commit 45c1564

File tree

11 files changed

+301
-72
lines changed

11 files changed

+301
-72
lines changed

channeldb/db.go

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/lightningnetwork/lnd/channeldb/migration31"
3030
"github.com/lightningnetwork/lnd/channeldb/migration32"
3131
"github.com/lightningnetwork/lnd/channeldb/migration33"
32+
"github.com/lightningnetwork/lnd/channeldb/migration34"
3233
"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
3334
"github.com/lightningnetwork/lnd/clock"
3435
graphdb "github.com/lightningnetwork/lnd/graph/db"
@@ -74,12 +75,14 @@ type mandatoryVersion struct {
7475
// optional migrations.
7576
type MigrationConfig interface {
7677
migration30.MigrateRevLogConfig
78+
migration34.MigrationConfig
7779
}
7880

7981
// MigrationConfigImpl is a super set of all the various migration configs and
8082
// an implementation of MigrationConfig.
8183
type MigrationConfigImpl struct {
8284
migration30.MigrateRevLogConfigImpl
85+
migration34.MigrationConfigImpl
8386
}
8487

8588
// optionalMigration defines an optional migration function. When a migration
@@ -308,13 +311,23 @@ var (
308311
// to determine its state.
309312
optionalVersions = []optionalVersion{
310313
{
311-
name: "prune revocation log",
314+
name: "prune_revocation_log",
312315
migration: func(db kvdb.Backend,
313316
cfg MigrationConfig) error {
314317

315318
return migration30.MigrateRevocationLog(db, cfg)
316319
},
317320
},
321+
{
322+
name: "gc_decayed_log",
323+
migration: func(db kvdb.Backend,
324+
cfg MigrationConfig) error {
325+
326+
return migration34.MigrateDecayedLog(
327+
db, cfg,
328+
)
329+
},
330+
},
318331
}
319332

320333
// Big endian is the preferred byte order, due to cursor scans over
@@ -1731,10 +1744,8 @@ func (d *DB) syncVersions(versions []mandatoryVersion) error {
17311744
}, func() {})
17321745
}
17331746

1734-
// applyOptionalVersions takes a config to determine whether the optional
1735-
// migrations will be applied.
1736-
//
1737-
// NOTE: only support the prune_revocation_log optional migration atm.
1747+
// applyOptionalVersions applies the optional migrations to the database if
1748+
// specified in the config.
17381749
func (d *DB) applyOptionalVersions(cfg OptionalMiragtionConfig) error {
17391750
// TODO(yy): need to design the db to support dry run for optional
17401751
// migrations.
@@ -1751,50 +1762,71 @@ func (d *DB) applyOptionalVersions(cfg OptionalMiragtionConfig) error {
17511762
Versions: make(map[uint64]string),
17521763
}
17531764
} else {
1754-
return err
1765+
return fmt.Errorf("unable to fetch optional "+
1766+
"meta: %w", err)
17551767
}
17561768
}
17571769

1758-
log.Infof("Checking for optional update: prune_revocation_log=%v, "+
1759-
"db_version=%s", cfg.PruneRevocationLog, om)
1760-
1761-
// Exit early if the optional migration is not specified.
1762-
if !cfg.PruneRevocationLog {
1763-
return nil
1764-
}
1765-
1766-
// Exit early if the optional migration has already been applied.
1767-
if _, ok := om.Versions[0]; ok {
1768-
return nil
1769-
}
1770-
1771-
// Get the optional version.
1772-
version := optionalVersions[0]
1773-
log.Infof("Performing database optional migration: %s", version.name)
1774-
1770+
// migrationCfg is the parent configuration which implements the config
1771+
// interfaces of all the single optional migrations.
17751772
migrationCfg := &MigrationConfigImpl{
17761773
migration30.MigrateRevLogConfigImpl{
17771774
NoAmountData: d.noRevLogAmtData,
17781775
},
1776+
migration34.MigrationConfigImpl{
1777+
DecayedLog: cfg.DecayedLog,
1778+
},
17791779
}
17801780

1781-
// Migrate the data.
1782-
if err := version.migration(d, migrationCfg); err != nil {
1783-
log.Errorf("Unable to apply optional migration: %s, error: %v",
1784-
version.name, err)
1785-
return err
1786-
}
1781+
log.Infof("Applying %d optional migrations", len(optionalVersions))
17871782

1788-
// Update the optional meta. Notice that unlike the mandatory db
1789-
// migrations where we perform the migration and updating meta in a
1790-
// single db transaction, we use different transactions here. Even when
1791-
// the following update is failed, we should be fine here as we would
1792-
// re-run the optional migration again, which is a noop, during next
1793-
// startup.
1794-
om.Versions[0] = version.name
1795-
if err := d.putOptionalMeta(om); err != nil {
1796-
log.Errorf("Unable to update optional meta: %v", err)
1797-
return err
1783+
// Apply the optional migrations if requested.
1784+
for number, version := range optionalVersions {
1785+
log.Infof("Checking for optional update: name=%v", version.name)
1786+
1787+
// Exit early if the optional migration is not specified.
1788+
if !cfg.MigrationFlags[number] {
1789+
log.Debugf("Skipping optional migration: name=%s as "+
1790+
"it is not specified in the config",
1791+
version.name)
1792+
1793+
continue
1794+
}
1795+
1796+
// Exit early if the optional migration has already been
1797+
// applied.
1798+
if _, ok := om.Versions[uint64(number)]; ok {
1799+
log.Debugf("Skipping optional migration: name=%s as "+
1800+
"it has already been applied", version.name)
1801+
1802+
continue
1803+
}
1804+
1805+
log.Infof("Performing database optional migration: %s",
1806+
version.name)
1807+
1808+
// Call the migration function for the specific optional
1809+
// migration.
1810+
if err := version.migration(d, migrationCfg); err != nil {
1811+
log.Errorf("Unable to apply optional migration: %s, "+
1812+
"error: %v", version.name, err)
1813+
return err
1814+
}
1815+
1816+
// Update the optional meta. Notice that unlike the mandatory db
1817+
// migrations where we perform the migration and updating meta
1818+
// in a single db transaction, we use different transactions
1819+
// here. Even when the following update is failed, we should be
1820+
// fine here as we would re-run the optional migration again,
1821+
// which is a noop, during next startup.
1822+
om.Versions[uint64(number)] = version.name
1823+
if err := d.putOptionalMeta(om); err != nil {
1824+
log.Errorf("Unable to update optional meta: %v", err)
1825+
return err
1826+
}
1827+
1828+
log.Infof("Successfully applied optional migration: %s",
1829+
version.name)
17981830
}
17991831

18001832
return nil

channeldb/log.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/lightningnetwork/lnd/channeldb/migration31"
1313
"github.com/lightningnetwork/lnd/channeldb/migration32"
1414
"github.com/lightningnetwork/lnd/channeldb/migration33"
15+
"github.com/lightningnetwork/lnd/channeldb/migration34"
1516
"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
1617
"github.com/lightningnetwork/lnd/kvdb"
1718
)
@@ -46,5 +47,6 @@ func UseLogger(logger btclog.Logger) {
4647
migration31.UseLogger(logger)
4748
migration32.UseLogger(logger)
4849
migration33.UseLogger(logger)
50+
migration34.UseLogger(logger)
4951
kvdb.UseLogger(logger)
5052
}

channeldb/meta.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7+
"slices"
8+
"strings"
79

810
"github.com/lightningnetwork/lnd/kvdb"
911
"github.com/lightningnetwork/lnd/tlv"
@@ -30,6 +32,10 @@ var (
3032
// ErrMarkerNotPresent is the error that is returned if the queried
3133
// marker is not present in the given database.
3234
ErrMarkerNotPresent = errors.New("marker not present")
35+
36+
// ErrInvalidOptionalVersion is the error that is returned if the
37+
// optional version persisted in the database is invalid.
38+
ErrInvalidOptionalVersion = errors.New("invalid optional version")
3339
)
3440

3541
// Meta structure holds the database meta information.
@@ -104,15 +110,28 @@ type OptionalMeta struct {
104110
Versions map[uint64]string
105111
}
106112

113+
// String returns a string representation of the optional meta.
107114
func (om *OptionalMeta) String() string {
108-
s := ""
109-
for index, name := range om.Versions {
110-
s += fmt.Sprintf("%d: %s", index, name)
115+
if len(om.Versions) == 0 {
116+
return "empty"
117+
}
118+
119+
// Create a slice of indices to sort
120+
indices := make([]uint64, 0, len(om.Versions))
121+
for index := range om.Versions {
122+
indices = append(indices, index)
111123
}
112-
if s == "" {
113-
s = "empty"
124+
125+
// Sort the indices in ascending order.
126+
slices.Sort(indices)
127+
128+
// Create the string parts in sorted order.
129+
parts := make([]string, len(indices))
130+
for i, index := range indices {
131+
parts[i] = fmt.Sprintf("%d: %s", index, om.Versions[index])
114132
}
115-
return s
133+
134+
return strings.Join(parts, ", ")
116135
}
117136

118137
// fetchOptionalMeta reads the optional meta from the database.
@@ -146,7 +165,20 @@ func (d *DB) fetchOptionalMeta() (*OptionalMeta, error) {
146165
if err != nil {
147166
return err
148167
}
149-
om.Versions[version] = optionalVersions[i].name
168+
169+
// This check would not allow to downgrade LND software
170+
// to a version with an optional migration when an
171+
// optional migration not known to the current version
172+
// has already been applied.
173+
if version >= uint64(len(optionalVersions)) {
174+
return fmt.Errorf("optional version read "+
175+
"from db is %d, but only optional "+
176+
"migrations up to %d are known: %w",
177+
version, len(optionalVersions)-1,
178+
ErrInvalidOptionalVersion)
179+
}
180+
181+
om.Versions[version] = optionalVersions[version].name
150182
}
151183

152184
return nil
@@ -174,8 +206,12 @@ func (d *DB) putOptionalMeta(om *OptionalMeta) error {
174206
return err
175207
}
176208

177-
// Write the version indexes.
209+
// Write the version indexes of the single migrations.
178210
for v := range om.Versions {
211+
if v >= uint64(len(optionalVersions)) {
212+
return ErrInvalidOptionalVersion
213+
}
214+
179215
err := tlv.WriteVarInt(&b, v, &[8]byte{})
180216
if err != nil {
181217
return err

channeldb/meta_test.go

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ func TestOptionalMeta(t *testing.T) {
498498
om = &OptionalMeta{
499499
Versions: map[uint64]string{
500500
0: optionalVersions[0].name,
501+
1: optionalVersions[1].name,
501502
},
502503
}
503504
err = db.putOptionalMeta(om)
@@ -506,43 +507,59 @@ func TestOptionalMeta(t *testing.T) {
506507
om1, err := db.fetchOptionalMeta()
507508
require.NoError(t, err, "error getting optional meta")
508509
require.Equal(t, om, om1, "unexpected empty versions")
509-
require.Equal(t, "0: prune revocation log", om.String())
510+
require.Equal(
511+
t, "0: prune_revocation_log, 1: gc_decayed_log",
512+
om1.String(),
513+
)
510514
}
511515

512516
// TestApplyOptionalVersions checks that the optional migration is applied as
513517
// expected based on the config.
518+
//
519+
// NOTE: Cannot be run in parallel because we alter the optionalVersions
520+
// global variable which could be used by other tests.
514521
func TestApplyOptionalVersions(t *testing.T) {
515-
t.Parallel()
516-
517522
db, err := MakeTestDB(t)
518523
require.NoError(t, err)
519524

520-
// Overwrite the migration function so we can count how many times the
521-
// migration has happened.
522-
migrateCount := 0
523-
optionalVersions[0].migration = func(_ kvdb.Backend,
524-
_ MigrationConfig) error {
525+
// migrateCount is the number of migrations that have been run. It
526+
// counts the number of times a migration function is called.
527+
var migrateCount int
525528

526-
migrateCount++
527-
return nil
529+
// Modify all migrations to track their execution.
530+
for i := range optionalVersions {
531+
optionalVersions[i].migration = func(_ kvdb.Backend,
532+
_ MigrationConfig) error {
533+
534+
migrateCount++
535+
536+
return nil
537+
}
528538
}
529539

530-
// Test that when the flag is false, no migration happens.
531-
cfg := OptionalMiragtionConfig{}
540+
// All migrations are disabled by default.
541+
cfg := NewOptionalMiragtionConfig()
542+
543+
// Run the optional migrations.
532544
err = db.applyOptionalVersions(cfg)
533545
require.NoError(t, err, "failed to apply optional migration")
534546
require.Equal(t, 0, migrateCount, "expected no migration")
535547

536548
// Check the optional meta is not updated.
537549
om, err := db.fetchOptionalMeta()
538550
require.NoError(t, err, "error getting optional meta")
539-
require.Empty(t, om.Versions, "expected empty versions")
540551

541-
// Test that when specified, the optional migration is applied.
542-
cfg.PruneRevocationLog = true
552+
// Enable all optional migrations.
553+
for i := range cfg.MigrationFlags {
554+
cfg.MigrationFlags[i] = true
555+
}
556+
543557
err = db.applyOptionalVersions(cfg)
544558
require.NoError(t, err, "failed to apply optional migration")
545-
require.Equal(t, 1, migrateCount, "expected migration")
559+
require.Equal(
560+
t, len(optionalVersions), migrateCount,
561+
"expected all migrations to be run",
562+
)
546563

547564
// Fetch the updated optional meta.
548565
om, err = db.fetchOptionalMeta()
@@ -552,16 +569,20 @@ func TestApplyOptionalVersions(t *testing.T) {
552569
omExpected := &OptionalMeta{
553570
Versions: map[uint64]string{
554571
0: optionalVersions[0].name,
572+
1: optionalVersions[1].name,
555573
},
556574
}
557575
require.Equal(t, omExpected, om, "unexpected empty versions")
558576

559-
// Test that though specified, the optional migration is not run since
560-
// it's already been applied.
561-
cfg.PruneRevocationLog = true
577+
// We make sure running the migrations again does not call the
578+
// migrations again because the meta data should signal that they have
579+
// already been run.
562580
err = db.applyOptionalVersions(cfg)
563581
require.NoError(t, err, "failed to apply optional migration")
564-
require.Equal(t, 1, migrateCount, "expected no migration")
582+
require.Equal(
583+
t, len(optionalVersions), migrateCount,
584+
"expected all migrations to be run",
585+
)
565586
}
566587

567588
// TestFetchMeta tests that the FetchMeta returns the latest DB version for a

channeldb/migration34/log.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package migration34
2+
3+
import (
4+
"github.com/btcsuite/btclog/v2"
5+
)
6+
7+
// log is a logger that is initialized as disabled. This means the package will
8+
// not perform any logging by default until a logger is set.
9+
var log = btclog.Disabled
10+
11+
// UseLogger uses a specified Logger to output package logging info.
12+
func UseLogger(logger btclog.Logger) {
13+
log = logger
14+
}

0 commit comments

Comments
 (0)