Commit d7971f96 authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.0

parents d2b5a579 acfb4f7a
......@@ -4,13 +4,20 @@
"complexity": "O(N) where N is the number of instances",
"group": "sentinel",
"since": "3.2.0",
"arity": 3,
"arity": -3,
"container": "SENTINEL",
"function": "sentinelCommand",
"command_flags": [
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "nodename",
"type": "string",
"multiple": true
}
]
}
}
......@@ -11,6 +11,24 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "ip",
"type": "string"
},
{
"name": "port",
"type": "integer"
},
{
"name": "current-epoch",
"type": "integer"
},
{
"name": "runid",
"type": "string"
}
]
}
}
......@@ -11,6 +11,12 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
]
}
}
......@@ -11,6 +11,24 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "name",
"type": "string"
},
{
"name": "ip",
"type": "string"
},
{
"name": "port",
"type": "integer"
},
{
"name": "quorum",
"type": "integer"
}
]
}
}
......@@ -11,6 +11,12 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
]
}
}
......@@ -11,6 +11,12 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
]
}
}
......@@ -11,6 +11,12 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "pattern",
"type": "pattern"
}
]
}
}
......@@ -11,6 +11,12 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
]
}
}
......@@ -11,6 +11,27 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
},
{
"name": "option_value",
"type": "block",
"multiple": true,
"arguments": [
{
"name": "option",
"type": "string"
},
{
"name": "value",
"type": "string"
}
]
}
]
}
}
......@@ -10,6 +10,28 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "mode",
"type": "oneof",
"optional":true,
"multiple":true,
"arguments": [
{
"name": "crash-after-election",
"type": "pure-token"
},
{
"name": "crash-after-promotion",
"type": "pure-token"
},
{
"name": "help",
"type": "pure-token"
}
]
}
]
}
}
......@@ -9,7 +9,7 @@
"history": [
[
"7.0.0",
"Added the `NOW`, `FORCE` and `ABORT` modifiers. Introduced waiting for lagging replicas before exiting."
"Added the `NOW`, `FORCE` and `ABORT` modifiers."
]
],
"command_flags": [
......
......@@ -24,7 +24,7 @@
},
{
"name": "port",
"type": "string"
"type": "integer"
}
]
}
......
......@@ -96,7 +96,7 @@
"token": "GET",
"name": "pattern",
"key_spec_index": 1,
"type": "string",
"type": "pattern",
"optional": true,
"multiple": true,
"multiple_token": true
......
......@@ -82,7 +82,7 @@
"token": "GET",
"name": "pattern",
"key_spec_index": 1,
"type": "string",
"type": "pattern",
"optional": true,
"multiple": true,
"multiple_token": true
......
{
"SPUBLISH": {
"summary": "Post a message to a shard channel",
"complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.",
"group": "pubsub",
"since": "7.0.0",
"arity": 3,
"function": "spublishCommand",
"command_flags": [
"PUBSUB",
"LOADING",
"STALE",
"FAST",
"MAY_REPLICATE"
],
"arguments": [
{
"name": "channel",
"type": "string"
},
{
"name": "message",
"type": "string"
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
"SPUBLISH": {
"summary": "Post a message to a shard channel",
"complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.",
"group": "pubsub",
"since": "7.0.0",
"arity": 3,
"function": "spublishCommand",
"command_flags": [
"PUBSUB",
"LOADING",
"STALE",
"FAST",
"MAY_REPLICATE"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": 0,
"step": 1,
"limit": 0
}
}
}
]
}
"arguments": [
{
"name": "channel",
"type": "string"
},
{
"name": "message",
"type": "string"
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": 0,
"step": 1,
"limit": 0
}
}
}
]
}
}
{
"SSUBSCRIBE": {
"summary": "Listen for messages published to the given shard channels",
"complexity": "O(N) where N is the number of shard channels to subscribe to.",
"group": "pubsub",
"since": "7.0.0",
"arity": -2,
"function": "ssubscribeCommand",
"command_flags": [
"PUBSUB",
"NOSCRIPT",
"LOADING",
"STALE"
],
"arguments": [
{
"name": "channel",
"type": "string",
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
"SSUBSCRIBE": {
"summary": "Listen for messages published to the given shard channels",
"complexity": "O(N) where N is the number of shard channels to subscribe to.",
"group": "pubsub",
"since": "7.0.0",
"arity": -2,
"function": "ssubscribeCommand",
"command_flags": [
"PUBSUB",
"NOSCRIPT",
"LOADING",
"STALE"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
"arguments": [
{
"name": "channel",
"type": "string",
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
}
......@@ -6,12 +6,7 @@
"since": "2.0.0",
"arity": -2,
"function": "subscribeCommand",
"history": [
[
"6.2.0",
"`RESET` can be called to exit subscribed state."
]
],
"history": [],
"command_flags": [
"PUBSUB",
"NOSCRIPT",
......
{
"SUNSUBSCRIBE": {
"summary": "Stop listening for messages posted to the given shard channels",
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
"group": "pubsub",
"since": "7.0.0",
"arity": -1,
"function": "sunsubscribeCommand",
"command_flags": [
"PUBSUB",
"NOSCRIPT",
"LOADING",
"STALE"
],
"arguments": [
{
"name": "channel",
"type": "string",
"optional": true,
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
"SUNSUBSCRIBE": {
"summary": "Stop listening for messages posted to the given shard channels",
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
"group": "pubsub",
"since": "7.0.0",
"arity": -1,
"function": "sunsubscribeCommand",
"command_flags": [
"PUBSUB",
"NOSCRIPT",
"LOADING",
"STALE"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
"arguments": [
{
"name": "channel",
"type": "string",
"optional": true,
"multiple": true
}
],
"key_specs": [
{
"flags": [
"NOT_KEY"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": -1,
"step": 1,
"limit": 0
}
}
}
]
}
}
This diff is collapsed.
......@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
val->lru = old->lru;
}
/* Although the key is not really deleted from the database, we regard
overwrite as two steps of unlink+add, so we still need to call the unlink
callback of the module. */
* overwrite as two steps of unlink+add, so we still need to call the unlink
* callback of the module. */
moduleNotifyKeyUnlink(key,old,db->id);
/* We want to try to unblock any client using a blocking XREADGROUP */
if (old->type == OBJ_STREAM)
signalKeyAsReady(db,key,old->type);
dictSetVal(db->dict, de, val);
if (server.lazyfree_lazy_server_del) {
......@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) {
robj *val = dictGetVal(de);
/* Tells the module that the key has been unlinked from the database. */
moduleNotifyKeyUnlink(key,val,db->id);
/* We want to try to unblock any client using a blocking XREADGROUP */
if (val->type == OBJ_STREAM)
signalKeyAsReady(db,key,val->type);
if (async) {
freeObjAsync(key, val, db->id);
dictSetVal(db->dict, de, NULL);
......@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) {
}
for (int j = startdb; j <= enddb; j++) {
scanDatabaseForDeletedStreams(&server.db[j], NULL);
touchAllWatchedKeysInDb(&server.db[j], NULL);
}
......@@ -1311,7 +1318,7 @@ void copyCommand(client *c) {
* one or more blocked clients for B[LR]POP or other blocking commands
* and signal the keys as ready if they are of the right type. See the comment
* where the function is used for more info. */
void scanDatabaseForReadyLists(redisDb *db) {
void scanDatabaseForReadyKeys(redisDb *db) {
dictEntry *de;
dictIterator *di = dictGetSafeIterator(db->blocking_keys);
while((de = dictNext(di)) != NULL) {
......@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) {
dictReleaseIterator(di);
}
/* Since we are unblocking XREADGROUP clients in the event the
* key was deleted/overwritten we must do the same in case the
* database was flushed/swapped. */
void scanDatabaseForDeletedStreams(redisDb *emptied, redisDb *replaced_with) {
/* Optimization: If no clients are in type BLOCKED_STREAM,
* we can skip this loop. */
if (!server.blocked_clients_by_type[BLOCKED_STREAM]) return;
dictEntry *de;
dictIterator *di = dictGetSafeIterator(emptied->blocking_keys);
while((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
int was_stream = 0, is_stream = 0;
dictEntry *kde = dictFind(emptied->dict, key->ptr);
if (kde) {
robj *value = dictGetVal(kde);
was_stream = value->type == OBJ_STREAM;
}
if (replaced_with) {
dictEntry *kde = dictFind(replaced_with->dict, key->ptr);
if (kde) {
robj *value = dictGetVal(kde);
is_stream = value->type == OBJ_STREAM;
}
}
/* We want to try to unblock any client using a blocking XREADGROUP */
if (was_stream && !is_stream)
signalKeyAsReady(emptied, key, OBJ_STREAM);
}
dictReleaseIterator(di);
}
/* Swap two databases at runtime so that all clients will magically see
* the new database even if already connected. Note that the client
* structure c->db points to a given DB, so we need to be smarter and
......@@ -1345,6 +1385,10 @@ int dbSwapDatabases(int id1, int id2) {
touchAllWatchedKeysInDb(db1, db2);
touchAllWatchedKeysInDb(db2, db1);
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
scanDatabaseForDeletedStreams(db1, db2);
scanDatabaseForDeletedStreams(db2, db1);
/* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since we want clients to
* remain in the same DB they were. */
......@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) {
* 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
* if needed. */
scanDatabaseForReadyLists(db1);
scanDatabaseForReadyLists(db2);
scanDatabaseForReadyKeys(db1);
scanDatabaseForReadyKeys(db2);
return C_OK;
}
......@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* client watching keys. */
touchAllWatchedKeysInDb(activedb, newdb);
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
scanDatabaseForDeletedStreams(activedb, newdb);
/* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since clients
* remain in the same DB they were. */
......@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* 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
* if needed. */
scanDatabaseForReadyLists(activedb);
scanDatabaseForReadyKeys(activedb);
}
trackingInvalidateKeysOnFlush(1);
......
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