Optimization: Avoid deferred array reply on ZRANGE commands BYRANK (#10337)

Avoid deferred array reply on genericZrangebyrankCommand() when consumer type is client.
I.e. any ZRANGE / ZREVRNGE (when tank is used).
This was a performance regression introduced in #7844 (v 6.2) mainly affecting pipelined workloads.

Co-authored-by: Oran Agra <oran@redislabs.com>
This commit is contained in:
filipe oliveira 2022-02-24 12:20:00 +00:00 committed by GitHub
parent e8c5b66ed2
commit 1dc89e2d02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 12 deletions

View File

@ -825,17 +825,18 @@ void addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {
* so we have a few shared objects to use if the integer is small
* like it is most of the times. */
const int opt_hdr = ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0;
const size_t hdr_len = OBJ_SHARED_HDR_STRLEN(ll);
if (prefix == '*' && opt_hdr) {
addReply(c,shared.mbulkhdr[ll]);
addReplyProto(c,shared.mbulkhdr[ll]->ptr,hdr_len);
return;
} else if (prefix == '$' && opt_hdr) {
addReply(c,shared.bulkhdr[ll]);
addReplyProto(c,shared.bulkhdr[ll]->ptr,hdr_len);
return;
} else if (prefix == '%' && opt_hdr) {
addReply(c,shared.maphdr[ll]);
addReplyProto(c,shared.maphdr[ll]->ptr,hdr_len);
return;
} else if (prefix == '~' && opt_hdr) {
addReply(c,shared.sethdr[ll]);
addReplyProto(c,shared.sethdr[ll]->ptr,hdr_len);
return;
}

View File

@ -2848,7 +2848,7 @@ typedef enum {
typedef struct zrange_result_handler zrange_result_handler;
typedef void (*zrangeResultBeginFunction)(zrange_result_handler *c);
typedef void (*zrangeResultBeginFunction)(zrange_result_handler *c, long length);
typedef void (*zrangeResultFinalizeFunction)(
zrange_result_handler *c, size_t result_count);
typedef void (*zrangeResultEmitCBufferFunction)(
@ -2876,8 +2876,22 @@ struct zrange_result_handler {
zrangeResultEmitLongLongFunction emitResultFromLongLong;
};
/* Result handler methods for responding the ZRANGE to clients. */
static void zrangeResultBeginClient(zrange_result_handler *handler) {
/* Result handler methods for responding the ZRANGE to clients.
* length can be used to provide the result length in advance (avoids deferred reply overhead).
* length can be set to -1 if the result length is not know in advance.
*/
static void zrangeResultBeginClient(zrange_result_handler *handler, long length) {
if (length > 0) {
/* In case of WITHSCORES, respond with a single array in RESP2, and
* nested arrays in RESP3. We can't use a map response type since the
* client library needs to know to respect the order. */
if (handler->withscores && (handler->client->resp == 2)) {
length *= 2;
}
addReplyArrayLen(handler->client, length);
handler->userdata = NULL;
return;
}
handler->userdata = addReplyDeferredLen(handler->client);
}
@ -2912,6 +2926,9 @@ static void zrangeResultEmitLongLongToClient(zrange_result_handler *handler,
static void zrangeResultFinalizeClient(zrange_result_handler *handler,
size_t result_count)
{
/* If the reply size was know at start there's nothing left to do */
if (!handler->userdata)
return;
/* In case of WITHSCORES, respond with a single array in RESP2, and
* nested arrays in RESP3. We can't use a map response type since the
* client library needs to know to respect the order. */
@ -2923,8 +2940,9 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
}
/* Result handler methods for storing the ZRANGESTORE to a zset. */
static void zrangeResultBeginStore(zrange_result_handler *handler)
static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
{
UNUSED(length);
handler->dstobj = createZsetListpackObject();
}
@ -3019,11 +3037,11 @@ void genericZrangebyrankCommand(zrange_result_handler *handler,
if (end < 0) end = llen+end;
if (start < 0) start = 0;
handler->beginResultEmission(handler);
/* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) {
handler->beginResultEmission(handler, 0);
handler->finalizeResultEmission(handler, 0);
return;
}
@ -3031,6 +3049,7 @@ void genericZrangebyrankCommand(zrange_result_handler *handler,
rangelen = (end-start)+1;
result_cardinality = rangelen;
handler->beginResultEmission(handler, rangelen);
if (zobj->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl = zobj->ptr;
unsigned char *eptr, *sptr;
@ -3124,7 +3143,7 @@ void genericZrangebyscoreCommand(zrange_result_handler *handler,
int reverse) {
unsigned long rangelen = 0;
handler->beginResultEmission(handler);
handler->beginResultEmission(handler, -1);
/* For invalid offset, return directly. */
if (offset > 0 && offset >= (long)zsetLength(zobj)) {
@ -3409,7 +3428,7 @@ void genericZrangebylexCommand(zrange_result_handler *handler,
{
unsigned long rangelen = 0;
handler->beginResultEmission(handler);
handler->beginResultEmission(handler, -1);
if (zobj->encoding == OBJ_ENCODING_LISTPACK) {
unsigned char *zl = zobj->ptr;
@ -3647,7 +3666,7 @@ void zrangeGenericCommand(zrange_result_handler *handler, int argc_start, int st
zobj = lookupKeyRead(c->db, key);
if (zobj == NULL) {
if (store) {
handler->beginResultEmission(handler);
handler->beginResultEmission(handler, -1);
handler->finalizeResultEmission(handler, 0);
} else {
addReply(c, shared.emptyarray);