mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	fix(redis_interface): fix derps in HSET and HSETNX command wrappers (#129)
				
					
				
			This commit is contained in:
		
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1292,7 +1292,6 @@ dependencies = [ | ||||
|  "rand", | ||||
|  "redis-protocol", | ||||
|  "semver", | ||||
|  "serde_json", | ||||
|  "sha-1", | ||||
|  "tokio", | ||||
|  "tokio-native-tls", | ||||
|  | ||||
| @ -9,7 +9,7 @@ license = "Apache-2.0" | ||||
|  | ||||
| [dependencies] | ||||
| error-stack = "0.2.4" | ||||
| fred = { version = "5.2.0", features = ["metrics", "partial-tracing", "serde-json"] } | ||||
| fred = { version = "5.2.0", features = ["metrics", "partial-tracing"] } | ||||
| serde = { version = "1.0.149", features = ["derive"] } | ||||
| thiserror = "1.0.37" | ||||
|  | ||||
|  | ||||
| @ -20,7 +20,7 @@ use router_env::{tracing, tracing::instrument}; | ||||
|  | ||||
| use crate::{ | ||||
|     errors, | ||||
|     types::{RedisEntryId, SetNXReply}, | ||||
|     types::{HsetnxReply, RedisEntryId, SetnxReply}, | ||||
| }; | ||||
|  | ||||
| impl super::RedisConnectionPool { | ||||
| @ -142,7 +142,7 @@ impl super::RedisConnectionPool { | ||||
|         &self, | ||||
|         key: &str, | ||||
|         value: V, | ||||
|     ) -> CustomResult<SetNXReply, errors::RedisError> | ||||
|     ) -> CustomResult<SetnxReply, errors::RedisError> | ||||
|     where | ||||
|         V: TryInto<RedisValue> + Debug, | ||||
|         V::Error: Into<fred::error::RedisError>, | ||||
| @ -203,28 +203,13 @@ impl super::RedisConnectionPool { | ||||
|             .change_context(errors::RedisError::SetHashFailed) | ||||
|     } | ||||
|  | ||||
|     #[instrument(level = "DEBUG", skip(self))] | ||||
|     pub async fn serialize_and_set_hash_fields<V>( | ||||
|         &self, | ||||
|         key: &str, | ||||
|         values: V, | ||||
|     ) -> CustomResult<(), errors::RedisError> | ||||
|     where | ||||
|         V: serde::Serialize + Debug, | ||||
|     { | ||||
|         let serialized = Encode::<V>::encode_to_value(&values) | ||||
|             .change_context(errors::RedisError::JsonSerializationFailed)?; | ||||
|  | ||||
|         self.set_hash_fields(key, serialized).await | ||||
|     } | ||||
|  | ||||
|     #[instrument(level = "DEBUG", skip(self))] | ||||
|     pub async fn set_hash_field_if_not_exist<V>( | ||||
|         &self, | ||||
|         key: &str, | ||||
|         field: &str, | ||||
|         value: V, | ||||
|     ) -> CustomResult<SetNXReply, errors::RedisError> | ||||
|     ) -> CustomResult<HsetnxReply, errors::RedisError> | ||||
|     where | ||||
|         V: TryInto<RedisValue> + Debug, | ||||
|         V::Error: Into<fred::error::RedisError>, | ||||
| @ -242,7 +227,7 @@ impl super::RedisConnectionPool { | ||||
|         key: &str, | ||||
|         field: &str, | ||||
|         value: V, | ||||
|     ) -> CustomResult<SetNXReply, errors::RedisError> | ||||
|     ) -> CustomResult<HsetnxReply, errors::RedisError> | ||||
|     where | ||||
|         V: serde::Serialize + Debug, | ||||
|     { | ||||
|  | ||||
| @ -67,18 +67,18 @@ impl From<&RedisEntryId> for fred::types::XID { | ||||
| } | ||||
|  | ||||
| #[derive(Eq, PartialEq)] | ||||
| pub enum SetNXReply { | ||||
| pub enum SetnxReply { | ||||
|     KeySet, | ||||
|     KeyNotSet, // Existing key | ||||
| } | ||||
|  | ||||
| impl fred::types::FromRedis for SetNXReply { | ||||
| impl fred::types::FromRedis for SetnxReply { | ||||
|     fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> { | ||||
|         match value { | ||||
|             // Returns String ( "OK" ) in case of success | ||||
|             fred::types::RedisValue::String(_) => Ok(SetNXReply::KeySet), | ||||
|             fred::types::RedisValue::String(_) => Ok(Self::KeySet), | ||||
|             // Return Null in case of failure | ||||
|             fred::types::RedisValue::Null => Ok(SetNXReply::KeyNotSet), | ||||
|             fred::types::RedisValue::Null => Ok(Self::KeyNotSet), | ||||
|             // Unexpected behaviour | ||||
|             _ => Err(fred::error::RedisError::new( | ||||
|                 fred::error::RedisErrorKind::Unknown, | ||||
| @ -87,3 +87,22 @@ impl fred::types::FromRedis for SetNXReply { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Eq, PartialEq)] | ||||
| pub enum HsetnxReply { | ||||
|     KeySet, | ||||
|     KeyNotSet, // Existing key | ||||
| } | ||||
|  | ||||
| impl fred::types::FromRedis for HsetnxReply { | ||||
|     fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> { | ||||
|         match value { | ||||
|             fred::types::RedisValue::Integer(1) => Ok(Self::KeySet), | ||||
|             fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet), | ||||
|             _ => Err(fred::error::RedisError::new( | ||||
|                 fred::error::RedisErrorKind::Unknown, | ||||
|                 "Unexpected HSETNX command reply", | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -305,7 +305,7 @@ impl PaymentAttemptInterface for MockDb { | ||||
| mod storage { | ||||
|     use common_utils::date_time; | ||||
|     use error_stack::{IntoReport, ResultExt}; | ||||
|     use redis_interface::{RedisEntryId, SetNXReply}; | ||||
|     use redis_interface::{HsetnxReply, RedisEntryId}; | ||||
|  | ||||
|     use super::PaymentAttemptInterface; | ||||
|     use crate::{ | ||||
| @ -377,11 +377,11 @@ mod storage { | ||||
|                         .serialize_and_set_hash_field_if_not_exist(&key, "pa", &created_attempt) | ||||
|                         .await | ||||
|                     { | ||||
|                         Ok(SetNXReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue( | ||||
|                         Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue( | ||||
|                             format!("Payment Attempt already exists for payment_id: {}", key), | ||||
|                         )) | ||||
|                         .into_report(), | ||||
|                         Ok(SetNXReply::KeySet) => { | ||||
|                         Ok(HsetnxReply::KeySet) => { | ||||
|                             let conn = pg_connection(&self.master_pool).await; | ||||
|                             let query = payment_attempt | ||||
|                                 .insert_query(&conn) | ||||
| @ -430,9 +430,12 @@ mod storage { | ||||
|  | ||||
|                     let updated_attempt = payment_attempt.clone().apply_changeset(this.clone()); | ||||
|                     // Check for database presence as well Maybe use a read replica here ? | ||||
|                     let redis_value = serde_json::to_string(&updated_attempt) | ||||
|                         .into_report() | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|                     let updated_attempt = self | ||||
|                         .redis_conn | ||||
|                         .serialize_and_set_hash_fields(&key, ("pa", &updated_attempt)) | ||||
|                         .set_hash_fields(&key, ("pa", &redis_value)) | ||||
|                         .await | ||||
|                         .map(|_| updated_attempt) | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|  | ||||
| @ -41,7 +41,7 @@ pub trait PaymentIntentInterface { | ||||
| mod storage { | ||||
|     use common_utils::date_time; | ||||
|     use error_stack::{IntoReport, ResultExt}; | ||||
|     use redis_interface::{RedisEntryId, SetNXReply}; | ||||
|     use redis_interface::{HsetnxReply, RedisEntryId}; | ||||
|  | ||||
|     use super::PaymentIntentInterface; | ||||
|     use crate::{ | ||||
| @ -100,11 +100,11 @@ mod storage { | ||||
|                         .serialize_and_set_hash_field_if_not_exist(&key, "pi", &created_intent) | ||||
|                         .await | ||||
|                     { | ||||
|                         Ok(SetNXReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue( | ||||
|                         Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue( | ||||
|                             format!("Payment Intent already exists for payment_id: {key}"), | ||||
|                         )) | ||||
|                         .into_report(), | ||||
|                         Ok(SetNXReply::KeySet) => { | ||||
|                         Ok(HsetnxReply::KeySet) => { | ||||
|                             let conn = pg_connection(&self.master_pool).await; | ||||
|                             let query = new | ||||
|                                 .insert_query(&conn) | ||||
| @ -153,9 +153,12 @@ mod storage { | ||||
|  | ||||
|                     let updated_intent = payment_intent.clone().apply_changeset(this.clone()); | ||||
|                     // Check for database presence as well Maybe use a read replica here ? | ||||
|                     let redis_value = serde_json::to_string(&updated_intent) | ||||
|                         .into_report() | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|                     let updated_intent = self | ||||
|                         .redis_conn | ||||
|                         .serialize_and_set_hash_fields(&key, ("pi", &updated_intent)) | ||||
|                         .set_hash_fields(&key, ("pi", &redis_value)) | ||||
|                         .await | ||||
|                         .map(|_| updated_intent) | ||||
|                         .change_context(errors::StorageError::KVError)?; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use redis_interface::{errors::RedisError, RedisEntryId, SetNXReply}; | ||||
| use redis_interface::{errors::RedisError, RedisEntryId, SetnxReply}; | ||||
| use router_env::logger; | ||||
|  | ||||
| use super::{MockDb, Store}; | ||||
| @ -70,7 +70,7 @@ impl QueueInterface for Store { | ||||
|         let conn = self.redis_conn.clone(); | ||||
|         let is_lock_acquired = conn.set_key_if_not_exist(lock_key, lock_val).await; | ||||
|         match is_lock_acquired { | ||||
|             Ok(SetNXReply::KeySet) => match conn.set_expiry(lock_key, ttl).await { | ||||
|             Ok(SetnxReply::KeySet) => match conn.set_expiry(lock_key, ttl).await { | ||||
|                 Ok(()) => true, | ||||
|  | ||||
|                 #[allow(unused_must_use)] | ||||
| @ -80,7 +80,7 @@ impl QueueInterface for Store { | ||||
|                     false | ||||
|                 } | ||||
|             }, | ||||
|             Ok(SetNXReply::KeyNotSet) => { | ||||
|             Ok(SetnxReply::KeyNotSet) => { | ||||
|                 logger::error!(%tag, "Lock not acquired, previous fetch still in progress"); | ||||
|                 false | ||||
|             } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sanchith Hegde
					Sanchith Hegde