dig_blockstore/config.rs
1//! [`BlockStoreConfig`] — paths, cache sizes, RocksDB tuning, pipeline, and pruning hooks.
2//!
3//! **Requirements trace**
4//! - [`TYP-008`](../../docs/requirements/domains/storage_types/specs/TYP-008.md) — field set, production defaults, manual [`Default`] (non-empty [`PathBuf`](std::path::PathBuf))
5//! - [`NORMATIVE` TYP-008](../../docs/requirements/domains/storage_types/NORMATIVE.md#typ-008-blockstoreconfig-struct)
6//! - [`STR-002`](../../docs/requirements/domains/crate_structure/specs/STR-002.md), [`STR-004`](../../docs/requirements/domains/crate_structure/specs/STR-004.md), [`STR-005`](../../docs/requirements/domains/crate_structure/specs/STR-005.md) (`test_config` overrides)
7//! - Shared numeric tunables: [`TYP-002`](../../docs/requirements/domains/storage_types/specs/TYP-002.md) via [`crate::constants`] (`DEFAULT_*`, [`ZSTD_COMPRESSION_LEVEL`](crate::constants::ZSTD_COMPRESSION_LEVEL))
8//!
9//! ## Defaults and `Default` impl
10//!
11//! [`Default::default`] follows TYP-008 for the **core** knobs (path, caches, RocksDB, zstd, pipeline flags,
12//! pruning flags). Numeric write-buffer / block-cache / cache capacities reuse [`crate::constants`] so
13//! TYP-002 and TYP-008 stay numerically identical ([`tests/typ_002_tests.rs`](../../tests/typ_002_tests.rs)).
14//!
15//! **Why manual `Default`:** `#[derive(Default)]` would set `path` to an empty [`PathBuf`]; the spec requires a
16//! conventional relative layout (`data/blockstore`) suitable for local dev and examples.
17//!
18//! ## Extension fields (beyond TYP-008’s core table)
19//!
20//! The crate also carries forward-looking fields wired by [`crate::store::BlockStore::open`] and future BLK/CAC work:
21//!
22//! - **`warm_cache_depth`** — how many trailing canonical heights to touch when [`warm_cache_on_open`] is true ([`CAC-006`](../../docs/requirements/domains/caching/specs/CAC-006_cache_warming_on_startup.md)).
23//! - **`write_pipeline_channel_capacity`** — bounded queue depth for [`BLK-008`](../../docs/requirements/domains/block_storage/specs/BLK-008.md).
24//! - **`readahead_size`** — sequential read hint ([`BLK-006`](../../docs/requirements/domains/block_storage/specs/BLK-006.md)).
25//!
26//! These are **not** duplicated in the short TYP-008 markdown table but are part of the public Rust API and are
27//! covered by [`tests/typ_008_tests.rs`](../../tests/typ_008_tests.rs).
28
29use std::path::PathBuf;
30
31use crate::constants::{
32 DEFAULT_BLOCK_CACHE_CAPACITY, DEFAULT_BLOCK_CACHE_SIZE, DEFAULT_HEADER_CACHE_CAPACITY,
33 DEFAULT_MAX_DECOMPRESSED_BLOCK_BYTES, DEFAULT_MAX_OPEN_FILES, DEFAULT_WRITE_BUFFER_SIZE,
34 ZSTD_COMPRESSION_LEVEL,
35};
36
37/// Configuration for opening or creating a [`crate::store::BlockStore`].
38///
39/// **Construction:** Use [`BlockStoreConfig::default`] and override fields, [`std::default::Default::default`]
40/// with struct update syntax (`BlockStoreConfig { path: my_dir, ..Default::default() }`), or [`STR-005`](../../docs/requirements/domains/crate_structure/specs/STR-005.md) `test_config` for tiny test tunables.
41///
42/// **Validation:** [`crate::store::BlockStore::open`] should eventually enforce `cache_shards` is a power of two ([`TYP-008`](../../docs/requirements/domains/storage_types/specs/TYP-008.md) implementation notes); today callers should follow that invariant.
43#[derive(Debug, Clone)]
44pub struct BlockStoreConfig {
45 // --- Storage path (TYP-008) ---
46 /// Root directory for the RocksDB database files (TYP-008 `path`).
47 pub path: PathBuf,
48
49 // --- In-memory blockstore caches (CAC-001 / CAC-002 precursors) ---
50 /// Max blocks retained in the sharded block cache.
51 pub block_cache_capacity: usize,
52
53 /// Max headers retained in the sharded header cache.
54 pub header_cache_capacity: usize,
55
56 /// Shard count for block/header caches; must be a power of two when enforced ([`TYP-008`](../../docs/requirements/domains/storage_types/specs/TYP-008.md)).
57 pub cache_shards: usize,
58
59 /// When true, [`crate::store::BlockStore::open`] preloads recent canonical blocks ([`STR-004`](../../docs/requirements/domains/crate_structure/specs/STR-004.md), [`CAC-006`](../../docs/requirements/domains/caching/specs/CAC-006_cache_warming_on_startup.md)).
60 pub warm_cache_on_open: bool,
61
62 /// Trailing heights (inclusive) to touch when warming, starting from tip ([`CAC-006`](../../docs/requirements/domains/caching/specs/CAC-006_cache_warming_on_startup.md)).
63 pub warm_cache_depth: u64,
64
65 // --- RocksDB tuning (TYP-002 / TYP-003 precursors) ---
66 /// RocksDB memtable / write buffer budget per column family (bytes).
67 pub write_buffer_size: usize,
68
69 /// Shared block cache for RocksDB (bytes).
70 pub block_cache_size: usize,
71
72 /// `max_open_files` passed to RocksDB options.
73 pub max_open_files: i32,
74
75 /// When true, enable BlobDB-style large-value handling for block bodies ([`TYP-003`](../../docs/requirements/domains/storage_types/specs/TYP-003.md)).
76 pub enable_blob_db: bool,
77
78 /// When true, store compressed block payloads ([`SER-001`](../../docs/requirements/domains/serialization/specs/SER-001.md)); tests often disable for simplicity ([`STR-005`](../../docs/requirements/domains/crate_structure/specs/STR-005.md)).
79 pub compress_blocks: bool,
80
81 /// Zstd level for block compression when `compress_blocks` is true.
82 pub compression_level: i32,
83
84 /// Whether to use a trained zstd dictionary ([`SER-005`](../../docs/requirements/domains/serialization/specs/SER-005.md)).
85 pub use_compression_dict: bool,
86
87 /// Max accepted **decompressed** byte length for a stored block body ([`SER-001`](../../docs/requirements/domains/serialization/specs/SER-001.md)).
88 pub max_decompressed_block_bytes: usize,
89
90 /// Optional zstd dictionary bytes wired at open time ([`SER-001`](../../docs/requirements/domains/serialization/specs/SER-001.md)).
91 ///
92 /// **Production:** leave `None` — [`crate::store::BlockStore::open`] loads [`crate::constants::META_ZSTD_DICT`]
93 /// from `CF_METADATA` when [`Self::use_compression_dict`] is true ([`SER-005`](../../docs/requirements/domains/serialization/specs/SER-005.md)).
94 /// **Tests:** set to a trained dictionary to exercise dictionary compress/decompress without persisting metadata first.
95 pub zstd_dictionary_override: Option<Vec<u8>>,
96
97 // --- Write pipeline (BLK-008 precursor) ---
98 /// Max blocks batched before a pipeline flush.
99 pub write_pipeline_batch_size: usize,
100
101 /// Max wait before flushing a partial pipeline batch (milliseconds).
102 pub write_pipeline_flush_ms: u64,
103
104 /// Bounded async channel capacity feeding the write pipeline ([`BLK-008`](../../docs/requirements/domains/block_storage/specs/BLK-008.md)).
105 pub write_pipeline_channel_capacity: usize,
106
107 /// When true, sync the WAL after each write (durability vs throughput).
108 pub sync_writes: bool,
109
110 /// Hint for sequential readahead ([`BLK-006`](../../docs/requirements/domains/block_storage/specs/BLK-006.md)).
111 pub readahead_size: usize,
112
113 // --- Caching (CAC-004 / CAC-005) ---
114 /// Max entries in the canonical height→hash `BTreeMap` cache ([`CAC-004`](../../docs/requirements/domains/caching/specs/CAC-004_canonical_height_index_cache.md)).
115 ///
116 /// When the cache exceeds this size, the **lowest** height entry is evicted.
117 /// Set to `0` to disable the height cache entirely.
118 pub canonical_height_cache_capacity: usize,
119
120 /// Max entries in the hash→height reverse lookup cache ([`CAC-005`](../../docs/requirements/domains/caching/specs/CAC-005_hash_to_height_reverse_cache.md)).
121 ///
122 /// Uses the same sharded LRU infrastructure as block/header caches.
123 /// Higher capacity is practical because each entry is only 40 bytes (32 hash + 8 height).
124 pub hash_to_height_cache_capacity: usize,
125
126 // --- Pruning (PRN-003 / PRN-004 precursors) ---
127 /// Register compaction-time pruning when true ([`PRN-003`](../../docs/requirements/domains/pruning/specs/PRN-003_compaction_filter.md)).
128 pub enable_compaction_pruning: bool,
129
130 /// Optional floor height below which compaction may drop data; `None` disables.
131 pub min_retained_height: Option<u64>,
132}
133
134impl Default for BlockStoreConfig {
135 fn default() -> Self {
136 Self {
137 path: PathBuf::from("data/blockstore"),
138 block_cache_capacity: DEFAULT_BLOCK_CACHE_CAPACITY,
139 header_cache_capacity: DEFAULT_HEADER_CACHE_CAPACITY,
140 cache_shards: 16,
141 warm_cache_on_open: true,
142 warm_cache_depth: 64,
143 write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
144 block_cache_size: DEFAULT_BLOCK_CACHE_SIZE,
145 max_open_files: DEFAULT_MAX_OPEN_FILES,
146 enable_blob_db: true,
147 compress_blocks: true,
148 compression_level: ZSTD_COMPRESSION_LEVEL,
149 use_compression_dict: true,
150 max_decompressed_block_bytes: DEFAULT_MAX_DECOMPRESSED_BLOCK_BYTES,
151 zstd_dictionary_override: None,
152 write_pipeline_batch_size: 64,
153 write_pipeline_flush_ms: 100,
154 write_pipeline_channel_capacity: 256,
155 sync_writes: false,
156 readahead_size: 2_097_152,
157 canonical_height_cache_capacity: 10_000,
158 hash_to_height_cache_capacity: 10_000,
159 enable_compaction_pruning: false,
160 min_retained_height: None,
161 }
162 }
163}