tidaldb/docs/planning/milestone-8/phase-1/task-05-node-config.md
jordan f4cfd6c81f feat: complete M8 replication primitives + forage enhancements + docs
Milestone 8 (phases 1-4):
- Shard-aware WAL segment naming, BatchHeader v2, ShardRouter
- Transport trait, InProcessTransport, WalShipper, FollowerDb
- HLC, PNCounter, LWWRegister, CrdtSignalState, ReconciliationEngine
- Session replication bridge with SeqNo/HWM, idempotency store

Forage application:
- Multi-source discovery engine with MAB exploration
- Embedding-based label system, server handlers, UI refresh

Other:
- QUICKSTART.md, README.md, milestone-8 planning docs
- Hard negative union semantics, RLHF export enhancements
- Recovery benchmark and visibility test expansions
- Split 8 oversized source files per CODING_GUIDELINES §9

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 13:17:19 -07:00

3.5 KiB

Task 05: NodeConfig

Delivers

Add NodeConfig struct to tidal/src/db/config.rs extending Config with cluster fields (role, shard_id, region_id, peer_shards). Defaults produce a single-node config with zero changes to existing embedders.

Complexity: S

Dependencies

  • Task 01 (ShardId, RegionId, NodeRole types)
  • Task 02 (ShardRouter)

Technical Design

// tidal/src/db/config.rs

/// Cluster configuration for distributed tidalDB deployments.
///
/// Defaults produce a single-node configuration identical to M0-M7 behavior.
/// Embedded deployments that do not set any cluster fields get single-node.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NodeConfig {
    /// The role of this node.
    ///
    /// Default: `NodeRole::Single` (standalone, accepts reads and writes).
    pub role: NodeRole,

    /// This node's shard identity.
    ///
    /// Default: `ShardId(0)` (single-node shard).
    pub shard_id: ShardId,

    /// This node's region identity.
    ///
    /// Default: `RegionId(0)` (single-node region).
    pub region_id: RegionId,

    /// Shards this node is aware of (including itself).
    ///
    /// Empty for single-node deployments.
    pub peer_shards: Vec<ShardId>,

    /// Routing strategy for entity-to-shard assignment.
    ///
    /// Default: `ShardRouter::single()` (all entities -> ShardId(0)).
    #[serde(skip)]
    pub router: ShardRouter,
}

impl Default for NodeConfig {
    fn default() -> Self {
        Self {
            role: NodeRole::Single,
            shard_id: ShardId::SINGLE,
            region_id: RegionId::SINGLE,
            peer_shards: vec![],
            router: ShardRouter::single(),
        }
    }
}

impl NodeConfig {
    /// Returns true if this is a standalone single-node deployment.
    pub fn is_single_node(&self) -> bool {
        self.role == NodeRole::Single
    }

    /// Returns true if this node accepts writes.
    pub fn accepts_writes(&self) -> bool {
        matches!(self.role, NodeRole::Single | NodeRole::Leader)
    }
}

Integration into Config

// tidal/src/db/config.rs -- extend existing Config struct

pub struct Config {
    // ... existing fields ...

    /// Cluster configuration. Default: single-node.
    pub cluster: NodeConfig,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            // ... existing defaults ...
            cluster: NodeConfig::default(),
        }
    }
}

Builder integration

// TidalDb::builder() -- add optional cluster config method

impl TidalDbBuilder {
    /// Configure this instance for distributed deployment.
    ///
    /// Not required for single-node embedded use.
    pub fn with_cluster(mut self, config: NodeConfig) -> Self {
        self.config.cluster = config;
        self
    }
}

Acceptance Criteria

  • NodeConfig::default() produces role=Single, shard_id=ShardId(0), region_id=RegionId(0), peer_shards=[], router=ShardRouter::single()
  • NodeConfig::is_single_node() returns true for Single, false for Leader/Follower
  • NodeConfig::accepts_writes() returns true for Single and Leader, false for Follower
  • Config gains a cluster: NodeConfig field with default NodeConfig::default()
  • All existing tests that construct Config or use TidalDb::builder() pass unchanged (cluster field defaults to single-node)
  • TidalDbBuilder::with_cluster(config) sets the cluster config
  • cargo clippy -D warnings and cargo fmt pass