Files
qdrant/lib/collection/src/optimizers_builder.rs
Luis Cossío 49dba4dd92 Disable indexing and memmap with 0 (#1824)
* feat: use 0 as disable for indexing and memmap

* amend: edit test, update doc comments, remove validation

* fix: use constant instead of literal, reword doc comments

* test: update test for loop and timeout

* fix: remove validation on OptimizerConfigDiff and fix test_validatioin.py

* fix: remove validation from build.rs, remove internal hack from  doccomments

* fix: update grpc docs

* revert max_optimization_threads validation in test

* update config comments and minor test change

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
2023-05-17 11:22:34 +02:00

168 lines
6.4 KiB
Rust

use std::path::Path;
use std::sync::Arc;
use schemars::JsonSchema;
use segment::common::cpu::get_num_cpus;
use segment::types::{HnswConfig, QuantizationConfig};
use serde::{Deserialize, Serialize};
use validator::Validate;
use crate::collection_manager::optimizers::indexing_optimizer::IndexingOptimizer;
use crate::collection_manager::optimizers::merge_optimizer::MergeOptimizer;
use crate::collection_manager::optimizers::segment_optimizer::OptimizerThresholds;
use crate::collection_manager::optimizers::vacuum_optimizer::VacuumOptimizer;
use crate::config::CollectionParams;
use crate::update_handler::Optimizer;
const DEFAULT_MAX_SEGMENT_PER_CPU_KB: usize = 200_000;
const DEFAULT_INDEXING_THRESHOLD_KB: usize = 20_000;
#[derive(Debug, Deserialize, Serialize, JsonSchema, Validate, Clone, PartialEq)]
pub struct OptimizersConfig {
/// The minimal fraction of deleted vectors in a segment, required to perform segment optimization
#[validate(range(min = 0.0, max = 1.0))]
pub deleted_threshold: f64,
/// The minimal number of vectors in a segment, required to perform segment optimization
#[validate(range(min = 100))]
pub vacuum_min_vector_number: usize,
/// Target amount of segments optimizer will try to keep.
/// Real amount of segments may vary depending on multiple parameters:
/// - Amount of stored points
/// - Current write RPS
///
/// It is recommended to select default number of segments as a factor of the number of search threads,
/// so that each segment would be handled evenly by one of the threads.
/// If `default_segment_number = 0`, will be automatically selected by the number of available CPUs.
pub default_segment_number: usize,
/// Do not create segments larger this size (in kilobytes).
/// Large segments might require disproportionately long indexation times,
/// therefore it makes sense to limit the size of segments.
///
/// If indexing speed is more important - make this parameter lower.
/// If search speed is more important - make this parameter higher.
/// Note: 1Kb = 1 vector of size 256
/// If not set, will be automatically selected considering the number of available CPUs.
#[serde(alias = "max_segment_size_kb")]
#[serde(default)]
pub max_segment_size: Option<usize>,
/// Maximum size (in kilobytes) of vectors to store in-memory per segment.
/// Segments larger than this threshold will be stored as read-only memmaped file.
///
/// Memmap storage is disabled by default, to enable it, set this threshold to a reasonable value.
///
/// To disable memmap storage, set this to `0`. Internally it will use the largest threshold possible.
///
/// Note: 1Kb = 1 vector of size 256
#[serde(alias = "memmap_threshold_kb")]
#[serde(default)]
pub memmap_threshold: Option<usize>,
/// Maximum size (in kilobytes) of vectors allowed for plain index, exceeding this threshold will enable vector indexing
///
/// Default value is 20,000, based on <https://github.com/google-research/google-research/blob/master/scann/docs/algorithms.md>.
///
/// To disable vector indexing, set to `0`.
///
/// Note: 1kB = 1 vector of size 256.
#[serde(alias = "indexing_threshold_kb")]
#[serde(default)]
pub indexing_threshold: Option<usize>,
/// Minimum interval between forced flushes.
pub flush_interval_sec: u64,
/// Maximum available threads for optimization workers
pub max_optimization_threads: usize,
}
impl OptimizersConfig {
#[cfg(test)]
pub fn fixture() -> Self {
Self {
deleted_threshold: 0.1,
vacuum_min_vector_number: 1000,
default_segment_number: 0,
max_segment_size: None,
memmap_threshold: None,
indexing_threshold: Some(100_000),
flush_interval_sec: 60,
max_optimization_threads: 0,
}
}
pub fn get_number_segments(&self) -> usize {
if self.default_segment_number == 0 {
let num_cpus = get_num_cpus();
// Do not configure less than 2 and more than 8 segments
// until it is not explicitly requested
num_cpus.clamp(2, 8)
} else {
self.default_segment_number
}
}
pub fn get_max_segment_size(&self) -> usize {
if let Some(max_segment_size) = self.max_segment_size {
max_segment_size
} else {
let num_cpus = get_num_cpus();
num_cpus.saturating_mul(DEFAULT_MAX_SEGMENT_PER_CPU_KB)
}
}
}
pub fn build_optimizers(
shard_path: &Path,
collection_params: &CollectionParams,
optimizers_config: &OptimizersConfig,
hnsw_config: &HnswConfig,
quantization_config: &Option<QuantizationConfig>,
) -> Arc<Vec<Arc<Optimizer>>> {
let segments_path = shard_path.join("segments");
let temp_segments_path = shard_path.join("temp_segments");
let indexing_threshold = match optimizers_config.indexing_threshold {
None => DEFAULT_INDEXING_THRESHOLD_KB, // default value
Some(0) => usize::MAX, // disable vector index
Some(custom) => custom,
};
let memmap_threshold = match optimizers_config.memmap_threshold {
None | Some(0) => usize::MAX, // default | disable memmap
Some(custom) => custom,
};
let threshold_config = OptimizerThresholds {
memmap_threshold,
indexing_threshold,
max_segment_size: optimizers_config.get_max_segment_size(),
};
Arc::new(vec![
Arc::new(MergeOptimizer::new(
optimizers_config.get_number_segments(),
threshold_config.clone(),
segments_path.clone(),
temp_segments_path.clone(),
collection_params.clone(),
hnsw_config.clone(),
quantization_config.clone(),
)),
Arc::new(IndexingOptimizer::new(
threshold_config.clone(),
segments_path.clone(),
temp_segments_path.clone(),
collection_params.clone(),
hnsw_config.clone(),
quantization_config.clone(),
)),
Arc::new(VacuumOptimizer::new(
optimizers_config.deleted_threshold,
optimizers_config.vacuum_min_vector_number,
threshold_config,
segments_path,
temp_segments_path,
collection_params.clone(),
hnsw_config.clone(),
quantization_config.clone(),
)),
])
}