1use std::collections::BTreeMap;
11use std::sync::{Arc, Weak};
12
13use differential_dataflow::consolidation::consolidate;
14use mz_compute_client::controller::error::{CollectionMissing, InstanceMissing};
15use mz_compute_client::controller::instance_client::InstanceClient;
16use mz_compute_client::controller::instance_client::{AcquireReadHoldsError, InstanceShutDown};
17use mz_compute_client::protocol::command::PeekTarget;
18use mz_compute_types::ComputeInstanceId;
19use mz_expr::row::RowCollection;
20use mz_ore::cast::CastFrom;
21use mz_ore::soft_panic_or_log;
22use mz_persist_client::PersistClient;
23use mz_repr::GlobalId;
24use mz_repr::Timestamp;
25use mz_repr::global_id::TransientIdGen;
26use mz_repr::{RelationDesc, Row};
27use mz_sql::ast::{Raw, Statement};
28use mz_sql::optimizer_metrics::OptimizerMetrics;
29use mz_sql::plan::Params;
30use mz_sql::session::hint::ApplicationNameHint;
31use mz_sql::session::metadata::SessionMetadata;
32use mz_sql_parser::ast::{CopyRelation, CopyStatement, SubscribeStatement};
33use mz_storage_types::sources::Timeline;
34use mz_timestamp_oracle::TimestampOracle;
35use prometheus::Histogram;
36use qcell::QCell;
37use thiserror::Error;
38use timely::progress::Antichain;
39use tokio::sync::{Semaphore, oneshot};
40use uuid::Uuid;
41
42use crate::catalog::Catalog;
43use crate::command::{CatalogSnapshot, Command, ExecuteResponse};
44use crate::coord::appends::GroupCommitNotifier;
45use crate::coord::peek::FastPathPlan;
46use crate::coord::timestamp_selection::TimestampDetermination;
47use crate::coord::{Coordinator, ExecuteContextExtra, ExecuteContextGuard, Message};
48use crate::metrics::Metrics;
49use crate::session::{LifecycleTimestamps, Session};
50use crate::statement_logging::{
51 FrontendStatementLoggingEvent, PreparedStatementEvent, PreparedStatementLoggingInfo,
52 StatementLoggingFrontend, StatementLoggingId, WatchSetCreation,
53};
54use crate::{
55 AdapterError, AdapterNotice, Client, CollectionIdBundle, ReadHolds, metrics, statement_logging,
56};
57
58pub type StorageCollectionsHandle =
60 Arc<dyn mz_storage_client::storage_collections::StorageCollections + Send + Sync>;
61
62#[derive(Debug, Clone)]
64pub struct PeekClient {
65 coordinator_client: CoordinatorClient,
66 catalog_cache: Weak<Catalog>,
73 compute_instances: BTreeMap<ComputeInstanceId, InstanceClient>,
78 pub storage_collections: StorageCollectionsHandle,
80 pub transient_id_gen: Arc<TransientIdGen>,
82 pub optimizer_metrics: OptimizerMetrics,
83 oracles: BTreeMap<Timeline, Arc<dyn TimestampOracle<Timestamp> + Send + Sync>>,
85 persist_client: PersistClient,
86 pub statement_logging_frontend: StatementLoggingFrontend,
88 pub occ_write_semaphore: Arc<Semaphore>,
90 pub(crate) group_commit_notifier: GroupCommitNotifier,
93 pub read_only: bool,
95}
96
97#[derive(Debug, Clone)]
99pub(crate) enum CoordinatorClient {
100 Session(Client),
101 Background {
102 tx: tokio::sync::mpsc::UnboundedSender<Message>,
103 metrics: Metrics,
104 },
105}
106
107impl CoordinatorClient {
108 pub(crate) fn send(&self, command: Command) {
117 if self.try_send(command) {
118 return;
119 }
120 match self {
121 CoordinatorClient::Session(_) => panic!("coordinator unexpectedly gone"),
122 CoordinatorClient::Background { .. } => {
123 tracing::debug!("dropping background command, coordinator is gone")
124 }
125 }
126 }
127
128 pub(crate) fn try_send(&self, command: Command) -> bool {
129 match self {
130 CoordinatorClient::Session(client) => client.try_send(command),
131 CoordinatorClient::Background { tx, .. } => tx
132 .send(Message::Command(
133 mz_ore::tracing::OpenTelemetryContext::obtain(),
134 command,
135 ))
136 .is_ok(),
137 }
138 }
139
140 pub(crate) fn metrics(&self) -> &Metrics {
141 match self {
142 CoordinatorClient::Session(client) => client.metrics(),
143 CoordinatorClient::Background { metrics, .. } => metrics,
144 }
145 }
146}
147
148impl PeekClient {
149 #[allow(clippy::too_many_arguments)]
154 pub(crate) fn new(
155 coordinator_client: CoordinatorClient,
156 catalog: &Arc<Catalog>,
157 storage_collections: StorageCollectionsHandle,
158 transient_id_gen: Arc<TransientIdGen>,
159 optimizer_metrics: OptimizerMetrics,
160 persist_client: PersistClient,
161 statement_logging_frontend: StatementLoggingFrontend,
162 occ_write_semaphore: Arc<Semaphore>,
163 group_commit_notifier: GroupCommitNotifier,
164 read_only: bool,
165 ) -> Self {
166 Self {
167 coordinator_client,
168 catalog_cache: Arc::downgrade(catalog),
169 compute_instances: Default::default(), storage_collections,
171 transient_id_gen,
172 optimizer_metrics,
173 statement_logging_frontend,
174 oracles: Default::default(), persist_client,
176 occ_write_semaphore,
177 group_commit_notifier,
178 read_only,
179 }
180 }
181
182 pub async fn ensure_compute_instance_client(
183 &mut self,
184 compute_instance: ComputeInstanceId,
185 ) -> Result<InstanceClient, CollectionLookupError> {
186 if !self.compute_instances.contains_key(&compute_instance) {
187 let client = self
188 .call_coordinator(|tx| Command::GetComputeInstanceClient {
189 instance_id: compute_instance,
190 tx,
191 })
192 .await
193 .map_err(|_| CollectionLookupError::InstanceShutDown)??;
194 self.compute_instances.insert(compute_instance, client);
195 }
196 Ok(self
197 .compute_instances
198 .get(&compute_instance)
199 .expect("ensured above")
200 .clone())
201 }
202
203 pub async fn ensure_oracle(
204 &mut self,
205 timeline: Timeline,
206 ) -> Result<&mut Arc<dyn TimestampOracle<Timestamp> + Send + Sync>, AdapterError> {
207 if !self.oracles.contains_key(&timeline) {
208 let oracle = self
209 .call_coordinator(|tx| Command::GetOracle {
210 timeline: timeline.clone(),
211 tx,
212 })
213 .await??;
214 self.oracles.insert(timeline.clone(), oracle);
215 }
216 Ok(self.oracles.get_mut(&timeline).expect("ensured above"))
217 }
218
219 pub async fn catalog_snapshot(&mut self, context: &str) -> Arc<Catalog> {
232 let cached = self
238 .catalog_cache
239 .upgrade()
240 .filter(|catalog| catalog.transient_revision_is_current());
241 if let Some(catalog) = cached {
242 self.coordinator_client
243 .metrics()
244 .catalog_snapshot_cache
245 .with_label_values(&[context, "hit"])
246 .inc();
247 return catalog;
248 }
249
250 let start = std::time::Instant::now();
253 let CatalogSnapshot { catalog } = self
257 .call_coordinator(|tx| Command::CatalogSnapshot { tx })
258 .await
259 .expect("coordinator unexpectedly dropped catalog snapshot response");
260 let metrics = self.coordinator_client.metrics();
261 metrics
262 .catalog_snapshot_seconds
263 .with_label_values(&[context])
264 .observe(start.elapsed().as_secs_f64());
265 metrics
266 .catalog_snapshot_cache
267 .with_label_values(&[context, "miss"])
268 .inc();
269 self.catalog_cache = Arc::downgrade(&catalog);
270 catalog
271 }
272
273 pub(crate) async fn call_coordinator<T, F>(&self, f: F) -> Result<T, AdapterError>
275 where
276 F: FnOnce(oneshot::Sender<T>) -> Command,
277 {
278 let (tx, rx) = oneshot::channel();
279 self.coordinator_client.send(f(tx));
280 Ok(rx.await?)
281 }
282
283 pub(crate) async fn add_timestamp_notice(
290 &self,
291 session: &Session,
292 cluster_id: ComputeInstanceId,
293 id_bundle: CollectionIdBundle,
294 determination: TimestampDetermination,
295 ) -> Result<(), AdapterError> {
296 fail::fail_point!("timestamp_notice_before_dispatch");
300 let request = self.call_coordinator(|tx| Command::ExplainTimestamp {
301 conn_id: session.conn_id().clone(),
302 session_wall_time: session.pcx().wall_time,
303 cluster_id,
304 id_bundle,
305 determination,
306 tx,
307 });
308 match request.await? {
309 Ok(explanation) => session.add_notice(AdapterNotice::QueryTimestamp { explanation }),
310 Err(AdapterError::ConcurrentDependencyDrop { .. }) => {}
311 Err(e) => return Err(e),
312 }
313 Ok(())
314 }
315
316 pub(crate) fn coordinator_client(&self) -> &CoordinatorClient {
318 &self.coordinator_client
319 }
320
321 pub async fn acquire_read_holds_and_least_valid_write(
334 &mut self,
335 id_bundle: &CollectionIdBundle,
336 ) -> Result<(ReadHolds, Antichain<Timestamp>), CollectionLookupError> {
337 let mut read_holds = ReadHolds::new();
338 let mut upper = Antichain::new();
339
340 if !id_bundle.storage_ids.is_empty() {
341 let desired_storage: Vec<_> = id_bundle.storage_ids.iter().copied().collect();
342 let storage_read_holds = self
343 .storage_collections
344 .acquire_read_holds(desired_storage)?;
345 read_holds.storage_holds = storage_read_holds
346 .into_iter()
347 .map(|hold| (hold.id(), hold))
348 .collect();
349
350 let storage_ids: Vec<_> = id_bundle.storage_ids.iter().copied().collect();
351 for f in self
352 .storage_collections
353 .collections_frontiers(storage_ids)?
354 {
355 upper.extend(f.write_frontier);
356 }
357 }
358
359 for (&instance_id, collection_ids) in &id_bundle.compute_ids {
360 let client = self.ensure_compute_instance_client(instance_id).await?;
361
362 for (id, read_hold, write_frontier) in client
363 .acquire_read_holds_and_collection_write_frontiers(
364 collection_ids.iter().copied().collect(),
365 )
366 .await?
367 {
368 let prev = read_holds
369 .compute_holds
370 .insert((instance_id, id), read_hold);
371 assert!(
372 prev.is_none(),
373 "duplicate compute ID in id_bundle {id_bundle:?}"
374 );
375
376 upper.extend(write_frontier);
377 }
378 }
379
380 Ok((read_holds, upper))
381 }
382
383 pub(crate) async fn implement_fast_path_peek_plan(
398 &mut self,
399 fast_path: FastPathPlan,
400 timestamp: Timestamp,
401 finishing: mz_expr::RowSetFinishing,
402 compute_instance: ComputeInstanceId,
403 target_replica: Option<mz_cluster_client::ReplicaId>,
404 intermediate_result_type: mz_repr::SqlRelationType,
405 max_result_size: u64,
406 max_returned_query_size: Option<u64>,
407 row_set_finishing_seconds: Histogram,
408 input_read_holds: ReadHolds,
409 peek_stash_read_batch_size_bytes: usize,
410 peek_stash_read_memory_budget_bytes: usize,
411 conn_id: mz_adapter_types::connection::ConnectionId,
412 depends_on: std::collections::BTreeSet<mz_repr::GlobalId>,
413 watch_set: Option<WatchSetCreation>,
414 logging: &mut ExecutionLogging,
415 ) -> Result<crate::ExecuteResponse, AdapterError> {
416 if let FastPathPlan::Constant(rows_res, _) = fast_path {
418 if let Some(ref ws) = watch_set {
421 self.log_lifecycle_event(
422 ws.logging_id,
423 statement_logging::StatementLifecycleEvent::StorageDependenciesFinished,
424 );
425 self.log_lifecycle_event(
426 ws.logging_id,
427 statement_logging::StatementLifecycleEvent::ComputeDependenciesFinished,
428 );
429 }
430
431 let mut rows = match rows_res {
432 Ok(rows) => rows,
433 Err(e) => return Err(e.into()),
434 };
435 consolidate(&mut rows);
436
437 let mut results = Vec::new();
438 for (row, count) in rows {
439 let count = match u64::try_from(count.into_inner()) {
440 Ok(u) => usize::cast_from(u),
441 Err(_) => {
442 return Err(AdapterError::Unstructured(anyhow::anyhow!(
443 "Negative multiplicity in constant result: {}",
444 count
445 )));
446 }
447 };
448 match std::num::NonZeroUsize::new(count) {
449 Some(nzu) => {
450 results.push((row, nzu));
451 }
452 None => {
453 }
455 };
456 }
457 let row_collection = RowCollection::new(results, &finishing.order_by);
458 return match finishing.finish(
459 row_collection,
460 max_result_size,
461 max_returned_query_size,
462 &row_set_finishing_seconds,
463 ) {
464 Ok((rows, _bytes)) => Ok(Coordinator::send_immediate_rows(rows)),
465 Err(e) => Err(AdapterError::ResultSize(e)),
467 };
468 }
469
470 let (peek_target, target_read_hold, literal_constraints, mfp, strategy) = match fast_path {
471 FastPathPlan::PeekExisting(_coll_id, idx_id, literal_constraints, mfp) => {
472 let peek_target = PeekTarget::Index { id: idx_id };
473 let target_read_hold = input_read_holds
474 .compute_holds
475 .get(&(compute_instance, idx_id))
476 .expect("missing compute read hold on PeekExisting peek target")
477 .clone();
478 let strategy = statement_logging::StatementExecutionStrategy::FastPath;
479 (
480 peek_target,
481 target_read_hold,
482 literal_constraints,
483 mfp,
484 strategy,
485 )
486 }
487 FastPathPlan::PeekPersist(coll_id, literal_constraint, mfp) => {
488 let literal_constraints = literal_constraint.map(|r| vec![r]);
489 let metadata = self
490 .storage_collections
491 .collection_metadata(coll_id)
492 .map_err(AdapterError::concurrent_dependency_drop_from_collection_missing)?
493 .clone();
494 let peek_target = PeekTarget::Persist {
495 id: coll_id,
496 metadata,
497 };
498 let target_read_hold = input_read_holds
499 .storage_holds
500 .get(&coll_id)
501 .expect("missing storage read hold on PeekPersist peek target")
502 .clone();
503 let strategy = statement_logging::StatementExecutionStrategy::PersistFastPath;
504 (
505 peek_target,
506 target_read_hold,
507 literal_constraints,
508 mfp,
509 strategy,
510 )
511 }
512 FastPathPlan::Constant(..) => {
513 unreachable!()
515 }
516 };
517
518 let (rows_tx, rows_rx) = oneshot::channel();
519 let uuid = Uuid::new_v4();
520
521 let cols = (0..intermediate_result_type.arity()).map(|i| format!("peek_{i}"));
524 let result_desc = RelationDesc::new(intermediate_result_type.clone(), cols);
525
526 let client = self
527 .ensure_compute_instance_client(compute_instance)
528 .await
529 .map_err(|error| {
530 AdapterError::concurrent_dependency_drop_from_collection_lookup_error(
531 error,
532 compute_instance,
533 )
534 })?;
535
536 self.call_coordinator(|tx| Command::RegisterFrontendPeek {
541 uuid,
542 conn_id: conn_id.clone(),
543 cluster_id: compute_instance,
544 depends_on,
545 is_fast_path: true,
546 watch_set,
547 tx,
548 })
549 .await??;
550
551 logging.defuse();
557
558 fail::fail_point!("peek_after_register_before_issue");
563
564 let finishing_for_instance = finishing.clone();
565 let peek_result = client
566 .peek(
567 peek_target,
568 literal_constraints,
569 uuid,
570 timestamp,
571 result_desc,
572 finishing_for_instance,
573 mfp,
574 target_read_hold,
575 target_replica,
576 rows_tx,
577 )
578 .await;
579
580 if let Err(err) = peek_result {
581 let err = AdapterError::concurrent_dependency_drop_from_instance_peek_error(
582 err,
583 compute_instance,
584 );
585 let _ = self
591 .call_coordinator(|tx| Command::UnregisterFrontendPeek {
592 uuid,
593 reason: statement_logging::StatementEndedExecutionReason::Errored {
594 error: err.to_string(),
595 },
596 tx,
597 })
598 .await;
599 return Err(err);
600 }
601
602 let peek_response_stream = Coordinator::create_peek_response_stream(
603 rows_rx,
604 finishing,
605 max_result_size,
606 max_returned_query_size,
607 row_set_finishing_seconds,
608 self.persist_client.clone(),
609 peek_stash_read_batch_size_bytes,
610 peek_stash_read_memory_budget_bytes,
611 );
612
613 Ok(crate::ExecuteResponse::SendingRowsStreaming {
614 rows: Box::pin(peek_response_stream),
615 instance_id: compute_instance,
616 strategy,
617 })
618 }
619
620 fn begin_statement_logging(
625 &self,
626 session: &mut Session,
627 params: &Params,
628 logging: &Arc<QCell<PreparedStatementLoggingInfo>>,
629 catalog: &Catalog,
630 lifecycle_timestamps: Option<LifecycleTimestamps>,
631 ) -> StatementLoggingGuard {
632 let result = self.statement_logging_frontend.begin_statement_execution(
633 session,
634 params,
635 logging,
636 catalog.system_config(),
637 lifecycle_timestamps,
638 );
639
640 let id = result.map(
641 |(logging_id, began_execution, mseh_update, prepared_statement)| {
642 self.log_began_execution(began_execution, mseh_update, prepared_statement);
643 logging_id
644 },
645 );
646
647 StatementLoggingGuard {
648 id,
649 coordinator_client: self.coordinator_client.clone(),
650 now: self.statement_logging_frontend.now.clone(),
651 }
652 }
653
654 pub(crate) fn log_began_execution(
656 &self,
657 record: statement_logging::StatementBeganExecutionRecord,
658 mseh_update: Row,
659 prepared_statement: Option<PreparedStatementEvent>,
660 ) {
661 self.coordinator_client
662 .send(Command::FrontendStatementLogging(
663 FrontendStatementLoggingEvent::BeganExecution {
664 record,
665 mseh_update,
666 prepared_statement,
667 },
668 ));
669 }
670
671 pub(crate) fn log_set_cluster(
673 &self,
674 id: StatementLoggingId,
675 cluster_id: mz_controller_types::ClusterId,
676 cluster_name: String,
677 ) {
678 self.coordinator_client
679 .send(Command::FrontendStatementLogging(
680 FrontendStatementLoggingEvent::SetCluster {
681 id,
682 cluster_id,
683 cluster_name,
684 },
685 ));
686 }
687
688 pub(crate) fn log_set_timestamp(&self, id: StatementLoggingId, timestamp: mz_repr::Timestamp) {
690 self.coordinator_client
691 .send(Command::FrontendStatementLogging(
692 FrontendStatementLoggingEvent::SetTimestamp { id, timestamp },
693 ));
694 }
695
696 pub(crate) fn log_set_transient_index_id(
698 &self,
699 id: StatementLoggingId,
700 transient_index_id: mz_repr::GlobalId,
701 ) {
702 self.coordinator_client
703 .send(Command::FrontendStatementLogging(
704 FrontendStatementLoggingEvent::SetTransientIndex {
705 id,
706 transient_index_id,
707 },
708 ));
709 }
710
711 pub(crate) fn log_lifecycle_event(
713 &self,
714 id: StatementLoggingId,
715 event: statement_logging::StatementLifecycleEvent,
716 ) {
717 let when = (self.statement_logging_frontend.now)();
718 self.coordinator_client
719 .send(Command::FrontendStatementLogging(
720 FrontendStatementLoggingEvent::Lifecycle { id, event, when },
721 ));
722 }
723}
724
725#[must_use = "StatementLoggingGuard must be explicitly retired or handed off; \
741 otherwise `Drop` will log the statement as Aborted"]
742struct StatementLoggingGuard {
743 id: Option<StatementLoggingId>,
745 coordinator_client: CoordinatorClient,
746 now: mz_ore::now::NowFn,
747}
748
749impl StatementLoggingGuard {
750 fn adopt(outer: ExecuteContextGuard, peek_client: &PeekClient) -> Self {
753 Self {
754 id: outer.defuse().retire(),
755 coordinator_client: peek_client.coordinator_client.clone(),
756 now: peek_client.statement_logging_frontend.now.clone(),
757 }
758 }
759
760 fn id(&self) -> Option<StatementLoggingId> {
762 self.id
763 }
764
765 fn retire(mut self, reason: statement_logging::StatementEndedExecutionReason) {
768 self.emit(reason);
769 }
770
771 fn release(mut self) -> ExecuteContextExtra {
774 ExecuteContextExtra::new(self.id.take())
775 }
776
777 fn defuse(&mut self) {
781 self.id = None;
782 }
783
784 fn emit(&mut self, reason: statement_logging::StatementEndedExecutionReason) {
785 let Some(id) = self.id.take() else {
786 return;
787 };
788 let ended_at = (self.now)();
789 let record = statement_logging::StatementEndedExecutionRecord {
790 id: id.0,
791 reason,
792 ended_at,
793 };
794 let _ = self
798 .coordinator_client
799 .try_send(Command::FrontendStatementLogging(
800 FrontendStatementLoggingEvent::EndedExecution(record),
801 ));
802 }
803}
804
805impl Drop for StatementLoggingGuard {
806 fn drop(&mut self) {
807 self.emit(statement_logging::StatementEndedExecutionReason::Aborted);
810 }
811}
812
813pub(crate) struct ExecutionLogging {
824 guard: Option<StatementLoggingGuard>,
825 coordinator_must_not_run: bool,
829}
830
831pub(crate) enum TakeOver {
833 StatementToRun,
835 UnrolledExecute,
844}
845
846impl ExecutionLogging {
847 pub(crate) fn adopt(outer: Option<ExecuteContextGuard>, peek_client: &PeekClient) -> Self {
851 Self {
852 guard: outer.map(|outer| StatementLoggingGuard::adopt(outer, peek_client)),
853 coordinator_must_not_run: false,
854 }
855 }
856
857 pub(crate) fn id(&self) -> Option<StatementLoggingId> {
860 self.guard.as_ref().and_then(|guard| guard.id())
861 }
862
863 pub(crate) fn take_over(
872 &mut self,
873 peek_client: &PeekClient,
874 session: &mut Session,
875 stmt: Option<&Statement<Raw>>,
876 params: &Params,
877 logging: &Arc<QCell<PreparedStatementLoggingInfo>>,
878 catalog: &Catalog,
879 lifecycle_timestamps: Option<LifecycleTimestamps>,
880 taking_over: TakeOver,
881 ) -> Option<StatementLoggingId> {
882 self.begin_or_inherit(
883 peek_client,
884 session,
885 params,
886 logging,
887 catalog,
888 lifecycle_timestamps,
889 );
890 count_statement(session, stmt);
891 if matches!(taking_over, TakeOver::StatementToRun) {
892 self.coordinator_must_not_run = true;
893 }
894 self.id()
895 }
896
897 fn begin_or_inherit(
900 &mut self,
901 peek_client: &PeekClient,
902 session: &mut Session,
903 params: &Params,
904 logging: &Arc<QCell<PreparedStatementLoggingInfo>>,
905 catalog: &Catalog,
906 lifecycle_timestamps: Option<LifecycleTimestamps>,
907 ) {
908 if self.guard.is_none() {
909 self.guard = Some(peek_client.begin_statement_logging(
910 session,
911 params,
912 logging,
913 catalog,
914 lifecycle_timestamps,
915 ));
916 }
917 }
918
919 #[must_use]
924 pub(crate) fn release(&mut self) -> Option<ExecuteContextExtra> {
925 if self.coordinator_must_not_run {
926 soft_panic_or_log!(
927 "statement handed to the coordinator after the session task took it over: \
928 its per-statement metrics are counted twice"
929 );
930 }
931 self.guard.take().map(|guard| guard.release())
932 }
933
934 pub(crate) fn retire(self, result: &Result<ExecuteResponse, AdapterError>) {
936 let Some(guard) = self.guard else {
937 return;
938 };
939 if guard.id().is_none() {
943 return;
944 }
945 guard.retire(end_reason(result));
946 }
947
948 pub(crate) fn defuse(&mut self) {
951 if let Some(guard) = self.guard.as_mut() {
952 guard.defuse();
953 }
954 }
955}
956
957fn end_reason(
964 result: &Result<ExecuteResponse, AdapterError>,
965) -> statement_logging::StatementEndedExecutionReason {
966 if let Ok(response) = result {
967 if terminates_elsewhere(response) {
968 soft_panic_or_log!(
969 "frontend-sequenced statement still owed an end event while returning {:?}",
970 crate::command::ExecuteResponseKind::from(response)
971 );
972 return statement_logging::StatementEndedExecutionReason::Aborted;
973 }
974 }
975 result.into()
976}
977
978fn count_statement(session: &Session, stmt: Option<&Statement<Raw>>) {
984 let Some(stmt) = stmt else {
985 return;
986 };
987 let session_type = metrics::session_type_label_value(session.user());
988 session
989 .metrics()
990 .query_total(&[session_type, metrics::statement_type_label_value(stmt)])
991 .inc();
992 if let Statement::Subscribe(SubscribeStatement { output, .. })
993 | Statement::Copy(CopyStatement {
994 relation: CopyRelation::Subscribe(SubscribeStatement { output, .. }),
995 ..
996 }) = stmt
997 {
998 session
999 .metrics()
1000 .subscribe_outputs(&[session_type, metrics::subscribe_output_label_value(output)])
1001 .inc();
1002 }
1003 let application_name = ApplicationNameHint::from_str(session.application_name());
1004 if let Some(labels) = metrics::as_of_query_label_values(session_type, application_name, stmt) {
1005 session.metrics().as_of_queries(&labels).inc();
1006 }
1007}
1008
1009fn terminates_elsewhere(response: &ExecuteResponse) -> bool {
1014 match response {
1015 ExecuteResponse::SendingRowsStreaming { .. }
1016 | ExecuteResponse::Subscribing { .. }
1017 | ExecuteResponse::Fetch { .. }
1018 | ExecuteResponse::CopyFrom { .. } => true,
1019 ExecuteResponse::CopyTo { resp, .. } => {
1022 !matches!(**resp, ExecuteResponse::SendingRowsImmediate { .. })
1023 }
1024 _ => false,
1025 }
1026}
1027
1028#[derive(Error, Debug)]
1030pub enum CollectionLookupError {
1031 #[error("instance does not exist: {0}")]
1033 InstanceMissing(ComputeInstanceId),
1034 #[error("the instance has shut down")]
1036 InstanceShutDown,
1037 #[error("collection does not exist: {0}")]
1039 CollectionMissing(GlobalId),
1040}
1041
1042impl From<InstanceMissing> for CollectionLookupError {
1043 fn from(error: InstanceMissing) -> Self {
1044 Self::InstanceMissing(error.0)
1045 }
1046}
1047
1048impl From<InstanceShutDown> for CollectionLookupError {
1049 fn from(_error: InstanceShutDown) -> Self {
1050 Self::InstanceShutDown
1051 }
1052}
1053
1054impl From<CollectionMissing> for CollectionLookupError {
1055 fn from(error: CollectionMissing) -> Self {
1056 Self::CollectionMissing(error.0)
1057 }
1058}
1059
1060impl From<AcquireReadHoldsError> for CollectionLookupError {
1061 fn from(error: AcquireReadHoldsError) -> Self {
1062 match error {
1063 AcquireReadHoldsError::CollectionMissing(id) => Self::CollectionMissing(id),
1064 AcquireReadHoldsError::InstanceShutDown => Self::InstanceShutDown,
1065 }
1066 }
1067}