Compare commits

...
110 Commits
Author SHA1 Message Date
antirez 1cb6effdc8 Redis 3.2.11. 2017-09-21 16:20:18 +02:00
Oran Agra 13e8e53806 Flush append only buffers before existing.
when SHUTDOWN command is recived it is possible that some of the recent
command were not yet flushed from the AOF buffer, and the server
experiences data loss at shutdown.
2017-09-18 12:04:33 +02:00
antirez c9ab43ef7c Redis 3.2.10. 2017-07-28 17:59:00 +02:00
Jan-Erik Rediger bf50875353 Check that the whole first argument is a number
Fixes #2258
2017-07-24 15:21:44 +02:00
WuYunlong 8774228cc5 fix rewrite config: auto-aof-rewrite-min-size 2017-07-24 15:21:14 +02:00
Byron Grobe ab900303b8 Fixed issue #1996 (Missing '-' in help message for redis-benchmark) 2017-07-24 15:21:14 +02:00
Jan-Erik Rediger 7fcca9baef Don't use extended Regexp Syntax
It's not POSIX (BSD systems have -E instead) and we don't actually need it.

Closes #1922
2017-07-24 15:21:14 +02:00
Leon Chen 41963fe676 fix return wrong value of clusterDelNodeSlots 2017-07-24 14:18:56 +02:00
Leon Chen 97d50344c4 fix mismatch argument 2017-07-24 14:18:56 +02:00
liangsijian e39849808c Fix lua ldb command log 2017-07-24 14:11:36 +02:00
antirez 84a4f20278 Make representClusterNodeFlags() more robust.
This function failed when an internal-only flag was set as an only flag
in a node: the string was trimmed expecting a final comma before
exiting the function, causing a crash. See issue #4142.
Moreover generation of flags representation only needed at DEBUG log
level was always performed: a waste of CPU time. This is fixed as well
by this commit.
2017-07-24 14:09:50 +02:00
antirez 5aa2525006 Fix isHLLObjectOrReply() to handle integer encoded strings.
Close #3766.
2017-07-11 12:46:30 +02:00
antirez 7018d27dfe Fix abort typo in Lua debugger help screen. 2017-06-30 12:12:17 +02:00
antirez d557144ebb Added GEORADIUS(BYMEMBER)_RO variants for read-only operations.
Issue #4084 shows how for a design error, GEORADIUS is a write command
because of the STORE option. Because of this it does not work
on readonly slaves, gets redirected to masters in Redis Cluster even
when the connection is in READONLY mode and so forth.

To break backward compatibility at this stage, with Redis 4.0 to be in
advanced RC state, is problematic for the user base. The API can be
fixed into the unstable branch soon if we'll decide to do so in order to
be more consistent, and reease Redis 5.0 with this incompatibility in
the future. This is still unclear.

However, the ability to scale GEO queries in slaves easily is too
important so this commit adds two read-only variants to the GEORADIUS
and GEORADIUSBYMEMBER command: GEORADIUS_RO and GEORADIUSBYMEMBER_RO.
The commands are exactly as the original commands, but they do not
accept the STORE and STOREDIST options.
2017-06-30 11:53:45 +02:00
Suraj Narkhede a309388dff Fix following issues in blocking commands:
1. brpop last key index, thus checking all keys for slots.
2. Memory leak in clusterRedirectBlockedClientIfNeeded.
3. Remove while loop in clusterRedirectBlockedClientIfNeeded.
2017-06-27 18:06:22 +02:00
Suraj Narkhede 55442262e1 Fix brpop command table entry and redirect blocked clients. 2017-06-27 18:06:02 +02:00
antirez e3641c8dee Fix PERSIST expired key resuscitation issue #4048. 2017-06-27 18:05:37 +02:00
Antonio Mallia 9ce105add5 Removed duplicate 'sys/socket.h' include 2017-06-27 18:03:22 +02:00
Zachary Marquez e084a394a6 Prevent expirations and evictions while paused
Proposed fix to https://github.com/antirez/redis/issues/4027
2017-06-27 18:03:00 +02:00
antirez 61c78a5215 Collect fork() timing info only if fork succeeded. 2017-06-27 17:56:07 +02:00
antirez d067e3342f Aesthetic changes to #4068 PR to conform to Redis coding standard.
1. Inline if ... statement if short.
2. No lines over 80 columns.
2017-06-22 11:01:51 +02:00
xuzhou 2b0f03e4f2 Optimize set command with ex/px when updating aof. 2017-06-22 11:01:51 +02:00
antirez ad949f2329 redis-benchmark: add -t hset target. 2017-06-22 11:01:47 +02:00
xuzhou 351663bd7b Fix set with ex/px option when propagated to aof 2017-06-22 11:01:39 +02:00
minghang.zmh d70ac1d105 fix server.stat_net_output_bytes calc bug 2017-06-20 17:03:35 +02:00
xuchengxuan 8da9a167e0 Fixed comments of slowlog duration 2017-06-20 16:56:49 +02:00
cbgbt 13546adcb9 cli: Only print elapsed time on OUTPUT_STANDARD 2017-06-20 16:54:47 +02:00
Aric Huang 373facf81c (fix) Update create-cluster README
Fix a few typos/adjust wording in `create-cluster` README
2017-06-20 16:54:47 +02:00
Salvatore Sanfilippo 29e3ff9bec Merge pull request #3926 from QuChen88/3.2
Implement getKeys procedure for georadius and georadiusbymember commands
2017-06-14 09:12:17 -07:00
antirez 5116130d42 Redis 3.2.9. 2017-05-17 17:39:00 +02:00
antirez 3b46cf97de redis-cli --bigkeys: show error when TYPE fails.
Close #3993.
2017-05-15 11:23:57 +02:00
antirez f59b4b93d9 Fix preprocessor if/else chain broken in order to fix #3927. 2017-04-21 11:28:02 +02:00
antirez dd80fedf6f Fix zmalloc_get_memory_size() ifdefs to actually use the else branch.
Close #3927.
2017-04-21 11:27:28 +02:00
antirez 697d3abeff Set lua-time-limit default value at safe place.
Otherwise, as it was, it will overwrite whatever the user set.

Close #3703.
2017-04-21 11:26:27 +02:00
antirez c9c04b11b9 Fix #3848 by closing the descriptor on error. 2017-04-21 11:25:08 +02:00
张文康 d3b4992450 update block->free after some diff data are written to the child process 2017-04-20 07:59:46 +02:00
antirez 6a33952b8a Test: fix, hopefully, false PSYNC failure like in issue #2715.
And many other related Github issues... all reporting the same problem.
There was probably just not enough backlog in certain unlucky runs.
I'll ask people that can reporduce if they see now this as fixed as
well.
2017-04-14 17:54:06 +02:00
Qu Chen bacb04f89f Implement getKeys procedure for georadius and georadiusbymember
commands.
2017-04-08 18:37:02 +00:00
John.Koepi b83f9fea81 fix #2883, #2857 pipe fds leak when fork() failed on bg aof rw 2017-02-20 10:28:53 +01:00
antirez 10dbb5cd33 Don't leak file descriptor on syncWithMaster().
Close #3804.
2017-02-20 10:28:52 +01:00
antirez db8a945cbb Redis 3.2.8. 2017-02-12 16:14:57 +01:00
antirez 7178cac031 Revert "Jemalloc updated to 4.4.0."
This reverts commit 153f2f00ea.

Jemalloc 4.4.0 is apparently causing deadlocks in certain
systems. See for example https://github.com/antirez/redis/issues/3799.
As a cautionary step we are reverting the commit back and
releasing a new stable Redis version.
2017-02-12 16:07:13 +01:00
antirez 33fad43c0f Fix MIGRATE closing of cached socket on error.
After investigating issue #3796, it was discovered that MIGRATE
could call migrateCloseSocket() after the original MIGRATE c->argv
was already rewritten as a DEL operation. As a result the host/port
passed to migrateCloseSocket() could be anything, often a NULL pointer
that gets deferenced crashing the server.

Now the socket is closed at an earlier time when there is a socket
error in a later stage where no retry will be performed, before we
rewrite the argument vector. Moreover a check was added so that later,
in the socket_err label, there is no further attempt at closing the
socket if the argument was rewritten.

This fix should resolve the bug reported in #3796.
2017-02-09 10:15:52 +01:00
antirez af12f8ec3c Redis 3.2.7. 2017-01-31 16:32:23 +01:00
antirez 3876d985a8 Ziplist: insertion bug under particular conditions fixed.
Ziplists had a bug that was discovered while investigating a different
issue, resulting in a corrupted ziplist representation, and a likely
segmentation foult and/or data corruption of the last element of the
ziplist, once the ziplist is accessed again.

The bug happens when a specific set of insertions / deletions is
performed so that an entry is encoded to have a "prevlen" field (the
length of the previous entry) of 5 bytes but with a count that could be
encoded in a "prevlen" field of a since byte. This could happen when the
"cascading update" process called by ziplistInsert()/ziplistDelete() in
certain contitious forces the prevlen to be bigger than necessary in
order to avoid too much data moving around.

Once such an entry is generated, inserting a very small entry
immediately before it will result in a resizing of the ziplist for a
count smaller than the current ziplist length (which is a violation,
inserting code expects the ziplist to get bigger actually). So an FF
byte is inserted in a misplaced position. Moreover a realloc() is
performed with a count smaller than the ziplist current length so the
final bytes could be trashed as well.

SECURITY IMPLICATIONS:

Currently it looks like an attacker can only crash a Redis server by
providing specifically choosen commands. However a FF byte is written
and there are other memory operations that depend on a wrong count, so
even if it is not immediately apparent how to mount an attack in order
to execute code remotely, it is not impossible at all that this could be
done. Attacks always get better... and we did not spent enough time in
order to think how to exploit this issue, but security researchers
or malicious attackers could.

REPRODUCING:

The bug can be reproduced with the following commands.

    redis-cli del list
    redis-cli rpush list one
    redis-cli rpush list two
    redis-cli rpush list
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    redis-cli rpush list
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    redis-cli rpush list three
    redis-cli rpush list a
    redis-cli lrem list 1
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    redis-cli linsert list after
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    10
    redis-cli lrange list 0 -1

Instead of "rpush list a", use "rpush list 10" in order to trigger a
data corruption instead of a crash.
2017-01-30 16:34:13 +01:00
antirez 153f2f00ea Jemalloc updated to 4.4.0.
The original jemalloc source tree was modified to:

1. Remove the configure error that prevents nested builds.
2. Insert the Redis private Jemalloc API in order to allow the
Redis fragmentation function to work.
2017-01-30 10:11:36 +01:00
miter ca532c94ef Change switch statment to if statment 2017-01-30 10:10:30 +01:00
oranagra a73503530e fix rare assertion in DEBUG DIGEST
getExpire calls dictFind which can do rehashing.
found by calling computeDatasetDigest from serverCron and running the test suite.
2017-01-30 10:10:21 +01:00
Itamar Haber b917e3fdb3 Verify pairs are provided after subcommands
Fixes https://github.com/antirez/redis/issues/3639
2017-01-30 10:10:14 +01:00
antirez 1177cf6395 Avoid geo.c warning in initialization.
This problem was properly solved in 4.0 / unstable. Here is just a quick
fix for the warning.
2017-01-30 09:16:05 +01:00
antirez 874804da0c Security: Cross Protocol Scripting protection.
This is an attempt at mitigating problems due to cross protocol
scripting, an attack targeting services using line oriented protocols
like Redis that can accept HTTP requests as valid protocol, by
discarding the invalid parts and accepting the payloads sent, for
example, via a POST request.

For this to be effective, when we detect POST and Host: and terminate
the connection asynchronously, the networking code was modified in order
to never process further input. It was later verified that in a
pipelined request containing a POST command, the successive commands are
not executed.
2017-01-30 09:08:58 +01:00
antirez 273cd7ff51 Ziplist: remove static from functions, they prevent good crash reports. 2017-01-27 10:49:53 +01:00
Jan-Erik Rediger 389b9f5945 Initialize help only in repl mode 2017-01-27 10:49:47 +01:00
Yossi Gottlieb 1370a88887 Fix redis-cli rare crash.
This happens if the server (mysteriously) returns an unexpected response
to the COMMAND command.
2016-12-21 11:03:44 +01:00
antirez 68aab8e811 MIGRATE: Remove upfront ttl initialization.
After the fix for #3673 the ttl var is always initialized inside the
loop itself, so the early initialization is not needed.

Variables declaration also moved to a more local scope.
2016-12-14 12:44:32 +01:00
Jan-Erik Rediger 788e892534 Reset the ttl for additional keys
Before, if a previous key had a TTL set but the current one didn't, the
TTL was reused and thus resulted in wrong expirations set.

This behaviour was experienced, when `MigrateDefaultPipeline` in
redis-trib was set to >1

Fixes #3655
2016-12-14 12:41:21 +01:00
antirez 77de5c2b71 Redis 3.2.6. 2016-12-06 09:38:33 +01:00
antirez f20f3ead1b Geo: improve fuzz test.
The test now uses more diverse radius sizes, especially sizes near or
greater the whole earth surface are used, that are known to trigger edge
cases. Moreover the PRNG seeding was probably resulting into the same
sequence tested over and over again, now seeding unsing the current unix
time in milliseconds.

Related to #3631.
2016-12-05 14:19:03 +01:00
antirez 8c22086c34 Geo: fix computation of bounding box.
A bug was reported in the context in issue #3631. The root cause of the
bug was that certain neighbor boxes were zeroed after the "inside the
bounding box or not" check, simply because the bounding box computation
function was wrong.

A few debugging infos where enhanced and moved in other parts of the
code. A check to avoid steps=0 was added, but is unrelated to this
issue and I did not verified it was an actual bug in practice.
2016-12-05 14:18:59 +01:00
antirez 92958df3b1 Cluster: handle zero bytes at the end of nodes.conf. 2016-11-16 14:13:13 +01:00
antirez 0ee28ae40c Test: regression test for #3564 added. 2016-10-31 15:47:09 +01:00
sunhe 289fadb2d0 bitops.c/bitfieldCommand: update higest_write_offset with check 2016-10-31 15:41:40 +01:00
jybaek f4f7d2c37f Add missing fclose() 2016-10-31 15:36:54 +01:00
antirez f61d6f0c4a Redis 3.2.5. 2016-10-26 09:17:45 +02:00
zach shipko 473640790f BSDs don't have -ldl 2016-10-26 09:11:04 +02:00
antirez 9ada818b86 Fix modules compilation when libc malloc is used.
Compiling Redis worked as a side effect of jemalloc target specifying
-ldl as needed linker options, otherwise it is not provided during
linking and dlopen() API will remain unresolved symbols.
2016-10-26 09:11:01 +02:00
antirez 381651fac0 3.2.4 release notes clarifications. 2016-09-26 09:18:59 +02:00
antirez 070d047179 Redis 3.2.4. 2016-09-26 09:10:17 +02:00
antirez 0539634777 Security: CONFIG SET client-output-buffer-limit overflow fixed.
This commit fixes a vunlerability reported by Cory Duplantis
of Cisco Talos, see TALOS-2016-0206 for reference.

CONFIG SET client-output-buffer-limit accepts as client class "master"
which is actually only used to implement CLIENT KILL. The "master" class
has ID 3. What happens is that the global structure:

    server.client_obuf_limits[class]

Is accessed with class = 3. However it is a 3 elements array, so writing
the 4th element means to write up to 24 bytes of memory *after* the end
of the array, since the structure is defined as:

    typedef struct clientBufferLimitsConfig {
        unsigned long long hard_limit_bytes;
        unsigned long long soft_limit_bytes;
        time_t soft_limit_seconds;
    } clientBufferLimitsConfig;

EVALUATION OF IMPACT:

Checking what's past the boundaries of the array in the global
'server' structure, we find AOF state fields:

    clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];
    /* AOF persistence */
    int aof_state;                  /* AOF_(ON|OFF|WAIT_REWRITE) */
    int aof_fsync;                  /* Kind of fsync() policy */
    char *aof_filename;             /* Name of the AOF file */
    int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */
    int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */
    off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */
    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */
    off_t aof_current_size;         /* AOF current size. */

Writing to most of these fields should be harmless and only cause problems in
Redis persistence that should not escalate to security problems.
However unfortunately writing to "aof_filename" could be potentially a
security issue depending on the access pattern.

Searching for "aof.filename" accesses in the source code returns many different
usages of the field, including using it as input for open(), logging to the
Redis log file or syslog, and calling the rename() syscall.

It looks possible that attacks could lead at least to informations
disclosure of the state and data inside Redis. However note that the
attacker must already have access to the server. But, worse than that,
it looks possible that being able to change the AOF filename can be used
to mount more powerful attacks: like overwriting random files with AOF
data (easily a potential security issue as demostrated here:
http://antirez.com/news/96), or even more subtle attacks where the
AOF filename is changed to a path were a malicious AOF file is loaded
in order to exploit other potential issues when the AOF parser is fed
with untrusted input (no known issue known currently).

The fix checks the places where the 'master' class is specifiedf in
order to access configuration data structures, and return an error in
this cases.

WHO IS AT RISK?

The "master" client class was introduced in Redis in Jul 28 2015.
Every Redis instance released past this date is not vulnerable
while all the releases after this date are. Notably:

    Redis 3.0.x is NOT vunlerable.
    Redis 3.2.x IS vulnerable.
    Redis unstable is vulnerable.

In order for the instance to be at risk, at least one of the following
conditions must be true:

    1. The attacker can access Redis remotely and is able to send
       the CONFIG SET command (often banned in managed Redis instances).

    2. The attacker is able to control the "redis.conf" file and
       can wait or trigger a server restart.

The problem was fixed 26th September 2016 in all the releases affected.
2016-09-26 08:56:37 +02:00
antirez c01abcdebf fix the fix for the TCP binding.
This commit attempts to fix a problem with PR #3467.
2016-09-12 10:56:25 +02:00
oranagra a6d0698b22 fix tcp binding when IPv6 is unsupported 2016-09-12 10:56:21 +02:00
antirez 22b6c28d25 debug.c: no need to define _GNU_SOURCE, is defined in fmacros.h. 2016-09-09 11:13:29 +02:00
antirez 9e9d398beb crash log - improve code dump with more info and called symbols. 2016-09-09 11:13:24 +02:00
oranagra 3745c5dc81 crash log - add hex dump of function code 2016-09-09 11:13:21 +02:00
antirez c1cc07b25d Sentinel example config: warn about protected mode. 2016-08-11 19:53:15 +02:00
rojingeorge 011dc9f9a5 Display the nodes summary once the cluster is established using redis-trib.rb
Display the nodes summary once the cluster is established using
redis-trib.rb
After the cluster meet and join was done, when the summary was shown, it
was giving info regarding the nodes. This fix ensures that confusion
where the slaves were shown as masters.
Fix would be to reset the nodes and reload the cluster information
before checking the cluster status after creating it.
2016-08-05 15:11:21 +02:00
Guo Xiao f4e3a940c4 Use the standard predefined identifier __func__ (since C99)
Fix warning: ISO C does not support '__FUNCTION__' predefined identifier
[-Wpedantic]
2016-08-04 10:26:23 +02:00
antirez 44bf2533cd Redis 3.2.3. 2016-08-02 11:00:29 +02:00
Salvatore Sanfilippo 2444c82a3f Merge pull request #3426 from QuChen88/3.2
Fix a bug to delay bgsave while AOF rewrite in progress for replication
2016-08-02 10:43:05 +02:00
Qu Chen e67ad1d1eb Fix a bug to delay bgsave while AOF rewrite in progress for replication 2016-08-01 12:16:17 -07:00
antirez 71536684a7 Update linenoise to fix insecure redis-cli history file creation.
The problem was fixed in antirez/linenoise repository applying a patch
contributed by @lamby. Here the new version is updated in the Redis
source tree.

Close #1418
Close #3322
2016-07-29 11:29:47 +02:00
antirez 7c6e288d76 Redis 3.2.2. 2016-07-28 14:53:24 +02:00
antirez 0a45fbc302 Ability of slave to announce arbitrary ip/port to master.
This feature is useful, especially in deployments using Sentinel in
order to setup Redis HA, where the slave is executed with NAT or port
forwarding, so that the auto-detected port/ip addresses, as listed in
the "INFO replication" output of the master, or as provided by the
"ROLE" command, don't match the real addresses at which the slave is
reachable for connections.
2016-07-28 13:05:19 +02:00
antirez c3982c0905 redis-benchmark: new option to show server errors on stdout.
Disabled by default, can be activated with -e. Maybe the reverse was
more safe but departs from the past behavior.
2016-07-28 13:04:42 +02:00
antirez fdafe23315 Multiple GEORADIUS bugs fixed.
By grepping the continuous integration errors log a number of GEORADIUS
tests failures were detected.

Fortunately when a GEORADIUS failure happens, the test suite logs enough
information in order to reproduce the problem: the PRNG seed,
coordinates and radius of the query.

By reproducing the issues, three different bugs were discovered and
fixed in this commit. This commit also improves the already good
reporting of the fuzzer and adds the failure vectors as regression
tests.

The issues found:

1. We need larger squares around the poles in order to cover the area
requested by the user. There were already checks in order to use a
smaller step (larger squares) but the limit set (+/- 67 degrees) is not
enough in certain edge cases, so 66 is used now.

2. Even near the equator, when the search area center is very near the
edge of the square, the north, south, west or ovest square may not be
able to fully cover the specified radius. Now a test is performed at the
edge of the initial guessed search area, and larger squares are used in
case the test fails.

3. Because of rounding errors between Redis and Tcl, sometimes the test
signaled false positives. This is now addressed.

Whenever possible the original code was improved a bit in other ways. A
debugging example stanza was added in order to make the next debugging
session simpler when the next bug is found.
2016-07-27 12:11:31 +02:00
antirez a1bfe22a80 Replication: when possible start RDB saving ASAP.
In a previous commit the replication code was changed in order to
centralize the BGSAVE for replication trigger in replicationCron(),
however after further testings, the 1 second delay imposed by this
change is not acceptable.

So now the BGSAVE is only delayed if the AOF rewriting process is
active. However past comments made sure that replicationCron() is always
able to trigger the BGSAVE when needed, making the code generally more
robust.

The new code is more similar to the initial @oranagra patch where the
BGSAVE was delayed only if an AOF rewrite was in progress.

Trivia: delaying the BGSAVE uncovered a minor Sentinel issue that is now
fixed.
2016-07-27 12:08:32 +02:00
antirez 7ca69aff26 Sentinel: new test unit 07 that tests master down conditions. 2016-07-27 12:08:25 +02:00
antirez 5b5e65203f Sentinel: check Slave INFO state more often when disconnected.
During the initial handshake with the master a slave will report to have
a very high disconnection time from its master (since technically it was
disconnected since forever, so the current UNIX time in seconds is
reported).

However when the slave is connected again the Sentinel may re-scan the
INFO output again only after 10 seconds, which is a long time. During
this time Sentinels will consider this instance unable to failover, so
a useless delay is introduced.

Actaully this hardly happened in the practice because when a slave's
master is down, the INFO period for slaves changes to 1 second. However
when a manual failover is attempted immediately after adding slaves
(like in the case of the Sentinel unit test), this problem may happen.

This commit changes the INFO period to 1 second even in the case the
slave's master is not down, but the slave reported to be disconnected
from the master (by publishing, last time we checked, a master
disconnection time field in INFO).

This change is required as a result of an unrelated change in the
replication code that adds a small delay in the master-slave first
synchronization.
2016-07-27 12:08:17 +02:00
antirez 21cffc2681 Avoid simultaneous RDB and AOF child process.
This patch, written in collaboration with Oran Agra (@oranagra) is a companion
to 780a8b1. Together the two patches should avoid that the AOF and RDB saving
processes can be spawned at the same time. Previously conditions that
could lead to two saving processes at the same time were:

1. When AOF is enabled via CONFIG SET and an RDB saving process is
   already active.

2. When the SYNC command decides to start an RDB saving process ASAP in
   order to serve a new slave that cannot partially resynchronize (but
   only if we have a disk target for replication, for diskless
   replication there is not such a problem).

Condition "1" is not very severe but "2" can happen often and is
definitely good at degrading Redis performances in an unexpected way.

The two commits have the effect of always spawning RDB savings for
replication in replicationCron() instead of attempting to start an RDB
save synchronously. Moreover when a BGSAVE or AOF rewrite must be
performed, they are instead just postponed using flags that will try to
perform such operations ASAP.

Finally the BGSAVE command was modified in order to accept a SCHEDULE
option so that if an AOF rewrite is in progress, when this option is
given, the command no longer returns an error, but instead schedules an
RDB rewrite operation for when it will be possible to start it.
2016-07-27 12:08:12 +02:00
antirez 017378eca9 Replication: start BGSAVE for replication always in replicationCron().
This makes the replication code conceptually simpler by removing the
synchronous BGSAVE trigger in syncCommand(). This also means that
socket and disk BGSAVE targets are handled by the same code.
2016-07-27 12:08:08 +02:00
antirez 940be9ab54 Regression test for issue #3333. 2016-07-06 11:50:13 +02:00
antirez 21736b41a2 getLongLongFromObject: use string2ll() instead of strict_strtoll().
strict_strtoll() has a bug that reports the empty string as ok and
parses it as zero.

Apparently nobody ever replaced this old call with the faster/saner
string2ll() which is used otherwise in the rest of the Redis core.

This commit close #3333.
2016-07-06 11:47:07 +02:00
antirez 0b748e9139 redis-cli: check SELECT reply type just in state updated.
In issues #3361 / #3365 a problem was reported / fixed with redis-cli
not updating correctly the current DB on error after SELECT.

In theory this bug was fixed in 0042fb0e, but actually the commit only
fixed the prompt updating, not the fact the state was set in a wrong
way.

This commit removes the check in the prompt update, now that hopefully
it is the state that is correct, there is no longer need for this check.
2016-07-05 17:40:32 +02:00
sskorgal 1158386bb8 Fix for redis_cli printing default DB when select command fails. 2016-07-05 17:40:32 +02:00
antirez 026f9fc7b0 Sentinel: fix cross-master Sentinel address update.
This commit both fixes the crash reported with issue #3364 and
also properly closes the old links after the Sentinel address for the
other masters gets updated.

The two problems where:

1. The Sentinel that switched address may not monitor all the masters,
   it is possible that there is no match, and the 'match' variable is
   NULL. Now we check for no match and 'continue' to the next master.

2. By ispecting the code because of issue "1" I noticed that there was a
   problem in the code that disconnects the link of the Sentinel that
   needs the address update. Basically link->disconnected is non-zero
   even if just *a single link* (cc -- command link or pc -- pubsub
   link) are disconnected, so to check with if (link->disconnected)
   in order to close the links risks to leave one link connected.

I was able to manually reproduce the crash at "1" and verify that the
commit resolves the issue.

Close #3364.
2016-07-04 18:50:40 +02:00
antirez 11523b3e0e CONFIG GET is now no longer case sensitive.
Like CONFIG SET always was. Close #3369.
2016-07-04 16:09:07 +02:00
antirez f5a7f4f2d9 Fix test for new RDB checksum failure message. 2016-07-04 12:41:25 +02:00
antirez 4c6ff74c07 Make tcp-keepalive default to 300 in internal conf.
We already changed the default in the redis.conf template, but I forgot
to change the internal config as well.
2016-07-04 12:33:29 +02:00
antirez 27dbec2a36 In Redis RDB check: more details in error reportings. 2016-07-04 12:33:28 +02:00
antirez 41f300473a In Redis RDB check: log decompression errors. 2016-07-04 12:24:15 +02:00
antirez 278fe3e965 In Redis RDB check: log object type on error. 2016-07-04 12:24:08 +02:00
antirez a117dfa807 Added a trivial program to randomly corrupt RDB files in /utils. 2016-07-04 12:24:05 +02:00
antirez f5110c3c7c In Redis RDB check: minor output message changes. 2016-07-04 12:24:02 +02:00
antirez 35b18bfba3 In Redis RDB check: better error reporting. 2016-07-04 12:23:59 +02:00
antirez f578f08544 In Redis RDB check: initial POC.
So far we used an external program (later executed within Redis) and
parser in order to check RDB files for correctness. This forces, at each
RDB format update, to have two copies of the same format implementation
that are hard to keep in sync. Morover the former RDB checker only
checked the very high-level format of the file, without actually trying
to load things in memory. Certain corruptions can only be handled by
really loading key-value pairs.

This first commit attempts to unify the Redis RDB loadig code with the
task of checking the RDB file for correctness. More work is needed but
it looks like a sounding direction so far.
2016-07-04 12:23:47 +02:00
tielei 7f1e1caee7 A string with 21 chars is not representable as a 64-bit integer. 2016-07-04 12:10:22 +02:00
antirez 7a3a595fb4 Test: new randomized stress tester for #3343 alike bugs. 2016-06-30 16:50:12 +02:00
antirez c75ca104f4 Stress tester WIP. 2016-06-30 16:50:10 +02:00
antirez 2c3fcf87cc Regression test for issue #3343 exact min crash sequence.
Note: it was verified that it can crash the test suite without the patch
applied.
2016-06-30 16:50:06 +02:00
antirez 704196790e Fix quicklistReplaceAtIndex() by updating the quicklist ziplist size.
The quicklist takes a cached version of the ziplist representation size
in bytes. The implementation must update this length every time the
underlying ziplist changes. However quicklistReplaceAtIndex() failed to
fix the length.

During LSET calls, the size of the ziplist blob and the cached size
inside the quicklist diverged. Later, when this size is used in an
authoritative way, for example during nodes splitting in order to copy
the nodes, we end with a duplicated node that may contain random
garbage.

This commit should fix issue #3343, however several problems were found
reviewing the quicklist.c code in search of this bug that should be
addressed soon or later.

For example:

1. To take a cached ziplist length is fragile since failing to update it
leads to this kind of issues.

2. The node splitting code needs auditing. For example it works just for
a side effect of ziplistDeleteRange() to be able to cope with a wrong
count of elements to remove. The code inside quicklist.c assumes that
-1 means "delete till the end" while actually it's just a count of how
many elements to delete, and is an unsigned count. So -1 gets converted
into the maximum integer, and just by chance the ziplist code stops
deleting elements after there are no more to delete.

3. Node splitting is extremely inefficient, it copies the node and
removes elements from both nodes even when actually there is to move a
single entry from one node to the other, or when the new resulting node
is empty at all so there is nothing to copy but just to create a new
node.

However at least for Redis 3.2 to introduce fresh code inside
quicklist.c may be even more risky, so instead I'm writing a better
fuzzy tester to stress the internals a bit more in order to anticipate
other possible bugs.

This bug was found using a fuzzy tester written after having some clue
about where the bug could be. The tester eventually created a ~2000
commands sequence able to always crash Redis. I wrote a better version
of the tester that searched for the smallest sequence that could crash
Redis automatically. Later this smaller sequence was minimized by
removing random commands till it still crashed the server. This resulted
into a sequence of 7 commands. With this small sequence it was just a
matter of filling the code with enough printf() to understand enough
state to fix the bug.
2016-06-27 18:12:12 +02:00
47 changed files with 2067 additions and 903 deletions
+685
View File
@@ -10,6 +10,691 @@ 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 3.2.11 Released Thu Sep 21 15:47:53 CEST 2017
================================================================================
Upgrade urgency HIGH: Potentially critical bugs fixed.
AOF flush on SHUTDOWN did not cared to really write the AOF buffers
(not in the kernel but in the Redis process memory) to disk before exiting.
Calling SHUTDOWN during traffic resulted into not every operation to be
persisted on disk.
Oran Agra (and also Buğra Gedik) in commit 8651e5d5:
Flush append only buffers before existing.
1 file changed, 2 insertions(+), 1 deletion(-)
================================================================================
Redis 3.2.10 Released Fri Jul 28 17:49:07 CEST 2017
================================================================================
Upgrade urgency MODERATE: This release contains a number of fixes that are not
critical in the general case, but quite important
in certain use cases. Upgrading is suggested but not
mandatory.
Dear Redis users,
while 4.0.1 is already out, this is a maintenance release of Redis 3.2.
It fixes several bugs, the most interesting ones are the following:
1. INFO server.stat_net_output_bytes field was computed in a wrong way, now
the output is correct.
2. SET with EX/PX options are now propagated correctly in the AOF, using
the absolute times.
3. CLIENT PAUSE was fixed to prevent eviction of keys, so that master and
slave continue to be consistent for the time the pause is active.
4. GEORADIUS(BYMEMBER)_RO variants for read-only operations added.
5. HyperLogLog sanity detection fixed in an edge case.
6. Redis Cluster crash due to mis-handling of hidden node flags fixed.
If you think you may be affected by the above problems, upgrading is a
good idea.
Have a nice day,
Salvatore
Full history of commits in this release:
Jan-Erik Rediger in commit bf508753:
Check that the whole first argument is a number
1 file changed, 3 insertions(+), 2 deletions(-)
WuYunlong in commit 8774228c:
fix rewrite config: auto-aof-rewrite-min-size
1 file changed, 2 insertions(+), 2 deletions(-)
Byron Grobe in commit ab900303:
Fixed issue #1996 (Missing '-' in help message for redis-benchmark)
1 file changed, 1 insertion(+), 1 deletion(-)
Jan-Erik Rediger in commit 7fcca9ba:
Don't use extended Regexp Syntax
1 file changed, 6 insertions(+), 6 deletions(-)
Leon Chen in commit 41963fe6:
fix return wrong value of clusterDelNodeSlots
1 file changed, 4 insertions(+), 2 deletions(-)
Leon Chen in commit 97d50344:
fix mismatch argument
1 file changed, 1 insertion(+), 1 deletion(-)
liangsijian in commit e3984980:
Fix lua ldb command log
1 file changed, 1 insertion(+)
antirez in commit 84a4f202:
Make representClusterNodeFlags() more robust.
1 file changed, 17 insertions(+), 16 deletions(-)
antirez in commit 5aa25250:
Fix isHLLObjectOrReply() to handle integer encoded strings.
1 file changed, 1 insertion(+)
antirez in commit 7018d27d:
Fix abort typo in Lua debugger help screen.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit d557144e:
Added GEORADIUS(BYMEMBER)_RO variants for read-only operations.
3 files changed, 32 insertions(+), 11 deletions(-)
Suraj Narkhede in commit a309388d:
Fix following issues in blocking commands: 1. brpop last key index, thus checking all keys for slots. 2. Memory leak in clusterRedirectBlockedClientIfNeeded. 3. Remove while loop in clusterRedirectBlockedClientIfNeeded.
1 file changed, 1 insertion(+)
Suraj Narkhede in commit 55442262:
Fix brpop command table entry and redirect blocked clients.
2 files changed, 3 insertions(+), 2 deletions(-)
antirez in commit e3641c8d:
Fix PERSIST expired key resuscitation issue #4048.
1 file changed, 4 insertions(+), 7 deletions(-)
Antonio Mallia in commit 9ce105ad:
Removed duplicate 'sys/socket.h' include
1 file changed, 1 deletion(-)
Zachary Marquez in commit e084a394:
Prevent expirations and evictions while paused
1 file changed, 10 insertions(+)
antirez in commit 61c78a52:
Collect fork() timing info only if fork succeeded.
1 file changed, 4 insertions(+), 3 deletions(-)
antirez in commit d067e334:
Aesthetic changes to #4068 PR to conform to Redis coding standard.
1 file changed, 6 insertions(+), 7 deletions(-)
xuzhou in commit 2b0f03e4:
Optimize set command with ex/px when updating aof.
1 file changed, 3 insertions(+), 3 deletions(-)
antirez in commit ad949f23:
redis-benchmark: add -t hset target.
1 file changed, 7 insertions(+)
xuzhou in commit 351663bd:
Fix set with ex/px option when propagated to aof
4 files changed, 36 insertions(+), 1 deletion(-)
minghang.zmh in commit d70ac1d1:
fix server.stat_net_output_bytes calc bug
1 file changed, 1 insertion(+), 1 deletion(-)
xuchengxuan in commit 8da9a167:
Fixed comments of slowlog duration
1 file changed, 1 insertion(+), 1 deletion(-)
cbgbt in commit 13546adc:
cli: Only print elapsed time on OUTPUT_STANDARD
1 file changed, 3 insertions(+), 1 deletion(-)
Aric Huang in commit 373facf8:
(fix) Update create-cluster README
1 file changed, 4 insertions(+), 4 deletions(-)
Salvatore Sanfilippo in commit 29e3ff9b:
Merge pull request #3926 from QuChen88/3.2
================================================================================
Redis 3.2.9 Released Mon May 17 17:35:38 CEST 2017
================================================================================
Upgrade urgency LOW: A few rarely harmful bugs were fixed.
This release just fixes bugs that are unlikely to cause serious problems
so there is no need to update ASAP. Please, see the list of commits
for the details on the bugs fixed and credits:
antirez in commit 3b46cf97:
redis-cli --bigkeys: show error when TYPE fails.
1 file changed, 7 insertions(+), 2 deletions(-)
antirez in commit f59b4b93:
Fix preprocessor if/else chain broken in order to fix #3927.
1 file changed, 3 insertions(+)
antirez in commit dd80fedf:
Fix zmalloc_get_memory_size() ifdefs to actually use the else branch.
1 file changed, 2 deletions(-)
antirez in commit 697d3abe:
Set lua-time-limit default value at safe place.
2 files changed, 1 insertion(+), 1 deletion(-)
antirez in commit c9c04b11:
Fix #3848 by closing the descriptor on error.
1 file changed, 2 insertions(+), 1 deletion(-)
张文康 in commit d3b49924:
update block->free after some diff data are written to the child process
1 file changed, 1 insertion(+)
antirez in commit 6a33952b:
Test: fix, hopefully, false PSYNC failure like in issue #2715.
1 file changed, 1 insertion(+), 1 deletion(-)
John.Koepi in commit b83f9fea:
fix #2883, #2857 pipe fds leak when fork() failed on bg aof rw
1 file changed, 1 insertion(+)
antirez in commit 10dbb5cd:
Don't leak file descriptor on syncWithMaster().
1 file changed, 2 insertions(+), 1 deletion(-)
================================================================================
Redis 3.2.8 Released Sun Feb 12 16:11:18 CET 2017
================================================================================
Upgrade urgency CRITICAL: This release reverts back the Jemalloc upgrade
that is believed to potentially cause a server
deadlock. A MIGRATE crash is also fixed.
Two important bug fixes, the first of one is critical:
1. Apparently Jemalloc 4.4.0 may contain a deadlock under particular
conditions. See https://github.com/antirez/redis/issues/3799.
We reverted back to the previously used Jemalloc versions and plan
to upgrade Jemalloc again after having more info about the
cause of the bug.
2. MIGRATE could crash the server after a socket error. See for reference:
https://github.com/antirez/redis/issues/3796.
List of commits:
antirez in commit 7178cac:
Revert "Jemalloc updated to 4.4.0."
150 files changed, 6330 insertions(+), 17245 deletions(-)
antirez in commit 33fad43:
Fix MIGRATE closing of cached socket on error.
1 file changed, 23 insertions(+), 6 deletions(-)
================================================================================
Redis 3.2.7 Released Tue Jan 31 16:21:41 CET 2017
================================================================================
Upgrade urgency HIGH: This release fixes important security and correctness
issues. It is especially important to upgrade for Redis
Cluster users and for users running Redis in their laptop
since a cross-scripting attack is fixed in this release.
Main bugs fixes and improvements in this release:
1. MIGRATE could incorrectly move keys between Redis Cluster nodes by turning
keys with an expire set into persisting keys. This bug was introduced with
the multiple-keys migration recently. It is now fixed. Only applies to
Redis Cluster users that use the resharding features of Redis Cluster.
2. As Redis 4.0 beta and the unstable branch already did (for some months at
this point), Redis 3.2.7 also aliases the Host: and POST commands to QUIT
avoiding to process the remaining pipeline if there are pending commands.
This is a security protection against a "Cross Scripting" attack, that
usually involves trying to feed Redis with HTTP in order to execute commands.
Example: a developer is running a local copy of Redis for development
purposes. She also runs a web browser in the same computer. The web browser
could send an HTTP request to http://127.0.0.1:6379 in order to access the
Redis instance, since a specially crafted HTTP requesta may also be partially
valid Redis protocol. However if POST and Host: break the connection, this
problem should be avoided. IMPORTANT: It is important to realize that it
is not impossible that another way will be found to talk with a localhost
Redis using a Cross Protocol attack not involving sending POST or Host: so
this is only a layer of protection but not a definitive fix for this class
of issues.
3. A ziplist bug that could cause data corruption, could crash the server and
MAY ALSO HAVE SECURITY IMPLICATIONS was fixed. The bug looks complex to
exploit, but attacks always get worse, never better (cit). The bug is very
very hard to catch in practice, it required manual analysis of the ziplist
code in order to be found. However it is also possible that rarely it
happened in the wild. Upgrading is required if you use LINSERT and other
in-the-middle list manipulation commands.
4. We upgraded to Jemalloc 4.4.0 since the version we used to ship with Redis
was an early 4.0 release of Jemalloc. This version may have several
improvements including the ability to better reclaim/use the memory of
system.
The following is the list of commits:
antirez in commit 3876d98:
Ziplist: insertion bug under particular conditions fixed.
1 file changed, 9 insertions(+), 1 deletion(-)
antirez in commit 153f2f0:
Jemalloc updated to 4.4.0.
150 files changed, 17271 insertions(+), 6356 deletions(-)
miter in commit ca532c9:
Change switch statment to if statment
1 file changed, 2 insertions(+), 4 deletions(-)
oranagra in commit a735035:
fix rare assertion in DEBUG DIGEST
1 file changed, 1 insertion(+), 1 deletion(-)
Itamar Haber in commit b917e3f:
Verify pairs are provided after subcommands
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 1177cf6:
Avoid geo.c warning in initialization.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 874804d:
Security: Cross Protocol Scripting protection.
3 files changed, 27 insertions(+), 2 deletions(-)
antirez in commit 273cd7f:
Ziplist: remove static from functions, they prevent good crash reports.
1 file changed, 14 insertions(+), 14 deletions(-)
Jan-Erik Rediger in commit 389b9f5:
Initialize help only in repl mode
1 file changed, 5 insertions(+), 5 deletions(-)
Yossi Gottlieb in commit 1370a88:
Fix redis-cli rare crash.
1 file changed, 4 insertions(+)
antirez in commit 68aab8e:
MIGRATE: Remove upfront ttl initialization.
1 file changed, 3 insertions(+), 4 deletions(-)
Jan-Erik Rediger in commit 788e892:
Reset the ttl for additional keys
1 file changed, 1 insertion(+)
================================================================================
Redis 3.2.6 Released Tue Dec 06 09:33:29 CET 2016
================================================================================
Upgrade urgency MODERATE: GEORADIUS, BITFIELD and Redis Cluster minor fixes.
This release mainly fixes three bugs:
1. A bug with BITFIELD that may cause the bitmap corruption when setting offsets
larger than the current string size.
2. A GEORADIUS bug that may happen when using very large radius lengths, in
the range of 10000km or alike, due to wrong bounding box calculation.
3. A bug with Redis Cluster which crashes when reading a nodes configuration
file with zero bytes at the end, which sometimes happens with certain ext4
configurations after a system crash.
The following is the list of commits:
antirez in commit f20f3ea:
Geo: improve fuzz test.
1 file changed, 11 insertions(+), 3 deletions(-)
antirez in commit 8c22086:
Geo: fix computation of bounding box.
2 files changed, 33 insertions(+), 27 deletions(-)
antirez in commit 92958df:
Cluster: handle zero bytes at the end of nodes.conf.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 0ee28ae:
Test: regression test for #3564 added.
1 file changed, 9 insertions(+)
sunhe in commit 289fadb:
bitops.c/bitfieldCommand: update higest_write_offset with check
1 file changed, 3 insertions(+), 2 deletions(-)
jybaek in commit f4f7d2c:
Add missing fclose()
1 file changed, 1 insertion(+)
================================================================================
Redis 3.2.5 Released Wed Oct 26 09:16:40 CEST 2016
================================================================================
Upgrade urgency LOW: This release only fixes a compilation issue due to the
missing -ldl at linking time.
zach shipko in commit 4736407:
BSDs don't have -ldl
1 file changed, 15 insertions(+), 5 deletions(-)
antirez in commit 9ada818:
Fix modules compilation when libc malloc is used.
1 file changed, 2 insertions(+), 2 deletions(-)
================================================================================
Redis 3.2.4 Released Mon Sep 26 08:58:21 CEST 2016
================================================================================
Upgrade urgency CRITICAL: Redis 3.2 and unstable contained a security
vulnerability fixed by this release.
Hello Redis Wizards of the Memory Stores Empire,
this is a Redis critical release in order to fix a security issue
which is documented clearly here:
https://github.com/antirez/redis/commit/6d9f8e2462fc2c426d48c941edeb78e5df7d2977
Thanks to Cory Duplantis of Cisco Talos for reporting the issue.
IMPACT:
The gist is that using CONFIG SET calls (or by manipulating redis.conf)
an attacker is able to compromise certain fields of the "server" global
structure, including the aof filename pointer, that could be made pointing
to something else. In turn the AOF name is used in different contexts such
as logging, rename(2) and open(2) syscalls, leading to potential problems.
Please note that since having access to CONFIG SET also means to be able
to change the AOF filename (and many other things) directly, this issue
actual real world impact is quite small, so I would not panik: if you
have CONFIG SET level of access, you can do more and more easily.
AFFECTED VERSIONS:
All Redis 3.2.x versions are affected.
OTHER CHANGES IN THIS RELEASE:
This release also includes other things:
* TCP binding bug fixed when only certain addresses were available for
a given port.
* A much better crash report that includes part of the Redis binary:
this will allow to fix bugs even when we just have a crash log and
no other help from the original poster oft the issue.
* A fix for Redis Cluster redis-trib displaying of info after creating
a new cluster.
Please check the following list of commits for credits about who did what.
Thanks to all the contributors and a special thank to Oran Agra for the
help in this release.
List of commits:
antirez in commit 0539634:
Security: CONFIG SET client-output-buffer-limit overflow fixed.
1 file changed, 5 insertions(+), 3 deletions(-)
antirez in commit c01abcd:
fix the fix for the TCP binding.
1 file changed, 15 insertions(+), 10 deletions(-)
oranagra in commit a6d0698:
fix tcp binding when IPv6 is unsupported
2 files changed, 14 insertions(+), 10 deletions(-)
antirez in commit 22b6c28:
debug.c: no need to define _GNU_SOURCE, is defined in fmacros.h.
1 file changed, 1 deletion(-)
antirez in commit 9e9d398:
crash log - improve code dump with more info and called symbols.
1 file changed, 59 insertions(+), 20 deletions(-)
oranagra in commit 3745c5d:
crash log - add hex dump of function code
1 file changed, 22 insertions(+)
antirez in commit c1cc07b:
Sentinel example config: warn about protected mode.
1 file changed, 16 insertions(+), 1 deletion(-)
rojingeorge in commit 011dc9f:
Display the nodes summary once the cluster is established using redis-trib.rb
1 file changed, 5 insertions(+)
Guo Xiao in commit f4e3a94:
Use the standard predefined identifier __func__ (since C99)
1 file changed, 1 insertion(+), 1 deletion(-)
================================================================================
Redis 3.2.3 Released Tue Aug 02 10:55:24 CEST 2016
================================================================================
Upgrade urgency MODERATE: Fix replication delay and redis-cli security issue.
Hello,
this is a minor release of Redis addressing two bugs:
1. There was an inverted if statement logic problem in replication.c causing
a replication delay. This is not an actual problem since things work
as expected, but worth to fix ASAP anyway.
2. Redis-cli created the history file with insecure permissions, allowing
reding from the file. This was actually a bug in linenoise which is
now fixed. The applied fix is from Chris Lamb.
List of commits:
Qu Chen in commit e67ad1d:
Fix a bug to delay bgsave while AOF rewrite in progress for replication
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 7153668:
Update linenoise to fix insecure redis-cli history file creation.
1 file changed, 6 insertions(+), 1 deletion(-)
================================================================================
Redis 3.2.2 Released Thu Jul 28 14:14:54 CEST 2016
================================================================================
Upgrade urgency MODERATE: A Redis server and a Sentinel crash are now fixed.
GEORADIUS errors in reported entries are fixed.
Hi all! Redis 3.2.2 is finally out with non trivial bugfixes and a few new
features. Let's start from the bugs:
1. There was a bug in the List type implementation, able to cause the
crash of the server under certain (non trivial to replicate) circumstances
when the LSET command was used. Now the bug is fixed and a new stress tester
that was able to easily trigger the bug was added to the test suite.
2. Redis Sentinel, when monitoring multiple masters, could crash after
a Sentinel address update event.
3. Redis Sentinel now checks slaves INFO state more often when disconnected.
This is not really a bug fix, but may allow to more easily detect that
a slave is able to fail over its master reducing certain delays.
4. It was possible, under a variety of conditions, that the AOF and RDB children
process could spawn at the same time. This is known to trash disk I/O,
AOF performances, and to ultimately create latency in the Redis server.
Normally Redis avoids to have the two writing children at the same time, but
there were edge cases discovered by Oran Agra (that also co-authored the
fix with me) where the double-fork could happen. In order to fix this bug
non trivial changes to the replication code were operated, however it was
important to back port this fix into 3.2.2 because the bug could lead to
bad latency experiences in certain cases.
5. Many GEORADIUS bugs are now fixed \o/. This started as a failing CI
test. I grepped for more clues and there were a number of random failures
in the points reported by GEORADIUS. The errors were found to be related
to three different bugs (one of these was a bug in the test itself).
It's not a critical bug: the effect is to, sometimes, don't report objects
that are near the radius, but only with specific sets of coordinates
and radius settings. However now the issues are fixed and the error
vectors were added as regression tests.
And now the good news (not that bug fixes are not good...), that is, the
new features!
1. Now slaves support the slave-announce-ip and slave-announce-port options.
Using these features a slave can be reported by the master `INFO` output
and `ROLE` command as having arbitrary IP and port. This allows to have
Sentinel deployments when working with containers or NAT-ed environments
more easily.
2. The RDB check utlity is now part of Redis and uses the same RDB code that
Redis uses in order to load the dataset in memory, so a given version
of Redis is always able to check the RDB it produced... without another
external check tool which is supposed to be taken in sync with the
rdb.c implementation. This in turn also means that the new RDB checking
is able to spot more complex bugs, since it really loads the dataset
instead of just skipping bytes.
About the redis-check-dump utility, now it is also able to show certain
information about the RDB file, like the version that produced it, when
it was produced, and so forth. Example:
./redis-check-rdb dump.rdb
[offset 0] Checking RDB file dump.rdb
[offset 32] AUX FIELD redis-ver = '3.2.2'
[offset 46] AUX FIELD redis-bits = '64'
[offset 58] AUX FIELD ctime = '1469710178'
[offset 73] AUX FIELD used-mem = '1186528'
The ability to check how much memory was used by the instance that produced
the RDB file can be especially useful.
For credits and the full change log, see the list of commits below, that was
modified in order to be a bit less verbose compared to the last releases.
Enjoy!
Salvatore
antirez in commit 0a45fbc:
Ability of slave to announce arbitrary ip/port to master.
6 files changed, 129 insertions(+), 17 deletions(-)
antirez in commit c3982c0:
redis-benchmark: new option to show server errors on stdout.
1 file changed, 17 insertions(+), 1 deletion(-)
antirez in commit fdafe23:
Multiple GEORADIUS bugs fixed.
6 files changed, 177 insertions(+), 21 deletions(-)
antirez in commit a1bfe22:
Replication: when possible start RDB saving ASAP.
1 file changed, 8 insertions(+), 2 deletions(-)
antirez in commit 7ca69af:
Sentinel: new test unit 07 that tests master down conditions.
1 file changed, 68 insertions(+)
antirez in commit 5b5e652:
Sentinel: check Slave INFO state more often when disconnected.
2 files changed, 10 insertions(+), 3 deletions(-)
antirez in commit 21cffc2:
Avoid simultaneous RDB and AOF child process.
4 files changed, 51 insertions(+), 8 deletions(-)
antirez in commit 017378e:
Replication: start BGSAVE for replication always in replicationCron().
1 file changed, 15 insertions(+), 12 deletions(-)
antirez in commit 940be9a:
Regression test for issue #3333.
1 file changed, 6 insertions(+)
antirez in commit 21736b4:
getLongLongFromObject: use string2ll() instead of strict_strtoll().
1 file changed, 1 insertion(+), 6 deletions(-)
antirez in commit 0b748e9:
redis-cli: check SELECT reply type just in state updated.
1 file changed, 1 insertion(+), 1 deletion(-)
sskorgal in commit 1158386:
Fix for redis_cli printing default DB when select command fails.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 026f9fc:
Sentinel: fix cross-master Sentinel address update.
1 file changed, 9 insertions(+), 2 deletions(-)
antirez in commit 11523b3:
CONFIG GET is now no longer case sensitive.
1 file changed, 12 insertions(+), 12 deletions(-)
antirez in commit f5a7f4f:
Fix test for new RDB checksum failure message.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 4c6ff74:
Make tcp-keepalive default to 300 in internal conf.
1 file changed, 1 insertion(+), 1 deletion(-)
antirez in commit 27dbec2:
In Redis RDB check: more details in error reportings.
1 file changed, 24 insertions(+), 13 deletions(-)
antirez in commit 41f3004:
In Redis RDB check: log decompression errors.
2 files changed, 23 insertions(+), 2 deletions(-)
antirez in commit 278fe3e:
In Redis RDB check: log object type on error.
1 file changed, 27 insertions(+), 2 deletions(-)
antirez in commit a117dfa:
Added a trivial program to randomly corrupt RDB files in /utils.
1 file changed, 44 insertions(+)
antirez in commit f5110c3:
In Redis RDB check: minor output message changes.
1 file changed, 4 insertions(+), 1 deletion(-)
antirez in commit 35b18bf:
In Redis RDB check: better error reporting.
4 files changed, 71 insertions(+), 14 deletions(-)
antirez in commit f578f08:
In Redis RDB check: initial POC.
2 files changed, 189 insertions(+), 647 deletions(-)
tielei in commit 7f1e1ca:
A string with 21 chars is not representable as a 64-bit integer.
1 file changed, 2 insertions(+), 2 deletions(-)
antirez in commit 7a3a595:
Test: new randomized stress tester for #3343 alike bugs.
1 file changed, 27 insertions(+), 2 deletions(-)
antirez in commit c75ca10:
Stress tester WIP.
1 file changed, 3 insertions(+)
antirez in commit 2c3fcf8:
Regression test for issue #3343 exact min crash sequence.
1 file changed, 16 insertions(+)
antirez in commit 7041967:
Fix quicklistReplaceAtIndex() by updating the quicklist ziplist size.
1 file changed, 1 insertion(+)
================================================================================
Redis 3.2.1 Released Fri Jun 17 15:01:56 CEST 2016
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
* Copyright (c) 2015, Salvatore Sanfilippo <antirez@gmail.com>.
* Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -118,7 +118,7 @@ void geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range) {
lat_range->min = GEO_LAT_MIN;
}
int geohashEncode(GeoHashRange *long_range, GeoHashRange *lat_range,
int geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,
double longitude, double latitude, uint8_t step,
GeoHashBits *hash) {
/* Check basic arguments sanity. */
+1 -1
View File
@@ -95,7 +95,7 @@ typedef struct {
* -1:failed
*/
void geohashGetCoordRange(GeoHashRange *long_range, GeoHashRange *lat_range);
int geohashEncode(GeoHashRange *long_range, GeoHashRange *lat_range,
int geohashEncode(const GeoHashRange *long_range, const GeoHashRange *lat_range,
double longitude, double latitude, uint8_t step,
GeoHashBits *hash);
int geohashEncodeType(double longitude, double latitude,
+60 -40
View File
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2013-2014, yinqiwen <yinqiwen@gmail.com>
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
* Copyright (c) 2015, Salvatore Sanfilippo <antirez@gmail.com>.
* Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
*/
#include "geohash_helper.h"
#include <math.h>
#define D_R (M_PI / 180.0)
#define R_MAJOR 6378137.0
@@ -54,8 +55,8 @@ const double MERCATOR_MIN = -20037726.37;
static inline double deg_rad(double ang) { return ang * D_R; }
static inline double rad_deg(double ang) { return ang / D_R; }
/* You must *ONLY* estimate steps when you are encoding.
* If you are decoding, always decode to GEO_STEP_MAX (26). */
/* This function is used in order to estimate the step (bits precision)
* of the 9 search area boxes during radius queries. */
uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
if (range_meters == 0) return 26;
int step = 1;
@@ -63,12 +64,15 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
range_meters *= 2;
step++;
}
step -= 2; /* Make sure range is included in the worst case. */
step -= 2; /* Make sure range is included in most of the base cases. */
/* Wider range torwards the poles... Note: it is possible to do better
* than this approximation by computing the distance between meridians
* at this latitude, but this does the trick for now. */
if (lat > 67 || lat < -67) step--;
if (lat > 80 || lat < -80) step--;
if (lat > 66 || lat < -66) {
step--;
if (lat > 80 || lat < -80) step--;
}
/* Frame to valid range. */
if (step < 1) step = 1;
@@ -76,44 +80,29 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
return step;
}
int geohashBitsComparator(const GeoHashBits *a, const GeoHashBits *b) {
/* If step not equal, compare on step. Else, compare on bits. */
return a->step != b->step ? a->step - b->step : a->bits - b->bits;
}
/* Return the bounding box of the search area centered at latitude,longitude
* having a radius of radius_meter. bounds[0] - bounds[2] is the minimum
* and maxium longitude, while bounds[1] - bounds[3] is the minimum and
* maximum latitude. */
int geohashBoundingBox(double longitude, double latitude, double radius_meters,
double *bounds) {
if (!bounds) return 0;
double lonr, latr;
lonr = deg_rad(longitude);
latr = deg_rad(latitude);
if (radius_meters > EARTH_RADIUS_IN_METERS)
radius_meters = EARTH_RADIUS_IN_METERS;
double distance = radius_meters / EARTH_RADIUS_IN_METERS;
double min_latitude = latr - distance;
double max_latitude = latr + distance;
/* Note: we're being lazy and not accounting for coordinates near poles */
double min_longitude, max_longitude;
double difference_longitude = asin(sin(distance) / cos(latr));
min_longitude = lonr - difference_longitude;
max_longitude = lonr + difference_longitude;
bounds[0] = rad_deg(min_longitude);
bounds[1] = rad_deg(min_latitude);
bounds[2] = rad_deg(max_longitude);
bounds[3] = rad_deg(max_latitude);
bounds[0] = longitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));
bounds[2] = longitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS/cos(deg_rad(latitude)));
bounds[1] = latitude - rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);
bounds[3] = latitude + rad_deg(radius_meters/EARTH_RADIUS_IN_METERS);
return 1;
}
/* Return a set of areas (center + 8) that are able to cover a range query
* for the specified position and radius. */
GeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double radius_meters) {
GeoHashRange long_range, lat_range;
GeoHashRadius radius = { { 0 } };
GeoHashBits hash = { 0 };
GeoHashNeighbors neighbors = { { 0 } };
GeoHashArea area = { { 0 } };
GeoHashRadius radius;
GeoHashBits hash;
GeoHashNeighbors neighbors;
GeoHashArea area;
double min_lon, max_lon, min_lat, max_lat;
double bounds[4];
int steps;
@@ -126,12 +115,43 @@ GeoHashRadius geohashGetAreasByRadius(double longitude, double latitude, double
steps = geohashEstimateStepsByRadius(radius_meters,latitude);
geohashGetCoordRange(&long_range, &lat_range);
geohashEncode(&long_range, &lat_range, longitude, latitude, steps, &hash);
geohashNeighbors(&hash, &neighbors);
geohashGetCoordRange(&long_range, &lat_range);
geohashDecode(long_range, lat_range, hash, &area);
geohashGetCoordRange(&long_range,&lat_range);
geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);
geohashNeighbors(&hash,&neighbors);
geohashDecode(long_range,lat_range,hash,&area);
/* Check if the step is enough at the limits of the covered area.
* Sometimes when the search area is near an edge of the
* area, the estimated step is not small enough, since one of the
* north / south / west / east square is too near to the search area
* to cover everything. */
int decrease_step = 0;
{
GeoHashArea north, south, east, west;
geohashDecode(long_range, lat_range, neighbors.north, &north);
geohashDecode(long_range, lat_range, neighbors.south, &south);
geohashDecode(long_range, lat_range, neighbors.east, &east);
geohashDecode(long_range, lat_range, neighbors.west, &west);
if (geohashGetDistance(longitude,latitude,longitude,north.latitude.max)
< radius_meters) decrease_step = 1;
if (geohashGetDistance(longitude,latitude,longitude,south.latitude.min)
< radius_meters) decrease_step = 1;
if (geohashGetDistance(longitude,latitude,east.longitude.max,latitude)
< radius_meters) decrease_step = 1;
if (geohashGetDistance(longitude,latitude,west.longitude.min,latitude)
< radius_meters) decrease_step = 1;
}
if (steps > 1 && decrease_step) {
steps--;
geohashEncode(&long_range,&lat_range,longitude,latitude,steps,&hash);
geohashNeighbors(&hash,&neighbors);
geohashDecode(long_range,lat_range,hash,&area);
}
/* Exclude the search areas that are useless. */
if (area.latitude.min < min_lat) {
GZERO(neighbors.south);
GZERO(neighbors.south_west);
+6 -1
View File
@@ -111,6 +111,7 @@
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -1160,10 +1161,14 @@ int linenoiseHistorySetMaxLen(int len) {
/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int linenoiseHistorySave(const char *filename) {
FILE *fp = fopen(filename,"w");
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
FILE *fp;
int j;
fp = fopen(filename,"w");
umask(old_umask);
if (fp == NULL) return -1;
chmod(filename,S_IRUSR|S_IWUSR);
for (j = 0; j < history_len; j++)
fprintf(fp,"%s\n",history[j]);
fclose(fp);
+29
View File
@@ -435,6 +435,35 @@ slave-priority 100
# By default min-slaves-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10.
# A Redis master is able to list the address and port of the attached
# slaves in different ways. For example the "INFO replication" section
# offers this information, which is used, among other tools, by
# Redis Sentinel in order to discover slave instances.
# Another place where this info is available is in the output of the
# "ROLE" command of a masteer.
#
# The listed IP and address normally reported by a slave is obtained
# in the following way:
#
# IP: The address is auto detected by checking the peer address
# of the socket used by the slave to connect with the master.
#
# Port: The port is communicated by the slave during the replication
# handshake, and is normally the port that the slave is using to
# list for connections.
#
# However when port forwarding or Network Address Translation (NAT) is
# used, the slave may be actually reachable via different IP and port
# pairs. The following two options can be used by a slave in order to
# report to its master a specific set of IP and port, so that both INFO
# and ROLE will report those values.
#
# There is no need to use both the options if you need to override just
# the port or the IP address.
#
# slave-announce-ip 5.5.5.5
# slave-announce-port 1234
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
+16 -1
View File
@@ -1,5 +1,21 @@
# Example sentinel.conf
# *** IMPORTANT ***
#
# By default Sentinel will not be reachable from interfaces different than
# localhost, either use the 'bind' directive to bind to a list of network
# interfaces, or disable protected mode with "protected-mode no" by
# adding it to this configuration file.
#
# Before doing that MAKE SURE the instance is protected from the outside
# world via firewalling or other means.
#
# For example you may use one of the following:
#
# bind 127.0.0.1 192.168.1.1
#
# protected-mode no
# port <sentinel-port>
# The port that this sentinel instance will run on
port 26379
@@ -178,4 +194,3 @@ sentinel failover-timeout mymaster 180000
#
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
+15 -5
View File
@@ -65,17 +65,27 @@ ifeq ($(uname_S),SunOS)
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt
else
ifeq ($(uname_S),Darwin)
# Darwin (nothing to do)
# Darwin
FINAL_LIBS+= -ldl
else
ifeq ($(uname_S),AIX)
# AIX
FINAL_LDFLAGS+= -Wl,-bexpall
FINAL_LIBS+= -pthread -lcrypt -lbsd
FINAL_LIBS+=-ldl -pthread -lcrypt -lbsd
else
ifeq ($(uname_S),OpenBSD)
# OpenBSD
FINAL_LIBS+= -lpthread
else
ifeq ($(uname_S),FreeBSD)
# FreeBSD
FINAL_LIBS+= -lpthread
else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+= -pthread
FINAL_LIBS+=-ldl -pthread
endif
endif
endif
endif
endif
@@ -95,7 +105,7 @@ endif
ifeq ($(MALLOC),jemalloc)
DEPENDENCY_TARGETS+= jemalloc
FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl
FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a
endif
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
+3 -2
View File
@@ -462,7 +462,7 @@ static int anetV6Only(char *err, int s) {
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
{
int s, rv;
int s = -1, rv;
char _port[6]; /* strlen("65535") */
struct addrinfo hints, *servinfo, *p;
@@ -486,11 +486,12 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
goto end;
}
if (p == NULL) {
anetSetError(err, "unable to bind socket");
anetSetError(err, "unable to bind socket, errno: %d", errno);
goto error;
}
error:
if (s != -1) close(s);
s = ANET_ERR;
end:
freeaddrinfo(servinfo);
+23 -2
View File
@@ -115,6 +115,7 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {
if (nwritten <= 0) return;
memmove(block->buf,block->buf+nwritten,block->used-nwritten);
block->used -= nwritten;
block->free += nwritten;
}
if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln);
}
@@ -251,7 +252,10 @@ int startAppendOnly(void) {
strerror(errno));
return C_ERR;
}
if (rewriteAppendOnlyFileBackground() == C_ERR) {
if (server.rdb_child_pid != -1) {
server.aof_rewrite_scheduled = 1;
serverLog(LL_WARNING,"AOF was enabled but there is already a child process saving an RDB file on disk. An AOF background was scheduled to start when possible.");
} else if (rewriteAppendOnlyFileBackground() == C_ERR) {
close(server.aof_fd);
serverLog(LL_WARNING,"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.");
return C_ERR;
@@ -532,6 +536,22 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
buf = catAppendOnlyGenericCommand(buf,3,tmpargv);
decrRefCount(tmpargv[0]);
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
} else if (cmd->proc == setCommand && argc > 3) {
int i;
robj *exarg = NULL, *pxarg = NULL;
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
buf = catAppendOnlyGenericCommand(buf,3,argv);
for (i = 3; i < argc; i ++) {
if (!strcasecmp(argv[i]->ptr, "ex")) exarg = argv[i+1];
if (!strcasecmp(argv[i]->ptr, "px")) pxarg = argv[i+1];
}
serverAssert(!(exarg && pxarg));
if (exarg)
buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],
exarg);
if (pxarg)
buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],
pxarg);
} else {
/* All the other commands don't need translation or need the
* same translation already operated in the command vector
@@ -1261,7 +1281,7 @@ int rewriteAppendOnlyFileBackground(void) {
pid_t childpid;
long long start;
if (server.aof_child_pid != -1) return C_ERR;
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
if (aofCreatePipes() != C_OK) return C_ERR;
start = ustime();
if ((childpid = fork()) == 0) {
@@ -1292,6 +1312,7 @@ int rewriteAppendOnlyFileBackground(void) {
serverLog(LL_WARNING,
"Can't rewrite append only file in background: fork: %s",
strerror(errno));
aofClosePipes();
return C_ERR;
}
serverLog(LL_NOTICE,
+3 -2
View File
@@ -907,7 +907,7 @@ void bitfieldCommand(client *c) {
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
int owtype = BFOVERFLOW_WRAP; /* Overflow type. */
int readonly = 1;
long higest_write_offset = 0;
size_t higest_write_offset = 0;
for (j = 2; j < c->argc; j++) {
int remargs = c->argc-j-1; /* Remaining args other than current. */
@@ -957,7 +957,8 @@ void bitfieldCommand(client *c) {
if (opcode != BITFIELDOP_GET) {
readonly = 0;
higest_write_offset = bitoffset + bits - 1;
if (higest_write_offset < bitoffset + bits - 1)
higest_write_offset = bitoffset + bits - 1;
/* INCRBY and SET require another argument. */
if (getLongLongFromObjectOrReply(c,c->argv[j+3],&i64,NULL) != C_OK){
zfree(ops);
+51 -30
View File
@@ -37,7 +37,6 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <math.h>
@@ -129,7 +128,7 @@ int clusterLoadConfig(char *filename) {
/* Skip blank lines, they can be created either by users manually
* editing nodes.conf or by the config writing process if stopped
* before the truncate() call. */
if (line[0] == '\n') continue;
if (line[0] == '\n' || line[0] == '\0') continue;
/* Split the line into arguments for processing. */
argv = sdssplitargs(line,&argc);
@@ -1300,13 +1299,15 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
clusterNode *node;
sds ci;
ci = representClusterNodeFlags(sdsempty(), flags);
serverLog(LL_DEBUG,"GOSSIP %.40s %s:%d %s",
g->nodename,
g->ip,
ntohs(g->port),
ci);
sdsfree(ci);
if (server.verbosity == LL_DEBUG) {
ci = representClusterNodeFlags(sdsempty(), flags);
serverLog(LL_DEBUG,"GOSSIP %.40s %s:%d %s",
g->nodename,
g->ip,
ntohs(g->port),
ci);
sdsfree(ci);
}
/* Update our state accordingly to the gossip sections */
node = clusterLookupNode(g->nodename);
@@ -3440,8 +3441,10 @@ int clusterDelNodeSlots(clusterNode *node) {
int deleted = 0, j;
for (j = 0; j < CLUSTER_SLOTS; j++) {
if (clusterNodeGetSlotBit(node,j)) clusterDelSlot(j);
deleted++;
if (clusterNodeGetSlotBit(node,j)) {
clusterDelSlot(j);
deleted++;
}
}
return deleted;
}
@@ -3675,15 +3678,14 @@ static struct redisNodeFlags redisNodeFlagsTable[] = {
/* Concatenate the comma separated list of node flags to the given SDS
* string 'ci'. */
sds representClusterNodeFlags(sds ci, uint16_t flags) {
if (flags == 0) {
ci = sdscat(ci,"noflags,");
} else {
int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);
for (i = 0; i < size; i++) {
struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;
if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);
}
size_t orig_len = sdslen(ci);
int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);
for (i = 0; i < size; i++) {
struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;
if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);
}
/* If no flag was added, add the "noflags" special flag. */
if (sdslen(ci) == orig_len) ci = sdscat(ci,"noflags,");
sdsIncrLen(ci,-1); /* Remove trailing comma. */
return ci;
}
@@ -4007,7 +4009,7 @@ void clusterCommand(client *c) {
}
if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {
addReplyErrorFormat(c,"I don't know about node %s",
(char*)c->argv[3]->ptr);
(char*)c->argv[4]->ptr);
return;
}
server.cluster->importing_slots_from[slot] = n;
@@ -4652,13 +4654,13 @@ void migrateCommand(client *c) {
int copy, replace, j;
long timeout;
long dbid;
long long ttl, expireat;
robj **ov = NULL; /* Objects to migrate. */
robj **kv = NULL; /* Key names. */
robj **newargv = NULL; /* Used to rewrite the command as DEL ... keys ... */
rio cmd, payload;
int may_retry = 1;
int write_error = 0;
int argv_rewritten = 0;
/* To support the KEYS option we need the following additional state. */
int first_key = 3; /* Argument index of the first key. */
@@ -4667,7 +4669,6 @@ void migrateCommand(client *c) {
/* Initialization */
copy = 0;
replace = 0;
ttl = 0;
/* Parse additional options */
for (j = 6; j < c->argc; j++) {
@@ -4743,7 +4744,9 @@ try_again:
/* Create RESTORE payload and generate the protocol to call the command. */
for (j = 0; j < num_keys; j++) {
expireat = getExpire(c->db,kv[j]);
long long ttl = 0;
long long expireat = getExpire(c->db,kv[j]);
if (expireat != -1) {
ttl = expireat-mstime();
if (ttl < 1) ttl = 1;
@@ -4841,12 +4844,20 @@ try_again:
goto socket_err; /* A retry is guaranteed because of tested conditions.*/
}
/* On socket errors, close the migration socket now that we still have
* the original host/port in the ARGV. Later the original command may be
* rewritten to DEL and will be too later. */
if (socket_error) migrateCloseSocket(c->argv[1],c->argv[2]);
if (!copy) {
/* Translate MIGRATE as DEL for replication/AOF. */
/* Translate MIGRATE as DEL for replication/AOF. Note that we do
* this only for the keys for which we received an acknowledgement
* from the receiving Redis server, by using the del_idx index. */
if (del_idx > 1) {
newargv[0] = createStringObject("DEL",3);
/* Note that the following call takes ownership of newargv. */
replaceClientCommandVector(c,del_idx,newargv);
argv_rewritten = 1;
} else {
/* No key transfer acknowledged, no need to rewrite as DEL. */
zfree(newargv);
@@ -4855,8 +4866,8 @@ try_again:
}
/* If we are here and a socket error happened, we don't want to retry.
* Just signal the problem to the client, but only do it if we don't
* already queued a different error reported by the destination server. */
* Just signal the problem to the client, but only do it if we did not
* already queue a different error reported by the destination server. */
if (!error_from_target && socket_error) {
may_retry = 0;
goto socket_err;
@@ -4864,7 +4875,11 @@ try_again:
if (!error_from_target) {
/* Success! Update the last_dbid in migrateCachedSocket, so that we can
* avoid SELECT the next time if the target DB is the same. Reply +OK. */
* avoid SELECT the next time if the target DB is the same. Reply +OK.
*
* Note: If we reached this point, even if socket_error is true
* still the SELECT command succeeded (otherwise the code jumps to
* socket_err label. */
cs->last_dbid = dbid;
addReply(c,shared.ok);
} else {
@@ -4874,7 +4889,6 @@ try_again:
sdsfree(cmd.io.buffer.ptr);
zfree(ov); zfree(kv); zfree(newargv);
if (socket_error) migrateCloseSocket(c->argv[1],c->argv[2]);
return;
/* On socket errors we try to close the cached socket and try again.
@@ -4884,7 +4898,12 @@ socket_err:
/* Cleanup we want to perform in both the retry and no retry case.
* Note: Closing the migrate socket will also force SELECT next time. */
sdsfree(cmd.io.buffer.ptr);
migrateCloseSocket(c->argv[1],c->argv[2]);
/* If the command was rewritten as DEL and there was a socket error,
* we already closed the socket earlier. While migrateCloseSocket()
* is idempotent, the host/port arguments are now gone, so don't do it
* again. */
if (!argv_rewritten) migrateCloseSocket(c->argv[1],c->argv[2]);
zfree(newargv);
newargv = NULL; /* This will get reallocated on retry. */
@@ -5187,8 +5206,9 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
return 1;
}
/* All keys must belong to the same slot, so check first key only. */
di = dictGetIterator(c->bpop.keys);
while((de = dictNext(di)) != NULL) {
if ((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));
clusterNode *node = server.cluster->slots[slot];
@@ -5206,6 +5226,7 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
clusterRedirectClient(c,node,slot,
CLUSTER_REDIR_MOVED);
}
dictReleaseIterator(di);
return 1;
}
}
+38 -17
View File
@@ -549,8 +549,9 @@ void loadServerConfigFromString(char *config) {
unsigned long long hard, soft;
int soft_seconds;
if (class == -1) {
err = "Unrecognized client limit class";
if (class == -1 || class == CLIENT_TYPE_MASTER) {
err = "Unrecognized client limit class: the user specified "
"an invalid one, or 'master' which has no buffer limits.";
goto loaderr;
}
hard = memtoll(argv[2],NULL);
@@ -570,6 +571,16 @@ void loadServerConfigFromString(char *config) {
}
} else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
server.slave_priority = atoi(argv[1]);
} else if (!strcasecmp(argv[0],"slave-announce-ip") && argc == 2) {
zfree(server.slave_announce_ip);
server.slave_announce_ip = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"slave-announce-port") && argc == 2) {
server.slave_announce_port = atoi(argv[1]);
if (server.slave_announce_port < 0 ||
server.slave_announce_port > 65535)
{
err = "Invalid port"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"min-slaves-to-write") && argc == 2) {
server.repl_min_slaves_to_write = atoi(argv[1]);
if (server.repl_min_slaves_to_write < 0) {
@@ -824,7 +835,8 @@ void configSetCommand(client *c) {
long val;
if ((j % 4) == 0) {
if (getClientTypeByName(v[j]) == -1) {
int class = getClientTypeByName(v[j]);
if (class == -1 || class == CLIENT_TYPE_MASTER) {
sdsfreesplitres(v,vlen);
goto badfmt;
}
@@ -857,6 +869,9 @@ void configSetCommand(client *c) {
if (flags == -1) goto badfmt;
server.notify_keyspace_events = flags;
} config_set_special_field("slave-announce-ip") {
zfree(server.slave_announce_ip);
server.slave_announce_ip = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
/* Boolean fields.
* config_set_bool_field(name,var). */
@@ -895,8 +910,6 @@ void configSetCommand(client *c) {
"timeout",server.maxidletime,0,LONG_MAX) {
} config_set_numerical_field(
"auto-aof-rewrite-percentage",server.aof_rewrite_perc,0,LLONG_MAX){
} config_set_numerical_field(
"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,0,LLONG_MAX) {
} config_set_numerical_field(
"hash-max-ziplist-entries",server.hash_max_ziplist_entries,0,LLONG_MAX) {
} config_set_numerical_field(
@@ -933,6 +946,8 @@ void configSetCommand(client *c) {
"repl-diskless-sync-delay",server.repl_diskless_sync_delay,0,LLONG_MAX) {
} config_set_numerical_field(
"slave-priority",server.slave_priority,0,LLONG_MAX) {
} config_set_numerical_field(
"slave-announce-port",server.slave_announce_port,0,65535) {
} config_set_numerical_field(
"min-slaves-to-write",server.repl_min_slaves_to_write,0,LLONG_MAX) {
refreshGoodSlavesCount();
@@ -969,6 +984,8 @@ void configSetCommand(client *c) {
}
} config_set_memory_field("repl-backlog-size",ll) {
resizeReplicationBacklog(ll);
} config_set_memory_field("auto-aof-rewrite-min-size",ll) {
server.aof_rewrite_min_size = ll;
/* Enumeration fields.
* config_set_enum_field(name,var,enum_var) */
@@ -1001,7 +1018,7 @@ badfmt: /* Bad format errors */
*----------------------------------------------------------------------------*/
#define config_get_string_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
if (stringmatch(pattern,_name,1)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,_var ? _var : ""); \
matches++; \
@@ -1009,7 +1026,7 @@ badfmt: /* Bad format errors */
} while(0);
#define config_get_bool_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
if (stringmatch(pattern,_name,1)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,_var ? "yes" : "no"); \
matches++; \
@@ -1017,7 +1034,7 @@ badfmt: /* Bad format errors */
} while(0);
#define config_get_numerical_field(_name,_var) do { \
if (stringmatch(pattern,_name,0)) { \
if (stringmatch(pattern,_name,1)) { \
ll2string(buf,sizeof(buf),_var); \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,buf); \
@@ -1026,7 +1043,7 @@ badfmt: /* Bad format errors */
} while(0);
#define config_get_enum_field(_name,_var,_enumvar) do { \
if (stringmatch(pattern,_name,0)) { \
if (stringmatch(pattern,_name,1)) { \
addReplyBulkCString(c,_name); \
addReplyBulkCString(c,configEnumGetNameOrUnknown(_enumvar,_var)); \
matches++; \
@@ -1048,6 +1065,7 @@ void configGetCommand(client *c) {
config_get_string_field("unixsocket",server.unixsocket);
config_get_string_field("logfile",server.logfile);
config_get_string_field("pidfile",server.pidfile);
config_get_string_field("slave-announce-ip",server.slave_announce_ip);
/* Numerical values */
config_get_numerical_field("maxmemory",server.maxmemory);
@@ -1090,6 +1108,7 @@ void configGetCommand(client *c) {
config_get_numerical_field("maxclients",server.maxclients);
config_get_numerical_field("watchdog-period",server.watchdog_period);
config_get_numerical_field("slave-priority",server.slave_priority);
config_get_numerical_field("slave-announce-port",server.slave_announce_port);
config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
config_get_numerical_field("hz",server.hz);
@@ -1138,12 +1157,12 @@ void configGetCommand(client *c) {
/* Everything we can't handle with macros follows. */
if (stringmatch(pattern,"appendonly",0)) {
if (stringmatch(pattern,"appendonly",1)) {
addReplyBulkCString(c,"appendonly");
addReplyBulkCString(c,server.aof_state == AOF_OFF ? "no" : "yes");
matches++;
}
if (stringmatch(pattern,"dir",0)) {
if (stringmatch(pattern,"dir",1)) {
char buf[1024];
if (getcwd(buf,sizeof(buf)) == NULL)
@@ -1153,7 +1172,7 @@ void configGetCommand(client *c) {
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"save",0)) {
if (stringmatch(pattern,"save",1)) {
sds buf = sdsempty();
int j;
@@ -1169,7 +1188,7 @@ void configGetCommand(client *c) {
sdsfree(buf);
matches++;
}
if (stringmatch(pattern,"client-output-buffer-limit",0)) {
if (stringmatch(pattern,"client-output-buffer-limit",1)) {
sds buf = sdsempty();
int j;
@@ -1187,14 +1206,14 @@ void configGetCommand(client *c) {
sdsfree(buf);
matches++;
}
if (stringmatch(pattern,"unixsocketperm",0)) {
if (stringmatch(pattern,"unixsocketperm",1)) {
char buf[32];
snprintf(buf,sizeof(buf),"%o",server.unixsocketperm);
addReplyBulkCString(c,"unixsocketperm");
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"slaveof",0)) {
if (stringmatch(pattern,"slaveof",1)) {
char buf[256];
addReplyBulkCString(c,"slaveof");
@@ -1206,7 +1225,7 @@ void configGetCommand(client *c) {
addReplyBulkCString(c,buf);
matches++;
}
if (stringmatch(pattern,"notify-keyspace-events",0)) {
if (stringmatch(pattern,"notify-keyspace-events",1)) {
robj *flagsobj = createObject(OBJ_STRING,
keyspaceEventsFlagsToString(server.notify_keyspace_events));
@@ -1215,7 +1234,7 @@ void configGetCommand(client *c) {
decrRefCount(flagsobj);
matches++;
}
if (stringmatch(pattern,"bind",0)) {
if (stringmatch(pattern,"bind",1)) {
sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
addReplyBulkCString(c,"bind");
@@ -1768,6 +1787,7 @@ int rewriteConfig(char *path) {
rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,CONFIG_DEFAULT_UNIX_SOCKET_PERM);
rewriteConfigNumericalOption(state,"timeout",server.maxidletime,CONFIG_DEFAULT_CLIENT_TIMEOUT);
rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,CONFIG_DEFAULT_TCP_KEEPALIVE);
rewriteConfigNumericalOption(state,"slave-announce-port",server.slave_announce_port,CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT);
rewriteConfigEnumOption(state,"loglevel",server.verbosity,loglevel_enum,CONFIG_DEFAULT_VERBOSITY);
rewriteConfigStringOption(state,"logfile",server.logfile,CONFIG_DEFAULT_LOGFILE);
rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,CONFIG_DEFAULT_SYSLOG_ENABLED);
@@ -1781,6 +1801,7 @@ int rewriteConfig(char *path) {
rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,CONFIG_DEFAULT_RDB_FILENAME);
rewriteConfigDirOption(state);
rewriteConfigSlaveofOption(state);
rewriteConfigStringOption(state,"slave-announce-ip",server.slave_announce_ip,CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP);
rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA);
rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,CONFIG_DEFAULT_SLAVE_READ_ONLY);
+42 -7
View File
@@ -90,7 +90,7 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
if (expireIfNeeded(db,key) == 1) {
/* Key expired. If we are in the context of a master, expireIfNeeded()
* returns 0 only when the key does not exist at all, so it's save
* returns 0 only when the key does not exist at all, so it's safe
* to return NULL ASAP. */
if (server.masterhost == NULL) return NULL;
@@ -1033,18 +1033,15 @@ void pttlCommand(client *c) {
}
void persistCommand(client *c) {
dictEntry *de;
de = dictFind(c->db->dict,c->argv[1]->ptr);
if (de == NULL) {
addReply(c,shared.czero);
} else {
if (lookupKeyWrite(c->db,c->argv[1])) {
if (removeExpire(c->db,c->argv[1])) {
addReply(c,shared.cone);
server.dirty++;
} else {
addReply(c,shared.czero);
}
} else {
addReply(c,shared.czero);
}
}
@@ -1234,6 +1231,44 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
return keys;
}
/* Helper function to extract keys from following commands:
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
* [COUNT count] [STORE key] [STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ... */
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int i, num, *keys;
UNUSED(cmd);
/* Check for the presence of the stored key in the command */
int stored_key = -1;
for (i = 5; i < argc; i++) {
char *arg = argv[i]->ptr;
/* For the case when user specifies both "store" and "storedist" options, the
* second key specified would override the first key. This behavior is kept
* the same as in georadiusCommand method.
*/
if ((!strcasecmp(arg, "store") || !strcasecmp(arg, "storedist")) && ((i+1) < argc)) {
stored_key = i+1;
i++;
}
}
num = 1 + (stored_key == -1 ? 0 : 1);
/* Keys in the command come from two places:
* argv[1] = key,
* argv[5...n] = stored key if present
*/
keys = zmalloc(sizeof(int) * num);
/* Add all key positions to keys[] */
keys[0] = 1;
if(num > 1) {
keys[1] = stored_key;
}
*numkeys = num;
return keys;
}
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
* a fast way a key that belongs to a specified hash slot. This is useful
* while rehashing the cluster. */
+64 -4
View File
@@ -39,6 +39,8 @@
#include <ucontext.h>
#include <fcntl.h>
#include "bio.h"
#include <unistd.h>
#include <dlfcn.h>
#endif /* HAVE_BACKTRACE */
#ifdef __CYGWIN__
@@ -124,7 +126,7 @@ void computeDatasetDigest(unsigned char *final) {
redisDb *db = server.db+j;
if (dictSize(db->dict) == 0) continue;
di = dictGetIterator(db->dict);
di = dictGetSafeIterator(db->dict);
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
@@ -671,6 +673,8 @@ static void *getMcontextEip(ucontext_t *uc) {
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
#elif defined(__ia64__) /* Linux IA64 */
return (void*) uc->uc_mcontext.sc_ip;
#elif defined(__arm__) /* Linux ARM */
return (void*) uc->uc_mcontext.arm_pc;
#endif
#else
return NULL;
@@ -972,6 +976,32 @@ int memtest_test_linux_anonymous_maps(void) {
}
#endif
/* Scans the (assumed) x86 code starting at addr, for a max of `len`
* bytes, searching for E8 (callq) opcodes, and dumping the symbols
* and the call offset if they appear to be valid. */
void dumpX86Calls(void *addr, size_t len) {
size_t j;
unsigned char *p = addr;
Dl_info info;
/* Hash table to best-effort avoid printing the same symbol
* multiple times. */
unsigned long ht[256] = {0};
if (len < 5) return;
for (j = 0; j < len-4; j++) {
if (p[j] != 0xE8) continue; /* Not an E8 CALL opcode. */
unsigned long target = (unsigned long)addr+j+5;
target += *((int32_t*)(p+j+1));
if (dladdr((void*)target, &info) != 0 && info.dli_sname != NULL) {
if (ht[target&0xff] != target) {
printf("Function at 0x%lx is %s\n",target,info.dli_sname);
ht[target&0xff] = target;
}
j += 4; /* Skip the 32 bit immediate. */
}
}
}
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
ucontext_t *uc = (ucontext_t*) secret;
void *eip = getMcontextEip(uc);
@@ -1022,19 +1052,49 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
bioKillThreads();
if (memtest_test_linux_anonymous_maps()) {
serverLogRaw(LL_WARNING|LL_RAW,
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\n");
} else {
serverLogRaw(LL_WARNING|LL_RAW,
"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.\n");
}
#endif
if (eip != NULL) {
Dl_info info;
if (dladdr(eip, &info) != 0) {
serverLog(LL_WARNING|LL_RAW,
"\n------ DUMPING CODE AROUND EIP ------\n"
"Symbol: %s (base: %p)\n"
"Module: %s (base %p)\n"
"$ xxd -r -p /tmp/dump.hex /tmp/dump.bin\n"
"$ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin\n"
"------\n",
info.dli_sname, info.dli_saddr, info.dli_fname, info.dli_fbase,
info.dli_saddr);
size_t len = (long)eip - (long)info.dli_saddr;
unsigned long sz = sysconf(_SC_PAGESIZE);
if (len < 1<<13) { /* we don't have functions over 8k (verified) */
/* Find the address of the next page, which is our "safety"
* limit when dumping. Then try to dump just 128 bytes more
* than EIP if there is room, or stop sooner. */
unsigned long next = ((unsigned long)eip + sz) & ~(sz-1);
unsigned long end = (unsigned long)eip + 128;
if (end > next) end = next;
len = end - (unsigned long)info.dli_saddr;
serverLogHexDump(LL_WARNING, "dump of function",
info.dli_saddr ,len);
dumpX86Calls(info.dli_saddr,len);
}
}
}
serverLogRaw(LL_WARNING|LL_RAW,
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash by opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
" Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
@@ -1055,7 +1115,7 @@ void serverLogHexDump(int level, char *descr, void *value, size_t len) {
unsigned char *v = value;
char charset[] = "0123456789abcdef";
serverLog(level,"%s (hexdump):", descr);
serverLog(level,"%s (hexdump of %zu bytes):", descr, len);
b = buf;
while(len) {
b[0] = charset[(*v)>>4];
+41
View File
@@ -0,0 +1,41 @@
/* This file contains debugging macros to be used when investigating issues.
*
* -----------------------------------------------------------------------------
*
* Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#define D(...) \
do { \
FILE *fp = fopen("/tmp/log.txt","a"); \
fprintf(fp,"%s:%s:%d:\t", __FILE__, __func__, __LINE__); \
fprintf(fp,__VA_ARGS__); \
fprintf(fp,"\n"); \
fclose(fp); \
} while (0);
+52 -11
View File
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2014, Matt Stancliff <matt@genges.com>.
* Copyright (c) 2015, Salvatore Sanfilippo <antirez@gmail.com>.
* Copyright (c) 2015-2016, Salvatore Sanfilippo <antirez@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@
#include "geo.h"
#include "geohash_helper.h"
#include "debugmacro.h"
/* Things exported from t_zset.c only for geo.c, since it is the only other
* part of Redis that requires close zset introspection. */
@@ -327,6 +328,7 @@ int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon,
int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {
GeoHashBits neighbors[9];
unsigned int i, count = 0, last_processed = 0;
int debugmsg = 0;
neighbors[0] = n.hash;
neighbors[1] = n.neighbors.north;
@@ -341,8 +343,26 @@ int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, d
/* For each neighbor (*and* our own hashbox), get all the matching
* members and add them to the potential result list. */
for (i = 0; i < sizeof(neighbors) / sizeof(*neighbors); i++) {
if (HASHISZERO(neighbors[i]))
if (HASHISZERO(neighbors[i])) {
if (debugmsg) D("neighbors[%d] is zero",i);
continue;
}
/* Debugging info. */
if (debugmsg) {
GeoHashRange long_range, lat_range;
geohashGetCoordRange(&long_range,&lat_range);
GeoHashArea myarea = {{0,0},{0,0},{0,0}};
geohashDecode(long_range, lat_range, neighbors[i], &myarea);
/* Dump center square. */
D("neighbors[%d]:\n",i);
D("area.longitude.min: %f\n", myarea.longitude.min);
D("area.longitude.max: %f\n", myarea.longitude.max);
D("area.latitude.min: %f\n", myarea.latitude.min);
D("area.latitude.max: %f\n", myarea.latitude.max);
D("\n");
}
/* When a huge Radius (in the 5000 km range or more) is used,
* adjacent neighbors can be the same, leading to duplicated
@@ -351,7 +371,11 @@ int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, d
if (last_processed &&
neighbors[i].bits == neighbors[last_processed].bits &&
neighbors[i].step == neighbors[last_processed].step)
{
if (debugmsg)
D("Skipping processing of %d, same as previous\n",i);
continue;
}
count += membersOfGeoHashBox(zobj, neighbors[i], ga, lon, lat, radius);
last_processed = i;
}
@@ -430,13 +454,14 @@ void geoaddCommand(client *c) {
#define SORT_ASC 1
#define SORT_DESC 2
#define RADIUS_COORDS 1
#define RADIUS_MEMBER 2
#define RADIUS_COORDS (1<<0) /* Search around coordinates. */
#define RADIUS_MEMBER (1<<1) /* Search around member. */
#define RADIUS_NOSTORE (1<<2) /* Do not acceot STORE/STOREDIST option. */
/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
* [COUNT count] [STORE key] [STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ... */
void georadiusGeneric(client *c, int type) {
void georadiusGeneric(client *c, int flags) {
robj *key = c->argv[1];
robj *storekey = NULL;
int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */
@@ -451,11 +476,11 @@ void georadiusGeneric(client *c, int type) {
/* Find long/lat to use for radius search based on inquiry type */
int base_args;
double xy[2] = { 0 };
if (type == RADIUS_COORDS) {
if (flags & RADIUS_COORDS) {
base_args = 6;
if (extractLongLatOrReply(c, c->argv + 2, xy) == C_ERR)
return;
} else if (type == RADIUS_MEMBER) {
} else if (flags & RADIUS_MEMBER) {
base_args = 5;
robj *member = c->argv[2];
if (longLatFromMember(zobj, member, xy) == C_ERR) {
@@ -463,7 +488,7 @@ void georadiusGeneric(client *c, int type) {
return;
}
} else {
addReplyError(c, "unknown georadius search type");
addReplyError(c, "Unknown georadius search type");
return;
}
@@ -500,11 +525,17 @@ void georadiusGeneric(client *c, int type) {
return;
}
i++;
} else if (!strcasecmp(arg, "store") && (i+1) < remaining) {
} else if (!strcasecmp(arg, "store") &&
(i+1) < remaining &&
!(flags & RADIUS_NOSTORE))
{
storekey = c->argv[base_args+i+1];
storedist = 0;
i++;
} else if (!strcasecmp(arg, "storedist") && (i+1) < remaining) {
} else if (!strcasecmp(arg, "storedist") &&
(i+1) < remaining &&
!(flags & RADIUS_NOSTORE))
{
storekey = c->argv[base_args+i+1];
storedist = 1;
i++;
@@ -652,10 +683,20 @@ void georadiusCommand(client *c) {
}
/* GEORADIUSBYMEMBER wrapper function. */
void georadiusByMemberCommand(client *c) {
void georadiusbymemberCommand(client *c) {
georadiusGeneric(c, RADIUS_MEMBER);
}
/* GEORADIUS_RO wrapper function. */
void georadiusroCommand(client *c) {
georadiusGeneric(c, RADIUS_COORDS|RADIUS_NOSTORE);
}
/* GEORADIUSBYMEMBER_RO wrapper function. */
void georadiusbymemberroCommand(client *c) {
georadiusGeneric(c, RADIUS_MEMBER|RADIUS_NOSTORE);
}
/* GEOHASH key ele1 ele2 ... eleN
*
* Returns an array with an 11 characters geohash representation of the
+1
View File
@@ -1128,6 +1128,7 @@ int isHLLObjectOrReply(client *c, robj *o) {
if (checkType(c,o,OBJ_STRING))
return C_ERR; /* Error already sent. */
if (!sdsEncodedObject(o)) goto invalid;
if (stringObjectLen(o) < sizeof(*hdr)) goto invalid;
hdr = o->ptr;
+26 -3
View File
@@ -105,6 +105,7 @@ client *createClient(int fd) {
c->repl_ack_off = 0;
c->repl_ack_time = 0;
c->slave_listening_port = 0;
c->slave_ip[0] = '\0';
c->slave_capa = SLAVE_CAPA_NONE;
c->reply = listCreate();
c->reply_bytes = 0;
@@ -948,11 +949,11 @@ int writeToClient(int fd, client *c, int handler_installed) {
*
* However if we are over the maxmemory limit we ignore that and
* just deliver as much data as it is possible to deliver. */
server.stat_net_output_bytes += totwritten;
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
(server.maxmemory == 0 ||
zmalloc_used_memory() < server.maxmemory)) break;
}
server.stat_net_output_bytes += totwritten;
if (nwritten == -1) {
if (errno == EAGAIN) {
nwritten = 0;
@@ -1268,8 +1269,10 @@ void processInputBuffer(client *c) {
/* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is
* written to the client. Make sure to not let the reply grow after
* this flag has been set (i.e. don't process more commands). */
if (c->flags & CLIENT_CLOSE_AFTER_REPLY) break;
* this flag has been set (i.e. don't process more commands).
*
* The same applies for clients we want to terminate ASAP. */
if (c->flags & (CLIENT_CLOSE_AFTER_REPLY|CLIENT_CLOSE_ASAP)) break;
/* Determine request type when unknown. */
if (!c->reqtype) {
@@ -1636,6 +1639,26 @@ void clientCommand(client *c) {
}
}
/* This callback is bound to POST and "Host:" command names. Those are not
* really commands, but are used in security attacks in order to talk to
* Redis instances via HTTP, with a technique called "cross protocol scripting"
* which exploits the fact that services like Redis will discard invalid
* HTTP headers and will process what follows.
*
* As a protection against this attack, Redis will terminate the connection
* when a POST or "Host:" header is seen, and will log the event from
* time to time (to avoid creating a DOS as a result of too many logs). */
void securityWarningCommand(client *c) {
static time_t logged_time;
time_t now = time(NULL);
if (labs(now-logged_time) > 60) {
serverLog(LL_WARNING,"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.");
logged_time = now;
}
freeClientAsync(c);
}
/* Rewrite the command vector of the client. All the new objects ref count
* is incremented. The old command vector is freed, and the old objects
* ref count is decremented. */
+5 -12
View File
@@ -241,11 +241,9 @@ void freeStringObject(robj *o) {
}
void freeListObject(robj *o) {
switch (o->encoding) {
case OBJ_ENCODING_QUICKLIST:
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
quicklistRelease(o->ptr);
break;
default:
} else {
serverPanic("Unknown list encoding type");
}
}
@@ -380,10 +378,10 @@ robj *tryObjectEncoding(robj *o) {
if (o->refcount > 1) return o;
/* Check if we can represent this string as a long integer.
* Note that we are sure that a string larger than 21 chars is not
* Note that we are sure that a string larger than 20 chars is not
* representable as a 32 nor 64 bit integer. */
len = sdslen(s);
if (len <= 21 && string2l(s,len,&value)) {
if (len <= 20 && string2l(s,len,&value)) {
/* This object is encodable as a long. Try to use a shared object.
* Note that we avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
@@ -616,18 +614,13 @@ int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, cons
int getLongLongFromObject(robj *o, long long *target) {
long long value;
char *eptr;
if (o == NULL) {
value = 0;
} else {
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
if (sdsEncodedObject(o)) {
errno = 0;
value = strtoll(o->ptr, &eptr, 10);
if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
errno == ERANGE)
return C_ERR;
if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;
} else if (o->encoding == OBJ_ENCODING_INT) {
value = (long)o->ptr;
} else {
+1
View File
@@ -671,6 +671,7 @@ int quicklistReplaceAtIndex(quicklist *quicklist, long index, void *data,
/* quicklistIndex provides an uncompressed node */
entry.node->zl = ziplistDelete(entry.node->zl, &entry.zi);
entry.node->zl = ziplistInsert(entry.node->zl, entry.zi, data, sz);
quicklistNodeUpdateSz(entry.node);
quicklistCompress(quicklist, entry.node);
return 1;
} else {
+65 -18
View File
@@ -45,13 +45,30 @@
#define RDB_LOAD_ENC (1<<0)
#define RDB_LOAD_PLAIN (1<<1)
#define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__);
#define rdbExitReportCorruptRDB(...) rdbCheckThenExit(__LINE__,__VA_ARGS__)
void rdbCheckThenExit(char *reason, int where) {
serverLog(LL_WARNING, "Corrupt RDB detected at rdb.c:%d (%s). "
"Running 'redis-check-rdb %s'",
where, reason, server.rdb_filename);
redis_check_rdb(server.rdb_filename);
extern int rdbCheckMode;
void rdbCheckError(const char *fmt, ...);
void rdbCheckSetError(const char *fmt, ...);
void rdbCheckThenExit(int linenum, char *reason, ...) {
va_list ap;
char msg[1024];
int len;
len = snprintf(msg,sizeof(msg),
"Internal error in RDB reading function at rdb.c:%d -> ", linenum);
va_start(ap,reason);
vsnprintf(msg+len,sizeof(msg)-len,reason,ap);
va_end(ap);
if (!rdbCheckMode) {
serverLog(LL_WARNING, "%s", msg);
char *argv[2] = {"",server.rdb_filename};
redis_check_rdb_main(2,argv);
} else {
rdbCheckError("%s",msg);
}
exit(1);
}
@@ -142,10 +159,14 @@ uint32_t rdbLoadLen(rio *rdb, int *isencoded) {
/* Read a 14 bit len. */
if (rioRead(rdb,buf+1,1) == 0) return RDB_LENERR;
return ((buf[0]&0x3F)<<8)|buf[1];
} else {
} else if (type == RDB_32BITLEN) {
/* Read a 32 bit len. */
if (rioRead(rdb,&len,4) == 0) return RDB_LENERR;
return ntohl(len);
} else {
rdbExitReportCorruptRDB(
"Unknown length encoding %d in rdbLoadLen()",type);
return -1; /* Never reached. */
}
}
@@ -199,7 +220,7 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) {
val = (int32_t)v;
} else {
val = 0; /* anti-warning */
rdbExitReportCorruptRDB("Unknown RDB integer encoding type");
rdbExitReportCorruptRDB("Unknown RDB integer encoding type %d",enctype);
}
if (plain) {
char buf[LONG_STR_SIZE], *p;
@@ -298,7 +319,10 @@ void *rdbLoadLzfStringObject(rio *rdb, int flags) {
/* Load the compressed representation and uncompress it to target. */
if (rioRead(rdb,c,clen) == 0) goto err;
if (lzf_decompress(c,clen,val,len) == 0) goto err;
if (lzf_decompress(c,clen,val,len) == 0) {
if (rdbCheckMode) rdbCheckSetError("Invalid LZF compressed string");
goto err;
}
zfree(c);
if (plain)
@@ -406,7 +430,7 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags) {
case RDB_ENC_LZF:
return rdbLoadLzfStringObject(rdb,flags);
default:
rdbExitReportCorruptRDB("Unknown RDB encoding type");
rdbExitReportCorruptRDB("Unknown RDB string encoding type %d",len);
}
}
@@ -895,7 +919,7 @@ int rdbSaveBackground(char *filename) {
pid_t childpid;
long long start;
if (server.rdb_child_pid != -1) return C_ERR;
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
server.dirty_before_bgsave = server.dirty;
server.lastbgsave_try = time(NULL);
@@ -1192,11 +1216,11 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
hashTypeConvert(o, OBJ_ENCODING_HT);
break;
default:
rdbExitReportCorruptRDB("Unknown encoding");
rdbExitReportCorruptRDB("Unknown RDB encoding type %d",rdbtype);
break;
}
} else {
rdbExitReportCorruptRDB("Unknown object type");
rdbExitReportCorruptRDB("Unknown RDB encoding type %d",rdbtype);
}
return o;
}
@@ -1562,7 +1586,7 @@ int rdbSaveToSlavesSockets(void) {
long long start;
int pipefds[2];
if (server.rdb_child_pid != -1) return C_ERR;
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
/* Before to fork, create a pipe that will be used in order to
* send back to the parent the IDs of the slaves that successfully
@@ -1666,9 +1690,6 @@ int rdbSaveToSlavesSockets(void) {
exitFromChild((retval == C_OK) ? 0 : 1);
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
if (childpid == -1) {
serverLog(LL_WARNING,"Can't save in background: fork: %s",
strerror(errno));
@@ -1691,6 +1712,10 @@ int rdbSaveToSlavesSockets(void) {
close(pipefds[0]);
close(pipefds[1]);
} else {
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
serverLog(LL_NOTICE,"Background RDB transfer started by pid %d",
childpid);
server.rdb_save_time_start = time(NULL);
@@ -1717,11 +1742,33 @@ void saveCommand(client *c) {
}
}
/* BGSAVE [SCHEDULE] */
void bgsaveCommand(client *c) {
int schedule = 0;
/* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite
* is in progress. Instead of returning an error a BGSAVE gets scheduled. */
if (c->argc > 1) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) {
schedule = 1;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
} else if (server.aof_child_pid != -1) {
addReplyError(c,"Can't BGSAVE while AOF log rewriting is in progress");
if (schedule) {
server.rdb_bgsave_scheduled = 1;
addReplyStatus(c,"Background saving scheduled");
} else {
addReplyError(c,
"An AOF log rewriting in progress: can't BGSAVE right now. "
"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenver "
"possible.");
}
} else if (rdbSaveBackground(server.rdb_filename) == C_OK) {
addReplyStatus(c,"Background saving started");
} else {
+25 -2
View File
@@ -65,6 +65,7 @@ static struct config {
int randomkeys_keyspacelen;
int keepalive;
int pipeline;
int showerrors;
long long start;
long long totlatency;
long long *latency;
@@ -212,6 +213,16 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
exit(1);
}
if (config.showerrors) {
static time_t lasterr_time = 0;
time_t now = time(NULL);
redisReply *r = reply;
if (r->type == REDIS_REPLY_ERROR && lasterr_time != now) {
lasterr_time = now;
printf("Error from server: %s\n", r->str);
}
}
freeReplyObject(reply);
/* This is an OK for prefix commands such as auth and select.*/
if (c->prefix_pending > 0) {
@@ -227,7 +238,7 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
c->randptr[j] -= c->prefixlen;
c->prefixlen = 0;
}
continue;
continue;
}
if (config.requests_finished < config.requests)
@@ -518,6 +529,8 @@ int parseOptions(int argc, const char **argv) {
config.loop = 1;
} else if (!strcmp(argv[i],"-I")) {
config.idlemode = 1;
} else if (!strcmp(argv[i],"-e")) {
config.showerrors = 1;
} else if (!strcmp(argv[i],"-t")) {
if (lastarg) goto invalid;
/* We get the list of tests to run as a string in the form
@@ -560,7 +573,7 @@ usage:
" -c <clients> Number of parallel connections (default 50)\n"
" -n <requests> Total number of requests (default 100000)\n"
" -d <size> Data size of SET/GET value in bytes (default 2)\n"
" -dbnum <db> SELECT the specified db number (default 0)\n"
" --dbnum <db> SELECT the specified db number (default 0)\n"
" -k <boolean> 1=keep alive 0=reconnect (default 1)\n"
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD\n"
" Using this option the benchmark will expand the string __rand_int__\n"
@@ -569,6 +582,8 @@ usage:
" is executed. Default tests use this to hit random keys in the\n"
" specified range.\n"
" -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).\n"
" -e If server replies with errors, show them on stdout.\n"
" (no more than 1 error per second is displayed)\n"
" -q Quiet. Just show query/sec values\n"
" --csv Output in CSV format\n"
" -l Loop. Run the tests forever\n"
@@ -649,6 +664,7 @@ int main(int argc, const char **argv) {
config.keepalive = 1;
config.datasize = 3;
config.pipeline = 1;
config.showerrors = 0;
config.randomkeys = 0;
config.randomkeys_keyspacelen = 0;
config.quiet = 0;
@@ -763,6 +779,13 @@ int main(int argc, const char **argv) {
free(cmd);
}
if (test_is_selected("hset")) {
len = redisFormatCommand(&cmd,
"HSET myset:__rand_int__ element:__rand_int__ %s",data);
benchmark("HSET",cmd,len);
free(cmd);
}
if (test_is_selected("spop")) {
len = redisFormatCommand(&cmd,"SPOP myset");
benchmark("SPOP",cmd,len);
+279 -644
View File
@@ -1,6 +1,5 @@
/*
* Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,683 +27,319 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "server.h"
#include "rdb.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "lzf.h"
#include "crc64.h"
#define ERROR(...) { \
serverLog(LL_WARNING, __VA_ARGS__); \
exit(1); \
#include <stdarg.h>
void createSharedObjects(void);
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len);
long long rdbLoadMillisecondTime(rio *rdb);
int rdbCheckMode = 0;
struct {
rio *rio;
robj *key; /* Current key we are reading. */
int key_type; /* Current key type if != -1. */
unsigned long keys; /* Number of keys processed. */
unsigned long expires; /* Number of keys with an expire. */
unsigned long already_expired; /* Number of keys already expired. */
int doing; /* The state while reading the RDB. */
int error_set; /* True if error is populated. */
char error[1024];
} rdbstate;
/* At every loading step try to remember what we were about to do, so that
* we can log this information when an error is encountered. */
#define RDB_CHECK_DOING_START 0
#define RDB_CHECK_DOING_READ_TYPE 1
#define RDB_CHECK_DOING_READ_EXPIRE 2
#define RDB_CHECK_DOING_READ_KEY 3
#define RDB_CHECK_DOING_READ_OBJECT_VALUE 4
#define RDB_CHECK_DOING_CHECK_SUM 5
#define RDB_CHECK_DOING_READ_LEN 6
#define RDB_CHECK_DOING_READ_AUX 7
char *rdb_check_doing_string[] = {
"start",
"read-type",
"read-expire",
"read-key",
"read-object-value",
"check-sum",
"read-len",
"read-aux"
};
char *rdb_type_string[] = {
"string",
"list-linked",
"set-hashtable",
"zset-v1",
"hash-hashtable",
"zset-v2",
"module-value",
"","",
"hash-zipmap",
"list-ziplist",
"set-intset",
"zset-ziplist",
"hash-ziplist",
"quicklist"
};
/* Show a few stats collected into 'rdbstate' */
void rdbShowGenericInfo(void) {
printf("[info] %lu keys read\n", rdbstate.keys);
printf("[info] %lu expires\n", rdbstate.expires);
printf("[info] %lu already expired\n", rdbstate.already_expired);
}
/* data type to hold offset in file and size */
typedef struct {
void *data;
size_t size;
size_t offset;
} pos;
/* Called on RDB errors. Provides details about the RDB and the offset
* we were when the error was detected. */
void rdbCheckError(const char *fmt, ...) {
char msg[1024];
va_list ap;
static unsigned char level = 0;
static pos positions[16];
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
#define CURR_OFFSET (positions[level].offset)
/* Hold a stack of errors */
typedef struct {
char error[16][1024];
size_t offset[16];
size_t level;
} errors_t;
static errors_t errors;
#define SHIFT_ERROR(provided_offset, ...) { \
sprintf(errors.error[errors.level], __VA_ARGS__); \
errors.offset[errors.level] = provided_offset; \
errors.level++; \
printf("--- RDB ERROR DETECTED ---\n");
printf("[offset %llu] %s\n",
(unsigned long long) (rdbstate.rio ?
rdbstate.rio->processed_bytes : 0), msg);
printf("[additional info] While doing: %s\n",
rdb_check_doing_string[rdbstate.doing]);
if (rdbstate.key)
printf("[additional info] Reading key '%s'\n",
(char*)rdbstate.key->ptr);
if (rdbstate.key_type != -1)
printf("[additional info] Reading type %d (%s)\n",
rdbstate.key_type,
((unsigned)rdbstate.key_type <
sizeof(rdb_type_string)/sizeof(char*)) ?
rdb_type_string[rdbstate.key_type] : "unknown");
rdbShowGenericInfo();
}
/* Data type to hold opcode with optional key name an success status */
typedef struct {
char* key;
int type;
char success;
} entry;
/* Print informations during RDB checking. */
void rdbCheckInfo(const char *fmt, ...) {
char msg[1024];
va_list ap;
#define MAX_TYPES_NUM 256
#define MAX_TYPE_NAME_LEN 16
/* store string types for output */
static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN];
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
/* Return true if 't' is a valid object type. */
static int rdbCheckType(unsigned char t) {
/* In case a new object type is added, update the following
* condition as necessary. */
return
(t >= RDB_TYPE_HASH_ZIPMAP && t <= RDB_TYPE_HASH_ZIPLIST) ||
t <= RDB_TYPE_HASH ||
t >= RDB_OPCODE_EXPIRETIME_MS;
printf("[offset %llu] %s\n",
(unsigned long long) (rdbstate.rio ?
rdbstate.rio->processed_bytes : 0), msg);
}
/* when number of bytes to read is negative, do a peek */
static int readBytes(void *target, long num) {
char peek = (num < 0) ? 1 : 0;
num = (num < 0) ? -num : num;
/* Used inside rdb.c in order to log specific errors happening inside
* the RDB loading internals. */
void rdbCheckSetError(const char *fmt, ...) {
va_list ap;
pos p = positions[level];
if (p.offset + num > p.size) {
return 0;
} else {
memcpy(target, (void*)((size_t)p.data + p.offset), num);
if (!peek) positions[level].offset += num;
}
return 1;
va_start(ap, fmt);
vsnprintf(rdbstate.error, sizeof(rdbstate.error), fmt, ap);
va_end(ap);
rdbstate.error_set = 1;
}
int processHeader(void) {
char buf[10] = "_________";
int dump_version;
/* During RDB check we setup a special signal handler for memory violations
* and similar conditions, so that we can log the offending part of the RDB
* if the crash is due to broken content. */
void rdbCheckHandleCrash(int sig, siginfo_t *info, void *secret) {
UNUSED(sig);
UNUSED(info);
UNUSED(secret);
if (!readBytes(buf, 9)) {
ERROR("Cannot read header");
}
/* expect the first 5 bytes to equal REDIS */
if (memcmp(buf,"REDIS",5) != 0) {
ERROR("Wrong signature in header");
}
dump_version = (int)strtol(buf + 5, NULL, 10);
if (dump_version < 1 || dump_version > 6) {
ERROR("Unknown RDB format version: %d", dump_version);
}
return dump_version;
rdbCheckError("Server crash checking the specified RDB file!");
exit(1);
}
static int loadType(entry *e) {
uint32_t offset = CURR_OFFSET;
void rdbCheckSetupSignals(void) {
struct sigaction act;
/* this byte needs to qualify as type */
unsigned char t;
if (readBytes(&t, 1)) {
if (rdbCheckType(t)) {
e->type = t;
return 1;
} else {
SHIFT_ERROR(offset, "Unknown type (0x%02x)", t);
}
} else {
SHIFT_ERROR(offset, "Could not read type");
}
/* failure */
return 0;
}
static int peekType() {
unsigned char t;
if (readBytes(&t, -1) && (rdbCheckType(t)))
return t;
return -1;
}
/* discard time, just consume the bytes */
static int processTime(int type) {
uint32_t offset = CURR_OFFSET;
unsigned char t[8];
int timelen = (type == RDB_OPCODE_EXPIRETIME_MS) ? 8 : 4;
if (readBytes(t,timelen)) {
return 1;
} else {
SHIFT_ERROR(offset, "Could not read time");
}
/* failure */
return 0;
}
static uint32_t loadLength(int *isencoded) {
unsigned char buf[2];
uint32_t len;
int type;
if (isencoded) *isencoded = 0;
if (!readBytes(buf, 1)) return RDB_LENERR;
type = (buf[0] & 0xC0) >> 6;
if (type == RDB_6BITLEN) {
/* Read a 6 bit len */
return buf[0] & 0x3F;
} else if (type == RDB_ENCVAL) {
/* Read a 6 bit len encoding type */
if (isencoded) *isencoded = 1;
return buf[0] & 0x3F;
} else if (type == RDB_14BITLEN) {
/* Read a 14 bit len */
if (!readBytes(buf+1,1)) return RDB_LENERR;
return ((buf[0] & 0x3F) << 8) | buf[1];
} else {
/* Read a 32 bit len */
if (!readBytes(&len, 4)) return RDB_LENERR;
return (unsigned int)ntohl(len);
}
}
static char *loadIntegerObject(int enctype) {
uint32_t offset = CURR_OFFSET;
unsigned char enc[4];
long long val;
if (enctype == RDB_ENC_INT8) {
uint8_t v;
if (!readBytes(enc, 1)) return NULL;
v = enc[0];
val = (int8_t)v;
} else if (enctype == RDB_ENC_INT16) {
uint16_t v;
if (!readBytes(enc, 2)) return NULL;
v = enc[0]|(enc[1]<<8);
val = (int16_t)v;
} else if (enctype == RDB_ENC_INT32) {
uint32_t v;
if (!readBytes(enc, 4)) return NULL;
v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);
val = (int32_t)v;
} else {
SHIFT_ERROR(offset, "Unknown integer encoding (0x%02x)", enctype);
return NULL;
}
/* convert val into string */
char *buf;
buf = zmalloc(sizeof(char) * 128);
sprintf(buf, "%lld", val);
return buf;
}
static char* loadLzfStringObject() {
unsigned int slen, clen;
char *c, *s;
if ((clen = loadLength(NULL)) == RDB_LENERR) return NULL;
if ((slen = loadLength(NULL)) == RDB_LENERR) return NULL;
c = zmalloc(clen);
if (!readBytes(c, clen)) {
zfree(c);
return NULL;
}
s = zmalloc(slen+1);
if (lzf_decompress(c,clen,s,slen) == 0) {
zfree(c); zfree(s);
return NULL;
}
zfree(c);
return s;
}
/* returns NULL when not processable, char* when valid */
static char* loadStringObject() {
uint32_t offset = CURR_OFFSET;
int isencoded;
uint32_t len;
len = loadLength(&isencoded);
if (isencoded) {
switch(len) {
case RDB_ENC_INT8:
case RDB_ENC_INT16:
case RDB_ENC_INT32:
return loadIntegerObject(len);
case RDB_ENC_LZF:
return loadLzfStringObject();
default:
/* unknown encoding */
SHIFT_ERROR(offset, "Unknown string encoding (0x%02x)", len);
return NULL;
}
}
if (len == RDB_LENERR) return NULL;
char *buf = zmalloc(sizeof(char) * (len+1));
if (buf == NULL) return NULL;
buf[len] = '\0';
if (!readBytes(buf, len)) {
zfree(buf);
return NULL;
}
return buf;
}
static int processStringObject(char** store) {
unsigned long offset = CURR_OFFSET;
char *key = loadStringObject();
if (key == NULL) {
SHIFT_ERROR(offset, "Error reading string object");
zfree(key);
return 0;
}
if (store != NULL) {
*store = key;
} else {
zfree(key);
}
return 1;
}
static double* loadDoubleValue() {
char buf[256];
unsigned char len;
double* val;
if (!readBytes(&len,1)) return NULL;
val = zmalloc(sizeof(double));
switch(len) {
case 255: *val = R_NegInf; return val;
case 254: *val = R_PosInf; return val;
case 253: *val = R_Nan; return val;
default:
if (!readBytes(buf, len)) {
zfree(val);
return NULL;
}
buf[len] = '\0';
sscanf(buf, "%lg", val);
return val;
}
}
static int processDoubleValue(double** store) {
unsigned long offset = CURR_OFFSET;
double *val = loadDoubleValue();
if (val == NULL) {
SHIFT_ERROR(offset, "Error reading double value");
zfree(val);
return 0;
}
if (store != NULL) {
*store = val;
} else {
zfree(val);
}
return 1;
}
static int loadPair(entry *e) {
uint32_t offset = CURR_OFFSET;
uint32_t i;
/* read key first */
char *key;
if (processStringObject(&key)) {
e->key = key;
} else {
SHIFT_ERROR(offset, "Error reading entry key");
return 0;
}
uint32_t length = 0;
if (e->type == RDB_TYPE_LIST ||
e->type == RDB_TYPE_SET ||
e->type == RDB_TYPE_ZSET ||
e->type == RDB_TYPE_HASH) {
if ((length = loadLength(NULL)) == RDB_LENERR) {
SHIFT_ERROR(offset, "Error reading %s length", types[e->type]);
return 0;
}
}
switch(e->type) {
case RDB_TYPE_STRING:
case RDB_TYPE_HASH_ZIPMAP:
case RDB_TYPE_LIST_ZIPLIST:
case RDB_TYPE_SET_INTSET:
case RDB_TYPE_ZSET_ZIPLIST:
case RDB_TYPE_HASH_ZIPLIST:
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading entry value");
return 0;
}
break;
case RDB_TYPE_LIST:
case RDB_TYPE_SET:
for (i = 0; i < length; i++) {
offset = CURR_OFFSET;
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading element at index %d (length: %d)", i, length);
return 0;
}
}
break;
case RDB_TYPE_ZSET:
for (i = 0; i < length; i++) {
offset = CURR_OFFSET;
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
return 0;
}
offset = CURR_OFFSET;
if (!processDoubleValue(NULL)) {
SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
return 0;
}
}
break;
case RDB_TYPE_HASH:
for (i = 0; i < length; i++) {
offset = CURR_OFFSET;
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
return 0;
}
offset = CURR_OFFSET;
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
return 0;
}
}
break;
default:
SHIFT_ERROR(offset, "Type not implemented");
return 0;
}
/* because we're done, we assume success */
e->success = 1;
return 1;
}
static entry loadEntry() {
entry e = { NULL, -1, 0 };
uint32_t length, offset[4];
/* reset error container */
errors.level = 0;
offset[0] = CURR_OFFSET;
if (!loadType(&e)) {
return e;
}
offset[1] = CURR_OFFSET;
if (e.type == RDB_OPCODE_SELECTDB) {
if ((length = loadLength(NULL)) == RDB_LENERR) {
SHIFT_ERROR(offset[1], "Error reading database number");
return e;
}
if (length > 63) {
SHIFT_ERROR(offset[1], "Database number out of range (%d)", length);
return e;
}
} else if (e.type == RDB_OPCODE_EOF) {
if (positions[level].offset < positions[level].size) {
SHIFT_ERROR(offset[0], "Unexpected EOF");
} else {
e.success = 1;
}
return e;
} else {
/* optionally consume expire */
if (e.type == RDB_OPCODE_EXPIRETIME ||
e.type == RDB_OPCODE_EXPIRETIME_MS) {
if (!processTime(e.type)) return e;
if (!loadType(&e)) return e;
}
offset[1] = CURR_OFFSET;
if (!loadPair(&e)) {
SHIFT_ERROR(offset[1], "Error for type %s", types[e.type]);
return e;
}
}
/* all entries are followed by a valid type:
* e.g. a new entry, SELECTDB, EXPIRE, EOF */
offset[2] = CURR_OFFSET;
if (peekType() == -1) {
SHIFT_ERROR(offset[2], "Followed by invalid type");
SHIFT_ERROR(offset[0], "Error for type %s", types[e.type]);
e.success = 0;
} else {
e.success = 1;
}
return e;
}
static void printCentered(int indent, int width, char* body) {
char head[256], tail[256];
memset(head, '\0', 256);
memset(tail, '\0', 256);
memset(head, '=', indent);
memset(tail, '=', width - 2 - indent - strlen(body));
serverLog(LL_WARNING, "%s %s %s", head, body, tail);
}
static void printValid(uint64_t ops, uint64_t bytes) {
char body[80];
sprintf(body, "Processed %llu valid opcodes (in %llu bytes)",
(unsigned long long) ops, (unsigned long long) bytes);
printCentered(4, 80, body);
}
static void printSkipped(uint64_t bytes, uint64_t offset) {
char body[80];
sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)",
(unsigned long long) bytes, (unsigned long long) offset);
printCentered(4, 80, body);
}
static void printErrorStack(entry *e) {
unsigned int i;
char body[64];
if (e->type == -1) {
sprintf(body, "Error trace");
} else if (e->type >= 253) {
sprintf(body, "Error trace (%s)", types[e->type]);
} else if (!e->key) {
sprintf(body, "Error trace (%s: (unknown))", types[e->type]);
} else {
char tmp[41];
strncpy(tmp, e->key, 40);
/* display truncation at the last 3 chars */
if (strlen(e->key) > 40) {
memset(&tmp[37], '.', 3);
}
/* display unprintable characters as ? */
for (i = 0; i < strlen(tmp); i++) {
if (tmp[i] <= 32) tmp[i] = '?';
}
sprintf(body, "Error trace (%s: %s)", types[e->type], tmp);
}
printCentered(4, 80, body);
/* display error stack */
for (i = 0; i < errors.level; i++) {
serverLog(LL_WARNING, "0x%08lx - %s",
(unsigned long) errors.offset[i], errors.error[i]);
}
}
void process(void) {
uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
entry entry = { NULL, -1, 0 };
int dump_version = processHeader();
/* Exclude the final checksum for RDB >= 5. Will be checked at the end. */
if (dump_version >= 5) {
if (positions[0].size < 8) {
serverLog(LL_WARNING, "RDB version >= 5 but no room for checksum.");
exit(1);
}
positions[0].size -= 8;
}
level = 1;
while(positions[0].offset < positions[0].size) {
positions[1] = positions[0];
entry = loadEntry();
if (!entry.success) {
printValid(num_valid_ops, num_valid_bytes);
printErrorStack(&entry);
num_errors++;
num_valid_ops = 0;
num_valid_bytes = 0;
/* search for next valid entry */
uint64_t offset = positions[0].offset + 1;
int i = 0;
while (!entry.success && offset < positions[0].size) {
positions[1].offset = offset;
/* find 3 consecutive valid entries */
for (i = 0; i < 3; i++) {
entry = loadEntry();
if (!entry.success) break;
}
/* check if we found 3 consecutive valid entries */
if (i < 3) {
offset++;
}
}
/* print how many bytes we have skipped to find a new valid opcode */
if (offset < positions[0].size) {
printSkipped(offset - positions[0].offset, offset);
}
positions[0].offset = offset;
} else {
num_valid_ops++;
num_valid_bytes += positions[1].offset - positions[0].offset;
/* advance position */
positions[0] = positions[1];
}
zfree(entry.key);
}
/* because there is another potential error,
* print how many valid ops we have processed */
printValid(num_valid_ops, num_valid_bytes);
/* expect an eof */
if (entry.type != RDB_OPCODE_EOF) {
/* last byte should be EOF, add error */
errors.level = 0;
SHIFT_ERROR(positions[0].offset, "Expected EOF, got %s", types[entry.type]);
/* this is an EOF error so reset type */
entry.type = -1;
printErrorStack(&entry);
num_errors++;
}
/* Verify checksum */
if (dump_version >= 5) {
uint64_t crc = crc64(0,positions[0].data,positions[0].size);
uint64_t crc2;
unsigned char *p = (unsigned char*)positions[0].data+positions[0].size;
crc2 = ((uint64_t)p[0] << 0) |
((uint64_t)p[1] << 8) |
((uint64_t)p[2] << 16) |
((uint64_t)p[3] << 24) |
((uint64_t)p[4] << 32) |
((uint64_t)p[5] << 40) |
((uint64_t)p[6] << 48) |
((uint64_t)p[7] << 56);
if (crc != crc2) {
SHIFT_ERROR(positions[0].offset, "RDB CRC64 does not match.");
} else {
serverLog(LL_WARNING, "CRC64 checksum is OK");
}
}
/* print summary on errors */
if (num_errors) {
serverLog(LL_WARNING, "Total unprocessable opcodes: %llu",
(unsigned long long) num_errors);
}
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
act.sa_sigaction = rdbCheckHandleCrash;
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
sigaction(SIGILL, &act, NULL);
}
/* Check the specified RDB file. */
int redis_check_rdb(char *rdbfilename) {
int fd;
off_t size;
struct stat stat;
void *data;
uint64_t dbid;
int type, rdbver;
char buf[1024];
long long expiretime, now = mstime();
FILE *fp;
rio rdb;
fd = open(rdbfilename, O_RDONLY);
if (fd < 1) {
ERROR("Cannot open file: %s", rdbfilename);
if ((fp = fopen(rdbfilename,"r")) == NULL) return C_ERR;
rioInitWithFile(&rdb,fp);
rdbstate.rio = &rdb;
rdb.update_cksum = rdbLoadProgressCallback;
if (rioRead(&rdb,buf,9) == 0) goto eoferr;
buf[9] = '\0';
if (memcmp(buf,"REDIS",5) != 0) {
rdbCheckError("Wrong signature trying to load DB from file");
return 1;
}
if (fstat(fd, &stat) == -1) {
ERROR("Cannot stat: %s", rdbfilename);
} else {
size = stat.st_size;
rdbver = atoi(buf+5);
if (rdbver < 1 || rdbver > RDB_VERSION) {
rdbCheckError("Can't handle RDB format version %d",rdbver);
return 1;
}
if (sizeof(size_t) == sizeof(int32_t) && size >= INT_MAX) {
ERROR("Cannot check dump files >2GB on a 32-bit platform");
startLoading(fp);
while(1) {
robj *key, *val;
expiretime = -1;
/* Read type. */
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
/* Handle special types. */
if (type == RDB_OPCODE_EXPIRETIME) {
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
/* EXPIRETIME: load an expire associated with the next key
* to load. Note that after loading an expire we need to
* load the actual type, and continue. */
if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;
/* We read the time so we need to read the object type again. */
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
/* the EXPIRETIME opcode specifies time in seconds, so convert
* into milliseconds. */
expiretime *= 1000;
} else if (type == RDB_OPCODE_EXPIRETIME_MS) {
/* EXPIRETIME_MS: milliseconds precision expire times introduced
* with RDB v3. Like EXPIRETIME but no with more precision. */
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;
/* We read the time so we need to read the object type again. */
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
} else if (type == RDB_OPCODE_EOF) {
/* EOF: End of file, exit the main loop. */
break;
} else if (type == RDB_OPCODE_SELECTDB) {
/* SELECTDB: Select the specified database. */
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
goto eoferr;
rdbCheckInfo("Selecting DB ID %d", dbid);
continue; /* Read type again. */
} else if (type == RDB_OPCODE_RESIZEDB) {
/* RESIZEDB: Hint about the size of the keys in the currently
* selected data base, in order to avoid useless rehashing. */
uint64_t db_size, expires_size;
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
goto eoferr;
if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
goto eoferr;
continue; /* Read type again. */
} else if (type == RDB_OPCODE_AUX) {
/* AUX: generic string-string fields. Use to add state to RDB
* which is backward compatible. Implementations of RDB loading
* are requierd to skip AUX fields they don't understand.
*
* An AUX field is composed of two strings: key and value. */
robj *auxkey, *auxval;
rdbstate.doing = RDB_CHECK_DOING_READ_AUX;
if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
rdbCheckInfo("AUX FIELD %s = '%s'",
(char*)auxkey->ptr, (char*)auxval->ptr);
decrRefCount(auxkey);
decrRefCount(auxval);
continue; /* Read type again. */
} else {
if (!rdbIsObjectType(type)) {
rdbCheckError("Invalid object type: %d", type);
return 1;
}
rdbstate.key_type = type;
}
/* Read key */
rdbstate.doing = RDB_CHECK_DOING_READ_KEY;
if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
rdbstate.key = key;
rdbstate.keys++;
/* Read value */
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
/* Check if the key already expired. This function is used when loading
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave. */
if (server.masterhost == NULL && expiretime != -1 && expiretime < now)
rdbstate.already_expired++;
if (expiretime != -1) rdbstate.expires++;
rdbstate.key = NULL;
decrRefCount(key);
decrRefCount(val);
rdbstate.key_type = -1;
}
/* Verify the checksum if RDB version is >= 5 */
if (rdbver >= 5 && server.rdb_checksum) {
uint64_t cksum, expected = rdb.cksum;
rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;
if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
memrev64ifbe(&cksum);
if (cksum == 0) {
rdbCheckInfo("RDB file was saved with checksum disabled: no check performed.");
} else if (cksum != expected) {
rdbCheckError("RDB CRC error");
} else {
rdbCheckInfo("Checksum OK");
}
}
data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
ERROR("Cannot mmap: %s", rdbfilename);
}
/* Initialize static vars */
positions[0].data = data;
positions[0].size = size;
positions[0].offset = 0;
errors.level = 0;
/* Object types */
sprintf(types[RDB_TYPE_STRING], "STRING");
sprintf(types[RDB_TYPE_LIST], "LIST");
sprintf(types[RDB_TYPE_SET], "SET");
sprintf(types[RDB_TYPE_ZSET], "ZSET");
sprintf(types[RDB_TYPE_HASH], "HASH");
/* Object types only used for dumping to disk */
sprintf(types[RDB_OPCODE_EXPIRETIME], "EXPIRETIME");
sprintf(types[RDB_OPCODE_SELECTDB], "SELECTDB");
sprintf(types[RDB_OPCODE_EOF], "EOF");
process();
munmap(data, size);
close(fd);
fclose(fp);
return 0;
eoferr: /* unexpected end of file is handled here with a fatal exit */
if (rdbstate.error_set) {
rdbCheckError(rdbstate.error);
} else {
rdbCheckError("Unexpected EOF reading RDB file");
}
return 1;
}
/* RDB check main: called form redis.c when Redis is executed with the
* redis-check-rdb alias. */
int redis_check_rdb_main(char **argv, int argc) {
* redis-check-rdb alias.
*
* The function never returns, but exits with the status code according
* to success (RDB is sane) or error (RDB is corrupted). */
int redis_check_rdb_main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <rdb-file-name>\n", argv[0]);
exit(1);
}
serverLog(LL_WARNING, "Checking RDB file %s", argv[1]);
exit(redis_check_rdb(argv[1]));
return 0;
createSharedObjects(); /* Needed for loading. */
server.loading_process_events_interval_bytes = 0;
rdbCheckMode = 1;
rdbCheckInfo("Checking RDB file %s", argv[1]);
rdbCheckSetupSignals();
int retval = redis_check_rdb(argv[1]);
if (retval == 0) {
rdbCheckInfo("\\o/ RDB looks OK! \\o/");
rdbShowGenericInfo();
}
exit(retval);
}
+25 -12
View File
@@ -161,7 +161,7 @@ static void cliRefreshPrompt(void) {
len = anetFormatAddr(config.prompt, sizeof(config.prompt),
config.hostip, config.hostport);
/* Add [dbnum] if needed */
if (config.dbnum != 0 && config.last_cmd_type != REDIS_REPLY_ERROR)
if (config.dbnum != 0)
len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
config.dbnum);
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
@@ -275,6 +275,10 @@ static void cliIntegrateHelp(void) {
* don't already match what we have. */
for (size_t j = 0; j < reply->elements; j++) {
redisReply *entry = reply->element[j];
if (entry->type != REDIS_REPLY_ARRAY || entry->elements < 4 ||
entry->element[0]->type != REDIS_REPLY_STRING ||
entry->element[1]->type != REDIS_REPLY_INTEGER ||
entry->element[3]->type != REDIS_REPLY_INTEGER) return;
char *cmdname = entry->element[0]->str;
int i;
@@ -916,7 +920,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
return REDIS_ERR;
} else {
/* Store database number when SELECT was successfully executed. */
if (!strcasecmp(command,"select") && argc == 2) {
if (!strcasecmp(command,"select") && argc == 2 && config.last_cmd_type != REDIS_REPLY_ERROR) {
config.dbnum = atoi(argv[1]);
cliRefreshPrompt();
} else if (!strcasecmp(command,"auth") && argc == 2) {
@@ -1255,6 +1259,7 @@ void cliLoadPreferences(void) {
if (argc > 0) cliSetPreferences(argv,argc,0);
sdsfreesplitres(argv,argc);
}
fclose(fp);
}
sdsfree(rcfile);
}
@@ -1266,6 +1271,11 @@ static void repl(void) {
int argc;
sds *argv;
/* Initialize the help and, if possible, use the COMMAND command in order
* to retrieve missing entries. */
cliInitHelp();
cliIntegrateHelp();
config.interactive = 1;
linenoiseSetMultiLine(1);
linenoiseSetCompletionCallback(completionCallback);
@@ -1320,9 +1330,10 @@ static void repl(void) {
} else {
long long start_time = mstime(), elapsed;
int repeat, skipargs = 0;
char *endptr;
repeat = atoi(argv[0]);
if (argc > 1 && repeat) {
repeat = strtol(argv[0], &endptr, 10);
if (argc > 1 && *endptr == '\0' && repeat) {
skipargs = 1;
} else {
repeat = 1;
@@ -1341,7 +1352,9 @@ static void repl(void) {
}
elapsed = mstime()-start_time;
if (elapsed >= 500) {
if (elapsed >= 500 &&
config.output == OUTPUT_STANDARD)
{
printf("(%.2fs)\n",(double)elapsed/1000);
}
}
@@ -2012,8 +2025,13 @@ static void getKeyTypes(redisReply *keys, int *types) {
keys->element[i]->str, context->err, context->errstr);
exit(1);
} else if(reply->type != REDIS_REPLY_STATUS) {
fprintf(stderr, "Invalid reply type (%d) for TYPE on key '%s'!\n",
reply->type, keys->element[i]->str);
if(reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr, "TYPE returned an error: %s\n", reply->str);
} else {
fprintf(stderr,
"Invalid reply type (%d) for TYPE on key '%s'!\n",
reply->type, keys->element[i]->str);
}
exit(1);
}
@@ -2596,11 +2614,6 @@ int main(int argc, char **argv) {
argc -= firstarg;
argv += firstarg;
/* Initialize the help and, if possible, use the COMMAND command in order
* to retrieve missing entries. */
cliInitHelp();
cliIntegrateHelp();
/* Latency mode */
if (config.latency_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
+5
View File
@@ -1305,6 +1305,11 @@ class RedisTrib
sleep 1
wait_cluster_join
flush_nodes_config # Useful for the replicas
# Reset the node information, so that when the
# final summary is listed in check_cluster about the newly created cluster
# all the nodes would get properly listed as slaves or masters
reset_nodes
load_cluster_info_from_node(argv[0])
check_cluster
end
+80 -20
View File
@@ -47,7 +47,7 @@ int cancelReplicationHandshake(void);
/* Return the pointer to a string representing the slave ip:listening_port
* pair. Mostly useful for logging, since we want to log a slave using its
* IP address and it's listening port which is more clear for the user, for
* IP address and its listening port which is more clear for the user, for
* example: "Closing connection with slave 10.1.2.3:6380". */
char *replicationGetSlaveName(client *c) {
static char buf[NET_PEER_ID_LEN];
@@ -55,7 +55,12 @@ char *replicationGetSlaveName(client *c) {
ip[0] = '\0';
buf[0] = '\0';
if (anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1) {
if (c->slave_ip[0] != '\0' ||
anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1)
{
/* Note that the 'ip' buffer is always larger than 'c->slave_ip' */
if (c->slave_ip[0] != '\0') memcpy(ip,c->slave_ip,sizeof(c->slave_ip));
if (c->slave_listening_port)
anetFormatAddr(buf,sizeof(buf),ip,c->slave_listening_port);
else
@@ -647,7 +652,7 @@ void syncCommand(client *c) {
} else {
/* No way, we need to wait for the next BGSAVE in order to
* register differences. */
serverLog(LL_NOTICE,"Waiting for next BGSAVE for SYNC");
serverLog(LL_NOTICE,"Can't attach the slave to the current BGSAVE. Waiting for next BGSAVE for SYNC");
}
/* CASE 2: BGSAVE is in progress, with socket target. */
@@ -657,7 +662,7 @@ void syncCommand(client *c) {
/* There is an RDB child process but it is writing directly to
* children sockets. We need to wait for the next BGSAVE
* in order to synchronize. */
serverLog(LL_NOTICE,"Waiting for next BGSAVE for SYNC");
serverLog(LL_NOTICE,"Current BGSAVE has socket target. Waiting for next BGSAVE for SYNC");
/* CASE 3: There is no BGSAVE is progress. */
} else {
@@ -666,12 +671,18 @@ void syncCommand(client *c) {
* replicationCron() since we want to delay its start a
* few seconds to wait for more slaves to arrive. */
if (server.repl_diskless_sync_delay)
serverLog(LL_NOTICE,"Delay next BGSAVE for SYNC");
serverLog(LL_NOTICE,"Delay next BGSAVE for diskless SYNC");
} else {
/* Target is disk (or the slave is not capable of supporting
* diskless replication) and we don't have a BGSAVE in progress,
* let's start one. */
if (startBgsaveForReplication(c->slave_capa) != C_OK) return;
if (server.aof_child_pid == -1) {
startBgsaveForReplication(c->slave_capa);
} else {
serverLog(LL_NOTICE,
"No BGSAVE in progress, but an AOF rewrite is active. "
"BGSAVE for replication delayed");
}
}
}
@@ -711,6 +722,15 @@ void replconfCommand(client *c) {
&port,NULL) != C_OK))
return;
c->slave_listening_port = port;
} else if (!strcasecmp(c->argv[j]->ptr,"ip-address")) {
sds ip = c->argv[j+1]->ptr;
if (sdslen(ip) < sizeof(c->slave_ip)) {
memcpy(c->slave_ip,ip,sdslen(ip)+1);
} else {
addReplyErrorFormat(c,"REPLCONF ip-address provided by "
"slave instance is too long: %zd bytes", sdslen(ip));
return;
}
} else if (!strcasecmp(c->argv[j]->ptr,"capa")) {
/* Ignore capabilities not understood by this master. */
if (!strcasecmp(c->argv[j+1]->ptr,"eof"))
@@ -1365,7 +1385,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
char tmpfile[256], *err = NULL;
int dfd, maxtries = 5;
int dfd = -1, maxtries = 5;
int sockerr = 0, psync_result;
socklen_t errlen = sizeof(sockerr);
UNUSED(el);
@@ -1453,7 +1473,8 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Set the slave port, so that Master's INFO command can list the
* slave listening port correctly. */
if (server.repl_state == REPL_STATE_SEND_PORT) {
sds port = sdsfromlonglong(server.port);
sds port = sdsfromlonglong(server.slave_announce_port ?
server.slave_announce_port : server.port);
err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
"listening-port",port, NULL);
sdsfree(port);
@@ -1473,6 +1494,37 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
"REPLCONF listening-port: %s", err);
}
sdsfree(err);
server.repl_state = REPL_STATE_SEND_IP;
}
/* Skip REPLCONF ip-address if there is no slave-announce-ip option set. */
if (server.repl_state == REPL_STATE_SEND_IP &&
server.slave_announce_ip == NULL)
{
server.repl_state = REPL_STATE_SEND_CAPA;
}
/* Set the slave ip, so that Master's INFO command can list the
* slave IP address port correctly in case of port forwarding or NAT. */
if (server.repl_state == REPL_STATE_SEND_IP) {
err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
"ip-address",server.slave_announce_ip, NULL);
if (err) goto write_error;
sdsfree(err);
server.repl_state = REPL_STATE_RECEIVE_IP;
return;
}
/* Receive REPLCONF ip-address reply. */
if (server.repl_state == REPL_STATE_RECEIVE_IP) {
err = sendSynchronousCommand(SYNC_CMD_READ,fd,NULL);
/* Ignore the error if any, not all the Redis versions support
* REPLCONF listening-port. */
if (err[0] == '-') {
serverLog(LL_NOTICE,"(Non critical) Master does not understand "
"REPLCONF ip-address: %s", err);
}
sdsfree(err);
server.repl_state = REPL_STATE_SEND_CAPA;
}
@@ -1588,6 +1640,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
error:
aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);
if (dfd != -1) close(dfd);
close(fd);
server.repl_transfer_s = -1;
server.repl_state = REPL_STATE_CONNECT;
@@ -1778,12 +1831,16 @@ void roleCommand(client *c) {
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
client *slave = ln->value;
char ip[NET_IP_STR_LEN];
char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;
if (anetPeerToString(slave->fd,ip,sizeof(ip),NULL) == -1) continue;
if (slaveip[0] == '\0') {
if (anetPeerToString(slave->fd,ip,sizeof(ip),NULL) == -1)
continue;
slaveip = ip;
}
if (slave->replstate != SLAVE_STATE_ONLINE) continue;
addReplyMultiBulkLen(c,3);
addReplyBulkCString(c,ip);
addReplyBulkCString(c,slaveip);
addReplyBulkLongLong(c,slave->slave_listening_port);
addReplyBulkLongLong(c,slave->repl_ack_off);
slaves++;
@@ -2304,13 +2361,12 @@ void replicationCron(void) {
replicationScriptCacheFlush();
}
/* If we are using diskless replication and there are slaves waiting
* in WAIT_BGSAVE_START state, check if enough seconds elapsed and
* start a BGSAVE.
/* Start a BGSAVE good for replication if we have slaves in
* WAIT_BGSAVE_START state.
*
* This code is also useful to trigger a BGSAVE if the diskless
* replication was turned off with CONFIG SET, while there were already
* slaves in WAIT_BGSAVE_START state. */
* In case of diskless replication, we make sure to wait the specified
* number of seconds (according to configuration) so that other slaves
* have the time to arrive before we start streaming. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {
time_t idle, max_idle = 0;
int slaves_waiting = 0;
@@ -2330,9 +2386,13 @@ void replicationCron(void) {
}
}
if (slaves_waiting && max_idle > server.repl_diskless_sync_delay) {
/* Start a BGSAVE. Usually with socket target, or with disk target
* if there was a recent socket -> disk config change. */
if (slaves_waiting &&
(!server.repl_diskless_sync ||
max_idle > server.repl_diskless_sync_delay))
{
/* Start the BGSAVE. The called function may start a
* BGSAVE with socket target or disk target depending on the
* configuration and slaves capabilities. */
startBgsaveForReplication(mincapa);
}
}
+2 -2
View File
@@ -443,6 +443,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
if (j == 10) {
cmdlog = sdscatprintf(cmdlog," ... (%d more)",
c->argc-j-1);
break;
} else {
cmdlog = sdscatlen(cmdlog," ",1);
cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);
@@ -900,7 +901,6 @@ void scriptingInit(int setup) {
server.lua_caller = NULL;
server.lua_timedout = 0;
server.lua_always_replicate_commands = 0; /* Only DEBUG can change it.*/
server.lua_time_limit = LUA_SCRIPT_TIME_LIMIT;
ldbInit();
}
@@ -2272,7 +2272,7 @@ ldbLog(sdsnew("[e]eval <code> Execute some Lua code (in a different callfr
ldbLog(sdsnew("[r]edis <cmd> Execute a Redis command."));
ldbLog(sdsnew("[m]axlen [len] Trim logged Redis replies and Lua var dumps to len."));
ldbLog(sdsnew(" Specifying zero as <len> means unlimited."));
ldbLog(sdsnew("[a]abort Stop the execution of the script. In sync"));
ldbLog(sdsnew("[a]bort Stop the execution of the script. In sync"));
ldbLog(sdsnew(" mode dataset changes will be retained."));
ldbLog(sdsnew(""));
ldbLog(sdsnew("Debugger functions you can call from Lua scripts:"));
+17 -4
View File
@@ -1062,11 +1062,18 @@ int sentinelUpdateSentinelAddressInAllMasters(sentinelRedisInstance *ri) {
sentinelRedisInstance *master = dictGetVal(de), *match;
match = getSentinelRedisInstanceByAddrAndRunID(master->sentinels,
NULL,0,ri->runid);
if (match->link->disconnected == 0) {
/* If there is no match, this master does not know about this
* Sentinel, try with the next one. */
if (match == NULL) continue;
/* Disconnect the old links if connected. */
if (match->link->cc != NULL)
instanceLinkCloseConnection(match->link,match->link->cc);
if (match->link->pc != NULL)
instanceLinkCloseConnection(match->link,match->link->pc);
}
if (match == ri) continue; /* Address already updated for it. */
/* Update the address of the matching Sentinel by copying the address
* of the Sentinel object that received the address update. */
releaseSentinelAddr(match->addr);
@@ -2572,9 +2579,15 @@ void sentinelSendPeriodicCommands(sentinelRedisInstance *ri) {
/* If this is a slave of a master in O_DOWN condition we start sending
* it INFO every second, instead of the usual SENTINEL_INFO_PERIOD
* period. In this state we want to closely monitor slaves in case they
* are turned into masters by another Sentinel, or by the sysadmin. */
* are turned into masters by another Sentinel, or by the sysadmin.
*
* Similarly we monitor the INFO output more often if the slave reports
* to be disconnected from the master, so that we can have a fresh
* disconnection time figure. */
if ((ri->flags & SRI_SLAVE) &&
(ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS))) {
((ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)) ||
(ri->master_link_down_time != 0)))
{
info_period = 1000;
} else {
info_period = SENTINEL_INFO_PERIOD;
+62 -12
View File
@@ -148,7 +148,7 @@ struct redisCommand redisCommandTable[] = {
{"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
{"rpop",rpopCommand,2,"wF",0,NULL,1,1,1,0,0},
{"lpop",lpopCommand,2,"wF",0,NULL,1,1,1,0,0},
{"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},
{"brpop",brpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
{"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},
{"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
{"llen",llenCommand,2,"rF",0,NULL,1,1,1,0,0},
@@ -231,7 +231,7 @@ struct redisCommand redisCommandTable[] = {
{"ping",pingCommand,-1,"tF",0,NULL,0,0,0,0,0},
{"echo",echoCommand,2,"F",0,NULL,0,0,0,0,0},
{"save",saveCommand,1,"as",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,1,"a",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,-1,"a",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"a",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"alt",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"RF",0,NULL,0,0,0,0,0},
@@ -284,8 +284,10 @@ struct redisCommand redisCommandTable[] = {
{"wait",waitCommand,3,"s",0,NULL,0,0,0,0,0},
{"command",commandCommand,0,"lt",0,NULL,0,0,0,0,0},
{"geoadd",geoaddCommand,-5,"wm",0,NULL,1,1,1,0,0},
{"georadius",georadiusCommand,-6,"w",0,NULL,1,1,1,0,0},
{"georadiusbymember",georadiusByMemberCommand,-5,"w",0,NULL,1,1,1,0,0},
{"georadius",georadiusCommand,-6,"w",0,georadiusGetKeys,1,1,1,0,0},
{"georadius_ro",georadiusroCommand,-6,"r",0,georadiusGetKeys,1,1,1,0,0},
{"georadiusbymember",georadiusbymemberCommand,-5,"w",0,georadiusGetKeys,1,1,1,0,0},
{"georadiusbymember_ro",georadiusbymemberroCommand,-5,"r",0,georadiusGetKeys,1,1,1,0,0},
{"geohash",geohashCommand,-2,"r",0,NULL,1,1,1,0,0},
{"geopos",geoposCommand,-2,"r",0,NULL,1,1,1,0,0},
{"geodist",geodistCommand,-4,"r",0,NULL,1,1,1,0,0},
@@ -294,6 +296,8 @@ struct redisCommand redisCommandTable[] = {
{"pfcount",pfcountCommand,-2,"r",0,NULL,1,-1,1,0,0},
{"pfmerge",pfmergeCommand,-2,"wm",0,NULL,1,-1,1,0,0},
{"pfdebug",pfdebugCommand,-3,"w",0,NULL,0,0,0,0,0},
{"post",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},
{"host:",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},
{"latency",latencyCommand,-2,"aslt",0,NULL,0,0,0,0,0}
};
@@ -775,6 +779,11 @@ void activeExpireCycle(int type) {
int dbs_per_call = CRON_DBS_PER_CALL;
long long start = ustime(), timelimit;
/* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of
* expires and evictions of keys not being performed. */
if (clientsArePaused()) return;
if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
/* Don't start a fast cycle if the previous cycle did not exited
* for time limt. Also don't repeat a fast cycle for the same period
@@ -1267,8 +1276,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Clear the paused clients flag if needed. */
clientsArePaused(); /* Don't check return value, just use the side effect. */
/* Replication cron function -- used to reconnect to master and
* to detect transfer failures. */
/* Replication cron function -- used to reconnect to master,
* detect transfer failures, start background RDB transfers and so forth. */
run_with_period(1000) replicationCron();
/* Run the Redis Cluster cron. */
@@ -1286,6 +1295,22 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
migrateCloseTimedoutSockets();
}
/* Start a scheduled BGSAVE if the corresponding flag is set. This is
* useful when we are forced to postpone a BGSAVE because an AOF
* rewrite is in progress.
*
* Note: this code must be after the replicationCron() call above so
* make sure when refactoring this file to keep this order. This is useful
* because we want to give priority to RDB savings for replication. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
server.rdb_bgsave_scheduled &&
(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||
server.lastbgsave_status == C_OK))
{
if (rdbSaveBackground(server.rdb_filename) == C_OK)
server.rdb_bgsave_scheduled = 0;
}
server.cronloops++;
return 1000/server.hz;
}
@@ -1515,6 +1540,7 @@ void initServerConfig(void) {
server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);
server.next_client_id = 1; /* Client IDs, start from 1 .*/
server.loading_process_events_interval_bytes = (1024*1024*2);
server.lua_time_limit = LUA_SCRIPT_TIME_LIMIT;
server.lruclock = getLRUClock();
resetServerSaveParams();
@@ -1538,6 +1564,8 @@ void initServerConfig(void) {
server.repl_diskless_sync = CONFIG_DEFAULT_REPL_DISKLESS_SYNC;
server.repl_diskless_sync_delay = CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY;
server.slave_priority = CONFIG_DEFAULT_SLAVE_PRIORITY;
server.slave_announce_ip = CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP;
server.slave_announce_port = CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT;
server.master_repl_offset = 0;
/* Replication partial resync backlog */
@@ -1572,6 +1600,8 @@ void initServerConfig(void) {
server.rpopCommand = lookupCommandByCString("rpop");
server.sremCommand = lookupCommandByCString("srem");
server.execCommand = lookupCommandByCString("exec");
server.expireCommand = lookupCommandByCString("expire");
server.pexpireCommand = lookupCommandByCString("pexpire");
/* Slow log */
server.slowlog_log_slower_than = CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN;
@@ -1758,6 +1788,7 @@ int listenToPort(int port, int *fds, int *count) {
if (server.bindaddr_count == 0) server.bindaddr[0] = NULL;
for (j = 0; j < server.bindaddr_count || j == 0; j++) {
if (server.bindaddr[j] == NULL) {
int unsupported = 0;
/* Bind * for both IPv6 and IPv4, we enter here only if
* server.bindaddr_count == 0. */
fds[*count] = anetTcp6Server(server.neterr,port,NULL,
@@ -1765,19 +1796,27 @@ int listenToPort(int port, int *fds, int *count) {
if (fds[*count] != ANET_ERR) {
anetNonBlock(NULL,fds[*count]);
(*count)++;
} else if (errno == EAFNOSUPPORT) {
unsupported++;
serverLog(LL_WARNING,"Not listening to IPv6: unsupproted");
}
if (*count == 1 || unsupported) {
/* Bind the IPv4 address as well. */
fds[*count] = anetTcpServer(server.neterr,port,NULL,
server.tcp_backlog);
if (fds[*count] != ANET_ERR) {
anetNonBlock(NULL,fds[*count]);
(*count)++;
} else if (errno == EAFNOSUPPORT) {
unsupported++;
serverLog(LL_WARNING,"Not listening to IPv4: unsupproted");
}
}
/* Exit the loop if we were able to bind * on IPv4 and IPv6,
* otherwise fds[*count] will be ANET_ERR and we'll print an
* error and return to the caller with an error. */
if (*count == 2) break;
if (*count + unsupported == 2) break;
} else if (strchr(server.bindaddr[j],':')) {
/* Bind IPv6 address. */
fds[*count] = anetTcp6Server(server.neterr,port,server.bindaddr[j],
@@ -1904,6 +1943,7 @@ void initServer(void) {
server.rdb_child_pid = -1;
server.aof_child_pid = -1;
server.rdb_child_type = RDB_CHILD_TYPE_NONE;
server.rdb_bgsave_scheduled = 0;
aofRewriteBufferReset();
server.aof_buf = sdsempty();
server.lastsave = time(NULL); /* At startup we consider the DB saved. */
@@ -2555,8 +2595,9 @@ int prepareForShutdown(int flags) {
"There is a child rewriting the AOF. Killing it!");
kill(server.aof_child_pid,SIGUSR1);
}
/* Append only file: fsync() the AOF and exit */
/* Append only file: flush buffers and fsync() the AOF at exit */
serverLog(LL_NOTICE,"Calling fsync() on the AOF file.");
flushAppendOnlyFile(1);
aof_fsync(server.aof_fd);
}
@@ -3174,11 +3215,15 @@ sds genRedisInfoString(char *section) {
while((ln = listNext(&li))) {
client *slave = listNodeValue(ln);
char *state = NULL;
char ip[NET_IP_STR_LEN];
char ip[NET_IP_STR_LEN], *slaveip = slave->slave_ip;
int port;
long lag = 0;
if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1) continue;
if (slaveip[0] == '\0') {
if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1)
continue;
slaveip = ip;
}
switch(slave->replstate) {
case SLAVE_STATE_WAIT_BGSAVE_START:
case SLAVE_STATE_WAIT_BGSAVE_END:
@@ -3198,7 +3243,7 @@ sds genRedisInfoString(char *section) {
info = sdscatprintf(info,
"slave%d:ip=%s,port=%d,state=%s,"
"offset=%lld,lag=%ld\r\n",
slaveid,ip,slave->slave_listening_port,state,
slaveid,slaveip,slave->slave_listening_port,state,
slave->repl_ack_off, lag);
slaveid++;
}
@@ -3427,6 +3472,11 @@ int freeMemoryIfNeeded(void) {
int slaves = listLength(server.slaves);
mstime_t latency, eviction_latency;
/* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of
* expires and evictions of keys not being performed. */
if (clientsArePaused()) return C_OK;
/* Remove the size of slaves output buffers and AOF buffer from the
* count of used memory. */
mem_used = zmalloc_used_memory();
@@ -3968,7 +4018,7 @@ int main(int argc, char **argv) {
* the program main. However the program is part of the Redis executable
* so that we can easily execute an RDB check on loading errors. */
if (strstr(argv[0],"redis-check-rdb") != NULL)
exit(redis_check_rdb_main(argv,argc));
redis_check_rdb_main(argc,argv);
if (argc >= 2) {
j = 1; /* First option to parse in argv[] */
+25 -12
View File
@@ -110,7 +110,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_CLUSTER_CONFIG_FILE "nodes.conf"
#define CONFIG_DEFAULT_DAEMONIZE 0
#define CONFIG_DEFAULT_UNIX_SOCKET_PERM 0
#define CONFIG_DEFAULT_TCP_KEEPALIVE 0
#define CONFIG_DEFAULT_TCP_KEEPALIVE 300
#define CONFIG_DEFAULT_PROTECTED_MODE 1
#define CONFIG_DEFAULT_LOGFILE ""
#define CONFIG_DEFAULT_SYSLOG_ENABLED 0
@@ -122,6 +122,8 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define CONFIG_DEFAULT_SLAVE_READ_ONLY 1
#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP NULL
#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT 0
#define CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
#define CONFIG_DEFAULT_MAXMEMORY 0
#define CONFIG_DEFAULT_MAXMEMORY_SAMPLES 5
@@ -302,13 +304,15 @@ typedef long long mstime_t; /* millisecond time type. */
#define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */
#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */
#define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */
#define REPL_STATE_SEND_CAPA 8 /* Send REPLCONF capa */
#define REPL_STATE_RECEIVE_CAPA 9 /* Wait for REPLCONF reply */
#define REPL_STATE_SEND_PSYNC 10 /* Send PSYNC */
#define REPL_STATE_RECEIVE_PSYNC 11 /* Wait for PSYNC reply */
#define REPL_STATE_SEND_IP 8 /* Send REPLCONF ip-address */
#define REPL_STATE_RECEIVE_IP 9 /* Wait for REPLCONF reply */
#define REPL_STATE_SEND_CAPA 10 /* Send REPLCONF capa */
#define REPL_STATE_RECEIVE_CAPA 11 /* Wait for REPLCONF reply */
#define REPL_STATE_SEND_PSYNC 12 /* Send PSYNC */
#define REPL_STATE_RECEIVE_PSYNC 13 /* Wait for PSYNC reply */
/* --- End of handshake states --- */
#define REPL_STATE_TRANSFER 12 /* Receiving .rdb from master */
#define REPL_STATE_CONNECTED 13 /* Connected to master */
#define REPL_STATE_TRANSFER 14 /* Receiving .rdb from master */
#define REPL_STATE_CONNECTED 15 /* Connected to master */
/* State of slaves from the POV of the master. Used in client->replstate.
* In SEND_BULK and ONLINE state the slave receives new updates
@@ -594,7 +598,8 @@ typedef struct client {
copying this slave output buffer
should use. */
char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. */
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
int slave_listening_port; /* As configured with: REPLCONF listening-port */
char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
multiState mstate; /* MULTI/EXEC state */
int btype; /* Type of blocking op if CLIENT_BLOCKED. */
@@ -747,7 +752,8 @@ struct redisServer {
off_t loading_process_events_interval_bytes;
/* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,
*rpopCommand, *sremCommand, *execCommand;
*rpopCommand, *sremCommand, *execCommand, *expireCommand,
*pexpireCommand;
/* Fields used only for stats */
time_t stat_starttime; /* Server start time */
long long stat_numcommands; /* Number of processed commands */
@@ -837,6 +843,7 @@ struct redisServer {
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
time_t rdb_save_time_last; /* Time used by last RDB save run. */
time_t rdb_save_time_start; /* Current RDB save start time. */
int rdb_bgsave_scheduled; /* BGSAVE when possible if true. */
int rdb_child_type; /* Type of save by active child. */
int lastbgsave_status; /* C_OK or C_ERR */
int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */
@@ -889,7 +896,9 @@ struct redisServer {
time_t repl_down_since; /* Unix time at which link with master went down */
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
int slave_priority; /* Reported in INFO and used by Sentinel. */
char repl_master_runid[CONFIG_RUN_ID_SIZE+1]; /* Master run id for PSYNC. */
int slave_announce_port; /* Give the master this listening port. */
char *slave_announce_ip; /* Give the master this ip address. */
char repl_master_runid[CONFIG_RUN_ID_SIZE+1]; /* Master run id for PSYNC.*/
long long repl_master_initial_offset; /* Master PSYNC offset. */
/* Replication script cache. */
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
@@ -1427,6 +1436,7 @@ int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *num
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
/* Cluster */
void clusterInit(void);
@@ -1446,7 +1456,7 @@ void sentinelIsRunning(void);
/* redis-check-rdb */
int redis_check_rdb(char *rdbfilename);
int redis_check_rdb_main(char **argv, int argc);
int redis_check_rdb_main(int argc, char **argv);
/* Scripting */
void scriptingInit(int setup);
@@ -1625,8 +1635,10 @@ void replconfCommand(client *c);
void waitCommand(client *c);
void geoencodeCommand(client *c);
void geodecodeCommand(client *c);
void georadiusByMemberCommand(client *c);
void georadiusbymemberCommand(client *c);
void georadiusbymemberroCommand(client *c);
void georadiusCommand(client *c);
void georadiusroCommand(client *c);
void geoaddCommand(client *c);
void geohashCommand(client *c);
void geoposCommand(client *c);
@@ -1637,6 +1649,7 @@ void pfcountCommand(client *c);
void pfmergeCommand(client *c);
void pfdebugCommand(client *c);
void latencyCommand(client *c);
void securityWarningCommand(client *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
+1 -1
View File
@@ -35,7 +35,7 @@ typedef struct slowlogEntry {
robj **argv;
int argc;
long long id; /* Unique entry identifier. */
long long duration; /* Time spent by the query, in nanoseconds. */
long long duration; /* Time spent by the query, in microseconds. */
time_t time; /* Unix time at which the query was executed. */
} slowlogEntry;
+1 -1
View File
@@ -1247,7 +1247,7 @@ void zaddGenericCommand(client *c, int flags) {
/* After the options, we expect to have an even number of args, since
* we expect any number of score-element pairs. */
elements = c->argc-scoreidx;
if (elements % 2) {
if (elements % 2 || !elements) {
addReply(c,shared.syntaxerr);
return;
}
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "3.2.1"
#define REDIS_VERSION "3.2.11"
+23 -15
View File
@@ -180,7 +180,7 @@ typedef struct zlentry {
void ziplistRepr(unsigned char *zl);
/* Return bytes needed to store integer encoded by 'encoding' */
static unsigned int zipIntSize(unsigned char encoding) {
unsigned int zipIntSize(unsigned char encoding) {
switch(encoding) {
case ZIP_INT_8B: return 1;
case ZIP_INT_16B: return 2;
@@ -195,7 +195,7 @@ static unsigned int zipIntSize(unsigned char encoding) {
/* Encode the length 'rawlen' writing it in 'p'. If p is NULL it just returns
* the amount of bytes required to encode such a length. */
static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
unsigned char len = 1, buf[5];
if (ZIP_IS_STR(encoding)) {
@@ -259,7 +259,7 @@ static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, un
/* Encode the length of the previous entry and write it to "p". Return the
* number of bytes needed to encode this length if "p" is NULL. */
static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
if (p == NULL) {
return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1;
} else {
@@ -277,7 +277,7 @@ static unsigned int zipPrevEncodeLength(unsigned char *p, unsigned int len) {
/* Encode the length of the previous entry and write it to "p". This only
* uses the larger encoding (required in __ziplistCascadeUpdate). */
static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
if (p == NULL) return;
p[0] = ZIP_BIGLEN;
memcpy(p+1,&len,sizeof(len));
@@ -309,14 +309,14 @@ static void zipPrevEncodeLengthForceLarge(unsigned char *p, unsigned int len) {
/* Return the difference in number of bytes needed to store the length of the
* previous element 'len', in the entry pointed to by 'p'. */
static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
unsigned int prevlensize;
ZIP_DECODE_PREVLENSIZE(p, prevlensize);
return zipPrevEncodeLength(NULL, len) - prevlensize;
}
/* Return the total number of bytes used by the entry pointed to by 'p'. */
static unsigned int zipRawEntryLength(unsigned char *p) {
unsigned int zipRawEntryLength(unsigned char *p) {
unsigned int prevlensize, encoding, lensize, len;
ZIP_DECODE_PREVLENSIZE(p, prevlensize);
ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
@@ -325,7 +325,7 @@ static unsigned int zipRawEntryLength(unsigned char *p) {
/* Check if string pointed to by 'entry' can be encoded as an integer.
* Stores the integer value in 'v' and its encoding in 'encoding'. */
static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
long long value;
if (entrylen >= 32 || entrylen == 0) return 0;
@@ -352,7 +352,7 @@ static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long
}
/* Store integer 'value' at 'p', encoded as 'encoding' */
static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
int16_t i16;
int32_t i32;
int64_t i64;
@@ -382,7 +382,7 @@ static void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encodi
}
/* Read integer encoded as 'encoding' from 'p' */
static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
int16_t i16;
int32_t i32;
int64_t i64, ret = 0;
@@ -414,7 +414,7 @@ static int64_t zipLoadInteger(unsigned char *p, unsigned char encoding) {
}
/* Return a struct with all information about an entry. */
static void zipEntry(unsigned char *p, zlentry *e) {
void zipEntry(unsigned char *p, zlentry *e) {
ZIP_DECODE_PREVLEN(p, e->prevrawlensize, e->prevrawlen);
ZIP_DECODE_LENGTH(p + e->prevrawlensize, e->encoding, e->lensize, e->len);
@@ -434,7 +434,7 @@ unsigned char *ziplistNew(void) {
}
/* Resize the ziplist. */
static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
zl = zrealloc(zl,len);
ZIPLIST_BYTES(zl) = intrev32ifbe(len);
zl[len-1] = ZIP_END;
@@ -461,7 +461,7 @@ static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
*
* The pointer "p" points to the first entry that does NOT need to be
* updated, i.e. consecutive fields MAY need an update. */
static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;
size_t offset, noffset, extra;
unsigned char *np;
@@ -523,7 +523,7 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
}
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
unsigned int i, totlen, deleted = 0;
size_t offset;
int nextdiff = 0;
@@ -583,7 +583,7 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig
}
/* Insert item at "p". */
static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
unsigned int prevlensize, prevlen = 0;
size_t offset;
@@ -621,7 +621,12 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig
/* When the insert position is not equal to the tail, we need to
* make sure that the next entry can hold this entry's length in
* its prevlen field. */
int forcelarge = 0;
nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
if (nextdiff == -4 && reqlen < 4) {
nextdiff = 0;
forcelarge = 1;
}
/* Store offset because a realloc may change the address of zl. */
offset = p-zl;
@@ -634,7 +639,10 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig
memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);
/* Encode this entry's raw length in the next entry. */
zipPrevEncodeLength(p+reqlen,reqlen);
if (forcelarge)
zipPrevEncodeLengthForceLarge(p+reqlen,reqlen);
else
zipPrevEncodeLength(p+reqlen,reqlen);
/* Update offset for tail */
ZIPLIST_TAIL_OFFSET(zl) =
+3 -2
View File
@@ -413,8 +413,9 @@ size_t zmalloc_get_memory_size(void) {
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#endif /* sysctl and sysconf variants */
#else
return 0L; /* Unknown method to get the data. */
#endif
#else
return 0L; /* Unknown OS. */
#endif
+1 -1
View File
@@ -89,7 +89,7 @@ close $fd
start_server_and_kill_it [list "dir" $server_path] {
test {Server should not start if RDB is corrupted} {
wait_for_condition 50 100 {
[string match {*RDB checksum*} \
[string match {*CRC error*} \
[exec tail -n10 < [dict get $srv stdout]]]
} else {
fail "Server started even if RDB was corrupted!"
+1 -1
View File
@@ -110,7 +110,7 @@ foreach diskless {no yes} {
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
} $diskless 0
test_psync {ok psync} 6 1000000 3600 0 {
test_psync {ok psync} 6 100000000 3600 0 {
assert {[s -1 sync_partial_ok] > 0}
} $diskless 1
+2 -1
View File
@@ -6,7 +6,8 @@ test "Manual failover works" {
set old_port [RI $master_id tcp_port]
set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]
assert {[lindex $addr 1] == $old_port}
S 0 SENTINEL FAILOVER mymaster
catch {S 0 SENTINEL FAILOVER mymaster} reply
assert {$reply eq "OK"}
foreach_sentinel_id id {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
@@ -0,0 +1,68 @@
# Test conditions where an instance is considered to be down
source "../tests/includes/init-tests.tcl"
proc ensure_master_up {} {
wait_for_condition 1000 50 {
[dict get [S 4 sentinel master mymaster] flags] eq "master"
} else {
fail "Master flags are not just 'master'"
}
}
proc ensure_master_down {} {
wait_for_condition 1000 50 {
[string match *down* \
[dict get [S 4 sentinel master mymaster] flags]]
} else {
fail "Master is not flagged SDOWN"
}
}
test "Crash the majority of Sentinels to prevent failovers for this unit" {
for {set id 0} {$id < $quorum} {incr id} {
kill_instance sentinel $id
}
}
test "SDOWN is triggered by non-responding but not crashed instance" {
lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port
ensure_master_up
exec ../../../src/redis-cli -h $host -p $port debug sleep 10 > /dev/null &
ensure_master_down
ensure_master_up
}
test "SDOWN is triggered by crashed instance" {
lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port
ensure_master_up
kill_instance redis 0
ensure_master_down
restart_instance redis 0
ensure_master_up
}
test "SDOWN is triggered by masters advertising as slaves" {
ensure_master_up
R 0 slaveof 127.0.0.1 34567
ensure_master_down
R 0 slaveof no one
ensure_master_up
}
test "SDOWN is triggered by misconfigured instance repling with errors" {
ensure_master_up
set orig_dir [lindex [R 0 config get dir] 1]
set orig_save [lindex [R 0 config get save] 1]
# Set dir to / and filename to "tmp" to make sure it will fail.
R 0 config set dir /
R 0 config set dbfilename tmp
R 0 config set save "1000000 1000000"
R 0 bgsave
ensure_master_down
R 0 config set save $orig_save
R 0 config set dir $orig_dir
R 0 config set dbfilename dump.rdb
R 0 bgsave
ensure_master_up
}
+9
View File
@@ -189,4 +189,13 @@ start_server {tags {"bitops"}} {
r set bits 1
r bitfield bits get u1 0
} {0}
test {BITFIELD regression for #3564} {
for {set j 0} {$j < 10} {incr j} {
r del mystring
set res [r BITFIELD mystring SET i8 0 10 SET i8 64 10 INCRBY i8 10 99900]
assert {$res eq {0 0 60}}
}
r del mystring
}
}
+21
View File
@@ -198,4 +198,25 @@ start_server {tags {"expire"}} {
r set foo b
lsort [r keys *]
} {a e foo s t}
test {EXPIRE with empty string as TTL should report an error} {
r set foo bar
catch {r expire foo ""} e
set e
} {*not an integer*}
test {SET - use EX/PX option, TTL should not be reseted after loadaof} {
r config set appendonly yes
r set foo bar EX 100
after 2000
r debug loadaof
set ttl [r ttl foo]
assert {$ttl <= 98 && $ttl > 90}
r set foo bar PX 100000
after 2000
r debug loadaof
set ttl [r ttl foo]
assert {$ttl <= 98 && $ttl > 90}
}
}
+89 -4
View File
@@ -1,4 +1,4 @@
# Helper functins to simulate search-in-radius in the Tcl side in order to
# Helper functions to simulate search-in-radius in the Tcl side in order to
# verify the Redis implementation with a fuzzy test.
proc geo_degrad deg {expr {$deg*atan(1)*8/360}}
@@ -23,6 +23,44 @@ proc geo_random_point {lonvar latvar} {
set lat [expr {-70 + rand()*140}]
}
# Return elements non common to both the lists.
# This code is from http://wiki.tcl.tk/15489
proc compare_lists {List1 List2} {
set DiffList {}
foreach Item $List1 {
if {[lsearch -exact $List2 $Item] == -1} {
lappend DiffList $Item
}
}
foreach Item $List2 {
if {[lsearch -exact $List1 $Item] == -1} {
if {[lsearch -exact $DiffList $Item] == -1} {
lappend DiffList $Item
}
}
}
return $DiffList
}
# The following list represents sets of random seed, search position
# and radius that caused bugs in the past. It is used by the randomized
# test later as a starting point. When the regression vectors are scanned
# the code reverts to using random data.
#
# The format is: seed km lon lat
set regression_vectors {
{1412 156 149.29737817929004 15.95807862745508}
{441574 143 59.235461856813856 66.269555127373678}
{160645 187 -101.88575239939883 49.061997951502917}
{750269 154 -90.187939661642517 66.615930412251487}
{342880 145 163.03472387745728 64.012747720821181}
{729955 143 137.86663517256579 63.986745399416776}
{939895 151 59.149620271823181 65.204186651485145}
{1412 156 149.29737817929004 15.95807862745508}
{564862 149 84.062063109158544 -65.685403922426232}
}
set rv_idx 0
start_server {tags {"geo"}} {
test {GEOADD create} {
r geoadd nyc -73.9454966 40.747533 "lic market"
@@ -183,16 +221,33 @@ start_server {tags {"geo"}} {
}
test {GEOADD + GEORANGE randomized test} {
set attempt 10
set attempt 30
while {[incr attempt -1]} {
set rv [lindex $regression_vectors $rv_idx]
incr rv_idx
unset -nocomplain debuginfo
set srand_seed [randomInt 1000000]
set srand_seed [clock milliseconds]
if {$rv ne {}} {set srand_seed [lindex $rv 0]}
lappend debuginfo "srand_seed is $srand_seed"
expr {srand($srand_seed)} ; # If you need a reproducible run
r del mypoints
set radius_km [expr {[randomInt 200]+10}]
if {[randomInt 10] == 0} {
# From time to time use very big radiuses
set radius_km [expr {[randomInt 50000]+10}]
} else {
# Normally use a few - ~200km radiuses to stress
# test the code the most in edge cases.
set radius_km [expr {[randomInt 200]+10}]
}
if {$rv ne {}} {set radius_km [lindex $rv 1]}
set radius_m [expr {$radius_km*1000}]
geo_random_point search_lon search_lat
if {$rv ne {}} {
set search_lon [lindex $rv 2]
set search_lat [lindex $rv 3]
}
lappend debuginfo "Search area: $search_lon,$search_lat $radius_km km"
set tcl_result {}
set argv {}
@@ -208,10 +263,40 @@ start_server {tags {"geo"}} {
set res [lsort [r georadius mypoints $search_lon $search_lat $radius_km km]]
set res2 [lsort $tcl_result]
set test_result OK
if {$res != $res2} {
set rounding_errors 0
set diff [compare_lists $res $res2]
foreach place $diff {
set mydist [geo_distance $lon $lat $search_lon $search_lat]
set mydist [expr $mydist/1000]
if {($mydist / $radius_km) > 0.999} {incr rounding_errors}
}
# Make sure this is a real error and not a rounidng issue.
if {[llength $diff] == $rounding_errors} {
set res $res2; # Error silenced
}
}
if {$res != $res2} {
set diff [compare_lists $res $res2]
puts "*** Possible problem in GEO radius query ***"
puts "Redis: $res"
puts "Tcl : $res2"
puts "Diff : $diff"
puts [join $debuginfo "\n"]
foreach place $diff {
if {[lsearch -exact $res2 $place] != -1} {
set where "(only in Tcl)"
} else {
set where "(only in Redis)"
}
lassign [lindex [r geopos mypoints $place] 0] lon lat
set mydist [geo_distance $lon $lat $search_lon $search_lat]
set mydist [expr $mydist/1000]
puts "$place -> [r geopos mypoints $place] $mydist $where"
if {($mydist / $radius_km) > 0.999} {incr rounding_errors}
}
set test_result FAIL
}
unset -nocomplain debuginfo
+44
View File
@@ -13,6 +13,50 @@ start_server {
assert_equal [r lindex l 1] [lindex $mylist 1]
}
test {Regression for quicklist #3343 bug} {
r del mylist
r lpush mylist 401
r lpush mylist 392
r rpush mylist [string repeat x 5105]"799"
r lset mylist -1 [string repeat x 1014]"702"
r lpop mylist
r lset mylist -1 [string repeat x 4149]"852"
r linsert mylist before 401 [string repeat x 9927]"12"
r lrange mylist 0 -1
r ping ; # It's enough if the server is still alive
} {PONG}
test {Stress tester for #3343-alike bugs} {
r del key
for {set j 0} {$j < 10000} {incr j} {
set op [randomInt 6]
set small_signed_count [expr 5-[randomInt 10]]
if {[randomInt 2] == 0} {
set ele [randomInt 1000]
} else {
set ele [string repeat x [randomInt 10000]][randomInt 1000]
}
switch $op {
0 {r lpush key $ele}
1 {r rpush key $ele}
2 {r lpop key}
3 {r rpop key}
4 {
catch {r lset key $small_signed_count $ele}
}
5 {
set otherele [randomInt 1000]
if {[randomInt 2] == 0} {
set where before
} else {
set where after
}
r linsert key $where $otherele $ele
}
}
}
}
tags {slow} {
test {ziplist implementation: value encoding and backlink} {
if {$::accurate} {set iterations 100} else {set iterations 10}
+44
View File
@@ -0,0 +1,44 @@
/* Trivia program to corrupt an RDB file in order to check the RDB check
* program behavior and effectiveness.
*
* Copyright (C) 2016 Salvatore Sanfilippo.
* This software is released in the 3-clause BSD license. */
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
int main(int argc, char **argv) {
struct stat stat;
int fd, cycles;
if (argc != 3) {
fprintf(stderr,"Usage: <filename> <cycles>\n");
exit(1);
}
srand(time(NULL));
cycles = atoi(argv[2]);
fd = open("dump.rdb",O_RDWR);
if (fd == -1) {
perror("open");
exit(1);
}
fstat(fd,&stat);
while(cycles--) {
unsigned char buf[32];
unsigned long offset = rand()%stat.st_size;
int writelen = 1+rand()%31;
int j;
for (j = 0; j < writelen; j++) buf[j] = (char)rand();
lseek(fd,offset,SEEK_SET);
printf("Writing %d bytes at offset %lu\n", writelen, offset);
write(fd,buf,writelen);
}
return 0;
}
+4 -4
View File
@@ -2,7 +2,7 @@ Create-custer is a small script used to easily start a big number of Redis
instances configured to run in cluster mode. Its main goal is to allow manual
testing in a condition which is not easy to replicate with the Redis cluster
unit tests, for example when a lot of instances are needed in order to trigger
a give bug.
a given bug.
The tool can also be used just to easily create a number of instances in a
Redis Cluster in order to experiment a bit with the system.
@@ -10,7 +10,7 @@ Redis Cluster in order to experiment a bit with the system.
USAGE
---
To create a cluster, follow this steps:
To create a cluster, follow these steps:
1. Edit create-cluster and change the start / end port, depending on the
number of instances you want to create.
@@ -21,7 +21,7 @@ an actual Redis cluster will be created.
In order to stop a cluster:
1. Use "./craete-cluster stop" to stop all the instances. After you stopped the instances you can use "./create-cluster start" to restart them if you change ideas.
2. Use "./create-cluster clean" to remove all the AOF / log files to restat with a clean environment.
1. Use "./create-cluster stop" to stop all the instances. After you stopped the instances you can use "./create-cluster start" to restart them if you change your mind.
2. Use "./create-cluster clean" to remove all the AOF / log files to restart with a clean environment.
Use the command "./create-cluster help" to get the full list of features.
+6 -6
View File
@@ -135,13 +135,13 @@ fi
echo "## Generated by install_server.sh ##" > $TMP_FILE
read -r SED_EXPR <<-EOF
s#^port [0-9]{4}\$#port ${REDIS_PORT}#; \
s#^logfile .+\$#logfile ${REDIS_LOG_FILE}#; \
s#^dir .+\$#dir ${REDIS_DATA_DIR}#; \
s#^pidfile .+\$#pidfile ${PIDFILE}#; \
s#^daemonize no\$#daemonize yes#;
s#^port .\+#port ${REDIS_PORT}#; \
s#^logfile .\+#logfile ${REDIS_LOG_FILE}#; \
s#^dir .\+#dir ${REDIS_DATA_DIR}#; \
s#^pidfile .\+#pidfile ${PIDFILE}#; \
s#^daemonize no#daemonize yes#;
EOF
sed -r "$SED_EXPR" $DEFAULT_CONFIG >> $TMP_FILE
sed "$SED_EXPR" $DEFAULT_CONFIG >> $TMP_FILE
#cat $TPL_FILE | while read line; do eval "echo \"$line\"" >> $TMP_FILE; done
cp $TMP_FILE $REDIS_CONFIG_FILE || die "Could not write redis config file $REDIS_CONFIG_FILE"