| use super::message_batch::{RetainedMessageBatch, RETAINED_BATCH_HEADER_LEN}; |
| use crate::streaming::models::messages::RetainedMessage; |
| use bytes::BytesMut; |
| use iggy::utils::byte_size::IggyByteSize; |
| use iggy::utils::sizeable::Sizeable; |
| use std::sync::Arc; |
| |
| #[derive(Debug)] |
| pub struct BatchAccumulator { |
| base_offset: u64, |
| current_size: IggyByteSize, |
| current_offset: u64, |
| current_timestamp: u64, |
| messages: Vec<Arc<RetainedMessage>>, |
| } |
| |
| impl BatchAccumulator { |
| pub fn new(base_offset: u64, capacity: usize) -> Self { |
| Self { |
| base_offset, |
| current_size: IggyByteSize::from(0), |
| current_offset: 0, |
| current_timestamp: 0, |
| messages: Vec::with_capacity(capacity), |
| } |
| } |
| |
| pub fn append(&mut self, batch_size: IggyByteSize, items: &[Arc<RetainedMessage>]) { |
| assert!(!items.is_empty()); |
| self.current_size += batch_size; |
| self.current_offset = items.last().unwrap().offset; |
| self.current_timestamp = items.last().unwrap().timestamp; |
| self.messages.extend(items.iter().cloned()); |
| } |
| |
| pub fn get_messages_by_offset( |
| &self, |
| start_offset: u64, |
| end_offset: u64, |
| ) -> Vec<Arc<RetainedMessage>> { |
| let start_idx = self |
| .messages |
| .partition_point(|msg| msg.offset < start_offset); |
| let end_idx = self |
| .messages |
| .partition_point(|msg| msg.offset <= end_offset); |
| self.messages[start_idx..end_idx].to_vec() |
| } |
| |
| pub fn is_empty(&self) -> bool { |
| self.messages.is_empty() |
| } |
| |
| pub fn unsaved_messages_count(&self) -> usize { |
| self.messages.len() |
| } |
| |
| pub fn batch_max_offset(&self) -> u64 { |
| self.current_offset |
| } |
| |
| pub fn batch_max_timestamp(&self) -> u64 { |
| self.current_timestamp |
| } |
| |
| pub fn batch_base_offset(&self) -> u64 { |
| self.base_offset |
| } |
| |
| pub fn materialize_batch_and_update_state(&mut self) -> RetainedMessageBatch { |
| let batch_base_offset = self.base_offset; |
| let batch_last_offset_delta = (self.current_offset - self.base_offset) as u32; |
| |
| let num_messages = self.messages.len(); |
| let last_batch_timestamp = if num_messages > 0 { |
| self.messages[num_messages - 1].timestamp |
| } else { |
| 0 |
| }; |
| |
| let messages = std::mem::take(&mut self.messages); |
| let mut bytes = BytesMut::with_capacity(self.current_size.as_bytes_u64() as usize); |
| for message in messages { |
| message.extend(&mut bytes); |
| } |
| |
| self.base_offset = 0; |
| self.current_size = IggyByteSize::from(0); |
| self.current_offset = 0; |
| self.current_timestamp = 0; |
| |
| let batch_payload = bytes.freeze(); |
| let batch_payload_len = IggyByteSize::from(batch_payload.len() as u64); |
| RetainedMessageBatch::new( |
| batch_base_offset, |
| batch_last_offset_delta, |
| last_batch_timestamp, |
| batch_payload_len, |
| batch_payload, |
| ) |
| } |
| } |
| |
| impl Sizeable for BatchAccumulator { |
| fn get_size_bytes(&self) -> IggyByteSize { |
| self.current_size + RETAINED_BATCH_HEADER_LEN.into() |
| } |
| } |