blob: d0a30f5bf84fc581caff6ae69cf9924b55e62f8f [file]
use crate::streaming::segments::index::{Index, IndexRange};
use crate::streaming::segments::segment::Segment;
use crate::streaming::segments::time_index::TimeIndex;
use iggy::error::IggyError;
use iggy::models::messages::Message;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use tracing::trace;
const EMPTY_MESSAGES: Vec<Arc<Message>> = vec![];
impl Segment {
pub fn get_messages_count(&self) -> u64 {
if self.size_bytes == 0 {
return 0;
}
self.current_offset - self.start_offset + 1
}
pub async fn get_messages(
&self,
mut offset: u64,
count: u32,
) -> Result<Vec<Arc<Message>>, IggyError> {
if count == 0 {
return Ok(EMPTY_MESSAGES);
}
if offset < self.start_offset {
offset = self.start_offset;
}
let mut end_offset = offset + (count - 1) as u64;
if end_offset > self.current_offset {
end_offset = self.current_offset;
}
// In case that the partition messages buffer is disabled, we need to check the unsaved messages buffer
if self.unsaved_messages.is_none() {
return self.load_messages_from_disk(offset, end_offset).await;
}
let unsaved_messages = self.unsaved_messages.as_ref().unwrap();
if unsaved_messages.is_empty() {
return self.load_messages_from_disk(offset, end_offset).await;
}
let first_offset = unsaved_messages[0].offset;
if end_offset < first_offset {
return self.load_messages_from_disk(offset, end_offset).await;
}
let last_offset = unsaved_messages[unsaved_messages.len() - 1].offset;
if end_offset <= last_offset {
return Ok(self.load_messages_from_unsaved_buffer(offset, end_offset));
}
let mut messages = self.load_messages_from_disk(offset, end_offset).await?;
let mut buffered_messages = self.load_messages_from_unsaved_buffer(offset, end_offset);
messages.append(&mut buffered_messages);
Ok(messages)
}
pub async fn get_all_messages(&self) -> Result<Vec<Arc<Message>>, IggyError> {
self.get_messages(self.start_offset, self.get_messages_count() as u32)
.await
}
pub async fn get_newest_messages_by_size(
&self,
size_bytes: u64,
) -> Result<Vec<Arc<Message>>, IggyError> {
let messages = self
.storage
.segment
.load_newest_messages_by_size(self, size_bytes)
.await?;
Ok(messages)
}
fn load_messages_from_unsaved_buffer(&self, offset: u64, end_offset: u64) -> Vec<Arc<Message>> {
self.unsaved_messages
.as_ref()
.unwrap()
.iter()
.filter(|message| message.offset >= offset && message.offset <= end_offset)
.cloned()
.collect::<Vec<Arc<Message>>>()
}
async fn load_messages_from_disk(
&self,
start_offset: u64,
end_offset: u64,
) -> Result<Vec<Arc<Message>>, IggyError> {
trace!(
"Loading messages from disk, segment start offset: {}, end offset: {}, current offset: {}...",
start_offset,
end_offset,
self.current_offset
);
if start_offset > end_offset || end_offset > self.current_offset {
trace!(
"Cannot load messages from disk, invalid offset range: {} - {}.",
start_offset,
end_offset
);
return Ok(EMPTY_MESSAGES);
}
if let Some(indexes) = &self.indexes {
let relative_start_offset = start_offset - self.start_offset;
let relative_end_offset = end_offset - self.start_offset;
let start_index = indexes.get(relative_start_offset as usize);
let end_index = indexes.get(1 + relative_end_offset as usize);
if let Some(start_index) = start_index {
let start_position = start_index.position;
let end_position = match end_index {
Some(index) => index.position,
None => self.size_bytes,
};
let index_range = IndexRange {
start: Index {
relative_offset: relative_start_offset as u32,
position: start_position,
},
end: Index {
relative_offset: relative_end_offset as u32,
position: end_position,
},
};
return self.load_messages_from_segment_file(&index_range).await;
}
}
let index_range = self
.storage
.segment
.load_index_range(self, self.start_offset, start_offset, end_offset)
.await?;
if index_range.is_none() {
trace!(
"Cannot load messages from disk, index range not found: {} - {}.",
start_offset,
end_offset
);
return Ok(EMPTY_MESSAGES);
}
self.load_messages_from_segment_file(&index_range.unwrap())
.await
}
async fn load_messages_from_segment_file(
&self,
index_range: &IndexRange,
) -> Result<Vec<Arc<Message>>, IggyError> {
let messages = self
.storage
.segment
.load_messages(self, index_range)
.await?;
trace!(
"Loaded {} messages from disk, segment start offset: {}, end offset: {}.",
messages.len(),
self.start_offset,
self.current_offset
);
Ok(messages)
}
pub async fn append_messages(&mut self, messages: &[Arc<Message>]) -> Result<(), IggyError> {
if self.is_closed {
return Err(IggyError::SegmentClosed(
self.start_offset,
self.partition_id,
));
}
let messages_count = messages.len();
let unsaved_messages = self.unsaved_messages.get_or_insert_with(Vec::new);
unsaved_messages.reserve(messages_count);
if let Some(indexes) = &mut self.indexes {
indexes.reserve(messages_count);
}
if let Some(time_indexes) = &mut self.time_indexes {
time_indexes.reserve(messages_count);
}
// Not the prettiest code. It's done this way to avoid repeatably
// checking if indexes and time_indexes are Some or None.
let mut messages_size = 0;
if self.indexes.is_some() && self.time_indexes.is_some() {
for message in messages {
let relative_offset = (message.offset - self.start_offset) as u32;
self.indexes.as_mut().unwrap().push(Index {
relative_offset,
position: self.size_bytes,
});
self.time_indexes.as_mut().unwrap().push(TimeIndex {
relative_offset,
timestamp: message.timestamp,
});
let message_size = message.get_size_bytes();
self.size_bytes += message_size;
messages_size += message_size;
self.current_offset = message.offset;
unsaved_messages.push(message.clone());
}
} else if self.indexes.is_some() {
for message in messages {
let relative_offset = (message.offset - self.start_offset) as u32;
self.indexes.as_mut().unwrap().push(Index {
relative_offset,
position: self.size_bytes,
});
let message_size = message.get_size_bytes();
self.size_bytes += message_size;
messages_size += message_size;
self.current_offset = message.offset;
unsaved_messages.push(message.clone());
}
} else if self.time_indexes.is_some() {
for message in messages {
let relative_offset = (message.offset - self.start_offset) as u32;
self.time_indexes.as_mut().unwrap().push(TimeIndex {
relative_offset,
timestamp: message.timestamp,
});
let message_size = message.get_size_bytes();
self.size_bytes += message_size;
messages_size += message_size;
self.current_offset = message.offset;
unsaved_messages.push(message.clone());
}
} else {
for message in messages {
let message_size = message.get_size_bytes();
self.size_bytes += message_size;
messages_size += message_size;
self.current_offset = message.offset;
unsaved_messages.push(message.clone());
}
}
self.size_of_parent_stream
.fetch_add(messages_size as u64, Ordering::SeqCst);
self.size_of_parent_topic
.fetch_add(messages_size as u64, Ordering::SeqCst);
self.size_of_parent_partition
.fetch_add(messages_size as u64, Ordering::SeqCst);
self.messages_count_of_parent_stream
.fetch_add(messages_count as u64, Ordering::SeqCst);
self.messages_count_of_parent_topic
.fetch_add(messages_count as u64, Ordering::SeqCst);
self.messages_count_of_parent_partition
.fetch_add(messages_count as u64, Ordering::SeqCst);
Ok(())
}
pub async fn persist_messages(&mut self) -> Result<(), IggyError> {
let storage = self.storage.segment.clone();
if self.unsaved_messages.is_none() {
return Ok(());
}
let unsaved_messages = self.unsaved_messages.as_ref().unwrap();
if unsaved_messages.is_empty() {
return Ok(());
}
trace!(
"Saving {} messages on disk in segment with start offset: {} for partition with ID: {}...",
unsaved_messages.len(),
self.start_offset,
self.partition_id
);
let saved_bytes = storage.save_messages(self, unsaved_messages).await?;
let current_position = self.size_bytes - saved_bytes;
storage
.save_index(self, current_position, unsaved_messages)
.await?;
storage.save_time_index(self, unsaved_messages).await?;
trace!(
"Saved {} messages on disk in segment with start offset: {} for partition with ID: {}, total bytes written: {}.",
unsaved_messages.len(),
self.start_offset,
self.partition_id,
saved_bytes
);
if self.is_full().await {
self.end_offset = self.current_offset;
self.is_closed = true;
self.unsaved_messages = None;
} else {
self.unsaved_messages.as_mut().unwrap().clear();
}
Ok(())
}
}