This is an automated email from the ASF dual-hosted git repository.

hulk 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 4ecb35b9 Fix should use the minimum compatible RDB version when 
dumping the payload (#2252)
4ecb35b9 is described below

commit 4ecb35b9c11a6b2cd961222bbf7a17d4d79cc8a3
Author: hulk <[email protected]>
AuthorDate: Tue Apr 16 15:24:24 2024 +0800

    Fix should use the minimum compatible RDB version when dumping the payload 
(#2252)
    
    Currently, we're using the maximum RDB version(12) when dumping the payload 
which is not allowed in the old Redis versions(before Redis 7), so it will 
throw the error:
    
    ```
    127.0.0.1:6379> RESTORE a 0 "\x00\xc0{\x0c\x00\x83\x94g!\xfaP\xf9\xf0"
    (error) ERR DUMP payload version or checksum are wrong
    ```
    
    This PR changes the payload's RDB version to 6 to make it work with the old 
Redis versions. And after applying this PR, it works well in Redis 4/6:
    
    ```
    127.0.0.1:6379> RESTORE a 0 "\x00\xc0{\x06\x00\xde\x0f;a\xf5/[*"
    OK
    127.0.0.1:6379> get a
    "123"
    
    127.0.0.1:6379> RESTORE a 0 "\x00\xc0{\x06\x00\xde\x0f;a\xf5/[*"
    OK
    127.0.0.1:6379> get a
    "123"
    ```
    
    Fixes #2251
---
 src/storage/rdb.cc                  |  8 ++--
 src/storage/rdb.h                   |  4 ++
 tests/gocase/unit/dump/dump_test.go | 83 +++++++++++++++++++++++++++----------
 3 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/src/storage/rdb.cc b/src/storage/rdb.cc
index e6da3594..8c5f6f11 100644
--- a/src/storage/rdb.cc
+++ b/src/storage/rdb.cc
@@ -700,9 +700,11 @@ Status RDB::Dump(const std::string &key, const RedisType 
type) {
    * RDB version and CRC are both in little endian.
    */
 
-  /* RDB version */
-  buf[0] = MaxRDBVersion & 0xff;
-  buf[1] = (MaxRDBVersion >> 8) & 0xff;
+  // We should choose the minimum RDB version for compatibility consideration.
+  // For the current DUMP implementation, it was supported since from the 
Redis 2.6,
+  // so we choose the RDB version of Redis 2.6 as the minimum version.
+  buf[0] = MinRDBVersion & 0xff;
+  buf[1] = (MinRDBVersion >> 8) & 0xff;
   s = stream_->Write((const char *)buf, 2);
   if (!s.IsOK()) {
     return {Status::RedisExecErr, s.Msg()};
diff --git a/src/storage/rdb.h b/src/storage/rdb.h
index a00c6d6d..5d7f78f3 100644
--- a/src/storage/rdb.h
+++ b/src/storage/rdb.h
@@ -61,6 +61,10 @@ constexpr const int QuickListNodeContainerPlain = 1;
 constexpr const int QuickListNodeContainerPacked = 2;
 
 constexpr const int MaxRDBVersion = 12;  // The current max rdb version 
supported by redis.
+// Min Redis RDB version supported by Kvrocks, we choose 6 because it's the 
first version
+// that supports the DUMP command.
+constexpr int MinRDBVersion = 6;
+
 class RdbStream;
 
 using RedisObjValue =
diff --git a/tests/gocase/unit/dump/dump_test.go 
b/tests/gocase/unit/dump/dump_test.go
index d12da7b1..faeee9a9 100644
--- a/tests/gocase/unit/dump/dump_test.go
+++ b/tests/gocase/unit/dump/dump_test.go
@@ -21,6 +21,7 @@ package dump
 
 import (
        "context"
+       "fmt"
        "testing"
 
        "github.com/apache/kvrocks/tests/gocase/util"
@@ -36,11 +37,20 @@ func TestDump_String(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       require.NoError(t, rdb.Del(ctx, "dump_test_key1", 
"dump_test_key2").Err())
-       require.NoError(t, rdb.Set(ctx, "dump_test_key1", "hello,world!", 
0).Err())
-       require.NoError(t, rdb.Set(ctx, "dump_test_key2", "654321", 0).Err())
-       require.Equal(t, "\x00\x0chello,world!\x0c\x00N~\xe6\xc8\xd38h\x17", 
rdb.Dump(ctx, "dump_test_key1").Val())
-       require.Equal(t, "\x00\xc2\xf1\xfb\t\x00\x0c\x00gSN\xfd\xf2y\xa2\x9d", 
rdb.Dump(ctx, "dump_test_key2").Val())
+       keyValues := map[string]string{
+               "test_string_key0": "hello,world!",
+               "test_string_key1": "654321",
+       }
+       for key, value := range keyValues {
+               require.NoError(t, rdb.Del(ctx, key).Err())
+               require.NoError(t, rdb.Set(ctx, key, value, 0).Err())
+               serialized, err := rdb.Dump(ctx, key).Result()
+               require.NoError(t, err)
+
+               restoredKey := fmt.Sprintf("restore_%s", key)
+               require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, 
serialized).Err())
+               require.Equal(t, value, rdb.Get(ctx, restoredKey).Val())
+       }
 }
 
 func TestDump_Hash(t *testing.T) {
@@ -51,9 +61,21 @@ func TestDump_Hash(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       require.NoError(t, rdb.Del(ctx, "dump_test_key1").Err())
-       require.NoError(t, rdb.HMSet(ctx, "dump_test_key1", "name", "redis 
tutorial", "description", "redis basic commands for caching", "likes", 20, 
"visitors", 23000).Err())
-       require.Equal(t, "\x04\x04\x0bdescription redis basic commands for 
caching\x05likes\xc0\x14\x04name\x0eredis 
tutorial\bvisitors\xc1\xd8Y\x0c\x008\x96\xa68b\xebuQ", rdb.Dump(ctx, 
"dump_test_key1").Val())
+       key := "test_hash_key"
+       fields := map[string]string{
+               "name":        "redis tutorial",
+               "description": "redis basic commands for caching",
+               "likes":       "20",
+               "visitors":    "23000",
+       }
+       require.NoError(t, rdb.Del(ctx, key).Err())
+       require.NoError(t, rdb.HMSet(ctx, key, fields).Err())
+       serialized, err := rdb.Dump(ctx, key).Result()
+       require.NoError(t, err)
+
+       restoredKey := fmt.Sprintf("restore_%s", key)
+       require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, 
serialized).Err())
+       require.EqualValues(t, fields, rdb.HGetAll(ctx, restoredKey).Val())
 }
 
 func TestDump_ZSet(t *testing.T) {
@@ -64,10 +86,17 @@ func TestDump_ZSet(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       zMember := []redis.Z{{Member: "kvrocks1", Score: 1}, {Member: 
"kvrocks2", Score: 2}, {Member: "kvrocks3", Score: 3}}
-       require.NoError(t, rdb.Del(ctx, "dump_test_key1").Err())
-       require.NoError(t, rdb.ZAdd(ctx, "dump_test_key1", zMember...).Err())
-       require.Equal(t, 
"\x05\x03\bkvrocks3\x00\x00\x00\x00\x00\x00\b@\bkvrocks2\x00\x00\x00\x00\x00\x00\x00@\bkvrocks1\x00\x00\x00\x00\x00\x00\xf0?\x0c\x00L_7\xd3\xd4\xc9\xf4\xe4",
 rdb.Dump(ctx, "dump_test_key1").Val())
+       memberScores := []redis.Z{{Member: "kvrocks1", Score: 1}, {Member: 
"kvrocks2", Score: 2}, {Member: "kvrocks3", Score: 3}}
+       key := "test_zset_key"
+       require.NoError(t, rdb.Del(ctx, key).Err())
+       require.NoError(t, rdb.ZAdd(ctx, key, memberScores...).Err())
+       serialized, err := rdb.Dump(ctx, key).Result()
+       require.NoError(t, err)
+
+       restoredKey := fmt.Sprintf("restore_%s", key)
+       require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, 
serialized).Err())
+
+       require.EqualValues(t, memberScores, rdb.ZRangeWithScores(ctx, 
restoredKey, 0, -1).Val())
 }
 
 func TestDump_List(t *testing.T) {
@@ -78,11 +107,16 @@ func TestDump_List(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       require.NoError(t, rdb.Del(ctx, "dump_test_key1").Err())
-       require.NoError(t, rdb.RPush(ctx, "dump_test_key1", "kvrocks1").Err())
-       require.NoError(t, rdb.RPush(ctx, "dump_test_key1", "kvrocks2").Err())
-       require.NoError(t, rdb.RPush(ctx, "dump_test_key1", "kvrocks3").Err())
-       require.Equal(t, 
"\x12\x03\x01\bkvrocks1\x01\bkvrocks2\x01\bkvrocks3\x0c\x00\xa8\xf9S\x986\x98\xaf\xcd",
 rdb.Dump(ctx, "dump_test_key1").Val())
+       elements := []string{"kvrocks1", "kvrocks2", "kvrocks3"}
+       key := "test_list_key"
+       require.NoError(t, rdb.Del(ctx, key).Err())
+       require.NoError(t, rdb.RPush(ctx, key, elements).Err())
+       serialized, err := rdb.Dump(ctx, key).Result()
+       require.NoError(t, err)
+
+       restoredKey := fmt.Sprintf("restore_%s", key)
+       require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, 
serialized).Err())
+       require.EqualValues(t, elements, rdb.LRange(ctx, restoredKey, 0, 
-1).Val())
 }
 
 func TestDump_Set(t *testing.T) {
@@ -93,9 +127,14 @@ func TestDump_Set(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       require.NoError(t, rdb.Del(ctx, "dump_test_key1").Err())
-       require.NoError(t, rdb.SAdd(ctx, "dump_test_key1", "kvrocks1").Err())
-       require.NoError(t, rdb.SAdd(ctx, "dump_test_key1", "kvrocks2").Err())
-       require.NoError(t, rdb.SAdd(ctx, "dump_test_key1", "kvrocks3").Err())
-       require.Equal(t, 
"\x02\x03\bkvrocks1\bkvrocks2\bkvrocks3\x0c\x00\xfdP\xc9\x95sS\x87\x18", 
rdb.Dump(ctx, "dump_test_key1").Val())
+       members := []string{"kvrocks1", "kvrocks2", "kvrocks3"}
+       key := "test_set_key"
+       require.NoError(t, rdb.Del(ctx, key).Err())
+       require.NoError(t, rdb.SAdd(ctx, key, members).Err())
+       serialized, err := rdb.Dump(ctx, key).Result()
+       require.NoError(t, err)
+
+       restoredKey := fmt.Sprintf("restore_%s", key)
+       require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, 
serialized).Err())
+       require.ElementsMatch(t, members, rdb.SMembers(ctx, restoredKey).Val())
 }

Reply via email to