Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 10 additions & 72 deletions Cargo.lock

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

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ deadpool = { default-features = false, version = "0.12" }
deadpool-diesel = { version = "0.6" }
deadpool-sync = { default-features = false, version = "0.1" }
diesel = { version = "2.3" }
diesel_migrations = { version = "2.3" }
fs-err = { version = "3" }
futures = { version = "0.3" }
hex = { version = "0.4" }
Expand Down
4 changes: 2 additions & 2 deletions bin/ntx-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ doctest = false
anyhow = { workspace = true }
clap = { features = ["env", "string"], workspace = true }
diesel = { features = ["numeric", "sqlite"], workspace = true }
diesel_migrations = { features = ["sqlite"], workspace = true }
futures = { workspace = true }
humantime = { workspace = true }
libsqlite3-sys = { workspace = true }
Expand All @@ -43,7 +42,8 @@ tracing = { workspace = true }
url = { workspace = true }

[build-dependencies]
build-rs = { workspace = true }
build-rs = { workspace = true }
miden-node-db = { workspace = true }

[dev-dependencies]
miden-node-utils = { features = ["testing"], workspace = true }
Expand Down
8 changes: 3 additions & 5 deletions bin/ntx-builder/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// This build.rs is required to trigger the `diesel_migrations::embed_migrations!` proc-macro in
// `src/db/migrations.rs` to include the latest version of the migrations into the binary, see
// <https://docs.rs/diesel_migrations/latest/diesel_migrations/macro.embed_migrations.html#automatic-rebuilds>.
fn main() -> Result<(), Box<dyn std::error::Error>> {
miden_node_db::migration::Migrator::generate("src/db/migrations")?;

fn main() {
build_rs::output::rerun_if_changed("src/db/migrations");
// If we do one re-write, the default rules are disabled,
// hence we need to trigger explicitly on `Cargo.toml`.
// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed>
build_rs::output::rerun_if_changed("Cargo.toml");
Ok(())
}
82 changes: 63 additions & 19 deletions bin/ntx-builder/src/db/migrations.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,73 @@
use diesel::SqliteConnection;
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
use std::path::Path;

use miden_node_db::DatabaseError;
use tracing::instrument;

use crate::COMPONENT;
use crate::db::schema_hash::verify_schema;

// The rebuild is automatically triggered by `build.rs` as described in
// <https://docs.rs/diesel_migrations/latest/diesel_migrations/macro.embed_migrations.html#automatic-rebuilds>.
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("src/db/migrations");
include!(concat!(env!("OUT_DIR"), "/db_migrator.rs"));

#[instrument(level = "debug", target = COMPONENT, skip_all, err)]
pub fn apply_migrations(conn: &mut SqliteConnection) -> Result<(), DatabaseError> {
let migrations = conn.pending_migrations(MIGRATIONS).expect("In memory migrations never fail");
tracing::info!(target: COMPONENT, migrations = migrations.len(), "Applying pending migrations");

let Err(e) = conn.run_pending_migrations(MIGRATIONS) else {
// Migrations applied successfully, verify schema hash.
verify_schema(conn)?;
return Ok(());
};
tracing::warn!(target: COMPONENT, "Failed to apply migration: {e:?}");
// Something went wrong; revert the last migration.
conn.revert_last_migration(MIGRATIONS)
.expect("Duality is maintained by the developer");
pub fn apply_migrations(database_filepath: &Path) -> Result<(), DatabaseError> {
let migrator = migrator().map_err(DatabaseError::migration)?;
tracing::info!(
target: COMPONENT,
migration_count = migrator.schema_hashes().len(),
"Applying database migrations"
);

migrator.migrate(database_filepath).map_err(DatabaseError::migration)?;
Ok(())
}

#[cfg(test)]
mod tests {
use std::process::Command;

use anyhow::{Context, Result, ensure};
use miden_node_db::migration::SchemaHash;

use super::*;

const EXPECTED_SCHEMA_HASHES: [SchemaHash; 1] = [SchemaHash::from_hex(
"c6434bc6a142cd96dd4072bea641546d99788b1495cb0e52c2d98b9138f9c30d",
)];

#[test]
fn migration_schema_hashes_are_stable() -> Result<()> {
let migrator = migrator()?;

assert_eq!(migrator.schema_hashes(), &EXPECTED_SCHEMA_HASHES);
Ok(())
}

#[test]
#[ignore = "requires diesel CLI; CI runs this in the diesel-schema job"]
fn diesel_schema_is_in_sync_with_migrations() -> Result<()> {
let temp_dir = tempfile::tempdir()?;
let database_filepath = temp_dir.path().join("ntx-builder.sqlite3");
apply_migrations(&database_filepath)?;

let output = Command::new("diesel")
.arg("print-schema")
.arg("--database-url")
.arg(&database_filepath)
.current_dir(env!("CARGO_MANIFEST_DIR"))
.output()
.context(
"failed to run diesel CLI; install it with \
`cargo install diesel_cli --no-default-features --features sqlite`",
)?;

ensure!(
output.status.success(),
"diesel print-schema failed: {}",
String::from_utf8_lossy(&output.stderr)
);

let generated =
String::from_utf8(output.stdout).context("diesel CLI output is not UTF-8")?;
assert_eq!(generated, include_str!("schema.rs"));
Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CREATE TABLE chain_state (
-- Singleton constraint: only one row allowed.
id INTEGER NOT NULL PRIMARY KEY CHECK (id = 0),
-- Block number of the chain tip.
block_num INTEGER NOT NULL,
block_num BIGINT NOT NULL,
-- Serialized BlockHeader.
block_header BLOB NOT NULL,

Expand Down Expand Up @@ -33,7 +33,7 @@ CREATE UNIQUE INDEX idx_accounts_inflight ON accounts(account_id, transaction_id
CREATE INDEX idx_accounts_account ON accounts(account_id);
CREATE INDEX idx_accounts_tx ON accounts(transaction_id) WHERE transaction_id IS NOT NULL;

-- Notes: committed, inflight, and nullified all in one table.
-- Notes: committed, inflight, and nullified - all in one table.
-- created_by = NULL means committed note; non-NULL means created by inflight tx.
-- consumed_by = NULL means unconsumed; non-NULL means consumed by inflight tx.
-- committed_at = block number when the consuming transaction was committed on-chain.
Expand All @@ -49,7 +49,7 @@ CREATE TABLE notes (
-- Backoff tracking: number of failed execution attempts.
attempt_count INTEGER NOT NULL DEFAULT 0,
-- Backoff tracking: block number of the last failed attempt. NULL if never attempted.
last_attempt INTEGER,
last_attempt BIGINT,
-- Latest execution error message. NULL if no error recorded.
last_error TEXT,
-- NULL if the note came from a committed block; transaction ID if created by inflight tx.
Expand All @@ -58,7 +58,7 @@ CREATE TABLE notes (
consumed_by BLOB,
-- Block number at which the note's consuming transaction was committed.
-- NULL while the note is still pending or in-flight; set on block commit.
committed_at INTEGER,
committed_at BIGINT,

CONSTRAINT notes_attempt_count_non_negative CHECK (attempt_count >= 0),
CONSTRAINT notes_last_attempt_is_u32 CHECK (last_attempt BETWEEN 0 AND 0xFFFFFFFF),
Expand Down

This file was deleted.

12 changes: 4 additions & 8 deletions bin/ntx-builder/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use crate::{COMPONENT, NoteError};
pub(crate) mod models;

mod migrations;
mod schema_hash;

/// [diesel](https://diesel.rs) generated schema.
pub(crate) mod schema;
Expand Down Expand Up @@ -58,6 +57,8 @@ impl Db {
database_filepath: PathBuf,
connection_pool_size: NonZeroUsize,
) -> anyhow::Result<Self> {
apply_migrations(&database_filepath).context("failed to apply migrations")?;

let inner = miden_node_db::Db::new_with_pool_size(&database_filepath, connection_pool_size)
.context("failed to build connection pool")?;

Expand All @@ -68,12 +69,7 @@ impl Db {
"Connected to the database"
);

let me = Db { inner };
me.inner
.query("migrations", apply_migrations)
.await
.context("failed to apply migrations on pool connection")?;
Ok(me)
Ok(Db { inner })
}

// PUBLIC QUERY METHODS
Expand Down Expand Up @@ -255,10 +251,10 @@ impl Db {

let dir = tempfile::tempdir().expect("failed to create temp directory");
let db_path = dir.path().join("test.sqlite3");
apply_migrations(&db_path).expect("migrations should apply on empty database");
let mut conn = SqliteConnection::establish(db_path.to_str().unwrap())
.expect("temp file sqlite should always work");
configure_connection_on_creation(&mut conn).expect("connection configuration should work");
apply_migrations(&mut conn).expect("migrations should apply on empty database");
(conn, dir)
}

Expand Down
Loading
Loading