Skip to content

Commit

Permalink
feat(routing): add invalidate window as a service for SR based routing (
Browse files Browse the repository at this point in the history
#6264)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Aprabhat19 and hyperswitch-bot[bot] authored Nov 19, 2024
1 parent 0302c30 commit 607b3df
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 103 deletions.
4 changes: 4 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -782,3 +782,7 @@ card_networks = "Visa, AmericanExpress, Mastercard"

[network_tokenization_supported_connectors]
connector_list = "cybersource"

[grpc_client.dynamic_routing_client]
host = "localhost"
port = 7000
37 changes: 29 additions & 8 deletions crates/external_services/src/grpc_client/dynamic_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use serde;
use success_rate::{
success_rate_calculator_client::SuccessRateCalculatorClient, CalSuccessRateConfig,
CalSuccessRateRequest, CalSuccessRateResponse,
CurrentBlockThreshold as DynamicCurrentThreshold, LabelWithStatus,
UpdateSuccessRateWindowConfig, UpdateSuccessRateWindowRequest, UpdateSuccessRateWindowResponse,
CurrentBlockThreshold as DynamicCurrentThreshold, InvalidateWindowsRequest,
InvalidateWindowsResponse, LabelWithStatus, UpdateSuccessRateWindowConfig,
UpdateSuccessRateWindowRequest, UpdateSuccessRateWindowResponse,
};
use tonic::Status;
#[allow(
Expand Down Expand Up @@ -111,6 +112,11 @@ pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync {
params: String,
response: Vec<RoutableConnectorChoiceWithStatus>,
) -> DynamicRoutingResult<UpdateSuccessRateWindowResponse>;
/// To invalidates the success rate routing keys
async fn invalidate_success_rate_routing_keys(
&self,
id: String,
) -> DynamicRoutingResult<InvalidateWindowsResponse>;
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -139,9 +145,8 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {
config,
});

let mut client = self.clone();

let response = client
let response = self
.clone()
.fetch_success_rate(request)
.await
.change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure(
Expand Down Expand Up @@ -179,9 +184,8 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {
config,
});

let mut client = self.clone();

let response = client
let response = self
.clone()
.update_success_rate_window(request)
.await
.change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure(
Expand All @@ -191,6 +195,23 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {

Ok(response)
}

async fn invalidate_success_rate_routing_keys(
&self,
id: String,
) -> DynamicRoutingResult<InvalidateWindowsResponse> {
let request = tonic::Request::new(InvalidateWindowsRequest { id });

let response = self
.clone()
.invalidate_windows(request)
.await
.change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure(
"Failed to invalidate the success rate routing keys".to_string(),
))?
.into_inner();
Ok(response)
}
}

impl ForeignTryFrom<CurrentBlockThreshold> for DynamicCurrentThreshold {
Expand Down
36 changes: 30 additions & 6 deletions crates/router/src/core/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ use api_models::{
routing::{self as routing_types, RoutingRetrieveQuery},
};
use async_trait::async_trait;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use common_utils::ext_traits::AsyncExt;
use diesel_models::routing_algorithm::RoutingAlgorithm;
use error_stack::ResultExt;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use external_services::grpc_client::dynamic_routing::SuccessBasedDynamicRouting;
use hyperswitch_domain_models::{mandates, payment_address};
#[cfg(feature = "v1")]
use router_env::logger;
use router_env::metrics::add_attributes;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use router_env::{logger, metrics::add_attributes};
use rustc_hash::FxHashSet;
#[cfg(feature = "v1")]
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use storage_impl::redis::cache;

#[cfg(feature = "payouts")]
Expand Down Expand Up @@ -1182,7 +1185,7 @@ pub async fn update_default_routing_config_for_profile(
))
}

#[cfg(feature = "v1")]
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn toggle_success_based_routing(
state: SessionState,
merchant_account: domain::MerchantAccount,
Expand Down Expand Up @@ -1379,7 +1382,7 @@ pub async fn toggle_success_based_routing(
}
}

#[cfg(feature = "v1")]
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn success_based_routing_update_configs(
state: SessionState,
request: routing_types::SuccessBasedRoutingConfig,
Expand Down Expand Up @@ -1449,6 +1452,27 @@ pub async fn success_based_routing_update_configs(
1,
&add_attributes([("profile_id", profile_id.get_string_repr().to_owned())]),
);

let prefix_of_dynamic_routing_keys = helpers::generate_tenant_business_profile_id(
&state.tenant.redis_key_prefix,
profile_id.get_string_repr(),
);
state
.grpc_client
.dynamic_routing
.success_rate_client
.as_ref()
.async_map(|sr_client| async {
sr_client
.invalidate_success_rate_routing_keys(prefix_of_dynamic_routing_keys)
.await
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
message: "Failed to invalidate the routing keys".to_string(),
})
})
.await
.transpose()?;

Ok(service_api::ApplicationResponse::Json(new_record))
}

Expand Down
77 changes: 42 additions & 35 deletions crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1724,47 +1724,54 @@ impl Profile {
#[cfg(all(feature = "olap", feature = "v1"))]
impl Profile {
pub fn server(state: AppState) -> Scope {
web::scope("/account/{account_id}/business_profile")
let mut route = web::scope("/account/{account_id}/business_profile")
.app_data(web::Data::new(state))
.service(
web::resource("")
.route(web::post().to(profiles::profile_create))
.route(web::get().to(profiles::profiles_list)),
)
.service(
web::scope("/{profile_id}")
.service(
web::scope("/dynamic_routing").service(
web::scope("/success_based")
.service(
web::resource("/toggle").route(
web::post().to(routing::toggle_success_based_routing),
),
)
.service(web::resource("/config/{algorithm_id}").route(
web::patch().to(|state, req, path, payload| {
routing::success_based_routing_update_configs(
state, req, path, payload,
)
}),
)),
),
)
.service(
web::resource("")
.route(web::get().to(profiles::profile_retrieve))
.route(web::post().to(profiles::profile_update))
.route(web::delete().to(profiles::profile_delete)),
)
.service(
web::resource("/toggle_extended_card_info")
.route(web::post().to(profiles::toggle_extended_card_info)),
)
.service(
web::resource("/toggle_connector_agnostic_mit")
.route(web::post().to(profiles::toggle_connector_agnostic_mit)),
);

#[cfg(feature = "dynamic_routing")]
{
route =
route.service(
web::scope("/{profile_id}/dynamic_routing").service(
web::scope("/success_based")
.service(
web::resource("/toggle")
.route(web::post().to(routing::toggle_success_based_routing)),
)
.service(web::resource("/config/{algorithm_id}").route(
web::patch().to(|state, req, path, payload| {
routing::success_based_routing_update_configs(
state, req, path, payload,
)
}),
)),
),
)
);
}

route = route.service(
web::scope("/{profile_id}")
.service(
web::resource("")
.route(web::get().to(profiles::profile_retrieve))
.route(web::post().to(profiles::profile_update))
.route(web::delete().to(profiles::profile_delete)),
)
.service(
web::resource("/toggle_extended_card_info")
.route(web::post().to(profiles::toggle_extended_card_info)),
)
.service(
web::resource("/toggle_connector_agnostic_mit")
.route(web::post().to(profiles::toggle_connector_agnostic_mit)),
),
);

route
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/router/src/routes/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ pub async fn routing_update_default_config_for_profile(
.await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn toggle_success_based_routing(
state: web::Data<AppState>,
Expand Down Expand Up @@ -1052,7 +1052,7 @@ pub async fn toggle_success_based_routing(
.await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn success_based_routing_update_configs(
state: web::Data<AppState>,
Expand Down
115 changes: 63 additions & 52 deletions proto/success_rate.proto
Original file line number Diff line number Diff line change
@@ -1,57 +1,68 @@
syntax = "proto3";
package success_rate;

service SuccessRateCalculator {
rpc FetchSuccessRate (CalSuccessRateRequest) returns (CalSuccessRateResponse);

rpc UpdateSuccessRateWindow (UpdateSuccessRateWindowRequest) returns (UpdateSuccessRateWindowResponse);
}

// API-1 types
message CalSuccessRateRequest {
string id = 1;
string params = 2;
repeated string labels = 3;
CalSuccessRateConfig config = 4;
}

message CalSuccessRateConfig {
uint32 min_aggregates_size = 1;
double default_success_rate = 2;
}

message CalSuccessRateResponse {
repeated LabelWithScore labels_with_score = 1;
}

message LabelWithScore {
double score = 1;
string label = 2;
}
service SuccessRateCalculator {
rpc FetchSuccessRate (CalSuccessRateRequest) returns (CalSuccessRateResponse);

rpc UpdateSuccessRateWindow (UpdateSuccessRateWindowRequest) returns (UpdateSuccessRateWindowResponse);

rpc InvalidateWindows (InvalidateWindowsRequest) returns (InvalidateWindowsResponse);
}

// API-1 types
message CalSuccessRateRequest {
string id = 1;
string params = 2;
repeated string labels = 3;
CalSuccessRateConfig config = 4;
}

message CalSuccessRateConfig {
uint32 min_aggregates_size = 1;
double default_success_rate = 2;
}

message CalSuccessRateResponse {
repeated LabelWithScore labels_with_score = 1;
}

message LabelWithScore {
double score = 1;
string label = 2;
}

// API-2 types
message UpdateSuccessRateWindowRequest {
string id = 1;
string params = 2;
repeated LabelWithStatus labels_with_status = 3;
UpdateSuccessRateWindowConfig config = 4;
}

message LabelWithStatus {
string label = 1;
bool status = 2;
}

message UpdateSuccessRateWindowConfig {
uint32 max_aggregates_size = 1;
CurrentBlockThreshold current_block_threshold = 2;
}

message CurrentBlockThreshold {
optional uint64 duration_in_mins = 1;
uint64 max_total_count = 2;
}

message UpdateSuccessRateWindowResponse {
string message = 1;
}
message UpdateSuccessRateWindowRequest {
string id = 1;
string params = 2;
repeated LabelWithStatus labels_with_status = 3;
UpdateSuccessRateWindowConfig config = 4;
}

message LabelWithStatus {
string label = 1;
bool status = 2;
}

message UpdateSuccessRateWindowConfig {
uint32 max_aggregates_size = 1;
CurrentBlockThreshold current_block_threshold = 2;
}

message CurrentBlockThreshold {
optional uint64 duration_in_mins = 1;
uint64 max_total_count = 2;
}

message UpdateSuccessRateWindowResponse {
string message = 1;
}

// API-3 types
message InvalidateWindowsRequest {
string id = 1;
}

message InvalidateWindowsResponse {
string message = 1;
}

0 comments on commit 607b3df

Please sign in to comment.