Files
qdrant/lib/segment/src/common/memory_usage.rs
Andrey Vasnetsov 5a899b74de deep memory reporting (#8606)
* Add mincore-based memory stats to MmapFile

Add `resident_bytes()`, `disk_bytes()`, and `probe_memory_stats()` methods
to `MmapFile` for measuring page cache residency via `mincore(2)`. This is
the foundation for per-collection memory usage reporting.

Also extract `page_size()` as a public function in `mmap::advice`, replacing
the internal `PAGE_SIZE_MASK` with a direct page size cache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] introduce trait for reporting memory usage per component

* [AI] memory reporter implementation for vector storage

* [AI] implement MemoryReporter for QuantizedVectors

* [AI] implement MemoryReporter for VectorIndexEnum

* Implement MemoryReporter for IdTrackerEnum with RAM estimation

Add ram_usage_bytes() to all ID tracker types and their data structures:
- PointMappings, CompressedPointMappings, CompressedVersions,
  CompressedInternalToExternal, CompressedExternalToInternal
- MutableIdTracker, ImmutableIdTracker, InMemoryIdTracker

All ID trackers load their data into RAM (none use mmap for working data).
Files are reported as OnDisk (persistence only), actual RAM footprint
is reported via extra_ram_bytes. Uses struct destructuring to ensure
new fields trigger compile errors if not accounted for.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] implement MemoryReporter for PayloadStorageEnum and adjust FileStorageIntent

* [AI] implement MemoryReporter for PayloadStorageEnum and adjust FileStorageIntent

* [AI] implement MemoryReporter for payload indexes: in-ram structures memory consumtion computation + caching

* [AI] implement MemoryReporter for payload indexes: in-ram structures memory consumtion computation + caching

* [AI] segment-level memory usage report

* [AI] Block 3: Aggregation Layer and Data Model + internal api for remote shard

* [AI] REST API handler

* fmt

* [AI] clippy fixes

* [AI] macos fix + proxy segment fix

* [AI] make text index estimation a bit more correct

* fix is_on_disk reporting for dense_vector_storage

* fix after rebase

* [AI] deep account for quantized vectors RAM usage + unify chunk size + shring volatile storage after load

* remove debug log

* cache in test

* make manual test easier to run

* rollback chunk size diff, but keep it for test only

* review fixes

* Use exhaustive match

* Use div_ceil on bits everywhere

It does not seem to be strictly necessary because the number of bits
should already be a multiple of the used container size bytes. Still
it's good practice to be careful with this calculation.

* Improve heap size bytes for encoded product quantization vectors

* Include vector stats for binary quantized vectors

* In volatile chunked vectors, include heap allocated vector

* Include rest of heap allocated structures for mutable map index

* In mutable geo index, the hash map is also heap allocated

* Update tests/manual/test_memory_reporting.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: timvisee <tim@visee.me>
Co-authored-by: Tim Visée <tim+github@visee.me>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-14 12:37:31 +02:00

94 lines
2.9 KiB
Rust

use std::path::PathBuf;
/// Describes how a component expects its files to be used.
///
/// Used to compute `expected_cache_bytes` and label components for users.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileStorageIntent {
/// Data may or may not be in RAM, residency managed by OS page cache.
Cached,
/// Data is on disk, not expected to be in RAM.
OnDisk,
}
/// A single file with its storage intent.
#[derive(Debug, Clone)]
pub struct ComponentFileEntry {
pub path: PathBuf,
pub intent: FileStorageIntent,
}
/// Memory usage reported by a single component.
///
/// Components return file entries (with intent metadata) and optionally
/// an estimate of additional heap RAM not backed by files.
#[derive(Debug, Clone)]
pub struct ComponentMemoryUsage {
/// Files owned by this component with their storage intent.
pub files: Vec<ComponentFileEntry>,
/// Additional RAM not backed by files (e.g., in-memory data structures).
pub extra_ram_bytes: Option<u64>,
}
impl ComponentMemoryUsage {
/// Convenience: empty report (no files, no extra RAM).
pub fn empty() -> Self {
Self {
files: Vec::new(),
extra_ram_bytes: None,
}
}
/// Convenience: report only non-file RAM usage.
pub fn ram_only(bytes: u64) -> Self {
Self {
files: Vec::new(),
extra_ram_bytes: Some(bytes),
}
}
/// Convenience: report files with a uniform intent and no extra RAM.
pub fn from_files(paths: Vec<PathBuf>, intent: FileStorageIntent) -> Self {
Self {
files: paths
.into_iter()
.map(|path| ComponentFileEntry { path, intent })
.collect(),
extra_ram_bytes: None,
}
}
/// Report files with a uniform intent plus additional non-file RAM.
pub fn from_files_and_ram(
paths: Vec<PathBuf>,
intent: FileStorageIntent,
extra_ram_bytes: u64,
) -> Self {
Self {
files: paths
.into_iter()
.map(|path| ComponentFileEntry { path, intent })
.collect(),
extra_ram_bytes: Some(extra_ram_bytes),
}
}
/// Merge another report into this one (concatenate files, sum RAM).
pub fn merge(&mut self, other: &ComponentMemoryUsage) {
self.files.extend(other.files.iter().cloned());
match (self.extra_ram_bytes, other.extra_ram_bytes) {
(Some(a), Some(b)) => self.extra_ram_bytes = Some(a + b),
(None, Some(b)) => self.extra_ram_bytes = Some(b),
(_, None) => {}
}
}
}
/// Trait for components to report their memory footprint.
///
/// Implementations should be cheap (no I/O). The actual `mincore` measurement
/// happens in the aggregation layer after file entries are collected.
pub trait MemoryReporter {
fn memory_usage(&self) -> ComponentMemoryUsage;
}