Skip to content

Commit 618ccd0

Browse files
committed
hexpire - listpack
1 parent 9c30f2e commit 618ccd0

17 files changed

+133
-36
lines changed

api/librdb-api.h

+2
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ typedef struct RdbHandlersStructCallbacks {
263263
RdbRes (*handleHashZL)(RdbParser *p, void *userData, RdbBulk ziplist);
264264
/* Callback to handle a listpack-based hash value */
265265
RdbRes (*handleHashLP)(RdbParser *p, void *userData, RdbBulk listpack);
266+
/* Callback to handle a listpackex-based hash (with expiry on fields) */
267+
RdbRes (*handleHashLPEx)(RdbParser *p, void *userData, RdbBulk listpackEx);
266268
/* Callback to handle a zipmap-based hash value */
267269
RdbRes (*handleHashZM)(RdbParser *p, void *userData, RdbBulk zipmap);
268270

src/ext/handlersFilter.c

+6
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ static RdbRes filterHashLP(RdbParser *p, void *userData, RdbBulk listpack) {
178178
return ((RdbxFilter *) userData)->cbReturnValue;
179179
}
180180

181+
static RdbRes filterHashLPEx(RdbParser *p, void *userData, RdbBulk listpackEx) {
182+
UNUSED(p, listpackEx);
183+
return ((RdbxFilter *) userData)->cbReturnValue;
184+
}
185+
181186
static RdbRes filterHashZM(RdbParser *p, void *userData, RdbBulk zipmap) {
182187
UNUSED(p, zipmap);
183188
return ((RdbxFilter *) userData)->cbReturnValue;
@@ -311,6 +316,7 @@ static void defaultFilterStructCb(RdbHandlersStructCallbacks *structCb) {
311316
filterHashPlain, /*handleHashPlain*/
312317
filterHashZL, /*handleHashZL*/
313318
filterHashLP, /*handleHashLP*/
319+
filterHashLPEx, /*handleHashLPEx*/
314320
filterHashZM, /*handleHashZM*/
315321
filterSetPlain, /*handleSetPlain*/
316322
filterSetIS, /*handleSetIS*/

src/ext/handlersToJson.c

+1
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,7 @@ RdbxToJson *RDBX_createHandlersToJson(RdbParser *p, const char *filename, RdbxTo
781781
toJsonHash, /*handleHashPlain*/
782782
toJsonStruct, /*handleHashZL*/
783783
toJsonStruct, /*handleHashLP*/
784+
toJsonStruct, /*handleHashLPEx*/
784785
toJsonStruct, /*handleHashZM*/
785786
/*set*/
786787
toJsonSet,

src/lib/parser.c

+39-6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct ParsingElementInfo peInfo[PE_MAX] = {
6565
[PE_HASH_META] = {elementHash, "elementHashMeta", "Parsing Hash with expiry on fields"},
6666
[PE_HASH_ZL] = {elementHashZL, "elementHashZL", "Parsing hash Ziplist"},
6767
[PE_HASH_LP] = {elementHashLP, "elementHashLP", "Parsing hash Listpack"},
68+
[PE_HASH_LP_EX] = {elementHashLPEx, "elementHashLPEx", "Parsing hash ListpackEx (with expiry)"},
6869
[PE_HASH_ZM] = {elementHashZM, "elementHashZM", "Parsing hash Zipmap"},
6970
/* set */
7071
[PE_SET] = {elementSet, "elementSet", "Parsing set"},
@@ -99,6 +100,7 @@ struct ParsingElementInfo peInfo[PE_MAX] = {
99100
[PE_RAW_HASH_META] = {elementRawHash, "elementRawHashMeta", "Parsing raw Hash with expiry"},
100101
[PE_RAW_HASH_ZL] = {elementRawHashZL, "elementRawHashZL", "Parsing raw hash Ziplist"},
101102
[PE_RAW_HASH_LP] = {elementRawHashLP, "elementRawHashLP", "Parsing raw hash Listpack"},
103+
[PE_RAW_HASH_LP_EX] = {elementRawHashLPEx, "elementRawHashLPEx", "Parsing raw hash ListpackEx"},
102104
[PE_RAW_HASH_ZM] = {elementRawHashZM, "elementRawHashZM", "Parsing raw hash Zipmap"},
103105
/* set */
104106
[PE_RAW_SET] = {elementRawSet, "elementRawSet", "Parsing raw set"},
@@ -563,6 +565,7 @@ _LIBRDB_API int RDB_handleByLevel(RdbParser *p, RdbDataType type, RdbHandlersLev
563565
p->handleTypeObjByLevel[RDB_TYPE_HASH_ZIPMAP] = lvl;
564566
p->handleTypeObjByLevel[RDB_TYPE_HASH_ZIPLIST] = lvl;
565567
p->handleTypeObjByLevel[RDB_TYPE_HASH_LISTPACK] = lvl;
568+
p->handleTypeObjByLevel[RDB_TYPE_HASH_LISTPACK_EX] = lvl;
566569
break;
567570
case RDB_DATA_TYPE_MODULE:
568571
p->handleTypeObjByLevel[RDB_TYPE_MODULE_2] = lvl;
@@ -1050,7 +1053,7 @@ static RdbStatus hashZiplist(RdbParser *p, BulkInfo *ziplistBulk) {
10501053
return RDB_STATUS_OK;
10511054
}
10521055

1053-
static RdbStatus hashListPack(RdbParser *p, BulkInfo *lpBulk) {
1056+
static RdbStatus hashListPack(RdbParser *p, BulkInfo *lpBulk, int withExpiry) {
10541057
size_t items = 0;
10551058

10561059
if (unlikely(0 == lpValidateIntegrity(lpBulk->ref, lpBulk->len, p->deepIntegCheck, counterCallback, &items))) {
@@ -1059,21 +1062,27 @@ static RdbStatus hashListPack(RdbParser *p, BulkInfo *lpBulk) {
10591062
return RDB_STATUS_ERROR;
10601063
}
10611064

1062-
if (unlikely((items & 1))) {
1065+
/* [field][value][expiry] vs [field][value] */
1066+
int tupleSize = (withExpiry) ? 3 : 2;
1067+
if (unlikely((items % tupleSize) != 0)) {
10631068
RDB_reportError(p, RDB_ERR_HASH_LP_INTEG_CHECK,
1064-
"hashListPack(): Listpack integrity check failed. Uneven number of items.");
1069+
"hashListPack(): listpack has unexpected number of items.");
10651070
return RDB_STATUS_ERROR;
10661071
}
10671072

10681073
if (p->elmCtx.key.handleByLevel == RDB_LEVEL_STRUCT) {
10691074
registerAppBulkForNextCb(p, lpBulk);
1070-
CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleHashLP, lpBulk->ref);
1075+
if (withExpiry)
1076+
CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleHashLPEx, lpBulk->ref);
1077+
else
1078+
CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleHashLP, lpBulk->ref);
10711079
return RDB_STATUS_OK;
10721080
}
10731081

10741082
p->elmCtx.key.numItemsHint = items;
10751083
unsigned char *iterLP = lpFirst(lpBulk->ref);
10761084
while (iterLP) {
1085+
int64_t expiryVal = -1;
10771086
unsigned char *field, *value;
10781087
unsigned int fieldLen, valueLen;
10791088
long long fieldVal, valueVal;
@@ -1083,6 +1092,16 @@ static RdbStatus hashListPack(RdbParser *p, BulkInfo *lpBulk) {
10831092
iterLP = lpNext(lpBulk->ref, iterLP);
10841093
value = lpGetValue(iterLP, &valueLen, &valueVal);
10851094
iterLP = lpNext(lpBulk->ref, iterLP);
1095+
if (withExpiry) {
1096+
if (lpGet(iterLP, &expiryVal, NULL) != NULL) {
1097+
RDB_reportError(p, RDB_ERR_HASH_LP_INTEG_CHECK,
1098+
"hashListPack(): integrity check failed. Expiry is a string instead of value.");
1099+
return RDB_STATUS_ERROR;
1100+
}
1101+
1102+
if (expiryVal == 0) expiryVal = -1; /* If expiry not set */
1103+
iterLP = lpNext(lpBulk->ref, iterLP);
1104+
}
10861105

10871106
if (!allocEmbeddedBulk(p, field, fieldLen, fieldVal, &embBulk1))
10881107
return RDB_STATUS_ERROR;
@@ -1101,7 +1120,7 @@ static RdbStatus hashListPack(RdbParser *p, BulkInfo *lpBulk) {
11011120
rdbData.handleHashField,
11021121
embBulk1.binfo.ref,
11031122
embBulk2.binfo.ref,
1104-
-1 /*no expiry*/);
1123+
expiryVal);
11051124
}
11061125
return RDB_STATUS_OK;
11071126
}
@@ -1451,6 +1470,7 @@ RdbStatus elementNextRdbType(RdbParser *p) {
14511470
case RDB_TYPE_HASH_METADATA: return nextParsingElementKeyValue(p, PE_RAW_HASH_META, PE_HASH_META);
14521471
case RDB_TYPE_HASH_ZIPLIST: return nextParsingElementKeyValue(p, PE_RAW_HASH_ZL, PE_HASH_ZL);
14531472
case RDB_TYPE_HASH_LISTPACK: return nextParsingElementKeyValue(p, PE_RAW_HASH_LP, PE_HASH_LP);
1473+
case RDB_TYPE_HASH_LISTPACK_EX: return nextParsingElementKeyValue(p, PE_RAW_HASH_LP_EX, PE_HASH_LP_EX);
14541474
case RDB_TYPE_HASH_ZIPMAP: return nextParsingElementKeyValue(p, PE_RAW_HASH_ZM, PE_HASH_ZM);
14551475
/* set */
14561476
case RDB_TYPE_SET: return nextParsingElementKeyValue(p, PE_RAW_SET, PE_SET);
@@ -1734,14 +1754,27 @@ RdbStatus elementHashZL(RdbParser *p) {
17341754
return nextParsingElement(p, PE_END_KEY);
17351755
}
17361756

1757+
RdbStatus elementHashLPEx(RdbParser *p) {
1758+
BulkInfo *listpackBulk;
1759+
1760+
IF_NOT_OK_RETURN(rdbLoadString(p, RQ_ALLOC_APP_BULK, NULL, &listpackBulk));
1761+
1762+
/*** ENTER SAFE STATE ***/
1763+
1764+
if (RDB_STATUS_ERROR == hashListPack(p, listpackBulk, 1 /*withExpiry*/))
1765+
return RDB_STATUS_ERROR;
1766+
1767+
return nextParsingElement(p, PE_END_KEY);
1768+
}
1769+
17371770
RdbStatus elementHashLP(RdbParser *p) {
17381771
BulkInfo *listpackBulk;
17391772

17401773
IF_NOT_OK_RETURN(rdbLoadString(p, RQ_ALLOC_APP_BULK, NULL, &listpackBulk));
17411774

17421775
/*** ENTER SAFE STATE ***/
17431776

1744-
if (RDB_STATUS_ERROR == hashListPack(p, listpackBulk))
1777+
if (RDB_STATUS_ERROR == hashListPack(p, listpackBulk, 0 /*withExpiry*/))
17451778
return RDB_STATUS_ERROR;
17461779

17471780
return nextParsingElement(p, PE_END_KEY);

src/lib/parser.h

+4
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ typedef enum ParsingElementType {
143143
PE_HASH_META,
144144
PE_HASH_ZL,
145145
PE_HASH_LP,
146+
PE_HASH_LP_EX,
146147
PE_HASH_ZM,
147148
PE_SET,
148149
PE_SET_IS,
@@ -167,6 +168,7 @@ typedef enum ParsingElementType {
167168
PE_RAW_HASH_META,
168169
PE_RAW_HASH_ZL,
169170
PE_RAW_HASH_LP,
171+
PE_RAW_HASH_LP_EX,
170172
PE_RAW_HASH_ZM,
171173
PE_RAW_SET,
172174
PE_RAW_SET_IS,
@@ -534,6 +536,7 @@ RdbStatus elementListZL(RdbParser *p);
534536
RdbStatus elementHash(RdbParser *p);
535537
RdbStatus elementHashZL(RdbParser *p);
536538
RdbStatus elementHashLP(RdbParser *p);
539+
RdbStatus elementHashLPEx(RdbParser *p);
537540
RdbStatus elementHashZM(RdbParser *p);
538541
RdbStatus elementSet(RdbParser *p);
539542
RdbStatus elementSetIS(RdbParser *p);
@@ -555,6 +558,7 @@ RdbStatus elementRawListZL(RdbParser *p);
555558
RdbStatus elementRawHash(RdbParser *p);
556559
RdbStatus elementRawHashZL(RdbParser *p);
557560
RdbStatus elementRawHashLP(RdbParser *p);
561+
RdbStatus elementRawHashLPEx(RdbParser *p);
558562
RdbStatus elementRawHashZM(RdbParser *p);
559563
RdbStatus elementRawSet(RdbParser *p);
560564
RdbStatus elementRawSetIS(RdbParser *p);

src/lib/parserRaw.c

+4
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ RdbStatus elementRawHashLP(RdbParser *p) {
525525
return singleStringTypeHandling(p, listpackValidateIntegrityCb, "elementRawHashLP");
526526
}
527527

528+
RdbStatus elementRawHashLPEx(RdbParser *p) {
529+
return singleStringTypeHandling(p, listpackValidateIntegrityCb, "elementRawHashLPEx");
530+
}
531+
528532
RdbStatus elementRawHashZM(RdbParser *p) {
529533
return singleStringTypeHandling(p, zipmapValidateIntegrityCb, "elementRawHashZM");
530534
}
File renamed without changes.

test/dumps/hash_ex_v12_data.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"__aux__" : {
2+
"redis-ver":"255.255.255",
3+
"redis-bits":"64",
4+
"ctime":"1718101998",
5+
"used-mem":"1168904",
6+
"aof-base":"0"
7+
},
8+
"myhash":{"field1":"value1","field3":"value3","field2":"value2"}

test/dumps/hash_lp_ex_v12_data.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"__aux__" : {
2+
"redis-ver":"255.255.255",
3+
"redis-bits":"64",
4+
"ctime":"1718036120",
5+
"used-mem":"1191512",
6+
"aof-base":"0"
7+
},
8+
9+
"myhash":{"field2":"value2","field1":"value1","field3":"value3"}
File renamed without changes.
File renamed without changes.
File renamed without changes.

test/test_common.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void assert_file_payload(const char *filename, char *expData, int expLen, MatchT
180180
printf("%s\n---- file [%s] ----\n", errMsg, filename);
181181
printHexDump(filedata, filelen, buf, (int) sizeof(buf));
182182
printf("%s", buf);
183-
printf("\n---- Expected %s %s ----\n", matchTypeName, (expMatch) ? "" : "not to match");
183+
printf("\n---- Expected %s %s ----\n", matchTypeName, (expMatch) ? "" : "NOT to match");
184184
printHexDump(expData, expLen, buf, (int) sizeof(buf));
185185
printf("%s", buf);
186186
printf("\n------------\n");

test/test_rdb_to_json.c

+27-9
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,29 @@ static void test_r2j_plain_list_raw (void **state) {
156156
testRdbToJsonCommon(DUMP_FOLDER("plain_list_v6.rdb"), DUMP_FOLDER("plain_list_v6_raw.json"), &r2jConf);
157157
}
158158

159-
static void test_r2j_plain_hash_data(void **state) {
159+
static void test_r2j_hash_data(void **state) {
160160
UNUSED(state);
161161
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA);
162-
testRdbToJsonCommon(DUMP_FOLDER("plain_hash_v3.rdb"), DUMP_FOLDER("plain_hash_data.json"), &r2jConf);
162+
testRdbToJsonCommon(DUMP_FOLDER("hash_v3.rdb"), DUMP_FOLDER("hash_data.json"), &r2jConf);
163163
}
164164

165-
static void test_r2j_plain_hash_struct(void **state) {
165+
static void test_r2j_hash_struct(void **state) {
166166
UNUSED(state);
167167
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT);
168-
testRdbToJsonCommon(DUMP_FOLDER("plain_hash_v3.rdb"), DUMP_FOLDER("plain_hash_struct.json"), &r2jConf);
168+
testRdbToJsonCommon(DUMP_FOLDER("hash_v3.rdb"), DUMP_FOLDER("hash_struct.json"), &r2jConf);
169169
}
170170

171-
static void test_r2j_plain_hash_raw (void **state) {
171+
static void test_r2j_hash_raw (void **state) {
172172
UNUSED(state);
173173
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_RAW);
174-
testRdbToJsonCommon(DUMP_FOLDER("plain_hash_v3.rdb"), DUMP_FOLDER("plain_hash_raw.json"), &r2jConf);
174+
testRdbToJsonCommon(DUMP_FOLDER("hash_v3.rdb"), DUMP_FOLDER("hash_raw.json"), &r2jConf);
175+
}
176+
177+
/* hash with expiry on fields */
178+
static void test_r2j_hash_ex_data(void **state) {
179+
UNUSED(state);
180+
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA);
181+
testRdbToJsonCommon(DUMP_FOLDER("hash_with_expire_v12.rdb"), DUMP_FOLDER("hash_ex_v12_data.json"), &r2jConf);
175182
}
176183

177184
static void test_r2j_hash_zl_data(void **state) {
@@ -198,6 +205,13 @@ static void test_r2j_hash_lp_data(void **state) {
198205
testRdbToJsonCommon(DUMP_FOLDER("hash_lp_v11.rdb"), DUMP_FOLDER("hash_lp_v11_data.json"), &r2jConf);
199206
}
200207

208+
/* lp with expiry on fields */
209+
static void test_r2j_hash_lp_ex_data(void **state) {
210+
UNUSED(state);
211+
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA);
212+
testRdbToJsonCommon(DUMP_FOLDER("hash_lp_with_hexpire_v12.rdb"), DUMP_FOLDER("hash_lp_ex_v12_data.json"), &r2jConf);
213+
}
214+
201215
static void test_r2j_hash_lp_struct(void **state) {
202216
UNUSED(state);
203217
RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT);
@@ -511,9 +525,11 @@ int group_rdb_to_json(void) {
511525
cmocka_unit_test(test_r2j_plain_list_raw),
512526

513527
/* hash */
514-
cmocka_unit_test(test_r2j_plain_hash_data),
515-
cmocka_unit_test(test_r2j_plain_hash_struct),
516-
cmocka_unit_test(test_r2j_plain_hash_raw),
528+
cmocka_unit_test(test_r2j_hash_data),
529+
cmocka_unit_test(test_r2j_hash_struct),
530+
cmocka_unit_test(test_r2j_hash_raw),
531+
532+
cmocka_unit_test(test_r2j_hash_ex_data),
517533

518534
cmocka_unit_test(test_r2j_hash_zl_data),
519535
cmocka_unit_test(test_r2j_hash_zl_struct),
@@ -523,6 +539,8 @@ int group_rdb_to_json(void) {
523539
cmocka_unit_test(test_r2j_hash_lp_struct),
524540
cmocka_unit_test(test_r2j_hash_lp_raw),
525541

542+
cmocka_unit_test(test_r2j_hash_lp_ex_data),
543+
526544
cmocka_unit_test(test_r2j_hash_zm_data),
527545
cmocka_unit_test(test_r2j_hash_zm_struct),
528546
cmocka_unit_test(test_r2j_hash_zm_raw),

test/test_rdb_to_redis.c

+20-17
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,19 @@ static void rdb_to_json(const char *rdbfile, const char *outfile) {
6767
RDB_deleteParser(parser);
6868
}
6969

70-
/* Save & Reload RDB file
70+
/* Is saving RDB, and librdb reload generates same digest
7171
*
7272
* isDigest - if set, compare DB digest before and after reload
7373
* isRestore - if set, use RESTORE command after reload. Otherwise, plain commands
7474
*/
75-
static void rdb_save_and_tcp_reload(int isDigest, int isRestore) {
75+
static void rdb_save_librdb_reload_eq(int isRestore) {
7676
char *res;
77-
const char *rdbfile = DUMP_FOLDER("reload.rdb");
77+
const char *rdbfile = TMP_FOLDER("reload.rdb");
7878
char expectedSha[100];
7979

8080
/* Calculate DB isDigest */
81-
if (isDigest) {
82-
res = sendRedisCmd("DEBUG DIGEST", REDIS_REPLY_STATUS, NULL);
83-
memcpy(expectedSha, res, strlen(res) + 1);
84-
}
81+
res = sendRedisCmd("DEBUG DIGEST", REDIS_REPLY_STATUS, NULL);
82+
memcpy(expectedSha, res, strlen(res) + 1);
8583

8684
/* Keep aside rdb file */
8785
sendRedisCmd("SAVE", REDIS_REPLY_STATUS, NULL);
@@ -94,7 +92,7 @@ static void rdb_save_and_tcp_reload(int isDigest, int isRestore) {
9492
/* Reload the RDB file */
9593
rdb_to_tcp(rdbfile, 1, isRestore, NULL);
9694

97-
if (isDigest) sendRedisCmd("DEBUG DIGEST", REDIS_REPLY_STATUS, expectedSha);
95+
sendRedisCmd("DEBUG DIGEST", REDIS_REPLY_STATUS, expectedSha);
9896
}
9997

10098
/*
@@ -186,30 +184,35 @@ static void test_rdb_to_redis_single_ziplist(void **state) {
186184

187185
static void test_rdb_to_redis_hash(void **state) {
188186
UNUSED(state);
189-
test_rdb_to_redis_common(DUMP_FOLDER("plain_hash_v3.rdb"), 0, "$4\r\nHSET", NULL);
187+
test_rdb_to_redis_common(DUMP_FOLDER("hash_v3.rdb"), 0, "$4\r\nHSET", NULL);
190188
}
191189

192-
/* - Create hash with expiry on fields via HIREDIS.
193-
* - Then rdb_save_and_tcp_reload and verify digest before and after
194-
*/
195190
static void test_rdb_to_redis_hash_with_expire(void **state) {
196191
UNUSED(state);
197192

198193
/* hash-field-expiration available since 7.4 */
199194
if ((serverMajorVer<7) || ((serverMajorVer==7) && (serverMinorVer<4)))
200195
skip();
201196

202-
/* Test listpack - create RDB file with HFEs */
203-
204-
/* Test HT - create RDB file with HFEs & calculate DB digest */
197+
/* dict (max-lp-entries=0) */
205198
sendRedisCmd("FLUSHALL", REDIS_REPLY_STATUS, NULL);
206199
sendRedisCmd("CONFIG SET HASH-MAX-LISTPACK-ENTRIES 0", REDIS_REPLY_STATUS, NULL);
207200
sendRedisCmd("HSET myhash f1 v1 f2 v2 f3 v3", REDIS_REPLY_INTEGER, "3");
208201
sendRedisCmd("HPEXPIREAT myhash 70368744177663 FIELDS 2 f1 f2", REDIS_REPLY_ARRAY, "1 1");
209-
rdb_save_and_tcp_reload(1 /*digest */, 0 /*restore*/);
210-
rdb_save_and_tcp_reload(1 /*digest */, 1 /*restore*/);
202+
rdb_save_librdb_reload_eq(0 /*restore*/);
203+
rdb_save_librdb_reload_eq(1 /*restore*/);
211204
sendRedisCmd("HPEXPIRETIME myhash FIELDS 3 f1 f2 f3", REDIS_REPLY_ARRAY,
212205
"70368744177663 70368744177663 -1"); /* verify expected output */
206+
207+
/* listpack */
208+
sendRedisCmd("FLUSHALL", REDIS_REPLY_STATUS, NULL);
209+
sendRedisCmd("CONFIG SET HASH-MAX-LISTPACK-ENTRIES 512", REDIS_REPLY_STATUS, NULL);
210+
sendRedisCmd("HSET myhash f4 v1 f5 v2 f6 v3", REDIS_REPLY_INTEGER, "3");
211+
sendRedisCmd("HPEXPIREAT myhash 70368744177663 FIELDS 2 f4 f5", REDIS_REPLY_ARRAY, "1 1");
212+
rdb_save_librdb_reload_eq(0 /*restore*/);
213+
rdb_save_librdb_reload_eq(1 /*restore*/);
214+
sendRedisCmd("HPEXPIRETIME myhash FIELDS 3 f4 f5 f6", REDIS_REPLY_ARRAY,
215+
"70368744177663 70368744177663 -1"); /* verify expected output */
213216
}
214217

215218
static void test_rdb_to_redis_hash_zl(void **state) {

0 commit comments

Comments
 (0)