aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-22 11:37:34 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-22 11:37:34 +0100
commite21c289f8d21b802d2dc609233bc28cde65da224 (patch)
tree9baf5d3576737019f7037665e2fe0a1919bde26e
parentfix(yt/status): Don't show the database version in `yt status` (diff)
downloadyt-e21c289f8d21b802d2dc609233bc28cde65da224.zip
fix(yt/storage/migrate): Improve error reporting
Diffstat (limited to '')
-rw-r--r--yt/src/storage/migrate/mod.rs113
1 files changed, 87 insertions, 26 deletions
diff --git a/yt/src/storage/migrate/mod.rs b/yt/src/storage/migrate/mod.rs
index ee43008..52f8c2b 100644
--- a/yt/src/storage/migrate/mod.rs
+++ b/yt/src/storage/migrate/mod.rs
@@ -25,6 +25,15 @@ pub enum DbVersion {
}
const CURRENT_VERSION: DbVersion = DbVersion::One;
+async fn add_error_context(
+ function: impl Future<Output = Result<()>>,
+ level: DbVersion,
+) -> Result<()> {
+ function
+ .await
+ .with_context(|| format!("Format failed to migrate database to version: {level}"))
+}
+
async fn set_db_version(
tx: &mut Transaction<'_, Sqlite>,
old_version: Option<DbVersion>,
@@ -83,40 +92,85 @@ impl DbVersion {
///
/// Each update is atomic, so if this function fails you are still guaranteed to have a
/// database at version `get_version`.
+ #[allow(clippy::too_many_lines)]
async fn update(self, app: &App) -> Result<()> {
match self {
DbVersion::Empty => {
- let mut tx = app.database.begin().await?;
- debug!("Migrate: Empty -> Zero");
+ add_error_context(
+ async {
+ let mut tx = app
+ .database
+ .begin()
+ .await
+ .context("Failed to start transaction")?;
+ debug!("Migrate: Empty -> Zero");
+
+ sqlx::raw_sql(include_str!("./sql/00_empty_to_zero.sql"))
+ .execute(&mut *tx)
+ .await
+ .context("Failed to execute sql update script")?;
+
+ set_db_version(&mut tx, None, DbVersion::Zero)
+ .await
+ .context("Failed to set new version")?;
- sqlx::raw_sql(include_str!("./sql/00_empty_to_zero.sql"))
- .execute(&mut *tx)
- .await?;
+ tx.commit().await.context("Failed to commit changes")?;
- set_db_version(&mut tx, None, DbVersion::Zero).await?;
+ // NOTE: This is needed, so that sqlite "sees" our changes to the table
+ // without having to reconnect. <2025-02-18>
+ query!("VACUUM")
+ .execute(&app.database)
+ .await
+ .context("Failed to vacuum database")?;
- tx.commit().await?;
+ Ok(())
+ },
+ DbVersion::One,
+ )
+ .await?;
Box::pin(Self::Zero.update(app)).await
}
DbVersion::Zero => {
- let mut tx = app.database.begin().await?;
- debug!("Migrate: Zero -> One");
+ add_error_context(
+ async {
+ let mut tx = app
+ .database
+ .begin()
+ .await
+ .context("Failed to start transaction")?;
+ debug!("Migrate: Zero -> One");
- sqlx::raw_sql(include_str!("./sql/01_zero_to_one.sql"))
- .execute(&mut *tx)
- .await?;
+ sqlx::raw_sql(include_str!("./sql/01_zero_to_one.sql"))
+ .execute(&mut *tx)
+ .await
+ .context("Failed to execute the update sql script")?;
- set_db_version(&mut tx, Some(DbVersion::Zero), DbVersion::One).await?;
+ set_db_version(&mut tx, Some(DbVersion::Zero), DbVersion::One)
+ .await
+ .context("Failed to set the new version")?;
+
+ tx.commit()
+ .await
+ .context("Failed to commit the update transaction")?;
+
+ // NOTE: This is needed, so that sqlite "sees" our changes to the table
+ // without having to reconnect. <2025-02-18>
+ query!("VACUUM")
+ .execute(&app.database)
+ .await
+ .context("Failed to vacuum database")?;
+
+ Ok(())
+ },
+ DbVersion::Zero,
+ )
+ .await?;
- tx.commit().await?;
Box::pin(Self::One.update(app)).await
}
-
// This is the current_version
DbVersion::One => {
- debug!("Migrate: One -> Two");
-
assert_eq!(self, CURRENT_VERSION);
assert_eq!(self, get_version(app).await?);
Ok(())
@@ -141,13 +195,8 @@ fn get_current_date() -> i64 {
)
.expect("Time does not go backwards");
- debug!(
- "Adding a date with timestamp: {}",
- seconds_since_epoch.num_seconds()
- );
-
// All database dates should be after the UNIX_EPOCH (and thus positiv)
- seconds_since_epoch.num_seconds()
+ seconds_since_epoch.num_milliseconds()
}
/// Return the current database version.
@@ -155,11 +204,21 @@ fn get_current_date() -> i64 {
/// # Panics
/// Only if internal assertions fail.
pub async fn get_version(app: &App) -> Result<DbVersion> {
+ get_version_db(&app.database).await
+}
+/// Return the current database version.
+///
+/// In contrast to the [`get_version`] function, this function does not
+/// a fully instantiated [`App`], a database connection suffices.
+///
+/// # Panics
+/// Only if internal assertions fail.
+pub async fn get_version_db(pool: &SqlitePool) -> Result<DbVersion> {
let version_table_exists = {
let query = query!(
"SELECT 1 as result FROM sqlite_master WHERE type = 'table' AND name = 'version'"
)
- .fetch_optional(&app.database)
+ .fetch_optional(pool)
.await?;
if let Some(output) = query {
assert_eq!(output.result, 1);
@@ -177,7 +236,7 @@ pub async fn get_version(app: &App) -> Result<DbVersion> {
SELECT namespace, number FROM version WHERE valid_to IS NULL;
"
)
- .fetch_one(&app.database)
+ .fetch_one(pool)
.await
.context("Failed to fetch version number")?;
@@ -185,7 +244,9 @@ pub async fn get_version(app: &App) -> Result<DbVersion> {
}
pub async fn migrate_db(app: &App) -> Result<()> {
- let current_version = get_version(app).await?;
+ let current_version = get_version(app)
+ .await
+ .context("Failed to determine initial version")?;
if current_version == CURRENT_VERSION {
return Ok(());