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 @@ ...@@ -4,13 +4,20 @@
"complexity": "O(N) where N is the number of instances", "complexity": "O(N) where N is the number of instances",
"group": "sentinel", "group": "sentinel",
"since": "3.2.0", "since": "3.2.0",
"arity": 3, "arity": -3,
"container": "SENTINEL", "container": "SENTINEL",
"function": "sentinelCommand", "function": "sentinelCommand",
"command_flags": [ "command_flags": [
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "nodename",
"type": "string",
"multiple": true
}
] ]
} }
} }
...@@ -11,6 +11,24 @@ ...@@ -11,6 +11,24 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_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 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,24 @@ ...@@ -11,6 +11,24 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "name",
"type": "string"
},
{
"name": "ip",
"type": "string"
},
{
"name": "port",
"type": "integer"
},
{
"name": "quorum",
"type": "integer"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "pattern",
"type": "pattern"
}
] ]
} }
} }
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_SENTINEL" "ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
] ]
} }
} }
...@@ -11,6 +11,27 @@ ...@@ -11,6 +11,27 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_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 @@ ...@@ -10,6 +10,28 @@
"ADMIN", "ADMIN",
"SENTINEL", "SENTINEL",
"ONLY_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 @@ ...@@ -9,7 +9,7 @@
"history": [ "history": [
[ [
"7.0.0", "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": [ "command_flags": [
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
}, },
{ {
"name": "port", "name": "port",
"type": "string" "type": "integer"
} }
] ]
} }
......
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
"token": "GET", "token": "GET",
"name": "pattern", "name": "pattern",
"key_spec_index": 1, "key_spec_index": 1,
"type": "string", "type": "pattern",
"optional": true, "optional": true,
"multiple": true, "multiple": true,
"multiple_token": true "multiple_token": true
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
"token": "GET", "token": "GET",
"name": "pattern", "name": "pattern",
"key_spec_index": 1, "key_spec_index": 1,
"type": "string", "type": "pattern",
"optional": true, "optional": true,
"multiple": true, "multiple": true,
"multiple_token": true "multiple_token": true
......
...@@ -6,12 +6,7 @@ ...@@ -6,12 +6,7 @@
"since": "2.0.0", "since": "2.0.0",
"arity": -2, "arity": -2,
"function": "subscribeCommand", "function": "subscribeCommand",
"history": [ "history": [],
[
"6.2.0",
"`RESET` can be called to exit subscribed state."
]
],
"command_flags": [ "command_flags": [
"PUBSUB", "PUBSUB",
"NOSCRIPT", "NOSCRIPT",
......
This diff is collapsed.
...@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) { ...@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
val->lru = old->lru; val->lru = old->lru;
} }
/* Although the key is not really deleted from the database, we regard /* 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 * overwrite as two steps of unlink+add, so we still need to call the unlink
callback of the module. */ * callback of the module. */
moduleNotifyKeyUnlink(key,old,db->id); 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); dictSetVal(db->dict, de, val);
if (server.lazyfree_lazy_server_del) { if (server.lazyfree_lazy_server_del) {
...@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) { ...@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) {
robj *val = dictGetVal(de); robj *val = dictGetVal(de);
/* Tells the module that the key has been unlinked from the database. */ /* Tells the module that the key has been unlinked from the database. */
moduleNotifyKeyUnlink(key,val,db->id); 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) { if (async) {
freeObjAsync(key, val, db->id); freeObjAsync(key, val, db->id);
dictSetVal(db->dict, de, NULL); dictSetVal(db->dict, de, NULL);
...@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) { ...@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) {
} }
for (int j = startdb; j <= enddb; j++) { for (int j = startdb; j <= enddb; j++) {
scanDatabaseForDeletedStreams(&server.db[j], NULL);
touchAllWatchedKeysInDb(&server.db[j], NULL); touchAllWatchedKeysInDb(&server.db[j], NULL);
} }
...@@ -1311,7 +1318,7 @@ void copyCommand(client *c) { ...@@ -1311,7 +1318,7 @@ void copyCommand(client *c) {
* one or more blocked clients for B[LR]POP or other blocking commands * 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 * and signal the keys as ready if they are of the right type. See the comment
* where the function is used for more info. */ * where the function is used for more info. */
void scanDatabaseForReadyLists(redisDb *db) { void scanDatabaseForReadyKeys(redisDb *db) {
dictEntry *de; dictEntry *de;
dictIterator *di = dictGetSafeIterator(db->blocking_keys); dictIterator *di = dictGetSafeIterator(db->blocking_keys);
while((de = dictNext(di)) != NULL) { while((de = dictNext(di)) != NULL) {
...@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) { ...@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) {
dictReleaseIterator(di); 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 /* Swap two databases at runtime so that all clients will magically see
* the new database even if already connected. Note that the client * 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 * 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) { ...@@ -1345,6 +1385,10 @@ int dbSwapDatabases(int id1, int id2) {
touchAllWatchedKeysInDb(db1, db2); touchAllWatchedKeysInDb(db1, db2);
touchAllWatchedKeysInDb(db2, db1); 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, /* 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. */
...@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) { ...@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) {
* 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. */
scanDatabaseForReadyLists(db1); scanDatabaseForReadyKeys(db1);
scanDatabaseForReadyLists(db2); scanDatabaseForReadyKeys(db2);
return C_OK; return C_OK;
} }
...@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* client watching keys. */ * client watching keys. */
touchAllWatchedKeysInDb(activedb, newdb); 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, /* 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. */
...@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) { ...@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
* 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. */
scanDatabaseForReadyLists(activedb); scanDatabaseForReadyKeys(activedb);
} }
trackingInvalidateKeysOnFlush(1); 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