blob: 2c8cd0e202e11f563032a6aa4d0d758e217b1f52 [file]
use crate::streaming::segments::segment::Segment;
use iggy::error::IggyError;
use iggy::error::IggyError::InvalidOffset;
#[derive(Debug, Eq, Clone, Copy, Default)]
pub struct Index {
pub relative_offset: u32,
pub position: u32,
}
impl PartialEq<Self> for Index {
fn eq(&self, other: &Self) -> bool {
self.relative_offset == other.relative_offset
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct IndexRange {
pub start: Index,
pub end: Index,
}
impl Segment {
pub fn load_highest_lower_bound_index(
&self,
indices: &[Index],
start_offset: u32,
end_offset: u32,
) -> Result<IndexRange, IggyError> {
let starting_offset_idx = binary_search_index(indices, start_offset);
let ending_offset_idx = binary_search_index(indices, end_offset);
match (starting_offset_idx, ending_offset_idx) {
(Some(starting_offset_idx), Some(ending_offset_idx)) => Ok(IndexRange {
start: indices[starting_offset_idx],
end: indices[ending_offset_idx],
}),
(Some(starting_offset_idx), None) => Ok(IndexRange {
start: indices[starting_offset_idx],
end: *indices.last().unwrap(),
}),
(None, _) => Err(InvalidOffset(start_offset as u64 + self.start_offset)),
}
}
}
fn binary_search_index(indices: &[Index], offset: u32) -> Option<usize> {
match indices.binary_search_by(|index| index.relative_offset.cmp(&offset)) {
Ok(index) => Some(index),
Err(index) => {
if index < indices.len() {
Some(index)
} else {
None
}
}
}
}
impl IndexRange {
pub fn max_range() -> Self {
Self {
start: Index {
relative_offset: 0,
position: 0,
},
end: Index {
relative_offset: u32::MAX - 1,
position: u32::MAX,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::configs::system::{SegmentConfig, SystemConfig};
use crate::streaming::storage::tests::get_test_system_storage;
use iggy::utils::expiry::IggyExpiry;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
fn create_segment() -> Segment {
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: true,
..Default::default()
},
..Default::default()
});
Segment::create(
stream_id,
topic_id,
partition_id,
start_offset,
config,
storage,
IggyExpiry::NeverExpire,
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)),
)
}
fn create_test_indices(segment: &mut Segment) {
let indexes = vec![
Index {
relative_offset: 5,
position: 0,
},
Index {
relative_offset: 20,
position: 100,
},
Index {
relative_offset: 35,
position: 200,
},
Index {
relative_offset: 50,
position: 300,
},
Index {
relative_offset: 65,
position: 400,
},
];
segment.indexes.as_mut().unwrap().extend(indexes);
}
#[test]
fn should_find_both_indices() {
let mut segment = create_segment();
create_test_indices(&mut segment);
let result = segment
.load_highest_lower_bound_index(segment.indexes.as_ref().unwrap(), 15, 45)
.unwrap();
assert_eq!(result.start.relative_offset, 20);
assert_eq!(result.end.relative_offset, 50);
}
#[test]
fn start_and_end_index_should_be_equal() {
let mut segment = create_segment();
create_test_indices(&mut segment);
let result_end_range = segment
.load_highest_lower_bound_index(segment.indexes.as_ref().unwrap(), 65, 100)
.unwrap();
assert_eq!(result_end_range.start.relative_offset, 65);
assert_eq!(result_end_range.end.relative_offset, 65);
let result_start_range = segment
.load_highest_lower_bound_index(segment.indexes.as_ref().unwrap(), 0, 5)
.unwrap();
assert_eq!(result_start_range.start.relative_offset, 5);
assert_eq!(result_start_range.end.relative_offset, 5);
}
#[test]
fn should_clamp_last_index_when_out_of_range() {
let mut segment = create_segment();
create_test_indices(&mut segment);
let result = segment
.load_highest_lower_bound_index(segment.indexes.as_ref().unwrap(), 5, 100)
.unwrap();
assert_eq!(result.start.relative_offset, 5);
assert_eq!(result.end.relative_offset, 65);
}
#[test]
fn should_return_err_when_both_indices_out_of_range() {
let mut segment = create_segment();
create_test_indices(&mut segment);
let result =
segment.load_highest_lower_bound_index(segment.indexes.as_ref().unwrap(), 100, 200);
assert!(result.is_err());
}
}