mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-13 12:32:48 +02:00
channeldb: preserve db version during init
Keep the top-level bucket creation introduced by PR #9653, since initialized DBs can still be missing newer buckets such as the historical channel bucket. Do not let the metadata bucket created during init make a fresh DB look initialized. Use strict metadata reads so a missing metadata/dbp key is distinct from a present DB version, then write metadata/dbp for genuinely fresh DBs. Existing DBs with a metadata bucket but missing metadata/dbp are left for the migration recovery path instead of being treated as latest.
This commit is contained in:
parent
0f4b9c9a18
commit
619913bfb2
4 changed files with 111 additions and 10 deletions
|
|
@ -502,21 +502,36 @@ func initChannelDB(db kvdb.Backend) error {
|
|||
return err
|
||||
}
|
||||
|
||||
meta := &Meta{}
|
||||
metaErr := FetchMeta(meta, tx)
|
||||
|
||||
for _, tlb := range dbTopLevelBuckets {
|
||||
if _, err := tx.CreateTopLevelBucket(tlb); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
meta := &Meta{}
|
||||
// Check if DB is already initialized.
|
||||
err := FetchMeta(meta, tx)
|
||||
if err == nil {
|
||||
switch {
|
||||
// Metadata with a DB version already exists. Required
|
||||
// top-level buckets were created above, so init is complete.
|
||||
case metaErr == nil:
|
||||
return nil
|
||||
|
||||
// There is no metadata bucket at all, so this is a fresh DB.
|
||||
// Initialize the DB version after creating the required
|
||||
// top-level buckets.
|
||||
case errors.Is(metaErr, ErrMetaNotFound):
|
||||
meta.DbVersionNumber = getLatestDBVersion(dbVersions)
|
||||
return putMeta(meta, tx)
|
||||
|
||||
// The DB already has a metadata bucket but no version key.
|
||||
// Leave recovery to the migration path, which can infer a
|
||||
// safe starting version before writing the version key.
|
||||
case errors.Is(metaErr, ErrDBVersionNotFound):
|
||||
return nil
|
||||
}
|
||||
|
||||
meta.DbVersionNumber = getLatestDBVersion(dbVersions)
|
||||
return putMeta(meta, tx)
|
||||
return metaErr
|
||||
}, func() {})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create new channeldb: %w", err)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ var (
|
|||
// created.
|
||||
ErrMetaNotFound = fmt.Errorf("unable to locate meta information")
|
||||
|
||||
// ErrDBVersionNotFound is returned when the meta bucket exists, but
|
||||
// the DB version key hasn't been written.
|
||||
ErrDBVersionNotFound = fmt.Errorf("unable to locate db version")
|
||||
|
||||
// ErrNoClosedChannels is returned when a node is queries for all the
|
||||
// channels it has closed, but it hasn't yet closed any channels.
|
||||
ErrNoClosedChannels = fmt.Errorf("no channel have been closed yet")
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type Meta struct {
|
|||
|
||||
// FetchMeta fetches the metadata from boltdb and returns filled meta structure.
|
||||
func (d *DB) FetchMeta() (*Meta, error) {
|
||||
var meta *Meta
|
||||
meta := &Meta{}
|
||||
|
||||
err := kvdb.View(d, func(tx kvdb.RTx) error {
|
||||
return FetchMeta(meta, tx)
|
||||
|
|
@ -70,11 +70,11 @@ func FetchMeta(meta *Meta, tx kvdb.RTx) error {
|
|||
|
||||
data := metaBucket.Get(dbVersionKey)
|
||||
if data == nil {
|
||||
meta.DbVersionNumber = getLatestDBVersion(dbVersions)
|
||||
} else {
|
||||
meta.DbVersionNumber = byteOrder.Uint32(data)
|
||||
return ErrDBVersionNotFound
|
||||
}
|
||||
|
||||
meta.DbVersionNumber = byteOrder.Uint32(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -603,6 +603,88 @@ func TestFetchMeta(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, LatestDBVersion(), meta.DbVersionNumber)
|
||||
|
||||
err = db.View(func(tx walletdb.ReadTx) error {
|
||||
metaBucket := tx.ReadBucket(metaBucket)
|
||||
require.NotNil(t, metaBucket)
|
||||
|
||||
versionBytes := metaBucket.Get(dbVersionKey)
|
||||
require.Len(t, versionBytes, 4)
|
||||
require.Equal(
|
||||
t, LatestDBVersion(), byteOrder.Uint32(versionBytes),
|
||||
)
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestFetchMetaMissingDBVersion asserts that metadata with no DB version key is
|
||||
// reported as incomplete metadata.
|
||||
func TestFetchMetaMissingDBVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
backend, cleanup, err := kvdb.GetTestBackend(t.TempDir(), "cdb")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
|
||||
_, err := tx.CreateTopLevelBucket(metaBucket)
|
||||
|
||||
return err
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
|
||||
db := &DB{
|
||||
Backend: backend,
|
||||
}
|
||||
|
||||
_, err = db.FetchMeta()
|
||||
require.ErrorIs(t, err, ErrDBVersionNotFound)
|
||||
|
||||
err = kvdb.View(backend, func(tx kvdb.RTx) error {
|
||||
meta := &Meta{}
|
||||
err := FetchMeta(meta, tx)
|
||||
require.ErrorIs(t, err, ErrDBVersionNotFound)
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestInitChannelDBCreatesMissingTopLevelBuckets asserts that initialized DBs
|
||||
// with missing top-level buckets are repaired during initialization.
|
||||
func TestInitChannelDBCreatesMissingTopLevelBuckets(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
backend, cleanup, err := kvdb.GetTestBackend(t.TempDir(), "cdb")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
|
||||
meta := &Meta{
|
||||
DbVersionNumber: LatestDBVersion(),
|
||||
}
|
||||
|
||||
return putMeta(meta, tx)
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = kvdb.View(backend, func(tx kvdb.RTx) error {
|
||||
require.Nil(t, tx.ReadBucket(historicalChannelBucket))
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, initChannelDB(backend))
|
||||
|
||||
err = kvdb.View(backend, func(tx kvdb.RTx) error {
|
||||
require.NotNil(t, tx.ReadBucket(historicalChannelBucket))
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestMarkerAndTombstone tests that markers like a tombstone can be added to a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue