Skip to content

Commit 8ac5c2a

Browse files
ggevayjunie-agent
andcommitted
Unify negative-multiplicity errors into EvalError::MultiplicityError
Replace the scattered "internal error: ..." messages raised on negative/non-positive/non-monotonic multiplicities and inconsistent accumulators with a single structured EvalError variant, MultiplicityError { kind, code_place, detail }. The new Display does not use the "internal error:" prefix; it names the likely root cause (invalid data in a Materialize source, caused either by a bug in the external system feeding the source or by a bug in Materialize) and mentions incorrect usage of the repeat_row table function as a secondary cause. Migrated sites: * src/compute/src/render/reduce.rs (DistinctBy, ReduceInaccumulable, ReduceMinsMaxes, MinsMaxesHierarchical, hierarchical mins-maxes final, ReduceMonotonic, AccumulableErrorCheck net-zero + unsigned-sum). * src/compute/src/render/top_k.rs (MonotonicTopK, TopK validator, MonotonicTop1). * src/compute/src/compute_state.rs (persist peek, index peek error trace) and compute_state/peek_result_iterator.rs (index peek ok trace), using err.to_string() to feed PeekResponse::Error. * src/adapter/src/coord/peek.rs and src/adapter/src/peek_client.rs constant-result fast paths: emit EvalError::MultiplicityError instead of a hand-rolled InvalidParameterValue / AdapterError::Unstructured. * src/storage-operators/src/s3_oneshot_sink.rs: build and stringify a MultiplicityError, mirroring the [customer-data] logging contract inline (storage-operators cannot depend on compute). * src/expr/src/relation/func.rs (scalar-subquery cardinality check): the old EvalError::NegativeRowsFromSubquery variant is removed. Internal error reporting is unified: every MultiplicityError site logs through log_multiplicity_error (free function in render/errors.rs, and a thin forwarding method on ErrorLogger). All sites share the static title "invalid record multiplicity", so they merge into a single Sentry group; kind/code_place/detail go into a [customer-data]-tagged WARN breadcrumb. Additionally fix the reduce.rs monoid-variant mismatch site: the lhs/rhs Row values no longer leak into the panic/Sentry title; a static title helps with Sentry's issue merging. Proto backcompat is not needed for EvalError::NegativeRowsFromSubquery because it was introduced this week and hasn't shipped in any release; its proto tag (83) is reused by the new ProtoMultiplicityError. Co-authored-by: Junie <junie@jetbrains.com>
1 parent 92730d3 commit 8ac5c2a

20 files changed

Lines changed: 432 additions & 155 deletions

File tree

src/adapter/src/coord/peek.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -676,9 +676,11 @@ impl crate::coord::Coordinator {
676676
let mut results = Vec::new();
677677
for (row, count) in rows {
678678
if count.is_negative() {
679-
Err(EvalError::InvalidParameterValue(
680-
format!("Negative multiplicity in constant result: {}", count).into(),
681-
))?
679+
Err(EvalError::MultiplicityError(mz_expr::MultiplicityError {
680+
kind: mz_expr::MultiplicityErrorKind::Negative,
681+
code_place: "constant result".into(),
682+
detail: format!("count={}", count).into(),
683+
}))?
682684
};
683685
if count.is_positive() {
684686
let count = usize::cast_from(

src/adapter/src/peek_client.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,12 @@ impl PeekClient {
265265
let count = match u64::try_from(count.into_inner()) {
266266
Ok(u) => usize::cast_from(u),
267267
Err(_) => {
268-
return Err(AdapterError::Unstructured(anyhow::anyhow!(
269-
"Negative multiplicity in constant result: {}",
270-
count
268+
return Err(AdapterError::Eval(mz_expr::EvalError::MultiplicityError(
269+
mz_expr::MultiplicityError {
270+
kind: mz_expr::MultiplicityErrorKind::Negative,
271+
code_place: "constant result".into(),
272+
detail: format!("count={}", count).into(),
273+
},
271274
)));
272275
}
273276
};

src/compute/src/compute_state.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,15 +1345,17 @@ impl PersistPeek {
13451345
}
13461346

13471347
let count: usize = d.try_into().map_err(|_| {
1348-
error!(
1349-
shard = %metadata.data_shard, diff = d, ?row,
1350-
"persist peek encountered negative multiplicities",
1351-
);
1352-
format!(
1353-
"Invalid data in source, \
1354-
saw retractions ({}) for row that does not exist: {:?}",
1355-
-d, row,
1356-
)
1348+
let err = mz_expr::MultiplicityError {
1349+
kind: mz_expr::MultiplicityErrorKind::Negative,
1350+
code_place: "persist peek".into(),
1351+
detail: format!(
1352+
"shard={}, diff={}, row={:?}",
1353+
metadata.data_shard, d, row,
1354+
)
1355+
.into(),
1356+
};
1357+
crate::render::errors::log_multiplicity_error(&err, "persist peek");
1358+
err.to_string()
13571359
})?;
13581360
let Some(count) = NonZeroUsize::new(count) else {
13591361
continue;
@@ -1491,15 +1493,19 @@ impl IndexPeek {
14911493
});
14921494
if copies.is_negative() {
14931495
let error = cursor.key(&storage);
1494-
error!(
1495-
target = %self.peek.target.id(), diff = %copies, %error,
1496-
"index peek encountered negative multiplicities in error trace",
1497-
);
1498-
return PeekStatus::Ready(PeekResponse::Error(format!(
1499-
"Invalid data in source errors, \
1500-
saw retractions ({}) for row that does not exist: {}",
1501-
-copies, error,
1502-
)));
1496+
let err = mz_expr::MultiplicityError {
1497+
kind: mz_expr::MultiplicityErrorKind::Negative,
1498+
code_place: "index peek error trace".into(),
1499+
detail: format!(
1500+
"target={}, diff={}, error={}",
1501+
self.peek.target.id(),
1502+
copies,
1503+
error,
1504+
)
1505+
.into(),
1506+
};
1507+
crate::render::errors::log_multiplicity_error(&err, "index peek error trace");
1508+
return PeekStatus::Ready(PeekResponse::Error(err.to_string()));
15031509
}
15041510
if copies.is_positive() {
15051511
return PeekStatus::Ready(PeekResponse::Error(cursor.key(&storage).to_string()));

src/compute/src/compute_state/peek_result_iterator.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,17 @@ where
247247
});
248248
let copies: i64 = if copies.is_negative() {
249249
let row = &*borrow;
250-
tracing::error!(
251-
target = %self.target_id, diff = %copies, ?row,
252-
"index peek encountered negative multiplicities in ok trace",
253-
);
254-
return Err(format!(
255-
"Invalid data in source, \
256-
saw retractions ({}) for row that does not exist: {:?}",
257-
-copies, row,
258-
));
250+
let err = mz_expr::MultiplicityError {
251+
kind: mz_expr::MultiplicityErrorKind::Negative,
252+
code_place: "index peek".into(),
253+
detail: format!(
254+
"target={}, diff={}, row={:?}",
255+
self.target_id, copies, row,
256+
)
257+
.into(),
258+
};
259+
crate::render::errors::log_multiplicity_error(&err, "index peek");
260+
return Err(err.to_string());
259261
} else {
260262
copies.into_inner()
261263
};

src/compute/src/render.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ use crate::typedefs::{ErrBatcher, ErrBuilder, ErrSpine, KeyBatcher, MzTimestamp}
166166

167167
pub mod context;
168168
pub(crate) mod continual_task;
169-
mod errors;
169+
pub(crate) mod errors;
170170
mod flat_map;
171171
mod join;
172172
mod reduce;

src/compute/src/render/errors.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,33 @@
99

1010
//! Helpers for handling errors encountered by operators.
1111
12+
use mz_expr::MultiplicityError;
1213
use mz_repr::Row;
1314

15+
/// Shared static title for every [`MultiplicityError`], so all occurrences
16+
/// merge into a single Sentry group regardless of kind or site.
17+
pub(crate) const MULTIPLICITY_ERROR_TITLE: &str = "invalid record multiplicity";
18+
19+
/// Log a [`MultiplicityError`] using the standard "static title + [customer-data]
20+
/// tagged details" contract, without needing an [`ErrorLogger`] instance.
21+
///
22+
/// Use this from call sites that don't have an [`ErrorLogger`] in scope (peek
23+
/// paths, S3 oneshot sink, constant-result fast path in the coordinator).
24+
/// `context` is a short, static-ish string identifying where the logger is
25+
/// being called from (e.g. `"index peek"`, `"persist peek"`,
26+
/// `"S3 oneshot sink"`).
27+
pub(crate) fn log_multiplicity_error(err: &MultiplicityError, context: &str) {
28+
tracing::warn!(
29+
context = context,
30+
"[customer-data] {} (kind={:?}, code_place={}, detail={})",
31+
MULTIPLICITY_ERROR_TITLE,
32+
err.kind,
33+
err.code_place,
34+
err.detail,
35+
);
36+
tracing::error!(MULTIPLICITY_ERROR_TITLE);
37+
}
38+
1439
/// Used to make possibly-validating code generic: think of this as a kind of `MaybeResult`,
1540
/// specialized for use in compute. Validation code will only run when the error constructor is
1641
/// Some.
@@ -87,6 +112,7 @@ impl ErrorLogger {
87112
/// the breadcrumbs to their associated error in Sentry.
88113
///
89114
// TODO(database-issues#5362): Rethink or justify our error logging strategy.
115+
#[allow(dead_code)] // kept for future non-multiplicity uses
90116
pub fn log(&self, message: &'static str, details: &str) {
91117
tracing::warn!(
92118
dataflow = self.dataflow_name,
@@ -105,4 +131,22 @@ impl ErrorLogger {
105131
);
106132
mz_ore::soft_panic_or_log!("{}", message);
107133
}
134+
135+
/// Log a [`MultiplicityError`] using the shared `MULTIPLICITY_ERROR_TITLE`
136+
/// so that all multiplicity errors merge into a single Sentry group.
137+
///
138+
/// The dynamic `kind`, `code_place`, and `detail` fields are emitted at
139+
/// WARN level with the `[customer-data]` tag, alongside the dataflow
140+
/// name of this `ErrorLogger`.
141+
pub fn log_multiplicity_error(&self, err: &MultiplicityError) {
142+
tracing::warn!(
143+
dataflow = self.dataflow_name,
144+
"[customer-data] {} (kind={:?}, code_place={}, detail={})",
145+
MULTIPLICITY_ERROR_TITLE,
146+
err.kind,
147+
err.code_place,
148+
err.detail,
149+
);
150+
tracing::error!(MULTIPLICITY_ERROR_TITLE);
151+
}
108152
}

0 commit comments

Comments
 (0)