#![allow(non_snake_case)]
use async_trait::async_trait;
use redis::{AsyncCommands, Client};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
use crate::core::RiResult;
use crate::queue::{RiQueue, RiQueueMessage, RiQueueProducer, RiQueueConsumer, RiQueueStats};
pub struct RiRedisQueue {
name: String,
client: Arc<Client>,
}
impl RiRedisQueue {
pub async fn new(name: &str, connection_string: &str) -> RiResult<Self> {
let client = Client::open(connection_string)?;
Self::new_with_client(name, client)
}
pub fn new_with_client(name: &str, client: Client) -> RiResult<Self> {
Ok(Self {
name: name.to_string(),
client: Arc::new(client),
})
}
pub async fn new_with_connection(name: &str, connection_string: &str) -> RiResult<Self> {
let client = Client::open(connection_string)?;
Ok(Self {
name: name.to_string(),
client: Arc::new(client),
})
}
}
#[async_trait]
impl RiQueue for RiRedisQueue {
async fn create_producer(&self) -> RiResult<Box<dyn RiQueueProducer>> {
let conn = self.client.get_async_connection().await?;
Ok(Box::new(RedisQueueProducer {
connection: Arc::new(Mutex::new(conn)),
queue_name: self.name.clone(),
}))
}
async fn create_consumer(&self, _consumer_group: &str) -> RiResult<Box<dyn RiQueueConsumer>> {
let conn = self.client.get_async_connection().await?;
Ok(Box::new(RedisQueueConsumer {
connection: Arc::new(Mutex::new(conn)),
queue_name: self.name.clone(),
paused: Arc::new(Mutex::new(false)),
}))
}
async fn get_stats(&self) -> RiResult<RiQueueStats> {
let mut conn = self.client.get_async_connection().await?;
let len: i64 = conn.llen(&self.name).await?;
Ok(RiQueueStats {
queue_name: self.name.clone(),
message_count: len as u64,
consumer_count: 0,
producer_count: 0,
processed_messages: 0,
failed_messages: 0,
avg_processing_time_ms: 0.0,
total_bytes_sent: 0,
total_bytes_received: 0,
last_message_time: 0,
})
}
async fn purge(&self) -> RiResult<()> {
let mut conn = self.client.get_async_connection().await?;
conn.del::<_, ()>(&self.name).await?;
Ok(())
}
async fn delete(&self) -> RiResult<()> {
self.purge().await
}
}
struct RedisQueueProducer {
connection: Arc<Mutex<redis::aio::Connection>>,
queue_name: String,
}
#[async_trait]
impl RiQueueProducer for RedisQueueProducer {
async fn send(&self, message: RiQueueMessage) -> RiResult<()> {
let mut conn = self.connection.lock().await;
let payload = serde_json::to_vec(&message)?;
conn.rpush::<_, _, ()>(&self.queue_name, payload).await?;
Ok(())
}
async fn send_batch(&self, messages: Vec<RiQueueMessage>) -> RiResult<()> {
let mut conn = self.connection.lock().await;
for message in messages {
let payload = serde_json::to_vec(&message)?;
conn.rpush::<_, _, ()>(&self.queue_name, payload).await?;
}
Ok(())
}
}
struct RedisQueueConsumer {
connection: Arc<Mutex<redis::aio::Connection>>,
queue_name: String,
paused: Arc<Mutex<bool>>,
}
#[async_trait]
impl RiQueueConsumer for RedisQueueConsumer {
async fn receive(&self) -> RiResult<Option<RiQueueMessage>> {
let paused = *self.paused.lock().await;
if paused {
return Ok(None);
}
let mut conn = self.connection.lock().await;
let result: Option<(String, Vec<u8>)> = conn.blpop(&self.queue_name, 5.0).await?;
if let Some((_, payload)) = result {
const MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024; if payload.len() > MAX_MESSAGE_SIZE {
log::warn!(
"[Ri.Redis.Queue] Message too large: {} bytes (max {} bytes), rejecting",
payload.len(), MAX_MESSAGE_SIZE
);
return Err(crate::core::RiError::Other(format!(
"Message too large: {} bytes (max {} bytes)",
payload.len(), MAX_MESSAGE_SIZE
)));
}
let message: RiQueueMessage = serde_json::from_slice(&payload)
.map_err(|e| {
log::warn!("[Ri.Redis.Queue] Failed to deserialize message: {}", e);
crate::core::RiError::Other(format!("Failed to deserialize message: {}", e))
})?;
Ok(Some(message))
} else {
Ok(None)
}
}
async fn ack(&self, _message_id: &str) -> RiResult<()> {
Ok(())
}
async fn nack(&self, message_id: &str) -> RiResult<()> {
log::info!("Message negatively acknowledged: {message_id}");
let mut conn = self.connection.lock().await;
let (original_data, retry_count): (Option<Vec<u8>>, u32) = conn.hgetall::<_, (Option<Vec<u8>>, u32)>(&format!("{}_meta", self.queue_name)).await
.map(|(data, count)| (data, count))
.unwrap_or((None, 0));
let max_retries = 3u32;
let base_delay_ms = 1000u64;
if retry_count >= max_retries {
log::warn!("Message {message_id} exceeded max retries ({max_retries}), moving to dead letter queue");
conn.rpush::<_, _, ()>(&format!("{}_dlq", self.queue_name), message_id.as_bytes()).await?;
conn.hdel::<_, &str, ()>(&format!("{}_meta", self.queue_name), "retry_count").await?;
return Ok(());
}
let new_retry_count = retry_count + 1;
let retry_delay_ms = base_delay_ms * (2u64.pow(new_retry_count - 1));
log::info!("Message {message_id} scheduled for retry {new_retry_count}/{max_retries} after {retry_delay_ms}ms delay");
conn.hset::<_, &str, u32, ()>(&format!("{}_meta", self.queue_name), "retry_count", new_retry_count).await?;
tokio::time::sleep(Duration::from_millis(retry_delay_ms)).await;
if let Some(data) = original_data {
conn.rpush::<_, _, ()>(&self.queue_name, &data).await?;
}
Ok(())
}
async fn pause(&self) -> RiResult<()> {
let mut paused = self.paused.lock().await;
*paused = true;
Ok(())
}
async fn resume(&self) -> RiResult<()> {
let mut paused = self.paused.lock().await;
*paused = false;
Ok(())
}
}