fix(db): use the correct state version for databases without a state version file (#7385)
* If there's an existing database with no version file, give it version 25.0.0 * Creating the RocksDB database makes a temporary change to the default database version
This commit is contained in:
parent
798b271279
commit
c116cff5f0
|
@ -308,6 +308,12 @@ pub fn database_format_version_in_code() -> Version {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the full semantic version of the on-disk database.
|
/// Returns the full semantic version of the on-disk database.
|
||||||
|
///
|
||||||
|
/// Typically, the version is read from a version text file.
|
||||||
|
///
|
||||||
|
/// If there is an existing on-disk database, but no version file, returns `Ok(Some(major.0.0))`.
|
||||||
|
/// (This happens even if the database directory was just newly created.)
|
||||||
|
///
|
||||||
/// If there is no existing on-disk database, returns `Ok(None)`.
|
/// If there is no existing on-disk database, returns `Ok(None)`.
|
||||||
///
|
///
|
||||||
/// This is the format of the data on disk, the minor and patch versions
|
/// This is the format of the data on disk, the minor and patch versions
|
||||||
|
@ -318,25 +324,44 @@ pub fn database_format_version_on_disk(
|
||||||
) -> Result<Option<Version>, BoxError> {
|
) -> Result<Option<Version>, BoxError> {
|
||||||
let version_path = config.version_file_path(network);
|
let version_path = config.version_file_path(network);
|
||||||
|
|
||||||
let version = match fs::read_to_string(version_path) {
|
let disk_version_file = match fs::read_to_string(version_path) {
|
||||||
Ok(version) => version,
|
Ok(version) => Some(version),
|
||||||
Err(e) if e.kind() == ErrorKind::NotFound => {
|
Err(e) if e.kind() == ErrorKind::NotFound => {
|
||||||
// If the version file doesn't exist, don't guess the version.
|
// If the version file doesn't exist, don't guess the version yet.
|
||||||
// (It will end up being the version in code, once the database is created.)
|
None
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e)?,
|
Err(e) => Err(e)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (minor, patch) = version
|
// The database has a version file on disk
|
||||||
.split_once('.')
|
if let Some(version) = disk_version_file {
|
||||||
.ok_or("invalid database format version file")?;
|
let (minor, patch) = version
|
||||||
|
.split_once('.')
|
||||||
|
.ok_or("invalid database format version file")?;
|
||||||
|
|
||||||
Ok(Some(Version::new(
|
return Ok(Some(Version::new(
|
||||||
DATABASE_FORMAT_VERSION,
|
DATABASE_FORMAT_VERSION,
|
||||||
minor.parse()?,
|
minor.parse()?,
|
||||||
patch.parse()?,
|
patch.parse()?,
|
||||||
)))
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let db_path = config.db_path(network);
|
||||||
|
|
||||||
|
// There's no version file on disk, so we need to guess the version
|
||||||
|
// based on the database content
|
||||||
|
match fs::metadata(db_path) {
|
||||||
|
// But there is a database on disk, so it has the current major version with no upgrades.
|
||||||
|
// If the database directory was just newly created, we also return this version.
|
||||||
|
Ok(_metadata) => Ok(Some(Version::new(DATABASE_FORMAT_VERSION, 0, 0))),
|
||||||
|
|
||||||
|
// There's no version file and no database on disk, so it's a new database.
|
||||||
|
// It will be created with the current version,
|
||||||
|
// but temporarily return the default version above until the version file is written.
|
||||||
|
Err(e) if e.kind() == ErrorKind::NotFound => Ok(None),
|
||||||
|
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes `changed_version` to the on-disk database after the format is changed.
|
/// Writes `changed_version` to the on-disk database after the format is changed.
|
||||||
|
|
|
@ -22,6 +22,7 @@ use DbFormatChange::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::write_database_format_version_to_disk,
|
config::write_database_format_version_to_disk,
|
||||||
|
constants::DATABASE_FORMAT_VERSION,
|
||||||
database_format_version_in_code, database_format_version_on_disk,
|
database_format_version_in_code, database_format_version_on_disk,
|
||||||
service::finalized_state::{DiskWriteBatch, ZebraDb},
|
service::finalized_state::{DiskWriteBatch, ZebraDb},
|
||||||
Config,
|
Config,
|
||||||
|
@ -478,8 +479,12 @@ impl DbFormatChange {
|
||||||
.expect("unable to read database format version file path");
|
.expect("unable to read database format version file path");
|
||||||
let running_version = database_format_version_in_code();
|
let running_version = database_format_version_in_code();
|
||||||
|
|
||||||
|
let default_new_version = Some(Version::new(DATABASE_FORMAT_VERSION, 0, 0));
|
||||||
|
|
||||||
|
// The database version isn't empty any more, because we've created the RocksDB database
|
||||||
|
// and acquired its lock. (If it is empty, we have a database locking bug.)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
disk_version, None,
|
disk_version, default_new_version,
|
||||||
"can't overwrite the format version in an existing database:\n\
|
"can't overwrite the format version in an existing database:\n\
|
||||||
disk: {disk_version:?}\n\
|
disk: {disk_version:?}\n\
|
||||||
running: {running_version}"
|
running: {running_version}"
|
||||||
|
|
|
@ -71,6 +71,10 @@ impl ZebraDb {
|
||||||
// Open the database and do initial checks.
|
// Open the database and do initial checks.
|
||||||
let mut db = ZebraDb {
|
let mut db = ZebraDb {
|
||||||
format_change_handle: None,
|
format_change_handle: None,
|
||||||
|
// After the database directory is created, a newly created database temporarily
|
||||||
|
// changes to the default database version. Then we set the correct version in the
|
||||||
|
// upgrade thread. We need to do the version change in this order, because the version
|
||||||
|
// file can only be changed while we hold the RocksDB database lock.
|
||||||
db: DiskDb::new(config, network),
|
db: DiskDb::new(config, network),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue