v0.3.14-pre.009

This commit is contained in:
2026-09-12 11:22:53 +02:00
parent bf394e0e4b
commit 4bcd942928
10 changed files with 853 additions and 58 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-worker-raw-transaction-ingest-lib/src/snapshot.rs
// version: 5
// version: 6
/// Runtime-neutral boxed future resolving to one newer concrete RAW transaction ingest Worker snapshot.
pub type RawTransactionIngestSnapshotFuture<'a> =
@@ -23,6 +23,11 @@ pub enum RawTransactionIngestSourceState {
/// Private latest-value source-processing projection emitted by the productive source task.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct RawTransactionIngestProcessingFrontierProjection {
continuity_frontier_slot: std::option::Option<u64>,
continuity_has_open_gaps: bool,
continuity_policy_observed: bool,
failed_source_losses_reconciled: bool,
future_target_coverage: bool,
hydration_pending: usize,
processing_frontier_slot: std::option::Option<u64>,
oldest_pending_slot: std::option::Option<u64>,
@@ -40,6 +45,11 @@ impl crate::RawTransactionIngestProcessingFrontierProjection {
/// Returns the empty run-local processing projection used before the source observes work.
pub(crate) const fn empty() -> Self {
return Self {
continuity_frontier_slot: std::option::Option::None,
continuity_has_open_gaps: false,
continuity_policy_observed: false,
failed_source_losses_reconciled: false,
future_target_coverage: false,
hydration_pending: 0,
processing_frontier_slot: std::option::Option::None,
oldest_pending_slot: std::option::Option::None,
@@ -61,6 +71,11 @@ impl crate::RawTransactionIngestProcessingFrontierProjection {
oldest_pending_slot: std::option::Option<u64>,
) -> Self {
return Self {
continuity_frontier_slot: std::option::Option::None,
continuity_has_open_gaps: false,
continuity_policy_observed: false,
failed_source_losses_reconciled: false,
future_target_coverage: false,
hydration_pending,
processing_frontier_slot,
oldest_pending_slot,
@@ -114,6 +129,51 @@ impl crate::RawTransactionIngestProcessingFrontierProjection {
return self;
}
/// Returns a copy carrying source-neutral run-local continuity evidence used only for Worker health classification.
pub(crate) const fn with_continuity_health(
mut self,
continuity_frontier_slot: std::option::Option<u64>,
continuity_has_open_gaps: bool,
future_target_coverage: bool,
) -> Self {
self.continuity_frontier_slot = continuity_frontier_slot;
self.continuity_has_open_gaps = continuity_has_open_gaps;
self.continuity_policy_observed = true;
self.future_target_coverage = future_target_coverage;
return self;
}
/// Returns whether a run-local continuity policy projection has been observed.
pub(crate) const fn continuity_policy_observed(&self) -> bool {
return self.continuity_policy_observed;
}
/// Returns the gap-aware continuity frontier carried by this private projection.
pub(crate) const fn continuity_frontier_slot(&self) -> std::option::Option<u64> {
return self.continuity_frontier_slot;
}
/// Returns whether at least one run-local continuity gap remains unresolved.
pub(crate) const fn continuity_has_open_gaps(&self) -> bool {
return self.continuity_has_open_gaps;
}
/// Returns whether currently Active sources still cover the complete configured future `TargetCoverage`.
pub(crate) const fn future_target_coverage(&self) -> bool {
return self.future_target_coverage;
}
/// Returns a copy carrying whether every terminal Failed source has a reconciled source-loss gap.
pub(crate) const fn with_failed_source_losses_reconciled(mut self, failed_source_losses_reconciled: bool) -> Self {
self.failed_source_losses_reconciled = failed_source_losses_reconciled;
return self;
}
/// Returns whether every terminal Failed source has a reconciled source-loss gap.
pub(crate) const fn failed_source_losses_reconciled(&self) -> bool {
return self.failed_source_losses_reconciled;
}
/// Returns the configured logical source count carried by this private projection.
pub(crate) const fn source_total(&self) -> usize {
return self.source_total;
@@ -159,6 +219,11 @@ impl crate::RawTransactionIngestProcessingFrontierProjection {
#[derive(Clone, Eq, PartialEq)]
pub struct RawTransactionIngestSnapshot {
worker: ksp_worker_api::WorkerSnapshot,
continuity_frontier_slot: std::option::Option<u64>,
continuity_has_open_gaps: bool,
continuity_policy_observed: bool,
failed_source_losses_reconciled: bool,
future_target_coverage: bool,
admission_queue_capacity: usize,
admission_queue_depth: usize,
persistence_concurrency: usize,
@@ -488,6 +553,11 @@ impl crate::RawTransactionIngestSnapshotPublisher {
);
let snapshot = crate::RawTransactionIngestSnapshot {
worker,
continuity_frontier_slot: std::option::Option::None,
continuity_has_open_gaps: false,
continuity_policy_observed: false,
failed_source_losses_reconciled: false,
future_target_coverage: false,
admission_queue_capacity: settings.admission_queue_capacity(),
admission_queue_depth: 0,
persistence_concurrency: settings.persistence_concurrency(),
@@ -527,14 +597,7 @@ impl crate::RawTransactionIngestSnapshotPublisher {
self.snapshot.worker.kind().clone(),
self.snapshot.worker.sequence(),
state,
health_for_state(
state,
self.snapshot.worker.health(),
self.snapshot.source_total,
self.snapshot.source_active,
self.snapshot.source_reconnecting,
self.snapshot.source_failed,
),
health_for_state(state, self.snapshot.worker.health(), &self.snapshot),
ksp_worker_api::WorkerActivity::Idle,
);
self.snapshot.worker = worker;
@@ -611,6 +674,11 @@ impl crate::RawTransactionIngestSnapshotPublisher {
in_flight_persistence: usize,
projection: crate::RawTransactionIngestProcessingFrontierProjection,
) -> ksp_core_lib::Result<()> {
self.snapshot.continuity_frontier_slot = projection.continuity_frontier_slot();
self.snapshot.continuity_has_open_gaps = projection.continuity_has_open_gaps();
self.snapshot.continuity_policy_observed = projection.continuity_policy_observed();
self.snapshot.failed_source_losses_reconciled = projection.failed_source_losses_reconciled();
self.snapshot.future_target_coverage = projection.future_target_coverage();
self.snapshot.hydration_pending = projection.hydration_pending();
self.snapshot.processing_frontier_slot = projection.processing_frontier_slot();
self.snapshot.oldest_pending_slot = projection.oldest_pending_slot();
@@ -737,14 +805,7 @@ impl crate::RawTransactionIngestSnapshotPublisher {
self.snapshot.worker.kind().clone(),
sequence,
state,
health_for_state(
state,
self.snapshot.worker.health(),
self.snapshot.source_total,
self.snapshot.source_active,
self.snapshot.source_reconnecting,
self.snapshot.source_failed,
),
health_for_state(state, self.snapshot.worker.health(), &self.snapshot),
activity_for_state(state, admission_queue_depth, in_flight_persistence, self.snapshot.hydration_pending, self.snapshot.source_state),
);
self.snapshot.worker = worker;
@@ -799,15 +860,35 @@ fn checked_optional_counter(current: u64, increment: bool, field: &'static str)
fn health_for_state(
state: ksp_worker_api::WorkerState,
previous: ksp_worker_api::WorkerHealth,
source_total: usize,
source_active: usize,
source_reconnecting: usize,
source_failed: usize,
snapshot: &crate::RawTransactionIngestSnapshot,
) -> ksp_worker_api::WorkerHealth {
if state == ksp_worker_api::WorkerState::Running && snapshot.continuity_policy_observed {
if snapshot.source_reconnecting > 0
|| snapshot.continuity_has_open_gaps
|| snapshot.continuity_frontier_slot != snapshot.processing_frontier_slot
|| !snapshot.future_target_coverage
{
return ksp_worker_api::WorkerHealth::Unhealthy;
}
if snapshot.source_total > 0 && snapshot.source_active == snapshot.source_total {
return ksp_worker_api::WorkerHealth::Healthy;
}
if snapshot.source_total > 0
&& snapshot.source_active < snapshot.source_total
&& snapshot.source_failed > 0
&& snapshot.failed_source_losses_reconciled
&& snapshot.source_failed == snapshot.source_total - snapshot.source_active
{
return ksp_worker_api::WorkerHealth::Degraded;
}
return ksp_worker_api::WorkerHealth::Unhealthy;
}
return match state {
ksp_worker_api::WorkerState::Running if source_failed > 0 => ksp_worker_api::WorkerHealth::Unhealthy,
ksp_worker_api::WorkerState::Running if source_reconnecting > 0 => ksp_worker_api::WorkerHealth::Degraded,
ksp_worker_api::WorkerState::Running if source_total > 0 && source_active < source_total => ksp_worker_api::WorkerHealth::Degraded,
ksp_worker_api::WorkerState::Running if snapshot.source_failed > 0 => ksp_worker_api::WorkerHealth::Unhealthy,
ksp_worker_api::WorkerState::Running if snapshot.source_reconnecting > 0 => ksp_worker_api::WorkerHealth::Degraded,
ksp_worker_api::WorkerState::Running if snapshot.source_total > 0 && snapshot.source_active < snapshot.source_total => {
ksp_worker_api::WorkerHealth::Degraded
},
ksp_worker_api::WorkerState::Running => ksp_worker_api::WorkerHealth::Healthy,
ksp_worker_api::WorkerState::Stopping | ksp_worker_api::WorkerState::Stopped => previous,
ksp_worker_api::WorkerState::Faulted(_) => ksp_worker_api::WorkerHealth::Unhealthy,