Skip to main content

ri/queue/
core.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Queue Core Implementation
21//! 
22//! This file defines the core queueing interfaces and message structures for the Ri queue system.
23//! It provides the fundamental building blocks for implementing various queue backends.
24//! 
25//! ## Key Components
26//! 
27//! - **RiQueueMessage**: Message structure for queue operations
28//! - **QueueStats**: Statistics for queue monitoring
29//! - **RiQueueProducer**: Trait for producing messages to queues
30//! - **RiQueueConsumer**: Trait for consuming messages from queues
31//! - **RiQueue**: Main queue trait defining queue operations
32//! 
33//! ## Design Principles
34//! 
35//! 1. **Async-First**: All queue operations are asynchronous
36//! 2. **Type Safety**: Strongly typed message structures
37//! 3. **Retry Mechanism**: Built-in support for message retry with configurable maximum attempts
38//! 4. **Header Support**: Allows adding custom headers to messages
39//! 5. **Statistics**: Comprehensive queue statistics for monitoring
40//! 6. **Extensible**: Easy to implement new queue backends
41//! 7. **Thread-safe**: All traits are Send + Sync for safe concurrent use
42//! 
43//! ## Usage
44//! 
45//! ```rust
46//! use ri::queue::{RiQueueMessage, RiQueueProducer, RiQueueConsumer, RiQueue};
47//! use ri::core::RiResult;
48//! use serde_json::json;
49//! 
50//! async fn example(queue: &dyn RiQueue) -> RiResult<()> {
51//!     // Create a producer
52//!     let producer = queue.create_producer().await?;
53//!     
54//!     // Create a message
55//!     let payload = json!({ "key": "value" }).to_string().into_bytes();
56//!     let message = RiQueueMessage::new(payload)
57//!         .with_max_retries(5);
58//!     
59//!     // Send the message
60//!     producer.send(message).await?;
61//!     
62//!     // Create a consumer
63//!     let consumer = queue.create_consumer("consumer_group_1").await?;
64//!     
65//!     // Receive a message
66//!     if let Some(message) = consumer.receive().await? {
67//!         // Process the message
68//!         let payload = String::from_utf8_lossy(&message.payload);
69//!         println!("Received message: {}", payload);
70//!         
71//!         // Acknowledge the message
72//!         consumer.ack(&message.id).await?;
73//!     }
74//!     
75//!     Ok(())
76//! }
77//! ```
78
79use async_trait::async_trait;
80use serde::{Serialize, Deserialize};
81use std::collections::HashMap as FxHashMap;
82use std::time::SystemTime;
83use crate::core::RiResult;
84
85/// Error types for queue operations.
86#[derive(Debug, Clone)]
87pub enum RiQueueError {
88    /// Backend-specific error with descriptive message
89    BackendError(String),
90    /// Configuration error
91    ConfigError(String),
92    /// Connection error
93    ConnectionError(String),
94    /// Message not found
95    MessageNotFound(String),
96    /// Consumer group error
97    ConsumerGroupError(String),
98    /// Serialization error
99    SerializationError(String),
100}
101
102impl std::fmt::Display for RiQueueError {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self {
105            RiQueueError::BackendError(msg) => write!(f, "Queue backend error: {}", msg),
106            RiQueueError::ConfigError(msg) => write!(f, "Queue configuration error: {}", msg),
107            RiQueueError::ConnectionError(msg) => write!(f, "Queue connection error: {}", msg),
108            RiQueueError::MessageNotFound(msg) => write!(f, "Message not found: {}", msg),
109            RiQueueError::ConsumerGroupError(msg) => write!(f, "Consumer group error: {}", msg),
110            RiQueueError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
111        }
112    }
113}
114
115impl std::error::Error for RiQueueError {}
116
117impl From<RiQueueError> for crate::core::RiError {
118    fn from(error: RiQueueError) -> Self {
119        crate::core::RiError::Queue(error.to_string())
120    }
121}
122
123/// Message structure for queue operations.
124/// 
125/// This struct represents a message that can be sent to and received from queues. It includes
126/// a unique ID, payload, headers, timestamp, and retry information.
127#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct RiQueueMessage {
130    /// Unique message ID
131    pub id: String,
132    /// Message payload as bytes
133    pub payload: Vec<u8>,
134    /// Custom headers for the message
135    pub headers: FxHashMap<String, String>,
136    /// Timestamp when the message was created
137    pub timestamp: SystemTime,
138    /// Number of times this message has been retried
139    pub retry_count: u32,
140    /// Maximum number of retry attempts allowed
141    pub max_retries: u32,
142}
143
144impl RiQueueMessage {
145    /// Maximum payload size in bytes (10 MB)
146    pub const MAX_PAYLOAD_SIZE: usize = 10 * 1024 * 1024;
147    
148    /// Maximum header key length
149    pub const MAX_HEADER_KEY_LEN: usize = 256;
150    
151    /// Maximum header value length
152    pub const MAX_HEADER_VALUE_LEN: usize = 4096;
153    
154    /// Maximum number of headers
155    pub const MAX_HEADERS: usize = 64;
156
157    /// Creates a new message with the given payload.
158    /// 
159    /// # Parameters
160    /// 
161    /// - `payload`: The message payload as bytes
162    /// 
163    /// # Returns
164    /// 
165    /// A new `RiQueueMessage` instance
166    /// 
167    /// # Errors
168    /// 
169    /// Returns an error if the payload exceeds `MAX_PAYLOAD_SIZE` (10 MB)
170    pub fn new(payload: Vec<u8>) -> Self {
171        // Security: Truncate payload if it exceeds maximum size
172        let payload = if payload.len() > Self::MAX_PAYLOAD_SIZE {
173            log::warn!(
174                "[Ri.Queue] Payload size {} exceeds maximum {}, truncating",
175                payload.len(),
176                Self::MAX_PAYLOAD_SIZE
177            );
178            payload[..Self::MAX_PAYLOAD_SIZE].to_vec()
179        } else {
180            payload
181        };
182        
183        Self {
184            id: uuid::Uuid::new_v4().to_string(),
185            payload,
186            headers: FxHashMap::default(),
187            timestamp: SystemTime::now(),
188            retry_count: 0,
189            max_retries: 3,
190        }
191    }
192
193    /// Creates a new message with validation.
194    /// 
195    /// # Parameters
196    /// 
197    /// - `payload`: The message payload as bytes
198    /// 
199    /// # Returns
200    /// 
201    /// A `RiResult<RiQueueMessage>` containing the new message or an error
202    pub fn new_validated(payload: Vec<u8>) -> RiResult<Self> {
203        if payload.len() > Self::MAX_PAYLOAD_SIZE {
204            return Err(RiQueueError::ConfigError(format!(
205                "Payload size {} exceeds maximum allowed {}",
206                payload.len(),
207                Self::MAX_PAYLOAD_SIZE
208            )).into());
209        }
210        
211        Ok(Self {
212            id: uuid::Uuid::new_v4().to_string(),
213            payload,
214            headers: FxHashMap::default(),
215            timestamp: SystemTime::now(),
216            retry_count: 0,
217            max_retries: 3,
218        })
219    }
220
221    /// Adds custom headers to the message.
222    /// 
223    /// # Parameters
224    /// 
225    /// - `headers`: A HashMap of custom headers
226    /// 
227    /// # Returns
228    /// 
229    /// The updated `RiQueueMessage` instance
230    /// 
231    /// # Security
232    /// 
233    /// Headers are validated for size and count limits to prevent memory exhaustion attacks.
234    pub fn with_headers(mut self, headers: FxHashMap<String, String>) -> Self {
235        // Security: Limit number of headers
236        let mut safe_headers = FxHashMap::with_capacity(
237            headers.len().min(Self::MAX_HEADERS)
238        );
239        
240        for (key, value) in headers {
241            if safe_headers.len() >= Self::MAX_HEADERS {
242                log::warn!("[Ri.Queue] Maximum header count {} reached, ignoring remaining headers", Self::MAX_HEADERS);
243                break;
244            }
245            
246            // Security: Validate header key length
247            let safe_key = if key.len() > Self::MAX_HEADER_KEY_LEN {
248                log::warn!("[Ri.Queue] Header key '{}' truncated to {} characters", key, Self::MAX_HEADER_KEY_LEN);
249                key[..Self::MAX_HEADER_KEY_LEN].to_string()
250            } else {
251                key
252            };
253            
254            // Security: Validate header value length
255            let safe_value = if value.len() > Self::MAX_HEADER_VALUE_LEN {
256                log::warn!("[Ri.Queue] Header value for key '{}' truncated to {} characters", safe_key, Self::MAX_HEADER_VALUE_LEN);
257                value[..Self::MAX_HEADER_VALUE_LEN].to_string()
258            } else {
259                value
260            };
261            
262            // Security: Check for control characters
263            if safe_key.chars().any(|c| c.is_control()) {
264                log::warn!("[Ri.Queue] Header key contains control characters, skipping");
265                continue;
266            }
267            
268            safe_headers.insert(safe_key, safe_value);
269        }
270        
271        self.headers = safe_headers;
272        self
273    }
274
275    /// Sets the maximum number of retry attempts for this message.
276    /// 
277    /// # Parameters
278    /// 
279    /// - `max_retries`: The maximum number of retry attempts
280    /// 
281    /// # Returns
282    /// 
283    /// The updated `RiQueueMessage` instance
284    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
285        self.max_retries = max_retries;
286        self
287    }
288
289    /// Increments the retry count for this message.
290    pub fn increment_retry(&mut self) {
291        self.retry_count += 1;
292    }
293
294    /// Checks if this message can be retried.
295    /// 
296    /// # Returns
297    /// 
298    /// `true` if the message can be retried, `false` otherwise
299    pub fn can_retry(&self) -> bool {
300        self.retry_count < self.max_retries
301    }
302}
303
304#[cfg(feature = "pyo3")]
305/// Python bindings for RiQueueMessage
306#[pyo3::prelude::pymethods]
307impl RiQueueMessage {
308    #[new]
309    fn py_new(payload: Vec<u8>) -> Self {
310        Self::new(payload)
311    }
312    
313    #[staticmethod]
314    fn py_new_with_string(payload: String) -> Self {
315        Self::new(payload.into_bytes())
316    }
317    
318    fn get_payload_string(&self) -> String {
319        String::from_utf8_lossy(&self.payload).to_string()
320    }
321    
322    fn get_id(&self) -> String {
323        self.id.clone()
324    }
325}
326
327/// Statistics for queue monitoring.
328/// 
329/// This struct contains comprehensive statistics about a queue's performance and usage.
330#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
331#[derive(Debug, Clone)]
332pub struct RiQueueStats {
333    /// Name of the queue
334    pub queue_name: String,
335    /// Current number of messages in the queue
336    pub message_count: u64,
337    /// Number of active consumers
338    pub consumer_count: u32,
339    /// Number of active producers
340    pub producer_count: u32,
341    /// Total number of processed messages
342    pub processed_messages: u64,
343    /// Total number of failed messages
344    pub failed_messages: u64,
345    /// Average processing time in milliseconds
346    pub avg_processing_time_ms: f64,
347    /// Total bytes sent
348    pub total_bytes_sent: u64,
349    /// Total bytes received
350    pub total_bytes_received: u64,
351    /// Timestamp of last message (milliseconds since start)
352    pub last_message_time: u64,
353}
354
355#[cfg(feature = "pyo3")]
356/// Python bindings for RiQueueStats
357#[pyo3::prelude::pymethods]
358impl RiQueueStats {
359    #[new]
360    fn py_new(queue_name: String) -> Self {
361        Self {
362            queue_name,
363            message_count: 0,
364            consumer_count: 0,
365            producer_count: 0,
366            processed_messages: 0,
367            failed_messages: 0,
368            avg_processing_time_ms: 0.0,
369            total_bytes_sent: 0,
370            total_bytes_received: 0,
371            last_message_time: 0,
372        }
373    }
374}
375
376/// Trait for producing messages to queues.
377/// 
378/// This trait defines the interface for sending messages to queues, including single message
379/// sends and batch sends.
380#[async_trait]
381pub trait RiQueueProducer: Send + Sync {
382    async fn send(&self, message: RiQueueMessage) -> RiResult<()>;
383    
384    async fn send_batch(&self, messages: Vec<RiQueueMessage>) -> RiResult<()>;
385
386    async fn send_multi(&self, messages: &[RiQueueMessage]) -> RiResult<()> {
387        for message in messages {
388            self.send(message.clone()).await?;
389        }
390        Ok(())
391    }
392}
393
394/// Trait for consuming messages from queues.
395/// 
396/// This trait defines the interface for receiving and acknowledging messages from queues.
397#[async_trait]
398pub trait RiQueueConsumer: Send + Sync {
399    /// Receives a message from the queue.
400    /// 
401    /// # Returns
402    /// 
403    /// A `RiResult<Option<RiQueueMessage>>` containing the message if available, or None if no message is available
404    async fn receive(&self) -> RiResult<Option<RiQueueMessage>>;
405    
406    /// Acknowledges a message, indicating it has been successfully processed.
407    /// 
408    /// # Parameters
409    /// 
410    /// - `message_id`: The ID of the message to acknowledge
411    /// 
412    /// # Returns
413    /// 
414    /// A `RiResult<()>` indicating success or failure
415    async fn ack(&self, message_id: &str) -> RiResult<()>;
416    
417    /// Negatively acknowledges a message, indicating it failed to process and should be retried.
418    /// 
419    /// # Parameters
420    /// 
421    /// - `message_id`: The ID of the message to negatively acknowledge
422    /// 
423    /// # Returns
424    /// 
425    /// A `RiResult<()>` indicating success or failure
426    async fn nack(&self, message_id: &str) -> RiResult<()>;
427    
428    /// Pauses message consumption.
429    /// 
430    /// # Returns
431    /// 
432    /// A `RiResult<()>` indicating success or failure
433    async fn pause(&self) -> RiResult<()>;
434    
435    /// Resumes message consumption after pausing.
436    /// 
437    /// # Returns
438    /// 
439    /// A `RiResult<()>` indicating success or failure
440    async fn resume(&self) -> RiResult<()>;
441
442    async fn receive_multi(&self, count: usize) -> RiResult<Vec<Option<RiQueueMessage>>> {
443        let mut messages = Vec::with_capacity(count);
444        for _ in 0..count {
445            messages.push(self.receive().await?);
446        }
447        Ok(messages)
448    }
449
450    async fn ack_multi(&self, message_ids: &[String]) -> RiResult<()> {
451        for id in message_ids {
452            self.ack(id).await?;
453        }
454        Ok(())
455    }
456}
457
458/// Main queue trait defining queue operations.
459/// 
460/// This trait defines the core operations for queues, including creating producers and consumers,
461/// getting statistics, purging queues, and deleting queues.
462#[async_trait]
463pub trait RiQueue: Send + Sync {
464    /// Creates a new producer for this queue.
465    /// 
466    /// # Returns
467    /// 
468    /// A `RiResult<Box<dyn RiQueueProducer>>` containing the producer
469    async fn create_producer(&self) -> RiResult<Box<dyn RiQueueProducer>>;
470    
471    /// Creates a new consumer for this queue with the given consumer group.
472    /// 
473    /// # Parameters
474    /// 
475    /// - `consumer_group`: The name of the consumer group
476    /// 
477    /// # Returns
478    /// 
479    /// A `RiResult<Box<dyn RiQueueConsumer>>` containing the consumer
480    async fn create_consumer(&self, consumer_group: &str) -> RiResult<Box<dyn RiQueueConsumer>>;
481    
482    /// Gets statistics for this queue.
483    /// 
484    /// # Returns
485    /// 
486    /// A `RiResult<RiQueueStats>` containing the queue statistics
487    async fn get_stats(&self) -> RiResult<RiQueueStats>;
488    
489    /// Purges all messages from this queue.
490    /// 
491    /// # Returns
492    /// 
493    /// A `RiResult<()>` indicating success or failure
494    async fn purge(&self) -> RiResult<()>;
495    
496    /// Deletes this queue.
497    /// 
498    /// # Returns
499    /// 
500    /// A `RiResult<()>` indicating success or failure
501    async fn delete(&self) -> RiResult<()>;
502}