From fc9f29103396eca37ecd594125bc0b9ae5f9d6e0 Mon Sep 17 00:00:00 2001 From: Binbin Date: Fri, 16 Aug 2024 10:18:36 +0800 Subject: [PATCH] Make a light weight version of DEBUG OBJECT, add FAST option (#881) Adding FAST option to DEBUG OBJECT command. The light version only shows the light weight infomation, which mostly O(1). The pre-existing version that show more stats such as serializedlength sometimes is time consuming. This should allow looking into debug stats (the key expired but not deleted), even on huge object, on which we're afraid to run the command for fear of causing a server freeze. Somehow like 3ca451c46fed894bf49e7561fa0282d2583f1c06. Signed-off-by: Binbin --- src/debug.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/debug.c b/src/debug.c index 27c4d804a..6a568315b 100644 --- a/src/debug.c +++ b/src/debug.c @@ -425,8 +425,10 @@ void debugCommand(client *c) { "MALLCTL-STR []", " Get or set a malloc tuning string.", #endif - "OBJECT ", + "OBJECT [fast]", " Show low level info about `key` and associated value.", + " Some fields of the default behavior may be time consuming to fetch,", + " and `fast` can be passed to avoid fetching them.", "DROP-CLUSTER-PACKET-FILTER ", " Drop all packets that match the filtered type. Set to -1 allow all packets.", "CLOSE-CLUSTER-LINK-ON-PACKET-DROP <0|1>", @@ -603,11 +605,14 @@ void debugCommand(client *c) { } else if (!strcasecmp(c->argv[1]->ptr, "close-cluster-link-on-packet-drop") && c->argc == 3) { server.debug_cluster_close_link_on_packet_drop = atoi(c->argv[2]->ptr); addReply(c, shared.ok); - } else if (!strcasecmp(c->argv[1]->ptr, "object") && c->argc == 3) { + } else if (!strcasecmp(c->argv[1]->ptr, "object") && (c->argc == 3 || c->argc == 4)) { dictEntry *de; robj *val; char *strenc; + int fast = 0; + if (c->argc == 4 && !strcasecmp(c->argv[3]->ptr, "fast")) fast = 1; + if ((de = dbFind(c->db, c->argv[2]->ptr)) == NULL) { addReplyErrorObject(c, shared.nokeyerr); return; @@ -638,22 +643,25 @@ void debugCommand(client *c) { used = snprintf(nextra, remaining, " ql_compressed:%d", compressed); nextra += used; remaining -= used; - /* Add total uncompressed size */ - unsigned long sz = 0; - for (quicklistNode *node = ql->head; node; node = node->next) { - sz += node->sz; + if (!fast) { + /* Add total uncompressed size */ + unsigned long sz = 0; + for (quicklistNode *node = ql->head; node; node = node->next) { + sz += node->sz; + } + used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz); + nextra += used; + remaining -= used; } - used = snprintf(nextra, remaining, " ql_uncompressed_size:%lu", sz); - nextra += used; - remaining -= used; } - addReplyStatusFormat(c, - "Value at:%p refcount:%d " - "encoding:%s serializedlength:%zu " - "lru:%d lru_seconds_idle:%llu%s", - (void *)val, val->refcount, strenc, rdbSavedObjectLen(val, c->argv[2], c->db->id), - val->lru, estimateObjectIdleTime(val) / 1000, extra); + sds s = sdsempty(); + s = sdscatprintf(s, "Value at:%p refcount:%d encoding:%s", (void *)val, val->refcount, strenc); + if (!fast) s = sdscatprintf(s, " serializedlength:%zu", rdbSavedObjectLen(val, c->argv[2], c->db->id)); + s = sdscatprintf(s, " lru:%d lru_seconds_idle:%llu", val->lru, estimateObjectIdleTime(val) / 1000); + s = sdscatprintf(s, "%s", extra); + addReplyStatusLength(c, s, sdslen(s)); + sdsfree(s); } else if (!strcasecmp(c->argv[1]->ptr, "sdslen") && c->argc == 3) { dictEntry *de; robj *val;