use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use cheetah_string::CheetahString;
use rocketmq_model::common::consumer::consume_from_where::ConsumeFromWhere;
use rocketmq_model::common::message::message_ext::MessageExt;
use rocketmq_model::common::message::message_queue::MessageQueue;
use rocketmq_protocol::protocol::body::consume_message_directly_result::ConsumeMessageDirectlyResult;
use rocketmq_protocol::protocol::body::consumer_running_info::ConsumerRunningInfo;
use rocketmq_protocol::protocol::heartbeat::consume_type::ConsumeType;
use rocketmq_protocol::protocol::heartbeat::message_model::MessageModel;
use rocketmq_protocol::protocol::heartbeat::subscription_data::SubscriptionData;
use crate::consumer::consumer_impl::default_lite_pull_consumer_impl::DefaultLitePullConsumerImpl;
use crate::consumer::consumer_impl::default_mq_push_consumer_impl::DefaultMQPushConsumerImpl;
use crate::consumer::consumer_impl::pop_request::PopRequest;
use crate::consumer::consumer_impl::pull_request::PullRequest;
#[trait_variant::make(MQConsumerInner: Send)]
pub trait MQConsumerInnerLocal: MQConsumerInnerAny + Sync + 'static {
fn group_name(&self) -> CheetahString;
fn message_model(&self) -> MessageModel;
fn consume_type(&self) -> ConsumeType;
fn consume_from_where(&self) -> ConsumeFromWhere;
fn subscriptions(&self) -> HashSet<SubscriptionData>;
async fn do_rebalance(&self);
async fn try_rebalance(&self) -> crate::ClientResult<bool>;
async fn persist_consumer_offset(&self);
async fn update_topic_subscribe_info(&self, topic: CheetahString, info: &HashSet<MessageQueue>);
async fn is_subscribe_topic_need_update(&self, topic: &str) -> bool;
fn is_unit_mode(&self) -> bool;
async fn reset_offsets(&self, topic: &CheetahString, offsets: HashMap<MessageQueue, i64>);
async fn consumer_status(&self, topic: &CheetahString) -> HashMap<MessageQueue, i64>;
async fn consumer_running_info(&self) -> ConsumerRunningInfo;
}
pub trait MQConsumerInnerAny: std::any::Any {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
fn as_any(&self) -> &dyn std::any::Any;
}
impl<T: MQConsumerInner> MQConsumerInnerAny for T {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[derive(Clone)]
pub struct MQConsumerInnerImpl {
inner: MQConsumerInnerImplKind,
}
#[derive(Clone)]
enum MQConsumerInnerImplKind {
Push(Arc<DefaultMQPushConsumerImpl>),
LitePull(Arc<DefaultLitePullConsumerImpl>),
}
impl MQConsumerInnerImpl {
pub(crate) fn from_push(default_mqpush_consumer_impl: Arc<DefaultMQPushConsumerImpl>) -> Self {
Self {
inner: MQConsumerInnerImplKind::Push(default_mqpush_consumer_impl),
}
}
pub(crate) fn from_lite_pull(default_lite_pull_consumer_impl: Arc<DefaultLitePullConsumerImpl>) -> Self {
Self {
inner: MQConsumerInnerImplKind::LitePull(default_lite_pull_consumer_impl),
}
}
pub(crate) fn is_push_consumer(&self) -> bool {
matches!(self.inner, MQConsumerInnerImplKind::Push(_))
}
pub(crate) async fn pop_message(&mut self, pop_request: PopRequest) {
match &mut self.inner {
MQConsumerInnerImplKind::Push(consumer) => consumer.pop_message(pop_request).await,
MQConsumerInnerImplKind::LitePull(consumer) => {
tracing::warn!(
"Ignoring broker pop request for lite pull consumer group={}",
MQConsumerInner::group_name(consumer.as_ref())
);
}
}
}
pub(crate) async fn pull_message(&mut self, pull_request: PullRequest) {
match &mut self.inner {
MQConsumerInnerImplKind::Push(consumer) => consumer.pull_message(pull_request).await,
MQConsumerInnerImplKind::LitePull(consumer) => {
tracing::warn!(
"Ignoring broker pull request for lite pull consumer group={}",
MQConsumerInner::group_name(consumer.as_ref())
);
}
}
}
pub(crate) async fn consume_message_directly(
&self,
msg: MessageExt,
broker_name: Option<CheetahString>,
) -> Option<ConsumeMessageDirectlyResult> {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => consumer.consume_message_directly(msg, broker_name).await,
MQConsumerInnerImplKind::LitePull(consumer) => {
tracing::warn!(
"consumeMessageDirectly is not supported for lite pull consumer group={}",
MQConsumerInner::group_name(consumer.as_ref())
);
None
}
}
}
}
impl MQConsumerInner for MQConsumerInnerImpl {
#[inline]
fn group_name(&self) -> CheetahString {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::group_name(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::group_name(consumer.as_ref()),
}
}
#[inline]
fn message_model(&self) -> MessageModel {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::message_model(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::message_model(consumer.as_ref()),
}
}
#[inline]
fn consume_type(&self) -> ConsumeType {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::consume_type(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::consume_type(consumer.as_ref()),
}
}
#[inline]
fn consume_from_where(&self) -> ConsumeFromWhere {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::consume_from_where(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::consume_from_where(consumer.as_ref()),
}
}
#[inline]
fn subscriptions(&self) -> HashSet<SubscriptionData> {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::subscriptions(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::subscriptions(consumer.as_ref()),
}
}
#[inline]
async fn do_rebalance(&self) {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::do_rebalance(consumer.as_ref()).await,
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::do_rebalance(consumer.as_ref()).await,
}
}
#[inline]
async fn try_rebalance(&self) -> crate::ClientResult<bool> {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::try_rebalance(consumer.as_ref()).await,
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::try_rebalance(consumer.as_ref()).await,
}
}
#[inline]
async fn persist_consumer_offset(&self) {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => {
MQConsumerInner::persist_consumer_offset(consumer.as_ref()).await
}
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::persist_consumer_offset(consumer.as_ref()).await
}
}
}
#[inline]
async fn update_topic_subscribe_info(&self, topic: CheetahString, info: &HashSet<MessageQueue>) {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => {
MQConsumerInner::update_topic_subscribe_info(consumer.as_ref(), topic, info).await
}
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::update_topic_subscribe_info(consumer.as_ref(), topic, info).await
}
}
}
#[inline]
async fn is_subscribe_topic_need_update(&self, topic: &str) -> bool {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => {
MQConsumerInner::is_subscribe_topic_need_update(consumer.as_ref(), topic).await
}
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::is_subscribe_topic_need_update(consumer.as_ref(), topic).await
}
}
}
#[inline]
fn is_unit_mode(&self) -> bool {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::is_unit_mode(consumer.as_ref()),
MQConsumerInnerImplKind::LitePull(consumer) => MQConsumerInner::is_unit_mode(consumer.as_ref()),
}
}
#[inline]
async fn reset_offsets(&self, topic: &CheetahString, offsets: HashMap<MessageQueue, i64>) {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => {
MQConsumerInner::reset_offsets(consumer.as_ref(), topic, offsets).await
}
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::reset_offsets(consumer.as_ref(), topic, offsets).await
}
}
}
#[inline]
async fn consumer_status(&self, topic: &CheetahString) -> HashMap<MessageQueue, i64> {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::consumer_status(consumer.as_ref(), topic).await,
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::consumer_status(consumer.as_ref(), topic).await
}
}
}
#[inline]
async fn consumer_running_info(&self) -> ConsumerRunningInfo {
match &self.inner {
MQConsumerInnerImplKind::Push(consumer) => MQConsumerInner::consumer_running_info(consumer.as_ref()).await,
MQConsumerInnerImplKind::LitePull(consumer) => {
MQConsumerInner::consumer_running_info(consumer.as_ref()).await
}
}
}
}
#[cfg(test)]
mod tests {
use rocketmq_protocol::protocol::heartbeat::consume_type::ConsumeType;
use super::*;
use crate::base::client_config::ClientConfig;
use crate::consumer::consumer_impl::default_lite_pull_consumer_impl::LitePullConsumerConfig;
#[test]
fn lite_pull_wrapper_delegates_heartbeat_fields() {
let consumer_group = CheetahString::from_static_str("lite_pull_wrapper_group");
let consumer_config = Arc::new(LitePullConsumerConfig {
consumer_group: consumer_group.clone(),
..Default::default()
});
let impl_ = Arc::new(DefaultLitePullConsumerImpl::new(
crate::runtime::test_client_runtime("mq-consumer-inner-test"),
Arc::new(ClientConfig::default()),
consumer_config,
));
let wrapper = MQConsumerInnerImpl::from_lite_pull(impl_);
assert_eq!(MQConsumerInner::group_name(&wrapper), consumer_group);
assert_eq!(MQConsumerInner::message_model(&wrapper), MessageModel::Clustering);
assert_eq!(MQConsumerInner::consume_type(&wrapper), ConsumeType::ConsumeActively);
assert_eq!(
MQConsumerInner::consume_from_where(&wrapper),
ConsumeFromWhere::ConsumeFromLastOffset
);
assert!(!MQConsumerInner::is_unit_mode(&wrapper));
assert!(MQConsumerInner::subscriptions(&wrapper).is_empty());
}
}