Skip to content

fix:When Codis deletes a key, deletion fails for the key in Pika slot…#3241

Open
chejinge wants to merge 1 commit intoOpenAtomFoundation:unstablefrom
chejinge:fix_slot_del
Open

fix:When Codis deletes a key, deletion fails for the key in Pika slot…#3241
chejinge wants to merge 1 commit intoOpenAtomFoundation:unstablefrom
chejinge:fix_slot_del

Conversation

@chejinge
Copy link
Copy Markdown
Collaborator

@chejinge chejinge commented Apr 16, 2026

… key.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling in slot cleanup operations to gracefully ignore certain storage errors.
    • Enhanced deletion operations with better type detection and validation.
  • Performance Improvements

    • Optimized deletion process through pre-fetched key type information.
  • Chores

    • Removed obsolete configuration comment.

@github-actions github-actions Bot added the ☢️ Bug Something isn't working label Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

This change enhances the deletion mechanism by introducing type-aware slot cleanup. A pre-pass collects key types during deletion, enabling the use of RemSlotKeyByType for slot-key removal instead of the generic RemSlotKey. Error handling is improved to ignore IsNotFound errors during slot set operations, and the storage layer now supports optional type collection via an overloaded Del method.

Changes

Cohort / File(s) Summary
Storage Layer Extension
src/storage/include/storage/storage.h, src/storage/src/storage.cc
Added overloaded Del method that optionally collects key type information via an out-parameter. Original Del(keys) preserved as a wrapper delegating to the new overload with nullptr.
Deletion Command Enhancement
src/pika_kv.cc
Modified DelCmd::Do() to perform a pre-pass collecting key types into key_type_map, then iterate over type-key pairs for slot cleanup using RemSlotKeyByType instead of RemSlotKey.
Slot Command Updates
src/pika_slot_command.cc
Updated RemSlotKeyByType to ignore RocksDB IsNotFound() errors on SRem operations; simplified GetKeyType to treat !s.ok() or kNones type as error and return -1 with consolidated logging.
Build Configuration
src/pstd/CMakeLists.txt
Removed comment line # 强制使用用户自定的 memcmp from compile options.

Sequence Diagram

sequenceDiagram
    participant DelCmd as DelCmd::Do()
    participant Storage as Storage Layer
    participant DB as Database Instance
    participant SlotCmd as SlotCommand

    DelCmd->>DelCmd: Pre-pass: iterate keys_<br/>GetKeyType(key) for each key
    DelCmd->>DelCmd: Build key_type_map<br/>only for keys with type > 0
    
    DelCmd->>Storage: Del(keys_, &key_types)
    Storage->>DB: For each key: GetType(key)
    DB-->>Storage: Return type info
    Storage->>Storage: Collect type info<br/>in key_types
    Storage->>DB: Del(key) for each key
    DB-->>Storage: Deletion status
    Storage-->>DelCmd: Return deletion count

    DelCmd->>SlotCmd: For each (key, type)<br/>in key_type_map
    SlotCmd->>DB: RemSlotKeyByType(type, key)
    DB->>DB: SRem from slot set<br/>and tag set
    DB-->>SlotCmd: Status (ignore IsNotFound)
    SlotCmd-->>DelCmd: Cleanup complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

☢️ Bug

Suggested reviewers

  • wangshao1
  • dingxiaoshuai123

Poem

🐰 With whiskers twitching, types we trace,
Before deletion finds its place,
Each slot now knows what left the store,
No orphaned keys to clean up more!
A hop, a skip—the bugs all gone,
Another day, another dawn! 🌙

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 directly addresses the core issue: fixing slot key deletion failure when Codis deletes keys in Pika, which aligns with the changes made across multiple files to improve slot-key removal via type information.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/pika_kv.cc (1)

246-254: ⚠️ Potential issue | 🟡 Minor

DelCmd::Split does not perform slot cleanup.

The Split method at lines 246-254 calls db_->storage()->Del(hint_keys.keys) and increments split_res_, but does not remove keys from slot tracking. The Do() method explicitly calls RemSlotKeyByType(type, key, db_) for each deleted key (lines 227-229), but Split() lacks this cleanup entirely.

While Split/Merge appear to be unused in current execution paths (no invocations found in the codebase), this inconsistency should still be addressed for correctness: either add the slot cleanup to Split() to match Do(), or document why it is intentionally omitted.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pika_kv.cc` around lines 246 - 254, DelCmd::Split currently calls
db_->storage()->Del(hint_keys.keys) and updates split_res_ or res_ but does not
remove deleted keys from slot tracking; update Split to iterate over
hint_keys.keys (the same way Do() does) and call RemSlotKeyByType(type, key,
db_) for each deleted key so slot metadata is cleaned up, ensuring behavior
matches DelCmd::Do; preserve existing error handling (res_.SetRes) and update
split_res_ only for successful deletions.
🧹 Nitpick comments (1)
src/storage/include/storage/storage.h (1)

1015-1017: Misleading comment for the new Del overload.

The comment "Return the key exists type count" appears to be a leftover or copy-paste error. This function returns the count of successfully deleted keys, not the count of existing key types. The key_types parameter collects type information as a side effect.

📝 Suggested comment fix
-  // Return the key exists type count
-  // Del keys and return deleted keys' types
+  // Removes the specified keys and optionally collects their types before deletion.
+  // `@param` key_types If non-null, populated with (key, type_tag) pairs for each key.
+  // `@return` The number of keys that were successfully removed, or -1 on error.
   int64_t Del(const std::vector<std::string>& keys, std::vector<std::pair<std::string, std::string>>* key_types);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/storage/include/storage/storage.h` around lines 1015 - 1017, Update the
misleading comment for the Del overload (int64_t Del(const
std::vector<std::string>& keys, std::vector<std::pair<std::string,
std::string>>* key_types);) to accurately state that the function deletes the
provided keys and returns the count of keys successfully deleted, and that the
optional key_types output parameter is populated with the deleted keys' type
information as a side effect (not that it returns "key exists type count").
🤖 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/pika_kv.cc`:
- Around line 208-229: DelCmd::Do currently does a separate GetKeyType loop then
calls db_->storage()->Del(keys_) which causes a TOCTOU race; replace that
two-step approach by calling the new Storage::Del(keys_, &key_types) overload
(collecting deletion count and types atomically) instead of GetKeyType and
db_->storage()->Del(keys_); then use the returned key_types map to call
RemSlotKeyByType(type, key, db_) for each entry, keep the existing
res_.AppendInteger(count) and s_ = rocksdb::Status::OK() semantics, and remove
the old GetKeyType loop and the separate Del call.

---

Outside diff comments:
In `@src/pika_kv.cc`:
- Around line 246-254: DelCmd::Split currently calls
db_->storage()->Del(hint_keys.keys) and updates split_res_ or res_ but does not
remove deleted keys from slot tracking; update Split to iterate over
hint_keys.keys (the same way Do() does) and call RemSlotKeyByType(type, key,
db_) for each deleted key so slot metadata is cleaned up, ensuring behavior
matches DelCmd::Do; preserve existing error handling (res_.SetRes) and update
split_res_ only for successful deletions.

---

Nitpick comments:
In `@src/storage/include/storage/storage.h`:
- Around line 1015-1017: Update the misleading comment for the Del overload
(int64_t Del(const std::vector<std::string>& keys,
std::vector<std::pair<std::string, std::string>>* key_types);) to accurately
state that the function deletes the provided keys and returns the count of keys
successfully deleted, and that the optional key_types output parameter is
populated with the deleted keys' type information as a side effect (not that it
returns "key exists type count").
🪄 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

Run ID: d1ad9b27-0725-4915-bf65-eb207cd79d43

📥 Commits

Reviewing files that changed from the base of the PR and between bc8de39 and 3850f2e.

📒 Files selected for processing (5)
  • src/pika_kv.cc
  • src/pika_slot_command.cc
  • src/pstd/CMakeLists.txt
  • src/storage/include/storage/storage.h
  • src/storage/src/storage.cc
💤 Files with no reviewable changes (1)
  • src/pstd/CMakeLists.txt

Comment thread src/pika_kv.cc
Comment on lines 208 to 229
void DelCmd::Do() {
// 先获取所有键的类型
std::map<std::string, std::string> key_type_map;
for (const auto& key : keys_) {
std::string type;
int result = GetKeyType(key, type, db_);
if (result > 0) {
key_type_map[key] = type;
}
}

// 然后删除键
int64_t count = db_->storage()->Del(keys_);

if (count >= 0) {
res_.AppendInteger(count);
s_ = rocksdb::Status::OK();
std::vector<std::string>::const_iterator it;
for (it = keys_.begin(); it != keys_.end(); it++) {
RemSlotKey(*it, db_);

// 使用预先获取的类型信息从 slotKey 中移除
for (const auto& [key, type] : key_type_map) {
RemSlotKeyByType(type, key, db_);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition between type lookup and deletion; consider using the new Del overload.

There's a TOCTOU (time-of-check-to-time-of-use) race: between GetKeyType (lines 211-217) and Del (line 220), another thread could delete or modify the key, causing:

  1. A key to be deleted but not removed from the slot (if deleted between GetKeyType and Del)
  2. Incorrect type being used for slot removal (if key type changed)

Additionally, the new Storage::Del(keys, &key_types) overload (added in this PR) collects types atomically with deletion. Consider using it to eliminate the race and the redundant type lookups:

🔧 Suggested fix using the new Del overload
 void DelCmd::Do() {
-  // 先获取所有键的类型
-  std::map<std::string, std::string> key_type_map;
-  for (const auto& key : keys_) {
-    std::string type;
-    int result = GetKeyType(key, type, db_);
-    if (result > 0) {
-      key_type_map[key] = type;
-    }
-  }
-  
-  // 然后删除键
-  int64_t count = db_->storage()->Del(keys_);
+  // Delete keys and collect their types atomically
+  std::vector<std::pair<std::string, std::string>> key_types;
+  int64_t count = db_->storage()->Del(keys_, &key_types);
   
   if (count >= 0) {
     res_.AppendInteger(count);
     s_ = rocksdb::Status::OK();
     
-    // 使用预先获取的类型信息从 slotKey 中移除
-    for (const auto& [key, type] : key_type_map) {
-      RemSlotKeyByType(type, key, db_);
+    // Remove from slotKey using the collected type information
+    for (const auto& [key, type] : key_types) {
+      if (!type.empty()) {
+        RemSlotKeyByType(type, key, db_);
+      }
     }
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pika_kv.cc` around lines 208 - 229, DelCmd::Do currently does a separate
GetKeyType loop then calls db_->storage()->Del(keys_) which causes a TOCTOU
race; replace that two-step approach by calling the new Storage::Del(keys_,
&key_types) overload (collecting deletion count and types atomically) instead of
GetKeyType and db_->storage()->Del(keys_); then use the returned key_types map
to call RemSlotKeyByType(type, key, db_) for each entry, keep the existing
res_.AppendInteger(count) and s_ = rocksdb::Status::OK() semantics, and remove
the old GetKeyType loop and the separate Del call.

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

Labels

☢️ Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants