Skip to content
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

feat(dynamic_routing): integration of elimination routing for core flows #6816

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7b21417
build theinterface for elimination routing
Aprabhat19 Nov 27, 2024
5598cb0
resolve conflicts
Aprabhat19 Nov 27, 2024
096bdf8
resolve conflicts add invalidation window
Aprabhat19 Nov 27, 2024
537c59a
chore: run formatter
hyperswitch-bot[bot] Nov 27, 2024
b91d640
rename files
Aprabhat19 Nov 27, 2024
03ab2fb
Update crates/external_services/src/grpc_client/dynamic_routing.rs
Aprabhat19 Dec 3, 2024
0048389
resolved conflict
Aprabhat19 Dec 3, 2024
477abe3
Merge branch 'main' of https://github.com/juspay/hyperswitch into eli…
Aprabhat19 Dec 3, 2024
a4b61ca
chore: run formatter
hyperswitch-bot[bot] Dec 3, 2024
3199e57
resolve conflicts
Aprabhat19 Dec 3, 2024
ef3c26e
Merge branch 'elimination-routing-interface' of https://github.com/ju…
Aprabhat19 Dec 3, 2024
7488944
add external fields
Aprabhat19 Dec 3, 2024
0353013
change the value types
Aprabhat19 Dec 3, 2024
2003e6c
minor changes
Aprabhat19 Dec 3, 2024
a06e7c8
proto update
Aprabhat19 Dec 4, 2024
a5f8a00
Merge branch 'main' of https://github.com/juspay/hyperswitch into eli…
Aprabhat19 Dec 6, 2024
651973c
address naming changes
Aprabhat19 Dec 6, 2024
6388c20
init for er in core
prajjwalkumar17 Dec 12, 2024
3ff2e00
chore: run formatter
hyperswitch-bot[bot] Dec 12, 2024
033f447
Merge branch 'elimination-routing-interface' of github.com:juspay/hyp…
prajjwalkumar17 Dec 12, 2024
d501753
add the cache functions
prajjwalkumar17 Dec 12, 2024
559e425
add the cache functions
prajjwalkumar17 Dec 12, 2024
b6dcc3c
Merge branch 'er_core' of https://github.com/juspay/hyperswitch into …
prajjwalkumar17 Dec 12, 2024
057f48c
add the er in core flows
prajjwalkumar17 Dec 17, 2024
38f51f2
Merge branch 'er_core' of https://github.com/juspay/hyperswitch into …
prajjwalkumar17 Dec 17, 2024
9f1137a
complete core flow
prajjwalkumar17 Dec 17, 2024
bd7ac21
minor refactors
prajjwalkumar17 Dec 17, 2024
a2fd1dc
Merge branch 'main' into er_core
prajjwalkumar17 Dec 17, 2024
2a41166
chore: run formatter
hyperswitch-bot[bot] Dec 17, 2024
e38df11
template for update window equivalent for elimination routing servicce
prajjwalkumar17 Dec 17, 2024
3b7e7ef
resolve conflicts
prajjwalkumar17 Dec 17, 2024
57ede65
chore: run formatter
hyperswitch-bot[bot] Dec 17, 2024
3c2a01a
added update window
prajjwalkumar17 Dec 23, 2024
427a354
chore: run formatter
hyperswitch-bot[bot] Dec 23, 2024
72659dd
minor refactors
prajjwalkumar17 Dec 23, 2024
14cbe25
Merge branch 'er_core' of https://github.com/juspay/hyperswitch into …
prajjwalkumar17 Dec 23, 2024
0fdb825
Merge branch 'main' into er_core
prajjwalkumar17 Dec 23, 2024
7e4f49b
minor refactors
prajjwalkumar17 Dec 23, 2024
6f567b2
minor refactors
prajjwalkumar17 Dec 23, 2024
edb8e04
chore: run formatter
hyperswitch-bot[bot] Dec 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/api_models/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ pub struct EliminationRoutingConfig {
pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, ToSchema)]
pub struct EliminationAnalyserConfig {
pub bucket_size: Option<u64>,
pub bucket_leak_interval_in_secs: Option<u64>,
Expand Down
11 changes: 11 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3537,6 +3537,17 @@ pub enum ErrorCategory {
ProcessorDeclineIncorrectData,
}

impl ErrorCategory {
pub fn should_perform_elimination_routing(self) -> bool {
match self {
Self::ProcessorDowntime | Self::ProcessorDeclineUnauthorized => true,
Self::IssueWithPaymentMethod
| Self::ProcessorDeclineIncorrectData
| Self::FrmDecline => false,
}
}
}

#[derive(
Clone,
Debug,
Expand Down
2 changes: 1 addition & 1 deletion crates/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license.workspace = true

[features]
default = ["common_default", "v1"]
common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"]
common_default = ["dynamic_routing", "kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"]
olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"]
tls = ["actix-web/rustls-0_22"]
email = ["external_services/email", "scheduler/email", "olap"]
Expand Down
12 changes: 12 additions & 0 deletions crates/router/src/core/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,18 @@ pub enum RoutingError {
SuccessRateCalculationError,
#[error("Success rate client from dynamic routing gRPC service not initialized")]
SuccessRateClientInitializationError,
#[error("Elimintaion client from dynamic routing gRPC service not initialized")]
ElimintaionClientInitializationError,
#[error("Unable to analyze elimintaion routing config from dynamic routing service")]
ElimintaionRoutingCalculationError,
#[error("Params not found in elimination based routing config")]
EliminationBasedRoutingParamsNotFoundError,
#[error("Unable to retrieve elimination based routing config")]
EliminationRoutingConfigError,
#[error(
"Invalid elimination based connector label received from dynamic routing service: '{0}'"
)]
InvalidEliminationBasedConnectorLabel(String),
#[error("Unable to convert from '{from}' to '{to}'")]
GenericConversionError { from: String, to: String },
#[error("Invalid success based connector label received from dynamic routing service: '{0}'")]
Expand Down
141 changes: 87 additions & 54 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6178,70 +6178,103 @@ where
.parse_value("DynamicRoutingAlgorithmRef")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?;
let dynamic_split = api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Dynamic,
split: dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
};
let static_split: api_models::routing::RoutingVolumeSplit =
api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Static,
split: crate::consts::DYNAMIC_ROUTING_MAX_VOLUME
- dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
};
let volume_split_vec = vec![dynamic_split, static_split];
let routing_choice =
routing::perform_dynamic_routing_volume_split(volume_split_vec, None)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to perform volume split on routing type")?;

if routing_choice.routing_type.is_dynamic_routing() {
let success_based_routing_config_params_interpolator =
routing_helpers::SuccessBasedRoutingConfigParamsInterpolator::new(
payment_data.get_payment_attempt().payment_method,
payment_data.get_payment_attempt().payment_method_type,
payment_data.get_payment_attempt().authentication_type,
payment_data.get_payment_attempt().currency,
payment_data
.get_billing_address()
.and_then(|address| address.address)
.and_then(|address| address.country),
payment_data
.get_payment_attempt()
.payment_method_data
.as_ref()
.and_then(|data| data.as_object())
.and_then(|card| card.get("card"))
.and_then(|data| data.as_object())
.and_then(|card| card.get("card_network"))
.and_then(|network| network.as_str())
.map(|network| network.to_string()),
payment_data
.get_payment_attempt()
.payment_method_data
.as_ref()
.and_then(|data| data.as_object())
.and_then(|card| card.get("card"))
.and_then(|data| data.as_object())
.and_then(|card| card.get("card_isin"))
.and_then(|card_isin| card_isin.as_str())
.map(|card_isin| card_isin.to_string()),
);
// let dynamic_split = api_models::routing::RoutingVolumeSplit {
// routing_type: api_models::routing::RoutingType::Dynamic,
// split: dynamic_routing_config
// .dynamic_routing_volume_split
// .unwrap_or_default(),
// };
// let static_split: api_models::routing::RoutingVolumeSplit =
// api_models::routing::RoutingVolumeSplit {
// routing_type: api_models::routing::RoutingType::Static,
// split: crate::consts::DYNAMIC_ROUTING_MAX_VOLUME
// - dynamic_routing_config
// .dynamic_routing_volume_split
// .unwrap_or_default(),
// };
// let volume_split_vec = vec![dynamic_split, static_split];
// let routing_choice =
// routing::perform_dynamic_routing_volume_split(volume_split_vec, None)
// .change_context(errors::ApiErrorResponse::InternalServerError)
// .attach_printable("failed to perform volume split on routing type")?;
//
// if routing_choice.routing_type.is_dynamic_routing() {
let dynamic_routing_config_params_interpolator =
routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
payment_data.get_payment_attempt().payment_method,
payment_data.get_payment_attempt().payment_method_type,
payment_data.get_payment_attempt().authentication_type,
payment_data.get_payment_attempt().currency,
payment_data
.get_billing_address()
.and_then(|address| address.address)
.and_then(|address| address.country),
payment_data
.get_payment_attempt()
.payment_method_data
.as_ref()
.and_then(|data| data.as_object())
.and_then(|card| card.get("card"))
.and_then(|data| data.as_object())
.and_then(|card| card.get("card_network"))
.and_then(|network| network.as_str())
.map(|network| network.to_string()),
payment_data
.get_payment_attempt()
.payment_method_data
.as_ref()
.and_then(|data| data.as_object())
.and_then(|card| card.get("card"))
.and_then(|data| data.as_object())
.and_then(|card| card.get("card_isin"))
.and_then(|card_isin| card_isin.as_str())
.map(|card_isin| card_isin.to_string()),
);
let mut connectors = if dynamic_routing_config.success_based_algorithm.is_some_and(
|success_based_algo| {
success_based_algo
.algorithm_id_with_timestamp
.algorithm_id
.is_some()
},
) {
routing::perform_success_based_routing(
state,
connectors.clone(),
business_profile,
success_based_routing_config_params_interpolator,
dynamic_routing_config_params_interpolator.clone(),
)
.await
.map_err(|e| logger::error!(success_rate_routing_error=?e))
.unwrap_or(connectors)
} else {
connectors
}
};

connectors = if dynamic_routing_config
.elimination_routing_algorithm
.is_some_and(|elimination_routing_algo| {
elimination_routing_algo
.algorithm_id_with_timestamp
.algorithm_id
.is_some()
}) {
routing::perform_elimination_routing(
state,
connectors.clone(),
business_profile,
dynamic_routing_config_params_interpolator,
)
.await
.map_err(|e| logger::error!(success_rate_routing_error=?e))
.unwrap_or(connectors)
} else {
connectors
};
connectors
// } else {
// connectors
// }
} else {
connectors
}
Expand Down
Loading
Loading