blob: 031462f9748a9c7d7ab259f751778aa6e3e5f68c [file]
use crate::streaming::common::test_setup::TestSetup;
use bytes::Bytes;
use iggy::models::messages::{Message, MessageState};
use iggy::utils::{checksum, timestamp::IggyTimestamp};
use server::streaming::segments::segment;
use server::streaming::segments::segment::{INDEX_EXTENSION, LOG_EXTENSION, TIME_INDEX_EXTENSION};
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
use tokio::fs;
#[tokio::test]
async fn should_persist_segment() {
let setup = TestSetup::init().await;
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offsets = get_start_offsets();
for start_offset in start_offsets {
let segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
None,
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
setup
.create_partition_directory(stream_id, topic_id, partition_id)
.await;
segment.persist().await.unwrap();
assert_persisted_segment(
&setup
.config
.get_partition_path(stream_id, topic_id, partition_id),
start_offset,
)
.await;
}
}
#[tokio::test]
async fn should_load_existing_segment_from_disk() {
let setup = TestSetup::init().await;
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offsets = get_start_offsets();
for start_offset in start_offsets {
let segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
None,
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
setup
.create_partition_directory(stream_id, topic_id, partition_id)
.await;
segment.persist().await.unwrap();
assert_persisted_segment(
&setup
.config
.get_partition_path(stream_id, topic_id, partition_id),
start_offset,
)
.await;
let mut loaded_segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
None,
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
loaded_segment.load().await.unwrap();
let loaded_messages = loaded_segment.get_messages(0, 10).await.unwrap();
assert_eq!(loaded_segment.partition_id, segment.partition_id);
assert_eq!(loaded_segment.start_offset, segment.start_offset);
assert_eq!(loaded_segment.current_offset, segment.current_offset);
assert_eq!(loaded_segment.end_offset, segment.end_offset);
assert_eq!(loaded_segment.size_bytes, segment.size_bytes);
assert_eq!(loaded_segment.is_closed, segment.is_closed);
assert_eq!(loaded_segment.log_path, segment.log_path);
assert_eq!(loaded_segment.index_path, segment.index_path);
assert_eq!(loaded_segment.time_index_path, segment.time_index_path);
assert!(loaded_messages.is_empty());
}
}
#[tokio::test]
async fn should_persist_and_load_segment_with_messages() {
let setup = TestSetup::init().await;
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offset = 0;
let mut segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
None,
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
setup
.create_partition_directory(stream_id, topic_id, partition_id)
.await;
segment.persist().await.unwrap();
assert_persisted_segment(
&setup
.config
.get_partition_path(stream_id, topic_id, partition_id),
start_offset,
)
.await;
let messages_count = 10;
for i in 0..messages_count {
let message = create_message(i, "test", IggyTimestamp::now().to_micros());
segment.append_messages(&[Arc::new(message)]).await.unwrap();
}
segment.persist_messages().await.unwrap();
let mut loaded_segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
None,
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
loaded_segment.load().await.unwrap();
let messages = loaded_segment
.get_messages(0, messages_count as u32)
.await
.unwrap();
assert_eq!(messages.len(), messages_count as usize);
}
#[tokio::test]
async fn given_all_expired_messages_segment_should_be_expired() {
let setup = TestSetup::init().await;
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offset = 0;
let message_expiry = 10;
let mut segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
Some(message_expiry),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
setup
.create_partition_directory(stream_id, topic_id, partition_id)
.await;
segment.persist().await.unwrap();
assert_persisted_segment(
&setup
.config
.get_partition_path(stream_id, topic_id, partition_id),
start_offset,
)
.await;
let messages_count = 10;
let now = IggyTimestamp::now().to_micros();
let message_expiry = message_expiry as u64;
let mut expired_timestamp = now - (1000 * 2 * message_expiry);
for i in 0..messages_count {
let message = create_message(i, "test", expired_timestamp);
expired_timestamp += 1;
segment.append_messages(&[Arc::new(message)]).await.unwrap();
}
segment.persist_messages().await.unwrap();
let is_expired = segment.is_expired(now).await;
assert!(is_expired);
}
#[tokio::test]
async fn given_at_least_one_not_expired_message_segment_should_not_be_expired() {
let setup = TestSetup::init().await;
let stream_id = 1;
let topic_id = 2;
let partition_id = 3;
let start_offset = 0;
let message_expiry = 10;
let mut segment = segment::Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
setup.config.clone(),
setup.storage.clone(),
Some(message_expiry),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
Arc::new(AtomicU64::new(0)),
);
setup
.create_partition_directory(stream_id, topic_id, partition_id)
.await;
segment.persist().await.unwrap();
assert_persisted_segment(
&setup
.config
.get_partition_path(stream_id, topic_id, partition_id),
start_offset,
)
.await;
let now = IggyTimestamp::now().to_micros();
let message_expiry = message_expiry as u64;
let expired_timestamp = now - (1000 * 2 * message_expiry);
let not_expired_timestamp = now - (1000 * message_expiry) + 1;
let expired_message = create_message(0, "test", expired_timestamp);
let not_expired_message = create_message(1, "test", not_expired_timestamp);
segment
.append_messages(&[Arc::new(expired_message)])
.await
.unwrap();
segment
.append_messages(&[Arc::new(not_expired_message)])
.await
.unwrap();
segment.persist_messages().await.unwrap();
let is_expired = segment.is_expired(now).await;
assert!(!is_expired);
}
async fn assert_persisted_segment(partition_path: &str, start_offset: u64) {
let segment_path = format!("{}/{:0>20}", partition_path, start_offset);
let log_path = format!("{}.{}", segment_path, LOG_EXTENSION);
let index_path = format!("{}.{}", segment_path, INDEX_EXTENSION);
let time_index_path = format!("{}.{}", segment_path, TIME_INDEX_EXTENSION);
assert!(fs::metadata(&log_path).await.is_ok());
assert!(fs::metadata(&index_path).await.is_ok());
assert!(fs::metadata(&time_index_path).await.is_ok());
}
fn create_message(offset: u64, payload: &str, timestamp: u64) -> Message {
let payload = Bytes::from(payload.to_string());
let checksum = checksum::calculate(payload.as_ref());
Message::create(
offset,
MessageState::Available,
timestamp,
0,
payload,
checksum,
None,
)
}
fn get_start_offsets() -> Vec<u64> {
vec![
0, 1, 2, 9, 10, 99, 100, 110, 200, 1000, 1234, 12345, 100000, 9999999,
]
}