v0.2.0-pre.002
This commit is contained in:
@@ -1,614 +1,344 @@
|
||||
<!-- file: docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Data, Materialization et Store
|
||||
|
||||
## Objet
|
||||
|
||||
Ce document constitue la sortie principale de `0.0.3-pre.006`.
|
||||
Ce document définit la chaîne durable KSP, la responsabilité du Store et les frontières de replay.
|
||||
|
||||
Il définit les frontières durables de données KSP et précise :
|
||||
|
||||
- les niveaux D1 à D4 ;
|
||||
- la séparation `ksp-materializer-api` / `ksp-materializer-lib` / Store ;
|
||||
- le rôle backend-agnostic de `ksp-store-api` ;
|
||||
- PostgreSQL comme implémentation de référence de `ksp-store-lib` ;
|
||||
- provenance et temporalités ;
|
||||
- idempotence et versionnement des processors ;
|
||||
- replay indépendant par frontière ;
|
||||
- sémantique des notifications de données persistées ;
|
||||
- stabilité différente entre niveaux structurants et projections spécialisées.
|
||||
|
||||
Le lifecycle complet, batching, concurrence, backlog et reprise des workers/jobs sont détaillés dans `009-ACQUISITION_WORKERS_AND_JOBS.md`.
|
||||
|
||||
## Nomenclature des niveaux durables
|
||||
|
||||
Les niveaux de données ne réutilisent pas N1 à N4, déjà réservés aux couches architecturales KSP.
|
||||
|
||||
La nomenclature durable retenue est :
|
||||
La nomenclature canonique est désormais :
|
||||
|
||||
```text
|
||||
D1 — Raw
|
||||
D2 — Core canonique
|
||||
D3 — Journal de matérialisation générique
|
||||
D4 — Projections spécialisées/queryables par domaine
|
||||
RAW -> CORE -> DECODE -> SPECIALIZED
|
||||
```
|
||||
|
||||
Flux général :
|
||||
Les aliases D1–D4 restent utilisés pour les niveaux persistés :
|
||||
|
||||
```text
|
||||
source on-chain
|
||||
|
|
||||
v
|
||||
ksp-worker-raw-retriever
|
||||
|
|
||||
v
|
||||
D1 Raw
|
||||
|
|
||||
v
|
||||
ksp-worker-core-processor
|
||||
|
|
||||
v
|
||||
D2 Core canonique
|
||||
|
|
||||
v
|
||||
ksp-worker-generic-materializer
|
||||
|
|
||||
v
|
||||
D3 journal générique
|
||||
|
|
||||
v
|
||||
ksp-worker-domain-projector
|
||||
|
|
||||
v
|
||||
D4 projections de domaine
|
||||
D1 = RAW
|
||||
D2 = CORE
|
||||
D3 = DECODE / matérialisation générique décodée
|
||||
D4 = SPECIALIZED
|
||||
```
|
||||
|
||||
Le nom `ksp-worker-domain-projector` reste révisable ; la frontière D3 -> D4 est, elle, retenue.
|
||||
Cette clarification remplace l'ancienne interprétation où D1 -> D2 pouvait déjà dépendre de `ksp-program-api`. **RAW et CORE sont indépendants de tout decoder Program.**
|
||||
|
||||
# D1 — Raw
|
||||
## Principes structurants
|
||||
|
||||
- le Store persiste des contrats de données ; il ne possède ni transport, ni decoder, ni materializer ;
|
||||
- chaque frontière durable peut être rejouée indépendamment ;
|
||||
- les données de provenance/versioning permettent de savoir quel processor a produit quel output ;
|
||||
- les couches dérivées ne rendent jamais obligatoire une nouvelle acquisition réseau lorsque l'input durable nécessaire existe déjà ;
|
||||
- D4 privilégie les faits métier génériques lorsqu'une normalisation inter-protocoles est pertinente.
|
||||
|
||||
# D1 — RAW
|
||||
|
||||
## Mission
|
||||
|
||||
D1 conserve l'acquisition suffisamment fidèlement pour permettre un nouveau processing sans redemander la donnée à la blockchain lorsque l'information nécessaire a déjà été capturée.
|
||||
RAW conserve l'acquisition suffisamment fidèlement pour reconstruire CORE sans redemander la donnée au provider lorsqu'elle a déjà été capturée.
|
||||
|
||||
`raw` ne signifie pas nécessairement « enveloppe propriétaire du provider conservée sans aucune normalisation ». Le transport peut normaliser ses différentes sources vers des modèles KSP homogènes.
|
||||
Le transport peut normaliser plusieurs providers vers un modèle KSP homogène, mais D1 doit rester lossless pour les besoins de replay couverts.
|
||||
|
||||
La persistance D1 doit toutefois rester **lossless pour les besoins de replay couverts** : toute information nécessaire à la reconstruction du Core doit être conservée, y compris le contenu brut et la provenance utile.
|
||||
D1 ne décode aucun programme Solana/SPL/Metaplex/DEX.
|
||||
|
||||
## Frontière transport -> D1
|
||||
## Frontière Transport -> RAW
|
||||
|
||||
```text
|
||||
Solana RPC ------\
|
||||
Helius -----------+--> modèle homogène ksp-onchain-transport-lib
|
||||
Yellowstone ------/
|
||||
|
|
||||
v
|
||||
conversion explicite
|
||||
|
|
||||
v
|
||||
DTO D1 de ksp-store-api
|
||||
HTTP / WS / gRPC / provider
|
||||
|
|
||||
v
|
||||
ksp-onchain-transport-lib
|
||||
|
|
||||
v
|
||||
conversion explicite
|
||||
|
|
||||
v
|
||||
ksp-store-api RAW DTO
|
||||
|
|
||||
v
|
||||
ksp-store-lib
|
||||
```
|
||||
|
||||
`ksp-onchain-transport-lib` ne dépend ni de `ksp-store-api` ni de `ksp-store-lib`.
|
||||
|
||||
La conversion appartient au composant de composition, principalement `ksp-worker-raw-retriever` ou `ksp-job-backfill`.
|
||||
## Provenance RAW
|
||||
|
||||
## Provenance D1
|
||||
Selon la catégorie, D1 doit pouvoir conserver notamment :
|
||||
|
||||
Selon la catégorie de donnée, D1 doit pouvoir conserver notamment :
|
||||
|
||||
- réseau ;
|
||||
- identité blockchain : slot, signature, pubkey ou autre identifiant applicable ;
|
||||
- contenu raw/replayable ;
|
||||
- cluster/network ;
|
||||
- slot/signature/pubkey/identité blockchain applicable ;
|
||||
- payload replayable ;
|
||||
- provider ;
|
||||
- transport/source ;
|
||||
- rôle d'acquisition : live, backfill, import ou autre ;
|
||||
- instant d'observation/acquisition ;
|
||||
- endpoint/source/transport ;
|
||||
- rôle d'acquisition : live, backfill, import ;
|
||||
- instant d'observation ;
|
||||
- instant de persistence ;
|
||||
- hash/identité d'idempotence ;
|
||||
- informations de pagination/capture nécessaires à la reprise lorsque pertinentes.
|
||||
- identité/hash d'idempotence ;
|
||||
- cursor/page/range/checkpoint lorsque pertinent.
|
||||
|
||||
`block_time` reste optionnel et n'est jamais inventé lorsqu'il n'est pas fourni ou reconstructible de manière fiable.
|
||||
|
||||
# D2 — Core canonique
|
||||
# D2 — CORE
|
||||
|
||||
## Mission
|
||||
|
||||
D2 contient les faits canoniques Solana produits à partir de D1.
|
||||
CORE est une **normalisation canonique générique de la blockchain Solana**.
|
||||
|
||||
Le Core doit être suffisamment durable et général pour être rejoué vers les matérialisations futures sans repasser par l'acquisition ou le décodage raw.
|
||||
Cette couche doit fonctionner même si `ksp-program-api` et `ksp-program-lib` ne sont pas encore capables de décoder le moindre programme métier.
|
||||
|
||||
Conceptuellement, D2 peut accueillir notamment :
|
||||
Exemples de faits CORE candidats :
|
||||
|
||||
- transactions canoniques ;
|
||||
- instructions top-level ;
|
||||
- instructions CPI ;
|
||||
- comptes/états canoniques ;
|
||||
- résultats de décodage Program ouverts ;
|
||||
- événements/return data lorsqu'ils sont supportés ;
|
||||
- autres faits génériques Solana qui appartiennent réellement au Core.
|
||||
- slots ;
|
||||
- blocks et block metadata ;
|
||||
- signatures ;
|
||||
- transactions ;
|
||||
- messages legacy/versioned ;
|
||||
- account keys et address lookups ;
|
||||
- comptes et états bruts structurés génériquement ;
|
||||
- instructions top-level brutes ;
|
||||
- instructions CPI brutes ;
|
||||
- logs ;
|
||||
- transaction meta ;
|
||||
- balances/fees/rewards lorsqu'ils appartiennent au contrat blockchain générique ;
|
||||
- return data brute ;
|
||||
- relations structurelles transaction/message/instruction/account.
|
||||
|
||||
## Top-level / CPI
|
||||
Un fait CORE peut contenir un `program_id`, des bytes et des indexes sans savoir que l'instruction représente un `Transfer`, un `Swap` ou une mutation Metadata.
|
||||
|
||||
Les instructions top-level et CPI restent des faits distincts.
|
||||
|
||||
Leur modèle peut partager des champs, mais KSP ne doit pas les fusionner artificiellement lorsque leurs invariants ou requêtes diffèrent.
|
||||
|
||||
La séparation physique exacte sera décidée dans la première release Store.
|
||||
|
||||
## Frontière D1 -> D2
|
||||
## Frontière RAW -> CORE
|
||||
|
||||
```text
|
||||
DTO D1 store
|
||||
|
|
||||
v
|
||||
ksp-worker-core-processor
|
||||
|
|
||||
+--> conversion vers entrée ksp-program-api
|
||||
|
|
||||
+--> ksp-program-lib / extension compatible
|
||||
|
|
||||
+--> sortie Core ouverte
|
||||
|
|
||||
v
|
||||
conversion vers DTO D2 store
|
||||
D1 RAW
|
||||
|
|
||||
v
|
||||
normalisation Solana générique
|
||||
|
|
||||
v
|
||||
D2 CORE
|
||||
```
|
||||
|
||||
`ksp-program-api` / `ksp-program-lib` restent indépendants du Store.
|
||||
Interdictions :
|
||||
|
||||
## Provenance D2
|
||||
```text
|
||||
RAW -> CORE -X-> ksp-program-api
|
||||
RAW -> CORE -X-> ksp-program-lib
|
||||
RAW -> CORE -X-> ksp-materializer-api
|
||||
```
|
||||
|
||||
D2 doit pouvoir relier un résultat à :
|
||||
Les codecs/wires génériques nécessaires à la structure Solana peuvent provenir de `ksp-interface-lib` lorsqu'ils appartiennent à la façade wire officielle, sans transformer cette étape en décodage Program.
|
||||
|
||||
## Provenance CORE
|
||||
|
||||
D2 doit pouvoir relier chaque résultat à :
|
||||
|
||||
- son input D1 ;
|
||||
- l'identité/version du processor ;
|
||||
- l'identité/version de l'implémentation Program/decoder lorsque pertinente ;
|
||||
- la capacité de décodage utilisée ;
|
||||
- un hash de l'input logique ;
|
||||
- l'identité/version du normalizer CORE ;
|
||||
- un hash logique d'input ;
|
||||
- l'instant de processing/persistence ;
|
||||
- l'état de processing lorsqu'un lifecycle durable est nécessaire.
|
||||
- son état de processing durable lorsque nécessaire.
|
||||
|
||||
# D3 — Journal de matérialisation générique
|
||||
# D3 — DECODE / matérialisation générique
|
||||
|
||||
## Mission
|
||||
|
||||
D3 est un **journal durable**, pas une étape volatile.
|
||||
DECODE commence lorsque KSP interprète un `program_id`, un layout d'instruction, un compte ou un événement selon un contrat Program/protocole.
|
||||
|
||||
Il conserve l'équivalent conceptuel obligatoire de l'ancien journal `k_sol_mat_outputs` de `ks-store`, sans figer encore le nom exact de la table KSP.
|
||||
|
||||
Il doit permettre de répondre à des questions telles que :
|
||||
La progression logique d'un groupe est :
|
||||
|
||||
```text
|
||||
quel input D2 ?
|
||||
CORE
|
||||
|
|
||||
v
|
||||
decoder Program
|
||||
|
|
||||
v
|
||||
decoded facts
|
||||
|
|
||||
v
|
||||
materialisation générique / journal durable
|
||||
|
|
||||
v
|
||||
D3 DECODE
|
||||
```
|
||||
|
||||
D3 conserve l'équivalent conceptuel obligatoire du journal générique de matérialisation de bot3 (`k_sol_mat_outputs`), sans imposer son ancien schéma ou son nom physique.
|
||||
|
||||
Le journal doit pouvoir répondre au minimum :
|
||||
|
||||
```text
|
||||
quel input CORE ?
|
||||
quel program/decoder ?
|
||||
quelle version ?
|
||||
quel materializer ?
|
||||
quelle version ?
|
||||
quel output logique ?
|
||||
quel type/domaine ?
|
||||
quel hash ?
|
||||
quel instant ?
|
||||
quel état courant/superseded/failed/replay ?
|
||||
quel état/superseded/failed/replay ?
|
||||
```
|
||||
|
||||
D3 permet notamment de reconstruire D4 après évolution d'un projector sans refaire D1 -> D2 ou D2 -> D3.
|
||||
Les types exacts de decoded facts et du journal sont décidés lorsque les premiers vertical slices Program existent.
|
||||
|
||||
## Frontière D2 -> D3
|
||||
## Frontière CORE -> DECODE
|
||||
|
||||
```text
|
||||
D2 Core
|
||||
D2 CORE
|
||||
|
|
||||
v
|
||||
ksp-worker-generic-materializer
|
||||
|
|
||||
+--> conversion vers ksp-materializer-api
|
||||
|
|
||||
+--> GenericMaterializer
|
||||
ksp-program-api implementation
|
||||
|
|
||||
v
|
||||
generic materialization output
|
||||
decoded output
|
||||
|
|
||||
v
|
||||
conversion vers DTO D3 store
|
||||
ksp-materializer-api implementation
|
||||
|
|
||||
v
|
||||
D3 journal / decoded materialization
|
||||
```
|
||||
|
||||
Les noms Rust exacts ne sont pas figés.
|
||||
Les implémentations officielles pourront provenir de `ksp-program-lib` et `ksp-materializer-lib`; des implémentations externes compatibles restent possibles.
|
||||
|
||||
## Extensibilité externe
|
||||
Program et Materializer ne dépendent pas du backend Store.
|
||||
|
||||
Une implémentation externe de materializer doit pouvoir produire un output générique compatible avec D3 sans exiger une nouvelle table PostgreSQL spécialisée.
|
||||
|
||||
Cela rend possible :
|
||||
|
||||
```text
|
||||
external materializer
|
||||
|
|
||||
v
|
||||
ksp-materializer-api
|
||||
|
|
||||
v
|
||||
D3 journal générique
|
||||
```
|
||||
|
||||
avant une éventuelle intégration officielle complète en D4.
|
||||
|
||||
# D4 — Projections spécialisées de domaine
|
||||
# D4 — SPECIALIZED
|
||||
|
||||
## Mission
|
||||
|
||||
D4 contient les représentations optimisées pour les requêtes métier, analytiques et produit.
|
||||
|
||||
Exemples futurs :
|
||||
|
||||
- assets/tokens ;
|
||||
- metadata d'assets/tokens ;
|
||||
- Solana Program Metadata ;
|
||||
- liquidity pools ;
|
||||
- order books ;
|
||||
- swaps/trades ;
|
||||
- prices/volumes ;
|
||||
- positions ;
|
||||
- autres projections de domaine.
|
||||
|
||||
## Faits canoniques, pas familles par protocole
|
||||
|
||||
D4 reste organisé par **fait canonique**, pas par Program ID/protocole.
|
||||
|
||||
Éviter par défaut :
|
||||
|
||||
```text
|
||||
meteora_pools
|
||||
raydium_pools
|
||||
orca_pools
|
||||
```
|
||||
|
||||
au profit d'une projection canonique telle que :
|
||||
|
||||
```text
|
||||
liquidity_pools
|
||||
```
|
||||
|
||||
avec provenance/identité du protocole lorsque nécessaire.
|
||||
|
||||
La même règle s'applique aux swaps, order books et autres faits pouvant être normalisés.
|
||||
|
||||
## Metadata
|
||||
|
||||
Les metadata d'assets/tokens constituent une famille canonique commune pouvant recevoir des données provenant notamment :
|
||||
|
||||
- Metaplex Token Metadata ;
|
||||
- Token-2022 Metadata.
|
||||
|
||||
Solana Program Metadata reste une projection distincte car le domaine fonctionnel est différent.
|
||||
|
||||
## Nouvelle projection externe
|
||||
|
||||
Une nouvelle projection D4 relationnelle implique nécessairement un contrat de persistence et une implémentation backend.
|
||||
|
||||
KSP ne masque pas cette réalité derrière une API de matérialisation « magique ».
|
||||
|
||||
Une extension externe peut fonctionner jusqu'à D3 sans migration Store spécialisée. Pour obtenir une nouvelle projection D4 officielle, il faut intégrer :
|
||||
|
||||
- le contrat DTO/repository approprié dans `ksp-store-api` ;
|
||||
- la migration/repository PostgreSQL dans `ksp-store-lib` ;
|
||||
- la conversion/projector appropriée dans le composant de processing.
|
||||
|
||||
Un mécanisme de backend/projection entièrement externe pourra être étudié seulement si un besoin concret apparaît.
|
||||
|
||||
# Stabilité des niveaux durables
|
||||
|
||||
La direction retenue est :
|
||||
|
||||
```text
|
||||
D1 — fortement stable
|
||||
D2 — fortement stable
|
||||
D3 — fortement stable
|
||||
D4 — volontairement plus évolutif
|
||||
```
|
||||
|
||||
Après stabilisation de la première série Store réelle, les contrats D1/D2/D3 doivent changer seulement en cas :
|
||||
|
||||
- d'erreur structurelle ;
|
||||
- d'omission majeure ;
|
||||
- de nécessité de compatibilité impossible à résoudre additivement.
|
||||
|
||||
D4 peut évoluer plus librement lorsque de nouveaux décodeurs, materializers ou produits révèlent des besoins queryables supplémentaires.
|
||||
|
||||
# `ksp-materializer-api`
|
||||
|
||||
## Rôle
|
||||
|
||||
Une seule crate publique est retenue :
|
||||
|
||||
```text
|
||||
ksp-materializer-api
|
||||
```
|
||||
|
||||
Elle expose les contrats de matérialisation réutilisables sans dépendre du Store.
|
||||
|
||||
Deux capacités conceptuelles doivent pouvoir être distinguées :
|
||||
|
||||
```text
|
||||
GenericMaterializer
|
||||
DomainProjector
|
||||
```
|
||||
|
||||
Les noms exacts restent à valider.
|
||||
|
||||
### GenericMaterializer
|
||||
|
||||
Transforme une représentation Core/runtime en output générique persistable en D3.
|
||||
|
||||
### DomainProjector
|
||||
|
||||
Transforme un ou plusieurs inputs génériques/canoniques en représentation spécialisée de domaine destinée à D4.
|
||||
|
||||
Le second contrat peut évoluer en fonction des premiers cas réels. La séparation des responsabilités est plus importante que le nom du trait.
|
||||
|
||||
## Dépendances
|
||||
|
||||
```text
|
||||
ksp-materializer-api
|
||||
-> ksp-core-lib
|
||||
-> ksp-program-api
|
||||
```
|
||||
|
||||
Pas de dépendance vers Store.
|
||||
|
||||
# `ksp-materializer-lib`
|
||||
|
||||
`ksp-materializer-lib` contient les implémentations officielles KSP de `ksp-materializer-api`.
|
||||
|
||||
Organisation conceptuelle possible :
|
||||
|
||||
```text
|
||||
generic/
|
||||
...
|
||||
|
||||
domain/
|
||||
metadata/
|
||||
token/
|
||||
dex/
|
||||
...
|
||||
```
|
||||
|
||||
La structure finale suivra les règles de domaine établies avec les premières implémentations.
|
||||
|
||||
Interdictions :
|
||||
|
||||
```text
|
||||
ksp-materializer-lib -X-> ksp-store-api
|
||||
ksp-materializer-lib -X-> ksp-store-lib
|
||||
```
|
||||
|
||||
Un materializer transforme ; il ne persiste pas directement.
|
||||
|
||||
Le worker/job/pipeline spécialisé relie la transformation à la persistence.
|
||||
|
||||
# `ksp-store-api`
|
||||
|
||||
## Rôle
|
||||
|
||||
`ksp-store-api` expose la frontière backend-agnostic de persistence KSP.
|
||||
|
||||
Il possède les contrats persistants correspondant aux niveaux durables :
|
||||
|
||||
```text
|
||||
raw
|
||||
core
|
||||
materialization
|
||||
domain
|
||||
```
|
||||
|
||||
La structure exacte des modules Rust sera définie avec la première implémentation.
|
||||
|
||||
`ksp-store-api` ne dépend pas de Program, Materializer ou Transport.
|
||||
|
||||
## Contrats génériques et contrats de domaine
|
||||
|
||||
D1/D2/D3 doivent rester génériques et fortement structurants.
|
||||
|
||||
D4 peut croître progressivement avec les domaines officiellement supportés.
|
||||
|
||||
Cette différence est volontaire : l'API Store doit pouvoir ajouter de nouvelles projections queryables sans déstabiliser les contrats de replay historiques.
|
||||
|
||||
# `ksp-store-lib`
|
||||
|
||||
`ksp-store-lib` est l'implémentation officielle de référence de `ksp-store-api`.
|
||||
|
||||
PostgreSQL est le backend de référence prévu.
|
||||
|
||||
La crate possède notamment :
|
||||
|
||||
- configuration backend/connexion ;
|
||||
- pool/transactions backend ;
|
||||
- migrations ;
|
||||
- repositories ;
|
||||
- queries ;
|
||||
- persistence D1/D2/D3/D4 ;
|
||||
- primitives de backlog/replay nécessaires au backend ;
|
||||
- mécanismes PostgreSQL de notification lorsqu'ils sont retenus.
|
||||
|
||||
Les autres crates KSP ne contournent pas `ksp-store-lib` pour exécuter directement leurs propres opérations PostgreSQL.
|
||||
|
||||
Un second backend n'est pas créé abstraitement ; il devra justifier l'évolution de l'architecture lorsqu'un besoin réel apparaît.
|
||||
|
||||
# Temporalités
|
||||
|
||||
Les temporalités blockchain et locales sont distinctes.
|
||||
|
||||
Exemples blockchain :
|
||||
|
||||
```text
|
||||
slot
|
||||
block_time: Option<...>
|
||||
```
|
||||
|
||||
Exemples locaux :
|
||||
|
||||
```text
|
||||
observed_at
|
||||
acquired_at
|
||||
persisted_at
|
||||
processed_at
|
||||
materialized_at
|
||||
projected_at
|
||||
```
|
||||
|
||||
Toutes ne doivent pas nécessairement être présentes dans chaque DTO/table. Leur sémantique doit cependant être explicite lorsqu'elles existent.
|
||||
|
||||
Aucune date locale ne remplace silencieusement un `block_time` absent.
|
||||
|
||||
# Provenance
|
||||
|
||||
Un niveau dérivé doit permettre de remonter à son input durable et au processor qui l'a produit.
|
||||
|
||||
## D1
|
||||
|
||||
Provenance d'acquisition :
|
||||
|
||||
- network ;
|
||||
- provider/source ;
|
||||
- transport ;
|
||||
- rôle d'acquisition ;
|
||||
- identité blockchain ;
|
||||
- temporalités d'observation/persistence.
|
||||
|
||||
## D2
|
||||
|
||||
En plus :
|
||||
|
||||
- input D1 ;
|
||||
- processor/decoder identity ;
|
||||
- processor/decoder version ;
|
||||
- input hash ;
|
||||
- processing time/state.
|
||||
|
||||
## D3
|
||||
|
||||
En plus :
|
||||
|
||||
- input D2 ;
|
||||
- materializer identity/version ;
|
||||
- output identity/type/domain ;
|
||||
- output/input hash ;
|
||||
- materialization time/state.
|
||||
|
||||
## D4
|
||||
|
||||
En plus :
|
||||
|
||||
- input(s) D3 ou références canoniques explicitement définies ;
|
||||
- projector identity/version ;
|
||||
- projection time/state.
|
||||
|
||||
La représentation exacte de la provenance sera conçue pour éviter de répéter inutilement de gros payloads.
|
||||
|
||||
# Idempotence et versionnement
|
||||
|
||||
Chaque frontière dérivée doit pouvoir rejouer le même input sans créer de doublons logiquement distincts.
|
||||
|
||||
Principe conceptuel :
|
||||
|
||||
```text
|
||||
same logical input
|
||||
+ same processor identity
|
||||
+ same processor version
|
||||
+ same logical output identity
|
||||
= same durable result
|
||||
```
|
||||
|
||||
Une nouvelle version du processor doit pouvoir coexister avec ou superséder le résultat précédent selon la politique du niveau.
|
||||
|
||||
Le système doit pouvoir représenter selon besoin des états tels que :
|
||||
|
||||
```text
|
||||
current
|
||||
superseded
|
||||
pending
|
||||
failed
|
||||
replay
|
||||
```
|
||||
|
||||
Les noms, colonnes et contraintes SQL exacts seront décidés avec la première implémentation.
|
||||
|
||||
L'idempotence ne doit pas reposer uniquement sur l'espoir qu'une notification soit livrée une seule fois.
|
||||
|
||||
# Replay
|
||||
|
||||
Les replays sont séparés par frontière durable :
|
||||
|
||||
```text
|
||||
D1 -> D2
|
||||
D2 -> D3
|
||||
D3 -> D4
|
||||
```
|
||||
|
||||
Ils doivent pouvoir être exécutés indépendamment.
|
||||
SPECIALIZED expose des projections queryables utiles aux applications, analyses et futurs modèles ML.
|
||||
|
||||
Exemples :
|
||||
|
||||
- nouveau decoder/Core processor : rejouer D1 -> D2 ;
|
||||
- nouveau materializer générique : rejouer D2 -> D3 ;
|
||||
- nouvelle version d'un projector : rejouer D3 -> D4.
|
||||
- token/asset state ;
|
||||
- metadata canonique d'asset ;
|
||||
- pools/markets ;
|
||||
- reserves/liquidity ;
|
||||
- positions ;
|
||||
- swaps/trades ;
|
||||
- fees ;
|
||||
- observations de prix ;
|
||||
- OHLC/candles ;
|
||||
- routes et legs ;
|
||||
- faits trading-adjacent ;
|
||||
- projections d'autres domaines futurs.
|
||||
|
||||
Il ne doit pas être nécessaire de refaire toute la chaîne lorsqu'un niveau inférieur inchangé contient déjà l'information requise.
|
||||
## Faits métier génériques
|
||||
|
||||
Les replays sont des **jobs bornés**, pas des modes cachés des workers live.
|
||||
Les projections de trading ne sont pas séparées automatiquement par protocole.
|
||||
|
||||
Les jobs de replay retenus sont `ksp-job-replay-core`, `ksp-job-replay-generic-materialization` et `ksp-job-replay-domain-projection`.
|
||||
|
||||
# Notifications de données persistées
|
||||
|
||||
## Contrat
|
||||
|
||||
`ksp-store-api` est le propriétaire du contrat canonique lorsqu'une notification signifie :
|
||||
|
||||
> une donnée durable de telle catégorie/référence est disponible.
|
||||
|
||||
Le même type de donnée utilise le même format de notification quelle que soit son origine :
|
||||
Préférer lorsque possible :
|
||||
|
||||
```text
|
||||
worker live
|
||||
backfill job
|
||||
import
|
||||
replay
|
||||
autre source
|
||||
liquidity_pools
|
||||
trades
|
||||
positions
|
||||
price_observations
|
||||
ohlc
|
||||
routes
|
||||
route_legs
|
||||
```
|
||||
|
||||
## Notification != source de vérité
|
||||
|
||||
Une notification est un **signal de réveil/accélération**, jamais la source de vérité du backlog.
|
||||
|
||||
Elle peut être :
|
||||
|
||||
- perdue ;
|
||||
- dupliquée ;
|
||||
- retardée ;
|
||||
- reçue après un restart.
|
||||
|
||||
Le consumer doit toujours pouvoir reconstruire son travail depuis le Store.
|
||||
|
||||
Exemple conceptuel :
|
||||
plutôt que :
|
||||
|
||||
```text
|
||||
notification RawAvailable
|
||||
|
|
||||
v
|
||||
wake-up core worker
|
||||
|
|
||||
v
|
||||
store query:
|
||||
"quels inputs D1 ne sont pas encore traités
|
||||
par CoreProcessor version X ?"
|
||||
meteora_trades
|
||||
raydium_trades
|
||||
orca_trades
|
||||
pump_trades
|
||||
```
|
||||
|
||||
Le Store, l'idempotence et les checkpoints durables constituent la vérité.
|
||||
Les champs réellement protocol-specific peuvent être conservés dans une extension ou une projection dédiée uniquement lorsqu'un besoin de requête/invariant le justifie.
|
||||
|
||||
Cette règle évite d'exiger immédiatement un broker exactly-once.
|
||||
## Metadata
|
||||
|
||||
## Ordre de publication
|
||||
La direction reste :
|
||||
|
||||
Une donnée n'est annoncée disponible qu'après persistence réussie :
|
||||
- projection canonique commune pour metadata d'assets/tokens alimentée par Metaplex Token Metadata et Token-2022 Metadata ;
|
||||
- SPM reste distinct et sera redéveloppé plus tard avec le décodage généraliste.
|
||||
|
||||
## OHLC
|
||||
|
||||
Les candles sont des projections SPECIALIZED calculées à partir des trades/price observations persistés.
|
||||
|
||||
Une application marché lit les OHLC matérialisés ; elle ne reparcourt pas toutes les transactions pour reconstruire les candles à chaque affichage.
|
||||
|
||||
# Vertical slices Program
|
||||
|
||||
RAW et CORE sont développés horizontalement.
|
||||
|
||||
À partir de DECODE, la progression est verticale par groupe :
|
||||
|
||||
```text
|
||||
wire
|
||||
-> decode
|
||||
-> generic materialization / D3
|
||||
-> specialized projection / D4 si utile
|
||||
-> execution preparation
|
||||
-> execution policy
|
||||
-> execution
|
||||
-> Devnet scenarios / validation
|
||||
```
|
||||
|
||||
Un groupe doit atteindre une cohérence verticale suffisante avant que le groupe suivant devienne prioritaire.
|
||||
|
||||
Les composants satellites nécessaires à un protocole appartiennent à son groupe : Pump fees avec Pump, Meteora vaults avec Meteora, etc.
|
||||
|
||||
# `ksp-store-api`
|
||||
|
||||
`ksp-store-api` est backend-agnostic et porte les contrats nécessaires aux consommateurs.
|
||||
|
||||
La première implementation `0.3.1` est volontairement **RAW-only** : elle ne crée pas prématurément les contrats physiques D2/D3/D4.
|
||||
|
||||
Les surfaces CORE/DECODE/SPECIALIZED sont ajoutées quand leurs couches sont réellement ouvertes.
|
||||
|
||||
# `ksp-store-lib`
|
||||
|
||||
`ksp-store-lib` fournit PostgreSQL comme backend officiel de référence derrière `ksp-store-api`.
|
||||
|
||||
Il possède :
|
||||
|
||||
- migrations ;
|
||||
- SQL ;
|
||||
- transactions ;
|
||||
- mapping backend ;
|
||||
- pagination ;
|
||||
- claim/lease lorsque nécessaire ;
|
||||
- notifications backend si retenues.
|
||||
|
||||
Il ne possède pas :
|
||||
|
||||
- transport réseau ;
|
||||
- decoder Program ;
|
||||
- materializer ;
|
||||
- orchestration de worker/job.
|
||||
|
||||
# `ksp-materializer-api` et `ksp-materializer-lib`
|
||||
|
||||
Ils sont introduits seulement lorsque le premier groupe DECODE démontre le contrat réel.
|
||||
|
||||
`ksp-materializer-api` porte les contrats extensibles ; `ksp-materializer-lib` contient les implementations officielles communes.
|
||||
|
||||
Une projection très locale/spécifique peut rester dans son groupe si la création d'une implémentation commune séparée n'apporte pas de réutilisation réelle.
|
||||
|
||||
# Replay
|
||||
|
||||
Les frontières durables restent replayables indépendamment :
|
||||
|
||||
```text
|
||||
RAW -> CORE
|
||||
CORE -> DECODE
|
||||
DECODE -> SPECIALIZED
|
||||
```
|
||||
|
||||
Un replay d'une couche dérivée ne doit pas refaire arbitrairement les couches précédentes.
|
||||
|
||||
# Notifications persistées
|
||||
|
||||
Le Store reste source de vérité du backlog.
|
||||
|
||||
Les notifications ne sont qu'un wake-up : elles peuvent être perdues ou dupliquées.
|
||||
|
||||
Ordre :
|
||||
|
||||
```text
|
||||
persist
|
||||
@@ -616,118 +346,35 @@ commit
|
||||
notify
|
||||
```
|
||||
|
||||
et non :
|
||||
|
||||
```text
|
||||
notify
|
||||
persist
|
||||
```
|
||||
|
||||
## Payload
|
||||
|
||||
La notification privilégie une référence durable compacte plutôt que la duplication de tout le payload :
|
||||
|
||||
- catégorie/type ;
|
||||
- réseau ;
|
||||
- identifiant/range/cursor durable ;
|
||||
- autres informations minimales nécessaires au consumer.
|
||||
|
||||
Les noms de DTO exacts restent à définir.
|
||||
|
||||
## Mécanisme de diffusion
|
||||
|
||||
Le contrat est indépendant du mécanisme.
|
||||
|
||||
Candidats :
|
||||
|
||||
```text
|
||||
channel in-process
|
||||
PostgreSQL LISTEN/NOTIFY
|
||||
IPC
|
||||
broker externe
|
||||
```
|
||||
|
||||
`ksp-store-lib` peut fournir PostgreSQL LISTEN/NOTIFY comme mécanisme de référence si cela répond au premier besoin.
|
||||
|
||||
Le mécanisme initial de référence et la reprise opérationnelle sont détaillés dans `009-ACQUISITION_WORKERS_AND_JOBS.md`.
|
||||
Le consumer reconstruit toujours son backlog depuis le Store avec les versions de processor et les marqueurs d'idempotence.
|
||||
|
||||
# Acquisition live et backfill
|
||||
|
||||
`ksp-worker-raw-retriever` et `ksp-job-backfill` diffèrent par leur lifecycle/orchestration mais produisent le **même contrat D1** pour la même catégorie de donnée.
|
||||
Live et backfill alimentent la même frontière RAW :
|
||||
|
||||
```text
|
||||
live source ----------\
|
||||
+--> D1 Raw
|
||||
historical backfill --/
|
||||
live worker ----\
|
||||
+--> RAW persistence
|
||||
backfill job ----/
|
||||
```
|
||||
|
||||
Cela garantit que le processing downstream ne dépend pas de la manière dont la donnée a été acquise.
|
||||
Ils ne dupliquent pas le contrat durable.
|
||||
|
||||
Le backfill n'est pas un mode historique du worker live.
|
||||
# Stabilité
|
||||
|
||||
# Workers de processing
|
||||
La stabilité cible est différente selon la couche :
|
||||
|
||||
Les frontières de responsabilité sont désormais :
|
||||
|
||||
```text
|
||||
ksp-worker-raw-retriever
|
||||
transport -> D1
|
||||
|
||||
ksp-worker-core-processor
|
||||
D1 -> D2
|
||||
|
||||
ksp-worker-generic-materializer
|
||||
D2 -> D3
|
||||
|
||||
ksp-worker-domain-projector
|
||||
D3 -> D4
|
||||
```
|
||||
|
||||
Chaque worker :
|
||||
|
||||
- peut utiliser les notifications pour réduire la latence ;
|
||||
- doit pouvoir reconstruire son backlog depuis le Store ;
|
||||
- écrit seulement le niveau durable dont il est propriétaire ;
|
||||
- ne transforme pas silencieusement plusieurs frontières en une étape monolithique.
|
||||
|
||||
Le lifecycle, la concurrence, les cursors/checkpoints et la hot reconfiguration sont détaillés dans `009-ACQUISITION_WORKERS_AND_JOBS.md`.
|
||||
|
||||
# Projections de trading et autres domaines
|
||||
|
||||
Le trading reste une priorité produit mais ne détermine pas la structure D1/D2/D3.
|
||||
|
||||
D4 doit accueillir progressivement des faits canoniques de nombreux domaines :
|
||||
|
||||
- token ;
|
||||
- metadata ;
|
||||
- staking ;
|
||||
- programmes ;
|
||||
- trading/DEX ;
|
||||
- autres domaines futurs.
|
||||
|
||||
Pour le trading, les materializers/projectors doivent normaliser les noms et structures propres aux protocoles vers des faits communs lorsque les invariants le permettent.
|
||||
- RAW : fortement stable après mise en production ;
|
||||
- CORE : fortement stable après validation de la normalisation Solana générique ;
|
||||
- DECODE : extensible par nouveaux Program/versions ;
|
||||
- SPECIALIZED : plus évolutif selon les besoins de query, trading et analytics.
|
||||
|
||||
# Questions laissées ouvertes
|
||||
|
||||
La première implémentation Store devra encore fixer :
|
||||
|
||||
- schémas SQL et noms exacts des tables ;
|
||||
- clés/idempotence exactes ;
|
||||
- représentation des hashes ;
|
||||
- états de processing exacts ;
|
||||
- temporalités obligatoires/optionnelles par table ;
|
||||
- format exact des DTO D1/D2/D3 ;
|
||||
- structure des repositories/transactions backend ;
|
||||
- pagination/cursors ;
|
||||
- conversion u64/slot/PostgreSQL ;
|
||||
- stratégie des migrations initiales.
|
||||
|
||||
Les sujets opérationnels worker/job ont été précisés dans `009-ACQUISITION_WORKERS_AND_JOBS.md`.
|
||||
|
||||
Restent à définir à l'implémentation :
|
||||
|
||||
- schémas SQL et contraintes exactes ;
|
||||
- format concret des DTO D1/D2/D3/D4 ;
|
||||
- claim/lease PostgreSQL ;
|
||||
- contexte des projectors stateful ;
|
||||
- mécanisme IPC des managers de services autonomes.
|
||||
- schémas SQL exacts RAW puis CORE ;
|
||||
- représentation persistable exacte d'un decoded output ;
|
||||
- contrat exact du journal D3 ;
|
||||
- granularité des projectors SPECIALIZED ;
|
||||
- politique de supersession/versioning des outputs ;
|
||||
- fenêtres OHLC initiales ;
|
||||
- mécanisme de contexte pour les projections stateful.
|
||||
|
||||
Reference in New Issue
Block a user