#[cfg(any(test, feature = "test-hooks"))]
use std::pin::pin;
use std::{
collections::{BTreeMap, BTreeSet, VecDeque},
mem,
sync::{Arc, Mutex, PoisonError, Weak},
time::{Duration, Instant},
};
use query_planning::{
admission::AttemptCapabilityEvidence,
error::{QueryError, QueryErrorCode, Result},
};
use tokio::time;
use super::{QueryActivityLease, admission_rejection, controller::AdmissionController, record_admission_lifecycle};
#[cfg(any(test, feature = "test-hooks"))]
use super::{QueryHandle, QueryService};
#[cfg(any(test, feature = "test-hooks"))]
use crate::{
attempt::AttemptOwner,
runtime::{deadline::block_until, query_service::service::transaction::AdmissionTransactionProbe},
};
use crate::{
attempt::{AdmittedGrantRelease, AttemptAdmission, AttemptHandle, AttemptLifecycleError, AttemptReportPublisher},
observability::{
AdmissionLifecycleState, AdmissionRequestId, AttemptAdmissionFact, AttemptAdmissionWaitFact,
AttemptFailureOperation, AttemptFailureSummary, AttemptId, AttemptTerminalCommit, QueryCleanupSealDelivery,
RuntimeObservationHandle, RuntimeTerminalOutcome,
},
runtime::{
ExecutionCancellation, MemoryGrant,
backend_core::{GpuResourcePermit, ObservationCompletionDispatcher},
backend_lifecycle::ObservationPermit,
},
};
#[derive(Debug)]
pub(crate) struct AdmissionWaiter {
pub(super) controller: Arc<AdmissionController>,
pub(super) admission_request_id: AdmissionRequestId,
pub(super) observation: RuntimeObservationHandle,
pub(super) requested_at: Instant,
pub(super) deadline: Option<Instant>,
pub(super) execution_cancellation: ExecutionCancellation,
pub(super) terminal: QueuedAdmissionTerminal,
pub(super) queued: bool,
}
#[cfg(any(test, feature = "test-hooks"))]
#[derive(Debug)]
pub(crate) struct QueryAdmissionWaiter {
pub(super) service: QueryService,
pub(super) waiter: AdmissionWaiter,
pub(super) test_attempt: Option<AttemptOwner>,
pub(super) probe: Option<Arc<AdmissionTransactionProbe>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum AdmissionTicketPhase {
Provisional,
Active,
ProvisionalCleanup,
ActiveCleanup,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(super) struct AdmissionLifecycleSnapshot {
pub(super) provisional: usize,
pub(super) active: usize,
pub(super) published_cleanup: usize,
pub(super) cleanup: usize,
}
#[derive(Debug, Default)]
struct AdmissionLifecycleTrackerState {
next_ticket_id: u64,
tickets: BTreeMap<u64, AdmissionTicketPhase>,
}
#[derive(Debug, Clone, Default)]
pub(super) struct AdmissionLifecycleTracker {
state: Arc<Mutex<AdmissionLifecycleTrackerState>>,
}
#[derive(Debug)]
pub(super) struct PendingPreflightReservation {
controller: Weak<AdmissionController>,
submission_seq: u64,
armed: bool,
}
impl PendingPreflightReservation {
pub(super) fn new(controller: Weak<AdmissionController>, submission_seq: u64) -> Self {
Self { controller, submission_seq, armed: true }
}
pub(super) const fn submission_seq(&self) -> u64 {
self.submission_seq
}
pub(super) fn release(mut self) {
self.release_if_armed();
}
pub(super) fn convert_locked(mut self, state: &mut AdmissionState) {
let removed = state.pending_preflight.remove(&self.submission_seq);
self.armed = false;
assert!(removed, "pending preflight sequence must be reserved before conversion");
}
fn release_if_armed(&mut self) {
if !self.armed {
return;
}
let Some(controller) = self.controller.upgrade() else {
self.armed = false;
return;
};
let removed = match controller.state.lock() {
Ok(mut state) => state.pending_preflight.remove(&self.submission_seq),
Err(error) => error.into_inner().pending_preflight.remove(&self.submission_seq),
};
self.armed = false;
if removed {
controller.notify_admission_available();
}
}
}
impl Drop for PendingPreflightReservation {
fn drop(&mut self) {
self.release_if_armed();
}
}
impl AdmissionLifecycleTracker {
pub(super) fn begin_provisional(&self, controller: Weak<AdmissionController>) -> AdmissionTicket {
let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
let id = state.next_ticket_id;
state.next_ticket_id = state.next_ticket_id.checked_add(1).expect("admission lifecycle ticket id overflow");
let previous = state.tickets.insert(id, AdmissionTicketPhase::Provisional);
debug_assert!(previous.is_none(), "admission lifecycle ticket ids must be unique");
AdmissionTicket { tracker: self.clone(), controller, id, phase: AdmissionTicketPhase::Provisional }
}
fn transition(&self, id: u64, expected: AdmissionTicketPhase, next: AdmissionTicketPhase) -> Result<()> {
let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
let Some(current) = state.tickets.get_mut(&id) else {
return Err(QueryError::new(
QueryErrorCode::Internal,
"query_engine.query_service.admission.lifecycle",
"admission lifecycle ticket is missing",
)
.with_fact("reason", "lifecycle_ticket_missing")
.with_fact("ticket_id", id));
};
if *current != expected {
return Err(QueryError::new(
QueryErrorCode::Internal,
"query_engine.query_service.admission.lifecycle",
"admission lifecycle ticket transition conflicts with its current phase",
)
.with_fact("reason", "lifecycle_transition_conflict")
.with_fact("ticket_id", id)
.with_fact("expected_phase", format!("{expected:?}"))
.with_fact("actual_phase", format!("{current:?}")));
}
*current = next;
Ok(())
}
fn finish_cleanup(&self, id: u64) -> Result<()> {
let mut state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
match state.tickets.get(&id).copied() {
Some(actual) if actual.is_cleanup() => {
state.tickets.remove(&id);
Ok(())
},
Some(actual) => Err(QueryError::new(
QueryErrorCode::Internal,
"query_engine.query_service.admission.lifecycle",
"admission lifecycle ticket dropped before cleanup completed",
)
.with_fact("reason", "lifecycle_drop_conflict")
.with_fact("ticket_id", id)
.with_fact("expected_phase", "cleanup")
.with_fact("actual_phase", format!("{actual:?}"))),
None => Err(QueryError::new(
QueryErrorCode::Internal,
"query_engine.query_service.admission.lifecycle",
"admission lifecycle ticket was already dropped",
)
.with_fact("reason", "lifecycle_ticket_missing")
.with_fact("ticket_id", id)),
}
}
pub(super) fn snapshot(&self) -> AdmissionLifecycleSnapshot {
let state = self.state.lock().unwrap_or_else(PoisonError::into_inner);
let mut snapshot = AdmissionLifecycleSnapshot::default();
for phase in state.tickets.values() {
match phase {
AdmissionTicketPhase::Provisional => snapshot.provisional += 1,
AdmissionTicketPhase::Active => snapshot.active += 1,
AdmissionTicketPhase::ProvisionalCleanup => snapshot.cleanup += 1,
AdmissionTicketPhase::ActiveCleanup => {
snapshot.published_cleanup += 1;
snapshot.cleanup += 1;
},
}
}
snapshot
}
pub(super) fn is_quiescent(&self) -> bool {
self.state.lock().unwrap_or_else(PoisonError::into_inner).tickets.is_empty()
}
}
#[derive(Debug)]
pub(super) struct AdmissionTicket {
tracker: AdmissionLifecycleTracker,
controller: Weak<AdmissionController>,
id: u64,
phase: AdmissionTicketPhase,
}
impl AdmissionTicket {
pub(super) fn is_cleanup(&self) -> bool {
self.phase.is_cleanup()
}
pub(super) fn transition_to_active(&mut self) -> Result<()> {
self.tracker.transition(self.id, AdmissionTicketPhase::Provisional, AdmissionTicketPhase::Active)?;
self.phase = AdmissionTicketPhase::Active;
Ok(())
}
pub(super) fn transition_to_cleanup(&mut self) -> Result<()> {
let cleanup_phase = match self.phase {
AdmissionTicketPhase::Provisional => AdmissionTicketPhase::ProvisionalCleanup,
AdmissionTicketPhase::Active => AdmissionTicketPhase::ActiveCleanup,
AdmissionTicketPhase::ProvisionalCleanup | AdmissionTicketPhase::ActiveCleanup => return Ok(()),
};
self.tracker.transition(self.id, self.phase, cleanup_phase)?;
self.phase = cleanup_phase;
Ok(())
}
}
impl AdmissionTicketPhase {
const fn is_cleanup(self) -> bool {
matches!(self, Self::ProvisionalCleanup | Self::ActiveCleanup)
}
}
impl Drop for AdmissionTicket {
fn drop(&mut self) {
match self.transition_to_cleanup().and_then(|()| self.tracker.finish_cleanup(self.id)) {
Ok(()) => {
if let Some(controller) = self.controller.upgrade() {
let _admission = controller.state.lock().unwrap_or_else(PoisonError::into_inner);
controller.notify_admission_available();
}
},
Err(error) => {
eprintln!("[query-engine] warning: admission lifecycle ticket drop invariant failed: {error}");
},
}
}
}
#[derive(Debug, Default)]
pub(super) struct AdmissionState {
pub(super) lifecycle: AdmissionLifecycleTracker,
pub(super) queued: VecDeque<AdmissionRequest>,
pub(super) pending_preflight: BTreeSet<u64>,
queue_high_watermark: usize,
pub(super) closed: bool,
}
impl AdmissionState {
pub(super) fn enqueue_request(&mut self, request: AdmissionRequest) {
let position = self
.queued
.iter()
.position(|queued| queued.submission_seq > request.submission_seq)
.unwrap_or(self.queued.len());
self.queued.insert(position, request);
self.record_queue_depth();
}
pub(super) fn remove_queued_at(&mut self, position: usize) -> Option<AdmissionRequest> {
let request = self.queued.remove(position);
self.record_queue_depth();
request
}
pub(super) fn take_queued_requests(&mut self) -> VecDeque<AdmissionRequest> {
mem::take(&mut self.queued)
}
pub(super) fn replace_queued_requests(&mut self, queued: VecDeque<AdmissionRequest>) {
self.queued = queued;
self.record_queue_depth();
}
pub(super) const fn queue_high_watermark(&self) -> usize {
self.queue_high_watermark
}
fn record_queue_depth(&mut self) {
self.queue_high_watermark = self.queue_high_watermark.max(self.queued.len());
}
}
#[derive(Debug, Default)]
struct QueuedAdmissionTerminalState {
error: Option<QueryError>,
primary_failure: Option<AttemptFailureSummary>,
}
#[derive(Debug, Clone, Default)]
pub(super) struct QueuedAdmissionTerminal {
state: Arc<Mutex<QueuedAdmissionTerminalState>>,
}
impl QueuedAdmissionTerminal {
pub(super) fn record(&self, error: QueryError) -> RuntimeTerminalOutcome {
let disposition = RuntimeTerminalOutcome::from_query_error(&error);
let primary_failure =
RuntimeTerminalOutcome::primary_failure_from_query_error(&error, AttemptFailureOperation::Admission);
let mut terminal = self.state.lock().unwrap_or_else(PoisonError::into_inner);
if terminal.error.is_none() {
terminal.primary_failure = primary_failure;
terminal.error = Some(error);
}
disposition
}
pub(super) fn take(&self) -> Option<QueryError> {
self.state.lock().unwrap_or_else(PoisonError::into_inner).error.take()
}
pub(super) fn take_primary_failure(&self) -> Option<AttemptFailureSummary> {
self.state.lock().unwrap_or_else(PoisonError::into_inner).primary_failure.take()
}
}
#[derive(Debug)]
pub(super) struct AdmissionRequest {
pub(super) admission_request_id: AdmissionRequestId,
pub(super) observation: RuntimeObservationHandle,
pub(super) attempt_id: AttemptId,
pub(super) completion: QueryCompletion,
pub(super) submitted_plan_capability_evidence: Option<AttemptCapabilityEvidence>,
pub(super) device_entries: DeviceAdmissionEntries,
pub(super) submission_seq: u64,
pub(super) compatible_devices: Vec<usize>,
pub(super) requested_query_cap_bytes: Option<u64>,
pub(super) deadline: Option<Instant>,
pub(super) execution_cancellation: ExecutionCancellation,
pub(super) terminal: QueuedAdmissionTerminal,
pub(super) validated_generation: Option<u64>,
}
pub(super) type DeviceAdmissionEntries = BTreeMap<usize, AttemptCapabilityEvidence>;
impl AdmissionRequest {
pub(super) fn requirement_for_device(&self, device_ordinal: usize) -> Result<&AttemptCapabilityEvidence> {
self.device_entries.get(&device_ordinal).ok_or_else(|| missing_device_admission_entry_error(device_ordinal))
}
pub(super) fn is_queue_member(&self, now: Instant) -> bool {
!self.execution_cancellation.is_cancelled() && self.deadline.is_none_or(|deadline| deadline > now)
}
pub(super) fn admission_wait_fact(&self) -> AttemptAdmissionWaitFact {
AttemptAdmissionWaitFact { submission_sequence: self.submission_seq }
}
}
pub(super) fn missing_device_admission_entry_error(device_ordinal: usize) -> QueryError {
QueryError::new(
QueryErrorCode::Internal,
"query_engine.query_service.admission",
"admission request is missing the selected device-specific requirement",
)
.with_fact("reason", "missing_device_specific_admission_resolution")
.with_fact("device_ordinal", device_ordinal)
}
#[derive(Debug)]
pub(crate) struct QueryCompletion {
attempt_admission: Option<AttemptAdmission>,
attempt_report: Option<AttemptReportPublisher>,
observation_permit: Option<ObservationPermit>,
observation_activity: Option<QueryActivityLease>,
completion_dispatcher: Option<ObservationCompletionDispatcher>,
gpu_resource_permit: Option<GpuResourcePermit>,
}
impl QueryCompletion {
pub(super) fn new(
attempt_admission: AttemptAdmission,
observation_permit: Option<ObservationPermit>,
observation_activity: Option<QueryActivityLease>,
completion_dispatcher: Option<ObservationCompletionDispatcher>,
gpu_resource_permit: Option<GpuResourcePermit>,
) -> Self {
Self {
attempt_report: Some(attempt_admission.report_publisher()),
attempt_admission: Some(attempt_admission),
observation_permit,
observation_activity,
completion_dispatcher,
gpu_resource_permit,
}
}
pub(super) fn into_admitted(mut self, envelope: &mut super::service::transaction::AdmissionEnvelope) -> Self {
envelope.retain_gpu_resource_permit(self.gpu_resource_permit.take());
self
}
pub(super) fn record_attempt_admitted(
&self,
fact: AttemptAdmissionFact,
wait: Option<AttemptAdmissionWaitFact>,
) -> std::result::Result<(), AttemptLifecycleError> {
self.attempt_admission.as_ref().map_or(Ok(()), |admission| admission.record_admitted(fact, wait))
}
pub(super) fn attempt(&self) -> Option<AttemptHandle> {
self.attempt_report.as_ref().map(AttemptReportPublisher::attempt)
}
pub(super) fn bind_grant_release(&self, release: Arc<dyn AdmittedGrantRelease>) -> bool {
self.attempt_admission.as_ref().is_some_and(|admission| admission.bind_grant_release(release))
}
pub(super) fn attempt_monotonic_ns(&self) -> Option<u64> {
self.attempt_admission.as_ref().map(AttemptAdmission::monotonic_ns)
}
pub(super) fn begin_query_cleanup(&self) {
if let Some(attempt_report) = &self.attempt_report {
attempt_report.begin_cleanup();
}
}
pub(super) fn finish_error(self, error: &QueryError) {
if let Some(summary) =
RuntimeTerminalOutcome::primary_failure_from_query_error(error, AttemptFailureOperation::Admission)
{
self.record_primary_failure(summary);
}
self.finish(RuntimeTerminalOutcome::from_query_error(error));
}
fn record_primary_failure(&self, summary: AttemptFailureSummary) {
if let Some(admission) = &self.attempt_admission {
admission.record_primary_failure(summary);
}
}
pub(super) fn finish_timeout(self, error: &QueryError) {
self.finish_error(error);
}
pub(super) fn finish_cancelled(self) {
self.finish(RuntimeTerminalOutcome::Cancelled);
}
pub(super) fn finish_terminal_disposition(
self,
disposition: RuntimeTerminalOutcome,
primary_failure: Option<AttemptFailureSummary>,
) {
if let Some(summary) = primary_failure {
self.record_primary_failure(summary);
}
self.finish(disposition);
}
pub(super) fn resolve_terminal_outcome_for_cleanup(
&self,
fallback: RuntimeTerminalOutcome,
) -> RuntimeTerminalOutcome {
self.attempt_admission.as_ref().map_or(fallback.clone(), |admission| {
match admission.terminalize_for_release(fallback.clone()) {
Ok(outcome) => outcome,
Err(error) => {
eprintln!(
"[query-engine] warning: failed to establish attempt terminal outcome before GPU cleanup: {error}"
);
fallback
},
}
})
}
pub(super) fn seal_after_gpu_cleanup(mut self, outcome: RuntimeTerminalOutcome) -> QueryCompletionActivityTail {
debug_assert!(self.gpu_resource_permit.is_none(), "GPU permit must leave before observer completion");
debug_assert!(self.attempt_admission.is_none(), "attempt admission must be consumed before report seal");
let dispatcher = self.completion_dispatcher.take();
let permit = self.observation_permit.take();
let activity = self.observation_activity.take();
let delivery = self.attempt_report.take().and_then(|attempt_report| {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| attempt_report.seal(outcome))).ok()
});
QueryCompletionActivityTail { activity, dispatcher, delivery, permit }
}
pub(super) fn finalize_attempt_after_gpu_cleanup(
&mut self,
terminal_commit: AttemptTerminalCommit,
) -> std::result::Result<(), AttemptLifecycleError> {
self.attempt_admission.take().map_or(Ok(()), |admission| admission.finish_admitted(terminal_commit))
}
fn finish(mut self, fallback: RuntimeTerminalOutcome) {
drop(self.gpu_resource_permit.take());
self.dispatch_after_gpu_cleanup(fallback);
}
fn dispatch_after_gpu_cleanup(mut self, fallback: RuntimeTerminalOutcome) {
debug_assert!(self.gpu_resource_permit.is_none(), "GPU permit must leave before observer completion");
if let Some(dispatcher) = self.completion_dispatcher.take() {
let activity_tail_dispatcher = dispatcher.clone();
dispatcher.dispatch(move || {
let activity = self.finish_observation(fallback);
if let Some(activity) = activity {
activity_tail_dispatcher.dispatch_activity_tail(move || Self::finish_activity(Some(activity)));
}
});
} else {
self.finish_inline(fallback);
}
}
fn finish_inline(mut self, fallback: RuntimeTerminalOutcome) {
let activity = self.finish_observation(fallback);
Self::finish_activity(activity);
}
fn finish_observation(&mut self, fallback: RuntimeTerminalOutcome) -> Option<QueryActivityLease> {
let permit = self.observation_permit.take();
let activity = self.observation_activity.take();
let attempt_outcome = match self.attempt_admission.take() {
Some(admission) if admission.is_admitted() => {
eprintln!(
"[query-engine] warning: admitted completion dropped before finalization; suppressing report publication"
);
None
},
Some(admission) => match admission.finish_not_admitted(fallback) {
Ok(outcome) => Some(outcome),
Err(error) => {
eprintln!(
"[query-engine] warning: failed to finalize unadmitted attempt before seal; suppressing report publication: {error}"
);
None
},
},
None => Some(fallback),
};
let attempt_report = self.attempt_report.take();
if let (Some(outcome), Some(attempt_report)) = (attempt_outcome, attempt_report) {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| attempt_report.seal(outcome).deliver()));
}
drop(permit);
activity
}
fn finish_activity(activity: Option<QueryActivityLease>) {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| drop(activity)));
}
}
pub(super) struct QueryCompletionActivityTail {
activity: Option<QueryActivityLease>,
dispatcher: Option<ObservationCompletionDispatcher>,
delivery: Option<QueryCleanupSealDelivery>,
permit: Option<ObservationPermit>,
}
impl QueryCompletionActivityTail {
pub(super) fn finish(self) {
let QueryCompletionActivityTail { activity, dispatcher, delivery, permit } = self;
match dispatcher {
Some(dispatcher) => {
let tail_dispatcher = dispatcher.clone();
dispatcher.dispatch(move || {
if let Some(delivery) = delivery {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| delivery.deliver()));
}
drop(permit);
tail_dispatcher.dispatch_activity_tail(move || QueryCompletion::finish_activity(activity));
});
},
None => {
if let Some(delivery) = delivery {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| delivery.deliver()));
}
drop(permit);
QueryCompletion::finish_activity(activity);
},
}
}
}
impl Drop for QueryCompletion {
fn drop(&mut self) {
if self.attempt_report.is_none()
&& self.attempt_admission.is_none()
&& self.observation_permit.is_none()
&& self.observation_activity.is_none()
&& self.gpu_resource_permit.is_none()
{
return;
}
let fallback =
if std::thread::panicking() { RuntimeTerminalOutcome::Panicked } else { RuntimeTerminalOutcome::Dropped };
let recovered = Self {
attempt_admission: self.attempt_admission.take(),
attempt_report: self.attempt_report.take(),
observation_permit: self.observation_permit.take(),
observation_activity: self.observation_activity.take(),
completion_dispatcher: self.completion_dispatcher.take(),
gpu_resource_permit: self.gpu_resource_permit.take(),
};
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| recovered.finish(fallback)));
}
}
pub(in crate::runtime::query_service) struct PlacedAdmission {
pub(in crate::runtime::query_service) placed_query: PlacedQuery,
pub(in crate::runtime::query_service) admitted_device_requirements: BTreeMap<usize, AttemptCapabilityEvidence>,
pub(in crate::runtime::query_service) submitted_plan_capability_evidence: Option<AttemptCapabilityEvidence>,
pub(in crate::runtime::query_service) admitted_compatible_devices: Vec<usize>,
pub(in crate::runtime::query_service) observation: RuntimeObservationHandle,
pub(in crate::runtime::query_service) attempt_id: AttemptId,
pub(in crate::runtime::query_service) completion: QueryCompletion,
pub(in crate::runtime::query_service) admission_request_id: AdmissionRequestId,
pub(in crate::runtime::query_service) admission_wait: Duration,
pub(in crate::runtime::query_service) execution_cancellation: ExecutionCancellation,
pub(in crate::runtime::query_service) admission_wait_fact: Option<AttemptAdmissionWaitFact>,
pub(in crate::runtime::query_service) lifecycle_ticket: AdmissionTicket,
}
#[derive(Debug)]
pub(super) struct PlacedQuery {
pub(super) device_ordinal: usize,
pub(super) memory_grant: MemoryGrant,
}
impl AdmissionWaiter {
pub(crate) fn try_place(&mut self) -> Result<Option<PlacedAdmission>> {
if let Some(error) = self.terminal.take() {
self.queued = false;
return Err(error);
}
if !self.queued {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.query_service.admission",
"query admission waiter has already completed",
)
.with_fact("admission_request_id", self.admission_request_id)
.with_fact("reason", "admission_waiter_completed"));
}
if self.execution_cancellation.is_cancelled() {
let error = admission_cancellation_or_deadline_error(&self.execution_cancellation);
let removed = match self.controller.state.lock() {
Ok(mut state) => {
let removed = self
.controller
.remove_queued_request_without_admission_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::CancellationRequested,
state.queued.len(),
None,
None,
);
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::Cancelled,
state.queued.len(),
None,
None,
);
}
self.controller.notify_admission_available();
removed
},
Err(error) => {
self.queued = false;
return Err(self
.controller
.recover_queued_request_after_admission_poison(error.into_inner(), self.admission_request_id));
},
};
finish_removed_request_cancelled(removed);
return Err(error);
}
let mut state = match self.controller.state.lock() {
Ok(state) => state,
Err(error) => {
self.queued = false;
return Err(self
.controller
.recover_queued_request_after_admission_poison(error.into_inner(), self.admission_request_id));
},
};
if let Some(error) = self.terminal.take() {
self.queued = false;
return Err(error);
}
if let Err(error) = self.controller.reconcile_native_admission_health() {
eprintln!("[query-engine] warning: native admission-health reconciliation failed: {error}");
}
let device_state_rejection =
match state.queued.iter_mut().find(|request| request.admission_request_id == self.admission_request_id) {
Some(request) => self.controller.revalidate_queued_request_device_state(request),
None => None,
};
if let Some(error) = device_state_rejection {
let removed = self.controller.abandon_queued_request_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
admission_rejection(&error),
state.queued.len(),
None,
None,
);
}
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
}
if let Some(error) = self.controller.admission_terminal_rejection(&state) {
let removed =
self.controller.remove_queued_request_without_admission_locked(&mut state, self.admission_request_id);
self.queued = false;
self.controller.notify_admission_available();
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
admission_rejection(&error),
state.queued.len(),
None,
None,
);
}
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
}
let selected = match self.controller.select_next_runnable_locked(&mut state) {
Ok(selected) => match selected
.filter(|(position, _)| state.queued[*position].admission_request_id == self.admission_request_id)
{
Some(selected) => Ok(Some(selected)),
None => self.controller.fallback_driver_selection_locked(&state, self.admission_request_id),
},
Err(error) => Err(error),
};
let selected = match selected {
Ok(selected) => selected,
Err(error) => {
let removed = self.controller.abandon_queued_request_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
admission_rejection(&error),
state.queued.len(),
None,
None,
);
}
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
},
};
if let Some((position, device_ordinal)) = selected {
let placed_query = match self.controller.try_place_on_device(
device_ordinal,
self.admission_request_id,
state.queued[position].attempt_id,
state.queued[position].requirement_for_device(device_ordinal)?,
state.queued[position].requested_query_cap_bytes,
) {
Ok(placed_query) => placed_query,
Err(error) => {
let removed = self.controller.abandon_queued_request_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
admission_rejection(&error),
state.queued.len(),
None,
None,
);
}
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
},
};
let Some(placed_query) = placed_query else {
if self.deadline.is_some_and(|deadline| Instant::now() >= deadline) {
let requested_query_cap_bytes = state.queued[position].requested_query_cap_bytes;
let removed = self.controller.abandon_queued_request_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::TimedOut,
state.queued.len(),
None,
None,
);
}
let error = self.controller.memory_grant_wait_timeout_error(
device_ordinal,
requested_query_cap_bytes,
self.controller.queue_member_count_locked(&state),
);
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
}
return Ok(None);
};
let admitted = state.remove_queued_at(position).expect("admissible request exists");
let admission_wait_fact = admitted.admission_wait_fact();
if let Some(error) = self.controller.admission_terminal_rejection(&state) {
record_admission_lifecycle(
&admitted.observation,
self.admission_request_id,
admission_rejection(&error),
state.queued.len(),
None,
None,
);
self.queued = false;
self.controller.notify_admission_available();
drop(state);
drop(placed_query);
finish_removed_request_error(Some(admitted), &error);
return Err(error);
}
let lifecycle_ticket = state.lifecycle.begin_provisional(Arc::downgrade(&self.controller));
self.queued = false;
self.controller.notify_admission_available();
drop(state);
return Ok(Some(PlacedAdmission {
placed_query,
admitted_device_requirements: admitted
.device_entries
.iter()
.map(|(&ordinal, requirement)| (ordinal, requirement.clone()))
.collect(),
submitted_plan_capability_evidence: admitted.submitted_plan_capability_evidence.clone(),
admitted_compatible_devices: admitted.compatible_devices.clone(),
observation: admitted.observation,
attempt_id: admitted.attempt_id,
completion: admitted.completion,
admission_request_id: self.admission_request_id,
admission_wait: self.requested_at.elapsed(),
execution_cancellation: admitted.execution_cancellation,
admission_wait_fact: Some(admission_wait_fact),
lifecycle_ticket,
}));
}
if self.deadline.is_some_and(|deadline| Instant::now() >= deadline) {
let removed = self.controller.abandon_queued_request_locked(&mut state, self.admission_request_id);
self.queued = false;
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::TimedOut,
state.queued.len(),
None,
None,
);
}
let error = service_overloaded_error(
"queue_wait_elapsed",
self.controller.queue_member_count_locked(&state),
self.controller.queue_bounds.max_waiters,
);
drop(state);
finish_removed_request_error(removed, &error);
return Err(error);
}
Ok(None)
}
pub(crate) fn remaining_wait(&self) -> Option<Duration> {
self.deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()))
}
pub(crate) async fn place_async(&mut self) -> Result<PlacedAdmission> {
let controller = Arc::clone(&self.controller);
loop {
let notified = controller.admission_notify.notified();
tokio::pin!(notified);
notified.as_mut().enable();
if let Some(placed) = self.try_place()? {
return Ok(placed);
}
let wake = async {
tokio::select! {
() = &mut notified => {},
() = self.execution_cancellation.cancelled_or_expired() => {},
}
};
match self.remaining_wait() {
Some(remaining_wait) if remaining_wait.is_zero() => continue,
Some(remaining_wait) => {
let _ = time::timeout(remaining_wait, wake).await;
},
None => wake.await,
}
}
}
#[cfg(any(test, feature = "test-hooks"))]
pub(crate) fn place_blocking(&mut self) -> Result<PlacedAdmission> {
let controller = Arc::clone(&self.controller);
loop {
let notified = controller.admission_notify.notified();
let mut notified = pin!(notified);
notified.as_mut().enable();
if let Some(placed) = self.try_place()? {
return Ok(placed);
}
let wake_deadline = match (self.execution_cancellation.deadline(), self.deadline) {
(Some(attempt), Some(queue)) => Some(attempt.min(queue)),
(attempt, queue) => attempt.or(queue),
};
let _ = block_until(
async {
tokio::select! {
biased;
() = notified.as_mut() => {},
() = self.execution_cancellation.cancel_requested() => {},
}
},
wake_deadline,
);
}
}
}
#[cfg(any(test, feature = "test-hooks"))]
impl QueryAdmissionWaiter {
pub(crate) fn try_admit(&mut self) -> Result<Option<QueryHandle>> {
let Some(placed) = self.waiter.try_place()? else {
return Ok(None);
};
let handle = self.service.commit_placed_admission(placed, self.probe.clone())?;
handle.own_test_attempt(self.test_attempt.take());
Ok(Some(handle))
}
pub(crate) fn admit_blocking(mut self) -> Result<QueryHandle> {
let placed = self.waiter.place_blocking()?;
let handle = self.service.commit_placed_admission(placed, self.probe.take())?;
handle.own_test_attempt(self.test_attempt.take());
Ok(handle)
}
}
impl Drop for AdmissionWaiter {
fn drop(&mut self) {
if !self.queued {
return;
}
let removed = match self.controller.state.lock() {
Ok(mut state) => {
let removed = self
.controller
.remove_queued_request_without_admission_locked(&mut state, self.admission_request_id);
self.controller.notify_admission_available();
if removed.is_some() {
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::CancellationRequested,
state.queued.len(),
None,
None,
);
record_admission_lifecycle(
&self.observation,
self.admission_request_id,
AdmissionLifecycleState::Cancelled,
state.queued.len(),
None,
None,
);
}
removed
},
Err(error) => {
self.queued = false;
self.controller
.recover_queued_request_after_admission_poison(error.into_inner(), self.admission_request_id);
return;
},
};
finish_removed_request_cancelled(removed);
}
}
pub(super) fn remove_queued_request(
state: &mut AdmissionState,
admission_request_id: AdmissionRequestId,
) -> Option<AdmissionRequest> {
if let Some(position) = state.queued.iter().position(|request| request.admission_request_id == admission_request_id)
{
state.remove_queued_at(position)
} else {
None
}
}
pub(super) fn finish_removed_request_error(request: Option<AdmissionRequest>, error: &QueryError) {
if let Some(request) = request {
request.completion.finish_error(error);
}
}
pub(super) fn admission_cancellation_error() -> QueryError {
QueryError::new(
QueryErrorCode::Cancelled,
"query_engine.query_service.admission",
"query admission was cancelled before a complete envelope was published",
)
.with_fact("reason", "admission_cancelled")
.with_fact("cancellation_requested", true)
}
pub(super) fn admission_cancellation_or_deadline_error(cancellation: &ExecutionCancellation) -> QueryError {
match cancellation.check("query_engine.query_service.admission") {
Err(error) if error.fact_value("reason") == Some("deadline_expired") => error,
_ => admission_cancellation_error(),
}
}
pub(super) fn finish_removed_request_cancelled(request: Option<AdmissionRequest>) {
if let Some(request) = request {
request.completion.finish_cancelled();
}
}
pub(super) fn finish_expired_queued_requests(expired: Vec<(AdmissionRequest, RuntimeTerminalOutcome)>) {
for (request, disposition) in expired {
let primary_failure = request.terminal.take_primary_failure();
request.completion.finish_terminal_disposition(disposition, primary_failure);
}
}
pub(super) fn finish_closed_queued_requests(closed: Vec<AdmissionRequest>) {
for request in closed {
let error = service_closed_error();
record_admission_lifecycle(
&request.observation,
request.admission_request_id,
AdmissionLifecycleState::Rejected { reason: "admission_closed".to_string() },
0,
None,
None,
);
finish_removed_request_error(Some(request), &error);
}
}
pub(super) fn service_overloaded_error(reason: &'static str, queued_queries: usize, max_waiters: usize) -> QueryError {
QueryError::new(
QueryErrorCode::ServiceOverloaded,
"query_engine.query_service.admission",
"query service admission is overloaded",
)
.with_fact("reason", reason)
.with_fact("queued_queries", queued_queries)
.with_fact("max_waiters", max_waiters)
}
pub(super) fn admission_queue_full_error(
queued_queries: usize,
max_waiters: usize,
pending_preflight: usize,
) -> QueryError {
service_overloaded_error("admission_queue_full", queued_queries, max_waiters)
.with_fact("pending_preflight", pending_preflight)
}
pub(super) fn service_closed_error() -> QueryError {
QueryError::new(
QueryErrorCode::Cancelled,
"query_engine.query_service.admission",
"query service admission is closed",
)
.with_fact("reason", "admission_closed")
}
#[cfg(test)]
mod tests {
use std::{sync::Arc, time::Duration};
use query_planning::error::AttemptFailureCode;
use super::*;
use crate::{
attempt::{AttemptBackendCounters, AttemptOwner, BeginAttemptRequest},
observability::{
AttemptDisposition, AttemptFailureFactKey, AttemptFailureFactValue, AttemptFailureOperation,
RuntimeObservationConfig,
},
runtime::BackendLifecycle,
};
#[test]
fn a_ticket_leaves_a_poisoned_lifecycle_tracker() {
let tracker = AdmissionLifecycleTracker::default();
let ticket = tracker.begin_provisional(Weak::new());
let poisoner = tracker.clone();
std::thread::spawn(move || {
let _state = poisoner.state.lock().unwrap();
panic!("poison the lifecycle tracker");
})
.join()
.expect_err("the poisoning thread panics while holding the tracker");
drop(ticket);
assert!(tracker.is_quiescent(), "the dropped ticket leaves the lifecycle drain fence");
}
#[test]
fn dropping_provisional_admission_ticket_restores_quiescence() {
let tracker = AdmissionLifecycleTracker::default();
let ticket = tracker.begin_provisional(Weak::new());
assert_eq!(tracker.snapshot().provisional, 1);
drop(ticket);
assert!(tracker.is_quiescent());
}
#[test]
fn dropping_active_admission_ticket_restores_quiescence() {
let tracker = AdmissionLifecycleTracker::default();
let mut ticket = tracker.begin_provisional(Weak::new());
ticket.transition_to_active().expect("provisional ticket activates");
assert_eq!(tracker.snapshot().active, 1);
drop(ticket);
assert!(tracker.is_quiescent());
}
#[test]
fn queued_admission_terminal_preserves_borrowed_primary_failure_after_waiter_consumes_error() {
let backend_lifecycle = BackendLifecycle::new();
let attempt = AttemptOwner::begin(
BeginAttemptRequest {
observation: RuntimeObservationConfig::bounded(8, 8 * 1024),
..BeginAttemptRequest::default()
},
backend_lifecycle.try_begin_observation().expect("attempt observation permit"),
Arc::new(AttemptBackendCounters::default()),
None,
);
let owner = attempt.owner;
let completion = attempt.completion.completion();
owner.handle().set_disposition(AttemptDisposition::Native).unwrap();
let binding = owner.admission_binding();
let admission = binding.claim().unwrap();
let query_completion = QueryCompletion::new(admission, None, None, None, None);
let error = QueryError::new(
QueryErrorCode::MemoryReservationFailed,
"query_engine.query_service.admission.test",
"admission failure with retained primary evidence",
)
.with_fact("reason", "backend_closing");
let queued_terminal = QueuedAdmissionTerminal::default();
let disposition = queued_terminal.record(error);
let waiter_error = queued_terminal.take().expect("queue waiter receives its full terminal error first");
assert_eq!(waiter_error.fact_value("reason"), Some("backend_closing"));
query_completion.finish_terminal_disposition(disposition, queued_terminal.take_primary_failure());
let report = completion.wait_timeout(Duration::from_secs(1)).expect("attempt report publishes");
let summary = report.primary_failure.as_ref().expect("borrowed primary failure is retained");
assert_eq!(summary.code(), AttemptFailureCode::Query(QueryErrorCode::MemoryReservationFailed));
assert_eq!(summary.operation(), AttemptFailureOperation::Admission);
assert_eq!(
summary.facts().iter().map(|fact| (fact.key(), fact.value())).collect::<Vec<_>>(),
vec![(AttemptFailureFactKey::Reason, AttemptFailureFactValue::BackendClosing)]
);
}
}