294 lines
10 KiB
Markdown
294 lines
10 KiB
Markdown
<!-- file: crates/ksp-store-lib/USAGE.md -->
|
|
<!-- version: 4 -->
|
|
|
|
# Utilisation de ksp-store-lib
|
|
|
|
## 1. Dépendance et backend compilé
|
|
|
|
Le consumer runtime dépend de la façade commune :
|
|
|
|
```toml
|
|
[dependencies]
|
|
ksp-store-lib = { path = "../ksp-store-lib" }
|
|
```
|
|
|
|
La feature par défaut compile le backend PostgreSQL :
|
|
|
|
```text
|
|
postgres
|
|
```
|
|
|
|
Pour compiler la façade sans backend physique :
|
|
|
|
```toml
|
|
ksp-store-lib = { path = "../ksp-store-lib", default-features = false }
|
|
```
|
|
|
|
Dans ce mode, les settings PostgreSQL restent représentables, mais `Store::open` retourne `ERROR_CODE_BACKEND_NOT_COMPILED` avant toute I/O si PostgreSQL est sélectionné.
|
|
|
|
Un consumer applicatif ordinaire ne dépend pas directement de `ksp-store-postgres-lib`.
|
|
|
|
## 2. Obtenir les settings depuis Config
|
|
|
|
Le chemin applicatif recommandé passe par `ksp-config-lib`, propriétaire de `std.store`, de la résolution `.env` et des secrets.
|
|
|
|
```rust
|
|
fn resolve_store_settings(
|
|
engine: &ksp_config_lib::ConfigDocumentEngine,
|
|
environment: &ksp_config_lib::ConfigEnvironment,
|
|
target: std::option::Option<&str>,
|
|
) -> ksp_core_lib::Result<ksp_store_lib::StoreSettings> {
|
|
let resolved = engine.load_resolved_store_config(target, environment);
|
|
let resolved = match resolved {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
return std::result::Result::Ok(resolved.into_settings());
|
|
}
|
|
```
|
|
|
|
Chaque `StoreSettings` sélectionne exactement un réseau logique et un backend physique. La sélection d'un target nommé appartient à Config ; `Store` ne route pas automatiquement entre plusieurs targets.
|
|
|
|
## 3. Construire des settings programmatiquement
|
|
|
|
La construction directe est utile pour les tests et outils qui ne passent pas par Config.
|
|
|
|
```rust
|
|
fn programmatic_store_settings(connection_uri: std::string::String) -> ksp_store_lib::Result<ksp_store_lib::StoreSettings> {
|
|
let network = ksp_store_lib::RawNetworkId::new("devnet");
|
|
let network = match network {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
let postgres = ksp_store_lib::PostgresStoreSettings::new(
|
|
connection_uri,
|
|
ksp_store_lib::PostgresPoolSettings::default(),
|
|
ksp_store_lib::PostgresTlsMode::VerifyFull,
|
|
ksp_store_lib::PostgresBootstrapSettings::default(),
|
|
);
|
|
|
|
let settings = ksp_store_lib::StoreSettings::with_default_shutdown(
|
|
network,
|
|
ksp_store_lib::StoreBackendSettings::Postgres(postgres),
|
|
);
|
|
|
|
if let std::result::Result::Err(error) = settings.validate() {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
|
|
return std::result::Result::Ok(settings);
|
|
}
|
|
```
|
|
|
|
`PostgresStoreSettings` ne fournit aucun getter public de l'URI. Son `Debug` remplace cette valeur par `<redacted>`.
|
|
|
|
## 4. Ouvrir, sonder et fermer un Store
|
|
|
|
`Store::open` est async et ne retourne un succès qu'après validation des settings, ouverture du backend compilé et bootstrap requis.
|
|
|
|
```rust
|
|
async fn use_store(settings: ksp_store_lib::StoreSettings) -> ksp_store_lib::Result<()> {
|
|
let store = ksp_store_lib::Store::open(settings).await;
|
|
let store = match store {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
let runtime = store.runtime_snapshot();
|
|
let _network = runtime.network();
|
|
let _capacity = runtime.pool_capacity();
|
|
let _size = runtime.pool_size();
|
|
let _available = runtime.pool_available();
|
|
let _waiting = runtime.pool_waiting();
|
|
|
|
let health = store.health().await;
|
|
match health.state() {
|
|
ksp_store_lib::StoreHealthState::Ready => {}
|
|
ksp_store_lib::StoreHealthState::NotReady => {
|
|
let _safe_error_code = health.last_error_code();
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
return store.close().await;
|
|
}
|
|
```
|
|
|
|
`Store::close(self)` consomme l'instance. Une fermeture explicite ne peut donc pas être suivie d'une nouvelle opération via la même valeur.
|
|
|
|
## 5. Lire une transaction RAW
|
|
|
|
Importer le trait correspondant suffit pour utiliser la façade :
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionRead;
|
|
|
|
async fn read_transaction(
|
|
store: &ksp_store_lib::Store,
|
|
reference: &ksp_store_lib::RawTransactionReference,
|
|
) -> ksp_store_lib::Result<std::option::Option<ksp_store_lib::RawTransaction>> {
|
|
return store.get_raw_transaction(reference).await;
|
|
}
|
|
```
|
|
|
|
Une transaction absente retourne `None`. Une transaction `Purged` retourne également `None` pour le payload canonique ; son tombstone reste accessible via la capability de rétention.
|
|
|
|
## 6. Paginer les références de transactions
|
|
|
|
La pagination est keyset et utilise un cursor opaque. Le consumer ne doit pas interpréter ses bytes.
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionRead;
|
|
|
|
async fn first_transaction_page(
|
|
store: &ksp_store_lib::Store,
|
|
network: ksp_store_lib::RawNetworkId,
|
|
) -> ksp_store_lib::Result<ksp_store_lib::RawPage<ksp_store_lib::RawTransactionReference>> {
|
|
let limit = ksp_store_lib::RawPageLimit::new(100);
|
|
let limit = match limit {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
let slots = ksp_store_lib::RawSlotRange::new(std::option::Option::None, std::option::Option::None);
|
|
let slots = match slots {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
let query = ksp_store_lib::RawTransactionQuery::new(
|
|
network,
|
|
slots,
|
|
ksp_store_lib::RawSortDirection::Ascending,
|
|
ksp_store_lib::RawPageRequest::first(limit),
|
|
);
|
|
|
|
return store.list_raw_transactions(&query).await;
|
|
}
|
|
```
|
|
|
|
Pour continuer, recopier le cursor retourné par `RawPage::next_cursor()` dans `RawPageRequest::after`. Le réseau, la direction et les bornes de slots doivent rester identiques à ceux de la query ayant produit le cursor.
|
|
|
|
## 7. Persister une acquisition canonique
|
|
|
|
La transaction canonique et son observation initiale forment une seule opération atomique.
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionWrite;
|
|
|
|
async fn persist_acquisition(
|
|
store: &ksp_store_lib::Store,
|
|
transaction: ksp_store_lib::RawTransaction,
|
|
observation: ksp_store_lib::RawTransactionObservation,
|
|
) -> ksp_store_lib::Result<ksp_store_lib::RawAcquisitionWriteOutcome> {
|
|
return store
|
|
.persist_raw_transaction_acquisition(
|
|
transaction,
|
|
observation,
|
|
ksp_store_lib::RawTransactionAcquisitionMode::Normal,
|
|
)
|
|
.await;
|
|
}
|
|
```
|
|
|
|
Le mode `Normal` respecte un tombstone `Purged`. `ForceRehydrate` doit être choisi explicitement lorsqu'un caller veut restaurer un payload purgé et que l'identité retenue est compatible.
|
|
|
|
Un contenu divergent sous la même identité produit `ERROR_CODE_RAW_CONFLICT`; Store ne remplace jamais silencieusement le contenu gagnant.
|
|
|
|
## 8. Lire et ajouter une observation
|
|
|
|
Une observation supplémentaire référence une transaction canonique déjà durable.
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionObservationRead;
|
|
use ksp_store_lib::RawTransactionObservationWrite;
|
|
|
|
async fn use_observation(
|
|
store: &ksp_store_lib::Store,
|
|
key: &ksp_store_lib::RawObservationKey,
|
|
observation: ksp_store_lib::RawTransactionObservation,
|
|
) -> ksp_store_lib::Result<ksp_store_lib::RawObservationWriteOutcome> {
|
|
let existing = store.get_raw_transaction_observation(key).await;
|
|
if let std::result::Result::Err(error) = existing {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
|
|
return store.record_raw_transaction_observation(observation).await;
|
|
}
|
|
```
|
|
|
|
`record_raw_transaction_observation` ne crée pas implicitement le canonique. Une référence absente est signalée par `ERROR_CODE_RAW_REFERENCE_NOT_FOUND`.
|
|
|
|
## 9. Lire la rétention et le tombstone
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionRetentionRead;
|
|
|
|
async fn read_retention(
|
|
store: &ksp_store_lib::Store,
|
|
reference: &ksp_store_lib::RawTransactionReference,
|
|
) -> ksp_store_lib::Result<std::option::Option<ksp_store_lib::RawRetentionState>> {
|
|
let tombstone = store.get_raw_transaction_tombstone(reference).await;
|
|
if let std::result::Result::Err(error) = tombstone {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
|
|
return store.get_raw_transaction_retention_state(reference).await;
|
|
}
|
|
```
|
|
|
|
Le tombstone minimal est utile uniquement lorsque le payload a été purgé ; il ne remplace pas le modèle canonique lorsqu'un payload est encore disponible.
|
|
|
|
## 10. Appliquer une transition de rétention
|
|
|
|
La policy qui décide qu'une transition est autorisée appartient au caller. Store applique uniquement la transition demandée de manière atomique.
|
|
|
|
```rust
|
|
use ksp_store_lib::RawTransactionRetentionWrite;
|
|
|
|
async fn archive_transaction(
|
|
store: &ksp_store_lib::Store,
|
|
reference: ksp_store_lib::RawTransactionReference,
|
|
) -> ksp_store_lib::Result<ksp_store_lib::RawRetentionWriteOutcome> {
|
|
let transition = ksp_store_lib::RawTransactionRetentionTransition::try_new(
|
|
reference,
|
|
ksp_store_lib::RawRetentionState::Full,
|
|
ksp_store_lib::RawRetentionState::Archived,
|
|
);
|
|
let transition = match transition {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
|
|
return store.transition_raw_transaction_retention(transition).await;
|
|
}
|
|
```
|
|
|
|
Le backend PostgreSQL supporte physiquement `Full -> Archived -> Purged`. Une transition impliquant `Compacted` est rejetée par ce backend tant qu'aucune représentation compactée réelle n'est implémentée.
|
|
|
|
## 11. Diagnostics et erreurs
|
|
|
|
Les snapshots et erreurs de façade n'exposent ni URI, host, user, database, SQL, handle backend, valeur de bind ni texte d'erreur PostgreSQL.
|
|
|
|
Les codes Store utiles incluent notamment :
|
|
|
|
```text
|
|
store.wrong_network
|
|
store.raw_reference_not_found
|
|
store.postgres_read_failed
|
|
store.postgres_write_failed
|
|
store.postgres_data_invalid
|
|
store.postgres_page_limit_unsupported
|
|
store.postgres_retention_compaction_unsupported
|
|
```
|
|
|
|
Les conflits et queries invalides utilisent les codes backend-neutral `store_api.raw_conflict` et `store_api.raw_query_invalid`.
|
|
|
|
## 12. Limites de la façade
|
|
|
|
La façade ne fournit pas d'accès public au SQL, au pool, aux clients ou transactions PostgreSQL. Les capabilities `RawAccount*` réexportées par l'API commune ne sont pas encore dispatchées par `Store`.
|
|
|
|
La taille de page est une primitive de navigation. Les décisions de batch, priorité, backlog et scheduling appartiennent aux workers/jobs, pas à Store.
|