v0.3.3-pre.012-fix.001
This commit is contained in:
@@ -6,7 +6,7 @@ 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.12"
|
||||
version = "0.3.3-pre.12.fix.1"
|
||||
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: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# ksp-store-lib
|
||||
|
||||
@@ -31,7 +31,7 @@ Une instance `Store` représente exactement :
|
||||
1 Store = 1 RawNetworkId + 1 backend physique sélectionné
|
||||
```
|
||||
|
||||
Le runtime Store n'est pas un multiplexeur multi-database ou multi-réseau. La sélection d'un target nommé appartient à Config. Le document `std.store` peut donc définir plusieurs targets indépendants — par exemple Devnet, Mainnet et Testnet — mais un appel à `Store::open` reçoit les settings d'un seul target.
|
||||
Le runtime Store n'est pas un multiplexeur multi-database ou multi-réseau. La sélection d'un target nommé appartient à Config. Le document `std.store` peut donc définir plusieurs targets indépendants, mais un appel à `Store::open` reçoit les settings d'un seul target.
|
||||
|
||||
Cette séparation permet d'utiliser des bases PostgreSQL distinctes par réseau tout en conservant le réseau dans l'identité logique des données RAW.
|
||||
|
||||
@@ -63,19 +63,9 @@ Store ne lit ni `.env`, ni variables `KSP_*` / `KSPB_*`, ni variables/fichiers i
|
||||
|
||||
`ksp-config-lib` possède `std.store`, la résolution des secrets et la sélection du target. Il construit ensuite un `StoreSettings` backend-neutral. L'URI PostgreSQL reste nécessaire au runtime mais n'a aucun getter public dans `ksp-store-lib` et son `Debug` est redacted.
|
||||
|
||||
Les targets committed sont actuellement :
|
||||
## Surface RawTransaction
|
||||
|
||||
```text
|
||||
devnet -> network devnet -> base indépendante
|
||||
mainnet -> network mainnet-beta -> base indépendante
|
||||
testnet -> network testnet -> base indépendante
|
||||
```
|
||||
|
||||
Les credentials restent dans les variables `KSP_SECRET_STORE_*_POSTGRES_URI` ou le `.env` possédé par Config.
|
||||
|
||||
## Surface actuelle et hors périmètre
|
||||
|
||||
Depuis `0.3.3`, `Store` implémente les six capabilities transactionnelles acquises dans `ksp-store-api` :
|
||||
`Store` implémente les six capabilities transactionnelles acquises dans `ksp-store-api` :
|
||||
|
||||
```text
|
||||
RawTransactionRead
|
||||
@@ -86,7 +76,18 @@ 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`.
|
||||
Le consumer manipule uniquement les modèles et outcomes backend-neutral. Les erreurs physiques PostgreSQL sont projetées vers des codes Store stables sans exposer le backend.
|
||||
|
||||
La façade fournit ainsi :
|
||||
|
||||
- lecture d'une transaction canonique, de ses observations et de sa rétention ;
|
||||
- écriture atomique transaction + observation ;
|
||||
- ajout idempotent d'observations ;
|
||||
- pagination keyset déterministe par cursor opaque ;
|
||||
- application de transitions de rétention demandées par le caller ;
|
||||
- validation réseau avant dispatch lorsqu'un input porte explicitement son réseau.
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
Sont volontairement hors de cette surface :
|
||||
|
||||
@@ -95,11 +96,9 @@ Sont volontairement hors de cette surface :
|
||||
- transport d'acquisition, Program decoding et materialization ;
|
||||
- exposition publique de SQL, pool, client, row, statement ou transaction PostgreSQL.
|
||||
|
||||
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
|
||||
|
||||
- [`USAGE.md`](USAGE.md) — construction des settings, ouverture, health et fermeture ;
|
||||
- [`USAGE.md`](USAGE.md) — guide pratique de construction, lifecycle et capabilities Store ;
|
||||
- [`../ksp-store-postgres-lib/README.md`](../ksp-store-postgres-lib/README.md) — responsabilité du backend PostgreSQL physique ;
|
||||
- [`../../config/std.store.json`](../../config/std.store.json) — targets Store committed ;
|
||||
- [`../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md`](../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md) — architecture durable Store ;
|
||||
|
||||
@@ -1,36 +1,58 @@
|
||||
<!-- file: crates/ksp-store-lib/USAGE.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Utilisation de ksp-store-lib
|
||||
|
||||
## 1. Dépendance et features
|
||||
## 1. Dépendance et backend compilé
|
||||
|
||||
Le consumer runtime normal dépend uniquement de la façade :
|
||||
Le consumer runtime dépend de la façade commune :
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ksp-store-lib = { path = "../ksp-store-lib" }
|
||||
```
|
||||
|
||||
La feature par défaut est :
|
||||
La feature par défaut compile le backend PostgreSQL :
|
||||
|
||||
```text
|
||||
postgres
|
||||
```
|
||||
|
||||
Pour construire un binaire sans backend physique :
|
||||
Pour compiler la façade sans backend physique :
|
||||
|
||||
```toml
|
||||
ksp-store-lib = { path = "../ksp-store-lib", default-features = false }
|
||||
```
|
||||
|
||||
Dans ce mode, le type PostgreSQL reste connu par la surface de settings mais `Store::open` retourne `ERROR_CODE_BACKEND_NOT_COMPILED` avant toute I/O si PostgreSQL est sélectionné.
|
||||
Dans ce mode, les settings PostgreSQL restent représentables, mais `Store::open` retourne `ERROR_CODE_BACKEND_NOT_COMPILED` avant toute I/O si PostgreSQL est sélectionné.
|
||||
|
||||
Un consumer ordinaire ne dépend pas directement de `ksp-store-postgres-lib`.
|
||||
Un consumer applicatif ordinaire ne dépend pas directement de `ksp-store-postgres-lib`.
|
||||
|
||||
## 2. Construire des settings PostgreSQL programmatiquement
|
||||
## 2. Obtenir les settings depuis Config
|
||||
|
||||
La construction directe est utile pour les tests, outils internes ou compositions qui n'utilisent pas `ksp-config-lib`.
|
||||
Le chemin applicatif recommandé passe par `ksp-config-lib`, propriétaire de `std.store`, de la résolution `.env` et des secrets.
|
||||
|
||||
```rust
|
||||
fn resolve_store_settings(
|
||||
engine: &ksp_config_lib::ConfigDocumentEngine,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
target: std::option::Option<&str>,
|
||||
) -> ksp_core_lib::Result<ksp_store_lib::StoreSettings> {
|
||||
let resolved = engine.load_resolved_store_config(target, environment);
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
return std::result::Result::Ok(resolved.into_settings());
|
||||
}
|
||||
```
|
||||
|
||||
Chaque `StoreSettings` sélectionne exactement un réseau logique et un backend physique. La sélection d'un target nommé appartient à Config ; `Store` ne route pas automatiquement entre plusieurs targets.
|
||||
|
||||
## 3. Construire des settings programmatiquement
|
||||
|
||||
La construction directe est utile pour les tests et outils qui ne passent pas par Config.
|
||||
|
||||
```rust
|
||||
fn programmatic_store_settings(connection_uri: std::string::String) -> ksp_store_lib::Result<ksp_store_lib::StoreSettings> {
|
||||
@@ -52,8 +74,7 @@ fn programmatic_store_settings(connection_uri: std::string::String) -> ksp_store
|
||||
ksp_store_lib::StoreBackendSettings::Postgres(postgres),
|
||||
);
|
||||
|
||||
let validation = settings.validate();
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
if let std::result::Result::Err(error) = settings.validate() {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
|
||||
@@ -61,11 +82,11 @@ fn programmatic_store_settings(connection_uri: std::string::String) -> ksp_store
|
||||
}
|
||||
```
|
||||
|
||||
`PostgresStoreSettings` ne fournit volontairement aucun getter public de l'URI. Son `Debug` remplace cette valeur par `<redacted>`.
|
||||
`PostgresStoreSettings` ne fournit aucun getter public de l'URI. Son `Debug` remplace cette valeur par `<redacted>`.
|
||||
|
||||
## 3. Ouvrir et fermer un Store
|
||||
## 4. Ouvrir, sonder et fermer un Store
|
||||
|
||||
`Store::open` est async et ne retourne un succès qu'après que le backend compilé a prouvé sa fondation runtime.
|
||||
`Store::open` est async et ne retourne un succès qu'après validation des settings, ouverture du backend compilé et bootstrap requis.
|
||||
|
||||
```rust
|
||||
async fn use_store(settings: ksp_store_lib::StoreSettings) -> ksp_store_lib::Result<()> {
|
||||
@@ -95,130 +116,11 @@ async fn use_store(settings: ksp_store_lib::StoreSettings) -> ksp_store_lib::Res
|
||||
}
|
||||
```
|
||||
|
||||
`Store::close(self)` consomme l'instance afin qu'une fermeture explicite ne puisse pas être suivie d'une nouvelle opération via la même valeur.
|
||||
`Store::close(self)` consomme l'instance. Une fermeture explicite ne peut donc pas être suivie d'une nouvelle opération via la même valeur.
|
||||
|
||||
## 4. Construire les settings depuis Config
|
||||
## 5. Lire une transaction RAW
|
||||
|
||||
Le chemin applicatif recommandé utilise `ksp-config-lib`, propriétaire du document `std.store`, de `.env` et des secrets.
|
||||
|
||||
Après construction du `ConfigDocumentEngine` :
|
||||
|
||||
```rust
|
||||
fn resolve_store_settings(
|
||||
engine: &ksp_config_lib::ConfigDocumentEngine,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
target: std::option::Option<&str>,
|
||||
) -> ksp_core_lib::Result<ksp_store_lib::StoreSettings> {
|
||||
let resolved = engine.load_resolved_store_config(target, environment);
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
return std::result::Result::Ok(resolved.into_settings());
|
||||
}
|
||||
```
|
||||
|
||||
Targets committed :
|
||||
|
||||
```text
|
||||
devnet -> RawNetworkId("devnet")
|
||||
mainnet -> RawNetworkId("mainnet-beta")
|
||||
testnet -> RawNetworkId("testnet")
|
||||
```
|
||||
|
||||
Chaque target peut utiliser une URI PostgreSQL distincte. `default_profile` sélectionne un seul target ; `Store` ne route pas automatiquement entre plusieurs targets.
|
||||
|
||||
## 5. Settings disponibles
|
||||
|
||||
### `PostgresPoolSettings`
|
||||
|
||||
Valeurs par défaut :
|
||||
|
||||
```text
|
||||
max_connections 8
|
||||
connect_timeout 10 s
|
||||
wait_timeout 5 s
|
||||
create_timeout 10 s
|
||||
recycle_timeout 5 s
|
||||
```
|
||||
|
||||
Les getters sont :
|
||||
|
||||
```text
|
||||
max_connections()
|
||||
connect_timeout()
|
||||
wait_timeout()
|
||||
create_timeout()
|
||||
recycle_timeout()
|
||||
```
|
||||
|
||||
`validate()` vérifie les bornes sans I/O.
|
||||
|
||||
### `PostgresBootstrapSettings`
|
||||
|
||||
Valeurs par défaut :
|
||||
|
||||
```text
|
||||
schema_autocreate true
|
||||
schema_autoupdate true
|
||||
migration_timeout 30 s
|
||||
migration_lock_timeout 10 s
|
||||
```
|
||||
|
||||
Getters :
|
||||
|
||||
```text
|
||||
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 :
|
||||
|
||||
```text
|
||||
backend()
|
||||
backend_kind()
|
||||
network()
|
||||
shutdown_timeout()
|
||||
validate()
|
||||
```
|
||||
|
||||
`StoreSettings::new` permet de choisir explicitement le timeout de shutdown. `StoreSettings::with_default_shutdown` utilise la borne commune par défaut de 5 secondes.
|
||||
|
||||
## 6. Health et diagnostics
|
||||
|
||||
`StoreRuntimeSnapshot` est synchrone et ne déclenche aucune I/O. Il expose uniquement :
|
||||
|
||||
```text
|
||||
backend_kind
|
||||
network
|
||||
pool_capacity
|
||||
pool_size
|
||||
pool_available
|
||||
pool_waiting
|
||||
```
|
||||
|
||||
`StoreHealthSnapshot` ajoute une probe async bornée :
|
||||
|
||||
```text
|
||||
state = Ready | NotReady
|
||||
migration_version
|
||||
pending_migration_count
|
||||
last_error_code
|
||||
runtime snapshot
|
||||
```
|
||||
|
||||
Aucun snapshot n'expose URI, host, user, database, SQL, handle backend ou texte d'erreur PostgreSQL.
|
||||
|
||||
## 7. Utiliser les capabilities RawTransaction
|
||||
|
||||
Depuis `0.3.3`, `Store` implémente directement les six traits `RawTransaction*`. Le consumer importe le trait correspondant puis appelle la méthode sur la façade :
|
||||
Importer le trait correspondant suffit pour utiliser la façade :
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionRead;
|
||||
@@ -231,9 +133,146 @@ async fn read_transaction(
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
Une transaction absente retourne `None`. Une transaction `Purged` retourne également `None` pour le payload canonique ; son tombstone reste accessible via la capability de rétention.
|
||||
|
||||
Codes runtime principaux :
|
||||
## 6. Paginer les références de transactions
|
||||
|
||||
La pagination est keyset et utilise un cursor opaque. Le consumer ne doit pas interpréter ses bytes.
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionRead;
|
||||
|
||||
async fn first_transaction_page(
|
||||
store: &ksp_store_lib::Store,
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
) -> ksp_store_lib::Result<ksp_store_lib::RawPage<ksp_store_lib::RawTransactionReference>> {
|
||||
let limit = ksp_store_lib::RawPageLimit::new(100);
|
||||
let limit = match limit {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
let slots = ksp_store_lib::RawSlotRange::new(std::option::Option::None, std::option::Option::None);
|
||||
let slots = match slots {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
let query = ksp_store_lib::RawTransactionQuery::new(
|
||||
network,
|
||||
slots,
|
||||
ksp_store_lib::RawSortDirection::Ascending,
|
||||
ksp_store_lib::RawPageRequest::first(limit),
|
||||
);
|
||||
|
||||
return store.list_raw_transactions(&query).await;
|
||||
}
|
||||
```
|
||||
|
||||
Pour continuer, recopier le cursor retourné par `RawPage::next_cursor()` dans `RawPageRequest::after`. Le réseau, la direction et les bornes de slots doivent rester identiques à ceux de la query ayant produit le cursor.
|
||||
|
||||
## 7. Persister une acquisition canonique
|
||||
|
||||
La transaction canonique et son observation initiale forment une seule opération atomique.
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionWrite;
|
||||
|
||||
async fn persist_acquisition(
|
||||
store: &ksp_store_lib::Store,
|
||||
transaction: ksp_store_lib::RawTransaction,
|
||||
observation: ksp_store_lib::RawTransactionObservation,
|
||||
) -> ksp_store_lib::Result<ksp_store_lib::RawAcquisitionWriteOutcome> {
|
||||
return store
|
||||
.persist_raw_transaction_acquisition(
|
||||
transaction,
|
||||
observation,
|
||||
ksp_store_lib::RawTransactionAcquisitionMode::Normal,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
```
|
||||
|
||||
Le mode `Normal` respecte un tombstone `Purged`. `ForceRehydrate` doit être choisi explicitement lorsqu'un caller veut restaurer un payload purgé et que l'identité retenue est compatible.
|
||||
|
||||
Un contenu divergent sous la même identité produit `ERROR_CODE_RAW_CONFLICT`; Store ne remplace jamais silencieusement le contenu gagnant.
|
||||
|
||||
## 8. Lire et ajouter une observation
|
||||
|
||||
Une observation supplémentaire référence une transaction canonique déjà durable.
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionObservationRead;
|
||||
use ksp_store_lib::RawTransactionObservationWrite;
|
||||
|
||||
async fn use_observation(
|
||||
store: &ksp_store_lib::Store,
|
||||
key: &ksp_store_lib::RawObservationKey,
|
||||
observation: ksp_store_lib::RawTransactionObservation,
|
||||
) -> ksp_store_lib::Result<ksp_store_lib::RawObservationWriteOutcome> {
|
||||
let existing = store.get_raw_transaction_observation(key).await;
|
||||
if let std::result::Result::Err(error) = existing {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
|
||||
return store.record_raw_transaction_observation(observation).await;
|
||||
}
|
||||
```
|
||||
|
||||
`record_raw_transaction_observation` ne crée pas implicitement le canonique. Une référence absente est signalée par `ERROR_CODE_RAW_REFERENCE_NOT_FOUND`.
|
||||
|
||||
## 9. Lire la rétention et le tombstone
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionRetentionRead;
|
||||
|
||||
async fn read_retention(
|
||||
store: &ksp_store_lib::Store,
|
||||
reference: &ksp_store_lib::RawTransactionReference,
|
||||
) -> ksp_store_lib::Result<std::option::Option<ksp_store_lib::RawRetentionState>> {
|
||||
let tombstone = store.get_raw_transaction_tombstone(reference).await;
|
||||
if let std::result::Result::Err(error) = tombstone {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
|
||||
return store.get_raw_transaction_retention_state(reference).await;
|
||||
}
|
||||
```
|
||||
|
||||
Le tombstone minimal est utile uniquement lorsque le payload a été purgé ; il ne remplace pas le modèle canonique lorsqu'un payload est encore disponible.
|
||||
|
||||
## 10. Appliquer une transition de rétention
|
||||
|
||||
La policy qui décide qu'une transition est autorisée appartient au caller. Store applique uniquement la transition demandée de manière atomique.
|
||||
|
||||
```rust
|
||||
use ksp_store_lib::RawTransactionRetentionWrite;
|
||||
|
||||
async fn archive_transaction(
|
||||
store: &ksp_store_lib::Store,
|
||||
reference: ksp_store_lib::RawTransactionReference,
|
||||
) -> ksp_store_lib::Result<ksp_store_lib::RawRetentionWriteOutcome> {
|
||||
let transition = ksp_store_lib::RawTransactionRetentionTransition::try_new(
|
||||
reference,
|
||||
ksp_store_lib::RawRetentionState::Full,
|
||||
ksp_store_lib::RawRetentionState::Archived,
|
||||
);
|
||||
let transition = match transition {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
return store.transition_raw_transaction_retention(transition).await;
|
||||
}
|
||||
```
|
||||
|
||||
Le backend PostgreSQL supporte physiquement `Full -> Archived -> Purged`. Une transition impliquant `Compacted` est rejetée par ce backend tant qu'aucune représentation compactée réelle n'est implémentée.
|
||||
|
||||
## 11. Diagnostics et erreurs
|
||||
|
||||
Les snapshots et erreurs de façade n'exposent ni URI, host, user, database, SQL, handle backend, valeur de bind ni texte d'erreur PostgreSQL.
|
||||
|
||||
Les codes Store utiles incluent notamment :
|
||||
|
||||
```text
|
||||
store.wrong_network
|
||||
@@ -245,17 +284,10 @@ 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`.
|
||||
Les conflits et queries invalides utilisent les codes backend-neutral `store_api.raw_conflict` et `store_api.raw_query_invalid`.
|
||||
|
||||
## 8. Limite fonctionnelle actuelle
|
||||
## 12. Limites de la façade
|
||||
|
||||
La façade n'implémente pas encore les capabilities `RawAccount*`. Elles appartiennent à la vertical slice `0.3.4`.
|
||||
La façade ne fournit pas d'accès public au SQL, au pool, aux clients ou transactions PostgreSQL. Les capabilities `RawAccount*` réexportées par l'API commune ne sont pas encore dispatchées par `Store`.
|
||||
|
||||
## 9. Preuve finale `0.3.3`
|
||||
|
||||
La conformance de la façade est verrouillée par les tests publics/hardening avec et sans feature PostgreSQL. Le gate technique final `0.3.3-pre.011` a également rejoué le workspace complet et la preuve PostgreSQL réelle `RawTransaction` sur PostgreSQL 17.
|
||||
|
||||
Références durables :
|
||||
|
||||
- [`../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md`](../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md) ;
|
||||
- [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md).
|
||||
La taille de page est une primitive de navigation. Les décisions de batch, priorité, backlog et scheduling appartiennent aux workers/jobs, pas à Store.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!-- file: crates/ksp-store-postgres-lib/README.md -->
|
||||
<!-- version: 9 -->
|
||||
<!-- version: 10 -->
|
||||
|
||||
# ksp-store-postgres-lib
|
||||
|
||||
`ksp-store-postgres-lib` est le backend PostgreSQL physique officiel du Store KSP.
|
||||
|
||||
La crate implémente la fondation connexion/pool/TLS/migrations/health derrière `ksp-store-lib`. Elle dépend directement de `ksp-store-api` mais ne dépend jamais de la façade `ksp-store-lib`.
|
||||
La crate implémente connexion, pool, TLS, migrations, health et persistence RAW derrière `ksp-store-lib`. Elle dépend directement de `ksp-store-api` mais ne dépend jamais de la façade `ksp-store-lib`.
|
||||
|
||||
## Responsabilités
|
||||
|
||||
@@ -15,10 +15,10 @@ La crate possède seule pour PostgreSQL :
|
||||
- le pool borné `deadpool-postgres` ;
|
||||
- la policy TLS physique avec Rustls ;
|
||||
- les roots système et le provider cryptographique AWS-LC ;
|
||||
- le bootstrap/moteur de migrations privé KSP ;
|
||||
- le bootstrap et le moteur de migrations privé KSP ;
|
||||
- la table metadata `ksp_store_schema_migrations` ;
|
||||
- la migration logique V000 relocalisée sous `migrations/v000_bootstrap/` et son checksum SHA-256 historique ;
|
||||
- l'advisory transaction lock borné des migrations ;
|
||||
- le schéma physique et les statements privés `RawTransaction` ;
|
||||
- les snapshots runtime/health sûrs destinés au bridge de façade ;
|
||||
- la fermeture explicite du pool et son fallback `Drop` best-effort ;
|
||||
- la classification d'erreurs backend sans conserver le texte d'erreur PostgreSQL.
|
||||
@@ -31,7 +31,7 @@ Les applications, jobs et workers KSP ne dépendent normalement pas de cette cra
|
||||
consumer -> ksp-store-lib -> [feature postgres] ksp-store-postgres-lib
|
||||
```
|
||||
|
||||
La surface publique de cette crate existe pour le bridge inter-crates et les tests d'intégration backend. Elle ne constitue pas une seconde façade Store.
|
||||
La surface publique de cette crate existe pour le bridge inter-crates et les tests/intégrations backend. Elle ne constitue pas une seconde façade Store.
|
||||
|
||||
`ksp-store-postgres-lib` ne réexporte pas `tokio-postgres`, Deadpool ou Rustls.
|
||||
|
||||
@@ -57,33 +57,15 @@ Disabled
|
||||
VerifyFull
|
||||
```
|
||||
|
||||
`VerifyFull` exige :
|
||||
`VerifyFull` exige TLS, roots système, certificat valide et vérification de l'identité serveur. Une configuration ne permettant pas de vérifier cette identité, comme `hostaddr` seul, est rejetée.
|
||||
|
||||
- TLS ;
|
||||
- roots système ;
|
||||
- certificat valide ;
|
||||
- vérification de l'identité serveur ;
|
||||
- aucune dégradation automatique en plaintext.
|
||||
## Migrations et schéma
|
||||
|
||||
Les configurations ne permettant pas de vérifier une identité serveur, comme `hostaddr` seul, sont rejetées.
|
||||
Le moteur de migrations embarqué vérifie version logique, 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.
|
||||
|
||||
## Migrations
|
||||
Le bootstrap metadata est conservé comme migration V000. La migration logique V001 matérialise le schéma `RawTransaction` en ressources séparées `tables/`, `constraints/` et `indexes/` afin que le backend puisse vérifier leur compatibilité effective sans transformer les fichiers SQL en parser généraliste.
|
||||
|
||||
La fondation historique V000 est embarquée sous :
|
||||
|
||||
```text
|
||||
migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql
|
||||
```
|
||||
|
||||
La vertical slice V001 est ensuite répartie sous `migrations/v001_raw_transaction/{tables,constraints,indexes}/` tout en restant une migration logique unique. V000 crée la metadata privée :
|
||||
|
||||
```text
|
||||
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`. `pre.008` ferme la conformance backend en implémentant les six traits `RawTransaction*` directement sur `PostgresBackend`.
|
||||
La base est liée à un seul `RawNetworkId` via `ksp_store_identity`. Une migration enregistrée mais physiquement divergente est un mismatch ; les réparations additives sûres dépendent de `schema_autoupdate`.
|
||||
|
||||
## Health et erreurs
|
||||
|
||||
@@ -100,13 +82,11 @@ Le texte d'erreur PostgreSQL, l'URI, SQL et les valeurs bind ne traversent pas c
|
||||
|
||||
## Support PostgreSQL
|
||||
|
||||
La politique de support de `0.3.2` fixe PostgreSQL 15 comme major minimal. Le test live de fondation refuse explicitement un serveur plus ancien ; le backend ne fixe aucun plafond arbitraire de major PostgreSQL. La compatibilité de migration reste basée sur le schéma KSP.
|
||||
Le major minimal supporté est PostgreSQL 15. Le backend ne fixe aucun plafond arbitraire de major ; la compatibilité opérationnelle reste fondée sur le contrat de schéma KSP et l'introspection du catalogue.
|
||||
|
||||
La preuve opérateur réelle et le major effectivement exercé sont conservés dans la matrice de validation, pas dans cette documentation durable.
|
||||
## Lectures RAW transaction
|
||||
|
||||
## Lectures RAW `0.3.3-pre.004`
|
||||
|
||||
Le backend expose désormais quatre lectures étroites qui retournent uniquement des modèles `ksp-store-api` :
|
||||
Le backend expose :
|
||||
|
||||
```text
|
||||
get_raw_transaction
|
||||
@@ -115,79 +95,56 @@ get_raw_transaction_retention_state
|
||||
get_raw_transaction_tombstone
|
||||
```
|
||||
|
||||
Le SQL et les rows restent privés au backend. Le mapping PostgreSQL est fallible et couvre notamment `NUMERIC(20,0) -> u64`, `BIGINT -> u32/u64`, timestamps bornés, bytes de taille fixe et codes de provenance. Une ligne stockée incompatible produit uniquement `PostgresBackendErrorKind::DataInvalid`; un échec de SELECT produit `ReadFailed`. Les lectures portant un `RawTransactionReference` rejettent un réseau différent avant toute acquisition de pool.
|
||||
Le SQL et les rows restent privés. Le mapping PostgreSQL est fallible et couvre notamment `NUMERIC(20,0) -> u64`, `BIGINT -> u32/u64`, timestamps bornés, bytes de taille fixe et codes de provenance.
|
||||
|
||||
`Full` lit le payload chaud, `Archived` le reconstruit depuis la relation archive et `Purged` retourne `None`; le tombstone reste accessible séparément pour `Purged`.
|
||||
`Full` lit le payload chaud, `Archived` le reconstruit depuis la relation archive et `Purged` retourne `None`; le tombstone reste accessible séparément.
|
||||
|
||||
## Écritures RAW `0.3.3-pre.005`
|
||||
## Écritures RAW transaction
|
||||
|
||||
Le backend expose deux écritures étroites :
|
||||
Le backend expose :
|
||||
|
||||
```text
|
||||
persist_raw_transaction_acquisition
|
||||
record_raw_transaction_observation
|
||||
```
|
||||
|
||||
L'acquisition canonique et son observation sont commises dans une seule transaction PostgreSQL. L'insertion utilise les clés uniques physiques sans prélecture `has_*`; après un conflit unique, le backend verrouille la ligne gagnante et compare le contenu réel avant de conclure `AlreadyPresent` ou `Conflict`. Les octets du payload sont comparés lorsqu'ils existent encore : le hash seul ne constitue jamais une preuve d'idempotence.
|
||||
L'acquisition canonique et son observation initiale sont commises dans une seule transaction PostgreSQL. Les clés uniques physiques fournissent l'admission idempotente ; après un conflit unique, le backend verrouille la ligne gagnante et compare le contenu réel avant de conclure `AlreadyPresent` ou `Conflict`.
|
||||
|
||||
Un tombstone `Purged` compatible produit `SkippedPurged/NotRecorded` en mode normal. `ForceRehydrate` restaure `Full` et l'observation dans la même transaction. Une observation supplémentaire ne crée jamais implicitement son canonique ; une référence absente est classée `ReferenceNotFound` et un canonical purgé retourne `NotRecorded`.
|
||||
Un tombstone `Purged` compatible produit `SkippedPurged/NotRecorded` en mode normal. `ForceRehydrate` restaure explicitement le payload `Full` et l'observation dans la même transaction.
|
||||
|
||||
Les erreurs physiques d'écriture sont réduites à `WriteFailed`; aucune erreur serveur, SQLSTATE, query ou valeur de bind n'est conservée.
|
||||
## Pagination RAW transaction
|
||||
|
||||
## Pagination RAW `0.3.3-pre.006`
|
||||
`list_raw_transactions` parcourt les références canoniques récupérables avec un ordre total `(slot, signature)`. Les tombstones `Purged` sont exclus.
|
||||
|
||||
`list_raw_transactions` parcourt uniquement les références canoniques récupérables, en excluant les tombstones `Purged`. L'ordre total est :
|
||||
La continuation est une keyset stricte, jamais un `OFFSET`. Le cursor backend V1 est opaque et lié au réseau, à la direction et aux bornes de slots de la query. Store n'impose aucun plafond métier arbitraire à la taille de page ; seule la limitation physique du `LIMIT + 1` PostgreSQL est exposée.
|
||||
|
||||
```text
|
||||
Ascending = (slot ASC, signature ASC)
|
||||
Descending = (slot DESC, signature DESC)
|
||||
```
|
||||
## Rétention RAW transaction
|
||||
|
||||
La continuation est une keyset stricte `>` / `<`, jamais un `OFFSET`. Le cursor backend V1 fait exactement 109 octets et transporte `KSPT`, version 1, le dernier slot, la dernière signature et un digest SHA-256 lié au réseau, à la direction et aux bornes de slots. Un cursor rejoué sous une autre query est `QueryInvalid`.
|
||||
|
||||
`RawPageLimit` n'est pas réduit par une policy KSP : PostgreSQL utilise `LIMIT requested + 1`, avec comme seule borne physique `requested <= i64::MAX - 1`. Une valeur supérieure produit `PageLimitUnsupported` sans clamp. La pagination ne promet aucun snapshot inter-pages.
|
||||
|
||||
## Rétention RAW `0.3.3-pre.007`
|
||||
|
||||
`transition_raw_transaction_retention` applique uniquement les transitions physiques supportées :
|
||||
`transition_raw_transaction_retention` applique les transitions physiques :
|
||||
|
||||
```text
|
||||
Full -> Archived -> Purged
|
||||
```
|
||||
|
||||
Le backend verrouille la ligne canonique avec `FOR UPDATE`, valide la forme physique courante, compare `current` avec `expected` et n'effectue la mutation qu'en cas de correspondance. `current == target` retourne `AlreadyAtTarget`; une race ayant déplacé l'état ailleurs retourne `ExpectedStateMismatch`. Une référence absente est `ReferenceNotFound`.
|
||||
Le backend verrouille la ligne canonique avec `FOR UPDATE`, compare l'état courant à l'état attendu et applique la mutation atomiquement. L'archivage conserve le payload exact dans la relation archive ; la purge conserve seulement le tombstone minimal.
|
||||
|
||||
`Full -> Archived` copie d'abord les octets exacts dans `ksp_raw_transaction_archive_payloads`, puis retire le payload chaud et passe l'état à `archived` dans la même transaction. `Archived -> Purged` supprime l'archive, efface `block_time`, conserve les cinq champs de tombstone et passe l'état à `purged` atomiquement. Les écritures d'acquisition/rehydration et les transitions utilisent le même verrou canonique, ce qui sérialise les races purge/ForceRehydrate.
|
||||
Toute transition impliquant `Compacted` est rejetée avec `RetentionCompactionUnsupported` tant qu'aucune représentation compactée réelle n'est implémentée.
|
||||
|
||||
Toute transition dont `expected` ou `target` vaut `Compacted` est rejetée avant `pool.get()` avec `RetentionCompactionUnsupported` et le code stable `store.postgres_retention_compaction_unsupported`. PostgreSQL n'utilise pas TOAST comme faux contrat de compaction.
|
||||
## Hors périmètre
|
||||
|
||||
## Preuve PostgreSQL live `0.3.3`
|
||||
La crate ne contient :
|
||||
|
||||
La vertical slice `RawTransaction` possède un test PostgreSQL réel opt-in dédié :
|
||||
|
||||
```text
|
||||
postgres_raw_transaction_live
|
||||
```
|
||||
|
||||
Il refuse une base où une table KSP gérée existe déjà, lit l’URI dédiée uniquement sur `stdin`, ne l’affiche jamais et nettoie seulement le schéma qu’il a lui-même créé. La preuve couvre bootstrap V000/V001, binding réseau, réparation additive contrôlée, écritures atomiques, concurrence réelle, observations, pagination/cursor, rétention/ForceRehydrate, races et rollback par annulation d’une tâche bloquée sur un verrou PostgreSQL.
|
||||
|
||||
Le test reste `#[ignore]` dans les gates ordinaires. Après les corrections de compatibilité catalogue/drift de `pre.009-fix.001..006`, il passe sur PostgreSQL 17 et a été rejoué une seconde fois avec succès au gate technique final `pre.011`. Son exécution réelle et le major exercé sont enregistrés dans la matrice de validation de la release.
|
||||
|
||||
## Hors périmètre actuel
|
||||
|
||||
La crate ne contient encore :
|
||||
|
||||
- aucune capability `RawAccount*` ;
|
||||
- aucune implémentation PostgreSQL des capabilities `RawAccount*` ;
|
||||
- aucune orchestration worker/job ;
|
||||
- aucun transport d'acquisition ou decoder Program.
|
||||
- aucun transport d'acquisition ou decoder Program ;
|
||||
- aucune policy autonome de batch, priorité ou rétention.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [`USAGE.md`](USAGE.md) — bridge physique et lifecycle ;
|
||||
- [`USAGE.md`](USAGE.md) — guide pratique du bridge physique et de ses capabilities ;
|
||||
- [`../ksp-store-lib/README.md`](../ksp-store-lib/README.md) — façade runtime destinée aux consumers ;
|
||||
- [`../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md`](../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md) — architecture Store ;
|
||||
- [`../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md`](../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md) — décisions pool/TLS/migrations ;
|
||||
- [`../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md`](../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md) — preuves de fondation ;
|
||||
- [`../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md`](../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md) — design et séquencement `RawTransaction` ;
|
||||
- [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md) — preuves déterministes et PostgreSQL réel `RawTransaction`.
|
||||
- [`../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md`](../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md) — validation de fondation ;
|
||||
- [`../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md`](../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md) — design `RawTransaction` ;
|
||||
- [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md) — validation `RawTransaction`.
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<!-- file: crates/ksp-store-postgres-lib/USAGE.md -->
|
||||
<!-- version: 9 -->
|
||||
<!-- version: 10 -->
|
||||
|
||||
# Utilisation de ksp-store-postgres-lib
|
||||
|
||||
## 1. Quand utiliser cette crate directement
|
||||
## 1. Quand dépendre directement du backend
|
||||
|
||||
Le consumer applicatif normal utilise `ksp-store-lib`.
|
||||
|
||||
Une dépendance directe à `ksp-store-postgres-lib` est réservée aux composants qui implémentent ou testent le bridge physique PostgreSQL. La crate backend ne doit pas devenir une façade parallèle.
|
||||
|
||||
Un tel composant doit déclarer explicitement le backend et `ksp-store-api`, car `PostgresBackendSettings::new` reçoit le `RawNetworkId` backend-neutral sans le réexporter :
|
||||
Une dépendance directe à `ksp-store-postgres-lib` est réservée aux composants qui implémentent, intègrent ou testent le bridge physique PostgreSQL. Cette crate ne doit pas devenir une façade Store parallèle.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
@@ -17,16 +15,18 @@ ksp-store-api = { path = "../ksp-store-api" }
|
||||
ksp-store-postgres-lib = { path = "../ksp-store-postgres-lib" }
|
||||
```
|
||||
|
||||
## 2. Construire le bridge physique
|
||||
Le backend reçoit les modèles et traits backend-neutral de `ksp-store-api`; il ne réexporte pas `tokio-postgres`, Deadpool ou Rustls.
|
||||
|
||||
`PostgresBackendSettings` reçoit des valeurs déjà possédées et validées par la couche appelante. L'URI est sensible et son `Debug` est redacted.
|
||||
## 2. Construire les settings physiques
|
||||
|
||||
Pour distinguer création initiale et mise à jour du schéma, utiliser `PostgresBackendSettings::with_schema_policy` :
|
||||
|
||||
```rust
|
||||
fn backend_settings(
|
||||
network: ksp_store_api::RawNetworkId,
|
||||
connection_uri: std::string::String,
|
||||
) -> ksp_store_postgres_lib::PostgresBackendSettings {
|
||||
return ksp_store_postgres_lib::PostgresBackendSettings::new(
|
||||
return ksp_store_postgres_lib::PostgresBackendSettings::with_schema_policy(
|
||||
network,
|
||||
connection_uri,
|
||||
8,
|
||||
@@ -36,15 +36,18 @@ fn backend_settings(
|
||||
std::time::Duration::from_secs(5),
|
||||
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull,
|
||||
true,
|
||||
true,
|
||||
std::time::Duration::from_secs(30),
|
||||
std::time::Duration::from_secs(10),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Le backend reçoit un seul `RawNetworkId`. Une instance physique n'est pas un routeur multi-réseau.
|
||||
`schema_autocreate` autorise l'initialisation d'un Store vierge. `schema_autoupdate` autorise les migrations pending et les réparations additives sûres d'une migration déjà enregistrée. Le constructeur `new(..., auto_migrate, ...)` existe pour les callers utilisant encore un switch unique et applique cette valeur aux deux politiques.
|
||||
|
||||
## 3. Ouvrir, sonder et fermer
|
||||
L'URI est sensible : elle n'est jamais rendue par `Debug`.
|
||||
|
||||
## 3. Ouvrir, sonder et fermer le backend
|
||||
|
||||
```rust
|
||||
async fn use_backend(
|
||||
@@ -63,189 +66,176 @@ async fn use_backend(
|
||||
let _waiting = runtime.pool_waiting();
|
||||
|
||||
let health = backend.health().await;
|
||||
let _ready = health.ready();
|
||||
let _ready = health.is_ready();
|
||||
let _migration_version = health.migration_version();
|
||||
let _pending = health.pending_migration_count();
|
||||
let _safe_error_kind = health.last_error_kind();
|
||||
let _safe_error_kind = health.error_kind();
|
||||
|
||||
return backend.close(std::time::Duration::from_secs(5)).await;
|
||||
}
|
||||
```
|
||||
|
||||
`open` prouve la connexion et le bootstrap avant de retourner. `close` ferme le pool puis attend son drain dans la deadline fournie.
|
||||
`open` valide la configuration, construit le pool, prouve une connexion et vérifie/applique le bootstrap avant de retourner. `close` ferme le pool et attend son drain dans la deadline fournie.
|
||||
|
||||
Une instance physique est liée à un seul `RawNetworkId`.
|
||||
|
||||
## 4. Choisir le mode TLS
|
||||
|
||||
### `VerifyFull`
|
||||
|
||||
À utiliser pour les connexions PostgreSQL protégées :
|
||||
Pour une connexion PostgreSQL protégée :
|
||||
|
||||
```rust
|
||||
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull
|
||||
```
|
||||
|
||||
Le backend charge les roots système et vérifie certificat + identité serveur. Il rejette une configuration ne fournissant pas d'identité vérifiable.
|
||||
`VerifyFull` impose TLS, les roots système et la vérification de l'identité serveur. Une configuration ne fournissant pas d'identité vérifiable est rejetée.
|
||||
|
||||
### `Disabled`
|
||||
Pour une topologie explicitement non chiffrée :
|
||||
|
||||
```rust
|
||||
ksp_store_postgres_lib::PostgresBackendTlsMode::Disabled
|
||||
```
|
||||
|
||||
Ce mode désactive explicitement TLS. Il ne doit être utilisé que lorsque la topologie de déploiement justifie clairement une connexion non chiffrée.
|
||||
La policy typée choisie par KSP prime sur les paramètres SSL de l'URI.
|
||||
|
||||
La valeur typée choisie par KSP prime sur les paramètres SSL de l'URI.
|
||||
## 5. Lire une transaction et ses métadonnées
|
||||
|
||||
## 5. Bootstrap et migrations
|
||||
|
||||
Le backend embarque son propre moteur de migrations. La migration historique V000 est désormais matérialisée par :
|
||||
|
||||
```text
|
||||
migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql
|
||||
```
|
||||
|
||||
Les migrations suivantes conservent une version logique unique tout en séparant leurs ressources physiques par famille sous `migrations/vNNN_name/{tables,constraints,indexes}/`. Le bootstrap maintient :
|
||||
|
||||
```text
|
||||
ksp_store_schema_migrations
|
||||
version
|
||||
name
|
||||
checksum SHA-256
|
||||
```
|
||||
|
||||
Le runner est transactionnel et sérialisé par advisory transaction lock. Une divergence de checksum/nom/version ou une history plus récente est terminale ; aucun down migration automatique n'est exécuté.
|
||||
|
||||
`schema_autocreate` contrôle l'initialisation/adoption du schéma et `schema_autoupdate` les migrations pending ainsi que les réparations additives sûres. Le constructeur legacy `auto_migrate` mappe encore les deux politiques pour compatibilité source.
|
||||
|
||||
## 6. Classifier les erreurs sans fuite
|
||||
Les méthodes backend retournent uniquement des modèles `ksp-store-api`.
|
||||
|
||||
```rust
|
||||
match error.kind() {
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::Conflict => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::DataInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::PageLimitUnsupported => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::QueryInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReadFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReferenceNotFound => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::RetentionCompactionUnsupported => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WriteFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WrongNetwork => {}
|
||||
_ => {}
|
||||
async fn read_transaction_state(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
reference: &ksp_store_api::RawTransactionReference,
|
||||
) -> std::result::Result<std::option::Option<ksp_store_api::RawTransaction>, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
let retention = backend.get_raw_transaction_retention_state(reference).await;
|
||||
if let std::result::Result::Err(error) = retention {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
|
||||
let tombstone = backend.get_raw_transaction_tombstone(reference).await;
|
||||
if let std::result::Result::Err(error) = tombstone {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
|
||||
return backend.get_raw_transaction(reference).await;
|
||||
}
|
||||
|
||||
let _safe_phase = error.phase();
|
||||
```
|
||||
|
||||
Ne pas reconstruire un diagnostic utilisateur à partir de l'erreur brute PostgreSQL : cette erreur n'est volontairement pas conservée par le bridge.
|
||||
`Full` lit le payload chaud, `Archived` reconstruit le payload depuis l'archive et `Purged` retourne `None`. Un tombstone purgé reste lisible séparément.
|
||||
|
||||
## 7. Lectures RAW transaction
|
||||
Un réseau différent de celui du backend est rejeté avant acquisition d'un client du pool.
|
||||
|
||||
Depuis `0.3.3-pre.004`, un backend ouvert expose quatre lectures backend-specific retournant exclusivement les modèles communs :
|
||||
## 6. Lire une observation
|
||||
|
||||
```rust
|
||||
let transaction = backend.get_raw_transaction(&reference).await;
|
||||
let observation = backend.get_raw_transaction_observation(&observation_key).await;
|
||||
let retention = backend.get_raw_transaction_retention_state(&reference).await;
|
||||
let tombstone = backend.get_raw_transaction_tombstone(&reference).await;
|
||||
async fn read_observation(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
key: &ksp_store_api::RawObservationKey,
|
||||
) -> std::result::Result<std::option::Option<ksp_store_api::RawTransactionObservation>, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
return backend.get_raw_transaction_observation(key).await;
|
||||
}
|
||||
```
|
||||
|
||||
Le backend ne rend jamais `tokio_postgres::Row`, SQL, SQLSTATE ou valeur de bind. Pour les références réseau-scopées, un mauvais réseau est rejeté avant acquisition d'un client du pool. Une corruption de row est projetée vers `DataInvalid`, un échec physique de lecture vers `ReadFailed`, et les erreurs de pool conservent leur classification bornée existante.
|
||||
Les rows PostgreSQL, SQLSTATE, statements et valeurs de bind ne traversent jamais cette API.
|
||||
|
||||
`get_raw_transaction` retourne :
|
||||
|
||||
```text
|
||||
Full -> payload chaud
|
||||
Archived -> payload archive reconstruit
|
||||
Purged -> None
|
||||
absent -> None
|
||||
```
|
||||
|
||||
Le tombstone `Purged` reste lisible séparément.
|
||||
|
||||
## 8. Écritures RAW transaction
|
||||
|
||||
Depuis `0.3.3-pre.005`, le backend physique expose également :
|
||||
## 7. Persister une acquisition canonique
|
||||
|
||||
```rust
|
||||
let acquisition = backend
|
||||
.persist_raw_transaction_acquisition(transaction, observation, mode)
|
||||
.await;
|
||||
|
||||
let observation = backend
|
||||
.record_raw_transaction_observation(additional_observation)
|
||||
.await;
|
||||
async fn persist_acquisition(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
transaction: ksp_store_api::RawTransaction,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
) -> std::result::Result<ksp_store_api::RawAcquisitionWriteOutcome, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
return backend
|
||||
.persist_raw_transaction_acquisition(
|
||||
transaction,
|
||||
observation,
|
||||
ksp_store_api::RawTransactionAcquisitionMode::Normal,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
```
|
||||
|
||||
La première opération est atomique : transaction canonique et observation sont toutes deux durables ou aucune ne l'est. Les doublons ne sont pas détectés par une prélecture `has_*` : l'insert unique est tenté directement, puis un conflit relit/verrouille la ligne gagnante et compare son contenu. Une divergence sous la même signature ou la même `observation_key` produit `PostgresBackendErrorKind::Conflict`.
|
||||
L'opération est atomique : le canonique et son observation initiale sont tous deux durables ou aucun ne l'est. Une identité déjà présente avec un contenu identique est idempotente ; un contenu divergent retourne `PostgresBackendErrorKind::Conflict` sans overwrite silencieux.
|
||||
|
||||
Pour une transaction purgée, le mode normal retourne `SkippedPurged/NotRecorded` lorsque le tombstone est compatible. `ForceRehydrate` restaure le payload `Full` puis enregistre l'observation dans la même transaction. `record_raw_transaction_observation` ne crée jamais de canonique : référence absente -> `ReferenceNotFound`, canonique `Purged` -> `NotRecorded`.
|
||||
Pour un tombstone purgé compatible, le mode `Normal` ne restaure pas le payload. `ForceRehydrate` doit être demandé explicitement pour rétablir un payload `Full`.
|
||||
|
||||
Les références réseau-scopées sont toujours validées avant `pool.get()`.
|
||||
|
||||
## 9. Paginer les transactions RAW
|
||||
|
||||
Depuis `0.3.3-pre.006`, le backend fournit une navigation keyset déterministe :
|
||||
## 8. Ajouter une observation à un canonique existant
|
||||
|
||||
```rust
|
||||
let page = backend.list_raw_transactions(&query).await;
|
||||
async fn record_observation(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
observation: ksp_store_api::RawTransactionObservation,
|
||||
) -> std::result::Result<ksp_store_api::RawObservationWriteOutcome, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
return backend.record_raw_transaction_observation(observation).await;
|
||||
}
|
||||
```
|
||||
|
||||
La query utilise les bornes inclusives de `RawSlotRange`, la direction demandée et le `RawPageLimit` exact. Les lignes `Purged` sont exclues. L'ordre est `(slot, signature)` dans les deux directions et la continuation utilise un cursor opaque de 109 octets lié au réseau, à la direction et aux bornes de la query.
|
||||
Cette opération ne crée jamais la transaction canonique. Une référence absente retourne `ReferenceNotFound`; une transaction purgée produit l'outcome `NotRecorded` prévu par l'API.
|
||||
|
||||
Le cursor peut être transmis uniquement à une query ayant le même binding. Un changement de réseau/range/direction ou des bytes hostiles produit `QueryInvalid` avant acquisition du pool. Le backend n'utilise aucun `OFFSET` et ne fournit pas de snapshot transactionnel entre deux pages.
|
||||
## 9. Paginer les transactions
|
||||
|
||||
La seule limite physique est liée au `LIMIT + 1` PostgreSQL : `requested <= i64::MAX - 1`. Au-delà, `PageLimitUnsupported` est retourné ; la demande n'est jamais ramenée à 100, 500, 1000 ou une autre policy de worker.
|
||||
```rust
|
||||
async fn list_transactions(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
query: &ksp_store_api::RawTransactionQuery,
|
||||
) -> std::result::Result<ksp_store_api::RawPage<ksp_store_api::RawTransactionReference>, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
return backend.list_raw_transactions(query).await;
|
||||
}
|
||||
```
|
||||
|
||||
La navigation est keyset sur `(slot, signature)` et exclut les tombstones `Purged`. Le cursor retourné est opaque et lié au réseau, à la direction et aux bornes de slots de la query qui l'a produit.
|
||||
|
||||
Le backend n'utilise pas `OFFSET` et n'impose pas de plafond métier arbitraire. La seule borne exposée ici provient de la représentation physique de `LIMIT + 1` dans PostgreSQL.
|
||||
|
||||
## 10. Appliquer une transition de rétention
|
||||
|
||||
Depuis `0.3.3-pre.007`, le backend expose :
|
||||
```rust
|
||||
async fn apply_retention(
|
||||
backend: &ksp_store_postgres_lib::PostgresBackend,
|
||||
transition: ksp_store_api::RawTransactionRetentionTransition,
|
||||
) -> std::result::Result<ksp_store_api::RawRetentionWriteOutcome, ksp_store_postgres_lib::PostgresBackendError> {
|
||||
return backend.transition_raw_transaction_retention(transition).await;
|
||||
}
|
||||
```
|
||||
|
||||
Le backend applique la transition choisie par le caller ; il ne décide pas de la policy d'éligibilité. Les transitions physiques prises en charge sont `Full -> Archived` puis `Archived -> Purged`.
|
||||
|
||||
Une transition impliquant `Compacted` est refusée avec `PostgresBackendErrorKind::RetentionCompactionUnsupported` tant qu'aucune représentation compactée réelle n'est disponible.
|
||||
|
||||
## 11. Classifier les erreurs sans fuite
|
||||
|
||||
```rust
|
||||
let outcome = backend.transition_raw_transaction_retention(transition).await;
|
||||
fn classify(error: &ksp_store_postgres_lib::PostgresBackendError) {
|
||||
match error.kind() {
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::Conflict => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::DataInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::PageLimitUnsupported => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::QueryInvalid => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReadFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ReferenceNotFound => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::RetentionCompactionUnsupported => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WriteFailed => {}
|
||||
ksp_store_postgres_lib::PostgresBackendErrorKind::WrongNetwork => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let _safe_phase = error.phase();
|
||||
}
|
||||
```
|
||||
|
||||
Le backend ne choisit jamais lui-même la policy de rétention. Le caller fournit un `RawTransactionRetentionTransition` avec `expected` et `target`; PostgreSQL verrouille le canonical avec `FOR UPDATE`, puis retourne `Applied`, `AlreadyAtTarget` ou `ExpectedStateMismatch` selon l'état réellement observé.
|
||||
`PostgresBackendError` conserve uniquement une classification KSP et une phase statique. Ne pas reconstruire de diagnostic utilisateur à partir d'une erreur brute PostgreSQL.
|
||||
|
||||
Les transitions physiques supportées sont `Full -> Archived` puis `Archived -> Purged`. L'archivage copie le payload exact vers la relation archive avant de retirer les octets chauds, et la purge supprime cette archive puis efface le block time en conservant uniquement le tombstone minimal. Tout est transactionnel : aucun état intermédiaire n'est committé.
|
||||
## 12. Limites du backend direct
|
||||
|
||||
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.
|
||||
Le backend ne lit aucune variable d'environnement et ne possède aucune sélection de target Config. Les applications, jobs et workers doivent normalement passer par `ksp-store-lib`.
|
||||
|
||||
## 11. Capabilities traitées directement
|
||||
|
||||
Depuis `0.3.3-pre.008`, `PostgresBackend` implémente directement :
|
||||
|
||||
```text
|
||||
RawTransactionRead
|
||||
RawTransactionWrite
|
||||
RawTransactionObservationRead
|
||||
RawTransactionObservationWrite
|
||||
RawTransactionRetentionRead
|
||||
RawTransactionRetentionWrite
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
## 13. Exécuter la preuve PostgreSQL live RawTransaction
|
||||
|
||||
Le test `postgres_raw_transaction_live` exige une base PostgreSQL dédiée et vide de toute table KSP gérée. Il lit son URI sur l’entrée standard afin de ne pas contourner Config par une variable d’environnement de test :
|
||||
|
||||
```bash
|
||||
printf '%s\n' '<URI_POSTGRES_DEDIEE>' | cargo test -p ksp-store-postgres-lib --test postgres_raw_transaction_live -- --ignored --nocapture --test-threads=1
|
||||
```
|
||||
|
||||
Le test refuse de démarrer si une table KSP V000/V001 existe déjà. Il ne logge pas l’URI et ne supprime que le schéma qu’il a prouvé absent avant son propre bootstrap.
|
||||
|
||||
Le gate technique final `0.3.3-pre.011` a rejoué cette preuve sur PostgreSQL 17 avec succès. Pour l'inventaire précis des scénarios et des corrections de compatibilité catalogue, voir [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md).
|
||||
Les capabilities `RawAccount*` ne sont pas implémentées par ce backend. Les décisions de batch, priorité, backlog, scheduling et policy de rétention restent hors de sa responsabilité.
|
||||
|
||||
125
deltas/0.3.3/pre.012-fix.001.md
Normal file
125
deltas/0.3.3/pre.012-fix.001.md
Normal file
@@ -0,0 +1,125 @@
|
||||
<!-- file: deltas/0.3.3/pre.012-fix.001.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.3.3-pre.012-fix.001` — conformité des rôles documentaires
|
||||
|
||||
## 1. Base requise
|
||||
|
||||
Base directe attendue :
|
||||
|
||||
```text
|
||||
0.3.3-pre.012
|
||||
workspace.package.version = 0.3.3-pre.12
|
||||
```
|
||||
|
||||
Le gate opérateur de `pre.012` du 30 août 2026 est entièrement propre.
|
||||
|
||||
## 2. Motif du correctif
|
||||
|
||||
La réconciliation `pre.012` avait laissé dans les guides `USAGE.md` des éléments qui appartiennent à d'autres familles documentaires :
|
||||
|
||||
```text
|
||||
chronologie 0.3.3-pre.*
|
||||
preuve de gate/live
|
||||
statut de validation
|
||||
renvois à une release future
|
||||
```
|
||||
|
||||
Ce contenu viole le rôle déjà défini par `KSP-REL-013` et `docs/rules/FILE_CONTRACTS.md` : un `USAGE.md` doit être durable, sans notes de version et centré sur l'utilisation concrète de la surface publique.
|
||||
|
||||
Le README PostgreSQL contenait également une chronologie de prerelease et un récit de preuve live qui appartiennent à `docs/validation` et aux deltas.
|
||||
|
||||
## 3. Correction
|
||||
|
||||
Les deux `USAGE.md` Store sont réécrits comme guides pratiques :
|
||||
|
||||
- dépendance et préconditions ;
|
||||
- construction/résolution des settings ;
|
||||
- ouverture, health et fermeture ;
|
||||
- lecture `RawTransaction` ;
|
||||
- pagination keyset ;
|
||||
- acquisition canonique atomique ;
|
||||
- lecture/écriture d'observations ;
|
||||
- lecture et transition de rétention ;
|
||||
- classification sûre des erreurs ;
|
||||
- limites de responsabilité.
|
||||
|
||||
Les exemples utilisent la surface publique actuelle et évitent toute chronologie de release.
|
||||
|
||||
Les deux README restent descriptifs : responsabilité, frontières, architecture runtime, surface courante et hors périmètre. Les preuves et résultats de gate restent dans la validation et les deltas.
|
||||
|
||||
## 4. Règles
|
||||
|
||||
Aucune nouvelle règle n'est ajoutée : les contrats canoniques existaient déjà.
|
||||
|
||||
```text
|
||||
KSP-REL-013
|
||||
DOC-CRATE-002
|
||||
DOC-CRATE-004
|
||||
docs/rules/FILE_CONTRACTS.md — Documentation durable des crates et applications
|
||||
```
|
||||
|
||||
Le correctif remet les documents en conformité avec ces règles au lieu de dupliquer la norme.
|
||||
|
||||
## 5. Version Cargo
|
||||
|
||||
```text
|
||||
0.3.3-pre.12
|
||||
-> 0.3.3-pre.12.fix.1
|
||||
```
|
||||
|
||||
## 6. Invariants techniques
|
||||
|
||||
Aucun changement n'est apporté à :
|
||||
|
||||
```text
|
||||
src/**
|
||||
tests/**
|
||||
config/**
|
||||
docs/architecture/**
|
||||
docs/rules/**
|
||||
migrations/**
|
||||
Cargo.toml de crate
|
||||
dépendances/features
|
||||
```
|
||||
|
||||
Les migrations V000/V001 et leurs checksums restent inchangés.
|
||||
|
||||
## 7. Fichiers ajoutés
|
||||
|
||||
```text
|
||||
deltas/0.3.3/pre.012-fix.001.md
|
||||
```
|
||||
|
||||
## 8. Fichiers modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
crates/ksp-store-lib/README.md
|
||||
crates/ksp-store-lib/USAGE.md
|
||||
crates/ksp-store-postgres-lib/README.md
|
||||
crates/ksp-store-postgres-lib/USAGE.md
|
||||
docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md
|
||||
docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md
|
||||
```
|
||||
|
||||
## 9. Fichiers supprimés
|
||||
|
||||
Aucun.
|
||||
|
||||
## 10. Gate attendu
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Aucun replay PostgreSQL n'est nécessaire : le correctif ne modifie ni code, ni test, ni SQL, ni migration.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md -->
|
||||
<!-- version: 13 -->
|
||||
<!-- version: 14 -->
|
||||
|
||||
# Plan `0.3.3` — Store/PostgreSQL RawTransaction vertical slice
|
||||
|
||||
@@ -42,10 +42,10 @@ Tranches techniques validées :
|
||||
Tranche courante :
|
||||
|
||||
```text
|
||||
0.3.3-pre.012 — réconciliation documentaire finale
|
||||
0.3.3-pre.012-fix.001 — conformité des documents durables
|
||||
```
|
||||
|
||||
Tous les gates techniques jusqu'à `pre.011` sont verts. Le gate final a rejoué audits, workspace check/Clippy, tests ciblés, façade avec `--no-default-features`, `cargo test --workspace`, graphes Cargo et le live `postgres_raw_transaction_live` sur PostgreSQL 17. `pre.012` ne rouvre aucun comportement, SQL, migration ou Config runtime ; elle fige uniquement la documentation durable et l'état de release candidate.
|
||||
La réconciliation documentaire technique est acquise. Le correctif courant recentre les documents durables sur leur contrat : README descriptifs, USAGE orientés utilisation sans chronologie de release, et preuves conservées exclusivement dans validation/deltas. Aucun comportement, SQL, migration ou Config runtime n'est rouvert.
|
||||
|
||||
## 2. Sources et autorité
|
||||
|
||||
@@ -1227,6 +1227,13 @@ Tranche matérialisée :
|
||||
- `ROADMAP.md` ferme `0.3.3`, garde `RawAccountState` en `0.3.4` et distingue la rétention physique acquise de la future policy worker/compaction ;
|
||||
- aucun `src/**`, test, Config, architecture, règle, migration ou checksum n'est modifié.
|
||||
|
||||
### `0.3.3-pre.012-fix.001` — conformité des documents durables
|
||||
|
||||
- nettoyer les README de toute chronologie de prerelease et de toute preuve de gate ;
|
||||
- rendre les USAGE indépendants de la release et centrés sur préconditions, appels publics, exemples et limites opérationnelles ;
|
||||
- conserver les preuves, échecs et résultats de gate dans la validation et les deltas ;
|
||||
- ne modifier aucun code, test, Config runtime, architecture ou migration.
|
||||
|
||||
### `0.3.3-pre.013` — préparation publication
|
||||
|
||||
- version/payload minimal de publication ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md -->
|
||||
<!-- version: 24 -->
|
||||
<!-- version: 25 -->
|
||||
|
||||
# Validation `0.3.3` — Store/PostgreSQL RawTransaction vertical slice
|
||||
|
||||
@@ -860,9 +860,17 @@ cargo check -p ksp-store-lib --no-default-features
|
||||
|
||||
### `pre.012` — réconciliation documentaire finale
|
||||
|
||||
- [MATÉRIALISÉ] README/USAGE Store façade/backend réconciliés ;
|
||||
- [MATÉRIALISÉ] plan `024`, validation `020` et indexes durables finalisés ;
|
||||
- [MATÉRIALISÉ] `CHANGELOG.md` et `ROADMAP.md` alignés sur la surface stable candidate ;
|
||||
- [INVARIANT] aucun code, test, Config runtime, architecture, règle, SQL ou migration n'est rouvert ;
|
||||
- [À FAIRE] gate documentaire/opérateur de `pre.012`.
|
||||
- [PASS] gate documentaire/opérateur complet propre le 2026-08-30 ;
|
||||
- [PASS] audits Rust/Markdown, workspace check, Clippy all-targets et tests ciblés Store/Config ;
|
||||
- [PASS] `cargo check -p ksp-store-lib --no-default-features` ;
|
||||
- [PASS] plan `024`, validation `020`, indexes durables, `CHANGELOG.md` et `ROADMAP.md` réconciliés ;
|
||||
- [INVARIANT] aucun code, test, Config runtime, architecture, règle, SQL ou migration n'est rouvert.
|
||||
|
||||
### `pre.012-fix.001` — conformité des rôles documentaires
|
||||
|
||||
- [CORRECTION] les `USAGE.md` façade/backend ne contiennent plus de chronologie `pre.*`, de preuve de gate ou de renvoi à la validation comme contenu d'usage ;
|
||||
- [CORRECTION] les `USAGE.md` sont restructurés en guide pratique avec exemples pour lifecycle, lecture, pagination, écriture, observation et rétention ;
|
||||
- [CORRECTION] les README restent descriptifs et ne portent plus l'historique des prereleases ni le récit de validation live ;
|
||||
- [CONFORMITÉ] `KSP-REL-013` et `docs/rules/FILE_CONTRACTS.md` étaient déjà normatifs ; aucune règle supplémentaire n'est nécessaire ;
|
||||
- [INVARIANT] aucun code, test, Config runtime, architecture, SQL ou migration n'est modifié ;
|
||||
- [À FAIRE] gate documentaire/opérateur du correctif.
|
||||
|
||||
Reference in New Issue
Block a user