Skip to main content

iqdb_index/
stats.rs

1//! Index introspection.
2//!
3//! [`IndexStats`] is a runtime snapshot every implementer returns from
4//! [`crate::IndexCore::stats`]. It carries the counters the engine and the
5//! operator need to reason about an index without reaching into its
6//! internals — how many vectors it holds, how much memory it occupies, what
7//! type of index it is, and an open-ended `extra` map for index-specific
8//! detail (tombstone counts, graph layer sizes, training state, …).
9
10use std::collections::HashMap;
11
12/// A runtime snapshot of an index's state.
13///
14/// Returned by [`crate::IndexCore::stats`]. The first four fields are the
15/// shared shape; `extra` is where index-specific counters live (for example,
16/// a tombstone count, or an HNSW layer histogram) without cluttering the
17/// trait. Construct one with the public fields directly, or start from
18/// [`IndexStats::default`] and override only what you have.
19///
20/// `disk_bytes` is [`Option`] because purely in-memory indexes have nothing
21/// on disk — they report [`None`], not zero. An index that does spill to
22/// disk reports the on-disk footprint in bytes.
23///
24/// `extra` is [`Option`] (not a bare [`HashMap`]) so an implementer with no
25/// per-kind counters reports [`None`] without allocating an empty
26/// [`HashMap`] on every `stats()` call. The default value is [`None`]. A
27/// typical dashboard reads `n_vectors`, `memory_bytes`, and `index_type`
28/// without touching `extra`; the occasional inspector that needs the
29/// per-kind detail unwraps the `Option`.
30///
31/// # Examples
32///
33/// ```
34/// use iqdb_index::IndexStats;
35///
36/// let stats = IndexStats {
37///     n_vectors: 42,
38///     memory_bytes: 4_096,
39///     index_type: "flat",
40///     ..IndexStats::default()
41/// };
42///
43/// assert_eq!(stats.n_vectors, 42);
44/// assert_eq!(stats.memory_bytes, 4_096);
45/// assert_eq!(stats.disk_bytes, None);
46/// assert_eq!(stats.index_type, "flat");
47/// assert!(stats.extra.is_none());
48/// ```
49#[derive(Debug, Clone, Default, PartialEq, Eq)]
50pub struct IndexStats {
51    /// The number of vectors currently searchable in the index.
52    ///
53    /// Excludes tombstoned entries: a vector that has been
54    /// [`crate::IndexCore::delete`]d is not counted here even if the
55    /// implementation has not yet reclaimed its storage.
56    pub n_vectors: usize,
57    /// Approximate resident-memory footprint of the index, in bytes.
58    ///
59    /// A best-effort number for the in-memory state of the index. It is
60    /// suitable for capacity dashboards, not for accounting.
61    pub memory_bytes: usize,
62    /// On-disk footprint of the index in bytes, when the index persists to
63    /// disk; [`None`] for purely in-memory indexes.
64    pub disk_bytes: Option<usize>,
65    /// A short, stable identifier for the index implementation (for
66    /// example, `"flat"`, `"hnsw"`, `"ivf"`). Used in logs, metrics, and
67    /// diagnostics.
68    pub index_type: &'static str,
69    /// Index-specific counters that do not fit the shared shape.
70    ///
71    /// Keys and values are both [`String`]s so this map can carry any
72    /// implementer's bookkeeping (tombstone counts, build progress, graph
73    /// statistics) without changing the trait surface. Wrapped in
74    /// [`Option`] so implementers with no per-kind counters do not
75    /// allocate an empty map on every `stats()` call.
76    pub extra: Option<HashMap<String, String>>,
77}