Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:HSET support HMSET #3003

Open
wants to merge 4 commits into
base: unstable
Choose a base branch
from

Conversation

chejinge
Copy link
Collaborator

@chejinge chejinge commented Feb 5, 2025

Summary by CodeRabbit

  • New Features
    • Enhanced hash operations now support handling multiple fields and field-value pairs in a single command.
  • Bug Fixes
    • Improved argument validation and error messaging, ensuring a smoother and more reliable user experience.
  • Tests
    • Added new test case for retrieving a value from a hash and refined existing tests for setting and retrieving multiple fields.
  • Chores
    • Updated workflow actions for uploading and downloading artifacts to the latest version.

Copy link

coderabbitai bot commented Feb 5, 2025

Walkthrough

The changes enhance the hash command processing by introducing new member variables in the header file to store multiple fields and field-value pairs. The implementation in the source file has been adjusted to improve argument validation and handling for commands such as HSetCmd, HGetCmd, HGetallCmd, and HExistsCmd. The modifications distinguish between single and multiple field operations with clear error handling and looping where necessary. Additionally, the workflow file has been updated to use the latest artifact management actions.

Changes

File(s) Summary
include/pika_hash.h Added new private members: std::vector<std::string> fields_ in HGetCmd and std::vector<storage::FieldValue> fields_values_ in HSetCmd to enable handling multiple fields and value pairs.
src/pika_hash.cc Updated argument validation and processing in HSetCmd, HGetCmd, HGetallCmd, and HExistsCmd. Modified DoInitial and Do methods to handle both single and multiple field(-value) operations with proper error checks.
.github/workflows/pika.yml Updated artifact upload action from version 3 to version 4 and added a new step to download artifacts using version 4, maintaining the overall structure of the workflow.
tests/integration/hash_test.go Added new test case "should HGet2" for retrieving a single key and updated "should HSet" to test multiple fields, ensuring comprehensive test coverage.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HSetCmd
    participant Storage

    Client ->> HSetCmd: Send hash set command with args
    HSetCmd ->> HSetCmd: Validate argument count (min 4)
    alt Single field-value pair
        HSetCmd ->> Storage: Process single field-value operation
    else Multiple field-value pairs
        HSetCmd ->> HSetCmd: Loop through pairs and populate fields_values_
        HSetCmd ->> Storage: Process multiple field-value operations
    end
    Storage -->> HSetCmd: Return result
    HSetCmd ->> Client: Return response
Loading
sequenceDiagram
    participant Client
    participant HGetCmd
    participant Storage

    Client ->> HGetCmd: Send hash get command with args
    HGetCmd ->> HGetCmd: Validate argument count (min 3)
    alt Single field
        HGetCmd ->> Storage: Retrieve a single field value
    else Multiple fields
        HGetCmd ->> HGetCmd: Populate fields_ with multiple fields
        HGetCmd ->> Storage: Retrieve multiple field values (HMGet)
    end
    Storage -->> HGetCmd: Return result(s)
    HGetCmd ->> Client: Send response
Loading

Suggested labels

🧹 Updates

Suggested reviewers

  • AlexStocks
  • dingxiaoshuai123
  • Mixficsol

Poem

I am a bunny, hopping through code so bright,
Fields and pairs now dance in the light.
With arguments checked and errors at bay,
My hash commands leap in a joyful display.
In every line I find a hidden delight
As I nibble on changes with all my might!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the ✏️ Feature New feature or request label Feb 5, 2025
Copy link

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

🧹 Nitpick comments (5)
src/pika_hash.cc (5)

51-69: Improve readability by using constants and adding documentation.

The argument validation logic is correct but could be more maintainable.

Consider these improvements:

+// Minimum arguments: HSET key field value
+constexpr size_t HSET_MIN_ARGS = 4;
+
 void HSetCmd::DoInitial() {
-  if (argv_.size() < 4) {
+  if (argv_.size() < HSET_MIN_ARGS) {
     res_.SetRes(CmdRes::kWrongNum, kCmdNameHSet);
     return;
   }
 
   key_ = argv_[1];
-  if (argv_.size() == 4) {
+  // Single field-value pair: HSET key field value
+  if (argv_.size() == HSET_MIN_ARGS) {
     field_ = argv_[2];
     value_ = argv_[3];
   }
 
-  else if (argv_.size() > 4 && argv_.size() % 2 == 0) {
+  // Multiple field-value pairs: HSET key field1 value1 field2 value2 ...
+  else if (argv_.size() > HSET_MIN_ARGS && argv_.size() % 2 == 0) {
     for (size_t i = 2; i < argv_.size(); i += 2) {
       fields_values_.emplace_back(argv_[i], argv_[i + 1]);
     }
   } else {
     res_.SetRes(CmdRes::kWrongNum, kCmdNameHSet);
   }
 }

74-92: Translate comment to English and improve error messages.

The implementation correctly handles both single and multiple field-value pairs but needs some improvements.

Consider these changes:

   if (argv_.size() == 4) {
-    // 处理传统 HSET,设置单个字段-值对
+    // Handle traditional HSET with single field-value pair
     int32_t count = 0;
     s_ = db_->storage()->HSet(key_, field_, value_, &count);
     if (s_.ok()) {
       res_.AppendContent(":" + std::to_string(count));
       AddSlotKey("h", key_, db_);
     } else {
-      res_.SetRes(CmdRes::kErrOther, s_.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to set single field-value pair: " + s_.ToString());
     }
   } else if (argv_.size() > 4 && argv_.size() % 2 == 0) {
     s_ = db_->storage()->HMSet(key_, fields_values_);
     if (s_.ok()) {
       res_.AppendContent(":" + std::to_string(fields_values_.size()));
       AddSlotKey("h", key_, db_);
     } else {
-      res_.SetRes(CmdRes::kErrOther, s_.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to set multiple field-value pairs: " + s_.ToString());
     }
   }

118-132: Improve readability by using constants and adding documentation.

The argument validation logic is correct but could be more maintainable.

Consider these improvements:

+// Minimum arguments: HGET key field
+constexpr size_t HGET_MIN_ARGS = 3;
+
 void HGetCmd::DoInitial() {
-  if (argv_.size() < 3) {
+  if (argv_.size() < HGET_MIN_ARGS) {
     res_.SetRes(CmdRes::kWrongNum, kCmdNameHGet);
     return;
   }
 
   key_ = argv_[1];
-  if (argv_.size() == 3) {
+  // Single field: HGET key field
+  if (argv_.size() == HGET_MIN_ARGS) {
     field_ = argv_[2];
   }
 
-  else if (argv_.size() > 3) {
+  // Multiple fields: HGET key field1 field2 ...
+  else if (argv_.size() > HGET_MIN_ARGS) {
     for (size_t i = 2; i < argv_.size(); ++i) {
       fields_.push_back(argv_[i]);
     }
   }
 }

137-167: Improve error messages and optimize single field case.

The implementation correctly handles both single and multiple fields but could be improved.

Consider these improvements:

   if (argv_.size() == 3) {
-    std::string value;
-    s_ = db_->storage()->HGet(key_, field_, &value);
+    // Reuse fields_ vector to avoid code duplication
+    fields_.clear();
+    fields_.push_back(field_);
+    std::vector<storage::ValueStatus> values;
+    s_ = db_->storage()->HMGet(key_, fields_, &values);
 
     if (s_.ok()) {
-      res_.AppendStringLenUint64(value.size());
-      res_.AppendContent(value);
+      res_.AppendStringLenUint64(values[0].value.size());
+      res_.AppendContent(values[0].value);
     } else if (s_.IsNotFound()) {
       res_.AppendContent("$-1");
     } else {
-      res_.SetRes(CmdRes::kErrOther, s_.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to get field value: " + s_.ToString());
     }
   }
   else if (argv_.size() > 3) {
     std::vector<storage::ValueStatus> values;
     s_ = db_->storage()->HMGet(key_, fields_, &values);
 
     if (s_.ok()) {
       res_.AppendArrayLen(values.size());
       for (const auto& vs : values) {
         if (vs.status.ok()) {
           res_.AppendStringLenUint64(vs.value.size());
           res_.AppendContent(vs.value);
         } else {
           res_.AppendContent("$-1");
         }
       }
     } else {
-      res_.SetRes(CmdRes::kErrOther, s_.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to get multiple field values: " + s_.ToString());
     }
   }

172-202: Improve error messages and optimize single field case in cache handling.

The cache implementation correctly handles both single and multiple fields but could be improved.

Consider these improvements:

   if (argv_.size() == 3) {
-    std::string value;
-    auto s = db_->cache()->HGet(key_, field_, &value);
+    // Reuse fields_ vector to avoid code duplication
+    fields_.clear();
+    fields_.push_back(field_);
+    std::vector<storage::ValueStatus> values;
+    auto s = db_->cache()->HMGet(key_, fields_, &values);
     if (s.ok()) {
-      res_.AppendStringLen(value.size());
-      res_.AppendContent(value);
+      res_.AppendStringLen(values[0].value.size());
+      res_.AppendContent(values[0].value);
     } else if (s.IsNotFound()) {
       res_.SetRes(CmdRes::kCacheMiss);
     } else {
-      res_.SetRes(CmdRes::kErrOther, s.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to get field value from cache: " + s.ToString());
     }
   }
   else if (argv_.size() > 3) {
     std::vector<storage::ValueStatus> values;
     auto s = db_->cache()->HMGet(key_, fields_, &values);
     if (s.ok()) {
       res_.AppendArrayLen(values.size());
       for (const auto& vs : values) {
         if (vs.status.ok()) {
           res_.AppendStringLen(vs.value.size());
           res_.AppendContent(vs.value);
         } else {
           res_.AppendContent("$-1");
         }
       }
     } else if (s.IsNotFound()) {
       res_.SetRes(CmdRes::kCacheMiss);
     } else {
-      res_.SetRes(CmdRes::kErrOther, s.ToString());
+      res_.SetRes(CmdRes::kErrOther, "Failed to get multiple field values from cache: " + s.ToString());
     }
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fdcdd8f and 3ab956d.

📒 Files selected for processing (2)
  • include/pika_hash.h (2 hunks)
  • src/pika_hash.cc (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build_on_macos
  • GitHub Check: Analyze (go)
  • GitHub Check: build_on_centos
🔇 Additional comments (2)
include/pika_hash.h (2)

62-62: LGTM! Good addition for supporting multiple field operations.

The new fields_ vector member enables handling multiple fields while maintaining backward compatibility with single field operations.


108-108: LGTM! Well-structured addition for supporting multiple field-value pairs.

The new fields_values_ vector member using storage::FieldValue struct provides a clean way to handle multiple field-value pairs while maintaining backward compatibility.

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c60a028 and ff4de98.

📒 Files selected for processing (2)
  • .github/workflows/pika.yml (2 hunks)
  • tests/integration/hash_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/pika.yml
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build_on_macos
  • GitHub Check: build_on_ubuntu
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (go)
  • GitHub Check: build_on_centos
🔇 Additional comments (1)
tests/integration/hash_test.go (1)

277-310: LGTM! Comprehensive struct testing.

The test thoroughly validates hash operations with structs, covering:

  • Various field types (string, int16, time.Duration, interface{})
  • Redis tag options (omitempty, ignored fields)
  • Nil value handling
  • Multiple hash keys

Comment on lines +218 to +225
It("should HGet2", func() {
err := client.HSet(ctx, "hash", "key1", "hello1").Err()
Expect(err).NotTo(HaveOccurred())

vals, err := client.HMGet(ctx, "hash", "key1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(vals).To(Equal([]interface{}{"hello1"}))
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate test case.

This test case is identical to the existing "should HMGet" test (lines 209-216) and doesn't add any new test coverage.

Remove this redundant test case to maintain a clean and maintainable test suite.

Copy link

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

🧹 Nitpick comments (3)
src/pika_hash.cc (3)

75-77: Inconsistent response format between single and multiple field operations.

The response format differs between single field HSet (:count) and multiple field HMSet (:fields_values_.size()). Consider standardizing the response format for better client compatibility.

-      res_.AppendContent(":" + std::to_string(count));
+      res_.AppendContent(":" + std::to_string(1));
       AddSlotKey("h", key_, db_);
     } else {

Also applies to: 84-85


104-109: Cache update could be more efficient.

The cache update logic performs separate checks for single and multiple field operations. Consider consolidating the cache update logic to reduce code duplication and improve maintainability.

-    if (argv_.size() == 4) {
-      db_->cache()->HSetIfKeyExist(key_, field_, value_);
-    }
-    else if (argv_.size() > 4 && argv_.size() % 2 == 0) {
-      db_->cache()->HMSet(key_, fields_values_);
-    }
+    if (argv_.size() == 4) {
+      fields_values_ = {{field_, value_}};
+    }
+    db_->cache()->HMSet(key_, fields_values_);

138-145: Standardize error handling between single and multiple field operations.

The error handling differs between single field HGet and multiple field HMGet operations. Consider standardizing the error handling for better maintainability.

     if (s_.ok()) {
-      res_.AppendStringLenUint64(value.size());
-      res_.AppendContent(value);
+      res_.AppendArrayLen(1);
+      if (value.empty()) {
+        res_.AppendContent("$-1");
+      } else {
+        res_.AppendStringLenUint64(value.size());
+        res_.AppendContent(value);
+      }
     } else if (s_.IsNotFound()) {
-      res_.AppendContent("$-1");
+      res_.AppendArrayLen(0);
     } else {
       res_.SetRes(CmdRes::kErrOther, s_.ToString());
     }

Also applies to: 151-163

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff4de98 and 9457e64.

📒 Files selected for processing (1)
  • src/pika_hash.cc (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build_on_ubuntu
  • GitHub Check: build_on_centos
  • GitHub Check: build_on_macos
  • GitHub Check: Analyze (go)

Copy link

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

🧹 Nitpick comments (2)
src/pika_hash.cc (2)

62-68: Consider adding size limit validation.

While the argument count validation is correct, consider adding a maximum limit check for the number of field-value pairs to prevent memory issues with extremely large inputs.

 else if (argv_.size() > 4 && argv_.size() % 2 == 0) {
+  const size_t MAX_PAIRS = 1000000;  // Adjust based on your requirements
+  if ((argv_.size() - 2) / 2 > MAX_PAIRS) {
+    res_.SetRes(CmdRes::kErrOther, "Too many field-value pairs");
+    return;
+  }
   for (size_t i = 2; i < argv_.size(); i += 2) {
     fields_values_.emplace_back(argv_[i], argv_[i + 1]);
   }

115-129: Consider adding size limit validation for multiple fields.

While the argument handling is correct, consider adding a maximum limit check for the number of fields to prevent memory issues with extremely large inputs.

 else if (argv_.size() > 3) {
+  const size_t MAX_FIELDS = 1000000;  // Adjust based on your requirements
+  if (argv_.size() - 2 > MAX_FIELDS) {
+    res_.SetRes(CmdRes::kErrOther, "Too many fields");
+    return;
+  }
   for (size_t i = 2; i < argv_.size(); ++i) {
     fields_.push_back(argv_[i]);
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9457e64 and 947916b.

📒 Files selected for processing (1)
  • src/pika_hash.cc (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: build_on_macos
  • GitHub Check: build_on_centos
  • GitHub Check: Analyze (go)
  • GitHub Check: build_on_ubuntu
🔇 Additional comments (2)
src/pika_hash.cc (2)

51-54: LGTM! Improved argument validation.

The argument validation now correctly handles both single field-value pair and multiple field-value pairs cases.


134-164: LGTM! Well-structured response handling.

The response handling is well-structured and correctly formats responses for both single and multiple field cases. The error handling is comprehensive and follows consistent patterns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✏️ Feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants