Skip to content

Commit

Permalink
Merge pull request #2126 from input-output-hk/sfa/2118/get_rid_of_sto…
Browse files Browse the repository at this point in the history
…re_adpater_in_signer

Get rid of store adapter in signer
  • Loading branch information
sfauvel authored Nov 21, 2024
2 parents 0b8564c + a14746c commit 3b7b365
Show file tree
Hide file tree
Showing 35 changed files with 1,590 additions and 546 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/mithril-persistence/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-persistence"
version = "0.2.34"
version = "0.2.35"
description = "Common types, interfaces, and utilities to persist data for Mithril nodes."
authors = { workspace = true }
edition = { workspace = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ insert into db_version (application_type, version, updated_at) values ('{applica
}

/// Represent a file containing SQL structure or data alterations.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct SqlMigration {
/// The semver version this migration targets.
pub version: DbVersion,
Expand Down
74 changes: 58 additions & 16 deletions internal/mithril-persistence/src/sqlite/connection_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,8 @@ impl ConnectionBuilder {
.with_context(|| "SQLite initialization: could not enable FOREIGN KEY support.")?;
}

if self.sql_migrations.is_empty().not() {
// Check database migrations
debug!(logger, "Applying database migrations");
let mut db_checker =
DatabaseVersionChecker::new(self.base_logger, self.node_type, &connection);

for migration in self.sql_migrations {
db_checker.add_migration(migration);
}

db_checker
.apply()
.with_context(|| "Database migration error")?;
}

let migrations = self.sql_migrations.clone();
self.apply_migrations(&connection, migrations)?;
if self
.options
.contains(&ConnectionOptions::ForceDisableForeignKeys)
Expand All @@ -131,9 +118,37 @@ impl ConnectionBuilder {
.execute("pragma foreign_keys=false")
.with_context(|| "SQLite initialization: could not disable FOREIGN KEY support.")?;
}

Ok(connection)
}

/// Apply a list of migration to the connection.
pub fn apply_migrations(
&self,
connection: &ConnectionThreadSafe,
sql_migrations: Vec<SqlMigration>,
) -> StdResult<()> {
let logger = self.base_logger.new_with_component_name::<Self>();

if sql_migrations.is_empty().not() {
// Check database migrations
debug!(logger, "Applying database migrations");
let mut db_checker = DatabaseVersionChecker::new(
self.base_logger.clone(),
self.node_type.clone(),
connection,
);

for migration in sql_migrations {
db_checker.add_migration(migration.clone());
}

db_checker
.apply()
.with_context(|| "Database migration error")?;
}

Ok(())
}
}

#[cfg(test)]
Expand Down Expand Up @@ -278,4 +293,31 @@ mod tests {
let foreign_keys = execute_single_cell_query(&connection, "pragma foreign_keys;");
assert_eq!(Value::Integer(false.into()), foreign_keys);
}

#[test]
fn test_apply_a_partial_migrations() {
let migrations = vec![
SqlMigration::new(1, "create table first(id integer);"),
SqlMigration::new(2, "create table second(id integer);"),
];

let connection = ConnectionBuilder::open_memory().build().unwrap();

assert!(connection.prepare("select * from first;").is_err());
assert!(connection.prepare("select * from second;").is_err());

ConnectionBuilder::open_memory()
.apply_migrations(&connection, migrations[0..1].to_vec())
.unwrap();

assert!(connection.prepare("select * from first;").is_ok());
assert!(connection.prepare("select * from second;").is_err());

ConnectionBuilder::open_memory()
.apply_migrations(&connection, migrations)
.unwrap();

assert!(connection.prepare("select * from first;").is_ok());
assert!(connection.prepare("select * from second;").is_ok());
}
}
2 changes: 1 addition & 1 deletion mithril-signer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-signer"
version = "0.2.214"
version = "0.2.215"
description = "A Mithril Signer"
authors = { workspace = true }
edition = { workspace = true }
Expand Down
51 changes: 50 additions & 1 deletion mithril-signer/src/database/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,61 @@ create index signed_beacon_signed_entity_type_id on signed_beacon(signed_entity_
// Migration 4
// Remove `network` from cardano immutable files full beacons in `signed_beacon` table
SqlMigration::new(
31,
4,
r#"
update signed_beacon
set beacon = json_remove(beacon, '$.network')
where signed_beacon.signed_entity_type_id = 2;
"#,
),
// Migration 5
// Add the `stake_pool` table and migration data from the previous
// `stake_store` JSON format.
SqlMigration::new(
5,
r#"
create table stake_pool (
stake_pool_id text not null,
epoch integer not null,
stake integer not null,
created_at text not null,
primary key (epoch, stake_pool_id)
);
create table if not exists stake (key_hash text primary key, key json not null, value json not null);
insert into stake_pool (epoch, stake_pool_id, stake, created_at)
select
stake.key as epoch,
stake_dis.key as stake_pool_id,
stake_dis.value as stake,
strftime('%Y-%m-%dT%H:%M:%fZ', current_timestamp)
from stake, json_each(stake.value) as stake_dis
order by epoch asc;
drop table stake;
"#,
),
// Migration 6
// Add the `protocol_initializer` table and migration data from the previous
// `protocol_initializer` JSON format.
SqlMigration::new(
6,
r#"
create table new_protocol_initializer (
epoch integer not null,
protocol json not null,
created_at text not null,
primary key (epoch)
);
create table if not exists protocol_initializer (key_hash text primary key, key json not null, value json not null);
insert into new_protocol_initializer (epoch, protocol, created_at)
select
protocol_initializer.key as epoch,
protocol_initializer.value,
strftime('%Y-%m-%dT%H:%M:%fZ', current_timestamp)
from protocol_initializer
order by epoch asc;
drop table protocol_initializer;
alter table new_protocol_initializer rename to protocol_initializer;
"#,
),
]
}
2 changes: 2 additions & 0 deletions mithril-signer/src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ pub mod record;
pub mod repository;
#[cfg(test)]
pub(crate) mod test_helper;
#[cfg(test)]
pub(crate) mod tests;
4 changes: 4 additions & 0 deletions mithril-signer/src/database/query/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Signer related database queries

mod protocol_initializer;
mod signed_beacon;
mod stake_pool;

pub use protocol_initializer::*;
pub use signed_beacon::*;
pub use stake_pool::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use sqlite::Value;

use mithril_common::entities::Epoch;
use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};

use crate::database::record::ProtocolInitializerRecord;

/// Query to delete old [ProtocolInitializer] from the sqlite database
pub struct DeleteProtocolInitializerQuery {
condition: WhereCondition,
}

impl Query for DeleteProtocolInitializerQuery {
type Entity = ProtocolInitializerRecord;

fn filters(&self) -> WhereCondition {
self.condition.clone()
}

fn get_definition(&self, condition: &str) -> String {
// it is important to alias the fields with the same name as the table
// since the table cannot be aliased in a RETURNING statement in SQLite.
let projection = Self::Entity::get_projection().expand(SourceAlias::new(&[(
"{:protocol_initializer:}",
"protocol_initializer",
)]));

format!("delete from protocol_initializer where {condition} returning {projection}")
}
}

impl DeleteProtocolInitializerQuery {
/// Create the SQL query to prune data older than the given Epoch.
pub fn below_epoch_threshold(epoch_threshold: Epoch) -> Self {
let condition = WhereCondition::new(
"epoch < ?*",
vec![Value::Integer(epoch_threshold.try_into().unwrap())],
);

Self { condition }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use sqlite::Value;

use mithril_common::entities::Epoch;
use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};

use crate::database::record::ProtocolInitializerRecord;

/// Simple queries to retrieve [ProtocolInitializer] from the sqlite database.
pub struct GetProtocolInitializerQuery {
condition: WhereCondition,
limit: Option<usize>,
}

impl GetProtocolInitializerQuery {
/// Get protocol initializer that match the epoch.
pub fn for_epoch(epoch: Epoch) -> Self {
let epoch_i64: i64 = epoch.try_into().unwrap();
let condition = WhereCondition::new(
"protocol_initializer.epoch = ?",
vec![Value::Integer(epoch_i64)],
);

Self {
condition,
limit: None,
}
}

pub fn last_n(limit: usize) -> Self {
let condition = WhereCondition::default();
Self {
condition,
limit: Some(limit),
}
}
}

impl Query for GetProtocolInitializerQuery {
type Entity = ProtocolInitializerRecord;

fn filters(&self) -> WhereCondition {
self.condition.clone()
}

fn get_definition(&self, condition: &str) -> String {
let aliases = SourceAlias::new(&[("{:protocol_initializer:}", "protocol_initializer")]);
let projection = Self::Entity::get_projection().expand(aliases);
let limit = self
.limit
.map_or("".to_string(), |limit| format!(" limit {}", limit));
format!("select {projection} from protocol_initializer where {condition} order by rowid desc{limit}")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use sqlite::Value;

use mithril_common::StdResult;
use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};

use crate::database::record::ProtocolInitializerRecord;

/// Query to insert or replace [ProtocolInitializerRecord] in the sqlite database
pub struct InsertOrReplaceProtocolInitializerQuery {
condition: WhereCondition,
}

impl InsertOrReplaceProtocolInitializerQuery {
pub fn one(record: ProtocolInitializerRecord) -> StdResult<Self> {
let value = serde_json::to_string(&record.protocol_initializer).unwrap();
let condition = WhereCondition::new(
"(epoch, protocol, created_at) values (?*, ?*, ?*)",
vec![
Value::Integer(record.epoch.try_into()?),
Value::String(value),
Value::String(record.created_at.to_rfc3339()),
],
);

Ok(Self { condition })
}
}

impl Query for InsertOrReplaceProtocolInitializerQuery {
type Entity = ProtocolInitializerRecord;

fn filters(&self) -> WhereCondition {
self.condition.clone()
}

fn get_definition(&self, condition: &str) -> String {
// it is important to alias the fields with the same name as the table
// since the table cannot be aliased in a RETURNING statement in SQLite.
let projection = Self::Entity::get_projection().expand(SourceAlias::new(&[(
"{:protocol_initializer:}",
"protocol_initializer",
)]));

format!("insert or replace into protocol_initializer {condition} returning {projection}")
}
}
7 changes: 7 additions & 0 deletions mithril-signer/src/database/query/protocol_initializer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod delete_protocol_initializer;
mod get_protocol_initializer;
mod insert_protocol_initializer;

pub use delete_protocol_initializer::*;
pub use get_protocol_initializer::*;
pub use insert_protocol_initializer::*;
Loading

0 comments on commit 3b7b365

Please sign in to comment.