Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
4e9acd4b
Commit
4e9acd4b
authored
Jun 27, 2024
by
YaacovHazan
Browse files
Merge remote-tracking branch 'upstream/unstable' into HEAD
parents
4606f91d
a9267137
Changes
33
Expand all
Hide whitespace changes
Inline
Side-by-side
src/replication.c
View file @
4e9acd4b
...
...
@@ -189,6 +189,9 @@ int canFeedReplicaReplBuffer(client *replica) {
/* Don't feed replicas that are still waiting for BGSAVE to start. */
if
(
replica
->
replstate
==
SLAVE_STATE_WAIT_BGSAVE_START
)
return
0
;
/* Don't feed replicas that are going to be closed ASAP. */
if
(
replica
->
flags
&
CLIENT_CLOSE_ASAP
)
return
0
;
return
1
;
}
...
...
src/script.h
View file @
4e9acd4b
...
...
@@ -74,6 +74,7 @@ extern scriptFlag scripts_flags_def[];
void
luaEnvInit
(
void
);
lua_State
*
createLuaState
(
void
);
dict
*
getLuaScripts
(
void
);
uint64_t
scriptFlagsToCmdFlags
(
uint64_t
cmd_flags
,
uint64_t
script_flags
);
int
scriptPrepareForRun
(
scriptRunCtx
*
r_ctx
,
client
*
engine_client
,
client
*
caller
,
const
char
*
funcname
,
uint64_t
script_flags
,
int
ro
);
void
scriptResetRun
(
scriptRunCtx
*
r_ctx
);
...
...
src/server.c
View file @
4e9acd4b
...
...
@@ -334,6 +334,10 @@ uint64_t dictObjHash(const void *key) {
return
dictGenHashFunction
(
o
->
ptr
,
sdslen
((
sds
)
o
->
ptr
));
}
uint64_t
dictPtrHash
(
const
void
*
key
)
{
return
dictGenHashFunction
((
unsigned
char
*
)
&
key
,
sizeof
(
key
));
}
uint64_t
dictSdsHash
(
const
void
*
key
)
{
return
dictGenHashFunction
((
unsigned
char
*
)
key
,
sdslen
((
char
*
)
key
));
}
...
...
src/server.h
View file @
4e9acd4b
...
...
@@ -2071,7 +2071,8 @@ struct redisServer {
char
*
locale_collate
;
};
#define MAX_KEYS_BUFFER 256
/* we use 6 so that all getKeyResult fits a cacheline */
#define MAX_KEYS_BUFFER 6
typedef
struct
{
int
pos
;
/* The position of the key within the client array */
...
...
@@ -2084,12 +2085,12 @@ typedef struct {
* for returning channel information.
*/
typedef
struct
{
int
numkeys
;
/* Number of key indices return */
int
size
;
/* Available array size */
keyReference
keysbuf
[
MAX_KEYS_BUFFER
];
/* Pre-allocated buffer, to save heap allocations */
keyReference
*
keys
;
/* Key indices array, points to keysbuf or heap */
int
numkeys
;
/* Number of key indices return */
int
size
;
/* Available array size */
}
getKeysResult
;
#define GETKEYS_RESULT_INIT {
{{0}}, NULL,
0, MAX_KEYS_BUFFER }
#define GETKEYS_RESULT_INIT { 0, MAX_KEYS_BUFFER
, {{0}}, NULL
}
/* Key specs definitions.
*
...
...
@@ -3163,7 +3164,9 @@ robj *setTypeDup(robj *o);
typedef
struct
listpackEx
{
ExpireMeta
meta
;
/* To be used in order to register the hash in the
global ebuckets (i.e. db->hexpires) with next,
minimum, hash-field to expire. */
minimum, hash-field to expire. TTL value might be
inaccurate up-to few seconds due to optimization
consideration. */
sds
key
;
/* reference to the key, same one that stored in
db->dict. Will be used from active-expiration flow
for notification and deletion of the object, if
...
...
@@ -3178,7 +3181,9 @@ typedef struct dictExpireMetadata {
ExpireMeta
expireMeta
;
/* embedded ExpireMeta in dict.
To be used in order to register the hash in the
global ebuckets (i.e db->hexpires) with next,
minimum, hash-field to expire */
minimum, hash-field to expire. TTL value might be
inaccurate up-to few seconds due to optimization
consideration. */
ebuckets
hfe
;
/* DS of Hash Fields Expiration, associated to each hash */
sds
key
;
/* reference to the key, same one that stored in
db->dict. Will be used from active-expiration flow
...
...
@@ -3191,9 +3196,16 @@ typedef struct dictExpireMetadata {
#define HASH_SET_TAKE_VALUE (1<<1)
#define HASH_SET_COPY 0
/* Hash field lazy expiration flags. Used by core hashTypeGetValue() and its callers */
#define HFE_LAZY_EXPIRE (0)
/* Delete expired field, and if last field also the hash */
#define HFE_LAZY_AVOID_FIELD_DEL (1<<0)
/* Avoid deleting expired field */
#define HFE_LAZY_AVOID_HASH_DEL (1<<1)
/* Avoid deleting hash if the field is the last one */
#define HFE_LAZY_NO_NOTIFICATION (1<<2)
/* Do not send notification, used when multiple fields
* may expire and only one notification is desired. */
void
hashTypeConvert
(
robj
*
o
,
int
enc
,
ebuckets
*
hexpires
);
void
hashTypeTryConversion
(
redisDb
*
db
,
robj
*
subject
,
robj
**
argv
,
int
start
,
int
end
);
int
hashTypeExists
(
redisDb
*
db
,
robj
*
o
,
sds
key
,
int
*
isHashDeleted
);
int
hashTypeExists
(
redisDb
*
db
,
robj
*
o
,
sds
key
,
int
hfeFlags
,
int
*
isHashDeleted
);
int
hashTypeDelete
(
robj
*
o
,
void
*
key
,
int
isSdsField
);
unsigned
long
hashTypeLength
(
const
robj
*
o
,
int
subtractExpiredFields
);
hashTypeIterator
*
hashTypeInitIterator
(
robj
*
subject
);
...
...
@@ -3210,20 +3222,17 @@ void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr,
unsigned
int
*
vlen
,
long
long
*
vll
,
uint64_t
*
expireTime
);
sds
hashTypeCurrentObjectNewSds
(
hashTypeIterator
*
hi
,
int
what
);
hfield
hashTypeCurrentObjectNewHfield
(
hashTypeIterator
*
hi
);
robj
*
hashTypeGetValueObject
(
redisDb
*
db
,
robj
*
o
,
sds
field
,
int
*
isHashDeleted
);
robj
*
hashTypeGetValueObject
(
redisDb
*
db
,
robj
*
o
,
sds
field
,
int
hfeFlags
,
int
*
isHashDeleted
);
int
hashTypeSet
(
redisDb
*
db
,
robj
*
o
,
sds
field
,
sds
value
,
int
flags
);
robj
*
hashTypeDup
(
robj
*
o
,
sds
newkey
,
uint64_t
*
minHashExpire
);
uint64_t
hashTypeRemoveFromExpires
(
ebuckets
*
hexpires
,
robj
*
o
);
void
hashTypeAddToExpires
(
redisDb
*
db
,
sds
key
,
robj
*
hashObj
,
uint64_t
expireTime
);
void
hashTypeFree
(
robj
*
o
);
int
hashTypeIsExpired
(
const
robj
*
o
,
uint64_t
expireAt
);
uint64_t
hashTypeGetMinExpire
(
robj
*
o
);
unsigned
char
*
hashTypeListpackGetLp
(
robj
*
o
);
uint64_t
hashTypeGetMinExpire
(
robj
*
o
);
uint64_t
hashTypeGetMinExpire
(
robj
*
o
,
int
accurate
);
void
hashTypeUpdateKeyRef
(
robj
*
o
,
sds
newkey
);
ebuckets
*
hashTypeGetDictMetaHFE
(
dict
*
d
);
uint64_t
hashTypeGetMinExpire
(
robj
*
keyObj
);
uint64_t
hashTypeGetNextTimeToExpire
(
robj
*
o
);
void
initDictExpireMetadata
(
sds
key
,
robj
*
o
);
struct
listpackEx
*
listpackExCreate
(
void
);
void
listpackExAddNew
(
robj
*
o
,
char
*
field
,
size_t
flen
,
...
...
@@ -3531,6 +3540,7 @@ void startEvictionTimeProc(void);
/* Keys hashing / comparison functions for dict.c hash tables. */
uint64_t
dictSdsHash
(
const
void
*
key
);
uint64_t
dictPtrHash
(
const
void
*
key
);
uint64_t
dictSdsCaseHash
(
const
void
*
key
);
int
dictSdsKeyCompare
(
dict
*
d
,
const
void
*
key1
,
const
void
*
key2
);
int
dictSdsMstrKeyCompare
(
dict
*
d
,
const
void
*
sdsLookup
,
const
void
*
mstrStored
);
...
...
src/sort.c
View file @
4e9acd4b
...
...
@@ -95,7 +95,7 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
/* Retrieve value from hash by the field name. The returned object
* is a new object with refcount already incremented. */
int
isHashDeleted
;
o
=
hashTypeGetValueObject
(
db
,
o
,
fieldobj
->
ptr
,
&
isHashDeleted
);
o
=
hashTypeGetValueObject
(
db
,
o
,
fieldobj
->
ptr
,
HFE_LAZY_EXPIRE
,
&
isHashDeleted
);
if
(
isHashDeleted
)
goto
noobj
;
...
...
src/t_hash.c
View file @
4e9acd4b
This diff is collapsed.
Click to expand it.
tests/integration/rdb.tcl
View file @
4e9acd4b
...
...
@@ -429,8 +429,8 @@ start_server [list overrides [list "dir" $server_path]] {
r HMSET key a 1 b 2 c 3 d 4 e 5
# expected to be expired long after restart
r HEXPIREAT key 2524600800 FIELDS 1 a
# expected long TTL value
(
6 b
yte
s
)
is saved and loaded correctly
r HPEXPIREAT key
188900976391764
FIELDS 1 b
# expected long TTL value
(
4
6 b
it
s
)
is saved and loaded correctly
r HPEXPIREAT key
65755674080852
FIELDS 1 b
# expected to be already expired after restart
r HPEXPIRE key 80 FIELDS 1 d
# expected to be expired soon after restart
...
...
@@ -443,7 +443,7 @@ start_server [list overrides [list "dir" $server_path]] {
wait_done_loading r
assert_equal
[
lsort
[
r hgetall key
]]
"1 2 3 a b c"
assert_equal
[
r hpexpiretime key FIELDS 3 a b c
]
{
2524600800000
188900976391764
-1
}
assert_equal
[
r hpexpiretime key FIELDS 3 a b c
]
{
2524600800000
65755674080852
-1
}
assert_equal
[
s rdb_last_load_keys_loaded
]
1
# wait until expired_hash_fields equals 2
...
...
tests/modules/propagate.c
View file @
4e9acd4b
...
...
@@ -302,6 +302,21 @@ int propagateTestIncr(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return
REDISMODULE_OK
;
}
int
propagateTestVerbatim
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
<
2
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
long
long
replicate_num
;
RedisModule_StringToLongLong
(
argv
[
1
],
&
replicate_num
);
/* Replicate the command verbatim for the specified number of times. */
for
(
long
long
i
=
0
;
i
<
replicate_num
;
i
++
)
RedisModule_ReplicateVerbatim
(
ctx
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
...
...
@@ -368,6 +383,11 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
propagateTestIncr
,
"write"
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.verbatim"
,
propagateTestVerbatim
,
"write"
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
...
...
tests/unit/moduleapi/hash.tcl
View file @
4e9acd4b
...
...
@@ -21,6 +21,45 @@ start_server {tags {"modules"}} {
r hgetall k
}
{
squirrel ofcourse banana no what nothing something nice
}
test
{
Module hash - set
(
override
)
NX expired field successfully
}
{
r debug set-active-expire 0
r del H1 H2
r hash.set H1
"n"
f1 v1
r hpexpire H1 1 FIELDS 1 f1
r hash.set H2
"n"
f1 v1 f2 v2
r hpexpire H2 1 FIELDS 1 f1
after 5
assert_equal 0
[
r hash.set H1
"n"
f1 xx
]
assert_equal
"f1 xx"
[
r hgetall H1
]
assert_equal 0
[
r hash.set H2
"n"
f1 yy
]
assert_equal
"f1 f2 v2 yy"
[
lsort
[
r hgetall H2
]]
r debug set-active-expire 1
}
{
OK
}
{
needs:debug
}
test
{
Module hash - set XX of expired field gets failed as expected
}
{
r debug set-active-expire 0
r del H1 H2
r hash.set H1
"n"
f1 v1
r hpexpire H1 1 FIELDS 1 f1
r hash.set H2
"n"
f1 v1 f2 v2
r hpexpire H2 1 FIELDS 1 f1
after 5
# expected to fail on condition XX. hgetall should return empty list
r hash.set H1
"x"
f1 xx
assert_equal
""
[
lsort
[
r hgetall H1
]]
# But expired field was not lazy deleted
assert_equal 1
[
r hlen H1
]
# expected to fail on condition XX. hgetall should return list without expired f1
r hash.set H2
"x"
f1 yy
assert_equal
"f2 v2"
[
lsort
[
r hgetall H2
]]
# But expired field was not lazy deleted
assert_equal 2
[
r hlen H2
]
r debug set-active-expire 1
}
{
OK
}
{
needs:debug
}
test
"Unload the module - hash"
{
assert_equal
{
OK
}
[
r module unload hash
]
}
...
...
tests/unit/moduleapi/propagate.tcl
View file @
4e9acd4b
...
...
@@ -761,3 +761,41 @@ tags "modules aof" {
}
}
}
# This test does not really test module functionality, but rather uses a module
# command to test Redis replication mechanisms.
test
{
Replicas that was marked as CLIENT_CLOSE_ASAP should not keep the replication backlog from been trimmed
}
{
start_server
[
list overrides
[
list loadmodule
"
$testmodule
"
]]
{
set replica
[
srv 0 client
]
start_server
[
list overrides
[
list loadmodule
"
$testmodule
"
]]
{
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
set master_port
[
srv 0 port
]
$master config set client-output-buffer-limit
"replica 10mb 5mb 0"
# Start the replication process...
$replica replicaof $master_host $master_port
wait_for_sync $replica
test
{
module propagates from timer
}
{
# Replicate large commands to make the replica disconnected.
$master write
[
format_command propagate-test.verbatim 100000
[
string repeat
"a"
1000
]]
;
# almost 100mb
# Execute this command together with module commands within the same
# event loop to prevent periodic cleanup of replication backlog.
$master write
[
format_command info memory
]
$master flush
$master read
;
# propagate-test.verbatim
set res
[
$master
read
]
;
# info memory
# Wait for the replica to be disconnected.
wait_for_log_messages 0
{
"*flags=S*scheduled to be closed ASAP for overcoming of output buffer limits*"
}
0 1500 10
# Due to the replica reaching the soft limit
(
5MB
)
, memory peaks should not significantly
# exceed the replica soft limit. Furthermore, as the replica release its reference to
# replication backlog, it should be properly trimmed, the memory usage of replication
# backlog should not significantly exceed repl-backlog-size
(
default 1MB
)
. */
assert_lessthan
[
getInfoProperty $res used_memory_peak
]
10000000
;
# less than 10mb
assert_lessthan
[
getInfoProperty $res mem_replication_backlog
]
2000000
;
# less than 2mb
}
}
}
}
tests/unit/moduleapi/scan.tcl
View file @
4e9acd4b
...
...
@@ -25,12 +25,16 @@ start_server {tags {"modules"}} {
}
{{
f1 1
}}
test
{
Module scan hash listpack with hexpire
}
{
r hmset hh f1 v1 f2 v2
r debug set-active-expire 0
r hmset hh f1 v1 f2 v2 f3 v3
r hexpire hh 100000 fields 1 f1
r hpexpire hh 1 fields 1 f3
after 10
assert_range
[
r httl hh fields 1 f1
]
10000 100000
assert_encoding listpackex hh
r debug set-active-expire 1
lsort
[
r scan.scan_key hh
]
}
{{
f1 v1
}
{
f2 v2
}}
}
{{
f1 v1
}
{
f2 v2
}}
{
needs:debug
}
test
{
Module scan hash dict
}
{
r config set hash-max-ziplist-entries 2
...
...
@@ -44,10 +48,22 @@ start_server {tags {"modules"}} {
r del hh
r hmset hh f1 v1 f2 v2 f3 v3
r hexpire hh 100000 fields 1 f2
r hpexpire hh 5 fields 1 f3
assert_range
[
r httl hh fields 1 f2
]
10000 100000
assert_encoding hashtable hh
after 10
lsort
[
r scan.scan_key hh
]
}
{{
f1 v1
}
{
f2 v2
}
{
f3 v3
}}
}
{{
f1 v1
}
{
f2 v2
}}
test
{
Module scan hash with hexpire can return no items
}
{
r del hh
r debug set-active-expire 0
r hmset hh f1 v1 f2 v2 f3 v3
r hpexpire hh 1 fields 3 f1 f2 f3
after 10
r debug set-active-expire 1
lsort
[
r scan.scan_key hh
]
}
{}
{
needs:debug
}
test
{
Module scan zset listpack
}
{
r zadd zz 1 f1 2 f2
...
...
tests/unit/pubsub.tcl
View file @
4e9acd4b
...
...
@@ -356,16 +356,17 @@ start_server {tags {"pubsub network"}} {
foreach
{
type max_lp_entries
}
{
listpackex 512 hashtable 0
}
{
test
"Keyspace notifications: hash events test (
$type
)"
{
r config set hash-max-listpack-entries $max_lp_entries
r config set notify-keyspace-events Kh
r config set notify-keyspace-events Kh
g
r del myhash
set rd1
[
redis_deferring_client
]
assert_equal
{
1
}
[
psubscribe $rd1 *
]
r hmset myhash yes 1 no 0
r hmset myhash yes 1 no 0
f1 1 f2 2 f3_hdel 3
r hincrby myhash yes 10
r hexpire myhash 999999 FIELDS 1 yes
r hexpireat myhash
[
expr
{[
clock seconds
]
+ 999999
}]
NX FIELDS 1 no
r hpexpire myhash 999999 FIELDS 1 yes
r hpersist myhash FIELDS 1 yes
r hpexpire myhash 0 FIELDS 1 yes
assert_encoding $type myhash
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hset"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hincrby"
[
$rd1
read
]
...
...
@@ -373,8 +374,38 @@ start_server {tags {"pubsub network"}} {
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpire"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpire"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hpersist"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpired"
[
$rd1
read
]
# Test that we will get `hexpired` notification when
# a hash field is removed by active expire.
r hpexpire myhash 10 FIELDS 1 no
after 100
;
# Wait for active expire
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpire"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpired"
[
$rd1
read
]
# Test that when a field with TTL is deleted by commands like hdel without
# updating the global DS, active expire will not send a notification.
r hpexpire myhash 100 FIELDS 1 f3_hdel
r hdel myhash f3_hdel
after 200
;
# Wait for active expire
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpire"
[
$rd1
read
]
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hdel"
[
$rd1
read
]
# Test that we will get `hexpired` notification when
# a hash field is removed by lazy expire.
r debug set-active-expire 0
r hpexpire myhash 10 FIELDS 2 f1 f2
after 20
r hmget myhash f1 f2
;
# Trigger lazy expire
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpire"
[
$rd1
read
]
# We should get only one `hexpired` notification even two fields was expired.
assert_equal
"pmessage * __keyspace@
${db}
__:myhash hexpired"
[
$rd1
read
]
# We should get a `del` notification after all fields were expired.
assert_equal
"pmessage * __keyspace@
${db}
__:myhash del"
[
$rd1
read
]
r debug set-active-expire 1
$rd1 close
}
}
{
0
}
{
needs:debug
}
}
;
# foreach
test
"Keyspace notifications: stream events test"
{
...
...
tests/unit/type/hash-field-expire.tcl
View file @
4e9acd4b
...
...
@@ -32,13 +32,6 @@ proc get_hashes_with_expiry_fields {r} {
return 0
}
proc create_hash
{
key entries
}
{
r del $key
foreach entry $entries
{
r hset $key
[
lindex $entry 0
]
[
lindex $entry 1
]
}
}
proc get_keys
{
l
}
{
set res
{}
foreach entry $l
{
...
...
@@ -48,22 +41,6 @@ proc get_keys {l} {
return $res
}
proc cmp_hrandfield_result
{
hash_name expected_result
}
{
# Accumulate hrandfield results
unset -nocomplain myhash
array set myhash
{}
for
{
set i 0
}
{
$i
< 100
}
{
incr i
}
{
set key
[
r hrandfield $hash_name
]
set myhash
(
$key
)
1
}
set res
[
lsort
[
array names myhash
]]
if
{
$res
eq $expected_result
}
{
return 1
}
else
{
return $res
}
}
proc dumpAllHashes
{
client
}
{
set keyAndFields
(
0,0
)
0
unset keyAndFields
...
...
@@ -77,36 +54,6 @@ proc dumpAllHashes {client} {
return
[
array get keyAndFields
]
}
proc hrandfieldTest
{
activeExpireConfig
}
{
r debug set-active-expire $activeExpireConfig
r del myhash
set contents
{{
field1 1
}
{
field2 2
}
}
create_hash myhash $contents
set factorValgrind
[
expr
{
$::valgrind
? 2 : 1
}]
# Set expiration time for field1 and field2 such that field1 expires first
r hpexpire myhash 1 NX FIELDS 1 field1
r hpexpire myhash 100 NX FIELDS 1 field2
# On call hrandfield command lazy expire deletes field1 first
wait_for_condition 8 10
{
[
cmp_hrandfield_result myhash
"field2"
]
== 1
}
else
{
fail
"Expected field2 to be returned by HRANDFIELD."
}
# On call hrandfield command lazy expire deletes field2 as well
wait_for_condition 8 20
{
[
cmp_hrandfield_result myhash
"{}"
]
== 1
}
else
{
fail
"Expected {} to be returned by HRANDFIELD."
}
# restore the default value
r debug set-active-expire 1
}
############################### TESTS #########################################
start_server
{
tags
{
"external:skip needs:debug"
}}
{
...
...
@@ -117,15 +64,12 @@ start_server {tags {"external:skip needs:debug"}} {
r config set hash-max-listpack-entries 512
}
test
"HEXPIRE/HEXPIREAT/HPEXPIRE/HPEXPIREAT - Returns
empty
array if key does not exist"
{
test
"HEXPIRE/HEXPIREAT/HPEXPIRE/HPEXPIREAT - Returns array if
the
key does not exist"
{
r del myhash
# Make sure we can distinguish between an empty array and a null response
r readraw 1
assert_equal
{
*0
}
[
r HEXPIRE myhash 1000 FIELDS 1 a
]
assert_equal
{
*0
}
[
r HEXPIREAT myhash 1000 FIELDS 1 a
]
assert_equal
{
*0
}
[
r HPEXPIRE myhash 1000 FIELDS 1 a
]
assert_equal
{
*0
}
[
r HPEXPIREAT myhash 1000 FIELDS 1 a
]
r readraw 0
assert_equal
[
r HEXPIRE myhash 1000 FIELDS 1 a
]
[
list $E_NO_FIELD
]
assert_equal
[
r HEXPIREAT myhash 1000 FIELDS 1 a
]
[
list $E_NO_FIELD
]
assert_equal
[
r HPEXPIRE myhash 1000 FIELDS 2 a b
]
[
list $E_NO_FIELD $E_NO_FIELD
]
assert_equal
[
r HPEXPIREAT myhash 1000 FIELDS 2 a b
]
[
list $E_NO_FIELD $E_NO_FIELD
]
}
test
"HEXPIRE/HEXPIREAT/HPEXPIRE/HPEXPIREAT - Verify that the expire time does not overflow"
{
...
...
@@ -205,15 +149,17 @@ start_server {tags {"external:skip needs:debug"}} {
r del myhash
r hset myhash f1 v1
assert_error
{
*Parameter `numFields` should be greater than 0
}
{
r hpexpire myhash 1000 NX FIELDS 0 f1 f2 f3
}
assert_error
{
*Parameter `numFields` is more than number of arguments
}
{
r hpexpire myhash 1000 NX FIELDS 4 f1 f2 f3
}
# <count> not match with actual number of fields
assert_error
{
*parameter must match the number*
}
{
r hpexpire myhash 1000 NX FIELDS 4 f1 f2 f3
}
assert_error
{
*parameter must match the number*
}
{
r hpexpire myhash 1000 NX FIELDS 2 f1 f2 f3
}
}
test
"HPEXPIRE - parameter expire-time near limit of 2^4
8
(
$type
)"
{
test
"HPEXPIRE - parameter expire-time near limit of 2^4
6
(
$type
)"
{
r del myhash
r hset myhash f1 v1
# below & above
assert_equal
[
r hpexpire myhash
[
expr
(
1<<4
8
)
-
[
clock milliseconds
]
- 1000
]
FIELDS 1 f1
]
[
list $E_OK
]
assert_error
{
*invalid expire time*
}
{
r hpexpire myhash
[
expr
(
1<<4
8
)
-
[
clock milliseconds
]
+ 100
]
FIELDS 1 f1
}
assert_equal
[
r hpexpire myhash
[
expr
(
1<<4
6
)
-
[
clock milliseconds
]
- 1000
]
FIELDS 1 f1
]
[
list $E_OK
]
assert_error
{
*invalid expire time*
}
{
r hpexpire myhash
[
expr
(
1<<4
6
)
-
[
clock milliseconds
]
+ 100
]
FIELDS 1 f1
}
}
test
"Lazy Expire - fields are lazy deleted (
$type
)"
{
...
...
@@ -305,13 +251,10 @@ start_server {tags {"external:skip needs:debug"}} {
r flushall async
}
test
"HTTL/HPTTL - Returns
empty
array if key does not exist"
{
test
"HTTL/HPTTL - Returns array if
the
key does not exist"
{
r del myhash
# Make sure we can distinguish between an empty array and a null response
r readraw 1
assert_equal
{
*0
}
[
r HTTL myhash FIELDS 1 a
]
assert_equal
{
*0
}
[
r HPTTL myhash FIELDS 1 a
]
r readraw 0
assert_equal
[
r HTTL myhash FIELDS 1 a
]
[
list $T_NO_FIELD
]
assert_equal
[
r HPTTL myhash FIELDS 2 a b
]
[
list $T_NO_FIELD $T_NO_FIELD
]
}
test
"HTTL/HPTTL - Input validation gets failed on nonexists field or field without expire (
$type
)"
{
...
...
@@ -320,10 +263,10 @@ start_server {tags {"external:skip needs:debug"}} {
r HPEXPIRE myhash 1000 NX FIELDS 1 field1
foreach cmd
{
HTTL HPTTL
}
{
assert_equal
[
r $cmd non_exists_key FIELDS 1 f
]
{}
assert_equal
[
r $cmd myhash FIELDS 2 field2 non_exists_field
]
"
$
T_NO_EXPIRY
$
T_NO_FIELD"
# Set numFields less than actual number of fields. Fine.
assert_equal
[
r $cmd myhash FIELDS 1 non_exists_field1 non_exists_field2
]
"
$
T_NO_FIELD"
# <count> not match with actual number of fields
assert_error
{
*parameter must match the number*
}
{
r $cmd myhash FIELDS 1 non_exists_field1 non_exists_field2
}
assert_error
{
*parameter must match the number*
}
{
r $cmd myhash FIELDS 3 non_exists_field1 non_exists_field2
}
}
}
...
...
@@ -337,13 +280,10 @@ start_server {tags {"external:skip needs:debug"}} {
assert_range $ttl 1000 2000
}
test
"HEXPIRETIME/HPEXPIRETIME - Returns
empty
array if key does not exist"
{
test
"HEXPIRETIME/HPEXPIRETIME - Returns array if
the
key does not exist"
{
r del myhash
# Make sure we can distinguish between an empty array and a null response
r readraw 1
assert_equal
{
*0
}
[
r HEXPIRETIME myhash FIELDS 1 a
]
assert_equal
{
*0
}
[
r HPEXPIRETIME myhash FIELDS 1 a
]
r readraw 0
assert_equal
[
r HEXPIRETIME myhash FIELDS 1 a
]
[
list $T_NO_FIELD
]
assert_equal
[
r HPEXPIRETIME myhash FIELDS 2 a b
]
[
list $T_NO_FIELD $T_NO_FIELD
]
}
test
"HEXPIRETIME - returns TTL in Unix timestamp (
$type
)"
{
...
...
@@ -406,22 +346,33 @@ start_server {tags {"external:skip needs:debug"}} {
r debug set-active-expire 1
}
# OPEN: To decide if to delete expired fields at start of HRANDFIELD.
# test
"Test HRANDFIELD does not return expired fields (
$type
)"
{
# hrandfieldTest 0
# hrandfieldTest 1
#
}
test
"Test HRANDFIELD can return expired fields (
$type
)"
{
test
"Test HRANDFIELD deletes all expired fields (
$type
)"
{
r debug set-active-expire 0
r
del myhash
r
flushall
r hset myhash f1 v1 f2 v2 f3 v3 f4 v4 f5 v5
r hpexpire myhash 1
NX
FIELDS
4
f1 f2
f3 f4
r hpexpire myhash 1 FIELDS
2
f1 f2
after 5
set res
[
cmp_hrandfield_result myhash
"f1 f2 f3 f4 f5"
]
assert
{
$res
== 1
}
r debug set-active-expire 1
assert_equal
[
lsort
[
r hrandfield myhash 5
]]
"f3 f4 f5"
r hpexpire myhash 1 FIELDS 3 f3 f4 f5
after 5
assert_equal
[
lsort
[
r hrandfield myhash 5
]]
""
assert_equal
[
r keys *
]
""
r del myhash
r hset myhash f1 v1 f2 v2 f3 v3
r hpexpire myhash 1 FIELDS 1 f1
after 5
set res
[
r hrandfield myhash
]
assert
{
$res
==
"f2"
|| $res ==
"f3"
}
r hpexpire myhash 1 FIELDS 1 f2
after 5
assert_equal
[
lsort
[
r hrandfield myhash 5
]]
"f3"
r hpexpire myhash 1 FIELDS 1 f3
after 5
assert_equal
[
r hrandfield myhash
]
""
assert_equal
[
r keys *
]
""
r debug set-active-expire 1
}
test
"Lazy Expire - HLEN does count expired fields (
$type
)"
{
...
...
@@ -711,12 +662,10 @@ start_server {tags {"external:skip needs:debug"}} {
r debug set-active-expire 1
}
test
"HPERSIST - Returns
empty
array if key does not exist (
$type
)"
{
test
"HPERSIST - Returns array if
the
key does not exist (
$type
)"
{
r del myhash
# Make sure we can distinguish between an empty array and a null response
r readraw 1
assert_equal
{
*0
}
[
r HPERSIST myhash FIELDS 1 a
]
r readraw 0
assert_equal
[
r HPERSIST myhash FIELDS 1 a
]
[
list $P_NO_FIELD
]
assert_equal
[
r HPERSIST myhash FIELDS 2 a b
]
[
list $P_NO_FIELD $P_NO_FIELD
]
}
test
"HPERSIST - input validation (
$type
)"
{
...
...
@@ -726,9 +675,11 @@ start_server {tags {"external:skip needs:debug"}} {
r hexpire myhash 1000 NX FIELDS 1 f1
assert_error
{
*wrong number of arguments*
}
{
r hpersist myhash
}
assert_error
{
*wrong number of arguments*
}
{
r hpersist myhash FIELDS 1
}
assert_equal
[
r hpersist not-exists-key FIELDS 1 f1
]
{}
assert_equal
[
r hpersist myhash FIELDS 2 f1 not-exists-field
]
"
$
P_OK
$
P_NO_FIELD"
assert_equal
[
r hpersist myhash FIELDS 1 f2
]
"
$
P_NO_EXPIRY"
# <count> not match with actual number of fields
assert_error
{
*parameter must match the number*
}
{
r hpersist myhash FIELDS 2 f1 f2 f3
}
assert_error
{
*parameter must match the number*
}
{
r hpersist myhash FIELDS 4 f1 f2 f3
}
}
test
"HPERSIST - verify fields with TTL are persisted (
$type
)"
{
...
...
@@ -969,14 +920,29 @@ start_server {tags {"external:skip needs:debug"}} {
r config set hash-max-listpack-entries 512
}
test
"Command
rewrite and expired hash fields are
propagated to replica (
$type
)"
{
test
"
Test
Command propagated to replica
as expected
(
$type
)"
{
start_server
{
overrides
{
appendonly
{
yes
}
appendfsync always
}
tags
{
external:skip
}}
{
set aof
[
get_last_incr_aof_path r
]
# Time is in the past so it should propagate HDELs to replica
# and delete the fields
r hset h0 x1 y1 x2 y2
r hexpireat h0 1 fields 3 x1 x2 non_exists_field
r hset h1 f1 v1 f2 v2
# Next command won't be propagated to replica
# because XX condition not met or field not exists
r hexpire h1 10 XX FIELDS 3 f1 f2 non_exists_field
r hpexpire h1 20 FIELDS 1 f1
r hpexpire h1 30 FIELDS 1 f2
# Next command will be propagate with only field 'f2'
# because NX condition not met for field 'f1'
r hpexpire h1 30 NX FIELDS 2 f1 f2
# Non exists field should be ignored
r hpexpire h1 30 FIELDS 1 non_exists_field
r hset h2 f1 v1 f2 v2 f3 v3 f4 v4
r hpexpire h2 40 FIELDS 2 f1 non_exists_field
...
...
@@ -993,11 +959,16 @@ start_server {tags {"external:skip needs:debug"}} {
# Assert that each TTL-related command are persisted with absolute timestamps in AOF
assert_aof_content $aof
{
{
select *
}
{
hset h0 x1 y1 x2 y2
}
{
multi
}
{
hdel h0 x1
}
{
hdel h0 x2
}
{
exec
}
{
hset h1 f1 v1 f2 v2
}
{
hpexpireat h1 * FIELDS 1 f1
}
{
hpexpireat h1 * FIELDS 1 f2
}
{
hset h2 f1 v1 f2 v2 f3 v3 f4 v4
}
{
hpexpireat h2 * FIELDS
2
f1
non_exists_field
}
{
hpexpireat h2 * FIELDS
1
f1
}
{
hpexpireat h2 * FIELDS 1 f2
}
{
hpexpireat h2 * FIELDS 1 f3
}
{
hpexpireat h2 * FIELDS 1 f4
}
...
...
@@ -1067,7 +1038,13 @@ start_server {tags {"external:skip needs:debug"}} {
r hpexpireat h1
[
expr
[
clock seconds
]
*1000+100000
]
NX FIELDS 1 f2
r hpexpire h1 100000 NX FIELDS 3 f3 f4 f5
r hexpire h1 100000 FIELDS 1 f6
r hset h5 f1 v1
# Verify HRANDFIELD deletes expired fields and propagates it
r hset h2 f1 v1 f2 v2
r hpexpire h2 1 FIELDS 2 f1 f2
after 5
assert_equal
[
r hrandfield h4 2
]
""
after 200
assert_aof_content $aof
{
{
select *
}
...
...
@@ -1076,7 +1053,10 @@ start_server {tags {"external:skip needs:debug"}} {
{
hpexpireat h1 * FIELDS 1 f2
}
{
hpexpireat h1 * NX FIELDS 3 f3 f4 f5
}
{
hpexpireat h1 * FIELDS 1 f6
}
{
hset h5 f1 v1
}
{
hset h2 f1 v1 f2 v2
}
{
hpexpireat h2 * FIELDS 2 f1 f2
}
{
hdel h2 *
}
{
hdel h2 *
}
}
array set keyAndFields1
[
dumpAllHashes r
]
...
...
@@ -1099,12 +1079,16 @@ start_server {tags {"external:skip needs:debug"}} {
r flushall
;
# Clean up keyspace to avoid interference by keys from other tests
set repl
[
attach_to_replication_stream
]
# HEXPIRE/HPEXPIRE should be translated into HPEXPIREAT
r hset h1 f1 v1
r hexpireat h1
[
expr
[
clock seconds
]
+100
]
NX FIELDS 1 f1
r hset h2 f2 v2
r hpexpireat h2
[
expr
[
clock seconds
]
*1000+100000
]
NX FIELDS 1 f2
r hset h3 f3 v3 f4 v4
r hset h3 f3 v3 f4 v4 f5 v5
# hpersist does nothing here. Verify it is not propagated.
r hpersist h3 FIELDS 1 f5
r hexpire h3 100 FIELDS 3 f3 f4 non_exists_field
r hpersist h3 FIELDS 1 f3
assert_replication_stream $repl
{
{
select *
}
...
...
@@ -1112,12 +1096,55 @@ start_server {tags {"external:skip needs:debug"}} {
{
hpexpireat h1 * NX FIELDS 1 f1
}
{
hset h2 f2 v2
}
{
hpexpireat h2 * NX FIELDS 1 f2
}
{
hset h3 f3 v3 f4 v4
}
{
hpexpireat h3 * FIELDS 3 f3 f4 non_exists_field
}
{
hset h3 f3 v3 f4 v4 f5 v5
}
{
hpexpireat h3 * FIELDS 2 f3 f4
}
{
hpersist h3 FIELDS 1 f3
}
}
close_replication_stream $repl
}
{}
{
needs:repl
}
test
{
HRANDFIELD delete expired fields and propagate DELs to replica
}
{
r debug set-active-expire 0
r flushall
set repl
[
attach_to_replication_stream
]
# HRANDFIELD delete expired fields and propagate MULTI-EXEC DELs. Reply none.
r hset h1 f1 v1 f2 v2
r hpexpire h1 1 FIELDS 2 f1 f2
after 5
assert_equal
[
r hrandfield h1 2
]
""
# HRANDFIELD delete expired field and propagate DEL. Reply non-expired field.
r hset h2 f1 v1 f2 v2
r hpexpire h2 1 FIELDS 1 f1
after 5
assert_equal
[
r hrandfield h2 2
]
"f2"
# HRANDFIELD delete expired field and propagate DEL. Reply none.
r hset h3 f1 v1
r hpexpire h3 1 FIELDS 1 f1
after 5
assert_equal
[
r hrandfield h3 2
]
""
assert_replication_stream $repl
{
{
select *
}
{
hset h1 f1 v1 f2 v2
}
{
hpexpireat h1 * FIELDS 2 f1 f2
}
{
multi
}
{
hdel h1 *
}
{
hdel h1 *
}
{
exec
}
{
hset h2 f1 v1 f2 v2
}
{
hpexpireat h2 * FIELDS 1 f1
}
{
hdel h2 f1
}
{
hset h3 f1 v1
}
{
hpexpireat h3 * FIELDS 1 f1
}
{
hdel h3 f1
}
}
close_replication_stream $repl
r debug set-active-expire 1
}
{
OK
}
{
needs:repl
}
# Start another server to test replication of TTLs
start_server
{
tags
{
needs:repl external:skip
}}
{
# Set the outer layer server as primary
...
...
@@ -1161,4 +1188,3 @@ start_server {tags {"external:skip needs:debug"}} {
}
}
}
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment