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:
parent
e8c5b66ed2
commit
1dc89e2d02
@ -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;
|
||||
}
|
||||
|
||||
|
35
src/t_zset.c
35
src/t_zset.c
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user