11 KiB
Delta 0.3.6-pre.008 — concurrence bornée, frontier contiguë et checkpoint Backfill
Base requise
0.3.6-pre.007 appliquée
workspace.package.version = 0.3.6-pre.7
Le gate opérateur fourni pour cette base confirme :
cargo fmt --all PASS
python3 scripts/audit_rust_workspace_rules.py PASS / clean
python3 scripts/audit_markdown_tables.py ... PASS / clean (264 tables / 152 fichiers)
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS / aucun warning
cargo test -p ksp-job-backfill-lib PASS
unitaires 29 PASS
dependency_boundary 2 PASS
public_api 4 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.008 du plan 027 : exécuter les candidats découverts avec une concurrence métier bornée, réconcilier les fins hors ordre par une frontier contiguë et produire un checkpoint sûr pour reprise contrôlée.
La tranche n'ouvre pas encore l'annulation coopérative, le snapshot latest-value concret, les états terminaux complets ou les courses d'annulation de pre.009.
Checkpoint caller-owned
La tranche ajoute le type public opaque :
BackfillCheckpoint
Il contient uniquement :
JobId
BackfillScopeFingerprint
completed_prefix
resume_before privé si scope BeforeAddress
Il ne contient ni payload RAW, ni URL, ni provider, ni endpoint, ni credential, ni liste de signatures.
BackfillRequest::with_checkpoint valide obligatoirement :
checkpoint.job_id == request.job_id
checkpoint.scope_fingerprint == request.scope_fingerprint
Un checkpoint incompatible est rejeté par :
ERROR_CODE_BACKFILL_CHECKPOINT_INVALID
Aucune table Store, migration ou persistance durable de Job n'est créée. Le checkpoint est caller-owned et qualifie uniquement une reprise contrôlée ; la v0.3.6 ne revendique pas une reprise crash-safe multi-processus.
Sémantique de reprise par scope
LatestAddress
La reprise repart volontairement sans curseur RPC depuis la vue latest courante. L'idempotence Store absorbe les candidats déjà durables. Le préfixe historique du checkpoint n'est jamais utilisé comme offset de la nouvelle vue latest.
BeforeAddress
Le checkpoint conserve comme curseur exclusif le dernier candidat du préfixe contigu durable. Si aucun candidat nouveau n'est durable, le curseur précédent — ou l'ancre initiale — est conservé. Le compteur completed_prefix est cumulatif entre reprises contrôlées.
AfterAddress
Le scope borné est rejoué et seul le préfixe contigu validé est ignoré. Si la découverte s'arrête sur :
AfterAnchorNotReached
aucun progrès nouveau de checkpoint n'est publié, même si certaines hydrations observées ont fini durablement ; le checkpoint précédent est conservé lorsqu'il existe.
ExplicitSignatures
La liste dédupliquée et fingerprintée est rejouée et seul son préfixe contigu déjà validé est ignoré.
Frontier contiguë
Chaque candidat conserve son index stable de découverte. Une bitmap privée bornée à max_candidates <= 10_000 suit les résultats durables.
La frontier avance uniquement pour :
Inserted
AlreadyPresent
SkippedPurged
Les résultats suivants créent un trou et ne sont jamais marqués durables :
Missing
Conflict
erreur Transport
conversion invalide
erreur Store
Une completion hors ordre ne saute jamais un index incomplet. Les tests reproduisent explicitement :
index 1 durable -> frontier 0
index 0 durable -> frontier 2
index 3 durable -> frontier 2
index 2 durable -> frontier 4
Exécution concurrente bornée
La tranche ajoute :
execute_backfill_discovery
BackfillExecutionBatch
L'implémentation utilise un FuturesUnordered privé et ne garde jamais plus de :
request.hydration_concurrency()
futures candidates en vol.
Aucun tokio::spawn, timer Job, retry Job, pacing Job ou choix d'endpoint n'est introduit. Transport reste propriétaire de sa politique réseau et Store de sa persistance.
Le batch expose uniquement des faits bornés utiles à la preuve de frontier : candidats, admis, terminés, durables, trous, maximum en vol, préfixe contigu local, checkpoint et premier code d'échec fatal.
Missing, conflit et erreurs
Missing est un trou non fatal : les admissions suivantes continuent, mais la frontier reste bloquée tant que ce trou existe.
Un Conflict ou toute erreur Transport/conversion/Store finale :
- devient le premier
failure_codestable du batch ; - arrête immédiatement les nouvelles admissions ;
- laisse finir toutes les futures déjà admises ;
- produit ensuite le checkpoint correspondant au dernier préfixe réellement durable.
Cette phase ne publie pas encore l'état Job terminal Failed/Partial; cette projection appartient au snapshot/runtime de pre.009.
Horodatage d'acquisition
Le chemin public d'exécution produit le RawTimestamp de réception à partir de SystemTime immédiatement avant l'hydratation. Une horloge avant epoch ou un timestamp non représentable devient une erreur structurée :
ERROR_CODE_BACKFILL_EXECUTION_INVALID
Aucun clock/provider type n'entre dans l'API publique.
Dépendances
La crate ajoute comme dépendance normale :
futures-util = { workspace = true, features = ["std"] }
Cette dépendance existe déjà dans [workspace.dependencies]; aucune nouvelle version externe n'est introduite.
Tokio reste exclusivement en dev-dependencies pour les tests async et aucun type Tokio/Futures n'est exposé par la crate root.
Tests matérialisés
La tranche ajoute 10 tests unitaires, portant le total à 39 unitaires :
- frontier hors ordre strictement contiguë ;
- checkpoint rejeté sur mauvais
JobIdou fingerprint ; - checkpoint Before cumulatif avec curseur du dernier candidat contigu ;
- After sans ancre atteinte n'avance jamais le checkpoint ;
- Before reprend au curseur checkpoint plutôt qu'à l'ancre initiale ;
- Latest reprend sans curseur depuis la vue courante ;
- concurrence observée strictement bornée malgré fins hors ordre ;
- Missing non fatal mais bloquant pour la frontier ;
- conflit arrêtant les admissions et drainant le travail déjà en vol ;
- reprise Explicit sautant uniquement le préfixe checkpointé.
Une canarie publique supplémentaire porte les canaries d'intégration à 9 :
dependency_boundary 2
public_api 5
release_completeness 2
Les canaries verrouillent également l'inventaire exact des modules, futures-util comme détail privé normal, l'absence de Tokio normal, l'absence de retry/pacing/endpoint selection Job et la fermeture des surfaces BackfillSnapshot/BackfillJobHandle de pre.009.
Fichiers ajoutés
crates/ksp-job-backfill-lib/src/checkpoint.rs
crates/ksp-job-backfill-lib/src/execution.rs
crates/ksp-job-backfill-lib/unit_tests/checkpoint.rs
crates/ksp-job-backfill-lib/unit_tests/execution.rs
deltas/0.3.6/pre.008.md
Fichiers modifiés
Cargo.toml
crates/ksp-job-backfill-lib/Cargo.toml
crates/ksp-job-backfill-lib/src/discovery.rs
crates/ksp-job-backfill-lib/src/error.rs
crates/ksp-job-backfill-lib/src/lib.rs
crates/ksp-job-backfill-lib/src/persistence.rs
crates/ksp-job-backfill-lib/src/request.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
crates/ksp-job-backfill-lib/unit_tests/discovery.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é :
Cargo.toml 405 -> 406
crates/ksp-job-backfill-lib/Cargo.toml 2 -> 3
crates/ksp-job-backfill-lib/src/discovery.rs 2 -> 3
crates/ksp-job-backfill-lib/src/error.rs 3 -> 4
crates/ksp-job-backfill-lib/src/lib.rs 3 -> 4
crates/ksp-job-backfill-lib/src/persistence.rs 1 -> 2
crates/ksp-job-backfill-lib/src/request.rs 2 -> 3
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs 3 -> 4
crates/ksp-job-backfill-lib/tests/public_api.rs 3 -> 4
crates/ksp-job-backfill-lib/tests/release_completeness.rs 3 -> 4
crates/ksp-job-backfill-lib/unit_tests/discovery.rs 1 -> 2
Les quatre nouveaux fichiers Rust commencent à version: 1. Le plan 027 et la validation 023 passent de 13 à 14. Le présent delta commence à version: 1.
Version workspace
La tranche modifie du Rust :
workspace.package.version = 0.3.6-pre.8
delivery = 0.3.6-pre.008
commit = v0.3.6-pre.008
Aucun tag prerelease.
Fichiers supprimés
Aucun.
Frontières conservées
- aucune persistence durable de checkpoint dans Store ;
- aucune table ou migration Job ;
- aucun retry/pacing/timer Job ;
- aucune sélection provider/endpoint Job ;
- aucun
ForceRehydrate; - aucun
tokio::spawn; - aucun type Tokio/Futures public ;
- aucune annulation coopérative de
pre.009; - aucun
BackfillSnapshotlatest-value concret ; - aucune application ou Worker ;
- aucun changement README/USAGE/CHANGELOG/ROADMAP/prompt.
Validations exécutées dans l'environnement d'assemblage
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
L'environnement d'assemblage ne fournit ni cargo, ni rustc, ni rustfmt; aucun résultat Rust de pre.008 n'est donc revendiqué localement.
Gate opérateur demandé
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 après gate : 39 unitaires + 9 canaries, sans warning. Un échec reste dans la lane pre.008-fix.001; un gate vert ouvre pre.009.