12 KiB
Data, Materialization et Store
Objet
Ce document définit la chaîne durable KSP, la responsabilité du Store et les frontières de replay.
La nomenclature canonique est désormais :
RAW -> STRUCTURAL -> DECODED -> DOMAIN
Les aliases D1–D4 restent utilisés pour les niveaux persistés :
D1 = RAW
D2 = STRUCTURAL
D3 = DECODED / matérialisation générique décodée
D4 = DOMAIN
Le nom de couche historique CORE est abandonné pour D2 parce qu'il confondait la fondation commune ksp-core-lib avec une opération de décomposition structurelle du Store. Core reste inchangé lorsqu'il désigne la crate fondamentale ou un nom propre comme « Solana Core Programs ».
Cette clarification remplace l'ancienne interprétation où D1 -> D2 pouvait déjà dépendre de ksp-program-api. RAW et STRUCTURAL sont indépendants de tout decoder Program.
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
RAW conserve l'acquisition suffisamment fidèlement pour reconstruire STRUCTURAL sans redemander la donnée au provider lorsqu'elle a déjà été capturée.
Le transport peut normaliser plusieurs providers vers un modèle KSP homogène, mais D1 doit rester lossless pour les besoins de replay couverts.
D1 ne décode aucun programme Solana/SPL/Metaplex/DEX.
Frontière Transport -> RAW
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.
Provenance RAW
Selon la catégorie, D1 doit pouvoir conserver notamment :
- cluster/network ;
- slot/signature/pubkey/identité blockchain applicable ;
- payload replayable ;
- provider ;
- endpoint/source/transport ;
- rôle d'acquisition : live, backfill, import ;
- instant d'observation ;
- instant de persistence ;
- identité/hash d'idempotence ;
- cursor/page/range/checkpoint lorsque pertinent.
D2 — STRUCTURAL
Mission
STRUCTURAL est une normalisation canonique générique de la blockchain Solana.
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.
Exemples de faits STRUCTURAL candidats :
- 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.
Un fait STRUCTURAL 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.
Frontière RAW -> STRUCTURAL
D1 RAW
|
v
normalisation Solana générique
|
v
D2 STRUCTURAL
Interdictions :
RAW -> STRUCTURAL -X-> ksp-program-api
RAW -> STRUCTURAL -X-> ksp-program-lib
RAW -> STRUCTURAL -X-> ksp-materializer-api
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 STRUCTURAL
D2 doit pouvoir relier chaque résultat à :
- son input D1 ;
- l'identité/version du normalizer STRUCTURAL ;
- un hash logique d'input ;
- l'instant de processing/persistence ;
- son état de processing durable lorsque nécessaire.
D3 — DECODED / matérialisation générique
Mission
DECODED commence lorsque KSP interprète un program_id, un layout d'instruction, un compte ou un événement selon un contrat Program/protocole.
La progression logique d'un groupe est :
STRUCTURAL
|
v
decoder Program
|
v
decoded facts
|
v
materialisation générique / journal durable
|
v
D3 DECODED
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 :
quel input STRUCTURAL ?
quel program/decoder ?
quelle version ?
quel materializer ?
quelle version ?
quel output logique ?
quel type/domaine ?
quel hash ?
quel instant ?
quel état/superseded/failed/replay ?
Les types exacts de decoded facts et du journal sont décidés lorsque les premiers vertical slices Program existent.
Frontière STRUCTURAL -> DECODED
D2 STRUCTURAL
|
v
ksp-program-api implementation
|
v
decoded output
|
v
ksp-materializer-api implementation
|
v
D3 journal / decoded materialization
Les implémentations officielles pourront provenir de ksp-program-lib et ksp-materializer-lib; des implémentations externes compatibles restent possibles.
Program et Materializer ne dépendent pas du backend Store.
D4 — DOMAIN
Mission
DOMAIN expose des projections queryables utiles aux applications, analyses et futurs modèles ML.
Exemples :
- 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.
Faits métier génériques
Les projections de trading ne sont pas séparées automatiquement par protocole.
Préférer lorsque possible :
liquidity_pools
trades
positions
price_observations
ohlc
routes
route_legs
plutôt que :
meteora_trades
raydium_trades
orca_trades
pump_trades
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.
Metadata
La direction reste :
- 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 DOMAIN 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 STRUCTURAL sont développés horizontalement.
À partir de DECODED, la progression est verticale par groupe :
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 STRUCTURAL/DECODED/DOMAIN sont ajoutées quand leurs couches sont réellement ouvertes.
ksp-store-lib et backends physiques
ksp-store-lib est la façade/runtime Store commune derrière ksp-store-api. Il sélectionne uniquement les backends compilés par ses features, convertit ses settings KSP-owned vers le backend retenu, expose le lifecycle commun et masque les objets physiques du moteur.
Il possède notamment :
- identité et sélection de backend côté façade ;
- settings Store publics KSP-owned indépendants de Config ;
- lifecycle commun
open/close; - dispatch vers le backend compilé ;
- mapping des diagnostics/health backend vers une projection portable lorsque cette surface est justifiée ;
- réexport de la surface
ksp-store-apiutile aux consumers.
Le backend PostgreSQL officiel appartient à ksp-store-postgres-lib. Cette crate dépend de ksp-store-api, ne dépend jamais de ksp-store-lib et possède seule :
tokio-postgreset le pool PostgreSQL ;- le connecteur TLS PostgreSQL ;
- SQL et statements physiques ;
- transactions PostgreSQL ;
- migrations/bootstrap et metadata de schéma ;
- mapping rows/backend ;
- détails de cursorisation physique ;
- diagnostics PostgreSQL internes.
ksp-store-lib et les crates backend ne possèdent pas :
- transport réseau d’acquisition ;
- decoder Program ;
- materializer ;
- orchestration de worker/job ;
- politique de batch, backlog, priorité ou retry de processing.
La pagination/cursorisation reste un contrat de navigation Store. Une limite demandée par l’appelant peut être validée pour sa forme/sécurité, mais aucun plafond métier global arbitraire ni batch-size de worker n’est introduit par le runtime Store.
Navigation machine et inspection humaine
Deux contrats complémentaires coexistent sans collision :
workers / jobs / replay -> RawPageRequest + RawPageCursor -> keyset
inspection UI -> RawInspectionPageRequest -> offset + limit + counts exacts
La voie cursor/keyset reste canonique pour la navigation machine et les replays : le cursor est opaque, lié au contexte de query et ne dépend pas d'un OFFSET profond.
La voie RawInspectionPageRequest est backend-neutral mais conçue pour une inspection humaine random-access. Elle peut demander des counts exacts et un offset absolu ; le backend PostgreSQL peut implémenter cette capability avec COUNT et OFFSET privés. Ce choix ne réécrit jamais les statements keyset historiques.
ksp-app-store-desk est le consumer actuel de cette voie d'inspection. DataTables possède l'unique pager visible et l'application ne reçoit aucun cursor Store. Les summaries d'inspection restent sans payload/data RAW ; un détail explicite peut recharger une seule entité avec une projection applicative bornée.
ksp-materializer-api et ksp-materializer-lib
Ils sont introduits seulement lorsque le premier groupe DECODED 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 :
RAW -> STRUCTURAL
STRUCTURAL -> DECODED
DECODED -> DOMAIN
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 :
persist
commit
notify
Le consumer reconstruit toujours son backlog depuis le Store avec les versions de processor et les marqueurs d'idempotence.
Acquisition live et backfill
Live et backfill alimentent la même frontière RAW :
live worker ----\
+--> RAW persistence
backfill job ----/
Ils ne dupliquent pas le contrat durable.
Stabilité
La stabilité cible est différente selon la couche :
- RAW : fortement stable après mise en production ;
- STRUCTURAL : fortement stable après validation de la normalisation Solana générique ;
- DECODED : extensible par nouveaux Program/versions ;
- DOMAIN : plus évolutif selon les besoins de query, trading et analytics.
Questions laissées ouvertes
- schémas SQL exacts RAW puis STRUCTURAL ;
- représentation persistable exacte d'un decoded output ;
- contrat exact du journal D3 ;
- granularité des projectors DOMAIN ;
- politique de supersession/versioning des outputs ;
- fenêtres OHLC initiales ;
- mécanisme de contexte pour les projections stateful.