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
RawTransactionetRawAccountState; - les snapshots runtime/health sûrs destinés au bridge de façade ;
- la fermeture explicite du pool et son fallback
Dropbest-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 :
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 :
- valide et normalise l'URI fournie explicitement ;
- impose la policy TLS typée ;
- construit un pool borné ;
- prouve une connexion physique ;
- vérifie/applique le bootstrap selon les settings ;
- 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 :
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 :
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 :
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 :
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 :
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 :
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 :
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— guide pratique du bridge physique et de ses capabilities ;../ksp-store-lib/README.md— façade runtime destinée aux consumers ;../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md— architecture Store ;../../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— validation de fondation ;../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md— designRawTransaction;../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md— validationRawTransaction;../../docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md— designRawAccountStateet complétude RAW ;../../docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md— validationRawAccountStateet conformance RAW.