mz_storage/upsert_continual_feedback_v2/
feedback.rs1use differential_dataflow::hashable::Hashable;
14use differential_dataflow::trace::chunk::ChunkBatch;
15use differential_dataflow::{AsCollection, VecCollection};
16use mz_repr::{Datum, Diff, Row};
17#[cfg(feature = "fuzzing")]
19use mz_row_spine::DatumSeq;
20use mz_storage_types::errors::{DataflowError, UpsertError};
21use mz_timely_util::columnar::Column;
22use mz_timely_util::columnar::builder::ColumnBuilder;
23use mz_timely_util::columnar::chunk::{ColumnChunk, StorageSpill};
24use timely::dataflow::Stream;
25use timely::dataflow::channels::pact::Pipeline;
26use timely::dataflow::operators::Exchange as _;
27use timely::dataflow::operators::generic::Operator;
28use timely::order::TotalOrder;
29use timely::progress::Timestamp;
30use timely::progress::timestamp::Refines;
31
32use crate::statistics::SourceStatistics;
33use crate::upsert::{UpsertKey, UpsertValue};
34
35pub(super) type FeedbackUpdate<T> = ((UpsertKey, Row), T, Diff);
38
39pub(super) type FeedbackChunk<T> = ColumnChunk<(UpsertKey, Row), T, Diff, StorageSpill>;
46
47pub(super) type FeedbackSpine<T> =
50 mz_timely_util::funded_spine::Spine<std::rc::Rc<ChunkBatch<FeedbackChunk<T>>>>;
51
52#[doc(hidden)]
65pub fn upsert_value_to_row(value: &UpsertValue) -> Row {
66 let mut row = Row::default();
67 let mut packer = row.packer();
68 match value {
69 Ok(ok) => {
70 packer.push(Datum::UInt8(0));
71 packer.extend(ok.iter());
72 }
73 Err(err) => {
74 packer.push(Datum::UInt8(1));
75 let bytes =
76 bincode::serialize(err.as_ref()).expect("UpsertError is serializable via bincode");
77 packer.push(Datum::Bytes(&bytes));
78 }
79 }
80 row
81}
82
83pub(super) fn upsert_value_byte_len(value: &UpsertValue) -> usize {
86 match value {
87 Ok(row) => row.byte_len(),
88 Err(err) => std::mem::size_of_val(err.as_ref()),
89 }
90}
91
92#[cfg(feature = "fuzzing")]
98#[doc(hidden)]
99pub fn datum_seq_to_upsert_value(seq: DatumSeq<'_>) -> UpsertValue {
100 decode_upsert_value(seq)
101}
102
103pub(super) fn decode_upsert_value<'a>(mut iter: impl Iterator<Item = Datum<'a>>) -> UpsertValue {
106 let tag = match iter.next() {
107 Some(Datum::UInt8(tag)) => tag,
108 other => panic!("upsert value missing UInt8 tag, got {:?}", other),
109 };
110 match tag {
111 0 => {
112 let mut row = Row::default();
113 row.packer().extend(iter);
114 Ok(row)
115 }
116 1 => {
117 let bytes = match iter.next() {
118 Some(Datum::Bytes(b)) => b,
119 other => panic!("upsert error tag missing Bytes payload, got {:?}", other),
120 };
121 let err: UpsertError =
122 bincode::deserialize(bytes).expect("UpsertError bincode round-trip");
123 Err(Box::new(err))
124 }
125 tag => panic!("unknown upsert value tag {tag}"),
126 }
127}
128
129pub(super) fn encode_feedback<'scope, T>(
135 persist_ok: VecCollection<'scope, T, Row, Diff>,
136 persist_err: VecCollection<'scope, T, DataflowError, Diff>,
137 key_indices: Vec<usize>,
138 source_statistics: SourceStatistics,
139) -> Stream<'scope, T, Column<((UpsertKey, Row), T, Diff)>>
140where
141 T: Timestamp + TotalOrder + Sync,
142 T: Refines<mz_repr::Timestamp> + differential_dataflow::lattice::Lattice,
143 T: columnation::Columnation,
144 T: columnar::Columnar + Default,
145 for<'a> columnar::Ref<'a, T>: Copy + Ord,
146{
147 let persist_keyed = crate::upsert::key_persist_feedback(persist_ok, persist_err, key_indices);
148 let persist_keyed = persist_keyed
149 .inner
150 .exchange(move |((key, _), _, _)| UpsertKey::hashed(key))
153 .as_collection()
154 .inspect(move |((_, row), _, diff)| {
155 source_statistics.update_records_indexed_by(diff.into_inner());
156 source_statistics.update_bytes_indexed_by(
157 row.as_ref().map_or(0, |r| r.byte_len().try_into().unwrap()) * diff.into_inner(),
158 );
159 });
160 persist_keyed
161 .inner
162 .unary::<ColumnBuilder<((UpsertKey, Row), T, Diff)>, _, _, _>(
163 Pipeline,
164 "Persist feedback encode",
165 |_, _| {
166 move |input, output| {
167 input.for_each(|time, data| {
168 let mut session = output.session_with_builder(&time);
169 for ((key, value), ts, diff) in data.drain(..) {
170 let row = upsert_value_to_row(&value);
171 session.give(((&key, &row), &ts, &diff));
172 }
173 });
174 }
175 },
176 )
177}