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;
if (force_delete_expired) {
/* Forcing deletion of expired keys on a replica makes the replica /* Forcing deletion of expired keys on a replica makes the replica
* inconsistent with the master. The reason it's allowed for write * inconsistent with the master. We forbid it on readonly replicas, but
* commands is to make writable replicas behave consistently. It * we have to allow it on writable replicas to make write commands
* shall not be used in readonly commands. Modules are accepted so * behave consistently.
* that we don't break old modules. */ *
client *c = server.in_script ? scriptGetClient() : server.current_client; * It's possible that the WRITE flag is set even during a readonly
serverAssert(!c || !c->cmd || (c->cmd->flags & (CMD_WRITE|CMD_MODULE))); * command, since the command may trigger events that cause modules to
} * perform additional writes. */
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 */
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result); int has_keyspec = (getAllKeySpecsFlags(cmd, 1) & CMD_KEY_NOT_KEY);
} else { /* The command has at least one key-spec marked as VARIABLE_FLAGS */
if (!(getAllKeySpecsFlags(cmd, 0) & CMD_KEY_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); int ret = getKeysUsingKeySpecs(cmd,argv,argc,search_flags,result);
if (ret >= 0) if (ret >= 0)
return ret; return ret;
/* If the specs returned with an error (probably an INVALID or INCOMPLETE spec),
* fallback to the callback method. */
} }
if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc)
/* Resort to getkeys callback methods. */
if (has_module_getkeys)
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
/* We use native getkeys as a last resort, since not all these native getkeys provide
* flags properly (only the ones that correspond to INVALID, INCOMPLETE or VARIABLE_FLAGS do.*/
if (has_native_getkeys)
return cmd->getkeys_proc(cmd,argv,argc,result); return cmd->getkeys_proc(cmd,argv,argc,result);
return 0; 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"
" end"
" return err\n" " return err\n"
" end\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();
......
...@@ -86,8 +86,8 @@ static void luaEngineLoadHook(lua_State *lua, lua_Debug *ar) { ...@@ -86,8 +86,8 @@ static void luaEngineLoadHook(lua_State *lua, lua_Debug *ar) {
if (duration > LOAD_TIMEOUT_MS) { if (duration > LOAD_TIMEOUT_MS) {
lua_sethook(lua, luaEngineLoadHook, LUA_MASKLINE, 0); lua_sethook(lua, luaEngineLoadHook, LUA_MASKLINE, 0);
lua_pushstring(lua,"FUNCTION LOAD timeout"); luaPushError(lua,"FUNCTION LOAD timeout");
lua_error(lua); luaError(lua);
} }
} }
...@@ -151,10 +151,13 @@ static int luaEngineCreate(void *engine_ctx, functionLibInfo *li, sds blob, sds ...@@ -151,10 +151,13 @@ static int luaEngineCreate(void *engine_ctx, functionLibInfo *li, sds blob, sds
lua_sethook(lua,luaEngineLoadHook,LUA_MASKCOUNT,100000); lua_sethook(lua,luaEngineLoadHook,LUA_MASKCOUNT,100000);
/* Run the compiled code to allow it to register functions */ /* Run the compiled code to allow it to register functions */
if (lua_pcall(lua,0,0,0)) { if (lua_pcall(lua,0,0,0)) {
*err = sdscatprintf(sdsempty(), "Error registering functions: %s", lua_tostring(lua, -1)); errorInfo err_info = {0};
luaExtractErrorInformation(lua, &err_info);
*err = sdscatprintf(sdsempty(), "Error registering functions: %s", err_info.msg);
lua_pop(lua, 2); /* pops the error and globals table */ lua_pop(lua, 2); /* pops the error and globals table */
lua_sethook(lua,NULL,0,0); /* Disable hook */ lua_sethook(lua,NULL,0,0); /* Disable hook */
luaSaveOnRegistry(lua, REGISTRY_LOAD_CTX_NAME, NULL); luaSaveOnRegistry(lua, REGISTRY_LOAD_CTX_NAME, NULL);
luaErrorInformationDiscard(&err_info);
return C_ERR; return C_ERR;
} }
lua_sethook(lua,NULL,0,0); /* Disable hook */ lua_sethook(lua,NULL,0,0); /* Disable hook */
...@@ -429,11 +432,11 @@ static int luaRegisterFunction(lua_State *lua) { ...@@ -429,11 +432,11 @@ static int luaRegisterFunction(lua_State *lua) {
loadCtx *load_ctx = luaGetFromRegistry(lua, REGISTRY_LOAD_CTX_NAME); loadCtx *load_ctx = luaGetFromRegistry(lua, REGISTRY_LOAD_CTX_NAME);
if (!load_ctx) { if (!load_ctx) {
luaPushError(lua, "redis.register_function can only be called on FUNCTION LOAD command"); luaPushError(lua, "redis.register_function can only be called on FUNCTION LOAD command");
return luaRaiseError(lua); return luaError(lua);
} }
if (luaRegisterFunctionReadArgs(lua, &register_f_args) != C_OK) { if (luaRegisterFunctionReadArgs(lua, &register_f_args) != C_OK) {
return luaRaiseError(lua); return luaError(lua);
} }
sds err = NULL; sds err = NULL;
...@@ -441,7 +444,7 @@ static int luaRegisterFunction(lua_State *lua) { ...@@ -441,7 +444,7 @@ static int luaRegisterFunction(lua_State *lua) {
luaRegisterFunctionArgsDispose(lua, &register_f_args); luaRegisterFunctionArgsDispose(lua, &register_f_args);
luaPushError(lua, err); luaPushError(lua, err);
sdsfree(err); sdsfree(err);
return luaRaiseError(lua); return luaError(lua);
} }
return 0; return 0;
...@@ -475,11 +478,14 @@ int luaEngineInitEngine() { ...@@ -475,11 +478,14 @@ int luaEngineInitEngine() {
" 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"
" end"
" return err\n" " return err\n"
" end\n"
"end\n" "end\n"
"return error_handler"; "return error_handler";
luaL_loadbuffer(lua_engine_ctx->lua, errh_func, strlen(errh_func), "@err_handler_def"); luaL_loadbuffer(lua_engine_ctx->lua, errh_func, strlen(errh_func), "@err_handler_def");
......
...@@ -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" {
......
...@@ -92,7 +92,7 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) { ...@@ -92,7 +92,7 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
* \ / / \ \ / * \ / / \ \ /
* \ (long,lat) / / (long,lat) \ \ (long,lat) / * \ (long,lat) / / (long,lat) \ \ (long,lat) /
* \ / / \ / \ * \ / / \ / \
* --------- /----------------\ /--------------\ * --------- /----------------\ /---------------\
* Northern Hemisphere Southern Hemisphere Around the equator * Northern Hemisphere Southern Hemisphere Around the equator
*/ */
int geohashBoundingBox(GeoShape *shape, double *bounds) { int geohashBoundingBox(GeoShape *shape, double *bounds) {
...@@ -164,14 +164,14 @@ GeoHashRadius geohashCalculateAreasByShapeWGS84(GeoShape *shape) { ...@@ -164,14 +164,14 @@ GeoHashRadius geohashCalculateAreasByShapeWGS84(GeoShape *shape) {
geohashDecode(long_range, lat_range, neighbors.east, &east); geohashDecode(long_range, lat_range, neighbors.east, &east);
geohashDecode(long_range, lat_range, neighbors.west, &west); geohashDecode(long_range, lat_range, neighbors.west, &west);
if (geohashGetDistance(longitude,latitude,longitude,north.latitude.max) if (north.latitude.max < max_lat)
< radius_meters) decrease_step = 1; decrease_step = 1;
if (geohashGetDistance(longitude,latitude,longitude,south.latitude.min) if (south.latitude.min > min_lat)
< radius_meters) decrease_step = 1; decrease_step = 1;
if (geohashGetDistance(longitude,latitude,east.longitude.max,latitude) if (east.longitude.max < max_lon)
< radius_meters) decrease_step = 1; decrease_step = 1;
if (geohashGetDistance(longitude,latitude,west.longitude.min,latitude) if (west.longitude.min > min_lon)
< radius_meters) decrease_step = 1; decrease_step = 1;
} }
if (steps > 1 && decrease_step) { if (steps > 1 && decrease_step) {
......
/* Automatically generated by ./utils/generate-command-help.rb, do not edit. */ /* Automatically generated by utils/generate-command-help.rb, do not edit. */
#ifndef __REDIS_HELP_H #ifndef __REDIS_HELP_H
#define __REDIS_HELP_H #define __REDIS_HELP_H
...@@ -429,6 +429,11 @@ struct commandHelp { ...@@ -429,6 +429,11 @@ struct commandHelp {
"Extract keys given a full Redis command", "Extract keys given a full Redis command",
9, 9,
"2.8.13" }, "2.8.13" },
{ "COMMAND GETKEYSANDFLAGS",
"",
"Extract keys given a full Redis command",
9,
"7.0.0" },
{ "COMMAND HELP", { "COMMAND HELP",
"", "",
"Show helpful text about the different subcommands", "Show helpful text about the different subcommands",
...@@ -571,12 +576,12 @@ struct commandHelp { ...@@ -571,12 +576,12 @@ struct commandHelp {
"6.2.0" }, "6.2.0" },
{ "FCALL", { "FCALL",
"function numkeys key [key ...] arg [arg ...]", "function numkeys key [key ...] arg [arg ...]",
"PATCH__TBD__38__", "Invoke a function",
10, 10,
"7.0.0" }, "7.0.0" },
{ "FCALL_RO", { "FCALL_RO",
"function numkeys key [key ...] arg [arg ...]", "function numkeys key [key ...] arg [arg ...]",
"PATCH__TBD__7__", "Invoke a read-only function",
10, 10,
"7.0.0" }, "7.0.0" },
{ "FLUSHALL", { "FLUSHALL",
...@@ -595,7 +600,7 @@ struct commandHelp { ...@@ -595,7 +600,7 @@ struct commandHelp {
10, 10,
"7.0.0" }, "7.0.0" },
{ "FUNCTION DELETE", { "FUNCTION DELETE",
"function-name", "library-name",
"Delete a function by name", "Delete a function by name",
10, 10,
"7.0.0" }, "7.0.0" },
...@@ -625,7 +630,7 @@ struct commandHelp { ...@@ -625,7 +630,7 @@ struct commandHelp {
10, 10,
"7.0.0" }, "7.0.0" },
{ "FUNCTION LOAD", { "FUNCTION LOAD",
"engine-name library-name [REPLACE] [DESC library-description] function-code", "engine-name library-name [REPLACE] [DESCRIPTION library-description] function-code",
"Create a function with the given arguments (name, code, description)", "Create a function with the given arguments (name, code, description)",
10, 10,
"7.0.0" }, "7.0.0" },
...@@ -820,7 +825,7 @@ struct commandHelp { ...@@ -820,7 +825,7 @@ struct commandHelp {
1, 1,
"2.6.0" }, "2.6.0" },
{ "INFO", { "INFO",
"[section]", "[section [section ...]]",
"Get information and statistics about the server", "Get information and statistics about the server",
9, 9,
"1.0.0" }, "1.0.0" },
...@@ -1180,7 +1185,7 @@ struct commandHelp { ...@@ -1180,7 +1185,7 @@ struct commandHelp {
6, 6,
"7.0.0" }, "7.0.0" },
{ "PUBSUB SHARDNUMSUB", { "PUBSUB SHARDNUMSUB",
"", "[channel [channel ...]]",
"Get the count of subscribers for shard channels", "Get the count of subscribers for shard channels",
6, 6,
"7.0.0" }, "7.0.0" },
...@@ -1391,7 +1396,7 @@ struct commandHelp { ...@@ -1391,7 +1396,7 @@ struct commandHelp {
"1.0.0" }, "1.0.0" },
{ "SLAVEOF", { "SLAVEOF",
"host port", "host port",
"Make the server a replica of another instance, or promote it as master. Deprecated starting with Redis 5. Use REPLICAOF instead.", "Make the server a replica of another instance, or promote it as master.",
9, 9,
"1.0.0" }, "1.0.0" },
{ "SLOWLOG", { "SLOWLOG",
...@@ -1590,7 +1595,7 @@ struct commandHelp { ...@@ -1590,7 +1595,7 @@ struct commandHelp {
14, 14,
"5.0.0" }, "5.0.0" },
{ "XGROUP CREATE", { "XGROUP CREATE",
"key groupname id|$ [MKSTREAM]", "key groupname id|$ [MKSTREAM] [ENTRIESREAD entries_read]",
"Create a consumer group.", "Create a consumer group.",
14, 14,
"5.0.0" }, "5.0.0" },
...@@ -1615,7 +1620,7 @@ struct commandHelp { ...@@ -1615,7 +1620,7 @@ struct commandHelp {
14, 14,
"5.0.0" }, "5.0.0" },
{ "XGROUP SETID", { "XGROUP SETID",
"key groupname id|$", "key groupname id|$ [ENTRIESREAD entries_read]",
"Set a consumer group to an arbitrary last delivered ID value.", "Set a consumer group to an arbitrary last delivered ID value.",
14, 14,
"5.0.0" }, "5.0.0" },
...@@ -1675,7 +1680,7 @@ struct commandHelp { ...@@ -1675,7 +1680,7 @@ struct commandHelp {
14, 14,
"5.0.0" }, "5.0.0" },
{ "XSETID", { "XSETID",
"key last-id", "key last-id [ENTRIESADDED entries_added] [MAXDELETEDID max_deleted_entry_id]",
"An internal command for replicating stream values", "An internal command for replicating stream values",
14, 14,
"5.0.0" }, "5.0.0" },
......
This diff is collapsed.
...@@ -11,6 +11,13 @@ else ...@@ -11,6 +11,13 @@ else
SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup
endif endif
# OS X 11.x doesn't have /usr/lib/libSystem.dylib and needs an explicit setting.
ifeq ($(uname_S),Darwin)
ifeq ("$(wildcard /usr/lib/libSystem.dylib)","")
LIBS = -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lsystem
endif
endif
.SUFFIXES: .c .so .xo .o .SUFFIXES: .c .so .xo .o
all: helloworld.so hellotype.so helloblock.so hellocluster.so hellotimer.so hellodict.so hellohook.so helloacl.so all: helloworld.so hellotype.so helloblock.so hellocluster.so hellotimer.so hellodict.so hellohook.so helloacl.so
......
...@@ -76,7 +76,7 @@ int ListCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int ...@@ -76,7 +76,7 @@ int ListCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
void PingReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) { void PingReceiver(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {
RedisModule_Log(ctx,"notice","PING (type %d) RECEIVED from %.*s: '%.*s'", RedisModule_Log(ctx,"notice","PING (type %d) RECEIVED from %.*s: '%.*s'",
type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload); type,REDISMODULE_NODE_ID_LEN,sender_id,(int)len, payload);
RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PONG,(unsigned char*)"Ohi!",4); RedisModule_SendClusterMessage(ctx,NULL,MSGTYPE_PONG,"Ohi!",4);
RedisModuleCallReply *reply = RedisModule_Call(ctx, "INCR", "c", "pings_received"); RedisModuleCallReply *reply = RedisModule_Call(ctx, "INCR", "c", "pings_received");
RedisModule_FreeCallReply(reply); RedisModule_FreeCallReply(reply);
} }
......
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