mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
894eba07c8
Previously two string encodings were used for string objects: 1) REDIS_ENCODING_RAW: a string object with obj->ptr pointing to an sds stirng. 2) REDIS_ENCODING_INT: a string object where the obj->ptr void pointer is casted to a long. This commit introduces a experimental new encoding called REDIS_ENCODING_EMBSTR that implements an object represented by an sds string that is not modifiable but allocated in the same memory chunk as the robj structure itself. The chunk looks like the following: +--------------+-----------+------------+--------+----+ | robj data... | robj->ptr | sds header | string | \0 | +--------------+-----+-----+------------+--------+----+ | ^ +-----------------------+ The robj->ptr points to the contiguous sds string data, so the object can be manipulated with the same functions used to manipulate plan string objects, however we need just on malloc and one free in order to allocate or release this kind of objects. Moreover it has better cache locality. This new allocation strategy should benefit both the memory usage and the performances. A performance gain between 60 and 70% was observed during micro-benchmarks, however there is more work to do to evaluate the performance impact and the memory usage behavior.
170 lines
6.6 KiB
C
170 lines
6.6 KiB
C
/* Slowlog implements a system that is able to remember the latest N
|
|
* queries that took more than M microseconds to execute.
|
|
*
|
|
* The execution time to reach to be logged in the slow log is set
|
|
* using the 'slowlog-log-slower-than' config directive, that is also
|
|
* readable and writable using the CONFIG SET/GET command.
|
|
*
|
|
* The slow queries log is actually not "logged" in the Redis log file
|
|
* but is accessible thanks to the SLOWLOG command.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|
* to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#include "redis.h"
|
|
#include "slowlog.h"
|
|
|
|
/* Create a new slowlog entry.
|
|
* Incrementing the ref count of all the objects retained is up to
|
|
* this function. */
|
|
slowlogEntry *slowlogCreateEntry(robj **argv, int argc, long long duration) {
|
|
slowlogEntry *se = zmalloc(sizeof(*se));
|
|
int j, slargc = argc;
|
|
|
|
if (slargc > SLOWLOG_ENTRY_MAX_ARGC) slargc = SLOWLOG_ENTRY_MAX_ARGC;
|
|
se->argc = slargc;
|
|
se->argv = zmalloc(sizeof(robj*)*slargc);
|
|
for (j = 0; j < slargc; j++) {
|
|
/* Logging too many arguments is a useless memory waste, so we stop
|
|
* at SLOWLOG_ENTRY_MAX_ARGC, but use the last argument to specify
|
|
* how many remaining arguments there were in the original command. */
|
|
if (slargc != argc && j == slargc-1) {
|
|
se->argv[j] = createObject(REDIS_STRING,
|
|
sdscatprintf(sdsempty(),"... (%d more arguments)",
|
|
argc-slargc+1));
|
|
} else {
|
|
/* Trim too long strings as well... */
|
|
if (argv[j]->type == REDIS_STRING &&
|
|
sdsEncodedObject(argv[j]) &&
|
|
sdslen(argv[j]->ptr) > SLOWLOG_ENTRY_MAX_STRING)
|
|
{
|
|
sds s = sdsnewlen(argv[j]->ptr, SLOWLOG_ENTRY_MAX_STRING);
|
|
|
|
s = sdscatprintf(s,"... (%lu more bytes)",
|
|
(unsigned long)
|
|
sdslen(argv[j]->ptr) - SLOWLOG_ENTRY_MAX_STRING);
|
|
se->argv[j] = createObject(REDIS_STRING,s);
|
|
} else {
|
|
se->argv[j] = argv[j];
|
|
incrRefCount(argv[j]);
|
|
}
|
|
}
|
|
}
|
|
se->time = time(NULL);
|
|
se->duration = duration;
|
|
se->id = server.slowlog_entry_id++;
|
|
return se;
|
|
}
|
|
|
|
/* Free a slow log entry. The argument is void so that the prototype of this
|
|
* function matches the one of the 'free' method of adlist.c.
|
|
*
|
|
* This function will take care to release all the retained object. */
|
|
void slowlogFreeEntry(void *septr) {
|
|
slowlogEntry *se = septr;
|
|
int j;
|
|
|
|
for (j = 0; j < se->argc; j++)
|
|
decrRefCount(se->argv[j]);
|
|
zfree(se->argv);
|
|
zfree(se);
|
|
}
|
|
|
|
/* Initialize the slow log. This function should be called a single time
|
|
* at server startup. */
|
|
void slowlogInit(void) {
|
|
server.slowlog = listCreate();
|
|
server.slowlog_entry_id = 0;
|
|
listSetFreeMethod(server.slowlog,slowlogFreeEntry);
|
|
}
|
|
|
|
/* Push a new entry into the slow log.
|
|
* This function will make sure to trim the slow log accordingly to the
|
|
* configured max length. */
|
|
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
|
|
if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
|
|
if (duration >= server.slowlog_log_slower_than)
|
|
listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));
|
|
|
|
/* Remove old entries if needed. */
|
|
while (listLength(server.slowlog) > server.slowlog_max_len)
|
|
listDelNode(server.slowlog,listLast(server.slowlog));
|
|
}
|
|
|
|
/* Remove all the entries from the current slow log. */
|
|
void slowlogReset(void) {
|
|
while (listLength(server.slowlog) > 0)
|
|
listDelNode(server.slowlog,listLast(server.slowlog));
|
|
}
|
|
|
|
/* The SLOWLOG command. Implements all the subcommands needed to handle the
|
|
* Redis slow log. */
|
|
void slowlogCommand(redisClient *c) {
|
|
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) {
|
|
slowlogReset();
|
|
addReply(c,shared.ok);
|
|
} else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) {
|
|
addReplyLongLong(c,listLength(server.slowlog));
|
|
} else if ((c->argc == 2 || c->argc == 3) &&
|
|
!strcasecmp(c->argv[1]->ptr,"get"))
|
|
{
|
|
long count = 10, sent = 0;
|
|
listIter li;
|
|
void *totentries;
|
|
listNode *ln;
|
|
slowlogEntry *se;
|
|
|
|
if (c->argc == 3 &&
|
|
getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != REDIS_OK)
|
|
return;
|
|
|
|
listRewind(server.slowlog,&li);
|
|
totentries = addDeferredMultiBulkLength(c);
|
|
while(count-- && (ln = listNext(&li))) {
|
|
int j;
|
|
|
|
se = ln->value;
|
|
addReplyMultiBulkLen(c,4);
|
|
addReplyLongLong(c,se->id);
|
|
addReplyLongLong(c,se->time);
|
|
addReplyLongLong(c,se->duration);
|
|
addReplyMultiBulkLen(c,se->argc);
|
|
for (j = 0; j < se->argc; j++)
|
|
addReplyBulk(c,se->argv[j]);
|
|
sent++;
|
|
}
|
|
setDeferredMultiBulkLength(c,totentries,sent);
|
|
} else {
|
|
addReplyError(c,
|
|
"Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN.");
|
|
}
|
|
}
|