This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new e5719251 feat(config): implement integer config option with unit
(#2448)
e5719251 is described below
commit e57192517b76515692a45726df7a0e071a7e6542
Author: Twice <[email protected]>
AuthorDate: Sun Jul 28 12:19:01 2024 +0900
feat(config): implement integer config option with unit (#2448)
---
kvrocks.conf | 1 +
src/common/bit_util.h | 18 +++++++
src/common/parse_util.cc | 20 +++----
src/config/config.cc | 8 +--
src/config/config_type.h | 92 +++++++++++++++++++++++++++++++++
src/types/redis_stream.cc | 3 +-
tests/gocase/unit/config/config_test.go | 6 +++
7 files changed, 130 insertions(+), 18 deletions(-)
diff --git a/kvrocks.conf b/kvrocks.conf
index 4d953c38..11f11fb6 100644
--- a/kvrocks.conf
+++ b/kvrocks.conf
@@ -63,6 +63,7 @@ repl-namespace-enabled no
# By default, the max length of bulk string is limited to 512MB. If you want to
# change this limit to a different value(must >= 1MiB), you can use the
following configuration.
+# It can be just an integer (e.g. 10000000), or an integer followed by a unit
(e.g. 12M, 7G, 2T).
#
# proto-max-bulk-len 536870912
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 9c23d78b..6663ed95 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -20,6 +20,14 @@
#pragma once
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "encoding.h"
+#include "status.h"
+#include "type_util.h"
+
namespace util {
/* Count number of bits set in the binary array pointed by 's' and long
@@ -146,4 +154,14 @@ inline int64_t RawBitpos(const uint8_t *c, int64_t count,
bool bit) {
} // namespace msb
+// num << bit <= MAX -> num <= MAX >> bit
+template <typename T, typename U>
+StatusOr<T> CheckedShiftLeft(T num, U bit) {
+ if (num <= std::numeric_limits<T>::max() >> bit) {
+ return num << bit;
+ }
+
+ return {Status::NotOK, "arithmetic overflow"};
+}
+
} // namespace util
diff --git a/src/common/parse_util.cc b/src/common/parse_util.cc
index 852c80f3..a7359c78 100644
--- a/src/common/parse_util.cc
+++ b/src/common/parse_util.cc
@@ -22,15 +22,7 @@
#include <limits>
-// num << bit <= MAX -> num <= MAX >> bit
-template <typename T, typename U>
-StatusOr<T> CheckedShiftLeft(T num, U bit) {
- if (num <= std::numeric_limits<T>::max() >> bit) {
- return num << bit;
- }
-
- return {Status::NotOK, "arithmetic overflow"};
-}
+#include "bit_util.h"
StatusOr<std::uint64_t> ParseSizeAndUnit(const std::string &v) {
auto [num, rest] = GET_OR_RET(TryParseInt<std::uint64_t>(v.c_str(), 10));
@@ -38,15 +30,15 @@ StatusOr<std::uint64_t> ParseSizeAndUnit(const std::string
&v) {
if (*rest == 0) {
return num;
} else if (util::EqualICase(rest, "k")) {
- return CheckedShiftLeft(num, 10);
+ return util::CheckedShiftLeft(num, 10);
} else if (util::EqualICase(rest, "m")) {
- return CheckedShiftLeft(num, 20);
+ return util::CheckedShiftLeft(num, 20);
} else if (util::EqualICase(rest, "g")) {
- return CheckedShiftLeft(num, 30);
+ return util::CheckedShiftLeft(num, 30);
} else if (util::EqualICase(rest, "t")) {
- return CheckedShiftLeft(num, 40);
+ return util::CheckedShiftLeft(num, 40);
} else if (util::EqualICase(rest, "p")) {
- return CheckedShiftLeft(num, 50);
+ return util::CheckedShiftLeft(num, 50);
}
return {Status::NotOK, "encounter unexpected unit"};
diff --git a/src/config/config.cc b/src/config/config.cc
index 58a15594..285130df 100644
--- a/src/config/config.cc
+++ b/src/config/config.cc
@@ -25,6 +25,7 @@
#include <strings.h>
#include <algorithm>
+#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
@@ -187,7 +188,8 @@ Config::Config() {
{"redis-cursor-compatible", false, new
YesNoField(&redis_cursor_compatible, true)},
{"resp3-enabled", false, new YesNoField(&resp3_enabled, false)},
{"repl-namespace-enabled", false, new
YesNoField(&repl_namespace_enabled, false)},
- {"proto-max-bulk-len", false, new UInt64Field(&proto_max_bulk_len, 512 *
MiB, 1 * MiB, UINT64_MAX)},
+ {"proto-max-bulk-len", false,
+ new IntWithUnitField<uint64_t>(&proto_max_bulk_len, std::to_string(512
* MiB), 1 * MiB, UINT64_MAX)},
{"json-max-nesting-depth", false, new IntField(&json_max_nesting_depth,
1024, 0, INT_MAX)},
{"json-storage-format", false,
new EnumField<JsonStorageFormat>(&json_storage_format,
json_storage_formats, JsonStorageFormat::JSON)},
@@ -885,7 +887,7 @@ Status Config::Set(Server *srv, std::string key, const
std::string &value) {
if (!s.IsOK()) return s.Prefixed("invalid value");
}
- auto origin_value = field->ToString();
+ auto origin_value = field->ToStringForRewrite();
auto s = field->Set(value);
if (!s.IsOK()) return s.Prefixed("failed to set new value");
@@ -922,7 +924,7 @@ Status Config::Rewrite(const std::map<std::string,
std::string> &tokens) {
// so skip it here to avoid rewriting it as new item.
continue;
}
- new_config[iter.first] = iter.second->ToString();
+ new_config[iter.first] = iter.second->ToStringForRewrite();
}
std::string namespace_prefix = "namespace.";
diff --git a/src/config/config_type.h b/src/config/config_type.h
index 2b0d2deb..299e1459 100644
--- a/src/config/config_type.h
+++ b/src/config/config_type.h
@@ -29,6 +29,7 @@
#include <string>
#include <utility>
+#include "bit_util.h"
#include "parse_util.h"
#include "status.h"
#include "string_util.h"
@@ -62,6 +63,7 @@ class ConfigField {
virtual ~ConfigField() = default;
virtual std::string Default() const = 0;
virtual std::string ToString() const = 0;
+ virtual std::string ToStringForRewrite() const { return ToString(); }
virtual Status Set(const std::string &v) = 0;
virtual Status ToNumber(int64_t *n) const { return {Status::NotOK, "not
supported"}; }
virtual Status ToBool(bool *b) const { return {Status::NotOK, "not
supported"}; }
@@ -127,6 +129,7 @@ class IntegerField : public ConfigField {
public:
IntegerField(IntegerType *receiver, IntegerType n, IntegerType min,
IntegerType max)
: receiver_(receiver), default_(n), min_(min), max_(max) {
+ CHECK(min <= n && n <= max);
*receiver_ = n;
}
~IntegerField() override = default;
@@ -251,3 +254,92 @@ class EnumField : public ConfigField {
return {};
}
};
+
+enum class IntUnit { None = 0, K = 10, M = 20, G = 30, T = 40, P = 50 };
+
+template <typename T>
+class IntWithUnitField : public ConfigField {
+ public:
+ IntWithUnitField(T *receiver, std::string default_val, T min, T max)
+ : receiver_(receiver), default_val_(std::move(default_val)), min_(min),
max_(max) {
+ CHECK(ReadFrom(default_val_));
+ }
+
+ std::string Default() const override { return default_val_; }
+ std::string ToString() const override { return std::to_string(*receiver_); }
+ std::string ToStringForRewrite() const override { return
ToString(*receiver_, current_unit_); }
+
+ Status ToNumber(int64_t *n) const override {
+ *n = static_cast<int64_t>(*receiver_);
+ return Status::OK();
+ }
+
+ Status Set(const std::string &v) override { return ReadFrom(v); }
+
+ Status ReadFrom(const std::string &val) {
+ auto [num, rest] = GET_OR_RET(TryParseInt<T>(val.c_str(), 10));
+
+ if (*rest == 0) {
+ *receiver_ = num;
+ current_unit_ = IntUnit::None;
+ } else if (util::EqualICase(rest, "k")) {
+ *receiver_ = GET_OR_RET(util::CheckedShiftLeft(num, 10));
+ current_unit_ = IntUnit::K;
+ } else if (util::EqualICase(rest, "m")) {
+ *receiver_ = GET_OR_RET(util::CheckedShiftLeft(num, 20));
+ current_unit_ = IntUnit::M;
+ } else if (util::EqualICase(rest, "g")) {
+ *receiver_ = GET_OR_RET(util::CheckedShiftLeft(num, 30));
+ current_unit_ = IntUnit::G;
+ } else if (util::EqualICase(rest, "t")) {
+ *receiver_ = GET_OR_RET(util::CheckedShiftLeft(num, 40));
+ current_unit_ = IntUnit::T;
+ } else if (util::EqualICase(rest, "p")) {
+ *receiver_ = GET_OR_RET(util::CheckedShiftLeft(num, 50));
+ current_unit_ = IntUnit::P;
+ } else {
+ return {Status::NotOK, fmt::format("encounter unexpected unit: `{}`",
rest)};
+ }
+
+ if (min_ > *receiver_ || max_ < *receiver_) {
+ return {Status::NotOK, fmt::format("this config value should be between
{} and {}", min_, max_)};
+ }
+
+ return Status::OK();
+ }
+
+ static std::string ToString(T val, IntUnit current_unit) {
+ std::string unit_str;
+
+ switch (current_unit) {
+ case IntUnit::None:
+ unit_str = "";
+ break;
+ case IntUnit::K:
+ unit_str = "K";
+ break;
+ case IntUnit::M:
+ unit_str = "M";
+ break;
+ case IntUnit::G:
+ unit_str = "G";
+ break;
+ case IntUnit::T:
+ unit_str = "T";
+ break;
+ case IntUnit::P:
+ unit_str = "P";
+ break;
+ }
+
+ return std::to_string(val >> int(current_unit)) + unit_str;
+ }
+
+ private:
+ // NOTE: the receiver here will get the converted integer (e.g. "10k" -> get
10240)
+ T *receiver_;
+ std::string default_val_;
+ T min_;
+ T max_;
+ IntUnit current_unit_ = IntUnit::None;
+};
diff --git a/src/types/redis_stream.cc b/src/types/redis_stream.cc
index 4df2a2ec..40f42012 100644
--- a/src/types/redis_stream.cc
+++ b/src/types/redis_stream.cc
@@ -1759,7 +1759,8 @@ rocksdb::Status
Stream::GetPendingEntries(StreamPendingOptions &options, StreamG
}
if (options.with_count) {
- ext_results.push_back({entry_id, pel_entry.last_delivery_time_ms,
pel_entry.last_delivery_count, consumer_name});
+ ext_results.push_back(
+ {entry_id, {pel_entry.last_delivery_time_ms,
pel_entry.last_delivery_count, consumer_name}});
ext_result_count++;
continue;
}
diff --git a/tests/gocase/unit/config/config_test.go
b/tests/gocase/unit/config/config_test.go
index a902bb89..0c434e72 100644
--- a/tests/gocase/unit/config/config_test.go
+++ b/tests/gocase/unit/config/config_test.go
@@ -266,6 +266,12 @@ func TestChangeProtoMaxBulkLen(t *testing.T) {
require.NoError(t, err)
require.EqualValues(t, "2097152", vals["proto-max-bulk-len"])
+ // change to 100MB
+ require.NoError(t, rdb.ConfigSet(ctx, "proto-max-bulk-len",
"100M").Err())
+ vals, err = rdb.ConfigGet(ctx, "proto-max-bulk-len").Result()
+ require.NoError(t, err)
+ require.EqualValues(t, "104857600", vals["proto-max-bulk-len"])
+
// Must be >= 1MB
require.Error(t, rdb.ConfigSet(ctx, "proto-max-bulk-len", "1024").Err())
}