v0.3.2-pre.005

This commit is contained in:
2026-08-29 19:33:43 +02:00
parent de3cec6a23
commit d93184d41d
20 changed files with 945 additions and 120 deletions

View File

@@ -1,5 +1,5 @@
# file: crates/ksp-store-lib/Cargo.toml
# version: 1
# version: 2
[package]
name = "ksp-store-lib"
@@ -12,6 +12,7 @@ default = ["postgres"]
postgres = ["dep:ksp-store-postgres-lib"]
[dependencies]
ksp-logging-lib = { path = "../ksp-logging-lib" }
ksp-store-api = { path = "../ksp-store-api" }
ksp-store-postgres-lib = { path = "../ksp-store-postgres-lib", optional = true }

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/error.rs
// version: 1
// version: 2
/// 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");
@@ -7,6 +7,14 @@ pub const ERROR_CODE_BACKEND_CLOSED: ksp_store_api::ErrorCode = ksp_store_api::E
pub const ERROR_CODE_BACKEND_NOT_COMPILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "backend_not_compiled");
/// Error code used when a compiled Store backend cannot complete its bounded opening lifecycle.
pub const ERROR_CODE_BACKEND_OPEN_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "backend_open_failed");
/// Error code used when the PostgreSQL backend rejects or cannot normalize its physical connection configuration.
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 bounded PostgreSQL pool wait, create or recycle operation reaches its deadline.
pub const ERROR_CODE_POSTGRES_POOL_TIMEOUT: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_pool_timeout");
/// Error code used when verified PostgreSQL TLS setup or negotiation cannot be completed safely.
pub const ERROR_CODE_POSTGRES_TLS_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_tls_failed");
/// Error code used when backend-neutral Store settings violate runtime bounds or invariants.
pub const ERROR_CODE_SETTINGS_INVALID: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "settings_invalid");
/// Error code used when a Store cannot complete its explicit shutdown inside the configured bound.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/lib.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -7,10 +7,9 @@
//! Common backend-neutral Store runtime facade for KSP.
//!
//! `0.3.2-pre.003` materializes Config-independent settings, stable backend
//! identity, bounded validation and the opaque async Store lifecycle contract.
//! Physical PostgreSQL connection, pool, TLS and migrations remain private
//! future slices of this release.
//! `0.3.2-pre.005` materializes the first physical PostgreSQL runtime path:
//! one bounded Deadpool pool, one startup connection proof and explicit TLS
//! policy. SQL migrations remain private future slices of this release.
//!
//! The default `postgres` feature compiles the official PostgreSQL backend as
//! an optional implementation dependency. No backend implementation type is
@@ -27,6 +26,14 @@ pub use self::error::ERROR_CODE_BACKEND_CLOSED;
pub use self::error::ERROR_CODE_BACKEND_NOT_COMPILED;
/// Error code used when a compiled Store backend cannot complete opening.
pub use self::error::ERROR_CODE_BACKEND_OPEN_FAILED;
/// Error code used when PostgreSQL physical configuration is malformed or unsupported.
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 bounded PostgreSQL pool operation reaches its deadline.
pub use self::error::ERROR_CODE_POSTGRES_POOL_TIMEOUT;
/// Error code used when PostgreSQL verified TLS setup or negotiation fails.
pub use self::error::ERROR_CODE_POSTGRES_TLS_FAILED;
/// Error code used when Store settings violate backend-neutral bounds or invariants.
pub use self::error::ERROR_CODE_SETTINGS_INVALID;
/// Error code used when explicit Store shutdown exceeds its configured deadline.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/settings.rs
// version: 2
// version: 3
const DEFAULT_CONNECT_TIMEOUT_MS: u64 = 10_000;
const DEFAULT_MAX_CONNECTIONS: u32 = 8;
@@ -239,6 +239,13 @@ impl PostgresStoreSettings {
return self.bootstrap;
}
/// Returns the sensitive PostgreSQL connection URI only to the compiled backend bridge.
#[cfg(feature = "postgres")]
#[must_use]
pub(crate) fn connection_uri(&self) -> &str {
return self.connection_uri.as_str();
}
/// Returns the PostgreSQL pool settings without exposing the sensitive connection URI.
#[must_use]
pub const fn pool(&self) -> PostgresPoolSettings {

View File

@@ -1,54 +1,155 @@
// file: crates/ksp-store-lib/src/store.rs
// version: 1
// version: 2
/// Opaque common Store runtime facade.
///
/// A successful value is returned only after the selected backend has completed its bounded readiness path. `0.3.2-pre.003` establishes the lifecycle
/// contract but intentionally has no successful opening path until the PostgreSQL runtime foundation is materialized by later prereleases.
#[derive(Debug)]
#[non_exhaustive]
pub struct Store;
/// A successful value is returned only after the selected compiled backend has completed its bounded physical opening path. PostgreSQL pool, client, TLS and
/// driver types remain private to the backend crate.
pub struct Store {
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
#[cfg(feature = "postgres")]
runtime: StoreRuntime,
shutdown_timeout: std::time::Duration,
}
impl Store {
/// Validates settings, selects the requested backend and opens a ready Store runtime.
/// Validates settings, selects the requested backend and opens one ready Store instance for exactly one logical network.
///
/// A known backend whose Cargo feature is absent is rejected before any I/O. During `0.3.2-pre.003`, the compiled PostgreSQL path also stops before I/O
/// with [`crate::ERROR_CODE_BACKEND_OPEN_FAILED`] because physical connection ownership is introduced in `pre.005`.
/// A known backend whose Cargo feature is absent is rejected before any I/O. A successful PostgreSQL result proves that one physical pooled connection has
/// been established under the typed TLS and timeout policy.
pub async fn open(settings: crate::StoreSettings) -> ksp_store_api::Result<Self> {
let validation = settings.validate();
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
let backend_kind = settings.backend_kind();
return match backend_kind {
crate::StoreBackendKind::Postgres => open_postgres_contract(backend_kind).await,
let network = settings.network().clone();
let shutdown_timeout = settings.shutdown_timeout();
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
backend = backend_kind.code(),
network = network.as_str(),
"opening Store runtime"
);
return match settings.backend() {
crate::StoreBackendSettings::Postgres(postgres) => open_postgres(backend_kind, network, shutdown_timeout, postgres).await,
};
}
/// Explicitly closes the Store runtime and consumes its facade handle.
///
/// `pre.003` cannot yet produce a successful Store instance, so the physical bounded shutdown path remains reserved for backend composition. The consuming
/// async signature is fixed here so no pool or backend handle needs to escape later.
/// 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<()> {
return std::result::Result::Ok(());
let backend_kind = self.backend_kind;
#[cfg(feature = "postgres")]
{
let network = self.network;
let shutdown_timeout = self.shutdown_timeout;
let result = match self.runtime {
StoreRuntime::Postgres(backend) => backend.close(shutdown_timeout).await,
};
return match result {
std::result::Result::Ok(()) => {
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
backend = backend_kind.code(),
network = network.as_str(),
"Store runtime closed"
);
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(map_postgres_error(error, backend_kind, network.as_str())),
};
}
#[cfg(not(feature = "postgres"))]
{
return std::result::Result::Err(unavailable_runtime_error(backend_kind));
}
}
}
impl std::fmt::Debug for Store {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("Store")
.field("backend_kind", &self.backend_kind)
.field("network", &self.network)
.field("shutdown_timeout", &self.shutdown_timeout)
.finish_non_exhaustive();
}
}
#[cfg(feature = "postgres")]
async fn open_postgres_contract(backend_kind: crate::StoreBackendKind) -> ksp_store_api::Result<Store> {
return std::result::Result::Err(
ksp_store_api::Error::new(crate::ERROR_CODE_BACKEND_OPEN_FAILED, "PostgreSQL Store runtime opening is not materialized in this prerelease")
.with_context("backend", backend_kind.code())
.with_context("stage", "runtime_foundation_pending"),
enum StoreRuntime {
Postgres(ksp_store_postgres_lib::PostgresBackend),
}
#[cfg(feature = "postgres")]
async fn open_postgres(
backend_kind: crate::StoreBackendKind,
network: ksp_store_api::RawNetworkId,
shutdown_timeout: std::time::Duration,
settings: &crate::PostgresStoreSettings,
) -> ksp_store_api::Result<Store> {
let pool = settings.pool();
let tls_mode = match settings.tls_mode() {
crate::PostgresTlsMode::Disabled => ksp_store_postgres_lib::PostgresBackendTlsMode::Disabled,
crate::PostgresTlsMode::VerifyFull => ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull,
};
let backend_settings = ksp_store_postgres_lib::PostgresBackendSettings::new(
network.clone(),
settings.connection_uri(),
pool.max_connections(),
pool.connect_timeout(),
pool.wait_timeout(),
pool.create_timeout(),
pool.recycle_timeout(),
tls_mode,
);
let opened = ksp_store_postgres_lib::PostgresBackend::open(backend_settings).await;
return match opened {
std::result::Result::Ok(backend) => {
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
backend = backend_kind.code(),
network = network.as_str(),
"Store backend is physically ready"
);
std::result::Result::Ok(Store { backend_kind, network, runtime: StoreRuntime::Postgres(backend), shutdown_timeout })
},
std::result::Result::Err(error) => std::result::Result::Err(map_postgres_error(error, backend_kind, network.as_str())),
};
}
#[cfg(not(feature = "postgres"))]
async fn open_postgres_contract(backend_kind: crate::StoreBackendKind) -> ksp_store_api::Result<Store> {
return std::result::Result::Err(
ksp_store_api::Error::new(crate::ERROR_CODE_BACKEND_NOT_COMPILED, "Selected Store backend is not compiled")
.with_context("backend", backend_kind.code()),
);
async fn open_postgres(
backend_kind: crate::StoreBackendKind,
_network: ksp_store_api::RawNetworkId,
_shutdown_timeout: std::time::Duration,
_settings: &crate::PostgresStoreSettings,
) -> ksp_store_api::Result<Store> {
return std::result::Result::Err(unavailable_runtime_error(backend_kind));
}
#[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() {
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::PoolTimeout => crate::ERROR_CODE_POSTGRES_POOL_TIMEOUT,
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => crate::ERROR_CODE_SHUTDOWN_TIMEOUT,
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"))]
fn unavailable_runtime_error(backend_kind: crate::StoreBackendKind) -> ksp_store_api::Error {
return ksp_store_api::Error::new(crate::ERROR_CODE_BACKEND_NOT_COMPILED, "Selected Store backend is not compiled")
.with_context("backend", backend_kind.code());
}
#[cfg(test)]

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/tests/dependency_boundary.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -8,10 +8,11 @@
//! Cargo-feature and dependency-boundary canaries for the common Store runtime facade.
#[test]
fn pre_002_manifest_owns_default_postgres_feature_and_optional_backend_edge() {
fn pre_005_manifest_keeps_backend_physical_dependencies_out_of_facade() {
let manifest = include_str!("../Cargo.toml");
assert!(manifest.contains("default = [\"postgres\"]"));
assert!(manifest.contains("postgres = [\"dep:ksp-store-postgres-lib\"]"));
assert!(manifest.contains("ksp-logging-lib = { path = \"../ksp-logging-lib\" }"));
assert!(manifest.contains("ksp-store-api = { path = \"../ksp-store-api\" }"));
assert!(manifest.contains("ksp-store-postgres-lib = { path = \"../ksp-store-postgres-lib\", optional = true }"));
for forbidden in [
@@ -30,11 +31,8 @@ fn pre_002_manifest_owns_default_postgres_feature_and_optional_backend_edge() {
}
#[test]
fn pre_003_facade_adds_only_backend_neutral_settings_lifecycle_and_api_reexports() {
fn pre_005_facade_exposes_no_physical_postgres_types_or_environment_bypass() {
let crate_root = include_str!("../src/lib.rs");
assert!(crate_root.contains("mod error;"));
assert!(crate_root.contains("mod settings;"));
assert!(crate_root.contains("mod store;"));
assert!(crate_root.contains("pub use self::settings::StoreSettings;"));
assert!(crate_root.contains("pub use self::store::Store;"));
assert!(crate_root.contains("pub use ksp_store_api::RawTransaction;"));

View File

@@ -1,11 +1,11 @@
// file: crates/ksp-store-lib/tests/feature_mismatch.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
//! Feature-selection canaries for known Store backends.
//! Feature-selection and pre-I/O PostgreSQL failure canaries for the common Store facade.
fn poll_ready<T>(future: impl std::future::Future<Output = T>) -> T {
let mut future = std::boxed::Box::pin(future);
@@ -13,7 +13,7 @@ fn poll_ready<T>(future: impl std::future::Future<Output = T>) -> T {
let mut context = std::task::Context::from_waker(waker);
return match std::future::Future::poll(future.as_mut(), &mut context) {
std::task::Poll::Ready(value) => value,
std::task::Poll::Pending => panic!("pre.003 Store feature-dispatch future unexpectedly became pending before any I/O exists"),
std::task::Poll::Pending => panic!("Store pre-I/O feature/config rejection unexpectedly became pending"),
};
}
@@ -24,9 +24,9 @@ fn valid_network() -> ksp_store_lib::RawNetworkId {
};
}
fn settings() -> ksp_store_lib::StoreSettings {
fn settings(connection_uri: &str) -> ksp_store_lib::StoreSettings {
let postgres = ksp_store_lib::PostgresStoreSettings::new(
"postgresql://operator-supplied-sensitive-value",
connection_uri,
ksp_store_lib::PostgresPoolSettings::default(),
ksp_store_lib::PostgresTlsMode::Disabled,
ksp_store_lib::PostgresBootstrapSettings::default(),
@@ -36,8 +36,8 @@ fn settings() -> ksp_store_lib::StoreSettings {
#[cfg(not(feature = "postgres"))]
#[test]
fn pre_003_known_postgres_without_feature_returns_stable_error_before_io() {
let result = poll_ready(ksp_store_lib::Store::open(settings()));
fn pre_005_known_postgres_without_feature_returns_stable_error_before_io() {
let result = poll_ready(ksp_store_lib::Store::open(settings("postgresql://operator-supplied-sensitive-value@localhost/ksp")));
let error = result.err();
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(ksp_store_lib::ERROR_CODE_BACKEND_NOT_COMPILED));
return;
@@ -45,9 +45,11 @@ fn pre_003_known_postgres_without_feature_returns_stable_error_before_io() {
#[cfg(feature = "postgres")]
#[test]
fn pre_003_compiled_postgres_path_refuses_to_fake_readiness_before_pre_005() {
let result = poll_ready(ksp_store_lib::Store::open(settings()));
fn pre_005_compiled_postgres_rejects_malformed_uri_without_secret_leak_before_io() {
let secret_canary = "not-a-postgresql-uri-secret-canary";
let result = poll_ready(ksp_store_lib::Store::open(settings(secret_canary)));
let error = result.err();
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(ksp_store_lib::ERROR_CODE_BACKEND_OPEN_FAILED));
assert_eq!(error.as_ref().map(|value| return value.code()), std::option::Option::Some(ksp_store_lib::ERROR_CODE_POSTGRES_CONFIG_INVALID));
assert!(!format!("{error:?}").contains(secret_canary));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/tests/public_api.rs
// version: 2
// version: 3
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -29,11 +29,15 @@ fn pre_003_settings_and_lifecycle_contract_are_available_from_crate_root() {
}
#[test]
fn pre_003_common_error_codes_are_stable_and_store_owned() {
fn pre_005_common_and_postgres_error_codes_are_stable_and_store_owned() {
assert_eq!(ksp_store_lib::ERROR_CODE_SETTINGS_INVALID.domain(), "store");
assert_eq!(ksp_store_lib::ERROR_CODE_SETTINGS_INVALID.code(), "settings_invalid");
assert_eq!(ksp_store_lib::ERROR_CODE_BACKEND_NOT_COMPILED.code(), "backend_not_compiled");
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_POOL_TIMEOUT.code(), "postgres_pool_timeout");
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_TLS_FAILED.code(), "postgres_tls_failed");
assert_eq!(ksp_store_lib::ERROR_CODE_BACKEND_CLOSED.code(), "backend_closed");
assert_eq!(ksp_store_lib::ERROR_CODE_SHUTDOWN_TIMEOUT.code(), "shutdown_timeout");
return;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/unit_tests/store.rs
// version: 3
// version: 4
fn poll_ready<T>(future: impl std::future::Future<Output = T>) -> T {
let mut future = std::boxed::Box::pin(future);
@@ -7,7 +7,7 @@ fn poll_ready<T>(future: impl std::future::Future<Output = T>) -> T {
let mut context = std::task::Context::from_waker(waker);
return match std::future::Future::poll(future.as_mut(), &mut context) {
std::task::Poll::Ready(value) => value,
std::task::Poll::Pending => panic!("pre.003 Store contract future unexpectedly became pending before any I/O exists"),
std::task::Poll::Pending => panic!("Store pre-I/O rejection unexpectedly became pending"),
};
}
@@ -18,11 +18,11 @@ fn valid_network() -> crate::RawNetworkId {
};
}
fn valid_store_settings() -> crate::StoreSettings {
fn store_settings(connection_uri: &str) -> crate::StoreSettings {
let postgres = crate::PostgresStoreSettings::new(
"postgresql://secret-user:secret-password@db.internal/ksp",
connection_uri,
crate::PostgresPoolSettings::default(),
crate::PostgresTlsMode::VerifyFull,
crate::PostgresTlsMode::Disabled,
crate::PostgresBootstrapSettings::default(),
);
return crate::StoreSettings::with_default_shutdown(valid_network(), crate::StoreBackendSettings::Postgres(postgres));
@@ -30,14 +30,7 @@ fn valid_store_settings() -> crate::StoreSettings {
#[test]
fn invalid_settings_are_rejected_before_backend_dispatch() {
let postgres = crate::PostgresStoreSettings::new(
std::string::String::new(),
crate::PostgresPoolSettings::default(),
crate::PostgresTlsMode::Disabled,
crate::PostgresBootstrapSettings::default(),
);
let settings = crate::StoreSettings::with_default_shutdown(valid_network(), crate::StoreBackendSettings::Postgres(postgres));
let result = poll_ready(crate::Store::open(settings));
let result = poll_ready(crate::Store::open(store_settings("")));
let error = result.err();
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(crate::ERROR_CODE_SETTINGS_INVALID));
return;
@@ -45,17 +38,19 @@ fn invalid_settings_are_rejected_before_backend_dispatch() {
#[cfg(feature = "postgres")]
#[test]
fn compiled_postgres_dispatch_does_not_fake_readiness_before_connection_materialization() {
let result = poll_ready(crate::Store::open(valid_store_settings()));
fn compiled_postgres_rejects_malformed_physical_configuration_before_io() {
let secret_canary = "not-a-postgresql-uri-secret-canary";
let result = poll_ready(crate::Store::open(store_settings(secret_canary)));
let error = result.err();
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(crate::ERROR_CODE_BACKEND_OPEN_FAILED));
assert_eq!(error.as_ref().map(|value| return value.code()), std::option::Option::Some(crate::ERROR_CODE_POSTGRES_CONFIG_INVALID));
assert!(!format!("{error:?}").contains(secret_canary));
return;
}
#[cfg(not(feature = "postgres"))]
#[test]
fn known_postgres_without_feature_is_rejected_before_io() {
let result = poll_ready(crate::Store::open(valid_store_settings()));
let result = poll_ready(crate::Store::open(store_settings("postgresql://operator-supplied-sensitive-value@localhost/ksp")));
let error = result.err();
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(crate::ERROR_CODE_BACKEND_NOT_COMPILED));
return;