mz_storage_controller/persist_handles/
read_only_table_worker.rs1use std::collections::{BTreeMap, BTreeSet, VecDeque};
14use std::ops::ControlFlow;
15
16use differential_dataflow::lattice::Lattice;
17use futures::FutureExt;
18use mz_persist_client::write::WriteHandle;
19use mz_repr::{GlobalId, Timestamp};
20use mz_storage_client::client::{TableData, Update};
21use mz_storage_types::StorageDiff;
22use mz_storage_types::controller::InvalidUpper;
23use mz_storage_types::sources::SourceData;
24use timely::PartialOrder;
25use timely::progress::Antichain;
26use tracing::Span;
27
28use crate::StorageError;
29use crate::persist_handles::{PersistTableWriteCmd, append_work};
30
31pub(crate) async fn read_only_mode_table_worker(
50 mut rx: tokio::sync::mpsc::UnboundedReceiver<(Span, PersistTableWriteCmd)>,
51 txns_handle: WriteHandle<SourceData, (), Timestamp, StorageDiff>,
52 persist_client: mz_persist_client::PersistClient,
53) {
54 let mut write_handles =
55 BTreeMap::<GlobalId, WriteHandle<SourceData, (), Timestamp, StorageDiff>>::new();
56
57 let gen_upper_future = |mut handle: WriteHandle<SourceData, (), Timestamp, StorageDiff>| {
58 let fut = async move {
59 let current_upper = handle.shared_upper();
60 handle.wait_for_upper_past(¤t_upper).await;
61 let new_upper = handle.shared_upper();
62 (handle, new_upper)
63 };
64
65 fut.boxed()
66 };
67
68 let mut txns_upper_future = {
69 let txns_upper_future = gen_upper_future(txns_handle);
70 txns_upper_future
71 };
72
73 let shutdown_reason = loop {
74 tokio::select! {
75 (handle, upper) = &mut txns_upper_future => {
76 tracing::debug!("new upper from txns shard: {:?}, advancing upper of migrated builtin tables", upper);
77 advance_uppers(&mut write_handles, upper).await;
78
79 let fut = gen_upper_future(handle);
80 txns_upper_future = fut;
81 }
82 cmd = rx.recv() => {
83 let Some(cmd) = cmd else {
84 break "command rx closed".to_string();
85 };
86
87 let mut commands = VecDeque::new();
92 commands.push_back(cmd);
93 while let Ok(cmd) = rx.try_recv() {
94 commands.push_back(cmd);
95 }
96
97 let result = handle_commands(&mut write_handles, commands, &persist_client).await;
98
99 match result {
100 ControlFlow::Continue(_) => {
101 continue;
102 }
103 ControlFlow::Break(msg) => {
104 break msg;
105 }
106 }
107
108 }
109 }
110 };
111
112 tracing::info!(%shutdown_reason, "PersistTableWriteWorker shutting down");
113}
114
115async fn handle_commands(
117 write_handles: &mut BTreeMap<GlobalId, WriteHandle<SourceData, (), Timestamp, StorageDiff>>,
118 mut commands: VecDeque<(Span, PersistTableWriteCmd)>,
119 persist_client: &mz_persist_client::PersistClient,
120) -> ControlFlow<String> {
121 let mut shutdown = false;
122
123 let mut all_updates = BTreeMap::default();
125 let mut all_responses = Vec::default();
126
127 while let Some((span, command)) = commands.pop_front() {
128 match command {
129 PersistTableWriteCmd::Register(_register_ts, tables, tx) => {
130 let ids_handles =
131 crate::persist_handles::open_table_write_handles(persist_client, tables).await;
132 for (id, write_handle) in ids_handles {
133 assert!(id.is_system(), "trying to register non-system id {id}");
135
136 let previous = write_handles.insert(id, write_handle);
137 if previous.is_some() {
138 panic!("already registered a WriteHandle for collection {:?}", id);
139 }
140 }
141 let _ = tx.send(Ok(()));
143 }
144 PersistTableWriteCmd::DropHandles {
145 forget_ts: _,
146 ids,
147 tx,
148 } => {
149 for id in ids {
156 write_handles.remove(&id);
157 }
158 let _ = tx.send(Ok(()));
160 }
161 PersistTableWriteCmd::Append {
162 write_ts,
163 advance_to,
164 updates,
165 tx,
166 } => {
167 let mut ids = BTreeSet::new();
168 for (id, updates_no_ts) in updates {
169 ids.insert(id);
170 let (old_span, updates, _expected_upper, old_new_upper) =
171 all_updates.entry(id).or_insert_with(|| {
172 (
173 span.clone(),
174 Vec::default(),
175 Antichain::from_elem(write_ts),
176 Antichain::from_elem(Timestamp::MIN),
177 )
178 });
179
180 if old_span.id() != span.id() {
181 old_span.follows_from(span.id());
187 }
188 let updates_with_ts = updates_no_ts.into_iter().flat_map(|x| match x {
189 TableData::Rows(rows) => {
190 let iter = rows.into_iter().map(|(row, diff)| Update {
191 row,
192 timestamp: write_ts,
193 diff,
194 });
195 itertools::Either::Left(iter)
196 }
197 TableData::Batches(_) => {
198 mz_ore::soft_panic_or_log!(
200 "handle Batches of updates in the ReadOnlyTableWorker"
201 );
202 itertools::Either::Right(std::iter::empty())
203 }
204 });
205 updates.extend(updates_with_ts);
206 old_new_upper.join_assign(&Antichain::from_elem(advance_to));
207 }
208 all_responses.push((ids, tx));
209 }
210 PersistTableWriteCmd::Shutdown => shutdown = true,
211 }
212 }
213
214 let result = append_work(write_handles, all_updates).await;
215
216 for (ids, response) in all_responses {
217 let result = match &result {
218 Err(bad_ids) => {
219 let filtered: Vec<_> = bad_ids
220 .iter()
221 .filter(|(id, _)| ids.contains(id))
222 .cloned()
223 .map(|(id, current_upper)| InvalidUpper { id, current_upper })
224 .collect();
225 if filtered.is_empty() {
226 Ok(())
227 } else {
228 Err(StorageError::InvalidUppers(filtered))
229 }
230 }
231 Ok(()) => Ok(()),
232 };
233 let _ = response.send(result);
235 }
236
237 if shutdown {
238 ControlFlow::Break("received a shutdown command".to_string())
239 } else {
240 ControlFlow::Continue(())
241 }
242}
243
244async fn advance_uppers(
247 write_handles: &mut BTreeMap<GlobalId, WriteHandle<SourceData, (), Timestamp, StorageDiff>>,
248 upper: Antichain<Timestamp>,
249) {
250 let mut all_updates = BTreeMap::default();
251
252 for (id, write_handle) in write_handles.iter_mut() {
253 assert!(id.is_system(), "trying to register non-system id {id}");
256
257 let expected_upper = write_handle.fetch_recent_upper().await.to_owned();
261
262 if expected_upper.elements() == &[Timestamp::MIN] {
264 continue;
265 }
266
267 if PartialOrder::less_equal(&upper, &expected_upper) {
268 continue;
271 }
272
273 all_updates.insert(
274 *id,
275 (Span::none(), Vec::new(), expected_upper, upper.clone()),
276 );
277 }
278
279 let result = append_work(write_handles, all_updates).await;
280 tracing::debug!(?result, "advanced upper of migrated builtin tables");
281}