167 lines
7.3 KiB
Markdown
167 lines
7.3 KiB
Markdown
<!-- file: docs/guides/POSTGRES_STORAGE.md -->
|
||
<!-- version: 7 -->
|
||
|
||
# Guide PostgreSQL et contrats de stockage
|
||
|
||
## Objectif
|
||
|
||
`ks-store` porte la frontière de stockage généraliste. PostgreSQL reste le backend opérationnel actif, mais il est encapsulé derrière la façade publique backend-agnostique `Store`.
|
||
|
||
## Connexion
|
||
|
||
Les consommateurs ne construisent plus `PostgresStore`, `PostgresStoreOptions` ni un pool SQLx. Ils transmettent le backend sélectionné et ses options résolues à `StoreOpenOptions`; `ks-store` valide et interprète ensuite les paramètres PostgreSQL.
|
||
|
||
```rust
|
||
let options = match ks_store::StoreOpenOptions::new(
|
||
true,
|
||
"postgres",
|
||
serde_json::json!({
|
||
"url": database_url,
|
||
"max_connections": 10,
|
||
"connect_timeout_ms": 10_000,
|
||
"auto_initialize_schema": true
|
||
}),
|
||
) {
|
||
std::result::Result::Ok(value) => value,
|
||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||
};
|
||
let store = match ks_store::Store::open(options).await {
|
||
std::result::Result::Ok(value) => value,
|
||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||
};
|
||
```
|
||
|
||
Le descripteur public éventuellement affiché provient du résumé runtime et est déjà masqué par `ks-store`. Le DSN brut et les options backend ne doivent jamais être propagés dans les DTO, erreurs applicatives ou logs normaux.
|
||
|
||
## Domaines
|
||
|
||
- N1 raw/acquisition : transactions canoniques, observations de transactions et observations génériques de comptes ;
|
||
- N2 Core : transactions, clés, top-level, CPI, logs, variations de balances, return data et états de comptes ;
|
||
- N3/transverse : ledger, observations décodées, couverture et `k_sol_mat_outputs` ;
|
||
- replay/diagnostic : sélections et résumés bornés au-dessus de ces contrats.
|
||
|
||
## Baseline `0.5.3-pre.003`
|
||
|
||
`pre.003` reconstruit directement le schéma sous `k_sol_*`. Les bases Devnet/Mainnet/test historiques sont considérées reconstructibles ; aucune migration in-place depuis `kb_sol_*` n'est fournie.
|
||
|
||
Le comportement attendu à l'ouverture est :
|
||
|
||
1. base vide + auto-init : création du baseline complet ;
|
||
2. base `k_sol_*` déjà conforme : vérification/idempotence ;
|
||
3. base contenant encore des objets `kb_sol_*` : refus non destructif et demande de reconstruction explicite ;
|
||
4. `DROP`/`TRUNCATE` : opérations de maintenance distinctes, jamais déclenchées implicitement par `Store::open()`.
|
||
|
||
Le baseline candidat au gel comprend 16 tables N1-N3.
|
||
|
||
## Ressources SQL
|
||
|
||
Les ressources actives sont sous :
|
||
|
||
```text
|
||
ks-store/migrations/postgres/
|
||
schema/
|
||
tables/
|
||
constraints/
|
||
indexes/
|
||
maintenance/
|
||
truncate/
|
||
drop/
|
||
```
|
||
|
||
Chaque fichier contient une instruction SQL. Le code PostgreSQL privé utilise `include_str!` et l'orchestrateur global exécute le schéma dans une transaction unique avec advisory lock.
|
||
|
||
Il n'existe plus trois initialiseurs raw/Core/decode indépendants : **l'initialisation du schéma est globale**.
|
||
|
||
Le corpus `pre.003` contient actuellement :
|
||
|
||
- 16 créations de tables ;
|
||
- 129 ressources de contraintes ;
|
||
- 59 créations d'index explicites ;
|
||
- 16 ressources `TRUNCATE` ;
|
||
- 16 ressources `DROP` ;
|
||
- quatre index supplémentaires justifiés par le replay/invalidation `pre.004` ;
|
||
- soit **240 ressources SQL**.
|
||
|
||
PostgreSQL attend désormais **79 index** au total dans son diagnostic : 63 index explicites plus les 16 index de clé primaire créés par PostgreSQL.
|
||
|
||
Les index non uniques restent des optimisations physiques et ne font pas partie du gel logique N1-N3.
|
||
|
||
## Repositories `pre.004`
|
||
|
||
La tranche `pre.004` conserve les 16 tables et ferme les comportements suivants :
|
||
|
||
- replay logique commun top-level/CPI avec scope explicite et pagination keyset ;
|
||
- lifecycle symétrique top-level/CPI ;
|
||
- invalidation N2 Core -> N3 lors d’un remplacement de signature ;
|
||
- invalidation decode -> matérialisation pour l’identité decoder exacte ;
|
||
- normalisation versionnée `k_sol_obs_account_observations` -> `k_sol_core_account_states` ;
|
||
- limites interactives à 500 et batches de processing à 1 000 ;
|
||
- traces fonctionnelles `TRACE ks-store` avec durée/lignes/outcome, sans bind values.
|
||
|
||
Les quatre nouveaux index couvrent l’ordre de replay CPI et les recherches d’invalidation/descendants dans coverage/materialization. Leur utilité finale doit encore être confirmée par des plans PostgreSQL réels avant le gel ; les index non uniques restent une optimisation physique.
|
||
|
||
## Statut du schéma
|
||
|
||
La façade n'utilise plus `_sqlx_migrations` comme preuve du baseline actuel. Le statut est dérivé du contrat réellement observé :
|
||
|
||
- aucune ressource connue : non initialisé ;
|
||
- toutes les ressources attendues : contrat courant ;
|
||
- présence partielle : drift/partial.
|
||
|
||
Le résumé d'initialisation expose des compteurs de modèles, tables et index sans publier leurs noms physiques.
|
||
|
||
## Compatibilité des identités de processeurs
|
||
|
||
Depuis `0.5.1-pre.003`, les identités techniques produites par `ks-lib` utilisent `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*`.
|
||
|
||
Comme les bases `0.5.2` doivent être reconstruites pour le nouveau baseline `0.5.3`, les anciennes identités `kb-lib.*` ne sont pas migrées automatiquement dans ce chantier.
|
||
|
||
## Core readiness
|
||
|
||
`pre.003` ajoute les éléments génériques nécessaires au futur redécodage :
|
||
|
||
- `block_time` sur raw/Core transaction ;
|
||
- `stack_height` sur top-level/CPI ;
|
||
- parent CPI immédiat reconstructible ;
|
||
- `k_sol_core_return_data` ;
|
||
- `k_sol_obs_account_observations` ;
|
||
- `k_sol_core_account_states` ;
|
||
- provenance de schéma optionnelle sur les observations décodées.
|
||
|
||
Ces contrats sont indépendants d'Anchor/IDL. Aucun parser Anchor ni table `anchor_*` n'appartient à `0.5.3`.
|
||
|
||
## Repositories
|
||
|
||
Les traits publics et la façade `Store` décrivent des opérations fonctionnelles indépendantes du moteur. PostgreSQL implémente ces contrats à l'intérieur de `ks-store`; aucune crate consommatrice ne reçoit `PgPool`, un type SQLx ou une requête concrète.
|
||
|
||
Le schéma des observations/états de comptes est introduit avant gel en `pre.003`; son ingestion/replay fonctionnel est raccordé dans la tranche repositories/replay suivante.
|
||
|
||
## Diagnostics
|
||
|
||
La surface publique expose :
|
||
|
||
- health snapshot ;
|
||
- schema/migration snapshot générique ;
|
||
- backend descriptor masqué ;
|
||
- résumé de configuration sanitisé ;
|
||
- résumé d'initialisation/vérification par modèle logique ;
|
||
- diagnostics de ressources sans nom physique d'objet.
|
||
|
||
Les noms de tables, index, contraintes et le SQL restent des détails backend-internes. Les traces PostgreSQL utilisent l'unique `target: crate::TRACING_TARGET` (`ks-store`) avec les champs structurés `backend="postgres"`, `domain="ks-store.pg"` et un `action` précis.
|
||
|
||
## Invariants
|
||
|
||
- aucune donnée canonique ne doit être dupliquée sans justification ;
|
||
- les écritures rejouables doivent être idempotentes ;
|
||
- la progression de campagne doit rester cohérente avec les lignes effectivement traitées ;
|
||
- les requêtes dynamiques n'acceptent que des identifiants validés ;
|
||
- les détails PostgreSQL utiles au diagnostic restent backend-internes ;
|
||
- les erreurs traversant la façade publique ne doivent jamais divulguer DSN, password ou options sensibles ;
|
||
- un nouveau décodeur doit pouvoir exploiter N1/N2/N3 sans remodelage normal de ces niveaux.
|
||
|
||
## Références
|
||
|
||
- `docs/architecture/STORAGE_ARCHITECTURE.md` ;
|
||
- `ks-store/USAGE.md` ;
|
||
- `ks-store/README.md`.
|