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 98c051cac fix(stream): allow group and consumer names starting with
digits (#3442)
98c051cac is described below
commit 98c051cacb0f77192f8f086050bcb3f5eee2351f
Author: Songqing Zhang <[email protected]>
AuthorDate: Fri Apr 17 09:13:43 2026 +0800
fix(stream): allow group and consumer names starting with digits (#3442)
Redis does not restrict group or consumer names from starting with
digits. The restriction was kvrocks-specific and broke compatibility.
---
src/types/redis_stream.cc | 6 ------
tests/gocase/unit/type/stream/stream_test.go | 27 ++++++++++++++++++++++++++-
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/src/types/redis_stream.cc b/src/types/redis_stream.cc
index 60606df08..6dc47fba2 100644
--- a/src/types/redis_stream.cc
+++ b/src/types/redis_stream.cc
@@ -685,9 +685,6 @@ rocksdb::Status Stream::AutoClaim(engine::Context &ctx,
const Slice &stream_name
}
rocksdb::Status Stream::CreateGroup(engine::Context &ctx, const Slice
&stream_name,
const StreamXGroupCreateOptions &options,
const std::string &group_name) {
- if (std::isdigit(group_name[0])) {
- return rocksdb::Status::InvalidArgument("group name cannot start with
number");
- }
std::string ns_key = AppendNamespacePrefix(stream_name);
StreamMetadata metadata;
@@ -797,9 +794,6 @@ rocksdb::Status Stream::DestroyGroup(engine::Context &ctx,
const Slice &stream_n
rocksdb::Status Stream::createConsumerWithoutLock(engine::Context &ctx, const
Slice &stream_name,
const std::string
&group_name, const std::string &consumer_name,
int *created_number) {
- if (std::isdigit(consumer_name[0])) {
- return rocksdb::Status::InvalidArgument("consumer name cannot start with
number");
- }
std::string ns_key = AppendNamespacePrefix(stream_name);
StreamMetadata metadata;
rocksdb::Status s = GetMetadata(ctx, ns_key, &metadata);
diff --git a/tests/gocase/unit/type/stream/stream_test.go
b/tests/gocase/unit/type/stream/stream_test.go
index df065ebf9..1590847af 100644
--- a/tests/gocase/unit/type/stream/stream_test.go
+++ b/tests/gocase/unit/type/stream/stream_test.go
@@ -962,7 +962,8 @@ func TestStreamOffset(t *testing.T) {
require.Error(t, rdb.Do(ctx, "XGROUP", "CREAT", streamName,
groupName, "$").Err())
require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName,
groupName, "$", "ENTRIEREAD", "10").Err())
require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName,
groupName, "$", "ENTRIESREAD", "-10").Err())
- require.Error(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName,
"1test-group-c", "$").Err())
+ // Group names may start with a digit (RESP bulk string); same
as Redis — not a syntax error.
+ require.NoError(t, rdb.Do(ctx, "XGROUP", "CREATE", streamName,
"1test-group-c", "$").Err())
require.NoError(t, rdb.Del(ctx, "myStream").Err())
require.NoError(t, rdb.XAdd(ctx, &redis.XAddArgs{Stream:
"myStream", Values: []string{"iTeM", "1", "vAluE", "a"}}).Err())
@@ -978,6 +979,30 @@ func TestStreamOffset(t *testing.T) {
require.Equal(t, int64(0), result)
})
+ t.Run("XGROUP CREATE and CREATECONSUMER with names starting with
digits", func(t *testing.T) {
+ streamKey := "stream-digit-names"
+ require.NoError(t, rdb.Del(ctx, streamKey).Err())
+ require.NoError(t, rdb.XAdd(ctx, &redis.XAddArgs{Stream:
streamKey, Values: []string{"f", "v"}}).Err())
+
+ // Group name starting with a digit should be allowed (matches
Redis behavior)
+ require.NoError(t, rdb.XGroupCreateMkStream(ctx, streamKey,
"1group", "0").Err())
+
+ // Consumer name starting with a digit should be allowed
+ result := rdb.Do(ctx, "XGROUP", "CREATECONSUMER", streamKey,
"1group", "2consumer")
+ require.NoError(t, result.Err())
+
+ // Read with the digit-prefixed group and consumer
+ _, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
+ Group: "1group",
+ Consumer: "2consumer",
+ Streams: []string{streamKey, ">"},
+ Count: 10,
+ }).Result()
+ require.NoError(t, err)
+
+ require.NoError(t, rdb.Del(ctx, streamKey).Err())
+ })
+
t.Run("XGROUP CREATECONSUMER with different kinds of commands", func(t
*testing.T) {
streamName := "test-stream"
groupName := "test-group"