Skip to main content

rocketmq_client_rust/consumer/
pop_callback.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::future::Future;
16use std::pin::Pin;
17use std::sync::Arc;
18
19use crate::ClientError;
20use rocketmq_model::common::message::message_queue::MessageQueue;
21use rocketmq_model::common::mix_all;
22use rocketmq_protocol::code::response_code::ResponseCode;
23use rocketmq_protocol::protocol::heartbeat::subscription_data::SubscriptionData;
24use tracing::warn;
25
26use crate::consumer::consumer_impl::default_mq_push_consumer_impl::DefaultMQPushConsumerImpl;
27use crate::consumer::consumer_impl::default_mq_push_consumer_impl::PULL_TIME_DELAY_MILLS_WHEN_BROKER_FLOW_CONTROL;
28use crate::consumer::consumer_impl::pop_request::PopRequest;
29use crate::consumer::pop_result::PopResult;
30use crate::consumer::pop_status::PopStatus;
31
32/// Trait for handling the results of a pop operation.
33///
34/// This trait defines the methods for handling successful and error results of a pop operation.
35#[trait_variant::make(PopCallback: Send)]
36pub trait PopCallbackInner {
37    /// Called when the pop operation is successful.
38    ///
39    /// # Arguments
40    ///
41    /// * `pop_result` - The result of the pop operation.
42    async fn on_success(&mut self, pop_result: PopResult);
43
44    /// Called when the pop operation encounters an error.
45    ///
46    /// # Arguments
47    ///
48    /// * `e` - The error encountered during the pop operation.
49    fn on_error(&mut self, e: ClientError);
50}
51
52/*impl<F, Fut> PopCallback for F
53where
54    F: Fn(Option<PopResult>, Option<ClientError>) -> Fut + Send + Sync,
55    Fut: Future<Output = ()> + Send,
56{
57    /// Calls the function with the pop result when the pop operation is successful.
58    ///
59    /// # Arguments
60    ///
61    /// * `pop_result` - The result of the pop operation.
62    async fn on_success(&mut self, pop_result: PopResult) {
63        (*self)(Some(pop_result), None).await;
64    }
65
66    /// Does nothing when the pop operation encounters an error.
67    ///
68    /// # Arguments
69    ///
70    /// * `e` - The error encountered during the pop operation.
71    fn on_error(&self, e: ClientError) {
72        (*self)(None, Some(e));
73    }
74}*/
75
76/// Type alias for a callback function that handles the result of a pop operation.
77///
78/// This type alias defines a callback function that takes a `PopResult` and returns a boxed future.
79pub type PopCallbackFn = Arc<
80    dyn Fn(Option<PopResult>, Option<ClientError>) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> + Send + Sync,
81>;
82
83fn broker_response_code(error: &ClientError) -> Option<ResponseCode> {
84    error.broker_response_code().map(ResponseCode::from)
85}
86
87pub struct DefaultPopCallback {
88    pub(crate) push_consumer_impl: Arc<DefaultMQPushConsumerImpl>,
89    pub(crate) message_queue_inner: Option<MessageQueue>,
90    pub(crate) subscription_data: Option<SubscriptionData>,
91    pub(crate) pop_request: Option<PopRequest>,
92}
93
94impl PopCallback for DefaultPopCallback {
95    async fn on_success(&mut self, pop_result: PopResult) {
96        let push_consumer_impl = self.push_consumer_impl.clone();
97
98        let Some(message_queue_inner) = self.message_queue_inner.take() else {
99            warn!("pop callback success ignored: message queue is missing");
100            return;
101        };
102        let Some(subscription_data) = self.subscription_data.take() else {
103            warn!(
104                "pop callback success ignored: subscription data is missing, mq={}",
105                message_queue_inner
106            );
107            if let Some(pop_request) = self.pop_request.take() {
108                let delay = push_consumer_impl.pull_time_delay_mills_when_exception;
109                push_consumer_impl.execute_pop_request_later(pop_request, delay);
110            }
111            return;
112        };
113        let Some(pop_request) = self.pop_request.take() else {
114            warn!(
115                "pop callback success ignored: pop request is missing, mq={}",
116                message_queue_inner
117            );
118            return;
119        };
120
121        let pop_result = push_consumer_impl
122            .process_pop_result(pop_result, &subscription_data)
123            .await;
124        match pop_result.pop_status {
125            PopStatus::Found => {
126                if pop_result.msg_found_list.as_ref().is_none_or(|value| value.is_empty()) {
127                    push_consumer_impl.execute_pop_request_immediately(pop_request).await;
128                } else {
129                    let Some(consume_message_pop_service) = push_consumer_impl.consume_message_pop_service() else {
130                        warn!(
131                            "pop callback found messages but ConsumeMessagePopService is not initialized, mq={}",
132                            message_queue_inner
133                        );
134                        let delay = push_consumer_impl.pull_time_delay_mills_when_exception;
135                        push_consumer_impl.execute_pop_request_later(pop_request, delay);
136                        return;
137                    };
138                    consume_message_pop_service
139                        .submit_pop_consume_request(
140                            pop_result.msg_found_list.unwrap_or_default(),
141                            pop_request.get_pop_process_queue(),
142                            pop_request.get_message_queue(),
143                        )
144                        .await;
145                    let pull_interval = push_consumer_impl.consumer_config_snapshot().pull_interval;
146                    if pull_interval > 0 {
147                        push_consumer_impl.execute_pop_request_later(pop_request, pull_interval);
148                    } else {
149                        push_consumer_impl.execute_pop_request_immediately(pop_request).await;
150                    }
151                }
152            }
153            PopStatus::NoNewMsg | PopStatus::PollingNotFound => {
154                push_consumer_impl.execute_pop_request_immediately(pop_request).await;
155            }
156            PopStatus::PollingFull => {
157                let pull_time_delay_mills_when_exception = push_consumer_impl.pull_time_delay_mills_when_exception;
158                push_consumer_impl.execute_pop_request_later(pop_request, pull_time_delay_mills_when_exception);
159            }
160        }
161    }
162
163    fn on_error(&mut self, err: ClientError) {
164        let push_consumer_impl = self.push_consumer_impl.clone();
165
166        let Some(message_queue_inner) = self.message_queue_inner.take() else {
167            warn!("pop callback exception ignored: message queue is missing, error={err}");
168            return;
169        };
170        let Some(pop_request) = self.pop_request.take() else {
171            warn!(
172                "pop callback exception ignored: pop request is missing, mq={}, error={}",
173                message_queue_inner, err
174            );
175            return;
176        };
177        let topic = message_queue_inner.topic_str();
178        let broker_code = broker_response_code(&err);
179        let consumer_group = push_consumer_impl.consumer_config_snapshot().consumer_group.clone();
180        if !topic.starts_with(mix_all::RETRY_GROUP_TOPIC_PREFIX) {
181            if broker_code == Some(ResponseCode::SubscriptionNotLatest) {
182                warn!("the subscription is not latest, group={}", consumer_group,);
183            } else {
184                warn!("execute the pop request exception, group={}", consumer_group);
185            }
186        }
187        let time_delay = if broker_code == Some(ResponseCode::FlowControl) {
188            PULL_TIME_DELAY_MILLS_WHEN_BROKER_FLOW_CONTROL
189        } else {
190            push_consumer_impl.pull_time_delay_mills_when_exception
191        };
192
193        push_consumer_impl.execute_pop_request_later(pop_request, time_delay);
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use cheetah_string::CheetahString;
200    use rocketmq_model::common::message::message_ext::MessageExt;
201
202    use super::*;
203    use crate::base::client_config::ClientConfig;
204    use crate::consumer::consumer_impl::pop_process_queue::PopProcessQueue;
205    use crate::consumer::default_mq_push_consumer::ConsumerConfig;
206
207    fn message_queue() -> MessageQueue {
208        MessageQueue::from_parts("topic", "broker-a", 0)
209    }
210
211    fn pop_request() -> PopRequest {
212        PopRequest::new(
213            CheetahString::from_static_str("topic"),
214            CheetahString::from_static_str("group"),
215            message_queue(),
216            PopProcessQueue::new(),
217            0,
218        )
219    }
220
221    fn new_callback() -> DefaultPopCallback {
222        let consumer_config = ConsumerConfig::default();
223        let push_consumer_impl = Arc::new(DefaultMQPushConsumerImpl::new(
224            crate::runtime::test_client_runtime("pop-callback-test"),
225            ClientConfig::default(),
226            consumer_config,
227            None,
228        ));
229        push_consumer_impl.initialize_self_reference();
230        DefaultPopCallback {
231            push_consumer_impl,
232            message_queue_inner: None,
233            subscription_data: None,
234            pop_request: None,
235        }
236    }
237
238    #[test]
239    fn broker_response_code_reads_broker_error_without_downcast() {
240        let error = crate::ClientError::broker_operation_failed(
241            "POP_MESSAGE",
242            ResponseCode::SubscriptionNotLatest.to_i32(),
243            "subscription not latest",
244        );
245
246        assert_eq!(broker_response_code(&error), Some(ResponseCode::SubscriptionNotLatest));
247    }
248
249    #[tokio::test]
250    async fn success_without_message_queue_is_ignored_without_panic() {
251        let mut callback = new_callback();
252
253        PopCallback::on_success(&mut callback, PopResult::default()).await;
254    }
255
256    #[tokio::test]
257    async fn success_without_subscription_data_is_delayed_without_panic() {
258        let mut callback = new_callback();
259        callback.message_queue_inner = Some(message_queue());
260        callback.pop_request = Some(pop_request());
261
262        PopCallback::on_success(&mut callback, PopResult::default()).await;
263    }
264
265    #[tokio::test]
266    async fn success_without_pop_request_is_ignored_without_panic() {
267        let mut callback = new_callback();
268        callback.message_queue_inner = Some(message_queue());
269        callback.subscription_data = Some(SubscriptionData::default());
270
271        PopCallback::on_success(&mut callback, PopResult::default()).await;
272    }
273
274    #[tokio::test]
275    async fn found_messages_without_pop_service_are_delayed_without_panic() {
276        let mut callback = new_callback();
277        callback.message_queue_inner = Some(message_queue());
278        callback.subscription_data = Some(SubscriptionData::default());
279        callback.pop_request = Some(pop_request());
280        let pop_result = PopResult {
281            msg_found_list: Some(vec![MessageExt::default()]),
282            pop_status: PopStatus::Found,
283            ..Default::default()
284        };
285
286        PopCallback::on_success(&mut callback, pop_result).await;
287    }
288
289    #[test]
290    fn error_without_message_queue_is_ignored_without_panic() {
291        let mut callback = new_callback();
292
293        PopCallback::on_error(&mut callback, ClientError::illegal_argument("test error"));
294    }
295
296    #[test]
297    fn error_without_pop_request_is_ignored_without_panic() {
298        let mut callback = new_callback();
299        callback.message_queue_inner = Some(message_queue());
300
301        PopCallback::on_error(&mut callback, ClientError::illegal_argument("test error"));
302    }
303}