Skip to content

Commit 0388418

Browse files
committed
add updated_at for application version
1 parent dac12cd commit 0388418

File tree

2 files changed

+63
-56
lines changed

2 files changed

+63
-56
lines changed

mithril-aggregator/src/command_args.rs

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use chrono::Local;
12
use clap::{Parser, Subcommand};
23
use config::{builder::DefaultState, ConfigBuilder, Map, Source, Value, ValueKind};
34
use semver::{Version, VersionReq};
@@ -37,45 +38,52 @@ use crate::{
3738
ProtocolParametersStorer, SingleSignatureStore, VerificationKeyStore,
3839
};
3940

40-
fn check_database_version(connection: &Connection) -> Result<bool, Box<dyn Error>> {
41-
let provider = VersionProvider::new(connection);
41+
fn check_database_version(filepath: &Option<PathBuf>) -> Result<(), Box<dyn Error>> {
42+
let connection = match filepath {
43+
Some(file) => Connection::open(file)?,
44+
None => Connection::open(":memory:")?,
45+
};
46+
let provider = VersionProvider::new(&connection);
4247
provider.create_table_if_not_exists()?;
4348
let application_type = ApplicationNodeType::new("aggregator")?;
44-
let maybe_option = provider.get_database_version(&application_type)?;
49+
let maybe_option = provider.get_application_version(&application_type)?;
50+
let current_version = ApplicationVersion {
51+
semver: Version::parse(env!("CARGO_PKG_VERSION"))?,
52+
application_type,
53+
updated_at: Local::now().naive_local(),
54+
};
4555

46-
let version = match maybe_option {
56+
match maybe_option {
4757
None => {
48-
let provider = VersionUpdaterProvider::new(connection);
49-
let version = ApplicationVersion {
50-
database_version: Version::parse(env!("CARGO_PKG_VERSION"))?,
51-
application_type,
52-
};
53-
provider.save(version)?
58+
let provider = VersionUpdaterProvider::new(&connection);
59+
let _ = provider.save(current_version)?;
60+
debug!("application version saved in database");
61+
}
62+
Some(version) => {
63+
let req = VersionReq::parse(&current_version.semver.to_string()).unwrap();
64+
65+
if !req.matches(&version.semver) {
66+
warn!(
67+
"application version '{}' is out of date, new version is '{}'. Upgrading database…",
68+
version.semver, current_version.semver
69+
);
70+
let upgrader_provider = VersionUpdaterProvider::new(&connection);
71+
upgrader_provider.save(current_version)?;
72+
debug!("database updated");
73+
} else {
74+
debug!("database up to date");
75+
}
5476
}
55-
Some(version) => version,
5677
};
57-
let req = VersionReq::parse(env!("CARGO_PKG_VERSION")).unwrap();
5878

59-
Ok(req.matches(&version.database_version))
79+
Ok(())
6080
}
6181

6282
fn setup_genesis_dependencies(
6383
config: &GenesisConfiguration,
6484
) -> Result<GenesisToolsDependency, Box<dyn std::error::Error>> {
6585
let sqlite_db_path = Some(config.get_sqlite_file());
66-
67-
let connection = match sqlite_db_path.clone() {
68-
Some(filepath) => Connection::open(filepath)?,
69-
None => Connection::open(":memory:")?,
70-
};
71-
if !check_database_version(&connection)? {
72-
warn!("❌ The database is out of date, application may fail.");
73-
} else {
74-
debug!(
75-
"Database schematic for application version {} has been checked OK!",
76-
env!("CARGO_PKG_VERSION")
77-
);
78-
}
86+
check_database_version(&sqlite_db_path)?;
7987

8088
let chain_observer = Arc::new(
8189
mithril_common::chain_observer::CardanoCliChainObserver::new(Box::new(
@@ -336,25 +344,13 @@ impl ServeCommand {
336344
.try_deserialize()
337345
.map_err(|e| format!("configuration deserialize error: {}", e))?;
338346
debug!("SERVE command"; "config" => format!("{:?}", config));
347+
let sqlite_db_path = Some(config.get_sqlite_file());
348+
check_database_version(&sqlite_db_path)?;
349+
339350
// Init dependencies
340351
let snapshot_store = config.build_snapshot_store()?;
341352
let snapshot_uploader = config.build_snapshot_uploader()?;
342353

343-
let sqlite_db_path = Some(config.get_sqlite_file());
344-
let connection = match sqlite_db_path.clone() {
345-
Some(filepath) => Connection::open(filepath)?,
346-
None => Connection::open(":memory:")?,
347-
};
348-
349-
if !check_database_version(&connection)? {
350-
warn!("❌ The database is out of date, application may fail.");
351-
} else {
352-
debug!(
353-
"Database schematic for application version {} has been checked OK!",
354-
env!("CARGO_PKG_VERSION")
355-
);
356-
}
357-
358354
let certificate_pending_store = Arc::new(CertificatePendingStore::new(Box::new(
359355
SQLiteAdapter::new("pending_certificate", sqlite_db_path.clone())?,
360356
)));

mithril-common/src/database/db_version.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{collections::HashMap, error::Error, fmt::Display};
22

3+
use chrono::NaiveDateTime;
34
use semver::Version;
45
use sqlite::{Connection, Row, Value};
56

@@ -36,22 +37,31 @@ impl Display for ApplicationNodeType {
3637
}
3738

3839
/// Entity related to the `app_version` database table.
39-
#[derive(Debug, PartialEq, Eq)]
40+
#[derive(Debug, PartialEq, Eq, Clone)]
4041
pub struct ApplicationVersion {
4142
/// Semver of the database structure.
42-
pub database_version: Version,
43+
pub semver: Version,
4344

4445
/// Name of the application.
4546
pub application_type: ApplicationNodeType,
47+
48+
/// Date of the last version upgrade, Sqlite does not store timezone
49+
/// information hence we have to use a `Chrono::NaiveDateTime` here.
50+
pub updated_at: NaiveDateTime,
4651
}
4752

4853
impl SqLiteEntity for ApplicationVersion {
4954
fn hydrate(row: Row) -> Result<Self, HydrationError> {
5055
Ok(Self {
51-
database_version: Version::parse(&row.get::<String, _>(0))
56+
semver: Version::parse(&row.get::<String, _>(0))
5257
.map_err(|e| HydrationError::InvalidData(format!("{}", e)))?,
5358
application_type: ApplicationNodeType::new(&row.get::<String, _>(1))
5459
.map_err(|e| HydrationError::InvalidData(format!("{}", e)))?,
60+
updated_at: NaiveDateTime::parse_from_str(
61+
&row.get::<String, _>(2),
62+
"%Y-%m-%d %H:%M:%S",
63+
)
64+
.map_err(|e| HydrationError::InvalidData(format!("{}", e)))?,
5565
})
5666
}
5767
}
@@ -73,12 +83,13 @@ impl Projection for ApplicationVersionProjection {
7383
impl ApplicationVersionProjection {
7484
pub fn new() -> Self {
7585
let mut projection = Self { fields: Vec::new() };
76-
projection.add_field("version", "{:app_version:}.version", "text");
86+
projection.add_field("semver", "{:app_version:}.semver", "text");
7787
projection.add_field(
7888
"application_type",
7989
"{:app_version:}.application_type",
8090
"text",
8191
);
92+
projection.add_field("updated_at", "{:app_version:}.updated_at", "timestamp");
8293

8394
projection
8495
}
@@ -115,16 +126,16 @@ impl<'conn> VersionProvider<'conn> {
115126

116127
if !table_exists {
117128
let sql = r#"
118-
create table app_version (application_type text not null primary key, version text not null)
129+
create table app_version (application_type text not null primary key, semver text not null, updated_at timestamp not null default CURRENT_TIMESTAMP)
119130
"#;
120131
connection.execute(sql)?;
121132
}
122133

123134
Ok(())
124135
}
125136

126-
/// Read the database version from the database.
127-
pub fn get_database_version(
137+
/// Read the application version from the database.
138+
pub fn get_application_version(
128139
&self,
129140
application_type: &ApplicationNodeType,
130141
) -> Result<Option<ApplicationVersion>, Box<dyn Error>> {
@@ -183,7 +194,7 @@ impl<'conn> VersionUpdaterProvider<'conn> {
183194
pub fn save(&self, version: ApplicationVersion) -> Result<ApplicationVersion, Box<dyn Error>> {
184195
let params = [
185196
Value::String(format!("{}", version.application_type)),
186-
Value::String(version.database_version.to_string()),
197+
Value::String(version.semver.to_string()),
187198
];
188199
let entity = self
189200
.find(None, &params)?
@@ -213,8 +224,8 @@ impl<'conn> Provider<'conn> for VersionUpdaterProvider<'conn> {
213224

214225
format!(
215226
r#"
216-
insert into app_version (application_type, version) values (?, ?)
217-
on conflict (application_type) do update set version = excluded.version
227+
insert into app_version (application_type, semver) values (?, ?)
228+
on conflict (application_type) do update set semver = excluded.semver, updated_at = CURRENT_TIMESTAMP
218229
returning {projection}
219230
"#
220231
)
@@ -232,7 +243,7 @@ mod tests {
232243
let _ = aliases.insert("{:app_version:}".to_string(), "whatever".to_string());
233244

234245
assert_eq!(
235-
"whatever.version as version, whatever.application_type as application_type"
246+
"whatever.semver as semver, whatever.application_type as application_type, whatever.updated_at as updated_at"
236247
.to_string(),
237248
projection.expand(aliases)
238249
);
@@ -245,7 +256,7 @@ mod tests {
245256

246257
assert_eq!(
247258
r#"
248-
select app_version.version as version, app_version.application_type as application_type
259+
select app_version.semver as semver, app_version.application_type as application_type, app_version.updated_at as updated_at
249260
from app_version
250261
where true
251262
"#,
@@ -260,9 +271,9 @@ where true
260271

261272
assert_eq!(
262273
r#"
263-
insert into app_version (application_type, version) values (?, ?)
264-
on conflict (application_type) do update set version = excluded.version
265-
returning app_version.version as version, app_version.application_type as application_type
274+
insert into app_version (application_type, semver) values (?, ?)
275+
on conflict (application_type) do update set semver = excluded.semver, updated_at = CURRENT_TIMESTAMP
276+
returning app_version.semver as semver, app_version.application_type as application_type, app_version.updated_at as updated_at
266277
"#,
267278
provider.get_definition(None)
268279
)

0 commit comments

Comments
 (0)