8.1 KiB
Delta 0.2.7-pre.007 — reconnect borné et resubscribe déterministe
Base
Base directe validée par l'opérateur :
0.2.7-pre.006-fix.001
Cargo 0.2.7-pre.6.fix.1
Le checkpoint précédent est vert sur cargo fmt --all, audit Python KSP, cargo check --workspace, cargo clippy --workspace --all-targets, cargo test -p ksp-onchain-transport-lib avec 275 tests réussis, puis cargo test --workspace avec uniquement les smokes/diagnostics déjà attendus en ignored.
Objectif
Matérialiser la résilience de session WebSocket prévue par le plan sans avancer sur les wrappers typed publics ni sur le backpressure per-subscription :
- reconnect automatique uniquement après perte physique ou protocolaire structurelle ;
- budget fini
max_retries; - backoff exponentiel borné sans jitter ;
- shutdown prioritaire pendant backoff et handshake ;
- invalidation immédiate des remote subscription IDs après rupture ;
- incrément du
continuity_gap_countune fois par perte de continuité ; - restauration déterministe des subscriptions encore désirées en ordre croissant de
WsSubscriptionId; - conservation et replay exact des paramètres de subscribe ;
- remapping des nouveaux remote IDs sans changer les IDs locaux ;
- policy
Neversans restauration automatique ; - cancellation locale gagnante pendant reconnect/resubscribe ;
- nettoyage best-effort d'un ACK de resubscribe devenu stale ;
- reset du budget seulement après retour complet à
Active; - aucun replay implicite des requests applicatives en vol ;
- aucun backfill HTTP et aucune promesse lossless.
Signal de version
livraison 0.2.7-pre.007
workspace.package.version 0.2.7-pre.7
commit attendu v0.2.7-pre.007
Git tag aucun
Fichiers modifiés
Cargo.toml
crates/ksp-core-lib/tests/workspace_dependencies.rs
crates/ksp-onchain-transport-lib/Cargo.toml
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
crates/ksp-onchain-transport-lib/src/ws_session.rs
crates/ksp-onchain-transport-lib/src/ws_subscription.rs
crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs
docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md
docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md
deltas/0.2.7/pre.007.md
Implémentation
Session physique
Une perte détectée après activation de la session entre dans Reconnecting { attempt } au lieu de terminer immédiatement l'actor. Les triggers retenus restent les erreurs physiques/socket/TLS/WebSocket, EOF ou Close distant inattendu et les violations protocolaires structurelles. Les erreurs RPC applicatives continuent de ne pas déclencher de reconnect.
Chaque tentative attend un backoff exponentiel borné par les settings existants. Le signal de shutdown est surveillé séparément de la command queue pendant le backoff et le handshake de remplacement afin qu'un WsSession::close() puisse interrompre la reprise sans ouvrir une nouvelle connexion uniquement pour effectuer du nettoyage.
Le budget est consommé pour la perte courante et n'est considéré réinitialisé qu'après établissement d'un nouveau socket, restauration des subscriptions retenues et publication complète de Active.
Continuity gaps et subscriptions
Lors d'une rupture, tous les remote IDs sont invalidés et le mapping remote vers local est vidé. continuity_gap_count est incrémenté une fois pour la perte logique. Ce compteur ne constitue pas une garantie de livraison et Transport n'effectue aucun backfill.
Avec WsResubscribePolicy::ActiveSubscriptions, les subscriptions Active passent en Resubscribing. L'actor conserve leurs paramètres de création et les rejoue séquentiellement dans l'ordre des IDs locaux. Chaque ACK réussi remappe un nouveau remote ID au même WsSubscriptionId.
Avec WsResubscribePolicy::Never, la reconnexion physique reste possible mais les anciennes subscriptions deviennent terminales et doivent être recréées par le consumer.
Races de cancellation
Un unsubscribe reçu pendant le backoff retire immédiatement la subscription de la restauration et retourne sans reconnecter pour nettoyer l'ancien remote ID devenu invalide.
Si la cancellation arrive après l'émission d'un resubscribe mais avant son ACK, le handle local devient terminal et ne peut plus repasser à Active. L'actor conserve toutefois cette request interne jusqu'à son ACK ou son timeout borné avant de poursuivre la restauration séquentielle. Lorsque l'ACK tardif fournit un nouvel ID distant, l'actor envoie un *Unsubscribe best-effort pour cet ID sans le publier dans le registry local. Cette attente bornée évite qu'une request stale consomme max_pending_requests pendant la restauration de la subscription suivante.
Dépendances
Le runtime Transport active désormais la feature Tokio net parce que la session de remplacement utilise le type concret tokio::net::TcpStream retourné par tokio-tungstenite. La dépendance reste déclarée au workspace root et la canary de frontière est mise à jour. Aucun nouveau package tiers n'est ajouté.
Tests déterministes ajoutés ou recalibrés
Les fixtures locales couvrent notamment :
- remote Close avec budget insuffisant puis terminaison
Failedbornée ; - deux subscriptions restaurées en ordre local stable avec remote IDs remappés ;
- replay exact des paramètres ;
- unsubscribe pendant backoff empêchant tout resubscribe ;
- unsubscribe après émission du resubscribe et ACK tardif nettoyé sans réactivation ;
- policy
Never; - deux pertes distinctes démontrant le reset du budget après retour complet à
Active; - progression de
continuity_gap_countde 1 à 2 ; - backoff exponentiel borné ;
- shutdown interrompant un backoff long ;
- erreur RPC applicative sur un resubscribe ne faisant échouer que la subscription concernée.
Les tests HTTP et les canaries de registry existants ne sont pas modifiés fonctionnellement.
Logging et sécurité
Les nouveaux diagnostics runtime passent exclusivement par ksp-logging-lib avec TRACING_TARGET = "ksp-onchain-transport-lib".
Les champs observables restent limités aux métadonnées sûres : session ID local, subscription ID local, nom logique d'endpoint, tentative de reconnect, compteur de gap, kind et code d'erreur sûr. Aucune URL, credential, payload arbitraire ni remote subscription ID n'est projeté dans les logs publics/snapshots.
Validation exécutée dans le sandbox
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
direct tracing scan runtime/tests clean
question-mark scan runtime modifié clean
Rust lines > 160 0
workspace.package.version 0.2.7-pre.7
Le sandbox ne fournit pas cargo, rustc ni rustfmt. Aucune validation Cargo de cette tranche n'est donc revendiquée ici.
Validation opérateur requise
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test --workspace
Décisions
- Le reconnect initial de
WsSession::connectn'est pas transformé en boucle automatique dans cette tranche : la policy de reprise s'applique après acquisition réussie d'une session physique. - Les requests applicatives en vol lors d'une rupture échouent et ne sont pas rejouées automatiquement.
- Une erreur RPC applicative pendant un resubscribe fait échouer la subscription concernée sans casser la nouvelle session physique.
- La restoration est séquentielle afin de préserver un ordre déterministe et de ne pas dépasser artificiellement
max_pending_requests. - Le backpressure/overflow par subscription et les canaries de leak associées restent réservés à
pre.008. - Les wrappers standards typed publics restent réservés aux lots
pre.009+.
Questions ouvertes
Aucune question bloquante pour ce checkpoint. Les décisions de backpressure per-subscription restent à matérialiser dans pre.008 conformément au plan.