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

refactor(conditional_configs): refactor conditional_configs to use Moka Cache instead of Static Cache #4814

Merged
merged 24 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c6d52d5
add moka cache for decision manager
Aprabhat19 May 29, 2024
6f89a32
minor changes
Aprabhat19 May 29, 2024
7f2bae1
chore: run formatter
hyperswitch-bot[bot] May 29, 2024
701e70d
Merge branch 'main' of https://github.com/juspay/hyperswitch into cac…
Aprabhat19 May 30, 2024
d02616a
change the name of the cgraph cache
Aprabhat19 May 30, 2024
ab744c4
Merge branch 'cache-decision-manager' of https://github.com/juspay/hy…
Aprabhat19 May 30, 2024
10c04ac
cache the Vir Program, instead of the config
Aprabhat19 May 31, 2024
529bce7
chore: run formatter
hyperswitch-bot[bot] May 31, 2024
e08d9b7
cache the surcharge configs as well
Aprabhat19 May 31, 2024
9354bcc
chore: run formatter
hyperswitch-bot[bot] May 31, 2024
d643c38
Merge branch 'main' into cache-decision-manager
Aprabhat19 Jun 1, 2024
f119438
make struct VirInterpreter as public
Aprabhat19 Jun 3, 2024
e44e6ae
Merge branch 'cache-decision-manager' of https://github.com/juspay/hy…
Aprabhat19 Jun 3, 2024
06e2830
address comments
Aprabhat19 Jun 5, 2024
9f3b0a0
resolve conflicts
Aprabhat19 Jun 5, 2024
0a5c04a
Merge branch 'main' of https://github.com/juspay/hyperswitch into cac…
Aprabhat19 Jun 6, 2024
1604e92
invalidate the cache for decision manager as well as surcharge
Aprabhat19 Jun 6, 2024
ba4ce65
introduce DecisionManger and Surcharge cache kinds
Aprabhat19 Jun 7, 2024
dff5af4
chore: run formatter
hyperswitch-bot[bot] Jun 7, 2024
6aa3a70
Merge branch 'main' of https://github.com/juspay/hyperswitch into cac…
Aprabhat19 Jun 7, 2024
afba6f8
Merge branch 'main' into cache-decision-manager
Aprabhat19 Jun 7, 2024
ffadf47
Merge branch 'cache-decision-manager' of https://github.com/juspay/hy…
Aprabhat19 Jun 7, 2024
28a9b50
fix failing checks
Aprabhat19 Jun 7, 2024
9a695c7
fix failing checks
Aprabhat19 Jun 7, 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
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
10 changes: 5 additions & 5 deletions crates/router/src/core/conditional_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ 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());
update_merchant_active_algorithm_ref(db, &key_store, &key, algo_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update routing algorithm ref")?;
Expand Down Expand Up @@ -134,8 +134,8 @@ 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());
update_merchant_active_algorithm_ref(db, &key_store, &key, algo_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update routing algorithm ref")?;
Expand Down Expand Up @@ -164,7 +164,7 @@ 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)
update_merchant_active_algorithm_ref(db, &key_store, &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
Loading