channeldb: move .SyncVersions() into .Open(), minor migration cleanups

This commit unexports the SyncVerions PR, in favor of making it private
and moving it into the .Open() method. With this change, callers no
longer need worry about ensuring the database version is up to sync
before usage, as it will happen automatically once the database is
opened.

This commit also unexports some additional variables within the package
that don’t need be consumed by the public, and exports the
DbVersionNumber attribute on the meta struct. Finally some minor
formatting fixes have neen carried out to ensure the new code more
closely matches the style of the rest of the codebase.
This commit is contained in:
Olaoluwa Osuntokun 2016-11-22 15:57:26 -06:00
parent 25167361d2
commit b70d1f8cfe
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
4 changed files with 71 additions and 65 deletions

View File

@ -19,9 +19,9 @@ const (
dbFilePermission = 0600
)
// Migration is a function which takes a prior outdated version of the database
// instances and mutates the key/bucket structure to arrive at a more up-to-date
// version of the database.
// migration is a function which takes a prior outdated version of the database
// instances and mutates the key/bucket structure to arrive at a more
// up-to-date version of the database.
type migration func(tx *bolt.Tx) error
type version struct {
@ -30,19 +30,20 @@ type version struct {
}
var (
// DBVersions is storing all versions of database. If current version of
// database don't match with latest version this list will be used for
// retrieving all migration function that are need to apply to the
// dbVersions is storing all versions of database. If current version
// of database don't match with latest version this list will be used
// for retrieving all migration function that are need to apply to the
// current db.
DBVersions = []version{
dbVersions = []version{
{
// The base DB version requires no migration.
number: 1,
migration: nil, // The base DB version requires no migration
migration: nil,
},
}
// Big endian is the preferred byte order, due to cursor scans over integer
// keys iterating in order.
// Big endian is the preferred byte order, due to cursor scans over
// integer keys iterating in order.
byteOrder = binary.BigEndian
)
@ -61,7 +62,6 @@ type DB struct {
// Open opens an existing channeldb created under the passed namespace with
// sensitive data encrypted by the passed EncryptorDecryptor implementation.
// TODO(roasbeef): versioning?
func Open(dbPath string, netParams *chaincfg.Params) (*DB, error) {
path := filepath.Join(dbPath, dbName)
@ -76,11 +76,19 @@ func Open(dbPath string, netParams *chaincfg.Params) (*DB, error) {
return nil, err
}
return &DB{
chanDB := &DB{
store: bdb,
netParams: netParams,
dbPath: dbPath,
}, nil
}
// Synchronize the version of database and apply migrations if needed.
if err := chanDB.syncVersions(dbVersions); err != nil {
bdb.Close()
return nil, err
}
return chanDB, nil
}
// Wipe completely deletes all saved state within all used buckets within the
@ -299,42 +307,44 @@ func (d *DB) FetchAllChannels() ([]*OpenChannel, error) {
return channels, err
}
// SyncVersions function is used for safe db version synchronization. It applies
// syncVersions function is used for safe db version synchronization. It applies
// migration functions to the current database and recovers the previous
// state of db if at least one error/panic appeared during migration.
func (d *DB) SyncVersions(versions []version) error {
func (d *DB) syncVersions(versions []version) error {
meta, err := d.FetchMeta(nil)
if err != nil {
return err
}
// If the current database version matches the latest version number,
// then we don't need to perform any migrations.
latestVersion := getLatestDBVersion(versions)
if meta.dbVersionNumber < latestVersion {
migrations := getMigrationsToApply(versions, meta.dbVersionNumber)
return d.store.Update(func(tx *bolt.Tx) error {
for _, migration := range migrations {
if migration == nil {
continue
}
if err := migration(tx); err != nil {
return err
}
}
meta.dbVersionNumber = latestVersion
if err := d.PutMeta(meta, tx); err != nil {
return err
}
return nil
})
if meta.DbVersionNumber == latestVersion {
return nil
}
return nil
// Otherwise, we fetch the migrations which need to applied, and
// execute them serially within a single database transaction to ensure
// the migration is atomic.
migrations := getMigrationsToApply(versions, meta.DbVersionNumber)
return d.store.Update(func(tx *bolt.Tx) error {
for _, migration := range migrations {
if migration == nil {
continue
}
if err := migration(tx); err != nil {
return err
}
}
meta.DbVersionNumber = latestVersion
if err := d.PutMeta(meta, tx); err != nil {
return err
}
return nil
})
}
func getLatestDBVersion(versions []version) uint32 {

View File

@ -9,14 +9,15 @@ var (
// the database.
metaBucket = []byte("metadata")
// dbVersionKey is a boltdb key and it's used for storing/retrieveing
// dbVersionKey is a boltdb key and it's used for storing/retrieving
// current database version.
dbVersionKey = []byte("dbp")
)
// Meta structure holds the database meta information.
type Meta struct {
dbVersionNumber uint32
// DbVersionNumber is the current schema version of the database.
DbVersionNumber uint32
}
// FetchMeta fetches the meta data from boltdb and returns filled meta
@ -74,7 +75,7 @@ func (d *DB) PutMeta(meta *Meta, tx *bolt.Tx) error {
func putDbVersion(metaBucket *bolt.Bucket, meta *Meta) error {
scratch := make([]byte, 4)
byteOrder.PutUint32(scratch, meta.dbVersionNumber)
byteOrder.PutUint32(scratch, meta.DbVersionNumber)
if err := metaBucket.Put(dbVersionKey, scratch); err != nil {
return err
}
@ -83,8 +84,8 @@ func putDbVersion(metaBucket *bolt.Bucket, meta *Meta) error {
func fetchDbVersion(metaBucket *bolt.Bucket, meta *Meta) {
if data := metaBucket.Get(dbVersionKey); data != nil {
meta.dbVersionNumber = byteOrder.Uint32(data)
meta.DbVersionNumber = byteOrder.Uint32(data)
} else {
meta.dbVersionNumber = getLatestDBVersion(DBVersions)
meta.DbVersionNumber = getLatestDBVersion(dbVersions)
}
}

View File

@ -2,9 +2,10 @@ package channeldb
import (
"bytes"
"testing"
"github.com/boltdb/bolt"
"github.com/go-errors/errors"
"testing"
)
// TestVersionFetchPut checks the propernces of fetch/put methods
@ -22,12 +23,12 @@ func TestVersionFetchPut(t *testing.T) {
t.Fatal(err)
}
if meta.dbVersionNumber != getLatestDBVersion(DBVersions) {
if meta.DbVersionNumber != getLatestDBVersion(dbVersions) {
t.Fatal("initialization of meta information wasn't performed")
}
var newVersion uint32 = getLatestDBVersion(DBVersions) + 1
meta.dbVersionNumber = newVersion
var newVersion uint32 = getLatestDBVersion(dbVersions) + 1
meta.DbVersionNumber = newVersion
if err := db.PutMeta(meta, nil); err != nil {
t.Fatalf("update of meta failed %v", err)
@ -38,7 +39,7 @@ func TestVersionFetchPut(t *testing.T) {
t.Fatal(err)
}
if meta.dbVersionNumber != newVersion {
if meta.DbVersionNumber != newVersion {
t.Fatal("update of meta information wasn't performed")
}
}
@ -87,17 +88,17 @@ func TestOrderOfMigrations(t *testing.T) {
// TestGlobalVersionList checks that there is no mistake in global version list
// in terms of version ordering.
func TestGlobalVersionList(t *testing.T) {
if DBVersions == nil {
if dbVersions == nil {
t.Fatal("can't find versions list")
}
if len(DBVersions) == 0 {
if len(dbVersions) == 0 {
t.Fatal("db versions list is empty")
}
prev := DBVersions[0].number
for i := 1; i < len(DBVersions); i++ {
version := DBVersions[i].number
prev := dbVersions[0].number
for i := 1; i < len(dbVersions); i++ {
version := dbVersions[i].number
if version == prev {
t.Fatal("duplicates db versions")
@ -127,7 +128,7 @@ func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB),
// Create test meta info with zero database version and put it on disk.
// Than creating the version list pretending that new version was added.
meta := &Meta{dbVersionNumber: 0}
meta := &Meta{DbVersionNumber: 0}
cdb.PutMeta(meta, nil)
versions := []version{
@ -158,7 +159,7 @@ func applyMigration(t *testing.T, beforeMigration, afterMigration func(d *DB),
}()
// Sync with the latest version - applying migration function.
err = cdb.SyncVersions(versions)
err = cdb.syncVersions(versions)
}
func TestMigrationWithPanic(t *testing.T) {
@ -200,7 +201,7 @@ func TestMigrationWithPanic(t *testing.T) {
t.Fatal(err)
}
if meta.dbVersionNumber != 0 {
if meta.DbVersionNumber != 0 {
t.Fatal("migration paniced but version is changed")
}
@ -268,7 +269,7 @@ func TestMigrationWithFatal(t *testing.T) {
t.Fatal(err)
}
if meta.dbVersionNumber != 0 {
if meta.DbVersionNumber != 0 {
t.Fatal("migration failed but version is changed")
}
@ -335,7 +336,7 @@ func TestMigrationWithoutErrors(t *testing.T) {
t.Fatal(err)
}
if meta.dbVersionNumber != 1 {
if meta.DbVersionNumber != 1 {
t.Fatal("version number isn't changed after " +
"succesfully aplied migration")
}

6
lnd.go
View File

@ -67,12 +67,6 @@ func lndMain() error {
}
defer chanDB.Close()
// Synchronize the version of database and apply migration if needed.
if err := chanDB.SyncVersions(channeldb.DBVersions); err != nil {
fmt.Println("unable to sync versions: ", err)
return err
}
// Next load btcd's TLS cert for the RPC connection. If a raw cert was
// specified in the config, then we'll se that directly. Otherwise, we
// attempt to read the cert from the path specified in the config.