-
-
Notifications
You must be signed in to change notification settings - Fork 1
ingest-router: Simplify handler type #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This chnage simplifies the handler trait so it is no longer generic. The generics solution does not work well for our use case since generics requires the concrete type to be known at compile time, however in our case the router picks the handler dynamically at runtime based on inspecting the incoming route. With Arc<dyn Handler>, we can more easily dyanamically dispatch to the appropriate handler based on the matched route. Each handler receives the raw requests/responses and is responsible for splitting and merging them as needed.
ingest-router/src/lib.rs
Outdated
| let (split, _metadata) = | ||
| handler.split_request(handler_req, &cells).await.unwrap(); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| // Downcast metadata to our specific type | ||
| if let Ok(project_metadata) = metadata.downcast::<ProjectConfigsMetadata>() { | ||
| merged.pending_keys.extend(*project_metadata); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is kind of the downside of this approach -- it's probably worth attempting to write a macro or enum here to avoid this need to manually downcast to the real type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a TODO for later
| assert!(pending.contains(&serde_json::json!("key_routing_failed"))); | ||
| assert!(pending.contains(&serde_json::json!("key_from_failed_cell1"))); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| let pending_from_split: ProjectConfigsMetadata = vec![ | ||
| "key_routing_failed".to_string(), | ||
| "key_from_failed_cell1".to_string(), | ||
| "key_from_failed_cell2".to_string(), | ||
| ]; |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| #[derive(Default, Debug)] | ||
| struct ProjectConfigsMetadata { | ||
| // keys that are assigned to a cell | ||
| cell_to_keys: HashMap<CellId, Vec<String>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has to be added to the metadata -- if the request to a cell fails for any reason, we can look it up on this map and add the keys to pending during the merge phase
| // TODO: The current implementation does not handle errors from the results | ||
| // parameter. The edge case to be handled are if any of the upstreams failed | ||
| // to return a response for whatever reason. In scenarios like this, the | ||
| // executor needs to provide all the project config keys which failed to | ||
| // resolve on the upstream. We would need to add those project keys to the | ||
| // pending response. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is done now -- the split phase returns the cell to keys map, which allows us to figure out which keys are the failed ones from this function
|
|
||
| let response = service.call(request).await.unwrap(); | ||
|
|
||
| // TODO: call the scripts/mock_relay_api.py server and validate the response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a note for later -- we should add a true end to end test here
| pub async fn deserialize_body<T: DeserializeOwned>( | ||
| body: HandlerBody, | ||
| ) -> Result<T, IngestRouterError> { | ||
| let bytes = body.collect().await?.to_bytes(); | ||
| serde_json::from_slice(&bytes).map_err(|e| IngestRouterError::RequestBodyError(e.to_string())) | ||
| } | ||
|
|
||
| /// Serializes a value to a JSON body. | ||
| pub fn serialize_to_body<T: Serialize>(value: &T) -> Result<HandlerBody, IngestRouterError> { | ||
| let bytes = serde_json::to_vec(value).map(Bytes::from)?; | ||
| Ok(Full::new(bytes).map_err(|e| match e {}).boxed()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved these functions out of the project config implementation into utils as we can reuse them for any kind of json endpoint
| } | ||
|
|
||
| /// Common header normalization for all requests and responses. | ||
| pub fn normalize_headers(headers: &mut HeaderMap, version: Version) -> &mut HeaderMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pulled this out as well, as we can reuse it for any relay endpoint in both directions - requests sent upstream and responses back
| if let Ok(parsed) = deserialize_body::<ProjectConfigsResponse>(body).await { | ||
| merged.project_configs.extend(parsed.project_configs); | ||
| merged.extra_fields.extend(parsed.extra_fields); | ||
| merged.pending_keys.extend(parsed.pending_keys); | ||
| } |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| /// Get the cells for a specific locale | ||
| pub fn get_cells(&self, locale: &str) -> Option<&Cells> { | ||
| self.locale_to_cells.get(locale) | ||
| pub fn get_cells(&self, locale: &str) -> Option<Arc<Cells>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to Arc<Cells> since we pass Cells to a bunch of places now - and it's easier for the data to be owned
| } | ||
| } | ||
|
|
||
| #[cfg(test)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed these tests since ProjectConfigsRequest and ProjectConfigsResponse are barebones structs now -- i don't think there's too much value in testing builtin rust structures
This change does a few things:
to now add more than one handler.
The generics solution does not work well for our use case since it requires the concrete type to be known at compile time. In our case the router picks the handler dynamically at runtime based on inspecting the incoming route.
With
Arc<dyn Handler>, we can more easily dyanamically dispatch to the appropriate handler based on the matched route.Each handler receives the raw requests/responses and is responsible for splitting and merging them as needed.