8.2 KiB
Delta v0.2.1-pre.004
Base
Base attendue :
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 :
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/RateLimitedet 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.versionpasse de0.2.1-pre.3.fix.1à0.2.1-pre.4;- aucune nouvelle dépendance tierce n'est introduite ;
ksp-onchain-transport-libconsomme désormaistokio.workspace = trueavec la feature localesync, 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
tracingdirect 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_capacityfixe 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 :
HttpRequestPermit
HttpTransportPool::acquire_for_method(...)
HttpTransportPool::acquire_for_request_kind(...)
HttpTransportPool::acquire_for_request_kind_with_timeout(...)
Algorithme runtime :
- filtrer endpoint/rôle/capability comme en
pre.003; - ordonner les candidats par priorité ;
- appliquer le round-robin dans chaque tier ;
- tenter l'admission cooldown + concurrence + token bucket ;
- essayer les pairs puis les tiers inférieurs lorsqu'un candidat est temporairement bloqué ;
- si tous les candidats sont temporairement bloqués, attendre notification ou prochain instant de refill/cooldown ;
- 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 :
HttpRetryCause
HttpDispatchState
HttpRetryDecision
evaluate_transport_retry(...)
La policy centralisée :
- respecte
HttpRetrySettings::max_retries; - applique un backoff exponentiel borné entre
initial_backoffetmax_backoff; - autorise seulement les causes transport classées retryables (
Connection,Timeout,RateLimited,TemporaryHttp) ; - exclut
Request,RpcApplicationetInvalidResponsedu retry automatique ; - respecte
TransportRetryClass::NotApplicable; - interdit tout retry d'une méthode
NeverAfterDispatchlorsque l'état estDispatchedAmbiguous; - 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 :
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 versHttpRetryCause; - parsing de
Retry-AfterHTTP ; - 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 :
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 :
cargo tree -p ksp-onchain-transport-lib -e features
cargo tree -p ksp-onchain-transport-lib -e normal
Suite
Tranche suivante prévue :
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.