v0.3.3-pre.008
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 355
|
||||
# version: 356
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.3.3-pre.7.fix.1"
|
||||
version = "0.3.3-pre.8"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-store-lib/README.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# ksp-store-lib
|
||||
|
||||
@@ -19,6 +19,8 @@ Elle expose aux consumers une surface backend-neutral, réexporte les contrats R
|
||||
- `Store::health().await` pour la readiness portable et bornée ;
|
||||
- `Store::close(self).await` pour la fermeture explicite bornée ;
|
||||
- le mapping des erreurs backend vers des codes Store stables sans exposer les erreurs physiques ;
|
||||
- les six capabilities `RawTransaction*` dispatchées vers le backend compilé ;
|
||||
- une validation réseau backend-neutral avant dispatch pour toutes les opérations qui portent explicitement un réseau ;
|
||||
- les réexports crate-root de `ksp-store-api` nécessaires aux consumers ordinaires.
|
||||
|
||||
## Une instance = un réseau
|
||||
@@ -73,17 +75,27 @@ Les credentials restent dans les variables `KSP_SECRET_STORE_*_POSTGRES_URI` ou
|
||||
|
||||
## Surface actuelle et hors périmètre
|
||||
|
||||
La fondation runtime ne fournit encore aucune implémentation PostgreSQL des capabilities métier RAW de `ksp-store-api`.
|
||||
Depuis `0.3.3-pre.008`, `Store` implémente les six capabilities transactionnelles acquises dans `ksp-store-api` :
|
||||
|
||||
```text
|
||||
RawTransactionRead
|
||||
RawTransactionWrite
|
||||
RawTransactionObservationRead
|
||||
RawTransactionObservationWrite
|
||||
RawTransactionRetentionRead
|
||||
RawTransactionRetentionWrite
|
||||
```
|
||||
|
||||
Le consumer continue à manipuler uniquement les modèles et outcomes backend-neutral. Les erreurs physiques PostgreSQL sont projetées vers des codes Store stables tels que `store.wrong_network`, `store.raw_reference_not_found`, `store.postgres_read_failed`, `store.postgres_write_failed`, `store.postgres_data_invalid` et `store.postgres_page_limit_unsupported`.
|
||||
|
||||
Sont volontairement hors de cette surface :
|
||||
|
||||
- persistence/query/rétention PostgreSQL de `RawTransaction` ;
|
||||
- persistence/query/rétention PostgreSQL de `RawAccountState` ;
|
||||
- batch-size, priorité, backlog ou policy de worker/job ;
|
||||
- transport d'acquisition, Program decoding et materialization ;
|
||||
- exposition publique de SQL, pool, client, row, statement ou transaction PostgreSQL.
|
||||
|
||||
Les premières vertical slices métier sont ajoutées séparément afin que la façade runtime reste stable et backend-neutral.
|
||||
La vertical slice `RawTransaction` est désormais dispatchée ; `RawAccountState` reste la tranche suivante de `0.3.4` afin que la façade conserve une progression explicite par capability.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-store-lib/USAGE.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Utilisation de ksp-store-lib
|
||||
|
||||
@@ -160,7 +160,8 @@ recycle_timeout()
|
||||
Valeurs par défaut :
|
||||
|
||||
```text
|
||||
auto_migrate true
|
||||
schema_autocreate true
|
||||
schema_autoupdate true
|
||||
migration_timeout 30 s
|
||||
migration_lock_timeout 10 s
|
||||
```
|
||||
@@ -168,11 +169,14 @@ migration_lock_timeout 10 s
|
||||
Getters :
|
||||
|
||||
```text
|
||||
auto_migrate()
|
||||
schema_autocreate()
|
||||
schema_autoupdate()
|
||||
migration_timeout()
|
||||
migration_lock_timeout()
|
||||
```
|
||||
|
||||
Le constructeur de compatibilité `new(..., auto_migrate, ...)` continue de mapper cette valeur sur les deux politiques, mais les nouveaux callers doivent préférer les deux switches séparés.
|
||||
|
||||
### `StoreSettings`
|
||||
|
||||
La surface expose :
|
||||
@@ -212,8 +216,37 @@ runtime snapshot
|
||||
|
||||
Aucun snapshot n'expose URI, host, user, database, SQL, handle backend ou texte d'erreur PostgreSQL.
|
||||
|
||||
## 7. Limite fonctionnelle actuelle
|
||||
## 7. Utiliser les capabilities RawTransaction
|
||||
|
||||
`ksp-store-lib` réexporte les modèles et traits RAW de `ksp-store-api`, mais le backend PostgreSQL de la fondation n'implémente encore aucune capability `RawTransaction*` ou `RawAccount*`.
|
||||
Depuis `0.3.3-pre.008`, `Store` implémente directement les six traits `RawTransaction*`. Le consumer importe le trait correspondant puis appelle la méthode sur la façade :
|
||||
|
||||
Les consumers ne doivent donc pas interpréter la disponibilité du runtime PostgreSQL comme une persistence métier déjà présente.
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionRead;
|
||||
|
||||
async fn read_transaction(
|
||||
store: &ksp_store_lib::Store,
|
||||
reference: &ksp_store_lib::RawTransactionReference,
|
||||
) -> ksp_store_lib::Result<std::option::Option<ksp_store_lib::RawTransaction>> {
|
||||
return store.get_raw_transaction(reference).await;
|
||||
}
|
||||
```
|
||||
|
||||
Le même pattern s'applique à l'écriture, aux observations et à la rétention. Les opérations portant un réseau explicite sont validées contre le réseau du `Store` avant dispatch vers le backend.
|
||||
|
||||
Codes runtime principaux :
|
||||
|
||||
```text
|
||||
store.wrong_network
|
||||
store.raw_reference_not_found
|
||||
store.postgres_read_failed
|
||||
store.postgres_write_failed
|
||||
store.postgres_data_invalid
|
||||
store.postgres_page_limit_unsupported
|
||||
store.postgres_retention_compaction_unsupported
|
||||
```
|
||||
|
||||
Les conflits et queries invalides conservent les codes API acquis `store_api.raw_conflict` et `store_api.raw_query_invalid`.
|
||||
|
||||
## 8. Limite fonctionnelle actuelle
|
||||
|
||||
La façade n'implémente pas encore les capabilities `RawAccount*`. Elles appartiennent à la vertical slice `0.3.4`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/src/error.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
/// 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,14 +11,20 @@ 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 PostgreSQL returns persisted RAW data incompatible with the stable Store contract.
|
||||
pub const ERROR_CODE_POSTGRES_DATA_INVALID: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_data_invalid");
|
||||
/// 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.
|
||||
pub const ERROR_CODE_POSTGRES_MIGRATION_MISMATCH: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_migration_mismatch");
|
||||
/// Error code used when the requested RAW page size exceeds the exact PostgreSQL LIMIT representation boundary.
|
||||
pub const ERROR_CODE_POSTGRES_PAGE_LIMIT_UNSUPPORTED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_page_limit_unsupported");
|
||||
/// 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 a PostgreSQL RAW read fails without exposing SQL, bind values or server text.
|
||||
pub const ERROR_CODE_POSTGRES_READ_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_read_failed");
|
||||
/// Error code used when PostgreSQL cannot represent a requested RAW retention compaction state.
|
||||
pub const ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED: ksp_store_api::ErrorCode =
|
||||
ksp_store_api::ErrorCode::new("store", "postgres_retention_compaction_unsupported");
|
||||
@@ -26,7 +32,13 @@ pub const ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED: ksp_store_api::E
|
||||
pub const ERROR_CODE_POSTGRES_SCHEMA_NEWER: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_schema_newer");
|
||||
/// 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 a PostgreSQL RAW write fails without exposing SQL, bind values or server text.
|
||||
pub const ERROR_CODE_POSTGRES_WRITE_FAILED: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "postgres_write_failed");
|
||||
/// Error code used when a RAW write requires a canonical reference that is not durable.
|
||||
pub const ERROR_CODE_RAW_REFERENCE_NOT_FOUND: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "raw_reference_not_found");
|
||||
/// 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.
|
||||
pub const ERROR_CODE_SHUTDOWN_TIMEOUT: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "shutdown_timeout");
|
||||
/// Error code used when a network-scoped Store operation targets a network different from the opened Store binding.
|
||||
pub const ERROR_CODE_WRONG_NETWORK: ksp_store_api::ErrorCode = ksp_store_api::ErrorCode::new("store", "wrong_network");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/src/lib.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
//! Common backend-neutral Store runtime facade for KSP.
|
||||
//!
|
||||
//! `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 runtime facade owns backend selection, lifecycle, safe diagnostics and
|
||||
//! backend-neutral capability dispatch. `0.3.3-pre.008` completes the PostgreSQL
|
||||
//! `RawTransaction` vertical slice by implementing the six transaction capabilities
|
||||
//! on both the physical backend and this common facade without exposing physical types.
|
||||
//!
|
||||
//! The default `postgres` feature compiles the official PostgreSQL backend as
|
||||
//! an optional implementation dependency. No backend implementation type is
|
||||
@@ -32,24 +32,36 @@ 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 PostgreSQL returns persisted RAW data incompatible with the Store contract.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_DATA_INVALID;
|
||||
/// 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.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_MIGRATION_MISMATCH;
|
||||
/// Error code used when a requested RAW page size exceeds PostgreSQL's exact physical LIMIT boundary.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_PAGE_LIMIT_UNSUPPORTED;
|
||||
/// 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 a PostgreSQL RAW read statement fails safely.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_READ_FAILED;
|
||||
/// Error code used when PostgreSQL cannot represent a requested RAW retention compaction state.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED;
|
||||
/// Error code used when PostgreSQL schema history is newer than this Store runtime.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_SCHEMA_NEWER;
|
||||
/// Error code used when PostgreSQL verified TLS setup or negotiation fails.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_TLS_FAILED;
|
||||
/// Error code used when a PostgreSQL RAW write statement or transaction fails safely.
|
||||
pub use self::error::ERROR_CODE_POSTGRES_WRITE_FAILED;
|
||||
/// Error code used when a RAW write requires a canonical reference that is not durable.
|
||||
pub use self::error::ERROR_CODE_RAW_REFERENCE_NOT_FOUND;
|
||||
/// 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.
|
||||
pub use self::error::ERROR_CODE_SHUTDOWN_TIMEOUT;
|
||||
/// Error code used when a network-scoped operation targets a network different from the Store binding.
|
||||
pub use self::error::ERROR_CODE_WRONG_NETWORK;
|
||||
/// Portable Store health/readiness projection containing only safe diagnostics.
|
||||
pub use self::health::StoreHealthSnapshot;
|
||||
/// Portable Store health state independent from physical backend types.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/src/store.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
/// Opaque common Store runtime facade.
|
||||
///
|
||||
@@ -116,6 +116,261 @@ impl std::fmt::Debug for Store {
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRead for Store {
|
||||
fn get_raw_transaction<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransaction>>> {
|
||||
let network_check = validate_operation_network(&self.network, reference.network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.get_raw_transaction(reference).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = reference;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn list_raw_transactions<'a>(
|
||||
&'a self,
|
||||
query: &'a ksp_store_api::RawTransactionQuery,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawPage<ksp_store_api::RawTransactionReference>>> {
|
||||
let network_check = validate_operation_network(&self.network, query.network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.list_raw_transactions(query).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = query;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionWrite for Store {
|
||||
fn persist_raw_transaction_acquisition<'a>(
|
||||
&'a self,
|
||||
transaction: ksp_store_api::RawTransaction,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
mode: ksp_store_api::RawTransactionAcquisitionMode,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawAcquisitionWriteOutcome>> {
|
||||
let transaction_network = validate_operation_network(&self.network, transaction.reference().network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = transaction_network {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
let observation_network = validate_operation_network(&self.network, observation.transaction().network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = observation_network {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.persist_raw_transaction_acquisition(transaction, observation, mode).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = transaction;
|
||||
let _ = observation;
|
||||
let _ = mode;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionObservationRead for Store {
|
||||
fn get_raw_transaction_observation<'a>(
|
||||
&'a self,
|
||||
observation_key: &'a ksp_store_api::RawObservationKey,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransactionObservation>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.get_raw_transaction_observation(observation_key).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = observation_key;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionObservationWrite for Store {
|
||||
fn record_raw_transaction_observation<'a>(
|
||||
&'a self,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawObservationWriteOutcome>> {
|
||||
let network_check = validate_operation_network(&self.network, observation.transaction().network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.record_raw_transaction_observation(observation).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = observation;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRetentionRead for Store {
|
||||
fn get_raw_transaction_retention_state<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawRetentionState>>> {
|
||||
let network_check = validate_operation_network(&self.network, reference.network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.get_raw_transaction_retention_state(reference).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = reference;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_raw_transaction_tombstone<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransactionTombstone>>> {
|
||||
let network_check = validate_operation_network(&self.network, reference.network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.get_raw_transaction_tombstone(reference).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = reference;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRetentionWrite for Store {
|
||||
fn transition_raw_transaction_retention<'a>(
|
||||
&'a self,
|
||||
transition: ksp_store_api::RawTransactionRetentionTransition,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawRetentionWriteOutcome>> {
|
||||
let network_check = validate_operation_network(&self.network, transition.reference().network(), self.backend_kind);
|
||||
if let std::result::Result::Err(error) = network_check {
|
||||
return std::boxed::Box::pin(async move {
|
||||
return std::result::Result::Err(error);
|
||||
});
|
||||
}
|
||||
return std::boxed::Box::pin(async move {
|
||||
#[cfg(feature = "postgres")]
|
||||
{
|
||||
return match &self.runtime {
|
||||
StoreRuntime::Postgres(backend) => {
|
||||
let result = backend.transition_raw_transaction_retention(transition).await;
|
||||
result.map_err(|error| return map_postgres_error(error, self.backend_kind, self.network.as_str()))
|
||||
},
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
{
|
||||
let _ = transition;
|
||||
return std::result::Result::Err(unavailable_runtime_error(self.backend_kind));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_operation_network(
|
||||
store_network: &ksp_store_api::RawNetworkId,
|
||||
operation_network: &ksp_store_api::RawNetworkId,
|
||||
backend_kind: crate::StoreBackendKind,
|
||||
) -> ksp_store_api::Result<()> {
|
||||
if store_network != operation_network {
|
||||
return std::result::Result::Err(
|
||||
ksp_store_api::Error::new(crate::ERROR_CODE_WRONG_NETWORK, "Store operation targeted a different logical network")
|
||||
.with_context("backend", backend_kind.code())
|
||||
.with_context("network", store_network.as_str()),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
enum StoreRuntime {
|
||||
Postgres(ksp_store_postgres_lib::PostgresBackend),
|
||||
@@ -176,7 +431,7 @@ 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 = postgres_error_code(error.kind());
|
||||
return ksp_store_api::Error::new(code, "PostgreSQL Store backend lifecycle operation failed")
|
||||
return ksp_store_api::Error::new(code, "PostgreSQL Store backend operation failed")
|
||||
.with_context("backend", backend_kind.code())
|
||||
.with_context("network", network)
|
||||
.with_context("phase", error.phase());
|
||||
@@ -215,13 +470,22 @@ fn postgres_error_code(kind: ksp_store_postgres_lib::PostgresBackendErrorKind) -
|
||||
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::Conflict => ksp_store_api::ERROR_CODE_RAW_CONFLICT,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::DataInvalid => crate::ERROR_CODE_POSTGRES_DATA_INVALID,
|
||||
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,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::PageLimitUnsupported => crate::ERROR_CODE_POSTGRES_PAGE_LIMIT_UNSUPPORTED,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::QueryInvalid => ksp_store_api::ERROR_CODE_RAW_QUERY_INVALID,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReadFailed => crate::ERROR_CODE_POSTGRES_READ_FAILED,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReferenceNotFound => crate::ERROR_CODE_RAW_REFERENCE_NOT_FOUND,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::RetentionCompactionUnsupported => crate::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer => crate::ERROR_CODE_POSTGRES_SCHEMA_NEWER,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => crate::ERROR_CODE_SHUTDOWN_TIMEOUT,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => crate::ERROR_CODE_POSTGRES_TLS_FAILED,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WriteFailed => crate::ERROR_CODE_POSTGRES_WRITE_FAILED,
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WrongNetwork => crate::ERROR_CODE_WRONG_NETWORK,
|
||||
_ => crate::ERROR_CODE_BACKEND_OPEN_FAILED,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/tests/dependency_boundary.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -58,3 +58,36 @@ fn pre_005_facade_exposes_no_physical_postgres_types_or_environment_bypass() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_facade_dispatches_six_raw_transaction_capabilities_without_physical_leak() {
|
||||
let store = include_str!("../src/store.rs");
|
||||
for required in [
|
||||
"impl ksp_store_api::RawTransactionRead for Store",
|
||||
"impl ksp_store_api::RawTransactionWrite for Store",
|
||||
"impl ksp_store_api::RawTransactionObservationRead for Store",
|
||||
"impl ksp_store_api::RawTransactionObservationWrite for Store",
|
||||
"impl ksp_store_api::RawTransactionRetentionRead for Store",
|
||||
"impl ksp_store_api::RawTransactionRetentionWrite for Store",
|
||||
"validate_operation_network",
|
||||
"StoreRuntime::Postgres(backend)",
|
||||
"map_postgres_error",
|
||||
] {
|
||||
assert!(store.contains(required), "missing pre.008 Store capability dispatch contract: {required}");
|
||||
}
|
||||
for forbidden in [
|
||||
"impl ksp_store_api::RawAccountStateRead for Store",
|
||||
"impl ksp_store_api::RawAccountStateWrite for Store",
|
||||
"impl ksp_store_api::RawAccountObservationRead for Store",
|
||||
"impl ksp_store_api::RawAccountObservationWrite for Store",
|
||||
"tokio_postgres::",
|
||||
"deadpool_postgres::",
|
||||
"CREATE TABLE",
|
||||
"INSERT INTO",
|
||||
"UPDATE ksp_",
|
||||
"DELETE FROM",
|
||||
] {
|
||||
assert!(!store.contains(forbidden), "pre.008 facade leaked physical or RawAccount scope: {forbidden}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/tests/hardening_completeness.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -98,21 +98,27 @@ fn pre_009_facade_modules_and_crate_root_exports_are_exact() {
|
||||
"ERROR_CODE_BACKEND_OPEN_FAILED",
|
||||
"ERROR_CODE_POSTGRES_CONFIG_INVALID",
|
||||
"ERROR_CODE_POSTGRES_CONNECT_FAILED",
|
||||
"ERROR_CODE_POSTGRES_DATA_INVALID",
|
||||
"ERROR_CODE_POSTGRES_HEALTH_FAILED",
|
||||
"ERROR_CODE_POSTGRES_MIGRATION_FAILED",
|
||||
"ERROR_CODE_POSTGRES_MIGRATION_MISMATCH",
|
||||
"ERROR_CODE_POSTGRES_PAGE_LIMIT_UNSUPPORTED",
|
||||
"ERROR_CODE_POSTGRES_POOL_TIMEOUT",
|
||||
"ERROR_CODE_POSTGRES_READ_FAILED",
|
||||
"ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED",
|
||||
"ERROR_CODE_POSTGRES_SCHEMA_NEWER",
|
||||
"ERROR_CODE_POSTGRES_TLS_FAILED",
|
||||
"ERROR_CODE_POSTGRES_WRITE_FAILED",
|
||||
"ERROR_CODE_RAW_CONFLICT",
|
||||
"ERROR_CODE_RAW_MODEL_INVALID",
|
||||
"ERROR_CODE_RAW_PAYLOAD_INVALID",
|
||||
"ERROR_CODE_RAW_PROVENANCE_INVALID",
|
||||
"ERROR_CODE_RAW_QUERY_INVALID",
|
||||
"ERROR_CODE_RAW_REFERENCE_NOT_FOUND",
|
||||
"ERROR_CODE_RAW_RETENTION_INVALID",
|
||||
"ERROR_CODE_SETTINGS_INVALID",
|
||||
"ERROR_CODE_SHUTDOWN_TIMEOUT",
|
||||
"ERROR_CODE_WRONG_NETWORK",
|
||||
"Error",
|
||||
"ErrorCode",
|
||||
"ErrorContext",
|
||||
@@ -181,7 +187,7 @@ fn pre_009_facade_modules_and_crate_root_exports_are_exact() {
|
||||
];
|
||||
expected.sort_unstable();
|
||||
assert_eq!(actual.as_slice(), expected.as_slice());
|
||||
assert_eq!(actual.len(), 85);
|
||||
assert_eq!(actual.len(), 91);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/tests/public_api.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -38,15 +38,21 @@ 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_DATA_INVALID.code(), "postgres_data_invalid");
|
||||
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_READ_FAILED.code(), "postgres_read_failed");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED.code(), "postgres_retention_compaction_unsupported");
|
||||
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");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_PAGE_LIMIT_UNSUPPORTED.code(), "postgres_page_limit_unsupported");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_SCHEMA_NEWER.code(), "postgres_schema_newer");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_TLS_FAILED.code(), "postgres_tls_failed");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_POSTGRES_WRITE_FAILED.code(), "postgres_write_failed");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_RAW_REFERENCE_NOT_FOUND.code(), "raw_reference_not_found");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_BACKEND_CLOSED.code(), "backend_closed");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_SHUTDOWN_TIMEOUT.code(), "shutdown_timeout");
|
||||
assert_eq!(ksp_store_lib::ERROR_CODE_WRONG_NETWORK.code(), "wrong_network");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,3 +73,21 @@ fn pre_007_health_and_runtime_snapshot_types_are_portable_crate_root_contracts()
|
||||
let _runtime = std::mem::size_of::<std::option::Option<ksp_store_lib::StoreRuntimeSnapshot>>();
|
||||
return;
|
||||
}
|
||||
|
||||
fn assert_raw_transaction_capabilities<T>()
|
||||
where
|
||||
T: ksp_store_lib::RawTransactionRead
|
||||
+ ksp_store_lib::RawTransactionWrite
|
||||
+ ksp_store_lib::RawTransactionObservationRead
|
||||
+ ksp_store_lib::RawTransactionObservationWrite
|
||||
+ ksp_store_lib::RawTransactionRetentionRead
|
||||
+ ksp_store_lib::RawTransactionRetentionWrite,
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_store_facade_implements_all_six_raw_transaction_capabilities() {
|
||||
assert_raw_transaction_capabilities::<ksp_store_lib::Store>();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-lib/unit_tests/store.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
fn poll_ready<T>(future: impl std::future::Future<Output = T>) -> T {
|
||||
let mut future = std::boxed::Box::pin(future);
|
||||
@@ -55,3 +55,20 @@ fn known_postgres_without_feature_is_rejected_before_io() {
|
||||
assert_eq!(error.map(|value| return value.code()), std::option::Option::Some(crate::ERROR_CODE_BACKEND_NOT_COMPILED));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_operation_network_guard_rejects_mismatch_without_echoing_requested_network() {
|
||||
let store_network = valid_network();
|
||||
let hostile = match crate::RawNetworkId::new("other-network") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("valid alternate network rejected: {error:?}"),
|
||||
};
|
||||
let result = super::validate_operation_network(&store_network, &hostile, crate::StoreBackendKind::Postgres);
|
||||
let error = match result {
|
||||
std::result::Result::Err(value) => value,
|
||||
std::result::Result::Ok(()) => panic!("wrong operation network unexpectedly accepted"),
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_WRONG_NETWORK);
|
||||
assert!(!std::format!("{error:?}").contains("other-network"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-store-postgres-lib/README.md -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# ksp-store-postgres-lib
|
||||
|
||||
@@ -83,7 +83,7 @@ ksp_store_schema_migrations
|
||||
|
||||
Le moteur vérifie version, nom et checksum SHA-256, sérialise les runners par advisory transaction lock et refuse une history divergente ou plus récente que le runtime.
|
||||
|
||||
V001 possède désormais le schéma physique `RawTransaction` et son contrat de compatibilité. Les lectures exactes sont acquises depuis `pre.004`; `pre.005` ajoute les écritures atomiques transaction + observation, l'idempotence réelle et la classification de conflit. `pre.006` ajoute la navigation keyset déterministe sur l'index `(slot, signature)` et son cursor opaque lié à la requête. `pre.007` ajoute les transitions de rétention atomiques `Full -> Archived -> Purged` et le rejet explicite de `Compacted` tant qu'aucune représentation compacte réelle n'existe.
|
||||
V001 possède désormais le schéma physique `RawTransaction` et son contrat de compatibilité. Les lectures exactes sont acquises depuis `pre.004`; `pre.005` ajoute les écritures atomiques transaction + observation, l'idempotence réelle et la classification de conflit. `pre.006` ajoute la navigation keyset déterministe sur l'index `(slot, signature)` et son cursor opaque lié à la requête. `pre.007` ajoute les transitions de rétention atomiques `Full -> Archived -> Purged` et le rejet explicite de `Compacted`. `pre.008` ferme la conformance backend en implémentant les six traits `RawTransaction*` directement sur `PostgresBackend`.
|
||||
|
||||
## Health et erreurs
|
||||
|
||||
@@ -165,7 +165,7 @@ Toute transition dont `expected` ou `target` vaut `Compacted` est rejetée avant
|
||||
|
||||
La crate ne contient encore :
|
||||
|
||||
- aucune implémentation complète des six traits `RawTransaction*` de `ksp-store-api` ;
|
||||
- aucune capability `RawAccount*` ;
|
||||
- aucun dispatch métier dans `ksp-store-lib` ;
|
||||
- aucune implémentation PostgreSQL des capabilities `RawAccount*` ;
|
||||
- aucune orchestration worker/job ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-store-postgres-lib/USAGE.md -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# Utilisation de ksp-store-postgres-lib
|
||||
|
||||
@@ -219,14 +219,21 @@ Les transitions physiques supportées sont `Full -> Archived` puis `Archived ->
|
||||
|
||||
Une transition impliquant `Compacted` est rejetée avant acquisition du pool avec `PostgresBackendErrorKind::RetentionCompactionUnsupported`. Le code KSP correspondant est `ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED`; aucune compression transparente PostgreSQL n'est présentée comme une représentation compactée KSP.
|
||||
|
||||
## 11. Ce que cette crate ne permet pas encore
|
||||
## 11. Capabilities traitées directement
|
||||
|
||||
La tranche ne fournit pas encore :
|
||||
Depuis `0.3.3-pre.008`, `PostgresBackend` implémente directement :
|
||||
|
||||
```text
|
||||
implémentations complètes des six traits RawTransaction*
|
||||
dispatch métier ksp-store-lib
|
||||
capabilities RawAccount*
|
||||
RawTransactionRead
|
||||
RawTransactionWrite
|
||||
RawTransactionObservationRead
|
||||
RawTransactionObservationWrite
|
||||
RawTransactionRetentionRead
|
||||
RawTransactionRetentionWrite
|
||||
```
|
||||
|
||||
Ces surfaces sont ajoutées dans les prereleases suivantes avant le dispatch `ksp-store-lib`.
|
||||
Cette conformance est principalement utile aux tests backend et à la façade. Le consumer applicatif normal continue à dépendre de `ksp-store-lib`, qui dispatch les mêmes six capabilities sans exposer `PostgresBackend`.
|
||||
|
||||
## 12. Ce que cette crate ne permet pas encore
|
||||
|
||||
La tranche ne fournit pas les capabilities `RawAccount*`. Elles appartiennent à `0.3.4`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/src/lib.rs
|
||||
// version: 13
|
||||
// version: 14
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -19,6 +19,8 @@
|
||||
//! pagination with a fixed opaque cursor bound to network, range and direction.
|
||||
//! `0.3.3-pre.007` adds atomic `Full -> Archived -> Purged` retention transitions
|
||||
//! with compare-and-transition outcomes and explicit rejection of `Compacted`.
|
||||
//! `0.3.3-pre.008` implements all six `RawTransaction*` capabilities directly on
|
||||
//! `PostgresBackend` while preserving the existing narrow backend bridge.
|
||||
//!
|
||||
//! 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/src/runtime.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
const APPLICATION_NAME: &str = "ksp-store";
|
||||
const MAX_CONNECTION_URI_BYTES: usize = 4_096;
|
||||
@@ -428,6 +428,126 @@ impl std::fmt::Debug for PostgresBackend {
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRead for PostgresBackend {
|
||||
fn get_raw_transaction<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransaction>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::get_raw_transaction(self, reference).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
|
||||
fn list_raw_transactions<'a>(
|
||||
&'a self,
|
||||
query: &'a ksp_store_api::RawTransactionQuery,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawPage<ksp_store_api::RawTransactionReference>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::list_raw_transactions(self, query).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionWrite for PostgresBackend {
|
||||
fn persist_raw_transaction_acquisition<'a>(
|
||||
&'a self,
|
||||
transaction: ksp_store_api::RawTransaction,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
mode: ksp_store_api::RawTransactionAcquisitionMode,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawAcquisitionWriteOutcome>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::persist_raw_transaction_acquisition(self, transaction, observation, mode).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionObservationRead for PostgresBackend {
|
||||
fn get_raw_transaction_observation<'a>(
|
||||
&'a self,
|
||||
observation_key: &'a ksp_store_api::RawObservationKey,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransactionObservation>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::get_raw_transaction_observation(self, observation_key).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend {
|
||||
fn record_raw_transaction_observation<'a>(
|
||||
&'a self,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawObservationWriteOutcome>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::record_raw_transaction_observation(self, observation).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRetentionRead for PostgresBackend {
|
||||
fn get_raw_transaction_retention_state<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawRetentionState>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::get_raw_transaction_retention_state(self, reference).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
|
||||
fn get_raw_transaction_tombstone<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransactionTombstone>>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::get_raw_transaction_tombstone(self, reference).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_store_api::RawTransactionRetentionWrite for PostgresBackend {
|
||||
fn transition_raw_transaction_retention<'a>(
|
||||
&'a self,
|
||||
transition: ksp_store_api::RawTransactionRetentionTransition,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawRetentionWriteOutcome>> {
|
||||
return std::boxed::Box::pin(async move {
|
||||
let result = PostgresBackend::transition_raw_transaction_retention(self, transition).await;
|
||||
return result.map_err(map_capability_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn map_capability_error(error: crate::PostgresBackendError) -> ksp_store_api::Error {
|
||||
let code = match error.kind() {
|
||||
crate::PostgresBackendErrorKind::ConfigInvalid => ksp_store_api::ErrorCode::new("store", "postgres_config_invalid"),
|
||||
crate::PostgresBackendErrorKind::ConnectFailed => ksp_store_api::ErrorCode::new("store", "postgres_connect_failed"),
|
||||
crate::PostgresBackendErrorKind::Conflict => ksp_store_api::ERROR_CODE_RAW_CONFLICT,
|
||||
crate::PostgresBackendErrorKind::DataInvalid => ksp_store_api::ErrorCode::new("store", "postgres_data_invalid"),
|
||||
crate::PostgresBackendErrorKind::HealthFailed => ksp_store_api::ErrorCode::new("store", "postgres_health_failed"),
|
||||
crate::PostgresBackendErrorKind::MigrationFailed => ksp_store_api::ErrorCode::new("store", "postgres_migration_failed"),
|
||||
crate::PostgresBackendErrorKind::MigrationMismatch => ksp_store_api::ErrorCode::new("store", "postgres_migration_mismatch"),
|
||||
crate::PostgresBackendErrorKind::PageLimitUnsupported => ksp_store_api::ErrorCode::new("store", "postgres_page_limit_unsupported"),
|
||||
crate::PostgresBackendErrorKind::PoolTimeout => ksp_store_api::ErrorCode::new("store", "postgres_pool_timeout"),
|
||||
crate::PostgresBackendErrorKind::QueryInvalid => ksp_store_api::ERROR_CODE_RAW_QUERY_INVALID,
|
||||
crate::PostgresBackendErrorKind::ReadFailed => ksp_store_api::ErrorCode::new("store", "postgres_read_failed"),
|
||||
crate::PostgresBackendErrorKind::ReferenceNotFound => ksp_store_api::ErrorCode::new("store", "raw_reference_not_found"),
|
||||
crate::PostgresBackendErrorKind::RetentionCompactionUnsupported => crate::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED,
|
||||
crate::PostgresBackendErrorKind::SchemaNewer => ksp_store_api::ErrorCode::new("store", "postgres_schema_newer"),
|
||||
crate::PostgresBackendErrorKind::ShutdownTimeout => ksp_store_api::ErrorCode::new("store", "shutdown_timeout"),
|
||||
crate::PostgresBackendErrorKind::TlsFailed => ksp_store_api::ErrorCode::new("store", "postgres_tls_failed"),
|
||||
crate::PostgresBackendErrorKind::WriteFailed => ksp_store_api::ErrorCode::new("store", "postgres_write_failed"),
|
||||
crate::PostgresBackendErrorKind::WrongNetwork => ksp_store_api::ErrorCode::new("store", "wrong_network"),
|
||||
};
|
||||
return ksp_store_api::Error::new(code, "PostgreSQL Store capability operation failed")
|
||||
.with_context("backend", "postgres")
|
||||
.with_context("phase", error.phase());
|
||||
}
|
||||
|
||||
impl std::ops::Drop for PostgresBackend {
|
||||
fn drop(&mut self) {
|
||||
self.pool.close();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
|
||||
// version: 13
|
||||
// version: 14
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -248,3 +248,27 @@ fn pre_007_raw_retention_is_atomic_compare_and_transition_without_fake_compactio
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_backend_trait_implementations_stay_in_runtime_bridge_and_raw_account_scope_stays_closed() {
|
||||
let runtime = include_str!("../src/runtime.rs");
|
||||
for required in [
|
||||
"impl ksp_store_api::RawTransactionRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionRetentionRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionRetentionWrite for PostgresBackend",
|
||||
] {
|
||||
assert!(runtime.contains(required), "missing pre.008 PostgreSQL capability implementation: {required}");
|
||||
}
|
||||
for forbidden in [
|
||||
"impl ksp_store_api::RawAccountStateRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountStateWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountObservationWrite for PostgresBackend",
|
||||
] {
|
||||
assert!(!runtime.contains(forbidden), "pre.008 opened RawAccount capability scope prematurely: {forbidden}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -183,7 +183,7 @@ fn pre_009_backend_error_bridge_cannot_retain_external_error_or_secret_text() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_009_backend_has_no_env_bypass_or_direct_store_trait_implementation() {
|
||||
fn pre_009_backend_has_no_env_bypass_reverse_facade_edge_or_raw_account_trait_implementation() {
|
||||
let production = std::format!(
|
||||
"{}
|
||||
{}
|
||||
@@ -219,10 +219,9 @@ fn pre_009_backend_has_no_env_bypass_or_direct_store_trait_implementation() {
|
||||
"ksp_store_lib",
|
||||
"ksp_config_lib",
|
||||
"sqlx::",
|
||||
"impl ksp_store_api::RawTransaction",
|
||||
"impl ksp_store_api::RawAccount",
|
||||
] {
|
||||
assert!(!production.contains(forbidden), "forbidden backend ownership/direct-trait material detected: {forbidden}");
|
||||
assert!(!production.contains(forbidden), "forbidden backend ownership/reverse-edge/RawAccount material detected: {forbidden}");
|
||||
}
|
||||
let bootstrap_sql = include_str!("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql");
|
||||
assert!(bootstrap_sql.contains("ksp_store_schema_migrations"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/public_api.rs
|
||||
// version: 8
|
||||
// version: 9
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -102,3 +102,21 @@ fn pre_007_raw_retention_write_bridge_uses_backend_independent_transition_and_ou
|
||||
let _transition = ksp_store_postgres_lib::PostgresBackend::transition_raw_transaction_retention;
|
||||
return;
|
||||
}
|
||||
|
||||
fn assert_raw_transaction_capabilities<T>()
|
||||
where
|
||||
T: ksp_store_api::RawTransactionRead
|
||||
+ ksp_store_api::RawTransactionWrite
|
||||
+ ksp_store_api::RawTransactionObservationRead
|
||||
+ ksp_store_api::RawTransactionObservationWrite
|
||||
+ ksp_store_api::RawTransactionRetentionRead
|
||||
+ ksp_store_api::RawTransactionRetentionWrite,
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_postgres_backend_implements_all_six_raw_transaction_capabilities() {
|
||||
assert_raw_transaction_capabilities::<ksp_store_postgres_lib::PostgresBackend>();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/unit_tests/runtime.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
fn network() -> ksp_store_api::RawNetworkId {
|
||||
return match ksp_store_api::RawNetworkId::new("devnet") {
|
||||
@@ -100,3 +100,27 @@ fn libpq_server_options_are_rejected_in_foundation_runtime() {
|
||||
assert_eq!(error.map(|value| return value.phase()), std::option::Option::Some("server_options"));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_capability_error_mapping_uses_stable_store_and_store_api_codes() {
|
||||
let cases = [
|
||||
(crate::PostgresBackendErrorKind::Conflict, ksp_store_api::ERROR_CODE_RAW_CONFLICT),
|
||||
(crate::PostgresBackendErrorKind::DataInvalid, ksp_store_api::ErrorCode::new("store", "postgres_data_invalid")),
|
||||
(crate::PostgresBackendErrorKind::PageLimitUnsupported, ksp_store_api::ErrorCode::new("store", "postgres_page_limit_unsupported")),
|
||||
(crate::PostgresBackendErrorKind::QueryInvalid, ksp_store_api::ERROR_CODE_RAW_QUERY_INVALID),
|
||||
(crate::PostgresBackendErrorKind::ReadFailed, ksp_store_api::ErrorCode::new("store", "postgres_read_failed")),
|
||||
(crate::PostgresBackendErrorKind::ReferenceNotFound, ksp_store_api::ErrorCode::new("store", "raw_reference_not_found")),
|
||||
(crate::PostgresBackendErrorKind::RetentionCompactionUnsupported, crate::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED),
|
||||
(crate::PostgresBackendErrorKind::WriteFailed, ksp_store_api::ErrorCode::new("store", "postgres_write_failed")),
|
||||
(crate::PostgresBackendErrorKind::WrongNetwork, ksp_store_api::ErrorCode::new("store", "wrong_network")),
|
||||
];
|
||||
for (kind, expected) in cases {
|
||||
let backend = crate::PostgresBackendError::new(kind, "pre_008_canary");
|
||||
let mapped = super::map_capability_error(backend);
|
||||
assert_eq!(mapped.code(), expected);
|
||||
let rendered = std::format!("{mapped:?}");
|
||||
assert!(!rendered.contains("postgresql://"));
|
||||
assert!(!rendered.contains("SELECT "));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
135
deltas/0.3.3/pre.008.md
Normal file
135
deltas/0.3.3/pre.008.md
Normal file
@@ -0,0 +1,135 @@
|
||||
<!-- file: deltas/0.3.3/pre.008.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.3.3-pre.008` — conformance capabilities et dispatch Store
|
||||
|
||||
## 1. Base et gate d'entrée
|
||||
|
||||
Base opérateur obligatoire :
|
||||
|
||||
```text
|
||||
0.3.3-pre.7.fix.1
|
||||
```
|
||||
|
||||
Le gate opérateur fourni le 2026-08-30 est entièrement vert : audits Rust/Markdown, workspace check, Clippy, tests Store API/façade/PostgreSQL/Config et `ksp-store-lib --no-default-features` passent. `pre.007` est donc acquise.
|
||||
|
||||
## 2. Version
|
||||
|
||||
```text
|
||||
workspace.package.version = 0.3.3-pre.8
|
||||
```
|
||||
|
||||
## 3. Six capabilities sur `PostgresBackend`
|
||||
|
||||
`PostgresBackend` implémente désormais directement :
|
||||
|
||||
```text
|
||||
RawTransactionRead
|
||||
RawTransactionWrite
|
||||
RawTransactionObservationRead
|
||||
RawTransactionObservationWrite
|
||||
RawTransactionRetentionRead
|
||||
RawTransactionRetentionWrite
|
||||
```
|
||||
|
||||
Les implémentations adaptent les méthodes physiques déjà acquises en `pre.004` à `pre.007` vers `StoreApiFuture` et `ksp_store_api::Result`. Aucun SQL n'est déplacé hors du backend et aucune capability `RawAccount*` n'est ouverte.
|
||||
|
||||
## 4. Six dispatches sur `Store`
|
||||
|
||||
`Store` implémente les mêmes six traits et dispatch vers `StoreRuntime::Postgres` lorsque la feature `postgres` est compilée. Aucun type `PostgresBackend`, pool, client, row, statement ou SQL n'entre dans la surface publique de `ksp-store-lib`.
|
||||
|
||||
Les consumers ordinaires continuent donc à dépendre uniquement de `ksp-store-lib`.
|
||||
|
||||
## 5. Validation réseau pré-I/O
|
||||
|
||||
La façade valide avant dispatch toutes les opérations dont l'input porte un réseau :
|
||||
|
||||
```text
|
||||
get/list transaction
|
||||
persist acquisition
|
||||
record observation
|
||||
retention read
|
||||
retention transition
|
||||
```
|
||||
|
||||
Une divergence retourne `store.wrong_network` sans rendre le réseau hostile. `RawTransactionObservationRead` reçoit uniquement une `RawObservationKey`; son scope réseau est celui de l'instance Store mono-réseau.
|
||||
|
||||
## 6. Taxonomie d'erreurs stabilisée
|
||||
|
||||
```text
|
||||
Conflict -> store_api.raw_conflict
|
||||
QueryInvalid -> store_api.raw_query_invalid
|
||||
WrongNetwork -> store.wrong_network
|
||||
ReferenceNotFound -> store.raw_reference_not_found
|
||||
ReadFailed -> store.postgres_read_failed
|
||||
WriteFailed -> store.postgres_write_failed
|
||||
DataInvalid -> store.postgres_data_invalid
|
||||
PageLimitUnsupported -> store.postgres_page_limit_unsupported
|
||||
RetentionCompactionUnsupported -> store.postgres_retention_compaction_unsupported
|
||||
```
|
||||
|
||||
Le mapping conserve seulement backend, réseau propre de l'instance et phase statique. Aucun SQLSTATE, SQL, bind ou texte serveur n'est exposé.
|
||||
|
||||
## 7. Feature mismatch
|
||||
|
||||
Le contrat `default = ["postgres"]` reste inchangé. Sans default feature, `Store::open` conserve le rejet pré-I/O `store.backend_not_compiled`; le gate `cargo check -p ksp-store-lib --no-default-features` reste obligatoire.
|
||||
|
||||
## 8. Migrations
|
||||
|
||||
Aucune ressource V000/V001 n'est modifiée. Checksums attendus :
|
||||
|
||||
```text
|
||||
V000 d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
|
||||
V001 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51
|
||||
```
|
||||
|
||||
## 9. Fichiers modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
crates/ksp-store-lib/README.md
|
||||
crates/ksp-store-lib/USAGE.md
|
||||
crates/ksp-store-lib/src/error.rs
|
||||
crates/ksp-store-lib/src/lib.rs
|
||||
crates/ksp-store-lib/src/store.rs
|
||||
crates/ksp-store-lib/tests/dependency_boundary.rs
|
||||
crates/ksp-store-lib/tests/hardening_completeness.rs
|
||||
crates/ksp-store-lib/tests/public_api.rs
|
||||
crates/ksp-store-lib/unit_tests/store.rs
|
||||
crates/ksp-store-postgres-lib/README.md
|
||||
crates/ksp-store-postgres-lib/USAGE.md
|
||||
crates/ksp-store-postgres-lib/src/lib.rs
|
||||
crates/ksp-store-postgres-lib/src/runtime.rs
|
||||
crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
|
||||
crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
|
||||
crates/ksp-store-postgres-lib/tests/public_api.rs
|
||||
crates/ksp-store-postgres-lib/unit_tests/runtime.rs
|
||||
docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md
|
||||
docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md
|
||||
deltas/0.3.3/pre.008.md
|
||||
```
|
||||
|
||||
Aucune suppression de fichier.
|
||||
|
||||
## 10. Gate opérateur demandé
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.3
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-store-api
|
||||
cargo test -p ksp-store-lib
|
||||
cargo test -p ksp-store-postgres-lib
|
||||
cargo test -p ksp-config-lib
|
||||
cargo check -p ksp-store-lib --no-default-features
|
||||
```
|
||||
|
||||
Le test PostgreSQL live reste ignoré dans ce gate ; la preuve métier/concurrence complète appartient à `pre.009`.
|
||||
|
||||
## 11. Suite si gate vert
|
||||
|
||||
```text
|
||||
0.3.3-pre.009 — preuve PostgreSQL live complète RawTransaction
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md -->
|
||||
<!-- version: 8 -->
|
||||
<!-- version: 9 -->
|
||||
|
||||
# Plan `0.3.3` — Store/PostgreSQL RawTransaction vertical slice
|
||||
|
||||
@@ -1148,11 +1148,17 @@ Matérialisation :
|
||||
|
||||
### `0.3.3-pre.008` — façade `ksp-store-lib`
|
||||
|
||||
- six implémentations/dispatches ;
|
||||
- validation réseau pré-I/O ;
|
||||
- erreurs sûres ;
|
||||
- feature mismatch/no-default-features ;
|
||||
- aucune fuite de type PostgreSQL.
|
||||
Tranche matérialisée :
|
||||
|
||||
- les six traits `RawTransaction*` sont implémentés directement sur `PostgresBackend` en adaptant le bridge physique existant vers `StoreApiFuture`/`ksp_store_api::Result` ;
|
||||
- `Store` implémente les mêmes six traits et dispatch uniquement vers le backend compilé ;
|
||||
- les opérations portant `RawNetworkId` valident le réseau dans la façade avant tout appel backend ; `RawTransactionObservationRead` n'a aucun réseau dans son input et reste naturellement scoped par l'instance mono-réseau ;
|
||||
- `Conflict` et `QueryInvalid` conservent les codes API `store_api.raw_conflict` / `store_api.raw_query_invalid` ;
|
||||
- les erreurs runtime stabilisées sont `store.wrong_network`, `store.raw_reference_not_found`, `store.postgres_read_failed`, `store.postgres_write_failed`, `store.postgres_data_invalid`, `store.postgres_page_limit_unsupported` et le code compaction acquis ;
|
||||
- les erreurs conservent uniquement backend/network propre de l'instance + phase statique, jamais SQL/SQLSTATE/binds/texte serveur ;
|
||||
- `cargo check -p ksp-store-lib --no-default-features` reste un gate obligatoire et `Store::open` sans backend compilé conserve `store.backend_not_compiled` ;
|
||||
- aucun trait `RawAccount*`, type PostgreSQL, SQL ou reverse edge backend -> façade n'est ouvert ;
|
||||
- V000/V001 restent byte-identiques.
|
||||
|
||||
### `0.3.3-pre.009` — preuve PostgreSQL live
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md -->
|
||||
<!-- version: 12 -->
|
||||
<!-- version: 13 -->
|
||||
|
||||
# Validation `0.3.3` — Store/PostgreSQL RawTransaction vertical slice
|
||||
|
||||
@@ -42,12 +42,12 @@ Le test live est `#[ignore]` dans les gates ordinaires mais sa preuve réelle an
|
||||
|
||||
| Capacité | Backend PostgreSQL | Façade Store | Preuve finale |
|
||||
|----------------------------------|--------------------|--------------|---------------------------|
|
||||
| `RawTransactionRead` | À FAIRE | À FAIRE | unit + integration + live |
|
||||
| `RawTransactionWrite` | À FAIRE | À FAIRE | unit + concurrency live |
|
||||
| `RawTransactionObservationRead` | À FAIRE | À FAIRE | unit + live |
|
||||
| `RawTransactionObservationWrite` | À FAIRE | À FAIRE | unit + concurrency live |
|
||||
| `RawTransactionRetentionRead` | À FAIRE | À FAIRE | unit + live |
|
||||
| `RawTransactionRetentionWrite` | À FAIRE | À FAIRE | unit + race live |
|
||||
| `RawTransactionRead` | PASS pre.008 | PASS pre.008 | unit + integration + live |
|
||||
| `RawTransactionWrite` | PASS pre.008 | PASS pre.008 | unit + concurrency live |
|
||||
| `RawTransactionObservationRead` | PASS pre.008 | PASS pre.008 | unit + live |
|
||||
| `RawTransactionObservationWrite` | PASS pre.008 | PASS pre.008 | unit + concurrency live |
|
||||
| `RawTransactionRetentionRead` | PASS pre.008 | PASS pre.008 | unit + live |
|
||||
| `RawTransactionRetentionWrite` | PASS pre.008 | PASS pre.008 | unit + race live |
|
||||
|
||||
Aucune capacité `RawAccountState`, structurale N2, job ou worker n'est admise dans cette matrice.
|
||||
|
||||
@@ -608,13 +608,19 @@ cap 500/1000 dans Store pagination
|
||||
- `RetentionCompactionUnsupported` stable, aucune représentation `compacted` SQL : PASS statique/unit ;
|
||||
- preuve des races et atomicité PostgreSQL réelle : différée à `pre.009` ;
|
||||
- premier gate opérateur : FAIL local sur un canari `pre.005` devenu obsolète après ouverture légitime du `DELETE` de purge en `pre.007` ;
|
||||
- `pre.007-fix.001` retire uniquement cette interdiction historique globale et conserve le garde-fou contre les implémentations directes des traits Store ; gate opérateur à rejouer.
|
||||
- `pre.007-fix.001` retire uniquement cette interdiction historique globale et conserve le garde-fou jusqu'à ouverture de `pre.008` ; gate opérateur complet PASS le 2026-08-30.
|
||||
|
||||
### `pre.008`
|
||||
|
||||
- façade/feature dispatch ;
|
||||
- no physical leak ;
|
||||
- pre-I/O mismatch.
|
||||
- six implémentations `RawTransaction*` sur `PostgresBackend` : PASS statique/compile-gate à exécuter ;
|
||||
- six dispatches correspondants sur `Store` : PASS statique/compile-gate à exécuter ;
|
||||
- validation réseau façade pré-dispatch : PASS unit/static ;
|
||||
- `RawTransactionObservationRead` reste scoped par l'instance mono-réseau car l'input ne porte aucun réseau ;
|
||||
- mapping `Conflict -> store_api.raw_conflict`, `QueryInvalid -> store_api.raw_query_invalid` : PASS statique ;
|
||||
- codes `wrong_network`, `raw_reference_not_found`, read/write/data/page-limit PostgreSQL stabilisés : PASS public canary ;
|
||||
- aucune fuite PostgreSQL/SQL/env/reverse edge et aucun `RawAccount*` : PASS boundary canaries ;
|
||||
- `--no-default-features` reste requis au gate opérateur ;
|
||||
- V000/V001 inchangées.
|
||||
|
||||
### `pre.009`
|
||||
|
||||
@@ -640,7 +646,7 @@ cap 500/1000 dans Store pagination
|
||||
|
||||
- publication stable.
|
||||
|
||||
## 22. Gate courant `pre.007`
|
||||
## 22. Gate courant `pre.008`
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
|
||||
Reference in New Issue
Block a user