1use std::cell::RefCell;
9use std::collections::BTreeMap;
10use std::rc::Rc;
11use std::time::{Duration, Instant};
12
13use differential_dataflow::VecCollection;
14use differential_dataflow::dynamic::pointstamp::PointStamp;
15use differential_dataflow::logging::{DifferentialEvent, DifferentialEventBuilder};
16use mz_compute_client::logging::{LogVariant, LoggingConfig};
17use mz_dyncfg::ConfigSet;
18use mz_ore::metrics::MetricsRegistry;
19use mz_repr::{Diff, Timestamp};
20use mz_storage_operators::persist_source::Subtime;
21use mz_timely_util::columnar::Column;
22use mz_timely_util::columnar::builder::ColumnBuilder;
23use mz_timely_util::operator::CollectionExt;
24use mz_timely_util::scope_label::ScopeExt;
25use prometheus::IntCounter;
26use timely::ContainerBuilder;
27use timely::container::{ContainerBuilder as _, PushInto};
28use timely::logging::{StartStop, TimelyEvent, TimelyEventBuilder, TimelyLogger};
29use timely::logging_core::{Logger, Registry};
30use timely::order::Product;
31use timely::progress::reachability::logging::{TrackerEvent, TrackerEventBuilder};
32
33use crate::arrangement::manager::TraceBundle;
34use crate::extensions::arrange::{KeyCollection, MzArrange};
35use crate::logging::compute::{ComputeEvent, ComputeEventBuilder};
36use crate::logging::{BatchLogger, EventQueue, SharedLoggingState};
37use crate::metrics::LoggingMetrics;
38use crate::render::errors::DataflowErrorSer;
39use crate::typedefs::ErrBatcher;
40
41pub fn initialize(
46 worker: &mut timely::worker::Worker,
47 config: &LoggingConfig,
48 metrics_registry: MetricsRegistry,
49 metrics: LoggingMetrics,
50 worker_config: Rc<ConfigSet>,
51 workers_per_process: usize,
52) -> LoggingTraces {
53 let interval_ms = std::cmp::max(1, config.interval.as_millis());
54
55 let now = Instant::now();
59 let start_offset = std::time::SystemTime::now()
60 .duration_since(std::time::SystemTime::UNIX_EPOCH)
61 .expect("Failed to get duration since Unix epoch");
62
63 let mut context = LoggingContext {
64 worker,
65 config,
66 interval_ms,
67 now,
68 start_offset,
69 t_event_queue: EventQueue::new("t"),
70 r_event_queue: EventQueue::new("r"),
71 d_event_queue: EventQueue::new("d"),
72 c_event_queue: EventQueue::new("c"),
73 shared_state: Default::default(),
74 metrics_registry,
75 metrics,
76 worker_config,
77 workers_per_process,
78 };
79
80 let dataflow_index = context.worker.next_dataflow_index();
83 let traces = if config.log_logging {
84 context.register_loggers();
85 context.construct_dataflow()
86 } else {
87 let traces = context.construct_dataflow();
88 context.register_loggers();
89 traces
90 };
91
92 let compute_logger = worker.logger_for("materialize/compute").unwrap();
93 LoggingTraces {
94 traces,
95 dataflow_index,
96 compute_logger,
97 }
98}
99
100pub(super) type ReachabilityEvent = (usize, Vec<(usize, usize, bool, Timestamp, Diff)>);
101
102struct LoggingContext<'a> {
103 worker: &'a mut timely::worker::Worker,
104 config: &'a LoggingConfig,
105 interval_ms: u128,
106 now: Instant,
107 start_offset: Duration,
108 t_event_queue: EventQueue<Vec<(Duration, TimelyEvent)>>,
109 r_event_queue: EventQueue<Column<(Duration, ReachabilityEvent)>, 3>,
110 d_event_queue: EventQueue<Vec<(Duration, DifferentialEvent)>>,
111 c_event_queue: EventQueue<Column<(Duration, ComputeEvent)>>,
112 shared_state: Rc<RefCell<SharedLoggingState>>,
113 metrics_registry: MetricsRegistry,
114 metrics: LoggingMetrics,
115 worker_config: Rc<ConfigSet>,
116 workers_per_process: usize,
117}
118
119pub(crate) struct LoggingTraces {
120 pub traces: BTreeMap<LogVariant, TraceBundle>,
122 pub dataflow_index: usize,
124 pub compute_logger: super::compute::Logger,
126}
127
128impl LoggingContext<'_> {
129 fn construct_dataflow(&mut self) -> BTreeMap<LogVariant, TraceBundle> {
130 let step_logger = self.step_logger();
131 self.worker.dataflow_core(
132 "Dataflow: logging",
133 step_logger,
134 Box::new(()),
135 |_, scope| {
136 let scope = scope.with_label();
137
138 let mut collections = BTreeMap::new();
139
140 let super::timely::Return {
141 collections: timely_collections,
142 } = super::timely::construct(
143 scope,
144 self.config,
145 self.t_event_queue.clone(),
146 Rc::clone(&self.shared_state),
147 );
148 collections.extend(timely_collections);
149
150 let super::reachability::Return {
151 collections: reachability_collections,
152 } = super::reachability::construct(scope, self.config, self.r_event_queue.clone());
153 collections.extend(reachability_collections);
154
155 let super::differential::Return {
156 collections: differential_collections,
157 } = super::differential::construct(
158 scope,
159 self.config,
160 self.d_event_queue.clone(),
161 Rc::clone(&self.shared_state),
162 );
163 collections.extend(differential_collections);
164
165 let super::compute::Return {
166 collections: compute_collections,
167 } = super::compute::construct(
168 scope.clone(),
169 scope.activations(),
170 self.config,
171 self.c_event_queue.clone(),
172 Rc::clone(&self.shared_state),
173 );
174 collections.extend(compute_collections);
175
176 let super::prometheus::Return {
177 collections: prometheus_collections,
178 } = super::prometheus::construct(
179 scope,
180 self.config,
181 self.metrics_registry.clone(),
182 self.now,
183 self.start_offset,
184 Rc::clone(&self.worker_config),
185 self.workers_per_process,
186 );
187 collections.extend(prometheus_collections);
188
189 let super::resource_usage::Return {
190 collections: resource_usage_collections,
191 } = super::resource_usage::construct(
192 scope,
193 self.config,
194 self.now,
195 self.start_offset,
196 self.workers_per_process,
197 );
198 collections.extend(resource_usage_collections);
199
200 let errs = scope.scoped("logging errors", |scope| {
201 let collection: KeyCollection<_, DataflowErrorSer, Diff> =
202 VecCollection::empty(scope).into();
203 collection
204 .mz_arrange::<ErrBatcher<_, _>, _>("Arrange logging err")
205 .trace
206 });
207
208 let traces = collections
209 .into_iter()
210 .map(|(log, collection)| {
211 let bundle = TraceBundle::new(collection.trace, errs.clone())
212 .with_drop(collection.token);
213 (log, bundle)
214 })
215 .collect();
216 traces
217 },
218 )
219 }
220
221 fn step_logger(&self) -> Option<TimelyLogger> {
228 if let Some(logger) = self.worker.logging() {
229 return Some(logger);
232 }
233
234 let dataflow_id = self.worker.peek_identifier();
236 let step_duration_seconds = self.metrics.step_duration_seconds.clone();
237 let mut started = None;
238 let logger = Logger::<TimelyEventBuilder>::new(
239 self.now,
240 self.start_offset,
241 move |_time, data: &mut Option<Vec<(Duration, TimelyEvent)>>| {
242 let Some(data) = data else { return };
243 for (time, event) in data.drain(..) {
244 if let TimelyEvent::Schedule(schedule) = event
245 && schedule.id == dataflow_id
246 {
247 match schedule.start_stop {
248 StartStop::Start => started = Some(time),
249 StartStop::Stop => {
250 if let Some(start) = started.take() {
251 let elapsed = time.saturating_sub(start);
252 step_duration_seconds.observe(elapsed.as_secs_f64());
253 }
254 }
255 }
256 }
257 }
258 },
259 );
260 let mut register = self.worker.log_register().expect("Logging must be enabled");
263 register.insert_logger("materialize/logging-step", logger.clone());
264 Some(logger.into())
265 }
266
267 fn register_reachability_logger<T: ExtractTimestamp>(
272 &self,
273 registry: &mut Registry,
274 index: usize,
275 ) {
276 let logger = self.reachability_logger::<T>(index);
277 let type_name = std::any::type_name::<T>();
278 registry.insert_logger(&format!("timely/reachability/{type_name}"), logger);
279 }
280
281 fn register_loggers(&self) {
285 let t_logger = self.simple_logger::<TimelyEventBuilder>(
286 self.t_event_queue.clone(),
287 self.metrics.timely_records_total.clone(),
288 );
289 let d_logger = self.simple_logger::<DifferentialEventBuilder>(
290 self.d_event_queue.clone(),
291 self.metrics.differential_records_total.clone(),
292 );
293 let c_logger = self.simple_logger::<ComputeEventBuilder>(
294 self.c_event_queue.clone(),
295 self.metrics.compute_records_total.clone(),
296 );
297
298 let mut register = self.worker.log_register().expect("Logging must be enabled");
299 register.insert_logger("timely", t_logger);
300 self.register_reachability_logger::<Timestamp>(&mut register, 0);
303 self.register_reachability_logger::<Product<Timestamp, PointStamp<u64>>>(&mut register, 1);
304 self.register_reachability_logger::<(Timestamp, Subtime)>(&mut register, 2);
305 register.insert_logger("differential/arrange", d_logger);
306 register.insert_logger("materialize/compute", c_logger.clone());
307
308 self.shared_state.borrow_mut().compute_logger = Some(c_logger);
309 }
310
311 fn simple_logger<CB: ContainerBuilder>(
312 &self,
313 event_queue: EventQueue<CB::Container>,
314 records_total: IntCounter,
315 ) -> Logger<CB> {
316 let [link] = event_queue.links;
317 let mut logger = BatchLogger::new(link, self.interval_ms, records_total);
318 let activator = event_queue.activator.clone();
319 Logger::new(
320 self.now,
321 self.start_offset,
322 move |time, data: &mut Option<CB::Container>| {
323 if let Some(data) = data.take() {
324 logger.publish_batch(data);
325 activator.activate();
332 } else if logger.report_progress(*time) {
333 activator.activate();
334 }
335 },
336 )
337 }
338
339 fn reachability_logger<T>(&self, index: usize) -> Logger<TrackerEventBuilder<T>>
342 where
343 T: ExtractTimestamp,
344 {
345 let link = Rc::clone(&self.r_event_queue.links[index]);
346 let mut logger = BatchLogger::new(
347 link,
348 self.interval_ms,
349 self.metrics.reachability_records_total.clone(),
350 );
351 let mut massaged = Vec::new();
352 let mut builder = ColumnBuilder::default();
353 let activator = self.r_event_queue.activator.clone();
354
355 let action = move |batch_time: &Duration, data: &mut Option<Vec<_>>| {
356 if let Some(data) = data {
357 for (time, event) in data.drain(..) {
359 match event {
360 TrackerEvent::SourceUpdate(update) => {
361 massaged.extend(update.updates.iter().map(
362 |(node, port, time, diff)| {
363 let is_source = true;
364 (*node, *port, is_source, T::extract(time), Diff::from(*diff))
365 },
366 ));
367
368 builder.push_into((time, (update.tracker_id, &massaged)));
369 massaged.clear();
370 }
371 TrackerEvent::TargetUpdate(update) => {
372 massaged.extend(update.updates.iter().map(
373 |(node, port, time, diff)| {
374 let is_source = false;
375 (*node, *port, is_source, time.extract(), Diff::from(*diff))
376 },
377 ));
378
379 builder.push_into((time, (update.tracker_id, &massaged)));
380 massaged.clear();
381 }
382 }
383 while let Some(container) = builder.extract() {
384 logger.publish_batch(std::mem::take(container));
385 activator.activate();
387 }
388 }
389 } else {
390 while let Some(container) = builder.finish() {
392 logger.publish_batch(std::mem::take(container));
393 activator.activate();
395 }
396
397 if logger.report_progress(*batch_time) {
398 activator.activate();
399 }
400 }
401 };
402
403 Logger::new(self.now, self.start_offset, action)
404 }
405}
406
407trait ExtractTimestamp: Clone + 'static {
409 fn extract(&self) -> Timestamp;
411}
412
413impl ExtractTimestamp for Timestamp {
414 fn extract(&self) -> Timestamp {
415 *self
416 }
417}
418
419impl ExtractTimestamp for Product<Timestamp, PointStamp<u64>> {
420 fn extract(&self) -> Timestamp {
421 self.outer
422 }
423}
424
425impl ExtractTimestamp for (Timestamp, Subtime) {
426 fn extract(&self) -> Timestamp {
427 self.0
428 }
429}