v0.2.1-pre.004
This commit is contained in:
231
deltas/0.2.1/pre.004.md
Normal file
231
deltas/0.2.1/pre.004.md
Normal file
@@ -0,0 +1,231 @@
|
||||
<!-- 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`.
|
||||
Reference in New Issue
Block a user