Skip to content

feat(conf): support configuring DB storage directory#243

Open
AlexStocks wants to merge 6 commits intofeat/raftfrom
feat/configurable-db-dir
Open

feat(conf): support configuring DB storage directory#243
AlexStocks wants to merge 6 commits intofeat/raftfrom
feat/configurable-db-dir

Conversation

@AlexStocks
Copy link
Copy Markdown
Contributor

@AlexStocks AlexStocks commented Mar 14, 2026

Summary

Add db-dir configuration option to specify the RocksDB data directory. Previously the path was hardcoded as "./db" in 4 places across the codebase.

Changes

  • Add db_dir field to Config struct (default: "./db")
  • Parse db-dir key in Config::load()
  • Pass db_dir through to initialize_storage_server() and legacy server constructors (TcpServer, ClusterTcpServer, UnixServer)
  • Add db-dir documentation to kiwi.conf
  • Add unit tests for default value and config file parsing

Usage

# In kiwi.conf
db-dir /data/kiwi/db

Each database instance creates a subdirectory under this path (e.g., /data/kiwi/db/0, /data/kiwi/db/1).

Test plan

  • Default value is "./db" when not configured
  • Config file parsing correctly reads db-dir value
  • Existing behavior unchanged (default path remains "./db")

Closes #193

Summary by CodeRabbit

  • New Features

    • Added a db-dir configuration option to customize where per-database subdirectories are created (default "./db").
    • Server components now use the configured db-dir so storage files are created under the specified path.
  • Tests

    • Added tests verifying the default db-dir and loading db-dir from configuration.

Add `db-dir` configuration option to specify the RocksDB data directory.
Previously the path was hardcoded as `"./db"` in 4 places across the
codebase.

Changes:
- Add `db_dir` field to Config struct (default: "./db")
- Parse `db-dir` key in Config::load()
- Pass db_dir through to initialize_storage_server() and legacy
  server constructors (TcpServer, ClusterTcpServer, UnixServer)
- Add `db-dir` documentation to kiwi.conf
- Add unit tests for default value and config file parsing

Usage in kiwi.conf:
  db-dir /data/kiwi/db

Closes #193
@github-actions github-actions bot added the ✏️ Feature new feature label Mar 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Warning

Rate limit exceeded

@AlexStocks has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 4 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: da29baa5-b3f2-4846-9c8b-2c7a65a1e67f

📥 Commits

Reviewing files that changed from the base of the PR and between bc86a5b and 95ce678.

📒 Files selected for processing (2)
  • src/conf/src/lib.rs
  • src/net/src/unix.rs
📝 Walkthrough

Walkthrough

Adds a configurable db-dir option to config, exposes db_dir: String with default "./db", and threads it through server factories and constructors (TCP, Unix, Cluster) into storage initialization replacing hard-coded "./db".

Changes

Cohort / File(s) Summary
Configuration Layer
src/conf/kiwi.conf, src/conf/src/config.rs, src/conf/src/lib.rs
Add db-dir directive and comments; add pub db_dir: String with default "./db"; parse "db-dir" in Config::load; add tests for default and file-based loading.
Network Server Layer
src/net/src/lib.rs, src/net/src/tcp.rs, src/net/src/unix.rs
Introduce optional db_dir parameter in server factory methods and constructors (ServerFactory::create_*, TcpServer::new, UnixServer::new, ClusterTcpServer::new) and propagate it through creation paths; adjust return/error handling where constructors now return Result.
Storage Initialization
src/server/src/main.rs
Thread config.db_dir from main() into initialize_storage_server(db_dir); replace hard-coded PathBuf::from("./db") with PathBuf::from(db_dir); update spawn/call sites accordingly.

Sequence Diagram(s)

sequenceDiagram
  participant Config as Config (file)
  participant Main as Main
  participant Factory as ServerFactory
  participant Server as Tcp/Unix/Cluster Server
  participant Storage as RocksDB Storage

  Config->>Main: load() -> config.db_dir
  Main->>Factory: create_* (protocol, addr, db_dir)
  Factory->>Server: new(addr, db_dir)
  Server->>Storage: open(db_path derived from db_dir)
  Storage-->>Server: opened
  Server-->>Factory: ready
  Factory-->>Main: server instance
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • marsevilspirit

Poem

🐰 I tunneled from config down to the ground,
A cozy db_dir where new files are found.
From main to server the burrow I mapped,
RocksDB now rests where the path was wrapped.
Hop in, little bytes — snug and sound! 🥕

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main change: adding support for configuring the database storage directory through configuration, which is the primary objective.
Linked Issues check ✅ Passed The code changes fully satisfy issue #193's requirements: a configurable db-dir key allows users to set RocksDB storage directory instead of hardcoded './db', improving deployment convenience.
Out of Scope Changes check ✅ Passed All changes are directly scoped to supporting configurable DB directory: config parsing, field addition, propagation through server constructors, and corresponding tests.
Description check ✅ Passed The changes comprehensively implement the feature with proper documentation, validation, error handling, and test coverage for both default and configured behaviors.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/configurable-db-dir
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
src/server/src/main.rs (1)

133-150: ⚠️ Potential issue | 🟠 Major

Wait for storage init success before opening the network server.

initialize_storage_server now depends on user-supplied db_dir, but this task is fire-and-forget: failures are only logged inside the spawned task, and the 100ms sleep is not a readiness check. A bad path or slow open can leave the process accepting connections before the storage backend exists.

💡 Suggested fix
+    let (storage_ready_tx, storage_ready_rx) = tokio::sync::oneshot::channel();
     let db_dir = config.db_dir.clone();
     storage_handle.spawn(async move {
         info!("Initializing storage server...");
-        match initialize_storage_server(storage_receiver, &db_dir).await {
+        let init_result = initialize_storage_server(storage_receiver, &db_dir).await;
+        let _ = storage_ready_tx.send(
+            init_result
+                .as_ref()
+                .map(|_| ())
+                .map_err(|e| e.to_string()),
+        );
+        match init_result {
             Ok(_) => {
                 error!("Storage server exited unexpectedly - this should never happen!");
             }
             Err(e) => {
                 error!("Storage server failed: {}", e);
             }
         }
     });

-    // Give storage server a moment to initialize
-    std::thread::sleep(std::time::Duration::from_millis(100));
+    network_handle.block_on(async {
+        storage_ready_rx
+            .await
+            .map_err(|_| std::io::Error::other("storage init task exited before signaling readiness"))?
+            .map_err(std::io::Error::other)
+    })?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/src/main.rs` around lines 133 - 150, The storage task is currently
fire-and-forget (spawned with storage_handle.spawn) and only sleeps 100ms for
readiness; update the code so the server waits for storage initialization
success before opening the network: either call
initialize_storage_server(&storage_receiver, &db_dir).await synchronously before
starting the network or spawn initialize_storage_server but use a oneshot/mpsc
readiness signal (send from inside initialize_storage_server on success/failure)
and await that signal here; store the JoinHandle returned by
storage_handle.spawn (or return/propagate initialization error) and fail fast if
initialization fails so the network server never starts when
initialize_storage_server or the provided db_dir are invalid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/conf/kiwi.conf`:
- Around line 193-195: The comment about RocksDB subdirectories overstates
current behavior: storage initializers use Storage::new(1, 0) so only <db-dir>/0
is created; either propagate the db_instance_num into the storage initialization
where Storage::new(...) is called (so multiple subdirs like ./db/0, ./db/1 will
be created) or simplify the comment to say the server currently creates a single
per-database directory (e.g., ./db/0) until db_instance_num is wired through;
update the comment to match whichever approach you take and reference the
Storage::new(1, 0) usage and the db_instance_num configuration symbol when
making the change.

In `@src/conf/src/config.rs`:
- Line 229: Validate and reject empty or whitespace-only values for the db_dir
configuration field before they are stored or converted to a PathBuf: add a
check (e.g., in the config parsing/validation function that constructs or
validates the struct containing pub db_dir: String) that trims the input and
returns an error if the result is empty, mirroring the existing handling for
cluster-data-dir; also apply the same validation where db_dir is read/assigned
(see the code around the db_dir usage at the other occurrence referenced) so
that whitespace-only or String::new() never flow into PathBuf::from(...) or
RocksDB initialization.

In `@src/conf/src/lib.rs`:
- Around line 117-129: The test test_db_dir_from_config_file uses a fixed path
"/tmp/kiwi_test_db_dir.conf" which races in parallel CI and fails on non-Unix
runners; change the test to create a unique temporary file (e.g., using
tempfile::NamedTempFile or constructing a path under std::env::temp_dir() with a
random suffix), write the two config lines to that temp file, pass its path to
Config::load, and rely on the tempfile to be cleaned up (remove manual
remove_file). Update references in the test function
test_db_dir_from_config_file to use the temporary file handle/path instead of
the hardcoded string.

In `@src/net/src/unix.rs`:
- Around line 36-41: Change unix::Server::new (the pub fn new in
src/net/src/unix.rs) to return Result<Self, E> instead of panicking: validate
db_dir and propagate any storage open errors rather than calling unwrap. Replace
the line creating Storage with using Storage::new(...) and call
storage.open(storage_options, db_path) and map or propagate its error into an
Err variant; on success construct and return Ok(Self { ... }). Follow the
TcpServer::new pattern for error type and propagation so
permission/invalid-path/RocksDB lock errors are returned to the caller.

---

Outside diff comments:
In `@src/server/src/main.rs`:
- Around line 133-150: The storage task is currently fire-and-forget (spawned
with storage_handle.spawn) and only sleeps 100ms for readiness; update the code
so the server waits for storage initialization success before opening the
network: either call initialize_storage_server(&storage_receiver, &db_dir).await
synchronously before starting the network or spawn initialize_storage_server but
use a oneshot/mpsc readiness signal (send from inside initialize_storage_server
on success/failure) and await that signal here; store the JoinHandle returned by
storage_handle.spawn (or return/propagate initialization error) and fail fast if
initialization fails so the network server never starts when
initialize_storage_server or the provided db_dir are invalid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e00dea7-0ec4-45ed-98f2-af25fa767216

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6cacd and 4642b03.

📒 Files selected for processing (7)
  • src/conf/kiwi.conf
  • src/conf/src/config.rs
  • src/conf/src/lib.rs
  • src/net/src/lib.rs
  • src/net/src/tcp.rs
  • src/net/src/unix.rs
  • src/server/src/main.rs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new db-dir configuration option so the RocksDB data directory is configurable instead of being hardcoded, aligning storage initialization with user-provided config and documenting the new setting.

Changes:

  • Introduce db_dir on Config with default "./db" and parse db-dir from config files.
  • Plumb db_dir into storage initialization and legacy server constructors (TCP/cluster TCP/Unix).
  • Document db-dir in kiwi.conf and add unit tests for default + parsing.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/server/src/main.rs Passes configured db_dir into storage runtime initialization.
src/net/src/unix.rs Adds db_dir parameter to UnixServer and uses it to pick the RocksDB path.
src/net/src/tcp.rs Adds db_dir parameter to TCP/cluster TCP legacy servers and uses it for RocksDB path.
src/net/src/lib.rs Updates factory methods to pass db_dir into legacy server constructors.
src/conf/src/config.rs Adds db_dir to Config, sets default, and parses db-dir.
src/conf/src/lib.rs Updates existing tests and adds tests for db_dir default/config parsing.
src/conf/kiwi.conf Documents the new db-dir option and its semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 38 to 42
let storage_options = Arc::new(StorageOptions::default());
let db_path = PathBuf::from("./db");
let db_path = PathBuf::from(db_dir.unwrap_or("./db"));
let mut storage = Storage::new(1, 0);
storage.open(storage_options, db_path).unwrap();
let executor = Arc::new(CmdExecutorBuilder::new().build());
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the same change — new() returns Result and all callers in ServerFactory use .ok().

Comment on lines 101 to 103
#[cfg(unix)]
"unix" => Some(Box::new(unix::UnixServer::new(addr))),
"unix" => Some(Box::new(unix::UnixServer::new(addr, None))),
#[cfg(not(unix))]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. The create_server_with_mode path uses the dual-runtime NetworkServer for TCP (storage is initialized separately in initialize_storage_server). The Unix fallback here is a legacy path — I've left it as None (uses default ./db) since the dual-runtime architecture doesn't use this code path. Added a comment to document this.

Comment on lines +120 to +130
let config_path = "/tmp/kiwi_test_db_dir.conf";
let mut f = std::fs::File::create(config_path).unwrap();
writeln!(f, "port 7379").unwrap();
writeln!(f, "db-dir /data/kiwi/db").unwrap();
drop(f);

let config = Config::load(config_path).unwrap();
assert_eq!("/data/kiwi/db", config.db_dir);

let _ = std::fs::remove_file(config_path);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: using std::env::temp_dir().join(format\!("kiwi_test_db_dir_{}.conf", std::process::id())) now.

AlexStocks and others added 5 commits March 14, 2026 15:18
The test_db_dir_from_config_file test used a hardcoded /tmp/ path
which does not exist on Windows. Use std::env::temp_dir() instead.
- Validate db-dir is not empty/whitespace in Config::load()
- UnixServer::new returns Result instead of panicking via unwrap()
- Update ServerFactory callers to handle UnixServer::new Result
- Use PID in temp config filename to avoid parallel test collisions
- Fix kiwi.conf doc: clarify subdirectories depend on db-instance-num
Fix cargo fmt violation in unix.rs, add clippy::unwrap_used check
as a non-blocking CI job to catch unwrap usage in new code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: enforce clippy::unwrap_used lint and remove all unwrap() from production code

- Replace all .unwrap() in production code with .expect("descriptive reason")
- Add #[allow(clippy::unwrap_used)] to all #[cfg(test)] modules
- Add #![allow(clippy::unwrap_used)] to standalone test files and benchmarks
- Add -D clippy::unwrap_used to Makefile lint target

This ensures panics from unwrap() are always accompanied by a meaningful
message, making debugging easier. Test code is exempted as unwrap() in
tests provides clear enough failure context via the test framework.

Closes #229

* fix: address cargo fmt and CodeRabbit review feedback

- Fix rustfmt line-length violations in unix.rs, segment_log.rs, redis_sets.rs
- proc-macro: use syn::Error::to_compile_error() instead of expect() for
  better diagnostics in macro_stack_trace_debug.rs
- Improve expect() messages: monitoring_api.rs, custom_comparator.rs
- Remove misplaced #[allow(clippy::unwrap_used)] on use statement in writer.rs

* ci: fix build error and improve CI workflows

Fix:
- Restore OperationType import in writer.rs test module (was broken
  when removing misplaced #[allow] on use statement)

CI improvements (inspired by QuantClaw):
- cargo fmt: run on ubuntu only (format output is platform-independent),
  saves 2 redundant macOS/Windows jobs
- Add Cargo registry cache to clippy and build-and-test jobs
- Add fail-fast: false to matrix strategies for better error visibility
- Add permissions: contents: read for least-privilege principle
- Add shared env vars (CARGO_TERM_COLOR, CARGO_INCREMENTAL, etc.)
- Add upload-artifact on test failure for debugging
- Move integration tests (Python) to a dedicated job after build-and-test
- Remove redundant test.yml (fully covered by ci.yml)

New workflows:
- clean-cache.yml: auto-delete PR branch caches when PR is closed
- security.yml: cargo audit (dependency vulnerabilities) + cargo deny
  (advisories & licenses), runs on push/PR to main + weekly schedule

* ci: make security audit jobs non-blocking

cargo-audit and cargo-deny report pre-existing dependency issues
(5 vulnerabilities, missing deny.toml config). Mark both jobs as
continue-on-error so they surface findings without blocking PRs.

* ci: remove security audit from PR triggers

Security audit checks pre-existing dependency vulnerabilities unrelated
to PR changes. Running them on PRs produces red check marks that confuse
contributors. Keep them on push-to-main and weekly schedule only.
PR #242 already enforces clippy::unwrap_used in the lint target,
so the separate unwrap-check CI job and lint-unwrap Makefile target
are no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@guozhihao-224
Copy link
Copy Markdown
Collaborator

如果代码没有冲突的话,可以直接合入 @AlexStocks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✏️ Feature new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] support configuring the DB storage directory in the config

3 participants