rocketmq_common/common/message/
message_batch.rs1use std::any::Any;
16use std::collections::HashMap;
17use std::fmt;
18use std::fmt::Debug;
19use std::fmt::Display;
20use std::fmt::Formatter;
21
22use bytes::Bytes;
23use cheetah_string::CheetahString;
24use rocketmq_error::RocketMQError;
26
27use crate::common::message::message_decoder;
28use crate::common::message::message_ext_broker_inner::MessageExtBrokerInner;
29use crate::common::message::message_property::MessageProperties;
30use crate::common::message::message_single::Message;
31use crate::common::message::MessageTrait;
32use crate::common::mix_all;
33
34#[derive(Clone, Default, Debug)]
35pub struct MessageBatch {
36 pub final_message: Message,
38
39 pub messages: Vec<Message>,
41}
42
43impl MessageBatch {
44 #[inline]
45 fn has_delay_property(message: &Message) -> bool {
46 message.delay_time_level() > 0
47 || message.properties().delay_time_ms().unwrap_or(0) > 0
48 || message.properties().delay_time_sec().unwrap_or(0) > 0
49 || message.properties().deliver_time_ms().unwrap_or(0) > 0
50 }
51
52 #[inline]
54 pub fn encode(&self) -> Bytes {
55 message_decoder::encode_messages(&self.messages)
56 }
57
58 #[inline]
60 pub fn iter(&self) -> std::slice::Iter<'_, Message> {
61 self.messages.iter()
62 }
63
64 #[inline]
66 pub fn len(&self) -> usize {
67 self.messages.len()
68 }
69
70 #[inline]
72 pub fn is_empty(&self) -> bool {
73 self.messages.is_empty()
74 }
75
76 pub fn generate_from_vec<M>(messages: Vec<M>) -> rocketmq_error::RocketMQResult<MessageBatch>
77 where
78 M: MessageTrait,
79 {
80 if messages.is_empty() {
81 return Err(RocketMQError::illegal_argument(
82 "MessageBatch::generate_from_vec: messages is empty",
83 ));
84 }
85
86 let mut message_list = Vec::with_capacity(messages.len());
87 for msg in messages {
88 if let Some(m) = msg.as_any().downcast_ref::<Message>() {
89 message_list.push(m.clone());
90 } else {
91 let mut m = Message::default();
92 m.set_topic(msg.topic().clone());
93 if let Some(body) = msg.get_body() {
94 m.set_body(Some(body.clone()));
95 }
96 m.set_flag(msg.get_flag());
97 if let Some(transaction_id) = msg.transaction_id() {
98 m.set_transaction_id(transaction_id.clone());
99 }
100 m.set_properties(msg.get_properties().clone());
101 message_list.push(m);
102 }
103 }
104
105 Self::generate_from_messages(message_list)
106 }
107
108 pub fn generate_from_messages(messages: Vec<Message>) -> rocketmq_error::RocketMQResult<MessageBatch> {
113 if messages.is_empty() {
114 return Err(RocketMQError::illegal_argument(
115 "MessageBatch::generate_from_vec: messages is empty",
116 ));
117 }
118
119 let mut first: Option<&Message> = None;
120 for message in &messages {
121 if Self::has_delay_property(message) {
122 return Err(RocketMQError::illegal_argument(
123 "Delayed messages are not supported for batching",
124 ));
125 }
126 if message.topic().starts_with(mix_all::RETRY_GROUP_TOPIC_PREFIX) {
127 return Err(RocketMQError::illegal_argument(
128 "Retry group topic is not supported for batching",
129 ));
130 }
131
132 if let Some(first_message) = first {
133 if first_message.topic() != message.topic() {
134 return Err(RocketMQError::illegal_argument(
135 "The topic of the messages in one batch should be the same",
136 ));
137 }
138 if first_message.is_wait_store_msg_ok() != message.is_wait_store_msg_ok() {
139 return Err(RocketMQError::illegal_argument(
140 "The waitStoreMsgOK of the messages in one batch should the same",
141 ));
142 }
143 } else {
144 first = Some(message);
145 }
146 }
147 let Some(first) = first else {
148 return Err(RocketMQError::illegal_argument(
149 "MessageBatch::generate_from_vec: messages is empty",
150 ));
151 };
152 let mut final_message = Message::default();
153 final_message.set_topic(first.topic().clone());
154 final_message.set_wait_store_msg_ok(first.is_wait_store_msg_ok());
155 Ok(MessageBatch {
156 final_message,
157 messages,
158 })
159 }
160}
161
162impl IntoIterator for MessageBatch {
163 type Item = Message;
164 type IntoIter = std::vec::IntoIter<Message>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 self.messages.into_iter()
168 }
169}
170
171impl<'a> IntoIterator for &'a MessageBatch {
172 type Item = &'a Message;
173 type IntoIter = std::slice::Iter<'a, Message>;
174
175 fn into_iter(self) -> Self::IntoIter {
176 self.messages.iter()
177 }
178}
179
180impl fmt::Display for MessageBatch {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let messages_str = self
183 .messages
184 .iter()
185 .map(|msg| msg.to_string())
186 .collect::<Vec<_>>()
187 .join(", ");
188
189 write!(
190 f,
191 "MessageBatch {{ final_message: {}, messages: {} }}",
192 self.final_message, messages_str
193 )
194 }
195}
196
197#[allow(unused_variables)]
198impl MessageTrait for MessageBatch {
199 #[inline]
200 fn put_property(&mut self, key: CheetahString, value: CheetahString) {
201 self.final_message.properties_mut().as_map_mut().insert(key, value);
202 }
203
204 #[inline]
205 fn clear_property(&mut self, name: &str) {
206 self.final_message.properties_mut().as_map_mut().remove(name);
207 }
208
209 #[inline]
210 fn property(&self, name: &CheetahString) -> Option<CheetahString> {
211 self.final_message.properties().as_map().get(name).cloned()
212 }
213
214 fn property_ref(&self, name: &CheetahString) -> Option<&CheetahString> {
215 self.final_message.properties().as_map().get(name)
216 }
217
218 #[inline]
219 fn topic(&self) -> &CheetahString {
220 self.final_message.topic()
221 }
222
223 #[inline]
224 fn set_topic(&mut self, topic: CheetahString) {
225 self.final_message.set_topic(topic);
226 }
227
228 #[inline]
229 fn get_flag(&self) -> i32 {
230 self.final_message.flag()
231 }
232
233 #[inline]
234 fn set_flag(&mut self, flag: i32) {
235 self.final_message.set_flag(flag);
236 }
237
238 #[inline]
239 fn get_body(&self) -> Option<&Bytes> {
240 self.final_message.get_body()
241 }
242
243 #[inline]
244 fn set_body(&mut self, body: Bytes) {
245 self.final_message.set_body(Some(body));
246 }
247
248 #[inline]
249 fn get_properties(&self) -> &HashMap<CheetahString, CheetahString> {
250 self.final_message.properties().as_map()
251 }
252
253 #[inline]
254 fn set_properties(&mut self, properties: HashMap<CheetahString, CheetahString>) {
255 *self.final_message.properties_mut() = MessageProperties::from_map(properties);
256 }
257
258 #[inline]
259 fn transaction_id(&self) -> Option<&CheetahString> {
260 MessageTrait::transaction_id(&self.final_message)
261 }
262
263 #[inline]
264 fn set_transaction_id(&mut self, transaction_id: CheetahString) {
265 *self.final_message.transaction_id_mut() = Some(transaction_id);
266 }
267
268 #[inline]
269 fn get_compressed_body_mut(&mut self) -> Option<&mut Bytes> {
270 self.final_message.body_mut().compressed_mut().as_mut()
271 }
272
273 #[inline]
274 fn get_compressed_body(&self) -> Option<&Bytes> {
275 self.final_message.compressed_body()
276 }
277
278 #[inline]
279 fn set_compressed_body_mut(&mut self, compressed_body: Bytes) {
280 self.final_message.set_compressed_body_mut(compressed_body);
281 }
282
283 #[inline]
284 fn take_body(&mut self) -> Option<Bytes> {
285 self.final_message.take_body()
286 }
287
288 fn as_any(&self) -> &dyn Any {
289 self
290 }
291
292 fn as_any_mut(&mut self) -> &mut dyn Any {
293 self
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use bytes::Bytes;
300 use cheetah_string::CheetahString;
301
302 use super::*;
303
304 fn create_test_message(topic: &str) -> Message {
305 let mut msg = Message::default();
306 msg.set_topic(CheetahString::from_string(topic.to_string()));
307 msg.set_body(Some(Bytes::from_static(b"test body")));
308 msg
309 }
310
311 #[test]
312 fn test_generate_from_vec_ok() {
313 let messages = vec![create_test_message("topic1"), create_test_message("topic1")];
314 let result = MessageBatch::generate_from_vec(messages);
315 assert!(result.is_ok());
316 let batch = result.unwrap();
317 assert_eq!(batch.messages.len(), 2);
318 assert_eq!(batch.len(), 2);
319 assert!(!batch.is_empty());
320 }
321
322 #[test]
323 fn test_generate_from_messages_ok() {
324 let messages = vec![create_test_message("topic1"), create_test_message("topic1")];
325 let batch = MessageBatch::generate_from_messages(messages).unwrap();
326
327 assert_eq!(batch.messages.len(), 2);
328 assert_eq!(batch.final_message.topic().as_str(), "topic1");
329 }
330
331 #[test]
332 fn test_generate_from_vec_empty() {
333 let messages: Vec<Message> = vec![];
334 let result = MessageBatch::generate_from_vec(messages);
335 assert!(result.is_err());
336 let err = result.unwrap_err();
337 assert!(err.to_string().contains("empty"));
338 }
339
340 #[test]
341 fn test_generate_from_vec_different_topic() {
342 let messages = vec![
343 create_test_message("topic1"),
344 create_test_message("topic2"), ];
346 let result = MessageBatch::generate_from_vec(messages);
347 assert!(result.is_err());
348 let err = result.unwrap_err();
349 assert!(err.to_string().contains("topic"));
350 }
351
352 #[test]
353 fn test_generate_from_vec_delay_message() {
354 let msg1 = create_test_message("topic1");
355 let mut msg2 = create_test_message("topic1");
356 msg2.set_delay_time_level(1); let messages = vec![msg1, msg2];
359 let result = MessageBatch::generate_from_vec(messages);
360 assert!(result.is_err());
361 let err = result.unwrap_err();
362 assert!(err.to_string().contains("Delayed messages"));
363 }
364
365 #[test]
366 fn test_generate_from_vec_delay_millis_message_matches_java_rejection() {
367 let msg1 = create_test_message("topic1");
368 let msg2 = Message::builder()
369 .topic("topic1")
370 .body(Bytes::from_static(b"test body"))
371 .delay_millis(1000)
372 .build_unchecked();
373
374 let result = MessageBatch::generate_from_vec(vec![msg1, msg2]);
375 assert!(result.is_err());
376 let err = result.unwrap_err();
377 assert!(err.to_string().contains("Delayed messages"));
378 }
379
380 #[test]
381 fn test_generate_from_vec_delay_secs_message_matches_java_rejection() {
382 let msg1 = create_test_message("topic1");
383 let msg2 = Message::builder()
384 .topic("topic1")
385 .body(Bytes::from_static(b"test body"))
386 .delay_secs(1)
387 .build_unchecked();
388
389 let result = MessageBatch::generate_from_vec(vec![msg1, msg2]);
390 assert!(result.is_err());
391 let err = result.unwrap_err();
392 assert!(err.to_string().contains("Delayed messages"));
393 }
394
395 #[test]
396 fn test_generate_from_vec_deliver_time_message_matches_java_rejection() {
397 let msg1 = create_test_message("topic1");
398 let msg2 = Message::builder()
399 .topic("topic1")
400 .body(Bytes::from_static(b"test body"))
401 .deliver_time_ms(1000)
402 .build_unchecked();
403
404 let result = MessageBatch::generate_from_vec(vec![msg1, msg2]);
405 assert!(result.is_err());
406 let err = result.unwrap_err();
407 assert!(err.to_string().contains("Delayed messages"));
408 }
409
410 #[test]
411 fn test_generate_from_vec_retry_group() {
412 let messages = vec![
413 create_test_message("topic1"),
414 create_test_message("%RETRY%topic1"), ];
416 let result = MessageBatch::generate_from_vec(messages);
417 assert!(result.is_err());
418 let err = result.unwrap_err();
419 assert!(err.to_string().contains("Retry"));
420 }
421
422 #[test]
423 fn test_generate_from_vec_different_wait_store_msg_ok() {
424 let mut msg1 = create_test_message("topic1");
425 let mut msg2 = create_test_message("topic1");
426 msg1.set_wait_store_msg_ok(true);
427 msg2.set_wait_store_msg_ok(false); let messages = vec![msg1, msg2];
430 let result = MessageBatch::generate_from_vec(messages);
431 assert!(result.is_err());
432 let err = result.unwrap_err();
433 assert!(err.to_string().contains("waitStoreMsgOK"));
434 }
435
436 #[test]
437 fn test_encode() {
438 let messages = vec![create_test_message("topic1"), create_test_message("topic1")];
439 let batch = MessageBatch::generate_from_vec(messages).unwrap();
440 let encoded = batch.encode();
441 assert!(!encoded.is_empty());
442 }
443
444 #[test]
445 fn test_iterator_slice() {
446 let messages = vec![
447 create_test_message("topic1"),
448 create_test_message("topic1"),
449 create_test_message("topic1"),
450 ];
451 let batch = MessageBatch::generate_from_vec(messages).unwrap();
452
453 let count = batch.iter().count();
454 assert_eq!(count, 3);
455 }
456
457 #[test]
458 fn test_into_iterator() {
459 let messages = vec![
460 create_test_message("topic1"),
461 create_test_message("topic1"),
462 create_test_message("topic1"),
463 ];
464 let batch = MessageBatch::generate_from_vec(messages).unwrap();
465
466 let count = batch.into_iter().count();
467 assert_eq!(count, 3);
468 }
469
470 #[test]
471 fn test_into_iterator_ref() {
472 let messages = vec![
473 create_test_message("topic1"),
474 create_test_message("topic1"),
475 create_test_message("topic1"),
476 ];
477 let batch = MessageBatch::generate_from_vec(messages).unwrap();
478
479 let count = (&batch).into_iter().count();
480 assert_eq!(count, 3);
481
482 assert_eq!(batch.len(), 3);
484 }
485
486 #[test]
487 fn test_batch_properties_inherited() {
488 let mut msg1 = create_test_message("topic1");
489 msg1.set_wait_store_msg_ok(true);
490
491 let messages = vec![msg1.clone(), msg1];
492 let batch = MessageBatch::generate_from_vec(messages).unwrap();
493
494 assert_eq!(batch.final_message.topic().as_str(), "topic1");
495 assert!(batch.final_message.is_wait_store_msg_ok());
496 }
497
498 #[test]
499 fn test_display_format() {
500 let messages = vec![create_test_message("topic1"), create_test_message("topic1")];
501 let batch = MessageBatch::generate_from_vec(messages).unwrap();
502
503 let display = format!("{}", batch);
504 assert!(display.contains("MessageBatch"));
505 assert!(display.contains("topic1"));
506 }
507}
508
509#[derive(Debug, Default)]
510pub struct MessageExtBatch {
511 pub message_ext_broker_inner: MessageExtBrokerInner,
512 pub is_inner_batch: bool,
513 pub encoded_buff: Option<bytes::BytesMut>,
514}
515
516impl MessageExtBatch {
517 pub fn wrap(&self) -> Option<Bytes> {
518 self.message_ext_broker_inner.body()
519 }
520
521 pub fn get_tags(&self) -> Option<CheetahString> {
522 self.message_ext_broker_inner.get_tags()
523 }
524}