blob: c4805f58e72ab7920aac5359afc4b6e8932d82ad [file]
use super::index::Indexes;
use crate::configs::system::SystemConfig;
use crate::streaming::batching::batch_accumulator::BatchAccumulator;
use crate::streaming::storage::SystemStorage;
use iggy::utils::expiry::IggyExpiry;
use iggy::utils::timestamp::IggyTimestamp;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
pub const LOG_EXTENSION: &str = "log";
pub const INDEX_EXTENSION: &str = "index";
pub const MAX_SIZE_BYTES: u32 = 1000 * 1000 * 1000;
#[derive(Debug)]
pub struct Segment {
pub stream_id: u32,
pub topic_id: u32,
pub partition_id: u32,
pub start_offset: u64,
pub end_offset: u64,
pub current_offset: u64,
pub index_path: String,
pub log_path: String,
pub size_bytes: u32,
pub last_index_position: u32,
pub max_size_bytes: u32,
pub size_of_parent_stream: Arc<AtomicU64>,
pub size_of_parent_topic: Arc<AtomicU64>,
pub size_of_parent_partition: Arc<AtomicU64>,
pub messages_count_of_parent_stream: Arc<AtomicU64>,
pub messages_count_of_parent_topic: Arc<AtomicU64>,
pub messages_count_of_parent_partition: Arc<AtomicU64>,
pub is_closed: bool,
pub(crate) message_expiry: IggyExpiry,
pub(crate) unsaved_messages: Option<BatchAccumulator>,
pub(crate) config: Arc<SystemConfig>,
pub(crate) indexes: Option<Indexes>,
pub(crate) storage: Arc<SystemStorage>,
}
impl Segment {
#[allow(clippy::too_many_arguments)]
pub fn create(
stream_id: u32,
topic_id: u32,
partition_id: u32,
start_offset: u64,
config: Arc<SystemConfig>,
storage: Arc<SystemStorage>,
message_expiry: IggyExpiry,
size_of_parent_stream: Arc<AtomicU64>,
size_of_parent_topic: Arc<AtomicU64>,
size_of_parent_partition: Arc<AtomicU64>,
messages_count_of_parent_stream: Arc<AtomicU64>,
messages_count_of_parent_topic: Arc<AtomicU64>,
messages_count_of_parent_partition: Arc<AtomicU64>,
) -> Segment {
let path = config.get_segment_path(stream_id, topic_id, partition_id, start_offset);
Segment {
stream_id,
topic_id,
partition_id,
start_offset,
end_offset: 0,
current_offset: start_offset,
log_path: Self::get_log_path(&path),
index_path: Self::get_index_path(&path),
size_bytes: 0,
last_index_position: 0,
max_size_bytes: config.segment.size.as_bytes_u64() as u32,
message_expiry: match message_expiry {
IggyExpiry::ServerDefault => config.segment.message_expiry,
_ => message_expiry,
},
indexes: match config.segment.cache_indexes {
true => Some(Indexes::default()),
false => None,
},
unsaved_messages: None,
is_closed: false,
size_of_parent_stream,
size_of_parent_partition,
size_of_parent_topic,
messages_count_of_parent_stream,
messages_count_of_parent_topic,
messages_count_of_parent_partition,
config,
storage,
}
}
pub async fn is_full(&self) -> bool {
if self.size_bytes >= self.max_size_bytes {
return true;
}
self.is_expired(IggyTimestamp::now()).await
}
pub async fn is_expired(&self, now: IggyTimestamp) -> bool {
if !self.is_closed {
return false;
}
match self.message_expiry {
IggyExpiry::NeverExpire => false,
IggyExpiry::ServerDefault => false,
IggyExpiry::ExpireDuration(expiry) => {
let last_messages = self.get_messages(self.current_offset, 1).await;
if last_messages.is_err() {
return false;
}
let last_messages = last_messages.unwrap();
if last_messages.is_empty() {
return false;
}
let last_message = &last_messages[0];
let last_message_timestamp = last_message.timestamp;
last_message_timestamp + expiry.as_micros() <= now.as_micros()
}
}
}
fn get_log_path(path: &str) -> String {
format!("{}.{}", path, LOG_EXTENSION)
}
fn get_index_path(path: &str) -> String {
format!("{}.{}", path, INDEX_EXTENSION)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::configs::system::SegmentConfig;
use crate::streaming::storage::tests::get_test_system_storage;
use iggy::utils::duration::IggyDuration;
#[tokio::test]
async fn should_be_created_given_valid_parameters() {
let storage = Arc::new(get_test_system_storage());
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offset = 0;
let config = Arc::new(SystemConfig::default());
let path = config.get_segment_path(stream_id, topic_id, partition_id, start_offset);
let log_path = Segment::get_log_path(&path);
let index_path = Segment::get_index_path(&path);
let message_expiry = IggyExpiry::ExpireDuration(IggyDuration::from(10));
let size_of_parent_stream = Arc::new(AtomicU64::new(0));
let size_of_parent_topic = Arc::new(AtomicU64::new(0));
let size_of_parent_partition = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_stream = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_topic = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_partition = Arc::new(AtomicU64::new(0));
let segment = Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
config,
storage,
message_expiry,
size_of_parent_stream,
size_of_parent_topic,
size_of_parent_partition,
messages_count_of_parent_stream,
messages_count_of_parent_topic,
messages_count_of_parent_partition,
);
assert_eq!(segment.stream_id, stream_id);
assert_eq!(segment.topic_id, topic_id);
assert_eq!(segment.partition_id, partition_id);
assert_eq!(segment.start_offset, start_offset);
assert_eq!(segment.current_offset, 0);
assert_eq!(segment.end_offset, 0);
assert_eq!(segment.size_bytes, 0);
assert_eq!(segment.log_path, log_path);
assert_eq!(segment.index_path, index_path);
assert_eq!(segment.message_expiry, message_expiry);
assert!(segment.unsaved_messages.is_none());
assert!(segment.indexes.is_some());
assert!(!segment.is_closed);
assert!(!segment.is_full().await);
}
#[test]
fn should_not_initialize_indexes_cache_when_disabled() {
let storage = Arc::new(get_test_system_storage());
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offset = 0;
let config = Arc::new(SystemConfig {
segment: SegmentConfig {
cache_indexes: false,
..Default::default()
},
..Default::default()
});
let message_expiry = IggyExpiry::NeverExpire;
let size_of_parent_stream = Arc::new(AtomicU64::new(0));
let size_of_parent_topic = Arc::new(AtomicU64::new(0));
let size_of_parent_partition = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_stream = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_topic = Arc::new(AtomicU64::new(0));
let messages_count_of_parent_partition = Arc::new(AtomicU64::new(0));
let segment = Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
config,
storage,
message_expiry,
size_of_parent_stream,
size_of_parent_topic,
size_of_parent_partition,
messages_count_of_parent_stream,
messages_count_of_parent_topic,
messages_count_of_parent_partition,
);
assert!(segment.indexes.is_none());
}
}