232 lines
8.2 KiB
Markdown
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`.
|