207 lines
7.5 KiB
Markdown
207 lines
7.5 KiB
Markdown
<!-- 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.
|