Files
khadhroony-solana-project/deltas/0.3.3/pre.005.md
2026-08-30 12:42:50 +02:00

7.9 KiB

Delta 0.3.3-pre.005 — écriture atomique RawTransaction + observation

1. Base requise

0.3.3-pre.4

Le gate opérateur fourni pour pre.004 est entièrement vert :

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 :

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 à :

0.3.3-pre.5

4. Pré-I/O

Avant pool.get() :

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 :

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 :

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 :

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 :

payload hot absent
payload archive absent
block_time absent

Il compare ensuite uniquement les métadonnées conservées par le tombstone :

reference
slot
format_id
format_version
content_hash

Une divergence produit Conflict.

Pour un tombstone compatible :

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 :

INSERT ...
ON CONFLICT (observation_key) DO NOTHING
RETURNING observation_key

Après collision :

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 :

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 :

SELECT retention_state FROM canonical FOR UPDATE

Puis :

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 :

Conflict
ReferenceNotFound
WriteFailed

Ces erreurs restent composées exclusivement de :

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 :

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 :

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 :

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 :

V000 d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
V001 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51

14. Fichiers modifiés

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

deltas/0.3.3/pre.005.md

16. Fichiers supprimés

Aucun.

17. Hors scope confirmé

Aucun changement n'est apporté à :

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

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.