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
9e67df2a
Commit
9e67df2a
authored
Jul 21, 2015
by
Oran Agra
Browse files
rebase from unstable
parents
f472bb10
bcb4d091
Changes
60
Hide whitespace changes
Inline
Side-by-side
src/sort.c
View file @
9e67df2a
...
...
@@ -209,7 +209,7 @@ void sortCommand(redisClient *c) {
}
/* Create a list of operations to perform for every sorted element.
* Operations can be GET
/DEL/INCR/DECR
*/
* Operations can be GET */
operations
=
listCreate
();
listSetFreeMethod
(
operations
,
zfree
);
j
=
2
;
/* options start at argv[2] */
...
...
src/t_set.c
View file @
9e67df2a
...
...
@@ -144,7 +144,11 @@ void setTypeReleaseIterator(setTypeIterator *si) {
* Since set elements can be internally be stored as redis objects or
* simple arrays of integers, setTypeNext returns the encoding of the
* set object you are iterating, and will populate the appropriate pointer
* (eobj) or (llobj) accordingly.
* (objele) or (llele) accordingly.
*
* Note that both the objele and llele pointers should be passed and cannot
* be NULL since the function will try to defensively populate the non
* used field with values which are easy to trap if misused.
*
* When there are no longer elements -1 is returned.
* Returned objects ref count is not incremented, so this function is
...
...
@@ -154,9 +158,13 @@ int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {
dictEntry
*
de
=
dictNext
(
si
->
di
);
if
(
de
==
NULL
)
return
-
1
;
*
objele
=
dictGetKey
(
de
);
*
llele
=
-
123456789
;
/* Not needed. Defensive. */
}
else
if
(
si
->
encoding
==
REDIS_ENCODING_INTSET
)
{
if
(
!
intsetGet
(
si
->
subject
->
ptr
,
si
->
ii
++
,
llele
))
return
-
1
;
*
objele
=
NULL
;
/* Not needed. Defensive. */
}
else
{
redisPanic
(
"Wrong set encoding in setTypeNext"
);
}
return
si
->
encoding
;
}
...
...
@@ -197,6 +205,10 @@ robj *setTypeNextObject(setTypeIterator *si) {
* field of the object and is used by the caller to check if the
* int64_t pointer or the redis object pointer was populated.
*
* Note that both the objele and llele pointers should be passed and cannot
* be NULL since the function will try to defensively populate the non
* used field with values which are easy to trap if misused.
*
* When an object is returned (the set was a real set) the ref count
* of the object is not incremented so this function can be considered
* copy on write friendly. */
...
...
@@ -204,8 +216,10 @@ int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
if
(
setobj
->
encoding
==
REDIS_ENCODING_HT
)
{
dictEntry
*
de
=
dictGetRandomKey
(
setobj
->
ptr
);
*
objele
=
dictGetKey
(
de
);
*
llele
=
-
123456789
;
/* Not needed. Defensive. */
}
else
if
(
setobj
->
encoding
==
REDIS_ENCODING_INTSET
)
{
*
llele
=
intsetRandom
(
setobj
->
ptr
);
*
objele
=
NULL
;
/* Not needed. Defensive. */
}
else
{
redisPanic
(
"Unknown set encoding"
);
}
...
...
@@ -240,7 +254,7 @@ void setTypeConvert(robj *setobj, int enc) {
/* To add the elements we extract integers and create redis objects */
si
=
setTypeInitIterator
(
setobj
);
while
(
setTypeNext
(
si
,
NULL
,
&
intele
)
!=
-
1
)
{
while
(
setTypeNext
(
si
,
&
element
,
&
intele
)
!=
-
1
)
{
element
=
createStringObjectFromLongLong
(
intele
);
redisAssertWithInfo
(
NULL
,
element
,
dictAdd
(
d
,
element
,
NULL
)
==
DICT_OK
);
...
...
@@ -329,7 +343,7 @@ void smoveCommand(redisClient *c) {
/* If srcset and dstset are equal, SMOVE is a no-op */
if
(
srcset
==
dstset
)
{
addReply
(
c
,
s
hared
.
cone
);
addReply
(
c
,
s
etTypeIsMember
(
srcset
,
ele
)
?
shared
.
cone
:
shared
.
czero
);
return
;
}
...
...
src/t_zset.c
View file @
9e67df2a
...
...
@@ -213,7 +213,7 @@ static int zslValueGteMin(double value, zrangespec *spec) {
return
spec
->
minex
?
(
value
>
spec
->
min
)
:
(
value
>=
spec
->
min
);
}
static
int
zslValueLteMax
(
double
value
,
zrangespec
*
spec
)
{
int
zslValueLteMax
(
double
value
,
zrangespec
*
spec
)
{
return
spec
->
maxex
?
(
value
<
spec
->
max
)
:
(
value
<=
spec
->
max
);
}
...
...
@@ -1166,40 +1166,109 @@ void zsetConvert(robj *zobj, int encoding) {
}
}
/* Return (by reference) the score of the specified member of the sorted set
* storing it into *score. If the element does not exist REDIS_ERR is returned
* otherwise REDIS_OK is returned and *score is correctly populated.
* If 'zobj' or 'member' is NULL, REDIS_ERR is returned. */
int
zsetScore
(
robj
*
zobj
,
robj
*
member
,
double
*
score
)
{
if
(
!
zobj
||
!
member
)
return
REDIS_ERR
;
if
(
zobj
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
if
(
zzlFind
(
zobj
->
ptr
,
member
,
score
)
==
NULL
)
return
REDIS_ERR
;
}
else
if
(
zobj
->
encoding
==
REDIS_ENCODING_SKIPLIST
)
{
zset
*
zs
=
zobj
->
ptr
;
dictEntry
*
de
=
dictFind
(
zs
->
dict
,
member
);
if
(
de
==
NULL
)
return
REDIS_ERR
;
*
score
=
*
(
double
*
)
dictGetVal
(
de
);
}
else
{
redisPanic
(
"Unknown sorted set encoding"
);
}
return
REDIS_OK
;
}
/*-----------------------------------------------------------------------------
* Sorted set commands
*----------------------------------------------------------------------------*/
/* This generic command implements both ZADD and ZINCRBY. */
void
zaddGenericCommand
(
redisClient
*
c
,
int
incr
)
{
#define ZADD_NONE 0
#define ZADD_INCR (1<<0)
/* Increment the score instead of setting it. */
#define ZADD_NX (1<<1)
/* Don't touch elements not already existing. */
#define ZADD_XX (1<<2)
/* Only touch elements already exisitng. */
#define ZADD_CH (1<<3)
/* Return num of elements added or updated. */
void
zaddGenericCommand
(
redisClient
*
c
,
int
flags
)
{
static
char
*
nanerr
=
"resulting score is not a number (NaN)"
;
robj
*
key
=
c
->
argv
[
1
];
robj
*
ele
;
robj
*
zobj
;
robj
*
curobj
;
double
score
=
0
,
*
scores
=
NULL
,
curscore
=
0
.
0
;
int
j
,
elements
=
(
c
->
argc
-
2
)
/
2
;
int
added
=
0
,
updated
=
0
;
if
(
c
->
argc
%
2
)
{
int
j
,
elements
;
int
scoreidx
=
0
;
/* The following vars are used in order to track what the command actually
* did during the execution, to reply to the client and to trigger the
* notification of keyspace change. */
int
added
=
0
;
/* Number of new elements added. */
int
updated
=
0
;
/* Number of elements with updated score. */
int
processed
=
0
;
/* Number of elements processed, may remain zero with
options like XX. */
/* Parse options. At the end 'scoreidx' is set to the argument position
* of the score of the first score-element pair. */
scoreidx
=
2
;
while
(
scoreidx
<
c
->
argc
)
{
char
*
opt
=
c
->
argv
[
scoreidx
]
->
ptr
;
if
(
!
strcasecmp
(
opt
,
"nx"
))
flags
|=
ZADD_NX
;
else
if
(
!
strcasecmp
(
opt
,
"xx"
))
flags
|=
ZADD_XX
;
else
if
(
!
strcasecmp
(
opt
,
"ch"
))
flags
|=
ZADD_CH
;
else
if
(
!
strcasecmp
(
opt
,
"incr"
))
flags
|=
ZADD_INCR
;
else
break
;
scoreidx
++
;
}
/* Turn options into simple to check vars. */
int
incr
=
(
flags
&
ZADD_INCR
)
!=
0
;
int
nx
=
(
flags
&
ZADD_NX
)
!=
0
;
int
xx
=
(
flags
&
ZADD_XX
)
!=
0
;
int
ch
=
(
flags
&
ZADD_CH
)
!=
0
;
/* After the options, we expect to have an even number of args, since
* we expect any number of score-element pairs. */
elements
=
c
->
argc
-
scoreidx
;
if
(
elements
%
2
)
{
addReply
(
c
,
shared
.
syntaxerr
);
return
;
}
elements
/=
2
;
/* Now this holds the number of score-element pairs. */
/* Check for incompatible options. */
if
(
nx
&&
xx
)
{
addReplyError
(
c
,
"XX and NX options at the same time are not compatible"
);
return
;
}
if
(
incr
&&
elements
>
1
)
{
addReplyError
(
c
,
"INCR option supports a single increment-element pair"
);
return
;
}
/* Start parsing all the scores, we need to emit any syntax error
* before executing additions to the sorted set, as the command should
* either execute fully or nothing at all. */
scores
=
zmalloc
(
sizeof
(
double
)
*
elements
);
for
(
j
=
0
;
j
<
elements
;
j
++
)
{
if
(
getDoubleFromObjectOrReply
(
c
,
c
->
argv
[
2
+
j
*
2
],
&
scores
[
j
],
NULL
)
if
(
getDoubleFromObjectOrReply
(
c
,
c
->
argv
[
scoreidx
+
j
*
2
],
&
scores
[
j
],
NULL
)
!=
REDIS_OK
)
goto
cleanup
;
}
/* Lookup the key and create the sorted set if does not exist. */
zobj
=
lookupKeyWrite
(
c
->
db
,
key
);
if
(
zobj
==
NULL
)
{
if
(
xx
)
goto
reply_to_client
;
/* No key + XX option: nothing to do. */
if
(
server
.
zset_max_ziplist_entries
==
0
||
server
.
zset_max_ziplist_value
<
sdslen
(
c
->
argv
[
3
]
->
ptr
))
server
.
zset_max_ziplist_value
<
sdslen
(
c
->
argv
[
scoreidx
+
1
]
->
ptr
))
{
zobj
=
createZsetObject
();
}
else
{
...
...
@@ -1220,8 +1289,9 @@ void zaddGenericCommand(redisClient *c, int incr) {
unsigned
char
*
eptr
;
/* Prefer non-encoded element when dealing with ziplists. */
ele
=
c
->
argv
[
3
+
j
*
2
];
ele
=
c
->
argv
[
scoreidx
+
1
+
j
*
2
];
if
((
eptr
=
zzlFind
(
zobj
->
ptr
,
ele
,
&
curscore
))
!=
NULL
)
{
if
(
nx
)
continue
;
if
(
incr
)
{
score
+=
curscore
;
if
(
isnan
(
score
))
{
...
...
@@ -1237,7 +1307,8 @@ void zaddGenericCommand(redisClient *c, int incr) {
server
.
dirty
++
;
updated
++
;
}
}
else
{
processed
++
;
}
else
if
(
!
xx
)
{
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj
->
ptr
=
zzlInsert
(
zobj
->
ptr
,
ele
,
score
);
...
...
@@ -1247,15 +1318,18 @@ void zaddGenericCommand(redisClient *c, int incr) {
zsetConvert
(
zobj
,
REDIS_ENCODING_SKIPLIST
);
server
.
dirty
++
;
added
++
;
processed
++
;
}
}
else
if
(
zobj
->
encoding
==
REDIS_ENCODING_SKIPLIST
)
{
zset
*
zs
=
zobj
->
ptr
;
zskiplistNode
*
znode
;
dictEntry
*
de
;
ele
=
c
->
argv
[
3
+
j
*
2
]
=
tryObjectEncoding
(
c
->
argv
[
3
+
j
*
2
]);
ele
=
c
->
argv
[
scoreidx
+
1
+
j
*
2
]
=
tryObjectEncoding
(
c
->
argv
[
scoreidx
+
1
+
j
*
2
]);
de
=
dictFind
(
zs
->
dict
,
ele
);
if
(
de
!=
NULL
)
{
if
(
nx
)
continue
;
curobj
=
dictGetKey
(
de
);
curscore
=
*
(
double
*
)
dictGetVal
(
de
);
...
...
@@ -1280,22 +1354,30 @@ void zaddGenericCommand(redisClient *c, int incr) {
server
.
dirty
++
;
updated
++
;
}
}
else
{
processed
++
;
}
else
if
(
!
xx
)
{
znode
=
zslInsert
(
zs
->
zsl
,
score
,
ele
);
incrRefCount
(
ele
);
/* Inserted in skiplist. */
redisAssertWithInfo
(
c
,
NULL
,
dictAdd
(
zs
->
dict
,
ele
,
&
znode
->
score
)
==
DICT_OK
);
incrRefCount
(
ele
);
/* Added to dictionary. */
server
.
dirty
++
;
added
++
;
processed
++
;
}
}
else
{
redisPanic
(
"Unknown sorted set encoding"
);
}
}
if
(
incr
)
/* ZINCRBY */
addReplyDouble
(
c
,
score
);
else
/* ZADD */
addReplyLongLong
(
c
,
added
);
reply_to_client:
if
(
incr
)
{
/* ZINCRBY or INCR option. */
if
(
processed
)
addReplyDouble
(
c
,
score
);
else
addReply
(
c
,
shared
.
nullbulk
);
}
else
{
/* ZADD. */
addReplyLongLong
(
c
,
ch
?
added
+
updated
:
added
);
}
cleanup:
zfree
(
scores
);
...
...
@@ -1307,11 +1389,11 @@ cleanup:
}
void
zaddCommand
(
redisClient
*
c
)
{
zaddGenericCommand
(
c
,
0
);
zaddGenericCommand
(
c
,
ZADD_NONE
);
}
void
zincrbyCommand
(
redisClient
*
c
)
{
zaddGenericCommand
(
c
,
1
);
zaddGenericCommand
(
c
,
ZADD_INCR
);
}
void
zremCommand
(
redisClient
*
c
)
{
...
...
@@ -2753,25 +2835,10 @@ void zscoreCommand(redisClient *c) {
if
((
zobj
=
lookupKeyReadOrReply
(
c
,
key
,
shared
.
nullbulk
))
==
NULL
||
checkType
(
c
,
zobj
,
REDIS_ZSET
))
return
;
if
(
zobj
->
encoding
==
REDIS_ENCODING_ZIPLIST
)
{
if
(
zzlFind
(
zobj
->
ptr
,
c
->
argv
[
2
],
&
score
)
!=
NULL
)
addReplyDouble
(
c
,
score
);
else
addReply
(
c
,
shared
.
nullbulk
);
}
else
if
(
zobj
->
encoding
==
REDIS_ENCODING_SKIPLIST
)
{
zset
*
zs
=
zobj
->
ptr
;
dictEntry
*
de
;
c
->
argv
[
2
]
=
tryObjectEncoding
(
c
->
argv
[
2
]);
de
=
dictFind
(
zs
->
dict
,
c
->
argv
[
2
]);
if
(
de
!=
NULL
)
{
score
=
*
(
double
*
)
dictGetVal
(
de
);
addReplyDouble
(
c
,
score
);
}
else
{
addReply
(
c
,
shared
.
nullbulk
);
}
if
(
zsetScore
(
zobj
,
c
->
argv
[
2
],
&
score
)
==
REDIS_ERR
)
{
addReply
(
c
,
shared
.
nullbulk
);
}
else
{
redisPanic
(
"Unknown sorted set encoding"
);
addReplyDouble
(
c
,
score
);
}
}
...
...
@@ -2819,7 +2886,7 @@ void zrankGenericCommand(redisClient *c, int reverse) {
dictEntry
*
de
;
double
score
;
ele
=
c
->
argv
[
2
]
=
tryObjectEncoding
(
c
->
argv
[
2
])
;
ele
=
c
->
argv
[
2
];
de
=
dictFind
(
zs
->
dict
,
ele
);
if
(
de
!=
NULL
)
{
score
=
*
(
double
*
)
dictGetVal
(
de
);
...
...
src/ziplist.h
View file @
9e67df2a
...
...
@@ -28,6 +28,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ZIPLIST_H
#define _ZIPLIST_H
#define ZIPLIST_HEAD 0
#define ZIPLIST_TAIL 1
...
...
@@ -49,3 +52,5 @@ size_t ziplistBlobLen(unsigned char *zl);
#ifdef REDIS_TEST
int
ziplistTest
(
int
argc
,
char
*
argv
[]);
#endif
#endif
/* _ZIPLIST_H */
tests/cluster/run.tcl
View file @
9e67df2a
...
...
@@ -17,6 +17,7 @@ proc main {} {
}
run_tests
cleanup
end_tests
}
if
{[
catch main e
]}
{
...
...
tests/cluster/tests/10-manual-failover.tcl
0 → 100644
View file @
9e67df2a
# Check the manual failover
source
"../tests/includes/init-tests.tcl"
test
"Create a 5 nodes cluster"
{
create_cluster 5 5
}
test
"Cluster is up"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 0
}
test
"Instance #5 is a slave"
{
assert
{[
RI 5 role
]
eq
{
slave
}}
}
test
"Instance #5 synced with the master"
{
wait_for_condition 1000 50
{
[
RI 5 master_link_status
]
eq
{
up
}
}
else
{
fail
"Instance #5 master link status is not up"
}
}
set current_epoch
[
CI 1 cluster_current_epoch
]
set numkeys 50000
set numops 10000
set cluster
[
redis_cluster 127.0.0.1:
[
get_instance_attrib redis 0 port
]]
catch
{
unset content
}
array set content
{}
test
"Send CLUSTER FAILOVER to #5, during load"
{
for
{
set j 0
}
{
$j
< $numops
}
{
incr j
}
{
# Write random data to random list.
set listid
[
randomInt $numkeys
]
set key
"key:
$listid
"
set ele
[
randomValue
]
# We write both with Lua scripts and with plain commands.
# This way we are able to stress Lua -> Redis command invocation
# as well, that has tests to prevent Lua to write into wrong
# hash slots.
if
{
$listid
% 2
}
{
$cluster rpush $key $ele
}
else
{
$cluster eval
{
redis.call
(
"rpush"
,KEYS
[
1
]
,ARGV
[
1
])}
1 $key $ele
}
lappend content
(
$key
)
$ele
if
{(
$j
% 1000
)
== 0
}
{
puts -nonewline W
;
flush stdout
}
if
{
$j
== $numops/2
}
{
R 5 cluster failover
}
}
}
test
"Wait for failover"
{
wait_for_condition 1000 50
{
[
CI 1 cluster_current_epoch
]
> $current_epoch
}
else
{
fail
"No failover detected"
}
}
test
"Cluster should eventually be up again"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 1
}
test
"Instance #5 is now a master"
{
assert
{[
RI 5 role
]
eq
{
master
}}
}
test
"Verify
$numkeys
keys for consistency with logical content"
{
# Check that the Redis Cluster content matches our logical content.
foreach
{
key value
}
[
array get content
]
{
assert
{[
$cluster
lrange $key 0 -1
]
eq $value
}
}
}
test
"Instance #0 gets converted into a slave"
{
wait_for_condition 1000 50
{
[
RI 0 role
]
eq
{
slave
}
}
else
{
fail
"Old master was not converted into slave"
}
}
## Check that manual failover does not happen if we can't talk with the master.
source
"../tests/includes/init-tests.tcl"
test
"Create a 5 nodes cluster"
{
create_cluster 5 5
}
test
"Cluster is up"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 0
}
test
"Instance #5 is a slave"
{
assert
{[
RI 5 role
]
eq
{
slave
}}
}
test
"Instance #5 synced with the master"
{
wait_for_condition 1000 50
{
[
RI 5 master_link_status
]
eq
{
up
}
}
else
{
fail
"Instance #5 master link status is not up"
}
}
test
"Make instance #0 unreachable without killing it"
{
R 0 deferred 1
R 0 DEBUG SLEEP 10
}
test
"Send CLUSTER FAILOVER to instance #5"
{
R 5 cluster failover
}
test
"Instance #5 is still a slave after some time (no failover)"
{
after 5000
assert
{[
RI 5 role
]
eq
{
master
}}
}
test
"Wait for instance #0 to return back alive"
{
R 0 deferred 0
assert
{[
R 0 read
]
eq
{
OK
}}
}
## Check with
"force"
failover happens anyway.
source
"../tests/includes/init-tests.tcl"
test
"Create a 5 nodes cluster"
{
create_cluster 5 5
}
test
"Cluster is up"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 0
}
test
"Instance #5 is a slave"
{
assert
{[
RI 5 role
]
eq
{
slave
}}
}
test
"Instance #5 synced with the master"
{
wait_for_condition 1000 50
{
[
RI 5 master_link_status
]
eq
{
up
}
}
else
{
fail
"Instance #5 master link status is not up"
}
}
test
"Make instance #0 unreachable without killing it"
{
R 0 deferred 1
R 0 DEBUG SLEEP 10
}
test
"Send CLUSTER FAILOVER to instance #5"
{
R 5 cluster failover force
}
test
"Instance #5 is a master after some time"
{
wait_for_condition 1000 50
{
[
RI 5 role
]
eq
{
master
}
}
else
{
fail
"Instance #5 is not a master after some time regardless of FORCE"
}
}
test
"Wait for instance #0 to return back alive"
{
R 0 deferred 0
assert
{[
R 0 read
]
eq
{
OK
}}
}
tests/cluster/tests/11-manual-takeover.tcl
0 → 100644
View file @
9e67df2a
# Manual takeover test
source
"../tests/includes/init-tests.tcl"
test
"Create a 5 nodes cluster"
{
create_cluster 5 5
}
test
"Cluster is up"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 0
}
test
"Killing majority of master nodes"
{
kill_instance redis 0
kill_instance redis 1
kill_instance redis 2
}
test
"Cluster should eventually be down"
{
assert_cluster_state fail
}
test
"Use takeover to bring slaves back"
{
R 5 cluster failover takeover
R 6 cluster failover takeover
R 7 cluster failover takeover
}
test
"Cluster should eventually be up again"
{
assert_cluster_state ok
}
test
"Cluster is writable"
{
cluster_write_test 4
}
test
"Instance #5, #6, #7 are now masters"
{
assert
{[
RI 5 role
]
eq
{
master
}}
assert
{[
RI 6 role
]
eq
{
master
}}
assert
{[
RI 7 role
]
eq
{
master
}}
}
test
"Restarting the previously killed master nodes"
{
restart_instance redis 0
restart_instance redis 1
restart_instance redis 2
}
test
"Instance #0, #1, #2 gets converted into a slaves"
{
wait_for_condition 1000 50
{
[
RI 0 role
]
eq
{
slave
}
&&
[
RI 1 role
]
eq
{
slave
}
&&
[
RI 2 role
]
eq
{
slave
}
}
else
{
fail
"Old masters not converted into slaves"
}
}
tests/instances.tcl
View file @
9e67df2a
...
...
@@ -19,6 +19,7 @@ set ::verbose 0
set ::valgrind 0
set ::pause_on_error 0
set ::simulate_error 0
set ::failed 0
set ::sentinel_instances
{}
set ::redis_instances
{}
set ::sentinel_base_port 20000
...
...
@@ -231,6 +232,7 @@ proc test {descr code} {
flush stdout
if
{[
catch
{
set retval
[
uplevel 1 $code
]}
error
]}
{
incr ::failed
if
{[
string match
"assertion:*"
$error
]}
{
set msg
[
string range $error 10 end
]
puts
[
colorstr red $msg
]
...
...
@@ -246,6 +248,7 @@ proc test {descr code} {
}
}
# Execute all the units inside the 'tests' directory.
proc run_tests
{}
{
set tests
[
lsort
[
glob ../tests/*
]]
foreach test $tests
{
...
...
@@ -258,6 +261,17 @@ proc run_tests {} {
}
}
# Print a message and exists with 0 / 1 according to zero or more failures.
proc end_tests
{}
{
if
{
$::failed
== 0
}
{
puts
"GOOD! No errors."
exit 0
}
else
{
puts
"WARNING
$::failed
tests faield."
exit 1
}
}
# The
"S"
command is used to interact with the N-th Sentinel.
# The general form is:
#
...
...
tests/integration/replication-4.tcl
View file @
9e67df2a
...
...
@@ -126,7 +126,7 @@ start_server {tags {"repl"}} {
test
{
Replication of SPOP command -- alsoPropagate
()
API
}
{
$master del myset
set size
[
randomInt 100
]
set size
[
expr 1+
[
randomInt 100
]
]
set content
{}
for
{
set j 0
}
{
$j
< $size
}
{
incr j
}
{
lappend content
[
randomValue
]
...
...
tests/integration/replication.tcl
View file @
9e67df2a
start_server
{
tags
{
"repl"
}}
{
set A
[
srv 0 client
]
set A_host
[
srv 0 host
]
set A_port
[
srv 0 port
]
start_server
{}
{
test
{
First server should have role slave after SLAVEOF
}
{
r -1 slaveof
[
srv 0 host
]
[
srv 0 port
]
set B
[
srv 0 client
]
set B_host
[
srv 0 host
]
set B_port
[
srv 0 port
]
test
{
Set instance A as slave of B
}
{
$A slaveof $B_host $B_port
wait_for_condition 50 100
{
[
s -1
role
]
eq
{
slave
}
&&
[
string match
{
*master_link_status:up*
}
[
r -1
info replication
]]
[
lindex
[
$A
role
]
0
]
eq
{
slave
}
&&
[
string match
{
*master_link_status:up*
}
[
$A
info replication
]]
}
else
{
fail
"Can't turn the instance into a slave"
}
...
...
@@ -15,9 +22,9 @@ start_server {tags {"repl"}} {
$rd brpoplpush a b 5
r lpush a foo
wait_for_condition 50 100
{
[
r
debug digest
]
eq
[
r -1
debug digest
]
[
$A
debug digest
]
eq
[
$B
debug digest
]
}
else
{
fail
"Master and slave have different digest:
[
r
debug digest
]
VS
[
r -1
debug digest
]
"
fail
"Master and slave have different digest:
[
$A
debug digest
]
VS
[
$B
debug digest
]
"
}
}
...
...
@@ -28,7 +35,36 @@ start_server {tags {"repl"}} {
r lpush c 3
$rd brpoplpush c d 5
after 1000
assert_equal
[
r debug digest
]
[
r -1 debug digest
]
assert_equal
[
$A debug digest
]
[
$B debug digest
]
}
test
{
BLPOP followed by role change, issue #2473
}
{
set rd
[
redis_deferring_client
]
$rd blpop foo 0
;
# Block while B is a master
# Turn B into master of A
$A slaveof no one
$B slaveof $A_host $A_port
wait_for_condition 50 100
{
[
lindex
[
$B role
]
0
]
eq
{
slave
}
&&
[
string match
{
*master_link_status:up*
}
[
$B info replication
]]
}
else
{
fail
"Can't turn the instance into a slave"
}
# Push elements into the
"foo"
list of the new slave.
# If the client is still attached to the instance, we'll get
# a desync between the two instances.
$A rpush foo a b c
after 100
wait_for_condition 50 100
{
[
$A debug digest
]
eq
[
$B debug digest
]
&&
[
$A lrange foo 0 -1
]
eq
{
a b c
}
&&
[
$B lrange foo 0 -1
]
eq
{
a b c
}
}
else
{
fail
"Master and slave have different digest:
[
$A debug digest
]
VS
[
$B debug digest
]
"
}
}
}
}
...
...
@@ -115,7 +151,7 @@ foreach mdl {no yes} {
start_server
{}
{
lappend slaves
[
srv 0 client
]
test
"Connect multiple slaves at the same time (issue #141), master diskless=
$mdl
, slave diskless=
$sdl
"
{
# Send S
A
LVEOF commands to slaves
# Send SL
A
VEOF commands to slaves
[
lindex $slaves 0
]
config set repl-diskless-sync $sdl
[
lindex $slaves 1
]
config set repl-diskless-sync $sdl
[
lindex $slaves 2
]
config set repl-diskless-sync $sdl
...
...
tests/sentinel/run.tcl
View file @
9e67df2a
...
...
@@ -13,6 +13,7 @@ proc main {} {
spawn_instance redis $::redis_base_port $::instances_count
run_tests
cleanup
end_tests
}
if
{[
catch main e
]}
{
...
...
tests/sentinel/tests/06-ckquorum.tcl
0 → 100644
View file @
9e67df2a
# Test for the SENTINEL CKQUORUM command
source
"../tests/includes/init-tests.tcl"
set num_sentinels
[
llength $::sentinel_instances
]
test
"CKQUORUM reports OK and the right amount of Sentinels"
{
foreach_sentinel_id id
{
assert_match
"*OK
$num
_sentinels usable*"
[
S $id SENTINEL CKQUORUM mymaster
]
}
}
test
"CKQUORUM detects quorum cannot be reached"
{
set orig_quorum
[
expr
{
$num
_sentinels/2+1
}]
S 0 SENTINEL SET mymaster quorum
[
expr
{
$num
_sentinels+1
}]
catch
{[
S 0 SENTINEL CKQUORUM mymaster
]}
err
assert_match
"*NOQUORUM*"
$err
S 0 SENTINEL SET mymaster quorum $orig_quorum
}
test
"CKQUORUM detects failover authorization cannot be reached"
{
set orig_quorum
[
expr
{
$num
_sentinels/2+1
}]
S 0 SENTINEL SET mymaster quorum 1
kill_instance sentinel 1
kill_instance sentinel 2
kill_instance sentinel 3
after 5000
catch
{[
S 0 SENTINEL CKQUORUM mymaster
]}
err
assert_match
"*NOQUORUM*"
$err
S 0 SENTINEL SET mymaster quorum $orig_quorum
restart_instance sentinel 1
restart_instance sentinel 2
restart_instance sentinel 3
}
tests/support/server.tcl
View file @
9e67df2a
...
...
@@ -54,10 +54,15 @@ proc kill_server config {
# kill server and wait for the process to be totally exited
catch
{
exec kill $pid
}
if
{
$::valgrind
}
{
set max_wait 60000
}
else
{
set max_wait 10000
}
while
{[
is_alive $config
]}
{
incr wait 10
if
{
$wait
>=
5000
}
{
if
{
$wait
>=
$max_wait
}
{
puts
"Forcing process
$pid
to exit..."
catch
{
exec kill -KILL $pid
}
}
elseif
{
$wait
% 1000 == 0
}
{
...
...
tests/unit/geo.tcl
0 → 100644
View file @
9e67df2a
# Helper functins to simulate search-in-radius in the Tcl side in order to
# verify the Redis implementation with a fuzzy test.
proc geo_degrad deg
{
expr
{
$deg
*atan
(
1
)
*8/360
}}
proc geo_distance
{
lon1d lat1d lon2d lat2d
}
{
set lon1r
[
geo_degrad $lon1d
]
set lat1r
[
geo_degrad $lat1d
]
set lon2r
[
geo_degrad $lon2d
]
set lat2r
[
geo_degrad $lat2d
]
set v
[
expr
{
sin
((
$lon2r
- $lon1r
)
/ 2
)}]
set u
[
expr
{
sin
((
$lat2r
- $lat1r
)
/ 2
)}]
expr
{
2.0 * 6372797.560856 *
\
asin
(
sqrt
(
$u
* $u + cos
(
$lat1r
)
* cos
(
$lat2r
)
* $v * $v
))}
}
proc geo_random_point
{
lonvar latvar
}
{
upvar 1 $lonvar lon
upvar 1 $latvar lat
# Note that the actual latitude limit should be -85 to +85, we restrict
# the test to -70 to +70 since in this range the algorithm is more precise
# while outside this range occasionally some element may be missing.
set lon
[
expr
{
-180 + rand
()
*360
}]
set lat
[
expr
{
-70 + rand
()
*140
}]
}
start_server
{
tags
{
"geo"
}}
{
test
{
GEOADD create
}
{
r geoadd nyc -73.9454966 40.747533
"lic market"
}
{
1
}
test
{
GEOADD update
}
{
r geoadd nyc -73.9454966 40.747533
"lic market"
}
{
0
}
test
{
GEOADD invalid coordinates
}
{
catch
{
r geoadd nyc -73.9454966 40.747533
"lic market"
\
foo bar
"luck market"
}
err
set err
}
{
*valid*
}
test
{
GEOADD multi add
}
{
r geoadd nyc -73.9733487 40.7648057
"central park n/q/r"
-73.9903085 40.7362513
"union square"
-74.0131604 40.7126674
"wtc one"
-73.7858139 40.6428986
"jfk"
-73.9375699 40.7498929
"q4"
-73.9564142 40.7480973 4545
}
{
6
}
test
{
Check geoset values
}
{
r zrange nyc 0 -1 withscores
}
{{
wtc one
}
1791873972053020
{
union square
}
1791875485187452
{
central park n/q/r
}
1791875761332224 4545 1791875796750882
{
lic market
}
1791875804419201 q4 1791875830079666 jfk 1791895905559723
}
test
{
GEORADIUS simple
(
sorted
)}
{
r georadius nyc -73.9798091 40.7598464 3 km asc
}
{{
central park n/q/r
}
4545
{
union square
}}
test
{
GEORADIUS withdist
(
sorted
)}
{
r georadius nyc -73.9798091 40.7598464 3 km withdist asc
}
{{{
central park n/q/r
}
0.7750
}
{
4545 2.3651
}
{{
union square
}
2.7697
}}
test
{
GEORADIUS with COUNT
}
{
r georadius nyc -73.9798091 40.7598464 10 km COUNT 3
}
{{
central park n/q/r
}
4545
{
union square
}}
test
{
GEORADIUS with COUNT DESC
}
{
r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
}
{{
wtc one
}
q4
}
test
{
GEORADIUSBYMEMBER simple
(
sorted
)}
{
r georadiusbymember nyc
"wtc one"
7 km
}
{{
wtc one
}
{
union square
}
{
central park n/q/r
}
4545
{
lic market
}}
test
{
GEORADIUSBYMEMBER withdist
(
sorted
)}
{
r georadiusbymember nyc
"wtc one"
7 km withdist
}
{{{
wtc one
}
0.0000
}
{{
union square
}
3.2544
}
{{
central park n/q/r
}
6.7000
}
{
4545 6.1975
}
{{
lic market
}
6.8969
}}
test
{
GEOHASH is able to return geohash strings
}
{
# Example from Wikipedia.
r del points
r geoadd points -5.6 42.6 test
lindex
[
r geohash points test
]
0
}
{
ezs42e44yx0
}
test
{
GEOPOS simple
}
{
r del points
r geoadd points 10 20 a 30 40 b
lassign
[
lindex
[
r geopos points a b
]
0
]
x1 y1
lassign
[
lindex
[
r geopos points a b
]
1
]
x2 y2
assert
{
abs
(
$x1
- 10
)
< 0.001
}
assert
{
abs
(
$y1
- 20
)
< 0.001
}
assert
{
abs
(
$x2
- 30
)
< 0.001
}
assert
{
abs
(
$y2
- 40
)
< 0.001
}
}
test
{
GEOPOS missing element
}
{
r del points
r geoadd points 10 20 a 30 40 b
lindex
[
r geopos points a x b
]
1
}
{}
test
{
GEODIST simple & unit
}
{
r del points
r geoadd points 13.361389 38.115556
"Palermo"
\
15.087269 37.502669
"Catania"
set m
[
r geodist points Palermo Catania
]
assert
{
$m
> 166274 && $m < 166275
}
set km
[
r geodist points Palermo Catania km
]
assert
{
$km
> 166.2 && $km < 166.3
}
}
test
{
GEODIST missing elements
}
{
r del points
r geoadd points 13.361389 38.115556
"Palermo"
\
15.087269 37.502669
"Catania"
set m
[
r geodist points Palermo Agrigento
]
assert
{
$m
eq
{}}
set m
[
r geodist points Ragusa Agrigento
]
assert
{
$m
eq
{}}
set m
[
r geodist empty_key Palermo Catania
]
assert
{
$m
eq
{}}
}
test
{
GEOADD + GEORANGE randomized test
}
{
set attempt 10
while
{[
incr attempt -1
]}
{
unset -nocomplain debuginfo
set srand_seed
[
randomInt 1000000
]
lappend debuginfo
"srand_seed is
$srand
_seed"
expr
{
srand
(
$srand
_seed
)}
;
# If you need a reproducible run
r del mypoints
set radius_km
[
expr
{[
randomInt 200
]
+10
}]
set radius_m
[
expr
{
$radius
_km*1000
}]
geo_random_point search_lon search_lat
lappend debuginfo
"Search area:
$search
_lon,
$search
_lat
$radius
_km km"
set tcl_result
{}
set argv
{}
for
{
set j 0
}
{
$j
< 20000
}
{
incr j
}
{
geo_random_point lon lat
lappend argv $lon $lat
"place:
$j
"
if
{[
geo_distance $lon $lat $search_lon $search_lat
]
< $radius_m
}
{
lappend tcl_result
"place:
$j
"
lappend debuginfo
"place:
$j
$lon
$lat
[
expr
{[
geo_distance $lon $lat $search_lon $search_lat
]
/1000
}]
km"
}
}
r geoadd mypoints
{*}
$argv
set res
[
lsort
[
r georadius mypoints $search_lon $search_lat $radius_km km
]]
set res2
[
lsort $tcl_result
]
set test_result OK
if
{
$res
!= $res2
}
{
puts
"Redis:
$res
"
puts
"Tcl :
$res2
"
puts
[
join $debuginfo
"
\n
"
]
set test_result FAIL
}
unset -nocomplain debuginfo
if
{
$test
_result ne
{
OK
}}
break
}
set test_result
}
{
OK
}
}
tests/unit/type/set.tcl
View file @
9e67df2a
...
...
@@ -519,6 +519,7 @@ start_server {
test
"SMOVE non existing key"
{
setup_move
assert_equal 0
[
r smove myset1 myset2 foo
]
assert_equal 0
[
r smove myset1 myset1 foo
]
assert_equal
{
1 a b
}
[
lsort
[
r smembers myset1
]]
assert_equal
{
2 3 4
}
[
lsort
[
r smembers myset2
]]
}
...
...
tests/unit/type/zset.tcl
View file @
9e67df2a
...
...
@@ -43,6 +43,84 @@ start_server {tags {"zset"}} {
assert_error
"*not*float*"
{
r zadd myzset nan abc
}
}
test
"ZADD with options syntax error with incomplete pair"
{
r del ztmp
catch
{
r zadd ztmp xx 10 x 20
}
err
set err
}
{
ERR*
}
test
"ZADD XX option without key -
$encoding
"
{
r del ztmp
assert
{[
r zadd ztmp xx 10 x
]
== 0
}
assert
{[
r type ztmp
]
eq
{
none
}}
}
test
"ZADD XX existing key -
$encoding
"
{
r del ztmp
r zadd ztmp 10 x
assert
{[
r zadd ztmp xx 20 y
]
== 0
}
assert
{[
r zcard ztmp
]
== 1
}
}
test
"ZADD XX returns the number of elements actually added"
{
r del ztmp
r zadd ztmp 10 x
set retval
[
r zadd ztmp 10 x 20 y 30 z
]
assert
{
$retval
== 2
}
}
test
"ZADD XX updates existing elements score"
{
r del ztmp
r zadd ztmp 10 x 20 y 30 z
r zadd ztmp xx 5 foo 11 x 21 y 40 zap
assert
{[
r zcard ztmp
]
== 3
}
assert
{[
r zscore ztmp x
]
== 11
}
assert
{[
r zscore ztmp y
]
== 21
}
}
test
"ZADD XX and NX are not compatible"
{
r del ztmp
catch
{
r zadd ztmp xx nx 10 x
}
err
set err
}
{
ERR*
}
test
"ZADD NX with non exisitng key"
{
r del ztmp
r zadd ztmp nx 10 x 20 y 30 z
assert
{[
r zcard ztmp
]
== 3
}
}
test
"ZADD NX only add new elements without updating old ones"
{
r del ztmp
r zadd ztmp 10 x 20 y 30 z
assert
{[
r zadd ztmp nx 11 x 21 y 100 a 200 b
]
== 2
}
assert
{[
r zscore ztmp x
]
== 10
}
assert
{[
r zscore ztmp y
]
== 20
}
assert
{[
r zscore ztmp a
]
== 100
}
assert
{[
r zscore ztmp b
]
== 200
}
}
test
"ZADD INCR works like ZINCRBY"
{
r del ztmp
r zadd ztmp 10 x 20 y 30 z
r zadd ztmp INCR 15 x
assert
{[
r zscore ztmp x
]
== 25
}
}
test
"ZADD INCR works with a single score-elemenet pair"
{
r del ztmp
r zadd ztmp 10 x 20 y 30 z
catch
{
r zadd ztmp INCR 15 x 10 y
}
err
set err
}
{
ERR*
}
test
"ZADD CH option changes return value to all changed elements"
{
r del ztmp
r zadd ztmp 10 x 20 y 30 z
assert
{[
r zadd ztmp 11 x 21 y 30 z
]
== 0
}
assert
{[
r zadd ztmp ch 12 x 22 y 30 z
]
== 2
}
}
test
"ZINCRBY calls leading to NaN result in error"
{
r zincrby myzset +inf abc
assert_error
"*NaN*"
{
r zincrby myzset -inf abc
}
...
...
@@ -77,6 +155,8 @@ start_server {tags {"zset"}} {
}
test
"ZCARD basics -
$encoding
"
{
r del ztmp
r zadd ztmp 10 a 20 b 30 c
assert_equal 3
[
r zcard ztmp
]
assert_equal 0
[
r zcard zdoesntexist
]
}
...
...
utils/create-cluster/.gitignore
View file @
9e67df2a
config.sh
*.rdb
*.aof
*.conf
*.log
utils/create-cluster/create-cluster
View file @
9e67df2a
...
...
@@ -43,7 +43,7 @@ then
while
[
$((
PORT < ENDPORT
))
!=
"0"
]
;
do
PORT
=
$((
PORT+1
))
echo
"Stopping
$PORT
"
redis-cli
-p
$PORT
shutdown nosave
../../src/
redis-cli
-p
$PORT
shutdown nosave
done
exit
0
fi
...
...
@@ -54,7 +54,7 @@ then
while
[
1
]
;
do
clear
date
redis-cli
-p
$PORT
cluster nodes |
head
-30
../../src/
redis-cli
-p
$PORT
cluster nodes |
head
-30
sleep
1
done
exit
0
...
...
utils/releasetools/0
0
_test_release.sh
→
utils/releasetools/0
3
_test_release.sh
View file @
9e67df2a
File moved
utils/releasetools/0
3
_release_hash.sh
→
utils/releasetools/0
4
_release_hash.sh
View file @
9e67df2a
File moved
Prev
1
2
3
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