From f6dde13d6c2920761f236969a3862fe61f3e0e3d Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Mon, 2 Dec 2024 20:13:37 +0530 Subject: [PATCH] feat(routing): elimination routing switch for toggling the feature (#6568) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 2 +- api-reference/openapi_spec.json | 64 ++-- crates/api_models/src/events/routing.rs | 6 +- crates/api_models/src/routing.rs | 184 +++++++++-- crates/openapi/src/openapi.rs | 6 +- crates/openapi/src/routes/routing.rs | 32 +- crates/router/src/core/payments/routing.rs | 2 +- crates/router/src/core/routing.rs | 215 ++++--------- crates/router/src/core/routing/helpers.rs | 343 +++++++++++++++++++-- crates/router/src/routes/app.rs | 14 +- crates/router/src/routes/routing.rs | 54 +++- crates/storage_impl/src/redis/cache.rs | 13 +- crates/storage_impl/src/redis/pub_sub.rs | 19 +- 13 files changed, 684 insertions(+), 270 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index d8ab5a7a20..6eab5ed265 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1278,7 +1278,7 @@ ], "summary": "Routing - Create", "description": "Create a routing algorithm", - "operationId": "Create a routing algprithm", + "operationId": "Create a routing algorithm", "requestBody": { "content": { "application/json": { diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index f903879c48..56b77bff1a 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -3929,7 +3929,7 @@ ], "summary": "Routing - Toggle success based dynamic routing for profile", "description": "Create a success based dynamic routing algorithm", - "operationId": "Toggle success based dynamic routing algprithm", + "operationId": "Toggle success based dynamic routing algorithm", "parameters": [ { "name": "account_id", @@ -3955,7 +3955,7 @@ "description": "Feature to enable for success based routing", "required": true, "schema": { - "$ref": "#/components/schemas/SuccessBasedRoutingFeatures" + "$ref": "#/components/schemas/DynamicRoutingFeatures" } } ], @@ -10234,6 +10234,14 @@ } } }, + "DynamicRoutingFeatures": { + "type": "string", + "enum": [ + "metrics", + "dynamic_connector_selection", + "none" + ] + }, "EnabledPaymentMethod": { "type": "object", "description": "Object for EnabledPaymentMethod", @@ -23872,14 +23880,6 @@ "destination" ] }, - "SuccessBasedRoutingFeatures": { - "type": "string", - "enum": [ - "metrics", - "dynamic_connector_selection", - "none" - ] - }, "SurchargeDetailsResponse": { "type": "object", "required": [ @@ -24096,6 +24096,28 @@ } } }, + "ToggleDynamicRoutingPath": { + "type": "object", + "required": [ + "profile_id" + ], + "properties": { + "profile_id": { + "type": "string" + } + } + }, + "ToggleDynamicRoutingQuery": { + "type": "object", + "required": [ + "enable" + ], + "properties": { + "enable": { + "$ref": "#/components/schemas/DynamicRoutingFeatures" + } + } + }, "ToggleKVRequest": { "type": "object", "required": [ @@ -24129,28 +24151,6 @@ } } }, - "ToggleSuccessBasedRoutingPath": { - "type": "object", - "required": [ - "profile_id" - ], - "properties": { - "profile_id": { - "type": "string" - } - } - }, - "ToggleSuccessBasedRoutingQuery": { - "type": "object", - "required": [ - "enable" - ], - "properties": { - "enable": { - "$ref": "#/components/schemas/SuccessBasedRoutingFeatures" - } - } - }, "TouchNGoRedirection": { "type": "object" }, diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs index ffa5e008f0..87e7fa2788 100644 --- a/crates/api_models/src/events/routing.rs +++ b/crates/api_models/src/events/routing.rs @@ -6,7 +6,7 @@ use crate::routing::{ RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery, RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, SuccessBasedRoutingUpdateConfigQuery, - ToggleSuccessBasedRoutingQuery, ToggleSuccessBasedRoutingWrapper, + ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper, }; impl ApiEventMetric for RoutingKind { @@ -79,7 +79,7 @@ impl ApiEventMetric for RoutingRetrieveLinkQueryWrapper { } } -impl ApiEventMetric for ToggleSuccessBasedRoutingQuery { +impl ApiEventMetric for ToggleDynamicRoutingQuery { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Routing) } @@ -97,7 +97,7 @@ impl ApiEventMetric for SuccessBasedRoutingPayloadWrapper { } } -impl ApiEventMetric for ToggleSuccessBasedRoutingWrapper { +impl ApiEventMetric for ToggleDynamicRoutingWrapper { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Routing) } diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index dbfa770cbf..6cd5d5251a 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -522,6 +522,92 @@ pub struct DynamicAlgorithmWithTimestamp { #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] pub struct DynamicRoutingAlgorithmRef { pub success_based_algorithm: Option, + pub elimination_routing_algorithm: Option, +} + +pub trait DynamicRoutingAlgoAccessor { + fn get_algorithm_id_with_timestamp( + self, + ) -> DynamicAlgorithmWithTimestamp; + fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures; +} + +impl DynamicRoutingAlgoAccessor for SuccessBasedAlgorithm { + fn get_algorithm_id_with_timestamp( + self, + ) -> DynamicAlgorithmWithTimestamp { + self.algorithm_id_with_timestamp + } + fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures { + &mut self.enabled_feature + } +} + +impl DynamicRoutingAlgoAccessor for EliminationRoutingAlgorithm { + fn get_algorithm_id_with_timestamp( + self, + ) -> DynamicAlgorithmWithTimestamp { + self.algorithm_id_with_timestamp + } + fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures { + &mut self.enabled_feature + } +} + +impl EliminationRoutingAlgorithm { + pub fn new( + algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp< + common_utils::id_type::RoutingId, + >, + ) -> Self { + Self { + algorithm_id_with_timestamp, + enabled_feature: DynamicRoutingFeatures::None, + } + } +} + +impl SuccessBasedAlgorithm { + pub fn new( + algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp< + common_utils::id_type::RoutingId, + >, + ) -> Self { + Self { + algorithm_id_with_timestamp, + enabled_feature: DynamicRoutingFeatures::None, + } + } +} + +impl DynamicRoutingAlgorithmRef { + pub fn update(&mut self, new: Self) { + if let Some(elimination_routing_algorithm) = new.elimination_routing_algorithm { + self.elimination_routing_algorithm = Some(elimination_routing_algorithm) + } + if let Some(success_based_algorithm) = new.success_based_algorithm { + self.success_based_algorithm = Some(success_based_algorithm) + } + } + + pub fn update_specific_ref( + &mut self, + algo_type: DynamicRoutingType, + feature_to_enable: DynamicRoutingFeatures, + ) { + match algo_type { + DynamicRoutingType::SuccessRateBasedRouting => { + self.success_based_algorithm + .as_mut() + .map(|algo| algo.enabled_feature = feature_to_enable); + } + DynamicRoutingType::EliminationRouting => { + self.elimination_routing_algorithm + .as_mut() + .map(|algo| algo.enabled_feature = feature_to_enable); + } + } + } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -529,11 +615,25 @@ pub struct SuccessBasedAlgorithm { pub algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp, #[serde(default)] - pub enabled_feature: SuccessBasedRoutingFeatures, + pub enabled_feature: DynamicRoutingFeatures, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct EliminationRoutingAlgorithm { + pub algorithm_id_with_timestamp: + DynamicAlgorithmWithTimestamp, + #[serde(default)] + pub enabled_feature: DynamicRoutingFeatures, +} + +impl EliminationRoutingAlgorithm { + pub fn update_enabled_features(&mut self, feature_to_enable: DynamicRoutingFeatures) { + self.enabled_feature = feature_to_enable + } } impl SuccessBasedAlgorithm { - pub fn update_enabled_features(&mut self, feature_to_enable: SuccessBasedRoutingFeatures) { + pub fn update_enabled_features(&mut self, feature_to_enable: DynamicRoutingFeatures) { self.enabled_feature = feature_to_enable } } @@ -542,26 +642,40 @@ impl DynamicRoutingAlgorithmRef { pub fn update_algorithm_id( &mut self, new_id: common_utils::id_type::RoutingId, - enabled_feature: SuccessBasedRoutingFeatures, + enabled_feature: DynamicRoutingFeatures, + dynamic_routing_type: DynamicRoutingType, ) { - self.success_based_algorithm = Some(SuccessBasedAlgorithm { - algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp { - algorithm_id: Some(new_id), - timestamp: common_utils::date_time::now_unix_timestamp(), - }, - enabled_feature, - }) + match dynamic_routing_type { + DynamicRoutingType::SuccessRateBasedRouting => { + self.success_based_algorithm = Some(SuccessBasedAlgorithm { + algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp { + algorithm_id: Some(new_id), + timestamp: common_utils::date_time::now_unix_timestamp(), + }, + enabled_feature, + }) + } + DynamicRoutingType::EliminationRouting => { + self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm { + algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp { + algorithm_id: Some(new_id), + timestamp: common_utils::date_time::now_unix_timestamp(), + }, + enabled_feature, + }) + } + }; } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] -pub struct ToggleSuccessBasedRoutingQuery { - pub enable: SuccessBasedRoutingFeatures, +pub struct ToggleDynamicRoutingQuery { + pub enable: DynamicRoutingFeatures, } #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, ToSchema)] #[serde(rename_all = "snake_case")] -pub enum SuccessBasedRoutingFeatures { +pub enum DynamicRoutingFeatures { Metrics, DynamicConnectorSelection, #[default] @@ -577,26 +691,52 @@ pub struct SuccessBasedRoutingUpdateConfigQuery { } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct ToggleSuccessBasedRoutingWrapper { +pub struct ToggleDynamicRoutingWrapper { pub profile_id: common_utils::id_type::ProfileId, - pub feature_to_enable: SuccessBasedRoutingFeatures, + pub feature_to_enable: DynamicRoutingFeatures, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] -pub struct ToggleSuccessBasedRoutingPath { +pub struct ToggleDynamicRoutingPath { #[schema(value_type = String)] pub profile_id: common_utils::id_type::ProfileId, } + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +pub struct EliminationRoutingConfig { + pub params: Option>, + // pub labels: Option>, + pub elimination_analyser_config: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +pub struct EliminationAnalyserConfig { + pub bucket_size: Option, + pub bucket_ttl_in_mins: Option, +} + +impl Default for EliminationRoutingConfig { + fn default() -> Self { + Self { + params: Some(vec![DynamicRoutingConfigParams::PaymentMethod]), + elimination_analyser_config: Some(EliminationAnalyserConfig { + bucket_size: Some(5), + bucket_ttl_in_mins: Some(2.0), + }), + } + } +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] pub struct SuccessBasedRoutingConfig { - pub params: Option>, + pub params: Option>, pub config: Option, } impl Default for SuccessBasedRoutingConfig { fn default() -> Self { Self { - params: Some(vec![SuccessBasedRoutingConfigParams::PaymentMethod]), + params: Some(vec![DynamicRoutingConfigParams::PaymentMethod]), config: Some(SuccessBasedRoutingConfigBody { min_aggregates_size: Some(2), default_success_rate: Some(100.0), @@ -611,7 +751,7 @@ impl Default for SuccessBasedRoutingConfig { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema, strum::Display)] -pub enum SuccessBasedRoutingConfigParams { +pub enum DynamicRoutingConfigParams { PaymentMethod, PaymentMethodType, AuthenticationType, @@ -642,6 +782,12 @@ pub struct SuccessBasedRoutingPayloadWrapper { pub profile_id: common_utils::id_type::ProfileId, } +#[derive(Debug, Clone, strum::Display, serde::Serialize, serde::Deserialize)] +pub enum DynamicRoutingType { + SuccessRateBasedRouting, + EliminationRouting, +} + impl SuccessBasedRoutingConfig { pub fn update(&mut self, new: Self) { if let Some(params) = new.params { diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index ed6efea27f..4ec1bb22b4 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -575,7 +575,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::routing::RoutingDictionaryRecord, api_models::routing::RoutingKind, api_models::routing::RoutableConnectorChoice, - api_models::routing::SuccessBasedRoutingFeatures, + api_models::routing::DynamicRoutingFeatures, api_models::routing::LinkedRoutingConfigRetrieveResponse, api_models::routing::RoutingRetrieveResponse, api_models::routing::ProfileDefaultRoutingConfig, @@ -586,8 +586,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::routing::StraightThroughAlgorithm, api_models::routing::ConnectorVolumeSplit, api_models::routing::ConnectorSelection, - api_models::routing::ToggleSuccessBasedRoutingQuery, - api_models::routing::ToggleSuccessBasedRoutingPath, + api_models::routing::ToggleDynamicRoutingQuery, + api_models::routing::ToggleDynamicRoutingPath, api_models::routing::ast::RoutableChoiceKind, api_models::enums::RoutableConnectors, api_models::routing::ast::ProgramConnectorSelection, diff --git a/crates/openapi/src/routes/routing.rs b/crates/openapi/src/routes/routing.rs index 9b8f56aeb8..b0fcb75fa4 100644 --- a/crates/openapi/src/routes/routing.rs +++ b/crates/openapi/src/routes/routing.rs @@ -37,7 +37,7 @@ pub async fn routing_create_config() {} (status = 403, description = "Forbidden"), ), tag = "Routing", - operation_id = "Create a routing algprithm", + operation_id = "Create a routing algorithm", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_create_config() {} @@ -264,7 +264,7 @@ pub async fn routing_update_default_config_for_profile() {} params( ("account_id" = String, Path, description = "Merchant id"), ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"), - ("enable" = SuccessBasedRoutingFeatures, Query, description = "Feature to enable for success based routing"), + ("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for success based routing"), ), responses( (status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord), @@ -275,7 +275,33 @@ pub async fn routing_update_default_config_for_profile() {} (status = 403, description = "Forbidden"), ), tag = "Routing", - operation_id = "Toggle success based dynamic routing algprithm", + operation_id = "Toggle success based dynamic routing algorithm", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn toggle_success_based_routing() {} + +#[cfg(feature = "v1")] +/// Routing - Toggle elimination routing for profile +/// +/// Create a elimination based dynamic routing algorithm +#[utoipa::path( + post, + path = "/account/:account_id/business_profile/:profile_id/dynamic_routing/elimination/toggle", + params( + ("account_id" = String, Path, description = "Merchant id"), + ("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"), + ("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for success based routing"), + ), + responses( + (status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord), + (status = 400, description = "Request body is malformed"), + (status = 500, description = "Internal server error"), + (status = 404, description = "Resource missing"), + (status = 422, description = "Unprocessable request"), + (status = 403, description = "Forbidden"), + ), + tag = "Routing", + operation_id = "Toggle elimination routing algorithm", + security(("api_key" = []), ("jwt_key" = [])) +)] +pub async fn toggle_elimination_routing() {} diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 28321529ef..2fb49c37d2 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -1263,7 +1263,7 @@ pub async fn perform_success_based_routing( )?; if success_based_algo_ref.enabled_feature - == api_routing::SuccessBasedRoutingFeatures::DynamicConnectorSelection + == api_routing::DynamicRoutingFeatures::DynamicConnectorSelection { logger::debug!( "performing success_based_routing for profile {}", diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 348f5229b7..9318e0c5b9 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -452,6 +452,16 @@ pub async fn link_routing_config( }, enabled_feature: _ }) if id == &algorithm_id + ) || matches!( + dynamic_routing_ref.elimination_routing_algorithm, + Some(routing::EliminationRoutingAlgorithm { + algorithm_id_with_timestamp: + routing_types::DynamicAlgorithmWithTimestamp { + algorithm_id: Some(ref id), + timestamp: _ + }, + enabled_feature: _ + }) if id == &algorithm_id ), || { Err(errors::ApiErrorResponse::PreconditionFailed { @@ -470,7 +480,9 @@ pub async fn link_routing_config( "missing success_based_algorithm in dynamic_algorithm_ref from business_profile table", )? .enabled_feature, + routing_types::DynamicRoutingType::SuccessRateBasedRouting, ); + helpers::update_business_profile_active_dynamic_algorithm_ref( db, key_manager_state, @@ -1185,13 +1197,16 @@ pub async fn update_default_routing_config_for_profile( )) } +// Toggle the specific routing type as well as add the default configs in RoutingAlgorithm table +// and update the same in business profile table. #[cfg(all(feature = "v1", feature = "dynamic_routing"))] -pub async fn toggle_success_based_routing( +pub async fn toggle_specific_dynamic_routing( state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, - feature_to_enable: routing::SuccessBasedRoutingFeatures, + feature_to_enable: routing::DynamicRoutingFeatures, profile_id: common_utils::id_type::ProfileId, + dynamic_routing_type: routing::DynamicRoutingType, ) -> RouterResponse { metrics::ROUTING_CREATE_REQUEST_RECEIVED.add( &metrics::CONTEXT, @@ -1214,170 +1229,44 @@ pub async fn toggle_success_based_routing( id: profile_id.get_string_repr().to_owned(), })?; - let mut success_based_dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef = - business_profile - .dynamic_routing_algorithm - .clone() - .map(|val| val.parse_value("DynamicRoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "unable to deserialize dynamic routing algorithm ref from business profile", - )? - .unwrap_or_default(); + let dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef = business_profile + .dynamic_routing_algorithm + .clone() + .map(|val| val.parse_value("DynamicRoutingAlgorithmRef")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "unable to deserialize dynamic routing algorithm ref from business profile", + )? + .unwrap_or_default(); match feature_to_enable { - routing::SuccessBasedRoutingFeatures::Metrics - | routing::SuccessBasedRoutingFeatures::DynamicConnectorSelection => { - if let Some(ref mut algo_with_timestamp) = - success_based_dynamic_routing_algo_ref.success_based_algorithm - { - match algo_with_timestamp - .algorithm_id_with_timestamp - .algorithm_id - .clone() - { - Some(algorithm_id) => { - // algorithm is already present in profile - if algo_with_timestamp.enabled_feature == feature_to_enable { - // algorithm already has the required feature - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Success rate based routing is already enabled" - .to_string(), - })? - } else { - // enable the requested feature for the algorithm - algo_with_timestamp.update_enabled_features(feature_to_enable); - let record = db - .find_routing_algorithm_by_profile_id_algorithm_id( - business_profile.get_id(), - &algorithm_id, - ) - .await - .to_not_found_response( - errors::ApiErrorResponse::ResourceIdNotFound, - )?; - let response = record.foreign_into(); - helpers::update_business_profile_active_dynamic_algorithm_ref( - db, - key_manager_state, - &key_store, - business_profile, - success_based_dynamic_routing_algo_ref, - ) - .await?; - - metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add( - &metrics::CONTEXT, - 1, - &add_attributes([( - "profile_id", - profile_id.get_string_repr().to_owned(), - )]), - ); - Ok(service_api::ApplicationResponse::Json(response)) - } - } - None => { - // algorithm isn't present in profile - helpers::default_success_based_routing_setup( - &state, - key_store, - business_profile, - feature_to_enable, - merchant_account.get_id().to_owned(), - success_based_dynamic_routing_algo_ref, - ) - .await - } - } - } else { - // algorithm isn't present in profile - helpers::default_success_based_routing_setup( - &state, - key_store, - business_profile, - feature_to_enable, - merchant_account.get_id().to_owned(), - success_based_dynamic_routing_algo_ref, - ) - .await - } + routing::DynamicRoutingFeatures::Metrics + | routing::DynamicRoutingFeatures::DynamicConnectorSelection => { + // occurs when algorithm is already present in the db + // 1. If present with same feature then return response as already enabled + // 2. Else update the feature and persist the same on db + // 3. If not present in db then create a new default entry + helpers::enable_dynamic_routing_algorithm( + &state, + key_store, + business_profile, + feature_to_enable, + dynamic_routing_algo_ref, + dynamic_routing_type, + ) + .await } - routing::SuccessBasedRoutingFeatures::None => { - // disable success based routing for the requested profile - let timestamp = common_utils::date_time::now_unix_timestamp(); - match success_based_dynamic_routing_algo_ref.success_based_algorithm { - Some(algorithm_ref) => { - if let Some(algorithm_id) = - algorithm_ref.algorithm_id_with_timestamp.algorithm_id - { - let dynamic_routing_algorithm = routing_types::DynamicRoutingAlgorithmRef { - success_based_algorithm: Some(routing::SuccessBasedAlgorithm { - algorithm_id_with_timestamp: - routing_types::DynamicAlgorithmWithTimestamp { - algorithm_id: None, - timestamp, - }, - enabled_feature: routing::SuccessBasedRoutingFeatures::None, - }), - }; - - // redact cache for success based routing configs - let cache_key = format!( - "{}_{}", - business_profile.get_id().get_string_repr(), - algorithm_id.get_string_repr() - ); - let cache_entries_to_redact = - vec![cache::CacheKind::SuccessBasedDynamicRoutingCache( - cache_key.into(), - )]; - let _ = cache::publish_into_redact_channel( - state.store.get_cache_store().as_ref(), - cache_entries_to_redact, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to publish into the redact channel for evicting the success based routing config cache")?; - - let record = db - .find_routing_algorithm_by_profile_id_algorithm_id( - business_profile.get_id(), - &algorithm_id, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - let response = record.foreign_into(); - helpers::update_business_profile_active_dynamic_algorithm_ref( - db, - key_manager_state, - &key_store, - business_profile, - dynamic_routing_algorithm, - ) - .await?; - - metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add( - &metrics::CONTEXT, - 1, - &add_attributes([( - "profile_id", - profile_id.get_string_repr().to_owned(), - )]), - ); - - Ok(service_api::ApplicationResponse::Json(response)) - } else { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Algorithm is already inactive".to_string(), - })? - } - } - None => Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Success rate based routing is already disabled".to_string(), - })?, - } + routing::DynamicRoutingFeatures::None => { + // disable specific dynamic routing for the requested profile + helpers::disable_dynamic_routing_algorithm( + &state, + key_store, + business_profile, + dynamic_routing_algo_ref, + dynamic_routing_type, + ) + .await } } } diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index c22a5021a2..9dc56b8bd0 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -26,6 +26,8 @@ use router_env::{instrument, metrics::add_attributes, tracing}; use rustc_hash::FxHashSet; use storage_impl::redis::cache; +#[cfg(all(feature = "dynamic_routing", feature = "v1"))] +use crate::db::errors::StorageErrorExt; #[cfg(feature = "v2")] use crate::types::domain::MerchantConnectorAccount; use crate::{ @@ -39,6 +41,8 @@ use crate::{ use crate::{core::metrics as core_metrics, routes::metrics, types::transformers::ForeignInto}; pub const SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM: &str = "Success rate based dynamic routing algorithm"; +pub const ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM: &str = + "Elimination based dynamic routing algorithm"; /// Provides us with all the configured configs of the Merchant in the ascending time configured /// manner and chooses the first of them @@ -263,9 +267,9 @@ pub async fn update_business_profile_active_dynamic_algorithm_ref( key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, current_business_profile: domain::Profile, - dynamic_routing_algorithm: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_algorithm_ref: routing_types::DynamicRoutingAlgorithmRef, ) -> RouterResult<()> { - let ref_val = dynamic_routing_algorithm + let ref_val = dynamic_routing_algorithm_ref .encode_to_value() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to convert dynamic routing ref to value")?; @@ -662,7 +666,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing( .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("success_based_algorithm not found in dynamic_routing_algorithm from business_profile table")?; - if success_based_algo_ref.enabled_feature != routing_types::SuccessBasedRoutingFeatures::None { + if success_based_algo_ref.enabled_feature != routing_types::DynamicRoutingFeatures::None { let client = state .grpc_client .dynamic_routing @@ -912,34 +916,303 @@ pub fn generate_tenant_business_profile_id( format!("{}:{}", redis_key_prefix, business_profile_id) } -/// default config setup for success_based_routing -#[cfg(feature = "v1")] -#[instrument(skip_all)] -pub async fn default_success_based_routing_setup( +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +pub async fn disable_dynamic_routing_algorithm( state: &SessionState, key_store: domain::MerchantKeyStore, business_profile: domain::Profile, - feature_to_enable: routing_types::SuccessBasedRoutingFeatures, - merchant_id: id_type::MerchantId, - mut success_based_dynamic_routing_algo: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_type: routing_types::DynamicRoutingType, ) -> RouterResult> { let db = state.store.as_ref(); let key_manager_state = &state.into(); - let profile_id = business_profile.get_id().to_owned(); - let default_success_based_routing_config = routing_types::SuccessBasedRoutingConfig::default(); + let timestamp = common_utils::date_time::now_unix_timestamp(); + let profile_id = business_profile + .get_id() + .clone() + .get_string_repr() + .to_owned(); + let (algorithm_id, dynamic_routing_algorithm, cache_entries_to_redact) = + match dynamic_routing_type { + routing_types::DynamicRoutingType::SuccessRateBasedRouting => { + let Some(algorithm_ref) = dynamic_routing_algo_ref.success_based_algorithm else { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "Success rate based routing is already disabled".to_string(), + })? + }; + let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id + else { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "Algorithm is already inactive".to_string(), + })? + }; + + let cache_key = format!( + "{}_{}", + business_profile.get_id().get_string_repr(), + algorithm_id.get_string_repr() + ); + let cache_entries_to_redact = + vec![cache::CacheKind::SuccessBasedDynamicRoutingCache( + cache_key.into(), + )]; + ( + algorithm_id, + routing_types::DynamicRoutingAlgorithmRef { + success_based_algorithm: Some(routing_types::SuccessBasedAlgorithm { + algorithm_id_with_timestamp: + routing_types::DynamicAlgorithmWithTimestamp { + algorithm_id: None, + timestamp, + }, + enabled_feature: routing_types::DynamicRoutingFeatures::None, + }), + elimination_routing_algorithm: dynamic_routing_algo_ref + .elimination_routing_algorithm, + }, + cache_entries_to_redact, + ) + } + routing_types::DynamicRoutingType::EliminationRouting => { + let Some(algorithm_ref) = dynamic_routing_algo_ref.elimination_routing_algorithm + else { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "Elimination routing is already disabled".to_string(), + })? + }; + let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id + else { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "Algorithm is already inactive".to_string(), + })? + }; + let cache_key = format!( + "{}_{}", + business_profile.get_id().get_string_repr(), + algorithm_id.get_string_repr() + ); + let cache_entries_to_redact = + vec![cache::CacheKind::EliminationBasedDynamicRoutingCache( + cache_key.into(), + )]; + ( + algorithm_id, + routing_types::DynamicRoutingAlgorithmRef { + success_based_algorithm: dynamic_routing_algo_ref.success_based_algorithm, + elimination_routing_algorithm: Some( + routing_types::EliminationRoutingAlgorithm { + algorithm_id_with_timestamp: + routing_types::DynamicAlgorithmWithTimestamp { + algorithm_id: None, + timestamp, + }, + enabled_feature: routing_types::DynamicRoutingFeatures::None, + }, + ), + }, + cache_entries_to_redact, + ) + } + }; + + // redact cache for dynamic routing config + let _ = cache::publish_into_redact_channel( + state.store.get_cache_store().as_ref(), + cache_entries_to_redact, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "unable to publish into the redact channel for evicting the dynamic routing config cache", + )?; + + let record = db + .find_routing_algorithm_by_profile_id_algorithm_id(business_profile.get_id(), &algorithm_id) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + let response = record.foreign_into(); + update_business_profile_active_dynamic_algorithm_ref( + db, + key_manager_state, + &key_store, + business_profile, + dynamic_routing_algorithm, + ) + .await?; + + core_metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add( + &metrics::CONTEXT, + 1, + &add_attributes([("profile_id", profile_id)]), + ); + + Ok(ApplicationResponse::Json(response)) +} + +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +pub async fn enable_dynamic_routing_algorithm( + state: &SessionState, + key_store: domain::MerchantKeyStore, + business_profile: domain::Profile, + feature_to_enable: routing_types::DynamicRoutingFeatures, + dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_type: routing_types::DynamicRoutingType, +) -> RouterResult> { + let dynamic_routing = dynamic_routing_algo_ref.clone(); + match dynamic_routing_type { + routing_types::DynamicRoutingType::SuccessRateBasedRouting => { + enable_specific_routing_algorithm( + state, + key_store, + business_profile, + feature_to_enable, + dynamic_routing_algo_ref, + dynamic_routing_type, + dynamic_routing.success_based_algorithm, + ) + .await + } + routing_types::DynamicRoutingType::EliminationRouting => { + enable_specific_routing_algorithm( + state, + key_store, + business_profile, + feature_to_enable, + dynamic_routing_algo_ref, + dynamic_routing_type, + dynamic_routing.elimination_routing_algorithm, + ) + .await + } + } +} + +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +pub async fn enable_specific_routing_algorithm( + state: &SessionState, + key_store: domain::MerchantKeyStore, + business_profile: domain::Profile, + feature_to_enable: routing_types::DynamicRoutingFeatures, + mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_type: routing_types::DynamicRoutingType, + algo_type: Option, +) -> RouterResult> +where + A: routing_types::DynamicRoutingAlgoAccessor + Clone + std::fmt::Debug, +{ + // Algorithm wasn't created yet + let Some(mut algo_type) = algo_type else { + return default_specific_dynamic_routing_setup( + state, + key_store, + business_profile, + feature_to_enable, + dynamic_routing_algo_ref, + dynamic_routing_type, + ) + .await; + }; + + // Algorithm was in disabled state + let Some(algo_type_algorithm_id) = algo_type + .clone() + .get_algorithm_id_with_timestamp() + .algorithm_id + else { + return default_specific_dynamic_routing_setup( + state, + key_store, + business_profile, + feature_to_enable, + dynamic_routing_algo_ref, + dynamic_routing_type, + ) + .await; + }; + let db = state.store.as_ref(); + let profile_id = business_profile.get_id().clone(); + let algo_type_enabled_features = algo_type.get_enabled_features(); + if *algo_type_enabled_features == feature_to_enable { + // algorithm already has the required feature + return Err(errors::ApiErrorResponse::PreconditionFailed { + message: format!("{} is already enabled", dynamic_routing_type), + } + .into()); + }; + *algo_type_enabled_features = feature_to_enable.clone(); + dynamic_routing_algo_ref + .update_specific_ref(dynamic_routing_type.clone(), feature_to_enable.clone()); + update_business_profile_active_dynamic_algorithm_ref( + db, + &state.into(), + &key_store, + business_profile, + dynamic_routing_algo_ref.clone(), + ) + .await?; + + let routing_algorithm = db + .find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algo_type_algorithm_id) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + let updated_routing_record = routing_algorithm.foreign_into(); + core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add( + &metrics::CONTEXT, + 1, + &add_attributes([("profile_id", profile_id.get_string_repr().to_owned())]), + ); + Ok(ApplicationResponse::Json(updated_routing_record)) +} + +#[cfg(feature = "v1")] +#[instrument(skip_all)] +pub async fn default_specific_dynamic_routing_setup( + state: &SessionState, + key_store: domain::MerchantKeyStore, + business_profile: domain::Profile, + feature_to_enable: routing_types::DynamicRoutingFeatures, + mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef, + dynamic_routing_type: routing_types::DynamicRoutingType, +) -> RouterResult> { + let db = state.store.as_ref(); + let key_manager_state = &state.into(); + let profile_id = business_profile.get_id().clone(); + let merchant_id = business_profile.merchant_id.clone(); let algorithm_id = common_utils::generate_routing_id_of_default_length(); let timestamp = common_utils::date_time::now(); - let algo = routing_algorithm::RoutingAlgorithm { - algorithm_id: algorithm_id.clone(), - profile_id: profile_id.clone(), - merchant_id, - name: SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(), - description: None, - kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic, - algorithm_data: serde_json::json!(default_success_based_routing_config), - created_at: timestamp, - modified_at: timestamp, - algorithm_for: common_enums::TransactionType::Payment, + let algo = match dynamic_routing_type { + routing_types::DynamicRoutingType::SuccessRateBasedRouting => { + let default_success_based_routing_config = + routing_types::SuccessBasedRoutingConfig::default(); + routing_algorithm::RoutingAlgorithm { + algorithm_id: algorithm_id.clone(), + profile_id: profile_id.clone(), + merchant_id, + name: SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(), + description: None, + kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic, + algorithm_data: serde_json::json!(default_success_based_routing_config), + created_at: timestamp, + modified_at: timestamp, + algorithm_for: common_enums::TransactionType::Payment, + } + } + routing_types::DynamicRoutingType::EliminationRouting => { + let default_elimination_routing_config = + routing_types::EliminationRoutingConfig::default(); + routing_algorithm::RoutingAlgorithm { + algorithm_id: algorithm_id.clone(), + profile_id: profile_id.clone(), + merchant_id, + name: ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(), + description: None, + kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic, + algorithm_data: serde_json::json!(default_elimination_routing_config), + created_at: timestamp, + modified_at: timestamp, + algorithm_for: common_enums::TransactionType::Payment, + } + } }; let record = db @@ -948,13 +1221,17 @@ pub async fn default_success_based_routing_setup( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to insert record in routing algorithm table")?; - success_based_dynamic_routing_algo.update_algorithm_id(algorithm_id, feature_to_enable); + dynamic_routing_algo_ref.update_algorithm_id( + algorithm_id, + feature_to_enable, + dynamic_routing_type, + ); update_business_profile_active_dynamic_algorithm_ref( db, key_manager_state, &key_store, business_profile, - success_based_dynamic_routing_algo, + dynamic_routing_algo_ref, ) .await?; @@ -1001,35 +1278,35 @@ impl SuccessBasedRoutingConfigParamsInterpolator { pub fn get_string_val( &self, - params: &Vec, + params: &Vec, ) -> String { let mut parts: Vec = Vec::new(); for param in params { let val = match param { - routing_types::SuccessBasedRoutingConfigParams::PaymentMethod => self + routing_types::DynamicRoutingConfigParams::PaymentMethod => self .payment_method .as_ref() .map_or(String::new(), |pm| pm.to_string()), - routing_types::SuccessBasedRoutingConfigParams::PaymentMethodType => self + routing_types::DynamicRoutingConfigParams::PaymentMethodType => self .payment_method_type .as_ref() .map_or(String::new(), |pmt| pmt.to_string()), - routing_types::SuccessBasedRoutingConfigParams::AuthenticationType => self + routing_types::DynamicRoutingConfigParams::AuthenticationType => self .authentication_type .as_ref() .map_or(String::new(), |at| at.to_string()), - routing_types::SuccessBasedRoutingConfigParams::Currency => self + routing_types::DynamicRoutingConfigParams::Currency => self .currency .as_ref() .map_or(String::new(), |cur| cur.to_string()), - routing_types::SuccessBasedRoutingConfigParams::Country => self + routing_types::DynamicRoutingConfigParams::Country => self .country .as_ref() .map_or(String::new(), |cn| cn.to_string()), - routing_types::SuccessBasedRoutingConfigParams::CardNetwork => { + routing_types::DynamicRoutingConfigParams::CardNetwork => { self.card_network.clone().unwrap_or_default() } - routing_types::SuccessBasedRoutingConfigParams::CardBin => { + routing_types::DynamicRoutingConfigParams::CardBin => { self.card_bin.clone().unwrap_or_default() } }; diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1964c1cbfa..bb8d0d4f2b 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1755,9 +1755,9 @@ impl Profile { #[cfg(feature = "dynamic_routing")] { - route = - route.service( - web::scope("/{profile_id}/dynamic_routing").service( + route = route.service( + web::scope("/{profile_id}/dynamic_routing") + .service( web::scope("/success_based") .service( web::resource("/toggle") @@ -1770,8 +1770,14 @@ impl Profile { ) }), )), + ) + .service( + web::scope("/elimination").service( + web::resource("/toggle") + .route(web::post().to(routing::toggle_elimination_routing)), + ), ), - ); + ); } route = route.service( diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index cb7744f520..d22c5e9749 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -1014,11 +1014,11 @@ pub async fn routing_update_default_config_for_profile( pub async fn toggle_success_based_routing( state: web::Data, req: HttpRequest, - query: web::Query, - path: web::Path, + query: web::Query, + path: web::Path, ) -> impl Responder { let flow = Flow::ToggleDynamicRouting; - let wrapper = routing_types::ToggleSuccessBasedRoutingWrapper { + let wrapper = routing_types::ToggleDynamicRoutingWrapper { feature_to_enable: query.into_inner().enable, profile_id: path.into_inner().profile_id, }; @@ -1029,14 +1029,15 @@ pub async fn toggle_success_based_routing( wrapper.clone(), |state, auth: auth::AuthenticationData, - wrapper: routing_types::ToggleSuccessBasedRoutingWrapper, + wrapper: routing_types::ToggleDynamicRoutingWrapper, _| { - routing::toggle_success_based_routing( + routing::toggle_specific_dynamic_routing( state, auth.merchant_account, auth.key_store, wrapper.feature_to_enable, wrapper.profile_id, + api_models::routing::DynamicRoutingType::SuccessRateBasedRouting, ) }, auth::auth_type( @@ -1085,3 +1086,46 @@ pub async fn success_based_routing_update_configs( )) .await } +#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))] +#[instrument(skip_all)] +pub async fn toggle_elimination_routing( + state: web::Data, + req: HttpRequest, + query: web::Query, + path: web::Path, +) -> impl Responder { + let flow = Flow::ToggleDynamicRouting; + let wrapper = routing_types::ToggleDynamicRoutingWrapper { + feature_to_enable: query.into_inner().enable, + profile_id: path.into_inner().profile_id, + }; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + wrapper.clone(), + |state, + auth: auth::AuthenticationData, + wrapper: routing_types::ToggleDynamicRoutingWrapper, + _| { + routing::toggle_specific_dynamic_routing( + state, + auth.merchant_account, + auth.key_store, + wrapper.feature_to_enable, + wrapper.profile_id, + api_models::routing::DynamicRoutingType::EliminationRouting, + ) + }, + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuthProfileFromRoute { + profile_id: wrapper.profile_id, + required_permission: Permission::ProfileRoutingWrite, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/storage_impl/src/redis/cache.rs b/crates/storage_impl/src/redis/cache.rs index b32c3d2204..fff5435fc7 100644 --- a/crates/storage_impl/src/redis/cache.rs +++ b/crates/storage_impl/src/redis/cache.rs @@ -72,7 +72,7 @@ pub static PM_FILTERS_CGRAPH_CACHE: Lazy = Lazy::new(|| { ) }); -/// Dynamic Algorithm Cache +/// Success based Dynamic Algorithm Cache pub static SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy = Lazy::new(|| { Cache::new( "SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE", @@ -82,6 +82,16 @@ pub static SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy = Lazy::new(|| { ) }); +/// Elimination based Dynamic Algorithm Cache +pub static ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy = Lazy::new(|| { + Cache::new( + "ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE", + CACHE_TTL, + CACHE_TTI, + Some(MAX_CAPACITY), + ) +}); + /// Trait which defines the behaviour of types that's gonna be stored in Cache pub trait Cacheable: Any + Send + Sync + DynClone { fn as_any(&self) -> &dyn Any; @@ -102,6 +112,7 @@ pub enum CacheKind<'a> { Surcharge(Cow<'a, str>), CGraph(Cow<'a, str>), SuccessBasedDynamicRoutingCache(Cow<'a, str>), + EliminationBasedDynamicRoutingCache(Cow<'a, str>), PmFiltersCGraph(Cow<'a, str>), All(Cow<'a, str>), } diff --git a/crates/storage_impl/src/redis/pub_sub.rs b/crates/storage_impl/src/redis/pub_sub.rs index 6a2012f9b2..42ad2ae079 100644 --- a/crates/storage_impl/src/redis/pub_sub.rs +++ b/crates/storage_impl/src/redis/pub_sub.rs @@ -6,8 +6,8 @@ use router_env::{logger, tracing::Instrument}; use crate::redis::cache::{ CacheKey, CacheKind, CacheRedact, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE, - DECISION_MANAGER_CACHE, PM_FILTERS_CGRAPH_CACHE, ROUTING_CACHE, - SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE, + DECISION_MANAGER_CACHE, ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE, PM_FILTERS_CGRAPH_CACHE, + ROUTING_CACHE, SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE, }; #[async_trait::async_trait] @@ -138,6 +138,15 @@ impl PubSubInterface for std::sync::Arc { .await; key } + CacheKind::EliminationBasedDynamicRoutingCache(key) => { + ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: message.tenant.clone(), + }) + .await; + key + } CacheKind::SuccessBasedDynamicRoutingCache(key) => { SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE .remove(CacheKey { @@ -205,6 +214,12 @@ impl PubSubInterface for std::sync::Arc { prefix: message.tenant.clone(), }) .await; + ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: message.tenant.clone(), + }) + .await; ROUTING_CACHE .remove(CacheKey { key: key.to_string(),