185 lines
9.0 KiB
Markdown
185 lines
9.0 KiB
Markdown
<!-- file: crates/ksp-store-postgres-lib/README.md -->
|
|
<!-- version: 11 -->
|
|
|
|
# ksp-store-postgres-lib
|
|
|
|
`ksp-store-postgres-lib` est le backend PostgreSQL physique officiel du Store KSP.
|
|
|
|
La crate implémente connexion, pool, TLS, migrations, health et persistence RAW derrière `ksp-store-lib`. Elle dépend directement de `ksp-store-api` mais ne dépend jamais de la façade `ksp-store-lib`.
|
|
|
|
## Responsabilités
|
|
|
|
La crate possède seule pour PostgreSQL :
|
|
|
|
- le parsing et la normalisation de la configuration physique `tokio-postgres` ;
|
|
- le pool borné `deadpool-postgres` ;
|
|
- la policy TLS physique avec Rustls ;
|
|
- les roots système et le provider cryptographique AWS-LC ;
|
|
- le bootstrap et le moteur de migrations privé KSP ;
|
|
- la table metadata `ksp_store_schema_migrations` ;
|
|
- l'advisory transaction lock borné des migrations ;
|
|
- les schémas physiques et statements privés `RawTransaction` et `RawAccountState` ;
|
|
- les snapshots runtime/health sûrs destinés au bridge de façade ;
|
|
- la fermeture explicite du pool et son fallback `Drop` best-effort ;
|
|
- la classification d'erreurs backend sans conserver le texte d'erreur PostgreSQL.
|
|
|
|
## Frontière d'utilisation
|
|
|
|
Les applications, jobs et workers KSP ne dépendent normalement pas de cette crate :
|
|
|
|
```text
|
|
consumer -> ksp-store-lib -> [feature postgres] ksp-store-postgres-lib
|
|
```
|
|
|
|
La surface publique de cette crate existe pour le bridge inter-crates et les tests/intégrations backend. Elle ne constitue pas une seconde façade Store.
|
|
|
|
`ksp-store-postgres-lib` ne réexporte pas `tokio-postgres`, Deadpool ou Rustls.
|
|
|
|
## Connexion et pool
|
|
|
|
`PostgresBackend::open` :
|
|
|
|
1. valide et normalise l'URI fournie explicitement ;
|
|
2. impose la policy TLS typée ;
|
|
3. construit un pool borné ;
|
|
4. prouve une connexion physique ;
|
|
5. vérifie/applique le bootstrap selon les settings ;
|
|
6. ne retourne qu'après succès de cette fondation.
|
|
|
|
Le backend ne lit aucun environnement, `.env`, `PG*`, `.pgpass` ou fichier TLS implicite libpq.
|
|
|
|
## TLS
|
|
|
|
Les modes sont exactement :
|
|
|
|
```text
|
|
Disabled
|
|
VerifyFull
|
|
```
|
|
|
|
`VerifyFull` exige TLS, roots système, certificat valide et vérification de l'identité serveur. Une configuration ne permettant pas de vérifier cette identité, comme `hostaddr` seul, est rejetée.
|
|
|
|
## Migrations et schéma
|
|
|
|
Le moteur de migrations embarqué vérifie version logique, nom et checksum SHA-256, sérialise les runners par advisory transaction lock et refuse une history divergente ou plus récente que le runtime.
|
|
|
|
Le bootstrap metadata est conservé comme migration V000. La migration logique V001 matérialise le schéma `RawTransaction` et V002 le schéma `RawAccountState`, chacune en ressources séparées `tables/`, `constraints/` et `indexes/` afin que le backend puisse vérifier leur compatibilité effective sans transformer les fichiers SQL en parser généraliste.
|
|
|
|
La base est liée à un seul `RawNetworkId` via `ksp_store_identity`. Une migration enregistrée mais physiquement divergente est un mismatch ; les réparations additives sûres dépendent de `schema_autoupdate`.
|
|
|
|
## Health et erreurs
|
|
|
|
`PostgresBackendRuntimeSnapshot` et `PostgresBackendHealthSnapshot` ne contiennent que des compteurs et états sûrs destinés à la façade.
|
|
|
|
`PostgresBackendError` ne conserve que :
|
|
|
|
```text
|
|
PostgresBackendErrorKind
|
|
phase statique
|
|
```
|
|
|
|
Le texte d'erreur PostgreSQL, l'URI, SQL et les valeurs bind ne traversent pas cette frontière.
|
|
|
|
## Support PostgreSQL
|
|
|
|
Le major minimal supporté est PostgreSQL 15. Le backend ne fixe aucun plafond arbitraire de major ; la compatibilité opérationnelle reste fondée sur le contrat de schéma KSP et l'introspection du catalogue.
|
|
|
|
## Lectures RAW transaction
|
|
|
|
Le backend expose :
|
|
|
|
```text
|
|
get_raw_transaction
|
|
get_raw_transaction_observation
|
|
get_raw_transaction_retention_state
|
|
get_raw_transaction_tombstone
|
|
```
|
|
|
|
Le SQL et les rows restent privés. Le mapping PostgreSQL est fallible et couvre notamment `NUMERIC(20,0) -> u64`, `BIGINT -> u32/u64`, timestamps bornés, bytes de taille fixe et codes de provenance.
|
|
|
|
`Full` lit le payload chaud, `Archived` le reconstruit depuis la relation archive et `Purged` retourne `None`; le tombstone reste accessible séparément.
|
|
|
|
## Écritures RAW transaction
|
|
|
|
Le backend expose :
|
|
|
|
```text
|
|
persist_raw_transaction_acquisition
|
|
record_raw_transaction_observation
|
|
```
|
|
|
|
L'acquisition canonique et son observation initiale sont commises dans une seule transaction PostgreSQL. Les clés uniques physiques fournissent l'admission idempotente ; après un conflit unique, le backend verrouille la ligne gagnante et compare le contenu réel avant de conclure `AlreadyPresent` ou `Conflict`.
|
|
|
|
Un tombstone `Purged` compatible produit `SkippedPurged/NotRecorded` en mode normal. `ForceRehydrate` restaure explicitement le payload `Full` et l'observation dans la même transaction.
|
|
|
|
## Pagination RAW transaction
|
|
|
|
`list_raw_transactions` parcourt les références canoniques récupérables avec un ordre total `(slot, signature)`. Les tombstones `Purged` sont exclus.
|
|
|
|
La continuation est une keyset stricte, jamais un `OFFSET`. Le cursor backend V1 est opaque et lié au réseau, à la direction et aux bornes de slots de la query. Store n'impose aucun plafond métier arbitraire à la taille de page ; seule la limitation physique du `LIMIT + 1` PostgreSQL est exposée.
|
|
|
|
## Rétention RAW transaction
|
|
|
|
`transition_raw_transaction_retention` applique les transitions physiques :
|
|
|
|
```text
|
|
Full -> Archived -> Purged
|
|
```
|
|
|
|
Le backend verrouille la ligne canonique avec `FOR UPDATE`, compare l'état courant à l'état attendu et applique la mutation atomiquement. L'archivage conserve le payload exact dans la relation archive ; la purge conserve seulement le tombstone minimal.
|
|
|
|
Toute transition impliquant `Compacted` est rejetée avec `RetentionCompactionUnsupported` tant qu'aucune représentation compactée réelle n'est implémentée.
|
|
|
|
## Lectures RAW account
|
|
|
|
Le backend expose :
|
|
|
|
```text
|
|
get_raw_account_state
|
|
get_raw_account_observation
|
|
```
|
|
|
|
`get_raw_account_state` reconstruit l'état complet à partir de `(pubkey, slot, state_hash)` sans narrowing du domaine `u64`. Les bytes `pubkey`, `owner` et `state_hash` sont revalidés à leur largeur exacte et `data` reste un `BYTEA` complet, vide autorisé, borné par le contrat Store API.
|
|
|
|
`get_raw_account_observation` reconstruit la provenance commune et les métadonnées account optionnelles, notamment `is_startup`, `transaction_signature` et `write_version`. La signature est une metadata fixed-width et ne crée aucune FK vers la famille transaction.
|
|
|
|
## Écritures RAW account
|
|
|
|
Le backend expose :
|
|
|
|
```text
|
|
persist_raw_account_acquisition
|
|
record_raw_account_observation
|
|
```
|
|
|
|
L'acquisition état+observation est transactionnelle. Les inserts utilisent `ON CONFLICT ... DO NOTHING`, puis verrouillent et comparent le contenu gagnant avant de conclure `AlreadyPresent` ou `Conflict`; aucun `DO UPDATE` n'est utilisé. Une collision divergente d'observation fait échouer toute l'acquisition et rollback un éventuel nouvel état.
|
|
|
|
L'ajout d'une observation vérifie que l'état référencé existe déjà et ne crée jamais implicitement cet état.
|
|
|
|
## Pagination RAW account
|
|
|
|
`list_raw_account_states` parcourt les références selon l'ordre total `(slot, pubkey, state_hash)`, en ASC ou DESC, avec filtre pubkey optionnel. La continuation est keyset, sans `OFFSET`.
|
|
|
|
Le cursor `KSPA` est opaque et lié au réseau, au filtre pubkey, à la direction, aux bornes de slots et à la dernière clé complète. Il est distinct du cursor transaction `KSPT`.
|
|
|
|
## Hors périmètre
|
|
|
|
La crate ne contient :
|
|
|
|
- aucune rétention, archive, purge, suppression ou compaction account ;
|
|
- aucune orchestration worker/job ;
|
|
- aucun transport d'acquisition ou decoder Program ;
|
|
- aucune policy autonome de batch, priorité ou rétention.
|
|
|
|
## Documentation
|
|
|
|
- [`USAGE.md`](USAGE.md) — guide pratique du bridge physique et de ses capabilities ;
|
|
- [`../ksp-store-lib/README.md`](../ksp-store-lib/README.md) — façade runtime destinée aux consumers ;
|
|
- [`../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md`](../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md) — architecture Store ;
|
|
- [`../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md`](../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md) — décisions pool/TLS/migrations ;
|
|
- [`../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md`](../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md) — validation de fondation ;
|
|
- [`../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md`](../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md) — design `RawTransaction` ;
|
|
- [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md) — validation `RawTransaction` ;
|
|
- [`../../docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md`](../../docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md) — design `RawAccountState` et complétude RAW ;
|
|
- [`../../docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md`](../../docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md) — validation `RawAccountState` et conformance RAW.
|