v0.3.6-pre.009-fix.001
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/checkpoint.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
/// Opaque caller-owned checkpoint for one controlled Backfill resumption.
|
||||
///
|
||||
@@ -14,7 +14,7 @@ pub struct BackfillCheckpoint {
|
||||
resume_before: std::option::Option<crate::BackfillSignature>,
|
||||
}
|
||||
|
||||
impl BackfillCheckpoint {
|
||||
impl crate::BackfillCheckpoint {
|
||||
/// Returns the logical Job identity that owns this checkpoint.
|
||||
#[must_use]
|
||||
pub const fn job_id(&self) -> &ksp_job_api::JobId {
|
||||
@@ -49,7 +49,7 @@ impl BackfillCheckpoint {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillCheckpoint {
|
||||
impl std::fmt::Debug for crate::BackfillCheckpoint {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("BackfillCheckpoint")
|
||||
@@ -67,7 +67,7 @@ pub(crate) struct CompletionFrontier {
|
||||
contiguous_completed: usize,
|
||||
}
|
||||
|
||||
impl CompletionFrontier {
|
||||
impl crate::CompletionFrontier {
|
||||
/// Creates one empty frontier for the exact bounded candidate count.
|
||||
pub(crate) fn new(candidate_count: usize) -> Self {
|
||||
return Self { durable: vec![false; candidate_count], contiguous_completed: 0 };
|
||||
@@ -109,7 +109,7 @@ pub(crate) fn validate_request_checkpoint(
|
||||
request_job_id: &ksp_job_api::JobId,
|
||||
scope_fingerprint: crate::BackfillScopeFingerprint,
|
||||
scope_kind: crate::BackfillScopeKind,
|
||||
checkpoint: &BackfillCheckpoint,
|
||||
checkpoint: &crate::BackfillCheckpoint,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
if checkpoint.job_id() != request_job_id {
|
||||
return std::result::Result::Err(checkpoint_error("checkpoint.job_id"));
|
||||
@@ -157,9 +157,9 @@ pub(crate) fn execution_resume_offset(request: &crate::BackfillRequest, discover
|
||||
pub(crate) fn checkpoint_from_frontier(
|
||||
request: &crate::BackfillRequest,
|
||||
discovery: &crate::BackfillDiscovery,
|
||||
frontier: &CompletionFrontier,
|
||||
) -> ksp_core_lib::Result<BackfillCheckpoint> {
|
||||
let validation = validate_discovery_identity(request, discovery);
|
||||
frontier: &crate::CompletionFrontier,
|
||||
) -> ksp_core_lib::Result<crate::BackfillCheckpoint> {
|
||||
let validation = crate::validate_discovery_identity(request, discovery);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
@@ -168,12 +168,12 @@ pub(crate) fn checkpoint_from_frontier(
|
||||
if let std::option::Option::Some(checkpoint) = request.checkpoint() {
|
||||
return std::result::Result::Ok(checkpoint.clone());
|
||||
}
|
||||
return std::result::Result::Ok(BackfillCheckpoint::new(request.job_id().clone(), request.scope_fingerprint(), 0, std::option::Option::None));
|
||||
return std::result::Result::Ok(crate::BackfillCheckpoint::new(request.job_id().clone(), request.scope_fingerprint(), 0, std::option::Option::None));
|
||||
}
|
||||
let (completed_prefix, resume_before) = match request.scope().kind() {
|
||||
crate::BackfillScopeKind::LatestAddress => (local_prefix, std::option::Option::None),
|
||||
crate::BackfillScopeKind::BeforeAddress => {
|
||||
let previous = request.checkpoint().map_or(0, BackfillCheckpoint::completed_prefix);
|
||||
let previous = request.checkpoint().map_or(0, crate::BackfillCheckpoint::completed_prefix);
|
||||
let completed_prefix = match previous.checked_add(local_prefix) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Err(checkpoint_error("checkpoint.completed_prefix")),
|
||||
@@ -195,7 +195,7 @@ pub(crate) fn checkpoint_from_frontier(
|
||||
},
|
||||
crate::BackfillScopeKind::AfterAddress | crate::BackfillScopeKind::ExplicitSignatures => (local_prefix, std::option::Option::None),
|
||||
};
|
||||
return std::result::Result::Ok(BackfillCheckpoint::new(request.job_id().clone(), request.scope_fingerprint(), completed_prefix, resume_before));
|
||||
return std::result::Result::Ok(crate::BackfillCheckpoint::new(request.job_id().clone(), request.scope_fingerprint(), completed_prefix, resume_before));
|
||||
}
|
||||
|
||||
/// Validates that one discovery result belongs to the request network and semantic scope.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/conversion.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
use sha2::Digest; // rust-rules: trait-import
|
||||
|
||||
@@ -24,7 +24,7 @@ struct BackfillRawAcquisitionInner {
|
||||
observation: ksp_store_lib::RawTransactionObservation,
|
||||
}
|
||||
|
||||
impl BackfillRawAcquisition {
|
||||
impl crate::BackfillRawAcquisition {
|
||||
/// Returns the canonical RAW transaction produced from the typed Transport response.
|
||||
#[must_use]
|
||||
pub const fn transaction(&self) -> &ksp_store_lib::RawTransaction {
|
||||
@@ -49,12 +49,12 @@ impl BackfillRawAcquisition {
|
||||
#[derive(Debug)]
|
||||
pub enum BackfillHydrationOutcome {
|
||||
/// The RPC returned one complete transaction and conversion produced canonical RAW plus provenance.
|
||||
Available(BackfillRawAcquisition),
|
||||
Available(crate::BackfillRawAcquisition),
|
||||
/// The RPC returned JSON `null`; only the canonical transaction identity exists and no provenance is fabricated.
|
||||
Missing(ksp_store_lib::RawTransactionReference),
|
||||
}
|
||||
|
||||
impl BackfillHydrationOutcome {
|
||||
impl crate::BackfillHydrationOutcome {
|
||||
/// Returns the network-scoped transaction identity represented by this hydration outcome.
|
||||
#[must_use]
|
||||
pub fn reference(&self) -> &ksp_store_lib::RawTransactionReference {
|
||||
@@ -81,7 +81,7 @@ pub async fn hydrate_backfill_candidate(
|
||||
request: &crate::BackfillRequest,
|
||||
candidate: &crate::BackfillCandidate,
|
||||
received_at: ksp_store_lib::RawTimestamp,
|
||||
) -> ksp_core_lib::Result<BackfillHydrationOutcome> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillHydrationOutcome> {
|
||||
let reference = canonical_reference(request, candidate);
|
||||
let reference = match reference {
|
||||
std::result::Result::Ok(reference) => reference,
|
||||
@@ -102,7 +102,7 @@ pub async fn hydrate_backfill_candidate(
|
||||
let transaction = observed.into_value();
|
||||
let transaction = match transaction {
|
||||
std::option::Option::Some(transaction) => transaction,
|
||||
std::option::Option::None => return std::result::Result::Ok(BackfillHydrationOutcome::Missing(reference)),
|
||||
std::option::Option::None => return std::result::Result::Ok(crate::BackfillHydrationOutcome::Missing(reference)),
|
||||
};
|
||||
let fields = CanonicalTransactionFields {
|
||||
slot: transaction.slot(),
|
||||
@@ -114,7 +114,7 @@ pub async fn hydrate_backfill_candidate(
|
||||
};
|
||||
let acquisition = convert_available_fields(request, reference, fields, provider.as_str(), endpoint.as_str(), received_at);
|
||||
return match acquisition {
|
||||
std::result::Result::Ok(acquisition) => std::result::Result::Ok(BackfillHydrationOutcome::Available(acquisition)),
|
||||
std::result::Result::Ok(acquisition) => std::result::Result::Ok(crate::BackfillHydrationOutcome::Available(acquisition)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
@@ -183,7 +183,7 @@ fn convert_available_fields(
|
||||
provider: &str,
|
||||
endpoint: &str,
|
||||
received_at: ksp_store_lib::RawTimestamp,
|
||||
) -> ksp_core_lib::Result<BackfillRawAcquisition> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillRawAcquisition> {
|
||||
let block_time = convert_block_time(fields.block_time);
|
||||
let block_time = match block_time {
|
||||
std::result::Result::Ok(block_time) => block_time,
|
||||
@@ -195,13 +195,17 @@ fn convert_available_fields(
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let hash: [u8; 32] = sha2::Sha256::digest(bytes.as_slice()).into();
|
||||
let format_id = ksp_store_lib::RawFormatId::new(RAW_TRANSACTION_FORMAT_ID);
|
||||
let format_id = ksp_store_lib::RawFormatId::new(crate::RAW_TRANSACTION_FORMAT_ID);
|
||||
let format_id = match format_id {
|
||||
std::result::Result::Ok(format_id) => format_id,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(conversion_error("payload.format_id")),
|
||||
};
|
||||
let payload =
|
||||
ksp_store_lib::RawPayload::try_new(format_id, RAW_TRANSACTION_FORMAT_VERSION, bytes.into_boxed_slice(), ksp_store_lib::RawContentHash::new(hash));
|
||||
let payload = ksp_store_lib::RawPayload::try_new(
|
||||
format_id,
|
||||
crate::RAW_TRANSACTION_FORMAT_VERSION,
|
||||
bytes.into_boxed_slice(),
|
||||
ksp_store_lib::RawContentHash::new(hash),
|
||||
);
|
||||
let payload = match payload {
|
||||
std::result::Result::Ok(payload) => payload,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -214,7 +218,7 @@ fn convert_available_fields(
|
||||
let observation_key = observation_key(request, &reference, provider, endpoint);
|
||||
let transaction = ksp_store_lib::RawTransaction::new(reference.clone(), fields.slot, block_time, payload);
|
||||
let observation = ksp_store_lib::RawTransactionObservation::new(observation_key, reference, provenance);
|
||||
return std::result::Result::Ok(BackfillRawAcquisition { inner: Box::new(BackfillRawAcquisitionInner { transaction, observation }) });
|
||||
return std::result::Result::Ok(crate::BackfillRawAcquisition { inner: Box::new(BackfillRawAcquisitionInner { transaction, observation }) });
|
||||
}
|
||||
|
||||
fn convert_block_time(value: std::option::Option<i64>) -> ksp_core_lib::Result<std::option::Option<ksp_store_lib::RawTimestamp>> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/discovery.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
/// Network-scoped identity of one discovered transaction candidate before canonical signature decoding.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
@@ -8,7 +8,7 @@ pub struct BackfillCandidateIdentity {
|
||||
signature: crate::BackfillSignature,
|
||||
}
|
||||
|
||||
impl BackfillCandidateIdentity {
|
||||
impl crate::BackfillCandidateIdentity {
|
||||
/// Creates one candidate identity from its logical network and encoded transaction signature.
|
||||
#[must_use]
|
||||
pub fn new(network: ksp_store_lib::RawNetworkId, signature: crate::BackfillSignature) -> Self {
|
||||
@@ -31,20 +31,20 @@ impl BackfillCandidateIdentity {
|
||||
/// One deterministic transaction candidate produced by bounded discovery.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct BackfillCandidate {
|
||||
identity: BackfillCandidateIdentity,
|
||||
identity: crate::BackfillCandidateIdentity,
|
||||
discovered_slot: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl BackfillCandidate {
|
||||
impl crate::BackfillCandidate {
|
||||
/// Creates one candidate from a network-scoped identity and optional discovery slot.
|
||||
#[must_use]
|
||||
pub fn new(identity: BackfillCandidateIdentity, discovered_slot: std::option::Option<u64>) -> Self {
|
||||
pub fn new(identity: crate::BackfillCandidateIdentity, discovered_slot: std::option::Option<u64>) -> Self {
|
||||
return Self { identity, discovered_slot };
|
||||
}
|
||||
|
||||
/// Returns the network-scoped candidate identity.
|
||||
#[must_use]
|
||||
pub const fn identity(&self) -> &BackfillCandidateIdentity {
|
||||
pub const fn identity(&self) -> &crate::BackfillCandidateIdentity {
|
||||
return &self.identity;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ pub enum BackfillDiscoveryBoundary {
|
||||
AfterAnchorNotReached,
|
||||
}
|
||||
|
||||
impl BackfillDiscoveryBoundary {
|
||||
impl crate::BackfillDiscoveryBoundary {
|
||||
/// Returns the stable diagnostic code for this boundary.
|
||||
#[must_use]
|
||||
pub const fn code(self) -> &'static str {
|
||||
@@ -94,19 +94,19 @@ impl BackfillDiscoveryBoundary {
|
||||
pub struct BackfillDiscovery {
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
scope_fingerprint: crate::BackfillScopeFingerprint,
|
||||
candidates: std::vec::Vec<BackfillCandidate>,
|
||||
candidates: std::vec::Vec<crate::BackfillCandidate>,
|
||||
pages_fetched: usize,
|
||||
boundary: BackfillDiscoveryBoundary,
|
||||
boundary: crate::BackfillDiscoveryBoundary,
|
||||
}
|
||||
|
||||
impl BackfillDiscovery {
|
||||
impl crate::BackfillDiscovery {
|
||||
/// Creates one internally validated bounded discovery value.
|
||||
pub(crate) fn new(
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
scope_fingerprint: crate::BackfillScopeFingerprint,
|
||||
candidates: std::vec::Vec<BackfillCandidate>,
|
||||
candidates: std::vec::Vec<crate::BackfillCandidate>,
|
||||
pages_fetched: usize,
|
||||
boundary: BackfillDiscoveryBoundary,
|
||||
boundary: crate::BackfillDiscoveryBoundary,
|
||||
) -> Self {
|
||||
return Self { network, scope_fingerprint, candidates, pages_fetched, boundary };
|
||||
}
|
||||
@@ -125,7 +125,7 @@ impl BackfillDiscovery {
|
||||
|
||||
/// Returns discovered candidates in deterministic processing order.
|
||||
#[must_use]
|
||||
pub fn candidates(&self) -> &[BackfillCandidate] {
|
||||
pub fn candidates(&self) -> &[crate::BackfillCandidate] {
|
||||
return self.candidates.as_slice();
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ impl BackfillDiscovery {
|
||||
|
||||
/// Returns the reason discovery stopped.
|
||||
#[must_use]
|
||||
pub const fn boundary(&self) -> BackfillDiscoveryBoundary {
|
||||
pub const fn boundary(&self) -> crate::BackfillDiscoveryBoundary {
|
||||
return self.boundary;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ impl BackfillDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillDiscovery {
|
||||
impl std::fmt::Debug for crate::BackfillDiscovery {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("BackfillDiscovery")
|
||||
@@ -206,7 +206,7 @@ impl SignaturePageSource for ksp_onchain_transport_lib::HttpTransportPool {
|
||||
pub async fn discover_backfill_candidates(
|
||||
transport: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
request: &crate::BackfillRequest,
|
||||
) -> ksp_core_lib::Result<BackfillDiscovery> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillDiscovery> {
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
job_id = request.job_id().as_str(),
|
||||
@@ -239,7 +239,7 @@ pub(crate) async fn discover_backfill_candidates_cancellable(
|
||||
transport: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
request: &crate::BackfillRequest,
|
||||
cancellation: &crate::BackfillCancellationSignal,
|
||||
) -> ksp_core_lib::Result<BackfillDiscovery> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillDiscovery> {
|
||||
return discover_with_source(transport, request, std::option::Option::Some(cancellation)).await;
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ async fn discover_with_source<S>(
|
||||
source: &S,
|
||||
request: &crate::BackfillRequest,
|
||||
cancellation: std::option::Option<&crate::BackfillCancellationSignal>,
|
||||
) -> ksp_core_lib::Result<BackfillDiscovery>
|
||||
) -> ksp_core_lib::Result<crate::BackfillDiscovery>
|
||||
where
|
||||
S: SignaturePageSource,
|
||||
{
|
||||
@@ -261,22 +261,22 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
fn discover_explicit(request: &crate::BackfillRequest) -> ksp_core_lib::Result<BackfillDiscovery> {
|
||||
fn discover_explicit(request: &crate::BackfillRequest) -> ksp_core_lib::Result<crate::BackfillDiscovery> {
|
||||
let signatures = match request.scope().signatures() {
|
||||
std::option::Option::Some(signatures) => signatures,
|
||||
std::option::Option::None => return std::result::Result::Err(discovery_invalid("scope.signatures")),
|
||||
};
|
||||
let mut candidates = std::vec::Vec::with_capacity(signatures.len());
|
||||
for signature in signatures {
|
||||
let identity = BackfillCandidateIdentity::new(request.network().clone(), signature.clone());
|
||||
candidates.push(BackfillCandidate::new(identity, std::option::Option::None));
|
||||
let identity = crate::BackfillCandidateIdentity::new(request.network().clone(), signature.clone());
|
||||
candidates.push(crate::BackfillCandidate::new(identity, std::option::Option::None));
|
||||
}
|
||||
return std::result::Result::Ok(BackfillDiscovery::new(
|
||||
return std::result::Result::Ok(crate::BackfillDiscovery::new(
|
||||
request.network().clone(),
|
||||
request.scope_fingerprint(),
|
||||
candidates,
|
||||
0,
|
||||
BackfillDiscoveryBoundary::ExplicitInput,
|
||||
crate::BackfillDiscoveryBoundary::ExplicitInput,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ async fn discover_older<S>(
|
||||
source: &S,
|
||||
request: &crate::BackfillRequest,
|
||||
cancellation: std::option::Option<&crate::BackfillCancellationSignal>,
|
||||
) -> ksp_core_lib::Result<BackfillDiscovery>
|
||||
) -> ksp_core_lib::Result<crate::BackfillDiscovery>
|
||||
where
|
||||
S: SignaturePageSource,
|
||||
{
|
||||
@@ -304,10 +304,10 @@ where
|
||||
let mut pages_fetched = 0_usize;
|
||||
let boundary = loop {
|
||||
if candidates.len() >= request.max_candidates() {
|
||||
break BackfillDiscoveryBoundary::CandidateLimit;
|
||||
break crate::BackfillDiscoveryBoundary::CandidateLimit;
|
||||
}
|
||||
if pages_fetched >= request.max_pages() {
|
||||
break BackfillDiscoveryBoundary::PageLimit;
|
||||
break crate::BackfillDiscoveryBoundary::PageLimit;
|
||||
}
|
||||
let remaining = request.max_candidates() - candidates.len();
|
||||
let page_limit = std::cmp::min(request.page_size(), remaining);
|
||||
@@ -345,33 +345,33 @@ where
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if seen.insert(signature.clone()) {
|
||||
let identity = BackfillCandidateIdentity::new(request.network().clone(), signature);
|
||||
candidates.push(BackfillCandidate::new(identity, std::option::Option::Some(entry.slot)));
|
||||
let identity = crate::BackfillCandidateIdentity::new(request.network().clone(), signature);
|
||||
candidates.push(crate::BackfillCandidate::new(identity, std::option::Option::Some(entry.slot)));
|
||||
if candidates.len() >= request.max_candidates() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if page_len < page_limit {
|
||||
break BackfillDiscoveryBoundary::RpcBoundary;
|
||||
break crate::BackfillDiscoveryBoundary::RpcBoundary;
|
||||
}
|
||||
let next_before = match next_before {
|
||||
std::option::Option::Some(next_before) => next_before,
|
||||
std::option::Option::None => break BackfillDiscoveryBoundary::RpcBoundary,
|
||||
std::option::Option::None => break crate::BackfillDiscoveryBoundary::RpcBoundary,
|
||||
};
|
||||
if before.as_deref() == std::option::Option::Some(next_before.as_str()) {
|
||||
return std::result::Result::Err(discovery_stalled());
|
||||
}
|
||||
before = std::option::Option::Some(next_before.as_str().to_owned());
|
||||
};
|
||||
return std::result::Result::Ok(BackfillDiscovery::new(request.network().clone(), request.scope_fingerprint(), candidates, pages_fetched, boundary));
|
||||
return std::result::Result::Ok(crate::BackfillDiscovery::new(request.network().clone(), request.scope_fingerprint(), candidates, pages_fetched, boundary));
|
||||
}
|
||||
|
||||
async fn discover_after<S>(
|
||||
source: &S,
|
||||
request: &crate::BackfillRequest,
|
||||
cancellation: std::option::Option<&crate::BackfillCancellationSignal>,
|
||||
) -> ksp_core_lib::Result<BackfillDiscovery>
|
||||
) -> ksp_core_lib::Result<crate::BackfillDiscovery>
|
||||
where
|
||||
S: SignaturePageSource,
|
||||
{
|
||||
@@ -385,12 +385,12 @@ where
|
||||
};
|
||||
let until = anchor.as_str().to_owned();
|
||||
let mut before = std::option::Option::<std::string::String>::None;
|
||||
let mut nearest = std::collections::VecDeque::<BackfillCandidate>::with_capacity(request.max_candidates());
|
||||
let mut nearest = std::collections::VecDeque::<crate::BackfillCandidate>::with_capacity(request.max_candidates());
|
||||
let mut seen = std::collections::HashSet::<crate::BackfillSignature>::new();
|
||||
let mut pages_fetched = 0_usize;
|
||||
let boundary = loop {
|
||||
if pages_fetched >= request.max_pages() {
|
||||
break BackfillDiscoveryBoundary::AfterAnchorNotReached;
|
||||
break crate::BackfillDiscoveryBoundary::AfterAnchorNotReached;
|
||||
}
|
||||
let config = ksp_onchain_transport_lib::SolanaSignaturesForAddressConfig::new(
|
||||
before.clone(),
|
||||
@@ -426,26 +426,26 @@ where
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if seen.insert(signature.clone()) {
|
||||
let identity = BackfillCandidateIdentity::new(request.network().clone(), signature);
|
||||
nearest.push_back(BackfillCandidate::new(identity, std::option::Option::Some(entry.slot)));
|
||||
let identity = crate::BackfillCandidateIdentity::new(request.network().clone(), signature);
|
||||
nearest.push_back(crate::BackfillCandidate::new(identity, std::option::Option::Some(entry.slot)));
|
||||
if nearest.len() > request.max_candidates() {
|
||||
nearest.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
if page_len < request.page_size() {
|
||||
break BackfillDiscoveryBoundary::RpcBoundary;
|
||||
break crate::BackfillDiscoveryBoundary::RpcBoundary;
|
||||
}
|
||||
let next_before = match next_before {
|
||||
std::option::Option::Some(next_before) => next_before,
|
||||
std::option::Option::None => break BackfillDiscoveryBoundary::RpcBoundary,
|
||||
std::option::Option::None => break crate::BackfillDiscoveryBoundary::RpcBoundary,
|
||||
};
|
||||
if before.as_deref() == std::option::Option::Some(next_before.as_str()) {
|
||||
return std::result::Result::Err(discovery_stalled());
|
||||
}
|
||||
before = std::option::Option::Some(next_before.as_str().to_owned());
|
||||
};
|
||||
return std::result::Result::Ok(BackfillDiscovery::new(
|
||||
return std::result::Result::Ok(crate::BackfillDiscovery::new(
|
||||
request.network().clone(),
|
||||
request.scope_fingerprint(),
|
||||
nearest.into_iter().collect(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/execution.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
use futures_util::StreamExt; // rust-rules: trait-import
|
||||
|
||||
@@ -27,7 +27,7 @@ pub struct BackfillExecutionBatch {
|
||||
failure_code: std::option::Option<ksp_core_lib::ErrorCode>,
|
||||
}
|
||||
|
||||
impl BackfillExecutionBatch {
|
||||
impl crate::BackfillExecutionBatch {
|
||||
/// Returns the number of candidates present in the bounded discovery result.
|
||||
#[must_use]
|
||||
pub const fn candidate_count(&self) -> usize {
|
||||
@@ -162,7 +162,7 @@ pub(crate) struct BackfillExecutionProgress {
|
||||
checkpoint: crate::BackfillCheckpoint,
|
||||
}
|
||||
|
||||
impl BackfillExecutionProgress {
|
||||
impl crate::BackfillExecutionProgress {
|
||||
/// Returns the cumulative admission count.
|
||||
pub(crate) const fn admitted_count(&self) -> usize {
|
||||
return self.admitted_count;
|
||||
@@ -243,7 +243,7 @@ pub async fn execute_backfill_discovery(
|
||||
store: &ksp_store_lib::Store,
|
||||
request: &crate::BackfillRequest,
|
||||
discovery: &crate::BackfillDiscovery,
|
||||
) -> ksp_core_lib::Result<BackfillExecutionBatch> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillExecutionBatch> {
|
||||
let validation = crate::validate_discovery_identity(request, discovery);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
@@ -260,7 +260,7 @@ pub(crate) async fn execute_backfill_discovery_cancellable(
|
||||
discovery: &crate::BackfillDiscovery,
|
||||
cancellation: &crate::BackfillCancellationSignal,
|
||||
publisher: &crate::BackfillRuntimePublisher,
|
||||
) -> ksp_core_lib::Result<BackfillExecutionBatch> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillExecutionBatch> {
|
||||
let processor = RuntimeCandidateProcessor { transport, store, request, cancellation: std::option::Option::Some(cancellation) };
|
||||
return execute_with_processor(&processor, request, discovery, std::option::Option::Some(cancellation), std::option::Option::Some(publisher)).await;
|
||||
}
|
||||
@@ -311,7 +311,7 @@ async fn execute_with_processor<P>(
|
||||
discovery: &crate::BackfillDiscovery,
|
||||
cancellation: std::option::Option<&crate::BackfillCancellationSignal>,
|
||||
publisher: std::option::Option<&crate::BackfillRuntimePublisher>,
|
||||
) -> ksp_core_lib::Result<BackfillExecutionBatch>
|
||||
) -> ksp_core_lib::Result<crate::BackfillExecutionBatch>
|
||||
where
|
||||
P: CandidateProcessor,
|
||||
{
|
||||
@@ -484,7 +484,7 @@ where
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(BackfillExecutionBatch {
|
||||
return std::result::Result::Ok(crate::BackfillExecutionBatch {
|
||||
candidate_count: discovery.candidates().len(),
|
||||
admitted_count,
|
||||
finished_count,
|
||||
@@ -524,12 +524,12 @@ fn progress_from_state(
|
||||
cancelled_count: usize,
|
||||
hole_count: usize,
|
||||
maximum_in_flight: usize,
|
||||
) -> ksp_core_lib::Result<BackfillExecutionProgress> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillExecutionProgress> {
|
||||
let checkpoint = match crate::checkpoint_from_frontier(request, discovery, frontier) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(BackfillExecutionProgress {
|
||||
return std::result::Result::Ok(crate::BackfillExecutionProgress {
|
||||
admitted_count,
|
||||
finished_count,
|
||||
inserted_count,
|
||||
@@ -549,7 +549,7 @@ fn progress_from_state(
|
||||
|
||||
fn publish_progress(
|
||||
publisher: std::option::Option<&crate::BackfillRuntimePublisher>,
|
||||
progress: &BackfillExecutionProgress,
|
||||
progress: &crate::BackfillExecutionProgress,
|
||||
cancelling: bool,
|
||||
draining: bool,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/lib.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -139,9 +139,5 @@ pub(crate) use self::execution::BackfillExecutionProgress;
|
||||
pub(crate) use self::execution::execute_backfill_discovery_cancellable;
|
||||
/// Internal concrete cancellation signal shared by discovery and execution.
|
||||
pub(crate) use self::runtime::BackfillCancellationSignal;
|
||||
/// Internal atomic terminal/cancellation arbitration shared by runtime tests and coordinator.
|
||||
pub(crate) use self::runtime::BackfillRuntimeControl;
|
||||
/// Internal execution progress publisher feeding the latest-value snapshot source.
|
||||
pub(crate) use self::runtime::BackfillRuntimePublisher;
|
||||
/// Internal terminal race result used by coordinator canaries.
|
||||
pub(crate) use self::runtime::TerminalClaim;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/persistence.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
/// Canonical entity disposition produced by one Backfill Store persistence attempt.
|
||||
#[non_exhaustive]
|
||||
@@ -38,16 +38,16 @@ pub enum BackfillObservationPersistence {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct BackfillPersistenceOutcome {
|
||||
reference: ksp_store_lib::RawTransactionReference,
|
||||
entity: BackfillEntityPersistence,
|
||||
observation: BackfillObservationPersistence,
|
||||
entity: crate::BackfillEntityPersistence,
|
||||
observation: crate::BackfillObservationPersistence,
|
||||
}
|
||||
|
||||
impl BackfillPersistenceOutcome {
|
||||
impl crate::BackfillPersistenceOutcome {
|
||||
/// Creates one internally classified persistence result.
|
||||
pub(crate) fn new(
|
||||
reference: ksp_store_lib::RawTransactionReference,
|
||||
entity: BackfillEntityPersistence,
|
||||
observation: BackfillObservationPersistence,
|
||||
entity: crate::BackfillEntityPersistence,
|
||||
observation: crate::BackfillObservationPersistence,
|
||||
) -> Self {
|
||||
return Self { reference, entity, observation };
|
||||
}
|
||||
@@ -60,13 +60,13 @@ impl BackfillPersistenceOutcome {
|
||||
|
||||
/// Returns the canonical transaction persistence disposition.
|
||||
#[must_use]
|
||||
pub const fn entity(&self) -> BackfillEntityPersistence {
|
||||
pub const fn entity(&self) -> crate::BackfillEntityPersistence {
|
||||
return self.entity;
|
||||
}
|
||||
|
||||
/// Returns the acquisition-observation persistence disposition.
|
||||
#[must_use]
|
||||
pub const fn observation(&self) -> BackfillObservationPersistence {
|
||||
pub const fn observation(&self) -> crate::BackfillObservationPersistence {
|
||||
return self.observation;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl BackfillPersistenceOutcome {
|
||||
pub async fn persist_backfill_hydration(
|
||||
store: &ksp_store_lib::Store,
|
||||
hydration: crate::BackfillHydrationOutcome,
|
||||
) -> ksp_core_lib::Result<BackfillPersistenceOutcome> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillPersistenceOutcome> {
|
||||
return persist_hydration_with_port(store, hydration).await;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ impl RawTransactionPersistencePort for ksp_store_lib::Store {
|
||||
}
|
||||
}
|
||||
|
||||
async fn persist_hydration_with_port<P>(port: &P, hydration: crate::BackfillHydrationOutcome) -> ksp_core_lib::Result<BackfillPersistenceOutcome>
|
||||
async fn persist_hydration_with_port<P>(port: &P, hydration: crate::BackfillHydrationOutcome) -> ksp_core_lib::Result<crate::BackfillPersistenceOutcome>
|
||||
where
|
||||
P: RawTransactionPersistencePort,
|
||||
{
|
||||
@@ -121,10 +121,10 @@ where
|
||||
return std::result::Result::Err(persistence_error("store.network"));
|
||||
}
|
||||
return match hydration {
|
||||
crate::BackfillHydrationOutcome::Missing(_) => std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
crate::BackfillHydrationOutcome::Missing(_) => std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::Missing,
|
||||
BackfillObservationPersistence::NotApplicable,
|
||||
crate::BackfillEntityPersistence::Missing,
|
||||
crate::BackfillObservationPersistence::NotApplicable,
|
||||
)),
|
||||
crate::BackfillHydrationOutcome::Available(acquisition) => {
|
||||
let (transaction, observation) = acquisition.into_parts();
|
||||
@@ -138,7 +138,7 @@ async fn persist_available_with_port<P>(
|
||||
reference: ksp_store_lib::RawTransactionReference,
|
||||
transaction: ksp_store_lib::RawTransaction,
|
||||
observation: ksp_store_lib::RawTransactionObservation,
|
||||
) -> ksp_core_lib::Result<BackfillPersistenceOutcome>
|
||||
) -> ksp_core_lib::Result<crate::BackfillPersistenceOutcome>
|
||||
where
|
||||
P: RawTransactionPersistencePort,
|
||||
{
|
||||
@@ -153,10 +153,10 @@ where
|
||||
std::result::Result::Ok(outcome) => map_store_outcome(reference, outcome),
|
||||
std::result::Result::Err(error) => {
|
||||
if error.code() == ksp_store_lib::ERROR_CODE_RAW_CONFLICT {
|
||||
return std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
return std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::Conflict,
|
||||
BackfillObservationPersistence::NotRecorded,
|
||||
crate::BackfillEntityPersistence::Conflict,
|
||||
crate::BackfillObservationPersistence::NotRecorded,
|
||||
));
|
||||
}
|
||||
std::result::Result::Err(error)
|
||||
@@ -167,35 +167,35 @@ where
|
||||
fn map_store_outcome(
|
||||
reference: ksp_store_lib::RawTransactionReference,
|
||||
outcome: ksp_store_lib::RawAcquisitionWriteOutcome,
|
||||
) -> ksp_core_lib::Result<BackfillPersistenceOutcome> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillPersistenceOutcome> {
|
||||
let entity = outcome.entity();
|
||||
let observation = outcome.observation();
|
||||
if entity == ksp_store_lib::RawEntityWriteOutcome::Inserted && observation == ksp_store_lib::RawObservationWriteOutcome::Inserted {
|
||||
return std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
return std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::Inserted,
|
||||
BackfillObservationPersistence::Inserted,
|
||||
crate::BackfillEntityPersistence::Inserted,
|
||||
crate::BackfillObservationPersistence::Inserted,
|
||||
));
|
||||
}
|
||||
if entity == ksp_store_lib::RawEntityWriteOutcome::AlreadyPresent && observation == ksp_store_lib::RawObservationWriteOutcome::Inserted {
|
||||
return std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
return std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::AlreadyPresent,
|
||||
BackfillObservationPersistence::Inserted,
|
||||
crate::BackfillEntityPersistence::AlreadyPresent,
|
||||
crate::BackfillObservationPersistence::Inserted,
|
||||
));
|
||||
}
|
||||
if entity == ksp_store_lib::RawEntityWriteOutcome::AlreadyPresent && observation == ksp_store_lib::RawObservationWriteOutcome::AlreadyPresent {
|
||||
return std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
return std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::AlreadyPresent,
|
||||
BackfillObservationPersistence::AlreadyPresent,
|
||||
crate::BackfillEntityPersistence::AlreadyPresent,
|
||||
crate::BackfillObservationPersistence::AlreadyPresent,
|
||||
));
|
||||
}
|
||||
if entity == ksp_store_lib::RawEntityWriteOutcome::SkippedPurged && observation == ksp_store_lib::RawObservationWriteOutcome::NotRecorded {
|
||||
return std::result::Result::Ok(BackfillPersistenceOutcome::new(
|
||||
return std::result::Result::Ok(crate::BackfillPersistenceOutcome::new(
|
||||
reference,
|
||||
BackfillEntityPersistence::SkippedPurged,
|
||||
BackfillObservationPersistence::NotRecorded,
|
||||
crate::BackfillEntityPersistence::SkippedPurged,
|
||||
crate::BackfillObservationPersistence::NotRecorded,
|
||||
));
|
||||
}
|
||||
return std::result::Result::Err(persistence_error("store.outcome"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/request.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
use sha2::Digest; // rust-rules: trait-import
|
||||
|
||||
@@ -25,7 +25,7 @@ pub enum BackfillCommitment {
|
||||
Finalized,
|
||||
}
|
||||
|
||||
impl BackfillCommitment {
|
||||
impl crate::BackfillCommitment {
|
||||
/// Returns the stable Backfill commitment code.
|
||||
#[must_use]
|
||||
pub const fn code(self) -> &'static str {
|
||||
@@ -52,7 +52,7 @@ impl BackfillCommitment {
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct BackfillSignature(std::string::String);
|
||||
|
||||
impl BackfillSignature {
|
||||
impl crate::BackfillSignature {
|
||||
/// Creates one bounded Base58-shaped signature text.
|
||||
pub fn new(value: impl std::convert::Into<std::string::String>) -> ksp_core_lib::Result<Self> {
|
||||
let value = value.into();
|
||||
@@ -74,7 +74,7 @@ impl BackfillSignature {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillSignature {
|
||||
impl std::fmt::Debug for crate::BackfillSignature {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("BackfillSignature(..)");
|
||||
}
|
||||
@@ -93,7 +93,7 @@ pub enum BackfillScopeKind {
|
||||
ExplicitSignatures,
|
||||
}
|
||||
|
||||
impl BackfillScopeKind {
|
||||
impl crate::BackfillScopeKind {
|
||||
/// Returns the stable scope code used by diagnostics and scope fingerprinting.
|
||||
#[must_use]
|
||||
pub const fn code(self) -> &'static str {
|
||||
@@ -109,9 +109,9 @@ impl BackfillScopeKind {
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
enum BackfillScopeValue {
|
||||
LatestAddress { address: ksp_core_lib::Pubkey },
|
||||
BeforeAddress { address: ksp_core_lib::Pubkey, anchor: BackfillSignature },
|
||||
AfterAddress { address: ksp_core_lib::Pubkey, anchor: BackfillSignature },
|
||||
ExplicitSignatures { signatures: std::vec::Vec<BackfillSignature> },
|
||||
BeforeAddress { address: ksp_core_lib::Pubkey, anchor: crate::BackfillSignature },
|
||||
AfterAddress { address: ksp_core_lib::Pubkey, anchor: crate::BackfillSignature },
|
||||
ExplicitSignatures { signatures: std::vec::Vec<crate::BackfillSignature> },
|
||||
}
|
||||
|
||||
/// Validated bounded discovery scope for one historical Backfill Job.
|
||||
@@ -120,7 +120,7 @@ pub struct BackfillScope {
|
||||
value: BackfillScopeValue,
|
||||
}
|
||||
|
||||
impl BackfillScope {
|
||||
impl crate::BackfillScope {
|
||||
/// Creates a scope starting at the newest known history for one address.
|
||||
#[must_use]
|
||||
pub const fn latest_address(address: ksp_core_lib::Pubkey) -> Self {
|
||||
@@ -129,18 +129,18 @@ impl BackfillScope {
|
||||
|
||||
/// Creates a scope reading history older than one exclusive address anchor.
|
||||
#[must_use]
|
||||
pub fn before_address(address: ksp_core_lib::Pubkey, anchor: BackfillSignature) -> Self {
|
||||
pub fn before_address(address: ksp_core_lib::Pubkey, anchor: crate::BackfillSignature) -> Self {
|
||||
return Self { value: BackfillScopeValue::BeforeAddress { address, anchor } };
|
||||
}
|
||||
|
||||
/// Creates a scope reading the bounded newer history closest to one exclusive address anchor.
|
||||
#[must_use]
|
||||
pub fn after_address(address: ksp_core_lib::Pubkey, anchor: BackfillSignature) -> Self {
|
||||
pub fn after_address(address: ksp_core_lib::Pubkey, anchor: crate::BackfillSignature) -> Self {
|
||||
return Self { value: BackfillScopeValue::AfterAddress { address, anchor } };
|
||||
}
|
||||
|
||||
/// Creates an explicit signature scope with stable first-occurrence deduplication.
|
||||
pub fn explicit_signatures(signatures: std::vec::Vec<BackfillSignature>) -> ksp_core_lib::Result<Self> {
|
||||
pub fn explicit_signatures(signatures: std::vec::Vec<crate::BackfillSignature>) -> ksp_core_lib::Result<Self> {
|
||||
if signatures.is_empty() || signatures.len() > crate::MAX_BACKFILL_CANDIDATES {
|
||||
return std::result::Result::Err(request_error("scope.signatures"));
|
||||
}
|
||||
@@ -159,12 +159,12 @@ impl BackfillScope {
|
||||
|
||||
/// Returns the stable category of this scope.
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> BackfillScopeKind {
|
||||
pub const fn kind(&self) -> crate::BackfillScopeKind {
|
||||
return match &self.value {
|
||||
BackfillScopeValue::LatestAddress { .. } => BackfillScopeKind::LatestAddress,
|
||||
BackfillScopeValue::BeforeAddress { .. } => BackfillScopeKind::BeforeAddress,
|
||||
BackfillScopeValue::AfterAddress { .. } => BackfillScopeKind::AfterAddress,
|
||||
BackfillScopeValue::ExplicitSignatures { .. } => BackfillScopeKind::ExplicitSignatures,
|
||||
BackfillScopeValue::LatestAddress { .. } => crate::BackfillScopeKind::LatestAddress,
|
||||
BackfillScopeValue::BeforeAddress { .. } => crate::BackfillScopeKind::BeforeAddress,
|
||||
BackfillScopeValue::AfterAddress { .. } => crate::BackfillScopeKind::AfterAddress,
|
||||
BackfillScopeValue::ExplicitSignatures { .. } => crate::BackfillScopeKind::ExplicitSignatures,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ impl BackfillScope {
|
||||
|
||||
/// Returns the exclusive anchor used by before/after scopes.
|
||||
#[must_use]
|
||||
pub const fn anchor(&self) -> std::option::Option<&BackfillSignature> {
|
||||
pub const fn anchor(&self) -> std::option::Option<&crate::BackfillSignature> {
|
||||
return match &self.value {
|
||||
BackfillScopeValue::BeforeAddress { anchor, .. } | BackfillScopeValue::AfterAddress { anchor, .. } => std::option::Option::Some(anchor),
|
||||
BackfillScopeValue::LatestAddress { .. } | BackfillScopeValue::ExplicitSignatures { .. } => std::option::Option::None,
|
||||
@@ -190,7 +190,7 @@ impl BackfillScope {
|
||||
|
||||
/// Returns the stable deduplicated explicit signature list, when this is an explicit scope.
|
||||
#[must_use]
|
||||
pub fn signatures(&self) -> std::option::Option<&[BackfillSignature]> {
|
||||
pub fn signatures(&self) -> std::option::Option<&[crate::BackfillSignature]> {
|
||||
return match &self.value {
|
||||
BackfillScopeValue::ExplicitSignatures { signatures } => std::option::Option::Some(signatures.as_slice()),
|
||||
BackfillScopeValue::LatestAddress { .. } | BackfillScopeValue::BeforeAddress { .. } | BackfillScopeValue::AfterAddress { .. } => {
|
||||
@@ -200,7 +200,7 @@ impl BackfillScope {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillScope {
|
||||
impl std::fmt::Debug for crate::BackfillScope {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut debug = formatter.debug_struct("BackfillScope");
|
||||
debug.field("kind", &self.kind());
|
||||
@@ -223,7 +223,7 @@ impl std::fmt::Debug for BackfillScope {
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct BackfillScopeFingerprint([u8; 32]);
|
||||
|
||||
impl BackfillScopeFingerprint {
|
||||
impl crate::BackfillScopeFingerprint {
|
||||
/// Returns the exact deterministic fingerprint bytes.
|
||||
#[must_use]
|
||||
pub const fn as_bytes(&self) -> &[u8; 32] {
|
||||
@@ -231,7 +231,7 @@ impl BackfillScopeFingerprint {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillScopeFingerprint {
|
||||
impl std::fmt::Debug for crate::BackfillScopeFingerprint {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("BackfillScopeFingerprint(..)");
|
||||
}
|
||||
@@ -243,26 +243,26 @@ pub struct BackfillRequest {
|
||||
job_id: ksp_job_api::JobId,
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
role: ksp_onchain_transport_lib::HttpRoleName,
|
||||
commitment: BackfillCommitment,
|
||||
scope: BackfillScope,
|
||||
commitment: crate::BackfillCommitment,
|
||||
scope: crate::BackfillScope,
|
||||
page_size: usize,
|
||||
max_pages: usize,
|
||||
max_candidates: usize,
|
||||
hydration_concurrency: usize,
|
||||
min_context_slot: std::option::Option<u64>,
|
||||
scope_fingerprint: BackfillScopeFingerprint,
|
||||
scope_fingerprint: crate::BackfillScopeFingerprint,
|
||||
checkpoint: std::option::Option<crate::BackfillCheckpoint>,
|
||||
}
|
||||
|
||||
impl BackfillRequest {
|
||||
impl crate::BackfillRequest {
|
||||
/// Creates and validates one fully explicit bounded Backfill request.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
job_id: ksp_job_api::JobId,
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
role: ksp_onchain_transport_lib::HttpRoleName,
|
||||
commitment: BackfillCommitment,
|
||||
scope: BackfillScope,
|
||||
commitment: crate::BackfillCommitment,
|
||||
scope: crate::BackfillScope,
|
||||
page_size: usize,
|
||||
max_pages: usize,
|
||||
max_candidates: usize,
|
||||
@@ -284,7 +284,7 @@ impl BackfillRequest {
|
||||
if role.as_str().is_empty() || role.as_str().trim() != role.as_str() {
|
||||
return std::result::Result::Err(request_error("role"));
|
||||
}
|
||||
if scope.kind() == BackfillScopeKind::ExplicitSignatures {
|
||||
if scope.kind() == crate::BackfillScopeKind::ExplicitSignatures {
|
||||
if min_context_slot.is_some() {
|
||||
return std::result::Result::Err(request_error("min_context_slot"));
|
||||
}
|
||||
@@ -333,13 +333,13 @@ impl BackfillRequest {
|
||||
|
||||
/// Returns the narrowed commitment used by discovery and hydration.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> BackfillCommitment {
|
||||
pub const fn commitment(&self) -> crate::BackfillCommitment {
|
||||
return self.commitment;
|
||||
}
|
||||
|
||||
/// Returns the validated discovery scope.
|
||||
#[must_use]
|
||||
pub const fn scope(&self) -> &BackfillScope {
|
||||
pub const fn scope(&self) -> &crate::BackfillScope {
|
||||
return &self.scope;
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ impl BackfillRequest {
|
||||
/// Transport role, provider, endpoint and protocol are deliberately excluded. They describe
|
||||
/// acquisition provenance, not transaction or scope identity.
|
||||
#[must_use]
|
||||
pub const fn scope_fingerprint(&self) -> BackfillScopeFingerprint {
|
||||
pub const fn scope_fingerprint(&self) -> crate::BackfillScopeFingerprint {
|
||||
return self.scope_fingerprint;
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ impl BackfillRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillRequest {
|
||||
impl std::fmt::Debug for crate::BackfillRequest {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("BackfillRequest")
|
||||
@@ -421,13 +421,13 @@ impl std::fmt::Debug for BackfillRequest {
|
||||
|
||||
fn fingerprint_scope(
|
||||
network: &ksp_store_lib::RawNetworkId,
|
||||
commitment: BackfillCommitment,
|
||||
scope: &BackfillScope,
|
||||
commitment: crate::BackfillCommitment,
|
||||
scope: &crate::BackfillScope,
|
||||
page_size: usize,
|
||||
max_pages: usize,
|
||||
max_candidates: usize,
|
||||
min_context_slot: std::option::Option<u64>,
|
||||
) -> BackfillScopeFingerprint {
|
||||
) -> crate::BackfillScopeFingerprint {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(b"ksp.job.backfill.scope.v1\0");
|
||||
hash_bytes(&mut hasher, network.as_str().as_bytes());
|
||||
@@ -455,7 +455,7 @@ fn fingerprint_scope(
|
||||
std::option::Option::None => hasher.update([0_u8]),
|
||||
}
|
||||
let bytes: [u8; 32] = hasher.finalize().into();
|
||||
return BackfillScopeFingerprint(bytes);
|
||||
return crate::BackfillScopeFingerprint(bytes);
|
||||
}
|
||||
|
||||
fn hash_bytes(hasher: &mut sha2::Sha256, value: &[u8]) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/src/runtime.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
/// Stable Job kind code used by the concrete historical RAW transaction Backfill runtime.
|
||||
pub const BACKFILL_JOB_KIND_CODE: &str = "solana.raw_transaction.backfill";
|
||||
@@ -26,7 +26,7 @@ pub enum BackfillJobPhase {
|
||||
Finished,
|
||||
}
|
||||
|
||||
impl BackfillJobPhase {
|
||||
impl crate::BackfillJobPhase {
|
||||
/// Returns the stable safe code for this concrete runtime phase.
|
||||
#[must_use]
|
||||
pub const fn code(self) -> &'static str {
|
||||
@@ -43,7 +43,7 @@ impl BackfillJobPhase {
|
||||
/// Complete safe latest-value snapshot of one concrete historical RAW transaction Backfill Job.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct BackfillJobSnapshot {
|
||||
phase: BackfillJobPhase,
|
||||
phase: crate::BackfillJobPhase,
|
||||
scope_kind: crate::BackfillScopeKind,
|
||||
discovery_boundary: std::option::Option<crate::BackfillDiscoveryBoundary>,
|
||||
candidates_selected: usize,
|
||||
@@ -64,10 +64,10 @@ pub struct BackfillJobSnapshot {
|
||||
failure_code: std::option::Option<ksp_core_lib::ErrorCode>,
|
||||
}
|
||||
|
||||
impl BackfillJobSnapshot {
|
||||
impl crate::BackfillJobSnapshot {
|
||||
fn initial(request: &crate::BackfillRequest) -> Self {
|
||||
return Self {
|
||||
phase: BackfillJobPhase::Created,
|
||||
phase: crate::BackfillJobPhase::Created,
|
||||
scope_kind: request.scope().kind(),
|
||||
discovery_boundary: std::option::Option::None,
|
||||
candidates_selected: 0,
|
||||
@@ -91,7 +91,7 @@ impl BackfillJobSnapshot {
|
||||
|
||||
/// Returns the concrete execution phase represented by this snapshot.
|
||||
#[must_use]
|
||||
pub const fn phase(&self) -> BackfillJobPhase {
|
||||
pub const fn phase(&self) -> crate::BackfillJobPhase {
|
||||
return self.phase;
|
||||
}
|
||||
|
||||
@@ -207,11 +207,11 @@ impl BackfillJobSnapshot {
|
||||
/// Cloneable runtime-neutral-facing latest-value source for concrete Backfill snapshots.
|
||||
#[derive(Clone)]
|
||||
pub struct BackfillSnapshotSource {
|
||||
receiver: tokio::sync::watch::Receiver<ksp_job_api::JobNotification<BackfillJobSnapshot>>,
|
||||
receiver: tokio::sync::watch::Receiver<ksp_job_api::JobNotification<crate::BackfillJobSnapshot>>,
|
||||
}
|
||||
|
||||
impl ksp_job_api::JobSnapshotSource for BackfillSnapshotSource {
|
||||
type Snapshot = BackfillJobSnapshot;
|
||||
impl ksp_job_api::JobSnapshotSource for crate::BackfillSnapshotSource {
|
||||
type Snapshot = crate::BackfillJobSnapshot;
|
||||
|
||||
fn current(&self) -> ksp_job_api::JobNotification<Self::Snapshot> {
|
||||
return self.receiver.borrow().clone();
|
||||
@@ -234,7 +234,7 @@ impl ksp_job_api::JobSnapshotSource for BackfillSnapshotSource {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillSnapshotSource {
|
||||
impl std::fmt::Debug for crate::BackfillSnapshotSource {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let current = self.receiver.borrow();
|
||||
return formatter.debug_struct("BackfillSnapshotSource").field("sequence", ¤t.sequence()).field("state", ¤t.state()).finish();
|
||||
@@ -245,10 +245,10 @@ impl std::fmt::Debug for BackfillSnapshotSource {
|
||||
#[derive(Clone)]
|
||||
pub struct BackfillJobHandle {
|
||||
control: BackfillRuntimeControl,
|
||||
snapshots: BackfillSnapshotSource,
|
||||
snapshots: crate::BackfillSnapshotSource,
|
||||
}
|
||||
|
||||
impl BackfillJobHandle {
|
||||
impl crate::BackfillJobHandle {
|
||||
/// Requests cooperative cancellation and returns `true` only when accepted before terminal publication.
|
||||
#[must_use]
|
||||
pub fn cancel(&self) -> bool {
|
||||
@@ -257,7 +257,7 @@ impl BackfillJobHandle {
|
||||
|
||||
/// Returns an independent latest-value snapshot source for one listener.
|
||||
#[must_use]
|
||||
pub fn snapshots(&self) -> BackfillSnapshotSource {
|
||||
pub fn snapshots(&self) -> crate::BackfillSnapshotSource {
|
||||
return self.snapshots.clone();
|
||||
}
|
||||
|
||||
@@ -268,12 +268,12 @@ impl BackfillJobHandle {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BackfillJobHandle {
|
||||
impl std::fmt::Debug for crate::BackfillJobHandle {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("BackfillJobHandle")
|
||||
.field("cancellation_requested", &self.is_cancellation_requested())
|
||||
.field("snapshot", &<BackfillSnapshotSource as ksp_job_api::JobSnapshotSource>::current(&self.snapshots))
|
||||
.field("snapshot", &<crate::BackfillSnapshotSource as ksp_job_api::JobSnapshotSource>::current(&self.snapshots))
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
@@ -282,19 +282,19 @@ impl std::fmt::Debug for BackfillJobHandle {
|
||||
pub struct BackfillJobRuntime {
|
||||
request: crate::BackfillRequest,
|
||||
control: BackfillRuntimeControl,
|
||||
cancellation: BackfillCancellationSignal,
|
||||
publisher: BackfillRuntimePublisher,
|
||||
handle: BackfillJobHandle,
|
||||
cancellation: crate::BackfillCancellationSignal,
|
||||
publisher: crate::BackfillRuntimePublisher,
|
||||
handle: crate::BackfillJobHandle,
|
||||
}
|
||||
|
||||
impl BackfillJobRuntime {
|
||||
impl crate::BackfillJobRuntime {
|
||||
/// Creates one runtime in `Created` state and its stable latest-value channel.
|
||||
pub fn new(request: crate::BackfillRequest) -> ksp_core_lib::Result<Self> {
|
||||
let kind = match ksp_job_api::JobKindCode::new(BACKFILL_JOB_KIND_CODE) {
|
||||
let kind = match ksp_job_api::JobKindCode::new(crate::BACKFILL_JOB_KIND_CODE) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let initial_snapshot = BackfillJobSnapshot::initial(&request);
|
||||
let initial_snapshot = crate::BackfillJobSnapshot::initial(&request);
|
||||
let initial = ksp_job_api::JobNotification::new(
|
||||
request.job_id().clone(),
|
||||
kind.clone(),
|
||||
@@ -305,16 +305,16 @@ impl BackfillJobRuntime {
|
||||
let (snapshot_sender, snapshot_receiver) = tokio::sync::watch::channel(initial);
|
||||
let (cancel_sender, cancel_receiver) = tokio::sync::watch::channel(false);
|
||||
let control = BackfillRuntimeControl::new(cancel_sender);
|
||||
let cancellation = BackfillCancellationSignal::new(control.token(), cancel_receiver);
|
||||
let snapshots = BackfillSnapshotSource { receiver: snapshot_receiver };
|
||||
let handle = BackfillJobHandle { control: control.clone(), snapshots: snapshots.clone() };
|
||||
let publisher = BackfillRuntimePublisher { sender: snapshot_sender, id: request.job_id().clone(), kind };
|
||||
let cancellation = crate::BackfillCancellationSignal::new(control.token(), cancel_receiver);
|
||||
let snapshots = crate::BackfillSnapshotSource { receiver: snapshot_receiver };
|
||||
let handle = crate::BackfillJobHandle { control: control.clone(), snapshots: snapshots.clone() };
|
||||
let publisher = crate::BackfillRuntimePublisher { sender: snapshot_sender, id: request.job_id().clone(), kind };
|
||||
return std::result::Result::Ok(Self { request, control, cancellation, publisher, handle });
|
||||
}
|
||||
|
||||
/// Returns a cloneable control and latest-value observation handle before the runtime is moved into execution.
|
||||
#[must_use]
|
||||
pub fn handle(&self) -> BackfillJobHandle {
|
||||
pub fn handle(&self) -> crate::BackfillJobHandle {
|
||||
return self.handle.clone();
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ impl BackfillJobRuntime {
|
||||
self,
|
||||
transport: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
store: &ksp_store_lib::Store,
|
||||
) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
if self.cancellation.is_requested() {
|
||||
let claimed = self.control.claim_normal_terminal();
|
||||
if claimed != TerminalClaim::Cancelled {
|
||||
@@ -331,7 +331,7 @@ impl BackfillJobRuntime {
|
||||
}
|
||||
return self.publisher.publish_cancelled_from_created();
|
||||
}
|
||||
let started = self.publisher.publish_running(BackfillJobPhase::Discovering);
|
||||
let started = self.publisher.publish_running(crate::BackfillJobPhase::Discovering);
|
||||
if let std::result::Result::Err(error) = started {
|
||||
self.control.claim_failed();
|
||||
return std::result::Result::Err(error);
|
||||
@@ -393,7 +393,7 @@ impl BackfillJobRuntime {
|
||||
};
|
||||
}
|
||||
|
||||
fn finish_cancelled(&self) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
fn finish_cancelled(&self) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
let terminal = self.control.claim_normal_terminal();
|
||||
if terminal != TerminalClaim::Cancelled {
|
||||
return std::result::Result::Err(runtime_error("terminal.cancelled"));
|
||||
@@ -408,7 +408,7 @@ impl BackfillJobRuntime {
|
||||
|
||||
/// Internal atomic terminal/cancellation arbitration shared by runtime and external handle.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BackfillRuntimeControl {
|
||||
struct BackfillRuntimeControl {
|
||||
state: std::sync::Arc<std::sync::atomic::AtomicU8>,
|
||||
token: ksp_job_api::JobCancellationToken,
|
||||
cancel_sender: tokio::sync::watch::Sender<bool>,
|
||||
@@ -416,7 +416,7 @@ pub(crate) struct BackfillRuntimeControl {
|
||||
|
||||
impl BackfillRuntimeControl {
|
||||
/// Creates one active control state paired with the cancellation wake channel.
|
||||
pub(crate) fn new(cancel_sender: tokio::sync::watch::Sender<bool>) -> Self {
|
||||
fn new(cancel_sender: tokio::sync::watch::Sender<bool>) -> Self {
|
||||
return Self {
|
||||
state: std::sync::Arc::new(std::sync::atomic::AtomicU8::new(CONTROL_ACTIVE)),
|
||||
token: ksp_job_api::JobCancellationToken::new(),
|
||||
@@ -425,12 +425,12 @@ impl BackfillRuntimeControl {
|
||||
}
|
||||
|
||||
/// Returns the runtime-neutral cancellation token mirrored by this control.
|
||||
pub(crate) fn token(&self) -> ksp_job_api::JobCancellationToken {
|
||||
fn token(&self) -> ksp_job_api::JobCancellationToken {
|
||||
return self.token.clone();
|
||||
}
|
||||
|
||||
/// Atomically accepts the first pre-terminal cancellation request.
|
||||
pub(crate) fn request_cancellation(&self) -> bool {
|
||||
fn request_cancellation(&self) -> bool {
|
||||
let accepted = self
|
||||
.state
|
||||
.compare_exchange(CONTROL_ACTIVE, CONTROL_CANCELLATION_REQUESTED, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire)
|
||||
@@ -442,12 +442,12 @@ impl BackfillRuntimeControl {
|
||||
}
|
||||
|
||||
/// Returns whether cooperative cancellation was accepted.
|
||||
pub(crate) fn is_cancellation_requested(&self) -> bool {
|
||||
fn is_cancellation_requested(&self) -> bool {
|
||||
return self.token.is_cancellation_requested();
|
||||
}
|
||||
|
||||
/// Atomically resolves the completion-versus-cancellation terminal race.
|
||||
pub(crate) fn claim_normal_terminal(&self) -> TerminalClaim {
|
||||
fn claim_normal_terminal(&self) -> TerminalClaim {
|
||||
let completed =
|
||||
self.state.compare_exchange(CONTROL_ACTIVE, CONTROL_COMPLETED, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire);
|
||||
if completed.is_ok() {
|
||||
@@ -466,7 +466,7 @@ impl BackfillRuntimeControl {
|
||||
}
|
||||
|
||||
/// Marks a non-terminal control as failed, overriding a pending cancellation request.
|
||||
pub(crate) fn claim_failed(&self) {
|
||||
fn claim_failed(&self) {
|
||||
loop {
|
||||
let state = self.state.load(std::sync::atomic::Ordering::Acquire);
|
||||
if matches!(state, CONTROL_COMPLETED | CONTROL_CANCELLED | CONTROL_FAILED) {
|
||||
@@ -487,7 +487,7 @@ pub(crate) struct BackfillCancellationSignal {
|
||||
receiver: tokio::sync::watch::Receiver<bool>,
|
||||
}
|
||||
|
||||
impl BackfillCancellationSignal {
|
||||
impl crate::BackfillCancellationSignal {
|
||||
/// Creates one signal from the runtime-neutral token and Tokio wake receiver.
|
||||
pub(crate) fn new(token: ksp_job_api::JobCancellationToken, receiver: tokio::sync::watch::Receiver<bool>) -> Self {
|
||||
return Self { token, receiver };
|
||||
@@ -529,7 +529,7 @@ async fn wait_for_cancellation(token: &ksp_job_api::JobCancellationToken, receiv
|
||||
|
||||
/// Internal result of atomically claiming a normal terminal state.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum TerminalClaim {
|
||||
enum TerminalClaim {
|
||||
Completed,
|
||||
Cancelled,
|
||||
Failed,
|
||||
@@ -538,23 +538,23 @@ pub(crate) enum TerminalClaim {
|
||||
/// Internal latest-value publisher owning the concrete Backfill notification stream.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BackfillRuntimePublisher {
|
||||
sender: tokio::sync::watch::Sender<ksp_job_api::JobNotification<BackfillJobSnapshot>>,
|
||||
sender: tokio::sync::watch::Sender<ksp_job_api::JobNotification<crate::BackfillJobSnapshot>>,
|
||||
id: ksp_job_api::JobId,
|
||||
kind: ksp_job_api::JobKindCode,
|
||||
}
|
||||
|
||||
impl BackfillRuntimePublisher {
|
||||
impl crate::BackfillRuntimePublisher {
|
||||
/// Publishes one non-terminal running phase.
|
||||
pub(crate) fn publish_running(&self, phase: BackfillJobPhase) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_running(&self, phase: crate::BackfillJobPhase) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(ksp_job_api::JobState::Running, |snapshot| {
|
||||
snapshot.phase = phase;
|
||||
});
|
||||
}
|
||||
|
||||
/// Publishes the complete bounded discovery result as the current execution snapshot.
|
||||
pub(crate) fn publish_discovery(&self, discovery: &crate::BackfillDiscovery) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_discovery(&self, discovery: &crate::BackfillDiscovery) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(ksp_job_api::JobState::Running, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Executing;
|
||||
snapshot.phase = crate::BackfillJobPhase::Executing;
|
||||
snapshot.discovery_boundary = std::option::Option::Some(discovery.boundary());
|
||||
snapshot.candidates_selected = discovery.candidates().len();
|
||||
});
|
||||
@@ -566,25 +566,25 @@ impl BackfillRuntimePublisher {
|
||||
progress: &crate::BackfillExecutionProgress,
|
||||
cancelling: bool,
|
||||
draining: bool,
|
||||
) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
let state = if cancelling { ksp_job_api::JobState::Cancelling } else { ksp_job_api::JobState::Running };
|
||||
return self.publish_with(state, |snapshot| {
|
||||
snapshot.phase = if draining { BackfillJobPhase::Draining } else { BackfillJobPhase::Executing };
|
||||
snapshot.phase = if draining { crate::BackfillJobPhase::Draining } else { crate::BackfillJobPhase::Executing };
|
||||
apply_progress(snapshot, progress);
|
||||
});
|
||||
}
|
||||
|
||||
/// Publishes cancellation observation before terminal cancellation.
|
||||
pub(crate) fn publish_cancelling(&self) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_cancelling(&self) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(ksp_job_api::JobState::Cancelling, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Draining;
|
||||
snapshot.phase = crate::BackfillJobPhase::Draining;
|
||||
});
|
||||
}
|
||||
|
||||
/// Publishes the drained batch state while cancellation is terminalizing.
|
||||
pub(crate) fn publish_batch_cancelling(&self, batch: &crate::BackfillExecutionBatch) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_batch_cancelling(&self, batch: &crate::BackfillExecutionBatch) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(ksp_job_api::JobState::Cancelling, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Draining;
|
||||
snapshot.phase = crate::BackfillJobPhase::Draining;
|
||||
apply_batch(snapshot, batch);
|
||||
});
|
||||
}
|
||||
@@ -595,16 +595,16 @@ impl BackfillRuntimePublisher {
|
||||
batch: &crate::BackfillExecutionBatch,
|
||||
state: ksp_job_api::JobState,
|
||||
failure_code: std::option::Option<ksp_core_lib::ErrorCode>,
|
||||
) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(state, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Finished;
|
||||
snapshot.phase = crate::BackfillJobPhase::Finished;
|
||||
snapshot.failure_code = failure_code;
|
||||
apply_batch(snapshot, batch);
|
||||
});
|
||||
}
|
||||
|
||||
/// Publishes a terminal failure before a batch exists.
|
||||
pub(crate) fn publish_failed(&self, code: ksp_core_lib::ErrorCode) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_failed(&self, code: ksp_core_lib::ErrorCode) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_terminal(ksp_job_api::JobState::Failed, std::option::Option::Some(code));
|
||||
}
|
||||
|
||||
@@ -613,23 +613,23 @@ impl BackfillRuntimePublisher {
|
||||
&self,
|
||||
state: ksp_job_api::JobState,
|
||||
failure_code: std::option::Option<ksp_core_lib::ErrorCode>,
|
||||
) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(state, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Finished;
|
||||
snapshot.phase = crate::BackfillJobPhase::Finished;
|
||||
snapshot.failure_code = failure_code;
|
||||
});
|
||||
}
|
||||
|
||||
/// Publishes direct Created-to-Cancelled termination before execution starts.
|
||||
pub(crate) fn publish_cancelled_from_created(&self) -> ksp_core_lib::Result<BackfillJobSnapshot> {
|
||||
pub(crate) fn publish_cancelled_from_created(&self) -> ksp_core_lib::Result<crate::BackfillJobSnapshot> {
|
||||
return self.publish_with(ksp_job_api::JobState::Cancelled, |snapshot| {
|
||||
snapshot.phase = BackfillJobPhase::Finished;
|
||||
snapshot.phase = crate::BackfillJobPhase::Finished;
|
||||
});
|
||||
}
|
||||
|
||||
fn publish_with<F>(&self, state: ksp_job_api::JobState, update: F) -> ksp_core_lib::Result<BackfillJobSnapshot>
|
||||
fn publish_with<F>(&self, state: ksp_job_api::JobState, update: F) -> ksp_core_lib::Result<crate::BackfillJobSnapshot>
|
||||
where
|
||||
F: FnOnce(&mut BackfillJobSnapshot),
|
||||
F: FnOnce(&mut crate::BackfillJobSnapshot),
|
||||
{
|
||||
let current = self.sender.borrow().clone();
|
||||
if current.state().is_terminal() {
|
||||
@@ -668,7 +668,7 @@ fn valid_snapshot_transition(source: ksp_job_api::JobState, target: ksp_job_api:
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_progress(snapshot: &mut BackfillJobSnapshot, progress: &crate::BackfillExecutionProgress) {
|
||||
fn apply_progress(snapshot: &mut crate::BackfillJobSnapshot, progress: &crate::BackfillExecutionProgress) {
|
||||
snapshot.candidates_admitted = progress.admitted_count();
|
||||
snapshot.candidates_finished = progress.finished_count();
|
||||
snapshot.entities_inserted = progress.inserted_count();
|
||||
@@ -685,7 +685,7 @@ fn apply_progress(snapshot: &mut BackfillJobSnapshot, progress: &crate::Backfill
|
||||
snapshot.checkpoint = std::option::Option::Some(progress.checkpoint().clone());
|
||||
}
|
||||
|
||||
fn apply_batch(snapshot: &mut BackfillJobSnapshot, batch: &crate::BackfillExecutionBatch) {
|
||||
fn apply_batch(snapshot: &mut crate::BackfillJobSnapshot, batch: &crate::BackfillExecutionBatch) {
|
||||
snapshot.candidates_admitted = batch.admitted_count();
|
||||
snapshot.candidates_finished = batch.finished_count();
|
||||
snapshot.entities_inserted = batch.inserted_count();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/tests/public_api.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Public API canaries through the concrete Backfill cancellation and latest-value runtime tranche.
|
||||
|
||||
@@ -109,13 +109,13 @@ fn pre_008_checkpoint_and_bounded_execution_contracts_are_available_from_crate_r
|
||||
|
||||
#[test]
|
||||
fn pre_009_concrete_runtime_snapshot_and_control_contracts_are_available_from_crate_root() {
|
||||
fn assert_source<T>()
|
||||
fn assert_source<T>(_: std::marker::PhantomData<T>)
|
||||
where
|
||||
T: ksp_job_api::JobSnapshotSource<Snapshot = ksp_job_backfill_lib::BackfillJobSnapshot>,
|
||||
{
|
||||
return;
|
||||
}
|
||||
assert_source::<ksp_job_backfill_lib::BackfillSnapshotSource>();
|
||||
assert_source(std::marker::PhantomData::<ksp_job_backfill_lib::BackfillSnapshotSource>);
|
||||
let _runtime_new = ksp_job_backfill_lib::BackfillJobRuntime::new;
|
||||
let _handle: std::option::Option<ksp_job_backfill_lib::BackfillJobHandle> = std::option::Option::None;
|
||||
let _phase = ksp_job_backfill_lib::BackfillJobPhase::Discovering;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/unit_tests/discovery.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct PageCall {
|
||||
@@ -392,13 +392,14 @@ async fn pre_009_discovery_rpc_wait_is_cancelled_cooperatively() {
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let source = PendingSource::new();
|
||||
let token = ksp_job_api::JobCancellationToken::new();
|
||||
let (cancel_sender, cancel_receiver) = tokio::sync::watch::channel(false);
|
||||
let control = crate::BackfillRuntimeControl::new(cancel_sender);
|
||||
let cancellation = crate::BackfillCancellationSignal::new(control.token(), cancel_receiver);
|
||||
let cancellation = crate::BackfillCancellationSignal::new(token.clone(), cancel_receiver);
|
||||
let discovery = super::discover_with_source(&source, &request, std::option::Option::Some(&cancellation));
|
||||
let cancel = async {
|
||||
tokio::task::yield_now().await;
|
||||
assert!(control.request_cancellation());
|
||||
assert!(token.cancel());
|
||||
assert!(cancel_sender.send(true).is_ok());
|
||||
};
|
||||
let (result, ()) = tokio::join!(discovery, cancel);
|
||||
let error = match result {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/unit_tests/execution.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum FakeDisposition {
|
||||
@@ -291,9 +291,9 @@ async fn pre_009_cancellation_stops_admission_and_drains_already_admitted_candid
|
||||
FakePlan { pending_polls: 0, disposition: FakeDisposition::Durable },
|
||||
FakePlan { pending_polls: 0, disposition: FakeDisposition::Durable },
|
||||
]);
|
||||
let token = ksp_job_api::JobCancellationToken::new();
|
||||
let (cancel_sender, cancel_receiver) = tokio::sync::watch::channel(false);
|
||||
let control = crate::runtime::BackfillRuntimeControl::new(cancel_sender);
|
||||
let signal = crate::runtime::BackfillCancellationSignal::new(control.token(), cancel_receiver);
|
||||
let signal = crate::BackfillCancellationSignal::new(token.clone(), cancel_receiver);
|
||||
let execution = super::execute_with_processor(&processor, &request, &discovery, std::option::Option::Some(&signal), std::option::Option::None);
|
||||
let cancellation = async {
|
||||
loop {
|
||||
@@ -302,7 +302,8 @@ async fn pre_009_cancellation_stops_admission_and_drains_already_admitted_candid
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
assert!(control.request_cancellation());
|
||||
assert!(token.cancel());
|
||||
assert!(cancel_sender.send(true).is_ok());
|
||||
};
|
||||
let (result, ()) = tokio::join!(execution, cancellation);
|
||||
let batch = match result {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-backfill-lib/unit_tests/runtime.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
use ksp_job_api::JobSnapshotSource; // rust-rules: trait-import
|
||||
|
||||
@@ -78,7 +78,7 @@ async fn pre_009_terminal_snapshot_is_retained_and_late_cancellation_is_rejected
|
||||
let listener = handle.snapshots();
|
||||
let started = runtime.publisher.publish_running(crate::BackfillJobPhase::Discovering);
|
||||
assert!(started.is_ok());
|
||||
assert_eq!(runtime.control.claim_normal_terminal(), crate::TerminalClaim::Completed);
|
||||
assert_eq!(runtime.control.claim_normal_terminal(), super::TerminalClaim::Completed);
|
||||
let terminal = runtime.publisher.publish_terminal(ksp_job_api::JobState::Completed(ksp_job_api::JobCompletion::Complete), std::option::Option::None);
|
||||
assert!(terminal.is_ok());
|
||||
assert!(!handle.cancel());
|
||||
@@ -94,13 +94,13 @@ async fn pre_009_terminal_snapshot_is_retained_and_late_cancellation_is_rejected
|
||||
#[test]
|
||||
fn pre_009_terminal_race_is_first_decision_wins_for_cancellation_vs_completion() {
|
||||
let (cancel_sender, _) = tokio::sync::watch::channel(false);
|
||||
let cancellation_first = crate::BackfillRuntimeControl::new(cancel_sender);
|
||||
let cancellation_first = super::BackfillRuntimeControl::new(cancel_sender);
|
||||
assert!(cancellation_first.request_cancellation());
|
||||
assert_eq!(cancellation_first.claim_normal_terminal(), crate::TerminalClaim::Cancelled);
|
||||
assert_eq!(cancellation_first.claim_normal_terminal(), super::TerminalClaim::Cancelled);
|
||||
assert!(!cancellation_first.request_cancellation());
|
||||
let (cancel_sender, _) = tokio::sync::watch::channel(false);
|
||||
let completion_first = crate::BackfillRuntimeControl::new(cancel_sender);
|
||||
assert_eq!(completion_first.claim_normal_terminal(), crate::TerminalClaim::Completed);
|
||||
let completion_first = super::BackfillRuntimeControl::new(cancel_sender);
|
||||
assert_eq!(completion_first.claim_normal_terminal(), super::TerminalClaim::Completed);
|
||||
assert!(!completion_first.request_cancellation());
|
||||
return;
|
||||
}
|
||||
@@ -108,10 +108,10 @@ fn pre_009_terminal_race_is_first_decision_wins_for_cancellation_vs_completion()
|
||||
#[test]
|
||||
fn pre_009_fatal_failure_overrides_pending_cancellation_before_terminal_publication() {
|
||||
let (cancel_sender, _) = tokio::sync::watch::channel(false);
|
||||
let control = crate::BackfillRuntimeControl::new(cancel_sender);
|
||||
let control = super::BackfillRuntimeControl::new(cancel_sender);
|
||||
assert!(control.request_cancellation());
|
||||
control.claim_failed();
|
||||
assert_eq!(control.claim_normal_terminal(), crate::TerminalClaim::Failed);
|
||||
assert_eq!(control.claim_normal_terminal(), super::TerminalClaim::Failed);
|
||||
assert!(!control.request_cancellation());
|
||||
return;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ fn pre_009_fatal_failure_overrides_pending_cancellation_before_terminal_publicat
|
||||
#[tokio::test]
|
||||
async fn pre_009_long_running_pre_store_future_is_cancelled_cooperatively() {
|
||||
let (cancel_sender, cancel_receiver) = tokio::sync::watch::channel(false);
|
||||
let control = crate::BackfillRuntimeControl::new(cancel_sender);
|
||||
let control = super::BackfillRuntimeControl::new(cancel_sender);
|
||||
let signal = crate::BackfillCancellationSignal::new(control.token(), cancel_receiver);
|
||||
let operation = std::future::pending::<ksp_core_lib::Result<usize>>();
|
||||
let wait = signal.run_cancellable(operation);
|
||||
|
||||
Reference in New Issue
Block a user