Standardize map index (#9305)

* rename to OnDiskMapIndex

* rename module

* remove `is_on_disk` field from `OnDiskMapIndex`

* add separate Immutable and OnDisk ReadOnlyMapIndex

* rename StoredPointToValues to OnDiskPointToValues

* rename module to `on_disk_point_to_values`

* rename to `build_from_iter` and add `populate` arg

* fix test
This commit is contained in:
Luis Cossío
2026-08-04 11:16:47 +02:00
committed by generall
parent 69a8e5ce72
commit c7f8bb2cea
49 changed files with 321 additions and 282 deletions
@@ -28,7 +28,7 @@ use super::{
use crate::common::Flusher;
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::geo_hash::{GeoHash, GeoHashRaw};
use crate::index::field_index::stored_point_to_values::StoredPointToValues;
use crate::index::field_index::on_disk_point_to_values::OnDiskPointToValues;
use crate::types::GeoPoint;
impl<S: UniversalRead> StoredGeoMapIndex<S> {
@@ -48,7 +48,7 @@ impl<S: UniversalRead> StoredGeoMapIndex<S> {
let points_map_ids_path = path.join(POINTS_MAP_IDS);
// Create the point-to-value mapping and persist in the file
StoredPointToValues::<GeoPoint, MmapFile>::from_iter(
OnDiskPointToValues::<GeoPoint, MmapFile>::build_from_iter(
&MmapFs,
path,
dynamic_index
@@ -56,6 +56,7 @@ impl<S: UniversalRead> StoredGeoMapIndex<S> {
.iter()
.enumerate()
.map(|(idx, values)| (idx as PointOffsetType, values.iter())),
!is_on_disk,
)?;
{
@@ -195,7 +196,7 @@ impl<S: UniversalRead> StoredGeoMapIndex<S> {
TypedStorage::open(fs, &points_map_path, open_options, Default::default())?;
let points_map_ids =
TypedStorage::open(fs, &points_map_ids_path, open_options, Default::default())?;
let point_to_values = StoredPointToValues::open(fs, path, true)?;
let point_to_values = OnDiskPointToValues::open(fs, path, true)?;
let mut deleted = deleted_points.to_owned();
@@ -6,7 +6,7 @@ use common::universal_io::{MmapFile, TypedStorage, UniversalRead};
use serde::{Deserialize, Serialize};
use crate::index::field_index::geo_hash::GeoHashRaw;
use crate::index::field_index::stored_point_to_values::StoredPointToValues;
use crate::index::field_index::on_disk_point_to_values::OnDiskPointToValues;
use crate::types::GeoPoint;
mod lifecycle;
@@ -79,7 +79,7 @@ pub(in super::super) struct Storage<S: UniversalRead = MmapFile> {
/// A storage of associations between geo-hashes and point ids. (See the diagram above)
pub(in super::super) points_map_ids: TypedStorage<S, PointOffsetType>,
/// One-to-many mapping of the PointOffsetType to the GeoPoint.
pub(in super::super) point_to_values: StoredPointToValues<GeoPoint, S>,
pub(in super::super) point_to_values: OnDiskPointToValues<GeoPoint, S>,
/// In-memory deletion bitmap. Reconstructed at load time as the union of
/// the build-time empty-payload bits read from `deleted.bin` and the
/// segment-level deleted bitslice supplied by the id-tracker. Not persisted.
@@ -12,7 +12,7 @@ use super::null_index::{ImmutableNullIndex, NullIndex};
use super::numeric_index::{
Encodable, NumericIndexGridstoreBuilder, NumericIndexIntoInnerValue, NumericIndexMmapBuilder,
};
use super::stored_point_to_values::StoredValue;
use super::on_disk_point_to_values::StoredValue;
use super::{FieldIndexBuilder, ValueIndexer};
use crate::common::operation_error::{OperationError, OperationResult};
use crate::data_types::index::TextIndexParams;
@@ -294,10 +294,10 @@ impl IndexSelector<'_> {
{
Ok(match self {
IndexSelector::NonAppendable { dir, is_on_disk } => {
MapIndex::new_mmap(&map_dir(dir, field), *is_on_disk, deleted_points)?
MapIndex::new_immutable(&map_dir(dir, field), *is_on_disk, deleted_points)?
}
IndexSelector::Appendable { dir } => {
MapIndex::new_gridstore(map_dir(dir, field), create_if_missing)?
MapIndex::new_mutable(map_dir(dir, field), create_if_missing)?
}
})
}
@@ -313,13 +313,11 @@ impl IndexSelector<'_> {
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
{
match self {
IndexSelector::NonAppendable { dir, is_on_disk } => make_mmap(MapIndex::builder_mmap(
&map_dir(dir, field),
*is_on_disk,
deleted_points,
)),
IndexSelector::NonAppendable { dir, is_on_disk } => make_mmap(
MapIndex::builder_immutable(&map_dir(dir, field), *is_on_disk, deleted_points),
),
IndexSelector::Appendable { dir } => {
make_gridstore(MapIndex::builder_gridstore(map_dir(dir, field)))
make_gridstore(MapIndex::builder_mutable(map_dir(dir, field)))
}
}
}
@@ -13,8 +13,9 @@ use serde_json::Value;
use super::MapIndex;
use super::key::MapIndexKey;
use super::universal_map_index::UniversalMapIndex;
use super::on_disk_map_index::OnDiskMapIndex;
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::map_index::immutable_map_index::ImmutableMapIndex;
use crate::index::field_index::{FieldIndexBuilderTrait, PayloadFieldIndex, ValueIndexer};
pub struct MapIndexBuilder<N: MapIndexKey + ?Sized>(pub(super) MapIndex<N>)
@@ -110,14 +111,23 @@ where
}
fn finalize(self) -> OperationResult<Self::FieldIndexType> {
Ok(MapIndex::OnDisk(Box::new(UniversalMapIndex::build(
let populate = !self.is_on_disk;
let on_disk_index = OnDiskMapIndex::build(
&MmapFs,
&self.path,
self.point_to_values,
self.values_to_points,
self.is_on_disk,
populate,
&self.deleted_points,
)?)))
)?;
let index = if self.is_on_disk {
MapIndex::OnDisk(on_disk_index)
} else {
MapIndex::Immutable(ImmutableMapIndex::load_from_on_disk(on_disk_index)?)
};
Ok(index)
}
}
@@ -152,7 +162,7 @@ where
"index must be initialized exactly once",
);
self.index.replace(
MapIndex::new_gridstore(self.dir.clone(), true)?.ok_or_else(|| {
MapIndex::new_mutable(self.dir.clone(), true)?.ok_or_else(|| {
OperationError::service_error("Failed to create mutable map index")
})?,
);
@@ -101,6 +101,12 @@ where
Ok(())
}
ReadOnlyMapIndex::Immutable(index) => {
index.for_points_values(points, |idx, slice| {
f(idx, &mut slice.iter().map(|v| v.borrow().into()));
});
Ok(())
}
ReadOnlyMapIndex::OnDisk(index) => {
index.for_points_values(points, hw_counter, |idx, vals| {
f(idx, &mut vals.map(|v| v.into()));
})
@@ -5,23 +5,27 @@ use std::path::PathBuf;
use bitvec::vec::BitVec;
use common::counter::hardware_counter::HardwareCounterCell;
use common::types::PointOffsetType;
use common::universal_io::{UniversalRead, UniversalWrite};
use gridstore::Blob;
use super::super::MapIndexKey;
use super::super::on_disk_map_index::OnDiskMapIndex;
use super::super::read_ops::MapIndexRead;
use super::super::universal_map_index::UniversalMapIndex;
use super::{ContainerSegment, ImmutableMapIndex, Storage};
use super::{ContainerSegment, ImmutableMapIndex};
use crate::common::Flusher;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::immutable_point_to_values::ImmutablePointToValues;
impl<N: MapIndexKey + ?Sized> ImmutableMapIndex<N>
impl<N, S> ImmutableMapIndex<N, S>
where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
N: MapIndexKey + ?Sized,
S: UniversalRead,
{
/// Open and load the immutable map index from mmap storage.
pub(in super::super) fn open_mmap(index: UniversalMapIndex<N>) -> OperationResult<Self> {
let index = Box::new(index);
pub(in super::super) fn load_from_on_disk(
index: OnDiskMapIndex<N, S>,
) -> OperationResult<Self> {
let hw_counter = HardwareCounterCell::disposable(); // Internal operation
let mut indexed_points = 0;
@@ -87,7 +91,7 @@ where
// Index is now loaded into memory, clear cache of backing mmap storage
if let Err(err) = index.clear_cache() {
log::warn!("Failed to clear mmap cache of ram mmap map index: {err}");
log::warn!("Failed to clear mmap cache of immutable map index: {err}");
}
let mut result = Self {
@@ -97,7 +101,7 @@ where
point_to_values,
indexed_points,
values_count,
storage: Storage::Mmap(index),
storage: index,
cached_ram_usage_bytes: 0,
};
result.cached_ram_usage_bytes = result.compute_ram_usage_bytes();
@@ -137,7 +141,14 @@ where
}
false
}
}
impl<N, S> ImmutableMapIndex<N, S>
where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
N: MapIndexKey + ?Sized,
S: UniversalWrite,
{
/// Removes `idx` from values-to-points-container.
/// It is implemented by shrinking the range of values-to-points by one and moving the removed element
/// out of the range.
@@ -209,12 +220,8 @@ where
idx,
);
// Update persisted storage
match self.storage {
Storage::Mmap(ref mut index) => {
index.remove_point(idx);
}
}
// Update storage
self.storage.remove_point(idx);
removed_values_count += 1;
}
@@ -229,9 +236,7 @@ where
#[inline]
pub(in super::super) fn wipe(self) -> OperationResult<()> {
match self.storage {
Storage::Mmap(index) => index.wipe(),
}
self.storage.wipe()
}
/// Clear cache
@@ -239,29 +244,21 @@ where
/// Only clears cache of mmap storage if used. Does not clear in-memory representation of
/// index.
pub fn clear_cache(&self) -> OperationResult<()> {
match self.storage {
Storage::Mmap(ref index) => index.clear_cache(),
}
self.storage.clear_cache()
}
#[inline]
pub(in super::super) fn files(&self) -> Vec<PathBuf> {
match self.storage {
Storage::Mmap(ref index) => index.files(),
}
self.storage.files()
}
#[inline]
pub(in super::super) fn immutable_files(&self) -> Vec<PathBuf> {
match &self.storage {
Storage::Mmap(index) => index.immutable_files(),
}
self.storage.immutable_files()
}
#[inline]
pub(in super::super) fn flusher(&self) -> Flusher {
match self.storage {
Storage::Mmap(ref index) => index.flusher(),
}
self.storage.flusher()
}
}
@@ -4,15 +4,20 @@ use std::ops::Range;
use bitvec::vec::BitVec;
use common::persisted_hashmap::Key;
use common::types::PointOffsetType;
use common::universal_io::{MmapFile, UniversalRead};
use super::MapIndexKey;
use super::universal_map_index::UniversalMapIndex;
use super::on_disk_map_index::OnDiskMapIndex;
use crate::index::field_index::immutable_point_to_values::ImmutablePointToValues;
mod lifecycle;
mod read_ops;
pub struct ImmutableMapIndex<N: MapIndexKey + Key + ?Sized> {
pub struct ImmutableMapIndex<N, S = MmapFile>
where
N: MapIndexKey + Key + ?Sized,
S: UniversalRead,
{
pub(super) value_to_points: HashMap<<N as MapIndexKey>::Owned, ContainerSegment>,
/// Container holding a slice of point IDs per value. `value_to_point` holds the range per value.
/// Each slice MUST be sorted so that we can binary search over it.
@@ -23,16 +28,12 @@ pub struct ImmutableMapIndex<N: MapIndexKey + Key + ?Sized> {
pub(super) indexed_points: usize,
pub(super) values_count: usize,
// Backing storage, source of state, persists deletions
pub(super) storage: Storage<N>,
pub(super) storage: OnDiskMapIndex<N, S>,
/// Snapshot of approximate RAM usage at construction time.
/// Not refreshed on `remove_point`.
pub(super) cached_ram_usage_bytes: usize,
}
pub(super) enum Storage<N: MapIndexKey + Key + ?Sized> {
Mmap(Box<UniversalMapIndex<N>>),
}
pub(super) struct ContainerSegment {
/// Range in the container which holds point IDs for the value.
range: Range<u32>,
@@ -3,17 +3,20 @@ use std::iter;
use common::counter::hardware_counter::HardwareCounterCell;
use common::types::PointOffsetType;
use common::universal_io::UniversalRead;
use gridstore::Blob;
use super::super::read_ops::MapIndexRead;
use super::super::{IdIter, MapIndexKey};
use super::{ContainerSegment, ImmutableMapIndex, Storage};
use super::{ContainerSegment, ImmutableMapIndex};
use crate::common::operation_error::OperationResult;
use crate::index::payload_config::StorageType;
impl<N: MapIndexKey + ?Sized> MapIndexRead<N> for ImmutableMapIndex<N>
impl<N, S> MapIndexRead<N> for ImmutableMapIndex<N, S>
where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
N: MapIndexKey + ?Sized,
S: UniversalRead,
{
fn check_values_any(
&self,
@@ -99,11 +102,7 @@ where
}
fn storage_type(&self) -> StorageType {
match &self.storage {
Storage::Mmap(index) => StorageType::Mmap {
is_on_disk: index.is_on_disk(),
},
}
StorageType::Mmap { is_on_disk: false }
}
/// Approximate RAM usage in bytes (cached at construction).
@@ -116,9 +115,11 @@ where
}
}
impl<N: MapIndexKey + ?Sized> ImmutableMapIndex<N>
impl<N, S> ImmutableMapIndex<N, S>
where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
N: MapIndexKey + ?Sized,
S: UniversalRead,
{
pub fn for_points_values(
&self,
@@ -7,7 +7,7 @@ use common::persisted_hashmap::Key;
use ecow::EcoString;
use super::BLOCK_SIZE_KEYWORD;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::types::{IntPayloadType, UuidIntType};
pub trait MapIndexKey: Key + StoredValue + Eq + Display + Debug {
@@ -10,7 +10,7 @@ use super::builders::MapIndexMmapBuilder;
use super::immutable_map_index::ImmutableMapIndex;
use super::key::MapIndexKey;
use super::mutable_map_index::MutableMapIndex;
use super::universal_map_index::UniversalMapIndex;
use super::on_disk_map_index::OnDiskMapIndex;
use crate::common::Flusher;
use crate::common::operation_error::OperationResult;
@@ -19,7 +19,7 @@ where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
{
/// Load immutable mmap based index, either in RAM or on disk
pub fn new_mmap(
pub fn new_immutable(
path: &Path,
is_on_disk: bool,
deleted_points: &BitSlice,
@@ -31,27 +31,27 @@ where
let effective_is_on_disk =
is_on_disk || common::low_memory::low_memory_mode().prefer_disk();
let Some(universal_index) =
UniversalMapIndex::open(&MmapFs, path, effective_is_on_disk, deleted_points)?
let Some(on_disk_index) =
OnDiskMapIndex::open(&MmapFs, path, !effective_is_on_disk, deleted_points)?
else {
return Ok(None);
};
let index = if effective_is_on_disk {
MapIndex::OnDisk(Box::new(universal_index))
MapIndex::OnDisk(on_disk_index)
} else {
// Load into RAM, use mmap as backing storage
MapIndex::Immutable(ImmutableMapIndex::open_mmap(universal_index)?)
MapIndex::Immutable(ImmutableMapIndex::load_from_on_disk(on_disk_index)?)
};
Ok(Some(index))
}
pub fn new_gridstore(dir: PathBuf, create_if_missing: bool) -> OperationResult<Option<Self>> {
pub fn new_mutable(dir: PathBuf, create_if_missing: bool) -> OperationResult<Option<Self>> {
let index = MutableMapIndex::open_gridstore(dir, create_if_missing)?;
Ok(index.map(MapIndex::Mutable))
}
pub fn builder_mmap(
pub fn builder_immutable(
path: &Path,
is_on_disk: bool,
deleted_points: &BitSlice,
@@ -65,7 +65,7 @@ where
}
}
pub fn builder_gridstore(dir: PathBuf) -> super::builders::MapIndexGridstoreBuilder<N> {
pub fn builder_mutable(dir: PathBuf) -> super::builders::MapIndexGridstoreBuilder<N> {
super::builders::MapIndexGridstoreBuilder::new(dir)
}
@@ -5,7 +5,7 @@ pub use self::builders::{MapIndexBuilder, MapIndexGridstoreBuilder, MapIndexMmap
use self::immutable_map_index::ImmutableMapIndex;
pub use self::key::MapIndexKey;
use self::mutable_map_index::MutableMapIndex;
use self::universal_map_index::UniversalMapIndex;
use self::on_disk_map_index::OnDiskMapIndex;
mod builders;
mod facet_index_impl;
@@ -13,11 +13,11 @@ pub mod immutable_map_index;
pub mod key;
mod lifecycle;
pub mod mutable_map_index;
pub mod on_disk_map_index;
mod payload_index_impl;
pub mod read_ops;
#[cfg(test)]
mod tests;
pub mod universal_map_index;
mod value_indexer_impl;
pub mod read_only;
@@ -40,5 +40,5 @@ where
/// Loaded in RAM, use immutable storage format
Immutable(ImmutableMapIndex<N>),
/// Served directly from storage (via mmap), use immutable format
OnDisk(Box<UniversalMapIndex<N>>),
OnDisk(OnDiskMapIndex<N>),
}
@@ -17,13 +17,13 @@ use fs_err as fs;
use super::super::MapIndexKey;
use super::{
CONFIG_PATH, DELETED_PATH, HASHMAP_PATH, Storage, UniversalMapIndex, UniversalMapIndexConfig,
CONFIG_PATH, DELETED_PATH, HASHMAP_PATH, OnDiskMapIndex, Storage, UniversalMapIndexConfig,
};
use crate::common::Flusher;
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::stored_point_to_values::StoredPointToValues;
use crate::index::field_index::on_disk_point_to_values::OnDiskPointToValues;
impl<N, S> UniversalMapIndex<N, S>
impl<N, S> OnDiskMapIndex<N, S>
where
N: MapIndexKey + Key + ?Sized,
S: UniversalRead,
@@ -32,7 +32,7 @@ where
pub fn open(
fs: &S::Fs,
path: &Path,
is_on_disk: bool,
populate: bool,
deleted_points: &BitSlice,
) -> OperationResult<Option<Self>> {
let hashmap_path = path.join(HASHMAP_PATH);
@@ -46,20 +46,18 @@ where
return Ok(None);
};
let do_populate = !is_on_disk;
let value_to_points = UniversalHashMap::open(
fs,
&hashmap_path,
OpenOptions {
writeable: false,
need_sequential: false,
populate: Populate::from(do_populate),
populate: Populate::from(populate),
advice: AdviceSetting::Global,
},
Default::default(),
)?;
let point_to_values = StoredPointToValues::open(fs, path, do_populate)?;
let point_to_values = OnDiskPointToValues::open(fs, path, populate)?;
let mut deleted = deleted_points.to_owned();
@@ -69,7 +67,7 @@ where
OpenOptions {
writeable: true,
need_sequential: false,
populate: Populate::from(do_populate),
populate: Populate::from(populate),
advice: AdviceSetting::Global,
},
Default::default(),
@@ -96,7 +94,6 @@ where
},
deleted_count,
total_key_value_pairs: config.total_key_value_pairs,
is_on_disk,
}))
}
@@ -111,105 +108,6 @@ where
self.deleted_count += 1;
}
}
}
impl<N, S> UniversalMapIndex<N, S>
where
N: MapIndexKey + Key + ?Sized,
S: UniversalWrite,
{
/// TODO: Use Fs to create config and hashmap files?
pub fn build(
fs: &S::Fs,
path: &Path,
point_to_values: Vec<Vec<<N as MapIndexKey>::Owned>>,
values_to_points: HashMap<<N as MapIndexKey>::Owned, Vec<PointOffsetType>>,
is_on_disk: bool,
deleted_points: &BitSlice,
) -> OperationResult<Self> {
fs::create_dir_all(path)?;
let hashmap_path = path.join(HASHMAP_PATH);
let deleted_path = path.join(DELETED_PATH);
let config_path = path.join(CONFIG_PATH);
atomic_save_json(
&config_path,
&UniversalMapIndexConfig {
total_key_value_pairs: point_to_values.iter().map(|v| v.len()).sum(),
},
)?;
serialize_hashmap(
&hashmap_path,
values_to_points
.iter()
.map(|(value, ids)| (value.borrow(), ids.iter().copied())),
)?;
StoredPointToValues::<N, MmapFile>::from_iter(
&MmapFs,
path,
point_to_values.iter().enumerate().map(|(idx, values)| {
(
idx as PointOffsetType,
values.iter().map(|value| value.borrow()),
)
}),
)?;
{
let deleted_flags_count = point_to_values.len();
let _ = create_and_ensure_length(
&deleted_path,
deleted_flags_count
.div_ceil(u8::BITS as usize)
.next_multiple_of(size_of::<u64>()),
)?;
let mut deleted = StoredBitSlice::<S>::open(
fs,
&deleted_path,
OpenOptions {
writeable: true,
need_sequential: false,
populate: Populate::Auto,
advice: AdviceSetting::Global,
},
Default::default(),
)?;
deleted.set_ascending_bits_batch(
point_to_values
.iter()
.enumerate()
.filter(|(_, values)| values.is_empty())
.map(|(idx, _)| (idx as u64, true)),
)?;
deleted.flusher()()?;
}
Self::open(fs, path, is_on_disk, deleted_points)?.ok_or_else(|| {
OperationError::service_error("Failed to open UniversalMapIndex after building it")
})
}
/// No-op flusher: the on-disk state is build-time only. See the type-level
/// docs on [`UniversalMapIndex`] for the deletion durability contract.
pub fn flusher(&self) -> Flusher {
Box::new(|| Ok(()))
}
pub fn wipe(self) -> OperationResult<()> {
let files = self.files();
let path = self.path.clone();
// drop mmap handles before deleting files
drop(self);
for file in files {
fs::remove_file(file)?;
}
let _ = fs::remove_dir(path);
Ok(())
}
pub fn files(&self) -> Vec<PathBuf> {
let mut files = vec![
@@ -246,7 +144,6 @@ where
storage,
deleted_count: _,
total_key_value_pairs: _,
is_on_disk: _,
} = self;
let Storage {
value_to_points,
@@ -263,3 +160,103 @@ where
self.storage.ram_usage_bytes()
}
}
impl<N, S> OnDiskMapIndex<N, S>
where
N: MapIndexKey + Key + ?Sized,
S: UniversalWrite,
{
/// TODO: Use Fs to create config and hashmap files?
pub fn build(
fs: &S::Fs,
path: &Path,
point_to_values: Vec<Vec<<N as MapIndexKey>::Owned>>,
values_to_points: HashMap<<N as MapIndexKey>::Owned, Vec<PointOffsetType>>,
populate: bool,
deleted_points: &BitSlice,
) -> OperationResult<Self> {
fs::create_dir_all(path)?;
let hashmap_path = path.join(HASHMAP_PATH);
let deleted_path = path.join(DELETED_PATH);
let config_path = path.join(CONFIG_PATH);
atomic_save_json(
&config_path,
&UniversalMapIndexConfig {
total_key_value_pairs: point_to_values.iter().map(|v| v.len()).sum(),
},
)?;
serialize_hashmap(
&hashmap_path,
values_to_points
.iter()
.map(|(value, ids)| (value.borrow(), ids.iter().copied())),
)?;
OnDiskPointToValues::<N, MmapFile>::build_from_iter(
&MmapFs,
path,
point_to_values.iter().enumerate().map(|(idx, values)| {
(
idx as PointOffsetType,
values.iter().map(|value| value.borrow()),
)
}),
populate,
)?;
{
let deleted_flags_count = point_to_values.len();
let _ = create_and_ensure_length(
&deleted_path,
deleted_flags_count
.div_ceil(u8::BITS as usize)
.next_multiple_of(size_of::<u64>()),
)?;
let mut deleted = StoredBitSlice::<S>::open(
fs,
&deleted_path,
OpenOptions {
writeable: true,
need_sequential: false,
populate: Populate::Auto,
advice: AdviceSetting::Global,
},
Default::default(),
)?;
deleted.set_ascending_bits_batch(
point_to_values
.iter()
.enumerate()
.filter(|(_, values)| values.is_empty())
.map(|(idx, _)| (idx as u64, true)),
)?;
deleted.flusher()()?;
}
Self::open(fs, path, populate, deleted_points)?.ok_or_else(|| {
OperationError::service_error("Failed to open UniversalMapIndex after building it")
})
}
/// No-op flusher: the on-disk state is build-time only. See the type-level
/// docs on [`OnDiskMapIndex`] for the deletion durability contract.
pub fn flusher(&self) -> Flusher {
Box::new(|| Ok(()))
}
pub fn wipe(self) -> OperationResult<()> {
let files = self.files();
let path = self.path.clone();
// drop mmap handles before deleting files
drop(self);
for file in files {
fs::remove_file(file)?;
}
let _ = fs::remove_dir(path);
Ok(())
}
}
@@ -5,9 +5,9 @@ use common::universal_io::UniversalRead;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::map_index::MapIndexKey;
use crate::index::field_index::map_index::universal_map_index::UniversalMapIndex;
use crate::index::field_index::map_index::on_disk_map_index::OnDiskMapIndex;
impl<N, S> UniversalMapIndex<N, S>
impl<N, S> OnDiskMapIndex<N, S>
where
N: MapIndexKey + Key + ?Sized,
S: UniversalRead,
@@ -7,7 +7,7 @@ use common::universal_io::{MmapFile, UniversalRead};
use serde::{Deserialize, Serialize};
use super::MapIndexKey;
use crate::index::field_index::stored_point_to_values::StoredPointToValues;
use crate::index::field_index::on_disk_point_to_values::OnDiskPointToValues;
mod lifecycle;
mod live_reload;
@@ -19,9 +19,6 @@ pub(super) const CONFIG_PATH: &str = "mmap_field_index_config.json";
/// Immutable map index served directly from a [`UniversalRead`] storage backend.
///
/// The storage parameter `S` defaults to [`MmapFile`], but any `UniversalRead`
/// implementation works — e.g. io_uring or disk-cache wrappers.
///
/// On-disk state (`values_to_points.bin`, `deleted.bin`, `point_to_values.*`,
/// `mmap_field_index_config.json`) is written once during [`Self::build`] and
/// not mutated afterwards: `deleted.bin` records only the points whose payload
@@ -32,17 +29,16 @@ pub(super) const CONFIG_PATH: &str = "mmap_field_index_config.json";
/// only updates the in-memory bitvec. Callers must re-supply the authoritative
/// deletion set (typically `id_tracker.deleted_point_bitslice()`) via the
/// `deleted_points` argument to [`Self::open`] on reload.
pub struct UniversalMapIndex<N: MapIndexKey + Key + ?Sized, S: UniversalRead = MmapFile> {
pub struct OnDiskMapIndex<N: MapIndexKey + Key + ?Sized, S: UniversalRead = MmapFile> {
pub(super) path: PathBuf,
pub(super) storage: Storage<N, S>,
pub(super) deleted_count: usize,
pub(super) total_key_value_pairs: usize,
pub(super) is_on_disk: bool,
}
pub(super) struct Storage<N: MapIndexKey + Key + ?Sized, S: UniversalRead = MmapFile> {
pub(super) value_to_points: UniversalHashMap<N, PointOffsetType, S>,
pub(super) point_to_values: StoredPointToValues<N, S>,
pub(super) point_to_values: OnDiskPointToValues<N, S>,
/// In-memory deletion bitmap. Reconstructed at load time as the union of
/// the build-time empty-payload bits read from `deleted.bin` and the
/// segment-level deleted bitslice supplied by the id-tracker. Not persisted.
@@ -12,19 +12,19 @@ use itertools::Itertools;
use super::super::read_ops::MapIndexRead;
use super::super::{IdIter, MapIndexKey};
use super::UniversalMapIndex;
use super::OnDiskMapIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::stored_point_to_values::ValuesIter;
use crate::index::field_index::on_disk_point_to_values::ValuesIter;
use crate::index::payload_config::StorageType;
impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for UniversalMapIndex<N, S> {
impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for OnDiskMapIndex<N, S> {
fn check_values_any(
&self,
idx: PointOffsetType,
hw_counter: &HardwareCounterCell,
check_fn: impl Fn(&N) -> bool,
) -> bool {
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
// Measure self.deleted access.
hw_counter
@@ -56,7 +56,7 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
where
N: 'a,
{
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
// We can account cost of reading `bool`, but it will likely be more expensive, than
// actually reading bool itself.
@@ -98,7 +98,7 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
}
fn get_count_for_value(&self, value: &N, hw_counter: &HardwareCounterCell) -> Option<usize> {
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
// Since `value_to_points.get` doesn't actually force read from disk for all values
// we need to only account for the overhead of hashmap lookup
@@ -125,7 +125,7 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
}
fn get_iterator(&self, value: &N, hw_counter: &HardwareCounterCell) -> IdIter<'_> {
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
match self.storage.value_to_points.unbatched_get(value) {
Ok(Some(values)) => {
@@ -188,7 +188,8 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
hw_counter: &HardwareCounterCell,
mut f: impl FnMut(&N, &mut dyn Iterator<Item = PointOffsetType>) -> OperationResult<()>,
) -> OperationResult<()> {
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
let deleted = &self.storage.deleted;
self.storage.value_to_points.for_each_entry(|k, v| {
@@ -211,9 +212,7 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
}
fn storage_type(&self) -> StorageType {
StorageType::Mmap {
is_on_disk: self.is_on_disk,
}
StorageType::Mmap { is_on_disk: true }
}
fn ram_usage_bytes(&self) -> usize {
@@ -225,14 +224,14 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> MapIndexRead<N> for Univer
}
}
impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> UniversalMapIndex<N, S> {
impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> OnDiskMapIndex<N, S> {
pub fn for_points_values(
&self,
mut points: impl Iterator<Item = PointOffsetType>,
hw_counter: &HardwareCounterCell,
mut f: impl FnMut(PointOffsetType, ValuesIter<'_, N>),
) -> OperationResult<()> {
let hw_counter = self.make_conditioned_counter(hw_counter);
let hw_counter = ConditionedCounter::always(hw_counter);
points.try_for_each(|idx| {
if self.storage.deleted.get_bit(idx as usize) != Some(false) {
@@ -245,14 +244,7 @@ impl<N: MapIndexKey + Key + ?Sized, S: UniversalRead> UniversalMapIndex<N, S> {
})
}
pub(super) fn make_conditioned_counter<'a>(
&self,
hw_counter: &'a HardwareCounterCell,
) -> ConditionedCounter<'a> {
ConditionedCounter::new(self.is_on_disk, hw_counter)
}
pub fn is_on_disk(&self) -> bool {
self.is_on_disk
true
}
}
@@ -6,9 +6,10 @@ use gridstore::Blob;
use super::super::MapIndexKey;
use super::super::mutable_map_index::read_only::ReadOnlyAppendableMapIndex;
use super::super::universal_map_index::UniversalMapIndex;
use super::super::on_disk_map_index::OnDiskMapIndex;
use super::ReadOnlyMapIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::map_index::immutable_map_index::ImmutableMapIndex;
use crate::index::payload_config::IndexMutability;
impl<N: MapIndexKey + ?Sized, S: UniversalRead> ReadOnlyMapIndex<N, S>
@@ -51,10 +52,19 @@ where
let effective_is_on_disk =
is_on_disk || common::low_memory::low_memory_mode().prefer_disk();
Ok(
UniversalMapIndex::open(fs, path, effective_is_on_disk, deleted_points)?
.map(Self::Immutable),
)
let Some(on_disk_index) =
OnDiskMapIndex::open(fs, path, !effective_is_on_disk, deleted_points)?
else {
return Ok(None);
};
if effective_is_on_disk {
Ok(Some(Self::OnDisk(on_disk_index)))
} else {
Ok(Some(Self::Immutable(ImmutableMapIndex::load_from_on_disk(
on_disk_index,
)?)))
}
}
/// Reports the on-disk format's mutability, mirroring
@@ -72,6 +82,7 @@ where
match self {
Self::Appendable(_) => IndexMutability::Mutable,
Self::Immutable(_) => IndexMutability::Immutable,
Self::OnDisk(_) => IndexMutability::Immutable,
}
}
}
@@ -2,8 +2,9 @@ use common::universal_io::UniversalRead;
use gridstore::Blob;
use crate::index::field_index::map_index::MapIndexKey;
use crate::index::field_index::map_index::immutable_map_index::ImmutableMapIndex;
use crate::index::field_index::map_index::mutable_map_index::read_only::ReadOnlyAppendableMapIndex;
use crate::index::field_index::map_index::universal_map_index::UniversalMapIndex;
use crate::index::field_index::map_index::on_disk_map_index::OnDiskMapIndex;
mod lifecycle;
mod read_ops;
@@ -32,8 +33,9 @@ where
{
/// Loads into RAM from appendable storage format
Appendable(ReadOnlyAppendableMapIndex<N, S>),
Immutable(ImmutableMapIndex<N, S>),
/// Directly reads from storage in immutable format
Immutable(UniversalMapIndex<N, S>),
OnDisk(OnDiskMapIndex<N, S>),
}
#[cfg(test)]
@@ -64,7 +66,7 @@ mod tests {
// Build via the writable gridstore builder (matches the existing map
// tests' `IndexType::MutableGridstore` path).
{
let mut builder = MapIndex::<str>::builder_gridstore(dir.path().to_path_buf());
let mut builder = MapIndex::<str>::builder_mutable(dir.path().to_path_buf());
builder.init().unwrap();
let entries: &[(PointOffsetType, &[&str])] = &[
(0, &["red", "green"]),
@@ -32,6 +32,7 @@ where
index.check_values_any(idx, hw_counter, check_fn)
}
ReadOnlyMapIndex::Immutable(index) => index.check_values_any(idx, hw_counter, check_fn),
ReadOnlyMapIndex::OnDisk(index) => index.check_values_any(idx, hw_counter, check_fn),
}
}
@@ -46,6 +47,7 @@ where
let boxed: Box<dyn Iterator<Item = Cow<'a, N>> + 'a> = match self {
ReadOnlyMapIndex::Appendable(index) => Box::new(index.get_values(idx, hw_counter)?),
ReadOnlyMapIndex::Immutable(index) => Box::new(index.get_values(idx, hw_counter)?),
ReadOnlyMapIndex::OnDisk(index) => Box::new(index.get_values(idx, hw_counter)?),
};
Some(boxed)
}
@@ -54,6 +56,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.values_count(idx),
ReadOnlyMapIndex::Immutable(index) => index.values_count(idx),
ReadOnlyMapIndex::OnDisk(index) => index.values_count(idx),
}
}
@@ -61,6 +64,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.get_indexed_points(),
ReadOnlyMapIndex::Immutable(index) => index.get_indexed_points(),
ReadOnlyMapIndex::OnDisk(index) => index.get_indexed_points(),
}
}
@@ -68,6 +72,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.get_values_count(),
ReadOnlyMapIndex::Immutable(index) => index.get_values_count(),
ReadOnlyMapIndex::OnDisk(index) => index.get_values_count(),
}
}
@@ -75,6 +80,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.get_unique_values_count(),
ReadOnlyMapIndex::Immutable(index) => index.get_unique_values_count(),
ReadOnlyMapIndex::OnDisk(index) => index.get_unique_values_count(),
}
}
@@ -82,6 +88,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.get_count_for_value(value, hw_counter),
ReadOnlyMapIndex::Immutable(index) => index.get_count_for_value(value, hw_counter),
ReadOnlyMapIndex::OnDisk(index) => index.get_count_for_value(value, hw_counter),
}
}
@@ -89,6 +96,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.get_iterator(value, hw_counter),
ReadOnlyMapIndex::Immutable(index) => index.get_iterator(value, hw_counter),
ReadOnlyMapIndex::OnDisk(index) => index.get_iterator(value, hw_counter),
}
}
@@ -96,6 +104,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.for_each_value(f),
ReadOnlyMapIndex::Immutable(index) => index.for_each_value(f),
ReadOnlyMapIndex::OnDisk(index) => index.for_each_value(f),
}
}
@@ -111,6 +120,9 @@ where
ReadOnlyMapIndex::Immutable(index) => {
index.for_each_count_per_value(deferred_internal_id, f)
}
ReadOnlyMapIndex::OnDisk(index) => {
index.for_each_count_per_value(deferred_internal_id, f)
}
}
}
@@ -122,6 +134,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.for_each_value_map(hw_counter, f),
ReadOnlyMapIndex::Immutable(index) => index.for_each_value_map(hw_counter, f),
ReadOnlyMapIndex::OnDisk(index) => index.for_each_value_map(hw_counter, f),
}
}
@@ -129,6 +142,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.storage_type(),
ReadOnlyMapIndex::Immutable(index) => index.storage_type(),
ReadOnlyMapIndex::OnDisk(index) => index.storage_type(),
}
}
@@ -136,6 +150,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.ram_usage_bytes(),
ReadOnlyMapIndex::Immutable(index) => index.ram_usage_bytes(),
ReadOnlyMapIndex::OnDisk(index) => index.ram_usage_bytes(),
}
}
@@ -143,6 +158,7 @@ where
match self {
ReadOnlyMapIndex::Appendable(index) => index.telemetry_index_type(),
ReadOnlyMapIndex::Immutable(index) => index.telemetry_index_type(),
ReadOnlyMapIndex::OnDisk(index) => index.telemetry_index_type(),
}
}
}
@@ -64,7 +64,7 @@ fn save_map_index<N>(
match index_type {
IndexType::MutableGridstore => {
let mut builder = MapIndex::<N>::builder_gridstore(path.to_path_buf());
let mut builder = MapIndex::<N>::builder_mutable(path.to_path_buf());
builder.init().unwrap();
for (idx, values) in data.iter().enumerate() {
let values: Vec<Value> = values.iter().map(&into_value).collect();
@@ -76,7 +76,7 @@ fn save_map_index<N>(
builder.finalize().unwrap();
}
IndexType::Mmap | IndexType::RamMmap => {
let mut builder = MapIndex::<N>::builder_mmap(path, false, &empty_deleted());
let mut builder = MapIndex::<N>::builder_immutable(path, false, &empty_deleted());
builder.init().unwrap();
for (idx, values) in data.iter().enumerate() {
let values: Vec<Value> = values.iter().map(&into_value).collect();
@@ -99,13 +99,13 @@ where
Vec<<N as MapIndexKey>::Owned>: Blob + Send + Sync,
{
let index = match index_type {
IndexType::MutableGridstore => MapIndex::<N>::new_gridstore(path.to_path_buf(), true)
IndexType::MutableGridstore => MapIndex::<N>::new_mutable(path.to_path_buf(), true)
.unwrap()
.unwrap(),
IndexType::Mmap => MapIndex::<N>::new_mmap(path, true, &empty_deleted())
IndexType::Mmap => MapIndex::<N>::new_immutable(path, true, &empty_deleted())
.unwrap()
.unwrap(),
IndexType::RamMmap => MapIndex::<N>::new_mmap(path, false, &empty_deleted())
IndexType::RamMmap => MapIndex::<N>::new_immutable(path, false, &empty_deleted())
.unwrap()
.unwrap(),
};
@@ -128,7 +128,7 @@ where
fn test_uuid_payload_index() {
let temp_dir = Builder::new().prefix("store_dir").tempdir().unwrap();
let mut builder =
MapIndex::<UuidIntType>::builder_mmap(temp_dir.path(), false, &empty_deleted());
MapIndex::<UuidIntType>::builder_immutable(temp_dir.path(), false, &empty_deleted());
builder.init().unwrap();
@@ -152,11 +152,13 @@ fn test_uuid_payload_index() {
.unwrap();
}
#[test]
fn test_index_non_ascending_insertion() {
#[rstest]
#[case(false)]
#[case(true)]
fn test_index_non_ascending_insertion(#[case] on_disk: bool) {
let temp_dir = Builder::new().prefix("store_dir").tempdir().unwrap();
let mut builder =
MapIndex::<IntPayloadType>::builder_mmap(temp_dir.path(), false, &empty_deleted());
MapIndex::<IntPayloadType>::builder_immutable(temp_dir.path(), on_disk, &empty_deleted());
builder.init().unwrap();
let data = [vec![1, 2, 3, 4, 5, 6], vec![25], vec![10, 11]];
@@ -173,13 +175,16 @@ fn test_index_non_ascending_insertion() {
let index = builder.finalize().unwrap();
let hw_counter = HardwareCounterCell::new();
for (idx, values) in data.iter().enumerate().rev() {
let res: Vec<_> = index
for (idx, values) in data.into_iter().enumerate().rev() {
// values themselves don't promise any particular order
// so we only compare the set of values.
let values = HashSet::from_iter(values);
let res: HashSet<_> = index
.get_values(idx as u32, &hw_counter)
.unwrap()
.map(|i| *i as i32)
.collect();
assert_eq!(res, *values);
assert_eq!(res, values);
}
}
@@ -347,15 +352,17 @@ fn test_map_index_reload(#[case] index_type: IndexType) {
let deleted = deleted_with(&[1, 2, 5]);
let new_index = match index_type {
IndexType::MutableGridstore => {
MapIndex::<IntPayloadType>::new_gridstore(temp_dir.path().to_path_buf(), true)
MapIndex::<IntPayloadType>::new_mutable(temp_dir.path().to_path_buf(), true)
.unwrap()
.unwrap()
}
IndexType::Mmap => {
MapIndex::<IntPayloadType>::new_immutable(temp_dir.path(), true, &deleted)
.unwrap()
.unwrap()
}
IndexType::Mmap => MapIndex::<IntPayloadType>::new_mmap(temp_dir.path(), true, &deleted)
.unwrap()
.unwrap(),
IndexType::RamMmap => {
MapIndex::<IntPayloadType>::new_mmap(temp_dir.path(), false, &deleted)
MapIndex::<IntPayloadType>::new_immutable(temp_dir.path(), false, &deleted)
.unwrap()
.unwrap()
}
@@ -417,12 +424,12 @@ fn test_map_index_reload_short_deleted_bitslice(#[case] index_type: IndexType) {
let new_index = match index_type {
IndexType::Mmap => {
MapIndex::<IntPayloadType>::new_mmap(temp_dir.path(), true, &short_deleted)
MapIndex::<IntPayloadType>::new_immutable(temp_dir.path(), true, &short_deleted)
.unwrap()
.unwrap()
}
IndexType::RamMmap => {
MapIndex::<IntPayloadType>::new_mmap(temp_dir.path(), false, &short_deleted)
MapIndex::<IntPayloadType>::new_immutable(temp_dir.path(), false, &short_deleted)
.unwrap()
.unwrap()
}
+1 -1
View File
@@ -19,9 +19,9 @@ mod memory_reporter;
pub mod null_index;
pub mod numeric_index;
mod numeric_point;
mod on_disk_point_to_values;
pub mod schema_transition;
mod stat_tools;
mod stored_point_to_values;
#[cfg(test)]
mod tests;
mod utils;
@@ -15,7 +15,7 @@ use super::{Encodable, NumericIndex, NumericIndexIntoInnerValue};
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::numeric_index::immutable_numeric_index::ImmutableNumericIndex;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{FieldIndexBuilderTrait, ValueIndexer};
pub struct NumericIndexBuilder<T: Encodable + Numericable + StoredValue + Send + Sync + Default, P>(
@@ -14,7 +14,7 @@ use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::immutable_point_to_values::ImmutablePointToValues;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
impl<T, S> ImmutableNumericIndex<T, S>
where
@@ -8,7 +8,7 @@ use super::on_disk_numeric_index::OnDiskNumericIndex;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::immutable_point_to_values::ImmutablePointToValues;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
mod lifecycle;
mod read_ops;
@@ -11,7 +11,7 @@ use super::ImmutableNumericIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
impl<T, S> ImmutableNumericIndex<T, S>
@@ -17,7 +17,7 @@ use super::{
};
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{PayloadFieldIndex, ValueIndexer};
use crate::index::payload_config::{IndexMutability, StorageType};
use crate::telemetry::PayloadIndexTelemetry;
@@ -27,7 +27,7 @@ pub use storage::NumericIndexInner;
pub use storage::read_only::ReadOnlyNumericIndexInner;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
#[cfg(test)]
mod tests;
@@ -17,7 +17,7 @@ use crate::common::Flusher;
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
impl<T: Encodable + Numericable> Default for InMemoryNumericIndex<T> {
fn default() -> Self {
@@ -11,7 +11,7 @@ use super::ReadOnlyAppendableNumericIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
impl<T: Encodable + Numericable + Send + Sync + Default + StoredValue, S: UniversalRead>
@@ -10,7 +10,7 @@ use super::{InMemoryNumericIndex, MutableNumericIndex};
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
impl<T: Encodable + Numericable + Default> InMemoryNumericIndex<T> {
@@ -7,7 +7,7 @@ use super::Encodable;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
use crate::telemetry::PayloadIndexTelemetry;
@@ -21,7 +21,7 @@ use crate::common::Flusher;
use crate::common::operation_error::{OperationError, OperationResult};
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::{StoredPointToValues, StoredValue};
use crate::index::field_index::on_disk_point_to_values::{OnDiskPointToValues, StoredValue};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct UniversalNumericIndexConfig {
@@ -56,7 +56,7 @@ where
in_memory_index.histogram.save(path)?;
StoredPointToValues::<T, S>::from_iter(
OnDiskPointToValues::<T, S>::build_from_iter(
fs,
path,
in_memory_index
@@ -64,6 +64,7 @@ where
.iter()
.enumerate()
.map(|(idx, values)| (idx as PointOffsetType, values.iter().map(|v| v.borrow()))),
populate,
)?;
{
@@ -142,7 +143,7 @@ where
};
let pairs = TypedStorage::open(fs, pairs_path, pairs_options, Default::default())?;
let point_to_values = StoredPointToValues::open(fs, path, populate)?;
let point_to_values = OnDiskPointToValues::open(fs, path, populate)?;
let mut deleted = deleted_points.to_owned();
let deleted_payload_mmap = StoredBitSlice::<S>::open(
@@ -6,7 +6,7 @@ use common::universal_io::{MmapFile, TypedStorage, UniversalRead};
use super::Encodable;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::{StoredPointToValues, StoredValue};
use crate::index::field_index::on_disk_point_to_values::{OnDiskPointToValues, StoredValue};
mod lifecycle;
mod read_ops;
@@ -45,7 +45,7 @@ pub(in super::super) struct Storage<
pub(super) deleted: BitVec,
// sorted pairs (id + value), sorted by value (by id if values are equal)
pub(super) pairs: TypedStorage<S, Point<T>>,
pub(in super::super) point_to_values: StoredPointToValues<T, S>,
pub(in super::super) point_to_values: OnDiskPointToValues<T, S>,
}
impl<T: Encodable + Numericable + Default + StoredValue + 'static, S: UniversalRead> Storage<T, S> {
@@ -16,7 +16,7 @@ use super::OnDiskNumericIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
impl<T: Encodable + Numericable + Default + StoredValue + 'static, S: UniversalRead>
@@ -23,8 +23,8 @@ use super::Encodable;
use super::numeric_index_read::NumericIndexRead;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::stat_tools::estimate_multi_value_selection_cardinality;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::utils::check_boundaries;
use crate::index::field_index::{CardinalityEstimation, PayloadBlockCondition, PrimaryCondition};
use crate::index::query_optimization::optimized_filter::ConditionCheckerFn;
@@ -10,7 +10,7 @@ use super::super::storage::read_only::ReadOnlyNumericIndexInner;
use super::ReadOnlyNumericIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::IndexMutability;
impl<T: Encodable + Numericable + StoredValue + Send + Sync + Default, P, S: UniversalRead>
@@ -6,7 +6,7 @@ use gridstore::Blob;
use super::Encodable;
use super::storage::read_only::ReadOnlyNumericIndexInner;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
mod lifecycle;
mod read_ops;
@@ -15,7 +15,7 @@ use super::ReadOnlyNumericIndex;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{
CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndexRead,
};
@@ -18,7 +18,7 @@ use super::super::numeric_index_read::NumericIndexRead;
use super::ReadOnlyNumericIndex;
use crate::common::utils::MultiValue;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::query_optimization::rescore_formula::value_retriever::VariableRetrieverFn;
use crate::types::{
DateTimePayloadType, FloatPayloadType, IntPayloadType, UuidIntType, UuidPayloadType,
@@ -14,7 +14,7 @@ use serde_json::Value;
use super::{Encodable, NumericIndex};
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{
CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndexRead,
};
@@ -16,7 +16,7 @@ use super::NumericIndexInner;
use crate::common::Flusher;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
impl<T: Encodable + Numericable + StoredValue + Send + Sync + Default> NumericIndexInner<T>
where
@@ -33,7 +33,7 @@ use super::immutable_numeric_index::ImmutableNumericIndex;
use super::mutable_numeric_index::MutableNumericIndex;
use super::on_disk_numeric_index::OnDiskNumericIndex;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
pub enum NumericIndexInner<T: Encodable + Numericable + StoredValue + Send + Sync + Default>
where
@@ -11,7 +11,7 @@ use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_index::immutable_numeric_index::ImmutableNumericIndex;
use crate::index::field_index::numeric_index::on_disk_numeric_index::OnDiskNumericIndex;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::IndexMutability;
impl<T: Encodable + Numericable + StoredValue + Send + Sync + Default, S: UniversalRead>
@@ -6,7 +6,7 @@ use super::super::mutable_numeric_index::read_only::ReadOnlyAppendableNumericInd
use super::super::on_disk_numeric_index::OnDiskNumericIndex;
use crate::index::field_index::numeric_index::immutable_numeric_index::ImmutableNumericIndex;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
mod lifecycle;
mod read_ops;
@@ -18,7 +18,7 @@ use super::ReadOnlyNumericIndexInner;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
use crate::types::RangeInterface;
@@ -15,7 +15,7 @@ use super::super::super::{Encodable, query};
use super::ReadOnlyNumericIndexInner;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{
CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndexRead,
};
@@ -17,7 +17,7 @@ use super::NumericIndexInner;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::histogram::Histogram;
use crate::index::field_index::numeric_point::{Numericable, Point};
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::payload_config::StorageType;
impl<T: Encodable + Numericable + StoredValue + Send + Sync + Default> NumericIndexRead<T>
@@ -24,7 +24,7 @@ use super::NumericIndexInner;
use crate::common::Flusher;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{
CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndex, PayloadFieldIndexRead,
};
@@ -16,7 +16,7 @@ use tempfile::{Builder, TempDir};
use super::*;
use crate::common::operation_error::OperationResult;
use crate::index::field_index::numeric_point::Numericable;
use crate::index::field_index::stored_point_to_values::StoredValue;
use crate::index::field_index::on_disk_point_to_values::StoredValue;
use crate::index::field_index::{
CardinalityEstimation, FieldIndexBuilderTrait, PayloadFieldIndexRead, ValueIndexer,
};
@@ -68,7 +68,7 @@ impl StoredValue for str {
/// This structure is immutable.
/// It's used in mmap field indices like `UniversalMapIndex`, `UniversalNumericIndex`, etc to store points-to-values map.
/// This structure is not generic to avoid boxing lifetimes for `&str` values.
pub struct StoredPointToValues<T: StoredValue + ?Sized, S: UniversalRead> {
pub struct OnDiskPointToValues<T: StoredValue + ?Sized, S: UniversalRead> {
file_name: PathBuf,
store: ReadOnly<S>,
header: Header,
@@ -92,15 +92,16 @@ struct Header {
points_count: u64,
}
impl<T, S> StoredPointToValues<T, S>
impl<T, S> OnDiskPointToValues<T, S>
where
T: StoredValue + ?Sized,
S: UniversalRead,
{
pub fn from_iter<'a>(
pub fn build_from_iter<'a>(
fs: &S::Fs,
path: &Path,
iter: impl Iterator<Item = (PointOffsetType, impl Iterator<Item = &'a T>)> + Clone,
populate: bool,
) -> OperationResult<Self>
where
T: 'a,
@@ -160,7 +161,7 @@ where
mmap.flush()?;
drop(mmap);
Self::open(fs, path, true)
Self::open(fs, path, populate)
}
pub fn open(fs: &S::Fs, path: &Path, populate: bool) -> OperationResult<Self> {
@@ -432,17 +433,18 @@ mod tests {
.prefix("mmap_point_to_values")
.tempdir()
.unwrap();
StoredPointToValues::<str, MmapFile>::from_iter(
OnDiskPointToValues::<str, MmapFile>::build_from_iter(
&MmapFs,
dir.path(),
values
.iter()
.enumerate()
.map(|(id, values)| (id as PointOffsetType, values.iter().map(|s| s.as_str()))),
true,
)
.unwrap();
let point_to_values =
StoredPointToValues::<str, MmapFile>::open(&MmapFs, dir.path(), false).unwrap();
OnDiskPointToValues::<str, MmapFile>::open(&MmapFs, dir.path(), false).unwrap();
for (idx, values) in values.iter().enumerate() {
let v = point_to_values
@@ -493,17 +495,18 @@ mod tests {
.prefix("mmap_point_to_values")
.tempdir()
.unwrap();
StoredPointToValues::<GeoPoint, MmapFile>::from_iter(
OnDiskPointToValues::<GeoPoint, MmapFile>::build_from_iter(
&MmapFs,
dir.path(),
values
.iter()
.enumerate()
.map(|(id, values)| (id as PointOffsetType, values.iter())),
true,
)
.unwrap();
let point_to_values =
StoredPointToValues::<GeoPoint, MmapFile>::open(&MmapFs, dir.path(), false).unwrap();
OnDiskPointToValues::<GeoPoint, MmapFile>::open(&MmapFs, dir.path(), false).unwrap();
for (idx, values) in values.iter().enumerate() {
let iter = point_to_values