Skip to content

Commit

Permalink
refactor(conditional_configs): refactor conditional_configs to use Mo…
Browse files Browse the repository at this point in the history
…ka Cache instead of Static Cache (#4814)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Aprabhat19 and hyperswitch-bot[bot] authored Jun 11, 2024
1 parent 2119de9 commit 4d0c893
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 170 deletions.
5 changes: 5 additions & 0 deletions crates/euclid/src/backend/vir_interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
pub mod types;

use std::fmt::Debug;

use serde::{Deserialize, Serialize};

use crate::{
backend::{self, inputs, EuclidBackend},
frontend::{
Expand All @@ -9,6 +13,7 @@ use crate::{
},
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VirInterpreterBackend<O> {
program: vir::ValuedProgram<O>,
}
Expand Down
12 changes: 7 additions & 5 deletions crates/euclid/src/frontend/vir.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Valued Intermediate Representation
use serde::{Deserialize, Serialize};

use crate::types::{EuclidValue, Metadata};

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValuedComparisonLogic {
NegativeConjunction,
PositiveDisjunction,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValuedComparison {
pub values: Vec<EuclidValue>,
pub logic: ValuedComparisonLogic,
Expand All @@ -16,20 +18,20 @@ pub struct ValuedComparison {

pub type ValuedIfCondition = Vec<ValuedComparison>;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValuedIfStatement {
pub condition: ValuedIfCondition,
pub nested: Option<Vec<ValuedIfStatement>>,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValuedRule<O> {
pub name: String,
pub connector_selection: O,
pub statements: Vec<ValuedIfStatement>,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ValuedProgram<O> {
pub default_selection: O,
pub rules: Vec<ValuedRule<O>>,
Expand Down
12 changes: 6 additions & 6 deletions crates/euclid/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub mod transformers;

use euclid_macros::EnumNums;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use strum::VariantNames;

use crate::{
Expand Down Expand Up @@ -143,7 +143,7 @@ impl EuclidKey {

enums::collect_variants!(EuclidKey);

#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NumValueRefinement {
NotEqual,
Expand Down Expand Up @@ -178,18 +178,18 @@ impl From<NumValueRefinement> for ast::ComparisonType {
}
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct StrValue {
pub value: String,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct MetadataValue {
pub key: String,
pub value: String,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct NumValue {
pub number: i64,
pub refinement: Option<NumValueRefinement>,
Expand Down Expand Up @@ -234,7 +234,7 @@ impl NumValue {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EuclidValue {
PaymentMethod(enums::PaymentMethod),
CardBin(StrValue),
Expand Down
14 changes: 9 additions & 5 deletions crates/router/src/core/conditional_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use common_utils::ext_traits::{Encode, StringExt, ValueExt};
use diesel_models::configs;
use error_stack::ResultExt;
use euclid::frontend::ast;
use storage_impl::redis::cache;

use super::routing::helpers::{
get_payment_config_routing_id, update_merchant_active_algorithm_ref,
Expand Down Expand Up @@ -99,8 +100,9 @@ pub async fn upsert_conditional_config(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error serializing the config")?;

algo_id.update_conditional_config_id(key);
update_merchant_active_algorithm_ref(db, &key_store, algo_id)
algo_id.update_conditional_config_id(key.clone());
let config_key = cache::CacheKind::DecisionManager(key.into());
update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update routing algorithm ref")?;
Expand Down Expand Up @@ -134,8 +136,9 @@ pub async fn upsert_conditional_config(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error fetching the config")?;

algo_id.update_conditional_config_id(key);
update_merchant_active_algorithm_ref(db, &key_store, algo_id)
algo_id.update_conditional_config_id(key.clone());
let config_key = cache::CacheKind::DecisionManager(key.into());
update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update routing algorithm ref")?;
Expand Down Expand Up @@ -164,7 +167,8 @@ pub async fn delete_conditional_config(
.attach_printable("Could not decode the conditional_config algorithm")?
.unwrap_or_default();
algo_id.config_algo_id = None;
update_merchant_active_algorithm_ref(db, &key_store, algo_id)
let config_key = cache::CacheKind::DecisionManager(key.clone().into());
update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update deleted algorithm ref")?;
Expand Down
106 changes: 35 additions & 71 deletions crates/router/src/core/payment_methods/surcharge_decision_configs.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use std::sync::Arc;

use api_models::{
payment_methods::SurchargeDetailsResponse,
payments, routing,
surcharge_decision_configs::{self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord},
};
use common_utils::{ext_traits::StringExt, static_cache::StaticCache, types as common_utils_types};
use common_utils::{ext_traits::StringExt, types as common_utils_types};
use error_stack::{self, ResultExt};
use euclid::{
backend,
backend::{inputs as dsl_inputs, EuclidBackend},
};
use router_env::{instrument, tracing};
use serde::{Deserialize, Serialize};
use storage_impl::redis::cache::{self, SURCHARGE_CACHE};

use crate::{
core::{
errors::ConditionalConfigError as ConfigError,
errors::{self, ConditionalConfigError as ConfigError},
payments::{
conditional_configs::ConditionalConfigResult, routing::make_dsl_input_for_surcharge,
types, PaymentData,
Expand All @@ -29,9 +29,8 @@ use crate::{
SessionState,
};

static CONF_CACHE: StaticCache<VirInterpreterBackendCacheWrapper> = StaticCache::new();

struct VirInterpreterBackendCacheWrapper {
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VirInterpreterBackendCacheWrapper {
cached_algorithm: backend::VirInterpreterBackend<SurchargeDecisionConfigs>,
merchant_surcharge_configs: surcharge_decision_configs::MerchantSurchargeConfigs,
}
Expand All @@ -53,7 +52,7 @@ impl TryFrom<SurchargeDecisionManagerRecord> for VirInterpreterBackendCacheWrapp

enum SurchargeSource {
/// Surcharge will be generated through the surcharge rules
Generate(Arc<VirInterpreterBackendCacheWrapper>),
Generate(VirInterpreterBackendCacheWrapper),
/// Surcharge is predefined by the merchant through payment create request
Predetermined(payments::RequestSurchargeDetails),
}
Expand Down Expand Up @@ -116,19 +115,13 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list(
surcharge_decision_configs::MerchantSurchargeConfigs::default(),
),
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached(
let cached_algo = ensure_algorithm_cached(
&*state.store,
&payment_attempt.merchant_id,
algorithm_ref.timestamp,
algorithm_id.as_str(),
)
.await?;
let cached_algo = CONF_CACHE
.retrieve(&key)
.change_context(ConfigError::CacheMiss)
.attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;

let merchant_surcharge_config = cached_algo.merchant_surcharge_configs.clone();
(
SurchargeSource::Generate(cached_algo),
Expand Down Expand Up @@ -233,19 +226,13 @@ where
SurchargeSource::Predetermined(request_surcharge_details)
}
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached(
let cached_algo = ensure_algorithm_cached(
&*state.store,
&payment_data.payment_attempt.merchant_id,
algorithm_ref.timestamp,
algorithm_id.as_str(),
)
.await?;
let cached_algo = CONF_CACHE
.retrieve(&key)
.change_context(ConfigError::CacheMiss)
.attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;

SurchargeSource::Generate(cached_algo)
}
(None, None) => return Ok(surcharge_metadata),
Expand Down Expand Up @@ -291,19 +278,13 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
SurchargeSource::Predetermined(request_surcharge_details)
}
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached(
let cached_algo = ensure_algorithm_cached(
&*state.store,
&payment_attempt.merchant_id,
algorithm_ref.timestamp,
algorithm_id.as_str(),
)
.await?;
let cached_algo = CONF_CACHE
.retrieve(&key)
.change_context(ConfigError::CacheMiss)
.attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;

SurchargeSource::Generate(cached_algo)
}
(None, None) => return Ok(surcharge_metadata),
Expand Down Expand Up @@ -388,48 +369,31 @@ fn get_surcharge_details_from_surcharge_output(
pub async fn ensure_algorithm_cached(
store: &dyn StorageInterface,
merchant_id: &str,
timestamp: i64,
algorithm_id: &str,
) -> ConditionalConfigResult<String> {
) -> ConditionalConfigResult<VirInterpreterBackendCacheWrapper> {
let key = format!("surcharge_dsl_{merchant_id}");
let present = CONF_CACHE
.present(&key)
.change_context(ConfigError::DslCachePoisoned)
.attach_printable("Error checking presence of DSL")?;
let expired = CONF_CACHE
.expired(&key, timestamp)
.change_context(ConfigError::DslCachePoisoned)
.attach_printable("Error checking presence of DSL")?;

if !present || expired {
refresh_surcharge_algorithm_cache(store, key.clone(), algorithm_id, timestamp).await?
}
Ok(key)
}

#[instrument(skip_all)]
pub async fn refresh_surcharge_algorithm_cache(
store: &dyn StorageInterface,
key: String,
algorithm_id: &str,
timestamp: i64,
) -> ConditionalConfigResult<()> {
let config = store
.find_config_by_key(algorithm_id)
.await
.change_context(ConfigError::DslMissingInDb)
.attach_printable("Error parsing DSL from config")?;
let record: SurchargeDecisionManagerRecord = config
.config
.parse_struct("Program")
.change_context(ConfigError::DslParsingError)
.attach_printable("Error parsing routing algorithm from configs")?;
let value_to_cache = VirInterpreterBackendCacheWrapper::try_from(record)?;
CONF_CACHE
.save(key, value_to_cache, timestamp)
.change_context(ConfigError::DslCachePoisoned)
.attach_printable("Error saving DSL to cache")?;
Ok(())
let value_to_cache = || async {
let config: diesel_models::Config = store.find_config_by_key(algorithm_id).await?;
let record: SurchargeDecisionManagerRecord = config
.config
.parse_struct("Program")
.change_context(errors::StorageError::DeserializationFailed)
.attach_printable("Error parsing routing algorithm from configs")?;
VirInterpreterBackendCacheWrapper::try_from(record)
.change_context(errors::StorageError::ValueNotFound("Program".to_string()))
.attach_printable("Error initializing DSL interpreter backend")
};
let interpreter = cache::get_or_populate_in_memory(
store.get_cache_store().as_ref(),
&key,
value_to_cache,
&SURCHARGE_CACHE,
)
.await
.change_context(ConfigError::CacheMiss)
.attach_printable("Unable to retrieve cached routing algorithm even after refresh")?;
Ok(interpreter)
}

pub fn execute_dsl_and_get_conditional_config(
Expand Down
Loading

0 comments on commit 4d0c893

Please sign in to comment.