Skip to content

Commit 51df3c5

Browse files
NicoMolinaOZzeljkoXtirumerla
authored
fix: Adding lock to init relayer instances (#622)
* fix: Adding lock to init relayer instances * fix: Fixing test * test: Adding test cases * test: Adding coverage * test: Adding coverage * test: Adding test cases * feat: Global lock for init and config file processing * fix: Improvements * fix: Suggestions * fix: Suggestions * chore: Use distributed mode flag * fix: Improvements * fix: Fixing redis config validation for dist mode * feat: Added logic to recovery after timeout * test: Test coverage * fix: Config processing path locks * fix: Change wording to avoid codeQL false positive --------- Co-authored-by: Zeljko <zeljko89markovic@gmail.com> Co-authored-by: tirumerla <tirumerla@gmail.com>
1 parent fabe2f3 commit 51df3c5

9 files changed

Lines changed: 2562 additions & 46 deletions

File tree

src/bootstrap/config_processor.rs

Lines changed: 407 additions & 17 deletions
Large diffs are not rendered by default.

src/bootstrap/initialize_relayers.rs

Lines changed: 909 additions & 13 deletions
Large diffs are not rendered by default.

src/repositories/api_key/api_key_redis.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -330,54 +330,54 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository {
330330
.await?;
331331
let plugin_list_key = self.api_key_list_key();
332332

333-
debug!("Checking if plugin entries exist");
333+
debug!("Checking if API key entries exist");
334334

335335
let exists: bool = conn
336336
.exists(&plugin_list_key)
337337
.await
338338
.map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
339339

340-
debug!("Plugin entries exist: {}", exists);
340+
debug!("API key entries exist: {}", exists);
341341
Ok(exists)
342342
}
343343

344344
async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
345345
let mut conn = self
346346
.get_connection(self.connections.primary(), "drop_all_entries")
347347
.await?;
348-
let plugin_list_key = self.api_key_list_key();
348+
let api_key_list_key = self.api_key_list_key();
349349

350-
debug!("Dropping all plugin entries");
350+
debug!("Dropping all API key entries");
351351

352-
// Get all plugin IDs first
353-
let plugin_ids: Vec<String> = conn
354-
.smembers(&plugin_list_key)
352+
// Get all API key IDs first
353+
let api_key_ids: Vec<String> = conn
354+
.smembers(&api_key_list_key)
355355
.await
356356
.map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
357357

358-
if plugin_ids.is_empty() {
359-
debug!("No plugin entries to drop");
358+
if api_key_ids.is_empty() {
359+
debug!("No API key entries to drop");
360360
return Ok(());
361361
}
362362

363363
// Use pipeline for atomic operations
364364
let mut pipe = redis::pipe();
365365
pipe.atomic();
366366

367-
// Delete all individual plugin entries
368-
for plugin_id in &plugin_ids {
369-
let plugin_key = self.api_key_key(plugin_id);
370-
pipe.del(&plugin_key);
367+
// Delete all individual API key entries
368+
for api_key_id in &api_key_ids {
369+
let api_key_key = self.api_key_key(api_key_id);
370+
pipe.del(&api_key_key);
371371
}
372372

373-
// Delete the plugin list key
374-
pipe.del(&plugin_list_key);
373+
// Delete the API key list key
374+
pipe.del(&api_key_list_key);
375375

376376
pipe.exec_async(&mut conn)
377377
.await
378378
.map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
379379

380-
debug!("Dropped {} plugin entries", plugin_ids.len());
380+
debug!("Dropped {} API key entries", api_key_ids.len());
381381
Ok(())
382382
}
383383
}

src/repositories/relayer/mod.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
utils::RedisConnections,
3535
};
3636
use async_trait::async_trait;
37+
use deadpool_redis::Pool;
3738
use std::sync::Arc;
3839

3940
#[async_trait]
@@ -67,6 +68,19 @@ pub trait RelayerRepository: Repository<RelayerRepoModel, String> + Send + Sync
6768
/// Returns true if this repository uses persistent storage (e.g., Redis).
6869
/// Returns false for in-memory storage.
6970
fn is_persistent_storage(&self) -> bool;
71+
72+
/// Returns connection info for distributed operations.
73+
///
74+
/// This method provides access to the underlying connection and key prefix
75+
/// when using persistent storage. This is useful for distributed locking and
76+
/// other coordination operations that need direct storage access.
77+
///
78+
/// # Returns
79+
/// * `Some((pool, prefix))` - If using persistent storage (e.g., Redis)
80+
/// * `None` - If using in-memory storage (default)
81+
fn connection_info(&self) -> Option<(Arc<Pool>, String)> {
82+
None
83+
}
7084
}
7185

7286
#[cfg(test)]
@@ -96,6 +110,7 @@ mockall::mock! {
96110
async fn disable_relayer(&self, relayer_id: String, reason: DisabledReason) -> Result<RelayerRepoModel, RepositoryError>;
97111
async fn update_policy(&self, id: String, policy: RelayerNetworkPolicy) -> Result<RelayerRepoModel, RepositoryError>;
98112
fn is_persistent_storage(&self) -> bool;
113+
fn connection_info(&self) -> Option<(Arc<Pool>, String)>;
99114
}
100115
}
101116

@@ -120,6 +135,24 @@ impl RelayerRepositoryStorage {
120135
key_prefix,
121136
)?))
122137
}
138+
139+
/// Returns connection info for distributed operations.
140+
///
141+
/// This method provides access to the underlying Redis connection and key prefix
142+
/// when using Redis-backed storage. This is useful for distributed locking and
143+
/// other coordination operations that need direct Redis access.
144+
///
145+
/// # Returns
146+
/// * `Some((pool, prefix))` - If using persistent storage (e.g., Redis)
147+
/// * `None` - If using in-memory storage
148+
pub fn connection_info(&self) -> Option<(Arc<Pool>, String)> {
149+
match self {
150+
RelayerRepositoryStorage::InMemory(_) => None,
151+
RelayerRepositoryStorage::Redis(repo) => {
152+
Some((repo.connections.primary().clone(), repo.key_prefix.clone()))
153+
}
154+
}
155+
}
123156
}
124157

125158
impl Default for RelayerRepositoryStorage {
@@ -285,6 +318,15 @@ impl RelayerRepository for RelayerRepositoryStorage {
285318
RelayerRepositoryStorage::Redis(_) => true,
286319
}
287320
}
321+
322+
fn connection_info(&self) -> Option<(Arc<Pool>, String)> {
323+
match self {
324+
RelayerRepositoryStorage::InMemory(_) => None,
325+
RelayerRepositoryStorage::Redis(repo) => {
326+
Some((repo.connections.primary().clone(), repo.key_prefix.clone()))
327+
}
328+
}
329+
}
288330
}
289331

290332
#[cfg(test)]
@@ -499,4 +541,38 @@ mod tests {
499541
repo.drop_all_entries().await.unwrap();
500542
assert!(!repo.has_entries().await.unwrap());
501543
}
544+
545+
#[tokio::test]
546+
async fn test_connection_info_returns_none_for_in_memory() {
547+
let storage = RelayerRepositoryStorage::new_in_memory();
548+
549+
// In-memory storage should return None for connection_info
550+
assert!(storage.connection_info().is_none());
551+
}
552+
553+
#[tokio::test]
554+
async fn test_is_persistent_storage_returns_false_for_in_memory() {
555+
let storage = RelayerRepositoryStorage::new_in_memory();
556+
557+
// In-memory storage should return false for is_persistent_storage
558+
assert!(!storage.is_persistent_storage());
559+
}
560+
561+
#[tokio::test]
562+
async fn test_trait_connection_info_returns_none_for_in_memory() {
563+
let storage = RelayerRepositoryStorage::new_in_memory();
564+
565+
// Test the RelayerRepository trait's connection_info method
566+
let trait_ref: &dyn RelayerRepository = &storage;
567+
assert!(trait_ref.connection_info().is_none());
568+
}
569+
570+
#[tokio::test]
571+
async fn test_struct_connection_info_returns_none_for_in_memory() {
572+
let storage = RelayerRepositoryStorage::new_in_memory();
573+
574+
// Test the struct's own connection_info method
575+
let result: Option<(Arc<Pool>, String)> = storage.connection_info();
576+
assert!(result.is_none());
577+
}
502578
}

src/repositories/relayer/relayer_in_memory.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ impl RelayerRepository for InMemoryRelayerRepository {
166166
fn is_persistent_storage(&self) -> bool {
167167
false
168168
}
169+
170+
// Uses the trait default implementation which returns None
169171
}
170172

171173
#[async_trait]

src/repositories/relayer/relayer_redis.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,10 @@ impl RelayerRepository for RedisRelayerRepository {
560560
fn is_persistent_storage(&self) -> bool {
561561
true
562562
}
563+
564+
fn connection_info(&self) -> Option<(Arc<deadpool_redis::Pool>, String)> {
565+
Some((self.connections.primary().clone(), self.key_prefix.clone()))
566+
}
563567
}
564568

565569
#[cfg(test)]

src/utils/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub use key::*;
88
mod auth;
99
pub use auth::*;
1010

11+
mod polling;
12+
pub use polling::*;
13+
1114
mod time;
1215
pub use time::*;
1316

0 commit comments

Comments
 (0)