v0.3.2-pre.005
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user