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.",
|
" Create a memory leak of the input string.",
|
||||||
"LOG <message>",
|
"LOG <message>",
|
||||||
" Write <message> to the server log.",
|
" Write <message> to the server log.",
|
||||||
"HTSTATS <dbid>",
|
"HTSTATS <dbid> [full]",
|
||||||
" Return hash table statistics of the specified Redis database.",
|
" 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.",
|
" Like HTSTATS but for the hash table stored at <key>'s value.",
|
||||||
"LOADAOF",
|
"LOADAOF",
|
||||||
" Flush the AOF buffers on disk and reload the AOF in memory.",
|
" 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,"sdshdr32:%d ",(int)sizeof(struct sdshdr32));
|
||||||
sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
|
sizes = sdscatprintf(sizes,"sdshdr64:%d ",(int)sizeof(struct sdshdr64));
|
||||||
addReplyBulkSds(c,sizes);
|
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;
|
long dbid;
|
||||||
sds stats = sdsempty();
|
sds stats = sdsempty();
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
int full = 0;
|
||||||
|
|
||||||
if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {
|
if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {
|
||||||
sdsfree(stats);
|
sdsfree(stats);
|
||||||
@ -897,20 +898,26 @@ NULL
|
|||||||
addReplyError(c,"Out of range database");
|
addReplyError(c,"Out of range database");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (c->argc >= 4 && !strcasecmp(c->argv[3]->ptr,"full"))
|
||||||
|
full = 1;
|
||||||
|
|
||||||
stats = sdscatprintf(stats,"[Dictionary HT]\n");
|
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 = sdscat(stats,buf);
|
||||||
|
|
||||||
stats = sdscatprintf(stats,"[Expires HT]\n");
|
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);
|
stats = sdscat(stats,buf);
|
||||||
|
|
||||||
addReplyVerbatim(c,stats,sdslen(stats),"txt");
|
addReplyVerbatim(c,stats,sdslen(stats),"txt");
|
||||||
sdsfree(stats);
|
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;
|
robj *o;
|
||||||
dict *ht = NULL;
|
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))
|
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
|
||||||
== NULL) return;
|
== NULL) return;
|
||||||
@ -933,7 +940,7 @@ NULL
|
|||||||
"represented using an hash table");
|
"represented using an hash table");
|
||||||
} else {
|
} else {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
dictGetStats(buf,sizeof(buf),ht);
|
dictGetStats(buf,sizeof(buf),ht,full);
|
||||||
addReplyVerbatim(c,buf,strlen(buf),"txt");
|
addReplyVerbatim(c,buf,strlen(buf),"txt");
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) {
|
||||||
|
33
src/dict.c
33
src/dict.c
@ -1502,7 +1502,7 @@ dictEntry *dictFindEntryByPtrAndHash(dict *d, const void *oldptr, uint64_t hash)
|
|||||||
/* ------------------------------- Debugging ---------------------------------*/
|
/* ------------------------------- Debugging ---------------------------------*/
|
||||||
|
|
||||||
#define DICT_STATS_VECTLEN 50
|
#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 i, slots = 0, chainlen, maxchainlen = 0;
|
||||||
unsigned long totchainlen = 0;
|
unsigned long totchainlen = 0;
|
||||||
unsigned long clvector[DICT_STATS_VECTLEN];
|
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");
|
"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. */
|
/* Compute stats. */
|
||||||
for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
|
for (i = 0; i < DICT_STATS_VECTLEN; i++) clvector[i] = 0;
|
||||||
for (i = 0; i < DICTHT_SIZE(d->ht_size_exp[htidx]); i++) {
|
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);
|
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. */
|
/* Unlike snprintf(), return the number of characters actually written. */
|
||||||
if (bufsize) buf[bufsize-1] = '\0';
|
|
||||||
return strlen(buf);
|
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;
|
size_t l;
|
||||||
char *orig_buf = buf;
|
char *orig_buf = buf;
|
||||||
size_t orig_bufsize = bufsize;
|
size_t orig_bufsize = bufsize;
|
||||||
|
|
||||||
l = _dictGetStatsHt(buf,bufsize,d,0);
|
l = _dictGetStatsHt(buf,bufsize,d,0,full);
|
||||||
buf += l;
|
if (dictIsRehashing(d) && bufsize > l) {
|
||||||
bufsize -= l;
|
buf += l;
|
||||||
if (dictIsRehashing(d) && bufsize > 0) {
|
bufsize -= l;
|
||||||
_dictGetStatsHt(buf,bufsize,d,1);
|
_dictGetStatsHt(buf,bufsize,d,1,full);
|
||||||
}
|
}
|
||||||
/* Make sure there is a NULL term at the end. */
|
/* 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 ---------------------------------*/
|
/* ------------------------------- Benchmark ---------------------------------*/
|
||||||
|
@ -210,7 +210,7 @@ void dictReleaseIterator(dictIterator *iter);
|
|||||||
dictEntry *dictGetRandomKey(dict *d);
|
dictEntry *dictGetRandomKey(dict *d);
|
||||||
dictEntry *dictGetFairRandomKey(dict *d);
|
dictEntry *dictGetFairRandomKey(dict *d);
|
||||||
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);
|
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 dictGenHashFunction(const void *key, size_t len);
|
||||||
uint64_t dictGenCaseHashFunction(const unsigned char *buf, size_t len);
|
uint64_t dictGenCaseHashFunction(const unsigned char *buf, size_t len);
|
||||||
void dictEmpty(dict *d, void(callback)(dict*));
|
void dictEmpty(dict *d, void(callback)(dict*));
|
||||||
|
Loading…
Reference in New Issue
Block a user