# TYP-008: BlockStoreConfig Struct

| Link | Path |
|------|------|
| **Normative** | [NORMATIVE.md](../NORMATIVE.md) |
| **Verification** | [VERIFICATION.md](../VERIFICATION.md) |
| **Tracking** | [TRACKING.yaml](../TRACKING.yaml) |
| **Spec** | [SPEC.md](../../../../resources/SPEC.md) Section 3.6 |

---

## Summary

BlockStoreConfig contains all tunable parameters for opening and operating a BlockStore, with sensible defaults for production use.

---

## Specification

```rust
use std::path::PathBuf;

/// Configuration for opening a BlockStore.
///
/// All fields have sensible defaults for production use.
/// Use `BlockStoreConfig::default()` and override specific fields as needed.
#[derive(Debug, Clone)]
pub struct BlockStoreConfig {
    // --- Storage path ---

    /// Path to the RocksDB database directory.
    pub path: PathBuf,

    // --- Cache settings ---

    /// Maximum number of blocks to keep in the in-memory block cache.
    /// Default: 1000
    pub block_cache_capacity: usize,

    /// Maximum number of headers to keep in the in-memory header cache.
    /// Default: 2000
    pub header_cache_capacity: usize,

    /// Number of shards for the block and header caches.
    /// Must be a power of two. Default: 16
    pub cache_shards: usize,

    /// Whether to warm caches with recent blocks on open.
    /// Default: true
    pub warm_cache_on_open: bool,

    /// Heights of canonical chain to warm when `warm_cache_on_open` is true (CAC-006 precursor).
    /// Default: 64
    pub warm_cache_depth: u64,

    // --- RocksDB tuning ---

    /// RocksDB write buffer size in bytes.
    /// Default: 67_108_864 (64 MB)
    pub write_buffer_size: usize,

    /// RocksDB block cache size in bytes (shared across column families).
    /// Default: 134_217_728 (128 MB)
    pub block_cache_size: usize,

    /// Maximum number of open file descriptors for RocksDB.
    /// Default: 1000
    pub max_open_files: i32,

    // --- BlobDB and compression ---

    /// Whether to enable BlobDB for CF_BLOCKS (large value separation).
    /// Default: true
    pub enable_blob_db: bool,

    /// Whether to compress blocks with zstd before storage.
    /// Default: true
    pub compress_blocks: bool,

    /// Zstd compression level (1-22, higher = better compression, slower).
    /// Default: 3
    pub compression_level: i32,

    /// Whether to use a trained zstd dictionary for compression.
    /// Default: true
    pub use_compression_dict: bool,

    // --- Write pipeline ---

    /// Maximum number of blocks to batch in the write pipeline before flushing.
    /// Default: 64
    pub write_pipeline_batch_size: usize,

    /// Maximum time in milliseconds to wait before flushing a partial batch.
    /// Default: 100
    pub write_pipeline_flush_ms: u64,

    /// Bounded async channel capacity for the write pipeline (BLK-008 precursor).
    /// Default: 256
    pub write_pipeline_channel_capacity: usize,

    /// Whether to use synchronous writes (fsync after each write).
    /// Default: false (async writes for performance)
    pub sync_writes: bool,

    /// Sequential readahead hint in bytes (BLK-006 precursor).
    /// Default: 2_097_152 (2 MiB)
    pub readahead_size: usize,

    // --- Pruning ---

    /// Whether to enable the compaction filter for automatic pruning.
    /// Default: false
    pub enable_compaction_pruning: bool,

    /// Minimum block height to retain. Blocks below this height may be pruned.
    /// None means no pruning. Default: None
    pub min_retained_height: Option<u64>,
}

impl Default for BlockStoreConfig {
    fn default() -> Self {
        Self {
            path: PathBuf::from("data/blockstore"),
            block_cache_capacity: 1000,
            header_cache_capacity: 2000,
            cache_shards: 16,
            warm_cache_on_open: true,
            warm_cache_depth: 64,
            write_buffer_size: 64 * 1024 * 1024,     // 64 MB
            block_cache_size: 128 * 1024 * 1024,      // 128 MB
            max_open_files: 1000,
            enable_blob_db: true,
            compress_blocks: true,
            compression_level: 3,
            use_compression_dict: true,
            write_pipeline_batch_size: 64,
            write_pipeline_flush_ms: 100,
            write_pipeline_channel_capacity: 256,
            sync_writes: false,
            readahead_size: 2_097_152,
            enable_compaction_pruning: false,
            min_retained_height: None,
        }
    }
}
```

### Field Summary

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `path` | `PathBuf` | `"data/blockstore"` | RocksDB directory path |
| `block_cache_capacity` | `usize` | `1000` | Max cached blocks |
| `header_cache_capacity` | `usize` | `2000` | Max cached headers |
| `cache_shards` | `usize` | `16` | Cache shard count |
| `warm_cache_on_open` | `bool` | `true` | Warm caches on open |
| `warm_cache_depth` | `u64` | `64` | Canonical heights to warm (CAC-006) |
| `write_buffer_size` | `usize` | `67_108_864` | RocksDB write buffer |
| `block_cache_size` | `usize` | `134_217_728` | RocksDB block cache |
| `max_open_files` | `i32` | `1000` | Max open FDs |
| `enable_blob_db` | `bool` | `true` | Enable BlobDB |
| `compress_blocks` | `bool` | `true` | Zstd compression |
| `compression_level` | `i32` | `3` | Zstd level |
| `use_compression_dict` | `bool` | `true` | Trained dict |
| `write_pipeline_batch_size` | `usize` | `64` | Pipeline batch size |
| `write_pipeline_flush_ms` | `u64` | `100` | Pipeline flush timeout |
| `write_pipeline_channel_capacity` | `usize` | `256` | Pipeline channel depth (BLK-008) |
| `sync_writes` | `bool` | `false` | Sync writes |
| `readahead_size` | `usize` | `2_097_152` | Sequential readahead (BLK-006) |
| `enable_compaction_pruning` | `bool` | `false` | Compaction pruning |
| `min_retained_height` | `Option<u64>` | `None` | Pruning floor |

---

## Acceptance Criteria

- [x] `BlockStoreConfig` has all fields in the specification above (TYP-008 core + CAC/BLK precursors)
- [x] `BlockStoreConfig` derives `Debug`, `Clone`
- [x] `Default::default()` sets `path` to `"data/blockstore"`
- [x] `Default::default()` sets `block_cache_capacity` to 1000
- [x] `Default::default()` sets `header_cache_capacity` to 2000
- [x] `Default::default()` sets `cache_shards` to 16
- [x] `Default::default()` sets `warm_cache_on_open` to true
- [x] `Default::default()` sets `warm_cache_depth` to 64
- [x] `Default::default()` sets `write_buffer_size` to 67_108_864
- [x] `Default::default()` sets `block_cache_size` to 134_217_728
- [x] `Default::default()` sets `max_open_files` to 1000
- [x] `Default::default()` sets `enable_blob_db` to true
- [x] `Default::default()` sets `compress_blocks` to true
- [x] `Default::default()` sets `compression_level` to 3
- [x] `Default::default()` sets `use_compression_dict` to true
- [x] `Default::default()` sets `write_pipeline_batch_size` to 64
- [x] `Default::default()` sets `write_pipeline_flush_ms` to 100
- [x] `Default::default()` sets `write_pipeline_channel_capacity` to 256
- [x] `Default::default()` sets `sync_writes` to false
- [x] `Default::default()` sets `readahead_size` to 2_097_152
- [x] `Default::default()` sets `enable_compaction_pruning` to false
- [x] `Default::default()` sets `min_retained_height` to None

---

## Implementation Notes

- Define in `src/config.rs`.
- Manual `Default` impl is used instead of `#[derive(Default)]` because `PathBuf::default()` produces an empty path, but we want `"data/blockstore"`.
- `cache_shards` should be validated as a power of two during `BlockStore::open()`.
- The test config (STR-005) overrides many of these defaults with smaller values.

---

## Test Plan

| Test Name | Type | Description |
|-----------|------|-------------|
| `test_default_path` | unit | Verify default path is `"data/blockstore"` |
| `test_default_block_cache_capacity` | unit | Verify default is 1000 |
| `test_default_header_cache_capacity` | unit | Verify default is 2000 |
| `test_default_cache_shards` | unit | Verify default is 16 |
| `test_default_warm_cache_on_open` | unit | Verify default is true |
| `test_default_write_buffer_size` | unit | Verify default is 64 MB |
| `test_default_block_cache_size` | unit | Verify default is 128 MB |
| `test_default_max_open_files` | unit | Verify default is 1000 |
| `test_default_enable_blob_db` | unit | Verify default is true |
| `test_default_compress_blocks` | unit | Verify default is true |
| `test_default_compression_level` | unit | Verify default is 3 |
| `test_default_use_compression_dict` | unit | Verify default is true |
| `test_default_write_pipeline_batch_size` | unit | Verify default is 64 |
| `test_default_write_pipeline_flush_ms` | unit | Verify default is 100 |
| `test_default_sync_writes` | unit | Verify default is false |
| `test_default_enable_compaction_pruning` | unit | Verify default is false |
| `test_default_min_retained_height` | unit | Verify default is None |
| `test_override_fields` | unit | Override path and cache sizes, verify they take effect |
| `test_clone` | unit | Clone config, verify equality of all fields |
| `test_default_extension_fields` | unit | `warm_cache_depth`, `write_pipeline_channel_capacity`, `readahead_size` defaults |
| `test_debug_format` | unit | `format!("{:?}")` does not panic; contains expected field names |

---

## Expected Test Files

- `tests/typ_008_tests.rs`
