7.9 KiB
ksp-store-postgres-lib
ksp-store-postgres-lib est le backend PostgreSQL physique officiel du Store KSP.
La crate implémente la fondation connexion/pool/TLS/migrations/health 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/moteur de migrations privé KSP ;
- la table metadata
ksp_store_schema_migrations; - la migration logique V000 relocalisée sous
migrations/v000_bootstrap/et son checksum SHA-256 historique ; - l'advisory transaction lock borné des migrations ;
- 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 d'intégration 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 ;
- vérification de l'identité serveur ;
- aucune dégradation automatique en plaintext.
Les configurations ne permettant pas de vérifier une identité serveur, comme hostaddr seul, sont rejetées.
Migrations
La fondation historique V000 est embarquée sous :
migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql
La vertical slice V001 est ensuite répartie sous migrations/v001_raw_transaction/{tables,constraints,indexes}/ tout en restant une migration logique unique. V000 crée la metadata privée :
ksp_store_schema_migrations
Le moteur vérifie version, 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.
V001 possède désormais le schéma physique RawTransaction et son contrat de compatibilité. Les lectures exactes sont acquises depuis pre.004; pre.005 ajoute les écritures atomiques transaction + observation, l'idempotence réelle et la classification de conflit. pre.006 ajoute la navigation keyset déterministe sur l'index (slot, signature) et son cursor opaque lié à la requête.
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
La politique de support de 0.3.2 fixe PostgreSQL 15 comme major minimal. Le test live de fondation refuse explicitement un serveur plus ancien ; le backend ne fixe aucun plafond arbitraire de major PostgreSQL. La compatibilité de migration reste basée sur le schéma KSP.
La preuve opérateur réelle et le major effectivement exercé sont conservés dans la matrice de validation, pas dans cette documentation durable.
Lectures RAW 0.3.3-pre.004
Le backend expose désormais quatre lectures étroites qui retournent uniquement des modèles ksp-store-api :
get_raw_transaction
get_raw_transaction_observation
get_raw_transaction_retention_state
get_raw_transaction_tombstone
Le SQL et les rows restent privés au backend. 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. Une ligne stockée incompatible produit uniquement PostgresBackendErrorKind::DataInvalid; un échec de SELECT produit ReadFailed. Les lectures portant un RawTransactionReference rejettent un réseau différent avant toute acquisition de pool.
Full lit le payload chaud, Archived le reconstruit depuis la relation archive et Purged retourne None; le tombstone reste accessible séparément pour Purged.
Écritures RAW 0.3.3-pre.005
Le backend expose deux écritures étroites :
persist_raw_transaction_acquisition
record_raw_transaction_observation
L'acquisition canonique et son observation sont commises dans une seule transaction PostgreSQL. L'insertion utilise les clés uniques physiques sans prélecture has_*; après un conflit unique, le backend verrouille la ligne gagnante et compare le contenu réel avant de conclure AlreadyPresent ou Conflict. Les octets du payload sont comparés lorsqu'ils existent encore : le hash seul ne constitue jamais une preuve d'idempotence.
Un tombstone Purged compatible produit SkippedPurged/NotRecorded en mode normal. ForceRehydrate restaure Full et l'observation dans la même transaction. Une observation supplémentaire ne crée jamais implicitement son canonique ; une référence absente est classée ReferenceNotFound et un canonical purgé retourne NotRecorded.
Les erreurs physiques d'écriture sont réduites à WriteFailed; aucune erreur serveur, SQLSTATE, query ou valeur de bind n'est conservée.
Pagination RAW 0.3.3-pre.006
list_raw_transactions parcourt uniquement les références canoniques récupérables, en excluant les tombstones Purged. L'ordre total est :
Ascending = (slot ASC, signature ASC)
Descending = (slot DESC, signature DESC)
La continuation est une keyset stricte > / <, jamais un OFFSET. Le cursor backend V1 fait exactement 109 octets et transporte KSPT, version 1, le dernier slot, la dernière signature et un digest SHA-256 lié au réseau, à la direction et aux bornes de slots. Un cursor rejoué sous une autre query est QueryInvalid.
RawPageLimit n'est pas réduit par une policy KSP : PostgreSQL utilise LIMIT requested + 1, avec comme seule borne physique requested <= i64::MAX - 1. Une valeur supérieure produit PageLimitUnsupported sans clamp. La pagination ne promet aucun snapshot inter-pages.
Hors périmètre actuel
La crate ne contient encore :
- aucune pagination/listing
RawTransaction; - aucune implémentation complète des traits
RawTransaction*deksp-store-apitant quelist_raw_transactionsmanque ; - aucune transition mutante de rétention ;
- aucune implémentation PostgreSQL des capabilities
RawAccount*; - aucune orchestration worker/job ;
- aucun transport d'acquisition ou decoder Program.
Documentation
USAGE.md— bridge physique et lifecycle ;../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— preuves déterministes et PostgreSQL réel.