Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8995634014 | ||
|
|
9a8ae5a553 | ||
|
|
0ff078d8d0 | ||
|
|
25ba2e9607 | ||
|
|
563d6b3f98 | ||
|
|
b6610a569d | ||
|
|
26cf5c8ac6 | ||
|
|
303cc97ff9 | ||
|
|
75bf5a4a4a | ||
|
|
8d0083ba25 | ||
|
|
fba0b23e72 | ||
|
|
dceaca1f69 | ||
|
|
bf307cfb4d | ||
|
|
4f9d30b33b | ||
|
|
b67f39da09 | ||
|
|
4c53178c6c | ||
|
|
7f6743a581 | ||
|
|
edae78999c | ||
|
|
3d7263aa41 | ||
|
|
333453646c | ||
|
|
4d650a3baa | ||
|
|
dd0ac4ac72 | ||
|
|
75347ada7f | ||
|
|
83333b08d0 | ||
|
|
50d140e90b | ||
|
|
2eb8a46061 | ||
|
|
a46f841df3 | ||
|
|
e5c577e679 | ||
|
|
dd239c37db | ||
|
|
c61cc761d5 | ||
|
|
d13635b2a9 | ||
|
|
d240202261 | ||
|
|
e83741746c | ||
|
|
7d30b3121d |
@@ -14,6 +14,42 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
|
||||
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--[ Redis 2.8.3 ] Release date: 11 Dec 2013
|
||||
|
||||
# UPGRADE URGENCY: MODERATE for Redis, HIGH for Sentinel.
|
||||
|
||||
* [FIX] Sentinel instance role sampling fixed, the system is now more
|
||||
reliable during failover and when reconfiguring instances with
|
||||
non matching configuration.
|
||||
* [FIX] Inline requests are now handled even when terminated with just LF.
|
||||
* [FIX] Replication timeout handling greatly improved, now the slave is able
|
||||
to ping the master while removing the old data from memory, and while
|
||||
loading the new RDB file. This avoid false timeouts sensed by
|
||||
masters.
|
||||
* [FIX] Fixed a replication bug involving 32 bit instances and big datasets
|
||||
hard to compress that resulted into more than 2GB of RDB file sent.
|
||||
* [FIX] Return error for inline requests with unbalanced quotes.
|
||||
* [FIX] Publish the slave replication offset even when disconnected from the
|
||||
master if there is still a cached master instance.
|
||||
|
||||
--[ Redis 2.8.2 ] Release date: 2 Dec 2013
|
||||
|
||||
# UPGRADE URGENCY: MODERATE for both Redis and Sentinel.
|
||||
|
||||
* [FIX] Sentinel better desynchronization to avoid split-brain elections
|
||||
where no Sentinel managed to get elected.
|
||||
* [FIX] Stop accepting writes on "MISCONF" error only if master, not slave.
|
||||
* [FIX] Reply to PING with an error on "MISCONF" errors.
|
||||
|
||||
--[ Redis 2.8.1 ] Release date: 25 Nov 2013
|
||||
|
||||
# UPGRADE URGENCY: LOW for Redis, CRITICAL for Senitnel. You don't need to
|
||||
upgrade your Redis instances but it is highly recommended
|
||||
to upgrade and restart all the Sentinel processes.
|
||||
|
||||
* [FIX] Fixed a bug in "new Sentinel" config propagation.
|
||||
* [FIX] Fixed a false positive in Redis tests.
|
||||
|
||||
--[ Redis 2.8.0 ] Release date: 22 Nov 2013
|
||||
|
||||
# UPGRADE URGENCY: LOW, unless you want to upgrade to new Sentinel code.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ each source file that you contribute.
|
||||
|
||||
1. Drop a message to the Redis Google Group with a proposal of semantics/API.
|
||||
|
||||
2. If in steps 1 you get an acknowledge from the project leaders, use the
|
||||
2. If in step 1 you get an acknowledge from the project leaders, use the
|
||||
following procedure to submit a patch:
|
||||
|
||||
a. Fork Redis on github ( http://help.github.com/fork-a-repo/ )
|
||||
|
||||
+11
-11
@@ -34,7 +34,7 @@ port 6379
|
||||
# bind 192.168.1.100 10.0.0.1
|
||||
# bind 127.0.0.1
|
||||
|
||||
# Specify the path for the unix socket that will be used to listen for
|
||||
# Specify the path for the Unix socket that will be used to listen for
|
||||
# incoming connections. There is no default, so Redis will not listen
|
||||
# on a unix socket when not specified.
|
||||
#
|
||||
@@ -68,7 +68,7 @@ tcp-keepalive 0
|
||||
# warning (only very important / critical messages are logged)
|
||||
loglevel notice
|
||||
|
||||
# Specify the log file name. Also the emptry string can be used to force
|
||||
# Specify the log file name. Also the empty string can be used to force
|
||||
# Redis to log on the standard output. Note that if you use standard
|
||||
# output for logging but daemonize, logs will be sent to /dev/null
|
||||
logfile ""
|
||||
@@ -116,16 +116,16 @@ save 60 10000
|
||||
|
||||
# By default Redis will stop accepting writes if RDB snapshots are enabled
|
||||
# (at least one save point) and the latest background save failed.
|
||||
# This will make the user aware (in an hard way) that data is not persisting
|
||||
# This will make the user aware (in a hard way) that data is not persisting
|
||||
# on disk properly, otherwise chances are that no one will notice and some
|
||||
# distater will happen.
|
||||
# disaster will happen.
|
||||
#
|
||||
# If the background saving process will start working again Redis will
|
||||
# automatically allow writes again.
|
||||
#
|
||||
# However if you have setup your proper monitoring of the Redis server
|
||||
# and persistence, you may want to disable this feature so that Redis will
|
||||
# continue to work as usually even if there are problems with disk,
|
||||
# continue to work as usual even if there are problems with disk,
|
||||
# permissions, and so forth.
|
||||
stop-writes-on-bgsave-error yes
|
||||
|
||||
@@ -197,7 +197,7 @@ slave-serve-stale-data yes
|
||||
# Note: read only slaves are not designed to be exposed to untrusted clients
|
||||
# on the internet. It's just a protection layer against misuse of the instance.
|
||||
# Still a read only slave exports by default all the administrative commands
|
||||
# such as CONFIG, DEBUG, and so forth. To a limited extend you can improve
|
||||
# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
|
||||
# security of read only slaves using 'rename-command' to shadow all the
|
||||
# administrative / dangerous commands.
|
||||
slave-read-only yes
|
||||
@@ -263,7 +263,7 @@ repl-disable-tcp-nodelay no
|
||||
#
|
||||
# A slave with a low priority number is considered better for promotion, so
|
||||
# for instance if there are three slaves with priority 10, 100, 25 Sentinel will
|
||||
# pick the one wtih priority 10, that is the lowest.
|
||||
# pick the one with priority 10, that is the lowest.
|
||||
#
|
||||
# However a special priority of 0 marks the slave as not able to perform the
|
||||
# role of master, so a slave with priority of 0 will never be selected by
|
||||
@@ -343,7 +343,7 @@ slave-priority 100
|
||||
|
||||
# Don't use more memory than the specified amount of bytes.
|
||||
# When the memory limit is reached Redis will try to remove keys
|
||||
# accordingly to the eviction policy selected (see maxmemmory-policy).
|
||||
# according to the eviction policy selected (see maxmemory-policy).
|
||||
#
|
||||
# If Redis can't remove keys according to the policy, or if the policy is
|
||||
# set to 'noeviction', Redis will start to reply with errors to commands
|
||||
@@ -351,7 +351,7 @@ slave-priority 100
|
||||
# to reply to read-only commands like GET.
|
||||
#
|
||||
# This option is usually useful when using Redis as an LRU cache, or to set
|
||||
# an hard memory limit for an instance (using the 'noeviction' policy).
|
||||
# a hard memory limit for an instance (using the 'noeviction' policy).
|
||||
#
|
||||
# WARNING: If you have slaves attached to an instance with maxmemory on,
|
||||
# the size of the output buffers needed to feed the slaves are subtracted
|
||||
@@ -607,7 +607,7 @@ zset-max-ziplist-value 64
|
||||
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
|
||||
# order to help rehashing the main Redis hash table (the one mapping top-level
|
||||
# keys to values). The hash table implementation Redis uses (see dict.c)
|
||||
# performs a lazy rehashing: the more operation you run into an hash table
|
||||
# performs a lazy rehashing: the more operation you run into a hash table
|
||||
# that is rehashing, the more rehashing "steps" are performed, so if the
|
||||
# server is idle the rehashing is never complete and some more memory is used
|
||||
# by the hash table.
|
||||
@@ -662,7 +662,7 @@ client-output-buffer-limit slave 256mb 64mb 60
|
||||
client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
|
||||
# Redis calls an internal function to perform many background tasks, like
|
||||
# closing connections of clients in timeot, purging expired keys that are
|
||||
# closing connections of clients in timeout, purging expired keys that are
|
||||
# never requested, and so forth.
|
||||
#
|
||||
# Not all tasks are performed with the same frequency, but Redis checks for
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
zfree(state);
|
||||
return -1;
|
||||
}
|
||||
state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */
|
||||
state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
|
||||
if (state->epfd == -1) {
|
||||
zfree(state->events);
|
||||
zfree(state);
|
||||
|
||||
@@ -771,7 +771,7 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write either the key or the value of the currently selected item of an hash.
|
||||
/* Write either the key or the value of the currently selected item of a hash.
|
||||
* The 'hi' argument passes a valid Redis hash iterator.
|
||||
* The 'what' filed specifies if to write a key or a value and can be
|
||||
* either REDIS_HASH_KEY or REDIS_HASH_VALUE.
|
||||
|
||||
@@ -166,14 +166,14 @@ int dbDelete(redisDb *db, robj *key) {
|
||||
}
|
||||
}
|
||||
|
||||
long long emptyDb() {
|
||||
long long emptyDb(void(callback)(void*)) {
|
||||
int j;
|
||||
long long removed = 0;
|
||||
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
removed += dictSize(server.db[j].dict);
|
||||
dictEmpty(server.db[j].dict);
|
||||
dictEmpty(server.db[j].expires);
|
||||
dictEmpty(server.db[j].dict,callback);
|
||||
dictEmpty(server.db[j].expires,callback);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
@@ -209,14 +209,14 @@ void signalFlushedDb(int dbid) {
|
||||
void flushdbCommand(redisClient *c) {
|
||||
server.dirty += dictSize(c->db->dict);
|
||||
signalFlushedDb(c->db->id);
|
||||
dictEmpty(c->db->dict);
|
||||
dictEmpty(c->db->expires);
|
||||
dictEmpty(c->db->dict,NULL);
|
||||
dictEmpty(c->db->expires,NULL);
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
|
||||
void flushallCommand(redisClient *c) {
|
||||
signalFlushedDb(-1);
|
||||
server.dirty += emptyDb();
|
||||
server.dirty += emptyDb(NULL);
|
||||
addReply(c,shared.ok);
|
||||
if (server.rdb_child_pid != -1) {
|
||||
kill(server.rdb_child_pid,SIGUSR1);
|
||||
@@ -360,7 +360,7 @@ int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor) {
|
||||
}
|
||||
|
||||
/* This command implements SCAN, HSCAN and SSCAN commands.
|
||||
* If object 'o' is passed, then it must be an Hash or Set object, otherwise
|
||||
* If object 'o' is passed, then it must be a Hash or Set object, otherwise
|
||||
* if 'o' is NULL the command will operate on the dictionary associated with
|
||||
* the current database.
|
||||
*
|
||||
@@ -368,7 +368,7 @@ int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor) {
|
||||
* the client arguments vector is a key so it skips it before iterating
|
||||
* in order to parse options.
|
||||
*
|
||||
* In the case of an Hash object the function returns both the field and value
|
||||
* In the case of a Hash object the function returns both the field and value
|
||||
* of every element on the Hash. */
|
||||
void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
|
||||
int rv;
|
||||
@@ -423,12 +423,12 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
|
||||
/* Step 2: Iterate the collection.
|
||||
*
|
||||
* Note that if the object is encoded with a ziplist, intset, or any other
|
||||
* representation that is not an hash table, we are sure that it is also
|
||||
* representation that is not a hash table, we are sure that it is also
|
||||
* composed of a small number of elements. So to avoid taking state we
|
||||
* just return everything inside the object in a single call, setting the
|
||||
* cursor to zero to signal the end of the iteration. */
|
||||
|
||||
/* Handle the case of an hash table. */
|
||||
/* Handle the case of a hash table. */
|
||||
ht = NULL;
|
||||
if (o == NULL) {
|
||||
ht = c->db->dict;
|
||||
@@ -510,7 +510,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
|
||||
listDelNode(keys, node);
|
||||
}
|
||||
|
||||
/* If this is an hash or a sorted set, we have a flat list of
|
||||
/* If this is a hash or a sorted set, we have a flat list of
|
||||
* key-value elements, so if this element was filtered, remove the
|
||||
* value, or skip it if it was not filtered: we only match keys. */
|
||||
if (o && (o->type == REDIS_ZSET || o->type == REDIS_HASH)) {
|
||||
|
||||
+2
-2
@@ -261,7 +261,7 @@ void debugCommand(redisClient *c) {
|
||||
addReply(c,shared.err);
|
||||
return;
|
||||
}
|
||||
emptyDb();
|
||||
emptyDb(NULL);
|
||||
if (rdbLoad(server.rdb_filename) != REDIS_OK) {
|
||||
addReplyError(c,"Error trying to load the RDB dump");
|
||||
return;
|
||||
@@ -269,7 +269,7 @@ void debugCommand(redisClient *c) {
|
||||
redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) {
|
||||
emptyDb();
|
||||
emptyDb(NULL);
|
||||
if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) {
|
||||
addReply(c,shared.err);
|
||||
return;
|
||||
|
||||
+10
-9
@@ -53,7 +53,7 @@
|
||||
* around when there is a child performing saving operations.
|
||||
*
|
||||
* Note that even when dict_can_resize is set to 0, not all resizes are
|
||||
* prevented: an hash table is still allowed to grow if the ratio between
|
||||
* prevented: a hash table is still allowed to grow if the ratio between
|
||||
* the number of elements and the buckets > dict_force_resize_ratio. */
|
||||
static int dict_can_resize = 1;
|
||||
static unsigned int dict_force_resize_ratio = 5;
|
||||
@@ -444,14 +444,15 @@ int dictDeleteNoFree(dict *ht, const void *key) {
|
||||
}
|
||||
|
||||
/* Destroy an entire dictionary */
|
||||
int _dictClear(dict *d, dictht *ht)
|
||||
{
|
||||
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
|
||||
unsigned long i;
|
||||
|
||||
/* Free all the elements */
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if (callback && (i & 65535) == 0) callback(d->privdata);
|
||||
|
||||
if ((he = ht->table[i]) == NULL) continue;
|
||||
while(he) {
|
||||
nextHe = he->next;
|
||||
@@ -472,8 +473,8 @@ int _dictClear(dict *d, dictht *ht)
|
||||
/* Clear & Release the hash table */
|
||||
void dictRelease(dict *d)
|
||||
{
|
||||
_dictClear(d,&d->ht[0]);
|
||||
_dictClear(d,&d->ht[1]);
|
||||
_dictClear(d,&d->ht[0],NULL);
|
||||
_dictClear(d,&d->ht[1],NULL);
|
||||
zfree(d);
|
||||
}
|
||||
|
||||
@@ -853,7 +854,7 @@ static unsigned long _dictNextPower(unsigned long size)
|
||||
}
|
||||
|
||||
/* Returns the index of a free slot that can be populated with
|
||||
* an hash entry for the given 'key'.
|
||||
* a hash entry for the given 'key'.
|
||||
* If the key already exists, -1 is returned.
|
||||
*
|
||||
* Note that if we are in the process of rehashing the hash table, the
|
||||
@@ -882,9 +883,9 @@ static int _dictKeyIndex(dict *d, const void *key)
|
||||
return idx;
|
||||
}
|
||||
|
||||
void dictEmpty(dict *d) {
|
||||
_dictClear(d,&d->ht[0]);
|
||||
_dictClear(d,&d->ht[1]);
|
||||
void dictEmpty(dict *d, void(callback)(void*)) {
|
||||
_dictClear(d,&d->ht[0],callback);
|
||||
_dictClear(d,&d->ht[1],callback);
|
||||
d->rehashidx = -1;
|
||||
d->iterators = 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@ dictEntry *dictGetRandomKey(dict *d);
|
||||
void dictPrintStats(dict *d);
|
||||
unsigned int dictGenHashFunction(const void *key, int len);
|
||||
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);
|
||||
void dictEmpty(dict *d);
|
||||
void dictEmpty(dict *d, void(callback)(void*));
|
||||
void dictEnableResize(void);
|
||||
void dictDisableResize(void);
|
||||
int dictRehash(dict *d, int n);
|
||||
|
||||
+35
-15
@@ -100,9 +100,7 @@ redisClient *createClient(int fd) {
|
||||
c->bpop.keys = dictCreate(&setDictType,NULL);
|
||||
c->bpop.timeout = 0;
|
||||
c->bpop.target = NULL;
|
||||
c->io_keys = listCreate();
|
||||
c->watched_keys = listCreate();
|
||||
listSetFreeMethod(c->io_keys,decrRefCountVoid);
|
||||
c->pubsub_channels = dictCreate(&setDictType,NULL);
|
||||
c->pubsub_patterns = listCreate();
|
||||
listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);
|
||||
@@ -650,13 +648,11 @@ void freeClient(redisClient *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note that if the client we are freeing is blocked into a blocking
|
||||
* call, we have to set querybuf to NULL *before* to call
|
||||
* unblockClientWaitingData() to avoid processInputBuffer() will get
|
||||
* called. Also it is important to remove the file events after
|
||||
* this, because this call adds the READABLE event. */
|
||||
/* Free the query buffer */
|
||||
sdsfree(c->querybuf);
|
||||
c->querybuf = NULL;
|
||||
|
||||
/* Deallocate structures used to block on blocking ops. */
|
||||
if (c->flags & REDIS_BLOCKED)
|
||||
unblockClientWaitingData(c);
|
||||
dictRelease(c->bpop.keys);
|
||||
@@ -664,11 +660,13 @@ void freeClient(redisClient *c) {
|
||||
/* UNWATCH all the keys */
|
||||
unwatchAllKeys(c);
|
||||
listRelease(c->watched_keys);
|
||||
|
||||
/* Unsubscribe from all the pubsub channels */
|
||||
pubsubUnsubscribeAllChannels(c,0);
|
||||
pubsubUnsubscribeAllPatterns(c,0);
|
||||
dictRelease(c->pubsub_channels);
|
||||
listRelease(c->pubsub_patterns);
|
||||
|
||||
/* Close socket, unregister events, and remove list of replies and
|
||||
* accumulated arguments. */
|
||||
if (c->fd != -1) {
|
||||
@@ -678,22 +676,24 @@ void freeClient(redisClient *c) {
|
||||
}
|
||||
listRelease(c->reply);
|
||||
freeClientArgv(c);
|
||||
|
||||
/* Remove from the list of clients */
|
||||
if (c->fd != -1) {
|
||||
ln = listSearchKey(server.clients,c);
|
||||
redisAssert(ln != NULL);
|
||||
listDelNode(server.clients,ln);
|
||||
}
|
||||
|
||||
/* When client was just unblocked because of a blocking operation,
|
||||
* remove it from the list with unblocked clients. */
|
||||
* remove it from the list of unblocked clients. */
|
||||
if (c->flags & REDIS_UNBLOCKED) {
|
||||
ln = listSearchKey(server.unblocked_clients,c);
|
||||
redisAssert(ln != NULL);
|
||||
listDelNode(server.unblocked_clients,ln);
|
||||
}
|
||||
listRelease(c->io_keys);
|
||||
/* Master/slave cleanup.
|
||||
* Case 1: we lost the connection with a slave. */
|
||||
|
||||
/* Master/slave cleanup Case 1:
|
||||
* we lost the connection with a slave. */
|
||||
if (c->flags & REDIS_SLAVE) {
|
||||
if (c->replstate == REDIS_REPL_SEND_BULK && c->repldbfd != -1)
|
||||
close(c->repldbfd);
|
||||
@@ -709,7 +709,8 @@ void freeClient(redisClient *c) {
|
||||
refreshGoodSlavesCount();
|
||||
}
|
||||
|
||||
/* Case 2: we lost the connection with the master. */
|
||||
/* Master/slave cleanup Case 2:
|
||||
* we lost the connection with the master. */
|
||||
if (c->flags & REDIS_MASTER) replicationHandleMasterDisconnection();
|
||||
|
||||
/* If this client was scheduled for async freeing we need to remove it
|
||||
@@ -720,7 +721,8 @@ void freeClient(redisClient *c) {
|
||||
listDelNode(server.clients_to_close,ln);
|
||||
}
|
||||
|
||||
/* Release memory */
|
||||
/* Release other dynamically allocated client structure fields,
|
||||
* and finally release the client structure itself. */
|
||||
if (c->name) decrRefCount(c->name);
|
||||
zfree(c->argv);
|
||||
freeClientMultiState(c);
|
||||
@@ -840,11 +842,14 @@ void resetClient(redisClient *c) {
|
||||
}
|
||||
|
||||
int processInlineBuffer(redisClient *c) {
|
||||
char *newline = strstr(c->querybuf,"\r\n");
|
||||
char *newline;
|
||||
int argc, j;
|
||||
sds *argv, aux;
|
||||
size_t querylen;
|
||||
|
||||
/* Search for end of line */
|
||||
newline = strchr(c->querybuf,'\n');
|
||||
|
||||
/* Nothing to do without a \r\n */
|
||||
if (newline == NULL) {
|
||||
if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
|
||||
@@ -854,11 +859,26 @@ int processInlineBuffer(redisClient *c) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Handle the \r\n case. */
|
||||
if (newline && newline != c->querybuf && *(newline-1) == '\r')
|
||||
newline--;
|
||||
|
||||
/* Split the input buffer up to the \r\n */
|
||||
querylen = newline-(c->querybuf);
|
||||
aux = sdsnewlen(c->querybuf,querylen);
|
||||
argv = sdssplitargs(aux,&argc);
|
||||
sdsfree(aux);
|
||||
if (argv == NULL) {
|
||||
addReplyError(c,"Protocol error: unbalanced quotes in request");
|
||||
setProtocolError(c,0);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Newline from slaves can be used to refresh the last ACK time.
|
||||
* This is useful for a slave to ping back while loading a big
|
||||
* RDB file. */
|
||||
if (querylen == 0 && c->flags & REDIS_SLAVE)
|
||||
c->repl_ack_time = server.unixtime;
|
||||
|
||||
/* Leave data after the first line of the query in the buffer */
|
||||
sdsrange(c->querybuf,querylen+2,-1);
|
||||
@@ -1148,7 +1168,7 @@ void getClientsMaxBuffers(unsigned long *longest_output_list,
|
||||
*biggest_input_buffer = bib;
|
||||
}
|
||||
|
||||
/* This is an helper function for getClientPeerId().
|
||||
/* This is a helper function for getClientPeerId().
|
||||
* It writes the specified ip/port to "peerid" as a null termiated string
|
||||
* in the form ip:port if ip does not contain ":" itself, otherwise
|
||||
* [ip]:port format is used (for IPv6 addresses basically). */
|
||||
|
||||
+1
-1
@@ -587,7 +587,7 @@ unsigned long estimateObjectIdleTime(robj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
/* This is an helper function for the DEBUG command. We need to lookup keys
|
||||
/* This is a helper function for the DEBUG command. We need to lookup keys
|
||||
* without any modification of LRU or other parameters. */
|
||||
robj *objectCommandLookup(redisClient *c, robj *key) {
|
||||
dictEntry *de;
|
||||
|
||||
@@ -892,7 +892,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
|
||||
|
||||
o = createHashObject();
|
||||
|
||||
/* Too many entries? Use an hash table. */
|
||||
/* Too many entries? Use a hash table. */
|
||||
if (len > server.hash_max_ziplist_entries)
|
||||
hashTypeConvert(o, REDIS_ENCODING_HT);
|
||||
|
||||
@@ -1064,7 +1064,10 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
|
||||
if (server.rdb_checksum)
|
||||
rioGenericUpdateChecksum(r, buf, len);
|
||||
if (server.loading_process_events_interval_bytes &&
|
||||
(r->processed_bytes + len)/server.loading_process_events_interval_bytes > r->processed_bytes/server.loading_process_events_interval_bytes) {
|
||||
(r->processed_bytes + len)/server.loading_process_events_interval_bytes > r->processed_bytes/server.loading_process_events_interval_bytes)
|
||||
{
|
||||
if (server.masterhost && server.repl_state == REDIS_REPL_TRANSFER)
|
||||
replicationSendNewlineToMaster();
|
||||
loadingProgress(r->processed_bytes);
|
||||
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
#define REDIS_ENCODING_RAW 0 /* Raw representation */
|
||||
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
|
||||
#define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
|
||||
#define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
|
||||
#define REDIS_ENCODING_HT 3 /* Encoded as a hash table */
|
||||
|
||||
/* Object types only used for dumping to disk */
|
||||
#define REDIS_EXPIRETIME_MS 252
|
||||
|
||||
+15
-5
@@ -370,7 +370,7 @@ void exitFromChild(int retcode) {
|
||||
|
||||
/*====================== Hash table type implementation ==================== */
|
||||
|
||||
/* This is an hash table type that uses the SDS dynamic strings library as
|
||||
/* This is a hash table type that uses the SDS dynamic strings library as
|
||||
* keys and radis objects as values (objects can hold SDS strings,
|
||||
* lists, sets). */
|
||||
|
||||
@@ -1918,11 +1918,14 @@ int processCommand(redisClient *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't accept write commands if there are problems persisting on disk. */
|
||||
/* Don't accept write commands if there are problems persisting on disk
|
||||
* and if this is a master instance. */
|
||||
if (server.stop_writes_on_bgsave_err &&
|
||||
server.saveparamslen > 0
|
||||
&& server.lastbgsave_status == REDIS_ERR &&
|
||||
c->cmd->flags & REDIS_CMD_WRITE)
|
||||
server.masterhost != NULL &&
|
||||
(c->cmd->flags & REDIS_CMD_WRITE ||
|
||||
c->cmd->proc == pingCommand))
|
||||
{
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.bgsaveerr);
|
||||
@@ -1930,7 +1933,7 @@ int processCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
/* Don't accept write commands if there are not enough good slaves and
|
||||
* used configured the min-slaves-to-write option. */
|
||||
* user configured the min-slaves-to-write option. */
|
||||
if (server.repl_min_slaves_to_write &&
|
||||
server.repl_min_slaves_max_lag &&
|
||||
c->cmd->flags & REDIS_CMD_WRITE &&
|
||||
@@ -2413,6 +2416,13 @@ sds genRedisInfoString(char *section) {
|
||||
"role:%s\r\n",
|
||||
server.masterhost == NULL ? "master" : "slave");
|
||||
if (server.masterhost) {
|
||||
long long slave_repl_offset = 1;
|
||||
|
||||
if (server.master)
|
||||
slave_repl_offset = server.master->reploff;
|
||||
else if (server.cached_master)
|
||||
slave_repl_offset = server.cached_master->reploff;
|
||||
|
||||
info = sdscatprintf(info,
|
||||
"master_host:%s\r\n"
|
||||
"master_port:%d\r\n"
|
||||
@@ -2427,7 +2437,7 @@ sds genRedisInfoString(char *section) {
|
||||
server.master ?
|
||||
((int)(server.unixtime-server.master->lastinteraction)) : -1,
|
||||
server.repl_state == REDIS_REPL_TRANSFER,
|
||||
server.master ? server.master->reploff : -1
|
||||
slave_repl_offset
|
||||
);
|
||||
|
||||
if (server.repl_state == REDIS_REPL_TRANSFER) {
|
||||
|
||||
+5
-4
@@ -466,7 +466,7 @@ typedef struct redisClient {
|
||||
int authenticated; /* when requirepass is non-NULL */
|
||||
int replstate; /* replication state if this is a slave */
|
||||
int repldbfd; /* replication DB file descriptor */
|
||||
long repldboff; /* replication DB file offset */
|
||||
off_t repldboff; /* replication DB file offset */
|
||||
off_t repldbsize; /* replication DB file size */
|
||||
long long reploff; /* replication offset if this is our master */
|
||||
long long repl_ack_off; /* replication ack offset, if this is a slave */
|
||||
@@ -475,8 +475,6 @@ typedef struct redisClient {
|
||||
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
|
||||
multiState mstate; /* MULTI/EXEC state */
|
||||
blockingState bpop; /* blocking state */
|
||||
list *io_keys; /* Keys this client is waiting to be loaded from the
|
||||
* swap file in order to continue. */
|
||||
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
|
||||
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
|
||||
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
|
||||
@@ -1025,6 +1023,9 @@ void replicationScriptCacheInit(void);
|
||||
void replicationScriptCacheFlush(void);
|
||||
void replicationScriptCacheAdd(sds sha1);
|
||||
int replicationScriptCacheExists(sds sha1);
|
||||
void replicationSetMaster(char *ip, int port);
|
||||
void replicationUnsetMaster(void);
|
||||
void replicationSendNewlineToMaster(void);
|
||||
|
||||
/* Generic persistence functions */
|
||||
void startLoading(FILE *fp);
|
||||
@@ -1165,7 +1166,7 @@ void setKey(redisDb *db, robj *key, robj *val);
|
||||
int dbExists(redisDb *db, robj *key);
|
||||
robj *dbRandomKey(redisDb *db);
|
||||
int dbDelete(redisDb *db, robj *key);
|
||||
long long emptyDb();
|
||||
long long emptyDb(void(callback)(void*));
|
||||
int selectDb(redisClient *c, int id);
|
||||
void signalModifiedKey(redisDb *db, robj *key);
|
||||
void signalFlushedDb(int dbid);
|
||||
|
||||
+56
-19
@@ -701,6 +701,31 @@ void replicationAbortSyncTransfer(void) {
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
}
|
||||
|
||||
/* Avoid the master to detect the slave is timing out while loading the
|
||||
* RDB file in initial synchronization. We send a single newline character
|
||||
* that is valid protocol but is guaranteed to either be sent entierly or
|
||||
* not, since the byte is indivisible.
|
||||
*
|
||||
* The function is called in two contexts: while we flush the current
|
||||
* data with emptyDb(), and while we load the new data received as an
|
||||
* RDB file from the master. */
|
||||
void replicationSendNewlineToMaster(void) {
|
||||
static time_t newline_sent;
|
||||
if (time(NULL) != newline_sent) {
|
||||
newline_sent = time(NULL);
|
||||
if (write(server.repl_transfer_s,"\n",1) == -1) {
|
||||
/* Pinging back in this stage is best-effort. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback used by emptyDb() while flushing away old data to load
|
||||
* the new dataset received by the master. */
|
||||
void replicationEmptyDbCallback(void *privdata) {
|
||||
REDIS_NOTUSED(privdata);
|
||||
replicationSendNewlineToMaster();
|
||||
}
|
||||
|
||||
/* Asynchronously read the SYNC payload we receive from a master */
|
||||
#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */
|
||||
void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
@@ -780,14 +805,15 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
replicationAbortSyncTransfer();
|
||||
return;
|
||||
}
|
||||
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
|
||||
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Flushing old data");
|
||||
signalFlushedDb(-1);
|
||||
emptyDb();
|
||||
emptyDb(replicationEmptyDbCallback);
|
||||
/* Before loading the DB into memory we need to delete the readable
|
||||
* handler, otherwise it will get called recursively since
|
||||
* rdbLoad() will call the event loop to process events from time to
|
||||
* time for non blocking loading. */
|
||||
aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);
|
||||
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
|
||||
if (rdbLoad(server.rdb_filename) != REDIS_OK) {
|
||||
redisLog(REDIS_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
|
||||
replicationAbortSyncTransfer();
|
||||
@@ -1205,16 +1231,35 @@ int cancelReplicationHandshake(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set replication to the specified master address and port. */
|
||||
void replicationSetMaster(char *ip, int port) {
|
||||
sdsfree(server.masterhost);
|
||||
server.masterhost = sdsdup(ip);
|
||||
server.masterport = port;
|
||||
if (server.master) freeClient(server.master);
|
||||
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
||||
replicationDiscardCachedMaster(); /* Don't try a PSYNC. */
|
||||
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
}
|
||||
|
||||
/* Cancel replication, setting the instance as a master itself. */
|
||||
void replicationUnsetMaster(void) {
|
||||
if (server.masterhost == NULL) return; /* Nothing to do. */
|
||||
sdsfree(server.masterhost);
|
||||
server.masterhost = NULL;
|
||||
if (server.master) freeClient(server.master);
|
||||
replicationDiscardCachedMaster();
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_NONE;
|
||||
}
|
||||
|
||||
void slaveofCommand(redisClient *c) {
|
||||
if (!strcasecmp(c->argv[1]->ptr,"no") &&
|
||||
!strcasecmp(c->argv[2]->ptr,"one")) {
|
||||
if (server.masterhost) {
|
||||
sdsfree(server.masterhost);
|
||||
server.masterhost = NULL;
|
||||
if (server.master) freeClient(server.master);
|
||||
replicationDiscardCachedMaster();
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_NONE;
|
||||
replicationUnsetMaster();
|
||||
redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)");
|
||||
}
|
||||
} else {
|
||||
@@ -1232,15 +1277,7 @@ void slaveofCommand(redisClient *c) {
|
||||
}
|
||||
/* There was no previous master or the user specified a different one,
|
||||
* we can continue. */
|
||||
sdsfree(server.masterhost);
|
||||
server.masterhost = sdsdup(c->argv[1]->ptr);
|
||||
server.masterport = port;
|
||||
if (server.master) freeClient(server.master);
|
||||
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
||||
replicationDiscardCachedMaster(); /* Don't try a PSYNC. */
|
||||
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
replicationSetMaster(c->argv[1]->ptr, port);
|
||||
redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)",
|
||||
server.masterhost, server.masterport);
|
||||
}
|
||||
@@ -1388,7 +1425,7 @@ void refreshGoodSlavesCount(void) {
|
||||
* connected slave, in order to be able to replicate EVALSHA as it is without
|
||||
* translating it to EVAL every time it is possible.
|
||||
*
|
||||
* We use a capped collection implemented by an hash table for fast lookup
|
||||
* We use a capped collection implemented by a hash table for fast lookup
|
||||
* of scripts we can send as EVALSHA, plus a linked list that is used for
|
||||
* eviction of the oldest entry when the max number of items is reached.
|
||||
*
|
||||
@@ -1433,7 +1470,7 @@ void replicationScriptCacheInit(void) {
|
||||
* to reclaim otherwise unused memory.
|
||||
*/
|
||||
void replicationScriptCacheFlush(void) {
|
||||
dictEmpty(server.repl_scriptcache_dict);
|
||||
dictEmpty(server.repl_scriptcache_dict,NULL);
|
||||
listRelease(server.repl_scriptcache_fifo);
|
||||
server.repl_scriptcache_fifo = listCreate();
|
||||
}
|
||||
|
||||
+2
-2
@@ -895,7 +895,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
/* Select the right DB in the context of the Lua client */
|
||||
selectDb(server.lua_client,c->db->id);
|
||||
|
||||
/* Set an hook in order to be able to stop the script execution if it
|
||||
/* Set a hook in order to be able to stop the script execution if it
|
||||
* is running for too much time.
|
||||
* We set the hook only if the time limit is enabled as the hook will
|
||||
* make the Lua script execution slower. */
|
||||
@@ -1059,7 +1059,7 @@ void scriptCommand(redisClient *c) {
|
||||
if (server.lua_caller == NULL) {
|
||||
addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n"));
|
||||
} else if (server.lua_write_dirty) {
|
||||
addReplySds(c,sdsnew("-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command.\r\n"));
|
||||
addReplySds(c,sdsnew("-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.\r\n"));
|
||||
} else {
|
||||
server.lua_kill = 1;
|
||||
addReply(c,shared.ok);
|
||||
|
||||
@@ -582,7 +582,7 @@ int is_hex_digit(char c) {
|
||||
(c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
/* Helper function for sdssplitargs() that converts an hex digit into an
|
||||
/* Helper function for sdssplitargs() that converts a hex digit into an
|
||||
* integer from 0 to 15 */
|
||||
int hex_digit_to_int(char c) {
|
||||
switch(c) {
|
||||
|
||||
+46
-31
@@ -135,7 +135,7 @@ typedef struct sentinelRedisInstance {
|
||||
if the link is idle and must be reconnected. */
|
||||
mstime_t last_pub_time; /* Last time we sent hello via Pub/Sub. */
|
||||
mstime_t last_hello_time; /* Only used if SRI_SENTINEL is set. Last time
|
||||
we received an hello from this Sentinel
|
||||
we received a hello from this Sentinel
|
||||
via Pub/Sub. */
|
||||
mstime_t last_master_down_reply_time; /* Time of last reply to
|
||||
SENTINEL is-master-down command. */
|
||||
@@ -393,7 +393,7 @@ void initSentinel(void) {
|
||||
|
||||
/* Remove usual Redis commands from the command table, then just add
|
||||
* the SENTINEL command. */
|
||||
dictEmpty(server.commands);
|
||||
dictEmpty(server.commands,NULL);
|
||||
for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
|
||||
int retval;
|
||||
struct redisCommand *cmd = sentinelcmds+j;
|
||||
@@ -1133,6 +1133,8 @@ void sentinelResetMaster(sentinelRedisInstance *ri, int flags) {
|
||||
ri->slave_master_host = NULL;
|
||||
ri->last_avail_time = mstime();
|
||||
ri->last_pong_time = mstime();
|
||||
ri->role_reported_time = mstime();
|
||||
ri->role_reported = SRI_MASTER;
|
||||
if (flags & SENTINEL_GENERATE_EVENT)
|
||||
sentinelEvent(REDIS_WARNING,"+reset-master",ri,"%@");
|
||||
}
|
||||
@@ -1474,21 +1476,25 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
|
||||
* On failure the function logs a warning on the Redis log. */
|
||||
void sentinelFlushConfig(void) {
|
||||
int fd;
|
||||
int saved_hz = server.hz;
|
||||
|
||||
if (rewriteConfig(server.configfile) == -1) {
|
||||
server.hz = REDIS_DEFAULT_HZ;
|
||||
if (rewriteConfig(server.configfile) != -1) {
|
||||
/* Rewrite succeded, fsync it. */
|
||||
if ((fd = open(server.configfile,O_RDONLY)) != -1) {
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
redisLog(REDIS_WARNING,"WARNING: Senitnel was not able to save the new configuration on disk!!!: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if ((fd = open(server.configfile,O_RDONLY)) != -1) {
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
}
|
||||
server.hz = saved_hz;
|
||||
return;
|
||||
}
|
||||
|
||||
/* ====================== hiredis connection handling ======================= */
|
||||
|
||||
/* Completely disconnect an hiredis link from an instance. */
|
||||
/* Completely disconnect a hiredis link from an instance. */
|
||||
void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) {
|
||||
if (ri->cc == c) {
|
||||
ri->cc = NULL;
|
||||
@@ -1500,7 +1506,7 @@ void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) {
|
||||
redisAsyncFree(c);
|
||||
}
|
||||
|
||||
/* This function takes an hiredis context that is in an error condition
|
||||
/* This function takes a hiredis context that is in an error condition
|
||||
* and make sure to mark the instance as disconnected performing the
|
||||
* cleanup needed.
|
||||
*
|
||||
@@ -1609,9 +1615,8 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
|
||||
}
|
||||
}
|
||||
/* Clear the DISCONNECTED flags only if we have both the connections
|
||||
* (or just the commands connection if this is a slave or a
|
||||
* sentinel instance). */
|
||||
if (ri->cc && (ri->flags & (SRI_SLAVE|SRI_SENTINEL) || ri->pc))
|
||||
* (or just the commands connection if this is a sentinel instance). */
|
||||
if (ri->cc && (ri->flags & SRI_SENTINEL || ri->pc))
|
||||
ri->flags &= ~SRI_DISCONNECTED;
|
||||
}
|
||||
|
||||
@@ -1759,22 +1764,22 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
|
||||
* Some things will not happen if sentinel.tilt is true, but some will
|
||||
* still be processed. */
|
||||
|
||||
/* Remember when the role changed. */
|
||||
if (role != ri->role_reported) {
|
||||
ri->role_reported_time = mstime();
|
||||
ri->role_reported = role;
|
||||
if (role == SRI_SLAVE) ri->slave_conf_change_time = mstime();
|
||||
}
|
||||
|
||||
/* Handle master -> slave role switch. */
|
||||
if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE) {
|
||||
if (ri->role_reported != SRI_SLAVE) {
|
||||
ri->role_reported_time = mstime();
|
||||
ri->role_reported = SRI_SLAVE;
|
||||
ri->slave_conf_change_time = mstime();
|
||||
}
|
||||
/* Nothing to do, but masters claiming to be slaves are
|
||||
* considered to be unreachable by Sentinel, so eventually
|
||||
* a failover will be triggered. */
|
||||
}
|
||||
|
||||
/* Handle slave -> master role switch. */
|
||||
if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) {
|
||||
if (ri->role_reported != SRI_MASTER) {
|
||||
ri->role_reported_time = mstime();
|
||||
ri->role_reported = SRI_MASTER;
|
||||
}
|
||||
|
||||
/* If this is a promoted slave we can change state to the
|
||||
* failover state machine. */
|
||||
if (!sentinel.tilt &&
|
||||
@@ -2027,7 +2032,7 @@ void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privd
|
||||
if (msgmaster->config_epoch < master_config_epoch) {
|
||||
msgmaster->config_epoch = master_config_epoch;
|
||||
if (master_port != msgmaster->addr->port ||
|
||||
!strcmp(msgmaster->addr->ip, token[5]))
|
||||
strcmp(msgmaster->addr->ip, token[5]))
|
||||
{
|
||||
sentinelAddr *old_addr;
|
||||
|
||||
@@ -2080,12 +2085,12 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
|
||||
/* Format and send the Hello message. */
|
||||
snprintf(payload,sizeof(payload),
|
||||
"%s,%d,%s,%llu," /* Info about this sentinel. */
|
||||
"%s,%s,%d,%lld", /* Info about current master. */
|
||||
"%s,%s,%d,%llu", /* Info about current master. */
|
||||
ip, server.port, server.runid,
|
||||
(unsigned long long) sentinel.current_epoch,
|
||||
/* --- */
|
||||
master->name,master_addr->ip,master_addr->port,
|
||||
master->config_epoch);
|
||||
(unsigned long long) master->config_epoch);
|
||||
retval = redisAsyncCommand(ri->cc,
|
||||
sentinelPublishReplyCallback, NULL, "PUBLISH %s %s",
|
||||
SENTINEL_HELLO_CHANNEL,payload);
|
||||
@@ -2654,6 +2659,11 @@ void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *p
|
||||
/* If the runid in the reply is not "*" the Sentinel actually
|
||||
* replied with a vote. */
|
||||
sdsfree(ri->leader);
|
||||
if (ri->leader_epoch != r->element[2]->integer)
|
||||
redisLog(REDIS_WARNING,
|
||||
"%s voted for %s %llu", ri->name,
|
||||
r->element[1]->str,
|
||||
(unsigned long long) r->element[2]->integer);
|
||||
ri->leader = sdsnew(r->element[1]->str);
|
||||
ri->leader_epoch = r->element[2]->integer;
|
||||
}
|
||||
@@ -2731,12 +2741,9 @@ char *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char
|
||||
master->leader, (unsigned long long) master->leader_epoch);
|
||||
/* If we did not voted for ourselves, set the master failover start
|
||||
* time to now, in order to force a delay before we can start a
|
||||
* failover for the same master.
|
||||
*
|
||||
* The random addition is useful to desynchronize a bit the slaves
|
||||
* and reduce the chance that no slave gets majority. */
|
||||
* failover for the same master. */
|
||||
if (strcasecmp(master->leader,server.runid))
|
||||
master->failover_start_time = mstime() + rand() % 2000;
|
||||
master->failover_start_time = mstime();
|
||||
}
|
||||
|
||||
*leader_epoch = master->leader_epoch;
|
||||
@@ -3387,5 +3394,13 @@ void sentinelTimer(void) {
|
||||
sentinelRunPendingScripts();
|
||||
sentinelCollectTerminatedScripts();
|
||||
sentinelKillTimedoutScripts();
|
||||
|
||||
/* We continuously change the frequency of the Redis "timer interrupt"
|
||||
* in order to desynchronize every Sentinel from every other.
|
||||
* This non-determinism avoids that Sentinels started at the same time
|
||||
* exactly continue to stay synchronized asking to be voted at the
|
||||
* same time again and again (resulting in nobody likely winning the
|
||||
* election because of split brain voting). */
|
||||
server.hz = REDIS_DEFAULT_HZ + rand() % REDIS_DEFAULT_HZ;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -48,8 +48,8 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
|
||||
* 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.
|
||||
*
|
||||
* 2) If 'pattern' matches the "->" string, everything on the left of
|
||||
* the arrow is treated as the name of an hash field, and the part on the
|
||||
* left as the key name containing an hash. The value of the specified
|
||||
* the arrow is treated as the name of a hash field, and the part on the
|
||||
* left as the key name containing a hash. The value of the specified
|
||||
* field is returned.
|
||||
*
|
||||
* 3) If 'pattern' equals "#", the function simply returns 'subst' itself so
|
||||
|
||||
+3
-3
@@ -837,7 +837,7 @@ void unblockClientWaitingData(redisClient *c) {
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Cleanup the client structure */
|
||||
dictEmpty(c->bpop.keys);
|
||||
dictEmpty(c->bpop.keys,NULL);
|
||||
if (c->bpop.target) {
|
||||
decrRefCount(c->bpop.target);
|
||||
c->bpop.target = NULL;
|
||||
@@ -850,7 +850,7 @@ void unblockClientWaitingData(redisClient *c) {
|
||||
|
||||
/* If the specified key has clients blocked waiting for list pushes, this
|
||||
* function will put the key reference into the server.ready_keys list.
|
||||
* Note that db->ready_keys is an hash table that allows us to avoid putting
|
||||
* Note that db->ready_keys is a hash table that allows us to avoid putting
|
||||
* the same key again and again in the list in case of multiple pushes
|
||||
* made by a script or in the context of MULTI/EXEC.
|
||||
*
|
||||
@@ -878,7 +878,7 @@ void signalListAsReady(redisClient *c, robj *key) {
|
||||
redisAssert(dictAdd(c->db->ready_keys,key,NULL) == DICT_OK);
|
||||
}
|
||||
|
||||
/* This is an helper function for handleClientsBlockedOnLists(). It's work
|
||||
/* This is a helper function for handleClientsBlockedOnLists(). It's work
|
||||
* is to serve a specific client (receiver) that is blocked on 'key'
|
||||
* in the context of the specified 'db', doing the following:
|
||||
*
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
* in order to get O(log(N)) INSERT and REMOVE operations into a sorted
|
||||
* data structure.
|
||||
*
|
||||
* The elements are added to an hash table mapping Redis objects to scores.
|
||||
* The elements are added to a hash table mapping Redis objects to scores.
|
||||
* At the same time the elements are added to a skip list mapping scores
|
||||
* to Redis objects (so objects are sorted by scores in this "view"). */
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.8.0"
|
||||
#define REDIS_VERSION "2.8.3"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* efficient.
|
||||
*
|
||||
* The Redis Hash type uses this data structure for hashes composed of a small
|
||||
* number of elements, to switch to an hash table once a given number of
|
||||
* number of elements, to switch to a hash table once a given number of
|
||||
* elements is reached.
|
||||
*
|
||||
* Given that many times Redis Hashes are used to represent objects composed
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ void *zrealloc(void *ptr, size_t size) {
|
||||
}
|
||||
|
||||
/* Provide zmalloc_size() for systems where this function is not provided by
|
||||
* malloc itself, given that in that case we store an header with this
|
||||
* malloc itself, given that in that case we store a header with this
|
||||
* information as the first bytes of every allocation. */
|
||||
#ifndef HAVE_MALLOC_SIZE
|
||||
size_t zmalloc_size(void *ptr) {
|
||||
|
||||
@@ -22,7 +22,7 @@ start_server {tags {"memefficiency"}} {
|
||||
64 0.25
|
||||
128 0.35
|
||||
1024 0.75
|
||||
16384 0.90
|
||||
16384 0.82
|
||||
} {
|
||||
test "Memory efficiency with values in range $size_range" {
|
||||
set efficiency [test_memory_efficiency $size_range]
|
||||
|
||||
@@ -60,6 +60,14 @@ start_server {tags {"protocol"}} {
|
||||
assert_error "*wrong*arguments*ping*" {r ping x y z}
|
||||
}
|
||||
|
||||
test "Unbalanced number of quotes" {
|
||||
reconnect
|
||||
r write "set \"\"\"test-key\"\"\" test-value\r\n"
|
||||
r write "ping\r\n"
|
||||
r flush
|
||||
assert_error "*unbalanced*" {r read}
|
||||
}
|
||||
|
||||
set c 0
|
||||
foreach seq [list "\x00" "*\x00" "$\x00"] {
|
||||
incr c
|
||||
|
||||
+12
-55
@@ -3,66 +3,23 @@
|
||||
#
|
||||
# Copy the whole dataset from one Redis instance to another one
|
||||
#
|
||||
# WARNING: currently hashes and sorted sets are not supported! This
|
||||
# program should be updated.
|
||||
# WARNING: this utility is deprecated and serves as a legacy adapter
|
||||
# for the more-robust redis-copy gem.
|
||||
|
||||
require 'rubygems'
|
||||
require 'redis'
|
||||
require 'digest/sha1'
|
||||
require 'shellwords'
|
||||
|
||||
def redisCopy(opts={})
|
||||
sha1=""
|
||||
src = Redis.new(:host => opts[:srchost], :port => opts[:srcport])
|
||||
dst = Redis.new(:host => opts[:dsthost], :port => opts[:dstport])
|
||||
puts "Loading key names..."
|
||||
keys = src.keys('*')
|
||||
puts "Copying #{keys.length} keys..."
|
||||
c = 0
|
||||
keys.each{|k|
|
||||
vtype = src.type?(k)
|
||||
ttl = src.ttl(k).to_i if vtype != "none"
|
||||
|
||||
if vtype == "string"
|
||||
dst[k] = src[k]
|
||||
elsif vtype == "list"
|
||||
list = src.lrange(k,0,-1)
|
||||
if list.length == 0
|
||||
# Empty list special case
|
||||
dst.lpush(k,"")
|
||||
dst.lpop(k)
|
||||
else
|
||||
list.each{|ele|
|
||||
dst.rpush(k,ele)
|
||||
}
|
||||
end
|
||||
elsif vtype == "set"
|
||||
set = src.smembers(k)
|
||||
if set.length == 0
|
||||
# Empty set special case
|
||||
dst.sadd(k,"")
|
||||
dst.srem(k,"")
|
||||
else
|
||||
set.each{|ele|
|
||||
dst.sadd(k,ele)
|
||||
}
|
||||
end
|
||||
elsif vtype == "none"
|
||||
puts "WARNING: key '#{k}' was removed in the meanwhile."
|
||||
end
|
||||
|
||||
# Handle keys with an expire time set
|
||||
if ttl != -1 and vtype != "none"
|
||||
dst.expire(k,ttl)
|
||||
end
|
||||
|
||||
c = c+1
|
||||
if (c % 1000) == 0
|
||||
puts "#{c}/#{keys.length} completed"
|
||||
end
|
||||
}
|
||||
puts "DONE!"
|
||||
src = "#{opts[:srchost]}:#{opts[:srcport]}"
|
||||
dst = "#{opts[:dsthost]}:#{opts[:dstport]}"
|
||||
`redis-copy #{src.shellescape} #{dst.shellescape}`
|
||||
rescue Errno::ENOENT
|
||||
$stderr.puts 'This utility requires the redis-copy executable',
|
||||
'from the redis-copy gem on https://rubygems.org',
|
||||
'To install it, run `gem install redis-copy`.'
|
||||
exit 1
|
||||
end
|
||||
|
||||
$stderr.puts "This utility is deprecated. Use the redis-copy gem instead."
|
||||
if ARGV.length != 4
|
||||
puts "Usage: redis-copy.rb <srchost> <srcport> <dsthost> <dstport>"
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user