From d9f6b996bccb02a088b47a9e846012a2a037fe41 Mon Sep 17 00:00:00 2001 From: Abhinav Chordia Date: Fri, 16 Oct 2026 17:28:06 +0000 Subject: [PATCH] Move the shared SQLite helpers out of common-svc so only sbx-mgr's store can reach them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit common-svc's `sql::sqlite` module opens, migrates and deletes a service's SQLite database by path (`open_rusqlite`, `migrate_embedded`, `migrate`, `remove_database_files`, `restrict_database_permissions`). It sat behind common-svc's `sqlite` feature, and Cargo feature unification turns that on for every crate that depends on common-svc once any crate in the build asks for it. So sbx-mgr's server could reach those helpers, and through them sbx-mgr's database, although only its store enables them. The module moves, as a pure move, into its own crate, `crates/libs/sqlite` (`pplx-space-sqlite`), with its runtime-manager catalog, its tests and the SQLite migration baseline constant. Only path rewrites change in the moved lines: `super::` paths into common-svc, `crate::baselines`, and the relative `migrate!`/`include_str!` paths, which are shorter now. common-svc loses the `sqlite` feature and its rusqlite dependency. Its `migration_checksums` module, which opens nothing and which the migrator's Postgres retry also uses, moves to a narrower `migration-checksums` feature. The store, node-ctl, node-mgr, runtime-manager, template-mgr, vol-mgr and the database migrator depend on the new crate instead; build-mgr never used the module and drops the feature. `dependency_fence.sbxmgr_rusqlite` now lists `pplx-space-sqlite`, so among sbx-mgr's crates only the store may depend on it, and a crate can only name what it depends on. `dependency_fence` reads only sbx-mgr's own manifests, so CI's `run_sbxmgr_production_checks` also asserts, for each feature configuration sbx-mgr's images are built with, that `pplx-space-sqlite` enters the server's and service's production graph only through the store, and rusqlite only through the store, the helpers, common-jobs and common-core. The new crate and common-svc's `migration-checksums` feature are also linted alone, so a feature that only unification supplies (as sqlx's `macros` was) fails CI. No behavior or message changes. Observable differences: - the `sqlite.migrate` span's module path, file and line, which OTel exports as `code.namespace`, `code.filepath` and `code.lineno` (`pplx_space_common_svc::sql::sqlite`, `sql/sqlite.rs:350` becomes `pplx_space_sqlite`, `crates/libs/sqlite/src/lib.rs:360`); - `sql!` locators in the moved code: the production one becomes `pplx_space_sqlite:252` (was `…::sql::sqlite:242`); the test-only `lib.rs:469` (was 459) and the four in `tests.rs` (same lines) change module path; - locator line shifts in callers: common-svc `sql/mod.rs` 188->185 and 264->261, runtime-manager `installs/store.rs` 175->172, 225->222 and 248->245. --- ARCHITECTURE.toml | 25 +++++--- Cargo.lock | 25 +++++++- Cargo.toml | 2 + ci/rust-artifact-inputs.json | 3 + ci/scripts/run-rust-checks.sh | 57 +++++++++++++++++++ crates/libs/build/mgr/server/Cargo.toml | 2 +- crates/libs/common/svc/Cargo.toml | 7 ++- crates/libs/common/svc/src/sql/mod.rs | 5 +- crates/libs/db/migrator/Cargo.toml | 3 +- crates/libs/db/migrator/src/lib.rs | 6 +- crates/libs/db/migrator/src/tests.rs | 2 +- .../tests/public_baseline_rollback_tests.rs | 2 +- crates/libs/node/ctl/server/Cargo.toml | 3 +- .../node/ctl/server/src/dirty_state/tests.rs | 32 +++-------- crates/libs/node/ctl/server/src/lib.rs | 10 ++-- crates/libs/node/mgr/server/Cargo.toml | 3 +- .../server/examples/firewall-reply-fixture.rs | 2 +- crates/libs/node/mgr/server/src/run.rs | 4 +- crates/libs/node/mgr/server/src/slot_pool.rs | 6 +- .../libs/node/mgr/server/src/slot_pool/db.rs | 2 +- .../node/runtime/manager/server/Cargo.toml | 3 +- .../manager/server/src/installs/store.rs | 17 +++--- .../server/src/installs/store/tests.rs | 4 +- crates/libs/sbx/mgr/store/AGENTS.md | 4 +- crates/libs/sbx/mgr/store/Cargo.toml | 3 +- crates/libs/sbx/mgr/store/src/db.rs | 12 ++-- .../src/db/auto_lifecycle_delete_receipts.rs | 3 +- .../sbx/mgr/store/src/db/eviction_scan.rs | 2 +- .../mgr/store/src/db/local_artifact_leases.rs | 2 +- .../store/src/db/route_activation_claims.rs | 2 +- crates/libs/sqlite/Cargo.toml | 28 +++++++++ .../svc/src/sql => sqlite/src}/baselines.rs | 1 - .../src/sql/sqlite.rs => sqlite/src/lib.rs} | 12 +++- .../sqlite => sqlite/src}/runtime_manager.rs | 6 +- .../src/sql/sqlite => sqlite/src}/tests.rs | 8 +-- crates/libs/template/mgr/Cargo.toml | 3 +- crates/libs/template/mgr/src/db.rs | 6 +- crates/libs/template/mgr/src/run.rs | 8 +-- crates/libs/vol/mgr/server/Cargo.toml | 3 +- crates/libs/vol/mgr/server/src/run.rs | 6 +- crates/tools/archcheck/README.md | 8 ++- 41 files changed, 235 insertions(+), 107 deletions(-) create mode 100644 crates/libs/sqlite/Cargo.toml rename crates/libs/{common/svc/src/sql => sqlite/src}/baselines.rs (80%) rename crates/libs/{common/svc/src/sql/sqlite.rs => sqlite/src/lib.rs} (96%) rename crates/libs/{common/svc/src/sql/sqlite => sqlite/src}/runtime_manager.rs (91%) rename crates/libs/{common/svc/src/sql/sqlite => sqlite/src}/tests.rs (99%) diff --git a/ARCHITECTURE.toml b/ARCHITECTURE.toml index 2ac4582aa..daa2cec26 100644 --- a/ARCHITECTURE.toml +++ b/ARCHITECTURE.toml @@ -247,17 +247,23 @@ mode = "required" # sbx-mgr's node-local SQLite database (`sandboxes` and every other sbx-mgr # table) is opened, migrated and queried only by its store, the # `pplx-space-sbx-mgr-store` crate. The guarantee is that crate's boundary: -# `dependency_fence.sbxmgr_rusqlite` below keeps rusqlite out of every other -# sbx-mgr crate, and outside its `test-support` feature the store's API hands +# `dependency_fence.sbxmgr_rusqlite` below keeps rusqlite and +# `pplx-space-sqlite` (the shared helpers that open, migrate and delete a +# SQLite database by path) out of every other sbx-mgr crate; CI's +# `run_sbxmgr_production_checks` (ci/scripts/run-rust-checks.sh) asserts that +# nothing else in the server's production graph brings either in; and +# outside its `test-support` feature the store's API hands # out no rusqlite connection, transaction, statement or row. (Its `FromSql` # impl for a column type can return rusqlite's `FromSqlError`, an error type # rather than a database capability, to a caller holding a rusqlite value.) # This ratchet is a line-based tripwire on top of it, within sbx-mgr's # crates: naming rusqlite, opening a connection or transaction, using the -# shared SQLite open and migration helpers or the shared job store's SQL -# functions, binding parameters, executing a statement, or writing an SQL -# statement literal outside the store is a crossing. Aliases, wrapped -# imports and calls through names imported earlier escape it. Other services' crates use rusqlite for their own databases, so +# shared job store's SQL functions, binding parameters, executing a +# statement, or writing an SQL statement literal outside the store is a +# crossing. Aliases, wrapped imports and calls through names imported +# earlier escape it. Its `sql::sqlite` pattern predates the shared helpers' +# move into `pplx-space-sqlite` and matches nothing now; the fence and the +# graph assertion cover them. Other services' crates use rusqlite for their own databases, so # `paths` limits the rule to sbx-mgr's. [rules.pattern_ratchet.sbxmgr_sqlite] patterns = [ @@ -331,10 +337,11 @@ mode = "required" mode = "required" # Only the store may depend on the SQLite drivers that can open sbx-mgr's -# database, so no other sbx-mgr crate can name rusqlite: no normal, dev or -# build dependency, for any target, under any name. +# database, or on the shared helpers that open, migrate and delete one by +# path, so no other sbx-mgr crate can name them: no normal, dev or build +# dependency, for any target, under any name. [rules.dependency_fence.sbxmgr_rusqlite] -packages = ["libsqlite3-sys", "rusqlite"] +packages = ["libsqlite3-sys", "pplx-space-sqlite", "rusqlite"] members = ["crates/libs/sbx/mgr/**", "crates/apps/sbx/**"] allow = ["crates/libs/sbx/mgr/store"] diff --git a/Cargo.lock b/Cargo.lock index 53537c138..6ca369e6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5466,7 +5466,6 @@ dependencies = [ "pplx-space-test-env", "rand 0.10.2", "redis", - "rusqlite", "rustls", "serde", "serde_json", @@ -5724,6 +5723,7 @@ dependencies = [ "pplx-space-common-svc", "pplx-space-db-test-fixtures", "pplx-space-sbx-mgr-store", + "pplx-space-sqlite", "pplx-space-test-env", "rusqlite", "sqlx", @@ -6222,6 +6222,7 @@ dependencies = [ "pplx-space-node-api-client", "pplx-space-node-mgr-client", "pplx-space-sbx-mgr-client", + "pplx-space-sqlite", "pplx-space-template-mgr-client", "prost-types", "rand 0.10.2", @@ -6285,6 +6286,7 @@ dependencies = [ "pplx-space-node-mgr-client", "pplx-space-node-runtime-core", "pplx-space-observability", + "pplx-space-sqlite", "pplx-space-test-env", "reqwest 0.13.5", "rstest", @@ -6411,6 +6413,7 @@ dependencies = [ "pplx-space-node-runtime-core", "pplx-space-node-runtime-manager-client", "pplx-space-s3-http-client", + "pplx-space-sqlite", "reqwest 0.13.5", "rusqlite", "rustix", @@ -6782,6 +6785,7 @@ dependencies = [ "pplx-space-observability", "pplx-space-sbx-mgr-client", "pplx-space-sbxmgr-migrations", + "pplx-space-sqlite", "prost-types", "ref-cast", "rusqlite", @@ -7114,6 +7118,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "pplx-space-sqlite" +version = "0.8.0" +dependencies = [ + "hex", + "pplx-space-common-core", + "pplx-space-common-svc", + "pplx-space-observability", + "rusqlite", + "sqlx", + "tempfile", + "thiserror 2.0.20", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "pplx-space-ssh-cli" version = "0.8.0" @@ -7149,6 +7170,7 @@ dependencies = [ "pplx-space-node-mgr-client", "pplx-space-observability", "pplx-space-sbx-mgr-client", + "pplx-space-sqlite", "pplx-space-template-mgr-client", "pplx-space-test-env", "pplx-space-volmgr-client", @@ -7274,6 +7296,7 @@ dependencies = [ "pplx-space-metrics", "pplx-space-node-mgr-client", "pplx-space-object-store", + "pplx-space-sqlite", "pplx-space-volmgr-client", "rusqlite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 238ae6fd2..c5b2310d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ members = [ "crates/libs/sbx/mgr/migrations", "crates/libs/sbx/mgr/server", "crates/libs/sbx/mgr/store", + "crates/libs/sqlite", "crates/libs/node/api/client", "crates/libs/node/api/server", "crates/libs/node/ctl/server", @@ -346,6 +347,7 @@ pplx-space-sbxmgr-migrations = { path = "crates/libs/sbx/mgr/migrations" } pplx-space-sbx-mgr = { path = "crates/libs/sbx/mgr/server" } pplx-space-sbx-mgr-client = { path = "crates/libs/sbx/mgr/client" } pplx-space-sbx-mgr-store = { path = "crates/libs/sbx/mgr/store" } +pplx-space-sqlite = { path = "crates/libs/sqlite" } pplx-space-spaced-build = { path = "crates/libs/spaced/build" } pplx-space-spaced-guest-protocol = { path = "crates/libs/spaced/guest/protocol" } pplx-space-spaced-guest-execution = { path = "crates/libs/spaced/guest/execution" } diff --git a/ci/rust-artifact-inputs.json b/ci/rust-artifact-inputs.json index f9cd73084..4e6e8f132 100644 --- a/ci/rust-artifact-inputs.json +++ b/ci/rust-artifact-inputs.json @@ -63,6 +63,7 @@ "crates/libs/sbx/mgr/store", "crates/libs/spaced/build", "crates/libs/spaced/common", + "crates/libs/sqlite", "crates/libs/template/mgr-client", "crates/libs/template/publish" ] @@ -103,6 +104,7 @@ "crates/libs/sbx/mgr/store", "crates/libs/spaced/build", "crates/libs/spaced/common", + "crates/libs/sqlite", "crates/libs/template/mgr-client" ] }, @@ -271,6 +273,7 @@ "crates/libs/spaced/guest/server", "crates/libs/spaced/host/client", "crates/libs/spaced/host/server", + "crates/libs/sqlite", "crates/libs/template/mgr", "crates/libs/template/mgr-client", "crates/libs/vol/mgr/client", diff --git a/ci/scripts/run-rust-checks.sh b/ci/scripts/run-rust-checks.sh index e9ae6d1e4..a1f2d503d 100755 --- a/ci/scripts/run-rust-checks.sh +++ b/ci/scripts/run-rust-checks.sh @@ -240,6 +240,36 @@ run_fmt_check() { cargo fmt --all -- --check } +# The direct dependents of package $1 in the normal dependency graph of +# sbx-mgr's server and service, built with the feature arguments that follow +# up to `--`, must all be among the packages after `--`. `dependency_fence` +# checks only sbx-mgr's own manifests; this catches a crate elsewhere in the +# graph that starts depending on the package and re-exports it. +sbxmgr_assert_direct_dependents() { + local package="$1" + shift + local feature_args=() + while [ "$1" != "--" ]; do + feature_args+=("$1") + shift + done + shift + local allowed=" $* " + local graph dependent + local unexpected=() + graph="$(cargo tree --locked -p pplx-space-sbx-mgr -p pplx-space-sbxmgr-svc \ + "${feature_args[@]}" -e normal -i "${package}" --depth 1 --prefix none)" + while read -r dependent; do + if [[ "${allowed}" != *" ${dependent} "* ]]; then + unexpected+=("${dependent}") + fi + done < <(awk 'NR > 1 { print $1 }' <<<"${graph}" | sort -u) + if [ "${#unexpected[@]}" -gt 0 ]; then + echo "${package} enters sbx-mgr's production graph (${feature_args[*]:-no features}) through ${unexpected[*]}; only $* may depend on it" >&2 + return 1 + fi +} + # sbx-mgr's SQLite store makes its raw connection and statement methods # public only under its `test-support` feature, which the workspace pass's # `--all-features` turns on. Lint sbx-mgr's production libraries and @@ -283,6 +313,16 @@ run_sbxmgr_production_checks() { echo "sbx-mgr's production graph (features: ${sbxmgr_features:-none}) must not enable the store's test-support" >&2 return 1 fi + # The shared SQLite helpers open, migrate and delete a database by + # path; only the store may bring them into the server's graph. + sbxmgr_assert_direct_dependents pplx-space-sqlite "${sbxmgr_feature_args[@]}" -- \ + pplx-space-sbx-mgr-store + # rusqlite enters only through the store, the helpers it depends on, + # and two shared crates whose rusqlite use hands no connection out: + # common-jobs' functions take a connection in, and common-core only + # implements rusqlite's value conversion traits for its types. + sbxmgr_assert_direct_dependents rusqlite "${sbxmgr_feature_args[@]}" -- \ + pplx-space-sbx-mgr-store pplx-space-sqlite pplx-space-common-jobs pplx-space-common-core done } @@ -322,6 +362,23 @@ run_clippy_checks() { -- \ -D warnings + # Built alone, so a sqlx or common-svc feature these crates need but only + # get through unification with the rest of the workspace fails here. + echo "--- :rust: Running isolated SQLite helper clippy" + CARGO_TARGET_DIR="${target_dir}" cargo clippy \ + --locked \ + -p pplx-space-sqlite \ + --lib \ + -- \ + -D warnings + CARGO_TARGET_DIR="${target_dir}" cargo clippy \ + --locked \ + -p pplx-space-common-svc \ + --no-default-features \ + --features migration-checksums \ + -- \ + -D warnings + run_sbxmgr_production_checks "${target_dir}" # feature-flags must lint clean with no vendor SDK compiled in; the matching diff --git a/crates/libs/build/mgr/server/Cargo.toml b/crates/libs/build/mgr/server/Cargo.toml index b6cb2da2e..3ebc65ae2 100644 --- a/crates/libs/build/mgr/server/Cargo.toml +++ b/crates/libs/build/mgr/server/Cargo.toml @@ -57,7 +57,7 @@ sqlx = { workspace = true, features = ["macros", "migrate"] } pplx-space-build-mgr-client = { workspace = true, features = ["test-support"] } axum = { workspace = true } base64 = { workspace = true } -pplx-space-common-svc = { workspace = true, features = ["test-support", "test-hooks", "k8s", "sqlite"] } +pplx-space-common-svc = { workspace = true, features = ["test-support", "test-hooks", "k8s"] } pplx-space-test-env = { workspace = true } rstest = { workspace = true } tempfile = { workspace = true } diff --git a/crates/libs/common/svc/Cargo.toml b/crates/libs/common/svc/Cargo.toml index 00e4fadcc..ebf1bc200 100644 --- a/crates/libs/common/svc/Cargo.toml +++ b/crates/libs/common/svc/Cargo.toml @@ -17,7 +17,9 @@ datadog = ["otel", "pplx-space-observability/datadog"] # Deprecated for one release; use otel and datadog. datadog-otel = ["otel", "datadog"] k8s = ["dep:k8s-openapi", "dep:kube"] -sqlite = ["dep:rusqlite", "dep:toml", "sqlx/migrate", "sqlx/runtime-tokio", "sqlx/sqlite-bundled", "sqlx/tls-rustls-aws-lc-rs"] +# Exposes `sql::migration_checksums`, the accepted-checksums adapter every +# migration runner embeds. +migration-checksums = ["dep:toml", "sqlx/migrate"] # Exposes shared route and authentication test helpers to other crates. # Unit tests get them under `cfg(test)`; external test suites opt in through # a dev-dependency. Mirrors `pplx-space-metrics`'s `test-capture`. @@ -43,7 +45,6 @@ nix = { workspace = true, features = ["fs", "user"] } pin-project-lite = { workspace = true } redis = { workspace = true } rand = { workspace = true } -rusqlite = { workspace = true, optional = true } rustls = { workspace = true, default-features = false, features = ["aws_lc_rs", "std", "tls12"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } @@ -70,6 +71,8 @@ pplx-space-metrics = { workspace = true } pplx-space-observability = { workspace = true } [dev-dependencies] +# The accepted-checksums tests migrate in-memory SQLite databases. +sqlx = { workspace = true, features = ["sqlite-bundled"] } insta = { workspace = true } pplx-space-common-core = { workspace = true, features = ["test-hooks"] } nix = { workspace = true, features = ["signal"] } diff --git a/crates/libs/common/svc/src/sql/mod.rs b/crates/libs/common/svc/src/sql/mod.rs index 5a7b5c3a5..26304d1a4 100644 --- a/crates/libs/common/svc/src/sql/mod.rs +++ b/crates/libs/common/svc/src/sql/mod.rs @@ -10,18 +10,15 @@ use sqlx::ConnectOptions as _; use sqlx::PgPool; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; -pub mod baselines; pub mod contention; pub mod federated_service_grants; pub mod identity; -#[cfg(feature = "sqlite")] +#[cfg(feature = "migration-checksums")] pub mod migration_checksums; pub mod node_registration; pub mod sandbox_quarantine; pub mod sandbox_residency; pub mod sandbox_routability; -#[cfg(feature = "sqlite")] -pub mod sqlite; const POOL_METRICS_INTERVAL: Duration = Duration::from_secs(10); diff --git a/crates/libs/db/migrator/Cargo.toml b/crates/libs/db/migrator/Cargo.toml index a3edc306e..023a497a9 100644 --- a/crates/libs/db/migrator/Cargo.toml +++ b/crates/libs/db/migrator/Cargo.toml @@ -20,7 +20,8 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } tracing = { workspace = true } # internal -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true, features = ["migration-checksums"] } +pplx-space-sqlite = { workspace = true } pplx-space-sbx-mgr-store = { workspace = true } [dev-dependencies] diff --git a/crates/libs/db/migrator/src/lib.rs b/crates/libs/db/migrator/src/lib.rs index 3456e8a9b..0e3e74e15 100644 --- a/crates/libs/db/migrator/src/lib.rs +++ b/crates/libs/db/migrator/src/lib.rs @@ -13,7 +13,7 @@ //! carry that weight, and no long-running service links this. use pplx_space_common_svc::sql; -use pplx_space_common_svc::sql::sqlite::runtime_manager::MIGRATOR as RUNTIME_MANAGER_MIGRATOR; +use pplx_space_sqlite::runtime_manager::MIGRATOR as RUNTIME_MANAGER_MIGRATOR; use std::borrow::Cow; use std::collections::BTreeSet; use std::path::{Path, PathBuf}; @@ -25,7 +25,7 @@ use sqlx::postgres::PgPoolOptions; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::{PgConnection, PgPool, SqlitePool}; -use pplx_space_common_svc::sql::sqlite::{self, SqliteError}; +use pplx_space_sqlite::{self as sqlite, SqliteError}; pub mod bootstrap_roles; pub mod migration_retry; @@ -577,7 +577,7 @@ async fn rollback_last_sqlite_database( .last() .copied(); if common_version.is_some_and(|version| { - version > pplx_space_common_svc::sql::baselines::PUBLIC_SQLITE_BASELINE + version > pplx_space_sqlite::baselines::PUBLIC_SQLITE_BASELINE && Some(version) > service_version }) { return rollback_last_sqlite(&pool, &common).await; diff --git a/crates/libs/db/migrator/src/tests.rs b/crates/libs/db/migrator/src/tests.rs index 5fc3ba461..7a0b35f42 100644 --- a/crates/libs/db/migrator/src/tests.rs +++ b/crates/libs/db/migrator/src/tests.rs @@ -61,7 +61,7 @@ fn embedded_migrations_have_unique_versions_and_public_baselines() { let baseline = if *name == "postgres" { 20261002070625 } else { - pplx_space_common_svc::sql::baselines::PUBLIC_SQLITE_BASELINE + pplx_space_sqlite::baselines::PUBLIC_SQLITE_BASELINE }; assert_eq!( migrator diff --git a/crates/libs/db/migrator/src/tests/public_baseline_rollback_tests.rs b/crates/libs/db/migrator/src/tests/public_baseline_rollback_tests.rs index 8b93da80e..28b7decda 100644 --- a/crates/libs/db/migrator/src/tests/public_baseline_rollback_tests.rs +++ b/crates/libs/db/migrator/src/tests/public_baseline_rollback_tests.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, sync::LazyLock}; use super::*; -use pplx_space_common_svc::sql::baselines; +use pplx_space_sqlite::baselines; use sqlx::SqlSafeStr as _; use sqlx::migrate::{Migration, MigrationType}; diff --git a/crates/libs/node/ctl/server/Cargo.toml b/crates/libs/node/ctl/server/Cargo.toml index 618ddeb99..d628be9f8 100644 --- a/crates/libs/node/ctl/server/Cargo.toml +++ b/crates/libs/node/ctl/server/Cargo.toml @@ -37,7 +37,8 @@ k8s-openapi = { workspace = true, features = ["v1_32"], optional = true } kube = { workspace = true, default-features = false, features = ["aws-lc-rs", "client", "config", "rustls-tls"], optional = true } pplx-space-common-core = { workspace = true } pplx-space-common-grpc = { workspace = true, features = ["internal-tls"] } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-execution-common = { workspace = true } pplx-space-net-common = { workspace = true } pplx-space-metrics = { workspace = true } diff --git a/crates/libs/node/ctl/server/src/dirty_state/tests.rs b/crates/libs/node/ctl/server/src/dirty_state/tests.rs index 431910546..17f054610 100644 --- a/crates/libs/node/ctl/server/src/dirty_state/tests.rs +++ b/crates/libs/node/ctl/server/src/dirty_state/tests.rs @@ -31,15 +31,10 @@ async fn mark_refs_revives_backed_off_dirty_row() -> anyhow::Result<()> { let temp = tempfile::tempdir()?; let db_path = temp.path().join("node-ctl.db"); - pplx_space_common_svc::sql::sqlite::migrate( - "node-ctl-test", + pplx_space_sqlite::migrate("node-ctl-test", &db_path, &crate::NODE_CTL_MIGRATOR).await?; + let conn = pplx_space_sqlite::open_rusqlite( &db_path, - &crate::NODE_CTL_MIGRATOR, - ) - .await?; - let conn = pplx_space_common_svc::sql::sqlite::open_rusqlite( - &db_path, - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), + pplx_space_sqlite::RusqliteOpenOptions::default(), )?; let old_now = crate::now_secs().saturating_sub(10); let old_next_attempt = old_now.saturating_add(300); @@ -53,15 +48,10 @@ async fn mark_refs_revives_backed_off_dirty_row() -> anyhow::Result<()> { )?; drop(conn); - pplx_space_common_svc::sql::sqlite::migrate( - "node-ctl-test", - &db_path, - &crate::NODE_CTL_MIGRATOR, - ) - .await?; - let mut conn = pplx_space_common_svc::sql::sqlite::open_rusqlite( + pplx_space_sqlite::migrate("node-ctl-test", &db_path, &crate::NODE_CTL_MIGRATOR).await?; + let mut conn = pplx_space_sqlite::open_rusqlite( &db_path, - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), + pplx_space_sqlite::RusqliteOpenOptions::default(), )?; let marked_at = crate::now_secs(); @@ -116,13 +106,9 @@ async fn mark_refs_revives_backed_off_dirty_row() -> anyhow::Result<()> { )?; drop(conn); - let error = pplx_space_common_svc::sql::sqlite::migrate( - "node-ctl-test", - &db_path, - &crate::NODE_CTL_MIGRATOR, - ) - .await - .unwrap_err(); + let error = pplx_space_sqlite::migrate("node-ctl-test", &db_path, &crate::NODE_CTL_MIGRATOR) + .await + .unwrap_err(); assert!(error.to_string().contains("db/README.md")); let conn = rusqlite::Connection::open(&db_path)?; diff --git a/crates/libs/node/ctl/server/src/lib.rs b/crates/libs/node/ctl/server/src/lib.rs index 9c10475cc..4213c8cec 100644 --- a/crates/libs/node/ctl/server/src/lib.rs +++ b/crates/libs/node/ctl/server/src/lib.rs @@ -242,9 +242,9 @@ pub enum RunNodeCtlError { #[error("failed to acquire the node-ctl service mutex: {0}")] ServiceMutex(#[source] pplx_space_common_svc::service_mutex::AcquireError), #[error("failed to migrate node-ctl's SQLite database: {0}")] - Migrate(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + Migrate(#[source] pplx_space_sqlite::SqliteError), #[error("failed to open node-ctl's SQLite database: {0}")] - OpenDatabase(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + OpenDatabase(#[source] pplx_space_sqlite::SqliteError), #[error("failed to seed reconciliation status rows: {0}")] InitReconciliationStatus(#[source] rusqlite::Error), #[error("failed to bind the node-ctl socket: {0}")] @@ -490,12 +490,12 @@ pub async fn run( ); health_server.mark_starting(); - pplx_space_common_svc::sql::sqlite::migrate("node-ctl", &cli.db_path, &NODE_CTL_MIGRATOR) + pplx_space_sqlite::migrate("node-ctl", &cli.db_path, &NODE_CTL_MIGRATOR) .await .map_err(RunNodeCtlError::Migrate)?; - let db = pplx_space_common_svc::sql::sqlite::open_rusqlite( + let db = pplx_space_sqlite::open_rusqlite( &cli.db_path, - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), + pplx_space_sqlite::RusqliteOpenOptions::default(), ) .map_err(RunNodeCtlError::OpenDatabase)?; init_reconciliation_status(&db, poll_interval_secs) diff --git a/crates/libs/node/mgr/server/Cargo.toml b/crates/libs/node/mgr/server/Cargo.toml index e0e80df12..a67df01a4 100644 --- a/crates/libs/node/mgr/server/Cargo.toml +++ b/crates/libs/node/mgr/server/Cargo.toml @@ -49,7 +49,8 @@ zeroize = { workspace = true } pplx-space-common-core = { workspace = true } pplx-space-common-grpc = { workspace = true } pplx-space-common-io = { workspace = true, features = ["reflink"] } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-credmgr-client = { workspace = true } pplx-space-events = { workspace = true } pplx-space-execution-common = { workspace = true } diff --git a/crates/libs/node/mgr/server/examples/firewall-reply-fixture.rs b/crates/libs/node/mgr/server/examples/firewall-reply-fixture.rs index ac3dbf537..bd03ccc10 100644 --- a/crates/libs/node/mgr/server/examples/firewall-reply-fixture.rs +++ b/crates/libs/node/mgr/server/examples/firewall-reply-fixture.rs @@ -40,7 +40,7 @@ async fn main() -> Result<(), Box> { // Real migrations and startup worker, inside the harness's disposable /space. async fn recover_pool() -> Result<(), Box> { let db = Path::new(pplx_space_common_core::host::linux::paths::NODE_MANAGER_DB); - pplx_space_common_svc::sql::sqlite::migrate("node-manager", db, &MIGRATOR).await?; + pplx_space_sqlite::migrate("node-manager", db, &MIGRATOR).await?; let mut config = NetworkPoolConfig::new(3); config.pod_netns_path = Some("host".into()); NetworkPool::validate_startup(&config, db)?; diff --git a/crates/libs/node/mgr/server/src/run.rs b/crates/libs/node/mgr/server/src/run.rs index 5c32758d7..c28f0d974 100644 --- a/crates/libs/node/mgr/server/src/run.rs +++ b/crates/libs/node/mgr/server/src/run.rs @@ -177,7 +177,7 @@ pub enum RunNodeMgrError { #[error("failed to apply sandbox parent disk I/O reservation: {0}")] ApplyParentDiskIoReservation(String), #[error("failed to migrate node-manager's SQLite database: {0}")] - Migrate(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + Migrate(#[source] pplx_space_sqlite::SqliteError), /// Incompatible ownership or slot-store state found before any host limit /// could shrink beneath live sandboxes. #[error("node-manager slot pool startup validation failed: {0}")] @@ -633,7 +633,7 @@ pub async fn run( // Reject incompatible ownership before any host limit can shrink beneath // live sandboxes. Pool construction repeats this check before row repair. let db_path = Path::new(pplx_space_common_svc::core::host::linux::paths::NODE_MANAGER_DB); - pplx_space_common_svc::sql::sqlite::migrate("node-manager", db_path, &NODE_MANAGER_MIGRATOR) + pplx_space_sqlite::migrate("node-manager", db_path, &NODE_MANAGER_MIGRATOR) .await .map_err(RunNodeMgrError::Migrate)?; crate::slot_pool::NetworkPool::validate_startup(&pool_config, db_path) diff --git a/crates/libs/node/mgr/server/src/slot_pool.rs b/crates/libs/node/mgr/server/src/slot_pool.rs index 400ab54f0..b6670f18c 100644 --- a/crates/libs/node/mgr/server/src/slot_pool.rs +++ b/crates/libs/node/mgr/server/src/slot_pool.rs @@ -317,7 +317,7 @@ pub enum PoolOpenError { source: std::io::Error, }, #[error("cannot open network pool database: {0}")] - Open(#[from] pplx_space_common_svc::sql::sqlite::SqliteError), + Open(#[from] pplx_space_sqlite::SqliteError), #[error(transparent)] Store(#[from] SlotStoreError), #[error("cannot install guest L2 ingress policy in namespace {namespace}: {source}")] @@ -704,9 +704,9 @@ impl NetworkPool { source, })?; } - let mut conn = pplx_space_common_svc::sql::sqlite::open_rusqlite( + let mut conn = pplx_space_sqlite::open_rusqlite( Path::new(db_path), - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions { + pplx_space_sqlite::RusqliteOpenOptions { foreign_keys: true, ..Default::default() }, diff --git a/crates/libs/node/mgr/server/src/slot_pool/db.rs b/crates/libs/node/mgr/server/src/slot_pool/db.rs index 6f4fa51d3..47d524c11 100644 --- a/crates/libs/node/mgr/server/src/slot_pool/db.rs +++ b/crates/libs/node/mgr/server/src/slot_pool/db.rs @@ -1,6 +1,6 @@ use super::*; -pub(super) use pplx_space_common_svc::sql::sqlite::SqliteU64; +pub(super) use pplx_space_sqlite::SqliteU64; const SLOT_COLUMNS: &str = "slot_id, generation, state, health, sandbox_id, sandbox_uid, pod_netns_path, lease_kind, pid, last_error, memory_reservation_mib"; diff --git a/crates/libs/node/runtime/manager/server/Cargo.toml b/crates/libs/node/runtime/manager/server/Cargo.toml index 2e6a16700..f65b8138d 100644 --- a/crates/libs/node/runtime/manager/server/Cargo.toml +++ b/crates/libs/node/runtime/manager/server/Cargo.toml @@ -50,7 +50,8 @@ tracing = { workspace = true } # internal pplx-space-common-core = { workspace = true, features = ["rusqlite", "serde"] } pplx-space-common-jobs = { workspace = true } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-execution-common = { workspace = true } pplx-space-node-mgr-client = { workspace = true } pplx-space-node-runtime-core = { workspace = true, features = ["artifact-store"] } diff --git a/crates/libs/node/runtime/manager/server/src/installs/store.rs b/crates/libs/node/runtime/manager/server/src/installs/store.rs index b132c1a0c..48769cf78 100644 --- a/crates/libs/node/runtime/manager/server/src/installs/store.rs +++ b/crates/libs/node/runtime/manager/server/src/installs/store.rs @@ -130,9 +130,9 @@ pub enum StoreError { #[error("runtime-manager database")] Sqlite(#[from] rusqlite::Error), #[error("runtime-manager database migration")] - Migrate(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + Migrate(#[source] pplx_space_sqlite::SqliteError), #[error("cannot open the runtime-manager database")] - Open(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + Open(#[source] pplx_space_sqlite::SqliteError), #[error("runtime-manager database holds an unreadable {column} value '{value}'")] Corrupt { column: &'static str, value: String }, } @@ -141,22 +141,19 @@ pub enum StoreError { /// The database is restricted to 0600 inside the already private directory. pub(crate) async fn open(state_dir: &Path) -> Result { let path = state_dir.join(DATABASE_FILE); - pplx_space_common_svc::sql::sqlite::migrate_embedded( + pplx_space_sqlite::migrate_embedded( DATABASE_NAME, &path, - pplx_space_common_svc::sql::sqlite::EmbeddedMigrators { - service: pplx_space_common_svc::sql::sqlite::runtime_manager::catalog(&path) + pplx_space_sqlite::EmbeddedMigrators { + service: pplx_space_sqlite::runtime_manager::catalog(&path) .map_err(StoreError::Migrate)?, common: Some(&COMMON_SCHEMA_MIGRATOR), }, ) .await .map_err(StoreError::Migrate)?; - pplx_space_common_svc::sql::sqlite::open_rusqlite( - &path, - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), - ) - .map_err(StoreError::Open) + pplx_space_sqlite::open_rusqlite(&path, pplx_space_sqlite::RusqliteOpenOptions::default()) + .map_err(StoreError::Open) } fn parsed(column: &'static str, value: &str) -> Result { diff --git a/crates/libs/node/runtime/manager/server/src/installs/store/tests.rs b/crates/libs/node/runtime/manager/server/src/installs/store/tests.rs index 795586b3b..3b75575f6 100644 --- a/crates/libs/node/runtime/manager/server/src/installs/store/tests.rs +++ b/crates/libs/node/runtime/manager/server/src/installs/store/tests.rs @@ -178,10 +178,10 @@ async fn prior_database(dir: &Path, prototype: bool) -> Connection { ..Migrator::DEFAULT }; let path = dir.join(DATABASE_FILE); - pplx_space_common_svc::sql::sqlite::migrate_embedded( + pplx_space_sqlite::migrate_embedded( DATABASE_NAME, &path, - pplx_space_common_svc::sql::sqlite::EmbeddedMigrators { + pplx_space_sqlite::EmbeddedMigrators { service: &migrator, common: Some(&COMMON_SCHEMA_MIGRATOR), }, diff --git a/crates/libs/sbx/mgr/store/AGENTS.md b/crates/libs/sbx/mgr/store/AGENTS.md index a4de517b8..c91091817 100644 --- a/crates/libs/sbx/mgr/store/AGENTS.md +++ b/crates/libs/sbx/mgr/store/AGENTS.md @@ -3,7 +3,9 @@ sbx-mgr's node-local SQLite store: `sandboxes` and every other sbx-mgr table, not the control-plane Postgres. Only this crate opens, migrates or queries that database. `dependency_fence.sbxmgr_rusqlite` in `ARCHITECTURE.toml` keeps -rusqlite out of every other sbx-mgr crate. +rusqlite and the shared path-taking helpers in `pplx-space-sqlite` out of +every other sbx-mgr crate, and CI's `run_sbxmgr_production_checks` asserts +that nothing else in the server's production graph brings them in. ## Changes diff --git a/crates/libs/sbx/mgr/store/Cargo.toml b/crates/libs/sbx/mgr/store/Cargo.toml index b833e9942..fda52c2dd 100644 --- a/crates/libs/sbx/mgr/store/Cargo.toml +++ b/crates/libs/sbx/mgr/store/Cargo.toml @@ -38,7 +38,8 @@ uuid = { workspace = true, features = ["v7"] } # internal pplx-space-common-core = { workspace = true, features = ["serde"] } pplx-space-common-jobs = { workspace = true } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-events = { workspace = true } pplx-space-metrics = { workspace = true } pplx-space-net-common = { workspace = true, features = ["serde"] } diff --git a/crates/libs/sbx/mgr/store/src/db.rs b/crates/libs/sbx/mgr/store/src/db.rs index 9e76b9074..6b656f4cc 100644 --- a/crates/libs/sbx/mgr/store/src/db.rs +++ b/crates/libs/sbx/mgr/store/src/db.rs @@ -19,7 +19,6 @@ use pplx_space_common_core::types::{ SandboxTransitionReceipt, SandboxTransitionReceiptInvariantError, SandboxTransitionReceiptKind, SandboxTransitionTargetState, SnapshotName, }; -use pplx_space_common_svc::sql::sqlite::SqliteU64; use pplx_space_events::{ SandboxEventDetails, SandboxEventSource, SandboxEventTrigger, SandboxEventType, emit_sandbox_event, @@ -27,6 +26,7 @@ use pplx_space_events::{ #[cfg(test)] use pplx_space_net_common::NetworkPolicyV1; use pplx_space_net_common::{NetworkPolicy, proto::NetworkPolicySource}; +use pplx_space_sqlite::SqliteU64; use rusqlite::types::Type; use pplx_space_common_core::sql; @@ -227,7 +227,7 @@ const STATEMENT_CACHE_CAPACITY: usize = 128; /// shared SQLite helpers' error's, unchanged. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct OpenError(#[from] pplx_space_common_svc::sql::sqlite::SqliteError); +pub struct OpenError(#[from] pplx_space_sqlite::SqliteError); /// Apply the service and common schema migrations to the database. pub async fn migrate() -> Result<(), OpenError> { @@ -238,10 +238,10 @@ pub async fn migrate() -> Result<(), OpenError> { pub async fn migrate_at(db_path: &Path) -> Result<(), OpenError> { use pplx_space_sbxmgr_migrations::COMMON_SCHEMA_MIGRATOR; let target = migration::target(); - Ok(pplx_space_common_svc::sql::sqlite::migrate_embedded( + Ok(pplx_space_sqlite::migrate_embedded( target.name, db_path, - pplx_space_common_svc::sql::sqlite::EmbeddedMigrators { + pplx_space_sqlite::EmbeddedMigrators { service: target.service, common: Some(&COMMON_SCHEMA_MIGRATOR), }, @@ -252,9 +252,9 @@ pub async fn migrate_at(db_path: &Path) -> Result<(), OpenError> { /// Open the migrated database, with the connection settings every store /// operation assumes. pub fn open() -> Result { - let conn = pplx_space_common_svc::sql::sqlite::open_rusqlite( + let conn = pplx_space_sqlite::open_rusqlite( Path::new(DB_PATH), - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), + pplx_space_sqlite::RusqliteOpenOptions::default(), )?; conn.set_prepared_statement_cache_capacity(STATEMENT_CACHE_CAPACITY); Ok(Connection::from_raw(conn)) diff --git a/crates/libs/sbx/mgr/store/src/db/auto_lifecycle_delete_receipts.rs b/crates/libs/sbx/mgr/store/src/db/auto_lifecycle_delete_receipts.rs index d0f563410..e6039aff3 100644 --- a/crates/libs/sbx/mgr/store/src/db/auto_lifecycle_delete_receipts.rs +++ b/crates/libs/sbx/mgr/store/src/db/auto_lifecycle_delete_receipts.rs @@ -5,7 +5,8 @@ use pplx_space_common_core::types::{ AutoLifecycleDeleteReceiptId, ExecutionGeneration, NodeManagedBy, NodeRole, PolicyAuthority, SandboxActivationAttemptId, SandboxId, }; -use pplx_space_common_svc::{sql::sqlite::SqliteU64, types::AutoLifecyclePolicyFingerprint}; +use pplx_space_common_svc::types::AutoLifecyclePolicyFingerprint; +use pplx_space_sqlite::SqliteU64; use rusqlite::{params, types::Type}; use crate::auto_lifecycle_delete_receipts::{ diff --git a/crates/libs/sbx/mgr/store/src/db/eviction_scan.rs b/crates/libs/sbx/mgr/store/src/db/eviction_scan.rs index a19b839bb..7d0dc4d75 100644 --- a/crates/libs/sbx/mgr/store/src/db/eviction_scan.rs +++ b/crates/libs/sbx/mgr/store/src/db/eviction_scan.rs @@ -2,7 +2,7 @@ //! scans and local eviction evidence. use crate::db::{Connection, OptionalExtension, Result}; -use pplx_space_common_svc::sql::sqlite::SqliteU64; +use pplx_space_sqlite::SqliteU64; use rusqlite::params; use crate::db::timestamps::EpochSeconds; diff --git a/crates/libs/sbx/mgr/store/src/db/local_artifact_leases.rs b/crates/libs/sbx/mgr/store/src/db/local_artifact_leases.rs index 811893820..87129b291 100644 --- a/crates/libs/sbx/mgr/store/src/db/local_artifact_leases.rs +++ b/crates/libs/sbx/mgr/store/src/db/local_artifact_leases.rs @@ -5,7 +5,7 @@ use crate::db::{Connection, Error, OptionalExtension, Result}; use pplx_space_common_core::sql; use pplx_space_common_core::types::{ArtifactKind, LocalArtifactId, SnapshotName}; -use pplx_space_common_svc::sql::sqlite::SqliteU64; +use pplx_space_sqlite::SqliteU64; use rusqlite::params; use crate::db::timestamps::EpochSeconds; diff --git a/crates/libs/sbx/mgr/store/src/db/route_activation_claims.rs b/crates/libs/sbx/mgr/store/src/db/route_activation_claims.rs index 81fe6959c..f8c3a096c 100644 --- a/crates/libs/sbx/mgr/store/src/db/route_activation_claims.rs +++ b/crates/libs/sbx/mgr/store/src/db/route_activation_claims.rs @@ -6,7 +6,7 @@ use pplx_space_common_core::types::{ ExecutionGeneration, NodeId, NodeManagedBy, PodUid, RouteGeneration, SandboxActivationAttemptId, SpaceApiRouteIdentity, }; -use pplx_space_common_svc::sql::sqlite::SqliteU64; +use pplx_space_sqlite::SqliteU64; use rusqlite::params; use crate::auto_lifecycle_delete_receipts::RouteIdentityProvenance; diff --git a/crates/libs/sqlite/Cargo.toml b/crates/libs/sqlite/Cargo.toml new file mode 100644 index 000000000..c19b7d70d --- /dev/null +++ b/crates/libs/sqlite/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "pplx-space-sqlite" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +publish.workspace = true + +[lints] +workspace = true + +[dependencies] +rusqlite = { workspace = true } +sqlx = { workspace = true, features = ["derive", "macros", "migrate", "runtime-tokio", "sqlite-bundled", "tls-rustls-aws-lc-rs"] } +thiserror = { workspace = true } +tracing = { workspace = true } + +# internal +pplx-space-common-core = { workspace = true } +pplx-space-common-svc = { workspace = true, features = ["migration-checksums"] } + +[dev-dependencies] +hex = { workspace = true } +pplx-space-observability = { workspace = true, features = ["test-capture"] } +tempfile = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +tracing-subscriber = { workspace = true } diff --git a/crates/libs/common/svc/src/sql/baselines.rs b/crates/libs/sqlite/src/baselines.rs similarity index 80% rename from crates/libs/common/svc/src/sql/baselines.rs rename to crates/libs/sqlite/src/baselines.rs index 8c19e085f..96bc71811 100644 --- a/crates/libs/common/svc/src/sql/baselines.rs +++ b/crates/libs/sqlite/src/baselines.rs @@ -1,4 +1,3 @@ //! First supported public SQLite migration epoch. -#[cfg(feature = "sqlite")] pub const PUBLIC_SQLITE_BASELINE: i64 = 20260930000000; diff --git a/crates/libs/common/svc/src/sql/sqlite.rs b/crates/libs/sqlite/src/lib.rs similarity index 96% rename from crates/libs/common/svc/src/sql/sqlite.rs rename to crates/libs/sqlite/src/lib.rs index 9333fc090..2cfe797fd 100644 --- a/crates/libs/common/svc/src/sql/sqlite.rs +++ b/crates/libs/sqlite/src/lib.rs @@ -1,7 +1,17 @@ +//! Shared helpers that open, migrate and maintain a service-owned SQLite +//! database by path, for the node services and the migrator that own one. +//! +//! They live apart from `pplx-space-common-svc` so that a crate that must not +//! touch a service's database can leave them out of its dependencies; Cargo +//! feature unification would otherwise expose them to every crate that +//! depends on common-svc. `cargo arch`'s `dependency_fence` keeps this crate +//! out of sbx-mgr's crates other than its store. + +pub mod baselines; pub mod runtime_manager; -use super::migration_checksums::AcceptedChecksums; use pplx_space_common_core::sql; +use pplx_space_common_svc::sql::migration_checksums::AcceptedChecksums; use std::collections::HashMap; use std::error::Error as StdError; use std::fmt::Display; diff --git a/crates/libs/common/svc/src/sql/sqlite/runtime_manager.rs b/crates/libs/sqlite/src/runtime_manager.rs similarity index 91% rename from crates/libs/common/svc/src/sql/sqlite/runtime_manager.rs rename to crates/libs/sqlite/src/runtime_manager.rs index 9f0f347ea..512d5729f 100644 --- a/crates/libs/common/svc/src/sql/sqlite/runtime_manager.rs +++ b/crates/libs/sqlite/src/runtime_manager.rs @@ -11,10 +11,10 @@ use sqlx::migrate::{Migration, MigrationType}; use super::*; -pub static MIGRATOR: Migrator = sqlx::migrate!("../../../../db/runtime-manager"); -const BASELINE: i64 = super::super::baselines::PUBLIC_SQLITE_BASELINE; +pub static MIGRATOR: Migrator = sqlx::migrate!("../../../db/runtime-manager"); +const BASELINE: i64 = crate::baselines::PUBLIC_SQLITE_BASELINE; const PROTOTYPE_SQL: &str = - include_str!("../../../../../../../db/runtime-manager/history/prototype-baseline.sql"); + include_str!("../../../../db/runtime-manager/history/prototype-baseline.sql"); static PROTOTYPE_MIGRATOR: LazyLock = LazyLock::new(|| { let mut migrations = MIGRATOR.migrations.to_vec(); diff --git a/crates/libs/common/svc/src/sql/sqlite/tests.rs b/crates/libs/sqlite/src/tests.rs similarity index 99% rename from crates/libs/common/svc/src/sql/sqlite/tests.rs rename to crates/libs/sqlite/src/tests.rs index f11090eb8..c8526ff89 100644 --- a/crates/libs/common/svc/src/sql/sqlite/tests.rs +++ b/crates/libs/sqlite/src/tests.rs @@ -91,7 +91,7 @@ async fn node_manager_migration_chain_preserves_leases_on_history_failure() { let temp = tempfile::tempdir().unwrap(); let db_path = temp.path().join("node-manager.db"); let migrator = - Migrator::new(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../../db/node-manager")) + Migrator::new(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../db/node-manager")) .await .unwrap(); @@ -377,7 +377,7 @@ fn write_table_migration(migrations_dir: &Path, version: i64, name: &str) { #[tokio::test] async fn public_baseline_preserves_rejected_history() { - let version = super::super::baselines::PUBLIC_SQLITE_BASELINE; + let version = crate::baselines::PUBLIC_SQLITE_BASELINE; for damage in [ "UPDATE _sqlx_migrations SET checksum = x'00'", "UPDATE _sqlx_migrations SET success = FALSE", @@ -442,7 +442,7 @@ fn public_test_migrators() -> (&'static Migrator, &'static Migrator) { let migration = |sql: &'static str| { Migrator::with_migrations(vec![Migration::new( - super::super::baselines::PUBLIC_SQLITE_BASELINE, + crate::baselines::PUBLIC_SQLITE_BASELINE, Cow::Borrowed("baseline"), MigrationType::Simple, sql.into_sql_str(), @@ -550,7 +550,7 @@ async fn public_embedded_migrations_reject_both_histories_before_writes() { service, common: Some(common), }; - let pending_version = super::super::baselines::PUBLIC_SQLITE_BASELINE + 1; + let pending_version = crate::baselines::PUBLIC_SQLITE_BASELINE + 1; let mut common_migrations = common.iter().cloned().collect::>(); common_migrations.push(Migration::new( pending_version, diff --git a/crates/libs/template/mgr/Cargo.toml b/crates/libs/template/mgr/Cargo.toml index a17c9702e..cbb361e78 100644 --- a/crates/libs/template/mgr/Cargo.toml +++ b/crates/libs/template/mgr/Cargo.toml @@ -47,7 +47,8 @@ pplx-space-common-core = { workspace = true, features = ["serde"] } pplx-space-common-io = { workspace = true, features = ["reflink"] } pplx-space-common-jobs = { workspace = true } pplx-space-common-lru-reaper = { workspace = true } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-execution-common = { workspace = true } pplx-space-metrics = { workspace = true } pplx-space-node-api-client = { workspace = true } diff --git a/crates/libs/template/mgr/src/db.rs b/crates/libs/template/mgr/src/db.rs index bfb7f4048..7dce21d6a 100644 --- a/crates/libs/template/mgr/src/db.rs +++ b/crates/libs/template/mgr/src/db.rs @@ -10,10 +10,10 @@ use pplx_space_net_common::NetworkPolicyV1; pub const DB_PATH: &str = pplx_space_common_core::host::linux::paths::TEMPLATE_MANAGER_DB; -pub fn open() -> Result { - pplx_space_common_svc::sql::sqlite::open_rusqlite( +pub fn open() -> Result { + pplx_space_sqlite::open_rusqlite( Path::new(DB_PATH), - pplx_space_common_svc::sql::sqlite::RusqliteOpenOptions::default(), + pplx_space_sqlite::RusqliteOpenOptions::default(), ) } diff --git a/crates/libs/template/mgr/src/run.rs b/crates/libs/template/mgr/src/run.rs index 17660bcb8..f56ba1ca0 100644 --- a/crates/libs/template/mgr/src/run.rs +++ b/crates/libs/template/mgr/src/run.rs @@ -64,9 +64,9 @@ pub enum RunTemplateMgrError { #[error("failed to acquire the template-manager service mutex: {0}")] ServiceMutex(#[source] pplx_space_common_svc::service_mutex::AcquireError), #[error("failed to migrate the template-manager SQLite database: {0}")] - Migrate(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + Migrate(#[source] pplx_space_sqlite::SqliteError), #[error("failed to open the template-manager SQLite database: {0}")] - OpenDatabase(#[source] pplx_space_common_svc::sql::sqlite::SqliteError), + OpenDatabase(#[source] pplx_space_sqlite::SqliteError), #[error("failed to initialize template-manager service state: {0}")] InitializeService(#[source] rusqlite::Error), #[error("failed to bind the template-manager socket: {0}")] @@ -159,10 +159,10 @@ async fn run_service( .map_err(RunTemplateMgrError::RootfsBackingFilesystem)?; } - pplx_space_common_svc::sql::sqlite::migrate_embedded( + pplx_space_sqlite::migrate_embedded( "template-manager", std::path::Path::new(DB_PATH), - pplx_space_common_svc::sql::sqlite::EmbeddedMigrators { + pplx_space_sqlite::EmbeddedMigrators { service: &TEMPLATE_MANAGER_MIGRATOR, common: Some(&COMMON_SCHEMA_MIGRATOR), }, diff --git a/crates/libs/vol/mgr/server/Cargo.toml b/crates/libs/vol/mgr/server/Cargo.toml index f918d7e9e..114443de4 100644 --- a/crates/libs/vol/mgr/server/Cargo.toml +++ b/crates/libs/vol/mgr/server/Cargo.toml @@ -42,7 +42,8 @@ tracing = { workspace = true } pplx-space-common-core = { workspace = true, features = ["serde", "rusqlite"] } pplx-space-common-io = { workspace = true, features = ["reflink"] } pplx-space-common-jobs = { workspace = true } -pplx-space-common-svc = { workspace = true, features = ["sqlite"] } +pplx-space-common-svc = { workspace = true } +pplx-space-sqlite = { workspace = true } pplx-space-credmgr-client = { workspace = true } pplx-space-metrics = { workspace = true } pplx-space-object-store = { workspace = true } diff --git a/crates/libs/vol/mgr/server/src/run.rs b/crates/libs/vol/mgr/server/src/run.rs index 5b2a1f32c..6559bc7ad 100644 --- a/crates/libs/vol/mgr/server/src/run.rs +++ b/crates/libs/vol/mgr/server/src/run.rs @@ -12,11 +12,11 @@ use std::time::Duration; use pplx_space_common_core::host::linux::identities::group_names; use pplx_space_common_svc::bootstrap::BindUnixSocketError; -use pplx_space_common_svc::sql::sqlite::SqliteError; use pplx_space_common_svc::{ AcquireOutcome, LocalHealthLiveness, LocalHealthServer, SERVICE_MUTEX_POLL_INTERVAL, ServiceMutex, ServiceMutexAcquireError, UnixSocketOptions, }; +use pplx_space_sqlite::SqliteError; use tokio_stream::wrappers::UnixListenerStream; use tokio_util::sync::CancellationToken; @@ -37,10 +37,10 @@ static VOLUME_MANAGER_MIGRATOR: sqlx::migrate::Migrator = static COMMON_SCHEMA_MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("../../../../../db/common"); pub(crate) async fn migrate_volume_manager_db(path: &Path) -> Result<(), SqliteError> { - pplx_space_common_svc::sql::sqlite::migrate_embedded( + pplx_space_sqlite::migrate_embedded( "volume-manager", path, - pplx_space_common_svc::sql::sqlite::EmbeddedMigrators { + pplx_space_sqlite::EmbeddedMigrators { service: &VOLUME_MANAGER_MIGRATOR, common: Some(&COMMON_SCHEMA_MIGRATOR), }, diff --git a/crates/tools/archcheck/README.md b/crates/tools/archcheck/README.md index 59f6b5bcf..8a4252d20 100644 --- a/crates/tools/archcheck/README.md +++ b/crates/tools/archcheck/README.md @@ -270,7 +270,7 @@ member directories: ```toml [rules.dependency_fence.sbxmgr_rusqlite] -packages = ["libsqlite3-sys", "rusqlite"] +packages = ["libsqlite3-sys", "pplx-space-sqlite", "rusqlite"] members = ["crates/libs/sbx/mgr/**", "crates/apps/sbx/**"] allow = ["crates/libs/sbx/mgr/store"] @@ -287,6 +287,12 @@ declares the dependency. There is no baseline: an existing dependency is removed or its member allowed. An empty `packages` or `members`, or a `members` or `allow` glob that matches no workspace member, exits 2. +A fence reads only the checked members' own dependencies, not what those +dependencies depend on. For sbx-mgr, `run_sbxmgr_production_checks` in +`ci/scripts/run-rust-checks.sh` covers the transitive side: with `cargo +tree -i`, it asserts which crates may bring rusqlite and `pplx-space-sqlite` +into the server's production graph. + ## Configuration and modes Each behaviour has a `mode`: `advisory` findings are reported, `required` ones