llkv_column_map/store/
write_hints.rs

1use super::config::ColumnStoreConfig;
2use super::constants::TARGET_CHUNK_BYTES;
3use llkv_types::ids::RowId;
4
5/// Heuristics that guide callers when sizing write batches for the column store.
6///
7/// The values are derived from the store's ingest configuration so higher layers can
8/// adapt without duplicating storage-level constants. Callers should treat these
9/// numbers as soft targets: exceeding the maximum batch rows will be clamped, but
10/// smaller batches are always accepted.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct ColumnStoreWriteHints {
13    /// Target chunk size used when splitting incoming arrays.
14    pub target_chunk_bytes: usize,
15    /// Preferred number of rows to buffer per insert before flushing.
16    pub recommended_insert_batch_rows: usize,
17    /// Hard ceiling for literal INSERT batches before storage splits them eagerly.
18    pub max_insert_batch_rows: usize,
19    /// Fallback slice size for exotic variable-width arrays lacking offset metadata.
20    pub varwidth_fallback_rows_per_slice: usize,
21}
22
23impl ColumnStoreWriteHints {
24    pub(crate) fn from_config(cfg: &ColumnStoreConfig) -> Self {
25        let row_id_width = std::mem::size_of::<RowId>().max(1);
26        let recommended_rows = (TARGET_CHUNK_BYTES / row_id_width).max(1);
27        let max_rows = recommended_rows.saturating_mul(4).max(recommended_rows);
28        Self {
29            target_chunk_bytes: TARGET_CHUNK_BYTES,
30            recommended_insert_batch_rows: recommended_rows,
31            max_insert_batch_rows: max_rows,
32            varwidth_fallback_rows_per_slice: cfg.varwidth_fallback_rows_per_slice,
33        }
34    }
35
36    /// Clamp a requested batch size to the store's supported envelope.
37    pub fn clamp_insert_batch_rows(&self, requested_rows: usize) -> usize {
38        match requested_rows {
39            0 => 0,
40            _ => requested_rows.min(self.max_insert_batch_rows),
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn from_config_uses_target_chunk_bytes() {
51        let cfg = ColumnStoreConfig {
52            varwidth_fallback_rows_per_slice: 2048,
53        };
54        let hints = ColumnStoreWriteHints::from_config(&cfg);
55        let expected_rows = TARGET_CHUNK_BYTES / std::mem::size_of::<RowId>();
56        assert_eq!(hints.target_chunk_bytes, TARGET_CHUNK_BYTES);
57        assert_eq!(hints.varwidth_fallback_rows_per_slice, 2048);
58        assert_eq!(hints.recommended_insert_batch_rows, expected_rows);
59        assert!(hints.max_insert_batch_rows >= hints.recommended_insert_batch_rows);
60    }
61
62    #[test]
63    fn clamp_insert_batch_rows_caps_large_values() {
64        let cfg = ColumnStoreConfig {
65            varwidth_fallback_rows_per_slice: 512,
66        };
67        let hints = ColumnStoreWriteHints::from_config(&cfg);
68        assert_eq!(hints.clamp_insert_batch_rows(0), 0);
69        let max = hints.max_insert_batch_rows;
70        assert_eq!(hints.clamp_insert_batch_rows(max * 10), max);
71        assert_eq!(hints.clamp_insert_batch_rows(max - 1), max - 1);
72    }
73}