v0.3.6-pre.012

This commit is contained in:
2026-09-01 17:42:24 +02:00
parent 9506df9487
commit 42374115c7
18 changed files with 869 additions and 88 deletions

View File

@@ -0,0 +1,206 @@
<!-- file: crates/ksp-job-backfill-lib/USAGE.md -->
<!-- version: 2 -->
# Utilisation de ksp-job-backfill-lib
Cette page décrit l'utilisation durable de la façade publique de `ksp-job-backfill-lib`. Elle suppose qu'un caller a déjà construit un `HttpTransportPool` et un `Store` compatibles avec le même réseau logique.
## Construire une signature et un scope
```rust
let anchor = match ksp_job_backfill_lib::BackfillSignature::new(
"1111111111111111111111111111111111111111111111111111111111111111",
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let address = ksp_core_lib::Pubkey::new_from_array([7_u8; 32]);
let scope = ksp_job_backfill_lib::BackfillScope::before_address(address, anchor);
```
Les autres formes sont :
```rust
let latest = ksp_job_backfill_lib::BackfillScope::latest_address(address);
let before = ksp_job_backfill_lib::BackfillScope::before_address(address, anchor.clone());
let after = ksp_job_backfill_lib::BackfillScope::after_address(address, anchor.clone());
let explicit = ksp_job_backfill_lib::BackfillScope::explicit_signatures(vec![anchor]);
```
`ExplicitSignatures` déduplique la liste en conservant la première occurrence. Les scopes address utilisent `getSignaturesForAddress` ; le scope explicite n'effectue aucune découverte address.
## Construire une requête bornée
```rust
fn request(scope: ksp_job_backfill_lib::BackfillScope) -> ksp_core_lib::Result<ksp_job_backfill_lib::BackfillRequest> {
let job_id = match ksp_job_api::JobId::new("raw-backfill-0001") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let network = match ksp_store_lib::RawNetworkId::new("mainnet") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let role = ksp_onchain_transport_lib::HttpRoleName::new("historical");
return ksp_job_backfill_lib::BackfillRequest::new(
job_id,
network,
role,
ksp_job_backfill_lib::BackfillCommitment::Finalized,
scope,
500,
20,
5_000,
16,
std::option::Option::None,
);
}
```
Bornes publiques :
```text
page_size 1 ..= 1_000
max_pages 1 ..= 10_000
max_candidates 1 ..= 10_000
hydration_concurrency 1 ..= 64
```
Le `min_context_slot` n'est pas admis pour `ExplicitSignatures`.
## Comprendre le fingerprint
```rust
let fingerprint = request.scope_fingerprint();
let bytes: &[u8; 32] = fingerprint.as_bytes();
```
Le fingerprint représente le scope sémantique et le réseau. Il ne change pas uniquement parce que le rôle Transport, le provider ou l'endpoint d'acquisition change.
Ne pas utiliser le fingerprint comme identité de transaction : l'identité durable reste `(network, signature)`.
## Exécuter le runtime complet
```rust
async fn run_backfill(
request: ksp_job_backfill_lib::BackfillRequest,
transport: &ksp_onchain_transport_lib::HttpTransportPool,
store: &ksp_store_lib::Store,
) -> ksp_core_lib::Result<ksp_job_backfill_lib::BackfillJobSnapshot> {
let runtime = match ksp_job_backfill_lib::BackfillJobRuntime::new(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return runtime.run(transport, store).await;
}
```
Le Store et la requête doivent cibler le même `RawNetworkId`. Un mismatch est rejeté avant l'écriture.
## Observer la progression
Obtenir le handle avant de déplacer le runtime dans `run` :
```rust
let runtime = ksp_job_backfill_lib::BackfillJobRuntime::new(request)?;
let handle = runtime.handle();
let snapshots = handle.snapshots();
let current = ksp_job_api::JobSnapshotSource::current(&snapshots);
let observed = current.sequence();
let newer = ksp_job_api::JobSnapshotSource::wait_for_change(&snapshots, observed).await;
assert!(newer.sequence().is_after(observed));
```
Chaque listener peut cloner sa propre `BackfillSnapshotSource`. La source est latest-value : plusieurs mises à jour intermédiaires peuvent être coalescées, mais la valeur retournée est toujours un snapshot complet.
Les compteurs publics du snapshot couvrent notamment :
```text
candidates_selected / admitted / finished
entities_inserted / existing / purged
observations_inserted / existing
missing / conflicts / holes
maximum_in_flight
contiguous_completed
checkpoint
failure_code
```
## Demander une annulation
```rust
let accepted = handle.cancel();
if accepted {
assert!(handle.is_cancellation_requested());
}
```
L'annulation est coopérative. Elle peut stopper de nouvelles admissions et certaines attentes avant persistance. Une écriture Store déjà soumise est drainée ; le caller ne doit donc pas supposer qu'une demande d'annulation rend immédiatement toutes les opérations in-flight inexistantes.
Une demande faite après publication terminale est rejetée (`false`).
## Reprendre avec un checkpoint
Le snapshot ou le batch d'exécution peut fournir un `BackfillCheckpoint` sûr. Pour une reprise contrôlée :
```rust
let resumed = match request.with_checkpoint(checkpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
```
Le checkpoint doit appartenir au même `JobId` et au même fingerprint de scope.
Sémantique de reprise :
```text
LatestAddress repart du latest courant ; frontier contiguë conservée dans le nouveau run
BeforeAddress reprend avec le dernier cursor before prouvé
AfterAddress rejoue le scope et saute seulement le préfixe contigu prouvé
ExplicitSignatures rejoue la liste et saute seulement le préfixe contigu prouvé
```
`BackfillCheckpoint` n'est pas persisté automatiquement. Si le caller exige une reprise après crash/process restart, il doit stocker ce checkpoint dans une surface durable appropriée puis le réinjecter explicitement.
## Utiliser les primitives séparément
La façade expose aussi les étapes pour des compositions/tests spécialisés :
```text
discover_backfill_candidates
hydrate_backfill_candidate
persist_backfill_hydration
execute_backfill_discovery
```
`hydrate_backfill_candidate` ne persiste rien. `persist_backfill_hydration` n'effectue aucun appel Transport. `execute_backfill_discovery` combine hydratation/persistance sur un résultat de découverte déjà validé.
Pour un flux applicatif normal qui veut lifecycle + snapshots + annulation, préférer `BackfillJobRuntime::run`.
## Interpréter les outcomes de persistance
Les outcomes distinguent explicitement :
```text
entity: Inserted | AlreadyPresent | SkippedPurged | Conflict
observation: Inserted | AlreadyPresent | NotRecorded | NotApplicable
```
`Missing` ne provoque aucune écriture Store. Un conflit de contenu reste observable comme conflit et ne doit pas être traité comme une relance idempotente réussie.
## Frontières à respecter
Le caller ne doit pas :
- pré-lire le Store pour décider s'il faut hydrater une transaction ;
- appeler directement `ksp-store-postgres-lib` depuis le job ;
- ajouter une politique de retry/pacing qui concurrence Transport ;
- utiliser provider/endpoint comme identité transactionnelle ;
- inventer une provenance pour `getTransaction = null` ;
- interpréter un checkpoint caller-owned comme une garantie de persistence crash-safe automatique ;
- utiliser le job RAW comme decoder Program ou processor CORE.