Fix slasher database schema migration to v2 (#2125)
## Issue Addressed Closes #2119 ## Proposed Changes Update the slasher schema version to v2 for the breaking changes to the config introduced in #2079. Implement a migration from v1 to v2 so that users can seamlessly upgrade from any version of Lighthouse <=1.0.5. Users who deleted their database for v1.0.5 can upgrade to a release including this patch without any manual intervention. Similarly, any users still on v1.0.4 or earlier can now upgrade without having to drop their database.
This commit is contained in:
parent
78d17c3255
commit
43ac3f7209
@ -4,6 +4,7 @@ use crate::{
|
||||
};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use lmdb::{Cursor, Database, DatabaseFlags, Environment, RwTransaction, Transaction, WriteFlags};
|
||||
use serde::Deserialize;
|
||||
use ssz::{Decode, Encode};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
@ -12,7 +13,7 @@ use types::{
|
||||
};
|
||||
|
||||
/// Current database schema version, to check compatibility of on-disk DB with software.
|
||||
const CURRENT_SCHEMA_VERSION: u64 = 1;
|
||||
pub const CURRENT_SCHEMA_VERSION: u64 = 2;
|
||||
|
||||
/// Metadata about the slashing database itself.
|
||||
const METADATA_DB: &str = "metadata";
|
||||
@ -209,15 +210,7 @@ impl<E: EthSpec> SlasherDB<E> {
|
||||
|
||||
let mut txn = db.begin_rw_txn()?;
|
||||
|
||||
if let Some(schema_version) = db.load_schema_version(&mut txn)? {
|
||||
if schema_version != CURRENT_SCHEMA_VERSION {
|
||||
return Err(Error::IncompatibleSchemaVersion {
|
||||
database_schema_version: schema_version,
|
||||
software_schema_version: CURRENT_SCHEMA_VERSION,
|
||||
});
|
||||
}
|
||||
}
|
||||
db.store_schema_version(&mut txn)?;
|
||||
db.migrate(&mut txn)?;
|
||||
|
||||
if let Some(on_disk_config) = db.load_config(&mut txn)? {
|
||||
if !db.config.is_compatible(&on_disk_config) {
|
||||
@ -227,7 +220,7 @@ impl<E: EthSpec> SlasherDB<E> {
|
||||
});
|
||||
}
|
||||
}
|
||||
db.store_config(&mut txn)?;
|
||||
db.store_config(&db.config, &mut txn)?;
|
||||
txn.commit()?;
|
||||
|
||||
Ok(db)
|
||||
@ -263,7 +256,14 @@ impl<E: EthSpec> SlasherDB<E> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_config(&self, txn: &mut RwTransaction<'_>) -> Result<Option<Config>, Error> {
|
||||
/// Load a config from disk.
|
||||
///
|
||||
/// This is generic in order to allow loading of configs for different schema versions.
|
||||
/// Care should be taken to ensure it is only called for `Config`-like `T`.
|
||||
pub fn load_config<'a, T: Deserialize<'a>>(
|
||||
&self,
|
||||
txn: &'a mut RwTransaction<'_>,
|
||||
) -> Result<Option<T>, Error> {
|
||||
Ok(txn
|
||||
.get(self.metadata_db, &METADATA_CONFIG_KEY)
|
||||
.optional()?
|
||||
@ -271,11 +271,11 @@ impl<E: EthSpec> SlasherDB<E> {
|
||||
.transpose()?)
|
||||
}
|
||||
|
||||
pub fn store_config(&self, txn: &mut RwTransaction<'_>) -> Result<(), Error> {
|
||||
pub fn store_config(&self, config: &Config, txn: &mut RwTransaction<'_>) -> Result<(), Error> {
|
||||
txn.put(
|
||||
self.metadata_db,
|
||||
&METADATA_CONFIG_KEY,
|
||||
&bincode::serialize(self.config.as_ref())?,
|
||||
&bincode::serialize(config)?,
|
||||
Self::write_flags(),
|
||||
)?;
|
||||
Ok(())
|
||||
|
@ -25,6 +25,7 @@ pub enum Error {
|
||||
on_disk_config: Config,
|
||||
config: Config,
|
||||
},
|
||||
ConfigMissing,
|
||||
DistanceTooLarge,
|
||||
DistanceCalculationOverflow,
|
||||
/// Missing an attester record that we expected to exist.
|
||||
|
@ -9,6 +9,7 @@ pub mod config;
|
||||
mod database;
|
||||
mod error;
|
||||
pub mod metrics;
|
||||
mod migrate;
|
||||
mod slasher;
|
||||
pub mod test_utils;
|
||||
mod utils;
|
||||
|
74
slasher/src/migrate.rs
Normal file
74
slasher/src/migrate.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use crate::{database::CURRENT_SCHEMA_VERSION, Config, Error, SlasherDB};
|
||||
use lmdb::RwTransaction;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use types::EthSpec;
|
||||
|
||||
/// Config from schema version 1, for migration to version 2+.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigV1 {
|
||||
database_path: PathBuf,
|
||||
chunk_size: usize,
|
||||
validator_chunk_size: usize,
|
||||
history_length: usize,
|
||||
update_period: u64,
|
||||
max_db_size_mbs: usize,
|
||||
}
|
||||
|
||||
type ConfigV2 = Config;
|
||||
|
||||
impl Into<ConfigV2> for ConfigV1 {
|
||||
fn into(self) -> ConfigV2 {
|
||||
Config {
|
||||
database_path: self.database_path,
|
||||
chunk_size: self.chunk_size,
|
||||
validator_chunk_size: self.validator_chunk_size,
|
||||
history_length: self.history_length,
|
||||
update_period: self.update_period,
|
||||
max_db_size_mbs: self.max_db_size_mbs,
|
||||
broadcast: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SlasherDB<E> {
|
||||
/// If the database exists, and has a schema, attempt to migrate it to the current version.
|
||||
pub fn migrate(&self, txn: &mut RwTransaction<'_>) -> Result<(), Error> {
|
||||
if let Some(schema_version) = self.load_schema_version(txn)? {
|
||||
match (schema_version, CURRENT_SCHEMA_VERSION) {
|
||||
// The migration from v1 to v2 is a bit messy because v1.0.5 silently
|
||||
// changed the schema to v2, so a v1 schema could have either a v1 or v2
|
||||
// config.
|
||||
(1, 2) => {
|
||||
match self.load_config::<ConfigV1>(txn) {
|
||||
Ok(Some(config_v1)) => {
|
||||
// Upgrade to v2 config and store on disk.
|
||||
let config_v2 = config_v1.into();
|
||||
self.store_config(&config_v2, txn)?;
|
||||
}
|
||||
Ok(None) => {
|
||||
// Impossible to have schema version and no config.
|
||||
return Err(Error::ConfigMissing);
|
||||
}
|
||||
Err(_) => {
|
||||
// If loading v1 config failed, ensure loading v2 config succeeds.
|
||||
// No further action is needed.
|
||||
let _config_v2 = self.load_config::<ConfigV2>(txn)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
(x, y) if x == y => {}
|
||||
(_, _) => {
|
||||
return Err(Error::IncompatibleSchemaVersion {
|
||||
database_schema_version: schema_version,
|
||||
software_schema_version: CURRENT_SCHEMA_VERSION,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the migration succeeded, update the schema version on-disk.
|
||||
self.store_schema_version(txn)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user