Skip to content

perf: use bytes instead of text for config table #2829

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions crates/diesel_models/src/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::schema::configs;

pub struct ConfigNew {
pub key: String,
pub config: String,
pub config: Vec<u8>,
}

#[derive(Default, Clone, Debug, Identifiable, Queryable, Deserialize, Serialize)]
Expand All @@ -20,18 +20,18 @@ pub struct Config {
#[serde(skip)]
pub id: i32,
pub key: String,
pub config: String,
pub config: Vec<u8>,
}

#[derive(Debug)]
pub enum ConfigUpdate {
Update { config: Option<String> },
Update { config: Option<Vec<u8>> },
}

#[derive(Clone, Debug, AsChangeset, Default)]
#[diesel(table_name = configs)]
pub struct ConfigUpdateInternal {
config: Option<String>,
config: Option<Vec<u8>>,
}

impl ConfigUpdateInternal {
Expand Down
2 changes: 1 addition & 1 deletion crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ diesel::table! {
id -> Int4,
#[max_length = 255]
key -> Varchar,
config -> Text,
config -> Bytea,
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/core/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ pub async fn create_merchant_account(

db.insert_config(diesel_models::configs::ConfigNew {
key: format!("{}_requires_cvv", merchant_account.merchant_id),
config: "true".to_string(),
config: Vec::from("true"),
})
.await
.map_err(|err| {
Expand Down
4 changes: 2 additions & 2 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1987,13 +1987,13 @@ pub async fn list_customer_payment_method(
let is_requires_cvv = db
.find_config_by_key_unwrap_or(
format!("{}_requires_cvv", merchant_account.merchant_id).as_str(),
Some("true".to_string()),
Some("true".to_string().into()),
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to fetch requires_cvv config")?;

let requires_cvv = is_requires_cvv.config != "false";
let requires_cvv = is_requires_cvv.config != Vec::from("false");

let resp = db
.find_payment_method_by_customer_id_merchant_id_list(
Expand Down
8 changes: 6 additions & 2 deletions crates/router/src/core/payments/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2548,7 +2548,7 @@ pub async fn insert_merchant_connector_creds_to_config(
"mcd_{merchant_id}_{}",
merchant_connector_details.creds_identifier
),
config: encoded_data.peek().to_owned(),
config: encoded_data.peek().to_owned().into(),
})
.await
{
Expand Down Expand Up @@ -2648,7 +2648,11 @@ pub async fn get_merchant_connector_account(
#[cfg(not(feature = "kms"))]
let private_key = state.conf.jwekey.tunnel_private_key.as_bytes();

let decrypted_mca = services::decrypt_jwe(mca_config.config.as_str(), services::KeyIdCheck::SkipKeyIdCheck, private_key, jwe::RSA_OAEP_256)
let creds = std::str::from_utf8(&mca_config.config)
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)?;

let decrypted_mca = services::decrypt_jwe(creds, services::KeyIdCheck::SkipKeyIdCheck, private_key, jwe::RSA_OAEP_256)
.await
.change_context(errors::ApiErrorResponse::UnprocessableEntity{
message: "decoding merchant_connector_details failed due to invalid data format!".into()})
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/core/payments/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use rand::{
use rustc_hash::FxHashMap;

#[cfg(not(feature = "business_profile_routing"))]
use crate::utils::StringExt;
use crate::utils::ByteSliceExt;
use crate::{
core::{
errors as oss_errors, errors, payments as payments_oss, routing::helpers as routing_helpers,
Expand Down
9 changes: 7 additions & 2 deletions crates/router/src/core/payments/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
configs::settings::{ConnectorRequestReferenceIdConfig, Server},
connector::{Helcim, Nexinets},
core::{
errors::{self, RouterResponse, RouterResult},
errors::{self, RouterResponse, RouterResult, StorageErrorExt},
payments::{self, helpers},
utils as core_utils,
},
Expand Down Expand Up @@ -108,7 +108,12 @@ where
.store
.find_config_by_key(&format!("connector_api_version_{connector_id}"))
.await
.map(|value| value.config)
.to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)
.and_then(|value| {
String::from_utf8(value.config)
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
})
.ok()
} else {
None
Expand Down
4 changes: 2 additions & 2 deletions crates/router/src/core/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use api_models::routing::{self as routing_types, RoutingAlgorithmId};
#[cfg(feature = "business_profile_routing")]
use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery};
#[cfg(not(feature = "business_profile_routing"))]
use common_utils::ext_traits::{Encode, StringExt};
use common_utils::ext_traits::{ByteSliceExt, Encode};
#[cfg(not(feature = "business_profile_routing"))]
use diesel_models::configs;
#[cfg(feature = "business_profile_routing")]
Expand Down Expand Up @@ -148,7 +148,7 @@ pub async fn create_routing_config(
#[cfg(not(feature = "business_profile_routing"))]
{
let algorithm_str =
utils::Encode::<routing_types::RoutingAlgorithm>::encode_to_string_of_json(&algorithm)
utils::Encode::<routing_types::RoutingAlgorithm>::encode_to_vec(&algorithm)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize routing algorithm to string")?;

Expand Down
46 changes: 20 additions & 26 deletions crates/router/src/core/routing/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
core::errors::{self, RouterResult},
db::StorageInterface,
types::{domain, storage},
utils::{self, StringExt},
utils::{self, ByteSliceExt},
};

/// provides the complete merchant routing dictionary that is basically a list of all the routing
Expand Down Expand Up @@ -43,11 +43,9 @@ pub async fn get_merchant_routing_dictionary(
};

let serialized =
utils::Encode::<routing_types::RoutingDictionary>::encode_to_string_of_json(
&new_dictionary,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error serializing newly created merchant dictionary")?;
utils::Encode::<routing_types::RoutingDictionary>::encode_to_vec(&new_dictionary)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error serializing newly created merchant dictionary")?;

let new_config = configs::ConfigNew {
key,
Expand Down Expand Up @@ -87,7 +85,7 @@ pub async fn get_merchant_default_config(
Err(e) if e.current_context().is_db_not_found() => {
let new_config_conns = Vec::<routing_types::RoutableConnectorChoice>::new();
let serialized =
utils::Encode::<Vec<routing_types::RoutableConnectorChoice>>::encode_to_string_of_json(
utils::Encode::<Vec<routing_types::RoutableConnectorChoice>>::encode_to_vec(
&new_config_conns,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
Expand Down Expand Up @@ -123,11 +121,11 @@ pub async fn update_merchant_default_config(
) -> RouterResult<()> {
let key = get_default_config_key(merchant_id);
let config_str =
Encode::<Vec<routing_types::RoutableConnectorChoice>>::encode_to_string_of_json(
&connectors,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize merchant default routing config during update")?;
Encode::<Vec<routing_types::RoutableConnectorChoice>>::encode_to_vec(&connectors)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"Unable to serialize merchant default routing config during update",
)?;

let config_update = configs::ConfigUpdate::Update {
config: Some(config_str),
Expand All @@ -147,10 +145,9 @@ pub async fn update_merchant_routing_dictionary(
dictionary: routing_types::RoutingDictionary,
) -> RouterResult<()> {
let key = get_routing_dictionary_key(merchant_id);
let dictionary_str =
Encode::<routing_types::RoutingDictionary>::encode_to_string_of_json(&dictionary)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize routing dictionary during update")?;
let dictionary_str = Encode::<routing_types::RoutingDictionary>::encode_to_vec(&dictionary)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize routing dictionary during update")?;

let config_update = configs::ConfigUpdate::Update {
config: Some(dictionary_str),
Expand All @@ -169,10 +166,9 @@ pub async fn update_routing_algorithm(
algorithm_id: String,
algorithm: routing_types::RoutingAlgorithm,
) -> RouterResult<()> {
let algorithm_str =
Encode::<routing_types::RoutingAlgorithm>::encode_to_string_of_json(&algorithm)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize routing algorithm to string")?;
let algorithm_str = Encode::<routing_types::RoutingAlgorithm>::encode_to_vec(&algorithm)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to serialize routing algorithm to string")?;

let config_update = configs::ConfigUpdate::Update {
config: Some(algorithm_str),
Expand Down Expand Up @@ -281,7 +277,7 @@ pub async fn get_merchant_connector_agnostic_mandate_config(
let new_mandate_config: Vec<routing_types::DetailedConnectorChoice> = Vec::new();

let serialized =
utils::Encode::<Vec<routing_types::DetailedConnectorChoice>>::encode_to_string_of_json(
utils::Encode::<Vec<routing_types::DetailedConnectorChoice>>::encode_to_vec(
&new_mandate_config,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
Expand Down Expand Up @@ -313,11 +309,9 @@ pub async fn update_merchant_connector_agnostic_mandate_config(
) -> RouterResult<Vec<routing_types::DetailedConnectorChoice>> {
let key = get_pg_agnostic_mandate_config_key(merchant_id);
let mandate_config_str =
Encode::<Vec<routing_types::DetailedConnectorChoice>>::encode_to_string_of_json(
&mandate_config,
)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to serialize pg agnostic mandate config during update")?;
Encode::<Vec<routing_types::DetailedConnectorChoice>>::encode_to_vec(&mandate_config)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to serialize pg agnostic mandate config during update")?;

let config_update = configs::ConfigUpdate::Update {
config: Some(mandate_config_str),
Expand Down
7 changes: 6 additions & 1 deletion crates/router/src/core/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ pub async fn construct_refund_router_data<'a, F>(
.store
.find_config_by_key(&format!("connector_api_version_{connector_id}"))
.await
.map(|value| value.config)
.to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)
.and_then(|value| {
String::from_utf8(value.config)
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
})
.ok()
} else {
None
Expand Down
10 changes: 8 additions & 2 deletions crates/router/src/core/webhooks/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::marker::PhantomData;

use common_utils::{errors::CustomResult, ext_traits::ValueExt};
use common_utils::{
errors::CustomResult,
ext_traits::{ByteSliceExt, ValueExt},
};
use error_stack::ResultExt;

use crate::{
Expand Down Expand Up @@ -42,7 +45,10 @@ pub async fn is_webhook_event_disabled(
db.find_config_by_key(&redis_key)
.await
.map(|config| {
match serde_json::from_str::<api::MerchantWebhookConfig>(&config.config) {
match config
.config
.parse_struct::<api::MerchantWebhookConfig>("MerchantWebhookConfig")
{
Ok(set) => set.contains(event),
Err(err) => {
logger::warn!(?err, "error while parsing merchant webhook config");
Expand Down
6 changes: 3 additions & 3 deletions crates/router/src/db/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub trait ConfigInterface {
&self,
key: &str,
// If the config is not found it will be created with the default value.
default_config: Option<String>,
default_config: Option<Vec<u8>>,
) -> CustomResult<storage::Config, errors::StorageError>;

async fn find_config_by_key_from_db(
Expand Down Expand Up @@ -119,7 +119,7 @@ impl ConfigInterface for Store {
&self,
key: &str,
// If the config is not found it will be created with the default value.
default_config: Option<String>,
default_config: Option<Vec<u8>>,
) -> CustomResult<storage::Config, errors::StorageError> {
let find_else_unwrap_or = || async {
let conn = connection::pg_connection_write(self).await?;
Expand Down Expand Up @@ -258,7 +258,7 @@ impl ConfigInterface for MockDb {
async fn find_config_by_key_unwrap_or(
&self,
key: &str,
_default_config: Option<String>,
_default_config: Option<Vec<u8>>,
) -> CustomResult<storage::Config, errors::StorageError> {
self.find_config_by_key(key).await
}
Expand Down
34 changes: 32 additions & 2 deletions crates/router/src/types/api/configs.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,42 @@
use serde::{
ser::{Error, Serializer},
Deserialize, Deserializer,
};

#[derive(Clone, serde::Serialize, Debug, serde::Deserialize)]
pub struct Config {
pub key: String,
pub value: String,
#[serde(
deserialize_with = "string_to_vec_deser",
serialize_with = "vec_to_string_serialize"
)]
pub value: Vec<u8>,
}

#[derive(Clone, serde::Deserialize, Debug, serde::Serialize)]
pub struct ConfigUpdate {
#[serde(skip_deserializing)]
pub key: String,
pub value: String,
#[serde(
deserialize_with = "string_to_vec_deser",
serialize_with = "vec_to_string_serialize"
)]
pub value: Vec<u8>,
}

fn string_to_vec_deser<'a, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'a>,
{
let value = <String>::deserialize(deserializer)?;
Ok(value.into())
}

pub fn vec_to_string_serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let value = std::str::from_utf8(value)
.map_err(|_| S::Error::custom("Unable to serialize config value"))?;
serializer.serialize_str(value)
}
2 changes: 1 addition & 1 deletion crates/router/src/workflows/payment_sync.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use common_utils::ext_traits::{OptionExt, StringExt, ValueExt};
use common_utils::ext_traits::{ByteSliceExt, OptionExt, ValueExt};
use error_stack::ResultExt;
use router_env::logger;
use scheduler::{
Expand Down
1 change: 1 addition & 0 deletions migrations/2023-11-09-101920_change_config_type/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE configs ALTER column config TYPE TEXT USING convert_from(config,'UTF8')::text;
1 change: 1 addition & 0 deletions migrations/2023-11-09-101920_change_config_type/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE configs ALTER COLUMN config TYPE BYTEA USING convert_to(config, 'UTF8');