refactor(access_token): handle timeout errors gracefully (#1882)

This commit is contained in:
Narayan Bhat
2023-08-08 15:18:23 +05:30
committed by GitHub
parent c205f064b9
commit cc4136f85f
4 changed files with 55 additions and 8 deletions

View File

@ -11,6 +11,8 @@ pub(crate) const ALPHABETS: [char; 62] = [
]; ];
/// API client request timeout (in seconds) /// API client request timeout (in seconds)
pub const REQUEST_TIME_OUT: u64 = 30; pub const REQUEST_TIME_OUT: u64 = 30;
pub const REQUEST_TIMEOUT_ERROR_CODE: &str = "TIMEOUT";
pub const REQUEST_TIMEOUT_ERROR_MESSAGE: &str = "Connector did not respond in specified time";
///Payment intent fulfillment default timeout (in seconds) ///Payment intent fulfillment default timeout (in seconds)
pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60; pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;

View File

@ -186,7 +186,7 @@ pub fn http_not_implemented() -> actix_web::HttpResponse<BoxBody> {
.error_response() .error_response()
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error, PartialEq)]
pub enum ApiClientError { pub enum ApiClientError {
#[error("Header map construction failed")] #[error("Header map construction failed")]
HeaderMapConstructionFailed, HeaderMapConstructionFailed,
@ -315,6 +315,8 @@ pub enum ConnectorError {
MissingPaymentMethodType, MissingPaymentMethodType,
#[error("Balance in the payment method is low")] #[error("Balance in the payment method is low")]
InSufficientBalanceInPaymentMethod, InSufficientBalanceInPaymentMethod,
#[error("Server responded with Request Timeout")]
RequestTimeoutReceived,
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@ -490,6 +492,18 @@ pub enum WebhooksFlowError {
MissingRequiredField { field_name: &'static str }, MissingRequiredField { field_name: &'static str },
} }
impl ApiClientError {
pub fn is_upstream_timeout(&self) -> bool {
self == &Self::RequestTimeoutReceived
}
}
impl ConnectorError {
pub fn is_connector_timeout(&self) -> bool {
self == &Self::RequestTimeoutReceived
}
}
#[cfg(feature = "detailed_errors")] #[cfg(feature = "detailed_errors")]
pub mod error_stack_parsing { pub mod error_stack_parsing {

View File

@ -4,6 +4,7 @@ use common_utils::ext_traits::AsyncExt;
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, ResultExt};
use crate::{ use crate::{
consts,
core::{ core::{
errors::{self, RouterResult}, errors::{self, RouterResult},
payments, payments,
@ -151,16 +152,38 @@ pub async fn refresh_connector_auth(
types::AccessToken, types::AccessToken,
> = connector.connector.get_connector_integration(); > = connector.connector.get_connector_integration();
let access_token_router_data = services::execute_connector_processing_step( let access_token_router_data_result = services::execute_connector_processing_step(
state, state,
connector_integration, connector_integration,
router_data, router_data,
payments::CallConnectorAction::Trigger, payments::CallConnectorAction::Trigger,
None, None,
) )
.await .await;
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not refresh access token")?; let access_token_router_data = match access_token_router_data_result {
Ok(router_data) => Ok(router_data.response),
Err(connector_error) => {
// If we receive a timeout error from the connector, then
// the error has to be handled gracefully by updating the payment status to failed.
// further payment flow will not be continued
if connector_error.current_context().is_connector_timeout() {
let error_response = types::ErrorResponse {
code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
status_code: 200,
};
Ok(Err(error_response))
} else {
Err(connector_error
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not refresh access token"))
}
}
}?;
metrics::ACCESS_TOKEN_CREATION.add( metrics::ACCESS_TOKEN_CREATION.add(
&metrics::CONTEXT, &metrics::CONTEXT,
1, 1,
@ -169,5 +192,5 @@ pub async fn refresh_connector_auth(
connector.connector_name.to_string(), connector.connector_name.to_string(),
)], )],
); );
Ok(access_token_router_data.response) Ok(access_token_router_data)
} }

View File

@ -325,8 +325,16 @@ where
}; };
Ok(response) Ok(response)
} }
Err(error) => Err(error Err(error) => {
.change_context(errors::ConnectorError::ProcessingStepFailed(None))), if error.current_context().is_upstream_timeout() {
Err(error
.change_context(errors::ConnectorError::RequestTimeoutReceived))
} else {
Err(error.change_context(
errors::ConnectorError::ProcessingStepFailed(None),
))
}
}
} }
} }
None => Ok(router_data), None => Ok(router_data),