use std::{
cell::Cell,
sync::{Arc, Weak},
time::Instant,
};
use query_runtime::observability::{RuntimeObservationConfig, RuntimeTerminalOutcome};
use super::{
super::observability::CapturedError,
QueryCoordinator,
types::{
CoordinatorTicketPayload, QueryCoordinatorError, QueryCoordinatorErrorKind, QueryHotCounters, QueryKey,
QueryPhase, QueryTerminal, RetainedUnavailableReason,
},
};
use crate::server::attempt::AlgeonAttempt;
impl QueryCoordinator {
pub(crate) fn consume_ticket(
self: &Arc<Self>,
query_id: &str,
now: Instant,
) -> Result<(QueryExecutionLease, CoordinatorTicketPayload), QueryCoordinatorError> {
let mut state = self.lock_state();
let key = state.current.get(query_id).cloned().ok_or_else(|| {
QueryCoordinatorError::new(QueryCoordinatorErrorKind::UnknownQuery, "unknown query ticket")
})?;
let (ticket, hot, expired) = {
let record = state.records.get_mut(&key).expect("current query key must reference a record");
if record.phase != QueryPhase::Ticketed {
return Err(QueryCoordinatorError::new(
QueryCoordinatorErrorKind::InvalidTransition,
"query is not ticketed",
));
}
let ticket = record.ticket.take().expect("ticketed query must own a ticket payload");
let expired = now >= ticket.deadline;
if expired {
if record.terminal_outcome.is_none() {
let mut terminal = QueryTerminal::Dropped;
Self::apply_terminal_to_capture(record, &mut terminal, now);
record.terminal_outcome = Some(terminal.compact_outcome());
record.terminal = Some(terminal);
}
record.unavailable = Some(RetainedUnavailableReason::TicketExpired);
record.phase = QueryPhase::Finalizing;
} else {
record.phase = QueryPhase::Executing;
record.active_leases = 1;
record.execution_cancellation = ticket.attempt.as_ref().map(AlgeonAttempt::cancellation);
}
(ticket, Arc::clone(&record.hot), expired)
};
state.ticketed_count = state.ticketed_count.checked_sub(1).expect("consumed ticket count underflow");
if expired {
drop(state);
self.finalize_ready();
return Err(QueryCoordinatorError::new(QueryCoordinatorErrorKind::TicketExpired, "query ticket expired"));
}
let lease = QueryExecutionLease { coordinator: Arc::downgrade(self), key, hot, released: false };
Ok((lease, ticket))
}
pub(crate) fn issue_ticket(
&self,
key: &QueryKey,
payload: CoordinatorTicketPayload,
) -> Result<(), QueryCoordinatorError> {
let mut state = self.lock_state();
if state.ticketed_count >= self.config.max_ticketed_queries {
return Err(QueryCoordinatorError::new(
QueryCoordinatorErrorKind::TicketCapacity,
"maximum ticketed query count reached",
));
}
let record = Self::record_mut(&mut state, key)?;
if record.phase != QueryPhase::Planning || record.active_leases != 1 || record.terminal_outcome.is_some() {
return Err(QueryCoordinatorError::new(
QueryCoordinatorErrorKind::InvalidTransition,
"planning query cannot transition to ticketed",
));
}
record.phase = QueryPhase::Ticketed;
record.ticket = Some(payload);
record.active_leases = 0;
state.ticketed_count = state.ticketed_count.checked_add(1).expect("ticketed count overflow");
self.notify.notify_waiters();
Ok(())
}
pub(crate) fn promote_execution(&self, key: &QueryKey) -> Result<(), QueryCoordinatorError> {
let mut state = self.lock_state();
let record = Self::record_mut(&mut state, key)?;
if matches!(record.terminal, Some(QueryTerminal::Cancelled { stage: "server_shutdown", .. })) {
return Err(QueryCoordinatorError::new(
QueryCoordinatorErrorKind::Shutdown,
"server shutdown cancelled query before execution began",
));
}
if record.phase != QueryPhase::Planning || record.active_leases != 1 || record.terminal_outcome.is_some() {
return Err(QueryCoordinatorError::new(
QueryCoordinatorErrorKind::InvalidTransition,
"planning query cannot transition to execution",
));
}
record.phase = QueryPhase::Executing;
Ok(())
}
pub(crate) fn set_terminal(&self, key: &QueryKey, mut terminal: QueryTerminal) {
let should_finalize = {
let mut state = self.lock_state();
let Ok(record) = Self::record_mut(&mut state, key) else { return };
if record.terminal_outcome.is_none() {
Self::apply_terminal_to_capture(record, &mut terminal, Instant::now());
record.terminal_outcome = Some(terminal.compact_outcome());
record.terminal = Some(terminal);
Self::bump_capture_version(record);
}
if record.active_leases > 0 {
record.active_leases -= 1;
}
record.active_leases == 0 && !matches!(record.phase, QueryPhase::Retained | QueryPhase::Tombstone)
};
self.notify.notify_waiters();
if should_finalize {
self.finalize_ready();
}
}
pub(crate) fn finish_planning_without_attempt_report(&self, key: &QueryKey, mut terminal: QueryTerminal) {
let should_finalize = {
let mut state = self.lock_state();
let Ok(record) = Self::record_mut(&mut state, key) else { return };
if record.phase == QueryPhase::Planning {
record.requires_attempt_report = false;
}
if record.terminal_outcome.is_none() {
Self::apply_terminal_to_capture(record, &mut terminal, Instant::now());
record.terminal_outcome = Some(terminal.compact_outcome());
record.terminal = Some(terminal);
Self::bump_capture_version(record);
}
if record.active_leases > 0 {
record.active_leases -= 1;
}
record.active_leases == 0 && !matches!(record.phase, QueryPhase::Retained | QueryPhase::Tombstone)
};
self.notify.notify_waiters();
if should_finalize {
self.finalize_ready();
}
}
}
#[derive(Debug)]
pub(crate) struct QueryPlanningLease {
pub(crate) coordinator: Weak<QueryCoordinator>,
pub(crate) key: QueryKey,
pub(crate) hot: Arc<QueryHotCounters>,
pub(crate) observation_config: RuntimeObservationConfig,
pub(crate) attempt_report_handoff_registered: Cell<bool>,
pub(crate) released: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct QueryPlanningReportDeliveryTerminal {
coordinator: Weak<QueryCoordinator>,
key: QueryKey,
}
impl QueryPlanningReportDeliveryTerminal {
pub(crate) fn key(&self) -> &QueryKey {
&self.key
}
pub(crate) fn finish_attempt_report_delivery_failed(&self, attempt_outcome: Option<RuntimeTerminalOutcome>) {
if let Some(coordinator) = self.coordinator.upgrade() {
coordinator.abandon_attempt_report_delivery(&self.key, attempt_outcome);
}
}
}
impl QueryPlanningLease {
pub(crate) fn key(&self) -> &QueryKey {
&self.key
}
pub(crate) fn observation_config(&self) -> RuntimeObservationConfig {
self.observation_config
}
pub(crate) fn attempt_report_delivery_terminal(&self) -> QueryPlanningReportDeliveryTerminal {
self.attempt_report_handoff_registered.set(true);
QueryPlanningReportDeliveryTerminal { coordinator: Weak::clone(&self.coordinator), key: self.key.clone() }
}
#[cfg(test)]
pub(crate) fn hot_counters(&self) -> Arc<QueryHotCounters> {
Arc::clone(&self.hot)
}
pub(crate) fn issue_ticket(mut self, payload: CoordinatorTicketPayload) -> Result<QueryKey, QueryCoordinatorError> {
let coordinator = self.coordinator.upgrade().ok_or_else(|| {
QueryCoordinatorError::new(QueryCoordinatorErrorKind::UnknownQuery, "coordinator dropped")
})?;
if let Err(error) = coordinator.issue_ticket(&self.key, payload) {
let captured = CapturedError::from_adapter_error(
&error.adapter_error("algeon_datafusion.server.query_coordinator.issue_ticket"),
);
self.finish(QueryTerminal::failed("ticket", captured));
return Err(error);
}
self.released = true;
Ok(self.key.clone())
}
pub(crate) fn into_execution(mut self) -> Result<QueryExecutionLease, QueryCoordinatorError> {
let coordinator = self.coordinator.upgrade().ok_or_else(|| {
QueryCoordinatorError::new(QueryCoordinatorErrorKind::UnknownQuery, "coordinator dropped")
})?;
if let Err(error) = coordinator.promote_execution(&self.key) {
let captured = CapturedError::from_adapter_error(
&error.adapter_error("algeon_datafusion.server.query_coordinator.promote_execution"),
);
self.finish(QueryTerminal::failed("planning", captured));
return Err(error);
}
self.released = true;
Ok(QueryExecutionLease {
coordinator: Weak::clone(&self.coordinator),
key: self.key.clone(),
hot: Arc::clone(&self.hot),
released: false,
})
}
pub(crate) fn fail(mut self, stage: &'static str, error: CapturedError) {
self.finish(QueryTerminal::failed(stage, error));
}
pub(crate) fn succeed(mut self) {
self.finish(QueryTerminal::Succeeded);
}
pub(crate) fn finish_captured(&mut self, stage: &'static str, status: &tonic::Status, error: CapturedError) {
let terminal = if status.code() == tonic::Code::Cancelled {
QueryTerminal::Cancelled { stage, error: Arc::new(error) }
} else {
QueryTerminal::failed(stage, error)
};
self.finish(terminal);
}
pub(crate) fn finish(&mut self, terminal: QueryTerminal) {
if self.released {
return;
}
self.released = true;
if let Some(coordinator) = self.coordinator.upgrade() {
if self.attempt_report_handoff_registered.get() {
coordinator.set_terminal(&self.key, terminal);
} else {
coordinator.finish_planning_without_attempt_report(&self.key, terminal);
}
}
}
}
impl Drop for QueryPlanningLease {
fn drop(&mut self) {
let terminal = if std::thread::panicking() { QueryTerminal::Panicked } else { QueryTerminal::Dropped };
self.finish(terminal);
}
}
#[derive(Debug)]
pub(crate) struct QueryExecutionLease {
pub(crate) coordinator: Weak<QueryCoordinator>,
pub(crate) key: QueryKey,
pub(crate) hot: Arc<QueryHotCounters>,
pub(crate) released: bool,
}
impl QueryExecutionLease {
pub(crate) fn hot_counters(&self) -> Arc<QueryHotCounters> {
Arc::clone(&self.hot)
}
pub(crate) fn succeed(mut self) {
self.finish_success();
}
#[cfg(test)]
pub(crate) fn fail(mut self, stage: &'static str, error: CapturedError) {
self.finish_failed(stage, error);
}
pub(crate) fn finish_success(&mut self) {
self.finish(QueryTerminal::Succeeded);
}
pub(crate) fn finish_failed(&mut self, stage: &'static str, error: CapturedError) {
self.finish(QueryTerminal::failed(stage, error));
}
pub(crate) fn finish_cancelled(&mut self, stage: &'static str, error: CapturedError) {
self.finish(QueryTerminal::Cancelled { stage, error: Arc::new(error) });
}
pub(crate) fn finish_panicked(&mut self) {
self.finish(QueryTerminal::Panicked);
}
#[cfg(test)]
pub(crate) fn finish_status(&mut self, stage: &'static str, status: &tonic::Status) {
let error = CapturedError::from_status(status);
self.finish_captured(stage, status, error);
}
pub(crate) fn finish_captured(&mut self, stage: &'static str, status: &tonic::Status, error: CapturedError) {
if status.code() == tonic::Code::Cancelled {
self.finish_cancelled(stage, error);
} else {
self.finish_failed(stage, error);
}
}
pub(crate) fn finish_attempt_report_delivery_failed(
&mut self,
stage: &'static str,
status: &tonic::Status,
error: CapturedError,
attempt_outcome: Option<RuntimeTerminalOutcome>,
) {
if self.released {
return;
}
if let Some(coordinator) = self.coordinator.upgrade() {
coordinator.abandon_attempt_report_delivery(&self.key, attempt_outcome);
}
self.finish_captured(stage, status, error);
}
pub(crate) fn finish(&mut self, terminal: QueryTerminal) {
if self.released {
return;
}
self.released = true;
if let Some(coordinator) = self.coordinator.upgrade() {
coordinator.set_terminal(&self.key, terminal);
}
}
}
impl Drop for QueryExecutionLease {
fn drop(&mut self) {
let terminal = if std::thread::panicking() { QueryTerminal::Panicked } else { QueryTerminal::Dropped };
self.finish(terminal);
}
}