247 lines
9.4 KiB
Markdown
247 lines
9.4 KiB
Markdown
<!-- file: kb_store_pg/README.md -->
|
||
<!-- version: 13 -->
|
||
|
||
# kb_store_pg
|
||
|
||
`kb_store_pg` implémente le backend PostgreSQL destiné à la production.
|
||
|
||
Ce crate dépend de `kb_store_core` pour les contrats et fournit les implémentations concrètes PostgreSQL : pool, healthcheck, migrations, requêtes SQL et repositories.
|
||
|
||
## Rôle exact
|
||
|
||
`kb_store_pg` contient :
|
||
|
||
- le handle de store PostgreSQL ;
|
||
- la création effective du pool PostgreSQL ;
|
||
- le healthcheck PostgreSQL minimal ;
|
||
- les diagnostics backend : DSN masqué, schéma courant et version serveur ;
|
||
- la stratégie de migrations PostgreSQL ;
|
||
- les requêtes SQL dans `queries/` ;
|
||
- les implémentations repository dans `repositories/`.
|
||
|
||
`kb_store_pg` ne contient pas :
|
||
|
||
- de contrat applicatif générique qui devrait vivre dans `kb_store_core` ;
|
||
- de logique Tauri ;
|
||
- de logique RPC ;
|
||
- de décodeur ;
|
||
- de matérialisateur métier ;
|
||
- de création de schémas PostgreSQL applicatifs explicites ;
|
||
- de création des tables `decode`, `mat`, `catalog`, `agg` ou `wallet` avant les jalons dédiés ; la seule table `ops` active en `0.3.4` est le ledger générique de traitement.
|
||
|
||
## Convention PostgreSQL
|
||
|
||
`kb_store_pg` utilise le schéma courant/default du profil PostgreSQL, généralement `public`.
|
||
|
||
Il est interdit de créer ou d'utiliser des schémas applicatifs explicites comme :
|
||
|
||
```text
|
||
raw
|
||
core
|
||
obs
|
||
decode
|
||
mat
|
||
catalog
|
||
agg
|
||
ops
|
||
wallet
|
||
```
|
||
|
||
La séparation logique se fait dans le nom de table :
|
||
|
||
```text
|
||
kb_sol_<domain>_<name>
|
||
```
|
||
|
||
Exemples valides :
|
||
|
||
```text
|
||
kb_sol_raw_transactions
|
||
kb_sol_obs_transaction_observations
|
||
kb_sol_core_transactions
|
||
kb_sol_core_account_keys
|
||
kb_sol_core_instructions
|
||
kb_sol_core_inner_instructions
|
||
kb_sol_core_logs
|
||
kb_sol_core_balance_changes
|
||
kb_sol_obs_program_observations
|
||
kb_sol_obs_instruction_observations
|
||
kb_sol_decode_decoded_events
|
||
kb_sol_mat_trade_events
|
||
kb_sol_catalog_tokens
|
||
kb_sol_catalog_pools
|
||
kb_sol_catalog_pairs
|
||
kb_sol_ops_processing_ledger
|
||
```
|
||
|
||
Exemples interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel :
|
||
|
||
```text
|
||
raw DOT kb_sol_rpc_transactions
|
||
raw DOT sol_transactions
|
||
core DOT sol_instructions
|
||
obs DOT program_observations
|
||
decode DOT protocol_events
|
||
ops DOT processing_ledger
|
||
```
|
||
|
||
## Layout obligatoire
|
||
|
||
```text
|
||
src/
|
||
lib.rs
|
||
pg_store.rs
|
||
migrations.rs
|
||
queries/
|
||
repositories/
|
||
```
|
||
|
||
## Conventions de dossiers
|
||
|
||
| Dossier | Rôle |
|
||
|-----------------|--------------------------------------------------------|
|
||
| `queries/` | SQL, bind et exécution SQL uniquement. |
|
||
| `repositories/` | Implémentations PostgreSQL des traits `kb_store_core`. |
|
||
|
||
Aucune structure métier, DTO ou entity ne doit être créée dans `queries/`.
|
||
|
||
## Infrastructure `0.2.2`
|
||
|
||
Le jalon `0.2.2` introduit `PostgresStoreOptions` et `PostgresStore`.
|
||
|
||
Le store sait :
|
||
|
||
- construire ses options depuis `kb_config::PostgresConfig` ;
|
||
- valider `url`, `max_connections` et `connect_timeout_ms` ;
|
||
- créer un pool `sqlx::PgPool` ;
|
||
- masquer le DSN pour les logs et l'interface ;
|
||
- lire `current_schema()` ;
|
||
- lire `version()` ;
|
||
- exécuter un healthcheck `SELECT 1` ;
|
||
- lire un snapshot de migrations non destructif.
|
||
|
||
Le store ne crée aucune table Solana lourde en `0.2.2`; raw est ajouté en `0.2.3` et core en `0.2.4`.
|
||
|
||
## Migrations
|
||
|
||
Le store applique un DDL idempotent contrôlé par le crate, dans le schéma courant du profil. Après validation réelle de la transition historique `0.2.x -> 0.3.1`, la baseline a été consolidée puis étendue par une migration additive dédiée au ledger :
|
||
|
||
```text
|
||
0001_canonical_transaction_store.sql
|
||
0002_core_store.sql
|
||
0003_processing_ledger.sql
|
||
```
|
||
|
||
Ces fichiers ne créent que les tables canoniques actuelles. Les anciens noms RPC/WS et la logique de conversion historique restent documentés dans `CHANGELOG.md`, mais ne font plus partie du DDL runtime.
|
||
|
||
Les futures migrations doivent :
|
||
|
||
- utiliser uniquement le schéma courant du profil ;
|
||
- créer des tables non qualifiées comme `kb_sol_raw_transactions` ;
|
||
- ajouter des contraintes strictes mais réversibles ;
|
||
- privilégier des index minimaux par `signature`, `slot`, `program_id` et `created_at` selon table ;
|
||
- éviter les contraintes métier prématurées.
|
||
|
||
## Règles locales
|
||
|
||
- Les commentaires de code restent en anglais.
|
||
- La documentation Markdown reste en français.
|
||
- Les exports publics sont contrôlés depuis `lib.rs`.
|
||
- Les erreurs passent par `kb_core::Error` et `kb_core::Result`.
|
||
- Les tests PostgreSQL réels sont reportables si aucun serveur local n'est garanti.
|
||
|
||
## Raw store `0.2.3`
|
||
|
||
Le jalon `0.2.3` ajoute les premières tables Solana réelles :
|
||
|
||
```text
|
||
kb_sol_raw_rpc_transactions
|
||
kb_sol_raw_ws_notifications
|
||
```
|
||
|
||
Ces tables restent dans le schéma PostgreSQL courant du profil actif. Elles sont créées par une initialisation idempotente du store PostgreSQL et non par des schémas applicatifs explicites.
|
||
|
||
La table RPC est dédupliquée par `signature`. La table WebSocket est dédupliquée par `notification_key`, avec signature optionnelle et lien optionnel vers une transaction RPC canonique déjà stockée.
|
||
|
||
La méthode `PostgresStore::initialize_raw_store_schema()` applique uniquement le schéma raw minimal. Si `database.postgres.auto_initialize_schema` vaut `true`, cette initialisation est appelée après la connexion.
|
||
|
||
`0.2.3` ne supprime pas encore physiquement les payloads raw. Les colonnes `retention_state`, `processing_state`, `raw_json_hash` et `lifecycle_reason` préparent la compaction, l'archivage et la purge future.
|
||
|
||
## Core store `0.2.4`
|
||
|
||
Le jalon `0.2.4` ajoute les tables core minimales :
|
||
|
||
```text
|
||
kb_sol_core_transactions
|
||
kb_sol_core_account_keys
|
||
kb_sol_core_instructions
|
||
kb_sol_core_inner_instructions
|
||
kb_sol_core_logs
|
||
kb_sol_core_balance_changes
|
||
```
|
||
|
||
`PostgresStore::initialize_core_store_schema()` applique le schéma raw puis le schéma core, car les transactions core peuvent référencer les transactions raw.
|
||
|
||
`PostgresStore::initialize_store_schema()` applique maintenant raw puis core. Si `database.postgres.auto_initialize_schema` vaut `true`, la connexion applique donc les deux familles de tables.
|
||
|
||
Le repository `CoreTransactionStore` est implémenté pour :
|
||
|
||
- insérer la transaction core canonique ;
|
||
- insérer les account keys résolus ;
|
||
- insérer les instructions replayables ;
|
||
- insérer les inner instructions comme contexte d'arbre ;
|
||
- insérer les logs ordonnés ;
|
||
- insérer les balance changes ;
|
||
- lister les instructions à rejouer ;
|
||
- construire `CoreInstructionReplayInput` avec contexte, dont toutes les instructions outer de la signature ordonnées par index numérique ;
|
||
- marquer le lifecycle d'une instruction.
|
||
|
||
## Baseline canonique `0.3.1`
|
||
|
||
`0.3.1` a validé sur PostgreSQL réel la transition des anciennes tables vers :
|
||
|
||
```text
|
||
kb_sol_raw_transactions
|
||
kb_sol_obs_transaction_observations
|
||
kb_sol_core_transactions.raw_transaction_id
|
||
```
|
||
|
||
La transaction raw est unique par signature et porte le document canonique versionné. Les observations sont légères et ne contiennent aucun payload transactionnel complet. Les repositories actifs exposent `RawTransactionInsert` et `TransactionObservationInsert`.
|
||
|
||
Après validation, les scripts SQL historiques ont été remplacés par la baseline courante `0001`/`0002`. L’initialiseur crate-managed conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`; il ne recrée pas les anciennes tables.
|
||
|
||
## Extraction core `0.3.4`
|
||
|
||
`kb_store_pg` implémente `CoreExtractionStore` avec une transaction PostgreSQL unique par signature. Une extraction réussie :
|
||
|
||
- remplace le graphe core de la signature par cascade ;
|
||
- insère transaction, account keys, instructions, inner instructions, logs et balance changes ;
|
||
- marque la ligne raw `core_extracted` ;
|
||
- upsert le ledger `kb_sol_ops_processing_ledger` avec la version et le hash canonique ;
|
||
- commit l’ensemble ou annule toutes les écritures.
|
||
|
||
Les échecs d’extraction sont enregistrés séparément dans le ledger et dans l’état raw afin de rester rejouables.
|
||
|
||
## Store decode et matérialisation `0.4.0`
|
||
|
||
La migration additive `0004_decode_materialization_store.sql` crée dans le schéma courant :
|
||
|
||
```text
|
||
kb_sol_decode_events
|
||
kb_sol_decode_coverage_declarations
|
||
kb_sol_decode_coverage_observations
|
||
kb_sol_mat_events
|
||
```
|
||
|
||
Ces tables réutilisent `kb_sol_ops_processing_ledger`. Les observations decode, leur couverture, l’état lifecycle et le ledger sont écrits dans une même transaction PostgreSQL. Les sorties materialized et leur ledger suivent la même règle. Les colonnes JSONB portent le suffixe `_jsonb`.
|
||
|
||
## Tracing opérationnel
|
||
|
||
`kb_store_pg` utilise le target canonique `kb_store_pg`. Les décisions SQL de decode/mat/ledger et leurs rollbacks sont émises par le store lui-même. Un `mark_decode_failed`, une persistance de bundle matérialisation en statut `failed` ou une erreur transactionnelle sont écrits au niveau `error` avec les identifiants de corrélation disponibles.
|
||
|
||
Le store ne délègue pas ces détails à `kb_app_demo` et ne journalise jamais un DSN non masqué.
|
||
|
||
## Contexte outer du replay `0.4.1-pre.014`
|
||
|
||
La lecture contextualisée agrège directement `kb_sol_core_instructions` pour la signature ciblée. Elle expose les chemins outer purement numériques sous la forme stable `instructionIndex`, `instructionPath`, `programId`, `payloadJson`, `payloadHash`, avec un `ORDER BY instruction_path::BIGINT`. L’instruction cible reste présente dans le tableau. La requête est strictement en lecture et n’ajoute aucune migration.
|