146 lines
7.1 KiB
Markdown
146 lines
7.1 KiB
Markdown
<!-- file: docs/BACKFILL_HTTP.md -->
|
||
<!-- version: 6 -->
|
||
|
||
# Backfill HTTP transactionnel
|
||
|
||
## Objectif
|
||
|
||
Le jalon `0.3.3` fournit un moteur de backfill réutilisable qui transforme les réponses HTTP Solana standard en transactions canoniques indépendantes du fournisseur.
|
||
|
||
```text
|
||
signatures explicites
|
||
ou getSignaturesForAddress
|
||
-> getTransaction
|
||
-> kb_model::CanonicalTransaction
|
||
-> kb_sol_raw_transactions
|
||
-> kb_sol_obs_transaction_observations
|
||
```
|
||
|
||
Aucun décodeur DEX, aucune extraction core et aucune source temps réel payante ne sont exécutés dans ce jalon.
|
||
|
||
## Sources de signatures
|
||
|
||
Le moteur accepte quatre catégories :
|
||
|
||
- liste explicite de signatures, une signature par ligne ;
|
||
- historique d’un `program_id` ;
|
||
- historique d’un mint de token ;
|
||
- historique d’une adresse de pool.
|
||
|
||
Les trois modes par adresse utilisent la même méthode standard `getSignaturesForAddress`. La catégorie est conservée dans le `filter_code` des observations afin de distinguer `program_before`, `program_after`, `program_latest`, et les équivalents token/pool.
|
||
|
||
## Sens de pagination
|
||
|
||
### Avant une signature
|
||
|
||
Avec une ancre, le paramètre RPC `before` sélectionne les transactions plus anciennes que cette signature. Sans ancre, le mode `Before` omet `before` et commence sur la page la plus récente retournée par le RPC. Le moteur poursuit la pagination jusqu’à atteindre la limite demandée, une page incomplète, la limite de pages ou une demande d’arrêt.
|
||
|
||
### Après une signature
|
||
|
||
Le moteur transmet directement la signature d’ancrage dans le paramètre RPC `until`. Les pages suivantes combinent le même `until` avec un curseur `before` correspondant à la dernière signature de la page précédente.
|
||
|
||
Le mode `after` utilise des pages RPC de 1 000 signatures afin que la limite par défaut de 20 pages puisse couvrir jusqu’à 20 000 signatures plus récentes que l’ancre. Le moteur ne conserve que les `X` candidates les plus proches de l’ancre, en plus de l’ensemble de déduplication borné par `max_pages × 1 000`.
|
||
|
||
Une page incomplète signifie que la borne `until` a été atteinte. Si toutes les pages autorisées sont pleines, la campagne échoue avec le nombre de signatures inspectées et demande d’augmenter `max_pages`. Une erreur RPC de borne inconnue est propagée sans être transformée en résultat vide.
|
||
|
||
## Limites et retries
|
||
|
||
Le moteur combine :
|
||
|
||
- les limites saisies par l’opérateur ;
|
||
- `requests_per_second` du rôle sélectionné ;
|
||
- `max_concurrent_requests` du rôle sélectionné ;
|
||
- `pause_after_rate_limit_ms` après une erreur 429 ;
|
||
- un backoff borné pour les autres erreurs temporaires.
|
||
|
||
Le rôle HTTP doit supporter à la fois :
|
||
|
||
```text
|
||
get_signatures_for_address
|
||
get_transaction
|
||
```
|
||
|
||
Le rôle recommandé reste `history_backfill`.
|
||
|
||
## Persistance
|
||
|
||
Une signature déjà présente dans `kb_sol_raw_transactions` est ignorée avant l’appel `getTransaction`.
|
||
|
||
Chaque tentative d’hydratation produit une observation légère :
|
||
|
||
- provider et endpoint ;
|
||
- protocole `solana_http_json_rpc` ;
|
||
- méthode `getTransaction` ;
|
||
- origine `backfill` ;
|
||
- commitment, session et filtre ;
|
||
- timestamps ;
|
||
- taille et hash du payload source lorsque disponibles ;
|
||
- statut `persisted`, `missing` ou `failed` ;
|
||
- erreur normalisée lorsque nécessaire.
|
||
|
||
Le payload source complet n’est jamais dupliqué dans la table d’observations.
|
||
|
||
## Démo Tauri et arrêt coopératif
|
||
|
||
La fenêtre `demo_backfill` regroupe dans des accordéons Bootstrap 5 :
|
||
|
||
1. paramètres communs ;
|
||
2. signatures explicites ;
|
||
3. programme ;
|
||
4. token ;
|
||
5. pool ;
|
||
6. journal et résumé JSON.
|
||
|
||
Une seule campagne peut fonctionner à la fois. Le bouton `Arrêter` pose un drapeau d’annulation coopérative lu entre les pages, les candidats, le pacer et les retries.
|
||
|
||
Depuis la préversion de clôture `0.3.4-pre.011`, l’annulation ne se limite plus aux frontières entre appels : les futures RPC `getSignaturesForAddress` et `getTransaction` sont mises en concurrence avec un observateur d’arrêt, l’attente du pacer est annulable et une pause de retry, y compris après un 429, est interrompue par sondage borné. L’abandon de la future HTTP empêche une campagne arrêtée d’attendre un timeout réseau complet.
|
||
|
||
Le moteur maintient une file bornée par la concurrence effective et cesse d’admettre de nouveaux candidats dès que l’arrêt est demandé. Les candidats non démarrés ne sont ni journalisés comme traités, ni inclus dans `candidates_completed`.
|
||
|
||
Le résumé distingue :
|
||
|
||
- `candidates_selected` : candidats découverts ;
|
||
- `candidates_started` : candidats admis dans la file d’exécution ;
|
||
- `candidates_completed` : candidats arrivés à un résultat terminal ;
|
||
- `candidates_cancelled` : candidats démarrés puis interrompus avant un résultat terminal ;
|
||
- `candidates_not_started` : candidats jamais admis après la demande d’arrêt.
|
||
|
||
Les invariants attendus sont :
|
||
|
||
```text
|
||
candidates_selected = candidates_started + candidates_not_started
|
||
candidates_started = candidates_completed + candidates_cancelled
|
||
```
|
||
|
||
Pour une campagne `before`, `resume_before_signature` correspond à la dernière signature terminée dans un préfixe contigu de la liste ordonnée. Une transaction terminée hors ordre ne fait pas avancer seule ce curseur. Si aucun candidat n’a terminé, le curseur reste la signature d’ancrage initiale. Cette règle interdit de sauter les candidats non traités lors d’une reprise.
|
||
|
||
Le calcul de cette frontière est couvert par les tests unitaires. Une campagne réelle de 500 candidats a déjà validé l’arrêt et la séparation entre terminés, annulés et non démarrés. Le rejeu manuel depuis `resume_before_signature` reste un contrôle opératoire recommandé, mais il ne constitue plus un jalon séparé ni un blocage pour `0.4.0`.
|
||
|
||
## Validation finale `0.3.3`
|
||
|
||
Validations locales :
|
||
|
||
```text
|
||
cargo test -p kb_onchain_transport : 54 tests passés
|
||
cargo test -p kb_pipeline : 10 tests passés
|
||
cargo test -p kb_store_core: 31 tests passés
|
||
cargo test -p kb_store_pg : 32 tests passés
|
||
cargo test -p kb_app_demo : 38 tests passés
|
||
cargo clippy --all-targets : validé
|
||
```
|
||
|
||
Les campagnes Tauri ont validé :
|
||
|
||
- signatures explicites ;
|
||
- programme avant/après ;
|
||
- token avant/après ;
|
||
- pool avant/après ;
|
||
- parcours profond `program_after` avec 5 516 signatures indexées parcourues pour 5 transactions hydratées ;
|
||
- arrêt d’une campagne de 500 candidats avec 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés.
|
||
|
||
Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations avant les derniers tests d’arrêt. Les tables core restent volontairement vides jusqu’à `0.3.4`.
|
||
|
||
## Recherche latest sans ancre
|
||
|
||
Pour programme, token et pool, une direction `before` avec ancre absente produit un filtre `*_latest`. La première page est demandée sans `before`, puis les pages suivantes utilisent normalement la dernière signature reçue comme curseur. Une direction `after` sans ancre est refusée, car la borne `until` ne peut pas être déterminée. En cas d’arrêt avant le premier candidat terminé, une campagne `*_latest` reprend depuis la page la plus récente et ne fabrique aucun curseur.
|