mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-18 13:08:33 +02:00
If trader A provides a signature for a batch, disconnects, and trader B, who is also in the auction, bails, then the auctioneer will re-attempt matchmaking. If trader A hasn't reconnected by and a successful batch is made without them, then the updates would need to be rolled back. Previously, since we would overwrite the main account and order state, we'd lack information in order to do so. By staging the update under a distinct bucket, we can now do so properly by simply removing the state within it.
139 lines
4 KiB
Go
139 lines
4 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
|
|
}
|
|
|
|
// getNestedBucket retrieves the nested bucket with the given key found within
|
|
// the given bucket. If the bucket does not exist and `create` is true, then the
|
|
// bucket is created.
|
|
func getNestedBucket(bucket *bbolt.Bucket, key []byte,
|
|
create bool) (*bbolt.Bucket, error) {
|
|
|
|
nestedBucket := bucket.Bucket(key)
|
|
if nestedBucket == nil && create {
|
|
return bucket.CreateBucketIfNotExists(key)
|
|
}
|
|
if nestedBucket == nil {
|
|
return nil, fmt.Errorf("nested bucket \"%v\" does not exist",
|
|
string(key))
|
|
}
|
|
return nestedBucket, 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)
|
|
})
|
|
}
|