Compare commits

..
59 Commits
Author SHA1 Message Date
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
49 changed files with 1468 additions and 214 deletions
+18
View File
@@ -14,6 +14,24 @@ 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.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.
+21
View File
@@ -562,6 +562,27 @@ 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.
+1 -1
View File
@@ -107,7 +107,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
+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;
+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;
+1 -1
View File
@@ -137,7 +137,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);
}
+38 -5
View File
@@ -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)) {
@@ -970,7 +990,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 +1051,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",
@@ -1080,13 +1102,17 @@ void aofRemoveTempFile(pid_t childpid) {
* 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 +1122,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 +1145,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 +1192,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 +1200,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
+16 -2
View File
@@ -420,6 +420,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") &&
@@ -747,6 +755,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 +955,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);
@@ -1255,7 +1268,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.
@@ -1381,7 +1394,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 +1737,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);
+1 -1
View File
@@ -185,7 +185,7 @@ 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__)
#define HAVE_ATOMIC
+2 -2
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);
}
@@ -816,7 +816,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;
+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
+4
View File
@@ -46,6 +46,10 @@
#define _XOPEN_SOURCE
#endif
#if defined(__sun)
#define _POSIX_C_SOURCE 199506L
#endif
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
+577
View File
@@ -0,0 +1,577 @@
/* 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) {
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 -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));
+1 -1
View File
@@ -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);
+6 -3
View File
@@ -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;
+1 -1
View File
@@ -608,7 +608,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;
+8 -1
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;
@@ -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",
@@ -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)
+5
View File
@@ -592,6 +592,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.");
exit(1);
}
if (config.csv) return 250;
float dt = (float)(mstime()-config.start)/1000.0;
float rps = (float)config.requests_finished/dt;
+2 -2
View File
@@ -143,7 +143,7 @@ static char types[256][16];
/* 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) ||
@@ -514,7 +514,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;
+21 -18
View File
@@ -598,8 +598,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;
}
@@ -1149,7 +1152,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 +1235,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 +1256,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 +1359,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 +1371,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);
@@ -1421,13 +1424,13 @@ 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"};
@@ -1436,10 +1439,10 @@ static void getKeySizes(redisReply *keys, int *types,
/* 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 +1462,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);
}
}
@@ -1523,12 +1526,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 +1551,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 +1564,7 @@ static void findBigKeys(void) {
if(sampled && (sampled %100) == 0 && config.interval) {
usleep(config.interval);
}
freeReplyObject(reply);
} while(it != 0);
+176 -79
View File
@@ -112,48 +112,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 +167,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 +180,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 +240,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 +251,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},
{"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ar",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 ============================ */
@@ -799,10 +805,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
@@ -1085,7 +1092,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) {
@@ -1421,11 +1428,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>";
@@ -1591,6 +1601,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;
@@ -1729,6 +1740,7 @@ void initServer() {
replicationScriptCacheInit();
scriptingInit();
slowlogInit();
latencyMonitorInit();
bioInit();
}
@@ -1756,6 +1768,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++;
@@ -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++;
@@ -2265,6 +2282,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) {
@@ -2315,7 +2404,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 +2838,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 +2871,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 +2975,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;
}
+14 -3
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 */
@@ -124,6 +128,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 +166,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
@@ -375,8 +382,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 */
@@ -627,6 +632,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. */
@@ -786,6 +792,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;
@@ -1232,6 +1241,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 +1390,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
+1 -1
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) {
+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
+1 -1
View File
@@ -1169,7 +1169,7 @@ void zsetConvert(robj *zobj, int encoding) {
}
/*-----------------------------------------------------------------------------
* Sorted set commands
* Sorted set commands
*----------------------------------------------------------------------------*/
/* This generic command implements both ZADD and ZINCRBY. */
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.8.12"
#define REDIS_VERSION "2.8.13"
+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
+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]
+12
View File
@@ -357,3 +357,15 @@ proc colorstr {color str} {
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
+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 {}}
}
}