v0.3.11-pre.012
This commit is contained in:
158
crates/ksp-worker-raw-transaction-ingest-lib/README.md
Normal file
158
crates/ksp-worker-raw-transaction-ingest-lib/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
<!-- file: crates/ksp-worker-raw-transaction-ingest-lib/README.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# ksp-worker-raw-transaction-ingest-lib
|
||||
|
||||
`ksp-worker-raw-transaction-ingest-lib` est le premier Worker concret de KSP pour l'alimentation continue de la couche RAW Transaction.
|
||||
|
||||
La crate fournit la fondation runtime **source-neutral** du Worker : identité et settings bornés, lifecycle Start/Stop, admission privée bornée, canonicalisation via `ksp-raw-transaction-lib`, persistance backend-neutral via `ksp-store-lib`, supervision des tâches, shutdown borné et snapshots latest-value projetables sur `ksp-worker-api`.
|
||||
|
||||
La fondation ne contient volontairement encore **aucune source réseau productive**. Elle ne dépend pas de `ksp-onchain-transport-lib` et n'expose pas d'API publique permettant au caller d'injecter directement des transactions dans la queue interne. Les adapters live/catch-up sont des responsabilités ultérieures du même Worker, pas de son API de fondation.
|
||||
|
||||
## Identité et réseau
|
||||
|
||||
Une exécution est liée à :
|
||||
|
||||
```text
|
||||
RawNetworkId
|
||||
WorkerId
|
||||
Worker kind = raw_transaction_ingest
|
||||
```
|
||||
|
||||
Le réseau logique doit être identique à celui du `Store` remis au démarrage. La transaction canonique conserve l'identité durable définie par la couche RAW commune ; le Worker n'ajoute ni provider, ni endpoint, ni protocole à cette identité.
|
||||
|
||||
## Runtime
|
||||
|
||||
Le point d'entrée public est :
|
||||
|
||||
```text
|
||||
RawTransactionIngestWorker::start(settings, Arc<Store>)
|
||||
-> RawTransactionIngestHandle
|
||||
```
|
||||
|
||||
Le démarrage est synchrone mais nécessite qu'un runtime Tokio courant appartienne déjà au caller. Le Worker ne crée pas de runtime global et n'expose aucun `JoinHandle` public.
|
||||
|
||||
`RawTransactionIngestHandle` permet de :
|
||||
|
||||
- demander un stop coopératif et idempotent ;
|
||||
- obtenir une source de snapshots concrets latest-value ;
|
||||
- utiliser cette même source via `WorkerSnapshotSource` ;
|
||||
- attendre le terminal après drain/abort+join des tâches possédées.
|
||||
|
||||
La destruction du dernier handle de contrôle ferme aussi la voie de contrôle privée ; le runtime termine alors selon les mêmes règles de shutdown.
|
||||
|
||||
## Admission et Common RAW
|
||||
|
||||
La queue centrale est un `tokio::sync::mpsc` privé borné par `admission_queue_capacity`. Les sources internes doivent subir la backpressure du channel ; aucune queue non bornée ni silent drop n'est autorisé.
|
||||
|
||||
Chaque ingress admis est :
|
||||
|
||||
1. vérifié contre le réseau attendu ;
|
||||
2. canonicalisé exclusivement par `ksp-raw-transaction-lib` ;
|
||||
3. associé à une observation key déterministe sous le domaine `ksp.raw_transaction_ingest.observation.v1` ;
|
||||
4. assemblé en acquisition RAW commune ;
|
||||
5. remis à la persistence Store.
|
||||
|
||||
La crate ne possède pas un second format RAW et ne duplique pas le canonicaliseur commun.
|
||||
|
||||
## Persistence Store
|
||||
|
||||
La persistance passe uniquement par `ksp-store-lib` avec `default-features = false` dans la crate Worker. Aucun backend physique n'est imposé ou importé directement.
|
||||
|
||||
L'écriture utilise le mode normal d'acquisition atomique `RawTransaction + RawTransactionObservation`. Les outcomes distingués sont notamment :
|
||||
|
||||
```text
|
||||
entity inserted
|
||||
entity already present
|
||||
entity skipped purged
|
||||
observation inserted
|
||||
observation already present
|
||||
observation not recorded for purged entity
|
||||
content conflict
|
||||
store failure
|
||||
```
|
||||
|
||||
`persistence_concurrency` borne le nombre d'écritures Store simultanées. Un conflit de contenu est terminal et n'est jamais converti en succès idempotent.
|
||||
|
||||
## Shutdown et faults
|
||||
|
||||
Le shutdown possède une deadline bornée par `shutdown_drain_timeout`.
|
||||
|
||||
Avant publication terminale, le supervisor :
|
||||
|
||||
- arrête les nouvelles admissions ;
|
||||
- signale le stop aux sources privées ;
|
||||
- draine le travail déjà admis tant que la deadline le permet ;
|
||||
- récolte les persistences et sources possédées ;
|
||||
- en cas de timeout, abort les tâches restantes puis les rejoint avant le terminal.
|
||||
|
||||
Les codes d'erreur publics du Worker sont :
|
||||
|
||||
```text
|
||||
worker_raw_transaction_ingest.settings_invalid
|
||||
worker_raw_transaction_ingest.runtime_invalid
|
||||
worker_raw_transaction_ingest.store_failed
|
||||
worker_raw_transaction_ingest.content_conflict
|
||||
worker_raw_transaction_ingest.counter_exhausted
|
||||
worker_raw_transaction_ingest.source_failed
|
||||
worker_raw_transaction_ingest.drain_timeout
|
||||
```
|
||||
|
||||
Les diagnostics ne recopient pas de payload RAW, URL, credential, signature hostile ou texte backend/provider arbitraire.
|
||||
|
||||
## Snapshots
|
||||
|
||||
`RawTransactionIngestSnapshotSource` est latest-value. Les listeners peuvent rater des états intermédiaires mais obtiennent toujours une valeur complète et monotone par `WorkerSnapshotSequence`.
|
||||
|
||||
Le snapshot concret expose notamment :
|
||||
|
||||
```text
|
||||
lifecycle / health / activity
|
||||
admission_queue_capacity / admission_queue_depth
|
||||
persistence_concurrency / in_flight_persistence
|
||||
admitted_total / canonicalized_total / persisted_total
|
||||
entity_inserted_total / entity_already_present_total / entity_skipped_purged_total
|
||||
observation_inserted_total / observation_already_present_total
|
||||
content_conflict_total / store_failure_total / source_failure_total
|
||||
backpressure_wait_total
|
||||
```
|
||||
|
||||
Les compteurs ne wrapent jamais silencieusement.
|
||||
|
||||
## Dépendances
|
||||
|
||||
Les dépendances normales sont exactement :
|
||||
|
||||
```text
|
||||
ksp-core-lib
|
||||
ksp-logging-lib
|
||||
ksp-raw-transaction-lib
|
||||
ksp-store-lib (default-features = false)
|
||||
ksp-worker-api
|
||||
sha2
|
||||
tokio (macros, rt, sync, time)
|
||||
```
|
||||
|
||||
La crate ne dépend pas de Config, Job, `ksp-store-api` directement, backend Store concret, Transport, Tauri ou SDK provider.
|
||||
|
||||
## Hors périmètre de la fondation source-neutral
|
||||
|
||||
Cette surface ne possède pas encore :
|
||||
|
||||
- adapter HTTP/WS/Yellowstone productif ;
|
||||
- sélection Config de sources/endpoints/credentials ;
|
||||
- discovery/hydration/replay de continuité live ;
|
||||
- hot reconfiguration de listeners/sources ;
|
||||
- application Desk ou process autonome ;
|
||||
- campagne historique/backfill ;
|
||||
- décodage STRUCTURAL/DECODED/DOMAIN.
|
||||
|
||||
Ces extensions doivent conserver la séparation avec `ksp-job-backfill-lib` et réutiliser les mêmes contrats RAW/Store.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [`USAGE.md`](USAGE.md) — utilisation de la façade publique actuelle ;
|
||||
- [`../ksp-worker-api/README.md`](../ksp-worker-api/README.md) — contrats Worker génériques ;
|
||||
- [`../ksp-raw-transaction-lib/README.md`](../ksp-raw-transaction-lib/README.md) — canonicalisation RAW commune ;
|
||||
- [`../../docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md`](../../docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md) — séparation Worker/Job ;
|
||||
- [`../../docs/architecture/011-RAW_TRANSACTION_ACQUISITION.md`](../../docs/architecture/011-RAW_TRANSACTION_ACQUISITION.md) — architecture d'acquisition RawTransaction.
|
||||
206
crates/ksp-worker-raw-transaction-ingest-lib/USAGE.md
Normal file
206
crates/ksp-worker-raw-transaction-ingest-lib/USAGE.md
Normal file
@@ -0,0 +1,206 @@
|
||||
<!-- file: crates/ksp-worker-raw-transaction-ingest-lib/USAGE.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Utilisation de ksp-worker-raw-transaction-ingest-lib
|
||||
|
||||
Cette page décrit l'utilisation de la façade publique source-neutral de `ksp-worker-raw-transaction-ingest-lib`. Le caller possède la composition du runtime Tokio et du `Store` ; la crate Worker ne construit ni Config, ni backend physique, ni Transport.
|
||||
|
||||
## Construire les settings
|
||||
|
||||
Les identités sont validées par leurs couches propriétaires :
|
||||
|
||||
```rust
|
||||
fn worker_settings() -> ksp_core_lib::Result<ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings> {
|
||||
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 worker_id = match ksp_worker_api::WorkerId::new("raw-ingest-mainnet-0001") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::with_defaults(
|
||||
network,
|
||||
worker_id,
|
||||
));
|
||||
}
|
||||
```
|
||||
|
||||
Le Worker kind est fixe :
|
||||
|
||||
```rust
|
||||
assert_eq!(
|
||||
ksp_worker_raw_transaction_ingest_lib::RAW_TRANSACTION_INGEST_WORKER_KIND_CODE,
|
||||
"raw_transaction_ingest",
|
||||
);
|
||||
```
|
||||
|
||||
Pour des limites explicites :
|
||||
|
||||
```rust
|
||||
let settings = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::new(
|
||||
network,
|
||||
worker_id,
|
||||
512,
|
||||
16,
|
||||
std::time::Duration::from_secs(10),
|
||||
);
|
||||
```
|
||||
|
||||
Bornes publiques :
|
||||
|
||||
```text
|
||||
admission_queue_capacity 1 ..= 65_536 défaut 256
|
||||
persistence_concurrency 1 ..= 64 défaut 8
|
||||
shutdown_drain_timeout 100 ms ..= 30 s défaut 5 s
|
||||
```
|
||||
|
||||
Une valeur hors borne retourne `worker_raw_transaction_ingest.settings_invalid` avec uniquement le nom stable du champ invalide.
|
||||
|
||||
## Démarrer sur le runtime du caller
|
||||
|
||||
`RawTransactionIngestWorker::start` doit être appelé depuis un contexte possédant déjà un runtime Tokio courant. Le `Store` est passé dans un `Arc` et doit cibler exactement le même `RawNetworkId` que les settings.
|
||||
|
||||
```rust
|
||||
async fn start_worker(
|
||||
settings: ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings,
|
||||
store: std::sync::Arc<ksp_store_lib::Store>,
|
||||
) -> ksp_core_lib::Result<ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestHandle> {
|
||||
return ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestWorker::start(settings, store);
|
||||
}
|
||||
```
|
||||
|
||||
Le démarrage retourne avant la fin du Worker. Aucun `JoinHandle` Tokio n'est exposé au caller.
|
||||
|
||||
Un appel hors runtime Tokio courant retourne une erreur `worker_raw_transaction_ingest.runtime_invalid`. Un mismatch réseau Store/settings est également rejeté avant le spawn.
|
||||
|
||||
## Observer le snapshot concret
|
||||
|
||||
```rust
|
||||
let source = handle.snapshot_source();
|
||||
let current = source.current();
|
||||
|
||||
let sequence = current.worker_snapshot().sequence();
|
||||
let state = current.worker_snapshot().state();
|
||||
let queue_depth = current.admission_queue_depth();
|
||||
let in_flight = current.in_flight_persistence();
|
||||
```
|
||||
|
||||
Pour attendre une valeur plus récente :
|
||||
|
||||
```rust
|
||||
let observed = source.current().worker_snapshot().sequence();
|
||||
let newer = source.wait_for_change(observed).await;
|
||||
assert!(newer.worker_snapshot().sequence().is_after(observed));
|
||||
```
|
||||
|
||||
Le flux est latest-value : les transitions intermédiaires peuvent être coalescées. Ne pas l'utiliser comme journal d'événements exhaustif.
|
||||
|
||||
## Utiliser la projection Worker API
|
||||
|
||||
La même source implémente `ksp_worker_api::WorkerSnapshotSource` :
|
||||
|
||||
```rust
|
||||
let source = handle.worker_snapshot_source();
|
||||
let current = ksp_worker_api::WorkerSnapshotSource::current(&source);
|
||||
let observed = current.sequence();
|
||||
let newer = ksp_worker_api::WorkerSnapshotSource::wait_for_change(&source, observed).await;
|
||||
assert!(newer.sequence().is_after(observed));
|
||||
```
|
||||
|
||||
Cette projection commune ne contient que les dimensions génériques Worker. Les compteurs d'admission/persistence restent disponibles sur `RawTransactionIngestSnapshot`.
|
||||
|
||||
## Lire les compteurs concrets
|
||||
|
||||
Les compteurs principaux sont monotones :
|
||||
|
||||
```rust
|
||||
let snapshot = handle.snapshot_source().current();
|
||||
|
||||
let admitted = snapshot.admitted_total();
|
||||
let canonicalized = snapshot.canonicalized_total();
|
||||
let persisted = snapshot.persisted_total();
|
||||
let inserted = snapshot.entity_inserted_total();
|
||||
let existing = snapshot.entity_already_present_total();
|
||||
let purged = snapshot.entity_skipped_purged_total();
|
||||
let observations = snapshot.observation_inserted_total();
|
||||
let conflicts = snapshot.content_conflict_total();
|
||||
let store_failures = snapshot.store_failure_total();
|
||||
let source_failures = snapshot.source_failure_total();
|
||||
let backpressure = snapshot.backpressure_wait_total();
|
||||
```
|
||||
|
||||
`admission_queue_depth()` et `in_flight_persistence()` sont des gauges latest-value, pas des totaux cumulés.
|
||||
|
||||
L'épuisement d'un compteur ou de la séquence est terminal et utilise `worker_raw_transaction_ingest.counter_exhausted` au lieu de wrapper silencieusement.
|
||||
|
||||
## Demander un stop et attendre le terminal
|
||||
|
||||
```rust
|
||||
let accepted = handle.request_stop();
|
||||
let terminal = handle.wait_terminal().await;
|
||||
|
||||
match terminal {
|
||||
std::result::Result::Ok(state) => {
|
||||
assert!(state.is_terminal());
|
||||
}
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
|
||||
if accepted {
|
||||
// La première demande de stop a été remise au runtime vivant.
|
||||
}
|
||||
```
|
||||
|
||||
`request_stop()` est idempotent. Il exprime une intention coopérative ; le terminal n'est publié qu'après traitement du drain et des tâches possédées.
|
||||
|
||||
Le shutdown est borné par `shutdown_drain_timeout`. Si les tâches ne peuvent pas être drainées à temps, le Worker les abort puis les rejoint avant de publier `worker_raw_transaction_ingest.drain_timeout`.
|
||||
|
||||
## Interpréter les terminaux faultés
|
||||
|
||||
Les codes Worker publics sont :
|
||||
|
||||
```text
|
||||
settings_invalid settings techniques hors contrat
|
||||
runtime_invalid invariant runtime/lifecycle impossible
|
||||
store_failed erreur Store non-conflict classifiée
|
||||
content_conflict transaction canonique incompatible avec le contenu durable
|
||||
counter_exhausted compteur/séquence monotone arrivé à sa borne
|
||||
source_failed tâche source privée terminée en erreur
|
||||
drain_timeout drain du shutdown hors deadline
|
||||
```
|
||||
|
||||
Un consumer doit traiter le `ErrorCode` comme contrat stable et ne pas dépendre d'un texte backend/provider arbitraire.
|
||||
|
||||
## Frontière d'admission
|
||||
|
||||
La queue d'admission et le type ingress sont volontairement privés. La façade publique actuelle ne propose ni `send`, ni `enqueue`, ni registration d'un adapter réseau.
|
||||
|
||||
Les adapters d'acquisition qui alimentent cette queue appartiennent à l'implémentation du Worker et doivent conserver :
|
||||
|
||||
```text
|
||||
backpressure mpsc bornée
|
||||
stop prioritaire sur send bloqué
|
||||
canonicalisation via ksp-raw-transaction-lib
|
||||
persistance via ksp-store-lib
|
||||
aucun backend physique direct
|
||||
aucun provider dans l'identité canonique
|
||||
```
|
||||
|
||||
Le caller ne doit pas contourner cette frontière en écrivant directement dans les structures privées du Worker.
|
||||
|
||||
## Composition supérieure
|
||||
|
||||
Le pattern de composition attendu reste :
|
||||
|
||||
```text
|
||||
Config / application / service owner
|
||||
-> construit le Store
|
||||
-> choisit ultérieurement les sources/adapters supportés
|
||||
-> construit RawTransactionIngestSettings
|
||||
-> démarre RawTransactionIngestWorker
|
||||
-> observe RawTransactionIngestSnapshotSource
|
||||
-> demande stop lorsque nécessaire
|
||||
```
|
||||
|
||||
Le Worker ne reçoit pas de requête historique métier. Une campagne `signature/program_id/plage/limite` appartient à `ksp-job-backfill-lib`, pas à ce runtime continu.
|
||||
Reference in New Issue
Block a user