Skip to content

feat #167: workspace structure - #175

Merged
JoshuaChi merged 26 commits into
developfrom
feature/167_workspace
Nov 5, 2025
Merged

JoshuaChi merged 26 commits into
developfrom
feature/167_workspace

Conversation

@JoshuaChi

@JoshuaChi JoshuaChi commented Nov 4, 2025 •

Copy link
Copy Markdown
Contributor

Type

  • New feature
  • Bug Fix

Description

Workspace structure refactor

Related Issues

Checklist

  • The code has been tested locally (unit test or integration test)
  • Squash down commits to one or two logical commits which clearly describe the work you've done.

Summary by CodeRabbit

  • New Features

    • Added comprehensive client library with key-value and cluster management operations
    • Introduced new build targets for testing, documentation, and code quality checks
    • Added support for configurable snapshot directory paths and enhanced build optimization profiles
  • Improvements

    • Restructured project into modular workspace crates for better maintainability
    • Simplified error handling with unified error codes across client operations
    • Enhanced CI/CD pipeline with improved Rust toolchain management and incremental build support
    • Expanded public API surface for internal components to support broader integration scenarios
  • Documentation

    • Added architecture documentation module with detailed design patterns and snapshots guide
    • Updated examples to reflect new module organization

…2/ migrate test-utils into d-engine-test-utils, waiting for fix all compile errors
…ready. And make d-engine-core/test_utils as feature.
… creating integration tests folder inside runtime
… creating integration tests folder inside runtime
@coderabbitai

coderabbitai Bot commented Nov 4, 2025 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request restructures the project from a single crate to a multi-crate Rust workspace with separated proto, client, core, and server implementations. It reorganizes Cargo manifests, proto definitions, updates CI toolchain and caching, expands the Makefile, promotes numerous internal APIs to public, refactors error handling with unified error codes, and establishes new test infrastructure.

Changes

Cohort / File(s) Summary
Workspace & Manifest Structure
Cargo.toml, d-engine-proto/Cargo.toml, d-engine-client/Cargo.toml, d-engine-core/Cargo.toml, d-engine-docs/Cargo.toml, d-engine-server/Cargo.toml, benches/d-engine-bench/Cargo.toml
Converts root Cargo.toml from single-package to workspace with five member crates; centralizes dependencies via [workspace.dependencies]; adds workspace-level profiles for incremental builds and optimization tuning; updates bench dependency from d-engine to d-engine-client.
CI/CD Pipeline
.github/workflows/ci.yml
Replaces cargo cache with explicit Rust toolchain installation (dtolnay/rust-toolchain@master 1.88.0 with llvm-tools-preview) and separate rust-cache step; adds RUST_TOOLCHAIN environment variable; consolidates tool installation; extends coverage ignore patterns to include d-engine-core/src/event.rs.
Build System
Makefile, clippy.toml
Expands Makefile from minimal release script to comprehensive workspace build system with 40+ targets for formatting, linting, testing, docs, and release workflows; adds clippy.toml with linting thresholds and configuration.
Proto Reorganization
d-engine-proto/proto/\*, d-engine-proto/build.rs
Restructures proto files from flat /proto/ to /proto/server/ and /proto/client/ directories; updates package namespaces from raft.* to d_engine.*; adds NodeRole enum to common.proto.
Client Implementation
d-engine-client/src/\*, d-engine-client/Cargo.toml, benches/d-engine-bench/src/main.rs, README.md
Creates new d-engine-client crate with public Client API, KvClient, ClusterClient using proto types from d-engine-proto; implements connection pooling, error handling, mock RPC services; updates example code import paths.
Error Handling Refactor
d-engine-client/src/error.rs
Replaces per-variant u32 codes with unified ErrorCode enum; removes intermediate error-type enums (NetworkErrorType, ProtocolErrorType, etc.); adds leader_hint field to Network variant; updates error conversions for tonic transport and status types.
Core API Public Surface
d-engine-core/src/\{lib.rs, raft.rs, raft_context.rs, election/\*, commit_handler/\*, replication/\*, state_machine_handler/\*, network/\*, membership.rs, event.rs, raft_role/\*\}
Promotes numerous pub(crate) items to pub; expands visibility of Raft, RaftContext, CommitHandler, ElectionHandler, Transport, MaybeCloneOneshot, and role-state structs/methods; updates test-config gates from #[cfg(test)] to #[cfg(any(test, feature = "test-utils"))].
Storage & Configuration
d-engine-core/src/storage/\*, d-engine-core/src/config/raft.rs
Introduces new RaftLog trait with async read/write/durability interface; adds snapshots_dir_prefix field to SnapshotConfig; updates snapshot path handling in state machine; reorganizes storage modules.
Test Infrastructure
d-engine-core/src/test_utils/\*, d-engine-client/src/mock_\*.rs
Expands test utilities with EntryBuilder, MockTypeConfig, MockRaftBuilder; creates comprehensive test suites (common_test, entry_builder_test); implements MockRpcService and MockNode for in-process RPC testing; promotes test helpers to public API.
Proto & Type Imports
d-engine-core/src/\*, d-engine-client/src/\*, config/base/raft.toml
Systematically updates imports from crate::proto::\* to d_engine_proto::\* across all modules; replaces role constants (LEADER, FOLLOWER, etc.) with NodeRole enum variants; updates cluster health-check service name.
Documentation
d-engine-docs/\*, d-engine-docs/src/\*
Creates d-engine-docs crate with architecture documentation, formatting fixes, and updated code examples; adds lib.rs with crate-level docs and module organization.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Code
    participant CB as ClientBuilder
    participant CP as ConnectionPool
    participant KvC as KvClient
    participant RPC as gRPC Service

    Client->>CB: builder(endpoints)
    CB->>CB: configure (timeout, id)
    CB->>CP: build pool
    CB->>KvC: create with pool
    CB-->>Client: Client { kv, cluster, ... }
    
    Note over Client: Later: read/write operation
    Client->>KvC: put(key, value)
    KvC->>CP: get_connection()
    CP->>RPC: RaftClientService.handle_client_write
    RPC-->>CP: ClientResponse
    CP-->>KvC: response
    KvC->>KvC: validate_error via ClientResponseExt
    KvC-->>Client: Result<bool>
Loading
sequenceDiagram
    participant Test as Test Code
    participant Mock as MockRpcService
    participant Node as MockNode
    participant Server as In-Process gRPC Server

    Test->>Mock: new() / with_metadata_response
    Test->>Node: mock_listener(mock_service)
    Node->>Node: bind TCP 127.0.0.1:0
    Node->>Node: health status updates
    Node->>Server: spawn tonic server + services
    Node-->>Test: (port, socket_addr)
    
    Note over Test: Test creates channel to localhost:port
    Test->>Test: mock_channel_with_port(port)
    Test->>Server: gRPC call (read/write/discover)
    Server->>Mock: route to RaftClientService/ClusterManagementService
    Mock-->>Server: return configured response or error
    Server-->>Test: gRPC response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Proto reorganization & code generation: Verify all proto files are correctly placed in server/client subdirectories and build.rs references are complete; check package name updates propagate correctly across all generated types.
  • ClientApiError refactoring: Review the removal of per-variant error-type enums and ensure all error conversions (Fromtonic::transport::Error, From, From) handle all cases correctly; validate error codes are properly assigned.
  • Workspace dependency resolution: Confirm all crates correctly reference [workspace = true] dependencies and that feature flags propagate correctly across workspace members.
  • Public API surface changes in d-engine-core: Review each visibility promotion (pub(crate) → pub) to ensure no unintended API stability commitments; verify test-utils feature gating is consistent across traits and types.
  • Test infrastructure: Validate MockRpcService and MockNode implementations cover all RPC variants (cluster management, replication, storage, client operations); ensure test modules are properly behind cfg gates.
  • Import path updates: Spot-check a few complex modules (raft_role, network, state_machine_handler) to ensure all crate::proto paths are correctly replaced with d_engine_proto equivalents.

Possibly related PRs

  • V0.1.4 release #149: Modifies same client/proto types (client_api, ReadConsistencyPolicy), workspace layout, and client/kv code paths at code level.
  • Feature/79 snapshot #127: Both update CI workflow (.github/workflows/ci.yml) with dtolnay/rust-toolchain@master 1.88.0 and adjust caching/coverage tooling.
  • Release/v0.1.3 #128: Also modifies CI workflow with Rust toolchain 1.88.0 and test/coverage tool changes.

Poem

🐰 A workspace blooms where once was one,
Proto boundaries, cleanly drawn and done,
From crate to crate, the types now flow,
With public APIs all aglow—
Test utilities flourish, refactored with care,
A structure rebuilt, beyond compare! ✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat #167: workspace structure' clearly describes the main change—a workspace structure refactor—and directly relates to the extensive modifications across the entire codebase converting it from a single-crate to a multi-crate workspace layout.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@JoshuaChi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 4, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (10)
README.md (1)

79-84: Update the RocksDB example to match the new import paths.

The RocksDB storage example still uses the old d_engine import path, which is inconsistent with the updated import path in the basic usage example (line 44). For consistency with the workspace restructuring, this should use d_engine_server.

Apply this diff to update the import path:

-use d_engine::{NodeBuilder, RocksDBStorageEngine, RocksDBStateMachine};
+use d_engine_server::{NodeBuilder, RocksDBStorageEngine, RocksDBStateMachine};
d-engine-core/src/storage/storage_test.rs (1)

39-59: Fix undefined FOLLOWER and LEARNER constants in test setup.

The FOLLOWER and LEARNER constants used at lines 39, 45, 51, and 57 are undefined. The imports provide enum variants Follower and Learner, which should be converted to i32 instead. Replace each occurrence with the imported variant plus .into() conversion (e.g., role: Follower.into(), and role: Learner.into(),).

d-engine-core/src/utils/file_io.rs (1)

64-86: Avoid panicking in write_into_file.
Now that this helper is pub, every failed create/open/write ends in unwrap() → panic, which is a correctness regression for callers that can hit I/O errors (permissions, ENOSPC, transient FS issues). Please bubble the errors back instead of aborting the process.

Apply this diff to propagate errors safely:

-#[allow(dead_code)]
-pub async fn write_into_file(
-    path: PathBuf,
-    buf: Vec<u8>,
-) {
+#[allow(dead_code)]
+pub async fn write_into_file(
+    path: PathBuf,
+    buf: Vec<u8>,
+) -> Result<()> {
     if let Some(parent) = path.parent() {
-        if let Err(e) = tokio::fs::create_dir_all(parent).await {
-            error!("failed to crate dir with error({})", e);
-        } else {
-            debug!("created successfully: {:?}", path);
-        }
+        tokio::fs::create_dir_all(parent)
+            .await
+            .map_err(StorageError::IoError)?;
+        debug!("created parent directories: {:?}", parent);
     }
 
     let file = tokio::fs::OpenOptions::new()
         .create(true)
         .append(true)
-        .open(path)
-        .await
-        .unwrap();
-    let mut active_file = BufWriter::new(file);
-    active_file.write_all(&buf).await.unwrap();
-    active_file.flush().await.unwrap();
+        .open(&path)
+        .await
+        .map_err(StorageError::IoError)?;
+    let mut active_file = BufWriter::new(file);
+    active_file
+        .write_all(&buf)
+        .await
+        .map_err(StorageError::IoError)?;
+    active_file.flush().await.map_err(StorageError::IoError)?;
+    Ok(())
 }
d-engine-core/src/maybe_clone_oneshot.rs (2)

61-72: Fix missing Clone bound when using broadcast sender

broadcast::Sender::send requires T: Clone. Because this impl is compiled under #[cfg(any(test, feature = "test-utils"))], the current signature impl<T: Send> does not satisfy the bound, so this block fails to compile as soon as a non-Clone type is used (and the compiler will complain even if you never call send). Tighten the impl to reflect the real requirement.

-#[cfg(any(test, feature = "test-utils"))]
-impl<T: Send> MaybeCloneOneshotSender<T> {
+#[cfg(any(test, feature = "test-utils"))]
+impl<T: Send + Clone> MaybeCloneOneshotSender<T> {
     pub fn send(
         &self,
         value: T,
     ) -> Result<usize, broadcast::error::SendError<T>> {
         if let Some(tx) = &self.test_inner {
             tx.send(value)
         } else {
             // Fallback for non-cloneable types
             panic!("Cannot broadcast non-cloneable type in tests");
         }
     }
 }

118-137: Avoid tight loop in broadcast-backed Future

Calling try_recv in a loop and immediately wake_by_ref causes the task to be re-polled continuously while the buffer is empty, burning CPU and starving other work. You can instead poll the async recv() future directly so the broadcast channel manages the waker for you.

-        if let Some(rx) = &mut this.test_inner {
-            match rx.try_recv() {
-                Ok(value) => Poll::Ready(Ok(value)),
-                Err(broadcast::error::TryRecvError::Empty) => {
-                    // Register a Waker to wake up the task when data arrives
-                    cx.waker().wake_by_ref();
-                    Poll::Pending
-                }
-                Err(broadcast::error::TryRecvError::Closed) => {
-                    Poll::Ready(Err(broadcast::error::RecvError::Closed))
-                }
-                Err(broadcast::error::TryRecvError::Lagged(n)) => {
-                    Poll::Ready(Err(broadcast::error::RecvError::Lagged(n)))
-                }
-            }
-        } else {
+        if let Some(rx) = &mut this.test_inner {
+            let recv = rx.recv();
+            tokio::pin!(recv);
+            recv.poll(cx)
+        } else {
             // Fallback for non-cloneable types
             panic!("Cannot broadcast non-cloneable type in tests");
         }
d-engine-core/src/replication/mod.rs (1)

352-385: Restore AppendEntriesResponse helpers (compile break).

response.is_conflict() / response.is_higher_term() are still invoked here, but the inherent impl AppendEntriesResponse that defined those helpers was removed in this PR. The prost-generated struct does not supply them, so the file no longer compiles (and any remaining callers of the constructors like AppendEntriesResponse::success will fail too). Please reinstate the helper impl or rewrite the call sites to pattern-match the result field.

+use d_engine_proto::server::replication::append_entries_response;
+
+impl AppendEntriesResponse {
+    pub fn is_success(&self) -> bool {
+        matches!(self.result, Some(append_entries_response::Result::Success(_)))
+    }
+
+    pub fn is_conflict(&self) -> bool {
+        matches!(self.result, Some(append_entries_response::Result::Conflict(_)))
+    }
+
+    pub fn is_higher_term(&self) -> bool {
+        matches!(self.result, Some(append_entries_response::Result::HigherTerm(_)))
+    }
+}
d-engine-core/src/state_machine_handler/default_state_machine_handler.rs (1)

1122-1138: Fix snapshot prefix slicing

parse_snapshot_dirname still slices at byte offset 9, so any non-default snapshots_dir_prefix fails to parse and old archives are never deleted. That defeats the new configuration knob and leaks snapshot files. Please derive the remainder from the actual prefix (e.g., strip_prefix) before splitting.

-    if !name.starts_with(snapshot_dir_prefix) {
-        return None;
-    }
-
-    // Remove fixed parts
-    let core = &name[9..name.len()]; // "snapshot-".len() = 9,
+    let core = match name.strip_prefix(snapshot_dir_prefix) {
+        Some(core) => core,
+        None => return None,
+    };
benches/d-engine-bench/src/main.rs (1)

121-133: Guard against key_size overflow in prefixed keys

10u64.pow(key_size as u32) overflows once key_size > 19, which now makes the bench crash in debug (panic) and silently wrap in release. The previous generator handled arbitrarily large widths, so this is a regression. Please handle the overflow case before calling pow.

-    if sequential {
-        let max_value = 10u64.pow(key_size as u32) - 1;
-        let value = max_value.saturating_sub(index);
-        format!("{:0width$}", value, width = key_size)
+    if sequential {
+        let max_value = match 10u64.checked_pow(key_size as u32) {
+            Some(v) => v - 1,
+            None => return "0".repeat(key_size),
+        };
+        let value = max_value.saturating_sub(index);
+        format!("{:0width$}", value, width = key_size)
d-engine-core/src/test_utils/mock/mock_rpc_service.rs (1)

34-112: Significant code duplication with d-engine-client mock implementation.

The mock_listener method and several simulate_* helpers are duplicated between d-engine-client/src/mock_rpc_service.rs and this file, with only minor differences (additional services in core version).

Consider:

  1. Consolidating shared mock infrastructure into a common test-utils crate
  2. Having the client mock compose/reuse the core mock
  3. Using feature flags to optionally include additional services

This would reduce maintenance burden and ensure consistency across test infrastructure.

Also applies to: 173-305

d-engine-core/src/raft_role/leader_state.rs (1)

2078-2095: Leadership loss result is dropped

verify_leadership_limited_retry returns Ok(false) when leadership is lost, but we immediately ? it and treat that as success. That clears the pending queue and prints “Promotion successful” even though nothing was committed. We need to surface the false case as an error (and likely re-queue the learners) so membership does not drift.

         self.verify_leadership_limited_retry(
             vec![EntryPayload::config(change)],
             true,
             ctx,
             role_tx,
-        )
-        .await?;
+        ).await?;
+
+        if !self
+            .verify_leadership_limited_retry(vec![EntryPayload::config(change)], true, ctx, role_tx)
+            .await?
+        {
+            return Err(NetworkError::TaskBackoffFailed(
+                "Batch promotion aborted: leadership could not be confirmed".to_string(),
+            )
+            .into());
+        }
🧹 Nitpick comments (11)
d-engine-core/src/test_utils/entry_builder_test.rs (1)

115-120: Consider verifying payload content in addition to presence.

The tests currently verify that payload.is_some() for various data inputs (empty, large, binary), which confirms the entry structure is correct. For more thorough validation, consider adding assertions that check the payload content matches the input data, especially for edge cases like empty and binary data.

Example enhancement for test_entry_builder_command_empty_data:

let (_, entry) = builder.command(b"");

assert!(entry.payload.is_some());
// Optional: verify empty data is preserved
if let Some(EntryPayload { payload: Some(d_engine_proto::common::entry_payload::Payload::Command(data)) }) = &entry.payload {
    assert_eq!(data.len(), 0);
}

Also applies to: 123-129, 166-172

d-engine-core/src/test_utils/snapshot.rs (2)

57-57: Extract hardcoded size limit to a named constant.

The 1GB limit is duplicated and lacks documentation. Consider extracting to a module-level constant.

Add a constant at the top of the file:

/// Maximum message size for test snapshot streams (1GB)
const TEST_SNAPSHOT_MAX_SIZE: usize = 1024 * 1024 * 1024;

Then replace both occurrences:

-        Some(1024 * 1024 * 1024),
+        Some(TEST_SNAPSHOT_MAX_SIZE),

Also applies to: 133-133


175-176: Remove redundant imports.

AsyncWriteExt is already imported at the module level (line 13). Consider moving GzipEncoder to module-level imports as well since it's used in multiple functions.

Apply this diff:

-    use async_compression::tokio::write::GzipEncoder;
-    use tokio::io::AsyncWriteExt;

And optionally add to module-level imports (around line 5):

use async_compression::tokio::write::GzipEncoder;
d-engine-core/src/utils/file_io_test.rs (3)

168-170: Document the safety of the unsafe block.

The unsafe block uses flock without explaining why this is safe. Add a safety comment documenting that the file descriptor is valid and the flock call is used correctly.

Apply this diff to add safety documentation:

-        // Apply file lock
-        unsafe {
-            flock(file.as_raw_fd(), LOCK_EX);
-        }
+        // Apply file lock
+        // SAFETY: file.as_raw_fd() returns a valid file descriptor from the open file handle,
+        // and flock is called with a valid lock operation (LOCK_EX).
+        unsafe {
+            flock(file.as_raw_fd(), LOCK_EX);
+        }

135-183: Consider clarifying the test name and behavior.

The test name test_delete_busy_file might be misleading. On Unix systems, file locks (via flock) don't prevent deletion—the directory entry is removed, though the file data persists until all handles close. The test correctly verifies this platform-specific behavior (success on Unix, failure on Windows), but the name "busy file" doesn't fully capture this nuance.

Consider renaming to something like test_delete_locked_file_platform_behavior or adding a comment explaining the expected platform differences.


438-438: Consider a more efficient approach for generating random test data.

The current approach generates 5MB of random data element-by-element. For better test performance, consider using rand::thread_rng().fill_bytes() with a pre-allocated buffer.

Example refactor:

-        // Generate 5MB of random data
-        let data: Vec<u8> = (0..5 * 1024 * 1024).map(|_| rand::random::<u8>()).collect();
+        // Generate 5MB of random data
+        let mut data = vec![0u8; 5 * 1024 * 1024];
+        rand::thread_rng().fill_bytes(&mut data);
d-engine-proto/Cargo.toml (1)

17-23: Clarify the prost dependency configuration.

The commented workspace dependency for prost (line 18) followed by a direct version specification (line 19) is inconsistent with other dependencies that use workspace = true. This could lead to version conflicts within the workspace.

Please clarify whether the direct prost version is intentional. If not, consider using the workspace dependency:

-# prost = { workspace = true }
-prost = { version = "0.13", features = ["derive"] }
+prost = { workspace = true, features = ["derive"] }

If the direct version is required for specific reasons (e.g., feature flags not in workspace), please document this with a comment.

d-engine-client/src/mock_rpc_service.rs (2)

43-43: Replace unwrap() calls with proper error propagation in test infrastructure.

Using unwrap() in test helper functions can lead to unclear panic messages. Use ? or expect() with descriptive messages instead.

Apply this diff:

-        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
+        let listener = TcpListener::bind("127.0.0.1:0")
+            .await
+            .map_err(|e| tonic::Status::internal(format!("Failed to bind listener: {e}")))?;
         let addr = listener
             .local_addr()
             .map_err(|e| tonic::Status::internal(format!("Failed to bind: {e}")))?;

And:

                 )
-                .await
-                .unwrap();
+                .await
+                .map_err(|e| tracing::error!("Mock server failed: {e:?}"))
+                .ok();

Also applies to: 77-77


99-110: Extract duplicate default metadata response builder.

The same default metadata response builder is duplicated across four methods. Extract it into a shared helper function.

Add a helper method to MockNode:

impl MockNode {
    fn default_metadata_builder() -> Box<dyn Fn(u16) -> std::result::Result<ClusterMembership, tonic::Status> + Send + Sync> {
        Box::new(|port: u16| {
            Ok(ClusterMembership {
                version: 1,
                nodes: vec![NodeMeta {
                    id: 1,
                    role: NodeRole::Leader.into(),
                    address: format!("127.0.0.1:{port}"),
                    status: NodeStatus::Active.into(),
                }],
            })
        })
    }
}

Then use it:

-        let builder = metadata_response_builder.unwrap_or_else(|| {
-            Box::new(|port: u16| {
-                Ok(ClusterMembership {
-                    version: 1,
-                    nodes: vec![NodeMeta {
-                        id: 1,
-                        role: NodeRole::Leader.into(),
-                        address: format!("127.0.0.1:{port}",),
-                        status: NodeStatus::Active.into(),
-                    }],
-                })
-            })
-        });
+        let builder = metadata_response_builder.unwrap_or_else(Self::default_metadata_builder);

Also applies to: 128-140, 159-171, 190-201

d-engine-client/src/lib.rs (1)

162-182: Unnecessary mutable reference in refresh method.

The refresh method takes &mut self but only performs atomic operations via ArcSwap. The mutable reference is unnecessary and restrictive.

Change the signature to use a shared reference:

-    pub async fn refresh(
-        &mut self,
-        new_endpoints: Option<Vec<String>>,
-    ) -> std::result::Result<(), ClientApiError> {
+    pub async fn refresh(
+        &self,
+        new_endpoints: Option<Vec<String>>,
+    ) -> std::result::Result<(), ClientApiError> {

This allows multiple concurrent callers to refresh the client configuration.

d-engine-core/src/test_utils/mock/mock_rpc_service.rs (1)

123-125: Trivial wrapper function.

The tcp_addr_to_http_addr function is a simple string format wrapper that could be inlined at call sites.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2169613 and 30f5caf.

⛔ Files ignored due to path filters (13)
  • Cargo.lock is excluded by !**/*.lock
  • benches/d-engine-bench/Cargo.lock is excluded by !**/*.lock
  • d-engine-client/Cargo.lock is excluded by !**/*.lock
  • d-engine-proto/src/generated/d_engine.client.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.common.error.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.common.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.error.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.server.cluster.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.server.election.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.server.replication.rs is excluded by !**/generated/**
  • d-engine-proto/src/generated/d_engine.server.storage.rs is excluded by !**/generated/**
  • examples/client_usage/Cargo.lock is excluded by !**/*.lock
  • examples/rocksdb-cluster/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (107)
  • .github/workflows/ci.yml (1 hunks)
  • Cargo.toml (1 hunks)
  • Makefile (1 hunks)
  • README.md (1 hunks)
  • benches/d-engine-bench/Cargo.toml (1 hunks)
  • benches/d-engine-bench/src/main.rs (6 hunks)
  • clippy.toml (1 hunks)
  • config/base/raft.toml (1 hunks)
  • d-engine-client/Cargo.toml (1 hunks)
  • d-engine-client/src/builder.rs (1 hunks)
  • d-engine-client/src/cluster.rs (2 hunks)
  • d-engine-client/src/cluster_test.rs (5 hunks)
  • d-engine-client/src/config.rs (1 hunks)
  • d-engine-client/src/error.rs (8 hunks)
  • d-engine-client/src/kv.rs (3 hunks)
  • d-engine-client/src/kv_test.rs (8 hunks)
  • d-engine-client/src/lib.rs (1 hunks)
  • d-engine-client/src/mock_rpc.rs (1 hunks)
  • d-engine-client/src/mock_rpc_service.rs (1 hunks)
  • d-engine-client/src/pool.rs (3 hunks)
  • d-engine-client/src/pool_test.rs (4 hunks)
  • d-engine-client/src/proto/client_ext.rs (1 hunks)
  • d-engine-client/src/proto/mod.rs (1 hunks)
  • d-engine-client/src/utils.rs (1 hunks)
  • d-engine-core/Cargo.toml (1 hunks)
  • d-engine-core/src/commit_handler/default_commit_handler.rs (3 hunks)
  • d-engine-core/src/commit_handler/default_commit_handler_test.rs (32 hunks)
  • d-engine-core/src/commit_handler/mod.rs (1 hunks)
  • d-engine-core/src/config/cluster.rs (2 hunks)
  • d-engine-core/src/config/config_test.rs (2 hunks)
  • d-engine-core/src/config/mod.rs (1 hunks)
  • d-engine-core/src/config/network_test.rs (1 hunks)
  • d-engine-core/src/config/raft.rs (7 hunks)
  • d-engine-core/src/config/tls_test.rs (2 hunks)
  • d-engine-core/src/election/election_handler.rs (4 hunks)
  • d-engine-core/src/election/election_handler_test.rs (3 hunks)
  • d-engine-core/src/election/mod.rs (2 hunks)
  • d-engine-core/src/event.rs (4 hunks)
  • d-engine-core/src/lib.rs (2 hunks)
  • d-engine-core/src/maybe_clone_oneshot.rs (15 hunks)
  • d-engine-core/src/maybe_clone_oneshot_test.rs (1 hunks)
  • d-engine-core/src/membership.rs (6 hunks)
  • d-engine-core/src/network/backgroup_snapshot_transfer.rs (1 hunks)
  • d-engine-core/src/network/backgroup_snapshot_transfer_test.rs (2 hunks)
  • d-engine-core/src/network/mod.rs (7 hunks)
  • d-engine-core/src/purge/default_executor.rs (2 hunks)
  • d-engine-core/src/purge/default_executor_test.rs (1 hunks)
  • d-engine-core/src/purge/mod.rs (2 hunks)
  • d-engine-core/src/raft.rs (11 hunks)
  • d-engine-core/src/raft_context.rs (3 hunks)
  • d-engine-core/src/raft_role/candidate_state.rs (12 hunks)
  • d-engine-core/src/raft_role/follower_state.rs (9 hunks)
  • d-engine-core/src/raft_role/leader_state.rs (26 hunks)
  • d-engine-core/src/raft_role/learner_state.rs (9 hunks)
  • d-engine-core/src/raft_role/mod.rs (6 hunks)
  • d-engine-core/src/raft_role/role_state.rs (3 hunks)
  • d-engine-core/src/replication/mod.rs (2 hunks)
  • d-engine-core/src/replication/replication_handler.rs (7 hunks)
  • d-engine-core/src/state_machine_handler/default_state_machine_handler.rs (9 hunks)
  • d-engine-core/src/state_machine_handler/default_state_machine_handler_test.rs (19 hunks)
  • d-engine-core/src/state_machine_handler/mod.rs (4 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_assembler.rs (2 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_assembler_test.rs (1 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_policy/composite_test.rs (2 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_policy/log_size.rs (1 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_policy/log_size_test.rs (7 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_policy/mod.rs (2 hunks)
  • d-engine-core/src/state_machine_handler/snapshot_policy/time_based_test.rs (4 hunks)
  • d-engine-core/src/storage/mod.rs (1 hunks)
  • d-engine-core/src/storage/raft_log.rs (1 hunks)
  • d-engine-core/src/storage/snapshot_path_manager.rs (1 hunks)
  • d-engine-core/src/storage/state_machine.rs (1 hunks)
  • d-engine-core/src/storage/state_machine_test.rs (1 hunks)
  • d-engine-core/src/storage/storage_engine.rs (3 hunks)
  • d-engine-core/src/storage/storage_engine_test.rs (2 hunks)
  • d-engine-core/src/storage/storage_test.rs (1 hunks)
  • d-engine-core/src/test_utils/common.rs (4 hunks)
  • d-engine-core/src/test_utils/common_test.rs (1 hunks)
  • d-engine-core/src/test_utils/entry_builder.rs (1 hunks)
  • d-engine-core/src/test_utils/entry_builder_test.rs (1 hunks)
  • d-engine-core/src/test_utils/mock/mock_raft_builder.rs (4 hunks)
  • d-engine-core/src/test_utils/mock/mock_rpc.rs (2 hunks)
  • d-engine-core/src/test_utils/mock/mock_rpc_service.rs (13 hunks)
  • d-engine-core/src/test_utils/mock/mock_storage_engine.rs (0 hunks)
  • d-engine-core/src/test_utils/mock/mock_type_config.rs (1 hunks)
  • d-engine-core/src/test_utils/mock/mod.rs (1 hunks)
  • d-engine-core/src/test_utils/mod.rs (1 hunks)
  • d-engine-core/src/test_utils/snapshot.rs (3 hunks)
  • d-engine-core/src/utils/cluster.rs (1 hunks)
  • d-engine-core/src/utils/convert.rs (2 hunks)
  • d-engine-core/src/utils/convert_test.rs (0 hunks)
  • d-engine-core/src/utils/file_io.rs (6 hunks)
  • d-engine-core/src/utils/file_io_test.rs (2 hunks)
  • d-engine-core/src/utils/mod.rs (1 hunks)
  • d-engine-core/src/utils/scoped_timer.rs (1 hunks)
  • d-engine-core/src/utils/stream.rs (2 hunks)
  • d-engine-core/src/utils/time_test.rs (1 hunks)
  • d-engine-docs/Cargo.toml (1 hunks)
  • d-engine-docs/src/docs/architecture/single-responsibility-principle.md (4 hunks)
  • d-engine-docs/src/docs/architecture/snapshot-module-design.md (1 hunks)
  • d-engine-docs/src/docs/overview.md (1 hunks)
  • d-engine-docs/src/lib.rs (1 hunks)
  • d-engine-proto/Cargo.toml (1 hunks)
  • d-engine-proto/build.rs (1 hunks)
  • d-engine-proto/proto/client/client_api.proto (2 hunks)
  • d-engine-proto/proto/common.proto (2 hunks)
  • d-engine-proto/proto/error.proto (4 hunks)
⛔ Files not processed due to max files limit (52)
  • d-engine-proto/proto/server/cluster.proto
  • d-engine-proto/proto/server/election.proto
  • d-engine-proto/proto/server/replication.proto
  • d-engine-proto/proto/server/storage.proto
  • d-engine-proto/src/exts/client_ext.rs
  • d-engine-proto/src/exts/client_ext_test.rs
  • d-engine-proto/src/exts/cluster_ext.rs
  • d-engine-proto/src/exts/cluster_ext_test.rs
  • d-engine-proto/src/exts/common_ext.rs
  • d-engine-proto/src/exts/common_ext_test.rs
  • d-engine-proto/src/exts/mod.rs
  • d-engine-proto/src/exts/replication_ext.rs
  • d-engine-proto/src/exts/replication_ext_test.rs
  • d-engine-proto/src/exts/storage_ext.rs
  • d-engine-proto/src/exts/storage_ext_test.rs
  • d-engine-proto/src/lib.rs
  • d-engine-server/Cargo.toml
  • d-engine-server/src/lib.rs
  • d-engine-server/src/membership/membership_guard.rs
  • d-engine-server/src/membership/membership_guard_test.rs
  • d-engine-server/src/membership/mod.rs
  • d-engine-server/src/membership/raft_membership.rs
  • d-engine-server/src/membership/raft_membership_test.rs
  • d-engine-server/src/network/connection_cache.rs
  • d-engine-server/src/network/connection_cache_test.rs
  • d-engine-server/src/network/grpc/grpc_raft_service.rs
  • d-engine-server/src/network/grpc/grpc_raft_service_test.rs
  • d-engine-server/src/network/grpc/grpc_transport.rs
  • d-engine-server/src/network/grpc/grpc_transport_test.rs
  • d-engine-server/src/network/grpc/mod.rs
  • d-engine-server/src/network/health_checker.rs
  • d-engine-server/src/network/health_checker_test.rs
  • d-engine-server/src/network/mod.rs
  • d-engine-server/src/network/network_test.rs
  • d-engine-server/src/node/builder.rs
  • d-engine-server/src/node/builder_test.rs
  • d-engine-server/src/node/mod.rs
  • d-engine-server/src/node/node_test.rs
  • d-engine-server/src/node/type_config/mod.rs
  • d-engine-server/src/node/type_config/raft_type_config.rs
  • d-engine-server/src/storage/adaptors/file/file_engine_test.rs
  • d-engine-server/src/storage/adaptors/file/file_state_machine.rs
  • d-engine-server/src/storage/adaptors/file/file_state_machine_test.rs
  • d-engine-server/src/storage/adaptors/file/file_storage_engine.rs
  • d-engine-server/src/storage/adaptors/file/file_storage_engine_test.rs
  • d-engine-server/src/storage/adaptors/rocksdb/rocksdb_engine_test.rs
  • d-engine-server/src/storage/adaptors/rocksdb/rocksdb_state_machine.rs
  • d-engine-server/src/storage/adaptors/rocksdb/rocksdb_storage_engine.rs
  • d-engine-server/src/storage/buffered/buffered_raft_log.rs
  • d-engine-server/src/storage/buffered/buffered_raft_log_test.rs
  • d-engine-server/src/storage/mod.rs
  • d-engine-server/src/test_utils/integration/mod.rs
💤 Files with no reviewable changes (2)
  • d-engine-core/src/test_utils/mock/mock_storage_engine.rs
  • d-engine-core/src/utils/convert_test.rs
🧰 Additional context used
🪛 checkmake (0.2.2)
Makefile

[warning] 54-54: Target body for "help" exceeds allowed length of 5 (35).

(maxbodylength)


[warning] 365-365: Target body for "audit" exceeds allowed length of 5 (6).

(maxbodylength)


[warning] 374-374: Target body for "deny" exceeds allowed length of 5 (6).

(maxbodylength)


[warning] 406-406: Target body for "troubleshoot" exceeds allowed length of 5 (34).

(maxbodylength)


[warning] 18-18: Missing required phony target "clean"

(minphony)


[warning] 115-115: Target "install" should be declared PHONY.

(phonydeclared)


[warning] 355-355: Target "doc" should be declared PHONY.

(phonydeclared)

🔇 Additional comments (100)
d-engine-core/src/utils/time_test.rs (1)

1-7: Import organization is improved.

The reordering of the get_now_as_u128 import now follows a logical pattern (ascending numeric type size: u32 → u64 → u128), which enhances readability without functional impact. The overall test module structure is clean and well-organized.

d-engine-docs/src/docs/architecture/snapshot-module-design.md (1)

174-190: Verify bracket escaping aligns with documentation build tool requirements.

The changes escape square brackets in component references (e.g., [**StateMachine**] → \[**StateMachine**\]) in the "Generating a new snapshot", "Applying a snapshot", and "Cleaning up old snapshots" sections. This is applied consistently across all affected references.

However, since bracket escaping requirements depend on the specific markdown renderer or documentation build system (e.g., mdx, docusaurus, standard markdown), please verify:

  1. Whether this escaping is required by your documentation build tool and not an artifact of a failed conversion
  2. Whether similar component references elsewhere in the documentation follow the same escaping pattern for consistency
  3. That the rendered output displays correctly (brackets should be visible in the final output, not hidden or malformed)

If you'd like, I can generate a script to validate markdown syntax consistency across the documentation directory and verify that bracket-escaping is uniformly applied.

d-engine-core/src/config/tls_test.rs (1)

4-4: LGTM! Cosmetic formatting improvements.

The import reordering and assertion reformatting are purely stylistic changes that maintain identical functionality while improving code organization and readability.

Also applies to: 91-95

d-engine-core/src/config/network_test.rs (1)

1-1: LGTM! Import consolidation improves organization.

Moving the Error import to the top of the file eliminates duplication and follows Rust conventions for grouping imports together.

d-engine-core/src/test_utils/entry_builder.rs (1)

3-5: LGTM! Import paths correctly updated for workspace structure.

The migration from local proto paths (crate::proto::common) to the shared d_engine_proto crate aligns with the workspace restructuring. The imported types (Entry, EntryPayload, Change) are used consistently throughout the file.

d-engine-core/src/test_utils/entry_builder_test.rs (1)

1-213: Excellent test coverage for EntryBuilder!

The test suite is comprehensive and well-structured, covering:

  • Basic operations (command, noop, config)
  • Chaining and sequencing behavior
  • Edge cases (empty/large data, boundary values for index/term, binary payloads)
  • Mixed operation sequences
  • State progression validation

The tests correctly verify index progression, term constancy, and payload presence across all operation types.

d-engine-core/src/commit_handler/default_commit_handler_test.rs (5)

27-32: LGTM! Clean migration to workspace proto crate.

The import updates consistently migrate from crate-local crate::proto to the shared d_engine_proto crate, aligning with the workspace structure refactoring.


316-323: LGTM! Type-safe role initialization.

The migration from raw constants to Leader.into() improves type safety and code clarity by using the NodeRole enum.


437-460: LGTM! Consistent enum usage in tests.

The test correctly uses Leader.into() for role conversions throughout, maintaining consistency with the workspace refactoring.


486-487: LGTM! Correct leadership transition test.

The test appropriately uses both Leader.into() and Follower.into() to verify that the handler correctly handles leadership loss during batch processing.


851-863: LGTM! Consistent refactoring in batch processing tests.

All tests in the process_batch_test module correctly use Leader.into() for role initialization, maintaining consistency with the workspace migration.

d-engine-core/src/test_utils/snapshot.rs (2)

7-7: LGTM! Import changes align with workspace restructuring.

The migration from internal proto paths to the d_engine_proto crate is consistent with the PR's workspace refactoring objectives.

Also applies to: 11-11, 21-23


138-160: LGTM! Promoting test utility to public API.

Exposing create_test_chunk as a public helper is reasonable for external test utilities.

d-engine-core/src/utils/file_io_test.rs (5)

1-20: LGTM! Import organization is clean.

The reorganized imports are all properly used throughout the test module.


186-224: LGTM! Permission test is well-structured.

The test correctly sets up a permission-denied scenario and properly cleans up afterwards.


226-261: LGTM! Checksum conversion tests are comprehensive.

Good coverage of valid input, boundary conditions, and edge cases.


263-381: LGTM! Compressed format validation tests are well-structured.

The tests cover valid files, invalid extensions, invalid magic numbers, and empty files comprehensively.


383-516: LGTM! File checksum tests provide excellent coverage.

The test suite thoroughly validates checksum computation for empty files, small files, large files, non-existent files, consistency checks, and content change detection.

README.md (1)

44-44: LGTM!

The import path correctly reflects the new workspace structure where server-side components are now in the d_engine_server crate.

d-engine-core/src/storage/snapshot_path_manager.rs (1)

3-3: LGTM!

The import path correctly reflects the new workspace structure where proto definitions are centralized in the d_engine_proto crate.

d-engine-core/src/purge/default_executor_test.rs (1)

8-8: LGTM!

The simplified import path indicates that test utilities are now more accessible, likely re-exported at the crate root for easier use across tests.

d-engine-client/src/builder.rs (1)

83-84: LGTM!

The documentation correctly reflects the new workspace structure where client components are now in the dedicated d_engine_client crate.

d-engine-core/src/state_machine_handler/snapshot_policy/log_size.rs (1)

71-74: LGTM!

Making the constructor public aligns with the PR objective to promote internal APIs for use across the workspace. The visibility change allows external crates to instantiate LogSizePolicy with custom thresholds and cooldown periods.

d-engine-core/src/storage/storage_test.rs (1)

4-8: LGTM!

The import paths correctly reflect the workspace structure with proto types centralized in d_engine_proto. The migration from crate-local paths to the dedicated proto crate is consistent with the overall refactoring.

d-engine-core/src/utils/convert.rs (2)

1-2: LGTM!

Import reordering and the addition of prost::Message support encoding functionality used in the skv function (line 51).


29-34: LGTM!

The documentation example correctly reflects the new workspace structure where this utility is part of the d_engine_core crate.

config/base/raft.toml (1)

55-55: Service name configuration is correct.

The configured service name d_engine.server.cluster.ClusterManagementService matches the proto definition exactly:

  • Package: d_engine.server.cluster (from git/d-engine-proto/proto/server/cluster.proto line 2)
  • Service: ClusterManagementService (from git/d-engine-proto/proto/server/cluster.proto line 120)

No changes needed.

d-engine-client/src/proto/mod.rs (1)

1-2: LGTM!

The module declaration and re-export pattern is idiomatic and correctly exposes the client extension API through the proto namespace.

d-engine-docs/src/docs/architecture/single-responsibility-principle.md (1)

8-151: LGTM!

The formatting improvements enhance readability without changing the document's content. The code fence language change from ignore to text at line 122 is appropriate for the folder structure example.

d-engine-core/src/purge/default_executor.rs (2)

9-10: LGTM!

The import path updates correctly reflect the workspace reorganization, migrating from local proto paths to the centralized d_engine_proto crate.


36-36: LGTM!

Making the constructor public is appropriate for the workspace reorganization and allows external crates to instantiate DefaultPurgeExecutor.

d-engine-docs/src/lib.rs (1)

278-278: LGTM!

The public module declaration correctly exposes the docs module for external access.

d-engine-core/src/utils/scoped_timer.rs (1)

1-23: LGTM! Clean RAII timing utility.

The ScopedTimer implementation follows the standard RAII pattern for timing measurements. Using tokio::time::Instant is appropriate for async contexts, and logging to the "timing" target enables easy filtering of timing data.

d-engine-proto/Cargo.toml (2)

1-15: LGTM!

Package metadata and docs.rs configuration are properly structured for a protocol definitions crate.


26-28: LGTM!

Build dependencies are appropriate for a proto crate using tonic code generation and build metadata.

d-engine-proto/build.rs (1)

7-16: Proto file reorganization verified.

All proto files exist at their new paths. The reorganization into proto/server/ and proto/client/ directories with simplified naming is correct and improves separation of concerns.

d-engine-docs/src/docs/overview.md (1)

27-30: Import paths are correct. All three symbols are properly exported from d_engine_server.

Verification confirms that NodeBuilder, FileStorageEngine, and FileStateMachine are all re-exported at the crate root of d_engine_server in lib.rs (lines 69, 73, and 75 respectively). The imports in the documentation are accurate.

d-engine-core/src/purge/mod.rs (4)

1-1: LGTM! Documentation path updated to new workspace structure.

The doc include path correctly references the new d-engine-docs location.


9-9: LGTM! Public re-export enables multi-crate workspace usage.

Changing from pub(crate) to pub is intentional for the workspace refactor, allowing other crates to use the default executor.


14-14: LGTM! Test utilities feature gating expanded appropriately.

Extending automock to any(test, feature = "test-utils") allows other crates to use mocks when the test-utils feature is enabled.

Also applies to: 28-28


19-19: LGTM! Proto import path migrated to workspace crate.

The import correctly references d_engine_proto::common::LogId from the new proto crate.

d-engine-core/src/network/backgroup_snapshot_transfer.rs (2)

11-11: LGTM! Import reorganization with no functional impact.

The import paths have been reorganized but maintain the same functionality.

Also applies to: 16-16, 18-18


30-33: LGTM! Proto types migrated to workspace proto crate.

The proto types are now correctly imported from d_engine_proto::server::storage, aligning with the workspace structure refactor.

d-engine-core/src/network/backgroup_snapshot_transfer_test.rs (2)

5-6: LGTM! Test utilities and proto imports updated.

The imports correctly reference the new proto paths and test utilities from the workspace structure.

Also applies to: 19-22


24-46: LGTM! Well-structured test helper function.

The create_snapshot_stream helper properly:

  • Creates test chunks with sequential data
  • Uses workspace test utilities (create_test_chunk, crate_test_snapshot_stream)
  • Maps errors appropriately to NetworkError::TonicStatusError
d-engine-core/src/utils/stream.rs (3)

13-13: LGTM! Import statement reorganized.

The ReceiverStream import has been repositioned with no functional impact.


49-53: LGTM! Default implementation provides good ergonomics.

The Default trait implementation for GrpcStreamDecoder<T> appropriately delegates to new(), making the API more convenient to use.


56-56: LGTM! Constructor visibility expanded for workspace usage.

Making new() public is intentional for the multi-crate workspace, allowing other crates to construct GrpcStreamDecoder instances.

d-engine-core/src/commit_handler/default_commit_handler.rs (3)

22-27: LGTM! Imports reorganized to use workspace proto crate.

The imports now correctly reference d_engine_proto::common types and internal aliases, aligning with the workspace structure.


117-117: LGTM! Constructor visibility expanded for workspace usage.

Making the new() constructor public is intentional for the multi-crate workspace refactor.


280-280: LGTM! Trace formatting simplified.

The trace statement formatting has been consolidated to a single line with no functional impact.

d-engine-core/src/commit_handler/mod.rs (1)

58-58: LGTM! Test utilities feature gating expanded consistently.

The automock configuration now applies when either test or feature = "test-utils" is enabled, supporting cross-crate test infrastructure.

Also applies to: 64-64

.github/workflows/ci.yml (3)

41-42: LGTM! CI modernized with explicit Rust toolchain.

The workflow now uses Rust 1.88.0 with explicit toolchain installation via dtolnay/rust-toolchain, which is more maintainable than the previous approach.

Also applies to: 48-52


54-55: LGTM! Cache strategy modernized.

Switching to Swatinem/rust-cache is a better approach as it's specifically designed for Rust projects and is actively maintained.


60-63: LGTM! Cargo tools installation consolidated.

The step has been renamed for clarity and still installs both cargo-llvm-cov and cargo-nextest.

d-engine-core/src/storage/storage_engine.rs (3)

1-1: LGTM! Documentation path updated to workspace docs crate.

The doc include path correctly references the new d-engine-docs location.


6-6: LGTM! Test utilities feature gating expanded for storage traits.

The automock configuration for LogStore and MetaStore now applies when either test or feature = "test-utils" is enabled, supporting cross-crate test infrastructure.

Also applies to: 36-36, 96-96


12-13: LGTM! Proto types migrated to workspace proto crate.

The Entry and LogId types are now correctly imported from d_engine_proto::common, aligning with the workspace structure refactor.

d-engine-client/src/config.rs (1)

3-3: LGTM: Import path refactored.

The utility function get_now_as_u32 has been relocated to the utils module, which is a reasonable consolidation of utility functions.

d-engine-proto/proto/common.proto (2)

2-2: LGTM: Package namespace updated.

The package has been renamed to align with the new workspace structure and d_engine_proto crate naming convention.


100-105: LGTM: Type-safe role enumeration.

The new NodeRole enum provides a type-safe alternative to scalar role constants, improving code clarity and reducing the risk of invalid role values.

d-engine-core/src/state_machine_handler/snapshot_assembler_test.rs (1)

10-11: LGTM: Proto imports migrated to external crate.

The import paths have been updated to use the new d_engine_proto crate, aligning with the workspace restructuring.

d-engine-core/src/test_utils/mock/mock_type_config.rs (1)

10-10: LGTM: Explicit import improves clarity.

Replacing the prelude import with an explicit import for MockStorageEngine makes the dependency clearer and more maintainable.

benches/d-engine-bench/Cargo.toml (3)

3-3: LGTM: Version bump reflects major refactor.

The version bump from 0.1.4 to 0.2.0 appropriately reflects the significant workspace restructuring changes.


7-7: LGTM: Dependency updated to new client crate.

The benchmark now depends on the new d-engine-client crate, which is consistent with the workspace restructuring and provides access to the client API.


4-4: Rust edition 2024 is now stable.

Rust Edition 2024 was released to stable with Rust 1.85 on February 20, 2025. The code change is valid and will not cause build failures.

Likely an incorrect or invalid review comment.

d-engine-client/src/pool.rs (2)

1-1: LGTM: Proto imports migrated to external crate.

The import paths have been updated to use types from the d_engine_proto crate, aligning with the workspace restructuring.

Also applies to: 11-15


185-185: LGTM: Type-safe role comparison.

The code now uses the typed NodeRole enum instead of a scalar constant, which improves type safety and makes the code more self-documenting. The .into() conversion properly converts the enum to the underlying proto i32 representation.

d-engine-core/src/state_machine_handler/snapshot_assembler.rs (1)

2-2: LGTM: Import paths refactored.

The imports have been reorganized: added explicit Arc import, relocated create_parent_dir_if_not_exist, and updated SnapshotMetadata to use the d_engine_proto crate. These are straightforward refactoring changes with no functional impact.

Also applies to: 19-20

d-engine-core/src/config/mod.rs (1)

39-39: LGTM: NodeStatus import migrated to external crate.

The import path has been updated to use d_engine_proto::common::NodeStatus, aligning with the workspace restructuring to separate proto definitions into their own crate.

d-engine-proto/proto/error.proto (1)

2-2: LGTM! Package namespace migration is clean.

The package rename from raft.error to d_engine.error aligns with the broader workspace restructuring. All error codes and metadata remain semantically unchanged.

d-engine-core/src/state_machine_handler/snapshot_policy/time_based_test.rs (2)

7-10: LGTM! Import migration to proto-based types is clean.

The import path updates correctly align with the new workspace structure, using d_engine_proto for common types like LogId and NodeRole.


22-22: LGTM! NodeRole enum usage is correct.

The conversion from the constant LEADER to Leader.into() properly leverages the new proto-based enum, maintaining test semantics while adopting the updated API.

d-engine-core/src/state_machine_handler/snapshot_policy/log_size_test.rs (2)

8-10: LGTM! Proto import migration is consistent.

The import updates for LogId and NodeRole types from d_engine_proto align with the workspace-wide restructuring.


35-129: LGTM! NodeRole enum conversion is applied consistently.

The migration from role constants (LEADER, FOLLOWER) to enum variants (Leader.into(), Follower.into()) is applied consistently across all test cases, maintaining test semantics.

d-engine-client/src/kv_test.rs (2)

3-22: LGTM! Import migration to proto types is complete.

The import updates correctly reference d_engine_proto for all client and server types, aligning with the workspace restructure.


96-96: LGTM! Error code comparisons updated correctly.

The error assertions now compare against ErrorCode enum variants rather than numeric values, providing better type safety and readability.

Also applies to: 247-247, 427-427, 464-464, 744-744

d-engine-core/src/storage/state_machine_test.rs (1)

9-18: LGTM! Import migration is clean.

The import path updates from crate::proto to d_engine_proto for Entry, EntryPayload, LogId, WriteCommand, and related types correctly align with the workspace restructuring.

d-engine-core/src/config/config_test.rs (2)

9-11: LGTM! Correct usage of unsafe for environment variable removal.

The unsafe block around std::env::remove_var is necessary as this function is marked unsafe in recent Rust versions due to potential data races in concurrent environments.


217-218: LGTM! Import migration to proto types is correct.

The imports for NodeStatus and NodeMeta now correctly reference d_engine_proto paths, consistent with the workspace restructuring.

d-engine-core/src/test_utils/mock/mock_rpc.rs (2)

8-32: LGTM! Mock RPC imports migrated correctly.

The import paths have been systematically updated from crate::proto to d_engine_proto for all client, server, and service types, maintaining consistency with the workspace restructure.


219-221: LGTM! Syntax error fixed.

The semicolon after the closing parenthesis on line 221 correctly terminates the return statement, fixing what would have been a compilation error.

d-engine-client/src/cluster.rs (2)

12-15: LGTM! Import migration to proto cluster types is clean.

The imports for cluster management types now correctly reference d_engine_proto::server::cluster, aligning with the workspace restructuring.


46-46: LGTM! Explicit return type improves API clarity.

The return type is now explicitly std::result::Result<Vec<NodeMeta>, ClientApiError>, making the public API signature clearer and more self-documenting.

d-engine-core/src/maybe_clone_oneshot_test.rs (1)

1-227: Great async coverage on MaybeCloneOneshot.

Appreciate the breadth of scenarios here—covering clones, futures, stress, and complex payloads gives strong confidence in the channel semantics. Nice work.

d-engine-client/src/kv.rs (1)

44-46: Docs now match ClientApiError variants

Thanks for documenting Protocol and Storage outcomes; that lines up with the new ClientResponseExt handling.

d-engine-core/src/test_utils/mod.rs (1)

19-24: Handy node_config helper

Appreciate having a reusable RaftNodeConfig initializer for tests.

d-engine-core/src/raft_role/role_state.rs (1)

398-400: More readable replication failure log

The reformatted error! call is easier to scan in logs when replication backoffs happen.

d-engine-core/src/test_utils/common.rs (1)

46-66: Expose common command generators

Making the insert/delete helpers public lets benches/tests share the encoding logic.

d-engine-core/src/utils/mod.rs (1)

1-15: Great to consolidate util modules

Explicitly exporting cluster/convert/etc. centralizes the namespace nicely.

d-engine-client/src/mock_rpc_service.rs (1)

183-211: Inconsistent visibility: method is public while similar methods are crate-private.

The simulate_mock_service_with_join_cluster_reps method is pub while other simulate_* methods are pub(crate). Ensure visibility is intentional.

Is this method intended to be public, or should it be pub(crate) like the other simulation helpers?

d-engine-core/src/raft_context.rs (2)

94-100: Good use of feature gating for test utilities.

Expanding the cfg gate from test to any(test, feature = "test-utils") properly enables these methods for external test usage while keeping them out of production builds.

Also applies to: 102-108


15-49: The review comment is incorrect and based on incomplete context.

The suggestion to restrict visibility to pub(crate) or mark with #[doc(hidden)] would break the codebase. d-engine-server (a sibling workspace crate) directly constructs RaftStorageHandles and RaftCoreHandlers using public field initialization in production code at d-engine-server/src/node/builder.rs lines 323–331.

These types are exported via pub use raft_context::*; in lib.rs and are intentionally part of the cross-crate workspace API, not internal-only components. Making them pub(crate) would prevent d-engine-server from accessing them across crate boundaries.

The codebase already provides accessor methods for read-only access where applicable, and field construction is a valid pattern for struct initialization. No changes are required.

Likely an incorrect or invalid review comment.

d-engine-core/src/raft_role/mod.rs (1)

28-30: Proto path migration and visibility expansion look good.

The changes consistently migrate from crate::proto to d_engine_proto paths and appropriately expand visibility with proper feature gating for test utilities.

Also applies to: 188-195, 226-253, 288-291, 342-346

d-engine-core/src/lib.rs (3)

2-56: Extensive public API surface expansion.

Multiple internal modules are now publicly exposed. Ensure this aligns with the intended public API design for the workspace structure.

The expansion appears intentional for the workspace refactor, but verify that sensitive internal details aren't inadvertently exposed. Consider using #[doc(hidden)] for workspace-internal APIs that shouldn't be in public documentation.


52-56: Appropriate use of test-utils feature gate.

Good practice gating test utilities behind a feature flag to keep them out of production builds while making them available for downstream testing.


87-87: The review comment's premise is incorrect: EntryPayload constructors have not been removed.

The initial script output definitively shows that EntryPayload::command(), EntryPayload::noop(), and EntryPayload::config() constructors remain active and widely used throughout the codebase. The search found 100+ usages across production code (d-engine-core, d-engine-server) and tests, with no compilation failures. These constructors are tested in d-engine-proto/src/exts/common_ext_test.rs and used in active code paths like d-engine-core/src/raft.rs and d-engine-core/src/raft_role/leader_state.rs.

No verification or fixes are necessary—no constructors were removed.

d-engine-core/src/raft.rs (1)

74-114: Test infrastructure properly gated.

Good use of cfg(any(test, feature = "test-utils")) for test-only listeners and methods.

Also applies to: 336-370

d-engine-client/src/mock_rpc.rs (1)

16-47: Mock service design looks good for testing.

Public fields on MockRpcService are appropriate for test infrastructure, allowing flexible configuration. The builder pattern with with_metadata_response provides a clean API.

d-engine-core/src/test_utils/mock/mock_rpc_service.rs (1)

127-305: All simulation helpers are public - verify necessity.

Unlike the client version where most simulate_* methods are pub(crate), all helpers here are pub. Ensure this broader visibility is intentional for the test-utils module.

Comment thread .github/workflows/ci.yml Outdated
Comment thread d-engine-client/src/error.rs
Comment thread d-engine-client/src/kv_test.rs Outdated
Comment thread d-engine-client/src/lib.rs
Comment thread d-engine-client/src/mock_rpc_service.rs
Comment thread d-engine-client/src/utils.rs Outdated
Comment thread d-engine-core/src/election/mod.rs Outdated
Comment thread d-engine-core/src/raft.rs
Comment thread d-engine-core/src/test_utils/snapshot.rs Outdated
Comment thread d-engine-docs/src/lib.rs
@JoshuaChi
JoshuaChi merged commit be93d34 into develop Nov 5, 2025
4 checks passed
@JoshuaChi
JoshuaChi deleted the feature/167_workspace branch November 5, 2025 09:17
@JoshuaChi
JoshuaChi restored the feature/167_workspace branch November 12, 2025 05:59
@coderabbitai coderabbitai Bot mentioned this pull request Nov 12, 2025
3 of 4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Nov 28, 2025
3 of 4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Dec 13, 2025
4 tasks done
JoshuaChi added a commit that referenced this pull request Dec 31, 2025
… engine

## 🎯 Overview

v0.2.0 is a major release that transforms d-engine from a Raft implementation into a **production-ready distributed coordination engine** with workspace structure, developer-friendly APIs, and comprehensive features.

---

## 🚀 Key Features

### 🏗️ **Workspace Structure (#167)**
- Modular design: `d-engine-core`, `d-engine-server`, `d-engine-client`, `d-engine-proto`
- Clean separation of concerns for library users
- Profile-optimized build configuration

### ⏱️ **TTL & Lease Support (#172)**
- Crash-safe TTL with absolute expiration timestamps
- WAL format change: relative `ttl_secs` → absolute `expire_at_secs`
- Piggyback cleanup mechanism (minimal overhead)
- **Breaking Change**: See MIGRATION_GUIDE.md for WAL upgrade

### 👁️ **Watch API (#174, #196)**
- Lock-free event notification with crossbeam-channel
- gRPC streaming support for real-time key monitoring
- <10ns apply-path overhead, <100μs end-to-end latency
- Service discovery examples included

### 🚀 **EmbeddedEngine API (#182)**
- Zero-config single-node quick start
- `wait_leader()` and `leader_notifier()` for event-driven apps
- Dynamic cluster expansion (1→3 nodes without downtime)
- In-process `LocalKvClient` with explicit consistency levels

### 🎯 **Single-Node Support (#179)**
- Configuration-based single-node detection
- Automatic election/replication optimization
- Production-ready for low-traffic scenarios

### 📖 **Read Consistency Policies (#142)**
- LinearizableRead (strong consistency)
- LeaseRead (optimized with leader lease)
- EventualConsistency (fast local reads)

### 🔧 **Go Client Support (#170, #219)**
- Pre-generated Go protobuf code (zero-config)
- Comprehensive error handling guide
- Service discovery pattern examples

---

## 🐛 Critical Fixes

- **#212**: Fix learner promotion stuck (voter count + role transition)
- **#218**: Fix leader next_index initialization for new learners
- **#222**: Return NOT_LEADER with leader metadata for client redirection
- **#209**: Fix node restart wait_ready() timeout (leader notification race)
- **#211**: Remove Arc::get_mut anti-pattern from lease injection
- **#145**: Fix undefined behavior in mmap zero-copy path
- **#197**: Fix integration test failures (Arc ownership, timing issues)

---

## ⚡ Performance Improvements

- **#138**: Long-lived peer tasks for append entries (100K+ throughput target)
- **#140**: Optimize proto bytes fields (use `bytes::Bytes`)
- **#141**: Optimize RocksDB write path for lower latency
- **#143**: Refactor gRPC compression configuration
- **#194**: Skip protobuf decoding when no active watchers
- **#208**: Eliminate redundant async calls in leader write hot path (+2-3% throughput)
- **#223**: Optimize check_learner_progress() lock contention

### Benchmark Results
- **LeaseRead**: 99,418 ops/s (+7.8% vs v0.1.4)
- **EventualConsistency**: 126,095 ops/s (+9.2% vs v0.1.4)
- **Linearizable p99**: 23.01ms (-8% vs v0.1.4)

---

## 🔧 Refactoring & Architecture

- **#210**: Simplify watch architecture (tokio::broadcast, 90% code sharing)
- **#217**: Refactor Node::run() with strategy pattern
- **#209**: Consolidate Raft unit tests (27 tests migrated from server to core)
- **#201**: Separate static membership from dynamic leader state
- **#223**: Extract 5 helper methods in learner promotion (SRP + 11 unit tests)

---

## 📚 Documentation

- Restructure quick-start docs (embedded + standalone examples)
- Add integration-modes.md and use-cases.md
- Comprehensive error handling guide
- Service discovery pattern documentation
- Delete 911 lines of internal architecture docs (20/80 principle)

---

## 🧪 Testing

- **430** core tests + **305** server tests + **292** integration tests passing
- Fix flaky tests and timing issues
- Add 14+ new unit tests across components
- Optimize test suite with nextest

---

## ⚠️ Breaking Changes

1. **WAL Format**: Relative `ttl_secs` → absolute `expire_at_secs` (requires data migration)
2. **Config**: `raft.watch.enabled` removed (Watch always available)
3. **API**: `StateMachine::start()` changed to async
4. **NodeStatus**: Refactored enum (PROMOTABLE/READ_ONLY/ACTIVE)

See **MIGRATION_GUIDE.md** for detailed upgrade instructions.

---

## 📦 Dependency Updates

- tokio-stream: Fix net feature requirement
- astral-tokio-tar: Replace tokio-tar (CVE-2025-62518 fix)
- simd-adler32: 0.3.7 → 0.3.8
- rustls-pemfile: unmaintained warning suppressed (tonic 0.12 dep)

---

## 🎯 Closes

#43, #45, #59, #66, #70, #71, #79, #89, #90, #101, #102, #106, #107, #109, #110, #119, #120, #121, #122, #123, #125, #133, #135, #138, #139, #140, #141, #142, #143, #145, #146, #147, #148, #150, #151, #152, #153, #154, #155, #156, #157, #158, #159, #161, #164, #167, #170, #172, #174, #175, #176, #178, #185, #186, #187, #192, #193, #194, #195, #196, #197, #200, #201, #203, #204, #205, #208, #209, #210, #211, #212, #213, #217, #218, #219, #222, #223, #224

---

**Files Changed:** 100+ files, ~5,000 insertions, ~1,500 deletions
@JoshuaChi
JoshuaChi deleted the feature/167_workspace branch January 2, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant