v0.3.2-pre.007

This commit is contained in:
2026-08-29 21:40:57 +02:00
parent c4f56d9e85
commit e39cf656b2
19 changed files with 849 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/error.rs
// version: 3
// version: 4
/// Error code reserved for operations attempted after a Store backend has entered its closed state.
pub const ERROR_CODE_BACKEND_CLOSED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "backend_closed");
@@ -11,6 +11,8 @@ pub const ERROR_CODE_BACKEND_OPEN_FAILED: ksp_store_api::ErrorCode = ksp_store_a
pub const ERROR_CODE_POSTGRES_CONFIG_INVALID: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_config_invalid");
/// Error code used when PostgreSQL physical connection establishment fails without exposing remote or credential details.
pub const ERROR_CODE_POSTGRES_CONNECT_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_connect_failed");
/// Error code used when a lightweight PostgreSQL health/readiness probe fails safely.
pub const ERROR_CODE_POSTGRES_HEALTH_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_health_failed");
/// Error code used when PostgreSQL migration/bootstrap execution fails without exposing server text or SQL.
pub const ERROR_CODE_POSTGRES_MIGRATION_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_migration_failed");
/// Error code used when persisted PostgreSQL migration history diverges from the embedded immutable KSP history.

View File

@@ -0,0 +1,132 @@
// file: crates/ksp-store-lib/src/health.rs
// version: 1
/// Portable Store health state independent from the selected physical backend.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum StoreHealthState {
/// The selected Store backend answered the bounded readiness probe and its migration foundation is current.
Ready,
/// The Store instance exists but its latest bounded readiness probe could not prove readiness.
NotReady,
}
/// Safe synchronous Store runtime snapshot without performing backend I/O.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StoreRuntimeSnapshot {
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
pool_available: u32,
pool_capacity: u32,
pool_size: u32,
pool_waiting: u32,
}
impl StoreRuntimeSnapshot {
/// Creates one portable runtime projection from backend-owned safe counters.
#[must_use]
pub(crate) fn new(
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
pool_capacity: u32,
pool_size: u32,
pool_available: u32,
pool_waiting: u32,
) -> Self {
return Self { backend_kind, network, pool_available, pool_capacity, pool_size, pool_waiting };
}
/// Returns the selected backend identity.
#[must_use]
pub const fn backend_kind(&self) -> crate::StoreBackendKind {
return self.backend_kind;
}
/// Returns the one logical network bound to this Store instance.
#[must_use]
pub const fn network(&self) -> &ksp_store_api::RawNetworkId {
return &self.network;
}
/// Returns the number of currently available pooled backend objects.
#[must_use]
pub const fn pool_available(&self) -> u32 {
return self.pool_available;
}
/// Returns the configured maximum pooled backend object count.
#[must_use]
pub const fn pool_capacity(&self) -> u32 {
return self.pool_capacity;
}
/// Returns the current pooled backend object count.
#[must_use]
pub const fn pool_size(&self) -> u32 {
return self.pool_size;
}
/// Returns the number of tasks currently waiting for a pooled backend object.
#[must_use]
pub const fn pool_waiting(&self) -> u32 {
return self.pool_waiting;
}
}
/// Portable Store readiness projection containing only safe runtime and migration diagnostics.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StoreHealthSnapshot {
last_error_code: std::option::Option<ksp_store_api::ErrorCode>,
migration_version: std::option::Option<u64>,
pending_migration_count: u32,
runtime: StoreRuntimeSnapshot,
state: StoreHealthState,
}
impl StoreHealthSnapshot {
/// Creates one safe health projection from already classified backend diagnostics.
#[must_use]
pub(crate) fn new(
state: StoreHealthState,
runtime: StoreRuntimeSnapshot,
migration_version: std::option::Option<u64>,
pending_migration_count: u32,
last_error_code: std::option::Option<ksp_store_api::ErrorCode>,
) -> Self {
return Self { last_error_code, migration_version, pending_migration_count, runtime, state };
}
/// Returns the latest safe error code when readiness could not be proven.
#[must_use]
pub const fn last_error_code(&self) -> std::option::Option<ksp_store_api::ErrorCode> {
return self.last_error_code;
}
/// Returns the migration version observed by the readiness probe when available.
#[must_use]
pub const fn migration_version(&self) -> std::option::Option<u64> {
return self.migration_version;
}
/// Returns the number of embedded migrations newer than the observed applied version.
#[must_use]
pub const fn pending_migration_count(&self) -> u32 {
return self.pending_migration_count;
}
/// Returns the safe synchronous runtime projection captured for this health probe.
#[must_use]
pub const fn runtime(&self) -> &StoreRuntimeSnapshot {
return &self.runtime;
}
/// Returns whether this probe proved the Store ready.
#[must_use]
pub const fn state(&self) -> StoreHealthState {
return self.state;
}
}
#[cfg(test)]
#[path = "../unit_tests/health.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/lib.rs
// version: 5
// version: 6
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -7,9 +7,10 @@
//! Common backend-neutral Store runtime facade for KSP.
//!
//! `0.3.2-pre.006` materializes the physical PostgreSQL runtime plus its private
//! migration/bootstrap foundation: bounded pool, explicit TLS, versioned SHA-256
//! history and no business persistence schema.
//! `0.3.2-pre.007` closes the physical PostgreSQL runtime composition with a
//! portable safe runtime snapshot and lightweight health/readiness projection,
//! while retaining the private migration/bootstrap foundation and no business
//! persistence schema.
//!
//! The default `postgres` feature compiles the official PostgreSQL backend as
//! an optional implementation dependency. No backend implementation type is
@@ -17,6 +18,7 @@
mod constants;
mod error;
mod health;
mod settings;
mod store;
@@ -30,6 +32,8 @@ pub use self::error::ERROR_CODE_BACKEND_OPEN_FAILED;
pub use self::error::ERROR_CODE_POSTGRES_CONFIG_INVALID;
/// Error code used when PostgreSQL physical connection establishment fails.
pub use self::error::ERROR_CODE_POSTGRES_CONNECT_FAILED;
/// Error code used when a lightweight PostgreSQL health/readiness probe fails safely.
pub use self::error::ERROR_CODE_POSTGRES_HEALTH_FAILED;
/// Error code used when PostgreSQL migration/bootstrap execution fails safely.
pub use self::error::ERROR_CODE_POSTGRES_MIGRATION_FAILED;
/// Error code used when PostgreSQL migration history diverges from the embedded immutable KSP history.
@@ -44,6 +48,12 @@ pub use self::error::ERROR_CODE_POSTGRES_TLS_FAILED;
pub use self::error::ERROR_CODE_SETTINGS_INVALID;
/// Error code used when explicit Store shutdown exceeds its configured deadline.
pub use self::error::ERROR_CODE_SHUTDOWN_TIMEOUT;
/// Portable Store health/readiness projection containing only safe diagnostics.
pub use self::health::StoreHealthSnapshot;
/// Portable Store health state independent from physical backend types.
pub use self::health::StoreHealthState;
/// Safe synchronous Store runtime snapshot containing backend-neutral pool counters.
pub use self::health::StoreRuntimeSnapshot;
/// Bounded PostgreSQL bootstrap and migration settings owned by the Store facade.
pub use self::settings::PostgresBootstrapSettings;
/// Bounded PostgreSQL connection-pool settings owned by the Store facade.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/store.rs
// version: 3
// version: 4
/// Opaque common Store runtime facade.
///
@@ -37,6 +37,44 @@ impl Store {
};
}
/// Returns a safe synchronous runtime snapshot without performing backend I/O.
#[must_use]
pub fn runtime_snapshot(&self) -> crate::StoreRuntimeSnapshot {
#[cfg(feature = "postgres")]
{
return match &self.runtime {
StoreRuntime::Postgres(backend) => map_postgres_runtime_snapshot(backend.runtime_snapshot(), self.backend_kind, self.network.clone()),
};
}
#[cfg(not(feature = "postgres"))]
{
return crate::StoreRuntimeSnapshot::new(self.backend_kind, self.network.clone(), 0, 0, 0, 0);
}
}
/// Runs the selected backend's lightweight bounded readiness probe and returns only portable redacted diagnostics.
pub async fn health(&self) -> crate::StoreHealthSnapshot {
#[cfg(feature = "postgres")]
{
return match &self.runtime {
StoreRuntime::Postgres(backend) => {
let snapshot = backend.health().await;
map_postgres_health_snapshot(snapshot, self.backend_kind, self.network.clone())
},
};
}
#[cfg(not(feature = "postgres"))]
{
return crate::StoreHealthSnapshot::new(
crate::StoreHealthState::NotReady,
self.runtime_snapshot(),
std::option::Option::None,
0,
std::option::Option::Some(crate::ERROR_CODE_BACKEND_NOT_COMPILED),
);
}
}
/// Explicitly closes the Store runtime, consumes its facade handle and applies the configured bounded shutdown deadline.
pub async fn close(self) -> ksp_store_api::Result<()> {
let backend_kind = self.backend_kind;
@@ -136,9 +174,47 @@ async fn open_postgres(
#[cfg(feature = "postgres")]
fn map_postgres_error(error: ksp_store_postgres_lib::PostgresBackendError, backend_kind: crate::StoreBackendKind, network: &str) -> ksp_store_api::Error {
let code = match error.kind() {
let code = postgres_error_code(error.kind());
return ksp_store_api::Error::new(code, "PostgreSQL Store backend lifecycle operation failed")
.with_context("backend", backend_kind.code())
.with_context("network", network)
.with_context("phase", error.phase());
}
#[cfg(feature = "postgres")]
fn map_postgres_runtime_snapshot(
snapshot: ksp_store_postgres_lib::PostgresBackendRuntimeSnapshot,
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
) -> crate::StoreRuntimeSnapshot {
return crate::StoreRuntimeSnapshot::new(
backend_kind,
network,
snapshot.pool_capacity(),
snapshot.pool_size(),
snapshot.pool_available(),
snapshot.pool_waiting(),
);
}
#[cfg(feature = "postgres")]
fn map_postgres_health_snapshot(
snapshot: ksp_store_postgres_lib::PostgresBackendHealthSnapshot,
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
) -> crate::StoreHealthSnapshot {
let state = if snapshot.is_ready() { crate::StoreHealthState::Ready } else { crate::StoreHealthState::NotReady };
let error_code = snapshot.error_kind().map(|kind| return postgres_error_code(kind));
let runtime = map_postgres_runtime_snapshot(snapshot.runtime().clone(), backend_kind, network);
return crate::StoreHealthSnapshot::new(state, runtime, snapshot.migration_version(), snapshot.pending_migration_count(), error_code);
}
#[cfg(feature = "postgres")]
fn postgres_error_code(kind: ksp_store_postgres_lib::PostgresBackendErrorKind) -> ksp_store_api::ErrorCode {
return match kind {
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid => crate::ERROR_CODE_POSTGRES_CONFIG_INVALID,
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed => crate::ERROR_CODE_POSTGRES_CONNECT_FAILED,
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed => crate::ERROR_CODE_POSTGRES_HEALTH_FAILED,
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout => crate::ERROR_CODE_POSTGRES_POOL_TIMEOUT,
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed => crate::ERROR_CODE_POSTGRES_MIGRATION_FAILED,
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch => crate::ERROR_CODE_POSTGRES_MIGRATION_MISMATCH,
@@ -147,10 +223,6 @@ fn map_postgres_error(error: ksp_store_postgres_lib::PostgresBackendError, backe
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => crate::ERROR_CODE_POSTGRES_TLS_FAILED,
_ => crate::ERROR_CODE_BACKEND_OPEN_FAILED,
};
return ksp_store_api::Error::new(code, "PostgreSQL Store backend lifecycle operation failed")
.with_context("backend", backend_kind.code())
.with_context("network", network)
.with_context("phase", error.phase());
}
#[cfg(not(feature = "postgres"))]

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/tests/dependency_boundary.rs
// version: 5
// version: 6
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -34,6 +34,8 @@ fn pre_005_manifest_keeps_backend_physical_dependencies_out_of_facade() {
fn pre_005_facade_exposes_no_physical_postgres_types_or_environment_bypass() {
let crate_root = include_str!("../src/lib.rs");
assert!(crate_root.contains("pub use self::settings::StoreSettings;"));
assert!(crate_root.contains("pub use self::health::StoreHealthSnapshot;"));
assert!(crate_root.contains("pub use self::health::StoreRuntimeSnapshot;"));
assert!(crate_root.contains("pub use self::store::Store;"));
assert!(crate_root.contains("pub use ksp_store_api::RawTransaction;"));
assert!(crate_root.contains("const _: &str = crate::TRACING_TARGET;"));
@@ -50,7 +52,7 @@ fn pre_005_facade_exposes_no_physical_postgres_types_or_environment_bypass() {
] {
assert!(!crate_root.contains(forbidden), "forbidden physical backend facade surface detected: {forbidden}");
}
let production = format!("{}\n{}", include_str!("../src/settings.rs"), include_str!("../src/store.rs"));
let production = format!("{}\n{}\n{}", include_str!("../src/health.rs"), include_str!("../src/settings.rs"), include_str!("../src/store.rs"));
for forbidden in ["ksp_config_lib", "std::env", "dotenv", "PGHOST", "PGPORT", "PGUSER", "PGPASSWORD", ".pgpass", "tokio_postgres", "deadpool_postgres"] {
assert!(!production.contains(forbidden), "forbidden Store facade ownership bypass detected: {forbidden}");
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/tests/public_api.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -25,6 +25,8 @@ fn pre_003_settings_and_lifecycle_contract_are_available_from_crate_root() {
assert!(settings.validate().is_ok());
let _open = ksp_store_lib::Store::open;
let _close = ksp_store_lib::Store::close;
let _runtime_snapshot = ksp_store_lib::Store::runtime_snapshot;
let _health = ksp_store_lib::Store::health;
return;
}
@@ -36,6 +38,7 @@ fn pre_005_common_and_postgres_error_codes_are_stable_and_store_owned() {
assert_eq!(ksp_store_lib::ERROR_CODE_BACKEND_OPEN_FAILED.code(), "backend_open_failed");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_CONFIG_INVALID.code(), "postgres_config_invalid");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_CONNECT_FAILED.code(), "postgres_connect_failed");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_HEALTH_FAILED.code(), "postgres_health_failed");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_POOL_TIMEOUT.code(), "postgres_pool_timeout");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_MIGRATION_FAILED.code(), "postgres_migration_failed");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_MIGRATION_MISMATCH.code(), "postgres_migration_mismatch");
@@ -55,3 +58,11 @@ fn pre_003_facade_reexports_backend_agnostic_store_api_types() {
let _result: ksp_store_lib::Result<()> = std::result::Result::Ok(());
return;
}
#[test]
fn pre_007_health_and_runtime_snapshot_types_are_portable_crate_root_contracts() {
let _state = std::mem::size_of::<std::option::Option<ksp_store_lib::StoreHealthState>>();
let _health = std::mem::size_of::<std::option::Option<ksp_store_lib::StoreHealthSnapshot>>();
let _runtime = std::mem::size_of::<std::option::Option<ksp_store_lib::StoreRuntimeSnapshot>>();
return;
}

View File

@@ -0,0 +1,45 @@
// file: crates/ksp-store-lib/unit_tests/health.rs
// version: 1
fn network() -> ksp_store_api::RawNetworkId {
return match ksp_store_api::RawNetworkId::new("devnet") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("valid Store health test network rejected: {error:?}"),
};
}
#[test]
fn runtime_snapshot_is_backend_neutral_and_contains_only_safe_counts() {
let runtime = crate::StoreRuntimeSnapshot::new(crate::StoreBackendKind::Postgres, network(), 8, 3, 2, 1);
assert_eq!(runtime.backend_kind(), crate::StoreBackendKind::Postgres);
assert_eq!(runtime.network().as_str(), "devnet");
assert_eq!(runtime.pool_capacity(), 8);
assert_eq!(runtime.pool_size(), 3);
assert_eq!(runtime.pool_available(), 2);
assert_eq!(runtime.pool_waiting(), 1);
let rendered = format!("{runtime:?}");
for forbidden in ["postgresql://", "password", "username", "database", "SELECT ", "ksp_store_schema_migrations"] {
assert!(!rendered.contains(forbidden), "unsafe runtime snapshot material detected: {forbidden}");
}
return;
}
#[test]
fn health_snapshot_carries_only_safe_state_migration_and_error_code() {
let runtime = crate::StoreRuntimeSnapshot::new(crate::StoreBackendKind::Postgres, network(), 8, 1, 1, 0);
let ready = crate::StoreHealthSnapshot::new(crate::StoreHealthState::Ready, runtime.clone(), std::option::Option::Some(0), 0, std::option::Option::None);
assert_eq!(ready.state(), crate::StoreHealthState::Ready);
assert_eq!(ready.migration_version(), std::option::Option::Some(0));
assert_eq!(ready.pending_migration_count(), 0);
assert_eq!(ready.last_error_code(), std::option::Option::None);
let not_ready = crate::StoreHealthSnapshot::new(
crate::StoreHealthState::NotReady,
runtime,
std::option::Option::None,
0,
std::option::Option::Some(crate::ERROR_CODE_POSTGRES_HEALTH_FAILED),
);
assert_eq!(not_ready.state(), crate::StoreHealthState::NotReady);
assert_eq!(not_ready.last_error_code(), std::option::Option::Some(crate::ERROR_CODE_POSTGRES_HEALTH_FAILED));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/error.rs
// version: 2
// version: 3
/// Safe backend-local classification used by the Store facade for stable error mapping.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -11,6 +11,8 @@ pub enum PostgresBackendErrorKind {
ConnectFailed,
/// A bounded pool wait, create or recycle operation reached its deadline.
PoolTimeout,
/// A lightweight PostgreSQL health/readiness probe failed without exposing server text or SQL.
HealthFailed,
/// PostgreSQL migration/bootstrap execution failed without exposing server text or SQL.
MigrationFailed,
/// Applied PostgreSQL migration history diverges from the embedded immutable KSP history.

View File

@@ -0,0 +1,101 @@
// file: crates/ksp-store-postgres-lib/src/health.rs
// version: 1
const DEFAULT_HEALTH_TIMEOUT_MS: u64 = 5_000;
const MIGRATION_VERSION_SQL: &str = "SELECT COALESCE(MAX(version), -1)::BIGINT FROM ksp_store_schema_migrations";
const READINESS_SQL: &str = "SELECT 1::BIGINT";
/// Runs one bounded lightweight readiness probe and returns only safe classified diagnostics.
pub(crate) async fn probe_health(pool: &deadpool_postgres::Pool) -> crate::PostgresBackendHealthSnapshot {
let runtime = crate::runtime_snapshot_from_status(pool.status());
let timeouts = pool.timeouts();
let timeout = match timeouts.wait {
std::option::Option::Some(value) => value,
std::option::Option::None => std::time::Duration::from_millis(DEFAULT_HEALTH_TIMEOUT_MS),
};
let bounded = tokio::time::timeout(timeout, probe_health_inner(pool, runtime.clone())).await;
return match bounded {
std::result::Result::Ok(snapshot) => snapshot,
std::result::Result::Err(_) => {
crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed)
},
};
}
async fn probe_health_inner(pool: &deadpool_postgres::Pool, runtime: crate::PostgresBackendRuntimeSnapshot) -> crate::PostgresBackendHealthSnapshot {
let client_result = pool.get().await;
let client = match client_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
let classified = crate::map_pool_error(error);
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, classified.kind());
},
};
let readiness_result = client.query_one(READINESS_SQL, &[]).await;
let readiness_row = match readiness_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
},
};
let readiness_value = match readiness_row.try_get::<usize, i64>(0) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
},
};
if readiness_value != 1 {
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
}
let migration_result = client.query_one(MIGRATION_VERSION_SQL, &[]).await;
let migration_row = match migration_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
},
};
let migration_value = migration_row.try_get::<usize, i64>(0);
let migration_version = match migration_value {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
},
};
let expected = crate::current_migration_version();
if migration_version < 0 || migration_version < expected {
let observed = nonnegative_version(migration_version);
let pending = pending_migration_count(migration_version, expected);
return crate::PostgresBackendHealthSnapshot::not_ready(runtime, observed, pending, crate::PostgresBackendErrorKind::MigrationMismatch);
}
if migration_version > expected {
return crate::PostgresBackendHealthSnapshot::not_ready(
runtime,
nonnegative_version(migration_version),
0,
crate::PostgresBackendErrorKind::SchemaNewer,
);
}
return crate::PostgresBackendHealthSnapshot::ready(runtime, migration_version as u64, 0);
}
fn nonnegative_version(value: i64) -> std::option::Option<u64> {
if value < 0 {
return std::option::Option::None;
}
return std::option::Option::Some(value as u64);
}
fn pending_migration_count(observed: i64, expected: i64) -> u32 {
if observed >= expected {
return 0;
}
let delta = expected.saturating_sub(observed);
if delta > i64::from(u32::MAX) {
return u32::MAX;
}
return delta as u32;
}
#[cfg(test)]
#[path = "../unit_tests/health.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/lib.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -7,9 +7,10 @@
//! Official PostgreSQL backend implementation for KSP Store.
//!
//! `0.3.2-pre.006` owns the physical `tokio-postgres` connection, bounded
//! Deadpool pool, explicit Rustls TLS policy and private KSP migration/bootstrap
//! engine. Business persistence remains absent from this foundation release.
//! `0.3.2-pre.007` owns the physical `tokio-postgres` connection, bounded
//! Deadpool pool, explicit Rustls TLS policy, private KSP migration/bootstrap
//! engine and safe lightweight health/readiness probe. Business persistence
//! remains absent from this foundation release.
//!
//! This crate depends on `ksp-store-api` and never on `ksp-store-lib`. The
//! common facade consumes only this crate's narrow backend bridge and never
@@ -17,6 +18,7 @@
mod constants;
mod error;
mod health;
mod migration;
mod runtime;
@@ -26,6 +28,10 @@ pub use self::error::PostgresBackendError;
pub use self::error::PostgresBackendErrorKind;
/// Opaque physical PostgreSQL backend owning its connection pool.
pub use self::runtime::PostgresBackend;
/// Safe PostgreSQL readiness projection returned through the backend bridge.
pub use self::runtime::PostgresBackendHealthSnapshot;
/// Safe PostgreSQL pool counter projection returned through the backend bridge.
pub use self::runtime::PostgresBackendRuntimeSnapshot;
/// Physical PostgreSQL settings bridge consumed only by the backend crate.
pub use self::runtime::PostgresBackendSettings;
/// TLS mode accepted by the physical PostgreSQL settings bridge.
@@ -33,7 +39,15 @@ pub use self::runtime::PostgresBackendTlsMode;
/// Crate-owned tracing target for PostgreSQL backend behavior.
pub(crate) use self::constants::TRACING_TARGET;
/// Private bounded health probe consumed by the physical backend runtime.
pub(crate) use self::health::probe_health;
/// Private migration/bootstrap runner consumed by the physical backend runtime.
pub(crate) use self::migration::bootstrap;
/// Current embedded migration version consumed by the private health probe.
pub(crate) use self::migration::current_migration_version;
/// Private Deadpool error mapper shared with the health probe.
pub(crate) use self::runtime::map_pool_error;
/// Private Deadpool status projector shared with the health probe.
pub(crate) use self::runtime::runtime_snapshot_from_status;
const _: &str = crate::TRACING_TARGET;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/migration.rs
// version: 1
// version: 2
use sha2::Digest; // rust-rules: trait-import
@@ -40,6 +40,12 @@ struct AppliedMigration {
version: i64,
}
/// Returns the latest migration version embedded by this backend runtime.
#[must_use]
pub(crate) const fn current_migration_version() -> i64 {
return BOOTSTRAP_MIGRATION_VERSION;
}
/// Runs the private bounded PostgreSQL schema bootstrap on one dedicated pooled client.
pub(crate) async fn bootstrap(
client: &mut deadpool_postgres::Client,

View File

@@ -1,10 +1,122 @@
// file: crates/ksp-store-postgres-lib/src/runtime.rs
// version: 2
// version: 3
const APPLICATION_NAME: &str = "ksp-store";
const MAX_CONNECTION_URI_BYTES: usize = 4_096;
const SHUTDOWN_POLL_INTERVAL_MS: u64 = 10;
/// Safe PostgreSQL runtime counters exported only through the narrow backend bridge.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PostgresBackendRuntimeSnapshot {
pool_available: u32,
pool_capacity: u32,
pool_size: u32,
pool_waiting: u32,
}
impl PostgresBackendRuntimeSnapshot {
/// Creates a safe pool-counter projection from already bounded values.
#[must_use]
pub(crate) const fn new(pool_capacity: u32, pool_size: u32, pool_available: u32, pool_waiting: u32) -> Self {
return Self { pool_available, pool_capacity, pool_size, pool_waiting };
}
/// Returns the number of currently available pooled PostgreSQL clients.
#[must_use]
pub const fn pool_available(&self) -> u32 {
return self.pool_available;
}
/// Returns the configured maximum pooled PostgreSQL client count.
#[must_use]
pub const fn pool_capacity(&self) -> u32 {
return self.pool_capacity;
}
/// Returns the current pooled PostgreSQL client count.
#[must_use]
pub const fn pool_size(&self) -> u32 {
return self.pool_size;
}
/// Returns the number of tasks currently waiting for a pooled PostgreSQL client.
#[must_use]
pub const fn pool_waiting(&self) -> u32 {
return self.pool_waiting;
}
}
/// Safe PostgreSQL readiness projection returned to the common Store facade.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PostgresBackendHealthSnapshot {
error_kind: std::option::Option<crate::PostgresBackendErrorKind>,
migration_version: std::option::Option<u64>,
pending_migration_count: u32,
ready: bool,
runtime: PostgresBackendRuntimeSnapshot,
}
impl PostgresBackendHealthSnapshot {
/// Creates one successful safe readiness projection.
#[must_use]
pub(crate) const fn ready(runtime: PostgresBackendRuntimeSnapshot, migration_version: u64, pending_migration_count: u32) -> Self {
return Self {
error_kind: std::option::Option::None,
migration_version: std::option::Option::Some(migration_version),
pending_migration_count,
ready: true,
runtime,
};
}
/// Creates one failed safe readiness projection from a classified backend error.
#[must_use]
pub(crate) const fn not_ready(
runtime: PostgresBackendRuntimeSnapshot,
migration_version: std::option::Option<u64>,
pending_migration_count: u32,
error_kind: crate::PostgresBackendErrorKind,
) -> Self {
return Self {
error_kind: std::option::Option::Some(error_kind),
migration_version,
pending_migration_count,
ready: false,
runtime,
};
}
/// Returns the safe backend error classification when readiness could not be proven.
#[must_use]
pub const fn error_kind(&self) -> std::option::Option<crate::PostgresBackendErrorKind> {
return self.error_kind;
}
/// Returns whether the latest bounded PostgreSQL probe proved readiness.
#[must_use]
pub const fn is_ready(&self) -> bool {
return self.ready;
}
/// Returns the migration version observed by the readiness probe when available.
#[must_use]
pub const fn migration_version(&self) -> std::option::Option<u64> {
return self.migration_version;
}
/// Returns the number of embedded migrations newer than the observed applied version.
#[must_use]
pub const fn pending_migration_count(&self) -> u32 {
return self.pending_migration_count;
}
/// Returns the safe PostgreSQL pool counters captured for this probe.
#[must_use]
pub const fn runtime(&self) -> &PostgresBackendRuntimeSnapshot {
return &self.runtime;
}
}
/// TLS mode accepted by the physical PostgreSQL backend bridge.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
@@ -165,6 +277,17 @@ impl PostgresBackend {
return std::result::Result::Ok(Self { network: settings.network, pool });
}
/// Returns a safe synchronous snapshot of bounded pool counters without performing PostgreSQL I/O.
#[must_use]
pub fn runtime_snapshot(&self) -> PostgresBackendRuntimeSnapshot {
return runtime_snapshot_from_status(self.pool.status());
}
/// Runs a bounded lightweight PostgreSQL readiness probe and returns only safe classified diagnostics.
pub async fn health(&self) -> PostgresBackendHealthSnapshot {
return crate::probe_health(&self.pool).await;
}
/// Explicitly closes the pool and waits for all owned pooled objects to drain inside the supplied bound.
pub async fn close(self, timeout: std::time::Duration) -> std::result::Result<(), crate::PostgresBackendError> {
self.pool.close();
@@ -304,7 +427,8 @@ fn build_verified_tls() -> std::result::Result<tokio_postgres_rustls::MakeRustls
return std::result::Result::Ok(tokio_postgres_rustls::MakeRustlsConnect::new(client_config));
}
fn map_pool_error(error: deadpool_postgres::PoolError) -> crate::PostgresBackendError {
/// Maps one Deadpool acquisition error into a redacted backend classification.
pub(crate) fn map_pool_error(error: deadpool_postgres::PoolError) -> crate::PostgresBackendError {
return match error {
deadpool_postgres::PoolError::Timeout(timeout_type) => crate::PostgresBackendError::new(
crate::PostgresBackendErrorKind::PoolTimeout,
@@ -321,6 +445,23 @@ fn map_pool_error(error: deadpool_postgres::PoolError) -> crate::PostgresBackend
};
}
/// Converts Deadpool status into a bounded safe backend runtime projection.
pub(crate) fn runtime_snapshot_from_status(status: deadpool_postgres::Status) -> PostgresBackendRuntimeSnapshot {
return PostgresBackendRuntimeSnapshot::new(
bounded_count(status.max_size),
bounded_count(status.size),
bounded_count(status.available),
bounded_count(status.waiting),
);
}
fn bounded_count(value: usize) -> u32 {
if value > u32::MAX as usize {
return u32::MAX;
}
return value as u32;
}
#[cfg(test)]
#[path = "../unit_tests/runtime.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -33,6 +33,7 @@ fn pre_005_backend_owns_exact_physical_runtime_dependencies_without_reverse_faca
fn pre_005_backend_keeps_environment_sql_migrations_and_physical_types_private() {
let crate_root = include_str!("../src/lib.rs");
assert!(crate_root.contains("mod error;"));
assert!(crate_root.contains("mod health;"));
assert!(crate_root.contains("mod migration;"));
assert!(crate_root.contains("mod runtime;"));
assert!(crate_root.contains("const _: &str = crate::TRACING_TARGET;"));
@@ -68,3 +69,16 @@ fn pre_005_backend_keeps_environment_sql_migrations_and_physical_types_private()
}
return;
}
#[test]
fn pre_007_health_probe_remains_foundation_only_and_private_sql() {
let health = include_str!("../src/health.rs");
assert!(health.contains("SELECT 1::BIGINT"));
assert!(health.contains("ksp_store_schema_migrations"));
for forbidden in
["RawTransaction", "RawAccountState", "raw_transaction", "raw_account", "CORE", "DECODE", "SPECIALIZED", "std::env", "dotenv", "KSP_SECRET_"]
{
assert!(!health.contains(forbidden), "forbidden health ownership/scope content detected: {forbidden}");
}
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/public_api.rs
// version: 2
// version: 3
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -39,12 +39,22 @@ fn pre_005_backend_error_projection_is_safe_and_static() {
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid,
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed,
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout,
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed,
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed,
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch,
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer,
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout,
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed,
];
assert_eq!(kinds.len(), 8);
assert_eq!(kinds.len(), 9);
return;
}
#[test]
fn pre_007_backend_health_bridge_exposes_only_safe_snapshot_types() {
let _runtime = std::mem::size_of::<std::option::Option<ksp_store_postgres_lib::PostgresBackendRuntimeSnapshot>>();
let _health = std::mem::size_of::<std::option::Option<ksp_store_postgres_lib::PostgresBackendHealthSnapshot>>();
let _runtime_snapshot = ksp_store_postgres_lib::PostgresBackend::runtime_snapshot;
let _health_probe = ksp_store_postgres_lib::PostgresBackend::health;
return;
}

View File

@@ -0,0 +1,42 @@
// file: crates/ksp-store-postgres-lib/unit_tests/health.rs
// version: 1
#[test]
fn pool_status_projection_is_bounded_and_contains_no_physical_handle() {
let status = deadpool_postgres::Status { max_size: 8, size: 3, available: 2, waiting: 1 };
let snapshot = crate::runtime_snapshot_from_status(status);
assert_eq!(snapshot.pool_capacity(), 8);
assert_eq!(snapshot.pool_size(), 3);
assert_eq!(snapshot.pool_available(), 2);
assert_eq!(snapshot.pool_waiting(), 1);
return;
}
#[test]
fn health_snapshot_distinguishes_ready_and_safe_failure_without_server_text() {
let status = deadpool_postgres::Status { max_size: 8, size: 1, available: 1, waiting: 0 };
let runtime = crate::runtime_snapshot_from_status(status);
let ready = crate::PostgresBackendHealthSnapshot::ready(runtime, 0, 0);
assert!(ready.is_ready());
assert_eq!(ready.migration_version(), std::option::Option::Some(0));
assert_eq!(ready.error_kind(), std::option::Option::None);
let failed =
crate::PostgresBackendHealthSnapshot::not_ready(ready.runtime().clone(), std::option::Option::None, 0, crate::PostgresBackendErrorKind::HealthFailed);
assert!(!failed.is_ready());
assert_eq!(failed.error_kind(), std::option::Option::Some(crate::PostgresBackendErrorKind::HealthFailed));
let rendered = format!("{failed:?}");
for forbidden in ["postgresql://", "SELECT ", "ksp_store_schema_migrations", "password", "server error"] {
assert!(!rendered.contains(forbidden), "unsafe backend health material detected: {forbidden}");
}
return;
}
#[test]
fn migration_health_helpers_keep_unknown_and_pending_counts_safe() {
assert_eq!(super::nonnegative_version(-1), std::option::Option::None);
assert_eq!(super::nonnegative_version(0), std::option::Option::Some(0));
assert_eq!(super::pending_migration_count(-1, 0), 1);
assert_eq!(super::pending_migration_count(0, 0), 0);
assert_eq!(super::pending_migration_count(1, 0), 0);
return;
}