Skip to content

Commit 1dc89e2

Browse files
Optimization: Avoid deferred array reply on ZRANGE commands BYRANK (redis#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 redis#7844 (v 6.2) mainly affecting pipelined workloads. Co-authored-by: Oran Agra <[email protected]>
1 parent e8c5b66 commit 1dc89e2

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

src/networking.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -825,17 +825,18 @@ void addReplyLongLongWithPrefix(client *c, long long ll, char prefix) {
825825
* so we have a few shared objects to use if the integer is small
826826
* like it is most of the times. */
827827
const int opt_hdr = ll < OBJ_SHARED_BULKHDR_LEN && ll >= 0;
828+
const size_t hdr_len = OBJ_SHARED_HDR_STRLEN(ll);
828829
if (prefix == '*' && opt_hdr) {
829-
addReply(c,shared.mbulkhdr[ll]);
830+
addReplyProto(c,shared.mbulkhdr[ll]->ptr,hdr_len);
830831
return;
831832
} else if (prefix == '$' && opt_hdr) {
832-
addReply(c,shared.bulkhdr[ll]);
833+
addReplyProto(c,shared.bulkhdr[ll]->ptr,hdr_len);
833834
return;
834835
} else if (prefix == '%' && opt_hdr) {
835-
addReply(c,shared.maphdr[ll]);
836+
addReplyProto(c,shared.maphdr[ll]->ptr,hdr_len);
836837
return;
837838
} else if (prefix == '~' && opt_hdr) {
838-
addReply(c,shared.sethdr[ll]);
839+
addReplyProto(c,shared.sethdr[ll]->ptr,hdr_len);
839840
return;
840841
}
841842

src/t_zset.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,7 +2848,7 @@ typedef enum {
28482848

28492849
typedef struct zrange_result_handler zrange_result_handler;
28502850

2851-
typedef void (*zrangeResultBeginFunction)(zrange_result_handler *c);
2851+
typedef void (*zrangeResultBeginFunction)(zrange_result_handler *c, long length);
28522852
typedef void (*zrangeResultFinalizeFunction)(
28532853
zrange_result_handler *c, size_t result_count);
28542854
typedef void (*zrangeResultEmitCBufferFunction)(
@@ -2876,8 +2876,22 @@ struct zrange_result_handler {
28762876
zrangeResultEmitLongLongFunction emitResultFromLongLong;
28772877
};
28782878

2879-
/* Result handler methods for responding the ZRANGE to clients. */
2880-
static void zrangeResultBeginClient(zrange_result_handler *handler) {
2879+
/* Result handler methods for responding the ZRANGE to clients.
2880+
* length can be used to provide the result length in advance (avoids deferred reply overhead).
2881+
* length can be set to -1 if the result length is not know in advance.
2882+
*/
2883+
static void zrangeResultBeginClient(zrange_result_handler *handler, long length) {
2884+
if (length > 0) {
2885+
/* In case of WITHSCORES, respond with a single array in RESP2, and
2886+
* nested arrays in RESP3. We can't use a map response type since the
2887+
* client library needs to know to respect the order. */
2888+
if (handler->withscores && (handler->client->resp == 2)) {
2889+
length *= 2;
2890+
}
2891+
addReplyArrayLen(handler->client, length);
2892+
handler->userdata = NULL;
2893+
return;
2894+
}
28812895
handler->userdata = addReplyDeferredLen(handler->client);
28822896
}
28832897

@@ -2912,6 +2926,9 @@ static void zrangeResultEmitLongLongToClient(zrange_result_handler *handler,
29122926
static void zrangeResultFinalizeClient(zrange_result_handler *handler,
29132927
size_t result_count)
29142928
{
2929+
/* If the reply size was know at start there's nothing left to do */
2930+
if (!handler->userdata)
2931+
return;
29152932
/* In case of WITHSCORES, respond with a single array in RESP2, and
29162933
* nested arrays in RESP3. We can't use a map response type since the
29172934
* client library needs to know to respect the order. */
@@ -2923,8 +2940,9 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
29232940
}
29242941

29252942
/* Result handler methods for storing the ZRANGESTORE to a zset. */
2926-
static void zrangeResultBeginStore(zrange_result_handler *handler)
2943+
static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
29272944
{
2945+
UNUSED(length);
29282946
handler->dstobj = createZsetListpackObject();
29292947
}
29302948

@@ -3019,18 +3037,19 @@ void genericZrangebyrankCommand(zrange_result_handler *handler,
30193037
if (end < 0) end = llen+end;
30203038
if (start < 0) start = 0;
30213039

3022-
handler->beginResultEmission(handler);
30233040

30243041
/* Invariant: start >= 0, so this test will be true when end < 0.
30253042
* The range is empty when start > end or start >= length. */
30263043
if (start > end || start >= llen) {
3044+
handler->beginResultEmission(handler, 0);
30273045
handler->finalizeResultEmission(handler, 0);
30283046
return;
30293047
}
30303048
if (end >= llen) end = llen-1;
30313049
rangelen = (end-start)+1;
30323050
result_cardinality = rangelen;
30333051

3052+
handler->beginResultEmission(handler, rangelen);
30343053
if (zobj->encoding == OBJ_ENCODING_LISTPACK) {
30353054
unsigned char *zl = zobj->ptr;
30363055
unsigned char *eptr, *sptr;
@@ -3124,7 +3143,7 @@ void genericZrangebyscoreCommand(zrange_result_handler *handler,
31243143
int reverse) {
31253144
unsigned long rangelen = 0;
31263145

3127-
handler->beginResultEmission(handler);
3146+
handler->beginResultEmission(handler, -1);
31283147

31293148
/* For invalid offset, return directly. */
31303149
if (offset > 0 && offset >= (long)zsetLength(zobj)) {
@@ -3409,7 +3428,7 @@ void genericZrangebylexCommand(zrange_result_handler *handler,
34093428
{
34103429
unsigned long rangelen = 0;
34113430

3412-
handler->beginResultEmission(handler);
3431+
handler->beginResultEmission(handler, -1);
34133432

34143433
if (zobj->encoding == OBJ_ENCODING_LISTPACK) {
34153434
unsigned char *zl = zobj->ptr;
@@ -3647,7 +3666,7 @@ void zrangeGenericCommand(zrange_result_handler *handler, int argc_start, int st
36473666
zobj = lookupKeyRead(c->db, key);
36483667
if (zobj == NULL) {
36493668
if (store) {
3650-
handler->beginResultEmission(handler);
3669+
handler->beginResultEmission(handler, -1);
36513670
handler->finalizeResultEmission(handler, 0);
36523671
} else {
36533672
addReply(c, shared.emptyarray);

0 commit comments

Comments
 (0)