mz_compute/render/errors.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Helpers for handling errors encountered by operators.
11
12use std::hash::Hash;
13
14use differential_dataflow::ExchangeData;
15use differential_dataflow::containers::Columnation;
16use mz_repr::Row;
17
18use crate::render::context::ShutdownToken;
19
20/// Used to make possibly-validating code generic: think of this as a kind of `MaybeResult`,
21/// specialized for use in compute. Validation code will only run when the error constructor is
22/// Some.
23pub(super) trait MaybeValidatingRow<T, E>: ExchangeData + Columnation + Hash {
24 fn ok(t: T) -> Self;
25 fn into_error() -> Option<fn(E) -> Self>;
26}
27
28impl<E> MaybeValidatingRow<Row, E> for Row {
29 fn ok(t: Row) -> Self {
30 t
31 }
32
33 fn into_error() -> Option<fn(E) -> Self> {
34 None
35 }
36}
37
38impl<E> MaybeValidatingRow<(), E> for () {
39 fn ok(t: ()) -> Self {
40 t
41 }
42
43 fn into_error() -> Option<fn(E) -> Self> {
44 None
45 }
46}
47
48impl<E, R> MaybeValidatingRow<Vec<R>, E> for Vec<R>
49where
50 R: ExchangeData + Columnation + Hash,
51{
52 fn ok(t: Vec<R>) -> Self {
53 t
54 }
55
56 fn into_error() -> Option<fn(E) -> Self> {
57 None
58 }
59}
60
61impl<T, E> MaybeValidatingRow<T, E> for Result<T, E>
62where
63 T: ExchangeData + Columnation + Hash,
64 E: ExchangeData + Columnation + Hash,
65{
66 fn ok(row: T) -> Self {
67 Ok(row)
68 }
69
70 fn into_error() -> Option<fn(E) -> Self> {
71 Some(Err)
72 }
73}
74
75/// Error logger to be used by rendering code.
76///
77/// Holds onto a token to ensure that no false-positive errors are logged while the dataflow is in
78/// the process of shutting down.
79#[derive(Clone)]
80pub(super) struct ErrorLogger {
81 token: ShutdownToken,
82 dataflow_name: String,
83}
84
85impl ErrorLogger {
86 pub fn new(token: ShutdownToken, dataflow_name: String) -> Self {
87 Self {
88 token,
89 dataflow_name,
90 }
91 }
92
93 /// Log the given error, unless the dataflow is shutting down.
94 ///
95 /// The logging format is optimized for surfacing errors with Sentry:
96 /// * `error` is logged at ERROR level and will appear as the error title in Sentry.
97 /// We require it to be a static string, to ensure that Sentry always merges instances of
98 /// the same error together.
99 /// * `details` is logged at WARN level and will appear in the breadcrumbs.
100 /// Put relevant dynamic information here.
101 ///
102 /// The message that's logged at WARN level has the format
103 /// "[customer-data] {message} ({details})"
104 /// We include the [customer-data] tag out of the expectation that `details` will always
105 /// contain some sensitive customer data. We include the `message` to make it possible to match
106 /// the breadcrumbs to their associated error in Sentry.
107 ///
108 // TODO(database-issues#5362): Rethink or justify our error logging strategy.
109 pub fn log(&self, message: &'static str, details: &str) {
110 if !self.token.in_shutdown() {
111 self.log_always(message, details);
112 }
113 }
114
115 /// Like [`Self::log`], but also logs errors when the dataflow is shutting down.
116 ///
117 /// Use this method to notify about errors that cannot be caused by dataflow shutdown.
118 pub fn log_always(&self, message: &'static str, details: &str) {
119 tracing::warn!(
120 dataflow = self.dataflow_name,
121 "[customer-data] {message} ({details})"
122 );
123 tracing::error!(message);
124 }
125
126 /// Like [`Self::log_always`], but panics in debug mode.
127 ///
128 /// Use this method to notify about errors that are certainly caused by bugs in Materialize.
129 pub fn soft_panic_or_log(&self, message: &'static str, details: &str) {
130 tracing::warn!(
131 dataflow = self.dataflow_name,
132 "[customer-data] {message} ({details})"
133 );
134 mz_ore::soft_panic_or_log!("{}", message);
135 }
136}