use std::sync::Arc;
use dashmap::DashMap;
use rocketmq_model::common::message::message_queue::MessageQueue;
use tokio::sync::Mutex;
type LockObject = Arc<Mutex<()>>;
#[derive(Default)]
pub struct MessageQueueLock {
mq_lock_table: Arc<DashMap<MessageQueue, Arc<DashMap<i32, LockObject>>>>,
}
impl MessageQueueLock {
pub fn new() -> Self {
MessageQueueLock {
mq_lock_table: Arc::new(DashMap::new()),
}
}
pub async fn fetch_lock_object(&self, mq: &MessageQueue) -> Arc<Mutex<()>> {
self.fetch_lock_object_with_sharding_key(mq, -1).await
}
pub async fn fetch_lock_object_with_sharding_key(
&self,
mq: &MessageQueue,
sharding_key_index: i32,
) -> Arc<Mutex<()>> {
let inner_map = self
.mq_lock_table
.entry(mq.clone())
.or_insert_with(|| Arc::new(DashMap::new()))
.value()
.clone();
let lock = inner_map
.entry(sharding_key_index)
.or_insert_with(|| Arc::new(Mutex::new(())))
.value()
.clone();
lock
}
}
#[cfg(test)]
mod tests {
use super::*;
fn message_queue() -> MessageQueue {
MessageQueue::from_parts("TopicA", "BrokerA", 0)
}
#[tokio::test]
async fn fetch_lock_object_reuses_lock_for_same_queue_like_java() {
let lock = MessageQueueLock::new();
let mq = message_queue();
let first = lock.fetch_lock_object(&mq).await;
let second = lock.fetch_lock_object(&mq).await;
assert!(Arc::ptr_eq(&first, &second));
}
#[tokio::test]
async fn fetch_lock_object_uses_independent_locks_per_sharding_key_like_java() {
let lock = MessageQueueLock::new();
let mq = message_queue();
let first = lock.fetch_lock_object_with_sharding_key(&mq, 0).await;
let second = lock.fetch_lock_object_with_sharding_key(&mq, 1).await;
assert!(!Arc::ptr_eq(&first, &second));
}
}