v0.3.3-pre.005
This commit is contained in:
324
deltas/0.3.3/pre.005.md
Normal file
324
deltas/0.3.3/pre.005.md
Normal file
@@ -0,0 +1,324 @@
|
||||
<!-- file: deltas/0.3.3/pre.005.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.3.3-pre.005` — écriture atomique RawTransaction + observation
|
||||
|
||||
## 1. Base requise
|
||||
|
||||
```text
|
||||
0.3.3-pre.4
|
||||
```
|
||||
|
||||
Le gate opérateur fourni pour `pre.004` est entièrement vert :
|
||||
|
||||
```text
|
||||
cargo fmt --all PASS
|
||||
audit Rust général / exports / workspace PASS
|
||||
audit Markdown PASS — 214 tables / 137 files
|
||||
cargo check --workspace PASS
|
||||
cargo clippy --workspace --all-targets PASS
|
||||
cargo test -p ksp-store-api PASS
|
||||
cargo test -p ksp-store-lib PASS
|
||||
cargo test -p ksp-store-postgres-lib PASS — 25 unit tests + canaris, live ignored
|
||||
cargo test -p ksp-config-lib PASS — 128 unit tests + ownership/public API
|
||||
cargo check -p ksp-store-lib --no-default-features PASS
|
||||
```
|
||||
|
||||
Les lectures physiques `RawTransaction` de `pre.004` sont donc acquises.
|
||||
|
||||
## 2. Objectif
|
||||
|
||||
Implémenter la tranche écriture de la vertical slice PostgreSQL sans ouvrir encore pagination ni transitions mutantes de rétention :
|
||||
|
||||
```text
|
||||
persist_raw_transaction_acquisition
|
||||
record_raw_transaction_observation
|
||||
```
|
||||
|
||||
La première opération doit rendre le canonical et son observation atomiques. Les deux opérations doivent être réellement idempotentes et ne jamais confondre une collision de clé avec un contenu identique.
|
||||
|
||||
## 3. Version
|
||||
|
||||
Le workspace passe à :
|
||||
|
||||
```text
|
||||
0.3.3-pre.5
|
||||
```
|
||||
|
||||
## 4. Pré-I/O
|
||||
|
||||
Avant `pool.get()` :
|
||||
|
||||
```text
|
||||
transaction.reference.network == backend.network
|
||||
observation.transaction.network == backend.network
|
||||
observation.transaction == transaction.reference
|
||||
```
|
||||
|
||||
Un mauvais réseau retourne `WrongNetwork`. Une acquisition composée de deux références logiques différentes retourne `Conflict` sans toucher PostgreSQL.
|
||||
|
||||
`record_raw_transaction_observation` valide également son réseau avant toute acquisition du pool.
|
||||
|
||||
## 5. Acquisition atomique canonique + observation
|
||||
|
||||
L'algorithme est :
|
||||
|
||||
```text
|
||||
BEGIN
|
||||
INSERT canonical
|
||||
ON CONFLICT (signature) DO NOTHING
|
||||
RETURNING signature
|
||||
|
||||
si inséré
|
||||
-> candidate Inserted
|
||||
|
||||
sinon
|
||||
SELECT canonical FOR UPDATE
|
||||
SELECT archive payload éventuel
|
||||
comparer le contenu réel
|
||||
|
||||
écrire/idempotenter observation
|
||||
|
||||
COMMIT
|
||||
```
|
||||
|
||||
Toute erreur après l'insert canonique, notamment une collision d'observation divergente, fait sortir sans commit : PostgreSQL rollbacke donc l'ensemble de l'acquisition.
|
||||
|
||||
Aucun `has_*` ni SELECT préventif n'est introduit.
|
||||
|
||||
## 6. Idempotence canonique
|
||||
|
||||
Pour `Full` et `Archived`, l'égalité exige exactement :
|
||||
|
||||
```text
|
||||
reference
|
||||
slot
|
||||
block_time
|
||||
format_id
|
||||
format_version
|
||||
content_hash
|
||||
payload bytes
|
||||
```
|
||||
|
||||
Le hash n'est jamais utilisé seul lorsque les octets restent disponibles.
|
||||
|
||||
Résultats :
|
||||
|
||||
```text
|
||||
première insertion -> Inserted
|
||||
même identité + contenu identique -> AlreadyPresent
|
||||
même identité + contenu divergent -> Conflict
|
||||
```
|
||||
|
||||
Le conflit backend sera projeté vers `ERROR_CODE_RAW_CONFLICT` par la façade dans `pre.008`.
|
||||
|
||||
## 7. Tombstone et ForceRehydrate
|
||||
|
||||
Lorsqu'une signature existe en `Purged`, le backend exige que la forme physique soit réellement minimale :
|
||||
|
||||
```text
|
||||
payload hot absent
|
||||
payload archive absent
|
||||
block_time absent
|
||||
```
|
||||
|
||||
Il compare ensuite uniquement les métadonnées conservées par le tombstone :
|
||||
|
||||
```text
|
||||
reference
|
||||
slot
|
||||
format_id
|
||||
format_version
|
||||
content_hash
|
||||
```
|
||||
|
||||
Une divergence produit `Conflict`.
|
||||
|
||||
Pour un tombstone compatible :
|
||||
|
||||
```text
|
||||
Normal
|
||||
-> SkippedPurged / NotRecorded
|
||||
|
||||
ForceRehydrate
|
||||
-> UPDATE payload + block_time + state=full
|
||||
-> observation dans la même transaction
|
||||
-> Rehydrated
|
||||
```
|
||||
|
||||
Le `ForceRehydrate` ne masque donc jamais une divergence détectable.
|
||||
|
||||
## 8. Observation idempotente
|
||||
|
||||
L'observation utilise :
|
||||
|
||||
```text
|
||||
INSERT ...
|
||||
ON CONFLICT (observation_key) DO NOTHING
|
||||
RETURNING observation_key
|
||||
```
|
||||
|
||||
Après collision :
|
||||
|
||||
```text
|
||||
SELECT observation ... FOR UPDATE
|
||||
```
|
||||
|
||||
Puis le modèle reconstruit est comparé intégralement à l'observation entrante : référence transaction + provenance complète.
|
||||
|
||||
Résultats :
|
||||
|
||||
```text
|
||||
key absente + insert réussi -> Inserted
|
||||
key présente + contenu identique -> AlreadyPresent
|
||||
key présente + contenu divergent -> Conflict
|
||||
```
|
||||
|
||||
## 9. Observation write séparée
|
||||
|
||||
`record_raw_transaction_observation` ne crée jamais un canonical implicite.
|
||||
|
||||
Sous transaction :
|
||||
|
||||
```text
|
||||
SELECT retention_state FROM canonical FOR UPDATE
|
||||
```
|
||||
|
||||
Puis :
|
||||
|
||||
```text
|
||||
canonical absent -> ReferenceNotFound
|
||||
canonical Purged -> NotRecorded
|
||||
Full / Archived -> insertion/idempotence observation
|
||||
```
|
||||
|
||||
Le verrou canonical prépare aussi la sérialisation future avec purge/rehydrate de `pre.007`.
|
||||
|
||||
## 10. Erreurs backend
|
||||
|
||||
`PostgresBackendErrorKind` ajoute :
|
||||
|
||||
```text
|
||||
Conflict
|
||||
ReferenceNotFound
|
||||
WriteFailed
|
||||
```
|
||||
|
||||
Ces erreurs restent composées exclusivement de :
|
||||
|
||||
```text
|
||||
kind
|
||||
phase &'static str
|
||||
```
|
||||
|
||||
Aucun texte PostgreSQL, SQLSTATE, query, bind, URI, signature, hash ou payload n'est retenu.
|
||||
|
||||
## 11. Surface backend
|
||||
|
||||
`PostgresBackend` expose désormais en plus :
|
||||
|
||||
```text
|
||||
persist_raw_transaction_acquisition
|
||||
record_raw_transaction_observation
|
||||
```
|
||||
|
||||
Les traits `RawTransactionWrite` / `RawTransactionObservationWrite` ne sont pas encore implémentés directement : la tranche finale des six traits attend `list_raw_transactions` en `pre.006`, puis la rétention mutante en `pre.007` et le dispatch façade en `pre.008`.
|
||||
|
||||
## 12. Tests déterministes
|
||||
|
||||
Le miroir `unit_tests/raw_transaction.rs` couvre en plus :
|
||||
|
||||
```text
|
||||
pré-I/O réseau + référence acquisition exacte
|
||||
égalité canonique stricte
|
||||
même hash mais payload différent -> Conflict
|
||||
tombstone compatible -> Purged match
|
||||
tombstone divergent -> Conflict
|
||||
mapping exact des origins d'observation
|
||||
```
|
||||
|
||||
Les canaris d'intégration figent :
|
||||
|
||||
```text
|
||||
INSERT unique direct sans has_*
|
||||
FOR UPDATE après collision
|
||||
observation unique idempotente
|
||||
ForceRehydrate limité à UPDATE
|
||||
aucun DELETE
|
||||
aucune pagination/cursor
|
||||
aucune transition retention mutante
|
||||
aucune impl complète de trait prématurée
|
||||
```
|
||||
|
||||
La concurrence PostgreSQL réelle et les preuves de rollback restent réservées au test live `pre.009`.
|
||||
|
||||
## 13. Migrations
|
||||
|
||||
Aucune ressource de migration n'est modifiée.
|
||||
|
||||
Checksums inchangés :
|
||||
|
||||
```text
|
||||
V000 d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
|
||||
V001 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51
|
||||
```
|
||||
|
||||
## 14. Fichiers modifiés
|
||||
|
||||
```text
|
||||
Cargo.toml
|
||||
crates/ksp-store-postgres-lib/README.md
|
||||
crates/ksp-store-postgres-lib/USAGE.md
|
||||
crates/ksp-store-postgres-lib/src/error.rs
|
||||
crates/ksp-store-postgres-lib/src/lib.rs
|
||||
crates/ksp-store-postgres-lib/src/raw_transaction.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/raw_transaction.rs
|
||||
docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md
|
||||
docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md
|
||||
```
|
||||
|
||||
## 15. Fichiers ajoutés
|
||||
|
||||
```text
|
||||
deltas/0.3.3/pre.005.md
|
||||
```
|
||||
|
||||
## 16. Fichiers supprimés
|
||||
|
||||
Aucun.
|
||||
|
||||
## 17. Hors scope confirmé
|
||||
|
||||
Aucun changement n'est apporté à :
|
||||
|
||||
```text
|
||||
ksp-store-api contrats
|
||||
ksp-store-lib dispatch métier
|
||||
list_raw_transactions / cursor
|
||||
transition Full -> Archived -> Purged
|
||||
Compacted physique
|
||||
RawAccountState
|
||||
worker/job/app
|
||||
migration SQL V000/V001
|
||||
```
|
||||
|
||||
## 18. Gate opérateur
|
||||
|
||||
```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 de fondation reste `#[ignore]`. Aucune URI réelle n'est requise pour cette tranche.
|
||||
Reference in New Issue
Block a user