Skip to content

Commit

Permalink
Make a light weight version (default) of DEBUG HTSTATS (#12212)
Browse files Browse the repository at this point in the history
The light version only shows the table sizes, while the pre-existing
version that shows chain length stats is reachable with the `full` argument.

This should allow looking into rehashing state, even on huge dicts, on
which we're afraid to run the command for fear of causing a server freeze.

Also, fix a possible overflow in dictGetStats.
  • Loading branch information
oranagra authored May 24, 2023
1 parent c871db2 commit 3ca451c
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 17 deletions.
21 changes: 14 additions & 7 deletions src/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,9 @@ void debugCommand(client *c) {
" Create a memory leak of the input string.",
"LOG <message>",
" Write <message> to the server log.",
"HTSTATS <dbid>",
"HTSTATS <dbid> [full]",
" Return hash table statistics of the specified Redis database.",
"HTSTATS-KEY <key>",
"HTSTATS-KEY <key> [full]",
" Like HTSTATS but for the hash table stored at <key>'s value.",
"LOADAOF",
" Flush the AOF buffers on disk and reload the AOF in memory.",
Expand Down Expand Up @@ -883,10 +883,11 @@ NULL
sizes = sdscatprintf(sizes,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
addReplyBulkSds(c,sizes);
} else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc == 3) {
} else if (!strcasecmp(c->argv[1]->ptr,"htstats") && c->argc >= 3) {
long dbid;
sds stats = sdsempty();
char buf[4096];
int full = 0;

if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {
sdsfree(stats);
Expand All @@ -897,20 +898,26 @@ NULL
addReplyError(c,"Out of range database");
return;
}
if (c->argc >= 4 && !strcasecmp(c->argv[3]->ptr,"full"))
full = 1;

stats = sdscatprintf(stats,"[Dictionary HT]\n");
dictGetStats(buf,sizeof(buf),server.db[dbid].dict);
dictGetStats(buf,sizeof(buf),server.db[dbid].dict,full);
stats = sdscat(stats,buf);

stats = sdscatprintf(stats,"[Expires HT]\n");
dictGetStats(buf,sizeof(buf),server.db[dbid].expires);
dictGetStats(buf,sizeof(buf),server.db[dbid].expires,full);
stats = sdscat(stats,buf);

addReplyVerbatim(c,stats,sdslen(stats),"txt");
sdsfree(stats);
} else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc == 3) {
} else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc >= 3) {
robj *o;
dict *ht = NULL;
int full = 0;

if (c->argc >= 4 && !strcasecmp(c->argv[3]->ptr,"full"))
full = 1;

if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
== NULL) return;
Expand All @@ -933,7 +940,7 @@ NULL
"represented using an hash table");
} else {
char buf[4096];
dictGetStats(buf,sizeof(buf),ht);
dictGetStats(buf,sizeof(buf),ht,full);
addReplyVerbatim(c,buf,strlen(buf),"txt");
}
} else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) {
Expand Down
33 changes: 24 additions & 9 deletions src/dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -1502,7 +1502,7 @@ dictEntry *dictFindEntryByPtrAndHash(dict *d, const void *oldptr, uint64_t hash)
/* ------------------------------- Debugging ---------------------------------*/

#define DICT_STATS_VECTLEN 50
size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) {
size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx, int full) {
unsigned long i, slots = 0, chainlen, maxchainlen = 0;
unsigned long totchainlen = 0;
unsigned long clvector[DICT_STATS_VECTLEN];
Expand All @@ -1513,6 +1513,20 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) {
"No stats available for empty dictionaries\n");
}

if (!full) {
l += snprintf(buf+l,bufsize-l,
"Hash table %d stats (%s):\n"
" table size: %lu\n"
" number of elements: %lu\n",
htidx, (htidx == 0) ? "main hash table" : "rehashing target",
DICTHT_SIZE(d->ht_size_exp[htidx]), d->ht_used[htidx]);

/* Make sure there is a NULL term at the end. */
buf[bufsize-1] = '\0';
/* Unlike snprintf(), return the number of characters actually written. */
return strlen(buf);
}

/* Compute stats. */
for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
for (i = 0; i < DICTHT_SIZE(d->ht_size_exp[htidx]); i++) {
Expand Down Expand Up @@ -1557,24 +1571,25 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dict *d, int htidx) {
i, clvector[i], ((float)clvector[i]/DICTHT_SIZE(d->ht_size_exp[htidx]))*100);
}

/* Make sure there is a NULL term at the end. */
buf[bufsize-1] = '\0';
/* Unlike snprintf(), return the number of characters actually written. */
if (bufsize) buf[bufsize-1] = '\0';
return strlen(buf);
}

void dictGetStats(char *buf, size_t bufsize, dict *d) {
void dictGetStats(char *buf, size_t bufsize, dict *d, int full) {
size_t l;
char *orig_buf = buf;
size_t orig_bufsize = bufsize;

l = _dictGetStatsHt(buf,bufsize,d,0);
buf += l;
bufsize -= l;
if (dictIsRehashing(d) && bufsize > 0) {
_dictGetStatsHt(buf,bufsize,d,1);
l = _dictGetStatsHt(buf,bufsize,d,0,full);
if (dictIsRehashing(d) && bufsize > l) {
buf += l;
bufsize -= l;
_dictGetStatsHt(buf,bufsize,d,1,full);
}
/* Make sure there is a NULL term at the end. */
if (orig_bufsize) orig_buf[orig_bufsize-1] = '\0';
orig_buf[orig_bufsize-1] = '\0';
}

/* ------------------------------- Benchmark ---------------------------------*/
Expand Down
2 changes: 1 addition & 1 deletion src/dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d);
dictEntry *dictGetFairRandomKey(dict *d);
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);
void dictGetStats(char *buf, size_t bufsize, dict *d);
void dictGetStats(char *buf, size_t bufsize, dict *d, int full);
uint64_t dictGenHashFunction(const void *key, size_t len);
uint64_t dictGenCaseHashFunction(const unsigned char *buf, size_t len);
void dictEmpty(dict *d, void(callback)(dict*));
Expand Down

0 comments on commit 3ca451c

Please sign in to comment.