Skip to content

Commit 11b9571

Browse files
committed
Cleanup error handling
1 parent 5f17d4b commit 11b9571

7 files changed

Lines changed: 37 additions & 83 deletions

File tree

.changeset/error-handling.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
hive-router: patch
3+
---
4+
5+
Cleanup Error Handling implementation

bin/router/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ async fn graphql_endpoint_dispatch(
144144
// If the request handler returns an error, convert it to an HTTP response.
145145
Err(err) => {
146146
write_graphql_response_metric_status(request, GraphQLResponseStatus::Error);
147-
handle_pipeline_error(err, &app_state, &response_mode)
147+
handle_pipeline_error(&err, &app_state, &response_mode)
148148
}
149149
};
150150

bin/router/src/pipeline/error.rs

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{sync::Arc, vec};
22

3-
use graphql_tools::validation::utils::ValidationError;
3+
use graphql_tools::{parser::query::MinifyError, validation::utils::ValidationError};
44
use hive_router_plan_executor::{
55
execution::{error::PlanExecutionError, jwt_forward::JwtForwardingError},
66
headers::errors::HeaderRuleRuntimeError,
@@ -69,13 +69,13 @@ pub enum PipelineError {
6969
FailedToParseExtensions(sonic_rs::Error),
7070
#[error("Failed to parse GraphQL operation: {0}")]
7171
#[strum(serialize = "GRAPHQL_PARSE_FAILED")]
72-
FailedToParseOperation(#[from] Arc<graphql_tools::parser::query::ParseError>),
72+
FailedToParseOperation(#[from] graphql_tools::parser::query::ParseError),
7373
#[error("Failed to minify parsed GraphQL operation: {0}")]
7474
#[strum(serialize = "GRAPHQL_PARSE_MINIFY_FAILED")]
75-
FailedToMinifyParsedOperation(String),
75+
FailedToMinifyParsedOperation(#[from] MinifyError),
7676
#[error("Failed to normalize GraphQL operation")]
7777
#[strum(serialize = "OPERATION_RESOLUTION_FAILURE")]
78-
NormalizationError(#[from] Arc<NormalizationError>),
78+
NormalizationError(#[from] NormalizationError),
7979
#[error("Failed to collect GraphQL variables: {0}")]
8080
#[strum(serialize = "BAD_USER_INPUT")]
8181
VariablesCoercionError(String),
@@ -90,7 +90,7 @@ pub enum PipelineError {
9090
PlanExecutionError(#[from] PlanExecutionError),
9191
#[error("Failed to produce a plan: {0}")]
9292
#[strum(serialize = "QUERY_PLAN_BUILD_FAILED")]
93-
PlannerError(#[from] Arc<PlannerError>),
93+
PlannerError(#[from] PlannerError),
9494
#[error(transparent)]
9595
#[strum(serialize = "OVERRIDE_LABEL_EVALUATION_FAILED")]
9696
LabelEvaluationError(#[from] LabelEvaluationError),
@@ -143,30 +143,9 @@ pub enum PipelineError {
143143
#[error("No supergraph available yet, unable to process request")]
144144
#[strum(serialize = "NO_SUPERGRAPH_AVAILABLE")]
145145
NoSupergraphAvailable,
146-
}
147-
148-
#[derive(Clone, Debug, thiserror::Error)]
149-
pub enum ParserCacheError {
150-
#[error("Failed to parse GraphQL operation: {0}")]
151-
ParseError(Arc<graphql_tools::parser::query::ParseError>),
152-
#[error("Failed to minify parsed GraphQL operation: {0}")]
153-
MinifyError(String),
154-
#[error("Validation errors")]
155-
ValidationErrors(Arc<Vec<ValidationError>>),
156-
}
157146

158-
impl From<Arc<ParserCacheError>> for PipelineError {
159-
fn from(value: Arc<ParserCacheError>) -> Self {
160-
match value.as_ref() {
161-
ParserCacheError::ParseError(err) => PipelineError::FailedToParseOperation(err.clone()),
162-
ParserCacheError::MinifyError(err) => {
163-
PipelineError::FailedToMinifyParsedOperation(err.clone())
164-
}
165-
ParserCacheError::ValidationErrors(errs) => {
166-
PipelineError::ValidationErrors(errs.clone())
167-
}
168-
}
169-
}
147+
#[error(transparent)]
148+
PipelineErrorArc(#[from] Arc<PipelineError>),
170149
}
171150

172151
impl PipelineError {
@@ -175,13 +154,15 @@ impl PipelineError {
175154
Self::JwtError(err) => err.error_code(),
176155
Self::PlanExecutionError(err) => err.error_code(),
177156
Self::ReadBodyStreamError(err) => err.error_code(),
157+
Self::PipelineErrorArc(err) => err.graphql_error_code(),
178158
_ => self.into(),
179159
}
180160
}
181161

182162
pub fn graphql_error_message(&self) -> String {
183163
match self {
184164
Self::PlannerError(_) => "Unexpected error".to_string(),
165+
Self::PipelineErrorArc(err) => err.graphql_error_message(),
185166
_ => self.to_string(),
186167
}
187168
}
@@ -226,6 +207,7 @@ impl PipelineError {
226207
(Self::HeaderPropagation(_), _) => StatusCode::INTERNAL_SERVER_ERROR,
227208
(Self::QueryPlanSerializationFailed(_), _) => StatusCode::INTERNAL_SERVER_ERROR,
228209
(Self::NoSupergraphAvailable, _) => StatusCode::SERVICE_UNAVAILABLE,
210+
(Self::PipelineErrorArc(err), _) => err.default_status_code(prefer_ok),
229211
}
230212
}
231213
}
@@ -237,10 +219,13 @@ struct FailedExecutionResult {
237219

238220
#[inline]
239221
pub fn handle_pipeline_error(
240-
err: PipelineError,
222+
err: &PipelineError,
241223
shared_state: &RouterSharedState,
242224
response_mode: &ResponseMode,
243225
) -> web::HttpResponse {
226+
if let PipelineError::PipelineErrorArc(inner) = &err {
227+
return handle_pipeline_error(inner, shared_state, response_mode);
228+
}
244229
let single_content_type = response_mode.single_content_type();
245230

246231
let prefer_ok = response_mode.prefer_status_ok_for_errors();

bin/router/src/pipeline/normalize.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use hive_router_plan_executor::hooks::on_graphql_params::GraphQLParams;
88
use hive_router_plan_executor::hooks::on_supergraph_load::SupergraphData;
99
use hive_router_plan_executor::introspection::partition::partition_operation;
1010
use hive_router_plan_executor::projection::plan::FieldProjectionPlan;
11-
use hive_router_query_planner::ast::normalization::error::NormalizationError;
1211
use hive_router_query_planner::ast::normalization::normalize_operation;
1312
use hive_router_query_planner::ast::operation::OperationDefinition;
1413
use xxhash_rust::xxh3::Xxh3;
@@ -68,10 +67,10 @@ pub async fn normalize_request_with_cache(
6867
None => parser_payload.cache_key,
6968
};
7069

71-
schema_state
70+
Ok(schema_state
7271
.normalize_cache
7372
.entry(cache_key)
74-
.or_try_insert_with::<_, NormalizationError>(async {
73+
.or_try_insert_with(async {
7574
let doc = normalize_operation(
7675
&supergraph.planner.supergraph,
7776
&parser_payload.parsed_operation,
@@ -106,7 +105,6 @@ pub async fn normalize_request_with_cache(
106105
Ok(Arc::new(payload))
107106
})
108107
.await
109-
.map_err(PipelineError::from)
110108
.into_result_with_hit_miss(|hit_miss| match hit_miss {
111109
CacheHitMiss::Hit => {
112110
normalize_span.record_cache_hit(true);
@@ -116,7 +114,7 @@ pub async fn normalize_request_with_cache(
116114
normalize_span.record_cache_hit(false);
117115
normalize_cache_capture.finish_miss();
118116
}
119-
})
117+
})?)
120118
}
121119
.instrument(normalize_span.clone())
122120
.await

bin/router/src/pipeline/parser.rs

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use hive_router_query_planner::utils::parsing::{
2121
use xxhash_rust::xxh3::Xxh3;
2222

2323
use crate::cache_state::{CacheHitMiss, EntryResultHitMissExt};
24-
use crate::pipeline::error::{ParserCacheError, PipelineError};
24+
use crate::pipeline::error::PipelineError;
2525
use crate::pipeline::execution_request::GetQueryStr;
2626
use crate::shared_state::RouterSharedState;
2727
use tracing::{error, trace, Instrument};
@@ -33,35 +33,6 @@ pub struct ParseCacheEntry {
3333
hive_operation_hash: Arc<String>,
3434
}
3535

36-
impl ParseCacheEntry {
37-
#[inline]
38-
pub fn try_new(
39-
parsed_arc: Arc<Document<'static, String>>,
40-
query_str: &str,
41-
) -> Result<Self, PipelineError> {
42-
let minified_arc = {
43-
Arc::new(minify_query(query_str).map_err(|err| {
44-
error!("Failed to minify parsed GraphQL operation: {}", err);
45-
PipelineError::FailedToMinifyParsedOperation(err.to_string())
46-
})?)
47-
};
48-
let hive_normalized_operation = hive_sdk_normalize_operation(&parsed_arc);
49-
let hive_minified =
50-
minify_query(hive_normalized_operation.to_string().as_ref()).map_err(|err| {
51-
error!(
52-
"Failed to minify GraphQL operation normalized for Hive SDK: {}",
53-
err
54-
);
55-
PipelineError::FailedToMinifyParsedOperation(err.to_string())
56-
})?;
57-
Ok(ParseCacheEntry {
58-
document: parsed_arc,
59-
document_minified_string: minified_arc,
60-
hive_operation_hash: Arc::new(format!("{:x}", md5::compute(hive_minified))),
61-
})
62-
}
63-
}
64-
6536
#[derive(Debug)]
6637
pub struct GraphQLParserPayload {
6738
pub parsed_operation: Arc<Document<'static, String>>,
@@ -136,7 +107,7 @@ pub async fn parse_operation_with_cache(
136107
let parse_cache_item = app_state
137108
.parse_cache
138109
.entry(cache_key)
139-
.or_try_insert_with::<_, ParserCacheError>(async {
110+
.or_try_insert_with(async {
140111
let parsed = match app_state.router_config.limits.max_tokens.as_ref() {
141112
Some(cfg) => safe_parse_operation_with_token_limit(query_str, cfg.n),
142113
_ => safe_parse_operation(query_str),
@@ -146,7 +117,7 @@ pub async fn parse_operation_with_cache(
146117
err.0.errors.first()
147118
{
148119
if *msg == "Token limit exceeded" {
149-
return ParserCacheError::ValidationErrors(
120+
return PipelineError::ValidationErrors(
150121
vec![ValidationError {
151122
locations: vec![err.0.position],
152123
message: "Token limit exceeded.".to_string(),
@@ -157,22 +128,20 @@ pub async fn parse_operation_with_cache(
157128
}
158129
}
159130
error!("Failed to parse GraphQL operation: {}", err);
160-
ParserCacheError::ParseError(Arc::new(err))
131+
err.into()
161132
})?;
162133
trace!("successfully parsed GraphQL operation");
163134
let parsed_arc = Arc::new(parsed);
164-
let minified_arc = Arc::new(minify_query(query_str).map_err(|err| {
135+
let minified_arc = Arc::new(minify_query(query_str).inspect_err(|err| {
165136
error!("Failed to minify parsed GraphQL operation: {}", err);
166-
ParserCacheError::MinifyError(err.to_string())
167137
})?);
168138
let hive_normalized_operation = hive_sdk_normalize_operation(&parsed_arc);
169139
let hive_minified = minify_query(hive_normalized_operation.to_string().as_ref())
170-
.map_err(|err| {
140+
.inspect_err(|err| {
171141
error!(
172142
"Failed to minify GraphQL operation normalized for Hive SDK: {}",
173143
err
174144
);
175-
ParserCacheError::MinifyError(err.to_string())
176145
})?;
177146

178147
Ok(ParseCacheEntry {
@@ -182,7 +151,6 @@ pub async fn parse_operation_with_cache(
182151
})
183152
})
184153
.await
185-
.map_err(PipelineError::from)
186154
.into_result_with_hit_miss(|hit_miss| match hit_miss {
187155
CacheHitMiss::Hit => {
188156
parse_span.record_cache_hit(true);

bin/router/src/pipeline/query_plan.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,15 @@ pub async fn plan_operation_with_cache(
109109
return Ok(EMPTY_QUERY_PLAN.clone());
110110
}
111111

112-
supergraph
113-
.planner
114-
.plan_from_normalized_operation(
115-
filtered_operation_for_plan,
116-
(&request_override_context.clone()).into(),
117-
cancellation_token,
118-
)
119-
.map(Arc::new)
112+
let query_plan = supergraph.planner.plan_from_normalized_operation(
113+
filtered_operation_for_plan,
114+
(&request_override_context.clone()).into(),
115+
cancellation_token,
116+
)?;
117+
118+
Ok(query_plan.into())
120119
})
121120
.await
122-
.map_err(PipelineError::from)
123121
.into_result_with_hit_miss(|hit_miss| match hit_miss {
124122
CacheHitMiss::Hit => {
125123
cache_hint = CacheHint::Hit;

lib/graphql-tools/src/parser/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ mod minify;
99
pub use self::ast::*;
1010
pub use self::error::ParseError;
1111
pub use self::grammar::{consume_definition, parse_query, parse_query_with_token_limit};
12-
pub use self::minify::{minify_query, minify_query_document};
12+
pub use self::minify::{minify_query, minify_query_document, MinifyError};

0 commit comments

Comments
 (0)