v0.3.14-pre.011
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
/// 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;
|
||||
@@ -105,6 +105,17 @@ impl RawTransactionIngestGapReason {
|
||||
Self::WebSocketReconnect,
|
||||
Self::YellowstoneRetention,
|
||||
];
|
||||
|
||||
const fn public(self) -> crate::RawTransactionIngestGapReason {
|
||||
return match self {
|
||||
Self::HttpProducedBlockUnavailable => crate::RawTransactionIngestGapReason::HttpProducedBlockUnavailable,
|
||||
Self::KnownReferenceMissing => crate::RawTransactionIngestGapReason::KnownReferenceMissing,
|
||||
Self::SourceFailure => crate::RawTransactionIngestGapReason::SourceFailure,
|
||||
Self::TransportOverflow => crate::RawTransactionIngestGapReason::TransportOverflow,
|
||||
Self::WebSocketReconnect => crate::RawTransactionIngestGapReason::WebSocketReconnect,
|
||||
Self::YellowstoneRetention => crate::RawTransactionIngestGapReason::YellowstoneRetention,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -121,6 +132,15 @@ impl RawTransactionIngestGapState {
|
||||
const fn is_open(self) -> bool {
|
||||
return !matches!(self, Self::Repaired);
|
||||
}
|
||||
|
||||
const fn public(self) -> crate::RawTransactionIngestGapState {
|
||||
return match self {
|
||||
Self::Pending => crate::RawTransactionIngestGapState::Pending,
|
||||
Self::Repairing => crate::RawTransactionIngestGapState::Repairing,
|
||||
Self::Repaired => crate::RawTransactionIngestGapState::Repaired,
|
||||
Self::Unresolved => crate::RawTransactionIngestGapState::Unresolved,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Private supervisor decision for one terminal live-source loss after conservative continuity reconciliation.
|
||||
@@ -317,6 +337,20 @@ struct RawTransactionIngestGap {
|
||||
reason: RawTransactionIngestGapReason,
|
||||
source_key: [u8; 32],
|
||||
state: RawTransactionIngestGapState,
|
||||
last_method: std::option::Option<crate::RawTransactionIngestRepairMethod>,
|
||||
}
|
||||
|
||||
impl RawTransactionIngestGap {
|
||||
fn snapshot(&self) -> crate::RawTransactionIngestGapSnapshot {
|
||||
return crate::RawTransactionIngestGapSnapshot::new(
|
||||
crate::RawTransactionIngestGapId::new(self.gap_id.0),
|
||||
self.range.start_slot(),
|
||||
self.range.end_slot(),
|
||||
self.state.public(),
|
||||
self.reason.public(),
|
||||
self.last_method,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct RawTransactionIngestGapLedger {
|
||||
@@ -410,23 +444,134 @@ impl RawTransactionIngestGapLedger {
|
||||
reason,
|
||||
source_key,
|
||||
state: RawTransactionIngestGapState::Pending,
|
||||
last_method: std::option::Option::None,
|
||||
});
|
||||
return self.validate_invariants();
|
||||
}
|
||||
|
||||
fn reconcile_with_coverage_epochs(&mut self, coverage_epochs: &RawTransactionIngestCoverageEpochLedger) -> ksp_core_lib::Result<()> {
|
||||
for gap in &mut self.gaps {
|
||||
if !gap.state.is_open() {
|
||||
if !gap.state.is_open() || gap.state == RawTransactionIngestGapState::Unresolved {
|
||||
continue;
|
||||
}
|
||||
let requirement = RawTransactionIngestCoverageRequirement { commitment: gap.commitment, scope: gap.coverage_requirement.clone() };
|
||||
if coverage_epochs.redundant_relation_for_gap(gap.source_key, &requirement, gap.range).is_some() {
|
||||
gap.state = RawTransactionIngestGapState::Repaired;
|
||||
gap.last_method = std::option::Option::Some(crate::RawTransactionIngestRepairMethod::RedundantCoverage);
|
||||
}
|
||||
}
|
||||
return self.validate_invariants();
|
||||
}
|
||||
|
||||
fn observability_projection(&self) -> ksp_core_lib::Result<crate::RawTransactionIngestContinuitySnapshotProjection> {
|
||||
let mut open_gap_count = 0_usize;
|
||||
let mut repairing_gap_count = 0_usize;
|
||||
let mut repaired_gap_total = 0_u64;
|
||||
let mut unresolved_gap_total = 0_u64;
|
||||
let mut replay_repair_total = 0_u64;
|
||||
let mut redundant_coverage_repair_total = 0_u64;
|
||||
let mut http_scan_repair_total = 0_u64;
|
||||
let mut repair_block_fetch_total = 0_u64;
|
||||
let mut repair_transaction_hydration_total = 0_u64;
|
||||
let mut oldest_open_gap_start_slot = std::option::Option::None;
|
||||
let mut gaps = std::vec::Vec::with_capacity(MAX_RAW_TRANSACTION_INGEST_OPEN_REPAIR_GAPS);
|
||||
for gap in &self.gaps {
|
||||
if gap.state.is_open() {
|
||||
open_gap_count = match open_gap_count.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Err(crate::counter_exhausted_error("continuity.open_gap_count")),
|
||||
};
|
||||
if gap.state == RawTransactionIngestGapState::Repairing {
|
||||
repairing_gap_count = match repairing_gap_count.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.repairing_gap_count"));
|
||||
},
|
||||
};
|
||||
}
|
||||
if gap.state == RawTransactionIngestGapState::Unresolved {
|
||||
unresolved_gap_total = match unresolved_gap_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.unresolved_gap_total"));
|
||||
},
|
||||
};
|
||||
}
|
||||
oldest_open_gap_start_slot = match oldest_open_gap_start_slot {
|
||||
std::option::Option::Some(value) => std::option::Option::Some(value.min(gap.range.start_slot())),
|
||||
std::option::Option::None => std::option::Option::Some(gap.range.start_slot()),
|
||||
};
|
||||
gaps.push(gap.snapshot());
|
||||
continue;
|
||||
}
|
||||
repaired_gap_total = match repaired_gap_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Err(crate::counter_exhausted_error("continuity.repaired_gap_total")),
|
||||
};
|
||||
match gap.last_method {
|
||||
std::option::Option::Some(crate::RawTransactionIngestRepairMethod::Replay) => {
|
||||
replay_repair_total = match replay_repair_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.replay_repair_total"));
|
||||
},
|
||||
};
|
||||
},
|
||||
std::option::Option::Some(crate::RawTransactionIngestRepairMethod::RedundantCoverage) => {
|
||||
redundant_coverage_repair_total = match redundant_coverage_repair_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.redundant_coverage_repair_total"));
|
||||
},
|
||||
};
|
||||
},
|
||||
std::option::Option::Some(crate::RawTransactionIngestRepairMethod::HttpScan) => {
|
||||
http_scan_repair_total = match http_scan_repair_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.http_scan_repair_total"));
|
||||
},
|
||||
};
|
||||
},
|
||||
std::option::Option::Some(crate::RawTransactionIngestRepairMethod::BlockFetch) => {
|
||||
repair_block_fetch_total = match repair_block_fetch_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.repair_block_fetch_total"));
|
||||
},
|
||||
};
|
||||
},
|
||||
std::option::Option::Some(crate::RawTransactionIngestRepairMethod::TransactionHydration) => {
|
||||
repair_transaction_hydration_total = match repair_transaction_hydration_total.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(crate::counter_exhausted_error("continuity.repair_transaction_hydration_total"));
|
||||
},
|
||||
};
|
||||
},
|
||||
std::option::Option::None => {},
|
||||
}
|
||||
}
|
||||
if gaps.len() < MAX_RAW_TRANSACTION_INGEST_OPEN_REPAIR_GAPS {
|
||||
for gap in self.gaps.iter().rev() {
|
||||
if gap.state != RawTransactionIngestGapState::Repaired {
|
||||
continue;
|
||||
}
|
||||
gaps.push(gap.snapshot());
|
||||
if gaps.len() == MAX_RAW_TRANSACTION_INGEST_OPEN_REPAIR_GAPS {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
gaps.sort_unstable_by_key(|gap| return gap.gap_id().value());
|
||||
return std::result::Result::Ok(
|
||||
crate::RawTransactionIngestContinuitySnapshotProjection::empty()
|
||||
.with_gap_state(gaps, open_gap_count, repairing_gap_count, oldest_open_gap_start_slot)
|
||||
.with_recovery_totals(repaired_gap_total, unresolved_gap_total, replay_repair_total, redundant_coverage_repair_total, http_scan_repair_total)
|
||||
.with_material_totals(repair_block_fetch_total, repair_transaction_hydration_total),
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_invariants(&self) -> ksp_core_lib::Result<()> {
|
||||
if self.next_gap_id == 0 {
|
||||
return std::result::Result::Err(crate::runtime_error("continuity.gap_id_exhausted"));
|
||||
@@ -923,6 +1068,11 @@ impl crate::RawTransactionIngestContinuityContracts {
|
||||
return self.gap_ledger.continuity_frontier(processing_frontier_slot);
|
||||
}
|
||||
|
||||
/// Returns the bounded source-neutral continuity observability projection for the latest Worker snapshot.
|
||||
pub(crate) fn observability_projection(&self) -> ksp_core_lib::Result<crate::RawTransactionIngestContinuitySnapshotProjection> {
|
||||
return self.gap_ledger.observability_projection();
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
Reference in New Issue
Block a user