Unverified Commit d2b5a579 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10355 from oranagra/release-7.0-rc2

Release 7.0 RC2
parents d5915a16 10dc57ab
...@@ -6,6 +6,12 @@ ...@@ -6,6 +6,12 @@
"since": "5.0.0", "since": "5.0.0",
"arity": -3, "arity": -3,
"container": "XINFO", "container": "XINFO",
"history": [
[
"7.0.0",
"Added the `max-deleted-entry-id`, `entries-added`, `recorded-first-entry-id`, `entries-read` and `lag` fields"
]
],
"function": "xinfoCommand", "function": "xinfoCommand",
"command_flags": [ "command_flags": [
"READONLY" "READONLY"
......
...@@ -4,8 +4,14 @@ ...@@ -4,8 +4,14 @@
"complexity": "O(1)", "complexity": "O(1)",
"group": "stream", "group": "stream",
"since": "5.0.0", "since": "5.0.0",
"arity": 3, "arity": -3,
"function": "xsetidCommand", "function": "xsetidCommand",
"history": [
[
"7.0.0",
"Added the `entries_added` and `max_deleted_entry_id` arguments."
]
],
"command_flags": [ "command_flags": [
"WRITE", "WRITE",
"DENYOOM", "DENYOOM",
...@@ -43,6 +49,18 @@ ...@@ -43,6 +49,18 @@
{ {
"name": "last-id", "name": "last-id",
"type": "string" "type": "string"
},
{
"name": "entries_added",
"token": "ENTRIESADDED",
"type": "integer",
"optional": true
},
{
"name": "max_deleted_entry_id",
"token": "MAXDELETEDID",
"type": "string",
"optional": true
} }
] ]
} }
......
...@@ -178,6 +178,21 @@ static int connSocketWrite(connection *conn, const void *data, size_t data_len) ...@@ -178,6 +178,21 @@ static int connSocketWrite(connection *conn, const void *data, size_t data_len)
return ret; return ret;
} }
static int connSocketWritev(connection *conn, const struct iovec *iov, int iovcnt) {
int ret = writev(conn->fd, iov, iovcnt);
if (ret < 0 && errno != EAGAIN) {
conn->last_errno = errno;
/* Don't overwrite the state of a connection that is not already
* connected, not to mess with handler callbacks.
*/
if (errno != EINTR && conn->state == CONN_STATE_CONNECTED)
conn->state = CONN_STATE_ERROR;
}
return ret;
}
static int connSocketRead(connection *conn, void *buf, size_t buf_len) { static int connSocketRead(connection *conn, void *buf, size_t buf_len) {
int ret = read(conn->fd, buf, buf_len); int ret = read(conn->fd, buf, buf_len);
if (!ret) { if (!ret) {
...@@ -349,6 +364,7 @@ ConnectionType CT_Socket = { ...@@ -349,6 +364,7 @@ ConnectionType CT_Socket = {
.ae_handler = connSocketEventHandler, .ae_handler = connSocketEventHandler,
.close = connSocketClose, .close = connSocketClose,
.write = connSocketWrite, .write = connSocketWrite,
.writev = connSocketWritev,
.read = connSocketRead, .read = connSocketRead,
.accept = connSocketAccept, .accept = connSocketAccept,
.connect = connSocketConnect, .connect = connSocketConnect,
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define __REDIS_CONNECTION_H #define __REDIS_CONNECTION_H
#include <errno.h> #include <errno.h>
#include <sys/uio.h>
#define CONN_INFO_LEN 32 #define CONN_INFO_LEN 32
...@@ -59,6 +60,7 @@ typedef struct ConnectionType { ...@@ -59,6 +60,7 @@ typedef struct ConnectionType {
void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask); void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask);
int (*connect)(struct connection *conn, const char *addr, int port, const char *source_addr, ConnectionCallbackFunc connect_handler); int (*connect)(struct connection *conn, const char *addr, int port, const char *source_addr, ConnectionCallbackFunc connect_handler);
int (*write)(struct connection *conn, const void *data, size_t data_len); int (*write)(struct connection *conn, const void *data, size_t data_len);
int (*writev)(struct connection *conn, const struct iovec *iov, int iovcnt);
int (*read)(struct connection *conn, void *buf, size_t buf_len); int (*read)(struct connection *conn, void *buf, size_t buf_len);
void (*close)(struct connection *conn); void (*close)(struct connection *conn);
int (*accept)(struct connection *conn, ConnectionCallbackFunc accept_handler); int (*accept)(struct connection *conn, ConnectionCallbackFunc accept_handler);
...@@ -142,6 +144,18 @@ static inline int connWrite(connection *conn, const void *data, size_t data_len) ...@@ -142,6 +144,18 @@ static inline int connWrite(connection *conn, const void *data, size_t data_len)
return conn->type->write(conn, data, data_len); return conn->type->write(conn, data, data_len);
} }
/* Gather output data from the iovcnt buffers specified by the members of the iov
* array: iov[0], iov[1], ..., iov[iovcnt-1] and write to connection, behaves the same as writev(3).
*
* Like writev(3), a short write is possible. A -1 return indicates an error.
*
* The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use
* connGetState() to see if the connection state is still CONN_STATE_CONNECTED.
*/
static inline int connWritev(connection *conn, const struct iovec *iov, int iovcnt) {
return conn->type->writev(conn, iov, iovcnt);
}
/* Read from the connection, behaves the same as read(2). /* Read from the connection, behaves the same as read(2).
* *
* Like read(2), a short read is possible. A return value of 0 will indicate the * Like read(2), a short read is possible. A return value of 0 will indicate the
......
...@@ -83,16 +83,16 @@ robj *lookupKey(redisDb *db, robj *key, int flags) { ...@@ -83,16 +83,16 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
robj *val = NULL; robj *val = NULL;
if (de) { if (de) {
val = dictGetVal(de); val = dictGetVal(de);
int force_delete_expired = flags & LOOKUP_WRITE; /* Forcing deletion of expired keys on a replica makes the replica
if (force_delete_expired) { * inconsistent with the master. We forbid it on readonly replicas, but
/* Forcing deletion of expired keys on a replica makes the replica * we have to allow it on writable replicas to make write commands
* inconsistent with the master. The reason it's allowed for write * behave consistently.
* commands is to make writable replicas behave consistently. It *
* shall not be used in readonly commands. Modules are accepted so * It's possible that the WRITE flag is set even during a readonly
* that we don't break old modules. */ * command, since the command may trigger events that cause modules to
client *c = server.in_script ? scriptGetClient() : server.current_client; * perform additional writes. */
serverAssert(!c || !c->cmd || (c->cmd->flags & (CMD_WRITE|CMD_MODULE))); int is_ro_replica = server.masterhost && server.repl_slave_ro;
} int force_delete_expired = flags & LOOKUP_WRITE && !is_ro_replica;
if (expireIfNeeded(db, key, force_delete_expired)) { if (expireIfNeeded(db, key, force_delete_expired)) {
/* The key is no longer valid. */ /* The key is no longer valid. */
val = NULL; val = NULL;
...@@ -1340,6 +1340,11 @@ int dbSwapDatabases(int id1, int id2) { ...@@ -1340,6 +1340,11 @@ int dbSwapDatabases(int id1, int id2) {
redisDb aux = server.db[id1]; redisDb aux = server.db[id1];
redisDb *db1 = &server.db[id1], *db2 = &server.db[id2]; redisDb *db1 = &server.db[id1], *db2 = &server.db[id2];
/* Swapdb should make transaction fail if there is any
* client watching keys */
touchAllWatchedKeysInDb(db1, db2);
touchAllWatchedKeysInDb(db2, db1);
/* Swap hash tables. Note that we don't swap blocking_keys, /* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since we want clients to * ready_keys and watched_keys, since we want clients to
* remain in the same DB they were. */ * remain in the same DB they were. */
...@@ -1361,14 +1366,9 @@ int dbSwapDatabases(int id1, int id2) { ...@@ -1361,14 +1366,9 @@ int dbSwapDatabases(int id1, int id2) {
* However normally we only do this check for efficiency reasons * However normally we only do this check for efficiency reasons
* in dbAdd() when a list is created. So here we need to rescan * in dbAdd() when a list is created. So here we need to rescan
* the list of clients blocked on lists and signal lists as ready * the list of clients blocked on lists and signal lists as ready
* if needed. * if needed. */
*
* Also the swapdb should make transaction fail if there is any
* client watching keys */
scanDatabaseForReadyLists(db1); scanDatabaseForReadyLists(db1);
touchAllWatchedKeysInDb(db1, db2);
scanDatabaseForReadyLists(db2); scanDatabaseForReadyLists(db2);
touchAllWatchedKeysInDb(db2, db1);
return C_OK; return C_OK;
} }
...@@ -1387,6 +1387,10 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1387,6 +1387,10 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
redisDb aux = server.db[i]; redisDb aux = server.db[i];
redisDb *activedb = &server.db[i], *newdb = &tempDb[i]; redisDb *activedb = &server.db[i], *newdb = &tempDb[i];
/* Swapping databases should make transaction fail if there is any
* client watching keys. */
touchAllWatchedKeysInDb(activedb, newdb);
/* Swap hash tables. Note that we don't swap blocking_keys, /* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since clients * ready_keys and watched_keys, since clients
* remain in the same DB they were. */ * remain in the same DB they were. */
...@@ -1408,12 +1412,8 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1408,12 +1412,8 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* However normally we only do this check for efficiency reasons * However normally we only do this check for efficiency reasons
* in dbAdd() when a list is created. So here we need to rescan * in dbAdd() when a list is created. So here we need to rescan
* the list of clients blocked on lists and signal lists as ready * the list of clients blocked on lists and signal lists as ready
* if needed. * if needed. */
*
* Also the swapdb should make transaction fail if there is any
* client watching keys. */
scanDatabaseForReadyLists(activedb); scanDatabaseForReadyLists(activedb);
touchAllWatchedKeysInDb(activedb, newdb);
} }
trackingInvalidateKeysOnFlush(1); trackingInvalidateKeysOnFlush(1);
...@@ -1692,7 +1692,7 @@ int64_t getAllKeySpecsFlags(struct redisCommand *cmd, int inv) { ...@@ -1692,7 +1692,7 @@ int64_t getAllKeySpecsFlags(struct redisCommand *cmd, int inv) {
/* Fetch the keys based of the provided key specs. Returns the number of keys found, or -1 on error. /* Fetch the keys based of the provided key specs. Returns the number of keys found, or -1 on error.
* There are several flags that can be used to modify how this function finds keys in a command. * There are several flags that can be used to modify how this function finds keys in a command.
* *
* GET_KEYSPEC_INCLUDE_CHANNELS: Return channels as if they were keys. * GET_KEYSPEC_INCLUDE_NOT_KEYS: Return 'fake' keys as if they were keys.
* GET_KEYSPEC_RETURN_PARTIAL: Skips invalid and incomplete keyspecs but returns the keys * GET_KEYSPEC_RETURN_PARTIAL: Skips invalid and incomplete keyspecs but returns the keys
* found in other valid keyspecs. * found in other valid keyspecs.
*/ */
...@@ -1703,8 +1703,8 @@ int getKeysUsingKeySpecs(struct redisCommand *cmd, robj **argv, int argc, int se ...@@ -1703,8 +1703,8 @@ int getKeysUsingKeySpecs(struct redisCommand *cmd, robj **argv, int argc, int se
for (j = 0; j < cmd->key_specs_num; j++) { for (j = 0; j < cmd->key_specs_num; j++) {
keySpec *spec = cmd->key_specs + j; keySpec *spec = cmd->key_specs + j;
serverAssert(spec->begin_search_type != KSPEC_BS_INVALID); serverAssert(spec->begin_search_type != KSPEC_BS_INVALID);
/* Skip specs that represent channels instead of keys */ /* Skip specs that represent 'fake' keys */
if ((spec->flags & CMD_KEY_CHANNEL) && !(search_flags & GET_KEYSPEC_INCLUDE_CHANNELS)) { if ((spec->flags & CMD_KEY_NOT_KEY) && !(search_flags & GET_KEYSPEC_INCLUDE_NOT_KEYS)) {
continue; continue;
} }
...@@ -1821,31 +1821,123 @@ invalid_spec: ...@@ -1821,31 +1821,123 @@ invalid_spec:
* associated with how Redis will access the key. * associated with how Redis will access the key.
* *
* 'cmd' must be point to the corresponding entry into the redisCommand * 'cmd' must be point to the corresponding entry into the redisCommand
* table, according to the command name in argv[0]. * table, according to the command name in argv[0]. */
*
* This function uses the command's key specs, which contain the key-spec flags,
* (e.g. RO / RW) and only resorts to the command-specific helper function if
* any of the keys-specs are marked as INCOMPLETE. */
int getKeysFromCommandWithSpecs(struct redisCommand *cmd, robj **argv, int argc, int search_flags, getKeysResult *result) { int getKeysFromCommandWithSpecs(struct redisCommand *cmd, robj **argv, int argc, int search_flags, getKeysResult *result) {
if (cmd->flags & CMD_MODULE_GETKEYS) { /* The command has at least one key-spec not marked as NOT_KEY */
int has_keyspec = (getAllKeySpecsFlags(cmd, 1) & CMD_KEY_NOT_KEY);
/* The command has at least one key-spec marked as VARIABLE_FLAGS */
int has_varflags = (getAllKeySpecsFlags(cmd, 0) & CMD_KEY_VARIABLE_FLAGS);
/* Flags indicating that we have a getkeys callback */
int has_module_getkeys = cmd->flags & CMD_MODULE_GETKEYS;
int has_native_getkeys = !(cmd->flags & CMD_MODULE) && cmd->getkeys_proc;
/* The key-spec that's auto generated by RM_CreateCommand sets VARIABLE_FLAGS since no flags are given.
* If the module provides getkeys callback, we'll prefer it, but if it didn't, we'll use key-spec anyway. */
if ((cmd->flags & CMD_MODULE) && has_varflags && !has_module_getkeys)
has_varflags = 0;
/* We prefer key-specs if there are any, and their flags are reliable. */
if (has_keyspec && !has_varflags) {
int ret = getKeysUsingKeySpecs(cmd,argv,argc,search_flags,result);
if (ret >= 0)
return ret;
/* If the specs returned with an error (probably an INVALID or INCOMPLETE spec),
* fallback to the callback method. */
}
/* Resort to getkeys callback methods. */
if (has_module_getkeys)
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result); return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
} else {
if (!(getAllKeySpecsFlags(cmd, 0) & CMD_KEY_VARIABLE_FLAGS)) { /* We use native getkeys as a last resort, since not all these native getkeys provide
int ret = getKeysUsingKeySpecs(cmd,argv,argc,search_flags,result); * flags properly (only the ones that correspond to INVALID, INCOMPLETE or VARIABLE_FLAGS do.*/
if (ret >= 0) if (has_native_getkeys)
return ret; return cmd->getkeys_proc(cmd,argv,argc,result);
} return 0;
if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc)
return cmd->getkeys_proc(cmd,argv,argc,result);
return 0;
}
} }
/* This function returns a sanity check if the command may have keys. */ /* This function returns a sanity check if the command may have keys. */
int doesCommandHaveKeys(struct redisCommand *cmd) { int doesCommandHaveKeys(struct redisCommand *cmd) {
return (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) || /* has getkeys_proc (non modules) */ return (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) || /* has getkeys_proc (non modules) */
(cmd->flags & CMD_MODULE_GETKEYS) || /* module with GETKEYS */ (cmd->flags & CMD_MODULE_GETKEYS) || /* module with GETKEYS */
(getAllKeySpecsFlags(cmd, 1) & CMD_KEY_CHANNEL); /* has at least one key-spec not marked as CHANNEL */ (getAllKeySpecsFlags(cmd, 1) & CMD_KEY_NOT_KEY); /* has at least one key-spec not marked as NOT_KEY */
}
/* A simplified channel spec table that contains all of the redis commands
* and which channels they have and how they are accessed. */
typedef struct ChannelSpecs {
redisCommandProc *proc; /* Command procedure to match against */
uint64_t flags; /* CMD_CHANNEL_* flags for this command */
int start; /* The initial position of the first channel */
int count; /* The number of channels, or -1 if all remaining
* arguments are channels. */
} ChannelSpecs;
ChannelSpecs commands_with_channels[] = {
{subscribeCommand, CMD_CHANNEL_SUBSCRIBE, 1, -1},
{ssubscribeCommand, CMD_CHANNEL_SUBSCRIBE, 1, -1},
{unsubscribeCommand, CMD_CHANNEL_UNSUBSCRIBE, 1, -1},
{sunsubscribeCommand, CMD_CHANNEL_UNSUBSCRIBE, 1, -1},
{psubscribeCommand, CMD_CHANNEL_PATTERN | CMD_CHANNEL_SUBSCRIBE, 1, -1},
{punsubscribeCommand, CMD_CHANNEL_PATTERN | CMD_CHANNEL_UNSUBSCRIBE, 1, -1},
{publishCommand, CMD_CHANNEL_PUBLISH, 1, 1},
{spublishCommand, CMD_CHANNEL_PUBLISH, 1, 1},
{NULL,0} /* Terminator. */
};
/* Returns 1 if the command may access any channels matched by the flags
* argument. */
int doesCommandHaveChannelsWithFlags(struct redisCommand *cmd, int flags) {
/* If a module declares get channels, we are just going to assume
* has channels. This API is allowed to return false positives. */
if (cmd->flags & CMD_MODULE_GETCHANNELS) {
return 1;
}
for (ChannelSpecs *spec = commands_with_channels; spec->proc != NULL; spec += 1) {
if (cmd->proc == spec->proc) {
return !!(spec->flags & flags);
}
}
return 0;
}
/* Return all the arguments that are channels in the command passed via argc / argv.
* This function behaves similar to getKeysFromCommandWithSpecs, but with channels
* instead of keys.
*
* The command returns the positions of all the channel arguments inside the array,
* so the actual return value is a heap allocated array of integers. The
* length of the array is returned by reference into *numkeys.
*
* Along with the position, this command also returns the flags that are
* associated with how Redis will access the channel.
*
* 'cmd' must be point to the corresponding entry into the redisCommand
* table, according to the command name in argv[0]. */
int getChannelsFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
keyReference *keys;
/* If a module declares get channels, use that. */
if (cmd->flags & CMD_MODULE_GETCHANNELS) {
return moduleGetCommandChannelsViaAPI(cmd, argv, argc, result);
}
/* Otherwise check the channel spec table */
for (ChannelSpecs *spec = commands_with_channels; spec != NULL; spec += 1) {
if (cmd->proc == spec->proc) {
int start = spec->start;
int stop = (spec->count == -1) ? argc : start + spec->count;
if (stop > argc) stop = argc;
int count = 0;
keys = getKeysPrepareResult(result, stop - start);
for (int i = start; i < stop; i++ ) {
keys[count].pos = i;
keys[count++].flags = spec->flags;
}
result->numkeys = count;
return count;
}
}
return 0;
} }
/* The base case is to use the keys position as given in the command table /* The base case is to use the keys position as given in the command table
......
...@@ -482,6 +482,10 @@ void debugCommand(client *c) { ...@@ -482,6 +482,10 @@ void debugCommand(client *c) {
" Show low level client eviction pools info (maxmemory-clients).", " Show low level client eviction pools info (maxmemory-clients).",
"PAUSE-CRON <0|1>", "PAUSE-CRON <0|1>",
" Stop periodic cron job processing.", " Stop periodic cron job processing.",
"REPLYBUFFER-PEAK-RESET-TIME <NEVER||RESET|time>",
" Sets the time (in milliseconds) to wait between client reply buffer peak resets.",
" In case NEVER is provided the last observed peak will never be reset",
" In case RESET is provided the peak reset time will be restored to the default value",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -825,7 +829,7 @@ NULL ...@@ -825,7 +829,7 @@ NULL
int memerr; int memerr;
unsigned long long sz = memtoull((const char *)c->argv[2]->ptr, &memerr); unsigned long long sz = memtoull((const char *)c->argv[2]->ptr, &memerr);
if (memerr || !quicklistisSetPackedThreshold(sz)) { if (memerr || !quicklistisSetPackedThreshold(sz)) {
addReplyError(c, "argument must be a memory value bigger then 1 and smaller than 4gb"); addReplyError(c, "argument must be a memory value bigger than 1 and smaller than 4gb");
} else { } else {
addReply(c,shared.ok); addReply(c,shared.ok);
} }
...@@ -921,12 +925,12 @@ NULL ...@@ -921,12 +925,12 @@ NULL
addReplyStatus(c,"Apparently Redis did not crash: test passed"); addReplyStatus(c,"Apparently Redis did not crash: test passed");
} else if (!strcasecmp(c->argv[1]->ptr,"set-disable-deny-scripts") && c->argc == 3) } else if (!strcasecmp(c->argv[1]->ptr,"set-disable-deny-scripts") && c->argc == 3)
{ {
server.script_disable_deny_script = atoi(c->argv[2]->ptr);; server.script_disable_deny_script = atoi(c->argv[2]->ptr);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"config-rewrite-force-all") && c->argc == 2) } else if (!strcasecmp(c->argv[1]->ptr,"config-rewrite-force-all") && c->argc == 2)
{ {
if (rewriteConfig(server.configfile, 1) == -1) if (rewriteConfig(server.configfile, 1) == -1)
addReplyError(c, "CONFIG-REWRITE-FORCE-ALL failed"); addReplyErrorFormat(c, "CONFIG-REWRITE-FORCE-ALL failed: %s", strerror(errno));
else else
addReply(c, shared.ok); addReply(c, shared.ok);
} else if(!strcasecmp(c->argv[1]->ptr,"client-eviction") && c->argc == 2) { } else if(!strcasecmp(c->argv[1]->ptr,"client-eviction") && c->argc == 2) {
...@@ -958,6 +962,16 @@ NULL ...@@ -958,6 +962,16 @@ NULL
{ {
server.pause_cron = atoi(c->argv[2]->ptr); server.pause_cron = atoi(c->argv[2]->ptr);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"replybuffer-peak-reset-time") && c->argc == 3 ) {
if (!strcasecmp(c->argv[2]->ptr, "never")) {
server.reply_buffer_peak_reset_time = -1;
} else if(!strcasecmp(c->argv[2]->ptr, "reset")) {
server.reply_buffer_peak_reset_time = REPLY_BUFFER_DEFAULT_PEAK_RESET_TIME;
} else {
if (getLongFromObjectOrReply(c, c->argv[2], &server.reply_buffer_peak_reset_time, NULL) != C_OK)
return;
}
addReply(c, shared.ok);
} else { } else {
addReplySubcommandSyntaxError(c); addReplySubcommandSyntaxError(c);
return; return;
...@@ -1681,13 +1695,19 @@ void logStackTrace(void *eip, int uplevel) { ...@@ -1681,13 +1695,19 @@ void logStackTrace(void *eip, int uplevel) {
void logServerInfo(void) { void logServerInfo(void) {
sds infostring, clients; sds infostring, clients;
serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n"); serverLogRaw(LL_WARNING|LL_RAW, "\n------ INFO OUTPUT ------\n");
infostring = genRedisInfoString("all"); int all = 0, everything = 0;
robj *argv[1];
argv[0] = createStringObject("all", strlen("all"));
dict *section_dict = genInfoSectionDict(argv, 1, NULL, &all, &everything);
infostring = genRedisInfoString(section_dict, all, everything);
serverLogRaw(LL_WARNING|LL_RAW, infostring); serverLogRaw(LL_WARNING|LL_RAW, infostring);
serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n"); serverLogRaw(LL_WARNING|LL_RAW, "\n------ CLIENT LIST OUTPUT ------\n");
clients = getAllClientsInfoString(-1); clients = getAllClientsInfoString(-1);
serverLogRaw(LL_WARNING|LL_RAW, clients); serverLogRaw(LL_WARNING|LL_RAW, clients);
sdsfree(infostring); sdsfree(infostring);
sdsfree(clients); sdsfree(clients);
releaseInfoSectionDict(section_dict);
decrRefCount(argv[0]);
} }
/* Log certain config values, which can be used for debuggin */ /* Log certain config values, which can be used for debuggin */
...@@ -1723,10 +1743,10 @@ void logCurrentClient(void) { ...@@ -1723,10 +1743,10 @@ void logCurrentClient(void) {
sdsfree(client); sdsfree(client);
for (j = 0; j < cc->argc; j++) { for (j = 0; j < cc->argc; j++) {
robj *decoded; robj *decoded;
decoded = getDecodedObject(cc->argv[j]); decoded = getDecodedObject(cc->argv[j]);
serverLog(LL_WARNING|LL_RAW,"argv[%d]: '%s'\n", j, sds repr = sdscatrepr(sdsempty(),decoded->ptr, min(sdslen(decoded->ptr), 128));
(char*)decoded->ptr); serverLog(LL_WARNING|LL_RAW,"argv[%d]: '%s'\n", j, (char*)repr);
sdsfree(repr);
decrRefCount(decoded); decrRefCount(decoded);
} }
/* Check if the first argument, usually a key, is found inside the /* Check if the first argument, usually a key, is found inside the
...@@ -1764,7 +1784,10 @@ int memtest_test_linux_anonymous_maps(void) { ...@@ -1764,7 +1784,10 @@ int memtest_test_linux_anonymous_maps(void) {
if (!fd) return 0; if (!fd) return 0;
fp = fopen("/proc/self/maps","r"); fp = fopen("/proc/self/maps","r");
if (!fp) return 0; if (!fp) {
closeDirectLogFiledes(fd);
return 0;
}
while(fgets(line,sizeof(line),fp) != NULL) { while(fgets(line,sizeof(line),fp) != NULL) {
char *start, *end, *p = line; char *start, *end, *p = line;
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef _REDIS_DEBUGMACRO_H_
#define _REDIS_DEBUGMACRO_H_
#include <stdio.h> #include <stdio.h>
#define D(...) \ #define D(...) \
do { \ do { \
...@@ -39,3 +42,5 @@ ...@@ -39,3 +42,5 @@
fprintf(fp,"\n"); \ fprintf(fp,"\n"); \
fclose(fp); \ fclose(fp); \
} while (0) } while (0)
#endif /* _REDIS_DEBUGMACRO_H_ */
...@@ -128,6 +128,27 @@ robj *activeDefragStringOb(robj* ob, long *defragged) { ...@@ -128,6 +128,27 @@ robj *activeDefragStringOb(robj* ob, long *defragged) {
return ret; return ret;
} }
/* Defrag helper for lua scripts
*
* returns NULL in case the allocation wasn't moved.
* when it returns a non-null value, the old pointer was already released
* and should NOT be accessed. */
luaScript *activeDefragLuaScript(luaScript *script, long *defragged) {
luaScript *ret = NULL;
/* try to defrag script struct */
if ((ret = activeDefragAlloc(script))) {
script = ret;
(*defragged)++;
}
/* try to defrag actual script object */
robj *ob = activeDefragStringOb(script->body, defragged);
if (ob) script->body = ob;
return ret;
}
/* Defrag helper for dictEntries to be used during dict iteration (called on /* Defrag helper for dictEntries to be used during dict iteration (called on
* each step). Returns a stat of how many pointers were moved. */ * each step). Returns a stat of how many pointers were moved. */
long dictIterDefragEntry(dictIterator *iter) { long dictIterDefragEntry(dictIterator *iter) {
...@@ -256,6 +277,7 @@ long activeDefragZsetEntry(zset *zs, dictEntry *de) { ...@@ -256,6 +277,7 @@ long activeDefragZsetEntry(zset *zs, dictEntry *de) {
#define DEFRAG_SDS_DICT_VAL_IS_SDS 1 #define DEFRAG_SDS_DICT_VAL_IS_SDS 1
#define DEFRAG_SDS_DICT_VAL_IS_STROB 2 #define DEFRAG_SDS_DICT_VAL_IS_STROB 2
#define DEFRAG_SDS_DICT_VAL_VOID_PTR 3 #define DEFRAG_SDS_DICT_VAL_VOID_PTR 3
#define DEFRAG_SDS_DICT_VAL_LUA_SCRIPT 4
/* Defrag a dict with sds key and optional value (either ptr, sds or robj string) */ /* Defrag a dict with sds key and optional value (either ptr, sds or robj string) */
long activeDefragSdsDict(dict* d, int val_type) { long activeDefragSdsDict(dict* d, int val_type) {
...@@ -280,6 +302,10 @@ long activeDefragSdsDict(dict* d, int val_type) { ...@@ -280,6 +302,10 @@ long activeDefragSdsDict(dict* d, int val_type) {
void *newptr, *ptr = dictGetVal(de); void *newptr, *ptr = dictGetVal(de);
if ((newptr = activeDefragAlloc(ptr))) if ((newptr = activeDefragAlloc(ptr)))
de->v.val = newptr, defragged++; de->v.val = newptr, defragged++;
} else if (val_type == DEFRAG_SDS_DICT_VAL_LUA_SCRIPT) {
void *newptr, *ptr = dictGetVal(de);
if ((newptr = activeDefragLuaScript(ptr, &defragged)))
de->v.val = newptr;
} }
defragged += dictIterDefragEntry(di); defragged += dictIterDefragEntry(di);
} }
...@@ -939,7 +965,7 @@ long defragOtherGlobals() { ...@@ -939,7 +965,7 @@ long defragOtherGlobals() {
/* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc. /* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc.
* but we assume most of these are short lived, we only need to defrag allocations * but we assume most of these are short lived, we only need to defrag allocations
* that remain static for a long time */ * that remain static for a long time */
defragged += activeDefragSdsDict(evalScriptsDict(), DEFRAG_SDS_DICT_VAL_IS_STROB); defragged += activeDefragSdsDict(evalScriptsDict(), DEFRAG_SDS_DICT_VAL_LUA_SCRIPT);
defragged += moduleDefragGlobals(); defragged += moduleDefragGlobals();
return defragged; return defragged;
} }
...@@ -1130,7 +1156,7 @@ void activeDefragCycle(void) { ...@@ -1130,7 +1156,7 @@ void activeDefragCycle(void) {
/* Move on to next database, and stop if we reached the last one. */ /* Move on to next database, and stop if we reached the last one. */
if (++current_db >= server.dbnum) { if (++current_db >= server.dbnum) {
/* defrag other items not part of the db / keys */ /* defrag other items not part of the db / keys */
defragOtherGlobals(); server.stat_active_defrag_hits += defragOtherGlobals();
long long now = ustime(); long long now = ustime();
size_t frag_bytes; size_t frag_bytes;
......
...@@ -47,11 +47,6 @@ void ldbEnable(client *c); ...@@ -47,11 +47,6 @@ void ldbEnable(client *c);
void evalGenericCommandWithDebugging(client *c, int evalsha); void evalGenericCommandWithDebugging(client *c, int evalsha);
sds ldbCatStackValue(sds s, lua_State *lua, int idx); sds ldbCatStackValue(sds s, lua_State *lua, int idx);
typedef struct luaScript {
uint64_t flags;
robj *body;
} luaScript;
static void dictLuaScriptDestructor(dict *d, void *val) { static void dictLuaScriptDestructor(dict *d, void *val) {
UNUSED(d); UNUSED(d);
if (val == NULL) return; /* Lazy freeing will set value to NULL. */ if (val == NULL) return; /* Lazy freeing will set value to NULL. */
...@@ -63,7 +58,7 @@ static uint64_t dictStrCaseHash(const void *key) { ...@@ -63,7 +58,7 @@ static uint64_t dictStrCaseHash(const void *key) {
return dictGenCaseHashFunction((unsigned char*)key, strlen((char*)key)); return dictGenCaseHashFunction((unsigned char*)key, strlen((char*)key));
} }
/* server.lua_scripts sha (as sds string) -> scripts (as robj) cache. */ /* server.lua_scripts sha (as sds string) -> scripts (as luaScript) cache. */
dictType shaScriptObjectDictType = { dictType shaScriptObjectDictType = {
dictStrCaseHash, /* hash function */ dictStrCaseHash, /* hash function */
NULL, /* key dup */ NULL, /* key dup */
...@@ -246,11 +241,14 @@ void scriptingInit(int setup) { ...@@ -246,11 +241,14 @@ void scriptingInit(int setup) {
" if i and i.what == 'C' then\n" " if i and i.what == 'C' then\n"
" i = dbg.getinfo(3,'nSl')\n" " i = dbg.getinfo(3,'nSl')\n"
" end\n" " end\n"
" if type(err) ~= 'table' then\n"
" err = {err='ERR' .. tostring(err)}"
" end"
" if i then\n" " if i then\n"
" return i.source .. ':' .. i.currentline .. ': ' .. err\n" " err['source'] = i.source\n"
" else\n" " err['line'] = i.currentline\n"
" return err\n" " end"
" end\n" " return err\n"
"end\n"; "end\n";
luaL_loadbuffer(lua,errh_func,strlen(errh_func),"@err_handler_def"); luaL_loadbuffer(lua,errh_func,strlen(errh_func),"@err_handler_def");
lua_pcall(lua,0,0,0); lua_pcall(lua,0,0,0);
...@@ -392,7 +390,7 @@ sds luaCreateFunction(client *c, robj *body) { ...@@ -392,7 +390,7 @@ sds luaCreateFunction(client *c, robj *body) {
if (luaL_loadbuffer(lctx.lua,funcdef,sdslen(funcdef),"@user_script")) { if (luaL_loadbuffer(lctx.lua,funcdef,sdslen(funcdef),"@user_script")) {
if (c != NULL) { if (c != NULL) {
addReplyErrorFormat(c, addReplyErrorFormat(c,
"Error compiling script (new function): %s\n", "Error compiling script (new function): %s",
lua_tostring(lctx.lua,-1)); lua_tostring(lctx.lua,-1));
} }
lua_pop(lctx.lua,1); lua_pop(lctx.lua,1);
...@@ -403,7 +401,7 @@ sds luaCreateFunction(client *c, robj *body) { ...@@ -403,7 +401,7 @@ sds luaCreateFunction(client *c, robj *body) {
if (lua_pcall(lctx.lua,0,0,0)) { if (lua_pcall(lctx.lua,0,0,0)) {
if (c != NULL) { if (c != NULL) {
addReplyErrorFormat(c,"Error running script (new function): %s\n", addReplyErrorFormat(c,"Error running script (new function): %s",
lua_tostring(lctx.lua,-1)); lua_tostring(lctx.lua,-1));
} }
lua_pop(lctx.lua,1); lua_pop(lctx.lua,1);
...@@ -1479,8 +1477,8 @@ int ldbRepl(lua_State *lua) { ...@@ -1479,8 +1477,8 @@ int ldbRepl(lua_State *lua) {
while((argv = ldbReplParseCommand(&argc, &err)) == NULL) { while((argv = ldbReplParseCommand(&argc, &err)) == NULL) {
char buf[1024]; char buf[1024];
if (err) { if (err) {
lua_pushstring(lua, err); luaPushError(lua, err);
lua_error(lua); luaError(lua);
} }
int nread = connRead(ldb.conn,buf,sizeof(buf)); int nread = connRead(ldb.conn,buf,sizeof(buf));
if (nread <= 0) { if (nread <= 0) {
...@@ -1497,8 +1495,8 @@ int ldbRepl(lua_State *lua) { ...@@ -1497,8 +1495,8 @@ int ldbRepl(lua_State *lua) {
if (sdslen(ldb.cbuf) > 1<<20) { if (sdslen(ldb.cbuf) > 1<<20) {
sdsfree(ldb.cbuf); sdsfree(ldb.cbuf);
ldb.cbuf = sdsempty(); ldb.cbuf = sdsempty();
lua_pushstring(lua, "max client buffer reached"); luaPushError(lua, "max client buffer reached");
lua_error(lua); luaError(lua);
} }
} }
...@@ -1558,8 +1556,8 @@ ldbLog(sdsnew(" next line of code.")); ...@@ -1558,8 +1556,8 @@ ldbLog(sdsnew(" next line of code."));
ldbEval(lua,argv,argc); ldbEval(lua,argv,argc);
ldbSendLogs(); ldbSendLogs();
} else if (!strcasecmp(argv[0],"a") || !strcasecmp(argv[0],"abort")) { } else if (!strcasecmp(argv[0],"a") || !strcasecmp(argv[0],"abort")) {
lua_pushstring(lua, "script aborted for user request"); luaPushError(lua, "script aborted for user request");
lua_error(lua); luaError(lua);
} else if (argc > 1 && } else if (argc > 1 &&
(!strcasecmp(argv[0],"r") || !strcasecmp(argv[0],"redis"))) { (!strcasecmp(argv[0],"r") || !strcasecmp(argv[0],"redis"))) {
ldbRedis(lua,argv,argc); ldbRedis(lua,argv,argc);
...@@ -1640,8 +1638,8 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) { ...@@ -1640,8 +1638,8 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
/* If the client closed the connection and we have a timeout /* If the client closed the connection and we have a timeout
* connection, let's kill the script otherwise the process * connection, let's kill the script otherwise the process
* will remain blocked indefinitely. */ * will remain blocked indefinitely. */
lua_pushstring(lua, "timeout during Lua debugging with client closing connection"); luaPushError(lua, "timeout during Lua debugging with client closing connection");
lua_error(lua); luaError(lua);
} }
rctx->start_time = getMonotonicUs(); rctx->start_time = getMonotonicUs();
rctx->snapshot_time = mstime(); rctx->snapshot_time = mstime();
......
This diff is collapsed.
...@@ -808,7 +808,7 @@ void functionFlushCommand(client *c) { ...@@ -808,7 +808,7 @@ void functionFlushCommand(client *c) {
void functionHelpCommand(client *c) { void functionHelpCommand(client *c) {
const char *help[] = { const char *help[] = {
"LOAD <ENGINE NAME> <LIBRARY NAME> [REPLACE] [DESC <LIBRARY DESCRIPTION>] <LIBRARY CODE>", "LOAD <ENGINE NAME> <LIBRARY NAME> [REPLACE] [DESCRIPTION <LIBRARY DESCRIPTION>] <LIBRARY CODE>",
" Create a new library with the given library name and code.", " Create a new library with the given library name and code.",
"DELETE <LIBRARY NAME>", "DELETE <LIBRARY NAME>",
" Delete the given library.", " Delete the given library.",
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdint.h>
#if defined(__cplusplus) #if defined(__cplusplus)
extern "C" { extern "C" {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment