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
4e78d5cd
Commit
4e78d5cd
authored
Feb 11, 2019
by
artix
Browse files
Redis Benchmark: update slots configuration after MOVED/ASK reply
parent
daaff484
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/Makefile
View file @
4e78d5cd
...
...
@@ -168,7 +168,7 @@ REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.
REDIS_CLI_NAME
=
redis-cli
REDIS_CLI_OBJ
=
anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o
REDIS_BENCHMARK_NAME
=
redis-benchmark
REDIS_BENCHMARK_OBJ
=
ae.o anet.o redis-benchmark.o adlist.o
zmalloc
.o redis-benchmark.o
REDIS_BENCHMARK_OBJ
=
ae.o anet.o redis-benchmark.o adlist.o
dict.o zmalloc.o siphash
.o redis-benchmark.o
REDIS_CHECK_RDB_NAME
=
redis-check-rdb
REDIS_CHECK_AOF_NAME
=
redis-check-aof
...
...
src/redis-benchmark.c
View file @
4e78d5cd
...
...
@@ -46,6 +46,7 @@
#include "ae.h"
#include "hiredis.h"
#include "adlist.h"
#include "dict.h"
#include "zmalloc.h"
#include "atomicvar.h"
#include "crc16_slottable.h"
...
...
@@ -100,10 +101,17 @@ static struct config {
int
cluster_node_count
;
struct
clusterNode
**
cluster_nodes
;
struct
redisConfig
*
redis_config
;
int
is_fetching_slots
;
int
is_updating_slots
;
int
slots_last_update
;
/* Thread mutexes to be used as fallbacks by atomicvar.h */
pthread_mutex_t
requests_issued_mutex
;
pthread_mutex_t
requests_finished_mutex
;
pthread_mutex_t
liveclients_mutex
;
pthread_mutex_t
is_fetching_slots_mutex
;
pthread_mutex_t
is_updating_slots_mutex
;
pthread_mutex_t
updating_slots_mutex
;
pthread_mutex_t
slots_last_update_mutex
;
}
config
;
typedef
struct
_client
{
...
...
@@ -125,6 +133,7 @@ typedef struct _client {
int
prefixlen
;
/* Size in bytes of the pending prefix commands */
int
thread_id
;
struct
clusterNode
*
cluster_node
;
int
slots_last_update
;
}
*
client
;
/* Threads. */
...
...
@@ -143,8 +152,10 @@ typedef struct clusterNode {
int
flags
;
sds
replicate
;
/* Master ID if node is a slave */
int
*
slots
;
int
current_slot_index
;
int
slots_count
;
int
current_slot_index
;
int
*
updated_slots
;
/* Used by updateClusterSlotsConfiguration */
int
updated_slots_count
;
/* Used by updateClusterSlotsConfiguration */
int
replicas_count
;
sds
*
migrating
;
/* An array of sds where even strings are slots and odd
* strings are the destination node IDs. */
...
...
@@ -171,9 +182,16 @@ static clusterNode *createClusterNode(char *ip, int port);
static
redisConfig
*
getRedisConfig
(
const
char
*
ip
,
int
port
,
const
char
*
hostsocket
);
static
void
freeRedisConfig
(
redisConfig
*
cfg
);
static
int
fetchClusterSlotsConfiguration
(
client
c
);
static
void
updateClusterSlotsConfiguration
();
int
showThroughput
(
struct
aeEventLoop
*
eventLoop
,
long
long
id
,
void
*
clientData
);
/* Dict callbacks */
static
uint64_t
dictSdsHash
(
const
void
*
key
);
static
int
dictSdsKeyCompare
(
void
*
privdata
,
const
void
*
key1
,
const
void
*
key2
);
/* Implementation */
static
long
long
ustime
(
void
)
{
struct
timeval
tv
;
...
...
@@ -195,6 +213,29 @@ static long long mstime(void) {
return
mst
;
}
static
uint64_t
dictSdsHash
(
const
void
*
key
)
{
return
dictGenHashFunction
((
unsigned
char
*
)
key
,
sdslen
((
char
*
)
key
));
}
static
int
dictSdsKeyCompare
(
void
*
privdata
,
const
void
*
key1
,
const
void
*
key2
)
{
int
l1
,
l2
;
DICT_NOTUSED
(
privdata
);
l1
=
sdslen
((
sds
)
key1
);
l2
=
sdslen
((
sds
)
key2
);
if
(
l1
!=
l2
)
return
0
;
return
memcmp
(
key1
,
key2
,
l1
)
==
0
;
}
/* _serverAssert is needed by dict */
void
_serverAssert
(
const
char
*
estr
,
const
char
*
file
,
int
line
)
{
fprintf
(
stderr
,
"=== ASSERTION FAILED ==="
);
fprintf
(
stderr
,
"==> %s:%d '%s' is not true"
,
file
,
line
,
estr
);
*
((
char
*
)
-
1
)
=
'x'
;
}
static
redisConfig
*
getRedisConfig
(
const
char
*
ip
,
int
port
,
const
char
*
hostsocket
)
{
...
...
@@ -320,6 +361,15 @@ static void setClusterKeyHashTag(client c) {
clusterNode
*
node
=
c
->
cluster_node
;
assert
(
node
);
assert
(
node
->
current_slot_index
<
node
->
slots_count
);
int
is_updating_slots
=
0
;
atomicGet
(
config
.
is_updating_slots
,
is_updating_slots
);
/* If updateClusterSlotsConfiguration is updating the slots array,
* call updateClusterSlotsConfiguration is order to block the thread
* since the mutex is locked. When the slots will be updated by the
* thread that's actually performing the update, the execution of
* updateClusterSlotsConfiguration won't actually do anything, since
* the updated_slots_count array will be already NULL. */
if
(
is_updating_slots
)
updateClusterSlotsConfiguration
();
int
slot
=
node
->
slots
[
node
->
current_slot_index
];
const
char
*
tag
=
crc16_slot_table
[
slot
];
int
taglen
=
strlen
(
tag
);
...
...
@@ -401,6 +451,11 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
(
!
strncmp
(
r
->
str
,
"MOVED"
,
5
)
||
!
strcmp
(
r
->
str
,
"ASK"
)))
{
/* Try to update slots configuration if the key of the
* command is using the slot hash tag. */
if
(
c
->
staglen
&&
!
fetchClusterSlotsConfiguration
(
c
))
exit
(
1
);
/*
clusterNode *node = c->cluster_node;
assert(node);
if (++node->current_slot_index >= node->slots_count) {
...
...
@@ -412,6 +467,7 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
freeClient(c);
return;
}
*/
}
freeReplyObject
(
reply
);
...
...
@@ -466,6 +522,7 @@ static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Really initialize: randomize keys and set start time. */
if
(
config
.
randomkeys
)
randomizeClientKey
(
c
);
if
(
config
.
cluster_mode
&&
c
->
staglen
>
0
)
setClusterKeyHashTag
(
c
);
atomicGet
(
config
.
slots_last_update
,
c
->
slots_last_update
);
c
->
start
=
ustime
();
c
->
latency
=
-
1
;
}
...
...
@@ -659,6 +716,7 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
aeCreateFileEvent
(
el
,
c
->
context
->
fd
,
AE_WRITABLE
,
writeHandler
,
c
);
listAddNodeTail
(
config
.
clients
,
c
);
atomicIncr
(
config
.
liveclients
,
1
);
atomicGet
(
config
.
slots_last_update
,
c
->
slots_last_update
);
return
c
;
}
...
...
@@ -846,6 +904,8 @@ static clusterNode *createClusterNode(char *ip, int port) {
node
->
slots
=
zmalloc
(
CLUSTER_SLOTS
*
sizeof
(
int
));
node
->
slots_count
=
0
;
node
->
current_slot_index
=
0
;
node
->
updated_slots
=
NULL
;
node
->
updated_slots_count
=
0
;
node
->
migrating
=
NULL
;
node
->
importing
=
NULL
;
node
->
migrating_count
=
0
;
...
...
@@ -1067,6 +1127,121 @@ cleanup:
return
success
;
}
/* Request the current cluster slots configuration by calling CLUSTER SLOTS
* and atomically update the slots after a successful reply. */
static
int
fetchClusterSlotsConfiguration
(
client
c
)
{
UNUSED
(
c
);
int
success
=
1
,
is_fetching_slots
=
0
,
last_update
=
0
;
size_t
i
;
atomicGet
(
config
.
slots_last_update
,
last_update
);
if
(
c
->
slots_last_update
<
last_update
)
{
c
->
slots_last_update
=
last_update
;
return
-
1
;
}
redisReply
*
reply
=
NULL
;
atomicGetIncr
(
config
.
is_fetching_slots
,
is_fetching_slots
,
1
);
if
(
is_fetching_slots
)
return
-
1
;
//TODO: use other codes || errno ?
atomicSet
(
config
.
is_fetching_slots
,
1
);
if
(
config
.
showerrors
)
printf
(
"Cluster slots configuration changed, fetching new one...
\n
"
);
const
char
*
errmsg
=
"Failed to update cluster slots configuration"
;
static
dictType
dtype
=
{
dictSdsHash
,
/* hash function */
NULL
,
/* key dup */
NULL
,
/* val dup */
dictSdsKeyCompare
,
/* key compare */
NULL
,
/* key destructor */
NULL
/* val destructor */
};
/* printf("[%d] fetchClusterSlotsConfiguration\n", c->thread_id); */
dict
*
masters
=
dictCreate
(
&
dtype
,
NULL
);
redisContext
*
ctx
=
NULL
;
for
(
i
=
0
;
i
<
(
size_t
)
config
.
cluster_node_count
;
i
++
)
{
clusterNode
*
node
=
config
.
cluster_nodes
[
i
];
assert
(
node
->
ip
!=
NULL
);
assert
(
node
->
name
!=
NULL
);
assert
(
node
->
port
);
/* Use first node as entry point to connect to. */
if
(
ctx
==
NULL
)
{
ctx
=
redisConnect
(
node
->
ip
,
node
->
port
);
if
(
!
ctx
||
ctx
->
err
)
{
success
=
0
;
if
(
ctx
&&
ctx
->
err
)
fprintf
(
stderr
,
"REDIS CONNECTION ERROR: %s
\n
"
,
ctx
->
errstr
);
goto
cleanup
;
}
}
if
(
node
->
updated_slots
!=
NULL
)
zfree
(
node
->
updated_slots
);
node
->
updated_slots
=
NULL
;
node
->
updated_slots_count
=
0
;
dictReplace
(
masters
,
node
->
name
,
node
)
;
}
reply
=
redisCommand
(
ctx
,
"CLUSTER SLOTS"
);
if
(
reply
==
NULL
||
reply
->
type
==
REDIS_REPLY_ERROR
)
{
success
=
0
;
if
(
reply
)
fprintf
(
stderr
,
"%s
\n
CLUSTER SLOTS ERROR: %s
\n
"
,
errmsg
,
reply
->
str
);
goto
cleanup
;
}
assert
(
reply
->
type
==
REDIS_REPLY_ARRAY
);
for
(
i
=
0
;
i
<
reply
->
elements
;
i
++
)
{
redisReply
*
r
=
reply
->
element
[
i
];
assert
(
r
->
type
=
REDIS_REPLY_ARRAY
);
assert
(
r
->
elements
>=
3
);
int
from
,
to
,
slot
;
from
=
r
->
element
[
0
]
->
integer
;
to
=
r
->
element
[
1
]
->
integer
;
redisReply
*
nr
=
r
->
element
[
2
];
assert
(
nr
->
type
==
REDIS_REPLY_ARRAY
&&
nr
->
elements
>=
3
);
assert
(
nr
->
element
[
2
]
->
str
!=
NULL
);
sds
name
=
sdsnew
(
nr
->
element
[
2
]
->
str
);
dictEntry
*
entry
=
dictFind
(
masters
,
name
);
if
(
entry
==
NULL
)
{
success
=
0
;
fprintf
(
stderr
,
"%s: could not find node with ID %s in current "
"configuration.
\n
"
,
errmsg
,
name
);
if
(
name
)
sdsfree
(
name
);
goto
cleanup
;
}
sdsfree
(
name
);
clusterNode
*
node
=
dictGetVal
(
entry
);
if
(
node
->
updated_slots
==
NULL
)
node
->
updated_slots
=
zcalloc
(
CLUSTER_SLOTS
*
sizeof
(
int
));
for
(
slot
=
from
;
slot
<=
to
;
slot
++
)
node
->
updated_slots
[
node
->
updated_slots_count
++
]
=
slot
;
}
updateClusterSlotsConfiguration
();
cleanup:
freeReplyObject
(
reply
);
redisFree
(
ctx
);
dictRelease
(
masters
);
atomicSet
(
config
.
is_fetching_slots
,
0
);
return
success
;
}
/* Atomically update the new slots configuration. */
static
void
updateClusterSlotsConfiguration
()
{
pthread_mutex_lock
(
&
config
.
is_updating_slots_mutex
);
atomicSet
(
config
.
is_updating_slots
,
1
);
int
i
;
for
(
i
=
0
;
i
<
config
.
cluster_node_count
;
i
++
)
{
clusterNode
*
node
=
config
.
cluster_nodes
[
i
];
if
(
node
->
updated_slots
!=
NULL
)
{
int
*
oldslots
=
node
->
slots
;
node
->
slots
=
node
->
updated_slots
;
node
->
slots_count
=
node
->
updated_slots_count
;
node
->
current_slot_index
=
0
;
node
->
updated_slots
=
NULL
;
node
->
updated_slots_count
=
0
;
zfree
(
oldslots
);
}
}
atomicSet
(
config
.
is_updating_slots
,
0
);
atomicIncr
(
config
.
slots_last_update
,
1
);
pthread_mutex_unlock
(
&
config
.
is_updating_slots_mutex
);
}
/* Returns number of consumed options. */
int
parseOptions
(
int
argc
,
const
char
**
argv
)
{
int
i
;
...
...
@@ -1301,6 +1476,9 @@ int main(int argc, const char **argv) {
config
.
cluster_node_count
=
0
;
config
.
cluster_nodes
=
NULL
;
config
.
redis_config
=
NULL
;
config
.
is_fetching_slots
=
0
;
config
.
is_updating_slots
=
0
;
config
.
slots_last_update
=
0
;
i
=
parseOptions
(
argc
,
argv
);
argc
-=
i
;
...
...
@@ -1354,6 +1532,10 @@ int main(int argc, const char **argv) {
pthread_mutex_init
(
&
(
config
.
requests_issued_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
requests_finished_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
liveclients_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
is_fetching_slots_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
is_updating_slots_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
updating_slots_mutex
),
NULL
);
pthread_mutex_init
(
&
(
config
.
slots_last_update_mutex
),
NULL
);
}
if
(
config
.
keepalive
==
0
)
{
...
...
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