Files
khadhroony-solana-project/deltas/0.2.1/pre.004.md
2026-08-17 19:26:26 +02:00

232 lines
8.2 KiB
Markdown

<!-- file: deltas/0.2.1/pre.004.md -->
<!-- version: 1 -->
# Delta `v0.2.1-pre.004`
## Base
Base attendue :
```text
v0.2.1-pre.003-fix.001
```
Cette base a été validée localement par le user avec `cargo fmt`, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, les tests ciblés Transport et `cargo test --workspace`.
Version Cargo cible :
```text
0.2.1-pre.4
```
## Objectif
Matérialiser la résilience runtime prévue par le plan `008` autour du client/pool HTTP déjà introduit :
- token bucket RPS/burst par rôle ;
- limite de concurrence par semaphore ;
- cooldown après rate-limit provider ;
- admission async avec deadline commune ;
- fallback vers les pairs et tiers moins prioritaires lorsqu'un candidat est temporairement indisponible ;
- états passifs `Degraded` / `RateLimited` et snapshots sûrs ;
- retry/backoff transport borné ;
- interdiction centralisée d'un resend après dispatch ambigu pour les opérations `NeverAfterDispatch`.
Cette tranche ne réalise toujours pas de POST JSON-RPC ni de méthode Solana typée ; le raccordement réseau appartient à `pre.005`.
## Modifications
### Workspace et dépendances
- `workspace.package.version` passe de `0.2.1-pre.3.fix.1` à `0.2.1-pre.4` ;
- aucune nouvelle dépendance tierce n'est introduite ;
- `ksp-onchain-transport-lib` consomme désormais `tokio.workspace = true` avec la feature locale `sync`, nécessaire au semaphore et à `Notify` ;
- les features runtime/time Tokio restent possédées par la déclaration workspace existante ;
- le firewall Transport -> Config/Store/Program et l'absence de `tracing` direct restent inchangés.
### Admission RPS / burst
Chaque rôle endpoint possède son état runtime indépendant.
Lorsque `requests_per_second` est configuré :
- un token bucket est créé au niveau endpoint/rôle ;
- `burst_capacity` fixe la capacité maximale ;
- si le burst n'est pas explicite, la capacité dérive de la valeur RPS, soit une seconde de capacité ;
- les tokens se reforment proportionnellement au temps écoulé ;
- une admission sans token disponible expose l'instant de prochaine admissibilité au pool plutôt que de bloquer un thread.
Sans RPS, le rôle n'est pas limité par le token bucket KSP.
### Concurrence
`max_concurrent_requests` est matérialisé par un `tokio::sync::Semaphore` par rôle.
Le nouveau `HttpRequestPermit` :
- réserve la capacité de concurrence ;
- conserve la sélection endpoint/rôle/request-kind ;
- transporte la deadline commune ;
- libère automatiquement la capacité à son drop ;
- réveille les waiters lorsque de la capacité redevient disponible.
Un candidat saturé n'empêche pas d'essayer les autres candidats du même tier puis les tiers moins prioritaires.
### Cooldown et rate-limit
`HttpRequestPermit::record_rate_limited()` :
- incrémente les compteurs failure/rate-limit ;
- marque le rôle dégradé ;
- applique le cooldown configuré par `pause_after_rate_limit` ;
- utilise un fallback runtime de 1 seconde lorsque ce cooldown n'est pas configuré ;
- accepte un délai provider déjà résolu et le borne à 60 secondes avant de pouvoir prolonger le cooldown local ;
- notifie le pool de la nouvelle disponibilité temporelle.
Le parsing concret du header HTTP `Retry-After` reste au futur exécuteur HTTP de `pre.005`; `pre.004` stabilise le contrat en `Duration`.
### Admission async et deadline commune
Nouvelles surfaces publiques :
```text
HttpRequestPermit
HttpTransportPool::acquire_for_method(...)
HttpTransportPool::acquire_for_request_kind(...)
HttpTransportPool::acquire_for_request_kind_with_timeout(...)
```
Algorithme runtime :
1. filtrer endpoint/rôle/capability comme en `pre.003` ;
2. ordonner les candidats par priorité ;
3. appliquer le round-robin dans chaque tier ;
4. tenter l'admission cooldown + concurrence + token bucket ;
5. essayer les pairs puis les tiers inférieurs lorsqu'un candidat est temporairement bloqué ;
6. si tous les candidats sont temporairement bloqués, attendre notification ou prochain instant de refill/cooldown ;
7. ne jamais dépasser la deadline commune.
Par défaut, la deadline commune utilise le plus petit `request_timeout` des endpoints structurellement compatibles. Une surcharge permet à une couche supérieure d'imposer un budget plus strict.
### Retry / backoff / no-resend
Nouveaux contrats publics :
```text
HttpRetryCause
HttpDispatchState
HttpRetryDecision
evaluate_transport_retry(...)
```
La policy centralisée :
- respecte `HttpRetrySettings::max_retries` ;
- applique un backoff exponentiel borné entre `initial_backoff` et `max_backoff` ;
- autorise seulement les causes transport classées retryables (`Connection`, `Timeout`, `RateLimited`, `TemporaryHttp`) ;
- exclut `Request`, `RpcApplication` et `InvalidResponse` du retry automatique ;
- respecte `TransportRetryClass::NotApplicable` ;
- interdit tout retry d'une méthode `NeverAfterDispatch` lorsque l'état est `DispatchedAmbiguous` ;
- autorise encore une tentative si le transport sait explicitement que la requête n'a pas été dispatchée ;
- peut prolonger le backoff d'un rate-limit par un délai provider borné à 60 secondes.
Aucune logique de retry métier/exécution Solana n'est introduite.
### Santé passive et snapshots
Les rôles endpoint exposent maintenant de manière sûre :
- `availability` ;
- RPS/burst configurés ;
- concurrence maximale et nombre en vol ;
- cooldown restant ;
- compteurs success/failure/rate-limit.
Transitions :
```text
Available --failure--> Degraded
Available/Degraded --rate-limit--> RateLimited
RateLimited --cooldown écoulé--> Degraded
Degraded --success--> Available
```
Les snapshots n'exposent toujours aucune URL ni credential provider.
### ROADMAP et plan
`ROADMAP.md` reçoit uniquement la mise à jour synthétique normale de l'état de `0.2.1` : la résilience runtime devient acquise et les prochains jalons majeurs restent les 4 canaris, Config standard et la clôture. Aucun historique de prerelease/fix n'y est ajouté.
Le plan `008` enregistre `pre.004` comme réalisé et décrit l'état précis dans sa section de suivi.
## Tests ajoutés
La suite Transport passe de **53 à 72 tests déclarés**.
Les nouveaux tests couvrent notamment :
- backoff exponentiel et borne maximale ;
- budget `max_retries` ;
- no-resend après dispatch ambigu ;
- retry permis lorsqu'un non-dispatch est prouvé ;
- RPC application errors et réponses invalides non retryées ;
- extension/cap du délai provider ;
- token bucket burst/refill déterministe ;
- burst implicite dérivé du RPS ;
- libération de semaphore ;
- cooldown/rate-limit ;
- fallback vers un tier inférieur sur saturation concurrence, RPS ou cooldown ;
- réveil après libération de concurrence ;
- timeout d'admission borné ;
- transition passive degraded -> available ;
- snapshot public de résilience ;
- admission async depuis l'API publique sans fuite de l'URL ;
- consommation publique de la policy de retry.
## Hors périmètre conservé
Restent à `pre.005` :
- exécution HTTP POST JSON-RPC réelle ;
- mapping concret des erreurs `reqwest`/HTTP vers `HttpRetryCause` ;
- parsing de `Retry-After` HTTP ;
- méthodes typées `getHealth`, `getVersion`, `getGenesisHash`, `getBalance` ;
- fixtures RPC de ces méthodes.
Restent à `pre.006+` :
- document/schema Config standard Transport ;
- adapter Config -> settings Transport ;
- smoke tests réseau opt-in et documentation finale.
## Validation
Non exécutée dans le sandbox de génération : `cargo` et `rustc` n'y sont pas installés.
Après application :
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test --workspace
```
Le graphe de dépendances n'ajoute aucune crate nouvelle, mais la feature locale `tokio/sync` devient directement utilisée par Transport. Pour auditer le feature-set effectif de cette tranche :
```bash
cargo tree -p ksp-onchain-transport-lib -e features
cargo tree -p ksp-onchain-transport-lib -e normal
```
## Suite
Tranche suivante prévue :
```text
0.2.1-pre.005
```
Périmètre : exécuteur HTTP JSON-RPC réel + quatre méthodes canari typées `getHealth`, `getVersion`, `getGenesisHash`, `getBalance`, avec fixtures déterministes et raccordement des timeouts/429/erreurs transport à la résilience de `pre.004`.