287 lines
9.0 KiB
Markdown
287 lines
9.0 KiB
Markdown
<!-- file: deltas/0.3.6/pre.007.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# Delta `0.3.6-pre.007` — persistance Store atomique et idempotence Backfill
|
|
|
|
## Base requise
|
|
|
|
```text
|
|
0.3.6-pre.006-fix.002 appliquée
|
|
workspace.package.version = 0.3.6-pre.6.fix.2
|
|
```
|
|
|
|
Le gate opérateur fourni pour cette base confirme :
|
|
|
|
```text
|
|
cargo fmt --all PASS
|
|
python3 scripts/audit_rust_workspace_rules.py PASS / clean
|
|
python3 scripts/audit_markdown_tables.py ... PASS / clean (264 tables / 151 fichiers)
|
|
cargo check --workspace PASS
|
|
cargo clippy --workspace --all-targets PASS / aucun warning
|
|
cargo test -p ksp-job-backfill-lib PASS
|
|
unitaires 20 PASS
|
|
dependency_boundary 2 PASS
|
|
public_api 3 PASS
|
|
release_completeness 2 PASS
|
|
cargo tree -p ksp-job-backfill-lib --edges normal exécuté
|
|
cargo tree -p ksp-job-backfill-lib -e features exécuté
|
|
```
|
|
|
|
## Objectif
|
|
|
|
Matérialiser exclusivement `pre.007` du plan 027 : composer la persistance normale d'un résultat d'hydratation Backfill avec les contrats Store déjà existants, sans prélecture, sans second chemin d'écriture et sans ouvrir la concurrence, la frontier ou le checkpoint de `pre.008`.
|
|
|
|
L'identité canonique reste :
|
|
|
|
```text
|
|
RawTransactionReference = (RawNetworkId, RawTransactionSignature)
|
|
```
|
|
|
|
Provider, endpoint et protocole ne participent toujours qu'à la provenance de l'observation.
|
|
|
|
## Contrat public Backfill
|
|
|
|
La tranche ajoute :
|
|
|
|
```text
|
|
persist_backfill_hydration
|
|
BackfillPersistenceOutcome
|
|
BackfillEntityPersistence
|
|
BackfillObservationPersistence
|
|
ERROR_CODE_BACKFILL_PERSISTENCE_INVALID
|
|
```
|
|
|
|
`BackfillPersistenceOutcome` conserve la référence réseau + signature et projette séparément l'état de l'entité canonique et celui de l'observation. Cette projection évite de réduire un conflit, une purge ou une nouvelle observation à un compteur `skip` ambigu.
|
|
|
|
Dispositions entité :
|
|
|
|
```text
|
|
Inserted
|
|
AlreadyPresent
|
|
SkippedPurged
|
|
Missing
|
|
Conflict
|
|
```
|
|
|
|
Dispositions observation :
|
|
|
|
```text
|
|
Inserted
|
|
AlreadyPresent
|
|
NotRecorded
|
|
NotApplicable
|
|
```
|
|
|
|
## Composition Store atomique
|
|
|
|
Pour `BackfillHydrationOutcome::Available`, la bibliothèque consomme l'acquisition en mémoire puis appelle exclusivement :
|
|
|
|
```text
|
|
ksp_store_lib::RawTransactionWrite::persist_raw_transaction_acquisition
|
|
```
|
|
|
|
avec :
|
|
|
|
```text
|
|
RawTransactionAcquisitionMode::Normal
|
|
```
|
|
|
|
La transaction et son observation restent donc une seule opération logique atomique possédée par Store. La tranche n'appelle jamais `record_raw_transaction_observation` séparément et n'introduit aucune prélecture `get_raw_transaction` avant écriture.
|
|
|
|
`ForceRehydrate` n'est pas exposé par le chemin Backfill normal.
|
|
|
|
## Réseau
|
|
|
|
Avant toute écriture, le réseau logique exposé par le snapshot du Store doit être identique au `RawNetworkId` de la référence hydratée. Un mismatch est rejeté par :
|
|
|
|
```text
|
|
ERROR_CODE_BACKFILL_PERSISTENCE_INVALID
|
|
field = store.network
|
|
```
|
|
|
|
Ce contrôle n'utilise ni endpoint, ni provider, ni protocole comme identité.
|
|
|
|
Une incohérence interne entre la référence annoncée, la transaction et l'observation est également rejetée avant soumission Store.
|
|
|
|
## Outcomes Store admis
|
|
|
|
En mode normal, les combinaisons Store suivantes sont acceptées et projetées explicitement :
|
|
|
|
```text
|
|
RawEntityWriteOutcome::Inserted
|
|
+ RawObservationWriteOutcome::Inserted
|
|
-> Inserted / Inserted
|
|
|
|
RawEntityWriteOutcome::AlreadyPresent
|
|
+ RawObservationWriteOutcome::Inserted
|
|
-> AlreadyPresent / Inserted
|
|
|
|
RawEntityWriteOutcome::AlreadyPresent
|
|
+ RawObservationWriteOutcome::AlreadyPresent
|
|
-> AlreadyPresent / AlreadyPresent
|
|
|
|
RawEntityWriteOutcome::SkippedPurged
|
|
+ RawObservationWriteOutcome::NotRecorded
|
|
-> SkippedPurged / NotRecorded
|
|
```
|
|
|
|
`RawEntityWriteOutcome::Rehydrated` est impossible dans le chemin normal et est rejeté, comme toute autre combinaison incohérente.
|
|
|
|
## Missing
|
|
|
|
`BackfillHydrationOutcome::Missing(reference)` devient :
|
|
|
|
```text
|
|
Missing / NotApplicable
|
|
```
|
|
|
|
sans aucune écriture Store. La tranche ne persiste toujours ni observation `missing`, ni observation d'échec fabriquée ; ces contrats n'existent pas dans le Store RAW transaction actuel.
|
|
|
|
## Conflit et erreurs
|
|
|
|
Une erreur Store dont le code stable est :
|
|
|
|
```text
|
|
ERROR_CODE_RAW_CONFLICT
|
|
```
|
|
|
|
est projetée en :
|
|
|
|
```text
|
|
Conflict / NotRecorded
|
|
```
|
|
|
|
Elle reste donc visible et distincte de l'idempotence. Les tranches de frontier/snapshot pourront ensuite la traiter comme trou/échec sans parsing de message d'erreur.
|
|
|
|
Toutes les autres erreurs Store sont propagées telles quelles.
|
|
|
|
## Idempotence
|
|
|
|
La même acquisition déterministe peut produire successivement :
|
|
|
|
```text
|
|
Inserted / Inserted
|
|
AlreadyPresent / AlreadyPresent
|
|
```
|
|
|
|
sans doublon logique. Une entité déjà présente peut aussi recevoir une nouvelle observation légitime :
|
|
|
|
```text
|
|
AlreadyPresent / Inserted
|
|
```
|
|
|
|
La distinction repose entièrement sur les outcomes Store officiels ; Job ne reconstruit aucune logique d'idempotence physique.
|
|
|
|
## Tests matérialisés
|
|
|
|
La tranche ajoute **9 tests unitaires**, portant le total de `ksp-job-backfill-lib` à **29 unitaires** :
|
|
|
|
- `Missing` ne soumet aucune écriture ;
|
|
- mismatch réseau rejeté avant Store ;
|
|
- insert atomique `Inserted/Inserted` ;
|
|
- entité existante + nouvelle observation ;
|
|
- relance idempotente `AlreadyPresent/AlreadyPresent` ;
|
|
- purge normale `SkippedPurged/NotRecorded` ;
|
|
- conflit Store explicite et non confondu avec idempotence ;
|
|
- erreur Store non conflit propagée ;
|
|
- combinaisons impossibles et mismatch transaction/observation rejetés.
|
|
|
|
Une canarie publique supplémentaire porte les canaries d'intégration à **8** :
|
|
|
|
```text
|
|
dependency_boundary 2
|
|
public_api 4
|
|
release_completeness 2
|
|
```
|
|
|
|
Les canaries statiques verrouillent l'usage de l'écriture atomique, du mode `Normal`, l'absence d'écriture d'observation séparée, l'absence de `ForceRehydrate`, l'absence de backend Store direct et la fermeture du checkpoint/runtime de `pre.008`.
|
|
|
|
## Fichiers ajoutés
|
|
|
|
```text
|
|
crates/ksp-job-backfill-lib/src/persistence.rs
|
|
crates/ksp-job-backfill-lib/unit_tests/persistence.rs
|
|
deltas/0.3.6/pre.007.md
|
|
```
|
|
|
|
## Fichiers modifiés
|
|
|
|
```text
|
|
Cargo.toml
|
|
crates/ksp-job-backfill-lib/src/error.rs
|
|
crates/ksp-job-backfill-lib/src/lib.rs
|
|
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs
|
|
crates/ksp-job-backfill-lib/tests/public_api.rs
|
|
crates/ksp-job-backfill-lib/tests/release_completeness.rs
|
|
docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md
|
|
docs/validation/023-V0_3_6_JOB_API_BACKFILL.md
|
|
```
|
|
|
|
## Versions d'en-tête
|
|
|
|
Les fichiers Rust/TOML modifiés incrémentent tous leur version d'en-tête exactement d'une unité :
|
|
|
|
```text
|
|
Cargo.toml 404 -> 405
|
|
crates/ksp-job-backfill-lib/src/error.rs 2 -> 3
|
|
crates/ksp-job-backfill-lib/src/lib.rs 2 -> 3
|
|
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs 2 -> 3
|
|
crates/ksp-job-backfill-lib/tests/public_api.rs 2 -> 3
|
|
crates/ksp-job-backfill-lib/tests/release_completeness.rs 2 -> 3
|
|
```
|
|
|
|
Les deux nouveaux fichiers Rust commencent à `version: 1`. Le plan 027 et la validation 023 passent de `12` à `13`. Le présent delta commence à `version: 1`.
|
|
|
|
## Version workspace
|
|
|
|
La tranche modifie du Rust :
|
|
|
|
```text
|
|
workspace.package.version = 0.3.6-pre.7
|
|
delivery = 0.3.6-pre.007
|
|
commit = v0.3.6-pre.007
|
|
```
|
|
|
|
Aucun tag prerelease.
|
|
|
|
## Fichiers supprimés
|
|
|
|
Aucun.
|
|
|
|
## Frontières conservées
|
|
|
|
- aucune prélecture Store avant hydratation/persistance ;
|
|
- aucun backend Store direct ;
|
|
- aucun `ForceRehydrate` ;
|
|
- aucune persistence de missing/failed artificielle ;
|
|
- aucune concurrence d'hydratation concrète ;
|
|
- aucune frontier contiguë ;
|
|
- aucun checkpoint/reprise runtime ;
|
|
- aucun snapshot Backfill concret ;
|
|
- aucune nouvelle dépendance ou feature ;
|
|
- aucune modification Transport, Store, Job API, Config, README, USAGE, CHANGELOG ou ROADMAP ;
|
|
- aucun code kbot3 copié.
|
|
|
|
## Validations dans l'environnement d'assemblage
|
|
|
|
L'environnement d'assemblage exécute les audits statiques et contrôles d'archive mais ne possède ni `cargo`, ni `rustc`, ni `rustfmt`. Les tests Rust matérialisés ne sont donc pas annoncés comme exécutés localement.
|
|
|
|
## 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.6
|
|
cargo check --workspace
|
|
cargo clippy --workspace --all-targets
|
|
cargo test -p ksp-job-backfill-lib
|
|
cargo tree -p ksp-job-backfill-lib --edges normal
|
|
cargo tree -p ksp-job-backfill-lib -e features
|
|
```
|
|
|
|
Résultat attendu : **29 tests unitaires + 8 canaries d'intégration**, aucun warning Clippy, et dépendance Store toujours limitée à `ksp-store-lib` avec `default-features = false`.
|
|
|
|
## Questions ouvertes
|
|
|
|
Aucune pour `pre.007`. `pre.008` reste fermé tant que ce gate n'est pas vert.
|