Files
khadhroony-solana-project/deltas/0.2.7/pre.007.md
2026-08-22 20:42:06 +02:00

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_count une 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 Never sans 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 Failed borné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_count de 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::connect n'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.