lightning-terminal/session/metadata.go
Elle Mouton e90df06d58
session: migration to populate group ID to session ID indexes
This commit adds a migration to the session store to back-fill the new
session ID to group ID and group ID to session IDs indexes.
2023-08-30 12:34:28 +02:00

131 lines
3.8 KiB
Go

package session
import (
"errors"
"fmt"
"time"
"github.com/lightninglabs/lightning-terminal/session/migration1"
"github.com/lightninglabs/lightning-terminal/session/migration2"
"go.etcd.io/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 the current
// version of the database doesn't match the latest version this list
// will be used for retrieving all migration function that are need to
// apply to the current db.
dbVersions = []migration{
func(tx *bbolt.Tx) error {
return migration1.MigrateSessionIDToKeyIndex(
tx, time.Now,
)
},
migration2.MigrateSessionIDToGroupIndex,
}
latestDBVersion = uint32(len(dbVersions))
)
// 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 := dbVersions[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)
})
}