use std::collections::HashMap;
use std::collections::HashSet;
#[cfg(test)]
use std::sync::atomic::AtomicUsize;
#[cfg(test)]
use std::sync::atomic::Ordering;
#[cfg(test)]
use std::sync::Arc;
#[cfg(test)]
use std::sync::Mutex;
use std::time::Duration;
use rocketmq_model::common::message::message_queue::MessageQueue;
use crate::consumer::store::local_file_offset_store::LocalFileOffsetStore;
use crate::consumer::store::read_offset_type::ReadOffsetType;
use crate::consumer::store::remote_broker_offset_store::RemoteBrokerOffsetStore;
pub enum OffsetStore {
Remote(RemoteBrokerOffsetStore),
Local(LocalFileOffsetStore),
#[cfg(test)]
Test(TestOffsetStore),
}
#[cfg(test)]
#[derive(Clone, Default)]
pub struct TestOffsetStore {
offsets: Arc<Mutex<HashMap<MessageQueue, i64>>>,
persisted_offsets: Arc<Mutex<HashMap<MessageQueue, i64>>>,
persist_all_count: Arc<AtomicUsize>,
}
impl OffsetStore {
pub fn new_with_remote(remote_broker_offset_store: RemoteBrokerOffsetStore) -> Self {
Self::Remote(remote_broker_offset_store)
}
pub fn new_with_local(local_file_offset_store: LocalFileOffsetStore) -> Self {
Self::Local(local_file_offset_store)
}
#[cfg(test)]
pub(crate) fn new_test() -> Self {
Self::Test(TestOffsetStore::default())
}
#[cfg(test)]
pub(crate) fn test_persist_all_count(&self) -> usize {
match self {
Self::Test(store) => store.persist_all_count.load(Ordering::Acquire),
Self::Remote(_) | Self::Local(_) => 0,
}
}
#[cfg(test)]
pub(crate) fn test_persisted_offset(&self, mq: &MessageQueue) -> Option<i64> {
match self {
Self::Test(store) => store.persisted_offset(mq),
Self::Remote(_) | Self::Local(_) => None,
}
}
pub async fn load(&self) -> crate::ClientResult<()> {
match self {
Self::Remote(store) => store.load().await,
Self::Local(store) => store.load().await,
#[cfg(test)]
Self::Test(_) => Ok(()),
}
}
pub async fn update_offset(&self, mq: &MessageQueue, offset: i64, increase_only: bool) {
match self {
Self::Remote(store) => store.update_offset(mq, offset, increase_only).await,
Self::Local(store) => store.update_offset(mq, offset, increase_only).await,
#[cfg(test)]
Self::Test(store) => store.update_offset(mq, offset, increase_only),
}
}
pub async fn update_and_freeze_offset(&self, mq: &MessageQueue, offset: i64) {
match self {
Self::Remote(store) => store.update_and_freeze_offset(mq, offset).await,
Self::Local(store) => store.update_and_freeze_offset(mq, offset).await,
#[cfg(test)]
Self::Test(store) => store.update_offset(mq, offset, false),
}
}
pub async fn read_offset(&self, mq: &MessageQueue, type_: ReadOffsetType) -> i64 {
match self {
Self::Remote(store) => store.read_offset(mq, type_).await,
Self::Local(store) => store.read_offset(mq, type_).await,
#[cfg(test)]
Self::Test(store) => store.read_offset(mq),
}
}
pub async fn persist_all(&self, mqs: &HashSet<MessageQueue>) {
match self {
Self::Remote(store) => store.persist_all(mqs).await,
Self::Local(store) => store.persist_all(mqs).await,
#[cfg(test)]
Self::Test(store) => store.persist_all(mqs),
}
}
pub async fn persist(&self, mq: &MessageQueue) {
match self {
Self::Remote(store) => store.persist(mq).await,
Self::Local(store) => store.persist(mq).await,
#[cfg(test)]
Self::Test(store) => store.persist(mq),
}
}
pub async fn remove_offset(&self, mq: &MessageQueue) {
match self {
Self::Remote(store) => store.remove_offset(mq).await,
Self::Local(store) => store.remove_offset(mq).await,
#[cfg(test)]
Self::Test(store) => store.remove_offset(mq),
}
}
pub async fn clone_offset_table(&self, topic: &str) -> HashMap<MessageQueue, i64> {
match self {
Self::Remote(store) => store.clone_offset_table(topic).await,
Self::Local(store) => store.clone_offset_table(topic).await,
#[cfg(test)]
Self::Test(store) => store.clone_offset_table(topic),
}
}
pub async fn update_consume_offset_to_broker(
&self,
mq: &MessageQueue,
offset: i64,
is_oneway: bool,
) -> crate::ClientResult<()> {
match self {
Self::Remote(store) => store.update_consume_offset_to_broker(mq, offset, is_oneway).await,
Self::Local(store) => store.update_consume_offset_to_broker(mq, offset, is_oneway).await,
#[cfg(test)]
Self::Test(store) => {
store.update_offset(mq, offset, false);
Ok(())
}
}
}
pub async fn shutdown(&self, timeout: Duration) -> bool {
match self {
Self::Remote(_) => true,
Self::Local(store) => store.shutdown_with_timeout(timeout).await,
#[cfg(test)]
Self::Test(_) => true,
}
}
}
#[cfg(test)]
impl TestOffsetStore {
fn update_offset(&self, mq: &MessageQueue, offset: i64, increase_only: bool) {
let mut offsets = self.offsets.lock().expect("test offset store lock");
if increase_only {
offsets
.entry(mq.clone())
.and_modify(|current| {
if offset > *current {
*current = offset;
}
})
.or_insert(offset);
} else {
offsets.insert(mq.clone(), offset);
}
}
fn read_offset(&self, mq: &MessageQueue) -> i64 {
self.offsets
.lock()
.expect("test offset store lock")
.get(mq)
.copied()
.unwrap_or(-1)
}
fn persist_all(&self, mqs: &HashSet<MessageQueue>) {
let offsets = self.offsets.lock().expect("test offset store lock");
let mut persisted_offsets = self.persisted_offsets.lock().expect("test persisted offset store lock");
for mq in mqs {
if let Some(offset) = offsets.get(mq) {
persisted_offsets.insert(mq.clone(), *offset);
}
}
self.persist_all_count.fetch_add(1, Ordering::AcqRel);
}
fn persist(&self, mq: &MessageQueue) {
self.persist_all(&HashSet::from([mq.clone()]));
}
fn remove_offset(&self, mq: &MessageQueue) {
self.offsets.lock().expect("test offset store lock").remove(mq);
}
fn persisted_offset(&self, mq: &MessageQueue) -> Option<i64> {
self.persisted_offsets
.lock()
.expect("test persisted offset store lock")
.get(mq)
.copied()
}
fn clone_offset_table(&self, topic: &str) -> HashMap<MessageQueue, i64> {
self.offsets
.lock()
.expect("test offset store lock")
.iter()
.filter(|(mq, _)| mq.topic() == topic)
.map(|(mq, offset)| (mq.clone(), *offset))
.collect()
}
}
pub(crate) trait OffsetStoreTrait {
async fn load(&self) -> crate::ClientResult<()>;
async fn update_offset(&self, mq: &MessageQueue, offset: i64, increase_only: bool);
async fn update_and_freeze_offset(&self, mq: &MessageQueue, offset: i64);
async fn read_offset(&self, mq: &MessageQueue, type_: ReadOffsetType) -> i64;
async fn persist_all(&self, mqs: &HashSet<MessageQueue>);
async fn persist(&self, mq: &MessageQueue);
async fn remove_offset(&self, mq: &MessageQueue);
async fn clone_offset_table(&self, topic: &str) -> HashMap<MessageQueue, i64>;
async fn update_consume_offset_to_broker(
&self,
mq: &MessageQueue,
offset: i64,
is_oneway: bool,
) -> crate::ClientResult<()>;
}