Compare commits

...
Author SHA1 Message Date
antirez 593ce5b206 Test: processing of master stream in slave -BUSY state.
See #5297.
2018-08-31 16:43:41 +02:00
antirez f2739ca9c4 After slave Lua script leaves busy state, re-process the master buffer.
Technically speaking we don't really need to put the master client in
the clients that need to be processed, since in practice the PING
commands from the master will take care, however it is conceptually more
sane to do so.
2018-08-31 16:07:03 +02:00
antirez e8d35809c4 While the slave is busy, just accumulate master input.
Processing command from the master while the slave is in busy state is
not correct, however we cannot, also, just reply -BUSY to the
replication stream commands from the master. The correct solution is to
stop processing data from the master, but just accumulate the stream
into the buffers and resume the processing later.

Related to #5297.
2018-08-30 17:52:04 +02:00
antirez 4b68dbcbd7 Allow scripts to timeout even if from the master instance.
However the master scripts will be impossible to kill.

Related to #5297.
2018-08-30 11:46:42 +02:00
antirez 17b0d99d9f Allow scripts to timeout on slaves as well.
See reasoning in #5297.
2018-08-29 18:14:46 +02:00
Salvatore SanfilippoandGitHub 476eea95da Merge pull request #4216 from lamby/did-not-received-typos
Correct "did not received" -> "did not receive" typos/grammar.
2018-08-29 16:18:11 +02:00
Salvatore SanfilippoandGitHub bd9259d12d Merge pull request #5282 from soloestoy/remove-duplicate-bind-in-sentinel.conf
remove duplicate bind in sentinel.conf
2018-08-29 16:13:42 +02:00
Salvatore SanfilippoandGitHub ed5cc77ce0 Merge pull request #5296 from soloestoy/command-script-flag2
Supplement to PR #4835, just take info/memory/command as random commands
2018-08-29 12:26:01 +02:00
zhaozhao.zz 7d39c149c4 Supplement to PR #4835, just take info/memory/command as random commands 2018-08-29 18:23:05 +08:00
Salvatore SanfilippoandGitHub 0e21efdb1c Merge pull request #4835 from soloestoy/command-script-flag
some commands' flags should be set correctly, issue #4834
2018-08-29 12:13:50 +02:00
zhaozhao.zz 32844178ac some commands' flags should be set correctly, issue #4834 2018-08-29 18:07:01 +08:00
Salvatore SanfilippoandGitHub 5b4bec9d33 Merge pull request #5265 from oranagra/stabilize_tests
Fix unstable tests on slow machines.
2018-08-27 13:19:31 +02:00
antirez 5941022b52 Document slave-ignore-maxmemory in redis.conf. 2018-08-27 12:34:29 +02:00
antirez e245a2046a Make slave-ignore-maxmemory configurable. 2018-08-27 12:27:17 +02:00
antirez 067647a783 Introduce repl_slave_ignore_maxmemory flag internally.
Note: this breaks backward compatibility with Redis 4, since now slaves
by default are exact copies of masters and do not try to evict keys
independently.
2018-08-27 12:20:27 +02:00
antirez abf52c7cf4 Better variable meaning in processCommand(). 2018-08-27 12:17:34 +02:00
antirez 66b5afdaa4 Re-apply rebased #2358. 2018-08-27 12:17:14 +02:00
antirez c241f51607 Fix build errors caused by #2358. 2018-08-27 12:15:55 +02:00
zhaozhao.zz e711e6f614 remove duplicate bind in sentinel.conf 2018-08-27 12:07:24 +08:00
Salvatore SanfilippoandGitHub 19880ab851 Merge pull request #5248 from soloestoy/rewrite-brpoplpush
rewrite BRPOPLPUSH as RPOPLPUSH to propagate
2018-08-26 16:31:24 +02:00
Salvatore SanfilippoandGitHub 80e1695652 Merge pull request #5244 from soloestoy/optimize-pipeline
pipeline: do not sdsrange querybuf unless all commands processed
2018-08-26 16:30:49 +02:00
Chris Lamb 132be8aed5 Correct "did not received" -> "did not receive" typos/grammar. 2018-08-26 14:45:39 +02:00
Salvatore SanfilippoandGitHub 46d89a9abb Merge pull request #2992 from lamby/source-date-epoch
Use SOURCE_DATE_EPOCH over unreproducible uname + date calls.
2018-08-26 11:25:44 +02:00
Salvatore SanfilippoandGitHub 590717782f Merge pull request #2358 from lamby/config-set-maxmemory-grammar
Tidy grammar in CONFIG SET maxmemory warning.
2018-08-26 11:23:41 +02:00
Salvatore SanfilippoandGitHub 8d519a85f4 Merge pull request #2292 from lamby/sentinel-conf-defaults
Make some defaults explicit in the sentinel.conf for package maintainers
2018-08-26 11:19:51 +02:00
Chris LambandGitHub f63e81c202 Merge branch 'unstable' into config-set-maxmemory-grammar 2018-08-25 21:49:29 +02:00
zhaozhao.zz f2ad89a314 networking: make setProtocolError simple and clear
Function setProtocolError just records proctocol error
details in server log, set client as CLIENT_CLOSE_AFTER_REPLY.
It doesn't care about querybuf sdsrange, because we
will do it after procotol parsing.
2018-08-23 12:21:28 +08:00
Oran Agra c8452ab005 Fix unstable tests on slow machines.
Few tests had borderline thresholds that were adjusted.

The slave buffers test had two issues, preventing the slave buffer from growing:
1) the slave didn't necessarily go to sleep on time, or woke up too early,
   now using SIGSTOP to make sure it goes to sleep exactly when we want.
2) the master disconnected the slave on timeout
2018-08-21 11:46:07 +03:00
zhaozhao.zz 9a65f9cd3e block: format code 2018-08-14 20:59:32 +08:00
zhaozhao.zz 8a1219d93b block: rewrite BRPOPLPUSH as RPOPLPUSH to propagate 2018-08-14 20:58:58 +08:00
zhaozhao.zz ef2a95c461 networking: just move qb_pos instead of sdsrange in processInlineBuffer 2018-08-14 14:50:37 +08:00
zhaozhao.zz e623bd22ba networking: just return C_OK if multibulk processing saw a <= 0 length. 2018-08-14 13:55:30 +08:00
zhaozhao.zz b89302c462 adjust qbuf to 26 in test case for client list 2018-08-14 00:57:22 +08:00
zhaozhao.zz 14c4ddb5a6 pipeline: do not sdsrange querybuf unless all commands processed
This is an optimization for processing pipeline, we discussed a
problem in issue #5229: clients may be paused if we apply `CLIENT
PAUSE` command, and then querybuf may grow too large, the cost of
memmove in sdsrange after parsing a completed command will be
horrible. The optimization is that parsing all commands in queyrbuf
, after that we can just call sdsrange only once.
2018-08-14 00:43:42 +08:00
Salvatore SanfilippoandGitHub 39c70e728b Merge pull request #5146 from 0xtonyxia/fix-xclaim-id-parse
Streams: ID of xclaim command should start from the sixth argument.
2018-08-03 13:45:27 +02:00
Salvatore SanfilippoandGitHub 7b5e7f3ed0 Merge pull request #5151 from shenlongxing/fix-stream
Fix stream command paras
2018-08-03 13:39:32 +02:00
shenlongxing fe56c67405 Fix stream command paras 2018-08-03 19:01:15 +08:00
antirez 0ce8323c0d Fix AOF comment to report the current behavior.
Realted to #5201.
2018-08-03 12:46:06 +02:00
antirez 3d56311f0c Test suite: add --loop option.
Very useful with --stop in order to catch heisenbugs.
2018-08-02 19:07:17 +02:00
antirez 26897c03e2 Test suite: new --stop option.
It pauses the test execution once the first failure is found.
2018-08-02 18:49:49 +02:00
antirez d506334b67 Test: new sorted set skiplist order consistency.
This should be able to find new bugs and regressions about the new
sorted set update function when ZADD is used to update an element
already existing.

The test is able to find the bug fixed at 2f282aee immediately.
2018-08-02 14:15:53 +02:00
antirez 2f282aee0b Fix zslUpdateScore() edge case.
When the element new score is the same of prev/next node, the
lexicographical order kicks in, so we can safely update the node in
place only when the new score is strictly between the adjacent nodes
but never equal to one of them.

Technically speaking we could do extra checks to make sure that even if the
score is the same as one of the adjacent nodes, we can still update on
place, but this rarely happens, so probably not a good deal to make it
more complex.

Related to #5179.
2018-08-01 19:04:53 +02:00
antirez 29226a3919 More commenting of zslUpdateScore(). 2018-08-01 18:54:15 +02:00
antirez 12ff0c0d79 Explain what's the point of zslUpdateScore() in top comment. 2018-08-01 18:53:09 +02:00
antirez e3e94ec35c Remove old commented zslUpdateScore() from source. 2018-08-01 18:50:49 +02:00
antirez 0b800c4332 Optimize zslUpdateScore() as asked in #5179. 2018-08-01 18:50:31 +02:00
antirez 6dd0d6f7dd zsetAdd() refactored adding zslUpdateScore(). 2018-08-01 18:27:56 +02:00
antirez 2f2987ffc5 Streams IDs parsing refactoring.
Related to #5184.
2018-07-31 18:08:52 +02:00
Salvatore SanfilippoandGitHub 187fa78637 Merge pull request #5184 from 0xtonyxia/streams-id-opt
Streams: rearrange the usage of '-' and '+' IDs in stream commands.
2018-07-31 17:58:13 +02:00
antirez 7a30be1237 Minor improvements to PR #5187. 2018-07-31 17:30:12 +02:00
Salvatore SanfilippoandGitHub 7b41701eb8 Merge pull request #5187 from oranagra/testsuite_improvements
Testsuite improvements
2018-07-31 17:20:09 +02:00
antirez e245ed9a44 Cluster cron announce IP minor refactoring. 2018-07-31 17:13:03 +02:00
Salvatore SanfilippoandGitHub 093adb7ef9 Merge pull request #5191 from shenlongxing/fix-memory-leak
Fix cluster-announce-ip memory leak
2018-07-31 17:11:16 +02:00
antirez b0392e75ec Tranfer -> transfer typo fixed. 2018-07-31 16:41:33 +02:00
Salvatore SanfilippoandGitHub 92b39a0abf Merge pull request #5189 from soloestoy/refactor-dbOverwrite
refactor dbOverwrite to make lazyfree work
2018-07-31 16:40:35 +02:00
antirez db693be00d Refactoring: replace low-level checks with writeCommandsDeniedByDiskError(). 2018-07-31 13:16:43 +02:00
antirez 11dd3f4b03 Fix writeCommandsDeniedByDiskError() inverted return value. 2018-07-31 13:14:24 +02:00
antirez 0e49938b68 Better top comment for writeCommandsDeniedByDiskError(). 2018-07-31 13:10:37 +02:00
antirez 5401fe7fb9 Introduce writeCommandsDeniedByDiskError(). 2018-07-31 13:09:38 +02:00
shenlongxing 35ca670060 Fix cluster-announce-ip memory leak 2018-07-31 16:01:44 +08:00
zhaozhao.zz fddeeae724 refactor dbOverwrite to make lazyfree work 2018-07-31 12:07:57 +08:00
Oran Agra 7829287691 test suite conveniency improvements
* allowing --single to be repeated
* adding --only so that only a specific test inside a unit can be run
* adding --skiptill useful to resume a test that crashed passed the problematic unit.
  useful together with --clients 1
* adding --skipfile to use a file containing list of tests names to skip
* printing the names of the tests that are skiped by skipfile or denytags
* adding --config to add config file options from command line
2018-07-30 19:13:15 +03:00
Salvatore SanfilippoandGitHub ac3c012a7f Merge pull request #5153 from trevor211/fixLuaScript
Consider aof write error as well as rdb in lua script.
2018-07-30 18:10:06 +02:00
Salvatore Sanfilippo 4ebb4d54c6 Merge pull request #5168 from rpv-tomsk/issue-5033
INFO CPU: higher precision of reported values
2018-07-30 18:06:54 +02:00
Pavel Rochnyackandantirez 1e394b73b7 INFO CPU: higher precision of reported values
Closes: #5033
2018-07-30 18:06:22 +02:00
Salvatore SanfilippoandGitHub 1465755e38 Merge pull request #5183 from gkorland/patch-1
Few typo fixes
2018-07-30 17:59:12 +02:00
antirez 3c19ae941d Add year in log.
User: "is there a reason why redis server logs  are missing the year in
the "date time"?"

Me: "I guess I did not imagine it would be stable enough to run for
several years".
2018-07-30 17:42:30 +02:00
dejun.xdj 6491717c88 Streams: rearrange the usage of '-' and '+' IDs in stream commands. 2018-07-30 21:33:01 +08:00
dejun.xdj d6f5ec6f09 Streams: add mmid_supp argument in streamParseIDOrReply().
If 'mmid_supp' is set to 0, "-" and "+" will be
treated as an invalid ID.
2018-07-30 21:32:07 +08:00
Guy KorlandandGitHub acaa18f1d1 Few typo fixes 2018-07-30 16:18:56 +03:00
antirez 15be174cf1 Document dynamic-hz in the example redis.conf. 2018-07-30 13:50:15 +02:00
antirez be28050ac0 Make dynamic hz actually configurable. 2018-07-30 13:44:52 +02:00
antirez c426d85c4c Control dynamic HZ via server configuration. 2018-07-30 13:37:30 +02:00
antirez 4e9c30a6ca Merge branch 'dynamic-hz' into unstable 2018-07-30 13:31:23 +02:00
Oran Agra 4a30adde31 add DEBUG LOG, to to assist test suite debugging 2018-07-30 12:23:51 +03:00
antirez fd174cca23 Remove useless conditional from emptyDb().
Related to #4852.
2018-07-25 16:34:57 +02:00
antirez 1c2352f0dd Make emptyDb() change introduced in #4852 simpler to read. 2018-07-25 16:32:52 +02:00
Salvatore SanfilippoandGitHub fe408ad23f Merge pull request #4852 from soloestoy/optimize-flushall
optimize flushdb, avoid useless loops
2018-07-25 16:30:11 +02:00
zhaozhao.zz dd071ff6c9 optimize flushdb, avoid useless loops 2018-07-25 18:13:34 +08:00
Salvatore SanfilippoandGitHub 73cbc33a0a Merge pull request #5133 from soloestoy/fix-xdel-memory-leak
Streams: fix xdel memory leak
2018-07-24 18:57:05 +02:00
zhaozhao.zz eb0e5fe4e3 Streams: fix xdel memory leak 2018-07-25 00:06:27 +08:00
antirez 0bdeb861f4 Example the magic +1 in migrateCommand().
Related to #5154.
2018-07-24 17:31:43 +02:00
antirez 53d46fa712 Make changes of PR #5154 hopefully simpler. 2018-07-24 17:27:43 +02:00
Salvatore SanfilippoandGitHub e52a036ac6 Merge pull request #5154 from trevor211/optimizeMigrate
Do not migrate already expired keys.
2018-07-24 17:24:37 +02:00
antirez 8513f31be0 Streams: refactoring of next entry seek in the iterator.
After #5161 the code could be made a bit more obvious for newcomers.
2018-07-24 11:07:27 +02:00
Salvatore SanfilippoandGitHub 41a886605a Merge pull request #5161 from soloestoy/optimize-stream-iterator
Streams: skip master fileds only when we are going forward in streamIteratorGetID
2018-07-24 10:59:55 +02:00
Salvatore SanfilippoandGitHub dfc82fca54 Merge pull request #5163 from oranagra/fix_slave_buffer_test
fix slave buffer test suite false positives
2018-07-24 10:28:48 +02:00
antirez f4ac796c34 string2ll(): better commenting. 2018-07-24 10:27:20 +02:00
Oran Agra d4ae76d1a6 fix slave buffer test suite false positives
it looks like on slow machines we're getting:
[err]: slave buffer are counted correctly in tests/unit/maxmemory.tcl
Expected condition '$slave_buf > 2*1024*1024' to be true (16914 > 2*1024*1024)

this is a result of the slave waking up too early and eating the
slave buffer before the traffic and the test ends.
2018-07-24 11:24:27 +03:00
antirez b9a8e56439 Merge branch 'removeRedundantCheck' of https://github.com/dsomeshwar/redis into unstable 2018-07-24 10:24:24 +02:00
antirez b28cbe90e1 Restore string2ll() to original version.
See PR #5157.
2018-07-24 10:22:12 +02:00
antirez 1a8d7cd9c2 Merge branch 'unstable' of github.com:/antirez/redis into unstable 2018-07-24 00:21:49 +02:00
antirez e8f5d21940 string2ll(): remove duplicated check for special case.
Related to #5157. The PR author correctly indentified that the check was
duplicated, but removing the second one introduces a bug that was fixed
in the past (hence the duplication). Instead we can remove the first
instance of the check without issues.
2018-07-24 00:20:20 +02:00
antirez 1daee8c748 string2ll(): test for NULL pointer in all the cases. 2018-07-24 00:18:39 +02:00
Salvatore SanfilippoandGitHub ab1d5fe11a Merge pull request #5160 from oranagra/fix_zmalloc_usable
fix recursion typo in zmalloc_usable
2018-07-24 00:07:59 +02:00
antirez 9a375e5408 Change 42 to 1000 as warning level for cached scripts.
Related to #4883.
2018-07-23 18:44:58 +02:00
Salvatore SanfilippoandGitHub 445a2a2b1b Merge pull request #4883 from itamarhaber/lua_scripts-in-info-memory
Adds memory information about the scripts' cache to INFO
2018-07-23 18:43:05 +02:00
antirez b65ddfb16a Dynamic HZ: adapt cron frequency to number of clients. 2018-07-23 14:21:04 +02:00
antirez e6ea603ad3 Dynamic HZ: separate hz from the configured hz.
This way we can remember what the user configured HZ is, but change the
actual HZ dynamically if needed in the dynamic HZ feature
implementation.
2018-07-23 14:13:58 +02:00
Itamar Haber 993716c351 Adds Lua overheads to MEMORY STATS, smartens the MEMORY DOCTOR 2018-07-22 21:16:00 +03:00
zhaozhao.zz 9674ede85a Streams: skip master fileds only when we are going forward in streamIteratorGetID 2018-07-22 17:52:09 +08:00
Oran Agra 780815dd6e fix recursion typo in zmalloc_usable 2018-07-22 10:17:35 +03:00
dsomeshwar bb5b8b3a6f removing redundant check 2018-07-21 23:42:08 +05:30
WuYunlong 4017a11144 Do not migrate already expired keys. 2018-07-21 10:00:32 +08:00
WuYunlong dd760bd5e6 Consider aof write error as well as rdb in lua script. 2018-07-21 08:48:51 +08:00
dejun.xdj de5ca516ae Streams: ID of xclaim command starts from the sixth argument. 2018-07-19 20:48:08 +08:00
Itamar Haber 49890c8ee9 Adds memory information about the script's cache to INFO
Implementation notes: as INFO is "already broken", I didn't want to break it further. Instead of computing the server.lua_script dict size on every call, I'm keeping a running sum of the body's length and dict overheads.

This implementation is naive as it **does not** take into consideration dict rehashing, but that inaccuracy pays off in speed ;)

Demo time:

```bash
$ redis-cli info memory | grep "script"
used_memory_scripts:96
used_memory_scripts_human:96B
number_of_cached_scripts:0
$ redis-cli eval "" 0 ; redis-cli info memory | grep "script"
(nil)
used_memory_scripts:120
used_memory_scripts_human:120B
number_of_cached_scripts:1
$ redis-cli script flush ; redis-cli info memory | grep "script"
OK
used_memory_scripts:96
used_memory_scripts_human:96B
number_of_cached_scripts:0
$ redis-cli eval "return('Hello, Script Cache :)')" 0 ; redis-cli info memory | grep "script"
"Hello, Script Cache :)"
used_memory_scripts:152
used_memory_scripts_human:152B
number_of_cached_scripts:1
$ redis-cli eval "return redis.sha1hex(\"return('Hello, Script Cache :)')\")" 0 ; redis-cli info memory | grep "script"
"1be72729d43da5114929c1260a749073732dc822"
used_memory_scripts:232
used_memory_scripts_human:232B
number_of_cached_scripts:2
✔ 19:03:54 redis [lua_scripts-in-info-memory L ✚…⚑] $ redis-cli evalsha 1be72729d43da5114929c1260a749073732dc822 0
"Hello, Script Cache :)"
```
2018-04-30 19:33:01 +03:00
Chris Lamb 82ae4f30ed Use SOURCE_DATE_EPOCH over unreproducible uname + date calls.
See <https://reproducible-builds.org/specs/source-date-epoch/> for more
details.

Signed-off-by: Chris Lamb <chris@chris-lamb.co.uk>
2016-04-29 16:45:53 +01:00
Chris Lamb eaeba1b2c8 Tidy grammar in CONFIG SET maxmemory warning.
Signed-off-by: Chris Lamb <chris@chris-lamb.co.uk>
2015-02-04 18:37:34 +00:00
Chris Lamb 3447062d5e Make some defaults explicit in the sentinel.conf for package maintainers
This may look a little pointless (and it is a complete no-op change here)
but as package maintainers need to modify these lines to actually
daemonize (etc. etc) but it's far preferable if the diff is restricted to
actually changing just that bit, not adding docs, etc. The less diff the
better, in general.

Signed-off-by: Chris Lamb <chris@chris-lamb.co.uk>
2015-01-16 09:03:00 +00:00
38 changed files with 712 additions and 221 deletions
+36
View File
@@ -602,6 +602,26 @@ slave-priority 100
#
# maxmemory-samples 5
# Starting from Redis 5, by default a slave will ignore its maxmemory setting
# (unless it is promoted to master after a failover or manually). It means
# that the eviction of keys will be just handled by the master, sending the
# DEL commands to the slave as keys evict in the master side.
#
# This behavior ensures that masters and slaves stay consistent, and is usually
# what you want, however if your slave is writable, or you want the slave to have
# a different memory setting, and you are sure all the writes performed to the
# slave are idempotent, then you may change this default (but be sure to understand
# what you are doing).
#
# Note that since the slave by default does not evict, it may end using more
# memory than the one set via maxmemory (there are certain buffers that may
# be larger on the slave, or data structures may sometimes take more memory and so
# forth). So make sure you monitor your slaves and make sure they have enough
# memory to never hit a real out-of-memory condition before the master hits
# the configured maxmemory setting.
#
# slave-ingore-maxmemory yes
############################# LAZY FREEING ####################################
# Redis has two primitives to delete keys. One is called DEL and is a blocking
@@ -1205,6 +1225,22 @@ client-output-buffer-limit pubsub 32mb 8mb 60
# 100 only in environments where very low latency is required.
hz 10
# Normally it is useful to have an HZ value which is proportional to the
# number of clients connected. This is useful in order, for instance, to
# avoid too many clients are processed for each background task invocation
# in order to avoid latency spikes.
#
# Since the default HZ value by default is conservatively set to 10, Redis
# offers, and enables by default, the ability to use an adaptive HZ value
# which will temporary raise when there are many connected clients.
#
# When dynamic HZ is enabled, the actual configured HZ will be used as
# as a baseline, but multiples of the configured HZ value will be actually
# used as needed once more clients are connected. In this way an idle
# instance will use very little CPU time while a busy instance will be
# more responsive.
dynamic-hz yes
# When a child rewrites the AOF file, if the following option is enabled
# the file will be fsync-ed every 32 MB of data generated. This is useful
# in order to commit the file to the disk more incrementally and avoid
+1 -1
View File
@@ -11,4 +11,4 @@ then
echo "You need tcl 8.5 or newer in order to run the Redis test"
exit 1
fi
$TCLSH tests/test_helper.tcl $*
$TCLSH tests/test_helper.tcl "${@}"
+15
View File
@@ -20,6 +20,21 @@
# The port that this sentinel instance will run on
port 26379
# By default Redis Sentinel does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis-sentinel.pid when
# daemonized.
daemonize no
# When running daemonized, Redis Sentinel writes a pid file in
# /var/run/redis-sentinel.pid by default. You can specify a custom pid file
# location here.
pidfile /var/run/redis-sentinel.pid
# Specify the log file name. Also the empty string can be used to force
# Sentinel to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
+3 -1
View File
@@ -798,7 +798,9 @@ int loadAppendOnlyFile(char *filename) {
}
/* This point can only be reached when EOF is reached without errors.
* If the client is in the middle of a MULTI/EXEC, log error and quit. */
* If the client is in the middle of a MULTI/EXEC, handle it as it was
* a short read, even if technically the protocol is correct: we want
* to remove the unprocessed tail and continue. */
if (fakeClient->flags & CLIENT_MULTI) goto uxeof;
loaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */
+2 -2
View File
@@ -269,7 +269,7 @@ void handleClientsBlockedOnKeys(void) {
robj *dstkey = receiver->bpop.target;
int where = (receiver->lastcmd &&
receiver->lastcmd->proc == blpopCommand) ?
LIST_HEAD : LIST_TAIL;
LIST_HEAD : LIST_TAIL;
robj *value = listTypePop(o,where);
if (value) {
@@ -285,7 +285,7 @@ void handleClientsBlockedOnKeys(void) {
{
/* If we failed serving the client we need
* to also undo the POP operation. */
listTypePush(o,value,where);
listTypePush(o,value,where);
}
if (dstkey) decrRefCount(dstkey);
+21 -4
View File
@@ -3321,14 +3321,18 @@ void clusterCron(void) {
int changed = 0;
if (prev_ip == NULL && curr_ip != NULL) changed = 1;
if (prev_ip != NULL && curr_ip == NULL) changed = 1;
if (prev_ip && curr_ip && strcmp(prev_ip,curr_ip)) changed = 1;
else if (prev_ip != NULL && curr_ip == NULL) changed = 1;
else if (prev_ip && curr_ip && strcmp(prev_ip,curr_ip)) changed = 1;
if (changed) {
if (prev_ip) zfree(prev_ip);
prev_ip = curr_ip;
if (prev_ip) prev_ip = zstrdup(prev_ip);
if (curr_ip) {
/* We always take a copy of the previous IP address, by
* duplicating the string. This way later we can check if
* the address really changed. */
prev_ip = zstrdup(prev_ip);
strncpy(myself->ip,server.cluster_announce_ip,NET_IP_STR_LEN);
myself->ip[NET_IP_STR_LEN-1] = '\0';
} else {
@@ -5146,6 +5150,11 @@ try_again:
serverAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));
}
int expired = 0; /* Number of keys that we'll find already expired.
Note that serializing large keys may take some time
so certain keys that were found non expired by the
lookupKey() function, may be expired later. */
/* Create RESTORE payload and generate the protocol to call the command. */
for (j = 0; j < num_keys; j++) {
long long ttl = 0;
@@ -5153,6 +5162,10 @@ try_again:
if (expireat != -1) {
ttl = expireat-mstime();
if (ttl < 0) {
expired++;
continue;
}
if (ttl < 1) ttl = 1;
}
serverAssertWithInfo(c,NULL,
@@ -5217,9 +5230,13 @@ try_again:
int socket_error = 0;
int del_idx = 1; /* Index of the key argument for the replicated DEL op. */
/* Allocate the new argument vector that will replace the current command,
* to propagate the MIGRATE as a DEL command (if no COPY option was given).
* We allocate num_keys+1 because the additional argument is for "DEL"
* command name itself. */
if (!copy) newargv = zmalloc(sizeof(robj*)*(num_keys+1));
for (j = 0; j < num_keys; j++) {
for (j = 0; j < num_keys-expired; j++) {
if (syncReadLine(cs->fd, buf2, sizeof(buf2), timeout) <= 0) {
socket_error = 1;
break;
+27 -9
View File
@@ -399,6 +399,10 @@ void loadServerConfigFromString(char *config) {
if ((server.repl_slave_ro = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"slave-ignore-maxmemory") && argc == 2) {
if ((server.repl_slave_ignore_maxmemory = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
if ((server.rdb_compression = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
@@ -440,10 +444,14 @@ void loadServerConfigFromString(char *config) {
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"dynamic-hz") && argc == 2) {
if ((server.dynamic_hz = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"hz") && argc == 2) {
server.hz = atoi(argv[1]);
if (server.hz < CONFIG_MIN_HZ) server.hz = CONFIG_MIN_HZ;
if (server.hz > CONFIG_MAX_HZ) server.hz = CONFIG_MAX_HZ;
server.config_hz = atoi(argv[1]);
if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
} else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
int yes;
@@ -1043,6 +1051,8 @@ void configSetCommand(client *c) {
"slave-serve-stale-data",server.repl_serve_stale_data) {
} config_set_bool_field(
"slave-read-only",server.repl_slave_ro) {
} config_set_bool_field(
"slave-ignore-maxmemory",server.repl_slave_ignore_maxmemory) {
} config_set_bool_field(
"activerehashing",server.activerehashing) {
} config_set_bool_field(
@@ -1072,6 +1082,8 @@ void configSetCommand(client *c) {
"slave-lazy-flush",server.repl_slave_lazy_flush) {
} config_set_bool_field(
"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite) {
} config_set_bool_field(
"dynamic-hz",server.dynamic_hz) {
/* Numerical fields.
* config_set_numerical_field(name,var,min,max) */
@@ -1158,11 +1170,11 @@ void configSetCommand(client *c) {
} config_set_numerical_field(
"cluster-slave-validity-factor",server.cluster_slave_validity_factor,0,INT_MAX) {
} config_set_numerical_field(
"hz",server.hz,0,INT_MAX) {
"hz",server.config_hz,0,INT_MAX) {
/* Hz is more an hint from the user, so we accept values out of range
* but cap them to reasonable values. */
if (server.hz < CONFIG_MIN_HZ) server.hz = CONFIG_MIN_HZ;
if (server.hz > CONFIG_MAX_HZ) server.hz = CONFIG_MAX_HZ;
if (server.config_hz < CONFIG_MIN_HZ) server.config_hz = CONFIG_MIN_HZ;
if (server.config_hz > CONFIG_MAX_HZ) server.config_hz = CONFIG_MAX_HZ;
} config_set_numerical_field(
"watchdog-period",ll,0,INT_MAX) {
if (ll)
@@ -1175,7 +1187,7 @@ void configSetCommand(client *c) {
} config_set_memory_field("maxmemory",server.maxmemory) {
if (server.maxmemory) {
if (server.maxmemory < zmalloc_used_memory()) {
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy.");
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.");
}
freeMemoryIfNeeded();
}
@@ -1329,7 +1341,7 @@ void configGetCommand(client *c) {
config_get_numerical_field("slave-announce-port",server.slave_announce_port);
config_get_numerical_field("min-slaves-to-write",server.repl_min_slaves_to_write);
config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
config_get_numerical_field("hz",server.hz);
config_get_numerical_field("hz",server.config_hz);
config_get_numerical_field("cluster-node-timeout",server.cluster_node_timeout);
config_get_numerical_field("cluster-migration-barrier",server.cluster_migration_barrier);
config_get_numerical_field("cluster-slave-validity-factor",server.cluster_slave_validity_factor);
@@ -1347,6 +1359,8 @@ void configGetCommand(client *c) {
server.repl_serve_stale_data);
config_get_bool_field("slave-read-only",
server.repl_slave_ro);
config_get_bool_field("slave-ignore-maxmemory",
server.repl_slave_ignore_maxmemory);
config_get_bool_field("stop-writes-on-bgsave-error",
server.stop_writes_on_bgsave_err);
config_get_bool_field("daemonize", server.daemonize);
@@ -1375,6 +1389,8 @@ void configGetCommand(client *c) {
server.lazyfree_lazy_server_del);
config_get_bool_field("slave-lazy-flush",
server.repl_slave_lazy_flush);
config_get_bool_field("dynamic-hz",
server.dynamic_hz);
/* Enum values */
config_get_enum_field("maxmemory-policy",
@@ -2041,6 +2057,7 @@ int rewriteConfig(char *path) {
rewriteConfigStringOption(state,"cluster-announce-ip",server.cluster_announce_ip,NULL);
rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA);
rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,CONFIG_DEFAULT_SLAVE_READ_ONLY);
rewriteConfigYesNoOption(state,"slave-ignore-maxmemory",server.repl_slave_ignore_maxmemory,CONFIG_DEFAULT_SLAVE_IGNORE_MAXMEMORY);
rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,CONFIG_DEFAULT_REPL_PING_SLAVE_PERIOD);
rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,CONFIG_DEFAULT_REPL_TIMEOUT);
rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,CONFIG_DEFAULT_REPL_BACKLOG_SIZE);
@@ -2098,7 +2115,7 @@ int rewriteConfig(char *path) {
rewriteConfigYesNoOption(state,"activedefrag",server.active_defrag_enabled,CONFIG_DEFAULT_ACTIVE_DEFRAG);
rewriteConfigYesNoOption(state,"protected-mode",server.protected_mode,CONFIG_DEFAULT_PROTECTED_MODE);
rewriteConfigClientoutputbufferlimitOption(state);
rewriteConfigNumericalOption(state,"hz",server.hz,CONFIG_DEFAULT_HZ);
rewriteConfigNumericalOption(state,"hz",server.config_hz,CONFIG_DEFAULT_HZ);
rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
rewriteConfigYesNoOption(state,"rdb-save-incremental-fsync",server.rdb_save_incremental_fsync,CONFIG_DEFAULT_RDB_SAVE_INCREMENTAL_FSYNC);
rewriteConfigYesNoOption(state,"aof-load-truncated",server.aof_load_truncated,CONFIG_DEFAULT_AOF_LOAD_TRUNCATED);
@@ -2108,6 +2125,7 @@ int rewriteConfig(char *path) {
rewriteConfigYesNoOption(state,"lazyfree-lazy-expire",server.lazyfree_lazy_expire,CONFIG_DEFAULT_LAZYFREE_LAZY_EXPIRE);
rewriteConfigYesNoOption(state,"lazyfree-lazy-server-del",server.lazyfree_lazy_server_del,CONFIG_DEFAULT_LAZYFREE_LAZY_SERVER_DEL);
rewriteConfigYesNoOption(state,"slave-lazy-flush",server.repl_slave_lazy_flush,CONFIG_DEFAULT_SLAVE_LAZY_FLUSH);
rewriteConfigYesNoOption(state,"dynamic-hz",server.dynamic_hz,CONFIG_DEFAULT_DYNAMIC_HZ);
/* Rewrite Sentinel config if in Sentinel mode. */
if (server.sentinel_mode) rewriteConfigSentinelOption(state);
+21 -9
View File
@@ -184,14 +184,19 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
dictEntry *de = dictFind(db->dict,key->ptr);
serverAssertWithInfo(NULL,key,de != NULL);
dictEntry auxentry = *de;
robj *old = dictGetVal(de);
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
robj *old = dictGetVal(de);
int saved_lru = old->lru;
dictReplace(db->dict, key->ptr, val);
val->lru = saved_lru;
} else {
dictReplace(db->dict, key->ptr, val);
val->lru = old->lru;
}
dictSetVal(db->dict, de, val);
if (server.lazyfree_lazy_server_del) {
freeObjAsync(old);
dictSetVal(db->dict, &auxentry, NULL);
}
dictFreeVal(db->dict, &auxentry);
}
/* High level Set operation. This function can be used in order to set
@@ -329,7 +334,7 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
* database(s). Otherwise -1 is returned in the specific case the
* DB number is out of range, and errno is set to EINVAL. */
long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
int j, async = (flags & EMPTYDB_ASYNC);
int async = (flags & EMPTYDB_ASYNC);
long long removed = 0;
if (dbnum < -1 || dbnum >= server.dbnum) {
@@ -337,8 +342,15 @@ long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
return -1;
}
for (j = 0; j < server.dbnum; j++) {
if (dbnum != -1 && dbnum != j) continue;
int startdb, enddb;
if (dbnum == -1) {
startdb = 0;
enddb = server.dbnum-1;
} else {
startdb = enddb = dbnum;
}
for (int j = startdb; j <= enddb; j++) {
removed += dictSize(server.db[j].dict);
if (async) {
emptyDbAsync(&server.db[j]);
+4
View File
@@ -290,6 +290,7 @@ void debugCommand(client *c) {
"CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.",
"DIGEST -- Output a hex signature representing the current DB content.",
"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
"LOG <message> -- write message to the server log.",
"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
"HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.",
"LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.",
@@ -333,6 +334,9 @@ NULL
} else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
serverAssertWithInfo(c,c->argv[0],1 == 2);
} else if (!strcasecmp(c->argv[1]->ptr,"log") && c->argc == 3) {
serverLog(LL_WARNING, "DEBUG LOG: %s", (char*)c->argv[2]->ptr);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
+4
View File
@@ -444,6 +444,10 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
* Otehrwise if we are over the memory limit, but not enough memory
* was freed to return back under the limit, the function returns C_ERR. */
int freeMemoryIfNeeded(void) {
/* By default slaves should ignore maxmemory and just be masters excat
* copies. */
if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK;
size_t mem_reported, mem_tofree, mem_freed;
mstime_t latency, eviction_latency;
long long delta;
+11
View File
@@ -90,6 +90,17 @@ int dbAsyncDelete(redisDb *db, robj *key) {
}
}
/* Free an object, if the object is huge enough, free it in async way. */
void freeObjAsync(robj *o) {
size_t free_effort = lazyfreeGetFreeEffort(o);
if (free_effort > LAZYFREE_THRESHOLD && o->refcount == 1) {
atomicIncr(lazyfree_objects,1);
bioCreateBackgroundJob(BIO_LAZY_FREE,o,NULL,NULL);
} else {
decrRefCount(o);
}
}
/* Empty a Redis DB asynchronously. What the function does actually is to
* create a new empty set of hash tables and scheduling the old ones for
* lazy freeing. */
+3
View File
@@ -2,6 +2,9 @@
GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1`
GIT_DIRTY=`git diff --no-ext-diff 2> /dev/null | wc -l`
BUILD_ID=`uname -n`"-"`date +%s`
if [ -n "$SOURCE_DATE_EPOCH" ]; then
BUILD_ID=$(date -u -d "@$SOURCE_DATE_EPOCH" +%s 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" +%s 2>/dev/null || date -u %s)
fi
test -f release.h || touch release.h
(cat release.h | grep SHA1 | grep $GIT_SHA1) && \
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date
+13 -13
View File
@@ -548,7 +548,7 @@ void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {
ctx->keys_pos[ctx->keys_count++] = pos;
}
/* Helper for RM_CreateCommand(). Truns a string representing command
/* Helper for RM_CreateCommand(). Turns a string representing command
* flags into the command flags used by the Redis core.
*
* It returns the set of flags, or -1 if unknown flags are found. */
@@ -595,7 +595,7 @@ int commandFlagsFromString(char *s) {
* And is supposed to always return REDISMODULE_OK.
*
* The set of flags 'strflags' specify the behavior of the command, and should
* be passed as a C string compoesd of space separated words, like for
* be passed as a C string composed of space separated words, like for
* example "write deny-oom". The set of flags are:
*
* * **"write"**: The command may modify the data set (it may also read
@@ -616,7 +616,7 @@ int commandFlagsFromString(char *s) {
* * **"allow-stale"**: The command is allowed to run on slaves that don't
* serve stale data. Don't use if you don't know what
* this means.
* * **"no-monitor"**: Don't propoagate the command on monitor. Use this if
* * **"no-monitor"**: Don't propagate the command on monitor. Use this if
* the command has sensible data among the arguments.
* * **"fast"**: The command time complexity is not greater
* than O(log(N)) where N is the size of the collection or
@@ -956,9 +956,9 @@ RedisModuleString *moduleAssertUnsharedString(RedisModuleString *str) {
return str;
}
/* Append the specified buffere to the string 'str'. The string must be a
/* Append the specified buffer to the string 'str'. The string must be a
* string created by the user that is referenced only a single time, otherwise
* REDISMODULE_ERR is returend and the operation is not performed. */
* REDISMODULE_ERR is returned and the operation is not performed. */
int RM_StringAppendBuffer(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) {
UNUSED(ctx);
str = moduleAssertUnsharedString(str);
@@ -1118,7 +1118,7 @@ int RM_ReplyWithArray(RedisModuleCtx *ctx, long len) {
*
* Note that in the above example there is no reason to postpone the array
* length, since we produce a fixed number of elements, but in the practice
* the code may use an interator or other ways of creating the output so
* the code may use an iterator or other ways of creating the output so
* that is not easy to calculate in advance the number of elements.
*/
void RM_ReplySetArrayLength(RedisModuleCtx *ctx, long len) {
@@ -1410,7 +1410,7 @@ int RM_SelectDb(RedisModuleCtx *ctx, int newid) {
* to call other APIs with the key handle as argument to perform
* operations on the key.
*
* The return value is the handle repesenting the key, that must be
* The return value is the handle representing the key, that must be
* closed with RM_CloseKey().
*
* If the key does not exist and WRITE mode is requested, the handle
@@ -1664,7 +1664,7 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) {
* Key API for List type
* -------------------------------------------------------------------------- */
/* Push an element into a list, on head or tail depending on 'where' argumnet.
/* Push an element into a list, on head or tail depending on 'where' argument.
* If the key pointer is about an empty key opened for writing, the key
* is created. On error (key opened for read-only operations or of the wrong
* type) REDISMODULE_ERR is returned, otherwise REDISMODULE_OK is returned. */
@@ -1769,7 +1769,7 @@ int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *f
* The input and output flags, and the return value, have the same exact
* meaning, with the only difference that this function will return
* REDISMODULE_ERR even when 'score' is a valid double number, but adding it
* to the existing score resuts into a NaN (not a number) condition.
* to the existing score results into a NaN (not a number) condition.
*
* This function has an additional field 'newscore', if not NULL is filled
* with the new score of the element after the increment, if no error
@@ -3793,11 +3793,11 @@ void moduleReleaseGIL(void) {
* -------------------------------------------------------------------------- */
/* Subscribe to keyspace notifications. This is a low-level version of the
* keyspace-notifications API. A module cand register callbacks to be notified
* keyspace-notifications API. A module can register callbacks to be notified
* when keyspce events occur.
*
* Notification events are filtered by their type (string events, set events,
* etc), and the subsriber callback receives only events that match a specific
* etc), and the subscriber callback receives only events that match a specific
* mask of event types.
*
* When subscribing to notifications with RedisModule_SubscribeToKeyspaceEvents
@@ -3832,7 +3832,7 @@ void moduleReleaseGIL(void) {
* used to send anything to the client, and has the db number where the event
* occurred as its selected db number.
*
* Notice that it is not necessary to enable norifications in redis.conf for
* Notice that it is not necessary to enable notifications in redis.conf for
* module notifications to work.
*
* Warning: the notification callbacks are performed in a synchronous manner,
@@ -3876,7 +3876,7 @@ void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid)
ctx.client = moduleKeyspaceSubscribersClient;
selectDb(ctx.client, dbid);
/* mark the handler as activer to avoid reentrant loops.
/* mark the handler as active to avoid reentrant loops.
* If the subscriber performs an action triggering itself,
* it will not be notified about it. */
sub->active = 1;
+61 -55
View File
@@ -33,7 +33,7 @@
#include <math.h>
#include <ctype.h>
static void setProtocolError(const char *errstr, client *c, long pos);
static void setProtocolError(const char *errstr, client *c);
/* Return the size consumed from the allocator, for the specified SDS string,
* including internal fragmentation. This function is used in order to compute
@@ -110,6 +110,7 @@ client *createClient(int fd) {
c->fd = fd;
c->name = NULL;
c->bufpos = 0;
c->qb_pos = 0;
c->querybuf = sdsempty();
c->pending_querybuf = sdsempty();
c->querybuf_peak = 0;
@@ -826,8 +827,7 @@ void freeClient(client *c) {
serverLog(LL_WARNING,"Connection with master lost.");
if (!(c->flags & (CLIENT_CLOSE_AFTER_REPLY|
CLIENT_CLOSE_ASAP|
CLIENT_BLOCKED|
CLIENT_UNBLOCKED)))
CLIENT_BLOCKED)))
{
replicationCacheMaster(c);
return;
@@ -1119,29 +1119,29 @@ int processInlineBuffer(client *c) {
size_t querylen;
/* Search for end of line */
newline = strchr(c->querybuf,'\n');
newline = strchr(c->querybuf+c->qb_pos,'\n');
/* Nothing to do without a \r\n */
if (newline == NULL) {
if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {
addReplyError(c,"Protocol error: too big inline request");
setProtocolError("too big inline request",c,0);
setProtocolError("too big inline request",c);
}
return C_ERR;
}
/* Handle the \r\n case. */
if (newline && newline != c->querybuf && *(newline-1) == '\r')
if (newline && newline != c->querybuf+c->qb_pos && *(newline-1) == '\r')
newline--, linefeed_chars++;
/* Split the input buffer up to the \r\n */
querylen = newline-(c->querybuf);
aux = sdsnewlen(c->querybuf,querylen);
querylen = newline-(c->querybuf+c->qb_pos);
aux = sdsnewlen(c->querybuf+c->qb_pos,querylen);
argv = sdssplitargs(aux,&argc);
sdsfree(aux);
if (argv == NULL) {
addReplyError(c,"Protocol error: unbalanced quotes in request");
setProtocolError("unbalanced quotes in inline request",c,0);
setProtocolError("unbalanced quotes in inline request",c);
return C_ERR;
}
@@ -1151,8 +1151,8 @@ int processInlineBuffer(client *c) {
if (querylen == 0 && c->flags & CLIENT_SLAVE)
c->repl_ack_time = server.unixtime;
/* Leave data after the first line of the query in the buffer */
sdsrange(c->querybuf,querylen+linefeed_chars,-1);
/* Move querybuffer position to the next query in the buffer. */
c->qb_pos += querylen+linefeed_chars;
/* Setup argv array on client structure */
if (argc) {
@@ -1173,19 +1173,19 @@ int processInlineBuffer(client *c) {
return C_OK;
}
/* Helper function. Trims query buffer to make the function that processes
* multi bulk requests idempotent. */
/* Helper function. Record protocol erro details in server log,
* and set the client as CLIENT_CLOSE_AFTER_REPLY. */
#define PROTO_DUMP_LEN 128
static void setProtocolError(const char *errstr, client *c, long pos) {
static void setProtocolError(const char *errstr, client *c) {
if (server.verbosity <= LL_VERBOSE) {
sds client = catClientInfoString(sdsempty(),c);
/* Sample some protocol to given an idea about what was inside. */
char buf[256];
if (sdslen(c->querybuf) < PROTO_DUMP_LEN) {
snprintf(buf,sizeof(buf),"Query buffer during protocol error: '%s'", c->querybuf);
if (sdslen(c->querybuf)-c->qb_pos < PROTO_DUMP_LEN) {
snprintf(buf,sizeof(buf),"Query buffer during protocol error: '%s'", c->querybuf+c->qb_pos);
} else {
snprintf(buf,sizeof(buf),"Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s'", PROTO_DUMP_LEN/2, c->querybuf, sdslen(c->querybuf)-PROTO_DUMP_LEN, PROTO_DUMP_LEN/2, c->querybuf+sdslen(c->querybuf)-PROTO_DUMP_LEN/2);
snprintf(buf,sizeof(buf),"Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s'", PROTO_DUMP_LEN/2, c->querybuf+c->qb_pos, sdslen(c->querybuf)-c->qb_pos-PROTO_DUMP_LEN, PROTO_DUMP_LEN/2, c->querybuf+sdslen(c->querybuf)-PROTO_DUMP_LEN/2);
}
/* Remove non printable chars. */
@@ -1201,7 +1201,6 @@ static void setProtocolError(const char *errstr, client *c, long pos) {
sdsfree(client);
}
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
sdsrange(c->querybuf,pos,-1);
}
/* Process the query buffer for client 'c', setting up the client argument
@@ -1217,7 +1216,6 @@ static void setProtocolError(const char *errstr, client *c, long pos) {
* to be '*'. Otherwise for inline commands processInlineBuffer() is called. */
int processMultibulkBuffer(client *c) {
char *newline = NULL;
long pos = 0;
int ok;
long long ll;
@@ -1226,34 +1224,32 @@ int processMultibulkBuffer(client *c) {
serverAssertWithInfo(c,NULL,c->argc == 0);
/* Multi bulk length cannot be read without a \r\n */
newline = strchr(c->querybuf,'\r');
newline = strchr(c->querybuf+c->qb_pos,'\r');
if (newline == NULL) {
if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {
addReplyError(c,"Protocol error: too big mbulk count string");
setProtocolError("too big mbulk count string",c,0);
setProtocolError("too big mbulk count string",c);
}
return C_ERR;
}
/* Buffer should also contain \n */
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))
return C_ERR;
/* We know for sure there is a whole line since newline != NULL,
* so go ahead and find out the multi bulk length. */
serverAssertWithInfo(c,NULL,c->querybuf[0] == '*');
ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);
serverAssertWithInfo(c,NULL,c->querybuf[c->qb_pos] == '*');
ok = string2ll(c->querybuf+1+c->qb_pos,newline-(c->querybuf+1+c->qb_pos),&ll);
if (!ok || ll > 1024*1024) {
addReplyError(c,"Protocol error: invalid multibulk length");
setProtocolError("invalid mbulk count",c,pos);
setProtocolError("invalid mbulk count",c);
return C_ERR;
}
pos = (newline-c->querybuf)+2;
if (ll <= 0) {
sdsrange(c->querybuf,pos,-1);
return C_OK;
}
c->qb_pos = (newline-c->querybuf)+2;
if (ll <= 0) return C_OK;
c->multibulklen = ll;
@@ -1266,37 +1262,37 @@ int processMultibulkBuffer(client *c) {
while(c->multibulklen) {
/* Read bulk length if unknown */
if (c->bulklen == -1) {
newline = strchr(c->querybuf+pos,'\r');
newline = strchr(c->querybuf+c->qb_pos,'\r');
if (newline == NULL) {
if (sdslen(c->querybuf) > PROTO_INLINE_MAX_SIZE) {
if (sdslen(c->querybuf)-c->qb_pos > PROTO_INLINE_MAX_SIZE) {
addReplyError(c,
"Protocol error: too big bulk count string");
setProtocolError("too big bulk count string",c,0);
setProtocolError("too big bulk count string",c);
return C_ERR;
}
break;
}
/* Buffer should also contain \n */
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
if (newline-(c->querybuf+c->qb_pos) > (ssize_t)(sdslen(c->querybuf)-c->qb_pos-2))
break;
if (c->querybuf[pos] != '$') {
if (c->querybuf[c->qb_pos] != '$') {
addReplyErrorFormat(c,
"Protocol error: expected '$', got '%c'",
c->querybuf[pos]);
setProtocolError("expected $ but got something else",c,pos);
c->querybuf[c->qb_pos]);
setProtocolError("expected $ but got something else",c);
return C_ERR;
}
ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);
ok = string2ll(c->querybuf+c->qb_pos+1,newline-(c->querybuf+c->qb_pos+1),&ll);
if (!ok || ll < 0 || ll > server.proto_max_bulk_len) {
addReplyError(c,"Protocol error: invalid bulk length");
setProtocolError("invalid bulk length",c,pos);
setProtocolError("invalid bulk length",c);
return C_ERR;
}
pos += newline-(c->querybuf+pos)+2;
c->qb_pos = newline-c->querybuf+2;
if (ll >= PROTO_MBULK_BIG_ARG) {
size_t qblen;
@@ -1304,8 +1300,8 @@ int processMultibulkBuffer(client *c) {
* try to make it likely that it will start at c->querybuf
* boundary so that we can optimize object creation
* avoiding a large copy of data. */
sdsrange(c->querybuf,pos,-1);
pos = 0;
sdsrange(c->querybuf,c->qb_pos,-1);
c->qb_pos = 0;
qblen = sdslen(c->querybuf);
/* Hint the sds library about the amount of bytes this string is
* going to contain. */
@@ -1316,14 +1312,14 @@ int processMultibulkBuffer(client *c) {
}
/* Read bulk argument */
if (sdslen(c->querybuf)-pos < (size_t)(c->bulklen+2)) {
if (sdslen(c->querybuf)-c->qb_pos < (size_t)(c->bulklen+2)) {
/* Not enough data (+2 == trailing \r\n) */
break;
} else {
/* Optimization: if the buffer contains JUST our bulk element
* instead of creating a new object by *copying* the sds we
* just use the current sds string. */
if (pos == 0 &&
if (c->qb_pos == 0 &&
c->bulklen >= PROTO_MBULK_BIG_ARG &&
sdslen(c->querybuf) == (size_t)(c->bulklen+2))
{
@@ -1333,20 +1329,16 @@ int processMultibulkBuffer(client *c) {
* likely... */
c->querybuf = sdsnewlen(SDS_NOINIT,c->bulklen+2);
sdsclear(c->querybuf);
pos = 0;
} else {
c->argv[c->argc++] =
createStringObject(c->querybuf+pos,c->bulklen);
pos += c->bulklen+2;
createStringObject(c->querybuf+c->qb_pos,c->bulklen);
c->qb_pos += c->bulklen+2;
}
c->bulklen = -1;
c->multibulklen--;
}
}
/* Trim to pos */
if (pos) sdsrange(c->querybuf,pos,-1);
/* We're done when c->multibulk == 0 */
if (c->multibulklen == 0) return C_OK;
@@ -1360,14 +1352,21 @@ int processMultibulkBuffer(client *c) {
* pending query buffer, already representing a full command, to process. */
void processInputBuffer(client *c) {
server.current_client = c;
/* Keep processing while there is something in the input buffer */
while(sdslen(c->querybuf)) {
while(c->qb_pos < sdslen(c->querybuf)) {
/* Return if clients are paused. */
if (!(c->flags & CLIENT_SLAVE) && clientsArePaused()) break;
/* Immediately abort if the client is in the middle of something. */
if (c->flags & CLIENT_BLOCKED) break;
/* Don't process input from the master while there is a busy script
* condition on the slave. We want just to accumulate the replication
* stream (instead of replying -BUSY like we do with other clients) and
* later resume the processing. */
if (server.lua_timedout && c->flags & CLIENT_MASTER) break;
/* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is
* written to the client. Make sure to not let the reply grow after
* this flag has been set (i.e. don't process more commands).
@@ -1377,7 +1376,7 @@ void processInputBuffer(client *c) {
/* Determine request type when unknown. */
if (!c->reqtype) {
if (c->querybuf[0] == '*') {
if (c->querybuf[c->qb_pos] == '*') {
c->reqtype = PROTO_REQ_MULTIBULK;
} else {
c->reqtype = PROTO_REQ_INLINE;
@@ -1400,7 +1399,7 @@ void processInputBuffer(client *c) {
if (processCommand(c) == C_OK) {
if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
/* Update the applied replication offset of our master. */
c->reploff = c->read_reploff - sdslen(c->querybuf);
c->reploff = c->read_reploff - sdslen(c->querybuf) + c->qb_pos;
}
/* Don't reset the client structure for clients blocked in a
@@ -1416,6 +1415,13 @@ void processInputBuffer(client *c) {
if (server.current_client == NULL) break;
}
}
/* Trim to pos */
if (c->qb_pos) {
sdsrange(c->querybuf,c->qb_pos,-1);
c->qb_pos = 0;
}
server.current_client = NULL;
}
+27 -2
View File
@@ -1017,6 +1017,18 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mh->aof_buffer = mem;
mem_total+=mem;
mem = 0;
mem += dictSize(server.lua_scripts) * sizeof(dictEntry) +
dictSlots(server.lua_scripts) * sizeof(dictEntry*);
mem += dictSize(server.repl_scriptcache_dict) * sizeof(dictEntry) +
dictSlots(server.repl_scriptcache_dict) * sizeof(dictEntry*);
if (listLength(server.repl_scriptcache_fifo) > 0) {
mem += listLength(server.repl_scriptcache_fifo) * (sizeof(listNode) +
sdsZmallocSize(listNodeValue(listFirst(server.repl_scriptcache_fifo))));
}
mh->lua_caches = mem;
mem_total+=mem;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
long long keyscount = dictSize(db->dict);
@@ -1074,6 +1086,7 @@ sds getMemoryDoctorReport(void) {
int high_alloc_rss = 0; /* High rss overhead. */
int big_slave_buf = 0; /* Slave buffers are too big. */
int big_client_buf = 0; /* Client buffers are too big. */
int many_scripts = 0; /* Script cache has too many scripts. */
int num_reports = 0;
struct redisMemOverhead *mh = getMemoryOverheadData();
@@ -1124,6 +1137,12 @@ sds getMemoryDoctorReport(void) {
big_slave_buf = 1;
num_reports++;
}
/* Too many scripts are cached? */
if (dictSize(server.lua_scripts) > 1000) {
many_scripts = 1;
num_reports++;
}
}
sds s;
@@ -1153,7 +1172,7 @@ sds getMemoryDoctorReport(void) {
s = sdscatprintf(s," * High allocator RSS overhead: This instance has an RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the allocator is much larger than the sum what the allocator actually holds). This problem is usually due to a large peak memory (check if there is a peak memory entry above in the report), you can try the MEMORY PURGE command to reclaim it.\n\n");
}
if (high_proc_rss) {
s = sdscatprintf(s," * High process RSS overhead: This instance has non-allocator RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the Redis process is much larger than the RSS the allocator holds). This problem may be due to LUA scripts or Modules.\n\n");
s = sdscatprintf(s," * High process RSS overhead: This instance has non-allocator RSS memory overhead is greater than 1.1 (this means that the Resident Set Size of the Redis process is much larger than the RSS the allocator holds). This problem may be due to Lua scripts or Modules.\n\n");
}
if (big_slave_buf) {
s = sdscat(s," * Big slave buffers: The slave output buffers in this instance are greater than 10MB for each slave (on average). This likely means that there is some slave instance that is struggling receiving data, either because it is too slow or because of networking issues. As a result, data piles on the master output buffers. Please try to identify what slave is not receiving data correctly and why. You can use the INFO output in order to check the slaves delays and the CLIENT LIST command to check the output buffers of each slave.\n\n");
@@ -1161,6 +1180,9 @@ sds getMemoryDoctorReport(void) {
if (big_client_buf) {
s = sdscat(s," * Big client buffers: The clients output buffers in this instance are greater than 200K per client (on average). This may result from different causes, like Pub/Sub clients subscribed to channels bot not receiving data fast enough, so that data piles on the Redis instance output buffer, or clients sending commands with large replies or very large sequences of commands in the same pipeline. Please use the CLIENT LIST command in order to investigate the issue if it causes problems in your instance, or to understand better why certain clients are using a big amount of memory.\n\n");
}
if (many_scripts) {
s = sdscat(s," * Many scripts: There seem to be many cached scripts in this instance (more than 1000). This may be because scripts are generated and `EVAL`ed, instead of being parameterized (with KEYS and ARGV), `SCRIPT LOAD`ed and `EVALSHA`ed. Unless `SCRIPT FLUSH` is called periodically, the scripts' caches may end up consuming most of your memory.\n\n");
}
s = sdscat(s,"I'm here to keep you safe, Sam. I want to help you.\n");
}
freeMemoryOverheadData(mh);
@@ -1293,7 +1315,7 @@ void memoryCommand(client *c) {
} else if (!strcasecmp(c->argv[1]->ptr,"stats") && c->argc == 2) {
struct redisMemOverhead *mh = getMemoryOverheadData();
addReplyMultiBulkLen(c,(24+mh->num_dbs)*2);
addReplyMultiBulkLen(c,(25+mh->num_dbs)*2);
addReplyBulkCString(c,"peak.allocated");
addReplyLongLong(c,mh->peak_allocated);
@@ -1316,6 +1338,9 @@ void memoryCommand(client *c) {
addReplyBulkCString(c,"aof.buffer");
addReplyLongLong(c,mh->aof_buffer);
addReplyBulkCString(c,"lua.caches");
addReplyLongLong(c,mh->lua_caches);
for (size_t j = 0; j < mh->num_dbs; j++) {
char dbname[32];
snprintf(dbname,sizeof(dbname),"db.%zd",mh->db[j].dbid);
+25 -11
View File
@@ -483,6 +483,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
* command marked as non-deterministic was already called in the context
* of this script. */
if (cmd->flags & CMD_WRITE) {
int deny_write_type = writeCommandsDeniedByDiskError();
if (server.lua_random_dirty && !server.lua_replicate_commands) {
luaPushError(lua,
"Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.");
@@ -493,11 +494,16 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
{
luaPushError(lua, shared.roslaveerr->ptr);
goto cleanup;
} else if (server.stop_writes_on_bgsave_err &&
server.saveparamslen > 0 &&
server.lastbgsave_status == C_ERR)
{
luaPushError(lua, shared.bgsaveerr->ptr);
} else if (deny_write_type != DISK_ERROR_TYPE_NONE) {
if (deny_write_type == DISK_ERROR_TYPE_RDB) {
luaPushError(lua, shared.bgsaveerr->ptr);
} else {
sds aof_write_err = sdscatfmt(sdsempty(),
"-MISCONF Errors writing to the AOF file: %s\r\n",
strerror(server.aof_last_write_errno));
luaPushError(lua, aof_write_err);
sdsfree(aof_write_err);
}
goto cleanup;
}
}
@@ -919,6 +925,7 @@ void scriptingInit(int setup) {
* This is useful for replication, as we need to replicate EVALSHA
* as EVAL, so we need to remember the associated script. */
server.lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);
server.lua_scripts_mem = 0;
/* Register the redis commands table and fields */
lua_newtable(lua);
@@ -1073,6 +1080,7 @@ void scriptingInit(int setup) {
* This function is used in order to reset the scripting environment. */
void scriptingRelease(void) {
dictRelease(server.lua_scripts);
server.lua_scripts_mem = 0;
lua_close(server.lua);
}
@@ -1207,17 +1215,19 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) {
* EVALSHA commands as EVAL using the original script. */
int retval = dictAdd(server.lua_scripts,sha,body);
serverAssertWithInfo(c ? c : server.lua_client,NULL,retval == DICT_OK);
server.lua_scripts_mem += sdsZmallocSize(sha) + sdsZmallocSize(body->ptr);
incrRefCount(body);
return sha;
}
/* This is the Lua script "count" hook that we use to detect scripts timeout. */
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
long long elapsed;
long long elapsed = mstime() - server.lua_time_start;
UNUSED(ar);
UNUSED(lua);
elapsed = mstime() - server.lua_time_start;
/* Set the timeout condition if not already set and the maximum
* execution time was reached. */
if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
serverLog(LL_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
server.lua_timedout = 1;
@@ -1336,9 +1346,7 @@ void evalGenericCommand(client *c, int evalsha) {
server.lua_caller = c;
server.lua_time_start = mstime();
server.lua_kill = 0;
if (server.lua_time_limit > 0 && server.masterhost == NULL &&
ldb.active == 0)
{
if (server.lua_time_limit > 0 && ldb.active == 0) {
lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
delhook = 1;
} else if (ldb.active) {
@@ -1359,6 +1367,10 @@ void evalGenericCommand(client *c, int evalsha) {
* script timeout was detected. */
aeCreateFileEvent(server.el,c->fd,AE_READABLE,
readQueryFromClient,c);
if (server.masterhost && server.master) {
server.master->flags |= CLIENT_UNBLOCKED;
listAddNodeTail(server.unblocked_clients,server.master);
}
}
server.lua_caller = NULL;
@@ -1488,6 +1500,8 @@ NULL
} else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"kill")) {
if (server.lua_caller == NULL) {
addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n"));
} else if (server.lua_caller->flags & CLIENT_MASTER) {
addReplySds(c,sdsnew("-UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed.\r\n"));
} else if (server.lua_write_dirty) {
addReplySds(c,sdsnew("-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.\r\n"));
} else {
@@ -1716,7 +1730,7 @@ int ldbRemoveChild(pid_t pid) {
return 0;
}
/* Return the number of children we still did not received termination
/* Return the number of children we still did not receive termination
* acknowledge via wait() in the parent process. */
int ldbPendingChildren(void) {
return listLength(ldb.children);
+3 -3
View File
@@ -2628,7 +2628,7 @@ int sentinelSendPing(sentinelRedisInstance *ri) {
ri->link->last_ping_time = mstime();
/* We update the active ping time only if we received the pong for
* the previous ping, otherwise we are technically waiting since the
* first ping that did not received a reply. */
* first ping that did not receive a reply. */
if (ri->link->act_ping_time == 0)
ri->link->act_ping_time = ri->link->last_ping_time;
return 1;
@@ -3569,7 +3569,7 @@ void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {
(mstime() - ri->link->cc_conn_time) >
SENTINEL_MIN_LINK_RECONNECT_PERIOD &&
ri->link->act_ping_time != 0 && /* There is a pending ping... */
/* The pending ping is delayed, and we did not received
/* The pending ping is delayed, and we did not receive
* error replies as well. */
(mstime() - ri->link->act_ping_time) > (ri->down_after_period/2) &&
(mstime() - ri->link->last_pong_time) > (ri->down_after_period/2))
@@ -3725,7 +3725,7 @@ void sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int f
*
* 1) We believe it is down, or there is a failover in progress.
* 2) Sentinel is connected.
* 3) We did not received the info within SENTINEL_ASK_PERIOD ms. */
* 3) We did not receive the info within SENTINEL_ASK_PERIOD ms. */
if ((master->flags & SRI_S_DOWN) == 0) continue;
if (ri->link->disconnected) continue;
if (!(flags & SENTINEL_ASK_FORCED) &&
+89 -37
View File
@@ -214,7 +214,7 @@ struct redisCommand redisCommandTable[] = {
{"hstrlen",hstrlenCommand,3,"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},
{"hgetall",hgetallCommand,2,"rR",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,"wmF",0,NULL,1,1,1,0,0},
@@ -240,9 +240,9 @@ struct redisCommand redisCommandTable[] = {
{"ping",pingCommand,-1,"tF",0,NULL,0,0,0,0,0},
{"echo",echoCommand,2,"F",0,NULL,0,0,0,0,0},
{"save",saveCommand,1,"as",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,-1,"a",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"a",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"alt",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,-1,"as",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"as",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"aslt",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"RF",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"rF",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"sF",0,NULL,0,0,0,0,0},
@@ -254,16 +254,16 @@ struct redisCommand redisCommandTable[] = {
{"flushdb",flushdbCommand,-1,"w",0,NULL,0,0,0,0,0},
{"flushall",flushallCommand,-1,"w",0,NULL,0,0,0,0,0},
{"sort",sortCommand,-2,"wm",0,sortGetKeys,1,1,1,0,0},
{"info",infoCommand,-1,"lt",0,NULL,0,0,0,0,0},
{"info",infoCommand,-1,"ltR",0,NULL,0,0,0,0,0},
{"monitor",monitorCommand,1,"as",0,NULL,0,0,0,0,0},
{"ttl",ttlCommand,2,"rF",0,NULL,1,1,1,0,0},
{"ttl",ttlCommand,2,"rFR",0,NULL,1,1,1,0,0},
{"touch",touchCommand,-2,"rF",0,NULL,1,1,1,0,0},
{"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0},
{"pttl",pttlCommand,2,"rFR",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,"lst",0,NULL,0,0,0,0,0},
{"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"lat",0,NULL,0,0,0,0,0},
{"config",configCommand,-2,"last",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"pslt",0,NULL,0,0,0,0,0},
{"unsubscribe",unsubscribeCommand,-1,"pslt",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"pslt",0,NULL,0,0,0,0,0},
@@ -275,24 +275,24 @@ struct redisCommand redisCommandTable[] = {
{"cluster",clusterCommand,-2,"a",0,NULL,0,0,0,0,0},
{"restore",restoreCommand,-4,"wm",0,NULL,1,1,1,0,0},
{"restore-asking",restoreCommand,-4,"wmk",0,NULL,1,1,1,0,0},
{"migrate",migrateCommand,-6,"w",0,migrateGetKeys,0,0,0,0,0},
{"migrate",migrateCommand,-6,"wR",0,migrateGetKeys,0,0,0,0,0},
{"asking",askingCommand,1,"F",0,NULL,0,0,0,0,0},
{"readonly",readonlyCommand,1,"F",0,NULL,0,0,0,0,0},
{"readwrite",readwriteCommand,1,"F",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0},
{"object",objectCommand,-2,"r",0,NULL,2,2,1,0,0},
{"memory",memoryCommand,-2,"r",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"rR",0,NULL,1,1,1,0,0},
{"object",objectCommand,-2,"rR",0,NULL,2,2,1,0,0},
{"memory",memoryCommand,-2,"rR",0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,"as",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"s",0,evalGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"a",0,NULL,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"aR",0,NULL,0,0,0,0,0},
{"script",scriptCommand,-2,"s",0,NULL,0,0,0,0,0},
{"time",timeCommand,1,"RF",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},
{"wait",waitCommand,3,"s",0,NULL,0,0,0,0,0},
{"command",commandCommand,0,"lt",0,NULL,0,0,0,0,0},
{"command",commandCommand,0,"ltR",0,NULL,0,0,0,0,0},
{"geoadd",geoaddCommand,-5,"wm",0,NULL,1,1,1,0,0},
{"georadius",georadiusCommand,-6,"w",0,georadiusGetKeys,1,1,1,0,0},
{"georadius_ro",georadiusroCommand,-6,"r",0,georadiusGetKeys,1,1,1,0,0},
@@ -310,14 +310,14 @@ struct redisCommand redisCommandTable[] = {
{"xrange",xrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
{"xrevrange",xrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
{"xlen",xlenCommand,2,"rF",0,NULL,1,1,1,0,0},
{"xread",xreadCommand,-3,"rs",0,xreadGetKeys,1,1,1,0,0},
{"xreadgroup",xreadCommand,-3,"ws",0,xreadGetKeys,1,1,1,0,0},
{"xread",xreadCommand,-4,"rs",0,xreadGetKeys,1,1,1,0,0},
{"xreadgroup",xreadCommand,-7,"ws",0,xreadGetKeys,1,1,1,0,0},
{"xgroup",xgroupCommand,-2,"wm",0,NULL,2,2,1,0,0},
{"xack",xackCommand,-3,"wF",0,NULL,1,1,1,0,0},
{"xack",xackCommand,-4,"wF",0,NULL,1,1,1,0,0},
{"xpending",xpendingCommand,-3,"r",0,NULL,1,1,1,0,0},
{"xclaim",xclaimCommand,-5,"wF",0,NULL,1,1,1,0,0},
{"xclaim",xclaimCommand,-6,"wF",0,NULL,1,1,1,0,0},
{"xinfo",xinfoCommand,-2,"r",0,NULL,2,2,1,0,0},
{"xdel",xdelCommand,-2,"wF",0,NULL,1,1,1,0,0},
{"xdel",xdelCommand,-3,"wF",0,NULL,1,1,1,0,0},
{"xtrim",xtrimCommand,-2,"wF",0,NULL,1,1,1,0,0},
{"post",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},
{"host:",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},
@@ -357,7 +357,7 @@ void serverLogRaw(int level, const char *msg) {
gettimeofday(&tv,NULL);
struct tm tm;
nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);
off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",&tm);
off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm);
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
if (server.sentinel_mode) {
role_char = 'X'; /* Sentinel. */
@@ -1096,6 +1096,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Update the time cache. */
updateCachedTime();
server.hz = server.config_hz;
/* Adapt the server.hz value to the number of configured clients. If we have
* many clients, we want to call serverCron() with an higher frequency. */
if (server.dynamic_hz) {
while (listLength(server.clients) / server.hz >
MAX_CLIENTS_PER_CLOCK_TICK)
{
server.hz *= 2;
if (server.hz > CONFIG_MAX_HZ) {
server.hz = CONFIG_MAX_HZ;
break;
}
}
}
run_with_period(100) {
trackInstantaneousMetric(STATS_METRIC_COMMAND,server.stat_numcommands);
trackInstantaneousMetric(STATS_METRIC_NET_INPUT,
@@ -1476,6 +1491,7 @@ void createSharedObjects(void) {
shared.rpop = createStringObject("RPOP",4);
shared.lpop = createStringObject("LPOP",4);
shared.lpush = createStringObject("LPUSH",5);
shared.rpoplpush = createStringObject("RPOPLPUSH",9);
shared.zpopmin = createStringObject("ZPOPMIN",7);
shared.zpopmax = createStringObject("ZPOPMAX",7);
for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {
@@ -1512,7 +1528,8 @@ void initServerConfig(void) {
server.timezone = timezone; /* Initialized by tzset(). */
server.configfile = NULL;
server.executable = NULL;
server.hz = CONFIG_DEFAULT_HZ;
server.config_hz = CONFIG_DEFAULT_HZ;
server.dynamic_hz = CONFIG_DEFAULT_DYNAMIC_HZ;
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
server.port = CONFIG_DEFAULT_SERVER_PORT;
server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG;
@@ -1632,6 +1649,7 @@ void initServerConfig(void) {
server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA;
server.repl_slave_ro = CONFIG_DEFAULT_SLAVE_READ_ONLY;
server.repl_slave_ignore_maxmemory = CONFIG_DEFAULT_SLAVE_IGNORE_MAXMEMORY;
server.repl_slave_lazy_flush = CONFIG_DEFAULT_SLAVE_LAZY_FLUSH;
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
server.repl_disable_tcp_nodelay = CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY;
@@ -1991,6 +2009,7 @@ void initServer(void) {
server.syslog_facility);
}
server.hz = server.config_hz;
server.pid = getpid();
server.current_client = NULL;
server.clients = listCreate();
@@ -2576,14 +2595,14 @@ int processCommand(client *c) {
* keys in the dataset). If there are not the only thing we can do
* is returning an error. */
if (server.maxmemory) {
int retval = freeMemoryIfNeeded();
int out_of_memory = freeMemoryIfNeeded() == C_ERR;
/* freeMemoryIfNeeded may flush slave output buffers. This may result
* into a slave, that may be the active client, to be freed. */
if (server.current_client == NULL) return C_ERR;
/* It was impossible to free enough memory, and the command the client
* is trying to execute is denied during OOM conditions? Error. */
if ((c->cmd->flags & CMD_DENYOOM) && retval == C_ERR) {
if ((c->cmd->flags & CMD_DENYOOM) && out_of_memory) {
flagTransaction(c);
addReply(c, shared.oomerr);
return C_OK;
@@ -2592,17 +2611,14 @@ int processCommand(client *c) {
/* Don't accept write commands if there are problems persisting on disk
* and if this is a master instance. */
if (((server.stop_writes_on_bgsave_err &&
server.saveparamslen > 0 &&
server.lastbgsave_status == C_ERR) ||
(server.aof_state != AOF_OFF &&
server.aof_last_write_status == C_ERR)) &&
int deny_write_type = writeCommandsDeniedByDiskError();
if (deny_write_type != DISK_ERROR_TYPE_NONE &&
server.masterhost == NULL &&
(c->cmd->flags & CMD_WRITE ||
c->cmd->proc == pingCommand))
{
flagTransaction(c);
if (server.aof_last_write_status == C_OK)
if (deny_write_type == DISK_ERROR_TYPE_RDB)
addReply(c, shared.bgsaveerr);
else
addReplySds(c,
@@ -2788,6 +2804,32 @@ int prepareForShutdown(int flags) {
/*================================== Commands =============================== */
/* Sometimes Redis cannot accept write commands because there is a perstence
* error with the RDB or AOF file, and Redis is configured in order to stop
* accepting writes in such situation. This function returns if such a
* condition is active, and the type of the condition.
*
* Function return values:
*
* DISK_ERROR_TYPE_NONE: No problems, we can accept writes.
* DISK_ERROR_TYPE_AOF: Don't accept writes: AOF errors.
* DISK_ERROR_TYPE_RDB: Don't accept writes: RDB errors.
*/
int writeCommandsDeniedByDiskError(void) {
if (server.stop_writes_on_bgsave_err &&
server.saveparamslen > 0 &&
server.lastbgsave_status == C_ERR)
{
return DISK_ERROR_TYPE_RDB;
} else if (server.aof_state != AOF_OFF &&
server.aof_last_write_status == C_ERR)
{
return DISK_ERROR_TYPE_AOF;
} else {
return DISK_ERROR_TYPE_NONE;
}
}
/* Return zero if strings are the same, non-zero if they are not.
* The comparison is performed in a way that prevents an attacker to obtain
* information about the nature of the strings just monitoring the execution
@@ -3074,6 +3116,7 @@ sds genRedisInfoString(char *section) {
"uptime_in_seconds:%jd\r\n"
"uptime_in_days:%jd\r\n"
"hz:%d\r\n"
"configured_hz:%d\r\n"
"lru_clock:%ld\r\n"
"executable:%s\r\n"
"config_file:%s\r\n",
@@ -3097,6 +3140,7 @@ sds genRedisInfoString(char *section) {
(intmax_t)uptime,
(intmax_t)(uptime/(3600*24)),
server.hz,
server.config_hz,
(unsigned long) lruclock,
server.executable ? server.executable : "",
server.configfile ? server.configfile : "");
@@ -3124,6 +3168,7 @@ sds genRedisInfoString(char *section) {
char peak_hmem[64];
char total_system_hmem[64];
char used_memory_lua_hmem[64];
char used_memory_scripts_hmem[64];
char used_memory_rss_hmem[64];
char maxmemory_hmem[64];
size_t zmalloc_used = zmalloc_used_memory();
@@ -3143,6 +3188,7 @@ sds genRedisInfoString(char *section) {
bytesToHuman(peak_hmem,server.stat_peak_memory);
bytesToHuman(total_system_hmem,total_system_mem);
bytesToHuman(used_memory_lua_hmem,memory_lua);
bytesToHuman(used_memory_scripts_hmem,server.lua_scripts_mem);
bytesToHuman(used_memory_rss_hmem,server.cron_malloc_stats.process_rss);
bytesToHuman(maxmemory_hmem,server.maxmemory);
@@ -3167,6 +3213,9 @@ sds genRedisInfoString(char *section) {
"total_system_memory_human:%s\r\n"
"used_memory_lua:%lld\r\n"
"used_memory_lua_human:%s\r\n"
"used_memory_scripts:%lld\r\n"
"used_memory_scripts_human:%s\r\n"
"number_of_cached_scripts:%lu\r\n"
"maxmemory:%lld\r\n"
"maxmemory_human:%s\r\n"
"maxmemory_policy:%s\r\n"
@@ -3204,6 +3253,9 @@ sds genRedisInfoString(char *section) {
total_system_hmem,
memory_lua,
used_memory_lua_hmem,
server.lua_scripts_mem,
used_memory_scripts_hmem,
dictSize(server.lua_scripts),
server.maxmemory,
maxmemory_hmem,
evict_policy,
@@ -3509,14 +3561,14 @@ sds genRedisInfoString(char *section) {
if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info,
"# CPU\r\n"
"used_cpu_sys:%.2f\r\n"
"used_cpu_user:%.2f\r\n"
"used_cpu_sys_children:%.2f\r\n"
"used_cpu_user_children:%.2f\r\n",
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000);
"used_cpu_sys:%ld.%06ld\r\n"
"used_cpu_user:%ld.%06ld\r\n"
"used_cpu_sys_children:%ld.%06ld\r\n"
"used_cpu_user_children:%ld.%06ld\r\n",
(long)self_ru.ru_stime.tv_sec, (long)self_ru.ru_stime.tv_usec,
(long)self_ru.ru_utime.tv_sec, (long)self_ru.ru_utime.tv_usec,
(long)c_ru.ru_stime.tv_sec, (long)c_ru.ru_stime.tv_usec,
(long)c_ru.ru_utime.tv_sec, (long)c_ru.ru_utime.tv_usec);
}
/* Command statistics */
+23 -6
View File
@@ -78,12 +78,14 @@ typedef long long mstime_t; /* millisecond time type. */
#define C_ERR -1
/* Static server configuration */
#define CONFIG_DEFAULT_HZ 10 /* Time interrupt calls/sec. */
#define CONFIG_DEFAULT_DYNAMIC_HZ 1 /* Adapt hz to # of clients.*/
#define CONFIG_DEFAULT_HZ 10 /* Time interrupt calls/sec. */
#define CONFIG_MIN_HZ 1
#define CONFIG_MAX_HZ 500
#define CONFIG_DEFAULT_SERVER_PORT 6379 /* TCP port */
#define CONFIG_DEFAULT_TCP_BACKLOG 511 /* TCP listen backlog */
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 0 /* default client timeout: infinite */
#define MAX_CLIENTS_PER_CLOCK_TICK 200 /* HZ is adapted based on that. */
#define CONFIG_DEFAULT_SERVER_PORT 6379 /* TCP port. */
#define CONFIG_DEFAULT_TCP_BACKLOG 511 /* TCP listen backlog. */
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 0 /* Default client timeout: infinite */
#define CONFIG_DEFAULT_DBNUM 16
#define CONFIG_MAX_LINE 1024
#define CRON_DBS_PER_CALL 16
@@ -91,7 +93,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define PROTO_SHARED_SELECT_CMDS 10
#define OBJ_SHARED_INTEGERS 10000
#define OBJ_SHARED_BULKHDR_LEN 32
#define LOG_MAX_LEN 1024 /* Default maximum length of syslog messages */
#define LOG_MAX_LEN 1024 /* Default maximum length of syslog messages.*/
#define AOF_REWRITE_PERC 100
#define AOF_REWRITE_MIN_SIZE (64*1024*1024)
#define AOF_REWRITE_ITEMS_PER_CMD 64
@@ -129,6 +131,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define CONFIG_DEFAULT_SLAVE_READ_ONLY 1
#define CONFIG_DEFAULT_SLAVE_IGNORE_MAXMEMORY 1
#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_IP NULL
#define CONFIG_DEFAULT_SLAVE_ANNOUNCE_PORT 0
#define CONFIG_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
@@ -708,6 +711,7 @@ typedef struct client {
redisDb *db; /* Pointer to currently SELECTed DB. */
robj *name; /* As set by CLIENT SETNAME. */
sds querybuf; /* Buffer we use to accumulate client queries. */
size_t qb_pos; /* The position we have read in querybuf. */
sds pending_querybuf; /* If this client is flagged as master, this buffer
represents the yet not applied portion of the
replication stream that we are receiving from
@@ -779,7 +783,7 @@ struct sharedObjectsStruct {
*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,
*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink,
*rpop, *lpop, *lpush, *zpopmin, *zpopmax, *emptyscan,
*rpop, *lpop, *lpush, *rpoplpush, *zpopmin, *zpopmax, *emptyscan,
*select[PROTO_SHARED_SELECT_CMDS],
*integers[OBJ_SHARED_INTEGERS],
*mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */
@@ -851,6 +855,7 @@ struct redisMemOverhead {
size_t clients_slaves;
size_t clients_normal;
size_t aof_buffer;
size_t lua_caches;
size_t overhead_total;
size_t dataset;
size_t total_keys;
@@ -923,6 +928,10 @@ struct redisServer {
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
char **exec_argv; /* Executable argv vector (copy). */
int dynamic_hz; /* Change hz value depending on # of clients. */
int config_hz; /* Configured HZ value. May be different than
the actual 'hz' field value if dynamic-hz
is enabled. */
int hz; /* serverCron() calls frequency in hertz */
redisDb *db;
dict *commands; /* Command table */
@@ -1149,6 +1158,7 @@ struct redisServer {
time_t repl_transfer_lastio; /* Unix time of the latest read, for timeout */
int repl_serve_stale_data; /* Serve stale data when link is down? */
int repl_slave_ro; /* Slave is read only? */
int repl_slave_ignore_maxmemory; /* If true slaves do not evict. */
time_t repl_down_since; /* Unix time at which link with master went down */
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
int slave_priority; /* Reported in INFO and used by Sentinel. */
@@ -1227,6 +1237,7 @@ struct redisServer {
client *lua_client; /* The "fake client" to query Redis from Lua */
client *lua_caller; /* The client running EVAL right now, or NULL */
dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
unsigned long long lua_scripts_mem; /* Cached scripts' memory + oh */
mstime_t lua_time_limit; /* Script timeout in milliseconds */
mstime_t lua_time_start; /* Start time of script, milliseconds time */
int lua_write_dirty; /* True if a write command was called during the
@@ -1583,6 +1594,11 @@ void startLoading(FILE *fp);
void loadingProgress(off_t pos);
void stopLoading(void);
#define DISK_ERROR_TYPE_AOF 1 /* Don't accept writes: AOF errors. */
#define DISK_ERROR_TYPE_RDB 2 /* Don't accept writes: RDB errors. */
#define DISK_ERROR_TYPE_NONE 0 /* No problems, we can accept writes. */
int writeCommandsDeniedByDiskError(void);
/* RDB persistence */
#include "rdb.h"
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi);
@@ -1822,6 +1838,7 @@ int dbAsyncDelete(redisDb *db, robj *key);
void emptyDbAsync(redisDb *db);
void slotToKeyFlushAsync(void);
size_t lazyfreeGetPendingObjectsCount(void);
void freeObjAsync(robj *o);
/* API to get key arguments from commands */
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
+3
View File
@@ -596,6 +596,9 @@ void rpoplpushCommand(client *c) {
signalModifiedKey(c->db,touchedkey);
decrRefCount(touchedkey);
server.dirty++;
if (c->lastcmd->proc == brpoplpushCommand) {
rewriteClientCommandVector(c,3,shared.rpoplpush,c->argv[1],c->argv[2]);
}
}
}
+4 -6
View File
@@ -516,11 +516,7 @@ void spopWithCountCommand(client *c) {
sdsfree(sdsele);
}
/* Assign the new set as the key value. */
incrRefCount(set); /* Protect the old set value. */
dbOverwrite(c->db,c->argv[1],newset);
/* Tranfer the old set to the client and release it. */
/* Transfer the old set to the client. */
setTypeIterator *si;
si = setTypeInitIterator(set);
while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) {
@@ -539,7 +535,9 @@ void spopWithCountCommand(client *c) {
decrRefCount(objele);
}
setTypeReleaseIterator(si);
decrRefCount(set);
/* Assign the new set as the key value. */
dbOverwrite(c->db,c->argv[1],newset);
}
/* Don't propagate the command itself even if we incremented the
+47 -20
View File
@@ -548,13 +548,20 @@ int streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields) {
si->master_fields_count = lpGetInteger(si->lp_ele);
si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek first field. */
si->master_fields_start = si->lp_ele;
/* Skip master fileds to seek the first entry. */
for (uint64_t i = 0; i < si->master_fields_count; i++)
si->lp_ele = lpNext(si->lp,si->lp_ele);
/* We are now pointing the zero term of the master entry. If
* we are iterating in reverse order, we need to seek the
* end of the listpack. */
if (si->rev) si->lp_ele = lpLast(si->lp);
/* We are now pointing to the first field of the master entry.
* We need to seek either the first or the last entry depending
* on the direction of the iteration. */
if (!si->rev) {
/* If we are iterating in normal order, skip the master fields
* to seek the first actual entry. */
for (uint64_t i = 0; i < si->master_fields_count; i++)
si->lp_ele = lpNext(si->lp,si->lp_ele);
} else {
/* If we are iterating in reverse direction, just seek the
* last part of the last entry in the listpack (that is, the
* fields count). */
si->lp_ele = lpLast(si->lp);
}
} else if (si->rev) {
/* If we are itereating in the reverse order, and this is not
* the first entry emitted for this listpack, then we already
@@ -757,6 +764,7 @@ int streamDeleteItem(stream *s, streamID *id) {
streamIteratorRemoveEntry(&si,&myid);
deleted = 1;
}
streamIteratorStop(&si);
return deleted;
}
@@ -1051,15 +1059,20 @@ int string2ull(const char *s, unsigned long long *value) {
* to the client, otherwise C_OK is returned. The ID may be in incomplete
* form, just stating the milliseconds time part of the stream. In such a case
* the missing part is set according to the value of 'missing_seq' parameter.
*
* The IDs "-" and "+" specify respectively the minimum and maximum IDs
* that can be represented.
* that can be represented. If 'strict' is set to 1, "-" and "+" will be
* treated as an invalid ID.
*
* If 'c' is set to NULL, no reply is sent to the client. */
int streamParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {
int streamGenericParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq, int strict) {
char buf[128];
if (sdslen(o->ptr) > sizeof(buf)-1) goto invalid;
memcpy(buf,o->ptr,sdslen(o->ptr)+1);
if (strict && (buf[0] == '-' || buf[0] == '+') && buf[1] == '\0')
goto invalid;
/* Handle the "-" and "+" special cases. */
if (buf[0] == '-' && buf[1] == '\0') {
id->ms = 0;
@@ -1088,6 +1101,20 @@ invalid:
return C_ERR;
}
/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to
* 0, to be used when - and + are accetable IDs. */
int streamParseIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {
return streamGenericParseIDOrReply(c,o,id,missing_seq,0);
}
/* Wrapper for streamGenericParseIDOrReply() with 'strict' argument set to
* 1, to be used when we want to return an error if the special IDs + or -
* are provided. */
int streamParseStrictIDOrReply(client *c, robj *o, streamID *id, uint64_t missing_seq) {
return streamGenericParseIDOrReply(c,o,id,missing_seq,1);
}
/* XADD key [MAXLEN <count>] <ID or *> [field value] [field value] ... */
void xaddCommand(client *c) {
streamID id;
@@ -1125,7 +1152,7 @@ void xaddCommand(client *c) {
maxlen_arg_idx = i;
} else {
/* If we are here is a syntax error or a valid ID. */
if (streamParseIDOrReply(c,c->argv[i],&id,0) != C_OK) return;
if (streamParseStrictIDOrReply(c,c->argv[i],&id,0) != C_OK) return;
id_given = 1;
break;
}
@@ -1381,7 +1408,7 @@ void xreadCommand(client *c) {
ids[id_idx].seq = UINT64_MAX;
continue;
}
if (streamParseIDOrReply(c,c->argv[i],ids+id_idx,0) != C_OK)
if (streamParseStrictIDOrReply(c,c->argv[i],ids+id_idx,0) != C_OK)
goto cleanup;
}
@@ -1655,7 +1682,7 @@ NULL
streamID id;
if (!strcmp(c->argv[4]->ptr,"$")) {
id = s->last_id;
} else if (streamParseIDOrReply(c,c->argv[4],&id,0) != C_OK) {
} else if (streamParseStrictIDOrReply(c,c->argv[4],&id,0) != C_OK) {
return;
}
streamCG *cg = streamCreateCG(s,grpname,sdslen(grpname),&id);
@@ -1732,7 +1759,7 @@ void xackCommand(client *c) {
for (int j = 3; j < c->argc; j++) {
streamID id;
unsigned char buf[sizeof(streamID)];
if (streamParseIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
streamEncodeID(buf,&id);
/* Lookup the ID in the group PEL: it will have a reference to the
@@ -1988,9 +2015,9 @@ void xclaimCommand(client *c) {
* the client successfully claimed some message, so it should be
* executed in a "all or nothing" fashion. */
int j;
for (j = 4; j < c->argc; j++) {
for (j = 5; j < c->argc; j++) {
streamID id;
if (streamParseIDOrReply(NULL,c->argv[j],&id,0) != C_OK) break;
if (streamParseStrictIDOrReply(NULL,c->argv[j],&id,0) != C_OK) break;
}
int last_id_arg = j-1; /* Next time we iterate the IDs we now the range. */
@@ -2013,12 +2040,12 @@ void xclaimCommand(client *c) {
} else if (!strcasecmp(opt,"TIME") && moreargs) {
j++;
if (getLongLongFromObjectOrReply(c,c->argv[j],&deliverytime,
"Invalid IDLE option argument for XCLAIM")
"Invalid TIME option argument for XCLAIM")
!= C_OK) return;
} else if (!strcasecmp(opt,"RETRYCOUNT") && moreargs) {
j++;
if (getLongLongFromObjectOrReply(c,c->argv[j],&retrycount,
"Invalid IDLE option argument for XCLAIM")
"Invalid RETRYCOUNT option argument for XCLAIM")
!= C_OK) return;
} else {
addReplyErrorFormat(c,"Unrecognized XCLAIM option '%s'",opt);
@@ -2049,7 +2076,7 @@ void xclaimCommand(client *c) {
for (int j = 5; j <= last_id_arg; j++) {
streamID id;
unsigned char buf[sizeof(streamID)];
if (streamParseIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
streamEncodeID(buf,&id);
/* Lookup the ID in the group PEL. */
@@ -2132,13 +2159,13 @@ void xdelCommand(client *c) {
* executed because at some point an invalid ID is parsed. */
streamID id;
for (int j = 2; j < c->argc; j++) {
if (streamParseIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
}
/* Actually apply the command. */
int deleted = 0;
for (int j = 2; j < c->argc; j++) {
streamParseIDOrReply(c,c->argv[j],&id,0); /* Retval already checked. */
streamParseStrictIDOrReply(c,c->argv[j],&id,0); /* Retval already checked. */
deleted += streamDeleteItem(s,&id);
}
+56 -7
View File
@@ -244,6 +244,61 @@ int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {
return 0; /* not found */
}
/* Update the score of an elmenent inside the sorted set skiplist.
* Note that the element must exist and must match 'score'.
* This function does not update the score in the hash table side, the
* caller should take care of it.
*
* Note that this function attempts to just update the node, in case after
* the score update, the node would be exactly at the same position.
* Otherwise the skiplist is modified by removing and re-adding a new
* element, which is more costly.
*
* The function returns the updated element skiplist node pointer. */
zskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
int i;
/* We need to seek to element to update to start: this is useful anyway,
* we'll have to update or remove it. */
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
while (x->level[i].forward &&
(x->level[i].forward->score < curscore ||
(x->level[i].forward->score == curscore &&
sdscmp(x->level[i].forward->ele,ele) < 0)))
{
x = x->level[i].forward;
}
update[i] = x;
}
/* Jump to our element: note that this function assumes that the
* element with the matching score exists. */
x = x->level[0].forward;
serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0);
/* If the node, after the score update, would be still exactly
* at the same position, we can just update the score without
* actually removing and re-inserting the element in the skiplist. */
if ((x->backward == NULL || x->backward->score < newscore) &&
(x->level[0].forward == NULL || x->level[0].forward->score > newscore))
{
x->score = newscore;
return x;
}
/* No way to reuse the old node: we need to remove and insert a new
* one at a different place. */
zslDeleteNode(zsl, x, update);
zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele);
/* We reused the old node x->ele SDS string, free the node now
* since zslInsert created a new one. */
x->ele = NULL;
zslFreeNode(x);
return newnode;
}
int zslValueGteMin(double value, zrangespec *spec) {
return spec->minex ? (value > spec->min) : (value >= spec->min);
}
@@ -1341,13 +1396,7 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
/* Remove and re-insert when score changes. */
if (score != curscore) {
zskiplistNode *node;
serverAssert(zslDelete(zs->zsl,curscore,ele,&node));
znode = zslInsert(zs->zsl,score,node->ele);
/* We reused the node->ele SDS string, free the node now
* since zslInsert created a new one. */
node->ele = NULL;
zslFreeNode(node);
znode = zslUpdateScore(zs->zsl,curscore,ele,score);
/* Note that we did not removed the original element from
* the hash table representing the sorted set, so we just
* update the score. */
+6 -3
View File
@@ -346,6 +346,7 @@ int string2ll(const char *s, size_t slen, long long *value) {
int negative = 0;
unsigned long long v;
/* A zero length string is not a valid number. */
if (plen == slen)
return 0;
@@ -355,6 +356,8 @@ int string2ll(const char *s, size_t slen, long long *value) {
return 1;
}
/* Handle negative numbers: just set a flag and continue like if it
* was a positive number. Later convert into negative. */
if (p[0] == '-') {
negative = 1;
p++; plen++;
@@ -368,13 +371,11 @@ int string2ll(const char *s, size_t slen, long long *value) {
if (p[0] >= '1' && p[0] <= '9') {
v = p[0]-'0';
p++; plen++;
} else if (p[0] == '0' && slen == 1) {
*value = 0;
return 1;
} else {
return 0;
}
/* Parse all the other digits, checking for overflow at every step. */
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
if (v > (ULLONG_MAX / 10)) /* Overflow. */
return 0;
@@ -391,6 +392,8 @@ int string2ll(const char *s, size_t slen, long long *value) {
if (plen < slen)
return 0;
/* Convert to negative if needed, and do the final overflow check when
* converting from unsigned long long to long long. */
if (negative) {
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
return 0;
+1 -1
View File
@@ -183,7 +183,7 @@ size_t zmalloc_size(void *ptr) {
return size+PREFIX_SIZE;
}
size_t zmalloc_usable(void *ptr) {
return zmalloc_usable(ptr)-PREFIX_SIZE;
return zmalloc_size(ptr)-PREFIX_SIZE;
}
#endif
+7 -3
View File
@@ -33,9 +33,8 @@ start_server {} {
set cycle 1
while {([clock seconds]-$start_time) < $duration} {
test "PSYNC2: --- CYCLE $cycle ---" {
incr cycle
}
test "PSYNC2: --- CYCLE $cycle ---" {}
incr cycle
# Create a random replication layout.
# Start with switching master (this simulates a failover).
@@ -139,6 +138,11 @@ start_server {} {
}
assert {$sum == 4}
}
# Limit anyway the maximum number of cycles. This is useful when the
# test is skipped via --only option of the test suite. In that case
# we don't want to see many seconds of this test being just skipped.
if {$cycle > 50} break
}
test "PSYNC2: Bring the master back again for next test" {
+44
View File
@@ -266,3 +266,47 @@ foreach dl {no yes} {
}
}
}
start_server {tags {"repl"}} {
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 3]
start_server {} {
test "Master stream is correctly processed while the slave has a script in -BUSY state" {
set slave [srv 0 client]
puts [srv 0 port]
$slave config set lua-time-limit 500
$slave slaveof $master_host $master_port
# Wait for the slave to be online
wait_for_condition 500 100 {
[lindex [$slave role] 3] eq {connected}
} else {
fail "Slave still not connected after some time"
}
# Wait some time to make sure the master is sending data
# to the slave.
after 5000
# Stop the ability of the slave to process data by sendig
# a script that will put it in BUSY state.
$slave eval {for i=1,3000000000 do end} 0
# Wait some time again so that more master stream will
# be processed.
after 2000
# Stop the write load
stop_write_load $load_handle0
# number of keys
wait_for_condition 500 100 {
[$master debug digest] eq [$slave debug digest]
} else {
fail "Different datasets between slave and master"
}
}
}
}
+2 -2
View File
@@ -17,7 +17,7 @@ test "Basic failover works if the master is down" {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "At least one Sentinel did not received failover info"
fail "At least one Sentinel did not receive failover info"
}
}
restart_instance redis $master_id
@@ -108,7 +108,7 @@ test "Failover works if we configure for absolute agreement" {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "At least one Sentinel did not received failover info"
fail "At least one Sentinel did not receive failover info"
}
}
restart_instance redis $master_id
+2 -2
View File
@@ -16,7 +16,7 @@ test "We can failover with Sentinel 1 crashed" {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "Sentinel $id did not received failover info"
fail "Sentinel $id did not receive failover info"
}
}
}
@@ -30,7 +30,7 @@ test "After Sentinel 1 is restarted, its config gets updated" {
wait_for_condition 1000 50 {
[lindex [S 1 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "Restarted Sentinel did not received failover info"
fail "Restarted Sentinel did not receive failover info"
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ proc 02_crash_and_failover {} {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "At least one Sentinel did not received failover info"
fail "At least one Sentinel did not receive failover info"
}
}
restart_instance redis $master_id
+1 -1
View File
@@ -12,7 +12,7 @@ test "Manual failover works" {
wait_for_condition 1000 50 {
[lindex [S $id SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] 1] != $old_port
} else {
fail "At least one Sentinel did not received failover info"
fail "At least one Sentinel did not receive failover info"
}
}
set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]
+20
View File
@@ -1,6 +1,8 @@
set ::num_tests 0
set ::num_passed 0
set ::num_failed 0
set ::num_skipped 0
set ::num_aborted 0
set ::tests_failed {}
proc fail {msg} {
@@ -68,10 +70,26 @@ proc test {name code {okpattern undefined}} {
# abort if tagged with a tag to deny
foreach tag $::denytags {
if {[lsearch $::tags $tag] >= 0} {
incr ::num_aborted
send_data_packet $::test_server_fd ignore $name
return
}
}
# abort if test name in skiptests
if {[lsearch $::skiptests $name] >= 0} {
incr ::num_skipped
send_data_packet $::test_server_fd skip $name
return
}
# abort if test name in skiptests
if {[llength $::only_tests] > 0 && [lsearch $::only_tests $name] < 0} {
incr ::num_skipped
send_data_packet $::test_server_fd skip $name
return
}
# check if tagged with at least 1 tag to allow when there *is* a list
# of tags to allow, because default policy is to run everything
if {[llength $::allowtags] > 0} {
@@ -82,6 +100,8 @@ proc test {name code {okpattern undefined}} {
}
}
if {$matched < 1} {
incr ::num_aborted
send_data_packet $::test_server_fd ignore $name
return
}
}
+79 -4
View File
@@ -74,7 +74,11 @@ set ::stack_logging 0
set ::verbose 0
set ::quiet 0
set ::denytags {}
set ::skiptests {}
set ::allowtags {}
set ::only_tests {}
set ::single_tests {}
set ::skip_till ""
set ::external 0; # If "1" this means, we are running against external instance
set ::file ""; # If set, runs only the tests in this comma separated list
set ::curfile ""; # Hold the filename of the current suite
@@ -85,6 +89,8 @@ set ::last_progress [clock seconds]
set ::active_servers {} ; # Pids of active Redis instances.
set ::dont_clean 0
set ::wait_server 0
set ::stop_on_failure 0
set ::loop 0
# Set to 1 when we are running in client mode. The Redis test uses a
# server-client model to run tests simultaneously. The server instance
@@ -255,6 +261,8 @@ proc accept_test_clients {fd addr port} {
# testing: just used to signal that a given test started.
# ok: a test was executed with success.
# err: a test was executed with an error.
# skip: a test was skipped by skipfile or individual test options.
# ignore: a test was skipped by a group tag.
# exception: there was a runtime exception while executing the test.
# done: all the specified test file was processed, this test client is
# ready to accept a new task.
@@ -283,11 +291,24 @@ proc read_from_test_client fd {
puts "\[[colorstr green $status]\]: $data"
}
set ::active_clients_task($fd) "(OK) $data"
} elseif {$status eq {skip}} {
if {!$::quiet} {
puts "\[[colorstr yellow $status]\]: $data"
}
} elseif {$status eq {ignore}} {
if {!$::quiet} {
puts "\[[colorstr cyan $status]\]: $data"
}
} elseif {$status eq {err}} {
set err "\[[colorstr red $status]\]: $data"
puts $err
lappend ::failed_tests $err
set ::active_clients_task($fd) "(ERR) $data"
if {$::stop_on_failure} {
puts -nonewline "(Test stopped, press enter to continue)"
flush stdout
gets stdin
}
} elseif {$status eq {exception}} {
puts "\[[colorstr red $status]\]: $data"
kill_clients
@@ -350,6 +371,9 @@ proc signal_idle_client fd {
send_data_packet $fd run [lindex $::all_tests $::next_test]
lappend ::active_clients $fd
incr ::next_test
if {$::loop && $::next_test == [llength $::all_tests]} {
set ::next_test 0
}
} else {
lappend ::idle_clients $fd
if {[llength $::active_clients] == 0} {
@@ -412,13 +436,19 @@ proc print_help_screen {} {
"--stack-logging Enable OSX leaks/malloc stack logging."
"--accurate Run slow randomized tests for more iterations."
"--quiet Don't show individual tests."
"--single <unit> Just execute the specified unit (see next option)."
"--single <unit> Just execute the specified unit (see next option). this option can be repeated."
"--list-tests List all the available test units."
"--only <test> Just execute the specified test by test name. this option can be repeated."
"--skip-till <unit> Skip all units until (and including) the specified one."
"--clients <num> Number of test clients (default 16)."
"--timeout <sec> Test timeout in seconds (default 10 min)."
"--force-failure Force the execution of a test that always fails."
"--dont-clean don't delete redis log files after the run"
"--wait-server wait after server is started (so that you can attach a debugger)"
"--config <k> <v> Extra config file argument."
"--skipfile <file> Name of a file containing test names that should be skipped (one per line)."
"--dont-clean Don't delete redis log files after the run."
"--stop Blocks once the first test fails."
"--loop Execute the specified set of tests forever."
"--wait-server Wait after server is started (so that you can attach a debugger)."
"--help Print this help screen."
} "\n"]
}
@@ -436,6 +466,17 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
}
}
incr j
} elseif {$opt eq {--config}} {
set arg2 [lindex $argv [expr $j+2]]
lappend ::global_overrides $arg
lappend ::global_overrides $arg2
incr j 2
} elseif {$opt eq {--skipfile}} {
incr j
set fp [open $arg r]
set file_data [read $fp]
close $fp
set ::skiptests [split $file_data "\n"]
} elseif {$opt eq {--valgrind}} {
set ::valgrind 1
} elseif {$opt eq {--stack-logging}} {
@@ -456,7 +497,13 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--force-failure}} {
set ::force_failure 1
} elseif {$opt eq {--single}} {
set ::all_tests $arg
lappend ::single_tests $arg
incr j
} elseif {$opt eq {--only}} {
lappend ::only_tests $arg
incr j
} elseif {$opt eq {--skiptill}} {
set ::skip_till $arg
incr j
} elseif {$opt eq {--list-tests}} {
foreach t $::all_tests {
@@ -476,6 +523,10 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
set ::dont_clean 1
} elseif {$opt eq {--wait-server}} {
set ::wait_server 1
} elseif {$opt eq {--stop}} {
set ::stop_on_failure 1
} elseif {$opt eq {--loop}} {
set ::loop 1
} elseif {$opt eq {--timeout}} {
set ::timeout $arg
incr j
@@ -488,6 +539,30 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
}
}
# If --skil-till option was given, we populate the list of single tests
# to run with everything *after* the specified unit.
if {$::skip_till != ""} {
set skipping 1
foreach t $::all_tests {
if {$skipping == 0} {
lappend ::single_tests $t
}
if {$t == $::skip_till} {
set skipping 0
}
}
if {$skipping} {
puts "test $::skip_till not found"
exit 0
}
}
# Override the list of tests with the specific tests we want to run
# in case there was some filter, that is --single or --skip-till options.
if {[llength $::single_tests] > 0} {
set ::all_tests $::single_tests
}
proc attach_to_replication_stream {} {
set s [socket [srv 0 "host"] [srv 0 "port"]]
fconfigure $s -translation binary
+2 -2
View File
@@ -33,7 +33,7 @@ start_server {tags {"dump"}} {
set now [clock milliseconds]
r restore foo [expr $now+3000] $encoded absttl
set ttl [r pttl foo]
assert {$ttl >= 2998 && $ttl <= 3000}
assert {$ttl >= 2990 && $ttl <= 3000}
r get foo
} {bar}
@@ -44,7 +44,7 @@ start_server {tags {"dump"}} {
r config set maxmemory-policy allkeys-lru
r restore foo 0 $encoded idletime 1000
set idle [r object idletime foo]
assert {$idle >= 1000 && $idle <= 1002}
assert {$idle >= 1000 && $idle <= 1010}
r get foo
} {bar}
+1 -1
View File
@@ -1,7 +1,7 @@
start_server {tags {"introspection"}} {
test {CLIENT LIST} {
r client list
} {*addr=*:* fd=* age=* idle=* flags=N db=9 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=* obl=0 oll=0 omem=0 events=r cmd=client*}
} {*addr=*:* fd=* age=* idle=* flags=N db=9 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* obl=0 oll=0 omem=0 events=r cmd=client*}
test {MONITOR can log executed commands} {
set rd [redis_deferring_client]
+20 -14
View File
@@ -143,9 +143,11 @@ start_server {tags {"maxmemory"}} {
}
}
proc test_slave_buffers {cmd_count payload_len limit_memory pipeline} {
proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline} {
start_server {tags {"maxmemory"}} {
start_server {} {
set slave_pid [s process_id]
test "$test_name" {
set slave [srv 0 client]
set slave_host [srv 0 host]
set slave_port [srv 0 port]
@@ -158,8 +160,10 @@ proc test_slave_buffers {cmd_count payload_len limit_memory pipeline} {
$master setrange "key:$j" 100000 asdf
}
# make sure master doesn't disconnect slave because of timeout
$master config set repl-timeout 300 ;# 5 minutes
$master config set maxmemory-policy allkeys-random
$master config set client-output-buffer-limit "slave 100000000 100000000 60"
$master config set client-output-buffer-limit "slave 100000000 100000000 300"
$master config set repl-backlog-size [expr {10*1024}]
$slave slaveof $master_host $master_port
@@ -182,9 +186,9 @@ proc test_slave_buffers {cmd_count payload_len limit_memory pipeline} {
# put the slave to sleep
set rd_slave [redis_deferring_client]
$rd_slave debug sleep 60
exec kill -SIGSTOP $slave_pid
# send some 10mb woth of commands that don't increase the memory usage
# send some 10mb worth of commands that don't increase the memory usage
if {$pipeline == 1} {
set rd_master [redis_deferring_client -1]
for {set k 0} {$k < $cmd_count} {incr k} {
@@ -218,19 +222,21 @@ proc test_slave_buffers {cmd_count payload_len limit_memory pipeline} {
set delta_no_repl [expr {$killed_used_no_repl - $used_no_repl}]
assert {$killed_slave_buf == 0}
assert {$delta_no_repl > -50*1024 && $delta_no_repl < 50*1024} ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
}
# unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server
exec kill -SIGCONT $slave_pid
}
}
}
test {slave buffer are counted correctly} {
# we wanna use many small commands, and we don't wanna wait long
# so we need to use a pipeline (redis_deferring_client)
# that may cause query buffer to fill and induce eviction, so we disable it
test_slave_buffers 1000000 10 0 1
}
# test that slave buffer are counted correctly
# we wanna use many small commands, and we don't wanna wait long
# so we need to use a pipeline (redis_deferring_client)
# that may cause query buffer to fill and induce eviction, so we disable it
test_slave_buffers {slave buffer are counted correctly} 1000000 10 0 1
test {slave buffer don't induce eviction} {
# test again with fewer (and bigger) commands without pipeline, but with eviction
test_slave_buffers 100000 100 1 0
}
# test that slave buffer don't induce eviction
# test again with fewer (and bigger) commands without pipeline, but with eviction
test_slave_buffers "slave buffer don't induce eviction" 100000 100 1 0
+1 -1
View File
@@ -201,7 +201,7 @@ start_server {tags {"defrag"}} {
assert {$frag < 1.1}
# due to high fragmentation, 10hz, and active-defrag-cycle-max set to 75,
# we expect max latency to be not much higher than 75ms
assert {$max_latency <= 80}
assert {$max_latency <= 120}
}
# verify the data isn't corrupted or changed
set newdigest [r debug digest]
+26
View File
@@ -1185,4 +1185,30 @@ start_server {tags {"zset"}} {
stressers ziplist
stressers skiplist
}
test {ZSET skiplist order consistency when elements are moved} {
set original_max [lindex [r config get zset-max-ziplist-entries] 1]
r config set zset-max-ziplist-entries 0
for {set times 0} {$times < 10} {incr times} {
r del zset
for {set j 0} {$j < 1000} {incr j} {
r zadd zset [randomInt 50] ele-[randomInt 10]
}
# Make sure that element ordering is correct
set prev_element {}
set prev_score -1
foreach {element score} [r zrange zset 0 -1 WITHSCORES] {
# Assert that elements are in increasing ordering
assert {
$prev_score < $score ||
($prev_score == $score &&
[string compare $prev_element $element] == -1)
}
set prev_element $element
set prev_score $score
}
}
r config set zset-max-ziplist-entries $original_max
}
}