Compare commits

..
95 Commits
Author SHA1 Message Date
antirez 4e809a9a19 Redis 2.8.5. 2014-02-04 11:17:21 +01:00
antirez ddcf160309 Move mstime_t define outside sentinel.c.
The define is now used in other parts of Redis 2.8 tree instead of long
long.

A nice side effect is that now 2.8 and unstable sentinel.c files are
identical as it should be.
2014-02-03 16:34:46 +01:00
antirez c5bc592650 Scripting: expire keys in scripts only at first access.
Keys expiring in the middle of the execution of Lua scripts are to
create inconsistencies in masters and / or AOF files. See the following
example:

    if redis.call("exists",KEYS[1]) == 1
    then
        redis.call("incr","mycounter")
    end

    if redis.call("exists",KEYS[1]) == 1
    then
        return redis.call("incr","mycounter")
    end

The script executes two times the same *if key exists then incrementcounter*
logic. However the two executions will work differently in the master and
the slaves, provided some unlucky timing happens.

In the master the first time the key may still exist, while the second time
the key may no longer exist. This will result in the key incremented just one
time. However as a side effect the master will generate a synthetic
`DEL` command in the replication channel in order to force the slaves to
expire the key (given that key expiration is master-driven).

When the same script will run in the slave, the key will no longer be
there, so the script will not increment the key.

The key idea used to implement the expire-at-first-lookup semantics was
provided by Marc Gravell.
2014-02-03 16:29:25 +01:00
antirez 5201ca0ca1 Allow CONFIG and SHUTDOWN while in stale-slave state. 2014-02-03 15:51:07 +01:00
antirez 3da5cbe5bb Scripting: use mstime() and mstime_t for lua_time_start.
server.lua_time_start is expressed in milliseconds. Use mstime_t instead
of long long, and populate it with mstime() instead of ustime()/1000.

Functionally identical but more natural.
2014-02-03 15:46:47 +01:00
PatrickJS 0be31e2d22 update copyright year 2014-02-03 11:19:25 +01:00
antirez 66304fb122 Test: fixed osx msg passing issue in testing framework.
The Redis test uses a server-clients model in order to parallelize the
execution of different tests. However in recent versions of osx not
setting the channel to a binary encoding caused issues even if AFAIK no
binary data is really sent via this channel. However now the channels
are deliberately set to a binary encoding and this solves the issue.

The exact issue was the test not terminating and giving the impression
of running forever, since test clients or servers were unable to
exchange the messages to continue.
2014-01-31 16:25:13 +01:00
antirez 1406112ec4 Redis.conf comment about tcp-backlog option improved. 2014-01-31 15:03:25 +01:00
antirez 917b851491 Option "backlog" renamed "tcp-backlog".
This is especially important since we already have a concept of backlog
(the replication backlog).
2014-01-31 15:03:22 +01:00
Nenad Merdanovic 8dda9dbef0 Add support for listen(2) backlog definition
In high RPS environments, the default listen backlog is not sufficient, so
giving users the power to configure it is the right approach, especially
since it requires only minor modifications to the code.
2014-01-31 15:03:19 +01:00
antirez f0652c37a5 Sentinel: check arity for SENTINEL MASTER command.
This fixes issue #1530.
2014-01-31 10:14:07 +01:00
antirez a2c9d38a9f SENTINEL SET master quorum implemented. 2014-01-28 11:23:51 +01:00
antirez 89a731b3aa Fixed inverted if condition in MISCONF error code path. 2014-01-28 10:10:56 +01:00
antirez ecfefde760 Don't log MONITOR clients as disconnecting slaves. 2014-01-25 11:54:02 +01:00
antirez 43503ae5d6 redis-cli --help output improved with --scan and periods. 2014-01-22 12:08:08 +01:00
antirez b9c84d4aef redis-cli: support for --scan option. 2014-01-22 12:08:04 +01:00
antirez 248e916550 Use fflush() before fsync() in rio.c.
Incremental flushing in rio.c is only used to avoid huge kernel buffers
synched to slow disks creating big latency spikes, so this fix has no
durability implications, however it is certainly more correct to make
sure that the FILE buffers are flushed to the kernel before calling
fsync on the file descriptor.

Thanks to Li Shao Kai for reporting this issue in the Redis mailing
list.
2014-01-22 09:56:35 +01:00
antirez d401022d14 Fix typo in aofRewriteBufferAppend() comment. 2014-01-14 15:38:26 +01:00
antirez 4ea91aadfb Set REDIS_AOF_REWRITE_MIN_SIZE to 64mb.
64mb is the default value in redis.conf. For some reason instead the
hard-coded default was 1mb that is too small.
2014-01-14 11:28:18 +01:00
antirez 71f7eb2590 Redis 2.8.4. 2014-01-13 17:09:58 +01:00
antirez df00b6ac05 SENTINEL SET: error on bad option name + flush config on error. 2014-01-13 16:39:56 +01:00
antirez b8db8a0c48 SENTINEL SET implemented.
The new command allows to change master-specific configurations
at runtime. All the settable parameters can be retrivied via the
SENTINEL MASTER command, so there is no equivalent "GET" command.
2014-01-13 16:39:51 +01:00
antirez 8ed0b49525 Sentinel: fix wrong arity error message. 2014-01-13 16:39:47 +01:00
antirez 560e548dc6 Sentinel: SENTINEL REMOVE command added.
The command totally removes a monitored master.
2014-01-13 16:39:44 +01:00
antirez 0ca750d94a Sentinel: releaseSentinelRedisInstance() top comment fixed.
The claim about unlinking the instance from the connected hash tables
was the opposite of the reality. Also the current actual behavior is
safer in most cases, so it is better to manually unlink when needed.
2014-01-13 16:39:40 +01:00
antirez 1f9580c125 Sentinel: flush config on disk when new master is added. 2014-01-13 16:39:36 +01:00
antirez bb207007ca anetResolveIP() prototype added to anet.h. 2014-01-13 16:39:31 +01:00
antirez b7dc3204a6 Sentinel: SENTINEL MONITOR command implemented.
It allows to add new masters to monitor at runtime.
2014-01-13 16:39:27 +01:00
antirez 2e0ba7bb5b anetResolveIP() added to anet.c.
The new function is used when we want to normalize an IP address without
performing a DNS lookup if the string to resolve is not a valid IP.

This is useful every time only IPs are valid inputs or when we want to
skip DNS resolution that is slow during runtime operations if we are
required to block.
2014-01-13 16:39:24 +01:00
antirez 52cf0975c7 Sentinel: added SENTINEL MASTER <name> command.
With SENTINEL MASTERS it was already possible to list all the configured
masters, but not a specific one.
2014-01-13 16:39:18 +01:00
antirez 6bcc370c9d Add all the configurable fields to addReplySentinelRedisInstance().
Note: the auth password with the master is voluntarily not exposed.
2014-01-13 16:39:15 +01:00
antirez ea2bffa030 Trip comment to 80 cols in SentinelCommand(). 2014-01-13 16:39:11 +01:00
antirez bca65866ff Test: regression for issues #1483. 2014-01-09 11:19:11 +01:00
antirez e4c51759f5 Fix RESTORE ttl handling in 32 bit archs.
long was used instead of long long in order to handle a 64 bit
resolution millisecond timestamp.

This fixes issue #1483.
2014-01-09 11:12:53 +01:00
antirez 51f71e2d8e Fix keyspace events flags-to-string conversion.
Fixes issue #1491 on Github.
2014-01-08 17:18:00 +01:00
antirez 05d219a6fa Test: stress events flags to/from string conversion. 2014-01-08 17:16:09 +01:00
antirez 2a1a31ca9d Don't send REPLCONF ACK to old masters.
Masters not understanding REPLCONF ACK will reply with errors to our
requests causing a number of possible issues.

This commit detects a global replication offest set to -1 at the end of
the replication, and marks the client representing the master with the
REDIS_PRE_PSYNC flag.

Note that this flag was called REDIS_PRE_PSYNC_SLAVE but now it is just
REDIS_PRE_PSYNC as it is used for both slaves and masters starting with
this commit.

This commit fixes issue #1488.
2014-01-08 14:27:49 +01:00
antirez 418d3d358a Clarify a comment in slaveTryPartialResynchronization(). 2014-01-08 14:11:02 +01:00
antirez 0a1a236e3e Log disconnection with slave only when ip:port is available. 2013-12-25 18:41:10 +01:00
antirez 27d06111db anetPeerToString / SockName: port can be NULL on errors too. 2013-12-25 18:39:49 +01:00
antirez 5b7c16137d anetTcpGenericConnect() bug introduced in 9d19977 fixed.
Durign a refactoring I mispelled _port for port.
This is one of the reasons I never used _varname myself.
2013-12-25 18:38:33 +01:00
antirez d07d4a876c Remove useless goto from anetTcpGenericConnect(). 2013-12-25 18:24:04 +01:00
antirez 9d1997706c anetTcpGenericConnect() code improved + 1 bug fix.
Now the socket is closed if anetNonBlock() fails, and in general the
code structure makes it harder to introduce this kind of bugs in the
future.

Reference: pull request #1059.
2013-12-25 18:16:46 +01:00
antirez 4ad219adc8 Fix CONFIG REWRITE handling of unknown options.
There were two problems with the implementation.

1) "save" was not correctly processed when no save point was configured,
   as reported in issue #1416.
2) The way the code checked if an option existed in the "processed"
   dictionary was wrong, as we add the element with as a key associated
   with a NULL value, so dictFetchValue() can't be used to check for
   existance, but dictFind() must be used, that returns NULL only if the
   entry does not exist at all.
2013-12-23 12:50:52 +01:00
antirez c6db326d1d Configuring port to 0 disables IP socket as specified.
This was no longer the case with 2.8 becuase of a bug introduced with
the IPv6 support. Now it is fixed.

This fixes issue #1287 and #1477.
2013-12-23 11:34:15 +01:00
antirez 4456ee1173 Make new masters inherit replication offsets.
Currently replication offsets could be used into a limited way in order
to understand, out of a set of slaves, what is the one with the most
updated data. For example this comparison is possible of N slaves
were replicating all with the same master.

However the replication offset was not transferred from master to slaves
(that are later promoted as masters) in any way, so for instance if
there were three instances A, B, C, with A master and B and C
replication from A, the following could happen:

C disconnects from A.
B is turned into master.
A is switched to master of B.
B receives some write.

In this context there was no way to compare the offset of A and C,
because B would use its own local master replication offset as
replication offset to initialize the replication with A.

With this commit what happens is that when B is turned into master it
inherits the replication offset from A, making A and C comparable.
In the above case assuming no inconsistencies are created during the
disconnection and failover process, A will show to have a replication
offset greater than C.

Note that this does not mean offsets are always comparable to understand
what is, in a set of instances, since in more complex examples the
replica with the higher replication offset could be partitioned away
when picking the instance to elect as new master. However this in
general improves the ability of a system to try to pick a good replica
to promote to master.
2013-12-22 11:54:10 +01:00
antirez 5fa937d956 Slave disconnection is an event worth logging. 2013-12-22 10:16:02 +01:00
antirez 9ba9d78ada Log when a slave lose the connection with its master. 2013-12-21 00:24:29 +01:00
antirez d33f3689d7 Clarify include directive behavior in example redis.conf. 2013-12-19 16:03:52 +01:00
antirez fb8a480f54 CONFIG REWRITE: no special handling or include and rename-command.
CONFIG REWRITE is now wiser and does not touch what it does not
understand inside redis.conf.
2013-12-19 16:03:49 +01:00
Yubao Liu d62bbfda16 CONFIG REWRITE: don't throw some options on config rewrite
Those options will be thrown without this patch:
  include, rename-command, min-slaves-to-write, min-slaves-max-lag,
appendfilename.
2013-12-19 16:03:37 +01:00
antirez 090bcc946f CONFIG REWRITE: old development comments removed. 2013-12-19 16:02:50 +01:00
antirez ddd529bc3b CONFIG REWRITE: don't wipe unknown options.
With this commit options not explicitly rewritten by CONFIG REWRITE are
not touched at all. These include new options that may not have support
for REWRITE, and other special cases like rename-command and include.
2013-12-19 16:02:46 +01:00
antirez 673c42bb4e Example redis.conf formatted to better show appendfilename option. 2013-12-19 10:19:13 +01:00
antirez 33f6f35f34 Makefile.dep updated. 2013-12-13 13:14:47 +01:00
antirez 8eb1cb3b52 SDIFF iterator misuse bug regression test added.
See commit c00453d for more info about the bug.
2013-12-13 11:37:35 +01:00
antirez 993e0ede76 SDIFF iterator misuse fixed in diff algorithm #1.
The bug could be easily triggered by:

    SADD foo a b c 1 2 3 4 5 6
    SDIFF foo foo

When the key was the same in two sets, an unsafe iterator was used to
check existence of elements in the same set we were iterating.
Usually this would just result into a wrong output, however with the
dict.c API misuse protection we have in place, the result was actually
an assertion failed that was triggered by the CI test, while creating
random datasets for the "MASTER and SLAVE consistency" test.
2013-12-13 11:29:59 +01:00
antirez 2507e366b2 Sentinel: dead code removed. 2013-12-13 11:01:20 +01:00
antirez f9e9448a5b Makefile: remove odd syntax not compatible with some make versions.
See issue #1448.
2013-12-12 15:19:29 +01:00
Yubao Liu 45c60a903a fix typo in redis.conf and sentinel.conf 2013-12-12 11:32:05 +01:00
codeeply 5e75e681fe comment mistake fixed 2013-12-12 11:25:48 +01:00
antirez 8995634014 Redis 2.8.3. 2013-12-11 15:31:57 +01:00
antirez 9a8ae5a553 Replication: publish the slave_repl_offset when disconnected from master.
When a slave was disconnected from its master the replication offset was
reported as -1. Now it is reported as the replication offset of the
previous master, so that failover can be performed using this value in
order to try to select a slave with more processed data from a set of
slaves of the old master.
2013-12-11 15:24:50 +01:00
Yossi Gottlieb 0ff078d8d0 Return proper error on requests with an unbalanced number of quotes. 2013-12-11 13:21:55 +01:00
Yossi Gottlieb 25ba2e9607 Fix wrong repldboff type which causes dropped replication in rare cases. 2013-12-11 11:37:58 +01:00
antirez 563d6b3f98 Slaves heartbeats during sync improved.
The previous fix for false positive timeout detected by master was not
complete. There is another blocking stage while loading data for the
first synchronization with the master, that is, flushing away the
current data from the DB memory.

This commit uses the newly introduced dict.c callback in order to make
some incremental work (to send "\n" heartbeats to the master) while
flushing the old data from memory.

It is hard to write a regression test for this issue unfortunately. More
support for debugging in the Redis core would be needed in terms of
functionalities to simulate a slow DB loading / deletion.
2013-12-10 18:42:22 +01:00
antirez b6610a569d dict.c: added optional callback to dictEmpty().
Redis hash table implementation has many non-blocking features like
incremental rehashing, however while deleting a large hash table there
was no way to have a callback called to do some incremental work.

This commit adds this support, as an optiona callback argument to
dictEmpty() that is currently called at a fixed interval (one time every
65k deletions).
2013-12-10 18:18:36 +01:00
antirez 26cf5c8ac6 Log empty DB + Loading data into two separated messages. 2013-12-10 17:51:16 +01:00
antirez 303cc97ff9 Don't send more than 1 newline/sec while loading RDB. 2013-12-10 17:50:26 +01:00
antirez 75bf5a4a4a Slaves heartbeat while loading RDB files.
Starting with Redis 2.8 masters are able to detect timed out slaves,
while before 2.8 only slaves were able to detect a timed out master.

Now that timeout detection is bi-directional the following problem
happens as described "in the field" by issue #1449:

1) Master and slave setup with big dataset.
2) Slave performs the first synchronization, or a full sync
   after a failed partial resync.
3) Master sends the RDB payload to the slave.
4) Slave loads this payload.
5) Master detects the slave as timed out since does not receive back the
   REPLCONF ACK acknowledges.

Here the problem is that the master has no way to know how much the
slave will take to load the RDB file in memory. The obvious solution is
to use a greater replication timeout setting, but this is a shame since
for the 0.1% of operation time we are forced to use a timeout that is
not what is suited for 99.9% of operation time.

This commit tries to fix this problem with a solution that is a bit of
an hack, but that modifies little of the replication internals, in order
to be back ported to 2.8 safely.

During the RDB loading time, we send the master newlines to avoid
being sensed as timed out. This is the same that the master already does
while saving the RDB file to still signal its presence to the slave.

The single newline is used because:

1) It can't desync the protocol, as it is only transmitted all or
nothing.
2) It can be safely sent while we don't have a client structure for the
master or in similar situations just with write(2).
2013-12-10 15:40:36 +01:00
antirez 8d0083ba25 Handle inline requested terminated with just \n. 2013-12-10 15:40:33 +01:00
antirez fba0b23e72 Sentinel: fix reported role info sampling.
The way the role change was recoded was not sane and too much
convoluted, causing the role information to be not always updated.

This commit fixes issue #1445.
2013-12-06 12:49:27 +01:00
antirez dceaca1f69 Sentinel: fix reported role fields when master is reset.
When there is a master address switch, the reported role must be set to
master so that we have a chance to re-sample the INFO output to check if
the new address is reporting the right role.

Otherwise if the role was wrong, it will be sensed as wrong even after
the address switch, and for enough time according to the role change
time, for Sentinel consider the master SDOWN.

This fixes isue #1446, that describes the effects of this bug in
practice.
2013-12-06 12:49:23 +01:00
antirez bf307cfb4d Fixed typo in redis.conf. 2013-12-06 10:48:40 +01:00
Anurag Ramdasan 4f9d30b33b Grammar fix. 2013-12-05 18:54:01 +01:00
Anurag Ramdasan b67f39da09 fixed typo 2013-12-05 17:18:37 +01:00
Anurag Ramdasan 4c53178c6c Fixed grammar: 'usually' to 'usual' 2013-12-05 16:42:28 +01:00
antirez 7f6743a581 Fixed grammar: before H the article is a, not an. 2013-12-05 16:37:21 +01:00
antirez edae78999c Fixed typos in redis.conf file. 2013-12-05 16:30:15 +01:00
antirez 3d7263aa41 Removed old comments and dead code from freeClient(). 2013-12-03 13:54:15 +01:00
antirez 333453646c Grammar fix in freeClient(). 2013-12-03 13:40:51 +01:00
antirez 4d650a3baa Redis 2.8.2. 2013-12-02 16:07:46 +01:00
antirez dd0ac4ac72 Sentinel: don't write HZ when flushing config.
See issue #1419.
2013-12-02 15:56:42 +01:00
antirez 75347ada7f Sentinel: better time desynchronization.
Sentinels are now desynchronized in a better way changing the time
handler frequency between 10 and 20 HZ. This way on average a
desynchronization of 25 milliesconds is produced that should be larger
enough compared to network latency, avoiding most split-brain condition
during the vote.

Now that the clocks are desynchronized, to have larger random delays when
performing operations can be easily achieved in the following way.
Take as example the function that starts the failover, that is
called with a frequency between 10 and 20 HZ and will start the
failover every time there are the conditions. By just adding as an
additional condition something like rand()%4 == 0, we can amplify the
desynchronization between Sentinel instances easily.

See issue #1419.
2013-12-02 15:56:39 +01:00
antirez 83333b08d0 Stop writes on MISCONF only if instance is a master.
From the point of view of the slave not accepting writes from the master
can only create a bigger consistency issue.
2013-11-28 16:25:49 +01:00
antirez 50d140e90b SLAVEOF command refactored into a proper API.
We now have replicationSetMaster() and replicationUnsetMaster() that can
be called in other contexts (for instance Redis Cluster).
2013-11-28 16:19:16 +01:00
antirez 2eb8a46061 Reply to PING with error when there is a MISCONF state. 2013-11-28 16:16:58 +01:00
antirez a46f841df3 Sentinel: log vote received from other Sentinels. 2013-11-28 15:23:51 +01:00
huangz1990 e5c577e679 fix a bug in sentinel.c about pub/sub link 2013-11-26 15:11:58 +01:00
antirez dd239c37db Redis 2.8.1. 2013-11-25 11:21:49 +01:00
Salvatore Sanfilippo c61cc761d5 Merge pull request #1370 from yaauie/deprecate-redis-copy-script-2.8
Deprecate redis copy script 2.8
2013-11-25 02:10:32 -08:00
antirez d13635b2a9 Sentinel: fixes inverted strcmp() test preventing config updates.
The result of this one-char bug was pretty serious, if the new master
had the same port of the previous master, but just a different IP
address, non-leader Sentinels would not be able to recognize the
configuration change.

This commit fixes issue #1394.

Many thanks to @shanemadden that reported the bug and helped
investigating it.
2013-11-25 10:57:20 +01:00
antirez d240202261 Sentinel: fix type specifier for Hello msg generation.
This fixes issue #1395.
2013-11-25 10:25:10 +01:00
antirez e83741746c Fix false positive in memory efficiency test.
Fixes issue #1298.
2013-11-25 10:21:18 +01:00
Ryan Biesemeyer 7d30b3121d Deprecate utils/redis-copy.rb in favor of redis-copy gem 2013-11-08 06:33:04 +00:00
44 changed files with 878 additions and 349 deletions
+78
View File
@@ -14,6 +14,84 @@ 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.5 ] Release date: 4 Feb 2014
# UPGRADE URGENCY: HIGH for Redis, LOW for Sentinel. Redis users using Lua
scripts with expires, and Redis users relying on the
ability of Redis to block writes on RDB saving errors
should plan to upgrade ASAP.
* [FIX] Fixed a replication bug caused by Lua scripts + expired keys: keys could
expire in the middle of scripts causing non-deterministic behavior.
* [FIX] MISCONFIG error if condition fixed, the server was no longer able
to stop writes on RDB misconfiguration after this error was introduced.
* [FIX] REDIS_AOF_REWRITE_MIN_SIZE is now 64mb like example redis.conf default.
* [FIX] Perform fflush() before fsync() in rio.c (bug without actual effects).
* [FIX] Don't log MONITOR clients as disconnecting slaves.
* [FIX] SENTINEL MASTER arity check fixed. Crashed the Sentinel instance when
the command was given without arguments.
* [NEW] Allow CONFIG and SHUTDOWN while in stale-slave state.
* [NEW] Support for configurable TCP listen(2) backlog size.
* [NEW] redis-cli supports SCAN via the --scan and --pattern options.
* [NEW] SENTINEL SET master quorum via runtime API implemented.
--[ Redis 2.8.4 ] Release date: 13 Jan 2014
# UPGRADE URGENCY: MODERATE for Redis and Sentinel.
* [FIX] Makefile compatibility with non common make variants improved.
* [FIX] SDIFF crash in very unlikely to trigger state fixed.
* [FIX] Config rewriting fixed: don't wipe options unknown to the rewrite
process.
* [FIX] Set TCP port to 0 works again to disable TCP networking.
* [FIX] Fixed replication with old Redis instances as masters by not
sending REPLCONF ACK to them.
* [FIX] Fix keyspace notifications rewrite and CONFIG GET output.
* [FIX] Fix RESTORE TTL handling in 32 bit systems (32 bit overflow).
* [NEW] Sentinel now has a run time configuration API.
* [NEW] Log when we lost connection with master or slave.
* [NEW] When instance is turned from slave to master now inherits the
old master replication offset when possible. This improves the
Sentinel failover procedure.
--[ 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
View File
@@ -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/ )
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2006-2012, Salvatore Sanfilippo
Copyright (c) 2006-2014, Salvatore Sanfilippo
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+45 -23
View File
@@ -12,6 +12,26 @@
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis server but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf
################################ GENERAL #####################################
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize no
@@ -24,6 +44,15 @@ pidfile /var/run/redis.pid
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379
# TCP listen() backlog.
#
# In high requests-per-second environments you need an high backlog in order
# to avoid slow clients connections issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.
tcp-backlog 511
# By default Redis listens for connections from all the network interfaces
# available on the server. It is possible to listen to just one or multiple
# interfaces using the "bind" configuration directive, followed by one or
@@ -34,7 +63,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 +97,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 ""
@@ -88,7 +117,7 @@ logfile ""
# dbid is a number between 0 and 'databases'-1
databases 16
################################ SNAPSHOTTING #################################
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
@@ -116,16 +145,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 +226,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 +292,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 +372,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 +380,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
@@ -420,7 +449,8 @@ slave-priority 100
appendonly no
# The name of the append only file (default: "appendonly.aof")
# appendfilename appendonly.aof
appendfilename "appendonly.aof"
# The fsync() call tells the Operating System to actually write data on disk
# instead to wait for more data in the output buffer. Some OS will really flush
@@ -467,6 +497,7 @@ appendfsync everysec
#
# If you have latency problems turn this to "yes". Otherwise leave it as
# "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
# Automatic rewrite of the append only file.
@@ -607,7 +638,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.
@@ -633,7 +664,7 @@ activerehashing yes
#
# normal -> normal clients
# slave -> slave clients and MONITOR clients
# pubsub -> clients subcribed to at least one pubsub channel or pattern
# pubsub -> clients subscribed to at least one pubsub channel or pattern
#
# The syntax of every client-output-buffer-limit directive is the following:
#
@@ -662,7 +693,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
@@ -684,12 +715,3 @@ hz 10
# big latency spikes.
aof-rewrite-incremental-fsync yes
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis server but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# include /path/to/local.conf
# include /path/to/other.conf
+3 -3
View File
@@ -6,7 +6,7 @@ port 26379
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# Tells Sentinel to monitor this slave, and to consider it in O_DOWN
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
@@ -63,7 +63,7 @@ sentinel parallel-syncs mymaster 1
# times the failover timeout.
#
# - The time needed for a slave replicating to a wrong master according
# to a Sentinel currnet configuration, to be forced to replicate
# to a Sentinel current configuration, to be forced to replicate
# with the right master, is exactly the failover timeout (counting since
# the moment a Sentinel detected the misconfiguration).
#
@@ -102,7 +102,7 @@ sentinel failover-timeout mymaster 180000
#
# sentinel notification-script <master-name> <script-path>
#
# Call the specified notification script for any sentienl event that is
# Call the specified notification script for any sentinel event that is
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
# This script should notify the system administrator via email, SMS, or any
# other messaging system, that there is something wrong with the monitored
+6 -2
View File
@@ -55,15 +55,19 @@ FINAL_LIBS=-lm
DEBUG=-g -ggdb
ifeq ($(uname_S),SunOS)
# SunOS
INSTALL=cp -pf
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
FINAL_LIBS+= -ldl -lnsl -lsocket -lpthread
else ifeq ($(uname_S),Darwin)
else
ifeq ($(uname_S),Darwin)
# Darwin (nothing to do)
else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+= -pthread
endif
endif
# Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
+10 -9
View File
@@ -1,5 +1,5 @@
adlist.o: adlist.c adlist.h zmalloc.h
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c ae_select.c ae_evport.c ae_epoll.c
ae_epoll.o: ae_epoll.c
ae_evport.o: ae_evport.c
ae_kqueue.o: ae_kqueue.c
@@ -24,7 +24,7 @@ db.o: db.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h crc64.h bio.h
dict.o: dict.c fmacros.h dict.h zmalloc.h
dict.o: dict.c fmacros.h dict.h zmalloc.h redisassert.h
endianconv.o: endianconv.c
intset.o: intset.c intset.h zmalloc.h endianconv.h config.h
lzf_c.o: lzf_c.c lzfP.h
@@ -70,18 +70,18 @@ replication.o: replication.c redis.h fmacros.h config.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
rio.o: rio.c fmacros.h rio.h sds.h util.h crc64.h
rio.o: rio.c fmacros.h rio.h sds.h util.h crc64.h config.h redis.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h dict.h adlist.h \
zmalloc.h anet.h ziplist.h intset.h version.h rdb.h
scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h rand.h \
../deps/lua/src/lauxlib.h ../deps/lua/src/lua.h \
../deps/lua/src/lualib.h
../deps/lua/src/lauxlib.h ../deps/lua/src/lualib.h
sds.o: sds.c sds.h zmalloc.h
sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h \
../deps/hiredis/hiredis.h ../deps/hiredis/async.h \
../deps/hiredis/hiredis.h
../deps/hiredis/hiredis.h ../deps/hiredis/async.h
setproctitle.o: setproctitle.c
sha1.o: sha1.c sha1.h config.h
slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
@@ -108,7 +108,8 @@ t_string.o: t_string.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
t_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
util.o: util.c fmacros.h util.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h config.h
util.o: util.c fmacros.h util.h sds.h
ziplist.o: ziplist.c zmalloc.h util.h sds.h ziplist.h endianconv.h \
config.h redisassert.h
zipmap.o: zipmap.c zmalloc.h endianconv.h config.h
zmalloc.o: zmalloc.c config.h zmalloc.h
+1 -1
View File
@@ -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);
+49 -28
View File
@@ -163,12 +163,21 @@ int anetTcpKeepAlive(char *err, int fd)
return ANET_OK;
}
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len)
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
* do the actual work. It resolves the hostname "host" and set the string
* representation of the IP address into the buffer pointed by "ipbuf".
*
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
* that are actually already IPv4 or IPv6 addresses. This turns the function
* into a validating / normalizing function. */
int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
int flags)
{
struct addrinfo hints, *info;
int rv;
memset(&hints,0,sizeof(hints));
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
@@ -188,6 +197,14 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len)
return ANET_OK;
}
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_NONE);
}
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_IP_ONLY);
}
static int anetSetReuseAddr(char *err, int fd) {
int yes = 1;
/* Make sure connection-intensive things like the redis benckmark
@@ -219,43 +236,50 @@ static int anetCreateSocket(char *err, int domain) {
#define ANET_CONNECT_NONBLOCK 1
static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
{
int s, rv;
char _port[6]; /* strlen("65535"); */
int s = ANET_ERR, rv;
char portstr[6]; /* strlen("65535") + 1; */
struct addrinfo hints, *servinfo, *p;
snprintf(_port,6,"%d",port);
snprintf(portstr,sizeof(portstr),"%d",port);
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) {
anetSetError(err, "%s", gai_strerror(rv));
return ANET_ERR;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
/* Try to create the socket and to connect it.
* If we fail in the socket() call, or on connect(), we retry with
* the next entry in servinfo. */
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
continue;
/* if we set err then goto cleanup, otherwise next */
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK)
goto error;
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) goto end;
/* If the socket is non-blocking, it is ok for connect() to
* return an EINPROGRESS error here. */
if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK)
goto end;
close(s);
s = ANET_ERR;
continue;
}
/* break with the socket */
/* If we ended an iteration of the for loop without errors, we
* have a connected socket. Let's return to the caller. */
goto end;
}
if (p == NULL) {
if (p == NULL)
anetSetError(err, "creating socket: %s", strerror(errno));
goto error;
}
error:
s = ANET_ERR;
if (s != ANET_ERR) {
close(s);
s = ANET_ERR;
}
end:
freeaddrinfo(servinfo);
return s;
@@ -337,17 +361,14 @@ int anetWrite(int fd, char *buf, int count)
return totlen;
}
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) {
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
if (bind(s,sa,len) == -1) {
anetSetError(err, "bind: %s", strerror(errno));
close(s);
return ANET_ERR;
}
/* Use a backlog of 512 entries. We pass 511 to the listen() call because
* the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
* which will thus give us a backlog of 512 entries */
if (listen(s, 511) == -1) {
if (listen(s, backlog) == -1) {
anetSetError(err, "listen: %s", strerror(errno));
close(s);
return ANET_ERR;
@@ -365,7 +386,7 @@ static int anetV6Only(char *err, int s) {
return ANET_OK;
}
static int _anetTcpServer(char *err, int port, char *bindaddr, int af)
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
{
int s, rv;
char _port[6]; /* strlen("65535") */
@@ -387,7 +408,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af)
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
if (anetListen(err,s,p->ai_addr,p->ai_addrlen) == ANET_ERR) goto error;
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) goto error;
goto end;
}
if (p == NULL) {
@@ -402,17 +423,17 @@ end:
return s;
}
int anetTcpServer(char *err, int port, char *bindaddr)
int anetTcpServer(char *err, int port, char *bindaddr, int backlog)
{
return _anetTcpServer(err, port, bindaddr, AF_INET);
return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);
}
int anetTcp6Server(char *err, int port, char *bindaddr)
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog)
{
return _anetTcpServer(err, port, bindaddr, AF_INET6);
return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog);
}
int anetUnixServer(char *err, char *path, mode_t perm)
int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
{
int s;
struct sockaddr_un sa;
@@ -423,7 +444,7 @@ int anetUnixServer(char *err, char *path, mode_t perm)
memset(&sa,0,sizeof(sa));
sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR)
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
return ANET_ERR;
if (perm)
chmod(sa.sun_path, perm);
@@ -481,7 +502,7 @@ int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
socklen_t salen = sizeof(sa);
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) {
*port = 0;
if (port) *port = 0;
ip[0] = '?';
ip[1] = '\0';
return -1;
@@ -503,7 +524,7 @@ int anetSockName(int fd, char *ip, size_t ip_len, int *port) {
socklen_t salen = sizeof(sa);
if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {
*port = 0;
if (port) *port = 0;
ip[0] = '?';
ip[1] = '\0';
return -1;
+8 -3
View File
@@ -35,6 +35,10 @@
#define ANET_ERR -1
#define ANET_ERR_LEN 256
/* Flags used with certain functions. */
#define ANET_NONE 0
#define ANET_IP_ONLY (1<<0)
#if defined(__sun)
#define AF_LOCAL AF_UNIX
#endif
@@ -45,9 +49,10 @@ int anetUnixConnect(char *err, char *path);
int anetUnixNonBlockConnect(char *err, char *path);
int anetRead(int fd, char *buf, int count);
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);
int anetTcpServer(char *err, int port, char *bindaddr);
int anetTcp6Server(char *err, int port, char *bindaddr);
int anetUnixServer(char *err, char *path, mode_t perm);
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len);
int anetTcpServer(char *err, int port, char *bindaddr, int backlog);
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);
int anetUnixServer(char *err, char *path, mode_t perm, int backlog);
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
+2 -2
View File
@@ -126,7 +126,7 @@ void aofRewriteBufferAppend(unsigned char *s, unsigned long len) {
}
/* Write the buffer (possibly composed of multiple blocks) into the specified
* fd. If no short write or any other error happens -1 is returned,
* fd. If a short write or any other error happens -1 is returned,
* otherwise the number of bytes written is returned. */
ssize_t aofRewriteBufferWrite(int fd) {
listNode *ln;
@@ -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.
+65 -23
View File
@@ -125,6 +125,11 @@ void loadServerConfigFromString(char *config) {
if (server.port < 0 || server.port > 65535) {
err = "Invalid port"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"tcp-backlog") && argc == 2) {
server.tcp_backlog = atoi(argv[1]);
if (server.tcp_backlog < 0) {
err = "Invalid backlog value"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"bind") && argc >= 2) {
int j, addresses = argc-1;
@@ -935,6 +940,7 @@ void configGetCommand(redisClient *c) {
config_get_numerical_field("slowlog-max-len",
server.slowlog_max_len);
config_get_numerical_field("port",server.port);
config_get_numerical_field("tcp-backlog",server.tcp_backlog);
config_get_numerical_field("databases",server.dbnum);
config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
config_get_numerical_field("repl-timeout",server.repl_timeout);
@@ -1107,8 +1113,8 @@ void configGetCommand(redisClient *c) {
/* We use the following dictionary type to store where a configuration
* option is mentioned in the old configuration file, so it's
* like "maxmemory" -> list of line numbers (first line is zero). */
unsigned int dictSdsHash(const void *key);
int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
unsigned int dictSdsCaseHash(const void *key);
int dictSdsKeyCaseCompare(void *privdata, const void *key1, const void *key2);
void dictSdsDestructor(void *privdata, void *val);
void dictListDestructor(void *privdata, void *val);
@@ -1117,17 +1123,27 @@ void dictListDestructor(void *privdata, void *val);
void rewriteConfigSentinelOption(struct rewriteConfigState *state);
dictType optionToLineDictType = {
dictSdsHash, /* hash function */
dictSdsCaseHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCompare, /* key compare */
dictSdsKeyCaseCompare, /* key compare */
dictSdsDestructor, /* key destructor */
dictListDestructor /* val destructor */
};
dictType optionSetDictType = {
dictSdsCaseHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCaseCompare, /* key compare */
dictSdsDestructor, /* key destructor */
NULL /* val destructor */
};
/* The config rewrite state. */
struct rewriteConfigState {
dict *option_to_line; /* Option -> list of config file lines map */
dict *rewritten; /* Dictionary of already processed options */
int numlines; /* Number of lines in current config */
sds *lines; /* Current lines as an array of sds strings */
int has_tail; /* True if we already added directives that were
@@ -1151,6 +1167,16 @@ void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds op
listAddNodeTail(l,(void*)(long)linenum);
}
/* Add the specified option to the set of processed options.
* This is useful as only unused lines of processed options will be blanked
* in the config file, while options the rewrite process does not understand
* remain untouched. */
void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, char *option) {
sds opt = sdsnew(option);
if (dictAdd(state->rewritten,opt,NULL) != DICT_OK) sdsfree(opt);
}
/* Read the old file, split it into lines to populate a newly created
* config rewrite state, and return it to the caller.
*
@@ -1165,6 +1191,7 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
if (fp == NULL && errno != ENOENT) return NULL;
state->option_to_line = dictCreate(&optionToLineDictType,NULL);
state->rewritten = dictCreate(&optionSetDictType,NULL);
state->numlines = 0;
state->lines = NULL;
state->has_tail = 0;
@@ -1232,6 +1259,8 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sd
sds o = sdsnew(option);
list *l = dictFetchValue(state->option_to_line,o);
rewriteConfigMarkAsProcessed(state,option);
if (!l && !force) {
/* Option not used previously, and we are not forced to use it. */
sdsfree(line);
@@ -1288,7 +1317,6 @@ void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, lo
rewriteConfigFormatMemory(buf,sizeof(buf),value);
line = sdscatprintf(sdsempty(),"%s %s",option,buf);
rewriteConfigRewriteLine(state,option,line,force);
}
/* Rewrite a yes/no option. */
@@ -1307,7 +1335,10 @@ void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, c
/* String options set to NULL need to be not present at all in the
* configuration file to be set to NULL again at the next reboot. */
if (value == NULL) return;
if (value == NULL) {
rewriteConfigMarkAsProcessed(state,option);
return;
}
/* Compare the strings as sds strings to have a binary safe comparison. */
if (defvalue && strcmp(value,defvalue) == 0) force = 0;
@@ -1391,13 +1422,18 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) {
server.saveparams[j].seconds, server.saveparams[j].changes);
rewriteConfigRewriteLine(state,"save",line,1);
}
/* Mark "save" as processed in case server.saveparamslen is zero. */
rewriteConfigMarkAsProcessed(state,"save");
}
/* Rewrite the dir option, always using absolute paths.*/
void rewriteConfigDirOption(struct rewriteConfigState *state) {
char cwd[1024];
if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */
if (getcwd(cwd,sizeof(cwd)) == NULL) {
rewriteConfigMarkAsProcessed(state,"dir");
return; /* no rewrite on error. */
}
rewriteConfigStringOption(state,"dir",cwd,NULL);
}
@@ -1408,23 +1444,15 @@ void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
/* If this is a master, we want all the slaveof config options
* in the file to be removed. */
if (server.masterhost == NULL) return;
if (server.masterhost == NULL) {
rewriteConfigMarkAsProcessed(state,"slaveof");
return;
}
line = sdscatprintf(sdsempty(),"%s %s %d", option,
server.masterhost, server.masterport);
rewriteConfigRewriteLine(state,option,line,1);
}
/* Rewrite the appendonly option. */
void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) {
int force = server.aof_state != REDIS_AOF_OFF;
char *option = "appendonly";
sds line;
line = sdscatprintf(sdsempty(),"%s %s", option,
(server.aof_state == REDIS_AOF_OFF) ? "no" : "yes");
rewriteConfigRewriteLine(state,option,line,force);
}
/* Rewrite the notify-keyspace-events option. */
void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
int force = server.notify_keyspace_events != 0;
@@ -1473,7 +1501,10 @@ void rewriteConfigBindOption(struct rewriteConfigState *state) {
char *option = "bind";
/* Nothing to rewrite if we don't have bind addresses. */
if (server.bindaddr_count == 0) return;
if (server.bindaddr_count == 0) {
rewriteConfigMarkAsProcessed(state,option);
return;
}
/* Rewrite as bind <addr1> <addr2> ... <addrN> */
addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
@@ -1509,6 +1540,7 @@ sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
void rewriteConfigReleaseState(struct rewriteConfigState *state) {
sdsfreesplitres(state->lines,state->numlines);
dictRelease(state->option_to_line);
dictRelease(state->rewritten);
zfree(state);
}
@@ -1526,6 +1558,14 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
while((de = dictNext(di)) != NULL) {
list *l = dictGetVal(de);
sds option = dictGetKey(de);
/* Don't blank lines about options the rewrite process
* don't understand. */
if (dictFind(state->rewritten,option) == NULL) {
redisLog(REDIS_DEBUG,"Not rewritten option: %s", option);
continue;
}
while(listLength(l)) {
listNode *ln = listFirst(l);
@@ -1615,11 +1655,10 @@ int rewriteConfig(char *path) {
/* Step 2: rewrite every single option, replacing or appending it inside
* the rewrite state. */
/* TODO: Turn every default into a define, use it also in
* initServerConfig(). */
rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE);
rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT);
rewriteConfigNumericalOption(state,"tcp-backlog",server.tcp_backlog,REDIS_TCP_BACKLOG);
rewriteConfigBindOption(state);
rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM);
@@ -1652,6 +1691,8 @@ int rewriteConfig(char *path) {
rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);
rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);
rewriteConfigNumericalOption(state,"min-slaves-to-write",server.repl_min_slaves_to_write,REDIS_DEFAULT_MIN_SLAVES_TO_WRITE);
rewriteConfigNumericalOption(state,"min-slaves-max-lag",server.repl_min_slaves_max_lag,REDIS_DEFAULT_MIN_SLAVES_MAX_LAG);
rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
rewriteConfigNumericalOption(state,"maxclients",server.maxclients,REDIS_MAX_CLIENTS);
rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,REDIS_DEFAULT_MAXMEMORY);
@@ -1664,7 +1705,8 @@ int rewriteConfig(char *path) {
"noeviction", REDIS_MAXMEMORY_NO_EVICTION,
NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);
rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES);
rewriteConfigAppendonlyOption(state);
rewriteConfigYesNoOption(state,"appendonly",server.aof_state != REDIS_AOF_OFF,0);
rewriteConfigStringOption(state,"appendfilename",server.aof_filename,REDIS_DEFAULT_AOF_FILENAME);
rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,
"everysec", AOF_FSYNC_EVERYSEC,
"always", AOF_FSYNC_ALWAYS,
+22 -16
View File
@@ -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)) {
@@ -755,13 +755,21 @@ void propagateExpire(redisDb *db, robj *key) {
}
int expireIfNeeded(redisDb *db, robj *key) {
long long when = getExpire(db,key);
mstime_t when = getExpire(db,key);
mstime_t now;
if (when < 0) return 0; /* No expire for this key */
/* Don't expire anything while loading. It will be done later. */
if (server.loading) return 0;
/* If we are in the context of a Lua script, we claim that time is
* blocked to when the Lua script started. This way a key can expire
* only the first time it is accessed and not in the middle of the
* script execution, making propagation to slaves / AOF consistent.
* See issue #1525 on Github for more information. */
now = server.lua_caller ? server.lua_time_start : mstime();
/* If we are running in the context of a slave, return ASAP:
* the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys.
@@ -769,12 +777,10 @@ int expireIfNeeded(redisDb *db, robj *key) {
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
if (server.masterhost != NULL) {
return mstime() > when;
}
if (server.masterhost != NULL) return now > when;
/* Return when this key has not expired */
if (mstime() <= when) return 0;
if (now <= when) return 0;
/* Delete the key */
server.stat_expiredkeys++;
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+4 -3
View File
@@ -84,7 +84,7 @@ void dumpCommand(redisClient *c) {
/* RESTORE key ttl serialized-value */
void restoreCommand(redisClient *c) {
long ttl;
long long ttl;
rio payload;
int type;
robj *obj;
@@ -96,7 +96,7 @@ void restoreCommand(redisClient *c) {
}
/* Check if the TTL value makes sense */
if (getLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != REDIS_OK) {
if (getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != REDIS_OK) {
return;
} else if (ttl < 0) {
addReplyError(c,"Invalid TTL value, must be >= 0");
@@ -104,7 +104,8 @@ void restoreCommand(redisClient *c) {
}
/* Verify RDB version and data checksum. */
if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == REDIS_ERR) {
if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == REDIS_ERR)
{
addReplyError(c,"DUMP payload version or checksum are wrong");
return;
}
+55 -24
View File
@@ -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);
@@ -639,24 +637,33 @@ void freeClient(redisClient *c) {
*
* Note that before doing this we make sure that the client is not in
* some unexpected state, by checking its flags. */
if (server.master &&
(c->flags & REDIS_MASTER) &&
!(c->flags & (REDIS_CLOSE_AFTER_REPLY|
REDIS_CLOSE_ASAP|
REDIS_BLOCKED|
REDIS_UNBLOCKED)))
{
replicationCacheMaster(c);
return;
if (server.master && c->flags & REDIS_MASTER) {
redisLog(REDIS_WARNING,"Connection with master lost.");
if (!(c->flags & (REDIS_CLOSE_AFTER_REPLY|
REDIS_CLOSE_ASAP|
REDIS_BLOCKED|
REDIS_UNBLOCKED)))
{
replicationCacheMaster(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. */
/* Log link disconnection with slave */
if ((c->flags & REDIS_SLAVE) && !(c->flags & REDIS_MONITOR)) {
char ip[REDIS_IP_STR_LEN];
if (anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1) {
redisLog(REDIS_WARNING,"Connection with slave %s:%d lost.",
ip, c->slave_listening_port);
}
}
/* 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 +671,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 +687,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 +720,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 +732,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 +853,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 +870,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 +1179,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). */
+12 -10
View File
@@ -67,17 +67,19 @@ int keyspaceEventsStringToFlags(char *classes) {
sds keyspaceEventsFlagsToString(int flags) {
sds res;
if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL)
return sdsnew("A");
res = sdsempty();
if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,"g",1);
if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,"$",1);
if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,"l",1);
if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,"s",1);
if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,"h",1);
if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,"z",1);
if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL) {
res = sdscatlen(res,"A",1);
} else {
if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,"g",1);
if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,"$",1);
if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,"l",1);
if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,"s",1);
if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,"h",1);
if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,"z",1);
if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
}
if (flags & REDIS_NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
if (flags & REDIS_NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
return res;
+1 -1
View File
@@ -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;
+5 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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
+68 -20
View File
@@ -81,6 +81,8 @@ static struct config {
int pipe_timeout;
int getrdb_mode;
int stat_mode;
int scan_mode;
char *pattern;
char *rdb_filename;
int bigkeys;
int stdinarg; /* get last arg from stdin. (-x option) */
@@ -711,6 +713,10 @@ static int parseOptions(int argc, char **argv) {
config.slave_mode = 1;
} else if (!strcmp(argv[i],"--stat")) {
config.stat_mode = 1;
} else if (!strcmp(argv[i],"--scan")) {
config.scan_mode = 1;
} else if (!strcmp(argv[i],"--pattern")) {
config.pattern = argv[++i];
} else if (!strcmp(argv[i],"--rdb") && !lastarg) {
config.getrdb_mode = 1;
config.rdb_filename = argv[++i];
@@ -770,33 +776,35 @@ static void usage() {
"redis-cli %s\n"
"\n"
"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
" -h <hostname> Server hostname (default: 127.0.0.1)\n"
" -p <port> Server port (default: 6379)\n"
" -s <socket> Server socket (overrides hostname and port)\n"
" -a <password> Password to use when connecting to the server\n"
" -r <repeat> Execute specified command N times\n"
" -h <hostname> Server hostname (default: 127.0.0.1).\n"
" -p <port> Server port (default: 6379).\n"
" -s <socket> Server socket (overrides hostname and port).\n"
" -a <password> Password to use when connecting to the server.\n"
" -r <repeat> Execute specified command N times.\n"
" -i <interval> When -r is used, waits <interval> seconds per command.\n"
" It is possible to specify sub-second times like -i 0.1\n"
" -n <db> Database number\n"
" -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections)\n"
" It is possible to specify sub-second times like -i 0.1.\n"
" -n <db> Database number.\n"
" -x Read last argument from STDIN.\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty)\n"
" --csv Output in CSV format\n"
" --latency Enter a special mode continuously sampling latency\n"
" not a tty).\n"
" --csv Output in CSV format.\n"
" --latency Enter a special mode continuously sampling latency.\n"
" --latency-history Like --latency but tracking latency changes over time.\n"
" Default time interval is 15 sec. Change it using -i.\n"
" --slave Simulate a slave showing commands received from the master\n"
" --slave Simulate a slave showing commands received from the master.\n"
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
" --pipe Transfer raw Redis protocol from stdin to server\n"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data\n"
" --pipe Transfer raw Redis protocol from stdin to server.\n"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
" no reply is received within <n> seconds.\n"
" Default timeout: %d. Use 0 to wait forever.\n"
" --bigkeys Sample Redis keys looking for big keys\n"
" --eval <file> Send an EVAL command using the Lua script at <file>\n"
" --help Output this help and exit\n"
" --version Output version and exit\n"
" --bigkeys Sample Redis keys looking for big keys.\n"
" --scan List all keys using the SCAN command.\n"
" --pattern <pat> Useful with --scan to specify a SCAN pattern.\n"
" --eval <file> Send an EVAL command using the Lua script at <file>.\n"
" --help Output this help and exit.\n"
" --version Output version and exit.\n"
"\n"
"Examples:\n"
" cat /etc/passwd | redis-cli -x set mypasswd\n"
@@ -804,6 +812,8 @@ static void usage() {
" redis-cli -r 100 lpush mylist x\n"
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
" redis-cli --scan --pattern '*:12345*'\n"
"\n"
" (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n"
"\n"
"When no command is given, redis-cli starts in interactive mode.\n"
@@ -1493,6 +1503,35 @@ static void statMode() {
}
}
static void scanMode() {
redisReply *reply;
unsigned long long cur = 0;
do {
if (config.pattern)
reply = redisCommand(context,"SCAN %llu MATCH %s",
cur,config.pattern);
else
reply = redisCommand(context,"SCAN %llu",cur);
if (reply == NULL) {
printf("I/O error\n");
exit(1);
} else if (reply->type == REDIS_REPLY_ERROR) {
printf("ERROR: %s\n", reply->str);
exit(1);
} else {
int j;
cur = strtoull(reply->element[0]->str,NULL,10);
for (j = 0; j < reply->element[1]->elements; j++)
printf("%s\n", reply->element[1]->element[j]->str);
}
freeReplyObject(reply);
} while(cur != 0);
exit(0);
}
int main(int argc, char **argv) {
int firstarg;
@@ -1511,6 +1550,9 @@ int main(int argc, char **argv) {
config.cluster_mode = 0;
config.slave_mode = 0;
config.getrdb_mode = 0;
config.stat_mode = 0;
config.scan_mode = 0;
config.pattern = NULL;
config.rdb_filename = NULL;
config.pipe_mode = 0;
config.pipe_timeout = REDIS_DEFAULT_PIPE_TIMEOUT;
@@ -1566,6 +1608,12 @@ int main(int argc, char **argv) {
statMode();
}
/* Scan mode */
if (config.scan_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
scanMode();
}
/* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) {
/* Note that in repl mode we don't abort on connection error.
+31 -14
View File
@@ -218,7 +218,7 @@ struct redisCommand redisCommandTable[] = {
{"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"arl",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"arlt",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"rR",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
@@ -237,7 +237,7 @@ struct redisCommand redisCommandTable[] = {
{"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
{"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"art",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
{"unsubscribe",unsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
@@ -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). */
@@ -1266,6 +1266,7 @@ void initServerConfig() {
server.runid[REDIS_RUN_ID_SIZE] = '\0';
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
server.port = REDIS_SERVERPORT;
server.tcp_backlog = REDIS_TCP_BACKLOG;
server.bindaddr_count = 0;
server.unixsocket = NULL;
server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;
@@ -1302,7 +1303,7 @@ void initServerConfig() {
server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
server.aof_filename = zstrdup("appendonly.aof");
server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME);
server.requirepass = NULL;
server.rdb_compression = REDIS_DEFAULT_RDB_COMPRESSION;
server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM;
@@ -1469,9 +1470,11 @@ int listenToPort(int port, int *fds, int *count) {
if (server.bindaddr[j] == NULL) {
/* Bind * for both IPv6 and IPv4, we enter here only if
* server.bindaddr_count == 0. */
fds[*count] = anetTcp6Server(server.neterr,port,NULL);
fds[*count] = anetTcp6Server(server.neterr,port,NULL,
server.tcp_backlog);
if (fds[*count] != ANET_ERR) (*count)++;
fds[*count] = anetTcpServer(server.neterr,port,NULL);
fds[*count] = anetTcpServer(server.neterr,port,NULL,
server.tcp_backlog);
if (fds[*count] != ANET_ERR) (*count)++;
/* Exit the loop if we were able to bind * on IPv4 or IPv6,
* otherwise fds[*count] will be ANET_ERR and we'll print an
@@ -1479,10 +1482,12 @@ int listenToPort(int port, int *fds, int *count) {
if (*count) break;
} else if (strchr(server.bindaddr[j],':')) {
/* Bind IPv6 address. */
fds[*count] = anetTcp6Server(server.neterr,port,server.bindaddr[j]);
fds[*count] = anetTcp6Server(server.neterr,port,server.bindaddr[j],
server.tcp_backlog);
} else {
/* Bind IPv4 address. */
fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j]);
fds[*count] = anetTcpServer(server.neterr,port,server.bindaddr[j],
server.tcp_backlog);
}
if (fds[*count] == ANET_ERR) {
redisLog(REDIS_WARNING,
@@ -1523,13 +1528,15 @@ void initServer() {
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
/* Open the TCP listening socket for the user commands. */
if (listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR)
if (server.port != 0 &&
listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR)
exit(1);
/* Open the listening Unix domain socket. */
if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
server.sofd = anetUnixServer(server.neterr,server.unixsocket,
server.unixsocketperm, server.tcp_backlog);
if (server.sofd == ANET_ERR) {
redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
exit(1);
@@ -1918,11 +1925,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 +1940,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 +2423,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 +2444,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) {
+14 -8
View File
@@ -71,6 +71,7 @@
#define REDIS_MIN_HZ 1
#define REDIS_MAX_HZ 500
#define REDIS_SERVERPORT 6379 /* TCP port */
#define REDIS_TCP_BACKLOG 511 /* TCP listen backlog */
#define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */
#define REDIS_DEFAULT_DBNUM 16
#define REDIS_CONFIGLINE_MAX 1024
@@ -81,7 +82,7 @@
#define REDIS_SHARED_BULKHDR_LEN 32
#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */
#define REDIS_AOF_REWRITE_PERC 100
#define REDIS_AOF_REWRITE_MIN_SIZE (1024*1024)
#define REDIS_AOF_REWRITE_MIN_SIZE (64*1024*1024)
#define REDIS_AOF_REWRITE_ITEMS_PER_CMD 64
#define REDIS_SLOWLOG_LOG_SLOWER_THAN 10000
#define REDIS_SLOWLOG_MAX_LEN 128
@@ -113,6 +114,7 @@
#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
#define REDIS_DEFAULT_MAXMEMORY 0
#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 3
#define REDIS_DEFAULT_AOF_FILENAME "appendonly.aof"
#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
#define REDIS_DEFAULT_ACTIVE_REHASHING 1
#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
@@ -228,7 +230,7 @@
#define REDIS_MASTER_FORCE_REPLY (1<<13) /* Queue replies even if is master */
#define REDIS_FORCE_AOF (1<<14) /* Force AOF propagation of current cmd. */
#define REDIS_FORCE_REPL (1<<15) /* Force replication of current cmd. */
#define REDIS_PRE_PSYNC_SLAVE (1<<16) /* Slave don't understand PSYNC. */
#define REDIS_PRE_PSYNC (1<<16) /* Instance don't understand PSYNC. */
/* Client request types */
#define REDIS_REQ_INLINE 1
@@ -366,6 +368,8 @@
* Data types
*----------------------------------------------------------------------------*/
typedef long long mstime_t; /* millisecond time type. */
/* A redis object, that is a type able to hold a string / list / set */
/* The actual Redis Object */
@@ -466,7 +470,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 +479,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) */
@@ -584,6 +586,7 @@ struct redisServer {
int sentinel_mode; /* True if this instance is a Sentinel. */
/* Networking */
int port; /* TCP listening port */
int tcp_backlog; /* TCP listen() backlog */
char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */
int bindaddr_count; /* Number of addresses in server.bindaddr[] */
char *unixsocket; /* UNIX socket path */
@@ -761,8 +764,8 @@ struct redisServer {
redisClient *lua_client; /* The "fake client" to query Redis from Lua */
redisClient *lua_caller; /* The client running EVAL right now, or NULL */
dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
long long lua_time_limit; /* Script timeout in seconds */
long long lua_time_start; /* Start time of script */
mstime_t lua_time_limit; /* Script timeout in milliseconds */
mstime_t lua_time_start; /* Start time of script, milliseconds time */
int lua_write_dirty; /* True if a write command was called during the
execution of the current script. */
int lua_random_dirty; /* True if a random command was called during the
@@ -1025,6 +1028,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 +1171,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);
+79 -24
View File
@@ -458,7 +458,7 @@ void syncCommand(redisClient *c) {
/* If a slave uses SYNC, we are dealing with an old implementation
* of the replication protocol (like redis-cli --slave). Flag the client
* so that we don't expect to receive REPLCONF ACK feedbacks. */
c->flags |= REDIS_PRE_PSYNC_SLAVE;
c->flags |= REDIS_PRE_PSYNC;
}
/* Full resynchronization. */
@@ -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();
@@ -803,6 +829,10 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.master->reploff = server.repl_master_initial_offset;
memcpy(server.master->replrunid, server.repl_master_runid,
sizeof(server.repl_master_runid));
/* If master offset is set to -1, this master is old and is not
* PSYNC capable, so we flag it accordingly. */
if (server.master->reploff == -1)
server.master->flags |= REDIS_PRE_PSYNC;
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
/* Restart the AOF subsystem now that we finished the sync. This
* will trigger an AOF rewrite, and when done will start appending
@@ -965,7 +995,7 @@ int slaveTryPartialResynchronization(int fd) {
/* If we reach this point we receied either an error since the master does
* not understand PSYNC, or an unexpected reply from the master.
* Reply with PSYNC_NOT_SUPPORTED in both cases. */
* Return PSYNC_NOT_SUPPORTED to the caller in both cases. */
if (strncmp(reply,"-ERR",4)) {
/* If it's not an error, log the unexpected event. */
@@ -1205,16 +1235,46 @@ 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;
server.master_repl_offset = 0;
}
/* 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) {
if (listLength(server.slaves) == 0) {
/* If this instance is turned into a master and there are no
* slaves, it inherits the replication offset from the master.
* Under certain conditions this makes replicas comparable by
* replication offset to understand what is the most updated. */
server.master_repl_offset = server.master->reploff;
freeReplicationBacklog();
}
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 +1292,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 +1440,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 +1485,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();
}
@@ -1506,8 +1558,11 @@ void replicationCron(void) {
}
}
/* Send ACK to master from time to time. */
if (server.masterhost && server.master)
/* Send ACK to master from time to time.
* Note that we do not send periodic acks to masters that don't
* support PSYNC and replication offsets. */
if (server.masterhost && server.master &&
!(server.master->flags & REDIS_PRE_PSYNC))
replicationSendAck();
/* If we have attached slaves, PING them from time to time.
@@ -1551,7 +1606,7 @@ void replicationCron(void) {
redisClient *slave = ln->value;
if (slave->replstate != REDIS_REPL_ONLINE) continue;
if (slave->flags & REDIS_PRE_PSYNC_SLAVE) continue;
if (slave->flags & REDIS_PRE_PSYNC) continue;
if ((server.unixtime - slave->repl_ack_time) > server.repl_timeout)
{
char ip[REDIS_IP_STR_LEN];
+1
View File
@@ -86,6 +86,7 @@ static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
if (r->io.file.autosync &&
r->io.file.buffered >= r->io.file.autosync)
{
fflush(r->io.file.fp);
aof_fsync(fileno(r->io.file.fp));
r->io.file.buffered = 0;
}
+4 -4
View File
@@ -441,7 +441,7 @@ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
REDIS_NOTUSED(ar);
REDIS_NOTUSED(lua);
elapsed = (ustime()/1000) - server.lua_time_start;
elapsed = mstime() - server.lua_time_start;
if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
server.lua_timedout = 1;
@@ -895,12 +895,12 @@ 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. */
server.lua_caller = c;
server.lua_time_start = ustime()/1000;
server.lua_time_start = mstime();
server.lua_kill = 0;
if (server.lua_time_limit > 0 && server.masterhost == NULL) {
lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
@@ -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);
+2 -2
View File
@@ -383,7 +383,7 @@ sds sdstrim(sds s, const char *cset) {
* Example:
*
* s = sdsnew("Hello World");
* sdstrim(s,1,-1); => "ello Worl"
* sdsrange(s,1,-1); => "ello World"
*/
void sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
@@ -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) {
+216 -46
View File
@@ -44,8 +44,6 @@ extern char **environ;
/* ======================== Sentinel global state =========================== */
typedef long long mstime_t; /* millisecond time type. */
/* Address object, used to describe an ip:port pair. */
typedef struct sentinelAddr {
char *ip;
@@ -135,7 +133,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. */
@@ -370,6 +368,7 @@ dictType leaderVotesDictType = {
void sentinelCommand(redisClient *c);
void sentinelInfoCommand(redisClient *c);
void sentinelSetCommand(redisClient *c);
struct redisCommand sentinelcmds[] = {
{"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
@@ -393,7 +392,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;
@@ -942,7 +941,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
}
/* Release this instance and all its slaves, sentinels, hiredis connections.
* This function also takes care of unlinking the instance from the main
* This function does not take care of unlinking the instance from the main
* masters table (if it is a master) or from its master sentinels/slaves table
* if it is a slave or sentinel. */
void releaseSentinelRedisInstance(sentinelRedisInstance *ri) {
@@ -1133,6 +1132,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 +1475,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 +1505,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 +1614,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;
}
@@ -1635,8 +1639,6 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
sds *lines;
int numlines, j;
int role = 0;
int runid_changed = 0; /* true if runid changed. */
int first_runid = 0; /* true if this is the first runid we receive. */
/* The following fields must be reset to a given value in the case they
* are not found at all in the INFO output. */
@@ -1652,10 +1654,8 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
if (sdslen(l) >= 47 && !memcmp(l,"run_id:",7)) {
if (ri->runid == NULL) {
ri->runid = sdsnewlen(l+7,40);
first_runid = 1;
} else {
if (strncmp(ri->runid,l+7,40) != 0) {
runid_changed = 1;
sentinelEvent(REDIS_NOTICE,"+reboot",ri,"%@");
sdsfree(ri->runid);
ri->runid = sdsnewlen(l+7,40);
@@ -1759,22 +1759,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 +2027,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 +2080,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);
@@ -2269,6 +2269,30 @@ void addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) {
addReplyBulkCString(c,"quorum");
addReplyBulkLongLong(c,ri->quorum);
fields++;
addReplyBulkCString(c,"down-after-milliseconds");
addReplyBulkLongLong(c,ri->down_after_period);
fields++;
addReplyBulkCString(c,"failover-timeout");
addReplyBulkLongLong(c,ri->failover_timeout);
fields++;
addReplyBulkCString(c,"parallel-syncs");
addReplyBulkLongLong(c,ri->parallel_syncs);
fields++;
if (ri->notification_script) {
addReplyBulkCString(c,"notification-script");
addReplyBulkCString(c,ri->notification_script);
fields++;
}
if (ri->client_reconfig_script) {
addReplyBulkCString(c,"client-reconfig-script");
addReplyBulkCString(c,ri->client_reconfig_script);
fields++;
}
}
/* Only slaves */
@@ -2355,8 +2379,15 @@ void sentinelCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"masters")) {
/* SENTINEL MASTERS */
if (c->argc != 2) goto numargserr;
addReplyDictOfRedisInstances(c,sentinel.masters);
} else if (!strcasecmp(c->argv[1]->ptr,"master")) {
/* SENTINEL MASTER <name> */
sentinelRedisInstance *ri;
if (c->argc != 3) goto numargserr;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
addReplySentinelRedisInstance(c,ri);
} else if (!strcasecmp(c->argv[1]->ptr,"slaves")) {
/* SENTINEL SLAVES <master-name> */
sentinelRedisInstance *ri;
@@ -2374,7 +2405,7 @@ void sentinelCommand(redisClient *c) {
return;
addReplyDictOfRedisInstances(c,ri->sentinels);
} else if (!strcasecmp(c->argv[1]->ptr,"is-master-down-by-addr")) {
/* SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid> */
/* SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>*/
sentinelRedisInstance *ri;
long long req_epoch;
uint64_t leader_epoch = 0;
@@ -2457,6 +2488,53 @@ void sentinelCommand(redisClient *c) {
if (c->argc != 2) goto numargserr;
sentinelPendingScriptsCommand(c);
} else if (!strcasecmp(c->argv[1]->ptr,"monitor")) {
/* SENTINEL MONITOR <name> <ip> <port> <quorum> */
long quorum, port;
char buf[32];
if (c->argc != 6) goto numargserr;
if (getLongFromObjectOrReply(c,c->argv[5],&quorum,"Invalid quorum")
!= REDIS_OK) return;
if (getLongFromObjectOrReply(c,c->argv[4],&port,"Invalid port")
!= REDIS_OK) return;
/* Make sure the IP field is actually a valid IP before passing it
* to createSentinelRedisInstance(), otherwise we may trigger a
* DNS lookup at runtime. */
if (anetResolveIP(NULL,c->argv[3]->ptr,buf,sizeof(buf)) == ANET_ERR) {
addReplyError(c,"Invalid IP address specified");
return;
}
if (createSentinelRedisInstance(c->argv[2]->ptr,SRI_MASTER,
c->argv[3]->ptr,port,quorum,NULL) == NULL)
{
switch(errno) {
case EBUSY:
addReplyError(c,"Duplicated master name");
break;
case EINVAL:
addReplyError(c,"Invalid port number");
break;
default:
addReplyError(c,"Unspecified error adding the instance");
break;
}
} else {
sentinelFlushConfig();
addReply(c,shared.ok);
}
} else if (!strcasecmp(c->argv[1]->ptr,"remove")) {
/* SENTINEL REMOVE <name> */
sentinelRedisInstance *ri;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
dictDelete(sentinel.masters,c->argv[2]->ptr);
sentinelFlushConfig();
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"set")) {
if (c->argc < 3 || c->argc % 2 == 0) goto numargserr;
sentinelSetCommand(c);
} else {
addReplyErrorFormat(c,"Unknown sentinel subcommand '%s'",
(char*)c->argv[1]->ptr);
@@ -2464,10 +2542,11 @@ void sentinelCommand(redisClient *c) {
return;
numargserr:
addReplyErrorFormat(c,"Wrong number of commands for 'sentinel %s'",
addReplyErrorFormat(c,"Wrong number of arguments for 'sentinel %s'",
(char*)c->argv[1]->ptr);
}
/* SENTINEL INFO [section] */
void sentinelInfoCommand(redisClient *c) {
char *section = c->argc == 2 ? c->argv[1]->ptr : "default";
sds info = sdsempty();
@@ -2527,6 +2606,92 @@ void sentinelInfoCommand(redisClient *c) {
addReply(c,shared.crlf);
}
/* SENTINEL SET <mastername> [<option> <value> ...] */
void sentinelSetCommand(redisClient *c) {
sentinelRedisInstance *ri;
int j, changes = 0;
char *option, *value;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
/* Process option - value pairs. */
for (j = 3; j < c->argc; j += 2) {
option = c->argv[j]->ptr;
value = c->argv[j+1]->ptr;
robj *o = c->argv[j+1];
long long ll;
if (!strcasecmp(option,"down-after-milliseconds")) {
/* down-after-millisecodns <milliseconds> */
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)
goto badfmt;
ri->down_after_period = ll;
changes++;
} else if (!strcasecmp(option,"failover-timeout")) {
/* failover-timeout <milliseconds> */
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)
goto badfmt;
ri->failover_timeout = ll;
changes++;
} else if (!strcasecmp(option,"parallel-syncs")) {
/* parallel-syncs <milliseconds> */
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)
goto badfmt;
ri->parallel_syncs = ll;
changes++;
} else if (!strcasecmp(option,"notification-script")) {
/* notification-script <path> */
if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c,
"Notification script seems non existing or non executable");
if (changes) sentinelFlushConfig();
return;
}
sdsfree(ri->notification_script);
ri->notification_script = strlen(value) ? sdsnew(value) : NULL;
changes++;
} else if (!strcasecmp(option,"client-reconfig-script")) {
/* client-reconfig-script <path> */
if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c,
"Client reconfiguration script seems non existing or "
"non executable");
if (changes) sentinelFlushConfig();
return;
}
sdsfree(ri->client_reconfig_script);
ri->client_reconfig_script = strlen(value) ? sdsnew(value) : NULL;
changes++;
} else if (!strcasecmp(option,"auth-pass")) {
/* auth-pass <password> */
sdsfree(ri->auth_pass);
ri->auth_pass = strlen(value) ? sdsnew(value) : NULL;
changes++;
} else if (!strcasecmp(option,"quorum")) {
/* quorum <count> */
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0)
goto badfmt;
ri->quorum = ll;
changes++;
} else {
addReplyErrorFormat(c,"Unknown option '%s' for SENTINEL SET",
option);
if (changes) sentinelFlushConfig();
return;
}
}
if (changes) sentinelFlushConfig();
addReply(c,shared.ok);
return;
badfmt: /* Bad format errors */
if (changes) sentinelFlushConfig();
addReplyErrorFormat(c,"Invalid argument '%s' for SENTINEL SET '%s'",
value, option);
}
/* ===================== SENTINEL availability checks ======================= */
/* Is this instance down from our point of view? */
@@ -2654,6 +2819,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 +2901,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;
@@ -3229,17 +3396,12 @@ void sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) {
void sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {
sentinelRedisInstance *ref = master->promoted_slave ?
master->promoted_slave : master;
sds old_master_ip;
int old_master_port;
sentinelEvent(REDIS_WARNING,"+switch-master",master,"%s %s %d %s %d",
master->name, master->addr->ip, master->addr->port,
ref->addr->ip, ref->addr->port);
old_master_ip = sdsdup(master->addr->ip);
old_master_port = master->addr->port;
sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);
sdsfree(old_master_ip);
}
void sentinelFailoverStateMachine(sentinelRedisInstance *ri) {
@@ -3387,5 +3549,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
View File
@@ -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
View File
@@ -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
View File
@@ -825,6 +825,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
while((ele = setTypeNextObject(si)) != NULL) {
for (j = 1; j < setnum; j++) {
if (!sets[j]) continue; /* no key is an empty set. */
if (sets[j] == sets[0]) break; /* same set! */
if (setTypeIsMember(sets[j],ele)) break;
}
if (j == setnum) {
+1 -1
View File
@@ -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
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.8.0"
#define REDIS_VERSION "2.8.5"
+1 -1
View File
@@ -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
View File
@@ -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) {
+23 -1
View File
@@ -204,6 +204,7 @@ proc test_server_main {} {
# Setup global state for the test server
set ::idle_clients {}
set ::active_clients {}
array set ::active_clients_task {}
array set ::clients_start_time {}
set ::clients_time_history {}
set ::failed_tests {}
@@ -217,9 +218,12 @@ proc test_server_main {} {
# may be used in the future in order to detect test clients taking too much
# time to execute the task.
proc test_server_cron {} {
# Do some work here.
after 100 test_server_cron
}
proc accept_test_clients {fd addr port} {
fconfigure $fd -encoding binary
fileevent $fd readable [list read_from_test_client $fd]
}
@@ -253,14 +257,17 @@ proc read_from_test_client fd {
puts "\[$completed_tests_count/$all_tests_count [colorstr yellow $status]\]: $data ($elapsed seconds)"
lappend ::clients_time_history $elapsed $data
signal_idle_client $fd
set ::active_clients_task($fd) DONE
} elseif {$status eq {ok}} {
if {!$::quiet} {
puts "\[[colorstr green $status]\]: $data"
}
set ::active_clients_task($fd) "(OK) $data"
} elseif {$status eq {err}} {
set err "\[[colorstr red $status]\]: $data"
puts $err
lappend ::failed_tests $err
set ::active_clients_task($fd) "(ERR) $data"
} elseif {$status eq {exception}} {
puts "\[[colorstr red $status]\]: $data"
foreach p $::clients_pids {
@@ -268,7 +275,7 @@ proc read_from_test_client fd {
}
exit 1
} elseif {$status eq {testing}} {
# No op
set ::active_clients_task($fd) "(IN PROGRESS) $data"
} else {
if {!$::quiet} {
puts "\[$status\]: $data"
@@ -282,10 +289,24 @@ proc signal_idle_client fd {
# Remove this fd from the list of active clients.
set ::active_clients \
[lsearch -all -inline -not -exact $::active_clients $fd]
if 0 {
# The following loop is only useful for debugging tests that may
# enter an infinite loop. Commented out normally.
foreach x $::active_clients {
if {[info exist ::active_clients_task($x)]} {
puts "$x => $::active_clients_task($x)"
} else {
puts "$x => ???"
}
}
}
# New unit to process?
if {$::next_test != [llength $::all_tests]} {
if {!$::quiet} {
puts [colorstr bold-white "Testing [lindex $::all_tests $::next_test]"]
set ::active_clients_task($fd) "ASSIGNED: $fd ([lindex $::all_tests $::next_test])"
}
set ::clients_start_time($fd) [clock seconds]
send_data_packet $fd run [lindex $::all_tests $::next_test]
@@ -326,6 +347,7 @@ proc the_end {} {
# to read the command, execute, reply... all this in a loop.
proc test_client_main server_port {
set ::test_server_fd [socket localhost $server_port]
fconfigure $::test_server_fd -encoding binary
send_data_packet $::test_server_fd ready [pid]
while 1 {
set bytes [gets $::test_server_fd]
+10
View File
@@ -16,6 +16,16 @@ start_server {tags {"dump"}} {
r get foo
} {bar}
test {RESTORE can set an expire that overflows a 32 bit integer} {
r set foo bar
set encoded [r dump foo]
r del foo
r restore foo 2569591501 $encoded
set ttl [r pttl foo]
assert {$ttl >= (2569591501-3000) && $ttl <= 2569591501}
r get foo
} {bar}
test {RESTORE returns an error of the key already exists} {
r set foo bar
set e {}
+1 -1
View File
@@ -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]
+8
View File
@@ -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
+11
View File
@@ -356,4 +356,15 @@ start_server {tags {"pubsub"}} {
r config set maxmemory 0
$rd1 close
}
test "Keyspace notifications: test CONFIG GET/SET of event flags" {
r config set notify-keyspace-events gKE
assert_equal {gKE} [lindex [r config get notify-keyspace-events] 1]
r config set notify-keyspace-events {$lshzxeKE}
assert_equal {$lshzxeKE} [lindex [r config get notify-keyspace-events] 1]
r config set notify-keyspace-events KA
assert_equal {AK} [lindex [r config get notify-keyspace-events] 1]
r config set notify-keyspace-events EA
assert_equal {AE} [lindex [r config get notify-keyspace-events] 1]
}
}
+6
View File
@@ -214,6 +214,12 @@ start_server {
r sdiff set1 set2 set3
} {}
test "SDIFF with same set two times" {
r del set1
r sadd set1 a b c 1 2 3 4 5 6
r sdiff set1 set1
} {}
test "SDIFF fuzzing" {
for {set j 0} {$j < 100} {incr j} {
unset -nocomplain s
+12 -55
View File
@@ -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