use std::collections::HashSet;
use rocketmq_common::common::message::message_queue::MessageQueue;
use rocketmq_common::utils::util_all;
use rocketmq_remoting::code::response_code::ResponseCode;
use rocketmq_remoting::protocol::heartbeat::consume_type::ConsumeType;
use crate::consumer::consumer_impl::pop_process_queue::PopProcessQueue;
use crate::consumer::consumer_impl::pop_request::PopRequest;
use crate::consumer::consumer_impl::process_queue::ProcessQueue;
use crate::consumer::consumer_impl::pull_request::PullRequest;
pub(crate) mod rebalance_impl;
pub(crate) mod rebalance_lite_pull_impl;
pub(crate) mod rebalance_push_impl;
pub(crate) mod rebalance_service;
pub(crate) fn parse_consume_timestamp_millis(
consume_timestamp: Option<&str>,
mq: &MessageQueue,
) -> rocketmq_error::RocketMQResult<u64> {
let Some(consume_timestamp) = consume_timestamp else {
return Err(crate::mq_client_err!(
ResponseCode::SystemError as i32,
format!("Consume timestamp is not configured for mq: {}", mq)
));
};
let timestamp = util_all::parse_date_to_millis(consume_timestamp, util_all::YYYYMMDDHHMMSS).ok_or_else(|| {
crate::mq_client_err!(
ResponseCode::SystemError as i32,
format!("Failed to parse consume timestamp for mq: {}", mq)
)
})?;
u64::try_from(timestamp).map_err(|_| {
crate::mq_client_err!(
ResponseCode::SystemError as i32,
format!("Consume timestamp is before Unix epoch for mq: {}", mq)
)
})
}
#[trait_variant::make(Rebalance: Send)]
pub trait RebalanceLocal {
async fn message_queue_changed(
&mut self,
topic: &str,
mq_all: &HashSet<MessageQueue>,
mq_divided: &HashSet<MessageQueue>,
);
async fn remove_unnecessary_message_queue(&mut self, mq: &MessageQueue, pq: &ProcessQueue) -> bool;
fn remove_unnecessary_pop_message_queue(&mut self, _mq: &MessageQueue, _pq: &PopProcessQueue) -> bool {
true
}
fn consume_type(&self) -> ConsumeType;
async fn remove_dirty_offset(&mut self, mq: &MessageQueue);
async fn compute_pull_from_where_with_exception(
&mut self,
mq: &MessageQueue,
) -> rocketmq_error::RocketMQResult<i64>;
async fn compute_pull_from_where(&mut self, mq: &MessageQueue) -> i64;
fn get_consume_init_mode(&self) -> i32;
async fn dispatch_pull_request(&self, pull_request_list: Vec<PullRequest>, delay: u64);
async fn dispatch_pop_pull_request(&self, pop_request_list: Vec<PopRequest>, delay: u64);
fn create_process_queue(&self) -> ProcessQueue;
fn create_pop_process_queue(&self) -> PopProcessQueue;
async fn remove_process_queue(&mut self, mq: &MessageQueue);
async fn unlock(&mut self, mq: &MessageQueue, oneway: bool);
async fn lock_all(&mut self);
async fn unlock_all(&mut self, oneway: bool);
async fn do_rebalance(&mut self, is_order: bool) -> bool;
fn client_rebalance(&mut self, topic: &str) -> bool;
fn destroy(&mut self);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_consume_timestamp_uses_java_millis_in_local_timezone() {
let mq = MessageQueue::from_parts("topic-a", "broker-a", 0);
let timestamp =
parse_consume_timestamp_millis(Some("20250102030405"), &mq).expect("Java consume timestamp should parse");
assert_eq!(
timestamp,
util_all::parse_date_to_millis("20250102030405", util_all::YYYYMMDDHHMMSS)
.expect("Java consume timestamp should parse") as u64
);
assert!(timestamp > 1_000_000_000_000);
}
#[test]
fn parse_consume_timestamp_rejects_missing_or_invalid_values() {
let mq = MessageQueue::from_parts("topic-a", "broker-a", 0);
assert!(parse_consume_timestamp_millis(None, &mq).is_err());
assert!(parse_consume_timestamp_millis(Some("2025-01-02 03:04:05"), &mq).is_err());
}
}