mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Make a light weight version (default) of DEBUG HTSTATS (#12212)
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.
This commit is contained in:
parent
c871db24c4
commit
3ca451c46f
21
src/debug.c
21
src/debug.c
@ -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.",
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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) {
|
||||
|
29
src/dict.c
29
src/dict.c
@ -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];
|
||||
@ -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++) {
|
||||
@ -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);
|
||||
l = _dictGetStatsHt(buf,bufsize,d,0,full);
|
||||
if (dictIsRehashing(d) && bufsize > l) {
|
||||
buf += l;
|
||||
bufsize -= l;
|
||||
if (dictIsRehashing(d) && bufsize > 0) {
|
||||
_dictGetStatsHt(buf,bufsize,d,1);
|
||||
_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 ---------------------------------*/
|
||||
|
@ -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*));
|
||||
|
Loading…
Reference in New Issue
Block a user