Skip to content

Commit 42d52a5

Browse files
gautamg795Convex, Inc.
authored andcommitted
add a source field to ErrorMetadata to prevent re-reporting (#34444)
Adds a `source` field to `ErrorMetadata` that is populated when we (attempt to) report an error to Sentry. We'll check this field in `should_report_to_sentry`, so errors that are sent across the wire to downstream callers won't be re-reported. GitOrigin-RevId: 3407b1d3e621abae6376c4430af6eb7bf85d5536
1 parent 161e326 commit 42d52a5

File tree

6 files changed

+52
-7
lines changed

6 files changed

+52
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/common/src/errors.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ pub use errors::{
1616
INTERNAL_SERVER_ERROR,
1717
INTERNAL_SERVER_ERROR_MSG,
1818
};
19-
use metrics::log_counter;
19+
use metrics::{
20+
log_counter,
21+
SERVICE_NAME,
22+
};
2023
use pb::common::{
2124
FrameData as FrameDataProto,
2225
JsError as JsErrorProto,
@@ -122,17 +125,30 @@ pub fn report_error_sync(err: &mut anyhow::Error) {
122125
log_errors_reported_total(label);
123126
}
124127

125-
if let Some(e) = err.downcast_ref::<ErrorMetadata>() {
126-
if let Some(counter) = e.custom_metric() {
127-
log_counter(counter, 1);
128-
}
129-
}
130-
131128
tracing::error!(
132129
"Caught error (RUST_BACKTRACE=1 RUST_LOG=info,{}=debug for full trace): {err:#}",
133130
module_path!(),
134131
);
135132
tracing::debug!("{err:?}");
133+
134+
if let Some(e) = err.downcast_mut::<ErrorMetadata>() {
135+
if let Some(counter) = e.custom_metric() {
136+
log_counter(counter, 1);
137+
}
138+
// Set the source of this error to the service name if it's not already set,
139+
// denoting that this error has been reported and downstream callers that
140+
// receive this error need not re-report it.
141+
match &e.source {
142+
Some(source) => {
143+
tracing::debug!("Not reporting above error: already reported by {source}");
144+
return;
145+
},
146+
None => {
147+
e.source = Some(SERVICE_NAME.clone());
148+
},
149+
}
150+
}
151+
136152
let Some(sentry_client) = sentry::Hub::current().client() else {
137153
tracing::error!("Not reporting above error: Sentry is not configured");
138154
return;

crates/errors/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ proptest-derive = { workspace = true, optional = true }
1818
sentry = { workspace = true }
1919
thiserror = { workspace = true }
2020
tonic = { workspace = true }
21+
tracing = { workspace = true }
2122
tungstenite = { workspace = true }
2223

2324
[dev-dependencies]

crates/errors/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub struct ErrorMetadata {
3636
/// human readable - developer facing. Should be longer and descriptive.
3737
/// Eg "The module name is invalid because it contains an invalid character"
3838
pub msg: Cow<'static, str>,
39+
40+
// Optional source of the error (i.e. a service name)
41+
// If present, this implies that the error originated in an upstream
42+
// service call (and may have already been reported to Sentry).
43+
pub r#source: Option<String>,
3944
}
4045

4146
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
@@ -74,6 +79,7 @@ impl ErrorMetadata {
7479
code,
7580
short_msg: Cow::Borrowed(""),
7681
msg: Cow::Borrowed(""),
82+
source: None,
7783
}
7884
}
7985

@@ -89,6 +95,7 @@ impl ErrorMetadata {
8995
code: ErrorCode::BadRequest,
9096
short_msg: short_msg.into(),
9197
msg: msg.into(),
98+
source: None,
9299
}
93100
}
94101

@@ -109,6 +116,7 @@ impl ErrorMetadata {
109116
code: ErrorCode::NotFound,
110117
short_msg: short_msg.into(),
111118
msg: msg.into(),
119+
source: None,
112120
}
113121
}
114122

@@ -125,6 +133,7 @@ impl ErrorMetadata {
125133
code: ErrorCode::Unauthenticated,
126134
short_msg: short_msg.into(),
127135
msg: msg.into(),
136+
source: None,
128137
}
129138
}
130139

@@ -143,6 +152,7 @@ impl ErrorMetadata {
143152
code: ErrorCode::AuthUpdateFailed,
144153
short_msg: short_msg.into(),
145154
msg: msg.into(),
155+
source: None,
146156
}
147157
}
148158

@@ -159,6 +169,7 @@ impl ErrorMetadata {
159169
code: ErrorCode::Forbidden,
160170
short_msg: short_msg.into(),
161171
msg: msg.into(),
172+
source: None,
162173
}
163174
}
164175

@@ -168,6 +179,7 @@ impl ErrorMetadata {
168179
code: ErrorCode::ClientDisconnect,
169180
short_msg: CLIENT_DISCONNECTED.into(),
170181
msg: CLIENT_DISCONNECTED_MSG.into(),
182+
source: None,
171183
}
172184
}
173185

@@ -179,6 +191,7 @@ impl ErrorMetadata {
179191
code: ErrorCode::MisdirectedRequest,
180192
short_msg: "MisdirectedRequest".into(),
181193
msg: "Instance not served by this Conductor".into(),
194+
source: None,
182195
}
183196
}
184197

@@ -200,6 +213,7 @@ impl ErrorMetadata {
200213
code: ErrorCode::RateLimited,
201214
short_msg: short_msg.into(),
202215
msg: msg.into(),
216+
source: None,
203217
}
204218
}
205219

@@ -213,6 +227,7 @@ impl ErrorMetadata {
213227
code: ErrorCode::OperationalInternalServerError,
214228
short_msg: INTERNAL_SERVER_ERROR.into(),
215229
msg: INTERNAL_SERVER_ERROR_MSG.into(),
230+
source: None,
216231
}
217232
}
218233

@@ -241,6 +256,7 @@ impl ErrorMetadata {
241256
code: ErrorCode::Overloaded,
242257
short_msg: short_msg.into(),
243258
msg: msg.into(),
259+
source: None,
244260
}
245261
}
246262

@@ -258,6 +274,7 @@ impl ErrorMetadata {
258274
code: ErrorCode::RejectedBeforeExecution,
259275
short_msg: short_msg.into(),
260276
msg: msg.into(),
277+
source: None,
261278
}
262279
}
263280

@@ -274,6 +291,7 @@ impl ErrorMetadata {
274291
},
275292
short_msg: OCC_ERROR.into(),
276293
msg: OCC_ERROR_MSG.into(),
294+
source: None,
277295
}
278296
}
279297

@@ -306,6 +324,7 @@ impl ErrorMetadata {
306324
subsequent retry. {write_source_description}See https://docs.convex.dev/error#1",
307325
)
308326
.into(),
327+
source: None,
309328
}
310329
}
311330

@@ -314,6 +333,7 @@ impl ErrorMetadata {
314333
code: ErrorCode::Overloaded,
315334
short_msg: "ServiceUnavailable".into(),
316335
msg: "Service temporarily unavailable".into(),
336+
source: None,
317337
}
318338
}
319339

@@ -326,6 +346,7 @@ impl ErrorMetadata {
326346
code: ErrorCode::OutOfRetention,
327347
short_msg: INTERNAL_SERVER_ERROR.into(),
328348
msg: INTERNAL_SERVER_ERROR_MSG.into(),
349+
source: None,
329350
}
330351
}
331352

@@ -343,6 +364,7 @@ impl ErrorMetadata {
343364
code: ErrorCode::PaginationLimit,
344365
short_msg: short_msg.into(),
345366
msg: msg.into(),
367+
source: None,
346368
}
347369
}
348370

@@ -356,6 +378,7 @@ impl ErrorMetadata {
356378
code,
357379
short_msg: short_msg.into(),
358380
msg: msg.into(),
381+
source: None,
359382
})
360383
}
361384

@@ -442,6 +465,7 @@ impl ErrorMetadata {
442465
if self.short_msg.is_empty() {
443466
return None;
444467
}
468+
445469
match self.code {
446470
ErrorCode::ClientDisconnect => None,
447471
ErrorCode::BadRequest

crates/pb/protos/errors.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ message ErrorMetadata {
3131
optional string short_msg = 2;
3232
optional string msg = 3;
3333
optional OccInfo occ_info = 4;
34+
optional string source = 5;
3435
}
3536

3637
// The message we put in tonic::Status details.

crates/pb/src/error_metadata.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl From<ErrorMetadata> for ErrorMetadataProto {
8585
}),
8686
_ => None,
8787
},
88+
source: metadata.source,
8889
}
8990
}
9091
}
@@ -101,6 +102,7 @@ impl TryFrom<ErrorMetadataProto> for ErrorMetadata {
101102
code,
102103
short_msg: short_msg.into(),
103104
msg: msg.into(),
105+
source: metadata.source,
104106
})
105107
}
106108
}

0 commit comments

Comments
 (0)