mz_adapter/coord/sequencer/inner/
subscribe.rs1use futures::StreamExt;
11use mz_adapter_types::connection::ConnectionId;
12use mz_adapter_types::dyncfgs::SUBSCRIBE_MAX_BUFFERED_BYTES;
13use mz_cluster_client::ReplicaId;
14use mz_compute_types::ComputeInstanceId;
15use mz_compute_types::dataflows::LirDataflowDescription;
16use mz_ore::collections::CollectionExt;
17use mz_ore::instrument;
18use mz_repr::GlobalId;
19use mz_sql::plan;
20use std::collections::BTreeSet;
21use std::sync::{Arc, Mutex};
22use timely::PartialOrder;
23use tokio::sync::mpsc;
24use tokio_stream::wrappers::UnboundedReceiverStream;
25use uuid::Uuid;
26
27use crate::active_compute_sink::{
28 ActiveComputeSink, ActiveComputeSinkRetireReason, ActiveSubscribe, ActiveSubscribeOwner,
29 SubscribeBacklogAccounting,
30};
31use crate::command::ExecuteResponse;
32use crate::coord::Coordinator;
33use crate::coord::appends::BuiltinTableAppendNotify;
34use crate::coord::peek::PeekResponseUnary;
35use crate::error::AdapterError;
36use crate::statement_logging::StatementLoggingId;
37use crate::{ExecuteContextGuard, ReadHolds};
38
39impl Coordinator {
40 #[instrument]
41 pub(crate) async fn implement_subscribe(
42 &mut self,
43 statement_logging_id: Option<StatementLoggingId>,
44 df_desc: LirDataflowDescription,
45 dependency_ids: BTreeSet<GlobalId>,
46 cluster_id: ComputeInstanceId,
47 replica_id: Option<ReplicaId>,
48 conn_id: ConnectionId,
49 session_uuid: Uuid,
50 read_holds: ReadHolds,
51 plan: plan::SubscribePlan,
52 ) -> Result<(ExecuteResponse, BuiltinTableAppendNotify), AdapterError> {
53 let sink_id = df_desc.sink_id();
54
55 let (tx, rx) = mpsc::unbounded_channel::<PeekResponseUnary>();
56 let backlog_accounting = Arc::new(Mutex::new(SubscribeBacklogAccounting::default()));
57 let max_buffered_bytes =
58 SUBSCRIBE_MAX_BUFFERED_BYTES.get(self.catalog().system_config().dyncfgs());
59 let active_subscribe = ActiveSubscribe {
60 owner: ActiveSubscribeOwner::Session {
61 conn_id: conn_id.clone(),
62 session_uuid,
63 },
64 channel: tx,
65 backlog_accounting: Arc::clone(&backlog_accounting),
66 max_buffered_bytes,
67 emit_progress: plan.emit_progress,
68 as_of: df_desc
69 .as_of
70 .as_ref()
71 .and_then(|t| t.as_option())
72 .copied()
73 .expect("set to Some in an earlier stage"),
74 arity: df_desc
75 .sink_exports
76 .values()
77 .into_element()
78 .from_desc
79 .arity(),
80 cluster_id,
81 depends_on: dependency_ids,
82 start_time: self.now(),
83 output: plan.output,
84 internal: false,
85 };
86 active_subscribe.initialize();
87
88 let is_empty = df_desc
95 .as_of
96 .as_ref()
97 .is_some_and(|as_of| PartialOrder::less_equal(&df_desc.until, as_of));
98 let write_notify: BuiltinTableAppendNotify = if is_empty {
99 active_subscribe.retire(ActiveComputeSinkRetireReason::Finished);
100 Box::pin(std::future::ready(()))
102 } else {
103 let write_notify = self
110 .add_active_compute_sink(sink_id, ActiveComputeSink::Subscribe(active_subscribe));
111
112 if let Err(e) = self
117 .try_ship_dataflow(df_desc, cluster_id, replica_id)
118 .await
119 {
120 self.remove_active_compute_sink(sink_id).await;
126 return Err(
127 AdapterError::concurrent_dependency_drop_from_dataflow_creation_error(e),
128 );
129 }
130 write_notify
131 };
132
133 drop(read_holds);
135
136 let rx = UnboundedReceiverStream::new(rx).map(move |response| {
142 backlog_accounting
143 .lock()
144 .expect("subscribe backlog accounting poisoned")
145 .pop();
146 response
147 });
148 let resp = ExecuteResponse::Subscribing {
149 rx: Box::new(rx),
150 ctx_extra: ExecuteContextGuard::new(statement_logging_id, self.internal_cmd_tx.clone()),
151 instance_id: cluster_id,
152 };
153 let resp = match plan.copy_to {
154 None => resp,
155 Some(format) => ExecuteResponse::CopyTo {
156 format,
157 resp: Box::new(resp),
158 },
159 };
160 Ok((resp, write_notify))
161 }
162}