Files
khadhroony-solana-project/deltas/0.2.7/pre.008.md
2026-08-22 21:44:18 +02:00

164 lines
8.1 KiB
Markdown

<!-- file: deltas/0.2.7/pre.008.md -->
<!-- version: 1 -->
# Delta `0.2.7-pre.008` — backpressure par subscription, causes terminales et cleanup borné
## Base
Base directe validée par l'opérateur :
```text
0.2.7-pre.007-fix.001
Cargo 0.2.7-pre.7.fix.1
```
Le checkpoint précédent est entièrement vert sur `cargo fmt --all`, audit Python KSP, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, `cargo test -p ksp-onchain-transport-lib` avec 283 tests réussis, puis `cargo test --workspace` avec uniquement les smokes/diagnostics déjà attendus en ignored.
## Objectif
Matérialiser la policy de backpressure WebSocket définie par le plan sans avancer sur les wrappers standard publics :
- queue de notifications réellement bornée par subscription ;
- aucun drop silencieux quand un consumer ne draine plus assez vite ;
- overflow isolé à la seule subscription lente ;
- compteur `overflow_count` réellement incrémenté et observable sur la session ;
- cause terminale KSP sûre observable sur `WsSubscription<T>` ;
- unsubscribe distant best-effort après overflow ou terminaison locale détectée ;
- libération de la capacité `max_active_subscriptions` après terminaison ;
- cleanup d'un receiver abandonné à la notification suivante ;
- maintien de la session physique et des subscriptions saines ;
- conservation des garanties reconnect/resubscribe acquises en `pre.007` ;
- aucune promesse lossless et aucun backfill implicite.
## Signal de version
```text
livraison 0.2.7-pre.008
workspace.package.version 0.2.7-pre.8
commit attendu v0.2.7-pre.008
Git tag aucun
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
crates/ksp-onchain-transport-lib/src/lib.rs
crates/ksp-onchain-transport-lib/src/ws_lifecycle.rs
crates/ksp-onchain-transport-lib/src/ws_session.rs
crates/ksp-onchain-transport-lib/src/ws_subscription.rs
crates/ksp-onchain-transport-lib/tests/public_api.rs
crates/ksp-onchain-transport-lib/unit_tests/ws_lifecycle.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
```
## Fichier ajouté
```text
deltas/0.2.7/pre.008.md
```
## Implémentation
### Overflow par subscription
Le dispatcher typed réserve d'abord une place avec `try_reserve` sur la queue `mpsc` bornée. Une queue pleine est donc détectée avant décodage et ne bloque jamais l'actor socket :
1. `overflow_count` est incrémenté de façon saturante ;
2. la subscription concernée reçoit la cause terminale `ERROR_CODE_WS_BACKPRESSURE_OVERFLOW` ;
3. son état devient `Failed` ;
4. son entrée registry et son mapping remote vers local sont retirés ;
5. un `*Unsubscribe` distant est envoyé best-effort lorsque le binding existait ;
6. les autres subscriptions et la session restent actives.
Une notification déjà présente dans la queue avant l'overflow reste lisible. Après drainage de cette valeur, le receiver se ferme parce que le dispatcher actor de la subscription terminale a été libéré.
### Causes terminales sûres
`WsSubscription<T>` expose désormais :
```text
terminal_error_code() -> Option<ErrorCode>
```
La projection contient uniquement un code KSP stable et sûr. Elle ne transporte ni message RPC distant, ni payload de notification, ni URL, ni credential, ni remote subscription ID.
Les transitions runtime vers `Failed` publient leur cause avant de terminer le handle : overflow, mismatch protocolaire, decode typed invalide, timeout, erreur RPC applicative, échec de subscribe/resubscribe, policy `Never` après rupture et exhaustion terminale du reconnect. Une fermeture normale ou un unsubscribe réussi conserve `None`.
`WsSubscriptionSnapshot` peut également porter cette cause sûre lorsqu'une entrée terminale reste présente dans un snapshot de session, notamment lors d'une exhaustion globale. Les subscriptions terminales retirées du registry restent observables par leur handle local.
### Cleanup et capacité
`max_active_subscriptions` reste une limite d'admission distincte du compteur d'overflow de notifications. Un rejet de création au plafond utilise le domaine d'erreur de capacité existant mais n'incrémente pas `overflow_count`.
Une subscription qui est fermée, échoue ou dont le receiver est abandonné puis détecté à la notification suivante libère son entrée locale. Le binding distant est nettoyé best-effort sans attendre son ACK et sans bloquer la session. Une nouvelle subscription peut ensuite réutiliser la capacité locale disponible avec un nouvel ID local monotone.
Le cleanup distant best-effort est également réutilisé pour les mismatchs de méthode, les erreurs de décodage typed et les ACK de resubscribe tardifs déjà traités en `pre.007`.
### Reconnect
Une subscription devenue terminale à cause d'un overflow ou d'une autre erreur locale est retirée du registry et ne peut donc pas être sélectionnée lors d'un reconnect ultérieur. Les subscriptions saines conservent le comportement `ActiveSubscriptions` ou `Never` défini en `pre.007`.
`overflow_count` est conservé à travers les snapshots et les reconnects. Il reste distinct de `continuity_gap_count` : le premier décrit une saturation locale d'un consumer, le second une interruption de continuité physique.
## Tests déterministes ajoutés ou renforcés
Les fixtures locales couvrent notamment :
- queue capacité 1 avec deux notifications et consumer lent ;
- `overflow_count == 1` après saturation ;
- cause terminale `ws_backpressure_overflow` sur le seul handle lent ;
- première notification déjà queueée encore lisible puis fermeture du receiver ;
- subscription saine parallèle toujours `Active` et recevant sa notification ;
- session physique toujours `Active` après overflow isolé ;
- `*Unsubscribe` distant best-effort exact après overflow ;
- plafond `max_active_subscriptions` sans incrément du compteur d'overflow ;
- capacité réutilisable après unsubscribe terminal ;
- receiver abandonné détecté sur notification suivante, cleanup distant et capacité réutilisable ;
- mismatch de méthode publiant `ws_protocol_error` sur le handle ;
- decode typed invalide publiant `invalid_response` sur le handle ;
- policy `Never` publiant `ws_connection_failed` sur le handle terminal ;
- erreur RPC applicative de resubscribe publiant `rpc_application_error` sur le handle concerné.
Les tests HTTP et la surface 52+14 restent inchangés fonctionnellement.
## Logging et sécurité
Tous les diagnostics passent par `ksp-logging-lib` avec le target crate-owned existant.
Les logs d'overflow/cleanup contiennent uniquement des métadonnées sûres : session ID local, subscription ID local, kind, compteur et code KSP. Ils ne contiennent jamais l'URL, le remote subscription ID ni le payload de notification.
## Validation exécutée dans le sandbox
Le sandbox ne fournit pas `cargo`, `rustc` ni `rustfmt`. Aucune validation Cargo de cette tranche n'est revendiquée ici.
Les contrôles statiques KSP et l'overlay exact sont exécutés avant publication de l'archive.
## Validation opérateur requise
```bash
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
- `overflow_count` compte uniquement les saturations des queues de notifications ; un rejet d'admission par `max_active_subscriptions` ne l'incrémente pas.
- Une queue pleine fait échouer la subscription au lieu de bloquer l'actor ou de dropper silencieusement des notifications.
- La cause terminale publique reste un `ErrorCode` KSP sans contenu distant arbitraire.
- Le cleanup distant est best-effort et non bloquant ; la terminaison locale reste prioritaire.
- Une subscription terminale libère sa capacité locale et n'est jamais resubscribe automatiquement.
- Les wrappers WebSocket Solana standard publics restent réservés à `pre.009+`.
## Questions ouvertes
Aucune question bloquante pour ce checkpoint. La tranche suivante peut ouvrir le premier lot de wrappers standard stables : account, program et logs.