Skip to content

refactor(storage): remove Engine abstraction - #365

Merged
AlexStocks merged 42 commits into
mainfrom
codex/issue-349-remove-engine
Jul 24, 2026
Merged

refactor(storage): remove Engine abstraction#365
AlexStocks merged 42 commits into
mainfrom
codex/issue-349-remove-engine

Conversation

@AlexStocks

@AlexStocks AlexStocks commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

Closes #349

本 PR 完成方案 A:删除没有第二生产后端价值的通用 Engine 伪抽象,明确 RocksDB 是主数据与 Raft 日志的唯一持久化后端,并收口 DB 所有权、shutdown、snapshot 热切换和迁移失败边界。

主要改动:

  • 删除 src/engine crate、Engine trait、RocksdbEngine 一对一转发层以及 workspace 依赖。
  • RedisRocksBatchRocksdbLogStore 直接使用具体 rocksdb::DB / Arc<DB>;保留具有真实双实现的 Batch 抽象。
  • 明确唯一 RocksDB shutdown owner,callback/factory 使用弱引用,补 close/drop/reopen 与 clone 生命周期回归。
  • Storage hot-swap 前暂停并 drain 所有外部 owner,避免 snapshot install 时旧 RocksDB 句柄残留或 placeholder 被请求观察。
  • 旧 memory-only Raft 状态、旧日志与已有主数据的不安全组合采用 fail-closed;不自动把空 durable Raft 状态当作可安全迁移。
  • snapshot restore 先完整 staging,再写 durable install marker 后进入破坏阶段;复制文件、子目录、staging root、正式 DB 目录及父目录均显式同步。
  • Unix 与 Windows 都实现真实目录 flush;Windows 使用目录 handle + FILE_FLAG_BACKUP_SEMANTICS,不再静默 no-op。
  • marker 清理采用 durable cleanup tombstone,任何返回错误的清理路径至少保留一个启动预检可识别的 blocker。
  • snapshot build/apply/install/current-snapshot 通过同一 operation gate 串行化,builder 固定捕获具体 Storage owner。
  • 为跨多个 RocksDB 实例的 MSET/MGET/DEL/MSETNX 增加进程内命令可见性门禁;该门禁保证并发命令看不到部分更新,但不声称提供跨数据库的 crash-atomic transaction/rollback。
  • 将 Python integration 纳入 CI,并修复测试揭示的 RESP2/RESP3 null、二进制 GET/MGET、KEYS glob、HSCAN/SSCAN 二进制匹配与 cursor、首次 LPUSH 后尾索引等兼容问题。

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring

Checklist

  • I confirm the target branch is main (or appropriate feature branch)
  • My code follows the project's Rust coding style and conventions
  • Code has passed local testing (cargo +stable test --workspace --all-features)
  • Code has passed clippy checks (cargo +stable clippy --all-features --workspace -- -D warnings -D clippy::unwrap_used)
  • Code is properly formatted (cargo +stable fmt --all -- --check)
  • I have added tests that prove my fix is effective or that my feature works
  • All new and existing tests pass
  • I have updated the documentation (if applicable)
  • My changes generate no new warnings or errors
  • I have checked for potential security issues

Testing

WSL / Ubuntu:

  • cargo +stable fmt --all -- --check
  • cargo +stable clippy --all-features --workspace -- -D warnings -D clippy::unwrap_used
  • RUST_TEST_THREADS=1 cargo +stable test --workspace --all-features
  • cargo +stable build --bin kiwi
  • tests/run_python_integration.sh:55 passed
  • snapshot rename 后父目录同步失败故障注入:marker 保留、请求保持 paused、restart preflight 拒绝
  • 主 marker 删除后父目录同步失败故障注入:durable cleanup tombstone 独立阻断重启
  • HSCAN/SSCAN 非 UTF-8 field/member、cursor cache、转义尾星与二进制 prefix 分页回归

Windows:

  • cargo +stable fmt --all -- --check
  • CreateFileW(GENERIC_WRITE, FILE_FLAG_BACKUP_SEMANTICS) + FlushFileBuffers 目录同步探针成功
  • 本机 GNU Rust native test 未完成:bundled dlltool.exe 无法启动其导入库子工具;CI 的 Windows job 继续作为正式构建/测试门禁

最新主干组合验证:

  • 合入 origin/main@85eaf27 的 synthetic merge tree:2d52cf42717614b4de304dc8a3242b872bd22b80
  • synthetic merge 上 fmt、严格 Clippy、workspace/all-features tests 全部通过
  • 当前分支合入最新 main 后的实际 tree 与该 synthetic tree 完全一致

Additional Context

  • 主 RocksDB 数据格式、Column Family 名称、key encoding 与 LogIndex 协议不变。
  • snapshot install 失败后不自动删除 recovery marker;安全恢复路径是停机后使用新的 node ID 和干净的 DB/Raft data directories 从健康 leader 重新入群。
  • 崩溃发生在 marker 创建前可能遗留旧格式 .restore_temp_* staging 目录。旧名称没有目标 DB 归属信息,不能安全自动删除;本 PR 不加入可能误删其他实例/用户目录的启发式清理,后续若自动清理应使用带 target manifest 的新格式。

AI assistance: code / tests / docs
Human verification: maintainer review required before merge

Summary by CodeRabbit

  • New Features

    • Preserve binary values for GET, MGET, KEYS, HSCAN, and SSCAN.
    • Add Redis-compatible glob matching (escaping, character classes) for KEYS and scan commands.
    • Make RESP null/null-shape encoding protocol-version aware (RESP2 vs RESP3).
  • Bug Fixes

    • Improve concurrency correctness for multi-key operations (e.g., MSET/MGET) and enforce consistent “storage exclusive” command routing.
    • Harden snapshot install/restore with durable, fail-closed marker handling and safer startup preflight checks.
  • Tests

    • Expanded binary-safe E2E/RESP regression coverage and improved Python integration/CI harness.

AlexStocks and others added 30 commits July 22, 2026 20:03
Wrap the concrete DB in a non-cloneable shutdown owner so Redis waits for active RocksDB background work before the final handle can be released from a callback thread. Expose only a borrowed DB accessor and cover the active compaction-filter drop race with a real RocksDB regression test.

Tested: cargo +1.95-x86_64-pc-windows-msvc test --package storage --quiet

Tested: 5x cargo +1.95-x86_64-pc-windows-msvc test --package storage --lib dropping_last_owner_waits_for_active_compaction_filter_before_reopen -- --test-threads=1

Tested: rustfmt +1.95-x86_64-pc-windows-msvc --edition 2024 --check src/storage/src/redis.rs src/storage/src/data_compaction_filter.rs src/storage/tests/redis_basic_test.rs src/storage/tests/redis_hash_test.rs src/storage/tests/redis_set_test.rs src/storage/tests/redis_zset_test.rs

Tested: cargo +1.95-x86_64-pc-windows-msvc clippy --package storage --lib -- -D warnings -D clippy::unwrap_used

Co-authored-by: OmX <omx@oh-my-codex.dev>
Signal from inside the unique owner Drop before background cancellation so the active-compaction regression cannot pass merely because the drop thread was not scheduled. Route the exported DB/CF macro through Redis::db() and exercise its expansion from an external integration crate.

Tested: 10x cargo +1.95-x86_64-pc-windows-msvc test --package storage --lib dropping_last_owner_waits_for_active_compaction_filter_before_reopen -- --test-threads=1

Tested: cargo +1.95-x86_64-pc-windows-msvc test --package storage --lib --quiet

Tested: cargo +1.95-x86_64-pc-windows-msvc test --package storage --quiet

Tested: rustfmt +1.95-x86_64-pc-windows-msvc --edition 2024 --check src/storage/src/redis.rs src/storage/tests/redis_basic_test.rs

Tested: cargo +1.95-x86_64-pc-windows-msvc clippy --package storage --lib -- -D warnings -D clippy::unwrap_used

Tested: cargo +1.95-x86_64-pc-windows-msvc clippy --package storage --test redis_basic_test --no-deps -- -D warnings -D clippy::unwrap_used

Co-authored-by: OmX <omx@oh-my-codex.dev>
Start a dedicated temporary Kiwi instance for Ubuntu integration tests and make server availability a strict pytest gate in CI. Isolated runs flush the dedicated database around every test while ordinary local runs retain skip and prefix-cleanup behavior.

Constraint: Keep lifecycle management in the test harness and do not modify product or test-case code.

Tested: strict unavailable-server failure; ordinary local skip; missing binary, occupied port, and early server exit paths; bash syntax; staged diff check; isolated list sequence reports 2 passed.

Not-tested: Full Python suite depends on the remaining Redis compatibility fixes in later plan tasks.

Co-authored-by: OmX <omx@oh-my-codex.dev>
Correct MSET compatibility expectations and make concurrent observations use one MGET snapshot. Propagate worker failures through futures so pytest cannot silently pass thread assertion errors.

Constraint: Python integration tests only; no Rust, fixture, or CI harness changes.

Rejected: Sequential GET and LLEN/LRANGE comparisons because they observe different command boundaries.

Confidence: high

Scope-risk: Test expectations and concurrency orchestration only.

Tested: py_compile; pytest --collect-only (55 tests); targeted old-binary probe (2 expected atomicity failures, corrected semantic tests pass).

Not-tested: Full Python suite awaits the matching multi-instance storage fix and rebuilt Kiwi binary.

Co-authored-by: OmX <omx@oh-my-codex.dev>
Let ThreadPoolExecutor futures preserve worker tracebacks, remove the stress-test exception sink, and register exact test keys for unconditional cleanup after failures.

Constraint: Follow-up touches only the four Python integration test files from the prior semantics commit.

Rejected: Prefix-based cleanup and collected exception counters because they can hide worker failures or affect unrelated local keys.

Confidence: high

Scope-risk: Test orchestration and cleanup only.

Tested: WSL venv py_compile; pytest --collect-only (55 tests); git diff --cached --check.

Not-tested: Runtime suite awaits the concurrent Rust storage changes in the shared worktree.

Co-authored-by: OmX <omx@oh-my-codex.dev>
Observe DEL/MSET races with a synchronized MGET worker, namespace every touched Redis key, and bound concurrent and slow tests with pytest-timeout plus future deadlines.

Constraint: Only the four Python integration test files are changed; existing Rust work remains separate.

Rejected: Final-state-only atomicity checks, generic local Redis key names, and unbounded executor waits.

Confidence: high

Scope-risk: Test naming, isolation, synchronization, and timeout behavior only.

Tested: WSL venv py_compile; pytest --collect-only (55 tests); combined Python diff check from 4b43a22^.

Not-tested: Runtime Python suite awaits the final rebuilt Kiwi binary.

Co-authored-by: OmX <omx@oh-my-codex.dev>
AlexStocks and others added 9 commits July 23, 2026 15:20
Bound redis-py connect/read operations and make the DEL/MSET observer handshake wait for a completed mutation before sampling while holding the last mutator until that sample completes.

Constraint: Only tests/python/conftest.py and the MSET concurrency test are changed; Rust work remains separate.

Rejected: Permanently-set activity flags because they do not prove the sample happened before mutation completion.

Confidence: high

Scope-risk: Python test client timeouts and deterministic concurrency synchronization only.

Tested: WSL venv py_compile; pytest --collect-only (55 tests); strict no-server mode exits non-zero without skipping; combined Python diff check.

Not-tested: Runtime suite awaits the final rebuilt Kiwi binary.

Co-authored-by: OmX <omx@oh-my-codex.dev>
Pause both mutators immediately after their first DEL or MSET until the observer validates a single MGET snapshot, then let them finish the remaining iterations before marking mutation completion.

Constraint: Only tests/python/test_mset_concurrent.py changes; client timeout and Rust history remain untouched.

Rejected: End-of-loop overlap gates because they do not force observation before subsequent mutations.

Confidence: high

Scope-risk: Deterministic test synchronization only.

Tested: WSL venv py_compile; pytest --collect-only (55 tests); current and combined Python diff checks.

Not-tested: Runtime suite awaits the final rebuilt Kiwi binary.

Co-authored-by: OmX <omx@oh-my-codex.dev>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 583d407d-81d2-4340-a4a6-0c7f80fc27a2

📥 Commits

Reviewing files that changed from the base of the PR and between 14fd6cb and 98eb08d.

📒 Files selected for processing (4)
  • src/cmd/src/hscan.rs
  • src/cmd/src/keys.rs
  • src/cmd/src/sscan.rs
  • src/storage/src/redis_sets.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/cmd/src/keys.rs
  • src/cmd/src/hscan.rs
  • src/cmd/src/sscan.rs
  • src/storage/src/redis_sets.rs

📝 Walkthrough

Walkthrough

The change removes the Engine abstraction, adds RAII storage-access coordination, hardens snapshot installation, preserves binary Redis and RESP behavior, and introduces a managed Python integration-test lifecycle.

Changes

Storage, Raft, and snapshot flow

Layer / File(s) Summary
Concrete RocksDB ownership
src/storage/*, src/raft/*, src/engine/*, Cargo.toml
Storage and Raft use concrete RocksDB handles; Engine wrappers and workspace membership are removed.
Storage access coordination
src/common/runtime/*, src/storage/src/storage.rs, src/executor/*, src/cmd/*
RAII permits and shared or exclusive guards coordinate requests, background tasks, and command execution.
Transactional snapshot installation
src/storage/src/checkpoint.rs, src/raft/src/state_machine.rs, src/server/src/main.rs
Checkpoint restoration is staged and committed separately, with durable markers, startup preflight, serialized operations, and failure-path tests.

Redis and integration compatibility

Layer / File(s) Summary
Binary-safe commands and scans
src/storage/src/redis_*.rs, src/cmd/src/*scan.rs, src/cmd/src/keys.rs
GET/MGET, scans, and KEYS use raw bytes and Redis-style glob matching.
RESP protocol behavior
src/resp/*, src/net/tests/*
Null encoding follows RESP2 or RESP3 negotiation, with binary-value and wire-format regression tests.
Python integration lifecycle
tests/run_python_integration.sh, tests/python/*, .github/workflows/ci.yml
CI starts Kiwi with temporary paths, waits for readiness, requires server availability, isolates tests, and strengthens concurrency assertions.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: 🧹 Updates

Suggested reviewers: marsevilspirit

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds major snapshot-hardening, RESP/binary-compat, Python CI, and command-gating changes that go beyond #349. Split the unrelated refactors into follow-up PRs or link them to separate issues so this change stays focused on removing Engine.
Docstring Coverage ⚠️ Warning Docstring coverage is 78.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: removing the Engine abstraction from storage.
Description check ✅ Passed The description matches the template sections and includes the issue, change summary, type, checklist, testing, and context.
Linked Issues check ✅ Passed The PR removes Engine, switches storage/Raft to concrete DB types, deletes the engine crate, and adds lifecycle tests, matching #349.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-349-remove-engine

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.

match parser.parse(Bytes::copy_from_slice(&buf[..n])) {
RespParseResult::Complete(data) => return data,
RespParseResult::Incomplete => continue,
RespParseResult::Error(e) => panic!("RESP parse error: {:?}", e),
observed.extend(remaining);

let expected: HashSet<Vec<u8>> = (0..PRODUCERS)
.flat_map(|producer_id| {

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
src/raft/src/log_store_rocksdb.rs (1)

964-973: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace the touched test unwrap() calls.

These writes violate the project-wide clippy::unwrap_used rule. Use descriptive expect(...) calls, or explicitly allow the lint for the test module if that is intentional.

Proposed fix
- engine_clone.write(&batch).unwrap();
+ engine_clone
+     .write(&batch)
+     .expect("test database batch write should succeed");

As per coding guidelines, “Do not use unwrap; clippy::unwrap_used is denied project-wide.”

Also applies to: 1100-1109, 1152-1159, 1226-1235, 1291-1298, 1692-1701, 1772-1781, 1866-1875, 2006-2015, 2131-2140, 2208-2217, 2284-2293, 2373-2382, 2489-2498, 2546-2553, 2611-2620, 2703-2712, 2771-2780, 2838-2847, 2928-2937, 3030-3039, 3106-3115, 3196-3203, 3253-3262, 3318-3325, 3354-3361, 3399-3406

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/raft/src/log_store_rocksdb.rs` around lines 964 - 973, Replace every
touched unwrap call in the test write paths, including the loops around
sequential_entries and the additional referenced ranges, with descriptive expect
messages that identify the failed operation; alternatively, explicitly allow
clippy::unwrap_used for the intentional test module. Preserve the existing write
behavior and error propagation.

Source: Coding guidelines

🧹 Nitpick comments (6)
tests/python/test_list_commands.py (2)

339-339: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add strict=True to zip().

Ruff B905 flags this zip() without an explicit strict parameter; silent truncation on length mismatch would otherwise hide a bug in this binary round-trip test.

♻️ Proposed fix
-        for original, retrieved_item in zip(reversed(binary_items), retrieved):
+        for original, retrieved_item in zip(reversed(binary_items), retrieved, strict=True):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/python/test_list_commands.py` at line 339, Update the zip call in the
binary round-trip test to pass strict=True, ensuring mismatched binary_items and
retrieved lengths raise an error instead of being silently truncated.

Source: Linters/SAST tools


130-178: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer truthy checks over == True.

Ruff flags assert r.lset(...) == True and assert r.ltrim(...) == True (lines 130, 134, 178). Idiomatic Python prefers the direct truthiness check.

♻️ Proposed fix
-        assert r.lset('test_kiwi_list_commands_list', 1, 'modified') == True
+        assert r.lset('test_kiwi_list_commands_list', 1, 'modified')
...
-        assert r.lset('test_kiwi_list_commands_list', -1, 'last') == True
+        assert r.lset('test_kiwi_list_commands_list', -1, 'last')
...
-        assert r.ltrim('test_kiwi_list_commands_list', 1, 4) == True
+        assert r.ltrim('test_kiwi_list_commands_list', 1, 4)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/python/test_list_commands.py` around lines 130 - 178, In the list
command tests, replace equality comparisons against True in the lset assertions
within the surrounding test and the ltrim assertion in test_ltrim with direct
truthiness assertions, preserving the existing calls and expected behavior.

Source: Linters/SAST tools

src/cmd/src/hscan.rs (2)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared TestStream test double. The no-op StreamTrait implementation is duplicated verbatim between the two test modules (and likely other cmd test modules per the executor.rs graph context). Moving it to a shared test-utility module would reduce duplication as more scan-family commands gain binary-safety tests.

  • src/cmd/src/hscan.rs#L149-166: remove the local TestStream and import it from a shared test-utils module instead.
  • src/cmd/src/sscan.rs#L147-164: remove the local TestStream and import it from the same shared test-utils module.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cmd/src/hscan.rs` at line 1, Extract the duplicated no-op TestStream and
its StreamTrait implementation from the hscan and sscan test modules into a
shared test-utility module. Remove both local definitions and update the tests
in hscan and sscan to import and reuse the shared TestStream.

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared cursor/MATCH/COUNT parsing helper. The cursor-parsing plus MATCH/COUNT option-parsing loop is duplicated verbatim between hscan.rs and sscan.rs. A shared helper (e.g. parse_scan_cursor_and_options(argv) -> Result<(u64, Option<Vec<u8>>, Option<usize>), RespData>) would remove the duplication and prevent future drift between the two commands' error messages/semantics.

  • src/cmd/src/hscan.rs#L67-117: replace this block with a call to the shared parsing helper.
  • src/cmd/src/sscan.rs#L67-117: replace this block with a call to the same shared parsing helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cmd/src/hscan.rs` at line 1, Extract the duplicated cursor, MATCH, and
COUNT parsing loop from the HSCAN and SSCAN command handlers into a shared
helper, such as parse_scan_cursor_and_options, returning the parsed cursor and
optional pattern/count or the existing RespData errors. Replace the
corresponding parsing blocks in both hscan and sscan with calls to this helper,
preserving their current validation, error messages, and semantics.
Cargo.toml (1)

77-77: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused windows-sys workspace dependency. windows-sys is not referenced by any Rust source or Cargo.toml in this repository, so version = "0.61.2" with Win32_Storage_FileSystem adds dead dependency metadata.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` at line 77, Remove the unused windows-sys workspace dependency
declaration, including its version and Win32_Storage_FileSystem feature
configuration, from the workspace dependency definitions in Cargo.toml.
src/cmd/src/mget.rs (1)

38-38: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

STORAGE_EXCLUSIVE on MGET serializes a hot read path against all other commands.

Marking MGET exclusive makes it take the write side of command_access_gate, blocking (and being blocked by) every other command including single-key GET and other MGETs. Since the multi-key writers (MSET/MSETNX/DEL) already hold exclusive access, a shared MGET is still mutually excluded from them via the RwLock, so it would still observe atomic multi-key writes without partial results. Exclusive access here appears stronger than needed for cross-instance visibility and can materially reduce read concurrency.

Please confirm whether MGET truly needs exclusive access, or whether shared access is sufficient (which would also require updating the only_multi_instance_atomic_commands_require_exclusive_storage_access contract test in src/cmd/src/lib.rs).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cmd/src/mget.rs` at line 38, The MGET command currently uses
STORAGE_EXCLUSIVE, unnecessarily serializing concurrent reads. Update its
CmdFlags to use shared read access while preserving exclusion from multi-key
writers, and adjust the
only_multi_instance_atomic_commands_require_exclusive_storage_access contract
test in lib.rs to reflect that MGET no longer requires exclusive storage access.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cmd/src/keys.rs`:
- Around line 80-81: Add #![allow(clippy::unwrap_used)] at the beginning of the
inline tests module in keys.rs, immediately inside mod tests, so its existing
unwrap calls are permitted during linting. Follow the established pattern used
by the glob_tests module.

In `@src/raft/src/state_machine.rs`:
- Around line 571-574: In the pause handling flow, create
ResumeBeforeMarkerGuard before calling
self.pause_controller.request_pause().await so cancellation during or
immediately after the pause request always has a resume guard. Preserve the
existing snapshot_operation_gate acquisition and guard lifetime.

---

Outside diff comments:
In `@src/raft/src/log_store_rocksdb.rs`:
- Around line 964-973: Replace every touched unwrap call in the test write
paths, including the loops around sequential_entries and the additional
referenced ranges, with descriptive expect messages that identify the failed
operation; alternatively, explicitly allow clippy::unwrap_used for the
intentional test module. Preserve the existing write behavior and error
propagation.

---

Nitpick comments:
In `@Cargo.toml`:
- Line 77: Remove the unused windows-sys workspace dependency declaration,
including its version and Win32_Storage_FileSystem feature configuration, from
the workspace dependency definitions in Cargo.toml.

In `@src/cmd/src/hscan.rs`:
- Line 1: Extract the duplicated no-op TestStream and its StreamTrait
implementation from the hscan and sscan test modules into a shared test-utility
module. Remove both local definitions and update the tests in hscan and sscan to
import and reuse the shared TestStream.
- Line 1: Extract the duplicated cursor, MATCH, and COUNT parsing loop from the
HSCAN and SSCAN command handlers into a shared helper, such as
parse_scan_cursor_and_options, returning the parsed cursor and optional
pattern/count or the existing RespData errors. Replace the corresponding parsing
blocks in both hscan and sscan with calls to this helper, preserving their
current validation, error messages, and semantics.

In `@src/cmd/src/mget.rs`:
- Line 38: The MGET command currently uses STORAGE_EXCLUSIVE, unnecessarily
serializing concurrent reads. Update its CmdFlags to use shared read access
while preserving exclusion from multi-key writers, and adjust the
only_multi_instance_atomic_commands_require_exclusive_storage_access contract
test in lib.rs to reflect that MGET no longer requires exclusive storage access.

In `@tests/python/test_list_commands.py`:
- Line 339: Update the zip call in the binary round-trip test to pass
strict=True, ensuring mismatched binary_items and retrieved lengths raise an
error instead of being silently truncated.
- Around line 130-178: In the list command tests, replace equality comparisons
against True in the lset assertions within the surrounding test and the ltrim
assertion in test_ltrim with direct truthiness assertions, preserving the
existing calls and expected behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 556711ce-eb57-42a9-bbec-389adeb0002c

📥 Commits

Reviewing files that changed from the base of the PR and between 85eaf27 and 49a9b30.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (66)
  • .github/workflows/ci.yml
  • CLAUDE.md
  • Cargo.toml
  • README.md
  • README_CN.md
  • docs/superpowers/plans/2026-07-22-remove-engine-trait-phase-a.md
  • docs/superpowers/plans/2026-07-22-snapshot-install-transaction-hardening.md
  • docs/superpowers/plans/2026-07-23-python-integration-and-redis-compat.md
  • docs/superpowers/specs/2026-07-21-remove-engine-trait-design.md
  • docs/superpowers/specs/2026-07-23-python-integration-and-redis-compat-design.md
  • src/cmd/src/del.rs
  • src/cmd/src/get.rs
  • src/cmd/src/hscan.rs
  • src/cmd/src/keys.rs
  • src/cmd/src/lib.rs
  • src/cmd/src/mget.rs
  • src/cmd/src/mset.rs
  • src/cmd/src/msetnx.rs
  • src/cmd/src/sscan.rs
  • src/common/runtime/lib.rs
  • src/common/runtime/storage_server.rs
  • src/engine/Cargo.toml
  • src/engine/src/engine.rs
  • src/engine/src/lib.rs
  • src/engine/src/rocksdb_engine.rs
  • src/executor/src/executor.rs
  • src/net/tests/storage_command_e2e_tests.rs
  • src/raft/Cargo.toml
  • src/raft/src/grpc/client.rs
  • src/raft/src/log_store_rocksdb.rs
  • src/raft/src/node.rs
  • src/raft/src/state_machine.rs
  • src/raft/tests/log_store_rocksdb_test.rs
  • src/raft/tests/snapshot_logindex_test.rs
  • src/raft/tests/snapshot_roundtrip_test.rs
  • src/resp/src/encode.rs
  • src/resp/tests/integration_tests.rs
  • src/server/src/main.rs
  • src/storage/Cargo.toml
  • src/storage/src/batch.rs
  • src/storage/src/checkpoint.rs
  • src/storage/src/data_compaction_filter.rs
  • src/storage/src/durable_fs.rs
  • src/storage/src/lib.rs
  • src/storage/src/redis.rs
  • src/storage/src/redis_hashes.rs
  • src/storage/src/redis_lists.rs
  • src/storage/src/redis_multi.rs
  • src/storage/src/redis_sets.rs
  • src/storage/src/redis_strings.rs
  • src/storage/src/storage.rs
  • src/storage/src/storage_impl.rs
  • src/storage/tests/checkpoint_test.rs
  • src/storage/tests/redis_basic_test.rs
  • src/storage/tests/redis_hash_test.rs
  • src/storage/tests/redis_list_test.rs
  • src/storage/tests/redis_set_test.rs
  • src/storage/tests/redis_string_test.rs
  • src/storage/tests/redis_zset_test.rs
  • src/storage/tests/ttl_test.rs
  • tests/python/conftest.py
  • tests/python/test_list_commands.py
  • tests/python/test_mset.py
  • tests/python/test_mset_concurrent.py
  • tests/python/test_wrongtype_errors.py
  • tests/run_python_integration.sh
💤 Files with no reviewable changes (4)
  • src/engine/Cargo.toml
  • src/engine/src/engine.rs
  • src/engine/src/lib.rs
  • src/engine/src/rocksdb_engine.rs

Comment thread src/cmd/src/keys.rs
Comment thread src/raft/src/state_machine.rs Outdated
Share cursor, MATCH, and COUNT parsing across HSCAN, SSCAN, and ZSCAN while preserving binary patterns and existing error semantics. Align the KEYS test module with the project unwrap lint convention.

Constraint: Keep storage scan APIs and command-visible behavior unchanged.

Rejected: Changing MGET locking, snapshot pause handling, Windows durability dependencies, or unrelated review suggestions.

Confidence: high

Scope-risk: Command parser refactor and test lint annotation only.

Tested: WSL Rust 1.95 cargo fmt --check; cargo test -p cmd (105 passed); cargo clippy -p cmd --all-features with warnings and unwrap denied.

Not-tested: Full workspace test suite and remote CI; both will run after push.

Co-authored-by: OmX <omx@oh-my-codex.dev>

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cmd/src/scan_options.rs`:
- Line 64: Remove the #[allow(clippy::unwrap_used)] attribute and update the
affected test unwrap() calls to use descriptive expect(...) messages, preserving
the existing test behavior while complying with the project-wide unwrap
prohibition.

In `@src/cmd/src/zscan.rs`:
- Around line 74-76: The ZSCAN command currently converts option patterns
through String::from_utf8_lossy, corrupting invalid UTF-8 before matching.
Update the pattern handling in the command flow and Storage::zscan/glob-matching
path to preserve raw bytes, and add a command-level test proving a non-UTF-8
pattern such as b"\xff?" matches the original bytes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 38066d81-efd1-4a4b-b7d5-058ba0c30f21

📥 Commits

Reviewing files that changed from the base of the PR and between 49a9b30 and 3f8a3af.

📒 Files selected for processing (6)
  • src/cmd/src/hscan.rs
  • src/cmd/src/keys.rs
  • src/cmd/src/lib.rs
  • src/cmd/src/scan_options.rs
  • src/cmd/src/sscan.rs
  • src/cmd/src/zscan.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/cmd/src/lib.rs
  • src/cmd/src/hscan.rs
  • src/cmd/src/keys.rs
  • src/cmd/src/sscan.rs

Comment thread src/cmd/src/scan_options.rs
Comment thread src/cmd/src/zscan.rs
Comment thread src/raft/src/state_machine.rs Outdated
KiwiSnapshotBuilder {
_storage: Arc::clone(&self.storage_swap),
storage: self.storage_swap.load_full(),
_snapshot_operation: snapshot_operation,

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.

[P1] 不要让完整快照打包过程阻塞 Raft apply

这里把 snapshot_operation_gate 的 guard 存进 builder,而 apply() 也获取同一把锁。OpenRaft 0.9.24 会把 build_snapshot() 放到独立任务执行,因此从 builder 创建开始,到 RocksDB checkpoint、pack_dir_to_vec() 读取整个数据库、persist_current_snapshot() 同步落盘结束之前,后续已提交日志都无法 apply。默认每 5000 条日志触发一次,大库会周期性产生随数据量增长的写停顿和 replication lag;Base 没有这个全程阻塞。建议只在固化 collector state 与 RocksDB checkpoint 的短阶段阻塞 apply,随后释放该门禁;另用 publication/install 门禁串行 tar 发布与 snapshot install。请补故障注入测试,在 checkpoint 完成后阻塞打包/落盘,验证 apply 仍能前进,同时旧 builder 不能覆盖 install 后的 current snapshot。

return t_idx >= text.len();
}
pub(crate) fn glob_match_bytes(pattern: &[u8], text: &[u8]) -> bool {
if text.is_empty() {

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.

[P1] 连续星号也必须匹配空字节串

这个 early return 使 ***** 对空 key/field/member 返回 false;Base 的 matcher 会把剩余全部 * 视为匹配零个字符,Redis 实测 KEYS "**" 会返回空 key,SSCAN ... MATCH "**" 也会返回空 member。本 PR 因此引入了 Redis glob 兼容回归,并在 keys_command_preserves_allkeys_behavior_for_empty_key 与本文件测试中把错误结果固化。建议删除该特判,让空输入也走末尾连续 * 消耗逻辑,并把 */**/*** 对空字节串的断言全部改为 true;同时补 KEYS/HSCAN/SSCAN 命令级回归。

Split snapshot state and publication coordination so apply resumes after checkpoint while snapshot publication remains serialized. Correct repeated-star matching for empty Redis keys and scan entries, with deterministic cancellation and error-path coverage.

Constraint: Preserve fail-closed snapshot install marker semantics and existing public storage APIs.

Rejected: A single long-held snapshot mutex, fixed-duration concurrency assertions, or public test hooks.

Confidence: high

Scope-risk: Raft snapshot locking and cancellation plus empty-byte glob matching in KEYS, HSCAN, and SSCAN.

Tested: WSL Rust 1.95 workspace clippy with warnings and unwrap denied; cargo test -p raft (106 passed); cargo test -p storage glob_tests (10 passed); cargo test -p cmd (107 passed); Windows and WSL cargo fmt --check; git diff --check.

Not-tested: Full workspace cargo test, Python integration suite, and remote CI.

Co-authored-by: OmX <omx@oh-my-codex.dev>
Ok(())
}

async fn assert_pending_after_one_poll<F: Future>(mut future: Pin<&mut F>, message: &str) {
Ok(())
}

async fn assert_pending_after_one_poll<F: Future>(mut future: Pin<&mut F>, message: &str) {
Restore Redis-compatible initial-empty-string handling in the generic glob matcher while retaining the command-level exact single-star special case. Update KEYS, HSCAN, and SSCAN tests to distinguish '*' from repeated-star patterns.

Constraint: Preserve the snapshot coordination fixes, public storage APIs, and all non-empty glob behavior.

Rejected: Updating the existing storage integration tests to accept repeated-star matches, because Redis 8.4 source and a live Redis comparison show that behavior is incompatible.

Confidence: high

Scope-risk: Empty key, hash field, and set member filtering for KEYS, HSCAN, and SSCAN patterns only.

Tested: Live Redis comparison for '*', '**'; WSL Rust 1.95 full workspace cargo test; workspace clippy with warnings and unwrap denied; six focused glob and command tests; cargo fmt --check; git diff --check.

Not-tested: Local address sanitizer instrumentation and remote CI rerun.

Co-authored-by: OmX <omx@oh-my-codex.dev>
@AlexStocks
AlexStocks merged commit 419d07b into main Jul 24, 2026
20 checks passed
@AlexStocks
AlexStocks deleted the codex/issue-349-remove-engine branch July 30, 2026 06: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.

refactor(storage): 移除无实际多后端价值的 Engine trait

2 participants