1use std::collections::BTreeMap;
15use std::iter;
16use std::sync::Arc;
17
18use differential_dataflow::{AsCollection, VecCollection};
19use mz_expr::MfpPlan;
20use mz_ore::cast::CastLossy;
21use mz_persist_client::operators::shard_source::SnapshotMode;
22use mz_repr::{Datum, Diff, GlobalId, Row, RowPacker};
23use mz_storage_operators::persist_source;
24use mz_storage_operators::persist_source::Subtime;
25use mz_storage_types::controller::CollectionMetadata;
26use mz_storage_types::dyncfgs;
27use mz_storage_types::errors::{
28 DataflowError, DecodeError, EnvelopeError, UpsertError, UpsertNullKeyError, UpsertValueError,
29};
30use mz_storage_types::parameters::StorageMaxInflightBytesConfig;
31use mz_storage_types::sources::envelope::{KeyEnvelope, NoneEnvelope, UpsertEnvelope, UpsertStyle};
32use mz_storage_types::sources::*;
33use mz_timely_util::builder_async::PressOnDropButton;
34use mz_timely_util::operator::CollectionExt;
35use mz_timely_util::order::refine_antichain;
36use serde::{Deserialize, Serialize};
37use timely::container::CapacityContainerBuilder;
38use timely::dataflow::StreamVec;
39use timely::dataflow::operators::vec::Map;
40use timely::dataflow::operators::{ConnectLoop, Feedback, Leave, OkErr};
41use timely::dataflow::scope::Scope;
42use timely::progress::{Antichain, Timestamp};
43
44use crate::decode::{render_decode_cdcv2, render_decode_delimited};
45use crate::healthcheck::{HealthStatusMessage, StatusNamespace};
46use crate::source::types::{DecodeResult, SourceOutput, SourceRender};
47use crate::source::{self, RawSourceCreationConfig, SourceExportCreationConfig};
48use crate::upsert::{UpsertKey, UpsertSourceTime, UpsertValue};
49
50pub fn render_source<'scope, 'root, C>(
62 scope: Scope<'scope, mz_repr::Timestamp>,
63 root_scope: Scope<'root, ()>,
64 dataflow_debug_name: &String,
65 connection: C,
66 description: IngestionDescription<CollectionMetadata>,
67 committed_uppers: BTreeMap<GlobalId, StreamVec<'scope, mz_repr::Timestamp, ()>>,
68 storage_state: &crate::storage_state::StorageState,
69 base_source_config: RawSourceCreationConfig,
70) -> (
71 BTreeMap<
72 GlobalId,
73 (
74 VecCollection<'scope, mz_repr::Timestamp, Row, Diff>,
75 VecCollection<'scope, mz_repr::Timestamp, DataflowError, Diff>,
76 ),
77 >,
78 Vec<StreamVec<'root, (), HealthStatusMessage>>,
79 StreamVec<'scope, mz_repr::Timestamp, ()>,
80 Vec<PressOnDropButton>,
81)
82where
83 C: SourceConnection + SourceRender + 'static,
84 C::Time: UpsertSourceTime,
85{
86 let mut needed_tokens = Vec::new();
88
89 let (starter, mut start_signal) = tokio::sync::mpsc::channel::<()>(1);
102 let start_signal = async move {
103 let _ = start_signal.recv().await;
104 };
105
106 let (exports, health, remap_upper, source_tokens) = source::create_raw_source(
109 scope,
110 root_scope,
111 storage_state,
112 committed_uppers,
113 &base_source_config,
114 connection,
115 start_signal,
116 );
117
118 needed_tokens.extend(source_tokens);
119
120 let mut health_streams = Vec::with_capacity(exports.len() + 1);
121 health_streams.push(health);
122
123 let mut outputs = BTreeMap::new();
124 for (export_id, export) in exports {
125 type CB<C> = CapacityContainerBuilder<C>;
126 let (ok_stream, err_stream) =
127 export.map_fallible::<CB<_>, CB<_>, _, _, _>("export-demux-ok-err", |r| r);
128
129 let mut error_collections = Vec::new();
133
134 let data_config = base_source_config.source_exports[&export_id]
135 .data_config
136 .clone();
137 let (ok, extra_tokens, health_stream) = render_source_stream(
138 scope,
139 dataflow_debug_name,
140 export_id,
141 ok_stream,
142 data_config,
143 &description,
144 &mut error_collections,
145 storage_state,
146 &base_source_config,
147 starter.clone(),
148 );
149 needed_tokens.extend(extra_tokens);
150
151 let err_collection = match error_collections.len() {
153 0 => err_stream,
154 _ => err_stream.concatenate(error_collections),
155 };
156
157 outputs.insert(export_id, (ok, err_collection));
158
159 health_streams.extend(health_stream.into_iter().map(|s| s.leave(root_scope)));
160 }
161 (outputs, health_streams, remap_upper, needed_tokens)
162}
163
164fn render_source_stream<'scope, FromTime>(
167 scope: Scope<'scope, mz_repr::Timestamp>,
168 dataflow_debug_name: &String,
169 export_id: GlobalId,
170 ok_source: VecCollection<'scope, mz_repr::Timestamp, SourceOutput<FromTime>, Diff>,
171 data_config: SourceExportDataConfig,
172 description: &IngestionDescription<CollectionMetadata>,
173 error_collections: &mut Vec<VecCollection<'scope, mz_repr::Timestamp, DataflowError, Diff>>,
174 storage_state: &crate::storage_state::StorageState,
175 base_source_config: &RawSourceCreationConfig,
176 rehydrated_token: impl std::any::Any + 'static,
177) -> (
178 VecCollection<'scope, mz_repr::Timestamp, Row, Diff>,
179 Vec<PressOnDropButton>,
180 Vec<StreamVec<'scope, mz_repr::Timestamp, HealthStatusMessage>>,
181)
182where
183 FromTime: Timestamp + Sync,
184 FromTime: UpsertSourceTime,
185{
186 let mut needed_tokens = vec![];
187
188 let SourceExportDataConfig { encoding, envelope } = data_config;
190
191 let SourceDesc {
192 connection: _,
193 timestamp_interval: _,
194 } = description.desc;
195
196 let (decoded_stream, decode_health) = match encoding {
197 None => (
198 ok_source.map(|r| DecodeResult {
199 key: Some(Ok(r.key)),
207 value: Some(Ok(r.value)),
208 metadata: r.metadata,
209 from_time: r.from_time,
210 }),
211 None,
212 ),
213 Some(encoding) => {
214 let (decoded_stream, decode_health) = render_decode_delimited(
215 ok_source,
216 encoding.key,
217 encoding.value,
218 dataflow_debug_name.clone(),
219 storage_state.metrics.decode_defs.clone(),
220 storage_state.storage_configuration.clone(),
221 );
222 (decoded_stream, Some(decode_health))
223 }
224 };
225
226 let (envelope_ok, envelope_health) = match &envelope {
228 SourceEnvelope::Upsert(upsert_envelope) => {
229 let upsert_input = upsert_commands(decoded_stream, upsert_envelope.clone());
230
231 let persist_clients = Arc::clone(&storage_state.persist_clients);
232 let resume_upper = base_source_config.resume_uppers[&export_id].clone();
234
235 let upper_ts = resume_upper
236 .as_option()
237 .expect("resuming an already finished ingestion")
238 .clone();
239 let outer_mz_scope = scope.clone();
240 let (upsert, health_update) = scope.scoped(
241 &format!("upsert_rehydration_backpressure({})", export_id),
242 |scope| {
243 let (
244 previous_ok,
245 previous_err,
246 previous_token,
247 feedback_handle,
248 backpressure_metrics,
249 ) = {
250 let as_of = Antichain::from_elem(upper_ts.saturating_sub(1));
251
252 let backpressure_max_inflight_bytes = get_backpressure_max_inflight_bytes(
253 &storage_state
254 .storage_configuration
255 .parameters
256 .storage_dataflow_max_inflight_bytes_config,
257 &storage_state.instance_context.cluster_memory_limit,
258 );
259
260 let (feedback_handle, flow_control, backpressure_metrics) =
261 if let Some(storage_dataflow_max_inflight_bytes) =
262 backpressure_max_inflight_bytes
263 {
264 tracing::info!(
265 ?backpressure_max_inflight_bytes,
266 "timely-{} using backpressure in upsert for source {}",
267 base_source_config.worker_id,
268 export_id
269 );
270 if !storage_state
271 .storage_configuration
272 .parameters
273 .storage_dataflow_max_inflight_bytes_config
274 .disk_only
275 || storage_state.instance_context.scratch_directory.is_some()
276 {
277 let (feedback_handle, feedback_data) =
278 scope.feedback(Default::default());
279
280 let backpressure_metrics = Some(
282 base_source_config
283 .metrics
284 .get_backpressure_metrics(export_id, scope.index()),
285 );
286
287 (
288 Some(feedback_handle),
289 Some(persist_source::FlowControl {
290 progress_stream: feedback_data,
291 max_inflight_bytes: storage_dataflow_max_inflight_bytes,
292 summary: (Default::default(), Subtime::least_summary()),
293 metrics: backpressure_metrics
294 .as_ref()
295 .map(|m| m.operator_metrics()),
296 }),
297 backpressure_metrics,
298 )
299 } else {
300 (None, None, None)
301 }
302 } else {
303 (None, None, None)
304 };
305
306 let storage_metadata = description.source_exports[&export_id]
307 .storage_metadata
308 .clone();
309
310 let error_handler =
311 storage_state.error_handler("upsert_rehydration", export_id);
312
313 let (ok_stream, err_stream, tok) = persist_source::persist_source_core(
314 outer_mz_scope,
315 scope,
316 export_id,
317 persist_clients,
318 storage_metadata,
319 None,
320 Some(as_of),
321 SnapshotMode::Include,
322 Antichain::new(),
323 None::<&mut MfpPlan>,
324 flow_control,
325 false.then_some(|| unreachable!()),
326 async {},
327 error_handler,
328 );
329 (
330 ok_stream.as_collection(),
331 err_stream.as_collection(),
332 Some(tok),
333 feedback_handle,
334 backpressure_metrics,
335 )
336 };
337
338 let export_statistics = storage_state
339 .aggregated_statistics
340 .get_source(&export_id)
341 .expect("statistics initialized")
342 .clone();
343 let export_config = SourceExportCreationConfig {
344 id: export_id,
345 worker_id: base_source_config.worker_id,
346 metrics: base_source_config.metrics.clone(),
347 source_statistics: export_statistics,
348 };
349 let use_upsert_v2 = dyncfgs::ENABLE_UPSERT_V2
354 .get(storage_state.storage_configuration.config_set())
355 && storage_state.instance_context.swap_available;
356 let (upsert, health_update, snapshot_progress, upsert_token) = if use_upsert_v2
357 {
358 let stash_flavor =
362 crate::upsert_continual_feedback_v2::UpsertStashFlavor::from_config(
363 storage_state.storage_configuration.config_set(),
364 );
365 crate::upsert::upsert_v2(
366 upsert_input.enter(scope),
367 upsert_envelope.clone(),
368 refine_antichain(&resume_upper),
369 previous_ok,
370 previous_err,
371 previous_token,
372 export_config,
373 backpressure_metrics,
374 stash_flavor,
375 )
376 } else {
377 crate::upsert::upsert(
378 upsert_input.enter(scope),
379 upsert_envelope.clone(),
380 refine_antichain(&resume_upper),
381 previous_ok,
382 previous_err,
383 previous_token,
384 export_config,
385 &storage_state.instance_context,
386 &storage_state.storage_configuration,
387 &storage_state.dataflow_parameters,
388 backpressure_metrics,
389 )
390 };
391
392 needed_tokens.push(upsert_token);
397
398 if dyncfgs::DELAY_SOURCES_PAST_REHYDRATION
402 .get(storage_state.storage_configuration.config_set())
403 {
404 crate::upsert::rehydration_finished(
405 scope.clone(),
406 base_source_config,
407 rehydrated_token,
408 refine_antichain(&resume_upper),
409 snapshot_progress.clone(),
410 );
411 } else {
412 drop(rehydrated_token)
413 };
414
415 if let Some(feedback_handle) = feedback_handle {
418 snapshot_progress.connect_loop(feedback_handle);
419 }
420
421 (
422 upsert.leave(outer_mz_scope),
423 health_update
424 .map(|(id, update)| HealthStatusMessage {
425 id,
426 namespace: StatusNamespace::Upsert,
427 update,
428 })
429 .leave(outer_mz_scope),
430 )
431 },
432 );
433
434 let (upsert_ok, upsert_err) = upsert.inner.ok_err(split_ok_err);
435 error_collections.push(upsert_err.as_collection());
436
437 (upsert_ok.as_collection(), Some(health_update))
438 }
439 SourceEnvelope::None(none_envelope) => {
440 let results = append_metadata_to_value(decoded_stream);
441
442 let flattened_stream = flatten_results_prepend_keys(none_envelope, results);
443
444 let (stream, errors) = flattened_stream.inner.ok_err(split_ok_err);
445
446 error_collections.push(errors.as_collection());
447 (stream.as_collection(), None)
448 }
449 SourceEnvelope::CdcV2 => {
450 let (oks, token) = render_decode_cdcv2(&decoded_stream);
451 needed_tokens.push(token);
452 (oks, None)
453 }
454 };
455
456 let health = decode_health.into_iter().chain(envelope_health).collect();
458 (envelope_ok, needed_tokens, health)
459}
460
461fn get_backpressure_max_inflight_bytes(
464 inflight_bytes_config: &StorageMaxInflightBytesConfig,
465 cluster_memory_limit: &Option<usize>,
466) -> Option<usize> {
467 let StorageMaxInflightBytesConfig {
468 max_inflight_bytes_default,
469 max_inflight_bytes_cluster_size_fraction,
470 disk_only: _,
471 } = inflight_bytes_config;
472
473 if max_inflight_bytes_default.is_some() {
475 let current_cluster_max_bytes_limit =
476 cluster_memory_limit.as_ref().and_then(|cluster_memory| {
477 max_inflight_bytes_cluster_size_fraction.map(|fraction| {
478 usize::cast_lossy(f64::cast_lossy(*cluster_memory) * fraction)
480 })
481 });
482 current_cluster_max_bytes_limit.or(*max_inflight_bytes_default)
483 } else {
484 None
485 }
486}
487
488fn split_ok_err<O, E, T, D>(x: (Result<O, E>, T, D)) -> Result<(O, T, D), (E, T, D)> {
491 match x {
492 (Ok(ok), ts, diff) => Ok((ok, ts, diff)),
493 (Err(err), ts, diff) => Err((err, ts, diff)),
494 }
495}
496
497#[derive(
499 Debug,
500 Clone,
501 Hash,
502 PartialEq,
503 Eq,
504 Ord,
505 PartialOrd,
506 Serialize,
507 Deserialize
508)]
509struct KV {
510 key: Option<Result<Row, DecodeError>>,
511 val: Option<Result<Row, DecodeError>>,
512}
513
514fn append_metadata_to_value<'scope, T: Timestamp, FromTime: Timestamp>(
515 results: VecCollection<'scope, T, DecodeResult<FromTime>, Diff>,
516) -> VecCollection<'scope, T, KV, Diff> {
517 results.map(move |res| {
518 let val = res.value.map(|val_result| {
519 val_result.map(|mut val| {
520 if !res.metadata.is_empty() {
521 RowPacker::for_existing_row(&mut val).extend_by_row(&res.metadata);
522 }
523 val
524 })
525 });
526
527 KV { val, key: res.key }
528 })
529}
530
531fn upsert_commands<'scope, T: Timestamp, FromTime: Timestamp>(
533 input: VecCollection<'scope, T, DecodeResult<FromTime>, Diff>,
534 upsert_envelope: UpsertEnvelope,
535) -> VecCollection<'scope, T, (UpsertKey, Option<UpsertValue>, FromTime), Diff> {
536 let mut row_buf = Row::default();
537 input.map(move |result| {
538 let from_time = result.from_time;
539
540 let key = match result.key {
541 Some(Ok(key)) => Ok(key),
542 None => Err(UpsertError::NullKey(UpsertNullKeyError)),
543 Some(Err(err)) => Err(UpsertError::KeyDecode(err)),
544 };
545
546 let key = match key {
548 Ok(key) => key,
549 Err(err) => match result.value {
550 Some(_) => {
551 return (
552 UpsertKey::from_key(Err(&err)),
553 Some(Err(Box::new(err))),
554 from_time,
555 );
556 }
557 None => return (UpsertKey::from_key(Err(&err)), None, from_time),
558 },
559 };
560
561 let key_row = match upsert_envelope.style {
563 UpsertStyle::Debezium { .. }
565 | UpsertStyle::Default(KeyEnvelope::Flattened)
566 | UpsertStyle::ValueErrInline {
567 key_envelope: KeyEnvelope::Flattened,
568 error_column: _,
569 } => key,
570 UpsertStyle::Default(KeyEnvelope::Named(_))
572 | UpsertStyle::ValueErrInline {
573 key_envelope: KeyEnvelope::Named(_),
574 error_column: _,
575 } => {
576 if key.iter().nth(1).is_none() {
577 key
578 } else {
579 row_buf.packer().push_list(key.iter());
580 row_buf.clone()
581 }
582 }
583 UpsertStyle::Default(KeyEnvelope::None)
584 | UpsertStyle::ValueErrInline {
585 key_envelope: KeyEnvelope::None,
586 error_column: _,
587 } => unreachable!(),
588 };
589
590 let key = UpsertKey::from_key(Ok(&key_row));
591
592 let metadata = result.metadata;
593
594 let value = match result.value {
595 Some(Ok(ref row)) => match upsert_envelope.style {
596 UpsertStyle::Debezium { after_idx } => match row.iter().nth(after_idx).unwrap() {
597 Datum::List(after) => {
598 let mut packer = row_buf.packer();
599 packer.extend(after.iter());
600 packer.extend_by_row(&metadata);
601 Some(Ok(row_buf.clone()))
602 }
603 Datum::Null => None,
604 d => panic!("type error: expected record, found {:?}", d),
605 },
606 UpsertStyle::Default(_) => {
607 let mut packer = row_buf.packer();
608 packer.extend_by_row(&key_row);
609 packer.extend_by_row(row);
610 packer.extend_by_row(&metadata);
611 Some(Ok(row_buf.clone()))
612 }
613 UpsertStyle::ValueErrInline { .. } => {
614 let mut packer = row_buf.packer();
615 packer.extend_by_row(&key_row);
616 packer.push(Datum::Null);
618 packer.extend_by_row(row);
619 packer.extend_by_row(&metadata);
620 Some(Ok(row_buf.clone()))
621 }
622 },
623 Some(Err(inner)) => {
624 match upsert_envelope.style {
625 UpsertStyle::ValueErrInline { .. } => {
626 let mut count = 0;
627 let err_string = inner.to_string();
629 let mut packer = row_buf.packer();
630 for datum in key_row.iter() {
631 packer.push(datum);
632 count += 1;
633 }
634 packer.push_list(iter::once(Datum::String(&err_string)));
636 count += 1;
637 let metadata_len = metadata.as_row_ref().iter().count();
638 packer.extend(
640 iter::repeat(Datum::Null)
641 .take(upsert_envelope.source_arity - count - metadata_len),
642 );
643 packer.extend_by_row(&metadata);
644 Some(Ok(row_buf.clone()))
645 }
646 _ => Some(Err(Box::new(UpsertError::Value(UpsertValueError {
647 for_key: key_row,
648 inner,
649 })))),
650 }
651 }
652 None => None,
653 };
654
655 (key, value, from_time)
656 })
657}
658
659fn flatten_results_prepend_keys<'scope, T: Timestamp>(
661 none_envelope: &NoneEnvelope,
662 results: VecCollection<'scope, T, KV, Diff>,
663) -> VecCollection<'scope, T, Result<Row, DataflowError>, Diff> {
664 let NoneEnvelope {
665 key_envelope,
666 key_arity,
667 } = none_envelope;
668
669 let null_key_columns = Row::pack_slice(&vec![Datum::Null; *key_arity]);
670
671 match key_envelope {
672 KeyEnvelope::None => {
673 results.flat_map(|KV { val, .. }| val.map(|result| result.map_err(Into::into)))
674 }
675 KeyEnvelope::Flattened => results
676 .flat_map(raise_key_value_errors)
677 .map(move |maybe_kv| {
678 maybe_kv.map(|(key, value)| {
679 let mut key = key.unwrap_or_else(|| null_key_columns.clone());
680 RowPacker::for_existing_row(&mut key).extend_by_row(&value);
681 key
682 })
683 }),
684 KeyEnvelope::Named(_) => {
685 results
686 .flat_map(raise_key_value_errors)
687 .map(move |maybe_kv| {
688 maybe_kv.map(|(key, value)| {
689 let mut key = key.unwrap_or_else(|| null_key_columns.clone());
690 let row = if key.iter().nth(1).is_none() {
693 RowPacker::for_existing_row(&mut key).extend_by_row(&value);
694 key
695 } else {
696 let mut new_row = Row::default();
697 let mut packer = new_row.packer();
698 packer.push_list(key.iter());
699 packer.extend_by_row(&value);
700 new_row
701 };
702 row
703 })
704 })
705 }
706 }
707}
708
709fn raise_key_value_errors(
711 KV { key, val }: KV,
712) -> Option<Result<(Option<Row>, Row), DataflowError>> {
713 match (key, val) {
714 (Some(Ok(key)), Some(Ok(value))) => Some(Ok((Some(key), value))),
715 (None, Some(Ok(value))) => Some(Ok((None, value))),
716 (_, Some(Err(e))) => Some(Err(e.into())),
718 (Some(Err(e)), _) => Some(Err(e.into())),
719 (None, None) => None,
720 _ => Some(Err(DataflowError::from(EnvelopeError::Flat(
722 "Value not present for message".into(),
723 )))),
724 }
725}
726
727#[cfg(test)]
728mod test {
729 use super::*;
730
731 #[mz_ore::test]
732 fn test_no_default() {
733 let config = StorageMaxInflightBytesConfig {
734 max_inflight_bytes_default: None,
735 max_inflight_bytes_cluster_size_fraction: Some(0.5),
736 disk_only: false,
737 };
738 let memory_limit = Some(1000);
739
740 let backpressure_inflight_bytes_limit =
741 get_backpressure_max_inflight_bytes(&config, &memory_limit);
742
743 assert_eq!(backpressure_inflight_bytes_limit, None)
744 }
745
746 #[mz_ore::test]
747 fn test_no_matching_size() {
748 let config = StorageMaxInflightBytesConfig {
749 max_inflight_bytes_default: Some(10000),
750 max_inflight_bytes_cluster_size_fraction: Some(0.5),
751 disk_only: false,
752 };
753
754 let backpressure_inflight_bytes_limit = get_backpressure_max_inflight_bytes(&config, &None);
755
756 assert_eq!(
757 backpressure_inflight_bytes_limit,
758 config.max_inflight_bytes_default
759 )
760 }
761
762 #[mz_ore::test]
763 fn test_calculated_cluster_limit() {
764 let config = StorageMaxInflightBytesConfig {
765 max_inflight_bytes_default: Some(10000),
766 max_inflight_bytes_cluster_size_fraction: Some(0.5),
767 disk_only: false,
768 };
769 let memory_limit = Some(2000);
770
771 let backpressure_inflight_bytes_limit =
772 get_backpressure_max_inflight_bytes(&config, &memory_limit);
773
774 assert_eq!(backpressure_inflight_bytes_limit, Some(1000));
776 }
777}