Compare commits

..
162 Commits
Author SHA1 Message Date
antirez a69534967b Redis 2.8.16. 2014-09-16 15:15:56 +02:00
antirez 5e38bc389c Better truncated AOF loading tests.
Now there are tests to write more data after loading a truncated AOF,
testing that the loaded data is correct, appending more, and testing
again.
2014-09-16 11:05:50 +02:00
antirez 01f7db608d Seek at the end of AOF after truncate call.
It is not clear if files open in append only mode will automatically fix
their offset after a truncate(2) operation. This commit makes sure that
we reposition the AOF file descriptor offset at the end of the file
after a truncated AOF is loaded and trimmed to the last valid command.
2014-09-16 11:05:50 +02:00
antirez 9faeff0215 On AOF end of file, truncate the AOF to last valid command.
Recently we introduced the ability to load truncated AOFs, but
unfortuantely the support was broken since the server, after loading the
truncated AOF, continues appending to the file that is corrupted at the
end. The problem is fixed only in the next AOF rewrite.

This commit fixes the issue by truncating the AOF to the last valid
opcode, and aborting if it is not possible to truncate the file
correctly.
2014-09-16 10:41:15 +02:00
antirez fbd9dc6039 Don't propagate SAVE.
This is a general fix (check that dirty delta is positive) but actually
should have as the only effect fixing the SAVE propagation to
AOF and slaves.
2014-09-16 10:41:15 +02:00
antirez 551ffc486c version.h updated to 2.8.15. 2014-09-12 16:16:58 +02:00
antirez 78dca8977f Redis 2.8.15. 2014-09-12 16:15:48 +02:00
antirez a6695e341b Sentinel sentinelGetLeader() top comment improved. 2014-09-11 19:28:10 +02:00
antirez 9fe4429a09 Sentinel: fix computation of total number of votes.
The code to check the number of voters was never updated to follow the new
Sentinel specification, so the number of voters was computed using only
the set of Sentinels that provided a vote.

This means that there is a changing majority on partitions, even if
usually the issue is not triggered because of the configured quorum
check (what was broken was the other implicit check that requires anyway
half of the known sentinels to agree in order to start a failover).
2014-09-11 19:28:09 +02:00
antirez 6fbcb19275 luaRedisGenericCommand() cached argv handling simplified.
As discussed in issue #1945.
2014-09-10 15:47:02 +02:00
xiaost 8375a6d0aa Limit the *SCAN command dictScan iterations
*SCAN will cause redis server to hang for seconds
after millions of keys was deleted by SCAN/DEL pairs
2014-09-10 09:58:20 +02:00
antirez 55024e9a9a Sentinel: don't set announce-ip if is empty. 2014-09-10 09:26:29 +02:00
antirez 3cc31da349 Sentinel: clarify announce-ip/port options in sentinel.conf. 2014-09-10 09:26:29 +02:00
antirez 8c3507aa8f Sentinel: announce ip/port changes + rewrite.
The original implementation was modified in order to allow to
selectively announce a different IP or port, and to rewrite the two
options in the config file after a rewrite.
2014-09-10 09:26:29 +02:00
Dara Kong 9f3abf436b sentinel: Decouple bind address from address sent to other sentinels
There are instances such as EC2 where the bind address is private
(behind a NAT) and cannot be accessible from WAN.

https://groups.google.com/d/msg/redis-db/PVVvjO4nMd0/P3oWC036v3cJ
2014-09-10 09:26:29 +02:00
antirez ff47d5ebf6 Tests for aof-load-truncated = yes. 2014-09-08 10:58:00 +02:00
antirez b3fdddfd1f AOF tests fixed turning aof-load-truncated to no.
When aof-load-truncated option was introduced, with a default of "yes",
the past behavior of the server to abort with trunncated AOF changed, so
we need to explicitly configure the tests to abort with truncated AOF
by setting the option to no.
2014-09-08 10:58:00 +02:00
antirez 670084b724 Make aof-load-truncated option actually configurable. 2014-09-08 10:58:00 +02:00
antirez 03a5c0ecb5 Correct cleanup when aborting AOF loading.
Because of the new ability to start with a truncated AOF, we need
to correctly release all the memory on EOF error. Otherwise there is a
small leak, that is not really a problem, but causes a false positive in
the tests that detect memory leaks.
2014-09-08 10:58:00 +02:00
antirez 683cf5b872 AOF ability to load truncated files. 2014-09-08 10:57:57 +02:00
antirez 94f7979414 Test AOF format error detection. 2014-09-08 10:57:11 +02:00
antirez 537f3053fa AOF loading: split handling of format errors from unexpected EOF. 2014-09-08 10:57:10 +02:00
Matt Stancliff 667e2991dc Return empty string if GETRANGE of empty string
Previously, GETRANGE of a key containing nothing ("")
would allocate a large (size_t)-1 return value causing
crashes on 32bit builds when it tried to allocate the
4 GB return string.
2014-09-04 11:47:21 +02:00
Matt Stancliff 4284b07220 Increase size of range request in getrange
32 bit builds don't have a big enough long to capture
the same range as a 64 bit build.  If we use "long long"
we get proper size limits everywhere.

Also updates size of unsigned comparison to fit new size of `end`.

Fixes #1981
2014-09-04 11:46:57 +02:00
antirez db6e874a25 Redis 2.8.14. 2014-09-01 17:00:38 +02:00
Matt Stancliff dd36f18c76 Sentinel: Abort Hello quicker if not connected
We can save a little work by aborting when we enter the function
if we're disconnected.
2014-09-01 16:35:04 +02:00
Paddy Byers d1f89bdc2b Add regression test for issue #1939 2014-09-01 10:41:15 +02:00
Paddy Byers 59ad68ca4f Store the length of the static argv when first allocated. 2014-09-01 10:40:04 +02:00
antirez 8fb8a474fd Fix dictRehash assert casting type.
Also related to #1929.
2014-08-27 10:31:52 +02:00
antirez 85b3db4065 Cast to right type in dictNext().
This closes issue #1929, the other part was fixed in the context of issue
2014-08-27 10:31:48 +02:00
antirez 3a706ba792 Use long for rehash and iterator index in dict.h.
This allows to support datasets with more than 2 billion of keys
(possible in very large memory instances, this bug was actually
reported).

Closes issue #1814.
2014-08-27 10:31:44 +02:00
antirez 97fdcd8746 Missing va_end also added in hiredis copy of sds.c. 2014-08-27 10:31:41 +02:00
Mariano Pérez Rodríguez 45fac9481a Missing assert removal from sdsIncrLen()
Companion for 8eeb1802ec, but dealing with hiredis.
2014-08-27 10:31:35 +02:00
antirez c3eac3ddff No longer useful assert removed from sdsIncrLen(). 2014-08-27 10:31:32 +02:00
Matt Stancliff fdcadd16c6 Fix redis-benchmark abort output 2014-08-27 10:31:29 +02:00
Matt Stancliff ac48aada30 Deny CLIENT command in scripts
We don't want scripts doing CLIENT SETNAME
or CLIENT KILL or CLIENT LIST or CLIENT PAUSE.

Originally reported by Chris Wj then proper
action inspired by Itamar Haber.

Reference: https://groups.google.com/forum/#!topic/redis-db/09B2EYwyVgk
2014-08-27 10:31:20 +02:00
Matt Stancliff abe29c83e6 redis-benchmark: Remove unused buffer
This just deletes old code that didn't get removed when
logic changed.  We were setting offsets that never
got read anywhere.

Since clients are now just cloned, we don't need to track
per-client buffer offsets anywhere because they are all
the same from the original client.
2014-08-27 10:30:49 +02:00
CharSyam 8fcce90b25 redis-benchmark: add auth option
Closes #1097
2014-08-27 10:30:49 +02:00
Matt Stancliff 4ac8472cac Rename two 'buf' vars to 'ip' for better clarity
Clearly ip[32] is wrong, but it's less clear that buf[32] was wrong
without further reading.
2014-08-27 10:30:42 +02:00
Eiichi Sato f7b5e2d11e Sentinel: fix bufsize to support IPv6 address
Closes #1914
2014-08-27 10:30:37 +02:00
Jan-Erik Rediger 32a94beca4 Use correct github url to find commands.json
Once this is merged:
  - merge the latest changes to commands.json in antirez/redis-doc
  - re-run: utils/generate-command-help.rb > src/help.h

Then we'll have nice and easy tab-completed help in redis-cli again.

Closes #1909
2014-08-27 10:30:34 +02:00
Mariano Pérez Rodríguez f862e5ddfa Stop tests from leaving a black background
Uses ANSI "default background" color code after closing tests
so any non-black terminals don't remain polluted.

Fixes #1649
Closes #1912
2014-08-27 10:30:29 +02:00
Matt Stancliff 32168a5e8a Sentinel: Reject config from STDIN
Sentinel needs to die with a more accurate error message
when attempted to open a config from STDIN.

See: https://groups.google.com/forum/#!topic/redis-db/sYx7VNMWaNM
2014-08-27 10:30:26 +02:00
Matt Stancliff 4bf7c5d0f8 Remove unused global variable
It has an important name, but nothing uses it...
2014-08-27 10:30:22 +02:00
antirez 33b37b1400 Fix invalid expire error for SET family commands. 2014-08-27 10:30:19 +02:00
Cong Ding ae2cd2e16f Remove unused function
Closes #878
2014-08-27 10:30:11 +02:00
David Palm 8c0743d80e Extend range of bytesToHuman to TB and PB
Also adds a fallthrough case for when given
large values (like overflow numbers of 2^64 by mistake).

Closes #858
2014-08-27 10:30:08 +02:00
yoav ef12b4c3eb Add error check for writing RDB checksum
Closes #857
2014-08-27 10:30:04 +02:00
Dov Murik 1580a378b8 redis-cli: fix prompt after shutdown command
Fix redis-cli prompt to state "not connected" after a SHUTDOWN command
is sent.
2014-08-27 10:30:01 +02:00
xiaoyu 467848460a Clarify argument to dict macro
d is more clear because the type of argument is dict not dictht

Closes #513
2014-08-27 10:29:57 +02:00
antirez 5e707c66dd Fix undefined behavior in ll2string().
The bug was found by @CAFxX, thanks!
See issue #1940.
2014-08-27 10:29:53 +02:00
antirez 65d47452f8 Remove warnings and improve integer sign correctness. 2014-08-27 10:29:26 +02:00
antirez 09757420a4 Add -W to compilation flags. 2014-08-27 10:25:57 +02:00
antirez 134345e332 Use unsigned integers in SDS header.
This raises the max string to 4GB without any downside.
2014-08-27 10:25:54 +02:00
Pieter Noordhuis f89a7bbe11 va_copy must be matched by va_end
Hat tip to @rfuchs. See: https://github.com/redis/hiredis/pull/178.

Fixes #1187
2014-08-27 10:25:44 +02:00
charsyam dbcf381099 Remove duplicate prototypes in redis.h
Also moves acceptHandler() to be near the other accept...() functions.

Closes #1105
2014-08-27 10:25:40 +02:00
NanXiao 6bc74b4393 Use resolv library in Solaris
For some Solaris flavours, the inet_aton in in resolv library.
Not linking this library will introduce link error.

Improves compatability with older Solaris and still
works on new Solaris.

Closes #1092
2014-08-27 10:25:37 +02:00
Daniel Price 1aff711d90 Define AR to help with lua cross-compilation
Closes #997
2014-08-27 10:25:34 +02:00
Matt Stancliff 866f6bcc80 Cleanup double semicolons
Closes #1161
2014-08-27 10:25:31 +02:00
Chris Lamb 29ed617ac4 redis-check-dump: Prevent segfault if can't malloc
Found by The Mayhem Team (Alexandre Rebert, Thanassis Avgerinos,
Sang Kil Cha, David Brumley, Manuel Egele) Cylab, Carnegie Mellon
University. See http://bugs.debian.org/716259 for more.

Signed-off-by: Chris Lamb <lamby@debian.org>

Fixes #1191
2014-08-27 10:25:28 +02:00
charsyam a43e45e391 Remove unused LINE_BUFLEN definition
Closes #1129
2014-08-27 10:25:25 +02:00
Matt Stancliff 25a4699a9e Reject MOVE to non-integer DBs
Previously, "MOVE key somestring" would move the key to
DB 0 which is just unexpected and wrong.
String as DB == error.

Test added too.

Modified by @antirez in order to use the getLongLongFromObject() API
instead of strtol().

Fixes #1428
2014-08-27 10:25:22 +02:00
Matt Stancliff 09a3976478 pubsub: Return integers for NUMSUB, not strings
Also adds test for numsub — due to tcl being tcl,
it doesn't capture the "numberness" of the fix,
but now we at least have one test case for numsub.

Closes #1561
2014-08-27 10:25:15 +02:00
Xiaojie Zhang 60c748628b Avoid unnecessary decoding in ziplist.c
Closes #1519
2014-08-27 10:25:12 +02:00
Samuel Chou 96614f5866 Improve accuracy of HAVE_ATOMIC dependency check
[I had to split out the clang check due to
 clang *really* not liking the __GLIBC_PREREQ macro; -matt]

Closes #1456
2014-08-27 10:25:07 +02:00
Jim Howard d1df50ad59 Remove redundant event loop fd processing
Reason:
https://github.com/antirez/redis/pull/1450#issuecomment-30432925

Closes #1450
2014-08-27 10:25:04 +02:00
Xian Li 282c6106ce memtest: Add missing free()
Closes #1614
2014-08-27 10:25:00 +02:00
Joshua Anderson 25b5abe8cf src/Makefile: Reword "to run make test" message
Closes #1610
2014-08-27 10:24:56 +02:00
cubicdaiya a7c46df612 Use 'void' for zero-argument functions
According to the C standard,
it is desirable to give the type 'void'
to functions have no argument.

Closes #1631
2014-08-27 10:24:52 +02:00
Matt Stancliff 8dbd0134e5 Fix assert technical correctness
dictAdd returns DICT_OK, not REDIS_OK. They both
have the same underlying values, so it works even though
the code is technically wrong.

Fixes #1512
2014-08-27 10:24:46 +02:00
Jan-Erik Rediger 0c8999dfeb install_server.sh: add missing bang
This was discovered by _bodya and reported in the IRC channel.
Everything worked fine as these scripts are always executed as shell
scripts.

Closes #1728
2014-08-27 10:24:42 +02:00
Edgars Irmejs 0f9ab49d58 Change unixsocketperm comment to 700 from 755
According to unix manuals, "Connecting to the socket object requires
read/write permission." -- mode 755 is useless for anybody
other than the owner.

Fixes #1696
2014-08-27 10:24:39 +02:00
ripcurld00d 4be50f7fbb redis-check-dump: use names instead of magic nums
Use constants to avoid magic numbers in `types`, which is an array
that stores the names of `REDIS` types.

Closes #1681
2014-08-27 10:24:36 +02:00
Kashif Rasul 8fc2247c9b Fix issues raised by clang analyzer
Modified by @antirez since the original fix to genInfoString() looked
weak. Probably the clang analyzer complained about `section` being
possibly NULL, and strcasecmp() called with a NULL pointer. In the
practice this can never happen, still for the sake of correctness
the right fix is not to modify only the first call, but to set `section`
to the value of "default" if it happens to be NULL.

Closes #1660
2014-08-27 10:24:33 +02:00
antirez ebd25710dd Force quit when receiving a second SIGINT.
Also quit ASAP when we are still loading a DB, since care is not needed
in this special condition, especially for a SIGINT.
2014-08-27 10:24:29 +02:00
Matt Stancliff 524b2626f7 Add graceful exit when Ctrl-C is received 2014-08-27 10:24:26 +02:00
Matt Stancliff 78bde472c0 Fix intset midpoint selection
The classic (min+max)/2 is provably unsafe.  Fixed
as recommended in research:
http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html

Fix inspired by @wjin, but I used a different approach.
(later, I found @kuebler fixed the same issue too).

Fixes #1741, #1602
2014-08-27 10:24:22 +02:00
Jan-Erik Rediger 07b2779bff Handle large getrange requests
Previously the end was casted to a smaller type
which resulted in a wrong check and failed
with values larger than handled by unsigned.

Closes #1847, #1844
2014-08-27 10:24:19 +02:00
Matt Stancliff 4024220fe6 scripting: no eval with negative key count
Negative key count causes segfault in Lua functions.

Fixes #1842
Closes #1843
2014-08-27 10:24:15 +02:00
Jan-Erik Rediger 50669f7b66 redis-cli: fix latency result output
(Cleaned up a little by @mattsta)

Closes #1774
2014-08-27 10:24:11 +02:00
Wei Jin 9b53f79e00 Remove redundant else/return block
Fixes #1741
2014-08-27 10:24:08 +02:00
antirez 9840df7bad Clarify AIX "#undef hz". 2014-08-27 10:22:59 +02:00
siahl b5dc2c5b91 Add support for compiling on AIX
Closes #1900
2014-08-27 10:22:28 +02:00
Matt Stancliff cbd1607c6a redis-cli: Add --no-raw option
Some people need formatted output even when they have no
interactive tty.

Fixes #760
2014-08-27 10:21:19 +02:00
Matt Stancliff 9a039f76c2 redis-cli: stop showing incorrectly selected DB
Previously redis-cli would happily show "-1" or "99999"
as valid DB choices.

Now, if the SELECT call returned an error, we don't update
the DB number in the CLI.

Inspired by @anupshendkar in #1313

Fixes #566, #1313
2014-08-27 10:21:19 +02:00
Matt Stancliff 28b54b5d94 redis-cli: Re-attach selected DB after auth
Previously, if you did SELECT then AUTH, redis-cli
would show your SELECT'd db even though it didn't
happen.

Note: running into this situation is a (hopefully) very limited
used case of people using multiple DBs and AUTH all at the same
time.

Fixes antirez#1639
2014-08-27 10:21:19 +02:00
Yossi Gottlieb ff58d5a380 Fail SYNC if background save child aborted due to a signal. 2014-07-28 14:46:03 +02:00
Salvatore Sanfilippo eb7b3427e7 Merge pull request #1894 from veselov/2.8
* fixed doc URL for keyspace events
2014-07-28 09:38:33 +02:00
vps 4d7b3815d5 * fixed doc URL for keyspace events 2014-07-27 20:14:39 -07:00
antirez 9fa51dd4d1 Example redis.conf: improve slaveof description. 2014-07-25 14:20:31 +02:00
antirez d558af8e6f Fix util.c compilation by including stdint.h. 2014-07-23 18:01:51 +02:00
antirez e426e77896 Faster ll2string() implementation.
Based on ideas documented in this blog post:

https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920

The original code was modified to handle signed integers, reformetted to
fit inside the Redis code base, and was stress-tested with a program
in order to validate the implementation against snprintf().

Redis was measured to be measurably faster from the point of view of
clients in real-world operations because of this change, since sometimes
number to string conversion is used extensively (for example every time
a GET results into an integer encoded object to be returned to the
user).
2014-07-23 14:58:26 +02:00
antirez 8a44e10848 PFSELFTEST: less false positives.
This is just a quickfix, for the nature of the test the right way to fix
it is to average the error of N runs, since otherwise it is always
possible to get a false positive with a bad run, or to minimize too much
this possibility we may end testing with too much "large" error ranges.
2014-07-23 11:45:03 +02:00
antirez 0bfdd4bb49 Test: check sorted set elements order after union.
This is not a regression but issue #1786 showed the need for this test.
2014-07-22 17:58:41 +02:00
antirez 098bb97703 Remove useless var and check in zunionInterGenericCommand(). 2014-07-22 17:58:26 +02:00
antirez 316e1db970 ZUNIONSTORE reimplemented for speed.
The user @kjmph provided excellent ideas to improve speed of ZUNIONSTORE
(in certain cases by many order of magnitude), together with an
implementation of the ideas.

While the ideas were sounding, the implementation could be improved both
in terms of speed and clearness, so that's my attempt at reimplementing
the speedup proposed, trying to improve by directly using just a
dictionary with an embedded score inside, and reusing the single-pass
aggregate + order-later approach.

Note that you can't apply this commit without applying the previous
commit in this branch that adds a double in the dictEntry value union.

Issue #1786.
2014-07-22 17:58:05 +02:00
antirez c8a6cd94b3 Add double field in dict.c entry value union. 2014-07-22 17:53:06 +02:00
antirez 7b11dfe05e Test: Pub/Sub PING. 2014-07-18 12:03:38 +02:00
antirez 156029b336 Test: small integer sharing depends on maxmemory policy. 2014-07-18 10:55:19 +02:00
antirez 06e9b3cad8 PING: backward compatible error for wrong number of args. 2014-07-18 10:46:21 +02:00
antirez ab5f20d19c tryObjectEncoding(): use shared objects with maxmemory and non-LRU policy.
In order to make sure every object has its own private LRU counter, when
maxmemory is enabled tryObjectEncoding() does not use the pool of shared
integers. However when the policy is not LRU-based, it does not make
sense to do so, and it is much better to save memory using shared
integers.
2014-07-18 10:46:05 +02:00
antirez 70e39481ab Variadic PING with support for Pub/Sub.
PING can now be called with an additional arugment, behaving exactly
like the ECHO command. PING can now also be called in Pub/Sub mode (with
one more more subscriptions to channels / patterns) in order to trigger
the delivery of an asynchronous pong message with the optional payload.

This fixes issue #420.
2014-07-18 10:41:47 +02:00
antirez 294bcfc4d2 PubSub clients refactoring and new PUBSUB flag.
The code tested many times if a client had active Pub/Sub subscriptions
by checking the length of a list and dictionary where the patterns and
channels are stored. This was substituted with a client flag called
REDIS_PUBSUB that is simpler to test for. Moreover in order to manage
this flag some code was refactored.

This commit is believed to have no effects in the behavior of the
server.
2014-07-18 10:41:22 +02:00
antirez 0c29cadd46 Redis 2.8.13. 2014-07-14 17:48:14 +02:00
michael-grunder 3df2ab673c Fix OBJECT arity
Previously, the command definition for the OBJECT command specified
a minimum of two args (and that it was variadic), which meant that
if you sent this:

OBJECT foo

When cluster was enabled, it would result in an assertion/SEGFAULT
when Redis was attempting to extract keys.

It appears that OBJECT is not variadic, and only ever takes 3 args.

https://gist.github.com/michael-grunder/25960ce1508396d0d36a
2014-07-14 15:38:32 +02:00
antirez c970507478 Fix typo in LATENCY DOCTOR output. 2014-07-11 10:57:34 +02:00
antirez bb10ad9c13 Test: more reliable AOF rewrite test under write load. 2014-07-10 16:42:52 +02:00
antirez c785d4cccc LATENCY command / monitor basic tests. 2014-07-10 16:24:24 +02:00
antirez e1fd73f634 LATENCY HISTORY returns empty array if no data is available.
Previously we returned an error.
2014-07-10 16:24:24 +02:00
antirez 315536d012 Test: enable latency monitor in the default config.
This way as a side effect of running the test we also stress the latency
monitor data collection.
2014-07-10 15:04:59 +02:00
antirez f0e67afd94 Test: AOF rewrite during write load. 2014-07-10 11:24:59 +02:00
antirez 1f3c097b1c Fixed a few missing newline in createLatencyReport(). 2014-07-09 20:02:01 +02:00
antirez e4db34e97c createLatencyReport(): compile before commit avoids commits. 2014-07-09 20:01:57 +02:00
antirez f9765990b2 createLatencyReport(), fix mount option name. 2014-07-09 20:01:53 +02:00
antirez 54ca682887 createLatencyReport(): initialize all advices to zero. 2014-07-09 20:01:48 +02:00
antirez 95d880a7bf LATENCY DOCTOR first implementation complete. 2014-07-09 20:01:44 +02:00
antirez 2e581217d9 Fix analyzeLatencyForEvent() MAD computation. 2014-07-09 20:01:41 +02:00
antirez 121a9a3579 LATENCY DOCTOR: initial draft and events summary output. 2014-07-09 20:01:37 +02:00
antirez 3dd113bf33 Latency: low level time series analysis implemented. 2014-07-09 20:01:33 +02:00
antirez 3e8f5113fa LATENCY SAMPLES renamed LATENCY HISTORY. 2014-07-09 20:01:30 +02:00
antirez c145854bac LATENCY RESET implemented. 2014-07-09 20:01:27 +02:00
antirez 65dbb3546a redis-benchmark: abort when all clients are disconnected. 2014-07-09 20:01:18 +02:00
antirez 4c68319370 Use fsync instead of aof_fsync in final AOF sync.
This happens in the child process so we don't care about latency:
better to sync metadata as well.
2014-07-09 20:01:01 +02:00
antirez f3eab7a9b4 Fixed conditional for aof-write-pending-fsync latency event selection. 2014-07-09 20:00:57 +02:00
antirez f3c4111edd Dependencies updated. 2014-07-09 20:00:50 +02:00
antirez 07b4d33734 Cast void* to char* to avoid waring in latencyCommand(). 2014-07-09 19:58:06 +02:00
antirez 58fb90fdac Properly initialize min/max in latency.c. 2014-07-09 19:57:59 +02:00
antirez c005315a64 latencyStartMonitor() modified to avoid warnings. 2014-07-09 19:57:53 +02:00
antirez e81b51ad8c Latency monitor: specialize delayed aof writes events. 2014-07-09 19:57:48 +02:00
antirez 8cc912db43 LATENCY GRAPH: filling under the curve is more readable. 2014-07-09 19:57:42 +02:00
antirez e905588062 LATENCY GRAPH implemented. 2014-07-09 19:57:37 +02:00
antirez b38996aab1 latencyTimeSeries structure max field type fixed. 2014-07-09 19:57:34 +02:00
antirez 062564c261 Free labels in freeSparklineSequence(). 2014-07-09 19:57:28 +02:00
antirez d20d89e630 LATENCY LATEST: add the max field. 2014-07-09 19:57:22 +02:00
antirez dc1c91a8bf Latency monitor trheshold value is now configurable.
This commit adds both support for redis.conf and CONFIG SET/GET.
2014-07-09 19:57:17 +02:00
antirez 350792bd72 ASCII sparklines generation API. 2014-07-09 19:57:08 +02:00
antirez ed95915d33 License added to latency.h. 2014-07-09 19:56:47 +02:00
antirez 34afc91030 Latency monitor turned off by default.
It is not a good idea to bloat the code with gettimeofday() calls if the
instance is working well, and turning monitoring on at runtime is a
joke.
2014-07-09 19:56:43 +02:00
antirez 35a62f7408 Latency monitor: more hooks around the code. 2014-07-09 19:56:43 +02:00
antirez 855c97ce1b Latency monitor: don't add new samples in the same second.
Instead we update the old sample with the new latency if it is greater.
2014-07-09 19:56:35 +02:00
antirez 3cb03b68a4 LATENCY LATEST implemented. 2014-07-09 19:56:30 +02:00
antirez e400ae60f5 Latency monitor: command duration is in useconds. Convert. 2014-07-09 19:04:02 +02:00
antirez a57eb3272d LATENCY SAMPLES implemented. 2014-07-09 19:03:59 +02:00
antirez 72c84acc99 Latency monitor: collect slow commands.
We introduce the distinction between slow and fast commands since those
are two different sources of latency. An O(1) or O(log N) command without
side effects (can't trigger deletion of large objects as a side effect of
its execution) if delayed is a symptom of inherent latency of the system.

A non-fast command (commands that may run large O(N) computations) if
delayed may just mean that the user is executing slow operations.

The advices LATENCY should provide in this two different cases are
different, so we log the two classes of commands in a separated way.
2014-07-09 19:03:26 +02:00
antirez 58ecc0bd68 Latency monitor: basic samples collection. 2014-07-09 18:57:57 +02:00
antirez 0baab66d16 Fixed 2.8 build breakage introduced back porting COMMAND. 2014-07-02 22:42:00 +02:00
antirez 670cf9598e Fix Solaris compilation due to ctime_r() call.
Introduced in Redis 2.8.10 because of a change in Sentinel.
This closes issue #1837.
2014-06-30 16:29:14 +02:00
antirez 41d7c90417 Test: fix instances.tcl restart_instance abort error. 2014-06-30 12:06:24 +02:00
antirez 63feb93a9f COMMAND COUNT subcommand added. 2014-06-27 18:38:08 +02:00
antirez 0a6649a7c0 COMMAND: fix argument parsing.
This fixes detection of wrong subcommand (that resulted in the default
all-commands output instead) and allows COMMAND INFO to be called
without arguments (resulting into an empty array) which is useful in
programmtically generated calls like the following (in Ruby):

    redis.commands("command","info",*mycommands)

Note: mycommands may be empty.
2014-06-27 18:38:08 +02:00
antirez 53377f8c6d COMMANDS command renamed COMMAND. 2014-06-27 18:38:03 +02:00
antirez f3efd529df COMMANDS command: remove static + aesthetic changes.
Static was removed since it is needed in order to get symbols in stack
traces. Minor changes in the source code were operated to make it more
similar to the existing Redis code base.
2014-06-27 18:37:34 +02:00
Matt Stancliff fdc5dbd5b1 Cluster: Add COMMANDS command
COMMANDS returns a nested multibulk reply for each
command in the command table.  The reply for each
command contains:
  - command name
  - arity
  - array of command flags
  - start key position
  - end key position
  - key offset step
  - optional: if the keys are not deterministic and
    Redis uses an internal key evaluation function,
    the 6th field appears and is defined as a status
    reply of: REQUIRES ARGUMENT PARSING

Cluster clients need to know where the keys are in each
command to implement proper routing to cluster nodes.

Redis commands can have multiple keys, keys at offset steps, or other
issues where you can't always assume the first element after
the command name is the cluster routing key.

Using the information exposed by COMMANDS, client implementations
can have live, accurate key extraction details for all commands.

Also implements COMMANDS INFO [commands...] to return only a
specific set of commands instead of all 160+ commands live in Redis.
2014-06-27 18:37:29 +02:00
antirez afe949efcb Remove infinite loop from PSYNC test.
Added for debugging and forgot there.
2014-06-27 18:36:59 +02:00
antirez 1066fed769 Test: hopefully more robust PSYNC test.
This is supposed to fix issue #1417, but we'll know if this is enough
only after a couple of runs of the CI test without false positives.
2014-06-27 18:36:59 +02:00
Matt Stancliff cd4f0dc775 Allow __powerpc__ to define HAVE_ATOMIC too
From mailing list post https://groups.google.com/forum/#!topic/redis-db/D3k7KmJmYgM

In the file “config.h”, the definition HAVE_ATOMIC is used to indicate
if an architecture on which redis is implemented supports atomic
synchronization primitives.  Powerpc  supports atomic synchronization
primitives, however, it is not listed as one of the architectures
supported in config.h. This patch  adds the __powerpc__ to the list of
architectures supporting these primitives. The improvement of redis
due to the atomic synchronization on powerpc is significant,
around 30% to 40%, over the default implementation using pthreads.

This proposal adds __powerpc__ to the list of architectures designated
to support atomic builtins.
2014-06-27 18:36:59 +02:00
Matt Stancliff 58fea46976 Allow atomic memory count update with C11 builtins
From mailing list post https://groups.google.com/forum/#!topic/redis-db/QLjiQe4D7LA

In zmalloc.c the following primitives are currently used
to synchronize access to single global variable:
__sync_add_and_fetch
__sync_sub_and_fetch

In some architectures such as powerpc these primitives are overhead
intensive. More efficient C11 __atomic builtins are available with
newer GCC versions, see
http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html#_005f_005fatomic-Builtins

By substituting the following  __atomic… builtins:
__atomic_add_fetch
__atomic_sub_fetch

the performance improvement on certain architectures such as powerpc can be significant,
around 10% to 15%, over the implementation using __sync builtins while there is only slight uptick on
Intel architectures because it was already enforcing Intel Strongly ordered memory semantics.

The selection of __atomic built-ins can be predicated on the definition of ATOMIC_RELAXED
which Is available on in gcc 4.8.2 and later versions.
2014-06-27 18:36:59 +02:00
Matt Stancliff ff3ca17bac Use predefined macro for used_memory() update 2014-06-27 18:36:59 +02:00
antirez 04749ee015 Fixed assert conditional in ROLE command test. 2014-06-26 22:13:57 +02:00
antirez 60ff8095d6 No more trailing spaces in Redis source code. 2014-06-26 18:52:16 +02:00
antirez e9cd75dcd1 CLIENT KILL: don't kill the master as a normal client.
Technically the problem is due to the client type API that does not
return a special value for the master, however fixing it locally in the
CLIENT KILL command is better currently because otherwise we would
introduce a new output buffer limit class as a side effect.
2014-06-26 18:43:42 +02:00
antirez ac6b656fd6 Old form of CLIENT KILL should still allow suicide. 2014-06-24 12:49:18 +02:00
79 changed files with 2425 additions and 482 deletions
+109
View File
@@ -14,6 +14,115 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
--------------------------------------------------------------------------------
--[ Redis 2.8.16 ] Release date: 16 Sep 2014
# UPGRADE URGENCY: HIGH for Redis if you are using 2.8.15 + AOF.
LOW for Sentinel.
* [FIX] The ability to load truncated AOF files introduced with Redis 2.8.15
contains a bug fixed in this release: after loading the file was not
truncated to the last valid command, so the new commands are appended
after a non well formed command. This means that:
1) The first AOF rewrite triggered by the server will automatically
fix the problem.
2) However, if the server is restarted before the rewrite, Redis may
not be able to load the file and you need to manually fix it.
In order to fix a corrupted file you should start the redis-check-aof
utility WITHOUT the --fix option, just to check the offset where the
corruption is found. Around the offset reported by the check utility
you'll find, inside your AOF file, a command which is not complete
according to the Redis protocol. Just remove this incomplete command
leafing the file unaltered before and after the offending command,
and restart the server.
IMPORTANT #1: Redis 2.8.15 is the only stable version of Redis with
this bug so probably no actual real-world problem happened since the
problem is automatically fixed at the first automatic AOF rewrite.
IMPORTANT #2: Before upgrading to Redis 2.8.16, if you are using Redis
2.8.15 with AOF enabled, make sure to trigger a manual AOF rewrite
using the BGREWRITEAOF command.
* [FIX] SAVE is no longer propagated to AOF / slaves.
--[ Redis 2.8.15 ] Release date: 12 Sep 2014
# UPGRADE URGENCY: LOW for Redis, HIGH for Sentinel.
* [FIX] Sentinel critical bug fixed: the absolute majority was computed in a
wrong way because of a programming error. Now the implementation does
what the specification says and the majority to authorize a failover
(that should not be confused with the ODOWN quorum) is the majority of
*all* the Sentinels ever seen for a given master, regardless of their
current state.
* [FIX] GETRANGE test no longer fails for 32 bit builds (Matt Stancliff).
* [FIX] Limit SCAN latency when the hash table is in an odd state (very few
populted buckets because rehashing is in progress). (Xiaost and
Salvatore Sanfilippo)
* [NEW] Redis is now able to load truncated AOF files without requiring a
redis-check-aof utility run. The default now is to load truncated
(but apparently not corrupted) AOFs, you can change this in redis.conf.
(Salvatore Sanfilippo).
* [NEW] Sentinel: ability to announce itself with an arbitrary IP/port to work
in the context of natted networks. However this is probably still
not enough since there is no equivalent mechanism for slaves listed
in the master INFO output. (Dara Kong and Salvatore Sanfilippo)
--[ Redis 2.8.14 ] Release date: 1 Sep 2014
# UPGRADE URGENCY: HIGH for Lua scripting users, the server could crash because
of a bug introduced in Redis 2.8.10, otherwise LOW.
LOW for Redis Sentinel.
* [FIX] Don't prevent use of shared integers if maxmemory policy is non-LRU.
(Salvatore Sanfilippo)
* [FIX] Fail SYNC if background save child aborted due to a signal.
(Yossi Gottlieb)
* [FIX] Different small redis-cli fixes. (Dov Murik, Charsyam, cubicdaiya,
Kashif Rasul, Jan-Erik Rediger, Matt Stancliff)
* [FIX] AIX compilation fixes. (Siah Lyimo)
* [FIX] A number of other smaller issues.
* [FIX] Improved SIGINT handling (Matt Stancliff, Salvatore Sanfilippo)
* [FIX] Use unsigned types in SDS header to raise limit to 4GB.
(Matt Stancliff, Salvatore Sanfilippo)
* [FIX] Handle signed/unsigned comparisons with more care around the code.
(Salvatore Sanfilippo)
* [FIX] Colorized test output fixed to don't change the background color.
(Mariano Pérez Rodríguez)
* [FIX] More Sentinel IPv6 fixes. (Eiichi Sato)
* [FIX] Deny CLIENT command in scripts. (Matt Stancliff)
* [FIX] Allow datasets with more than 2 billion of keys, initial work.
* [FIX] Fix a Lua scripting crash by storing the length of the static
argv when first allocated. (Paddy Byers)
* [NEW] Pub/Sub PING. (Salvatore Sanfilippo)
* [NEW] Much faster ZUNIONSTORE. (Kyle Hubert, Salvatore Sanfilippo)
* [NEW] Faster ll2string() implementation. (Salvatore Sanfilippo)
* [NEW] **WARNING, minor API change**: PUBSUB NUMSUB: return type modified
to integer. (Matt Stancliff)
* [NEW] redis-benchmark support for AUTH. (CharSyam)
--[ Redis 2.8.13 ] Release date: 14 Jul 2014
# UPGRADE URGENCY: LOW for Redis and Sentinel, this is a features enhancement
release mostly. Since this release introduces the latency
monitoring feature, Redis 2.8 users experiencing latency
issues are strongly encouraged to upgrade.
* [FIX] CLIENT KILL minor backward compatibility fixes. (Salvatore Sanfilippo)
* [FIX] Enable HAVE_ATOMIC for PowerPC. (Matt Stancliff)
* [FIX] More robust PSYNC and AOF rewrites tests. (Salvatore Sanfilippo)
* [FIX] Solaris build fixed. (Matt Stancliff, Salvatore Sanfilippo)
* [NEW] The new latency monitoring feature, as documented at
http://redis.io/topics/latency-monitor (Salvatore Sanfilippo)
* [NEW] The COMMAND command, exposing the Redis command table
as an API. (Matt Stancliff)
* [NEW] Update used memory with C11 __atomic. (Matt Stancliff)
--[ Redis 2.8.12 ] Release date: 23 Jun 2014
# UPGRADE URGENCY: HIGH for Redis, CRITICAL for Sentinel.
+6 -1
View File
@@ -60,10 +60,15 @@ endif
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)
LUA_LDFLAGS+= $(LDFLAGS)
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
# challenging to cross-compile lua (and redis). These defines make it easier
# to fit redis into cross-compilation environments, which typically set AR.
AR=ar
ARFLAGS=rcu
lua: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)"
cd lua/src && $(MAKE) all CFLAGS="$(LUA_CFLAGS)" MYLDFLAGS="$(LUA_LDFLAGS)" AR="$(AR) $(ARFLAGS)"
.PHONY: lua
+4
View File
@@ -5,6 +5,10 @@
#define _BSD_SOURCE
#endif
#if defined(_AIX)
#define _ALL_SOURCE
#endif
#if defined(__sun__)
#define _POSIX_C_SOURCE 200112L
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
+1 -1
View File
@@ -35,7 +35,7 @@
#include "hiredis.h"
#if defined(__sun)
#if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX
#endif
+7 -4
View File
@@ -123,7 +123,7 @@ void sdsclear(sds s) {
/* Enlarge the free space at the end of the sds string so that the caller
* is sure that after calling this function can overwrite up to addlen
* bytes after the end of the string, plus one more byte for nul term.
*
*
* Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */
sds sdsMakeRoomFor(sds s, size_t addlen) {
@@ -200,10 +200,12 @@ size_t sdsAllocSize(sds s) {
void sdsIncrLen(sds s, int incr) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
assert(sh->free >= incr);
if (incr >= 0)
assert(sh->free >= (unsigned int)incr);
else
assert(sh->len >= (unsigned int)(-incr));
sh->len += incr;
sh->free -= incr;
assert(sh->free >= 0);
s[sh->len] = '\0';
}
@@ -388,6 +390,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
va_end(ap);
if (buf[buflen-2] != '\0') {
if (buf != staticbuf) zfree(buf);
buflen *= 2;
@@ -457,7 +460,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
i = initlen; /* Position of the next byte to write to dest str. */
while(*f) {
char next, *str;
int l;
unsigned int l;
long long num;
unsigned long long unum;
+2 -2
View File
@@ -39,8 +39,8 @@
typedef char *sds;
struct sdshdr {
int len;
int free;
unsigned int len;
unsigned int free;
char buf[];
};
+59 -5
View File
@@ -68,7 +68,7 @@ tcp-backlog 511
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocketperm 755
# unixsocketperm 700
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0
@@ -189,9 +189,18 @@ dir ./
################################# REPLICATION #################################
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. Note that the configuration is local to the slave
# so for example it is possible to configure the slave to save the DB with a
# different interval, or to listen to another port, and so on.
# another Redis server. A few things to understand ASAP about Redis replication.
#
# 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least
# a given number of slaves.
# 2) Redis slaves are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
# network partition slaves automatically try to reconnect to masters
# and resynchronize with them.
#
# slaveof <masterip> <masterport>
@@ -520,6 +529,30 @@ no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# An AOF file may be found to be truncated at the end during the Redis
# startup process, when the AOF data gets loaded back into memory.
# This may happen when the system where Redis is running
# crashes, especially when an ext4 filesystem is mounted without the
# data=ordered option (however this can't happen when Redis itself
# crashes or aborts but the operating system still works correctly).
#
# Redis can either exit with an error when this happens, or load as much
# data as possible (the default now) and start if the AOF file is found
# to be truncated at the end. The following option controls this behavior.
#
# If aof-load-truncated is set to yes, a truncated AOF file is loaded and
# the Redis server starts emitting a log to inform the user of the event.
# Otherwise if the option is set to no, the server aborts with an error
# and refuses to start. When the option is set to no, the user requires
# to fix the AOF file using the "redis-check-aof" utility before to restart
# the server.
#
# Note that if the AOF file will be found to be corrupted in the middle
# the server will still exit with an error. This option only applies when
# Redis will try to read more data from the AOF file but not enough bytes
# will be found.
aof-load-truncated yes
################################ LUA SCRIPTING ###############################
# Max execution time of a Lua script in milliseconds.
@@ -562,10 +595,31 @@ slowlog-log-slower-than 10000
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128
################################ LATENCY MONITOR ##############################
# The Redis latency monitoring subsystem samples different operations
# at runtime in order to collect data related to possible sources of
# latency of a Redis instance.
#
# Via the LATENCY command this information is available to the user that can
# print graphs and obtain reports.
#
# The system only logs operations that were performed in a time equal or
# greater than the amount of milliseconds specified via the
# latency-monitor-threshold configuration directive. When its value is set
# to zero, the latency monitor is turned off.
#
# By default latency monitoring is disabled since it is mostly not needed
# if you don't have latency issues, and collecting data has a performance
# impact, that while very small, can be measured under big load. Latency
# monitoring can easily be enalbed at runtime using the command
# "CONFIG SET latency-monitor-threshold <milliseconds>" if needed.
latency-monitor-threshold 0
############################# Event notification ##############################
# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/keyspace-events
# This feature is documented at http://redis.io/topics/notifications
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
+22
View File
@@ -4,6 +4,28 @@
# The port that this sentinel instance will run on
port 26379
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4
# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
+11 -5
View File
@@ -19,7 +19,7 @@ DEPENDENCY_TARGETS=hiredis linenoise lua
# Default settings
STD=-std=c99 -pedantic
WARN=-Wall
WARN=-Wall -W
OPT=$(OPTIMIZATION)
PREFIX?=/usr/local
@@ -58,17 +58,23 @@ ifeq ($(uname_S),SunOS)
# SunOS
INSTALL=cp -pf
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
FINAL_LIBS+= -ldl -lnsl -lsocket -lpthread
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread
else
ifeq ($(uname_S),Darwin)
# Darwin (nothing to do)
else
ifeq ($(uname_S),AIX)
# AIX
FINAL_LDFLAGS+= -Wl,-bexpall
FINAL_LIBS+= -pthread -lcrypt -lbsd
else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+= -pthread
endif
endif
endif
# Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
@@ -107,7 +113,7 @@ endif
REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o hyperloglog.o
REDIS_SERVER_OBJ=adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o hyperloglog.o latency.o sparkline.o
REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
REDIS_BENCHMARK_NAME=redis-benchmark
@@ -119,7 +125,7 @@ REDIS_CHECK_AOF_OBJ=redis-check-aof.o
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
@echo ""
@echo "Hint: To run 'make test' is a good idea ;)"
@echo "Hint: It's a good idea to run 'make test' ;)"
@echo ""
.PHONY: all
+47 -34
View File
@@ -7,58 +7,65 @@ ae_select.o: ae_select.c
anet.o: anet.c fmacros.h anet.h
aof.o: aof.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
bio.h
bio.o: bio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
bio.h
bitops.o: bitops.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
config.o: config.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
crc64.o: crc64.c
db.o: db.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h crc64.h bio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
sha1.h crc64.h bio.h
dict.o: dict.c fmacros.h dict.h zmalloc.h redisassert.h
endianconv.o: endianconv.c
hyperloglog.o: hyperloglog.c redis.h fmacros.h config.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h \
latency.h sparkline.h rdb.h rio.h
intset.o: intset.c intset.h zmalloc.h endianconv.h config.h
latency.o: latency.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
lzf_c.o: lzf_c.c lzfP.h
lzf_d.o: lzf_d.c lzfP.h
memtest.o: memtest.c config.h
migrate.o: migrate.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h endianconv.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
endianconv.h
multi.o: multi.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
networking.o: networking.c redis.h fmacros.h config.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h \
latency.h sparkline.h rdb.h rio.h
notify.o: notify.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
object.o: object.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
pqsort.o: pqsort.c
pubsub.o: pubsub.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
rand.o: rand.c
rdb.o: rdb.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h lzf.h zipmap.h \
endianconv.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
lzf.h zipmap.h endianconv.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
@@ -67,51 +74,57 @@ redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h anet.h ae.h
redis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h bio.h \
asciilogo.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
slowlog.h bio.h asciilogo.h
release.o: release.c release.h version.h crc64.h
replication.o: replication.c redis.h fmacros.h config.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
rio.h
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h \
latency.h sparkline.h rdb.h rio.h
rio.o: rio.c fmacros.h rio.h sds.h util.h crc64.h config.h redis.h \
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h dict.h adlist.h \
zmalloc.h anet.h ziplist.h intset.h version.h rdb.h
zmalloc.h anet.h ziplist.h intset.h version.h latency.h sparkline.h \
rdb.h
scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h rand.h \
../deps/lua/src/lauxlib.h ../deps/lua/src/lualib.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
sha1.h rand.h ../deps/lua/src/lauxlib.h ../deps/lua/src/lualib.h
sds.o: sds.c sds.h zmalloc.h
sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h \
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
../deps/hiredis/hiredis.h ../deps/hiredis/async.h
setproctitle.o: setproctitle.c
sha1.o: sha1.c sha1.h config.h
slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
slowlog.h
sort.o: sort.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h pqsort.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h \
pqsort.h
sparkline.o: sparkline.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
syncio.o: syncio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
t_hash.o: t_hash.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
t_list.o: t_list.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
t_set.o: t_set.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
t_string.o: t_string.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
t_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
ziplist.h intset.h version.h util.h latency.h sparkline.h rdb.h rio.h
util.o: util.c fmacros.h util.h sds.h
ziplist.o: ziplist.c zmalloc.h util.h sds.h ziplist.h endianconv.h \
config.h redisassert.h
+1 -1
View File
@@ -178,7 +178,7 @@ void listDelNode(list *list, listNode *node)
listIter *listGetIterator(list *list, int direction)
{
listIter *iter;
if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
if (direction == AL_START_HEAD)
iter->next = list->head;
+2 -2
View File
@@ -156,8 +156,9 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{
if (fd >= eventLoop->setsize) return;
aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return;
aeApiDelEvent(eventLoop, fd, mask);
fe->mask = fe->mask & (~mask);
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
/* Update the max fd */
@@ -167,7 +168,6 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j;
}
aeApiDelEvent(eventLoop, fd, mask);
}
int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
+6 -6
View File
@@ -54,7 +54,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
return -1;
}
eventLoop->apidata = state;
return 0;
return 0;
}
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
@@ -75,7 +75,7 @@ static void aeApiFree(aeEventLoop *eventLoop) {
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeApiState *state = eventLoop->apidata;
struct kevent ke;
if (mask & AE_READABLE) {
EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;
@@ -118,16 +118,16 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
if (retval > 0) {
int j;
numevents = retval;
for(j = 0; j < numevents; j++) {
int mask = 0;
struct kevent *e = state->events+j;
if (e->filter == EVFILT_READ) mask |= AE_READABLE;
if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE;
eventLoop->fired[j].fd = e->ident;
eventLoop->fired[j].mask = mask;
eventLoop->fired[j].fd = e->ident;
eventLoop->fired[j].mask = mask;
}
}
return numevents;
+3 -1
View File
@@ -117,6 +117,8 @@ int anetKeepAlive(char *err, int fd, int interval)
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
return ANET_ERR;
}
#else
((void) interval); /* Avoid unused var warning for non Linux systems. */
#endif
return ANET_OK;
@@ -137,7 +139,7 @@ int anetEnableTcpNoDelay(char *err, int fd)
return anetSetTcpNoDelay(err, fd, 1);
}
int anetDisableTcpNoDelay(char *err, int fd)
int anetDisableTcpNoDelay(char *err, int fd)
{
return anetSetTcpNoDelay(err, fd, 0);
}
+5 -1
View File
@@ -39,10 +39,14 @@
#define ANET_NONE 0
#define ANET_IP_ONLY (1<<0)
#if defined(__sun)
#if defined(__sun) || defined(_AIX)
#define AF_LOCAL AF_UNIX
#endif
#ifdef _AIX
#undef ip_len
#endif
int anetTcpConnect(char *err, char *addr, int port);
int anetTcpNonBlockConnect(char *err, char *addr, int port);
int anetUnixConnect(char *err, char *path);
+106 -21
View File
@@ -140,7 +140,7 @@ ssize_t aofRewriteBufferWrite(int fd) {
if (block->used) {
nwritten = write(fd,block->buf,block->used);
if (nwritten != block->used) {
if (nwritten != (ssize_t)block->used) {
if (nwritten == 0) errno = EIO;
return -1;
}
@@ -230,6 +230,7 @@ int startAppendOnly(void) {
void flushAppendOnlyFile(int force) {
ssize_t nwritten;
int sync_in_progress = 0;
mstime_t latency;
if (sdslen(server.aof_buf) == 0) return;
@@ -257,16 +258,32 @@ void flushAppendOnlyFile(int force) {
redisLog(REDIS_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.");
}
}
/* If you are following this code path, then we are going to write so
* set reset the postponed flush sentinel to zero. */
server.aof_flush_postponed_start = 0;
/* We want to perform a single write. This should be guaranteed atomic
* at least if the filesystem we are writing is a real physical one.
* While this will save us against the server being killed I don't think
* there is much to do about the whole server stopping for power problems
* or alike */
latencyStartMonitor(latency);
nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));
latencyEndMonitor(latency);
/* We want to capture different events for delayed writes:
* when the delay happens with a pending fsync, or with a saving child
* active, and when the above two conditions are missing.
* We also use an additional event name to save all samples which is
* useful for graphing / monitoring purposes. */
if (sync_in_progress) {
latencyAddSampleIfNeeded("aof-write-pending-fsync",latency);
} else if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) {
latencyAddSampleIfNeeded("aof-write-active-child",latency);
} else {
latencyAddSampleIfNeeded("aof-write-alone",latency);
}
latencyAddSampleIfNeeded("aof-write",latency);
/* We performed the write so reset the postponed flush sentinel to zero. */
server.aof_flush_postponed_start = 0;
if (nwritten != (signed)sdslen(server.aof_buf)) {
static time_t last_write_error_log = 0;
int can_log = 0;
@@ -360,7 +377,10 @@ void flushAppendOnlyFile(int force) {
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
/* aof_fsync is defined as fdatasync() for Linux in order to avoid
* flushing metadata. */
latencyStartMonitor(latency);
aof_fsync(server.aof_fd); /* Let's try to get this data on the disk */
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fsync-always",latency);
server.aof_last_fsync = server.unixtime;
} else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&
server.unixtime > server.aof_last_fsync)) {
@@ -513,6 +533,14 @@ struct redisClient *createFakeClient(void) {
return c;
}
void freeFakeClientArgv(struct redisClient *c) {
int j;
for (j = 0; j < c->argc; j++)
decrRefCount(c->argv[j]);
zfree(c->argv);
}
void freeFakeClient(struct redisClient *c) {
sdsfree(c->querybuf);
listRelease(c->reply);
@@ -530,6 +558,7 @@ int loadAppendOnlyFile(char *filename) {
struct redis_stat sb;
int old_aof_state = server.aof_state;
long loops = 0;
off_t valid_up_to = 0; /* Offset of the latest well-formed command loaded. */
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.aof_current_size = 0;
@@ -570,18 +599,35 @@ int loadAppendOnlyFile(char *filename) {
goto readerr;
}
if (buf[0] != '*') goto fmterr;
if (buf[1] == '\0') goto readerr;
argc = atoi(buf+1);
if (argc < 1) goto fmterr;
argv = zmalloc(sizeof(robj*)*argc);
fakeClient->argc = argc;
fakeClient->argv = argv;
for (j = 0; j < argc; j++) {
if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr;
if (fgets(buf,sizeof(buf),fp) == NULL) {
fakeClient->argc = j; /* Free up to j-1. */
freeFakeClientArgv(fakeClient);
goto readerr;
}
if (buf[0] != '$') goto fmterr;
len = strtol(buf+1,NULL,10);
argsds = sdsnewlen(NULL,len);
if (len && fread(argsds,len,1,fp) == 0) goto fmterr;
if (len && fread(argsds,len,1,fp) == 0) {
sdsfree(argsds);
fakeClient->argc = j; /* Free up to j-1. */
freeFakeClientArgv(fakeClient);
goto readerr;
}
argv[j] = createObject(REDIS_STRING,argsds);
if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */
if (fread(buf,2,1,fp) == 0) {
fakeClient->argc = j+1; /* Free up to j. */
freeFakeClientArgv(fakeClient);
goto readerr; /* discard CRLF */
}
}
/* Command lookup */
@@ -590,9 +636,8 @@ int loadAppendOnlyFile(char *filename) {
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr);
exit(1);
}
/* Run the command in the context of a fake client */
fakeClient->argc = argc;
fakeClient->argv = argv;
cmd->proc(fakeClient);
/* The fake client should not have a reply */
@@ -602,15 +647,15 @@ int loadAppendOnlyFile(char *filename) {
/* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */
for (j = 0; j < fakeClient->argc; j++)
decrRefCount(fakeClient->argv[j]);
zfree(fakeClient->argv);
freeFakeClientArgv(fakeClient);
if (server.aof_load_truncated) valid_up_to = ftello(fp);
}
/* This point can only be reached when EOF is reached without errors.
* If the client is in the middle of a MULTI/EXEC, log error and quit. */
if (fakeClient->flags & REDIS_MULTI) goto readerr;
if (fakeClient->flags & REDIS_MULTI) goto uxeof;
loaded_ok: /* DB loaded, cleanup and return REDIS_OK to the caller. */
fclose(fp);
freeFakeClient(fakeClient);
server.aof_state = old_aof_state;
@@ -619,14 +664,41 @@ int loadAppendOnlyFile(char *filename) {
server.aof_rewrite_base_size = server.aof_current_size;
return REDIS_OK;
readerr:
if (feof(fp)) {
redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file");
} else {
readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */
if (!feof(fp)) {
redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno));
exit(1);
}
uxeof: /* Unexpected AOF end of file. */
if (server.aof_load_truncated) {
redisLog(REDIS_WARNING,"!!! Warning: short read while loading the AOF file !!!");
redisLog(REDIS_WARNING,"!!! Truncating the AOF at offset %llu !!!",
(unsigned long long) valid_up_to);
if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) {
if (valid_up_to == -1) {
redisLog(REDIS_WARNING,"Last valid command offset is invalid");
} else {
redisLog(REDIS_WARNING,"Error truncating the AOF file: %s",
strerror(errno));
}
} else {
/* Make sure the AOF file descriptor points to the end of the
* file after the truncate call. */
if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) {
redisLog(REDIS_WARNING,"Can't seek the end of the AOF file: %s",
strerror(errno));
} else {
redisLog(REDIS_WARNING,
"AOF loaded anyway because aof-load-truncated is enabled");
goto loaded_ok;
}
}
}
redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.");
exit(1);
fmterr:
fmterr: /* Format error. */
redisLog(REDIS_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>");
exit(1);
}
@@ -970,7 +1042,7 @@ int rewriteAppendOnlyFile(char *filename) {
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
if (aof_fsync(fileno(fp)) == -1) goto werr;
if (fsync(fileno(fp)) == -1) goto werr;
if (fclose(fp) == EOF) goto werr;
/* Use RENAME to make sure the DB file is changed atomically only
@@ -1031,6 +1103,8 @@ int rewriteAppendOnlyFileBackground(void) {
} 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) {
redisLog(REDIS_WARNING,
"Can't rewrite append only file in background: fork: %s",
@@ -1074,19 +1148,23 @@ void aofRemoveTempFile(pid_t childpid) {
unlink(tmpfile);
}
/* Update the server.aof_current_size filed explicitly using stat(2)
/* Update the server.aof_current_size field explicitly using stat(2)
* to check the size of the file. This is useful after a rewrite or after
* a restart, normally the size is updated just adding the write length
* to the current length, that is much faster. */
void aofUpdateCurrentSize(void) {
struct redis_stat sb;
mstime_t latency;
latencyStartMonitor(latency);
if (redis_fstat(server.aof_fd,&sb) == -1) {
redisLog(REDIS_WARNING,"Unable to obtain the AOF file length. stat: %s",
strerror(errno));
} else {
server.aof_current_size = sb.st_size;
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fstat",latency);
}
/* A background append only file rewriting (BGREWRITEAOF) terminated its work.
@@ -1096,12 +1174,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
int newfd, oldfd;
char tmpfile[256];
long long now = ustime();
mstime_t latency;
redisLog(REDIS_NOTICE,
"Background AOF rewrite terminated with success");
/* Flush the differences accumulated by the parent to the
* rewritten AOF. */
latencyStartMonitor(latency);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
(int)server.aof_child_pid);
newfd = open(tmpfile,O_WRONLY|O_APPEND);
@@ -1117,6 +1197,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
close(newfd);
goto cleanup;
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-rewrite-diff-write",latency);
redisLog(REDIS_NOTICE,
"Parent diff successfully flushed to the rewritten AOF (%lu bytes)", aofRewriteBufferSize());
@@ -1162,6 +1244,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* Rename the temporary file. This will not unlink the target file if
* it exists, because we reference it with "oldfd". */
latencyStartMonitor(latency);
if (rename(tmpfile,server.aof_filename) == -1) {
redisLog(REDIS_WARNING,
"Error trying to rename the temporary AOF file: %s", strerror(errno));
@@ -1169,6 +1252,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
if (oldfd != -1) close(oldfd);
goto cleanup;
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-rename",latency);
if (server.aof_fd == -1) {
/* AOF disabled, we don't need to set the AOF file descriptor
+11 -9
View File
@@ -107,12 +107,12 @@ size_t redisPopcount(void *s, long count) {
* no zero bit is found, it returns count*8 assuming the string is zero
* padded on the right. However if 'bit' is 1 it is possible that there is
* not a single set bit in the bitmap. In this special case -1 is returned. */
long redisBitpos(void *s, long count, int bit) {
long redisBitpos(void *s, unsigned long count, int bit) {
unsigned long *l;
unsigned char *c;
unsigned long skipval, word = 0, one;
long pos = 0; /* Position of bit, to return to the caller. */
int j;
unsigned long j;
/* Process whole words first, seeking for first word that is not
* all ones or all zeros respectively if we are lookig for zeros
@@ -276,11 +276,12 @@ void getbitCommand(redisClient *c) {
void bitopCommand(redisClient *c) {
char *opname = c->argv[1]->ptr;
robj *o, *targetkey = c->argv[2];
long op, j, numkeys;
unsigned long op, j, numkeys;
robj **objects; /* Array of source objects. */
unsigned char **src; /* Array of source strings pointers. */
long *len, maxlen = 0; /* Array of length of src strings, and max len. */
long minlen = 0; /* Min len among the input keys. */
unsigned long *len, maxlen = 0; /* Array of length of src strings,
and max len. */
unsigned long minlen = 0; /* Min len among the input keys. */
unsigned char *res = NULL; /* Resulting string. */
/* Parse the operation name. */
@@ -320,9 +321,10 @@ void bitopCommand(redisClient *c) {
}
/* Return an error if one of the keys is not a string. */
if (checkType(c,o,REDIS_STRING)) {
for (j = j-1; j >= 0; j--) {
if (objects[j])
decrRefCount(objects[j]);
unsigned long i;
for (i = 0; i < j; i++) {
if (objects[i])
decrRefCount(objects[i]);
}
zfree(src);
zfree(len);
@@ -340,7 +342,7 @@ void bitopCommand(redisClient *c) {
if (maxlen) {
res = (unsigned char*) sdsnewlen(NULL,maxlen);
unsigned char output, byte;
long i;
unsigned long i;
/* Fast path: as far as we have data for all the input bitmaps we
* can take a fast path that performs much better than the
+33 -6
View File
@@ -72,7 +72,7 @@ void appendServerSaveParams(time_t seconds, int changes) {
server.saveparamslen++;
}
void resetServerSaveParams() {
void resetServerSaveParams(void) {
zfree(server.saveparams);
server.saveparams = NULL;
server.saveparamslen = 0;
@@ -355,7 +355,12 @@ void loadServerConfigFromString(char *config) {
} else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
argc == 2)
{
if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {
if ((server.aof_rewrite_incremental_fsync =
yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"aof-load-truncated") && argc == 2) {
if ((server.aof_load_truncated = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
@@ -420,6 +425,14 @@ void loadServerConfigFromString(char *config) {
argc == 2)
{
server.slowlog_log_slower_than = strtoll(argv[1],NULL,10);
} else if (!strcasecmp(argv[0],"latency-monitor-threshold") &&
argc == 2)
{
server.latency_monitor_threshold = strtoll(argv[1],NULL,10);
if (server.latency_monitor_threshold < 0) {
err = "The latency threshold can't be negative";
goto loaderr;
}
} else if (!strcasecmp(argv[0],"slowlog-max-len") && argc == 2) {
server.slowlog_max_len = strtoll(argv[1],NULL,10);
} else if (!strcasecmp(argv[0],"client-output-buffer-limit") &&
@@ -581,7 +594,7 @@ void configSetCommand(redisClient *c) {
server.maxclients = orig_value;
return;
}
if (aeGetSetSize(server.el) <
if ((unsigned int) aeGetSetSize(server.el) <
server.maxclients + REDIS_EVENTLOOP_FDSET_INCR)
{
if (aeResizeSetSize(server.el,
@@ -665,6 +678,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.aof_rewrite_incremental_fsync = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"aof-load-truncated")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.aof_load_truncated = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"save")) {
int vlen, j;
sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
@@ -747,6 +765,9 @@ void configSetCommand(redisClient *c) {
} else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.slowlog_max_len = (unsigned)ll;
} else if (!strcasecmp(c->argv[2]->ptr,"latency-monitor-threshold")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.latency_monitor_threshold = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
if (!strcasecmp(o->ptr,"warning")) {
server.verbosity = REDIS_WARNING;
@@ -944,6 +965,8 @@ void configGetCommand(redisClient *c) {
config_get_numerical_field("lua-time-limit",server.lua_time_limit);
config_get_numerical_field("slowlog-log-slower-than",
server.slowlog_log_slower_than);
config_get_numerical_field("latency-monitor-threshold",
server.latency_monitor_threshold);
config_get_numerical_field("slowlog-max-len",
server.slowlog_max_len);
config_get_numerical_field("port",server.port);
@@ -977,6 +1000,8 @@ void configGetCommand(redisClient *c) {
server.repl_disable_tcp_nodelay);
config_get_bool_field("aof-rewrite-incremental-fsync",
server.aof_rewrite_incremental_fsync);
config_get_bool_field("aof-load-truncated",
server.aof_load_truncated);
/* Everything we can't handle with macros follows. */
@@ -1255,7 +1280,7 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
* "force" is non-zero, the line is appended to the configuration file.
* Usually "force" is true when an option has not its default value, so it
* must be rewritten even if not present previously.
*
*
* The first time a line is appended into a configuration file, a comment
* is added to show that starting from that point the config file was generated
* by CONFIG REWRITE.
@@ -1347,7 +1372,7 @@ void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, c
return;
}
/* Compare the strings as sds strings to have a binary safe comparison. */
/* Set force to zero if the value is set to its default. */
if (defvalue && strcmp(value,defvalue) == 0) force = 0;
line = sdsnew(option);
@@ -1381,7 +1406,7 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int
char *enum_name, *matching_name = NULL;
int enum_val, def_val, force;
sds line;
va_start(ap, value);
while(1) {
enum_name = va_arg(ap,char*);
@@ -1724,6 +1749,7 @@ int rewriteConfig(char *path) {
rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE);
rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,REDIS_LUA_TIME_LIMIT);
rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN);
rewriteConfigNumericalOption(state,"latency-monitor-threshold",server.latency_monitor_threshold,REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD);
rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN);
rewriteConfigNotifykeyspaceeventsOption(state);
rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);
@@ -1738,6 +1764,7 @@ int rewriteConfig(char *path) {
rewriteConfigClientoutputbufferlimitOption(state);
rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ);
rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,REDIS_DEFAULT_AOF_LOAD_TRUNCATED);
if (server.sentinel_mode) rewriteConfigSentinelOption(state);
/* Step 3: remove all the orphaned lines in the old file, that is, lines
+7 -2
View File
@@ -185,11 +185,16 @@ void setproctitle(const char *fmt, ...);
#error "Undefined or invalid BYTE_ORDER"
#endif
#if (__i386 || __amd64) && __GNUC__
#if (__i386 || __amd64 || __powerpc__) && __GNUC__
#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if (GNUC_VERSION >= 40100) || defined(__clang__)
#if defined(__clang__)
#define HAVE_ATOMIC
#endif
#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))
#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6))
#define HAVE_ATOMIC
#endif
#endif
#endif
#endif
+17 -9
View File
@@ -103,7 +103,7 @@ void dbAdd(redisDb *db, robj *key, robj *val) {
* The program is aborted if the key was not already present. */
void dbOverwrite(redisDb *db, robj *key, robj *val) {
struct dictEntry *de = dictFind(db->dict,key->ptr);
redisAssertWithInfo(NULL,key,de != NULL);
dictReplace(db->dict, key->ptr, val);
}
@@ -411,9 +411,7 @@ int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor) {
* In the case of a Hash object the function returns both the field and value
* of every element on the Hash. */
void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
int rv;
int i, j;
char buf[REDIS_LONGSTR_SIZE];
list *keys = listCreate();
listNode *node, *nextnode;
long count = 10;
@@ -485,6 +483,11 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
if (ht) {
void *privdata[2];
/* We set the max number of iterations to ten times the specified
* COUNT, so if the hash table is in a pathological state (very
* sparsely populated) we avoid to block too much time at the cost
* of returning no or very few elements. */
long maxiterations = count*10;
/* We pass two pointers to the callback: the list to which it will
* add new elements, and the object containing the dictionary so that
@@ -493,7 +496,9 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
privdata[1] = o;
do {
cursor = dictScan(ht, cursor, scanCallback, privdata);
} while (cursor && listLength(keys) < count);
} while (cursor &&
maxiterations-- &&
listLength(keys) < (unsigned long)count);
} else if (o->type == REDIS_SET) {
int pos = 0;
int64_t ll;
@@ -567,9 +572,7 @@ void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) {
/* Step 4: Reply to the client. */
addReplyMultiBulkLen(c, 2);
rv = snprintf(buf, sizeof(buf), "%lu", cursor);
redisAssert(rv < sizeof(buf));
addReplyBulkCBuffer(c, buf, rv);
addReplyBulkLongLong(c,cursor);
addReplyMultiBulkLen(c, listLength(keys));
while ((node = listFirst(keys)) != NULL) {
@@ -697,11 +700,16 @@ void moveCommand(redisClient *c) {
robj *o;
redisDb *src, *dst;
int srcid;
long long dbid;
/* Obtain source and target DB pointers */
src = c->db;
srcid = c->db->id;
if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
if (getLongLongFromObject(c->argv[2],&dbid) == REDIS_ERR ||
dbid < INT_MIN || dbid > INT_MAX ||
selectDb(c,dbid) == REDIS_ERR)
{
addReply(c,shared.outofrangeerr);
return;
}
@@ -816,7 +824,7 @@ int expireIfNeeded(redisDb *db, robj *key) {
* the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys.
*
* Still we try to return the right information to the caller,
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
if (server.masterhost != NULL) return now > when;
+2 -8
View File
@@ -79,12 +79,6 @@ unsigned int dictIntHashFunction(unsigned int key)
return key;
}
/* Identity hash function for integer keys */
unsigned int dictIdentityHashFunction(unsigned int key)
{
return key;
}
static uint32_t dict_hash_function_seed = 5381;
void dictSetHashFunctionSeed(uint32_t seed) {
@@ -257,7 +251,7 @@ int dictRehash(dict *d, int n) {
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned)d->rehashidx);
assert(d->ht[0].size > (unsigned long)d->rehashidx);
while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
@@ -576,7 +570,7 @@ dictEntry *dictNext(dictIterator *iter)
iter->fingerprint = dictFingerprint(iter->d);
}
iter->index++;
if (iter->index >= (signed) ht->size) {
if (iter->index >= (long) ht->size) {
if (dictIsRehashing(iter->d) && iter->table == 0) {
iter->table++;
iter->index = 0;
+11 -4
View File
@@ -50,6 +50,7 @@ typedef struct dictEntry {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
@@ -76,7 +77,7 @@ typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
@@ -86,9 +87,11 @@ typedef struct dict {
* should be called while iterating. */
typedef struct dictIterator {
dict *d;
int table, index, safe;
long index;
int table, safe;
dictEntry *entry, *nextEntry;
long long fingerprint; /* unsafe iterator fingerprint for misuse detection */
/* unsafe iterator fingerprint for misuse detection. */
long long fingerprint;
} dictIterator;
typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
@@ -114,6 +117,9 @@ typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
#define dictSetUnsignedIntegerVal(entry, _val_) \
do { entry->v.u64 = _val_; } while(0)
#define dictSetDoubleVal(entry, _val_) \
do { entry->v.d = _val_; } while(0)
#define dictFreeKey(d, entry) \
if ((d)->type->keyDestructor) \
(d)->type->keyDestructor((d)->privdata, (entry)->key)
@@ -135,9 +141,10 @@ typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
#define dictGetVal(he) ((he)->v.val)
#define dictGetSignedIntegerVal(he) ((he)->v.s64)
#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)
#define dictGetDoubleVal(he) ((he)->v.d)
#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
#define dictIsRehashing(ht) ((ht)->rehashidx != -1)
#define dictIsRehashing(d) ((d)->rehashidx != -1)
/* API */
dict *dictCreate(dictType *type, void *privDataPtr);
+1 -1
View File
@@ -28,7 +28,7 @@
* 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.
*/
*/
#ifndef __ENDIANCONV_H
#define __ENDIANCONV_H
+8
View File
@@ -36,6 +36,10 @@
#define _GNU_SOURCE
#endif
#if defined(_AIX)
#define _ALL_SOURCE
#endif
#if defined(__linux__) || defined(__OpenBSD__)
#define _XOPEN_SOURCE 700
/*
@@ -46,6 +50,10 @@
#define _XOPEN_SOURCE
#endif
#if defined(__sun)
#define _POSIX_C_SOURCE 199506L
#endif
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
+11 -3
View File
@@ -1349,7 +1349,7 @@ void pfmergeCommand(redisClient *c) {
* Something that is not easy to test from within the outside. */
#define HLL_TEST_CYCLES 1000
void pfselftestCommand(redisClient *c) {
int j, i;
unsigned int j, i;
sds bitcounters = sdsnewlen(NULL,HLL_DENSE_SIZE);
struct hllhdr *hdr = (struct hllhdr*) bitcounters, *hdr2;
robj *o = NULL;
@@ -1386,7 +1386,7 @@ void pfselftestCommand(redisClient *c) {
* The test adds unique elements and check that the estimated value
* is always reasonable bounds.
*
* We check that the error is smaller than 4 times than the expected
* We check that the error is smaller than a few times than the expected
* standard error, to make it very unlikely for the test to fail because
* of a "bad" run.
*
@@ -1422,8 +1422,16 @@ void pfselftestCommand(redisClient *c) {
/* Check error. */
if (j == checkpoint) {
int64_t abserr = checkpoint - (int64_t)hllCount(hdr,NULL);
uint64_t maxerr = ceil(relerr*6*checkpoint);
/* Adjust the max error we expect for cardinality 10
* since from time to time it is statistically likely to get
* much higher error due to collision, resulting into a false
* positive. */
if (j == 10) maxerr = 1;
if (abserr < 0) abserr = -abserr;
if (abserr > (uint64_t)(relerr*4*checkpoint)) {
if (abserr > (int64_t)maxerr) {
addReplyErrorFormat(c,
"TESTFAILED Too big error. card:%llu abserr:%llu",
(unsigned long long) checkpoint,
+1 -1
View File
@@ -133,7 +133,7 @@ static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
}
while(max >= min) {
mid = (min+max)/2;
mid = ((unsigned int)min + (unsigned int)max) >> 1;
cur = _intsetGet(is,mid);
if (value > cur) {
min = mid+1;
+578
View File
@@ -0,0 +1,578 @@
/* The latency monitor allows to easily observe the sources of latency
* in a Redis instance using the LATENCY command. Different latency
* sources are monitored, like disk I/O, execution of commands, fork
* system call, and so forth.
*
* ----------------------------------------------------------------------------
*
* Copyright (c) 2014, 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 "redis.h"
/* Dictionary type for latency events. */
int dictStringKeyCompare(void *privdata, const void *key1, const void *key2) {
REDIS_NOTUSED(privdata);
return strcmp(key1,key2) == 0;
}
unsigned int dictStringHash(const void *key) {
return dictGenHashFunction(key, strlen(key));
}
void dictVanillaFree(void *privdata, void *val);
dictType latencyTimeSeriesDictType = {
dictStringHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictStringKeyCompare, /* key compare */
dictVanillaFree, /* key destructor */
dictVanillaFree /* val destructor */
};
/* ---------------------------- Latency API --------------------------------- */
/* Latency monitor initialization. We just need to create the dictionary
* of time series, each time serie is craeted on demand in order to avoid
* having a fixed list to maintain. */
void latencyMonitorInit(void) {
server.latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);
}
/* Add the specified sample to the specified time series "event".
* This function is usually called via latencyAddSampleIfNeeded(), that
* is a macro that only adds the sample if the latency is higher than
* server.latency_monitor_threshold. */
void latencyAddSample(char *event, mstime_t latency) {
struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);
time_t now = time(NULL);
int prev;
/* Create the time series if it does not exist. */
if (ts == NULL) {
ts = zmalloc(sizeof(*ts));
ts->idx = 0;
ts->max = 0;
memset(ts->samples,0,sizeof(ts->samples));
dictAdd(server.latency_events,zstrdup(event),ts);
}
/* If the previous sample is in the same second, we update our old sample
* if this latency is > of the old one, or just return. */
prev = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;
if (ts->samples[prev].time == now) {
if (latency > ts->samples[prev].latency)
ts->samples[prev].latency = latency;
return;
}
ts->samples[ts->idx].time = time(NULL);
ts->samples[ts->idx].latency = latency;
if (latency > ts->max) ts->max = latency;
ts->idx++;
if (ts->idx == LATENCY_TS_LEN) ts->idx = 0;
}
/* Reset data for the specified event, or all the events data if 'event' is
* NULL.
*
* Note: this is O(N) even when event_to_reset is not NULL because makes
* the code simpler and we have a small fixed max number of events. */
int latencyResetEvent(char *event_to_reset) {
dictIterator *di;
dictEntry *de;
int resets = 0;
di = dictGetSafeIterator(server.latency_events);
while((de = dictNext(di)) != NULL) {
char *event = dictGetKey(de);
if (event_to_reset == NULL || strcasecmp(event,event_to_reset) == 0) {
dictDelete(server.latency_events, event);
resets++;
}
}
dictReleaseIterator(di);
return resets;
}
/* ------------------------ Latency reporting (doctor) ---------------------- */
/* Analyze the samples avaialble for a given event and return a structure
* populate with different metrics, average, MAD, min, max, and so forth.
* Check latency.h definition of struct latenctStat for more info.
* If the specified event has no elements the structure is populate with
* zero values. */
void analyzeLatencyForEvent(char *event, struct latencyStats *ls) {
struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);
int j;
uint64_t sum;
ls->all_time_high = ts ? ts->max : 0;
ls->avg = 0;
ls->min = 0;
ls->max = 0;
ls->mad = 0;
ls->samples = 0;
ls->period = 0;
if (!ts) return;
/* First pass, populate everything but the MAD. */
sum = 0;
for (j = 0; j < LATENCY_TS_LEN; j++) {
if (ts->samples[j].time == 0) continue;
ls->samples++;
if (ls->samples == 1) {
ls->min = ls->max = ts->samples[j].latency;
} else {
if (ls->min > ts->samples[j].latency)
ls->min = ts->samples[j].latency;
if (ls->max < ts->samples[j].latency)
ls->max = ts->samples[j].latency;
}
sum += ts->samples[j].latency;
/* Track the oldest event time in ls->period. */
if (ls->period == 0 || ts->samples[j].time < ls->period)
ls->period = ts->samples[j].time;
}
/* So far avg is actually the sum of the latencies, and period is
* the oldest event time. We need to make the first an average and
* the second a range of seconds. */
if (ls->samples) {
ls->avg = sum / ls->samples;
ls->period = time(NULL) - ls->period;
if (ls->period == 0) ls->period = 1;
}
/* Second pass, compute MAD. */
sum = 0;
for (j = 0; j < LATENCY_TS_LEN; j++) {
int64_t delta;
if (ts->samples[j].time == 0) continue;
delta = (int64_t)ls->avg - ts->samples[j].latency;
if (delta < 0) delta = -delta;
sum += delta;
}
if (ls->samples) ls->mad = sum / ls->samples;
}
/* Create a human readable report of latency events for this Redis instance. */
sds createLatencyReport(void) {
sds report = sdsempty();
int advise_better_vm = 0; /* Better virtual machines. */
int advise_slowlog_enabled = 0; /* Enable slowlog. */
int advise_slowlog_tuning = 0; /* Reconfigure slowlog. */
int advise_slowlog_inspect = 0; /* Check your slowlog. */
int advise_disk_contention = 0; /* Try to lower disk contention. */
int advise_scheduler = 0; /* Intrinsic latency. */
int advise_data_writeback = 0; /* data=writeback. */
int advise_no_appendfsync = 0; /* don't fsync during rewrites. */
int advise_local_disk = 0; /* Avoid remote disks. */
int advise_ssd = 0; /* Use an SSD drive. */
int advise_write_load_info = 0; /* Print info about AOF and write load. */
int advise_hz = 0; /* Use higher HZ. */
int advise_large_objects = 0; /* Deletion of large objects. */
int advise_relax_fsync_policy = 0; /* appendfsync always is slow. */
int advices = 0;
/* Return ASAP if the latency engine is disabled and it looks like it
* was never enabled so far. */
if (dictSize(server.latency_events) == 0 &&
server.latency_monitor_threshold == 0)
{
report = sdscat(report,"I'm sorry, Dave, I can't do that. Latency monitoring is disabled in this Redis instance. You may use \"CONFIG SET latency-monitor-threshold <milliseconds>.\" in order to enable it. If we weren't in a deep space mission I'd suggest to take a look at http://redis.io/topics/latency-monitor.\n");
return report;
}
/* Show all the events stats and add for each event some event-related
* comment depending on the values. */
dictIterator *di;
dictEntry *de;
int eventnum = 0;
di = dictGetIterator(server.latency_events);
while((de = dictNext(di)) != NULL) {
char *event = dictGetKey(de);
struct latencyTimeSeries *ts = dictGetVal(de);
struct latencyStats ls;
if (ts == NULL) continue;
eventnum++;
if (eventnum == 1) {
report = sdscat(report,"Dave, I have observed latency spikes in this Redis instance. You don't mind talking about it, do you Dave?\n\n");
}
analyzeLatencyForEvent(event,&ls);
report = sdscatprintf(report,
"%d. %s: %d latency spikes (average %lums, mean deviation %lums, period %.2f sec). Worst all time event %lums.",
eventnum, event,
ls.samples,
(unsigned long) ls.avg,
(unsigned long) ls.mad,
(double) ls.period/ls.samples,
(unsigned long) ts->max);
/* Fork */
if (!strcasecmp(event,"fork")) {
char *fork_quality;
if (server.stat_fork_rate < 10) {
fork_quality = "terrible";
advise_better_vm = 1;
advices++;
} else if (server.stat_fork_rate < 25) {
fork_quality = "poor";
advise_better_vm = 1;
advices++;
} else if (server.stat_fork_rate < 100) {
fork_quality = "good";
} else {
fork_quality = "excellent";
}
report = sdscatprintf(report,
" Fork rate is %.2f GB/sec (%s).", server.stat_fork_rate,
fork_quality);
}
/* Potentially commands. */
if (!strcasecmp(event,"command")) {
if (server.slowlog_log_slower_than == 0) {
advise_slowlog_enabled = 1;
advices++;
} else if (server.slowlog_log_slower_than/1000 >
server.latency_monitor_threshold)
{
advise_slowlog_tuning = 1;
advices++;
}
advise_slowlog_inspect = 1;
advise_large_objects = 1;
advices += 2;
}
/* fast-command. */
if (!strcasecmp(event,"fast-command")) {
advise_scheduler = 1;
advices++;
}
/* AOF and I/O. */
if (!strcasecmp(event,"aof-write-pending-fsync")) {
advise_local_disk = 1;
advise_disk_contention = 1;
advise_ssd = 1;
advise_data_writeback = 1;
advices += 4;
}
if (!strcasecmp(event,"aof-write-active-child")) {
advise_no_appendfsync = 1;
advise_data_writeback = 1;
advise_ssd = 1;
advices += 3;
}
if (!strcasecmp(event,"aof-write-alone")) {
advise_local_disk = 1;
advise_data_writeback = 1;
advise_ssd = 1;
advices += 3;
}
if (!strcasecmp(event,"aof-fsync-always")) {
advise_relax_fsync_policy = 1;
advices++;
}
if (!strcasecmp(event,"aof-fstat") ||
!strcasecmp(event,"rdb-unlik-temp-file")) {
advise_disk_contention = 1;
advise_local_disk = 1;
advices += 2;
}
if (!strcasecmp(event,"aof-rewrite-diff-write") ||
!strcasecmp(event,"aof-rename")) {
advise_write_load_info = 1;
advise_data_writeback = 1;
advise_ssd = 1;
advise_local_disk = 1;
advices += 4;
}
/* Expire cycle. */
if (!strcasecmp(event,"expire-cycle")) {
advise_hz = 1;
advise_large_objects = 1;
advices += 2;
}
/* Eviction cycle. */
if (!strcasecmp(event,"eviction-cycle")) {
advise_large_objects = 1;
advices++;
}
report = sdscatlen(report,"\n",1);
}
dictReleaseIterator(di);
if (eventnum == 0) {
report = sdscat(report,"Dave, no latency spike was observed during the lifetime of this Redis instance, not in the slightest bit. I honestly think you ought to sit down calmly, take a stress pill, and think things over.\n");
} else if (advices == 0) {
report = sdscat(report,"\nWhile there are latency events logged, I'm not able to suggest any easy fix. Please use the Redis community to get some help, providing this report in your help request.\n");
} else {
/* Add all the suggestions accumulated so far. */
/* Better VM. */
report = sdscat(report,"\nI have a few advices for you:\n\n");
if (advise_better_vm) {
report = sdscat(report,"- If you are using a virtual machine, consider upgrading it with a faster one using an hypervisior that provides less latency during fork() calls. Xen is known to have poor fork() performance. Even in the context of the same VM provider, certain kinds of instances can execute fork faster than others.\n");
}
/* Slow log. */
if (advise_slowlog_enabled) {
report = sdscatprintf(report,"- There are latency issues with potentially slow commands you are using. Try to enable the Slow Log Redis feature using the command 'CONFIG SET slowlog-log-slower-than %llu'. If the Slow log is disabled Redis is not able to log slow commands execution for you.\n", (unsigned long long)server.latency_monitor_threshold*1000);
}
if (advise_slowlog_tuning) {
report = sdscatprintf(report,"- Your current Slow Log configuration only logs events that are slower than your configured latency monitor threshold. Please use 'CONFIG SET slowlog-log-slower-than %llu'.\n", (unsigned long long)server.latency_monitor_threshold*1000);
}
if (advise_slowlog_inspect) {
report = sdscat(report,"- Check your Slow Log to understand what are the commands you are running which are too slow to execute. Please check http://redis.io/commands/slowlog for more information.\n");
}
/* Intrinsic latency. */
if (advise_scheduler) {
report = sdscat(report,"- The system is slow to execute Redis code paths not containing system calls. This usually means the system does not provide Redis CPU time to run for long periods. You should try to:\n"
" 1) Lower the system load.\n"
" 2) Use a computer / VM just for Redis if you are running other softawre in the same system.\n"
" 3) Check if you have a \"noisy neighbour\" problem.\n"
" 4) Check with 'redis-cli --intrinsic-latency 100' what is the intrinsic latency in your system.\n"
" 5) Check if the problem is allocator-related by recompiling Redis with MALLOC=libc, if you are using Jemalloc. However this may create fragmentation problems.\n");
}
/* AOF / Disk latency. */
if (advise_local_disk) {
report = sdscat(report,"- It is strongly advised to use local disks for persistence, especially if you are using AOF. Remote disks provided by platform-as-a-service providers are known to be slow.\n");
}
if (advise_ssd) {
report = sdscat(report,"- SSD disks are able to reduce fsync latency, and total time needed for snapshotting and AOF log rewriting (resulting in smaller memory usage and smaller final AOF rewrite buffer flushes). With extremely high write load SSD disks can be a good option. However Redis should perform reasonably with high load using normal disks. Use this advice as a last resort.\n");
}
if (advise_data_writeback) {
report = sdscat(report,"- Mounting ext3/4 filesystems with data=writeback can provide a performance boost compared to data=ordered, however this mode of operation provides less guarantees, and sometimes it can happen that after a hard crash the AOF file will have an half-written command at the end and will require to be repaired before Redis restarts.\n");
}
if (advise_disk_contention) {
report = sdscat(report,"- Try to lower the disk contention. This is often caused by other disk intensive processes running in the same computer (including other Redis instances).\n");
}
if (advise_no_appendfsync) {
report = sdscat(report,"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing an RDB file (the moment where there is high disk contention).\n");
}
if (advise_relax_fsync_policy && server.aof_fsync == AOF_FSYNC_ALWAYS) {
report = sdscat(report,"- Your fsync policy is set to 'always'. It is very hard to get good performances with such a setup, if possible try to relax the fsync policy to 'onesec'.\n");
}
if (advise_write_load_info) {
report = sdscat(report,"- Latency during the AOF atomic rename operation or when the final difference is flushed to the AOF file at the end of the rewrite, sometimes is caused by very high write load, causing the AOF buffer to get very large. If possible try to send less commands to accomplish the same work, or use Lua scripts to group multiple operations into a single EVALSHA call.\n");
}
if (advise_hz && server.hz < 100) {
report = sdscat(report,"- In order to make the Redis keys expiring process more incremental, try to set the 'hz' configuration parameter to 100 using 'CONFIG SET hz 100'.\n");
}
if (advise_large_objects) {
report = sdscat(report,"- Deleting, expiring or evicting (because of maxmemory policy) large objects is a blocking operation. If you have very large objects that are often deleted, expired, or evicted, try to fragment those objects into multiple smaller objects.\n");
}
}
return report;
}
/* ---------------------- Latency command implementation -------------------- */
/* latencyCommand() helper to produce a time-delay reply for all the samples
* in memory for the specified time series. */
void latencyCommandReplyWithSamples(redisClient *c, struct latencyTimeSeries *ts) {
void *replylen = addDeferredMultiBulkLength(c);
int samples = 0, j;
for (j = 0; j < LATENCY_TS_LEN; j++) {
int i = (ts->idx + j) % LATENCY_TS_LEN;
if (ts->samples[i].time == 0) continue;
addReplyMultiBulkLen(c,2);
addReplyLongLong(c,ts->samples[i].time);
addReplyLongLong(c,ts->samples[i].latency);
samples++;
}
setDeferredMultiBulkLength(c,replylen,samples);
}
/* latencyCommand() helper to produce the reply for the LATEST subcommand,
* listing the last latency sample for every event type registered so far. */
void latencyCommandReplyWithLatestEvents(redisClient *c) {
dictIterator *di;
dictEntry *de;
addReplyMultiBulkLen(c,dictSize(server.latency_events));
di = dictGetIterator(server.latency_events);
while((de = dictNext(di)) != NULL) {
char *event = dictGetKey(de);
struct latencyTimeSeries *ts = dictGetVal(de);
int last = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;
addReplyMultiBulkLen(c,4);
addReplyBulkCString(c,event);
addReplyLongLong(c,ts->samples[last].time);
addReplyLongLong(c,ts->samples[last].latency);
addReplyLongLong(c,ts->max);
}
dictReleaseIterator(di);
}
#define LATENCY_GRAPH_COLS 80
sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {
int j;
struct sequence *seq = createSparklineSequence();
sds graph = sdsempty();
uint32_t min = 0, max = 0;
for (j = 0; j < LATENCY_TS_LEN; j++) {
int i = (ts->idx + j) % LATENCY_TS_LEN;
int elapsed;
char *label;
char buf[64];
if (ts->samples[i].time == 0) continue;
/* Update min and max. */
if (seq->length == 0) {
min = max = ts->samples[i].latency;
} else {
if (ts->samples[i].latency > max) max = ts->samples[i].latency;
if (ts->samples[i].latency < min) min = ts->samples[i].latency;
}
/* Use as label the number of seconds / minutes / hours / days
* ago the event happened. */
elapsed = time(NULL) - ts->samples[i].time;
if (elapsed < 60)
snprintf(buf,sizeof(buf),"%ds",elapsed);
else if (elapsed < 3600)
snprintf(buf,sizeof(buf),"%dm",elapsed/60);
else if (elapsed < 3600*24)
snprintf(buf,sizeof(buf),"%dh",elapsed/3600);
else
snprintf(buf,sizeof(buf),"%dd",elapsed/(3600*24));
label = zstrdup(buf);
sparklineSequenceAddSample(seq,ts->samples[i].latency,label);
}
graph = sdscatprintf(graph,
"%s - high %lu ms, low %lu ms (all time high %lu ms)\n", event,
(unsigned long) max, (unsigned long) min, (unsigned long) ts->max);
for (j = 0; j < LATENCY_GRAPH_COLS; j++)
graph = sdscatlen(graph,"-",1);
graph = sdscatlen(graph,"\n",1);
graph = sparklineRender(graph,seq,LATENCY_GRAPH_COLS,4,SPARKLINE_FILL);
freeSparklineSequence(seq);
return graph;
}
/* LATENCY command implementations.
*
* LATENCY SAMPLES: return time-latency samples for the specified event.
* LATENCY LATEST: return the latest latency for all the events classes.
* LATENCY DOCTOR: returns an human readable analysis of instance latency.
* LATENCY GRAPH: provide an ASCII graph of the latency of the specified event.
*/
void latencyCommand(redisClient *c) {
struct latencyTimeSeries *ts;
if (!strcasecmp(c->argv[1]->ptr,"history") && c->argc == 3) {
/* LATENCY HISTORY <event> */
ts = dictFetchValue(server.latency_events,c->argv[2]->ptr);
if (ts == NULL) {
addReplyMultiBulkLen(c,0);
} else {
latencyCommandReplyWithSamples(c,ts);
}
} else if (!strcasecmp(c->argv[1]->ptr,"graph") && c->argc == 3) {
/* LATENCY GRAPH <event> */
sds graph;
dictEntry *de;
char *event;
de = dictFind(server.latency_events,c->argv[2]->ptr);
if (de == NULL) goto nodataerr;
ts = dictGetVal(de);
event = dictGetKey(de);
graph = latencyCommandGenSparkeline(event,ts);
addReplyBulkCString(c,graph);
sdsfree(graph);
} else if (!strcasecmp(c->argv[1]->ptr,"latest") && c->argc == 2) {
/* LATENCY LATEST */
latencyCommandReplyWithLatestEvents(c);
} else if (!strcasecmp(c->argv[1]->ptr,"doctor") && c->argc == 2) {
/* LATENCY DOCTOR */
sds report = createLatencyReport();
addReplyBulkCBuffer(c,report,sdslen(report));
sdsfree(report);
} else if (!strcasecmp(c->argv[1]->ptr,"reset") && c->argc >= 2) {
/* LATENCY RESET */
if (c->argc == 2) {
addReplyLongLong(c,latencyResetEvent(NULL));
} else {
int j, resets = 0;
for (j = 2; j < c->argc; j++)
resets += latencyResetEvent(c->argv[j]->ptr);
addReplyLongLong(c,resets);
}
} else {
addReply(c,shared.syntaxerr);
}
return;
nodataerr:
/* Common error when the user asks for an event we have no latency
* information about. */
addReplyErrorFormat(c,
"No samples available for event '%s'", (char*) c->argv[2]->ptr);
}
+88
View File
@@ -0,0 +1,88 @@
/* latency.h -- latency monitor API header file
* See latency.c for more information.
*
* ----------------------------------------------------------------------------
*
* Copyright (c) 2014, 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.
*/
#ifndef __LATENCY_H
#define __LATENCY_H
#define LATENCY_TS_LEN 160 /* History length for every monitored event. */
/* Representation of a latency sample: the sampling time and the latency
* observed in milliseconds. */
struct latencySample {
int32_t time; /* We don't use time_t to force 4 bytes usage everywhere. */
uint32_t latency; /* Latency in milliseconds. */
};
/* The latency time series for a given event. */
struct latencyTimeSeries {
int idx; /* Index of the next sample to store. */
uint32_t max; /* Max latency observed for this event. */
struct latencySample samples[LATENCY_TS_LEN]; /* Latest history. */
};
/* Latency statistics structure. */
struct latencyStats {
uint32_t all_time_high; /* Absolute max observed since latest reset. */
uint32_t avg; /* Average of current samples. */
uint32_t min; /* Min of current samples. */
uint32_t max; /* Max of current samples. */
uint32_t mad; /* Mean absolute deviation. */
uint32_t samples; /* Number of non-zero samples. */
time_t period; /* Number of seconds since first event and now. */
};
void latencyMonitorInit(void);
void latencyAddSample(char *event, mstime_t latency);
/* Latency monitoring macros. */
/* Start monitoring an event. We just set the current time. */
#define latencyStartMonitor(var) if (server.latency_monitor_threshold) { \
var = mstime(); \
} else { \
var = 0; \
}
/* End monitoring an event, compute the difference with the current time
* to check the amount of time elapsed. */
#define latencyEndMonitor(var) if (server.latency_monitor_threshold) { \
var = mstime() - var; \
}
/* Add the sample only if the elapsed time is >= to the configured threshold. */
#define latencyAddSampleIfNeeded(event,var) \
if (server.latency_monitor_threshold && \
(var) >= server.latency_monitor_threshold) \
latencyAddSample((event),(var));
#endif /* __LATENCY_H */
+6 -6
View File
@@ -1,16 +1,16 @@
/*
* Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>
*
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
*
* 2. 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.
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
@@ -73,7 +73,7 @@
* and lzf_c.c.
*
*/
unsigned int
unsigned int
lzf_compress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len);
@@ -92,7 +92,7 @@ lzf_compress (const void *const in_data, unsigned int in_len,
*
* This function is very fast, about as fast as a copying loop.
*/
unsigned int
unsigned int
lzf_decompress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len);
+4 -4
View File
@@ -1,16 +1,16 @@
/*
* Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>
*
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
*
* 2. 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.
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+4 -4
View File
@@ -1,16 +1,16 @@
/*
* Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>
*
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
*
* 2. 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.
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+5 -5
View File
@@ -1,16 +1,16 @@
/*
* Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>
*
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
*
* 2. 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.
*
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
@@ -52,7 +52,7 @@
#endif
*/
unsigned int
unsigned int
lzf_decompress (const void *const in_data, unsigned int in_len,
void *out_data, unsigned int out_len)
{
+1
View File
@@ -237,6 +237,7 @@ void memtest_test(size_t megabytes, int passes) {
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
}
free(m);
}
void memtest_non_destructive_invert(void *addr, size_t size) {
+1 -1
View File
@@ -149,7 +149,7 @@ void migrateCommand(redisClient *c) {
addReplySds(c,sdsnew("+NOKEY\r\n"));
return;
}
/* Connect */
fd = anetTcpNonBlockConnect(server.neterr,c->argv[1]->ptr,
atoi(c->argv[2]->ptr));
+2 -2
View File
@@ -72,7 +72,7 @@ void queueMultiCommand(redisClient *c) {
void discardTransaction(redisClient *c) {
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);;
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);
unwatchAllKeys(c);
}
@@ -214,7 +214,7 @@ void watchForKey(redisClient *c, robj *key) {
}
/* This key is not already watched in this DB. Let's add it */
clients = dictFetchValue(c->db->watched_keys,key);
if (!clients) {
if (!clients) {
clients = listCreate();
dictAdd(c->db->watched_keys,key,clients);
incrRefCount(key);
+8 -5
View File
@@ -1028,7 +1028,7 @@ int processMultibulkBuffer(redisClient *c) {
qblen = sdslen(c->querybuf);
/* Hint the sds library about the amount of bytes this string is
* going to contain. */
if (qblen < ll+2)
if (qblen < (size_t)ll+2)
c->querybuf = sdsMakeRoomFor(c->querybuf,ll+2-qblen);
}
c->bulklen = ll;
@@ -1335,6 +1335,7 @@ void clientCommand(redisClient *c) {
if (c->argc == 3) {
/* Old style syntax: CLIENT KILL <addr> */
addr = c->argv[2]->ptr;
skipme = 0; /* With the old form, you can kill yourself. */
} else if (c->argc > 3) {
int i = 2; /* Next option index. */
@@ -1382,7 +1383,9 @@ void clientCommand(redisClient *c) {
while ((ln = listNext(&li)) != NULL) {
client = listNodeValue(ln);
if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;
if (type != -1 && getClientType(client) != type) continue;
if (type != -1 &&
(client->flags & REDIS_MASTER ||
getClientType(client) != type)) continue;
if (id != 0 && client->id != id) continue;
if (c == client && skipme) continue;
@@ -1458,7 +1461,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
va_start(ap,argc);
for (j = 0; j < argc; j++) {
robj *a;
a = va_arg(ap, robj*);
argv[j] = a;
incrRefCount(a);
@@ -1480,7 +1483,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
* The new val ref count is incremented, and the old decremented. */
void rewriteClientCommandArgument(redisClient *c, int i, robj *newval) {
robj *oldval;
redisAssertWithInfo(c,NULL,i < c->argc);
oldval = c->argv[i];
c->argv[i] = newval;
@@ -1524,7 +1527,7 @@ unsigned long getClientOutputBufferMemoryUsage(redisClient *c) {
int getClientType(redisClient *c) {
if ((c->flags & REDIS_SLAVE) && !(c->flags & REDIS_MONITOR))
return REDIS_CLIENT_TYPE_SLAVE;
if (dictSize(c->pubsub_channels) || listLength(c->pubsub_patterns))
if (c->flags & REDIS_PUBSUB)
return REDIS_CLIENT_TYPE_PUBSUB;
return REDIS_CLIENT_TYPE_NORMAL;
}
+6 -2
View File
@@ -325,7 +325,11 @@ robj *tryObjectEncoding(robj *o) {
* Note that we also avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* algorithm to work well. */
if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS) {
if ((server.maxmemory == 0 ||
(server.maxmemory_policy != REDIS_MAXMEMORY_VOLATILE_LRU &&
server.maxmemory_policy != REDIS_MAXMEMORY_ALLKEYS_LRU)) &&
value >= 0 && value < REDIS_SHARED_INTEGERS)
{
decrRefCount(o);
incrRefCount(shared.integers[value]);
return shared.integers[value];
@@ -608,7 +612,7 @@ robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
}
/* Object command allows to inspect the internals of an Redis Object.
* Usage: OBJECT <verb> ... arguments ... */
* Usage: OBJECT <refcount|encoding|idletime> <key> */
void objectCommand(redisClient *c) {
robj *o;
+3 -3
View File
@@ -69,7 +69,7 @@ static inline void
swapfunc(char *a, char *b, size_t n, int swaptype)
{
if (swaptype <= 1)
if (swaptype <= 1)
swapcode(long, a, b, n)
else
swapcode(char, a, b, n)
@@ -160,9 +160,9 @@ loop: SWAPINIT(a, es);
(lrange > _r && rrange > _r)))
_pqsort(a, r / es, es, cmp, lrange, rrange);
}
if ((r = pd - pc) > es) {
if ((r = pd - pc) > es) {
void *_l, *_r;
/* Iterate rather than recurse to save stack space */
a = pn - r;
n = r / es;
+14 -4
View File
@@ -47,6 +47,12 @@ int listMatchPubsubPattern(void *a, void *b) {
(equalStringObjects(pa->pattern,pb->pattern));
}
/* Return the number of channels + patterns a client is subscribed to. */
int clientSubscriptionsCount(redisClient *c) {
return dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns);
}
/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or
* 0 if the client was already subscribed to that channel. */
int pubsubSubscribeChannel(redisClient *c, robj *channel) {
@@ -73,7 +79,7 @@ int pubsubSubscribeChannel(redisClient *c, robj *channel) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.subscribebulk);
addReplyBulk(c,channel);
addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));
addReplyLongLong(c,clientSubscriptionsCount(c));
return retval;
}
@@ -135,7 +141,7 @@ int pubsubSubscribePattern(redisClient *c, robj *pattern) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.psubscribebulk);
addReplyBulk(c,pattern);
addReplyLongLong(c,dictSize(c->pubsub_channels)+listLength(c->pubsub_patterns));
addReplyLongLong(c,clientSubscriptionsCount(c));
return retval;
}
@@ -168,7 +174,7 @@ int pubsubUnsubscribePattern(redisClient *c, robj *pattern, int notify) {
}
/* Unsubscribe from all the channels. Return the number of channels the
* client was subscribed from. */
* client was subscribed to. */
int pubsubUnsubscribeAllChannels(redisClient *c, int notify) {
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
dictEntry *de;
@@ -273,6 +279,7 @@ void subscribeCommand(redisClient *c) {
for (j = 1; j < c->argc; j++)
pubsubSubscribeChannel(c,c->argv[j]);
c->flags |= REDIS_PUBSUB;
}
void unsubscribeCommand(redisClient *c) {
@@ -284,6 +291,7 @@ void unsubscribeCommand(redisClient *c) {
for (j = 1; j < c->argc; j++)
pubsubUnsubscribeChannel(c,c->argv[j],1);
}
if (clientSubscriptionsCount(c) == 0) c->flags &= ~REDIS_PUBSUB;
}
void psubscribeCommand(redisClient *c) {
@@ -291,6 +299,7 @@ void psubscribeCommand(redisClient *c) {
for (j = 1; j < c->argc; j++)
pubsubSubscribePattern(c,c->argv[j]);
c->flags |= REDIS_PUBSUB;
}
void punsubscribeCommand(redisClient *c) {
@@ -302,6 +311,7 @@ void punsubscribeCommand(redisClient *c) {
for (j = 1; j < c->argc; j++)
pubsubUnsubscribePattern(c,c->argv[j],1);
}
if (clientSubscriptionsCount(c) == 0) c->flags &= ~REDIS_PUBSUB;
}
void publishCommand(redisClient *c) {
@@ -345,7 +355,7 @@ void pubsubCommand(redisClient *c) {
list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);
addReplyBulk(c,c->argv[j]);
addReplyBulkLongLong(c,l ? listLength(l) : 0);
addReplyLongLong(c,l ? listLength(l) : 0);
}
} else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
/* PUBSUB NUMPAT */
+2 -2
View File
@@ -66,7 +66,7 @@
#define HI_BIT (1L << (2 * N - 1))
static uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C;
static void next();
static void next(void);
int32_t redisLrand48() {
next();
@@ -77,7 +77,7 @@ void redisSrand48(int32_t seedval) {
SEED(X0, LOW(seedval), HIGH(seedval));
}
static void next() {
static void next(void) {
uint32_t p[2], q[2], r[2], carry0, carry1;
MUL(a[0], x[0], p);
+12 -5
View File
@@ -671,7 +671,7 @@ int rdbSave(char *filename) {
sds keystr = dictGetKey(de);
robj key, *o = dictGetVal(de);
long long expire;
initStaticStringObject(key,keystr);
expire = getExpire(db,&key);
if (rdbSaveKeyValuePair(&rdb,&key,o,expire,now) == -1) goto werr;
@@ -687,7 +687,7 @@ int rdbSave(char *filename) {
* loading code skips the check in this case. */
cksum = rdb.cksum;
memrev64ifbe(&cksum);
rioWrite(&rdb,&cksum,8);
if (rioWrite(&rdb,&cksum,8) == 0) goto werr;
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
@@ -745,6 +745,8 @@ int rdbSaveBackground(char *filename) {
} 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) {
server.lastbgsave_status = REDIS_ERR;
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
@@ -763,7 +765,7 @@ int rdbSaveBackground(char *filename) {
void rdbRemoveTempFile(pid_t childpid) {
char tmpfile[256];
snprintf(tmpfile,256,"temp-%d.rdb", (int) childpid);
snprintf(tmpfile,sizeof(tmpfile),"temp-%d.rdb", (int) childpid);
unlink(tmpfile);
}
@@ -941,7 +943,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
/* Add pair to hash table */
ret = dictAdd((dict*)o->ptr, field, value);
redisAssert(ret == REDIS_OK);
redisAssert(ret == DICT_OK);
}
/* All pairs should be read by now */
@@ -1201,9 +1203,14 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
redisLog(REDIS_WARNING, "Background saving error");
server.lastbgsave_status = REDIS_ERR;
} else {
mstime_t latency;
redisLog(REDIS_WARNING,
"Background saving terminated by signal %d", bysignal);
latencyStartMonitor(latency);
rdbRemoveTempFile(server.rdb_child_pid);
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("rdb-unlink-temp-file",latency);
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error conditon. */
if (bysignal != SIGUSR1)
@@ -1214,7 +1221,7 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
server.rdb_save_time_start = -1;
/* Possibly there are slaves waiting for a BGSAVE in order to be served
* (the first stage of SYNC is a bulk transfer of dump.rdb) */
updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR);
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? REDIS_OK : REDIS_ERR);
}
void saveCommand(redisClient *c) {
+21 -11
View File
@@ -77,6 +77,7 @@ static struct config {
int dbnum;
sds dbnumstr;
char *tests;
char *auth;
} config;
typedef struct _client {
@@ -213,7 +214,7 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
freeReplyObject(reply);
if (c->selectlen) {
int j;
size_t j;
/* This is the OK from SELECT. Just discard the SELECT
* from the buffer. */
@@ -325,6 +326,13 @@ static client createClient(char *cmd, size_t len, client from) {
* the example client buffer. */
c->obuf = sdsempty();
if (config.auth) {
char *buf = NULL;
int len = redisFormatCommand(&buf, "AUTH %s", config.auth);
c->obuf = sdscatlen(c->obuf, buf, len);
free(buf);
}
/* If a DB number different than zero is selected, prefix our request
* buffer with the SELECT command, that will be discarded the first
* time the replies are received, so if the client is reused the
@@ -346,6 +354,7 @@ static client createClient(char *cmd, size_t len, client from) {
for (j = 0; j < config.pipeline; j++)
c->obuf = sdscatlen(c->obuf,cmd,len);
}
c->written = 0;
c->pending = config.pipeline;
c->randptr = NULL;
@@ -359,7 +368,7 @@ static client createClient(char *cmd, size_t len, client from) {
c->randfree = 0;
c->randptr = zmalloc(sizeof(char*)*c->randlen);
/* copy the offsets. */
for (j = 0; j < c->randlen; j++) {
for (j = 0; j < (int)c->randlen; j++) {
c->randptr[j] = c->obuf + (from->randptr[j]-from->obuf);
/* Adjust for the different select prefix length. */
c->randptr[j] += c->selectlen - from->selectlen;
@@ -389,15 +398,6 @@ static client createClient(char *cmd, size_t len, client from) {
static void createMissingClients(client c) {
int n = 0;
char *buf = c->obuf;
size_t buflen = sdslen(c->obuf);
/* If we are cloning from a client with a SELECT prefix, skip it since the
* client will be created with the prefixed SELECT if needed. */
if (c->selectlen) {
buf += c->selectlen;
buflen -= c->selectlen;
}
while(config.liveclients < config.numclients) {
createClient(NULL,0,c);
@@ -489,6 +489,9 @@ int parseOptions(int argc, const char **argv) {
} else if (!strcmp(argv[i],"-s")) {
if (lastarg) goto invalid;
config.hostsocket = strdup(argv[++i]);
} else if (!strcmp(argv[i],"-a") ) {
if (lastarg) goto invalid;
config.auth = strdup(argv[++i]);
} else if (!strcmp(argv[i],"-d")) {
if (lastarg) goto invalid;
config.datasize = atoi(argv[++i]);
@@ -550,6 +553,7 @@ usage:
" -h <hostname> Server hostname (default 127.0.0.1)\n"
" -p <port> Server port (default 6379)\n"
" -s <socket> Server socket (overrides host and port)\n"
" -a <password> Password for Redis Auth\n"
" -c <clients> Number of parallel connections (default 50)\n"
" -n <requests> Total number of requests (default 10000)\n"
" -d <size> Data size of SET/GET value in bytes (default 2)\n"
@@ -592,6 +596,11 @@ int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData
REDIS_NOTUSED(id);
REDIS_NOTUSED(clientData);
if (config.liveclients == 0) {
fprintf(stderr,"All clients disconnected... aborting.\n");
exit(1);
}
if (config.csv) return 250;
float dt = (float)(mstime()-config.start)/1000.0;
float rps = (float)config.requests_finished/dt;
@@ -646,6 +655,7 @@ int main(int argc, const char **argv) {
config.hostsocket = NULL;
config.tests = NULL;
config.dbnum = 0;
config.auth = NULL;
i = parseOptions(argc,argv);
argc -= i;
+9 -6
View File
@@ -138,12 +138,14 @@ typedef struct {
* at runtime to avoid strange compiler optimizations. */
static double R_Zero, R_PosInf, R_NegInf, R_Nan;
#define MAX_TYPES_NUM 256
#define MAX_TYPE_NAME_LEN 16
/* store string types for output */
static char types[256][16];
static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN];
/* Return true if 't' is a valid object type. */
int checkType(unsigned char t) {
/* In case a new object type is added, update the following
/* In case a new object type is added, update the following
* condition as necessary. */
return
(t >= REDIS_HASH_ZIPMAP && t <= REDIS_HASH_ZIPLIST) ||
@@ -166,7 +168,7 @@ int readBytes(void *target, long num) {
return 1;
}
int processHeader() {
int processHeader(void) {
char buf[10] = "_________";
int dump_version;
@@ -335,6 +337,7 @@ char* loadStringObject() {
if (len == REDIS_RDB_LENERR) return NULL;
char *buf = malloc(sizeof(char) * (len+1));
if (buf == NULL) return NULL;
buf[len] = '\0';
if (!readBytes(buf, len)) {
free(buf);
@@ -514,7 +517,7 @@ entry loadEntry() {
return e;
} else {
/* optionally consume expire */
if (e.type == REDIS_EXPIRETIME ||
if (e.type == REDIS_EXPIRETIME ||
e.type == REDIS_EXPIRETIME_MS) {
if (!processTime(e.type)) return e;
if (!loadType(&e)) return e;
@@ -600,7 +603,7 @@ void printErrorStack(entry *e) {
}
}
void process() {
void process(void) {
uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
entry entry;
int dump_version = processHeader();
@@ -611,7 +614,7 @@ void process() {
printf("RDB version >= 5 but no room for checksum.\n");
exit(1);
}
positions[0].size -= 8;;
positions[0].size -= 8;
}
level = 1;
+62 -39
View File
@@ -94,10 +94,11 @@ static struct config {
sds mb_delim;
char prompt[128];
char *eval;
int last_cmd_type;
} config;
static volatile sig_atomic_t force_cancel_loop = 0;
static void usage();
static void usage(void);
static void slaveMode(void);
char *redisGitSHA1(void);
char *redisGitDirty(void);
@@ -131,7 +132,7 @@ static void cliRefreshPrompt(void) {
strchr(config.hostip,':') ? "[%s]:%d" : "%s:%d",
config.hostip, config.hostport);
/* Add [dbnum] if needed */
if (config.dbnum != 0)
if (config.dbnum != 0 && config.last_cmd_type != REDIS_REPLY_ERROR)
len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
config.dbnum);
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
@@ -157,7 +158,7 @@ typedef struct {
static helpEntry *helpEntries;
static int helpEntriesLen;
static sds cliVersion() {
static sds cliVersion(void) {
sds version;
version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
@@ -171,7 +172,7 @@ static sds cliVersion() {
return version;
}
static void cliInitHelp() {
static void cliInitHelp(void) {
int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
int groupslen = sizeof(commandGroups)/sizeof(char*);
int i, len, pos = 0;
@@ -210,7 +211,7 @@ static void cliOutputCommandHelp(struct commandHelp *help, int group) {
}
/* Print generic help. */
static void cliOutputGenericHelp() {
static void cliOutputGenericHelp(void) {
sds version = cliVersion();
printf(
"redis-cli %s\r\n"
@@ -320,8 +321,10 @@ static int cliSelect() {
reply = redisCommand(context,"SELECT %d",config.dbnum);
if (reply != NULL) {
int result = REDIS_OK;
if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;
freeReplyObject(reply);
return REDIS_OK;
return result;
}
return REDIS_ERR;
}
@@ -365,7 +368,7 @@ static int cliConnect(int force) {
return REDIS_OK;
}
static void cliPrintContextError() {
static void cliPrintContextError(void) {
if (context == NULL) return;
fprintf(stderr,"Error: %s\n",context->errstr);
}
@@ -514,8 +517,11 @@ static int cliReadReply(int output_raw_strings) {
int output = 1;
if (redisGetReply(context,&_reply) != REDIS_OK) {
if (config.shutdown)
if (config.shutdown) {
redisFree(context);
context = NULL;
return REDIS_OK;
}
if (config.interactive) {
/* Filter cases where we should reconnect */
if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
@@ -530,6 +536,8 @@ static int cliReadReply(int output_raw_strings) {
reply = (redisReply*)_reply;
config.last_cmd_type = reply->type;
/* Check if we need to connect to a different node and reissue the
* request. */
if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&
@@ -598,8 +606,11 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
(!strcasecmp(argv[1],"nodes") ||
!strcasecmp(argv[1],"info"))) ||
(argc == 2 && !strcasecmp(command,"client") &&
!strcasecmp(argv[1],"list")))
!strcasecmp(argv[1],"list")) ||
(argc == 3 && !strcasecmp(command,"latency") &&
!strcasecmp(argv[1],"graph")) ||
(argc == 2 && !strcasecmp(command,"latency") &&
!strcasecmp(argv[1],"doctor")))
{
output_raw = 1;
}
@@ -635,6 +646,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
printf("Entering slave output mode... (press Ctrl-C to quit)\n");
slaveMode();
config.slave_mode = 0;
free(argvlen);
return REDIS_ERR; /* Error = slaveMode lost connection to master */
}
@@ -646,6 +658,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
if (!strcasecmp(command,"select") && argc == 2) {
config.dbnum = atoi(argv[1]);
cliRefreshPrompt();
} else if (!strcasecmp(command,"auth") && argc == 2) {
cliSelect();
}
}
if (config.interval) usleep(config.interval);
@@ -720,6 +734,8 @@ static int parseOptions(int argc, char **argv) {
config.auth = argv[++i];
} else if (!strcmp(argv[i],"--raw")) {
config.output = OUTPUT_RAW;
} else if (!strcmp(argv[i],"--no-raw")) {
config.output = OUTPUT_STANDARD;
} else if (!strcmp(argv[i],"--csv")) {
config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) {
@@ -791,7 +807,7 @@ static sds readArgFromStdin(void) {
return arg;
}
static void usage() {
static void usage(void) {
sds version = cliVersion();
fprintf(stderr,
"redis-cli %s\n"
@@ -810,6 +826,7 @@ static void usage() {
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
" --csv Output in CSV format.\n"
" --latency Enter a special mode continuously sampling latency.\n"
" --latency-history Like --latency but tracking latency changes over time.\n"
@@ -858,8 +875,7 @@ static char **convertToSds(int count, char** args) {
return sds;
}
#define LINE_BUFLEN 4096
static void repl() {
static void repl(void) {
sds historyfile = NULL;
int history = 0;
char *line;
@@ -1149,7 +1165,7 @@ static void getRDB(void) {
while(payload) {
ssize_t nread, nwritten;
nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"I/O Error reading RDB payload from socket\n");
@@ -1232,7 +1248,7 @@ static void pipeMode(void) {
errors++;
} else if (eof && reply->type == REDIS_REPLY_STRING &&
reply->len == 20) {
/* Check if this is the reply to our final ECHO
/* Check if this is the reply to our final ECHO
* command. If so everything was received
* from the server. */
if (memcmp(reply->str,magic,20) == 0) {
@@ -1253,7 +1269,7 @@ static void pipeMode(void) {
/* Transfer current buffer to server. */
if (obuf_len != 0) {
ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);
if (nwritten == -1) {
if (errno != EAGAIN && errno != EINTR) {
fprintf(stderr, "Error writing to the server: %s\n",
@@ -1356,7 +1372,7 @@ static redisReply *sendScan(unsigned long long *it) {
/* Validate our types are correct */
assert(reply->element[0]->type == REDIS_REPLY_STRING);
assert(reply->element[1]->type == REDIS_REPLY_ARRAY);
/* Update iterator */
*it = atoi(reply->element[0]->str);
@@ -1368,7 +1384,7 @@ static int getDbSize(void) {
int size;
reply = redisCommand(context, "DBSIZE");
if(reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
fprintf(stderr, "Couldn't determine DBSIZE!\n");
exit(1);
@@ -1402,7 +1418,7 @@ static int toIntType(char *key, char *type) {
static void getKeyTypes(redisReply *keys, int *types) {
redisReply *reply;
int i;
unsigned int i;
/* Pipeline TYPE commands */
for(i=0;i<keys->elements;i++) {
@@ -1421,25 +1437,25 @@ static void getKeyTypes(redisReply *keys, int *types) {
exit(1);
}
types[i] = toIntType(keys->element[i]->str, reply->str);
types[i] = toIntType(keys->element[i]->str, reply->str);
freeReplyObject(reply);
}
}
static void getKeySizes(redisReply *keys, int *types,
unsigned long long *sizes)
static void getKeySizes(redisReply *keys, int *types,
unsigned long long *sizes)
{
redisReply *reply;
char *sizecmds[] = {"STRLEN","LLEN","SCARD","HLEN","ZCARD"};
int i;
unsigned int i;
/* Pipeline size commands */
for(i=0;i<keys->elements;i++) {
/* Skip keys that were deleted */
if(types[i]==TYPE_NONE)
if(types[i]==TYPE_NONE)
continue;
redisAppendCommand(context, "%s %s", sizecmds[types[i]],
redisAppendCommand(context, "%s %s", sizecmds[types[i]],
keys->element[i]->str);
}
@@ -1459,14 +1475,14 @@ static void getKeySizes(redisReply *keys, int *types,
} else if(reply->type != REDIS_REPLY_INTEGER) {
/* Theoretically the key could have been removed and
* added as a different type between TYPE and SIZE */
fprintf(stderr,
fprintf(stderr,
"Warning: %s on '%s' failed (may have changed type)\n",
sizecmds[types[i]], keys->element[i]->str);
sizes[i] = 0;
} else {
sizes[i] = reply->integer;
}
freeReplyObject(reply);
}
}
@@ -1478,7 +1494,8 @@ static void findBigKeys(void) {
char *typename[] = {"string","list","set","hash","zset"};
char *typeunit[] = {"bytes","items","members","fields","members"};
redisReply *reply, *keys;
int type, *types=NULL, arrsize=0, i;
unsigned int arrsize=0, i;
int type, *types=NULL;
double pct;
/* Total keys pre scanning */
@@ -1523,12 +1540,12 @@ static void findBigKeys(void) {
/* Retreive types and then sizes */
getKeyTypes(keys, types);
getKeySizes(keys, types, sizes);
/* Now update our stats */
for(i=0;i<keys->elements;i++) {
if((type = types[i]) == TYPE_NONE)
continue;
totalsize[type] += sizes[i];
counts[type]++;
totlen += keys->element[i]->len;
@@ -1548,7 +1565,7 @@ static void findBigKeys(void) {
}
/* Keep track of the biggest size for this type */
biggest[type] = sizes[i];
biggest[type] = sizes[i];
}
/* Update overall progress */
@@ -1561,7 +1578,7 @@ static void findBigKeys(void) {
if(sampled && (sampled %100) == 0 && config.interval) {
usleep(config.interval);
}
freeReplyObject(reply);
} while(it != 0);
@@ -1662,7 +1679,7 @@ void bytesToHuman(char *s, long long n) {
}
}
static void statMode() {
static void statMode(void) {
redisReply *reply;
long aux, requests = 0;
int i = 0;
@@ -1748,7 +1765,7 @@ static void statMode() {
* Scan mode
*--------------------------------------------------------------------------- */
static void scanMode() {
static void scanMode(void) {
redisReply *reply;
unsigned long long cur = 0;
@@ -1765,7 +1782,7 @@ static void scanMode() {
printf("ERROR: %s\n", reply->str);
exit(1);
} else {
int j;
unsigned int j;
cur = strtoull(reply->element[0]->str,NULL,10);
for (j = 0; j < reply->element[1]->elements; j++)
@@ -1836,11 +1853,15 @@ static void intrinsicLatencyMode(void) {
printf("Max latency so far: %lld microseconds.\n", max_latency);
}
double avg_us = (double)run_time/runs;
double avg_ns = avg_us * 10e3;
if (force_cancel_loop || end > test_end) {
printf("\n%lld total runs (avg %lld microseconds per run).\n",
runs, run_time/runs);
printf("Worst run took %.02fx times the average.\n",
(double) max_latency / (run_time/runs));
printf("\n%lld total runs "
"(avg latency: "
"%.4f microseconds / %.2f nanoseconds per run).\n",
runs, avg_us, avg_ns);
printf("Worst run took %.0fx longer than the average latency.\n",
max_latency / avg_us);
exit(0);
}
}
@@ -1879,6 +1900,8 @@ int main(int argc, char **argv) {
config.stdinarg = 0;
config.auth = NULL;
config.eval = NULL;
config.last_cmd_type = -1;
if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
config.output = OUTPUT_RAW;
else
+263 -106
View File
@@ -65,7 +65,6 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
/* Global vars */
struct redisServer server; /* server global state */
struct redisCommand *commandTable;
/* Our command table.
*
@@ -112,48 +111,52 @@ struct redisCommand *commandTable;
* server this data. Normally no command is accepted in this condition
* but just a few.
* M: Do not automatically propagate the command on MONITOR.
* F: Fast command: O(1) or O(log(N)) command that should never delay
* its execution as long as the kernel scheduler is giving us time.
* Note that commands that may trigger a DEL as a side effect (like SET)
* are not fast commands.
*/
struct redisCommand redisCommandTable[] = {
{"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
{"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},
{"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
{"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},
{"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0},
{"exists",existsCommand,2,"r",0,NULL,1,1,1,0,0},
{"strlen",strlenCommand,2,"rF",0,NULL,1,1,1,0,0},
{"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},
{"exists",existsCommand,2,"rF",0,NULL,1,1,1,0,0},
{"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},
{"getbit",getbitCommand,3,"r",0,NULL,1,1,1,0,0},
{"getbit",getbitCommand,3,"rF",0,NULL,1,1,1,0,0},
{"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},
{"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
{"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
{"incr",incrCommand,2,"wm",0,NULL,1,1,1,0,0},
{"decr",decrCommand,2,"wm",0,NULL,1,1,1,0,0},
{"incr",incrCommand,2,"wmF",0,NULL,1,1,1,0,0},
{"decr",decrCommand,2,"wmF",0,NULL,1,1,1,0,0},
{"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0},
{"rpush",rpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"lpush",lpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"rpushx",rpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
{"lpushx",lpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
{"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
{"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
{"rpushx",rpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"lpushx",lpushxCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
{"rpop",rpopCommand,2,"w",0,NULL,1,1,1,0,0},
{"lpop",lpopCommand,2,"w",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},
{"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,"r",0,NULL,1,1,1,0,0},
{"llen",llenCommand,2,"rF",0,NULL,1,1,1,0,0},
{"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},
{"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},
{"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},
{"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},
{"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},
{"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},
{"sadd",saddCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"srem",sremCommand,-3,"w",0,NULL,1,1,1,0,0},
{"smove",smoveCommand,4,"w",0,NULL,1,2,1,0,0},
{"sismember",sismemberCommand,3,"r",0,NULL,1,1,1,0,0},
{"scard",scardCommand,2,"r",0,NULL,1,1,1,0,0},
{"spop",spopCommand,2,"wRs",0,NULL,1,1,1,0,0},
{"sadd",saddCommand,-3,"wmF",0,NULL,1,1,1,0,0},
{"srem",sremCommand,-3,"wF",0,NULL,1,1,1,0,0},
{"smove",smoveCommand,4,"wF",0,NULL,1,2,1,0,0},
{"sismember",sismemberCommand,3,"rF",0,NULL,1,1,1,0,0},
{"scard",scardCommand,2,"rF",0,NULL,1,1,1,0,0},
{"spop",spopCommand,2,"wRsF",0,NULL,1,1,1,0,0},
{"srandmember",srandmemberCommand,-2,"rR",0,NULL,1,1,1,0,0},
{"sinter",sinterCommand,-2,"rS",0,NULL,1,-1,1,0,0},
{"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
@@ -163,9 +166,9 @@ struct redisCommand redisCommandTable[] = {
{"sdiffstore",sdiffstoreCommand,-3,"wm",0,NULL,1,-1,1,0,0},
{"smembers",sinterCommand,2,"rS",0,NULL,1,1,1,0,0},
{"sscan",sscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
{"zadd",zaddCommand,-4,"wm",0,NULL,1,1,1,0,0},
{"zincrby",zincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
{"zrem",zremCommand,-3,"w",0,NULL,1,1,1,0,0},
{"zadd",zaddCommand,-4,"wmF",0,NULL,1,1,1,0,0},
{"zincrby",zincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"zrem",zremCommand,-3,"wF",0,NULL,1,1,1,0,0},
{"zremrangebyscore",zremrangebyscoreCommand,4,"w",0,NULL,1,1,1,0,0},
{"zremrangebyrank",zremrangebyrankCommand,4,"w",0,NULL,1,1,1,0,0},
{"zremrangebylex",zremrangebylexCommand,4,"w",0,NULL,1,1,1,0,0},
@@ -176,58 +179,58 @@ struct redisCommand redisCommandTable[] = {
{"zrevrangebyscore",zrevrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
{"zrangebylex",zrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},
{"zrevrangebylex",zrevrangebylexCommand,-4,"r",0,NULL,1,1,1,0,0},
{"zcount",zcountCommand,4,"r",0,NULL,1,1,1,0,0},
{"zlexcount",zlexcountCommand,4,"r",0,NULL,1,1,1,0,0},
{"zcount",zcountCommand,4,"rF",0,NULL,1,1,1,0,0},
{"zlexcount",zlexcountCommand,4,"rF",0,NULL,1,1,1,0,0},
{"zrevrange",zrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
{"zcard",zcardCommand,2,"r",0,NULL,1,1,1,0,0},
{"zscore",zscoreCommand,3,"r",0,NULL,1,1,1,0,0},
{"zrank",zrankCommand,3,"r",0,NULL,1,1,1,0,0},
{"zrevrank",zrevrankCommand,3,"r",0,NULL,1,1,1,0,0},
{"zcard",zcardCommand,2,"rF",0,NULL,1,1,1,0,0},
{"zscore",zscoreCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
{"hset",hsetCommand,4,"wm",0,NULL,1,1,1,0,0},
{"hsetnx",hsetnxCommand,4,"wm",0,NULL,1,1,1,0,0},
{"hget",hgetCommand,3,"r",0,NULL,1,1,1,0,0},
{"hset",hsetCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0},
{"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},
{"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},
{"hincrby",hincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
{"hincrbyfloat",hincrbyfloatCommand,4,"wm",0,NULL,1,1,1,0,0},
{"hdel",hdelCommand,-3,"w",0,NULL,1,1,1,0,0},
{"hlen",hlenCommand,2,"r",0,NULL,1,1,1,0,0},
{"hincrby",hincrbyCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"hincrbyfloat",hincrbyfloatCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"hdel",hdelCommand,-3,"wF",0,NULL,1,1,1,0,0},
{"hlen",hlenCommand,2,"rF",0,NULL,1,1,1,0,0},
{"hkeys",hkeysCommand,2,"rS",0,NULL,1,1,1,0,0},
{"hvals",hvalsCommand,2,"rS",0,NULL,1,1,1,0,0},
{"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},
{"hexists",hexistsCommand,3,"r",0,NULL,1,1,1,0,0},
{"hexists",hexistsCommand,3,"rF",0,NULL,1,1,1,0,0},
{"hscan",hscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
{"incrby",incrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
{"decrby",decrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
{"incrbyfloat",incrbyfloatCommand,3,"wm",0,NULL,1,1,1,0,0},
{"incrby",incrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"decrby",decrbyCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"incrbyfloat",incrbyfloatCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
{"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
{"select",selectCommand,2,"rl",0,NULL,0,0,0,0,0},
{"move",moveCommand,3,"w",0,NULL,1,1,1,0,0},
{"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
{"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
{"expire",expireCommand,3,"w",0,NULL,1,1,1,0,0},
{"expireat",expireatCommand,3,"w",0,NULL,1,1,1,0,0},
{"pexpire",pexpireCommand,3,"w",0,NULL,1,1,1,0,0},
{"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0},
{"select",selectCommand,2,"rlF",0,NULL,0,0,0,0,0},
{"move",moveCommand,3,"wF",0,NULL,1,1,1,0,0},
{"rename",renameCommand,3,"w",0,NULL,1,2,1,0,0},
{"renamenx",renamenxCommand,3,"wF",0,NULL,1,2,1,0,0},
{"expire",expireCommand,3,"wF",0,NULL,1,1,1,0,0},
{"expireat",expireatCommand,3,"wF",0,NULL,1,1,1,0,0},
{"pexpire",pexpireCommand,3,"wF",0,NULL,1,1,1,0,0},
{"pexpireat",pexpireatCommand,3,"wF",0,NULL,1,1,1,0,0},
{"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},
{"scan",scanCommand,-2,"rR",0,NULL,0,0,0,0,0},
{"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"rslt",0,NULL,0,0,0,0,0},
{"ping",pingCommand,1,"rt",0,NULL,0,0,0,0,0},
{"echo",echoCommand,2,"r",0,NULL,0,0,0,0,0},
{"dbsize",dbsizeCommand,1,"rF",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"rsltF",0,NULL,0,0,0,0,0},
{"ping",pingCommand,-1,"rtF",0,NULL,0,0,0,0,0},
{"echo",echoCommand,2,"rF",0,NULL,0,0,0,0,0},
{"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"arlt",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"rR",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"rRF",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"rF",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"rsF",0,NULL,0,0,0,0,0},
{"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0},
{"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
{"discard",discardCommand,1,"rsF",0,NULL,0,0,0,0,0},
{"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
{"psync",syncCommand,3,"ars",0,NULL,0,0,0,0,0},
{"replconf",replconfCommand,-1,"arslt",0,NULL,0,0,0,0,0},
@@ -236,9 +239,9 @@ struct redisCommand redisCommandTable[] = {
{"sort",sortCommand,-2,"wm",0,NULL,1,1,1,0,0},
{"info",infoCommand,-1,"rlt",0,NULL,0,0,0,0,0},
{"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
{"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
{"ttl",ttlCommand,2,"rF",0,NULL,1,1,1,0,0},
{"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"wF",0,NULL,1,1,1,0,0},
{"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},
{"role",roleCommand,1,"last",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
@@ -247,28 +250,30 @@ struct redisCommand redisCommandTable[] = {
{"unsubscribe",unsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
{"punsubscribe",punsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"pltr",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"pltrF",0,NULL,0,0,0,0,0},
{"pubsub",pubsubCommand,-2,"pltrR",0,NULL,0,0,0,0,0},
{"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
{"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
{"watch",watchCommand,-2,"rsF",0,NULL,1,-1,1,0,0},
{"unwatch",unwatchCommand,1,"rsF",0,NULL,0,0,0,0,0},
{"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
{"migrate",migrateCommand,6,"aw",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0},
{"object",objectCommand,-2,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0},
{"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ars",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
{"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0},
{"time",timeCommand,1,"rR",0,NULL,0,0,0,0,0},
{"time",timeCommand,1,"rRF",0,NULL,0,0,0,0,0},
{"bitop",bitopCommand,-4,"wm",0,NULL,2,-1,1,0,0},
{"bitcount",bitcountCommand,-2,"r",0,NULL,1,1,1,0,0},
{"bitpos",bitposCommand,-3,"r",0,NULL,1,1,1,0,0},
{"command",commandCommand,0,"rlt",0,NULL,0,0,0,0,0},
{"pfselftest",pfselftestCommand,1,"r",0,NULL,0,0,0,0,0},
{"pfadd",pfaddCommand,-2,"wm",0,NULL,1,1,1,0,0},
{"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},
{"pfcount",pfcountCommand,-2,"w",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}
{"pfdebug",pfdebugCommand,-3,"w",0,NULL,0,0,0,0,0},
{"latency",latencyCommand,-2,"arslt",0,NULL,0,0,0,0,0}
};
/*============================ Utility functions ============================ */
@@ -701,8 +706,8 @@ void activeExpireCycle(int type) {
static int timelimit_exit = 0; /* Time limit hit in previous call? */
static long long last_fast_cycle = 0; /* When last fast cycle ran. */
unsigned int j, iteration = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
int j, iteration = 0;
int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
long long start = ustime(), timelimit;
if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
@@ -799,10 +804,11 @@ void activeExpireCycle(int type) {
* expire. So after a given amount of milliseconds return to the
* caller waiting for the other active expire cycle. */
iteration++;
if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */
(ustime()-start) > timelimit)
{
timelimit_exit = 1;
if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */
long long elapsed = ustime()-start;
latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);
if (elapsed > timelimit) timelimit_exit = 1;
}
if (timelimit_exit) return;
/* We don't repeat the cycle if there are less than 25% of keys
@@ -849,8 +855,7 @@ int clientsCronHandleTimeout(redisClient *c) {
!(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
!(c->flags & REDIS_MASTER) && /* no timeout for masters */
!(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */
dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */
listLength(c->pubsub_patterns) == 0 &&
!(c->flags & REDIS_PUBSUB) && /* no timeout for Pub/Sub clients */
(now - c->lastinteraction > server.maxidletime))
{
redisLog(REDIS_VERBOSE,"Closing idle client");
@@ -938,8 +943,8 @@ void databasesCron(void) {
* cron loop iteration. */
static unsigned int resize_db = 0;
static unsigned int rehash_db = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
unsigned int j;
int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
int j;
/* Don't test more DBs than we have. */
if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;
@@ -1085,7 +1090,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
int exitcode = WEXITSTATUS(statloc);
int bysignal = 0;
if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);
if (pid == server.rdb_child_pid) {
@@ -1291,7 +1296,7 @@ void createSharedObjects(void) {
shared.maxstring = createStringObject("maxstring",9);
}
void initServerConfig() {
void initServerConfig(void) {
int j;
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
@@ -1335,6 +1340,7 @@ void initServerConfig() {
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
server.aof_load_truncated = REDIS_DEFAULT_AOF_LOAD_TRUNCATED;
server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME);
@@ -1421,11 +1427,14 @@ void initServerConfig() {
server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop");
/* Slow log */
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
/* Latency monitor */
server.latency_monitor_threshold = REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD;
/* Debugging */
server.assert_failed = "<no assertion failed>";
server.assert_file = "<no file>";
@@ -1463,7 +1472,7 @@ void adjustOpenFilesLimit(void) {
* to the higher value supported less than maxfiles. */
f = maxfiles;
while(f > oldlimit) {
int decr_step = 16;
rlim_t decr_step = 16;
limit.rlim_cur = f;
limit.rlim_max = f;
@@ -1591,6 +1600,7 @@ void resetServerStats(void) {
server.stat_keyspace_misses = 0;
server.stat_keyspace_hits = 0;
server.stat_fork_time = 0;
server.stat_fork_rate = 0;
server.stat_rejected_conn = 0;
server.stat_sync_full = 0;
server.stat_sync_partial_ok = 0;
@@ -1601,7 +1611,7 @@ void resetServerStats(void) {
server.ops_sec_last_sample_ops = 0;
}
void initServer() {
void initServer(void) {
int j;
signal(SIGHUP, SIG_IGN);
@@ -1729,6 +1739,7 @@ void initServer() {
replicationScriptCacheInit();
scriptingInit();
slowlogInit();
latencyMonitorInit();
bioInit();
}
@@ -1756,6 +1767,7 @@ void populateCommandTable(void) {
case 'l': c->flags |= REDIS_CMD_LOADING; break;
case 't': c->flags |= REDIS_CMD_STALE; break;
case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;
case 'F': c->flags |= REDIS_CMD_FAST; break;
default: redisPanic("Unsupported command flag"); break;
}
f++;
@@ -1902,6 +1914,7 @@ void call(redisClient *c, int flags) {
c->cmd->proc(c);
duration = ustime()-start;
dirty = server.dirty-dirty;
if (dirty < 0) dirty = 0;
/* When EVAL is called loading the AOF we don't want commands called
* from Lua to go into the slowlog or to populate statistics. */
@@ -1920,8 +1933,12 @@ void call(redisClient *c, int flags) {
/* Log the command into the Slow log if needed, and populate the
* per-command statistics that we show in INFO commandstats. */
if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)
if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand) {
char *latency_event = (c->cmd->flags & REDIS_CMD_FAST) ?
"fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
}
if (flags & REDIS_CALL_STATS) {
c->cmd->microseconds += duration;
c->cmd->calls++;
@@ -2061,8 +2078,8 @@ int processCommand(redisClient *c) {
}
/* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
&&
if (c->flags & REDIS_PUBSUB &&
c->cmd->proc != pingCommand &&
c->cmd->proc != subscribeCommand &&
c->cmd->proc != unsubscribeCommand &&
c->cmd->proc != psubscribeCommand &&
@@ -2207,9 +2224,9 @@ int time_independent_strcmp(char *a, char *b) {
* a or b are fixed (our password) length, and the difference is only
* relative to the length of the user provided string, so no information
* leak is possible in the following two lines of code. */
int alen = strlen(a);
int blen = strlen(b);
int j;
unsigned int alen = strlen(a);
unsigned int blen = strlen(b);
unsigned int j;
int diff = 0;
/* We can't compare strings longer than our static buffers.
@@ -2246,8 +2263,29 @@ void authCommand(redisClient *c) {
}
}
/* The PING command. It works in a different way if the client is in
* in Pub/Sub mode. */
void pingCommand(redisClient *c) {
addReply(c,shared.pong);
/* The command takes zero or one arguments. */
if (c->argc > 2) {
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
c->cmd->name);
return;
}
if (c->flags & REDIS_PUBSUB) {
addReply(c,shared.mbulkhdr[2]);
addReplyBulkCBuffer(c,"pong",4);
if (c->argc == 1)
addReplyBulkCBuffer(c,"",0);
else
addReplyBulk(c,c->argv[1]);
} else {
if (c->argc == 1)
addReply(c,shared.pong);
else
addReplyBulk(c,c->argv[1]);
}
}
void echoCommand(redisClient *c) {
@@ -2265,6 +2303,78 @@ void timeCommand(redisClient *c) {
addReplyBulkLongLong(c,tv.tv_usec);
}
/* Helper function for addReplyCommand() to output flags. */
int addReplyCommandFlag(redisClient *c, struct redisCommand *cmd, int f, char *reply) {
if (cmd->flags & f) {
addReplyStatus(c, reply);
return 1;
}
return 0;
}
/* Output the representation of a Redis command. Used by the COMMAND command. */
void addReplyCommand(redisClient *c, struct redisCommand *cmd) {
if (!cmd) {
addReply(c, shared.nullbulk);
} else {
/* We are adding: command name, arg count, flags, first, last, offset */
addReplyMultiBulkLen(c, 6);
addReplyBulkCString(c, cmd->name);
addReplyLongLong(c, cmd->arity);
int flagcount = 0;
void *flaglen = addDeferredMultiBulkLength(c);
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_WRITE, "write");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_READONLY, "readonly");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_DENYOOM, "denyoom");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_ADMIN, "admin");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_PUBSUB, "pubsub");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_NOSCRIPT, "noscript");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_RANDOM, "random");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_SORT_FOR_SCRIPT,"sort_for_script");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_LOADING, "loading");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_STALE, "stale");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_SKIP_MONITOR, "skip_monitor");
flagcount += addReplyCommandFlag(c,cmd,REDIS_CMD_FAST, "fast");
if (cmd->getkeys_proc) {
addReplyStatus(c, "movablekeys");
flagcount += 1;
}
setDeferredMultiBulkLength(c, flaglen, flagcount);
addReplyLongLong(c, cmd->firstkey);
addReplyLongLong(c, cmd->lastkey);
addReplyLongLong(c, cmd->keystep);
}
}
/* COMMAND <subcommand> <args> */
void commandCommand(redisClient *c) {
dictIterator *di;
dictEntry *de;
if (c->argc == 1) {
addReplyMultiBulkLen(c, dictSize(server.commands));
di = dictGetIterator(server.commands);
while ((de = dictNext(di)) != NULL) {
addReplyCommand(c, dictGetVal(de));
}
dictReleaseIterator(di);
} else if (!strcasecmp(c->argv[1]->ptr, "info")) {
int i;
addReplyMultiBulkLen(c, c->argc-2);
for (i = 2; i < c->argc; i++) {
addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr));
}
} else if (!strcasecmp(c->argv[1]->ptr, "count") && c->argc == 2) {
addReplyLongLong(c, dictSize(server.commands));
} else {
addReplyError(c, "Unknown subcommand or wrong number of arguments.");
return;
}
}
/* Convert an amount of bytes into a human readable string in the form
* of 100B, 2G, 100M, 4K, and so forth. */
void bytesToHuman(char *s, unsigned long long n) {
@@ -2283,6 +2393,15 @@ void bytesToHuman(char *s, unsigned long long n) {
} else if (n < (1024LL*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024);
sprintf(s,"%.2fG",d);
} else if (n < (1024LL*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024);
sprintf(s,"%.2fT",d);
} else if (n < (1024LL*1024*1024*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024*1024*1024);
sprintf(s,"%.2fP",d);
} else {
/* Let's hope we never need this */
sprintf(s,"%lluB",n);
}
}
@@ -2298,10 +2417,9 @@ sds genRedisInfoString(char *section) {
int allsections = 0, defsections = 0;
int sections = 0;
if (section) {
allsections = strcasecmp(section,"all") == 0;
defsections = strcasecmp(section,"default") == 0;
}
if (section == NULL) section = "default";
allsections = strcasecmp(section,"all") == 0;
defsections = strcasecmp(section,"default") == 0;
getrusage(RUSAGE_SELF, &self_ru);
getrusage(RUSAGE_CHILDREN, &c_ru);
@@ -2315,7 +2433,7 @@ sds genRedisInfoString(char *section) {
if (server.sentinel_mode) mode = "sentinel";
else mode = "standalone";
if (sections++) info = sdscat(info,"\r\n");
if (call_uname) {
@@ -2749,6 +2867,7 @@ void monitorCommand(redisClient *c) {
int freeMemoryIfNeeded(void) {
size_t mem_used, mem_tofree, mem_freed;
int slaves = listLength(server.slaves);
mstime_t latency;
/* Remove the size of slaves output buffers and AOF buffer from the
* count of used memory. */
@@ -2781,6 +2900,7 @@ int freeMemoryIfNeeded(void) {
/* Compute how much memory we need to free. */
mem_tofree = mem_used - server.maxmemory;
mem_freed = 0;
latencyStartMonitor(latency);
while (mem_freed < mem_tofree) {
int j, k, keys_freed = 0;
@@ -2884,8 +3004,14 @@ int freeMemoryIfNeeded(void) {
if (slaves) flushSlavesOutputBuffers();
}
}
if (!keys_freed) return REDIS_ERR; /* nothing to free... */
if (!keys_freed) {
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("eviction-cycle",latency);
return REDIS_ERR; /* nothing to free... */
}
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("eviction-cycle",latency);
return REDIS_OK;
}
@@ -2939,7 +3065,7 @@ void daemonize(void) {
}
}
void version() {
void version(void) {
printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d build=%llx\n",
REDIS_VERSION,
redisGitSHA1(),
@@ -2950,7 +3076,7 @@ void version() {
exit(0);
}
void usage() {
void usage(void) {
fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf] [options]\n");
fprintf(stderr," ./redis-server - (read config from stdin)\n");
fprintf(stderr," ./redis-server -v or --version\n");
@@ -2986,10 +3112,33 @@ void redisAsciiArt(void) {
zfree(buf);
}
static void sigtermHandler(int sig) {
REDIS_NOTUSED(sig);
static void sigShutdownHandler(int sig) {
char *msg;
redisLogFromHandler(REDIS_WARNING,"Received SIGTERM, scheduling shutdown...");
switch (sig) {
case SIGINT:
msg = "Received SIGINT scheduling shutdown...";
break;
case SIGTERM:
msg = "Received SIGTERM scheduling shutdown...";
break;
default:
msg = "Received shutdown signal, scheduling shutdown...";
};
/* SIGINT is often delivered via Ctrl+C in an interactive session.
* If we receive the signal the second time, we interpret this as
* the user really wanting to quit ASAP without waiting to persist
* on disk. */
if (server.shutdown_asap && sig == SIGINT) {
redisLogFromHandler(REDIS_WARNING, "You insist... exiting now.");
rdbRemoveTempFile(getpid());
exit(1); /* Exit with an error since this was not a clean shutdown. */
} else if (server.loading) {
exit(0);
}
redisLogFromHandler(REDIS_WARNING, msg);
server.shutdown_asap = 1;
}
@@ -3000,8 +3149,9 @@ void setupSignalHandlers(void) {
* Otherwise, sa_handler is used. */
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sigtermHandler;
act.sa_handler = sigShutdownHandler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
#ifdef HAVE_BACKTRACE
sigemptyset(&act.sa_mask);
@@ -3127,6 +3277,13 @@ int main(int argc, char **argv) {
}
j++;
}
if (server.sentinel_mode && configfile && *configfile == '-') {
redisLog(REDIS_WARNING,
"Sentinel config from STDIN not allowed.");
redisLog(REDIS_WARNING,
"Sentinel needs config file on disk to save state. Exiting...");
exit(1);
}
if (configfile) server.configfile = getAbsolutePath(configfile);
resetServerSaveParams();
loadServerConfig(configfile,options);
+29 -10
View File
@@ -51,6 +51,8 @@
#include <lua.h>
#include <signal.h>
typedef long long mstime_t; /* millisecond time type. */
#include "ae.h" /* Event driven programming library */
#include "sds.h" /* Dynamic safe strings */
#include "dict.h" /* Hash tables */
@@ -61,6 +63,8 @@
#include "intset.h" /* Compact integer set structure */
#include "version.h" /* Version macro */
#include "util.h" /* Misc functions useful in many places */
#include "latency.h" /* Latency monitor API */
#include "sparkline.h" /* ASII graphs API */
/* Error codes */
#define REDIS_OK 0
@@ -69,7 +73,7 @@
/* Static server configuration */
#define REDIS_DEFAULT_HZ 10 /* Time interrupt calls/sec. */
#define REDIS_MIN_HZ 1
#define REDIS_MAX_HZ 500
#define REDIS_MAX_HZ 500
#define REDIS_SERVERPORT 6379 /* TCP port */
#define REDIS_TCP_BACKLOG 511 /* TCP listen backlog */
#define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */
@@ -116,6 +120,7 @@
#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 3
#define REDIS_DEFAULT_AOF_FILENAME "appendonly.aof"
#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
#define REDIS_DEFAULT_AOF_LOAD_TRUNCATED 1
#define REDIS_DEFAULT_ACTIVE_REHASHING 1
#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
#define REDIS_DEFAULT_MIN_SLAVES_TO_WRITE 0
@@ -124,6 +129,7 @@
#define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */
#define REDIS_BINDADDR_MAX 16
#define REDIS_MIN_RESERVED_FDS 32
#define REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD 0
#define ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 20 /* Loopkups per loop. */
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds */
@@ -161,6 +167,8 @@
#define REDIS_CMD_LOADING 512 /* "l" flag */
#define REDIS_CMD_STALE 1024 /* "t" flag */
#define REDIS_CMD_SKIP_MONITOR 2048 /* "M" flag */
#define REDIS_CMD_ASKING 4096 /* "k" flag */
#define REDIS_CMD_FAST 8192 /* "F" flag */
/* Object types */
#define REDIS_STRING 0
@@ -232,6 +240,8 @@
#define REDIS_FORCE_AOF (1<<14) /* Force AOF propagation of current cmd. */
#define REDIS_FORCE_REPL (1<<15) /* Force replication of current cmd. */
#define REDIS_PRE_PSYNC (1<<16) /* Instance don't understand PSYNC. */
#define REDIS_READONLY (1<<17) /* Cluster client is in read-only state. */
#define REDIS_PUBSUB (1<<18) /* Client is in Pub/Sub mode. */
/* Client request types */
#define REDIS_REQ_INLINE 1
@@ -375,8 +385,6 @@
* Data types
*----------------------------------------------------------------------------*/
typedef long long mstime_t; /* millisecond time type. */
/* A redis object, that is a type able to hold a string / list / set */
/* The actual Redis Object */
@@ -575,6 +583,12 @@ typedef struct redisOpArray {
* Global server state
*----------------------------------------------------------------------------*/
/* AIX defines hz to __hz, we don't use this define and in order to allow
* Redis build on AIX we need to undef it. */
#ifdef _AIX
#undef hz
#endif
struct redisServer {
/* General */
char *configfile; /* Absolute config file path, or NULL */
@@ -627,6 +641,7 @@ struct redisServer {
long long stat_keyspace_misses; /* Number of failed lookups of keys */
size_t stat_peak_memory; /* Max used memory record */
long long stat_fork_time; /* Time needed to perform latest fork() */
double stat_fork_rate; /* Fork rate in GB/sec. */
long long stat_rejected_conn; /* Clients rejected because of maxclients */
long long stat_sync_full; /* Number of full resyncs with slaves. */
long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */
@@ -675,6 +690,7 @@ struct redisServer {
int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */
int aof_last_write_status; /* REDIS_OK or REDIS_ERR */
int aof_last_write_errno; /* Valid if aof_last_write_status is ERR */
int aof_load_truncated; /* Don't stop on unexpected AOF EOF. */
/* RDB persistence */
long long dirty; /* Changes to DB from the last save */
long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */
@@ -740,9 +756,9 @@ struct redisServer {
/* Replication script cache. */
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */
int repl_scriptcache_size; /* Max number of elements. */
unsigned int repl_scriptcache_size; /* Max number of elements. */
/* Limits */
int maxclients; /* Max number of simultaneous clients */
unsigned int maxclients; /* Max number of simultaneous clients */
unsigned long long maxmemory; /* Max number of memory bytes to use */
int maxmemory_policy; /* Policy for key eviction */
int maxmemory_samples; /* Pricision of random sampling */
@@ -786,6 +802,9 @@ struct redisServer {
int lua_timedout; /* True if we reached the time limit for script
execution. */
int lua_kill; /* Kill the script if true. */
/* Latency monitor */
long long latency_monitor_threshold;
dict *latency_events;
/* Assert & bug reporting */
char *assert_failed;
char *assert_file;
@@ -909,11 +928,10 @@ void freeClient(redisClient *c);
void freeClientAsync(redisClient *c);
void resetClient(redisClient *c);
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
void addReply(redisClient *c, robj *obj);
void *addDeferredMultiBulkLength(redisClient *c);
void setDeferredMultiBulkLength(redisClient *c, void *node, long length);
void addReplySds(redisClient *c, sds s);
void processInputBuffer(redisClient *c);
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);
@@ -921,7 +939,6 @@ void addReplyBulk(redisClient *c, robj *obj);
void addReplyBulkCString(redisClient *c, char *s);
void addReplyBulkCBuffer(redisClient *c, void *p, size_t len);
void addReplyBulkLongLong(redisClient *c, long long ll);
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void addReply(redisClient *c, robj *obj);
void addReplySds(redisClient *c, sds s);
void addReplyError(redisClient *c, char *err);
@@ -1114,7 +1131,7 @@ void redisLog(int level, const char *fmt, ...);
#endif
void redisLogRaw(int level, const char *msg);
void redisLogFromHandler(int level, const char *msg);
void usage();
void usage(void);
void updateDictResizePolicy(void);
int htNeedsResize(dict *dict);
void oom(const char *msg);
@@ -1173,7 +1190,7 @@ sds keyspaceEventsFlagsToString(int flags);
/* Configuration */
void loadServerConfig(char *filename, char *options);
void appendServerSaveParams(time_t seconds, int changes);
void resetServerSaveParams();
void resetServerSaveParams(void);
struct rewriteConfigState; /* Forward declaration to export API. */
void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force);
int rewriteConfig(char *path);
@@ -1232,6 +1249,7 @@ uint64_t redisBuildId(void);
void authCommand(redisClient *c);
void pingCommand(redisClient *c);
void echoCommand(redisClient *c);
void commandCommand(redisClient *c);
void setCommand(redisClient *c);
void setnxCommand(redisClient *c);
void setexCommand(redisClient *c);
@@ -1380,6 +1398,7 @@ void pfaddCommand(redisClient *c);
void pfcountCommand(redisClient *c);
void pfmergeCommand(redisClient *c);
void pfdebugCommand(redisClient *c);
void latencyCommand(redisClient *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
+1 -1
View File
@@ -27,7 +27,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Every time the Redis Git SHA1 or Dirty status changes only this small
/* Every time the Redis Git SHA1 or Dirty status changes only this small
* file is recompiled, as we access this information in all the other
* files using this functions. */
+2 -2
View File
@@ -198,7 +198,7 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
long objlen = stringObjectLen(argv[j]);
/* We need to feed the buffer with the object as a bulk reply
* not just as a plain string, so create the $..CRLF payload len
* not just as a plain string, so create the $..CRLF payload len
* ad add the final CRLF */
aux[0] = '$';
len = ll2string(aux+1,sizeof(aux)-1,objlen);
@@ -1620,7 +1620,7 @@ void replicationCron(void) {
if (server.masterhost && server.master &&
!(server.master->flags & REDIS_PRE_PSYNC))
replicationSendAck();
/* If we have attached slaves, PING them from time to time.
* So slaves can implement an explicit timeout to masters, and will
* be able to detect a link disconnection even if the TCP connection
+6 -4
View File
@@ -212,7 +212,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
static robj **argv = NULL;
static int argv_size = 0;
static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE];
static int cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
/* Require at least one argument */
if (argc == 0) {
@@ -222,9 +222,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
/* Build the arguments vector */
if (!argv) {
argv = zmalloc(sizeof(robj*)*argc);
} else if (argv_size < argc) {
if (argv_size < argc) {
argv = zrealloc(argv,sizeof(robj*)*argc);
argv_size = argc;
}
@@ -401,6 +399,7 @@ cleanup:
if (c->argv != argv) {
zfree(c->argv);
argv = NULL;
argv_size = 0;
}
if (raise_error) {
@@ -909,6 +908,9 @@ void evalGenericCommand(redisClient *c, int evalsha) {
if (numkeys > (c->argc - 3)) {
addReplyError(c,"Number of keys can't be greater than number of args");
return;
} else if (numkeys < 0) {
addReplyError(c,"Number of keys can't be negative");
return;
}
/* We obtain the script SHA1, then check if this function is already
+7 -4
View File
@@ -123,7 +123,7 @@ void sdsclear(sds s) {
/* Enlarge the free space at the end of the sds string so that the caller
* is sure that after calling this function can overwrite up to addlen
* bytes after the end of the string, plus one more byte for nul term.
*
*
* Note: this does not change the *length* of the sds string as returned
* by sdslen(), but only the free buffer space we have. */
sds sdsMakeRoomFor(sds s, size_t addlen) {
@@ -200,10 +200,12 @@ size_t sdsAllocSize(sds s) {
void sdsIncrLen(sds s, int incr) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
assert(sh->free >= incr);
if (incr >= 0)
assert(sh->free >= (unsigned int)incr);
else
assert(sh->len >= (unsigned int)(-incr));
sh->len += incr;
sh->free -= incr;
assert(sh->free >= 0);
s[sh->len] = '\0';
}
@@ -388,6 +390,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
va_end(ap);
if (buf[buflen-2] != '\0') {
if (buf != staticbuf) zfree(buf);
buflen *= 2;
@@ -457,7 +460,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
i = initlen; /* Position of the next byte to write to dest str. */
while(*f) {
char next, *str;
int l;
unsigned int l;
long long num;
unsigned long long unum;
+2 -2
View File
@@ -39,8 +39,8 @@
typedef char *sds;
struct sdshdr {
int len;
int free;
unsigned int len;
unsigned int free;
char buf[];
};
+66 -18
View File
@@ -159,7 +159,7 @@ typedef struct sentinelRedisInstance {
/* Master specific. */
dict *sentinels; /* Other sentinels monitoring the same master. */
dict *slaves; /* Slaves for this master instance. */
int quorum; /* Number of sentinels that need to agree on failure. */
unsigned int quorum;/* Number of sentinels that need to agree on failure. */
int parallel_syncs; /* How many slaves to reconfigure at same time. */
char *auth_pass; /* Password to use for AUTH against master & slaves. */
@@ -203,6 +203,10 @@ struct sentinelState {
mstime_t tilt_start_time; /* When TITL started. */
mstime_t previous_time; /* Last time we ran the time handler. */
list *scripts_queue; /* Queue of user scripts to execute. */
char *announce_ip; /* IP addr that is gossiped to other sentinels if
not NULL. */
int announce_port; /* Port that is gossiped to other sentinels if
non zero. */
} sentinel;
/* A script execution job. */
@@ -345,6 +349,7 @@ int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
void releaseSentinelRedisInstance(sentinelRedisInstance *ri);
void dictInstancesValDestructor (void *privdata, void *obj) {
REDIS_NOTUSED(privdata);
releaseSentinelRedisInstance(obj);
}
@@ -403,7 +408,7 @@ void initSentinelConfig(void) {
/* Perform the Sentinel mode initialization. */
void initSentinel(void) {
int j;
unsigned int j;
/* Remove usual Redis commands from the command table, then just add
* the SENTINEL command. */
@@ -424,6 +429,8 @@ void initSentinel(void) {
sentinel.previous_time = mstime();
sentinel.running_scripts = 0;
sentinel.scripts_queue = listCreate();
sentinel.announce_ip = NULL;
sentinel.announce_port = 0;
}
/* This function gets called when the server is in Sentinel mode, started,
@@ -455,19 +462,19 @@ void sentinelIsRunning(void) {
* EINVAL: Invalid port number.
*/
sentinelAddr *createSentinelAddr(char *hostname, int port) {
char buf[32];
char ip[REDIS_IP_STR_LEN];
sentinelAddr *sa;
if (port <= 0 || port > 65535) {
errno = EINVAL;
return NULL;
}
if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) {
if (anetResolve(NULL,hostname,ip,sizeof(ip)) == ANET_ERR) {
errno = ENOENT;
return NULL;
}
sa = zmalloc(sizeof(*sa));
sa->ip = sdsnew(buf);
sa->ip = sdsnew(ip);
sa->port = port;
return sa;
}
@@ -1424,6 +1431,13 @@ char *sentinelHandleConfiguration(char **argv, int argc) {
return "Wrong hostname or port for sentinel.";
}
if (argc == 5) si->runid = sdsnew(argv[4]);
} else if (!strcasecmp(argv[0],"announce-ip") && argc == 2) {
/* announce-ip <ip-address> */
if (strlen(argv[1]))
sentinel.announce_ip = sdsnew(argv[1]);
} else if (!strcasecmp(argv[0],"announce-port") && argc == 2) {
/* announce-port <port> */
sentinel.announce_port = atoi(argv[1]);
} else {
return "Unrecognized sentinel configuration statement.";
}
@@ -1555,6 +1569,20 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
"sentinel current-epoch %llu", (unsigned long long) sentinel.current_epoch);
rewriteConfigRewriteLine(state,"sentinel",line,1);
/* sentinel announce-ip. */
if (sentinel.announce_ip) {
line = sdsnew("sentinel announce-ip ");
line = sdscatrepr(line, sentinel.announce_ip, sdslen(sentinel.announce_ip));
rewriteConfigRewriteLine(state,"sentinel",line,1);
}
/* sentinel announce-port. */
if (sentinel.announce_port) {
line = sdscatprintf(sdsempty(),"sentinel announce-port %d",
sentinel.announce_port);
rewriteConfigRewriteLine(state,"sentinel",line,1);
}
dictReleaseIterator(di);
}
@@ -1634,6 +1662,7 @@ void sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) {
}
void sentinelDisconnectCallback(const redisAsyncContext *c, int status) {
REDIS_NOTUSED(status);
sentinelDisconnectInstanceFromContext(c);
}
@@ -1998,6 +2027,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--;
if (!reply || !ri) return;
@@ -2012,6 +2042,8 @@ void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata
* value of the command but its effects directly. */
void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
REDIS_NOTUSED(reply);
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--;
}
@@ -2019,6 +2051,7 @@ void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privd
void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--;
if (!reply || !ri) return;
@@ -2057,6 +2090,7 @@ void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata
void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--;
if (!reply || !ri) return;
@@ -2166,6 +2200,7 @@ cleanup:
void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
redisReply *r;
REDIS_NOTUSED(privdata);
if (!reply || !ri) return;
r = reply;
@@ -2205,18 +2240,30 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
char ip[REDIS_IP_STR_LEN];
char payload[REDIS_IP_STR_LEN+1024];
int retval;
char *announce_ip;
int announce_port;
sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;
sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);
/* Try to obtain our own IP address. */
if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1) return REDIS_ERR;
if (ri->flags & SRI_DISCONNECTED) return REDIS_ERR;
/* Use the specified announce address if specified, otherwise try to
* obtain our own IP address. */
if (sentinel.announce_ip) {
announce_ip = sentinel.announce_ip;
} else {
if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1)
return REDIS_ERR;
announce_ip = ip;
}
announce_port = sentinel.announce_port ?
sentinel.announce_port : server.port;
/* Format and send the Hello message. */
snprintf(payload,sizeof(payload),
"%s,%d,%s,%llu," /* Info about this sentinel. */
"%s,%s,%d,%llu", /* Info about current master. */
ip, server.port, server.runid,
announce_ip, announce_port, server.runid,
(unsigned long long) sentinel.current_epoch,
/* --- */
master->name,master_addr->ip,master_addr->port,
@@ -2559,7 +2606,7 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,
{
sentinelRedisInstance *ri;
ri = dictFetchValue(sentinel.masters,c->argv[2]->ptr);
ri = dictFetchValue(sentinel.masters,name->ptr);
if (!ri) {
addReplyError(c,"No such master with that name");
return NULL;
@@ -2682,7 +2729,7 @@ void sentinelCommand(redisClient *c) {
/* SENTINEL MONITOR <name> <ip> <port> <quorum> */
sentinelRedisInstance *ri;
long quorum, port;
char buf[32];
char ip[REDIS_IP_STR_LEN];
if (c->argc != 6) goto numargserr;
if (getLongFromObjectOrReply(c,c->argv[5],&quorum,"Invalid quorum")
@@ -2692,7 +2739,7 @@ void sentinelCommand(redisClient *c) {
/* Make sure the IP field is actually a valid IP before passing it
* to createSentinelRedisInstance(), otherwise we may trigger a
* DNS lookup at runtime. */
if (anetResolveIP(NULL,c->argv[3]->ptr,buf,sizeof(buf)) == ANET_ERR) {
if (anetResolveIP(NULL,c->argv[3]->ptr,ip,sizeof(ip)) == ANET_ERR) {
addReplyError(c,"Invalid IP address specified");
return;
}
@@ -2997,7 +3044,7 @@ void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {
void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {
dictIterator *di;
dictEntry *de;
int quorum = 0, odown = 0;
unsigned int quorum = 0, odown = 0;
if (master->flags & SRI_S_DOWN) {
/* Is down for enough sentinels? */
@@ -3034,6 +3081,7 @@ void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {
void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) {
sentinelRedisInstance *ri = c->data;
redisReply *r;
REDIS_NOTUSED(privdata);
if (ri) ri->pending_commands--;
if (!reply || !ri) return;
@@ -3057,7 +3105,7 @@ void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *p
/* If the runid in the reply is not "*" the Sentinel actually
* replied with a vote. */
sdsfree(ri->leader);
if (ri->leader_epoch != r->element[2]->integer)
if ((long long)ri->leader_epoch != r->element[2]->integer)
redisLog(REDIS_WARNING,
"%s voted for %s %llu", ri->name,
r->element[1]->str,
@@ -3176,9 +3224,9 @@ int sentinelLeaderIncr(dict *counters, char *runid) {
/* Scan all the Sentinels attached to this master to check if there
* is a leader for the specified epoch.
*
* To be a leader for a given epoch, we should have the majorify of
* the Sentinels we know that reported the same instance as
* leader for the same epoch. */
* To be a leader for a given epoch, we should have the majority of
* the Sentinels we know (ever seen since the last SENTINEL RESET) that
* reported the same instance as leader for the same epoch. */
char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
dict *counters;
dictIterator *di;
@@ -3192,13 +3240,14 @@ char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
redisAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS));
counters = dictCreate(&leaderVotesDictType,NULL);
voters = dictSize(master->sentinels)+1; /* All the other sentinels and me. */
/* Count other sentinels votes */
di = dictGetIterator(master->sentinels);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch)
sentinelLeaderIncr(counters,ri->leader);
voters++;
}
dictReleaseIterator(di);
@@ -3232,7 +3281,6 @@ char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) {
winner = myvote;
}
}
voters++; /* Anyway, count me as one of the voters. */
voters_quorum = voters/2+1;
if (winner && (max_votes < voters_quorum || max_votes < master->quorum))
+175
View File
@@ -0,0 +1,175 @@
/* sparkline.c -- ASCII Sparklines
* This code is modified from http://github.com/antirez/aspark and adapted
* in order to return SDS strings instead of outputting directly to
* the terminal.
*
* ---------------------------------------------------------------------------
*
* Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@gmail.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.
*
* 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 "redis.h"
#include <math.h>
/* This is the charset used to display the graphs, but multiple rows are used
* to increase the resolution. */
static char charset[] = "_-`";
static char charset_fill[] = "_o#";
static int charset_len = sizeof(charset)-1;
static int label_margin_top = 1;
/* ----------------------------------------------------------------------------
* Sequences are arrays of samples we use to represent data to turn
* into sparklines. This is the API in order to generate a sparkline:
*
* struct sequence *seq = createSparklineSequence();
* sparklineSequenceAddSample(seq, 10, NULL);
* sparklineSequenceAddSample(seq, 20, NULL);
* sparklineSequenceAddSample(seq, 30, "last sample label");
* sds output = sparklineRender(seq, 80, 4);
* freeSparklineSequence(seq);
* ------------------------------------------------------------------------- */
/* Create a new sequence. */
struct sequence *createSparklineSequence(void) {
struct sequence *seq = zmalloc(sizeof(*seq));
seq->length = 0;
seq->samples = NULL;
return seq;
}
/* Add a new sample into a sequence. */
void sparklineSequenceAddSample(struct sequence *seq, double value, char *label) {
if (seq->length == 0) {
seq->min = seq->max = value;
} else {
if (value < seq->min) seq->min = value;
else if (value > seq->max) seq->max = value;
}
seq->samples = zrealloc(seq->samples,sizeof(struct sample)*(seq->length+1));
seq->samples[seq->length].value = value;
seq->samples[seq->length].label = label;
seq->length++;
if (label) seq->labels++;
}
/* Free a sequence. */
void freeSparklineSequence(struct sequence *seq) {
int j;
for (j = 0; j < seq->length; j++)
zfree(seq->samples[j].label);
zfree(seq->samples);
zfree(seq);
}
/* ----------------------------------------------------------------------------
* ASCII rendering of sequence
* ------------------------------------------------------------------------- */
/* Render part of a sequence, so that render_sequence() call call this function
* with differnent parts in order to create the full output without overflowing
* the current terminal columns. */
sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) {
int j;
double relmax = seq->max - seq->min;
int steps = charset_len*rows;
int row = 0;
char *chars = zmalloc(len);
int loop = 1;
int opt_fill = flags & SPARKLINE_FILL;
int opt_log = flags & SPARKLINE_LOG_SCALE;
if (opt_log) {
relmax = log(relmax+1);
} else if (relmax == 0) {
relmax = 1;
}
while(loop) {
loop = 0;
memset(chars,' ',len);
for (j = 0; j < len; j++) {
struct sample *s = &seq->samples[j+offset];
double relval = s->value - seq->min;
int step;
if (opt_log) relval = log(relval+1);
step = (int) (relval*steps)/relmax;
if (step < 0) step = 0;
if (step >= steps) step = steps-1;
if (row < rows) {
/* Print the character needed to create the sparkline */
int charidx = step-((rows-row-1)*charset_len);
loop = 1;
if (charidx >= 0 && charidx < charset_len) {
chars[j] = opt_fill ? charset_fill[charidx] :
charset[charidx];
} else if(opt_fill && charidx >= charset_len) {
chars[j] = '|';
}
} else {
/* Labels spacing */
if (seq->labels && row-rows < label_margin_top) {
loop = 1;
break;
}
/* Print the label if needed. */
if (s->label) {
int label_len = strlen(s->label);
int label_char = row - rows - label_margin_top;
if (label_len > label_char) {
loop = 1;
chars[j] = s->label[label_char];
}
}
}
}
if (loop) {
row++;
output = sdscatlen(output,chars,len);
output = sdscatlen(output,"\n",1);
}
}
zfree(chars);
return output;
}
/* Turn a sequence into its ASCII representation */
sds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags) {
int j;
for (j = 0; j < seq->length; j += columns) {
int sublen = (seq->length-j) < columns ? (seq->length-j) : columns;
if (j != 0) output = sdscatlen(output,"\n",1);
output = sparklineRenderRange(output, seq, rows, j, sublen, flags);
}
return output;
}
+56
View File
@@ -0,0 +1,56 @@
/* sparkline.h -- ASCII Sparklines header file
*
* ---------------------------------------------------------------------------
*
* Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@gmail.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.
*
* 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.
*/
#ifndef __SPARKLINE_H
#define __SPARKLINE_H
/* A sequence is represented of many "samples" */
struct sample {
double value;
char *label;
};
struct sequence {
int length;
int labels;
struct sample *samples;
double min, max;
};
#define SPARKLINE_NO_FLAGS 0
#define SPARKLINE_FILL 1 /* Fill the area under the curve. */
#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */
struct sequence *createSparklineSequence(void);
void sparklineSequenceAddSample(struct sequence *seq, double value, char *label);
void freeSparklineSequence(struct sequence *seq);
sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags);
sds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags);
#endif /* __SPARKLINE_H */
+1 -1
View File
@@ -119,7 +119,7 @@ ssize_t syncRead(int fd, char *ptr, ssize_t size, long long timeout) {
/* Read a line making sure that every char will not require more than 'timeout'
* milliseconds to be read.
*
*
* On success the number of bytes read is returned, otherwise -1.
* On success the string is always correctly terminated with a 0 byte. */
ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout) {
+1 -1
View File
@@ -1015,7 +1015,7 @@ void handleClientsBlockedOnLists(void) {
}
}
}
if (listTypeLength(o) == 0) dbDelete(rl->db,rl->key);
/* We don't call signalModifiedKey() as it was already called
* when an element was pushed on the list. */
+1 -1
View File
@@ -515,7 +515,7 @@ void srandmemberWithCountCommand(redisClient *c) {
size--;
}
}
/* CASE 4: We have a big set compared to the requested number of elements.
* In this case we can simply get random elements from the set and add
* to the temporary set, trying to eventually get enough unique elements
+6 -6
View File
@@ -69,7 +69,7 @@ void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *ex
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
return;
if (milliseconds <= 0) {
addReplyError(c,"invalid expire time in SETEX");
addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
return;
}
if (unit == UNIT_SECONDS) milliseconds *= 1000;
@@ -231,13 +231,13 @@ void setrangeCommand(redisClient *c) {
void getrangeCommand(redisClient *c) {
robj *o;
long start, end;
long long start, end;
char *str, llbuf[32];
size_t strlen;
if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
return;
if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
@@ -255,11 +255,11 @@ void getrangeCommand(redisClient *c) {
if (end < 0) end = strlen+end;
if (start < 0) start = 0;
if (end < 0) end = 0;
if ((unsigned)end >= strlen) end = strlen-1;
if ((unsigned long long)end >= strlen) end = strlen-1;
/* Precondition: end >= 0 && end < strlen, so the only condition where
* nothing can be returned is: start > end. */
if (start > end) {
if (start > end || strlen == 0) {
addReply(c,shared.emptybulk);
} else {
addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
+60 -35
View File
@@ -205,8 +205,6 @@ int zslDelete(zskiplist *zsl, double score, robj *obj) {
zslDeleteNode(zsl, x, update);
zslFreeNode(x);
return 1;
} else {
return 0; /* not found */
}
return 0; /* not found */
}
@@ -1169,7 +1167,7 @@ void zsetConvert(robj *zobj, int encoding) {
}
/*-----------------------------------------------------------------------------
* Sorted set commands
* Sorted set commands
*----------------------------------------------------------------------------*/
/* This generic command implements both ZADD and ZINCRBY. */
@@ -1980,50 +1978,77 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
zuiClearIterator(&src[0]);
}
} else if (op == REDIS_OP_UNION) {
dict *accumulator = dictCreate(&setDictType,NULL);
dictIterator *di;
dictEntry *de;
double score;
if (setnum) {
/* Our union is at least as large as the largest set.
* Resize the dictionary ASAP to avoid useless rehashing. */
dictExpand(accumulator,zuiLength(&src[setnum-1]));
}
/* Step 1: Create a dictionary of elements -> aggregated-scores
* by iterating one sorted set after the other. */
for (i = 0; i < setnum; i++) {
if (zuiLength(&src[i]) == 0)
continue;
if (zuiLength(&src[i]) == 0) continue;
zuiInitIterator(&src[i]);
while (zuiNext(&src[i],&zval)) {
double score, value;
/* Skip an element that when already processed */
if (dictFind(dstzset->dict,zuiObjectFromValue(&zval)) != NULL)
continue;
/* Initialize score */
/* Initialize value */
score = src[i].weight * zval.score;
if (isnan(score)) score = 0;
/* We need to check only next sets to see if this element
* exists, since we process every element just one time so
* it can't exist in a previous set (otherwise it would be
* already processed). */
for (j = (i+1); j < setnum; j++) {
/* It is not safe to access the zset we are
* iterating, so explicitly check for equal object. */
if(src[j].subject == src[i].subject) {
value = zval.score*src[j].weight;
zunionInterAggregate(&score,value,aggregate);
} else if (zuiFind(&src[j],&zval,&value)) {
value *= src[j].weight;
zunionInterAggregate(&score,value,aggregate);
/* Search for this element in the accumulating dictionary. */
de = dictFind(accumulator,zuiObjectFromValue(&zval));
/* If we don't have it, we need to create a new entry. */
if (de == NULL) {
tmp = zuiObjectFromValue(&zval);
/* Remember the longest single element encountered,
* to understand if it's possible to convert to ziplist
* at the end. */
if (tmp->encoding == REDIS_ENCODING_RAW) {
if (sdslen(tmp->ptr) > maxelelen)
maxelelen = sdslen(tmp->ptr);
}
/* Add the element with its initial score. */
de = dictAddRaw(accumulator,tmp);
incrRefCount(tmp);
dictSetDoubleVal(de,score);
} else {
/* Update the score with the score of the new instance
* of the element found in the current sorted set.
*
* Here we access directly the dictEntry double
* value inside the union as it is a big speedup
* compared to using the getDouble/setDouble API. */
zunionInterAggregate(&de->v.d,score,aggregate);
}
tmp = zuiObjectFromValue(&zval);
znode = zslInsert(dstzset->zsl,score,tmp);
incrRefCount(zval.ele); /* added to skiplist */
dictAdd(dstzset->dict,tmp,&znode->score);
incrRefCount(zval.ele); /* added to dictionary */
if (tmp->encoding == REDIS_ENCODING_RAW)
if (sdslen(tmp->ptr) > maxelelen)
maxelelen = sdslen(tmp->ptr);
}
zuiClearIterator(&src[i]);
}
/* Step 2: convert the dictionary into the final sorted set. */
di = dictGetIterator(accumulator);
/* We now are aware of the final size of the resulting sorted set,
* let's resize the dictionary embedded inside the sorted set to the
* right size, in order to save rehashing time. */
dictExpand(dstzset->dict,dictSize(accumulator));
while((de = dictNext(di)) != NULL) {
robj *ele = dictGetKey(de);
score = dictGetDoubleVal(de);
znode = zslInsert(dstzset->zsl,score,ele);
incrRefCount(ele); /* added to skiplist */
dictAdd(dstzset->dict,ele,&znode->score);
incrRefCount(ele); /* added to dictionary */
}
dictReleaseIterator(di);
/* We can free the accumulator dictionary now. */
dictRelease(accumulator);
} else {
redisPanic("Unknown operator");
}
+85 -21
View File
@@ -37,6 +37,7 @@
#include <unistd.h>
#include <sys/time.h>
#include <float.h>
#include <stdint.h>
#include "util.h"
@@ -214,28 +215,91 @@ long long memtoll(const char *p, int *err) {
return val*mul;
}
/* Convert a long long into a string. Returns the number of
* characters needed to represent the number, that can be shorter if passed
* buffer length is not enough to store the whole number. */
int ll2string(char *s, size_t len, long long value) {
char buf[32], *p;
unsigned long long v;
size_t l;
/* Return the number of digits of 'v' when converted to string in radix 10.
* See ll2string() for more information. */
uint32_t digits10(uint64_t v) {
if (v < 10) return 1;
if (v < 100) return 2;
if (v < 1000) return 3;
if (v < 1000000000000UL) {
if (v < 100000000UL) {
if (v < 1000000) {
if (v < 10000) return 4;
return 5 + (v >= 100000);
}
return 7 + (v >= 10000000UL);
}
if (v < 10000000000UL) {
return 9 + (v >= 1000000000UL);
}
return 11 + (v >= 100000000000UL);
}
return 12 + digits10(v / 1000000000000UL);
}
if (len == 0) return 0;
v = (value < 0) ? -value : value;
p = buf+31; /* point to the last character */
do {
*p-- = '0'+(v%10);
v /= 10;
} while(v);
if (value < 0) *p-- = '-';
p++;
l = 32-(p-buf);
if (l+1 > len) l = len-1; /* Make sure it fits, including the nul term */
memcpy(s,p,l);
s[l] = '\0';
return l;
/* Convert a long long into a string. Returns the number of
* characters needed to represent the number.
* If the buffer is not big enough to store the string, 0 is returned.
*
* Based on the following article (that apparently does not provide a
* novel approach but only publicizes an already used technique):
*
* https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
*
* Modified in order to handle signed integers since the original code was
* designed for unsigned integers. */
int ll2string(char* dst, size_t dstlen, long long svalue) {
static const char digits[201] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
int negative;
unsigned long long value;
/* The main loop works with 64bit unsigned integers for simplicity, so
* we convert the number here and remember if it is negative. */
if (svalue < 0) {
if (svalue != LLONG_MIN) {
value = -svalue;
} else {
value = ((unsigned long long) LLONG_MAX)+1;
}
negative = 1;
} else {
value = svalue;
negative = 0;
}
/* Check length. */
uint32_t const length = digits10(value)+negative;
if (length >= dstlen) return 0;
/* Null term. */
uint32_t next = length;
dst[next] = '\0';
next--;
while (value >= 100) {
int const i = (value % 100) * 2;
value /= 100;
dst[next] = digits[i + 1];
dst[next - 1] = digits[i];
next -= 2;
}
/* Handle last 1-2 digits. */
if (value < 10) {
dst[next] = '0' + (uint32_t) value;
} else {
int i = (uint32_t) value * 2;
dst[next] = digits[i + 1];
dst[next - 1] = digits[i];
}
/* Add sign. */
if (negative) dst[0] = '-';
return length;
}
/* Convert a string into a long long. Returns 1 if the string could be parsed
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.8.12"
#define REDIS_VERSION "2.8.16"
+13 -13
View File
@@ -576,19 +576,19 @@ 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) {
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen, prevlen = 0;
size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
unsigned int prevlensize, prevlen = 0;
size_t offset;
int nextdiff = 0;
unsigned char encoding = 0;
long long value = 123456789; /* initialized to avoid warning. Using a value
that is easy to see if for some reason
we use it uninitialized. */
zlentry entry, tail;
zlentry tail;
/* Find out prevlen for the entry that is inserted. */
if (p[0] != ZIP_END) {
entry = zipEntry(p);
prevlen = entry.prevrawlen;
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
} else {
unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
if (ptail[0] != ZIP_END) {
@@ -676,15 +676,15 @@ unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int sle
* doesn't contain an element at the provided index, NULL is returned. */
unsigned char *ziplistIndex(unsigned char *zl, int index) {
unsigned char *p;
zlentry entry;
unsigned int prevlensize, prevlen = 0;
if (index < 0) {
index = (-index)-1;
p = ZIPLIST_ENTRY_TAIL(zl);
if (p[0] != ZIP_END) {
entry = zipEntry(p);
while (entry.prevrawlen > 0 && index--) {
p -= entry.prevrawlen;
entry = zipEntry(p);
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
while (prevlen > 0 && index--) {
p -= prevlen;
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
}
}
} else {
@@ -722,7 +722,7 @@ unsigned char *ziplistNext(unsigned char *zl, unsigned char *p) {
/* Return pointer to previous entry in ziplist. */
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
zlentry entry;
unsigned int prevlensize, prevlen = 0;
/* Iterating backwards from ZIP_END should return the tail. When "p" is
* equal to the first element of the list, we're already at the head,
@@ -733,9 +733,9 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
} else if (p == ZIPLIST_ENTRY_HEAD(zl)) {
return NULL;
} else {
entry = zipEntry(p);
assert(entry.prevrawlen > 0);
return p-entry.prevrawlen;
ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
assert(prevlen > 0);
return p-prevlen;
}
}
+3 -3
View File
@@ -56,7 +56,7 @@
* signal the end of the hash. The special value 254 is used to mark
* empty space that can be used to add new key/value pairs.
*
* <free> is the number of free unused bytes after the string, resulting
* <free> is the number of free unused bytes after the string, resulting
* from modification of values associated to a key. For instance if "foo"
* is set to "bar", and later "foo" will be set to "hi", it will have a
* free byte to use if the value will enlarge again later, or even in
@@ -186,7 +186,7 @@ static unsigned int zipmapRawKeyLength(unsigned char *p) {
static unsigned int zipmapRawValueLength(unsigned char *p) {
unsigned int l = zipmapDecodeLength(p);
unsigned int used;
used = zipmapEncodeLength(NULL,l);
used += p[used] + 1 + l;
return used;
@@ -214,7 +214,7 @@ unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int kle
unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);
unsigned int empty, vempty;
unsigned char *p;
freelen = reqlen;
if (update) *update = 0;
p = zipmapLookupRaw(zm,key,klen,&zmlen);
+6 -3
View File
@@ -67,7 +67,10 @@ void zlibc_free(void *ptr) {
#define free(ptr) je_free(ptr)
#endif
#ifdef HAVE_ATOMIC
#if defined(__ATOMIC_RELAXED)
#define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED)
#define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED)
#elif defined(HAVE_ATOMIC)
#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))
#define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))
#else
@@ -219,8 +222,8 @@ size_t zmalloc_used_memory(void) {
size_t um;
if (zmalloc_thread_safe) {
#ifdef HAVE_ATOMIC
um = __sync_add_and_fetch(&used_memory, 0);
#if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)
um = update_zmalloc_stat_add(0);
#else
pthread_mutex_lock(&used_memory_mutex);
um = used_memory;
+1
View File
@@ -9,6 +9,7 @@ bind 127.0.0.1
loglevel verbose
logfile ''
databases 16
latency-monitor-threshold 1
save 900 1
save 300 10
+1 -1
View File
@@ -396,7 +396,7 @@ proc restart_instance {type id} {
# Check that the instance is running
if {[server_is_up 127.0.0.1 $port 100] == 0} {
abort_sentinel_test "Problems starting $type #$j: ping timeout"
abort_sentinel_test "Problems starting $type #$id: ping timeout"
}
# Connect with it with a fresh link
+84 -6
View File
@@ -23,6 +23,84 @@ proc start_server_aof {overrides code} {
}
tags {"aof"} {
## Server can start when aof-load-truncated is set to yes and AOF
## is truncated, with an incomplete MULTI block.
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof [formatCommand multi]
append_to_aof [formatCommand set bar world]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Unfinished MULTI: Server should start if load-truncated is yes" {
assert_equal 1 [is_alive $srv]
}
}
## Should also start with truncated AOF without incomplete MULTI block.
create_aof {
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [string range [formatCommand incr foo] 0 end-1]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Short read: Server should start if load-truncated is yes" {
assert_equal 1 [is_alive $srv]
}
set client [redis [dict get $srv host] [dict get $srv port]]
test "Truncated AOF loaded: we expect foo to be equal to 5" {
assert {[$client get foo] eq "5"}
}
test "Append a new command after loading an incomplete AOF" {
$client incr foo
}
}
# Now the AOF file is expected to be correct
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Short read + command: Server should start" {
assert_equal 1 [is_alive $srv]
}
set client [redis [dict get $srv host] [dict get $srv port]]
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
assert {[$client get foo] eq "6"}
}
}
## Test that the server exits when the AOF contains a format error
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof "!!!"
append_to_aof [formatCommand set foo hello]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Bad format: Server should have logged an error" {
set pattern "*Bad file format reading the append only file*"
set retry 10
while {$retry} {
set result [exec tail -n1 < [dict get $srv stdout]]
if {[string match $pattern $result]} {
break
}
incr retry -1
after 1000
}
if {$retry == 0} {
error "assertion:expected error not found on config file"
}
}
}
## Test the server doesn't start when the AOF contains an unfinished MULTI
create_aof {
append_to_aof [formatCommand set foo hello]
@@ -30,7 +108,7 @@ tags {"aof"} {
append_to_aof [formatCommand set bar world]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Unfinished MULTI: Server should have logged an error" {
set pattern "*Unexpected end of file reading the append only file*"
set retry 10
@@ -54,9 +132,9 @@ tags {"aof"} {
append_to_aof [string range [formatCommand set bar world] 0 end-1]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Short read: Server should have logged an error" {
set pattern "*Bad file format reading the append only file*"
set pattern "*Unexpected end of file reading the append only file*"
set retry 10
while {$retry} {
set result [exec tail -n1 < [dict get $srv stdout]]
@@ -86,7 +164,7 @@ tags {"aof"} {
}
## Test that the server can be started using the truncated AOF
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "Fixed AOF: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
@@ -110,7 +188,7 @@ tags {"aof"} {
append_to_aof [formatCommand spop set]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "AOF+SPOP: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
@@ -133,7 +211,7 @@ tags {"aof"} {
append_to_aof [formatCommand rpush list bar]
}
start_server_aof [list dir $server_path] {
start_server_aof [list dir $server_path aof-load-truncated no] {
test "AOF+EXPIRE: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
+13 -3
View File
@@ -29,15 +29,25 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
test {First server should have role slave after SLAVEOF} {
test {Slave should be able to synchronize with the master} {
$slave slaveof $master_host $master_port
wait_for_condition 50 100 {
[s 0 role] eq {slave}
[lindex [r role] 0] eq {slave} &&
[lindex [r role] 3] eq {connected}
} else {
fail "Replication not started."
}
}
# Check that the background clients are actually writing.
test {Detect write load to master} {
wait_for_condition 50 100 {
[$master dbsize] > 100
} else {
fail "Can't detect write load from background clients."
}
}
test "Test replication partial resync: $descr" {
# Now while the clients are writing data, break the maste-slave
# link multiple times.
@@ -92,7 +102,7 @@ test_psync {ok psync} 6 1000000 3600 0 {
assert {[s -1 sync_partial_ok] > 0}
}
test_psync {no backlog} 6 100 3600 0 {
test_psync {no backlog} 6 100 3600 0.5 {
assert {[s -1 sync_partial_err] > 0}
}
+1 -10
View File
@@ -82,7 +82,7 @@ start_server {tags {"repl"}} {
assert {$offset > 0}
assert {[llength $slaves] == 1}
lassign [lindex $slaves 0] master_host master_port slave_offset
assert {$slave_offset < $offset}
assert {$slave_offset <= $offset}
}
test {ROLE in slave reports slave in connected state} {
@@ -94,15 +94,6 @@ start_server {tags {"repl"}} {
}
}
proc start_write_load {host port seconds} {
set tclsh [info nameofexecutable]
exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
}
proc stop_write_load {handle} {
catch {exec /bin/kill -9 $handle}
}
start_server {tags {"repl"}} {
set master [srv 0 client]
set master_host [srv 0 host]
+13 -1
View File
@@ -351,9 +351,21 @@ proc colorstr {color str} {
default {set colorcode {37}}
}
if {$colorcode ne {}} {
return "\033\[$b;${colorcode};40m$str\033\[0m"
return "\033\[$b;${colorcode};49m$str\033\[0m"
}
} else {
return $str
}
}
# Execute a background process writing random data for the specified number
# of seconds to the specified Redis instance.
proc start_write_load {host port seconds} {
set tclsh [info nameofexecutable]
exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
}
# Stop a process generating write load executed with start_write_load.
proc stop_write_load {handle} {
catch {exec /bin/kill -9 $handle}
}
+62
View File
@@ -1,5 +1,67 @@
start_server {tags {"aofrw"}} {
# Enable the AOF
r config set appendonly yes
r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.
waitForBgrewriteaof r
test {AOF rewrite during write load} {
# Start a write load for 10 seconds
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
set load_handle0 [start_write_load $master_host $master_port 10]
set load_handle1 [start_write_load $master_host $master_port 10]
set load_handle2 [start_write_load $master_host $master_port 10]
set load_handle3 [start_write_load $master_host $master_port 10]
set load_handle4 [start_write_load $master_host $master_port 10]
# Make sure the instance is really receiving data
wait_for_condition 50 100 {
[r dbsize] > 0
} else {
fail "No write load detected."
}
# After 3 seconds, start a rewrite, while the write load is still
# active.
after 3000
r bgrewriteaof
waitForBgrewriteaof r
# Let it run a bit more so that we'll append some data to the new
# AOF.
after 1000
# Stop the processes generating the load if they are still active
stop_write_load $load_handle0
stop_write_load $load_handle1
stop_write_load $load_handle2
stop_write_load $load_handle3
stop_write_load $load_handle4
# Make sure that we remain the only connected client.
# This step is needed to make sure there are no pending writes
# that will be processed between the two "debug digest" calls.
wait_for_condition 50 100 {
[llength [split [string trim [r client list]] "\n"]] == 1
} else {
puts [r client list]
fail "Clients generating loads are not disconnecting"
}
# Get the data set digest
set d1 [r debug digest]
# Load the AOF
r debug loadaof
set d2 [r debug digest]
# Make sure they are the same
assert {$d1 eq $d2}
}
}
start_server {tags {"aofrw"}} {
test {Turning off AOF kills the background writing child if any} {
r config set appendonly yes
waitForBgrewriteaof r
+11
View File
@@ -404,6 +404,12 @@ start_server {tags {"basic"}} {
r move mykey 10
} {0}
test {MOVE against non-integer DB (#1428)} {
r set mykey hello
catch {r move mykey notanumber} e
set e
} {*ERR*index out of range}
test {SET/GET keys in different DBs} {
r set a hello
r set b world
@@ -769,4 +775,9 @@ start_server {tags {"basic"}} {
r keys *
r keys *
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
test {GETRANGE with huge ranges, Github issue #1844} {
r set foo bar
r getrange foo 0 4294967297
} {bar}
}
+50
View File
@@ -0,0 +1,50 @@
start_server {tags {"latency-monitor"}} {
# Set a threshold high enough to avoid spurious latency events.
r config set latency-monitor-threshold 200
r latency reset
test {Test latency events logging} {
r debug sleep 0.3
after 1100
r debug sleep 0.4
after 1100
r debug sleep 0.5
assert {[r latency history command] >= 3}
}
test {LATENCY HISTORY output is ok} {
set min 250
set max 450
foreach event [r latency history command] {
lassign $event time latency
assert {$latency >= $min && $latency <= $max}
incr min 100
incr max 100
set last_time $time ; # Used in the next test
}
}
test {LATENCY LATEST output is ok} {
foreach event [r latency latest] {
lassign $event eventname time latency max
assert {$eventname eq "command"}
assert {$max >= 450 & $max <= 650}
assert {$time == $last_time}
break
}
}
test {LATENCY HISTORY / RESET with wrong event name is fine} {
assert {[llength [r latency history blabla]] == 0}
assert {[r latency reset blabla] == 0}
}
test {LATENCY DOCTOR produces some output} {
assert {[string length [r latency doctor]] > 0}
}
test {LATENCY RESET is able to reset events} {
assert {[r latency reset] > 0}
assert {[r latency latest] eq {}}
}
}
+24
View File
@@ -1,4 +1,28 @@
start_server {tags {"maxmemory"}} {
test "Without maxmemory small integers are shared" {
r config set maxmemory 0
r set a 1
assert {[r object refcount a] > 1}
}
test "With maxmemory and non-LRU policy integers are still shared" {
r config set maxmemory 1073741824
r config set maxmemory-policy allkeys-random
r set a 1
assert {[r object refcount a] > 1}
}
test "With maxmemory and LRU policy integers are not shared" {
r config set maxmemory 1073741824
r config set maxmemory-policy allkeys-lru
r set a 1
r config set maxmemory-policy volatile-lru
r set b 1
assert {[r object refcount a] == 1}
assert {[r object refcount b] == 1}
r config set maxmemory 0
}
foreach policy {
allkeys-random allkeys-lru volatile-lru volatile-random volatile-ttl
} {
+20
View File
@@ -47,6 +47,22 @@ start_server {tags {"pubsub"}} {
__consume_subscribe_messages $client punsubscribe $channels
}
test "Pub/Sub PING" {
set rd1 [redis_deferring_client]
subscribe $rd1 somechannel
# While subscribed to non-zero channels PING works in Pub/Sub mode.
$rd1 ping
$rd1 ping "foo"
set reply1 [$rd1 read]
set reply2 [$rd1 read]
unsubscribe $rd1 somechannel
# Now we are unsubscribed, PING should just return PONG.
$rd1 ping
set reply3 [$rd1 read]
$rd1 close
list $reply1 $reply2 $reply3
} {{pong {}} {pong foo} PONG}
test "PUBLISH/SUBSCRIBE basics" {
set rd1 [redis_deferring_client]
@@ -180,6 +196,10 @@ start_server {tags {"pubsub"}} {
$rd1 close
}
test "NUMSUB returns numbers, not strings (#1561)" {
r pubsub numsub abc def
} {abc 0 def 0}
test "Mix SUBSCRIBE and PSUBSCRIBE" {
set rd1 [redis_deferring_client]
assert_equal {1} [subscribe $rd1 {foo.bar}]
+17
View File
@@ -358,6 +358,23 @@ start_server {tags {"scripting"}} {
return redis.call("get", "key")
} 0
} {12039611435714932082}
test {Verify negative arg count is error instead of crash (issue #1842)} {
catch { r eval { return "hello" } -12 } e
set e
} {ERR Number of keys can't be negative}
test {Correct handling of reused argv (issue #1939)} {
r eval {
for i = 0, 10 do
redis.call('SET', 'a', '1')
redis.call('MGET', 'a', 'b', 'c')
redis.call('EXPIRE', 'a', 0)
redis.call('GET', 'a')
redis.call('MGET', 'a', 'b', 'c')
end
} 0
}
}
# Start a new server since the last test in this stanza will kill the
+22
View File
@@ -588,6 +588,28 @@ start_server {tags {"zset"}} {
r zrange to_here 0 -1
} {100}
test {ZUNIONSTORE result is sorted} {
# Create two sets with common and not common elements, perform
# the UNION, check that elements are still sorted.
r del one two dest
set cmd1 [list r zadd one]
set cmd2 [list r zadd two]
for {set j 0} {$j < 1000} {incr j} {
lappend cmd1 [expr rand()] [randomInt 1000]
lappend cmd2 [expr rand()] [randomInt 1000]
}
{*}$cmd1
{*}$cmd2
assert {[r zcard one] > 100}
assert {[r zcard two] > 100}
r zunionstore dest 2 one two
set oldscore 0
foreach {ele score} [r zrange dest 0 -1 withscores] {
assert {$score >= $oldscore}
set oldscore $score
}
}
proc stressers {encoding} {
if {$encoding == "ziplist"} {
# Little extra to allow proper fuzzing in the sorting stresser
+1 -1
View File
@@ -50,7 +50,7 @@ def commands
require "json"
require "uri"
url = URI.parse "https://raw.github.com/antirez/redis-doc/master/commands.json"
url = URI.parse "https://raw.githubusercontent.com/antirez/redis-doc/master/commands.json"
client = Net::HTTP.new url.host, url.port
client.use_ssl = true
response = client.get url.path
+2 -2
View File
@@ -152,7 +152,7 @@ rm -f $TMP_FILE
#we hard code the configs here to avoid issues with templates containing env vars
#kinda lame but works!
REDIS_INIT_HEADER=\
"#/bin/sh\n
"#!/bin/sh\n
#Configurations injected by install_server below....\n\n
EXEC=$REDIS_EXECUTABLE\n
CLIEXEC=$CLI_EXEC\n
@@ -193,7 +193,7 @@ fi
# warning if init info is not available.
cat > ${TMP_FILE} <<EOT
#/bin/sh
#!/bin/sh
#Configurations injected by install_server below....
EXEC=$REDIS_EXECUTABLE