Commit 090bc0c1 authored by antirez's avatar antirez
Browse files

Merge branch 'csc2' into unstable

parents c21c23bb 8ea7a3ee
...@@ -2160,7 +2160,7 @@ standardConfig configs[] = { ...@@ -2160,7 +2160,7 @@ standardConfig configs[] = {
createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL), createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),
createIntConfig("rdb-key-save-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL), createIntConfig("rdb-key-save-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),
createIntConfig("key-load-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL), createIntConfig("key-load-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),
createIntConfig("tracking-table-max-fill", NULL, MODIFIABLE_CONFIG, 0, 100, server.tracking_table_max_fill, 10, INTEGER_CONFIG, NULL, NULL), /* Default: 10% tracking table max fill. */ createIntConfig("tracking-table-max-fill", NULL, MODIFIABLE_CONFIG, 0, 100, server.tracking_table_max_keys, 1000000, INTEGER_CONFIG, NULL, NULL), /* Default: 10% tracking table max number of keys tracked. */
createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */ createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, server.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */
createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ), createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves), createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),
......
...@@ -154,6 +154,7 @@ client *createClient(connection *conn) { ...@@ -154,6 +154,7 @@ client *createClient(connection *conn) {
c->peerid = NULL; c->peerid = NULL;
c->client_list_node = NULL; c->client_list_node = NULL;
c->client_tracking_redirection = 0; c->client_tracking_redirection = 0;
c->client_tracking_prefixes = NULL;
c->auth_callback = NULL; c->auth_callback = NULL;
c->auth_callback_privdata = NULL; c->auth_callback_privdata = NULL;
c->auth_module = NULL; c->auth_module = NULL;
...@@ -2027,7 +2028,6 @@ int clientSetNameOrReply(client *c, robj *name) { ...@@ -2027,7 +2028,6 @@ int clientSetNameOrReply(client *c, robj *name) {
void clientCommand(client *c) { void clientCommand(client *c) {
listNode *ln; listNode *ln;
listIter li; listIter li;
client *client;
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
...@@ -2141,7 +2141,7 @@ NULL ...@@ -2141,7 +2141,7 @@ NULL
/* Iterate clients killing all the matching clients. */ /* Iterate clients killing all the matching clients. */
listRewind(server.clients,&li); listRewind(server.clients,&li);
while ((ln = listNext(&li)) != NULL) { while ((ln = listNext(&li)) != NULL) {
client = listNodeValue(ln); client *client = listNodeValue(ln);
if (addr && strcmp(getClientPeerId(client),addr) != 0) continue; if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;
if (type != -1 && getClientType(client) != type) continue; if (type != -1 && getClientType(client) != type) continue;
if (id != 0 && client->id != id) continue; if (id != 0 && client->id != id) continue;
...@@ -2219,38 +2219,74 @@ NULL ...@@ -2219,38 +2219,74 @@ NULL
UNIT_MILLISECONDS) != C_OK) return; UNIT_MILLISECONDS) != C_OK) return;
pauseClients(duration); pauseClients(duration);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"tracking") && } else if (!strcasecmp(c->argv[1]->ptr,"tracking") && c->argc >= 3) {
(c->argc == 3 || c->argc == 5)) /* CLIENT TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first]
{ * [PREFIX second] ... */
/* CLIENT TRACKING (on|off) [REDIRECT <id>] */
long long redir = 0; long long redir = 0;
int bcast = 0;
robj **prefix = NULL;
size_t numprefix = 0;
/* Parse the redirection option: we'll require the client with /* Parse the options. */
* the specified ID to exist right now, even if it is possible for (int j = 3; j < c->argc; j++) {
* it will get disconnected later. */ int moreargs = (c->argc-1) - j;
if (c->argc == 5) {
if (strcasecmp(c->argv[3]->ptr,"redirect") != 0) { if (!strcasecmp(c->argv[j]->ptr,"redirect") && moreargs) {
addReply(c,shared.syntaxerr); j++;
return; if (getLongLongFromObjectOrReply(c,c->argv[j],&redir,NULL) !=
} else {
if (getLongLongFromObjectOrReply(c,c->argv[4],&redir,NULL) !=
C_OK) return; C_OK) return;
/* We will require the client with the specified ID to exist
* right now, even if it is possible that it gets disconnected
* later. Still a valid sanity check. */
if (lookupClientByID(redir) == NULL) { if (lookupClientByID(redir) == NULL) {
addReplyError(c,"The client ID you want redirect to " addReplyError(c,"The client ID you want redirect to "
"does not exist"); "does not exist");
return; return;
} }
} else if (!strcasecmp(c->argv[j]->ptr,"bcast")) {
bcast++;
} else if (!strcasecmp(c->argv[j]->ptr,"prefix") && moreargs) {
j++;
prefix = zrealloc(prefix,sizeof(robj*)*(numprefix+1));
prefix[numprefix++] = c->argv[j];
} else {
zfree(prefix);
addReply(c,shared.syntaxerr);
return;
} }
} }
/* Options are ok: enable or disable the tracking for this client. */
if (!strcasecmp(c->argv[2]->ptr,"on")) { if (!strcasecmp(c->argv[2]->ptr,"on")) {
enableTracking(c,redir); /* Before enabling tracking, make sure options are compatible
* among each other and with the current state of the client. */
if (!bcast && numprefix) {
addReplyError(c,
"PREFIX option requires BCAST mode to be enabled");
zfree(prefix);
return;
}
if (c->flags & CLIENT_TRACKING) {
int oldbcast = !!(c->flags & CLIENT_TRACKING_BCAST);
if (oldbcast != bcast) {
addReplyError(c,
"You can't switch BCAST mode on/off before disabling "
"tracking for this client, and then re-enabling it with "
"a different mode.");
zfree(prefix);
return;
}
}
enableTracking(c,redir,bcast,prefix,numprefix);
} else if (!strcasecmp(c->argv[2]->ptr,"off")) { } else if (!strcasecmp(c->argv[2]->ptr,"off")) {
disableTracking(c); disableTracking(c);
} else { } else {
zfree(prefix);
addReply(c,shared.syntaxerr); addReply(c,shared.syntaxerr);
return; return;
} }
zfree(prefix);
addReply(c,shared.ok); addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"getredir") && c->argc == 2) { } else if (!strcasecmp(c->argv[1]->ptr,"getredir") && c->argc == 2) {
/* CLIENT GETREDIR */ /* CLIENT GETREDIR */
......
...@@ -35,7 +35,11 @@ int clientSubscriptionsCount(client *c); ...@@ -35,7 +35,11 @@ int clientSubscriptionsCount(client *c);
* Pubsub client replies API * Pubsub client replies API
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
/* Send a pubsub message of type "message" to the client. */ /* Send a pubsub message of type "message" to the client.
* Normally 'msg' is a Redis object containing the string to send as
* message. However if the caller sets 'msg' as NULL, it will be able
* to send a special message (for instance an Array type) by using the
* addReply*() API family. */
void addReplyPubsubMessage(client *c, robj *channel, robj *msg) { void addReplyPubsubMessage(client *c, robj *channel, robj *msg) {
if (c->resp == 2) if (c->resp == 2)
addReply(c,shared.mbulkhdr[3]); addReply(c,shared.mbulkhdr[3]);
...@@ -43,7 +47,7 @@ void addReplyPubsubMessage(client *c, robj *channel, robj *msg) { ...@@ -43,7 +47,7 @@ void addReplyPubsubMessage(client *c, robj *channel, robj *msg) {
addReplyPushLen(c,3); addReplyPushLen(c,3);
addReply(c,shared.messagebulk); addReply(c,shared.messagebulk);
addReplyBulk(c,channel); addReplyBulk(c,channel);
addReplyBulk(c,msg); if (msg) addReplyBulk(c,msg);
} }
/* Send a pubsub message of type "pmessage" to the client. The difference /* Send a pubsub message of type "pmessage" to the client. The difference
......
...@@ -1766,6 +1766,7 @@ int raxRandomWalk(raxIterator *it, size_t steps) { ...@@ -1766,6 +1766,7 @@ int raxRandomWalk(raxIterator *it, size_t steps) {
if (n->iskey) steps--; if (n->iskey) steps--;
} }
it->node = n; it->node = n;
it->data = raxGetData(it->node);
return 1; return 1;
} }
......
...@@ -2124,6 +2124,10 @@ void beforeSleep(struct aeEventLoop *eventLoop) { ...@@ -2124,6 +2124,10 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
if (listLength(server.unblocked_clients)) if (listLength(server.unblocked_clients))
processUnblockedClients(); processUnblockedClients();
/* Send the invalidation messages to clients participating to the
* client side caching protocol in broadcasting (BCAST) mode. */
trackingBroadcastInvalidationMessages();
/* Write the AOF buffer on disk */ /* Write the AOF buffer on disk */
flushAppendOnlyFile(0); flushAppendOnlyFile(0);
...@@ -3310,8 +3314,11 @@ void call(client *c, int flags) { ...@@ -3310,8 +3314,11 @@ void call(client *c, int flags) {
if (c->cmd->flags & CMD_READONLY) { if (c->cmd->flags & CMD_READONLY) {
client *caller = (c->flags & CLIENT_LUA && server.lua_caller) ? client *caller = (c->flags & CLIENT_LUA && server.lua_caller) ?
server.lua_caller : c; server.lua_caller : c;
if (caller->flags & CLIENT_TRACKING) if (caller->flags & CLIENT_TRACKING &&
!(caller->flags & CLIENT_TRACKING_BCAST))
{
trackingRememberKeys(caller); trackingRememberKeys(caller);
}
} }
server.fixed_time_expire--; server.fixed_time_expire--;
...@@ -4221,7 +4228,8 @@ sds genRedisInfoString(const char *section) { ...@@ -4221,7 +4228,8 @@ sds genRedisInfoString(const char *section) {
"active_defrag_misses:%lld\r\n" "active_defrag_misses:%lld\r\n"
"active_defrag_key_hits:%lld\r\n" "active_defrag_key_hits:%lld\r\n"
"active_defrag_key_misses:%lld\r\n" "active_defrag_key_misses:%lld\r\n"
"tracking_used_slots:%lld\r\n", "tracking_total_keys:%lld\r\n"
"tracking_total_items:%lld\r\n",
server.stat_numconnections, server.stat_numconnections,
server.stat_numcommands, server.stat_numcommands,
getInstantaneousMetric(STATS_METRIC_COMMAND), getInstantaneousMetric(STATS_METRIC_COMMAND),
...@@ -4249,7 +4257,8 @@ sds genRedisInfoString(const char *section) { ...@@ -4249,7 +4257,8 @@ sds genRedisInfoString(const char *section) {
server.stat_active_defrag_misses, server.stat_active_defrag_misses,
server.stat_active_defrag_key_hits, server.stat_active_defrag_key_hits,
server.stat_active_defrag_key_misses, server.stat_active_defrag_key_misses,
trackingGetUsedSlots()); (unsigned long long) trackingGetTotalKeys(),
(unsigned long long) trackingGetTotalItems());
} }
/* Replication */ /* Replication */
......
...@@ -247,6 +247,7 @@ typedef long long ustime_t; /* microsecond time type. */ ...@@ -247,6 +247,7 @@ typedef long long ustime_t; /* microsecond time type. */
#define CLIENT_TRACKING (1ULL<<31) /* Client enabled keys tracking in order to #define CLIENT_TRACKING (1ULL<<31) /* Client enabled keys tracking in order to
perform client side caching. */ perform client side caching. */
#define CLIENT_TRACKING_BROKEN_REDIR (1ULL<<32) /* Target client is invalid. */ #define CLIENT_TRACKING_BROKEN_REDIR (1ULL<<32) /* Target client is invalid. */
#define CLIENT_TRACKING_BCAST (1ULL<<33) /* Tracking in BCAST mode. */
/* Client block type (btype field in client structure) /* Client block type (btype field in client structure)
* if CLIENT_BLOCKED flag is set. */ * if CLIENT_BLOCKED flag is set. */
...@@ -822,7 +823,9 @@ typedef struct client { ...@@ -822,7 +823,9 @@ typedef struct client {
* invalidation messages for keys fetched by this client will be send to * invalidation messages for keys fetched by this client will be send to
* the specified client ID. */ * the specified client ID. */
uint64_t client_tracking_redirection; uint64_t client_tracking_redirection;
rax *client_tracking_prefixes; /* A dictionary of prefixes we are already
subscribed to in BCAST mode, in the
context of client side caching. */
/* Response buffer */ /* Response buffer */
int bufpos; int bufpos;
char buf[PROTO_REPLY_CHUNK_BYTES]; char buf[PROTO_REPLY_CHUNK_BYTES];
...@@ -1306,7 +1309,7 @@ struct redisServer { ...@@ -1306,7 +1309,7 @@ struct redisServer {
list *ready_keys; /* List of readyList structures for BLPOP & co */ list *ready_keys; /* List of readyList structures for BLPOP & co */
/* Client side caching. */ /* Client side caching. */
unsigned int tracking_clients; /* # of clients with tracking enabled.*/ unsigned int tracking_clients; /* # of clients with tracking enabled.*/
int tracking_table_max_fill; /* Max fill percentage. */ int tracking_table_max_keys; /* Max number of keys in tracking table. */
/* Sort parameters - qsort_r() is only available under BSD so we /* Sort parameters - qsort_r() is only available under BSD so we
* have to take this state global, in order to pass it to sortCompare() */ * have to take this state global, in order to pass it to sortCompare() */
int sort_desc; int sort_desc;
...@@ -1648,13 +1651,15 @@ void addReplyStatusFormat(client *c, const char *fmt, ...); ...@@ -1648,13 +1651,15 @@ void addReplyStatusFormat(client *c, const char *fmt, ...);
#endif #endif
/* Client side caching (tracking mode) */ /* Client side caching (tracking mode) */
void enableTracking(client *c, uint64_t redirect_to); void enableTracking(client *c, uint64_t redirect_to, int bcast, robj **prefix, size_t numprefix);
void disableTracking(client *c); void disableTracking(client *c);
void trackingRememberKeys(client *c); void trackingRememberKeys(client *c);
void trackingInvalidateKey(robj *keyobj); void trackingInvalidateKey(robj *keyobj);
void trackingInvalidateKeysOnFlush(int dbid); void trackingInvalidateKeysOnFlush(int dbid);
void trackingLimitUsedSlots(void); void trackingLimitUsedSlots(void);
unsigned long long trackingGetUsedSlots(void); uint64_t trackingGetTotalItems(void);
uint64_t trackingGetTotalKeys(void);
void trackingBroadcastInvalidationMessages(void);
/* List data type */ /* List data type */
void listTypeTryConversion(robj *subject, robj *value); void listTypeTryConversion(robj *subject, robj *value);
......
This diff is collapsed.
start_server {tags {"tracking"}} {
# Create a deferred client we'll use to redirect invalidation
# messages to.
set rd1 [redis_deferring_client]
$rd1 client id
set redir [$rd1 read]
$rd1 subscribe __redis__:invalidate
$rd1 read ; # Consume the SUBSCRIBE reply.
test {Clients are able to enable tracking and redirect it} {
r CLIENT TRACKING on REDIRECT $redir
} {*OK}
test {The other connection is able to get invalidations} {
r SET a 1
r GET a
r INCR a
r INCR b ; # This key should not be notified, since it wasn't fetched.
set keys [lindex [$rd1 read] 2]
assert {[llength $keys] == 1}
assert {[lindex $keys 0] eq {a}}
}
test {The client is now able to disable tracking} {
# Make sure to add a few more keys in the tracking list
# so that we can check for leaks, as a side effect.
r MGET a b c d e f g
r CLIENT TRACKING off
}
test {Clients can enable the BCAST mode with the empty prefix} {
r CLIENT TRACKING on BCAST REDIRECT $redir
} {*OK*}
test {The connection gets invalidation messages about all the keys} {
r MSET a 1 b 2 c 3
set keys [lsort [lindex [$rd1 read] 2]]
assert {$keys eq {a b c}}
}
test {Clients can enable the BCAST mode with prefixes} {
r CLIENT TRACKING off
r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX a: PREFIX b:
r MULTI
r INCR a:1
r INCR a:2
r INCR b:1
r INCR b:2
r EXEC
# Because of the internals, we know we are going to receive
# two separated notifications for the two different prefixes.
set keys1 [lsort [lindex [$rd1 read] 2]]
set keys2 [lsort [lindex [$rd1 read] 2]]
set keys [lsort [list {*}$keys1 {*}$keys2]]
assert {$keys eq {a:1 a:2 b:1 b:2}}
}
test {Adding prefixes to BCAST mode works} {
r CLIENT TRACKING on BCAST REDIRECT $redir PREFIX c:
r INCR c:1234
set keys [lsort [lindex [$rd1 read] 2]]
assert {$keys eq {c:1234}}
}
$rd1 close
}
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