mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(router): add route to invalidate cache entry (#1100)
Co-authored-by: jeeva <jeeva.ramu@codurance.com> Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com>
This commit is contained in:
		| @ -117,6 +117,10 @@ impl Cache { | ||||
|         let val = self.get(key)?; | ||||
|         (*val).as_any().downcast_ref::<T>().cloned() | ||||
|     } | ||||
|  | ||||
|     pub async fn remove(&self, key: &str) { | ||||
|         self.invalidate(key).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| @ -130,6 +134,23 @@ mod cache_tests { | ||||
|         assert_eq!(cache.get_val::<String>("key"), Some(String::from("val"))); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test] | ||||
|     async fn eviction_on_size_test() { | ||||
|         let cache = Cache::new(2, 2, Some(0)); | ||||
|         cache.push("key".to_string(), "val".to_string()).await; | ||||
|         assert_eq!(cache.get_val::<String>("key"), None); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test] | ||||
|     async fn invalidate_cache_for_key() { | ||||
|         let cache = Cache::new(1800, 1800, None); | ||||
|         cache.push("key".to_string(), "val".to_string()).await; | ||||
|  | ||||
|         cache.remove("key").await; | ||||
|  | ||||
|         assert_eq!(cache.get_val::<String>("key"), None); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test] | ||||
|     async fn eviction_on_time_test() { | ||||
|         let cache = Cache::new(2, 2, None); | ||||
| @ -137,11 +158,4 @@ mod cache_tests { | ||||
|         tokio::time::sleep(std::time::Duration::from_secs(3)).await; | ||||
|         assert_eq!(cache.get_val::<String>("key"), None); | ||||
|     } | ||||
|  | ||||
|     #[tokio::test] | ||||
|     async fn eviction_on_size_test() { | ||||
|         let cache = Cache::new(2, 2, Some(0)); | ||||
|         cache.push("key".to_string(), "val".to_string()).await; | ||||
|         assert_eq!(cache.get_val::<String>("key"), None); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| pub mod admin; | ||||
| pub mod api_keys; | ||||
| pub mod cache; | ||||
| pub mod cards_info; | ||||
| pub mod configs; | ||||
| pub mod customers; | ||||
|  | ||||
							
								
								
									
										25
									
								
								crates/router/src/core/cache.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								crates/router/src/core/cache.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| use common_utils::errors::CustomResult; | ||||
| use redis_interface::DelReply; | ||||
|  | ||||
| use super::errors; | ||||
| use crate::{ | ||||
|     cache::{ACCOUNTS_CACHE, CONFIG_CACHE}, | ||||
|     db::StorageInterface, | ||||
|     services, | ||||
| }; | ||||
|  | ||||
| pub async fn invalidate( | ||||
|     store: &dyn StorageInterface, | ||||
|     key: &str, | ||||
| ) -> CustomResult<services::api::ApplicationResponse<serde_json::Value>, errors::ApiErrorResponse> { | ||||
|     CONFIG_CACHE.remove(key).await; | ||||
|     ACCOUNTS_CACHE.remove(key).await; | ||||
|  | ||||
|     match store.get_redis_conn().delete_key(key).await { | ||||
|         Ok(DelReply::KeyDeleted) => Ok(services::api::ApplicationResponse::StatusOk), | ||||
|         Ok(DelReply::KeyNotDeleted) => Err(errors::ApiErrorResponse::InvalidRequestUrl.into()), | ||||
|         Err(error) => Err(error | ||||
|             .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|             .attach_printable("Failed to invalidate cache")), | ||||
|     } | ||||
| } | ||||
| @ -109,7 +109,7 @@ impl QueueInterface for Store { | ||||
|     async fn release_pt_lock(&self, tag: &str, lock_key: &str) -> CustomResult<bool, RedisError> { | ||||
|         let is_lock_released = self.redis_conn()?.delete_key(lock_key).await; | ||||
|         Ok(match is_lock_released { | ||||
|             Ok(()) => true, | ||||
|             Ok(_del_reply) => true, | ||||
|             Err(error) => { | ||||
|                 logger::error!(error=%error.current_context(), %tag, "Error while releasing lock"); | ||||
|                 false | ||||
|  | ||||
| @ -138,7 +138,9 @@ pub fn mk_app( | ||||
|         server_app = server_app.service(routes::StripeApis::server(state.clone())); | ||||
|     } | ||||
|     server_app = server_app.service(routes::Cards::server(state.clone())); | ||||
|     server_app = server_app.service(routes::Cache::server(state.clone())); | ||||
|     server_app = server_app.service(routes::Health::server(state)); | ||||
|  | ||||
|     server_app | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| pub mod admin; | ||||
| pub mod api_keys; | ||||
| pub mod app; | ||||
| pub mod cache; | ||||
| pub mod cards_info; | ||||
| pub mod configs; | ||||
| pub mod customers; | ||||
| @ -21,9 +22,9 @@ pub mod webhooks; | ||||
| #[cfg(feature = "dummy_connector")] | ||||
| pub use self::app::DummyConnector; | ||||
| pub use self::app::{ | ||||
|     ApiKeys, AppState, Cards, Configs, Customers, Disputes, EphemeralKey, Files, Health, Mandates, | ||||
|     MerchantAccount, MerchantConnectorAccount, PaymentMethods, Payments, Payouts, Refunds, | ||||
|     Webhooks, | ||||
|     ApiKeys, AppState, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, Files, Health, | ||||
|     Mandates, MerchantAccount, MerchantConnectorAccount, PaymentMethods, Payments, Payouts, | ||||
|     Refunds, Webhooks, | ||||
| }; | ||||
| #[cfg(feature = "stripe")] | ||||
| pub use super::compatibility::stripe::StripeApis; | ||||
|  | ||||
| @ -5,9 +5,9 @@ use tokio::sync::oneshot; | ||||
|  | ||||
| #[cfg(feature = "dummy_connector")] | ||||
| use super::dummy_connector::*; | ||||
| use super::health::*; | ||||
| #[cfg(feature = "olap")] | ||||
| use super::{admin::*, api_keys::*, disputes::*, files::*}; | ||||
| use super::{cache::*, health::*}; | ||||
| #[cfg(any(feature = "olap", feature = "oltp"))] | ||||
| use super::{configs::*, customers::*, mandates::*, payments::*, payouts::*, refunds::*}; | ||||
| #[cfg(feature = "oltp")] | ||||
| @ -424,7 +424,6 @@ impl Configs { | ||||
|     pub fn server(config: AppState) -> Scope { | ||||
|         web::scope("/configs") | ||||
|             .app_data(web::Data::new(config)) | ||||
|             .service(web::resource("/").route(web::post().to(config_key_create))) | ||||
|             .service( | ||||
|                 web::resource("/{key}") | ||||
|                     .route(web::get().to(config_key_retrieve)) | ||||
| @ -465,10 +464,6 @@ impl Disputes { | ||||
|                     .route(web::post().to(submit_dispute_evidence)) | ||||
|                     .route(web::put().to(attach_dispute_evidence)), | ||||
|             ) | ||||
|             .service( | ||||
|                 web::resource("/evidence/{dispute_id}") | ||||
|                     .route(web::get().to(retrieve_dispute_evidence)), | ||||
|             ) | ||||
|             .service(web::resource("/{dispute_id}").route(web::get().to(retrieve_dispute))) | ||||
|     } | ||||
| } | ||||
| @ -498,3 +493,13 @@ impl Files { | ||||
|             ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Cache; | ||||
|  | ||||
| impl Cache { | ||||
|     pub fn server(state: AppState) -> Scope { | ||||
|         web::scope("/cache") | ||||
|             .app_data(web::Data::new(state)) | ||||
|             .service(web::resource("/invalidate/{key}").route(web::post().to(invalidate))) | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										29
									
								
								crates/router/src/routes/cache.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								crates/router/src/routes/cache.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| use actix_web::{web, HttpRequest, Responder}; | ||||
| use router_env::{instrument, tracing, Flow}; | ||||
|  | ||||
| use super::AppState; | ||||
| use crate::{ | ||||
|     core::cache, | ||||
|     services::{api, authentication as auth}, | ||||
| }; | ||||
|  | ||||
| #[instrument(skip_all)] | ||||
| pub async fn invalidate( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     key: web::Path<String>, | ||||
| ) -> impl Responder { | ||||
|     let flow = Flow::CacheInvalidate; | ||||
|  | ||||
|     let key = key.into_inner().to_owned(); | ||||
|  | ||||
|     api::server_wrap( | ||||
|         flow, | ||||
|         state.get_ref(), | ||||
|         &req, | ||||
|         &key, | ||||
|         |state, _, key| cache::invalidate(&*state.store, key), | ||||
|         &auth::AdminApiAuth, | ||||
|     ) | ||||
|     .await | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jeeva
					Jeeva