diff --git a/lib/segment/src/common/flags/in_memory_bitvec_flags.rs b/lib/segment/src/common/flags/in_memory_bitvec_flags.rs index 8bcf4afadc..e32812ccde 100644 --- a/lib/segment/src/common/flags/in_memory_bitvec_flags.rs +++ b/lib/segment/src/common/flags/in_memory_bitvec_flags.rs @@ -66,6 +66,14 @@ impl InMemoryBitvecFlags { Ok(Self { bitvec, count }) } + /// Wrap an already-materialized deletion `bitvec`, computing the set-flag + /// count. For flags coming from an on-disk format other than the dynamic + /// flags read by [`Self::open`] (e.g. the immutable dense `deleted.dat`). + pub fn from_bitvec(bitvec: BitVec) -> Self { + let count = bitvec.count_ones(); + Self { bitvec, count } + } + /// Whether the flag at `key` is set; out-of-range keys read as unset. pub fn get(&self, key: PointOffsetType) -> bool { self.bitvec.get(key as usize).is_some_and(|bit| *bit) diff --git a/lib/segment/src/vector_storage/dense/dense_vector_storage.rs b/lib/segment/src/vector_storage/dense/dense_vector_storage.rs index 147f1eeb02..319ff18774 100644 --- a/lib/segment/src/vector_storage/dense/dense_vector_storage.rs +++ b/lib/segment/src/vector_storage/dense/dense_vector_storage.rs @@ -25,8 +25,8 @@ use crate::vector_storage::{ DenseVectorStorage, DenseVectorStorageRead, VectorStorage, VectorStorageEnum, VectorStorageRead, }; -const VECTORS_PATH: &str = "matrix.dat"; -const DELETED_PATH: &str = "deleted.dat"; +pub(crate) const VECTORS_PATH: &str = "matrix.dat"; +pub(crate) const DELETED_PATH: &str = "deleted.dat"; /// Stores all dense vectors in mem-mapped file /// @@ -216,7 +216,7 @@ where S: UniversalRead, { fn vector_dim(&self) -> usize { - self.vectors.as_ref().unwrap().dim + self.vectors.as_ref().unwrap().dim() } fn get_dense(&self, key: PointOffsetType) -> Cow<'_, [T]> { @@ -244,7 +244,7 @@ where stopped: &AtomicBool, ) -> OperationResult> { let dim = self.vector_dim(); - let start_index = self.vectors.as_ref().unwrap().num_vectors as PointOffsetType; + let start_index = self.vectors.as_ref().unwrap().num_vectors() as PointOffsetType; let mut end_index = start_index; // Extend vectors file, write other vectors into it @@ -318,7 +318,7 @@ where } fn total_vector_count(&self) -> usize { - self.vectors.as_ref().unwrap().num_vectors + self.vectors.as_ref().unwrap().num_vectors() } fn get_vector(&self, key: PointOffsetType) -> CowVector<'_> { diff --git a/lib/segment/src/vector_storage/dense/immutable_dense_vectors.rs b/lib/segment/src/vector_storage/dense/immutable_dense_vectors.rs index 7ccf0b5547..394bd81d92 100644 --- a/lib/segment/src/vector_storage/dense/immutable_dense_vectors.rs +++ b/lib/segment/src/vector_storage/dense/immutable_dense_vectors.rs @@ -26,9 +26,11 @@ const HEADER_SIZE: usize = 4; const VECTORS_HEADER: &[u8; HEADER_SIZE] = b"data"; const DELETED_HEADER: &[u8; HEADER_SIZE] = b"drop"; -/// Immutable storage for dense vectors. +/// Immutable dense vector blob, shared by the writable [`ImmutableDenseVectors`] +/// and the read-only dense storage. Provides typed read access for `T` through +/// the [`UniversalRead`] backend `S`; holds no deletion flags. #[derive(Debug)] -pub struct ImmutableDenseVectors +pub struct ImmutableDenseVectorData where T: PrimitiveVectorElement, S: UniversalRead, @@ -37,24 +39,17 @@ where pub num_vectors: usize, /// Vector data storage, providing typed read access for `T`. storage: TypedStorage, T>, - /// Memory mapped deletion flags - deleted: MmapBitSlice, - /// Current number of deleted vectors. - pub deleted_count: usize, } -impl ImmutableDenseVectors { +impl ImmutableDenseVectorData { + /// Open the immutable vector blob read-only through `fs`. The file must + /// already exist (the writer creates it); nothing is created here. pub fn open( fs: &S::Fs, vectors_path: &Path, - deleted_path: &Path, dim: usize, populate: bool, ) -> OperationResult { - // Allocate/open vectors file - ensure_mmap_file_size(vectors_path, VECTORS_HEADER, None) - .describe("Create mmap data file")?; - let file_len = fs_err::metadata(vectors_path)?.len() as usize; let num_vectors = file_len.saturating_sub(HEADER_SIZE) / dim / size_of::(); @@ -73,36 +68,13 @@ impl ImmutableDenseVectors { })?; let storage = TypedStorage::, T>::wrap(read_only); - // Allocate/open deleted mmap - let deleted_mmap_size = deleted_mmap_size(num_vectors); - ensure_mmap_file_size(deleted_path, DELETED_HEADER, Some(deleted_mmap_size as u64)) - .describe("Create mmap deleted file")?; - let deleted_mmap = mmap::open_write_mmap(deleted_path, AdviceSetting::Global, false) - .describe("Open mmap deleted for writing")?; - - // Advise kernel that we'll need this page soon so the kernel can prepare - #[cfg(unix)] - if let Err(err) = deleted_mmap.advise(memmap2::Advice::WillNeed) { - log::error!("Failed to advise MADV_WILLNEED for deleted flags: {err}"); - } - - // Transform into mmap BitSlice - let deleted = MmapBitSlice::try_from(deleted_mmap, deleted_mmap_data_start())?; - let deleted_count = deleted.count_ones(); - Ok(Self { dim, num_vectors, storage, - deleted, - deleted_count, }) } - pub fn flusher(&self) -> MmapFlusher { - self.deleted.flusher() - } - /// Returns the byte offset within the file at which the vector for `key` begins. /// /// File layout: @@ -201,6 +173,97 @@ impl ImmutableDenseVectors { .expect("vectors read"); } + pub fn populate(&self) { + if let Err(err) = self.storage.populate() { + log::error!("Failed to populate vector storage: {err}"); + } + } + + pub fn clear_cache(&self) -> OperationResult<()> { + self.storage.clear_ram_cache()?; + Ok(()) + } +} + +/// Immutable storage for dense vectors. +/// +/// Wraps the shared [`ImmutableDenseVectorData`] blob with a writable deletion +/// bitmap, so it can mark vectors as removed even though the vector data itself +/// is append-only and can only be constructed from another storage. +#[derive(Debug)] +pub struct ImmutableDenseVectors +where + T: PrimitiveVectorElement, + S: UniversalRead, +{ + /// Vector data blob, read-only through `S`. + data: ImmutableDenseVectorData, + /// Memory mapped deletion flags + deleted: MmapBitSlice, + /// Current number of deleted vectors. + pub deleted_count: usize, +} + +impl ImmutableDenseVectors { + pub fn open( + fs: &S::Fs, + vectors_path: &Path, + deleted_path: &Path, + dim: usize, + populate: bool, + ) -> OperationResult { + // Allocate/open vectors file + ensure_mmap_file_size(vectors_path, VECTORS_HEADER, None) + .describe("Create mmap data file")?; + + let data = ImmutableDenseVectorData::open(fs, vectors_path, dim, populate)?; + let num_vectors = data.num_vectors; + + // Allocate/open deleted mmap + let deleted_mmap_size = deleted_mmap_size(num_vectors); + ensure_mmap_file_size(deleted_path, DELETED_HEADER, Some(deleted_mmap_size as u64)) + .describe("Create mmap deleted file")?; + let deleted_mmap = mmap::open_write_mmap(deleted_path, AdviceSetting::Global, false) + .describe("Open mmap deleted for writing")?; + + // Advise kernel that we'll need this page soon so the kernel can prepare + #[cfg(unix)] + if let Err(err) = deleted_mmap.advise(memmap2::Advice::WillNeed) { + log::error!("Failed to advise MADV_WILLNEED for deleted flags: {err}"); + } + + // Transform into mmap BitSlice + let deleted = MmapBitSlice::try_from(deleted_mmap, deleted_mmap_data_start())?; + let deleted_count = deleted.count_ones(); + + Ok(Self { + data, + deleted, + deleted_count, + }) + } + + pub fn dim(&self) -> usize { + self.data.dim + } + + pub fn num_vectors(&self) -> usize { + self.data.num_vectors + } + + pub fn flusher(&self) -> MmapFlusher { + self.deleted.flusher() + } + + /// Returns an optional vector data by key + pub fn get_vector_opt(&self, key: PointOffsetType) -> Option> { + self.data.get_vector_opt::

(key) + } + + pub fn for_each_in_batch(&self, keys: &[PointOffsetType], f: F) { + self.data.for_each_in_batch(keys, f); + } + /// Marks the key as deleted. /// /// Returns true if the key was not deleted before, and it is now deleted. @@ -225,21 +288,12 @@ impl ImmutableDenseVectors { } pub fn populate(&self) { - if let Err(err) = self.storage.populate() { - log::error!("Failed to populate vector storage: {err}"); - } + self.data.populate(); } pub fn clear_cache(&self) -> OperationResult<()> { - let Self { - dim: _, - num_vectors: _, - storage, - deleted, - deleted_count: _, - } = self; - storage.clear_ram_cache()?; - deleted.clear_cache()?; + self.data.clear_cache()?; + self.deleted.clear_cache()?; Ok(()) } } @@ -273,7 +327,7 @@ fn ensure_mmap_file_size(path: &Path, header: &[u8], size: Option) -> Opera /// Get start position of flags `BitSlice` in deleted mmap. #[inline] -const fn deleted_mmap_data_start() -> usize { +pub(crate) const fn deleted_mmap_data_start() -> usize { let align = mem::align_of::(); HEADER_SIZE.div_ceil(align) * align } diff --git a/lib/segment/src/vector_storage/dense/read_only/immutable/lifecycle.rs b/lib/segment/src/vector_storage/dense/read_only/immutable/lifecycle.rs new file mode 100644 index 0000000000..406332bc1e --- /dev/null +++ b/lib/segment/src/vector_storage/dense/read_only/immutable/lifecycle.rs @@ -0,0 +1,73 @@ +use std::path::Path; + +use common::bitvec::BitVec; +use common::mmap::AdviceSetting; +use common::stored_bitslice::StoredBitSlice; +use common::universal_io::{OpenOptions, Populate, UniversalRead}; + +use super::ReadOnlyImmutableDenseVectorStorage; +use crate::common::flags::in_memory_bitvec_flags::InMemoryBitvecFlags; +use crate::common::operation_error::{OperationError, OperationResult}; +use crate::data_types::primitive::PrimitiveVectorElement; +use crate::types::Distance; +use crate::vector_storage::dense::dense_vector_storage::{DELETED_PATH, VECTORS_PATH}; +use crate::vector_storage::dense::immutable_dense_vectors::{ + ImmutableDenseVectorData, deleted_mmap_data_start, +}; + +/// Read-only mmap options: never writable, lazily paged, nothing populated. +const READ_ONLY_OPTIONS: OpenOptions = OpenOptions { + writeable: false, + need_sequential: false, + populate: Populate::No, + advice: AdviceSetting::Global, +}; + +impl ReadOnlyImmutableDenseVectorStorage { + /// Open the read-only counterpart of the immutable dense storage at `path`, + /// threading every file open through `fs`; reads the existing layout but + /// creates and writes nothing. `populate` warms the vector data. + pub fn open( + fs: &S::Fs, + path: &Path, + dim: usize, + distance: Distance, + populate: bool, + ) -> OperationResult { + let vectors = ImmutableDenseVectorData::open(fs, &path.join(VECTORS_PATH), dim, populate)?; + let deleted = open_deleted_flags::(fs, &path.join(DELETED_PATH), vectors.num_vectors)?; + + Ok(Self { + vectors, + deleted, + distance, + populate, + }) + } +} + +/// Read the immutable storage's `deleted.dat` through `fs` into an in-memory flag +/// set. The file holds a header padded to `deleted_mmap_data_start()` bytes +/// followed by the deletion `BitSlice`; the leading header bits are dropped and +/// `num_vectors` deletion flags are kept. +fn open_deleted_flags( + fs: &S::Fs, + deleted_path: &Path, + num_vectors: usize, +) -> OperationResult { + let stored = + StoredBitSlice::::open(fs, deleted_path, READ_ONLY_OPTIONS, Default::default())?; + let all = stored.read_all()?; + + let start = deleted_mmap_data_start() * 8; + let bits = all.get(start..start + num_vectors).ok_or_else(|| { + OperationError::service_error(format!( + "Deleted flags file {} holds fewer than {num_vectors} bits", + deleted_path.display(), + )) + })?; + + Ok(InMemoryBitvecFlags::from_bitvec(BitVec::from_bitslice( + bits, + ))) +} diff --git a/lib/segment/src/vector_storage/dense/read_only/immutable/live_reload.rs b/lib/segment/src/vector_storage/dense/read_only/immutable/live_reload.rs new file mode 100644 index 0000000000..082b9cfe3f --- /dev/null +++ b/lib/segment/src/vector_storage/dense/read_only/immutable/live_reload.rs @@ -0,0 +1,29 @@ +use common::counter::hardware_counter::HardwareCounterCell; +use common::sorted_slice::SortedSlice; +use common::types::PointOffsetType; +use common::universal_io::UniversalRead; + +use super::ReadOnlyImmutableDenseVectorStorage; +use crate::common::live_reload::LiveReload; +use crate::common::operation_error::OperationResult; +use crate::data_types::primitive::PrimitiveVectorElement; + +impl LiveReload + for ReadOnlyImmutableDenseVectorStorage +{ + type Fs = S::Fs; + + /// Vector data is immutable, so only the in-memory deletion flags are patched + /// from the authoritative `deleted_points`; `fs` and `new_points` are unused. + fn live_reload( + &mut self, + _fs: &S::Fs, + deleted_points: &SortedSlice<'_, PointOffsetType>, + _new_points: &SortedSlice<'_, PointOffsetType>, + _hw_counter: &HardwareCounterCell, + ) -> OperationResult<()> { + self.deleted.insert_all(deleted_points); + + Ok(()) + } +} diff --git a/lib/segment/src/vector_storage/dense/read_only/immutable/mod.rs b/lib/segment/src/vector_storage/dense/read_only/immutable/mod.rs new file mode 100644 index 0000000000..539315f0c3 --- /dev/null +++ b/lib/segment/src/vector_storage/dense/read_only/immutable/mod.rs @@ -0,0 +1,181 @@ +use common::universal_io::UniversalRead; + +use crate::common::flags::in_memory_bitvec_flags::InMemoryBitvecFlags; +use crate::data_types::primitive::PrimitiveVectorElement; +use crate::types::Distance; +use crate::vector_storage::dense::immutable_dense_vectors::ImmutableDenseVectorData; + +mod lifecycle; +mod live_reload; +mod read_ops; + +/// Read-only counterpart of the immutable (mmap) dense vector storage. +/// +/// Vector data is immutable, so it is read straight from the shared +/// [`ImmutableDenseVectorData`] blob; deletions are tracked in memory and folded +/// from the live-reload delta. +#[derive(Debug)] +pub struct ReadOnlyImmutableDenseVectorStorage { + vectors: ImmutableDenseVectorData, + /// Flags marking deleted vectors. + deleted: InMemoryBitvecFlags, + distance: Distance, + /// Whether vector data is populated into RAM (drives `is_on_disk`). + populate: bool, +} + +#[cfg(test)] +mod tests { + use common::counter::hardware_counter::HardwareCounterCell; + use common::generic_consts::Random; + use common::sorted_slice::SortedSlice; + use common::types::PointOffsetType; + use common::universal_io::{MmapFile, MmapFs}; + use rand::rngs::StdRng; + use rand::{RngExt, SeedableRng}; + use tempfile::Builder; + + use super::*; + use crate::common::live_reload::LiveReload; + use crate::data_types::vectors::{DenseVector, VectorElementType, VectorRef}; + use crate::segment_constructor::batched_reader::merge_from_single_source; + use crate::vector_storage::dense::dense_vector_storage::open_dense_vector_storage; + use crate::vector_storage::dense::volatile_dense_vector_storage::new_volatile_dense_vector_storage; + use crate::vector_storage::{VectorStorage, VectorStorageRead}; + + fn rand_vec(rng: &mut StdRng, dim: usize) -> DenseVector { + std::iter::repeat_with(|| rng.random_range(-1.0..1.0)) + .take(dim) + .collect() + } + + /// Build the immutable storage (deleting some) from a staging storage, then + /// reopen the same directory read-only and assert it mirrors the state. + #[test] + fn read_only_immutable_dense_round_trip() { + const DIM: usize = 32; + const POINT_COUNT: PointOffsetType = 300; + + let dir = Builder::new() + .prefix("ro_immutable_dense") + .tempdir() + .unwrap(); + let mut rng = StdRng::seed_from_u64(42); + let hw = HardwareCounterCell::disposable(); + + let vectors: Vec = (0..POINT_COUNT).map(|_| rand_vec(&mut rng, DIM)).collect(); + + let mut deleted_ids = Vec::new(); + { + let mut storage = + open_dense_vector_storage(dir.path(), DIM, Distance::Dot, false).unwrap(); + let mut staging = new_volatile_dense_vector_storage(DIM, Distance::Dot); + for (id, vector) in vectors.iter().enumerate() { + staging + .insert_vector(id as PointOffsetType, VectorRef::from(vector), &hw) + .unwrap(); + } + for id in (0..POINT_COUNT).step_by(11) { + staging.delete_vector(id).unwrap(); + deleted_ids.push(id); + } + merge_from_single_source(&mut storage, &staging, POINT_COUNT).unwrap(); + storage.flusher()().unwrap(); + } + + let storage = ReadOnlyImmutableDenseVectorStorage::::open( + &MmapFs, + dir.path(), + DIM, + Distance::Dot, + false, + ) + .unwrap(); + + assert_eq!(storage.total_vector_count(), POINT_COUNT as usize); + assert_eq!(storage.distance(), Distance::Dot); + assert_eq!(storage.deleted_vector_count(), deleted_ids.len()); + + // The immutable storage keeps deleted vectors' data, so every vector reads back. + for id in 0..POINT_COUNT { + assert_eq!(storage.is_deleted_vector(id), deleted_ids.contains(&id)); + let got: DenseVector = storage + .get_vector::(id) + .to_owned() + .try_into() + .unwrap(); + assert_eq!(got, vectors[id as usize], "vector {id} mismatch"); + } + } + + /// After `live_reload`, deletions made through the writer are reflected; + /// vector data is immutable so nothing else changes. + #[test] + fn live_reload_picks_up_deletions() { + const DIM: usize = 24; + const POINT_COUNT: PointOffsetType = 200; + + let dir = Builder::new() + .prefix("ro_immutable_dense_reload") + .tempdir() + .unwrap(); + let mut rng = StdRng::seed_from_u64(7); + let hw = HardwareCounterCell::disposable(); + + let vectors: Vec = (0..POINT_COUNT).map(|_| rand_vec(&mut rng, DIM)).collect(); + + let mut writer = open_dense_vector_storage(dir.path(), DIM, Distance::Dot, false).unwrap(); + { + let mut staging = new_volatile_dense_vector_storage(DIM, Distance::Dot); + for (id, vector) in vectors.iter().enumerate() { + staging + .insert_vector(id as PointOffsetType, VectorRef::from(vector), &hw) + .unwrap(); + } + merge_from_single_source(&mut writer, &staging, POINT_COUNT).unwrap(); + writer.flusher()().unwrap(); + } + + let mut reader = ReadOnlyImmutableDenseVectorStorage::::open( + &MmapFs, + dir.path(), + DIM, + Distance::Dot, + false, + ) + .unwrap(); + assert_eq!(reader.deleted_vector_count(), 0); + + // Delete points through the writer, then feed the delta to the reader. + let deleted_ids: Vec = vec![1, 7, 42, 199]; + for &id in &deleted_ids { + writer.delete_vector(id).unwrap(); + } + writer.flusher()().unwrap(); + + reader + .live_reload( + &MmapFs, + &SortedSlice::new(&deleted_ids).unwrap(), + &SortedSlice::new(&[]).unwrap(), + &hw, + ) + .unwrap(); + + assert_eq!(reader.total_vector_count(), POINT_COUNT as usize); + assert_eq!(reader.deleted_vector_count(), deleted_ids.len()); + for &id in &deleted_ids { + assert!(reader.is_deleted_vector(id)); + } + assert!(!reader.is_deleted_vector(0)); + assert!(!reader.is_deleted_vector(2)); + + // Vector data is immutable: an untouched point still reads back correctly. + let got: DenseVector = reader + .get_vector::(2) + .to_owned() + .try_into() + .unwrap(); + assert_eq!(got, vectors[2]); + } +} diff --git a/lib/segment/src/vector_storage/dense/read_only/immutable/read_ops.rs b/lib/segment/src/vector_storage/dense/read_only/immutable/read_ops.rs new file mode 100644 index 0000000000..2147c4981b --- /dev/null +++ b/lib/segment/src/vector_storage/dense/read_only/immutable/read_ops.rs @@ -0,0 +1,91 @@ +use std::borrow::Cow; + +use common::bitvec::BitSlice; +use common::generic_consts::AccessPattern; +use common::types::PointOffsetType; +use common::universal_io::UniversalRead; + +use super::ReadOnlyImmutableDenseVectorStorage; +use crate::data_types::named_vectors::CowVector; +use crate::data_types::primitive::PrimitiveVectorElement; +use crate::types::{Distance, VectorStorageDatatype}; +use crate::vector_storage::{DenseVectorStorageRead, VectorStorageRead}; + +impl DenseVectorStorageRead + for ReadOnlyImmutableDenseVectorStorage +{ + fn vector_dim(&self) -> usize { + self.vectors.dim + } + + fn get_dense(&self, key: PointOffsetType) -> Cow<'_, [T]> { + self.vectors + .get_vector_opt::

(key) + .expect("vector not found") + } +} + +impl VectorStorageRead + for ReadOnlyImmutableDenseVectorStorage +{ + fn size_of_available_vectors_in_bytes(&self) -> usize { + self.available_vector_count() * self.vector_dim() * std::mem::size_of::() + } + + fn distance(&self) -> Distance { + self.distance + } + + fn datatype(&self) -> VectorStorageDatatype { + T::datatype() + } + + fn is_on_disk(&self) -> bool { + !self.populate + } + + fn total_vector_count(&self) -> usize { + self.vectors.num_vectors + } + + fn get_vector(&self, key: PointOffsetType) -> CowVector<'_> { + self.vectors + .get_vector_opt::

(key) + .map(|vector| T::slice_to_float_cow(vector).into()) + .expect("Vector not found") + } + + fn read_vectors( + &self, + keys: impl IntoIterator, + mut callback: impl FnMut(U, PointOffsetType, CowVector<'_>), + ) { + // Split into parallel arrays in one pass: `for_each_in_batch` needs an + // offsets slice, but we still want `user_data[idx]` in the callback. + let (user_data, point_offsets): (Vec, Vec) = keys.into_iter().unzip(); + + self.vectors + .for_each_in_batch(&point_offsets, |idx, vector| { + let vector = CowVector::from(T::slice_to_float_cow(Cow::Borrowed(vector))); + callback(user_data[idx], point_offsets[idx], vector); + }); + } + + fn get_vector_opt(&self, key: PointOffsetType) -> Option> { + self.vectors + .get_vector_opt::

(key) + .map(|vector| T::slice_to_float_cow(vector).into()) + } + + fn is_deleted_vector(&self, key: PointOffsetType) -> bool { + self.deleted.get(key) + } + + fn deleted_vector_count(&self) -> usize { + self.deleted.count() + } + + fn deleted_vector_bitslice(&self) -> &BitSlice { + self.deleted.as_bitslice() + } +} diff --git a/lib/segment/src/vector_storage/dense/read_only/mod.rs b/lib/segment/src/vector_storage/dense/read_only/mod.rs index aeaf8ac34f..06e119473a 100644 --- a/lib/segment/src/vector_storage/dense/read_only/mod.rs +++ b/lib/segment/src/vector_storage/dense/read_only/mod.rs @@ -5,10 +5,13 @@ use crate::data_types::primitive::PrimitiveVectorElement; use crate::types::Distance; use crate::vector_storage::chunked_vectors::ChunkedVectorsRead; +mod immutable; mod lifecycle; mod live_reload; mod read_ops; +pub use immutable::ReadOnlyImmutableDenseVectorStorage; + #[derive(Debug)] pub struct ReadOnlyChunkedDenseVectorStorage { vectors: ChunkedVectorsRead, diff --git a/lib/segment/src/vector_storage/read_only/lifecycle.rs b/lib/segment/src/vector_storage/read_only/lifecycle.rs index 7a013b8a76..81606d2ec5 100644 --- a/lib/segment/src/vector_storage/read_only/lifecycle.rs +++ b/lib/segment/src/vector_storage/read_only/lifecycle.rs @@ -5,10 +5,10 @@ use common::universal_io::UniversalRead; use super::VectorStorageReadEnum; use crate::common::operation_error::{OperationError, OperationResult}; -use crate::data_types::vectors::{VectorElementType, VectorElementTypeByte, VectorElementTypeHalf}; use crate::types::{VectorDataConfig, VectorStorageDatatype, VectorStorageType}; -use crate::vector_storage::dense::dense_vector_storage::open_dense_vector_storage_impl; -use crate::vector_storage::dense::read_only::ReadOnlyChunkedDenseVectorStorage; +use crate::vector_storage::dense::read_only::{ + ReadOnlyChunkedDenseVectorStorage, ReadOnlyImmutableDenseVectorStorage, +}; use crate::vector_storage::multi_dense::read_only::ReadOnlyChunkedMultiDenseVectorStorage; impl VectorStorageReadEnum { @@ -20,10 +20,7 @@ impl VectorStorageReadEnum { fs: &S::Fs, vector_config: &VectorDataConfig, path: &Path, - ) -> OperationResult> - where - S::Fs: Clone, - { + ) -> OperationResult> { let dim = vector_config.size; let distance = vector_config.distance; let datatype = vector_config.datatype.unwrap_or_default(); @@ -109,30 +106,15 @@ impl VectorStorageReadEnum { } } else { match datatype { - VectorStorageDatatype::Float32 => { - Self::Dense(Box::new(open_dense_vector_storage_impl::< - VectorElementType, - S, - >( - fs.clone(), path, dim, distance, populate - )?)) - } - VectorStorageDatatype::Uint8 => { - Self::DenseByte(Box::new(open_dense_vector_storage_impl::< - VectorElementTypeByte, - S, - >( - fs.clone(), path, dim, distance, populate - )?)) - } - VectorStorageDatatype::Float16 => { - Self::DenseHalf(Box::new(open_dense_vector_storage_impl::< - VectorElementTypeHalf, - S, - >( - fs.clone(), path, dim, distance, populate - )?)) - } + VectorStorageDatatype::Float32 => Self::Dense(Box::new( + ReadOnlyImmutableDenseVectorStorage::open(fs, path, dim, distance, populate)?, + )), + VectorStorageDatatype::Uint8 => Self::DenseByte(Box::new( + ReadOnlyImmutableDenseVectorStorage::open(fs, path, dim, distance, populate)?, + )), + VectorStorageDatatype::Float16 => Self::DenseHalf(Box::new( + ReadOnlyImmutableDenseVectorStorage::open(fs, path, dim, distance, populate)?, + )), VectorStorageDatatype::Turbo4 => { return Err(OperationError::service_error( "Turbo4 datatype storage is not yet supported", diff --git a/lib/segment/src/vector_storage/read_only/live_reload.rs b/lib/segment/src/vector_storage/read_only/live_reload.rs index c9834490bf..3cad873b00 100644 --- a/lib/segment/src/vector_storage/read_only/live_reload.rs +++ b/lib/segment/src/vector_storage/read_only/live_reload.rs @@ -18,12 +18,14 @@ impl LiveReload for VectorStorageReadEnum { hw_counter: &HardwareCounterCell, ) -> OperationResult<()> { match self { - // Immutable dense (mmap) live-reload is postponed: needs the deleted - // flags threaded through `ImmutableDenseVectors` (Dense* step). - VectorStorageReadEnum::Dense(_) - | VectorStorageReadEnum::DenseByte(_) - | VectorStorageReadEnum::DenseHalf(_) => { - todo!("live_reload for immutable dense (mmap) storage is not yet implemented") + VectorStorageReadEnum::Dense(s) => { + s.live_reload(fs, deleted_points, new_points, hw_counter) + } + VectorStorageReadEnum::DenseByte(s) => { + s.live_reload(fs, deleted_points, new_points, hw_counter) + } + VectorStorageReadEnum::DenseHalf(s) => { + s.live_reload(fs, deleted_points, new_points, hw_counter) } VectorStorageReadEnum::DenseChunked(s) => { s.live_reload(fs, deleted_points, new_points, hw_counter) diff --git a/lib/segment/src/vector_storage/read_only/mod.rs b/lib/segment/src/vector_storage/read_only/mod.rs index faf338fc2e..7b1fc38e3b 100644 --- a/lib/segment/src/vector_storage/read_only/mod.rs +++ b/lib/segment/src/vector_storage/read_only/mod.rs @@ -5,8 +5,9 @@ use crate::common::operation_error::OperationResult; use crate::data_types::vectors::{ QueryVector, VectorElementType, VectorElementTypeByte, VectorElementTypeHalf, }; -use crate::vector_storage::dense::dense_vector_storage::DenseVectorStorageImpl; -use crate::vector_storage::dense::read_only::ReadOnlyChunkedDenseVectorStorage; +use crate::vector_storage::dense::read_only::{ + ReadOnlyChunkedDenseVectorStorage, ReadOnlyImmutableDenseVectorStorage, +}; use crate::vector_storage::multi_dense::read_only::ReadOnlyChunkedMultiDenseVectorStorage; use crate::vector_storage::sparse::read_only::ReadOnlySparseVectorStorage; use crate::vector_storage::{ @@ -22,9 +23,9 @@ mod read_ops; /// Wraps each on-disk storage type with its [`super`] read-only variant. /// Volatile, empty and test-only variants are intentionally absent. pub enum VectorStorageReadEnum { - Dense(Box>), - DenseByte(Box>), - DenseHalf(Box>), + Dense(Box>), + DenseByte(Box>), + DenseHalf(Box>), DenseChunked(Box>), DenseChunkedByte(Box>), DenseChunkedHalf(Box>),