Skip to content

Commit a10b6e1

Browse files
feat(rdb): add DUMP support for SortedInt type (#3366)
- Serialize SortedInt as RDB Set for Redis compatibility - Use RangeByValue() to get all integers deterministically - No RESTORE changes (SortedInt dumps restore as Set, like Bitmap→String) - Resolves part of #3319 Testing: - Manual test confirms DUMP no longer returns error - RESTORE successfully creates Set with preserved data - All integers maintained in restored Set Manuel test: root@codespaces-6e5713:/workspaces/kvrocks# # Connect to Kvrocks redis-cli -p 6666 127.0.0.1:6666> SIADD testkey 100 200 300 (integer) 0 // already exists 127.0.0.1:6666> SICARD testkey (integer) 3 127.0.0.1:6666> TYPE testkey sortedint 127.0.0.1:6666> DUMP testkey "\x02\x03\xc0d\xc1\xc8\x00\xc1,\x01\x06\x00\xb2\x01\xaa\xe2\x06h\xba\x8d" 127.0.0.1:6666> SIADD mykey 5 12 23 89 100 (integer) 0 127.0.0.1:6666> DUMP mykey "\x02\x05\xc0\x05\xc0\x0c\xc0\x17\xc0Y\xc0d\x06\x00\xa8\x19U\x90\x8f\x011o" 127.0.0.1:6666> RESTORE newkey 0 "\x02\x05\xc0\x05\xc0\x0c\xc0\x17\xc0Y\xc0d\x06\x00\xa8\x19U\x90\x8f\x011o" OK 127.0.0.1:6666> TYPE newkey set 127.0.0.1:6666> SMEMBERS newkey 1) "100" 2) "12" 3) "23" 4) "5" 5) "89" 127.0.0.1:6666> DUMP nonexistent (nil) 127.0.0.1:6666> exit --------- Co-authored-by: hulk <hulk.website@gmail.com>
1 parent 11fa20b commit a10b6e1

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

src/storage/rdb/rdb.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#include "rdb.h"
2222

23+
#include <limits>
24+
2325
#include "common/encoding.h"
2426
#include "common/rdb_stream.h"
2527
#include "common/time_util.h"
@@ -35,6 +37,7 @@
3537
#include "types/redis_hash.h"
3638
#include "types/redis_list.h"
3739
#include "types/redis_set.h"
40+
#include "types/redis_sortedint.h"
3841
#include "types/redis_string.h"
3942
#include "types/redis_zset.h"
4043
#include "vendor/crc64.h"
@@ -730,6 +733,8 @@ Status RDB::SaveObjectType(const RedisType type) {
730733
robj_type = RDBTypeSet;
731734
} else if (type == kRedisZSet) {
732735
robj_type = RDBTypeZSet2;
736+
} else if (type == kRedisSortedint) {
737+
robj_type = RDBTypeSet;
733738
} else {
734739
WARN("Invalid or Not supported object type: {}", (int)type);
735740
return {Status::NotOK, "Invalid or Not supported object type"};
@@ -793,6 +798,23 @@ Status RDB::SaveObject(const std::string &key, const RedisType type) {
793798
return {Status::RedisExecErr, s.ToString()};
794799
}
795800
return SaveStringObject(value);
801+
} else if (type == kRedisSortedint) {
802+
redis::Sortedint sortedint_db(storage_, ns_);
803+
std::vector<uint64_t> ids;
804+
SortedintRangeSpec spec;
805+
spec.min = 0;
806+
spec.max = std::numeric_limits<uint64_t>::max();
807+
spec.minex = false;
808+
spec.maxex = false;
809+
spec.offset = 0;
810+
spec.count = std::numeric_limits<int>::max();
811+
spec.reversed = false;
812+
int size = 0;
813+
auto si_status = sortedint_db.RangeByValue(ctx, key, spec, &ids, &size);
814+
if (!si_status.ok()) {
815+
return {Status::RedisExecErr, si_status.ToString()};
816+
}
817+
return SaveSortedintObject(ids);
796818
} else {
797819
WARN("Invalid or Not supported object type: {}", (int)type);
798820
return {Status::NotOK, "Invalid or Not supported object type"};
@@ -930,6 +952,20 @@ Status RDB::SaveHashObject(const std::vector<FieldValue> &field_values) {
930952
}
931953
return Status::OK();
932954
}
955+
Status RDB::SaveSortedintObject(const std::vector<uint64_t> &ids) {
956+
if (ids.size() > 0) {
957+
auto status = RdbSaveLen(ids.size());
958+
if (!status.IsOK()) return status;
959+
960+
for (const auto &id : ids) {
961+
status = SaveStringObject(std::to_string(id));
962+
if (!status.IsOK()) return status;
963+
}
964+
} else {
965+
return {Status::NotOK, "the size of sortedint is zero"};
966+
}
967+
return Status::OK();
968+
}
933969

934970
int RDB::rdbEncodeInteger(const long long value, unsigned char *enc) {
935971
if (value >= -(1 << 7) && value <= (1 << 7) - 1) {

src/storage/rdb/rdb.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ class RDB {
130130
// Hash
131131
Status SaveHashObject(const std::vector<FieldValue> &field_value);
132132

133+
// SortedInt
134+
Status SaveSortedintObject(const std::vector<uint64_t> &ids);
135+
133136
private:
134137
engine::Storage *storage_;
135138
std::string ns_;

tests/gocase/unit/dump/dump_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,30 @@ func TestDump_IntegerEncoding(t *testing.T) {
193193
require.Equal(t, v, got)
194194
}
195195
}
196+
197+
func TestDump_SortedInt(t *testing.T) {
198+
srv := util.StartServer(t, map[string]string{})
199+
defer srv.Close()
200+
201+
ctx := context.Background()
202+
rdb := srv.NewClient()
203+
defer func() { require.NoError(t, rdb.Close()) }()
204+
205+
key := "test_sortedint_key"
206+
require.NoError(t, rdb.Del(ctx, key).Err())
207+
208+
require.EqualValues(t, 5, rdb.Do(ctx, "SIADD", key, 5, 12, 23, 89, 100).Val())
209+
require.EqualValues(t, 5, rdb.Do(ctx, "SICARD", key).Val())
210+
require.EqualValues(t, "sortedint", rdb.Type(ctx, key).Val())
211+
212+
serialized, err := rdb.Dump(ctx, key).Result()
213+
require.NoError(t, err)
214+
215+
restoredKey := fmt.Sprintf("restore_%s", key)
216+
require.NoError(t, rdb.RestoreReplace(ctx, restoredKey, 0, serialized).Err())
217+
218+
require.EqualValues(t, "set", rdb.Type(ctx, restoredKey).Val())
219+
members := rdb.SMembers(ctx, restoredKey).Val()
220+
expectedMembers := []string{"5", "12", "23", "89", "100"}
221+
require.ElementsMatch(t, expectedMembers, members)
222+
}

0 commit comments

Comments
 (0)