Skip to content

Commit e3ac6bf

Browse files
authored
Merge pull request #6116 from hstove/fix/signerdb-migrations-refactor
feat: refactor / cleanup signerDB migrations code
2 parents 157cab5 + 9de4eaa commit e3ac6bf

File tree

1 file changed

+125
-224
lines changed

1 file changed

+125
-224
lines changed

stacks-signer/src/signerdb.rs

Lines changed: 125 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ use clarity::types::Address;
3232
use libsigner::v0::messages::{RejectReason, RejectReasonPrefix, StateMachineUpdate};
3333
use libsigner::BlockProposal;
3434
use rusqlite::functions::FunctionFlags;
35-
use rusqlite::{
36-
params, Connection, Error as SqliteError, OpenFlags, OptionalExtension, Transaction,
37-
};
35+
use rusqlite::{params, Connection, Error as SqliteError, OpenFlags, OptionalExtension};
3836
use serde::{Deserialize, Serialize};
3937
use stacks_common::codec::{read_next, write_next, Error as CodecError, StacksMessageCodec};
4038
use stacks_common::types::chainstate::ConsensusHash;
@@ -708,9 +706,73 @@ static SCHEMA_14: &[&str] = &[
708706
"INSERT INTO db_config (version) VALUES (14);",
709707
];
710708

709+
struct Migration {
710+
version: u32,
711+
statements: &'static [&'static str],
712+
}
713+
714+
static MIGRATIONS: &[Migration] = &[
715+
Migration {
716+
version: 1,
717+
statements: SCHEMA_1,
718+
},
719+
Migration {
720+
version: 2,
721+
statements: SCHEMA_2,
722+
},
723+
Migration {
724+
version: 3,
725+
statements: SCHEMA_3,
726+
},
727+
Migration {
728+
version: 4,
729+
statements: SCHEMA_4,
730+
},
731+
Migration {
732+
version: 5,
733+
statements: SCHEMA_5,
734+
},
735+
Migration {
736+
version: 6,
737+
statements: SCHEMA_6,
738+
},
739+
Migration {
740+
version: 7,
741+
statements: SCHEMA_7,
742+
},
743+
Migration {
744+
version: 8,
745+
statements: SCHEMA_8,
746+
},
747+
Migration {
748+
version: 9,
749+
statements: SCHEMA_9,
750+
},
751+
Migration {
752+
version: 10,
753+
statements: SCHEMA_10,
754+
},
755+
Migration {
756+
version: 11,
757+
statements: SCHEMA_11,
758+
},
759+
Migration {
760+
version: 12,
761+
statements: SCHEMA_12,
762+
},
763+
Migration {
764+
version: 13,
765+
statements: SCHEMA_13,
766+
},
767+
Migration {
768+
version: 14,
769+
statements: SCHEMA_14,
770+
},
771+
];
772+
711773
impl SignerDb {
712774
/// The current schema version used in this build of the signer binary.
713-
pub const SCHEMA_VERSION: u32 = 12;
775+
pub const SCHEMA_VERSION: u32 = 14;
714776

715777
/// Create a new `SignerState` instance.
716778
/// This will create a new SQLite database at the given path
@@ -740,202 +802,6 @@ impl SignerDb {
740802
}
741803
}
742804

743-
/// Migrate from schema 0 to schema 1
744-
fn schema_1_migration(tx: &Transaction) -> Result<(), DBError> {
745-
if Self::get_schema_version(tx)? >= 1 {
746-
// no migration necessary
747-
return Ok(());
748-
}
749-
750-
for statement in SCHEMA_1.iter() {
751-
tx.execute_batch(statement)?;
752-
}
753-
754-
Ok(())
755-
}
756-
757-
/// Migrate from schema 1 to schema 2
758-
fn schema_2_migration(tx: &Transaction) -> Result<(), DBError> {
759-
if Self::get_schema_version(tx)? >= 2 {
760-
// no migration necessary
761-
return Ok(());
762-
}
763-
764-
for statement in SCHEMA_2.iter() {
765-
tx.execute_batch(statement)?;
766-
}
767-
768-
Ok(())
769-
}
770-
771-
/// Migrate from schema 2 to schema 3
772-
fn schema_3_migration(tx: &Transaction) -> Result<(), DBError> {
773-
if Self::get_schema_version(tx)? >= 3 {
774-
// no migration necessary
775-
return Ok(());
776-
}
777-
778-
for statement in SCHEMA_3.iter() {
779-
tx.execute_batch(statement)?;
780-
}
781-
782-
Ok(())
783-
}
784-
785-
/// Migrate from schema 3 to schema 4
786-
fn schema_4_migration(tx: &Transaction) -> Result<(), DBError> {
787-
if Self::get_schema_version(tx)? >= 4 {
788-
// no migration necessary
789-
return Ok(());
790-
}
791-
792-
for statement in SCHEMA_4.iter() {
793-
tx.execute_batch(statement)?;
794-
}
795-
796-
Ok(())
797-
}
798-
799-
/// Migrate from schema 4 to schema 5
800-
fn schema_5_migration(tx: &Transaction) -> Result<(), DBError> {
801-
if Self::get_schema_version(tx)? >= 5 {
802-
// no migration necessary
803-
return Ok(());
804-
}
805-
806-
for statement in SCHEMA_5.iter() {
807-
tx.execute_batch(statement)?;
808-
}
809-
810-
Ok(())
811-
}
812-
813-
/// Migrate from schema 5 to schema 6
814-
fn schema_6_migration(tx: &Transaction) -> Result<(), DBError> {
815-
if Self::get_schema_version(tx)? >= 6 {
816-
// no migration necessary
817-
return Ok(());
818-
}
819-
820-
for statement in SCHEMA_6.iter() {
821-
tx.execute_batch(statement)?;
822-
}
823-
824-
Ok(())
825-
}
826-
827-
/// Migrate from schema 6 to schema 7
828-
fn schema_7_migration(tx: &Transaction) -> Result<(), DBError> {
829-
if Self::get_schema_version(tx)? >= 7 {
830-
// no migration necessary
831-
return Ok(());
832-
}
833-
834-
for statement in SCHEMA_7.iter() {
835-
tx.execute_batch(statement)?;
836-
}
837-
838-
Ok(())
839-
}
840-
841-
/// Migrate from schema 7 to schema 8
842-
fn schema_8_migration(tx: &Transaction) -> Result<(), DBError> {
843-
if Self::get_schema_version(tx)? >= 8 {
844-
// no migration necessary
845-
return Ok(());
846-
}
847-
848-
for statement in SCHEMA_8.iter() {
849-
tx.execute_batch(statement)?;
850-
}
851-
852-
Ok(())
853-
}
854-
855-
/// Migrate from schema 8 to schema 9
856-
fn schema_9_migration(tx: &Transaction) -> Result<(), DBError> {
857-
if Self::get_schema_version(tx)? >= 9 {
858-
// no migration necessary
859-
return Ok(());
860-
}
861-
862-
for statement in SCHEMA_9.iter() {
863-
tx.execute_batch(statement)?;
864-
}
865-
866-
Ok(())
867-
}
868-
869-
/// Migrate from schema 9 to schema 10
870-
fn schema_10_migration(tx: &Transaction) -> Result<(), DBError> {
871-
if Self::get_schema_version(tx)? >= 10 {
872-
// no migration necessary
873-
return Ok(());
874-
}
875-
876-
for statement in SCHEMA_10.iter() {
877-
tx.execute_batch(statement)?;
878-
}
879-
880-
Ok(())
881-
}
882-
883-
/// Migrate from schema 10 to schema 11
884-
fn schema_11_migration(tx: &Transaction) -> Result<(), DBError> {
885-
if Self::get_schema_version(tx)? >= 11 {
886-
// no migration necessary
887-
return Ok(());
888-
}
889-
890-
for statement in SCHEMA_11.iter() {
891-
tx.execute_batch(statement)?;
892-
}
893-
894-
Ok(())
895-
}
896-
897-
/// Migrate from schema 11 to schema 12
898-
fn schema_12_migration(tx: &Transaction) -> Result<(), DBError> {
899-
if Self::get_schema_version(tx)? >= 12 {
900-
// no migration necessary
901-
return Ok(());
902-
}
903-
904-
for statement in SCHEMA_12.iter() {
905-
tx.execute_batch(statement)?;
906-
}
907-
908-
Ok(())
909-
}
910-
911-
/// Migrate from schema 12 to schema 13
912-
fn schema_13_migration(tx: &Transaction) -> Result<(), DBError> {
913-
if Self::get_schema_version(tx)? >= 13 {
914-
// no migration necessary
915-
return Ok(());
916-
}
917-
918-
for statement in SCHEMA_13.iter() {
919-
tx.execute_batch(statement)?;
920-
}
921-
922-
Ok(())
923-
}
924-
925-
/// Migrate from schema 13 to schema 14
926-
fn schema_14_migration(tx: &Transaction) -> Result<(), DBError> {
927-
if Self::get_schema_version(tx)? >= 14 {
928-
// no migration necessary
929-
return Ok(());
930-
}
931-
932-
for statement in SCHEMA_14.iter() {
933-
tx.execute_batch(statement)?;
934-
}
935-
936-
Ok(())
937-
}
938-
939805
/// Register custom scalar functions used by the database
940806
fn register_scalar_functions(&self) -> Result<(), DBError> {
941807
// Register helper function for determining if a block is a tenure change transaction
@@ -961,35 +827,70 @@ impl SignerDb {
961827
}
962828

963829
/// Either instantiate a new database, or migrate an existing one
964-
/// If the detected version of the existing database is 0 (i.e., a pre-migration
965-
/// logic DB, the DB will be dropped).
966830
fn create_or_migrate(&mut self) -> Result<(), DBError> {
967831
self.register_scalar_functions()?;
968832
let sql_tx = tx_begin_immediate(&mut self.db)?;
969-
loop {
970-
let version = Self::get_schema_version(&sql_tx)?;
971-
match version {
972-
0 => Self::schema_1_migration(&sql_tx)?,
973-
1 => Self::schema_2_migration(&sql_tx)?,
974-
2 => Self::schema_3_migration(&sql_tx)?,
975-
3 => Self::schema_4_migration(&sql_tx)?,
976-
4 => Self::schema_5_migration(&sql_tx)?,
977-
5 => Self::schema_6_migration(&sql_tx)?,
978-
6 => Self::schema_7_migration(&sql_tx)?,
979-
7 => Self::schema_8_migration(&sql_tx)?,
980-
8 => Self::schema_9_migration(&sql_tx)?,
981-
9 => Self::schema_10_migration(&sql_tx)?,
982-
10 => Self::schema_11_migration(&sql_tx)?,
983-
11 => Self::schema_12_migration(&sql_tx)?,
984-
12 => Self::schema_13_migration(&sql_tx)?,
985-
13 => Self::schema_14_migration(&sql_tx)?,
986-
14 => break,
987-
x => return Err(DBError::Other(format!(
988-
"Database schema is newer than supported by this binary. Expected version = {}, Database version = {x}",
989-
Self::SCHEMA_VERSION,
990-
))),
833+
834+
let mut current_db_version = Self::get_schema_version(&sql_tx)?;
835+
debug!("Current SignerDB schema version: {}", current_db_version);
836+
837+
for migration in MIGRATIONS.iter() {
838+
if current_db_version >= migration.version {
839+
// don't need this migration, continue to see if we need later migrations
840+
continue;
841+
}
842+
if current_db_version != migration.version - 1 {
843+
// This implies a gap or out-of-order migration definition,
844+
// or the database is at a version X, and the next migration is X+2 instead of X+1.
845+
sql_tx.rollback()?;
846+
return Err(DBError::Other(format!(
847+
"Migration step missing or out of order. Current DB version: {}, trying to apply migration for version: {}",
848+
current_db_version, migration.version
849+
)));
850+
}
851+
debug!(
852+
"Applying SignerDB migration for schema version {}",
853+
migration.version
854+
);
855+
for statement in migration.statements.iter() {
856+
sql_tx.execute_batch(statement)?;
991857
}
858+
859+
// Verify that the migration script updated the version correctly
860+
let new_version_check = Self::get_schema_version(&sql_tx)?;
861+
if new_version_check != migration.version {
862+
sql_tx.rollback()?;
863+
return Err(DBError::Other(format!(
864+
"Migration to version {} failed to update DB version. Expected {}, got {}.",
865+
migration.version, migration.version, new_version_check
866+
)));
867+
}
868+
current_db_version = new_version_check;
869+
debug!(
870+
"Successfully migrated to schema version {}",
871+
current_db_version
872+
);
992873
}
874+
875+
match current_db_version.cmp(&Self::SCHEMA_VERSION) {
876+
std::cmp::Ordering::Less => {
877+
sql_tx.rollback()?;
878+
return Err(DBError::Other(format!(
879+
"Database migration incomplete. Current version: {}, SCHEMA_VERSION: {}",
880+
current_db_version,
881+
Self::SCHEMA_VERSION
882+
)));
883+
}
884+
std::cmp::Ordering::Greater => {
885+
sql_tx.rollback()?;
886+
return Err(DBError::Other(format!(
887+
"Database schema is newer than SCHEMA_VERSION. SCHEMA_VERSION = {}, Current version = {}. Did you forget to update SCHEMA_VERSION?",
888+
Self::SCHEMA_VERSION, current_db_version
889+
)));
890+
}
891+
std::cmp::Ordering::Equal => {}
892+
}
893+
993894
sql_tx.commit()?;
994895
self.remove_scalar_functions()?;
995896
Ok(())

0 commit comments

Comments
 (0)