mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
feat: add timeout for set command on hashes and add function to allow retry in database (#509)
This commit is contained in:
@ -320,10 +320,11 @@ mod storage {
|
||||
use super::PaymentAttemptInterface;
|
||||
use crate::{
|
||||
connection::pg_connection,
|
||||
core::errors::{self, utils::RedisErrorExt, CustomResult},
|
||||
core::errors::{self, CustomResult},
|
||||
db::reverse_lookup::ReverseLookupInterface,
|
||||
services::Store,
|
||||
types::storage::{enums, kv, payment_attempt::*, ReverseLookupNew},
|
||||
utils::db_utils,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -517,15 +518,15 @@ mod storage {
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
// [#439]: get the attempt_id from payment_intent
|
||||
let key = format!("{merchant_id}_{payment_id}");
|
||||
@ -534,15 +535,16 @@ mod storage {
|
||||
.await
|
||||
.map_err(Into::<errors::StorageError>::into)
|
||||
.into_report()?;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<PaymentAttempt>(
|
||||
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
&lookup.pk_id,
|
||||
&lookup.sk_id,
|
||||
"PaymentAttempt",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(&key))
|
||||
// Check for database presence as well Maybe use a read replica here ?
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -554,19 +556,20 @@ mod storage {
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_connector_transaction_id_payment_id_merchant_id(
|
||||
&conn,
|
||||
connector_transaction_id,
|
||||
payment_id,
|
||||
merchant_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_connector_transaction_id_payment_id_merchant_id(
|
||||
&conn,
|
||||
connector_transaction_id,
|
||||
payment_id,
|
||||
merchant_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
// We assume that PaymentAttempt <=> PaymentIntent is a one-to-one relation for now
|
||||
let lookup_id = format!("{merchant_id}_{connector_transaction_id}");
|
||||
@ -576,14 +579,16 @@ mod storage {
|
||||
.map_err(Into::<errors::StorageError>::into)
|
||||
.into_report()?;
|
||||
let key = &lookup.pk_id;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<PaymentAttempt>(
|
||||
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
key,
|
||||
&lookup.sk_id,
|
||||
"PaymentAttempt",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(key))
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -615,18 +620,19 @@ mod storage {
|
||||
connector_txn_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_merchant_id_connector_txn_id(
|
||||
&conn,
|
||||
merchant_id,
|
||||
connector_txn_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_merchant_id_connector_txn_id(
|
||||
&conn,
|
||||
merchant_id,
|
||||
connector_txn_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
let lookup_id = format!("{merchant_id}_{connector_txn_id}");
|
||||
@ -637,14 +643,15 @@ mod storage {
|
||||
.into_report()?;
|
||||
|
||||
let key = &lookup.pk_id;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<PaymentAttempt>(
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
key,
|
||||
&lookup.sk_id,
|
||||
"PaymentAttempt",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(key))
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -655,14 +662,15 @@ mod storage {
|
||||
attempt_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<PaymentAttempt, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_merchant_id_attempt_id(&conn, merchant_id, attempt_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentAttempt::find_by_merchant_id_attempt_id(&conn, merchant_id, attempt_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
let lookup_id = format!("{merchant_id}_{attempt_id}");
|
||||
@ -672,14 +680,15 @@ mod storage {
|
||||
.map_err(Into::<errors::StorageError>::into)
|
||||
.into_report()?;
|
||||
let key = &lookup.pk_id;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<PaymentAttempt>(
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
key,
|
||||
&lookup.sk_id,
|
||||
"PaymentAttempt",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(key))
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ mod storage {
|
||||
core::errors::{self, CustomResult},
|
||||
services::Store,
|
||||
types::storage::{enums, kv, payment_intent::*},
|
||||
utils::{self, storage_partitioning},
|
||||
utils::{self, db_utils, storage_partitioning},
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -188,32 +188,24 @@ mod storage {
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<PaymentIntent, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentIntent::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
PaymentIntent::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
let key = format!("{merchant_id}_{payment_id}");
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<PaymentIntent>(
|
||||
&key,
|
||||
"pi",
|
||||
"PaymentIntent",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| match error.current_context() {
|
||||
errors::RedisError::NotFound => errors::StorageError::ValueNotFound(
|
||||
format!("Payment Intent does not exist for {key}"),
|
||||
)
|
||||
.into(),
|
||||
_ => error.change_context(errors::StorageError::KVError),
|
||||
})
|
||||
// Check for database presence as well Maybe use a read replica here ?
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize(&key, "pi", "PaymentIntent"),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ mod storage {
|
||||
use super::RefundInterface;
|
||||
use crate::{
|
||||
connection::pg_connection,
|
||||
core::errors::{self, utils::RedisErrorExt, CustomResult},
|
||||
core::errors::{self, CustomResult},
|
||||
db::reverse_lookup::ReverseLookupInterface,
|
||||
logger,
|
||||
services::Store,
|
||||
@ -219,18 +219,19 @@ mod storage {
|
||||
merchant_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_types::Refund, errors::StorageError> {
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
storage_types::Refund::find_by_internal_reference_id_merchant_id(
|
||||
&conn,
|
||||
internal_reference_id,
|
||||
merchant_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
storage_types::Refund::find_by_internal_reference_id_merchant_id(
|
||||
&conn,
|
||||
internal_reference_id,
|
||||
merchant_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
let lookup_id = format!("{merchant_id}_{internal_reference_id}");
|
||||
let lookup = self
|
||||
@ -240,14 +241,15 @@ mod storage {
|
||||
.into_report()?;
|
||||
|
||||
let key = &lookup.pk_id;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<storage_types::Refund>(
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
key,
|
||||
&lookup.sk_id,
|
||||
"Refund",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(key))
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,18 +465,15 @@ mod storage {
|
||||
refund_id: &str,
|
||||
storage_scheme: enums::MerchantStorageScheme,
|
||||
) -> CustomResult<storage_types::Refund, errors::StorageError> {
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
storage_types::Refund::find_by_merchant_id_refund_id(
|
||||
&conn,
|
||||
merchant_id,
|
||||
refund_id,
|
||||
)
|
||||
let database_call = || async {
|
||||
let conn = pg_connection(&self.master_pool).await;
|
||||
storage_types::Refund::find_by_merchant_id_refund_id(&conn, merchant_id, refund_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
};
|
||||
match storage_scheme {
|
||||
enums::MerchantStorageScheme::PostgresOnly => database_call().await,
|
||||
enums::MerchantStorageScheme::RedisKv => {
|
||||
let lookup_id = format!("{merchant_id}_{refund_id}");
|
||||
let lookup = self
|
||||
@ -484,14 +483,15 @@ mod storage {
|
||||
.into_report()?;
|
||||
|
||||
let key = &lookup.pk_id;
|
||||
self.redis_conn
|
||||
.get_hash_field_and_deserialize::<storage_types::Refund>(
|
||||
db_utils::try_redis_get_else_try_database_get(
|
||||
self.redis_conn.get_hash_field_and_deserialize(
|
||||
key,
|
||||
&lookup.sk_id,
|
||||
"Refund",
|
||||
)
|
||||
.await
|
||||
.map_err(|error| error.to_redis_failed_response(key))
|
||||
),
|
||||
database_call,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,3 +10,6 @@ static GLOBAL_METER: Lazy<Meter> = Lazy::new(|| global::meter("ROUTER_API"));
|
||||
|
||||
pub(crate) static HEALTH_METRIC: Lazy<Counter<u64>> =
|
||||
Lazy::new(|| GLOBAL_METER.u64_counter("HEALTH_API").init());
|
||||
|
||||
pub(crate) static KV_MISS: Lazy<Counter<u64>> =
|
||||
Lazy::new(|| GLOBAL_METER.u64_counter("KV_MISS").init());
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use crate::{core::errors, routes::metrics};
|
||||
|
||||
#[cfg(feature = "kv_store")]
|
||||
/// Generates hscan field pattern. Suppose the field is pa_1234_ref_1211 it will generate
|
||||
/// pa_1234_ref_*
|
||||
@ -8,3 +10,26 @@ pub fn generate_hscan_pattern_for_refund(sk: &str) -> String {
|
||||
.collect::<Vec<&str>>()
|
||||
.join("_")
|
||||
}
|
||||
|
||||
// The first argument should be a future while the second argument should be a closure that returns a future for a database call
|
||||
pub async fn try_redis_get_else_try_database_get<F, RFut, DFut, T>(
|
||||
redis_fut: RFut,
|
||||
database_call_closure: F,
|
||||
) -> errors::CustomResult<T, errors::StorageError>
|
||||
where
|
||||
F: FnOnce() -> DFut,
|
||||
RFut: futures::Future<Output = errors::CustomResult<T, redis_interface::errors::RedisError>>,
|
||||
DFut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>>,
|
||||
{
|
||||
let redis_output = redis_fut.await;
|
||||
match redis_output {
|
||||
Ok(output) => Ok(output),
|
||||
Err(redis_error) => match redis_error.current_context() {
|
||||
redis_interface::errors::RedisError::NotFound => {
|
||||
metrics::KV_MISS.add(&metrics::CONTEXT, 1, &[]);
|
||||
database_call_closure().await
|
||||
}
|
||||
_ => Err(redis_error.change_context(errors::StorageError::KVError)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user