v0.3.14-pre.009
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs
|
||||
// version: 8
|
||||
// version: 9
|
||||
|
||||
/// Maximum number of slots admitted by one private continuity HTTP discovery window outside this module.
|
||||
pub(crate) const MAX_RAW_TRANSACTION_INGEST_CONTINUITY_DISCOVERY_WINDOW_SLOTS: u64 = MAX_RAW_TRANSACTION_INGEST_REPAIR_DISCOVERY_WINDOW_SLOTS;
|
||||
@@ -347,6 +347,25 @@ impl RawTransactionIngestGapLedger {
|
||||
return self.gaps.iter().any(|gap| return gap.state.is_open());
|
||||
}
|
||||
|
||||
fn source_failures_reconciled(&self, source_keys: &std::collections::BTreeSet<[u8; 32]>) -> bool {
|
||||
for source_key in source_keys {
|
||||
let mut found = false;
|
||||
for gap in &self.gaps {
|
||||
if gap.source_key != *source_key || gap.reason != RawTransactionIngestGapReason::SourceFailure {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
if gap.state.is_open() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn record_gap(
|
||||
&mut self,
|
||||
source_key: [u8; 32],
|
||||
@@ -904,6 +923,44 @@ impl crate::RawTransactionIngestContinuityContracts {
|
||||
return self.gap_ledger.continuity_frontier(processing_frontier_slot);
|
||||
}
|
||||
|
||||
/// Reconciles known gaps and projects source-neutral present/future coverage evidence for Worker health.
|
||||
///
|
||||
/// The returned tuple is `(continuity_frontier, has_open_gaps, future_target_coverage, failed_source_losses_reconciled)`. Configuration alone may
|
||||
/// satisfy only the future-coverage component; present continuity and terminal source-loss history still require proven run-local reconciliation.
|
||||
pub(crate) fn health_projection(
|
||||
&mut self,
|
||||
active_source_keys: &[[u8; 32]],
|
||||
failed_source_keys: &[[u8; 32]],
|
||||
processing_frontier_slot: std::option::Option<u64>,
|
||||
) -> ksp_core_lib::Result<(std::option::Option<u64>, bool, bool, bool)> {
|
||||
let mut active = std::collections::BTreeSet::new();
|
||||
for source_key in active_source_keys {
|
||||
if !active.insert(*source_key) {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.health_active_set_invalid"));
|
||||
}
|
||||
if !self.capabilities.iter().any(|capability| return capability.source_key == *source_key) {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.health_active_source_unknown"));
|
||||
}
|
||||
}
|
||||
let mut failed = std::collections::BTreeSet::new();
|
||||
for source_key in failed_source_keys {
|
||||
if active.contains(source_key) || !failed.insert(*source_key) {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.health_failed_set_invalid"));
|
||||
}
|
||||
if !self.capabilities.iter().any(|capability| return capability.source_key == *source_key) {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.health_failed_source_unknown"));
|
||||
}
|
||||
}
|
||||
if let std::result::Result::Err(error) = self.gap_ledger.reconcile_with_coverage_epochs(&self.coverage_epochs) {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let continuity_frontier = self.continuity_frontier(processing_frontier_slot);
|
||||
let has_open_gaps = self.gap_ledger.has_open_gaps();
|
||||
let future_target_coverage = self.target_coverage.is_covered_by_active_sources(self.capabilities.as_slice(), &active);
|
||||
let failed_source_losses_reconciled = self.gap_ledger.source_failures_reconciled(&failed);
|
||||
return std::result::Result::Ok((continuity_frontier, has_open_gaps, future_target_coverage, failed_source_losses_reconciled));
|
||||
}
|
||||
|
||||
/// Reconciles known gaps against already-proven coverage and decides whether one lost source may remain absent without stopping sibling sources.
|
||||
///
|
||||
/// Continuation requires all configured `TargetCoverage` requirements to remain covered by distinct currently active sources and requires the known
|
||||
@@ -926,14 +983,12 @@ impl crate::RawTransactionIngestContinuityContracts {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.source_loss_active_source_unknown"));
|
||||
}
|
||||
}
|
||||
if let std::result::Result::Err(error) = self.gap_ledger.reconcile_with_coverage_epochs(&self.coverage_epochs) {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let continuity_frontier = self.continuity_frontier(processing_frontier_slot);
|
||||
if self.gap_ledger.has_open_gaps() || continuity_frontier != processing_frontier_slot {
|
||||
return std::result::Result::Ok(crate::RawTransactionIngestSourceLossDecision::Fault);
|
||||
}
|
||||
if !self.target_coverage.is_covered_by_active_sources(self.capabilities.as_slice(), &active) {
|
||||
let (continuity_frontier, has_open_gaps, future_target_coverage, failed_source_losses_reconciled) =
|
||||
match self.health_projection(active_source_keys, std::slice::from_ref(&lost_source_key), processing_frontier_slot) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if has_open_gaps || continuity_frontier != processing_frontier_slot || !future_target_coverage || !failed_source_losses_reconciled {
|
||||
return std::result::Result::Ok(crate::RawTransactionIngestSourceLossDecision::Fault);
|
||||
}
|
||||
return std::result::Result::Ok(crate::RawTransactionIngestSourceLossDecision::Continue);
|
||||
|
||||
Reference in New Issue
Block a user