use std::cell::RefCell;
use std::sync::Arc;
use bytes::BytesMut;
use rocketmq_common::common::message::message_batch::MessageExtBatch;
use rocketmq_common::common::message::message_ext_broker_inner::MessageExtBrokerInner;
use crate::base::message_result::PutMessageResult;
use crate::base::put_message_context::PutMessageContext;
use crate::config::message_store_config::MessageStoreConfig;
use crate::message_encoder::message_ext_encoder::MessageExtEncoder;
pub struct MessageEncoderPool {
encoder: RefCell<Option<MessageExtEncoder>>,
key_buffer: RefCell<String>,
}
impl MessageEncoderPool {
fn new() -> Self {
Self {
encoder: RefCell::new(None),
key_buffer: RefCell::new(String::with_capacity(128)),
}
}
fn get_or_create_encoder(&self, config: &Arc<MessageStoreConfig>) -> std::cell::RefMut<'_, MessageExtEncoder> {
let mut encoder_ref = self.encoder.borrow_mut();
if encoder_ref.is_none() {
*encoder_ref = Some(MessageExtEncoder::new(Arc::clone(config)));
}
std::cell::RefMut::map(encoder_ref, |opt| opt.as_mut().unwrap())
}
pub fn encode_message(
&self,
message: &MessageExtBrokerInner,
config: &Arc<MessageStoreConfig>,
) -> (Option<PutMessageResult>, BytesMut) {
let mut encoder = self.get_or_create_encoder(config);
let result = encoder.encode(message);
let bytes = encoder.byte_buf();
(result, bytes)
}
pub fn encode_message_batch(
&self,
batch: &MessageExtBatch,
context: &mut PutMessageContext,
config: &Arc<MessageStoreConfig>,
) -> Option<BytesMut> {
let mut encoder = self.get_or_create_encoder(config);
encoder.encode_batch(batch, context)
}
pub fn generate_topic_queue_key(&self, message: &MessageExtBrokerInner) -> String {
let mut key = self.key_buffer.borrow_mut();
key.clear();
key.push_str(message.topic());
key.push('-');
key.push_str(&message.queue_id().to_string());
key.clone()
}
}
thread_local! {
static ENCODER_POOL: MessageEncoderPool = MessageEncoderPool::new();
}
#[inline]
pub fn encode_message_with_pool(
message: &MessageExtBrokerInner,
config: &Arc<MessageStoreConfig>,
) -> (Option<PutMessageResult>, BytesMut) {
ENCODER_POOL.with(|pool| pool.encode_message(message, config))
}
#[inline]
pub fn encode_message_batch_with_pool(
batch: &MessageExtBatch,
context: &mut PutMessageContext,
config: &Arc<MessageStoreConfig>,
) -> Option<BytesMut> {
ENCODER_POOL.with(|pool| pool.encode_message_batch(batch, context, config))
}
#[inline]
pub fn generate_key_with_pool(message: &MessageExtBrokerInner) -> String {
ENCODER_POOL.with(|pool| pool.generate_topic_queue_key(message))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoder_pool_reuse() {
let config = Arc::new(MessageStoreConfig::default());
ENCODER_POOL.with(|pool| {
let encoder1_ptr = {
let encoder = pool.get_or_create_encoder(&config);
&*encoder as *const MessageExtEncoder
};
let encoder2_ptr = {
let encoder = pool.get_or_create_encoder(&config);
&*encoder as *const MessageExtEncoder
};
assert_eq!(encoder1_ptr, encoder2_ptr, "Encoder should be reused");
});
}
#[test]
fn test_key_buffer_reuse() {
let msg1 = MessageExtBrokerInner::default();
let msg2 = MessageExtBrokerInner::default();
let key1 = generate_key_with_pool(&msg1);
let key2 = generate_key_with_pool(&msg2);
assert!(!key1.is_empty() || !key2.is_empty());
}
}