Skip to content

Commit

Permalink
feat(locking): validate request_id before releasing lock
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhicodes-crypto committed Sep 20, 2023
1 parent a8660ee commit 2eb2460
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 15 deletions.
43 changes: 29 additions & 14 deletions crates/router/src/core/api_locking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl LockAction {
let redis_lock_result = redis_conn
.set_key_if_not_exists_with_expiry(
redis_locking_key.as_str(),
true, // [#2129] pick up request_id from AppState
state.get_request_id(),
Some(i64::from(redis_lock_expiry_seconds)),
)
.await;
Expand Down Expand Up @@ -120,19 +120,34 @@ impl LockAction {
.change_context(errors::ApiErrorResponse::InternalServerError)?;

let redis_locking_key = input.get_redis_locking_key(merchant_id);
// [#2129] Add a step to check whether the current lock is acquired by the current request and only then delete
match redis_conn.delete_key(redis_locking_key.as_str()).await {
Ok(redis::types::DelReply::KeyDeleted) => {
logger::info!("Lock freed for locking input {:?}", input);
tracing::Span::current().record("redis_lock_released", redis_locking_key);
Ok(())
}
Ok(redis::types::DelReply::KeyNotDeleted) => {
Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(
"Status release lock called but key is not found in redis",
)

match redis_conn
.get_key::<Option<String>>(&redis_locking_key)
.await
{
Ok(val) => {
if val == state.get_request_id() {
match redis_conn.delete_key(redis_locking_key.as_str()).await {
Ok(redis::types::DelReply::KeyDeleted) => {
logger::info!("Lock freed for locking input {:?}", input);
tracing::Span::current()
.record("redis_lock_released", redis_locking_key);
Ok(())
}
Ok(redis::types::DelReply::KeyNotDeleted) => Err(
errors::ApiErrorResponse::InternalServerError,
)
.into_report()
.attach_printable(
"Status release lock called but key is not found in redis",
),
Err(error) => Err(error)
.change_context(errors::ApiErrorResponse::InternalServerError),
}
} else {
Err(errors::ApiErrorResponse::InternalServerError)
.into_report().attach_printable("The request_id which acquired the lock is not equal to the request_id requesting for releasing the lock")
}
}
Err(error) => {
Err(error).change_context(errors::ApiErrorResponse::InternalServerError)
Expand Down
24 changes: 23 additions & 1 deletion crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ pub trait AppStateInfo {
fn flow_name(&self) -> String;
fn store(&self) -> Box<dyn StorageInterface>;
#[cfg(feature = "email")]
fn email_client(&self) -> Box<dyn EmailClient>;
fn email_client(&self) -> Arc<dyn EmailClient>;
fn add_request_id(&mut self, request_id: Option<String>);
fn add_merchant_id(&mut self, merchant_id: Option<String>);
fn add_flow_name(&mut self, flow_name: String);
fn get_request_id(&self) -> Option<String>;
}

impl AppStateInfo for AppState {
Expand All @@ -67,6 +71,24 @@ impl AppStateInfo for AppState {
fn email_client(&self) -> Box<dyn EmailClient> {
self.email_client.to_owned()
}
fn add_request_id(&mut self, request_id: Option<String>) {
self.api_client.add_request_id(request_id);
}
fn add_merchant_id(&mut self, merchant_id: Option<String>) {
self.api_client.add_merchant_id(merchant_id);
}
fn add_flow_name(&mut self, flow_name: String) {
self.flow_name = flow_name;
}
fn get_request_id(&self) -> Option<String> {
self.api_client.get_request_id()
}
}

impl AsRef<Self> for AppState {
fn as_ref(&self) -> &Self {
self
}
}

impl AppState {
Expand Down

0 comments on commit 2eb2460

Please sign in to comment.