feat: remove old migrations
This commit is contained in:
parent
b28546d2de
commit
bf8e79b88c
@ -15,8 +15,6 @@ pub mod db;
|
||||
mod migrations;
|
||||
pub mod operations;
|
||||
|
||||
pub const STATE_FILE: &str = "datastore/private/state.ron";
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn main() {
|
||||
use crate::server::cli::Cli;
|
||||
@ -50,8 +48,9 @@ async fn serve(cli: Cli) -> Result<(), Error> {
|
||||
use leptos_axum::generate_route_list;
|
||||
|
||||
tracing::debug!("Creating DB");
|
||||
let db = db::Database::create()?;
|
||||
let db = db::Database::create().map_err(db::DatabaseOperationError::from)?;
|
||||
|
||||
db::init_at_current_version(&db).await?;
|
||||
migrations::run_migrations(&db).await.map_err(Error::Migration)?;
|
||||
|
||||
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
||||
@ -125,5 +124,5 @@ pub enum Error {
|
||||
|
||||
Confik(confik::Error),
|
||||
|
||||
Database(redb::Error),
|
||||
Database(db::DatabaseOperationError),
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use bincode::Bincode;
|
||||
use redb::ReadTransaction;
|
||||
use redb::ReadableTable;
|
||||
use redb::ReadableTableMetadata;
|
||||
use redb::TableDefinition;
|
||||
use redb::WriteTransaction;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -83,6 +86,46 @@ impl Version {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub async fn init_at_current_version(db: &Database) -> Result<(), DatabaseOperationError> {
|
||||
db.write(|txn| {
|
||||
let mut version_table = txn.open_table(TABLE_VERSION)?;
|
||||
let is_missing_version = version_table.get(())?.is_none();
|
||||
if is_missing_version {
|
||||
let v = Version::current();
|
||||
tracing::warn!("INITIALIZING DATABASE AT VERSION {v}");
|
||||
version_table.insert((), v)?;
|
||||
|
||||
// Root table
|
||||
{
|
||||
let mut table = txn.open_table(current::TABLE_ROOT)?;
|
||||
assert!(table.is_empty()?);
|
||||
table.insert((), models::Root { walls: BTreeSet::new() })?;
|
||||
}
|
||||
|
||||
// Walls table
|
||||
{
|
||||
// Opening the table creates the table
|
||||
let table = txn.open_table(current::TABLE_WALLS)?;
|
||||
assert!(table.is_empty()?);
|
||||
}
|
||||
|
||||
// Problems table
|
||||
{
|
||||
// Opening the table creates the table
|
||||
let table = txn.open_table(current::TABLE_PROBLEMS)?;
|
||||
assert!(table.is_empty()?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use crate::models;
|
||||
pub use v2 as current;
|
||||
|
||||
pub mod v2 {
|
||||
|
@ -1,218 +1,6 @@
|
||||
use super::db;
|
||||
use super::db::Database;
|
||||
use crate::models;
|
||||
use image::ImageDecoder;
|
||||
use redb::ReadableTable;
|
||||
use redb::ReadableTableMetadata;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use type_toppings::ResultExt;
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub async fn run_migrations(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
migrate_from_ron_to_redb(db).await?;
|
||||
init_at_current_version(db).await?;
|
||||
migrate_to_v2(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use redb DB instead of Ron state file
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn migrate_from_ron_to_redb(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let ron_state_file_path = PathBuf::from(super::STATE_FILE);
|
||||
|
||||
if ron_state_file_path
|
||||
.try_exists()
|
||||
.expect_or_report_with(|| format!("Failed to read {}", ron_state_file_path.display()))
|
||||
{
|
||||
tracing::warn!("MIGRATING");
|
||||
|
||||
let ron_state: models::v1::PersistentState = {
|
||||
let content = tokio::fs::read_to_string(&ron_state_file_path).await?;
|
||||
ron::from_str(&content)?
|
||||
};
|
||||
|
||||
db.write(|txn| {
|
||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
||||
assert!(version_table.is_empty()?);
|
||||
version_table.insert((), db::Version { version: 1 })?;
|
||||
|
||||
let mut root_table = txn.open_table(db::v1::TABLE_ROOT)?;
|
||||
assert!(root_table.is_empty()?);
|
||||
|
||||
let persistent_state = models::v1::PersistentState {
|
||||
version: ron_state.version,
|
||||
wall: ron_state.wall,
|
||||
problems: ron_state.problems,
|
||||
};
|
||||
|
||||
root_table.insert((), persistent_state)?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
tracing::info!("Removing ron state");
|
||||
tokio::fs::remove_file(ron_state_file_path).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Move out, is not really a migration
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn init_at_current_version(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
db.write(|txn| {
|
||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
||||
let is_missing_version = version_table.get(())?.is_none();
|
||||
if is_missing_version {
|
||||
let v = db::Version::current();
|
||||
tracing::warn!("INITIALIZING DATABASE AT VERSION {v}");
|
||||
version_table.insert((), v)?;
|
||||
|
||||
// Root table
|
||||
{
|
||||
let mut table = txn.open_table(db::current::TABLE_ROOT)?;
|
||||
assert!(table.is_empty()?);
|
||||
table.insert((), models::Root { walls: BTreeSet::new() })?;
|
||||
}
|
||||
|
||||
// Walls table
|
||||
{
|
||||
// Opening the table creates the table
|
||||
let table = txn.open_table(db::current::TABLE_WALLS)?;
|
||||
assert!(table.is_empty()?);
|
||||
}
|
||||
|
||||
// Problems table
|
||||
{
|
||||
// Opening the table creates the table
|
||||
let table = txn.open_table(db::current::TABLE_PROBLEMS)?;
|
||||
assert!(table.is_empty()?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn migrate_to_v2(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use super::db;
|
||||
|
||||
db.write(|txn| {
|
||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
||||
let version = version_table.get(())?.unwrap().value().version;
|
||||
if version == 1 {
|
||||
tracing::warn!("MIGRATING");
|
||||
version_table.insert((), db::Version { version: 2 })?;
|
||||
|
||||
let root_table_v1 = txn.open_table(db::v1::TABLE_ROOT)?;
|
||||
let root_v1 = root_table_v1.get(())?.unwrap().value();
|
||||
drop(root_table_v1);
|
||||
txn.delete_table(db::v1::TABLE_ROOT)?;
|
||||
|
||||
let models::v1::PersistentState { version: _, wall, problems } = root_v1;
|
||||
|
||||
// we'll reimport them instead of a lossy conversion.
|
||||
drop(problems);
|
||||
|
||||
let mut walls = BTreeSet::new();
|
||||
let wall_uid = models::v2::WallUid(uuid::Uuid::new_v4());
|
||||
let holds = wall
|
||||
.holds
|
||||
.into_iter()
|
||||
.map(|(hold_position, hold)| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let image = hold
|
||||
.image
|
||||
.map(|i| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let holds_dir = Path::new("datastore/public/holds");
|
||||
|
||||
let p = holds_dir.join(i.filename);
|
||||
tracing::info!("reading {}", p.display());
|
||||
let file_contents = std::fs::read(p)?;
|
||||
|
||||
let mut decoder = image::ImageReader::new(std::io::Cursor::new(file_contents))
|
||||
.with_guessed_format()?
|
||||
.into_decoder()?;
|
||||
let orientation = decoder.orientation()?;
|
||||
let mut img = image::DynamicImage::from_decoder(decoder)?;
|
||||
img.apply_orientation(orientation);
|
||||
|
||||
std::fs::create_dir_all(holds_dir)?;
|
||||
|
||||
let targets = [(50, 50), (150, 150), (300, 300), (400, 400)];
|
||||
|
||||
let uid = models::ImageUid::create();
|
||||
let mut resolutions = BTreeMap::new();
|
||||
for (width, height) in targets {
|
||||
tracing::info!("resizing to {width}x{height}");
|
||||
let resized = img.resize_to_fill(width, height, image::imageops::FilterType::Lanczos3);
|
||||
|
||||
let filename = format!("hold_row{}_col{}_{width}x{height}_{uid}.webp", hold_position.row, hold_position.col);
|
||||
let path = holds_dir.join(&filename);
|
||||
tracing::info!("opening {}", path.display());
|
||||
let mut file = std::fs::OpenOptions::new().write(true).append(false).create_new(true).open(&path)?;
|
||||
resized.write_to(&mut file, image::ImageFormat::WebP)?;
|
||||
|
||||
let res = models::ImageResolution {
|
||||
width: width.into(),
|
||||
height: height.into(),
|
||||
};
|
||||
resolutions.insert(res, models::ImageFilename { filename });
|
||||
}
|
||||
|
||||
Ok(models::Image { uid, resolutions })
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
Ok((
|
||||
models::v1::HoldPosition {
|
||||
row: hold_position.row,
|
||||
col: hold_position.col,
|
||||
},
|
||||
models::v2::Hold {
|
||||
position: models::v1::HoldPosition {
|
||||
row: hold.position.row,
|
||||
col: hold.position.col,
|
||||
},
|
||||
image,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
|
||||
let wall_v2 = models::v2::Wall {
|
||||
uid: wall_uid,
|
||||
rows: wall.rows,
|
||||
cols: wall.cols,
|
||||
holds,
|
||||
problems: BTreeSet::new(),
|
||||
};
|
||||
|
||||
walls.insert(wall_v2.uid);
|
||||
let root_v2 = models::v2::Root { walls };
|
||||
|
||||
let mut root_table_v2 = txn.open_table(db::v2::TABLE_ROOT)?;
|
||||
root_table_v2.insert((), root_v2)?;
|
||||
drop(root_table_v2);
|
||||
|
||||
let mut walls_table = txn.open_table(db::v2::TABLE_WALLS)?;
|
||||
walls_table.insert(wall_v2.uid, wall_v2)?;
|
||||
drop(walls_table);
|
||||
|
||||
let problems_table = txn.open_table(db::v2::TABLE_PROBLEMS)?;
|
||||
drop(problems_table);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
pub async fn run_migrations(_db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user