-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added grpc based health check (#6441)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
- Loading branch information
1 parent
8fbb766
commit e922f96
Showing
13 changed files
with
285 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
crates/external_services/src/grpc_client/health_check_client.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
use std::{collections::HashMap, fmt::Debug}; | ||
|
||
use api_models::health_check::{HealthCheckMap, HealthCheckServices}; | ||
use common_utils::{errors::CustomResult, ext_traits::AsyncExt}; | ||
use error_stack::ResultExt; | ||
pub use health_check::{ | ||
health_check_response::ServingStatus, health_client::HealthClient, HealthCheckRequest, | ||
HealthCheckResponse, | ||
}; | ||
use router_env::logger; | ||
|
||
#[allow( | ||
missing_docs, | ||
unused_qualifications, | ||
clippy::unwrap_used, | ||
clippy::as_conversions, | ||
clippy::use_self | ||
)] | ||
pub mod health_check { | ||
tonic::include_proto!("grpc.health.v1"); | ||
} | ||
|
||
use super::{Client, DynamicRoutingClientConfig, GrpcClientSettings}; | ||
|
||
/// Result type for Dynamic Routing | ||
pub type HealthCheckResult<T> = CustomResult<T, HealthCheckError>; | ||
/// Dynamic Routing Errors | ||
#[derive(Debug, Clone, thiserror::Error)] | ||
pub enum HealthCheckError { | ||
/// The required input is missing | ||
#[error("Missing fields: {0} for building the Health check connection")] | ||
MissingFields(String), | ||
/// Error from gRPC Server | ||
#[error("Error from gRPC Server : {0}")] | ||
ConnectionError(String), | ||
/// status is invalid | ||
#[error("Invalid Status from server")] | ||
InvalidStatus, | ||
} | ||
|
||
/// Health Check Client type | ||
#[derive(Debug, Clone)] | ||
pub struct HealthCheckClient { | ||
/// Health clients for all gRPC based services | ||
pub clients: HashMap<HealthCheckServices, HealthClient<Client>>, | ||
} | ||
|
||
impl HealthCheckClient { | ||
/// Build connections to all gRPC services | ||
pub async fn build_connections( | ||
config: &GrpcClientSettings, | ||
client: Client, | ||
) -> Result<Self, Box<dyn std::error::Error>> { | ||
let dynamic_routing_config = &config.dynamic_routing_client; | ||
let connection = match dynamic_routing_config { | ||
DynamicRoutingClientConfig::Enabled { | ||
host, | ||
port, | ||
service, | ||
} => Some((host.clone(), *port, service.clone())), | ||
_ => None, | ||
}; | ||
|
||
let mut client_map = HashMap::new(); | ||
|
||
if let Some(conn) = connection { | ||
let uri = format!("http://{}:{}", conn.0, conn.1).parse::<tonic::transport::Uri>()?; | ||
let health_client = HealthClient::with_origin(client, uri); | ||
|
||
client_map.insert(HealthCheckServices::DynamicRoutingService, health_client); | ||
} | ||
|
||
Ok(Self { | ||
clients: client_map, | ||
}) | ||
} | ||
/// Perform health check for all services involved | ||
pub async fn perform_health_check( | ||
&self, | ||
config: &GrpcClientSettings, | ||
) -> HealthCheckResult<HealthCheckMap> { | ||
let dynamic_routing_config = &config.dynamic_routing_client; | ||
let connection = match dynamic_routing_config { | ||
DynamicRoutingClientConfig::Enabled { | ||
host, | ||
port, | ||
service, | ||
} => Some((host.clone(), *port, service.clone())), | ||
_ => None, | ||
}; | ||
|
||
let health_client = self | ||
.clients | ||
.get(&HealthCheckServices::DynamicRoutingService); | ||
|
||
// SAFETY : This is a safe cast as there exists a valid | ||
// integer value for this variant | ||
#[allow(clippy::as_conversions)] | ||
let expected_status = ServingStatus::Serving as i32; | ||
|
||
let mut service_map = HealthCheckMap::new(); | ||
|
||
let health_check_succeed = connection | ||
.as_ref() | ||
.async_map(|conn| self.get_response_from_grpc_service(conn.2.clone(), health_client)) | ||
.await | ||
.transpose() | ||
.change_context(HealthCheckError::ConnectionError( | ||
"error calling dynamic routing service".to_string(), | ||
)) | ||
.map_err(|err| logger::error!(error=?err)) | ||
.ok() | ||
.flatten() | ||
.is_some_and(|resp| resp.status == expected_status); | ||
|
||
connection.and_then(|_conn| { | ||
service_map.insert( | ||
HealthCheckServices::DynamicRoutingService, | ||
health_check_succeed, | ||
) | ||
}); | ||
|
||
Ok(service_map) | ||
} | ||
|
||
async fn get_response_from_grpc_service( | ||
&self, | ||
service: String, | ||
client: Option<&HealthClient<Client>>, | ||
) -> HealthCheckResult<HealthCheckResponse> { | ||
let request = tonic::Request::new(HealthCheckRequest { service }); | ||
|
||
let mut client = client | ||
.ok_or(HealthCheckError::MissingFields( | ||
"[health_client]".to_string(), | ||
))? | ||
.clone(); | ||
|
||
let response = client | ||
.check(request) | ||
.await | ||
.change_context(HealthCheckError::ConnectionError( | ||
"Failed to call dynamic routing service".to_string(), | ||
))? | ||
.into_inner(); | ||
|
||
Ok(response) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.