mirror of https://mirror.osredm.com/root/redis.git
Reduce getNodeByQuery overhead (#13221)
The following PR does the following changes based upon on CPU profile info. The `getNodeByQuery` function represents 8.2% of an overhead of 12.3% when comparing single shard cluster with standalone. Proposed changes: - inlinging keyHashSlot to reduce overhead of that function call - Reduce duplicate calls to getCommandFlags within getNodeByQuery The above changes represent an improvement of approximately 5% on the achievable ops/sec. Co-authored-by: filipecosta90 <filipecosta.90@gmail.com>
This commit is contained in:
parent
a84cc20aef
commit
26a2dcb936
|
@ -23,33 +23,6 @@
|
||||||
* Key space handling
|
* Key space handling
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* We have 16384 hash slots. The hash slot of a given key is obtained
|
|
||||||
* as the least significant 14 bits of the crc16 of the key.
|
|
||||||
*
|
|
||||||
* However, if the key contains the {...} pattern, only the part between
|
|
||||||
* { and } is hashed. This may be useful in the future to force certain
|
|
||||||
* keys to be in the same node (assuming no resharding is in progress). */
|
|
||||||
unsigned int keyHashSlot(char *key, int keylen) {
|
|
||||||
int s, e; /* start-end indexes of { and } */
|
|
||||||
|
|
||||||
for (s = 0; s < keylen; s++)
|
|
||||||
if (key[s] == '{') break;
|
|
||||||
|
|
||||||
/* No '{' ? Hash the whole key. This is the base case. */
|
|
||||||
if (s == keylen) return crc16(key,keylen) & 0x3FFF;
|
|
||||||
|
|
||||||
/* '{' found? Check if we have the corresponding '}'. */
|
|
||||||
for (e = s+1; e < keylen; e++)
|
|
||||||
if (key[e] == '}') break;
|
|
||||||
|
|
||||||
/* No '}' or nothing between {} ? Hash the whole key. */
|
|
||||||
if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
|
|
||||||
|
|
||||||
/* If we are here there is both a { and a } on its right. Hash
|
|
||||||
* what is in the middle between { and }. */
|
|
||||||
return crc16(key+s+1,e-s-1) & 0x3FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it can be inferred that the given glob-style pattern, as implemented in
|
/* If it can be inferred that the given glob-style pattern, as implemented in
|
||||||
* stringmatchlen() in util.c, only can match keys belonging to a single slot,
|
* stringmatchlen() in util.c, only can match keys belonging to a single slot,
|
||||||
* that slot is returned. Otherwise -1 is returned. */
|
* that slot is returned. Otherwise -1 is returned. */
|
||||||
|
@ -980,7 +953,7 @@ void clusterCommand(client *c) {
|
||||||
*
|
*
|
||||||
* CLUSTER_REDIR_DOWN_STATE and CLUSTER_REDIR_DOWN_RO_STATE if the cluster is
|
* CLUSTER_REDIR_DOWN_STATE and CLUSTER_REDIR_DOWN_RO_STATE if the cluster is
|
||||||
* down but the user attempts to execute a command that addresses one or more keys. */
|
* down but the user attempts to execute a command that addresses one or more keys. */
|
||||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *error_code) {
|
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, uint64_t cmd_flags, int *error_code) {
|
||||||
clusterNode *myself = getMyClusterNode();
|
clusterNode *myself = getMyClusterNode();
|
||||||
clusterNode *n = NULL;
|
clusterNode *n = NULL;
|
||||||
robj *firstkey = NULL;
|
robj *firstkey = NULL;
|
||||||
|
@ -1116,7 +1089,6 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||||
* without redirections or errors in all the cases. */
|
* without redirections or errors in all the cases. */
|
||||||
if (n == NULL) return myself;
|
if (n == NULL) return myself;
|
||||||
|
|
||||||
uint64_t cmd_flags = getCommandFlags(c);
|
|
||||||
/* Cluster is globally down but we got keys? We only serve the request
|
/* Cluster is globally down but we got keys? We only serve the request
|
||||||
* if it is a read command and when allow_reads_when_down is enabled. */
|
* if it is a read command and when allow_reads_when_down is enabled. */
|
||||||
if (!isClusterHealthy()) {
|
if (!isClusterHealthy()) {
|
||||||
|
|
|
@ -33,6 +33,34 @@ struct clusterState;
|
||||||
#define CLUSTER_MODULE_FLAG_NO_REDIRECTION (1<<2)
|
#define CLUSTER_MODULE_FLAG_NO_REDIRECTION (1<<2)
|
||||||
|
|
||||||
/* ---------------------- API exported outside cluster.c -------------------- */
|
/* ---------------------- API exported outside cluster.c -------------------- */
|
||||||
|
|
||||||
|
/* We have 16384 hash slots. The hash slot of a given key is obtained
|
||||||
|
* as the least significant 14 bits of the crc16 of the key.
|
||||||
|
*
|
||||||
|
* However, if the key contains the {...} pattern, only the part between
|
||||||
|
* { and } is hashed. This may be useful in the future to force certain
|
||||||
|
* keys to be in the same node (assuming no resharding is in progress). */
|
||||||
|
static inline unsigned int keyHashSlot(char *key, int keylen) {
|
||||||
|
int s, e; /* start-end indexes of { and } */
|
||||||
|
|
||||||
|
for (s = 0; s < keylen; s++)
|
||||||
|
if (key[s] == '{') break;
|
||||||
|
|
||||||
|
/* No '{' ? Hash the whole key. This is the base case. */
|
||||||
|
if (likely(s == keylen)) return crc16(key,keylen) & 0x3FFF;
|
||||||
|
|
||||||
|
/* '{' found? Check if we have the corresponding '}'. */
|
||||||
|
for (e = s+1; e < keylen; e++)
|
||||||
|
if (key[e] == '}') break;
|
||||||
|
|
||||||
|
/* No '}' or nothing between {} ? Hash the whole key. */
|
||||||
|
if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
|
||||||
|
|
||||||
|
/* If we are here there is both a { and a } on its right. Hash
|
||||||
|
* what is in the middle between { and }. */
|
||||||
|
return crc16(key+s+1,e-s-1) & 0x3FFF;
|
||||||
|
}
|
||||||
|
|
||||||
/* functions requiring mechanism specific implementations */
|
/* functions requiring mechanism specific implementations */
|
||||||
void clusterInit(void);
|
void clusterInit(void);
|
||||||
void clusterInitLast(void);
|
void clusterInitLast(void);
|
||||||
|
@ -105,11 +133,10 @@ long long clusterNodeReplOffset(clusterNode *node);
|
||||||
clusterNode *clusterLookupNode(const char *name, int length);
|
clusterNode *clusterLookupNode(const char *name, int length);
|
||||||
|
|
||||||
/* functions with shared implementations */
|
/* functions with shared implementations */
|
||||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
|
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, uint64_t cmd_flags, int *error_code);
|
||||||
int clusterRedirectBlockedClientIfNeeded(client *c);
|
int clusterRedirectBlockedClientIfNeeded(client *c);
|
||||||
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
|
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
|
||||||
void migrateCloseTimedoutSockets(void);
|
void migrateCloseTimedoutSockets(void);
|
||||||
unsigned int keyHashSlot(char *key, int keylen);
|
|
||||||
int patternHashSlot(char *pattern, int length);
|
int patternHashSlot(char *pattern, int length);
|
||||||
int isValidAuxString(char *s, unsigned int length);
|
int isValidAuxString(char *s, unsigned int length);
|
||||||
void migrateCommand(client *c);
|
void migrateCommand(client *c);
|
||||||
|
|
|
@ -6515,7 +6515,8 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
|
||||||
/* Duplicate relevant flags in the module client. */
|
/* Duplicate relevant flags in the module client. */
|
||||||
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
|
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
|
||||||
c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);
|
c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);
|
||||||
if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,&error_code) !=
|
const uint64_t cmd_flags = getCommandFlags(c);
|
||||||
|
if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,cmd_flags,&error_code) !=
|
||||||
getMyClusterNode())
|
getMyClusterNode())
|
||||||
{
|
{
|
||||||
sds msg = NULL;
|
sds msg = NULL;
|
||||||
|
|
|
@ -470,8 +470,9 @@ static int scriptVerifyClusterState(scriptRunCtx *run_ctx, client *c, client *or
|
||||||
/* Duplicate relevant flags in the script client. */
|
/* Duplicate relevant flags in the script client. */
|
||||||
c->flags &= ~(CLIENT_READONLY | CLIENT_ASKING);
|
c->flags &= ~(CLIENT_READONLY | CLIENT_ASKING);
|
||||||
c->flags |= original_c->flags & (CLIENT_READONLY | CLIENT_ASKING);
|
c->flags |= original_c->flags & (CLIENT_READONLY | CLIENT_ASKING);
|
||||||
|
const uint64_t cmd_flags = getCommandFlags(c);
|
||||||
int hashslot = -1;
|
int hashslot = -1;
|
||||||
if (getNodeByQuery(c, c->cmd, c->argv, c->argc, &hashslot, &error_code) != getMyClusterNode()) {
|
if (getNodeByQuery(c, c->cmd, c->argv, c->argc, &hashslot, cmd_flags, &error_code) != getMyClusterNode()) {
|
||||||
if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) {
|
if (error_code == CLUSTER_REDIR_DOWN_RO_STATE) {
|
||||||
*err = sdsnew(
|
*err = sdsnew(
|
||||||
"Script attempted to execute a write command while the "
|
"Script attempted to execute a write command while the "
|
||||||
|
|
|
@ -3949,7 +3949,7 @@ int processCommand(client *c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t cmd_flags = getCommandFlags(c);
|
const uint64_t cmd_flags = getCommandFlags(c);
|
||||||
|
|
||||||
int is_read_command = (cmd_flags & CMD_READONLY) ||
|
int is_read_command = (cmd_flags & CMD_READONLY) ||
|
||||||
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_READONLY));
|
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_READONLY));
|
||||||
|
@ -4004,7 +4004,7 @@ int processCommand(client *c) {
|
||||||
{
|
{
|
||||||
int error_code;
|
int error_code;
|
||||||
clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,
|
clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,
|
||||||
&c->slot,&error_code);
|
&c->slot,cmd_flags,&error_code);
|
||||||
if (n == NULL || !clusterNodeIsMyself(n)) {
|
if (n == NULL || !clusterNodeIsMyself(n)) {
|
||||||
if (c->cmd->proc == execCommand) {
|
if (c->cmd->proc == execCommand) {
|
||||||
discardTransaction(c);
|
discardTransaction(c);
|
||||||
|
|
Loading…
Reference in New Issue