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