218 lines
9.3 KiB
Markdown
218 lines
9.3 KiB
Markdown
<!-- file: crates/ksp-store-postgres-lib/USAGE.md -->
|
|
<!-- version: 5 -->
|
|
|
|
# Utilisation de ksp-store-postgres-lib
|
|
|
|
## 1. Quand utiliser cette crate directement
|
|
|
|
Le consumer applicatif normal utilise `ksp-store-lib`.
|
|
|
|
Une dépendance directe à `ksp-store-postgres-lib` est réservée aux composants qui implémentent ou testent le bridge physique PostgreSQL. La crate backend ne doit pas devenir une façade parallèle.
|
|
|
|
Un tel composant doit déclarer explicitement le backend et `ksp-store-api`, car `PostgresBackendSettings::new` reçoit le `RawNetworkId` backend-neutral sans le réexporter :
|
|
|
|
```toml
|
|
[dependencies]
|
|
ksp-store-api = { path = "../ksp-store-api" }
|
|
ksp-store-postgres-lib = { path = "../ksp-store-postgres-lib" }
|
|
```
|
|
|
|
## 2. Construire le bridge physique
|
|
|
|
`PostgresBackendSettings` reçoit des valeurs déjà possédées et validées par la couche appelante. L'URI est sensible et son `Debug` est redacted.
|
|
|
|
```rust
|
|
fn backend_settings(
|
|
network: ksp_store_api::RawNetworkId,
|
|
connection_uri: std::string::String,
|
|
) -> ksp_store_postgres_lib::PostgresBackendSettings {
|
|
return ksp_store_postgres_lib::PostgresBackendSettings::new(
|
|
network,
|
|
connection_uri,
|
|
8,
|
|
std::time::Duration::from_secs(10),
|
|
std::time::Duration::from_secs(5),
|
|
std::time::Duration::from_secs(10),
|
|
std::time::Duration::from_secs(5),
|
|
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull,
|
|
true,
|
|
std::time::Duration::from_secs(30),
|
|
std::time::Duration::from_secs(10),
|
|
);
|
|
}
|
|
```
|
|
|
|
Le backend reçoit un seul `RawNetworkId`. Une instance physique n'est pas un routeur multi-réseau.
|
|
|
|
## 3. Ouvrir, sonder et fermer
|
|
|
|
```rust
|
|
async fn use_backend(
|
|
settings: ksp_store_postgres_lib::PostgresBackendSettings,
|
|
) -> std::result::Result<(), ksp_store_postgres_lib::PostgresBackendError> {
|
|
let backend = ksp_store_postgres_lib::PostgresBackend::open(settings).await;
|
|
let backend = match backend {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
let runtime = backend.runtime_snapshot();
|
|
let _capacity = runtime.pool_capacity();
|
|
let _size = runtime.pool_size();
|
|
let _available = runtime.pool_available();
|
|
let _waiting = runtime.pool_waiting();
|
|
|
|
let health = backend.health().await;
|
|
let _ready = health.ready();
|
|
let _migration_version = health.migration_version();
|
|
let _pending = health.pending_migration_count();
|
|
let _safe_error_kind = health.last_error_kind();
|
|
|
|
return backend.close(std::time::Duration::from_secs(5)).await;
|
|
}
|
|
```
|
|
|
|
`open` prouve la connexion et le bootstrap avant de retourner. `close` ferme le pool puis attend son drain dans la deadline fournie.
|
|
|
|
## 4. Choisir le mode TLS
|
|
|
|
### `VerifyFull`
|
|
|
|
À utiliser pour les connexions PostgreSQL protégées :
|
|
|
|
```rust
|
|
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull
|
|
```
|
|
|
|
Le backend charge les roots système et vérifie certificat + identité serveur. Il rejette une configuration ne fournissant pas d'identité vérifiable.
|
|
|
|
### `Disabled`
|
|
|
|
```rust
|
|
ksp_store_postgres_lib::PostgresBackendTlsMode::Disabled
|
|
```
|
|
|
|
Ce mode désactive explicitement TLS. Il ne doit être utilisé que lorsque la topologie de déploiement justifie clairement une connexion non chiffrée.
|
|
|
|
La valeur typée choisie par KSP prime sur les paramètres SSL de l'URI.
|
|
|
|
## 5. Bootstrap et migrations
|
|
|
|
Le backend embarque son propre moteur de migrations. La migration historique V000 est désormais matérialisée par :
|
|
|
|
```text
|
|
migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql
|
|
```
|
|
|
|
Les migrations suivantes conservent une version logique unique tout en séparant leurs ressources physiques par famille sous `migrations/vNNN_name/{tables,constraints,indexes}/`. Le bootstrap maintient :
|
|
|
|
```text
|
|
ksp_store_schema_migrations
|
|
version
|
|
name
|
|
checksum SHA-256
|
|
```
|
|
|
|
Le runner est transactionnel et sérialisé par advisory transaction lock. Une divergence de checksum/nom/version ou une history plus récente est terminale ; aucun down migration automatique n'est exécuté.
|
|
|
|
`schema_autocreate` contrôle l'initialisation/adoption du schéma et `schema_autoupdate` les migrations pending ainsi que les réparations additives sûres. Le constructeur legacy `auto_migrate` mappe encore les deux politiques pour compatibilité source.
|
|
|
|
## 6. Classifier les erreurs sans fuite
|
|
|
|
```rust
|
|
match error.kind() {
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::Conflict => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::DataInvalid => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::PageLimitUnsupported => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::QueryInvalid => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::ReadFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::ReferenceNotFound => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::WriteFailed => {}
|
|
ksp_store_postgres_lib::PostgresBackendErrorKind::WrongNetwork => {}
|
|
_ => {}
|
|
}
|
|
|
|
let _safe_phase = error.phase();
|
|
```
|
|
|
|
Ne pas reconstruire un diagnostic utilisateur à partir de l'erreur brute PostgreSQL : cette erreur n'est volontairement pas conservée par le bridge.
|
|
|
|
## 7. Lectures RAW transaction
|
|
|
|
Depuis `0.3.3-pre.004`, un backend ouvert expose quatre lectures backend-specific retournant exclusivement les modèles communs :
|
|
|
|
```rust
|
|
let transaction = backend.get_raw_transaction(&reference).await;
|
|
let observation = backend.get_raw_transaction_observation(&observation_key).await;
|
|
let retention = backend.get_raw_transaction_retention_state(&reference).await;
|
|
let tombstone = backend.get_raw_transaction_tombstone(&reference).await;
|
|
```
|
|
|
|
Le backend ne rend jamais `tokio_postgres::Row`, SQL, SQLSTATE ou valeur de bind. Pour les références réseau-scopées, un mauvais réseau est rejeté avant acquisition d'un client du pool. Une corruption de row est projetée vers `DataInvalid`, un échec physique de lecture vers `ReadFailed`, et les erreurs de pool conservent leur classification bornée existante.
|
|
|
|
`get_raw_transaction` retourne :
|
|
|
|
```text
|
|
Full -> payload chaud
|
|
Archived -> payload archive reconstruit
|
|
Purged -> None
|
|
absent -> None
|
|
```
|
|
|
|
Le tombstone `Purged` reste lisible séparément.
|
|
|
|
## 8. Écritures RAW transaction
|
|
|
|
Depuis `0.3.3-pre.005`, le backend physique expose également :
|
|
|
|
```rust
|
|
let acquisition = backend
|
|
.persist_raw_transaction_acquisition(transaction, observation, mode)
|
|
.await;
|
|
|
|
let observation = backend
|
|
.record_raw_transaction_observation(additional_observation)
|
|
.await;
|
|
```
|
|
|
|
La première opération est atomique : transaction canonique et observation sont toutes deux durables ou aucune ne l'est. Les doublons ne sont pas détectés par une prélecture `has_*` : l'insert unique est tenté directement, puis un conflit relit/verrouille la ligne gagnante et compare son contenu. Une divergence sous la même signature ou la même `observation_key` produit `PostgresBackendErrorKind::Conflict`.
|
|
|
|
Pour une transaction purgée, le mode normal retourne `SkippedPurged/NotRecorded` lorsque le tombstone est compatible. `ForceRehydrate` restaure le payload `Full` puis enregistre l'observation dans la même transaction. `record_raw_transaction_observation` ne crée jamais de canonique : référence absente -> `ReferenceNotFound`, canonique `Purged` -> `NotRecorded`.
|
|
|
|
Les références réseau-scopées sont toujours validées avant `pool.get()`.
|
|
|
|
## 9. Paginer les transactions RAW
|
|
|
|
Depuis `0.3.3-pre.006`, le backend fournit une navigation keyset déterministe :
|
|
|
|
```rust
|
|
let page = backend.list_raw_transactions(&query).await;
|
|
```
|
|
|
|
La query utilise les bornes inclusives de `RawSlotRange`, la direction demandée et le `RawPageLimit` exact. Les lignes `Purged` sont exclues. L'ordre est `(slot, signature)` dans les deux directions et la continuation utilise un cursor opaque de 109 octets lié au réseau, à la direction et aux bornes de la query.
|
|
|
|
Le cursor peut être transmis uniquement à une query ayant le même binding. Un changement de réseau/range/direction ou des bytes hostiles produit `QueryInvalid` avant acquisition du pool. Le backend n'utilise aucun `OFFSET` et ne fournit pas de snapshot transactionnel entre deux pages.
|
|
|
|
La seule limite physique est liée au `LIMIT + 1` PostgreSQL : `requested <= i64::MAX - 1`. Au-delà, `PageLimitUnsupported` est retourné ; la demande n'est jamais ramenée à 100, 500, 1000 ou une autre policy de worker.
|
|
|
|
## 10. Ce que cette crate ne permet pas encore
|
|
|
|
La tranche ne fournit pas encore :
|
|
|
|
```text
|
|
transitions de rétention
|
|
implémentations complètes des six traits RawTransaction*
|
|
capabilities RawAccount*
|
|
```
|
|
|
|
Ces surfaces sont ajoutées dans les prereleases suivantes avant le dispatch `ksp-store-lib`.
|