mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-17 13:06:52 +02:00
In this commit, we add a persistent store backed by bolt for the client portion of CLM in which we'll store critical information required for its proper operation.
122 lines
3.5 KiB
Go
122 lines
3.5 KiB
Go
package clientdb
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/coreos/bbolt"
|
|
)
|
|
|
|
// migration is a function which takes a prior outdated version of the database
|
|
// instance and mutates the key/bucket structure to arrive at a more up-to-date
|
|
// version of the database.
|
|
type migration func(tx *bbolt.Tx) error
|
|
|
|
var (
|
|
// metadataBucketKey stores all the metadata concerning the state of the
|
|
// database.
|
|
metadataBucketKey = []byte("metadata")
|
|
|
|
// dbVersionKey is the key used for storing/retrieving the current
|
|
// database version.
|
|
dbVersionKey = []byte("version")
|
|
|
|
// ErrDBReversion is returned when detecting an attempt to revert to a
|
|
// prior database version.
|
|
ErrDBReversion = errors.New("cannot revert to prior version")
|
|
|
|
// 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.
|
|
migrations []migration
|
|
|
|
latestDBVersion = uint32(len(migrations))
|
|
)
|
|
|
|
// getDBVersion retrieves the current database version.
|
|
func getDBVersion(bucket *bbolt.Bucket) (uint32, error) {
|
|
versionBytes := bucket.Get(dbVersionKey)
|
|
if versionBytes == nil {
|
|
return 0, errors.New("database version not found")
|
|
}
|
|
return byteOrder.Uint32(versionBytes), nil
|
|
}
|
|
|
|
// setDBVersion updates the current database version.
|
|
func setDBVersion(bucket *bbolt.Bucket, version uint32) error {
|
|
var b [4]byte
|
|
byteOrder.PutUint32(b[:], version)
|
|
return bucket.Put(dbVersionKey, b[:])
|
|
}
|
|
|
|
// getBucket retrieves the bucket with the given key.
|
|
func getBucket(tx *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
|
|
bucket := tx.Bucket(key)
|
|
if bucket == nil {
|
|
return nil, fmt.Errorf("bucket \"%v\" does not exist",
|
|
string(key))
|
|
}
|
|
return bucket, nil
|
|
}
|
|
|
|
// 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 syncVersions(db *bbolt.DB) error {
|
|
var currentVersion uint32
|
|
err := db.View(func(tx *bbolt.Tx) error {
|
|
metadata, err := getBucket(tx, metadataBucketKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currentVersion, err = getDBVersion(metadata)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Infof("Checking for schema update: latest_version=%v, "+
|
|
"db_version=%v", latestDBVersion, currentVersion)
|
|
|
|
switch {
|
|
|
|
// If the database reports a higher version that we are aware of, the
|
|
// user is probably trying to revert to a prior version of lnd. We fail
|
|
// here to prevent reversions and unintended corruption.
|
|
case currentVersion > latestDBVersion:
|
|
log.Errorf("Refusing to revert from db_version=%d to "+
|
|
"lower version=%d", currentVersion,
|
|
latestDBVersion)
|
|
|
|
return ErrDBReversion
|
|
|
|
// If the current database version matches the latest version number,
|
|
// then we don't need to perform any migrations.
|
|
case currentVersion == latestDBVersion:
|
|
return nil
|
|
}
|
|
|
|
log.Infof("Performing database schema migration")
|
|
|
|
// Otherwise we execute the migrations serially within a single database
|
|
// transaction to ensure the migration is atomic.
|
|
return db.Update(func(tx *bbolt.Tx) error {
|
|
for v := currentVersion; v < latestDBVersion; v++ {
|
|
log.Infof("Applying migration #%v", v+1)
|
|
|
|
migration := migrations[v]
|
|
if err := migration(tx); err != nil {
|
|
log.Infof("Unable to apply migration #%v", v+1)
|
|
return err
|
|
}
|
|
}
|
|
|
|
metadata, err := getBucket(tx, metadataBucketKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return setDBVersion(metadata, latestDBVersion)
|
|
})
|
|
}
|