Compare commits

..
239 Commits
Author SHA1 Message Date
antirez 973ce1b5bd Redis 2.8.23. 2015-10-15 15:40:57 +02:00
antirez d85eed2400 Redis.conf example: make clear user must pass its path as argument. 2015-10-15 12:46:19 +02:00
antirez c3a7fe4d32 Regression test for issue #2813. 2015-10-15 11:25:22 +02:00
antirez 34b3a045f8 Move end-comment of handshake states.
For an error I missed the last handshake state.
Related to issue #2813.
2015-10-15 10:04:35 +02:00
antirez 38b1867fe2 Make clear that slave handshake states must be ordered.
Make sure that people from the future will not break this rule.
Related to issue #2813.
2015-10-15 10:03:33 +02:00
antirez 816dda3971 Minor changes to PR #2813.
* Function to test for slave handshake renamed slaveIsInHandshakeState.
* Function no longer accepts arguments since it always tests the
  same global state.
* Test for state translated to a range test since defines are guaranteed
  to stay in order in the future.
* Use the new function in the ROLE command implementation as well.
2015-10-15 09:59:07 +02:00
Salvatore Sanfilippo db98e54982 Merge pull request #2813 from kevinmcgehee/2.8
Fix master timeout during handshake
2015-10-15 09:51:28 +02:00
Kevin McGehee 3e0b34cf6d Fix master timeout during handshake
This change allows a slave to properly time out a dead master during
the extended asynchronous synchronization state machine.  Now, slaves
will record their last interaction with the master and apply the
replication timeout before a response to the PSYNC request is received.
2015-10-14 12:03:47 -07:00
antirez 560142e6a7 redis-cli pipe mode: don't stay in the write loop forever.
The code was broken and resulted in redis-cli --pipe to, most of the
times, writing everything received in the standard input to the Redis
connection socket without ever reading back the replies, until all the
content to write was written.

This means that Redis had to accumulate all the output in the output
buffers of the client, consuming a lot of memory.

Fixed thanks to the original report of anomalies in the behavior
provided by Twitter user @fsaintjacques.
2015-09-30 16:27:26 +02:00
antirez 18bdc2790d Test: fix false positive in HSTRLEN test.
HINCRBY* tests later used the value "tmp" that was sometimes generated
by the random key generation function. The result was ovewriting what
Tcl expected to be inside Redis with another value, causing the next
HSTRLEN test to fail.
2015-09-15 09:38:30 +02:00
antirez 8819800310 Test: MOVE expire test improved.
Related to #2765.
2015-09-14 12:37:49 +02:00
antirez 5fd2dc3186 MOVE re-add TTL check fixed.
getExpire() returns -1 when no expire exists.

Related to #2765.
2015-09-14 12:37:49 +02:00
antirez 37843be92f MOVE now can move TTL metadata as well.
MOVE was not able to move the TTL: when a key was moved into a different
database number, it became persistent like if PERSIST was used.

In some incredible way (I guess almost nobody uses Redis MOVE) this bug
remained unnoticed inside Redis internals for many years.
Finally Andy Grunwald discovered it and opened an issue.

This commit fixes the bug and adds a regression test.

Close #2765.
2015-09-14 12:37:49 +02:00
antirez 54b368bb81 Redis 2.8.22. 2015-09-08 10:03:48 +02:00
antirez 35507e7871 Sentinel: command arity check added where missing. 2015-09-08 09:33:48 +02:00
Rogerio Goncalves fa81cc2624 Check args before run ckquorum. Fix issue #2635 2015-09-08 09:28:30 +02:00
MOON_CLJ 06894b8fe4 pfcount support multi keys 2015-09-08 09:22:11 +02:00
antirez 7bac7d37cb Fix merge issues in 490847c. 2015-09-07 17:29:43 +02:00
antirez 9155cdc3d6 Undo slaves state change on failed rdbSaveToSlavesSockets().
As Oran Agra suggested, in startBgsaveForReplication() when the BGSAVE
attempt returns an error, we scan the list of slaves in order to remove
them since there is no way to serve them currently.

However we check for the replication state BGSAVE_START, which was
modified by rdbSaveToSlaveSockets() before forking(). So when fork fails
the state of slaves remain BGSAVE_END and no cleanup is performed.

This commit fixes the problem by making rdbSaveToSlavesSockets() able to
undo the state change on fork failure.
2015-09-07 16:21:33 +02:00
antirez 8c8a7cded9 Sentinel: fix bug in config rewriting during failover
We have a check to rewrite the config properly when a failover is in
progress, in order to add the current (already failed over) master as
slave, and don't include in the slave list the promoted slave itself.

However there was an issue, the variable with the right address was
computed but never used when the code was modified, and no tests are
available for this feature for two reasons:

1. The Sentinel unit test currently does not test Sentinel ability to
persist its state at all.
2. It is a very hard to trigger state since it lasts for little time in
the context of the testing framework.

However this feature should be covered in the test in some way.

The bug was found by @badboy using the clang static analyzer.

Effects of the bug on safety of Sentinel
===

This bug results in severe issues in the following case:

1. A Sentinel is elected leader.
2. During the failover, it persists a wrong config with a known-slave
entry listing the master address.
3. The Sentinel crashes and restarts, reading invalid configuration from
disk.
4. It sees that the slave now does not obey the logical configuration
(should replicate from the current master), so it sends a SLAVEOF
command to the master (since the slave master is the same) creating a
replication loop (attempt to replicate from itself) which Redis is
currently unable to detect.
5. This means that the master is no longer available because of the bug.

However the lack of availability should be only transient (at least
in my tests, but other states could be possible where the problem
is not recovered automatically) because:

6. Sentinels treat masters reporting to be slaves as failing.
7. A new failover is triggered, and a slave is promoted to master.

Bug lifetime
===

The bug is there forever. Commit 16237d78 actually tried to fix the bug
but in the wrong way (the computed variable was never used! My fault).
So this bug is there basically since the start of Sentinel.

Since the bug is hard to trigger, I remember little reports matching
this condition, but I remember at least a few. Also in automated tests
where instances were stopped and restarted multiple times automatically
I remember hitting this issue, however I was not able to reproduce nor
to determine with the information I had at the time what was causing the
issue.
2015-09-07 15:53:08 +02:00
antirez c47854615d Sentinel: clarify effect of resetting failover_start_time. 2015-09-07 15:53:08 +02:00
ubuntu 3ff2d65ffa SCAN iter parsing changed from atoi to chartoull 2015-09-07 13:25:38 +02:00
antirez 194b7e2186 Force slaves to resync after unsuccessful PSYNC.
Using chained replication where C is slave of B which is in turn slave of
A, if B reconnects the replication link with A but discovers it is no
longer possible to PSYNC, slaves of B must be disconnected and PSYNC
not allowed, since the new B dataset may be completely different after
the synchronization with the master.

Note that there are varius semantical differences in the way this is
handled now compared to the past. In the past the semantics was:

1. When a slave lost connection with its master, disconnected the chained
slaves ASAP. Which is not needed since after a successful PSYNC with the
master, the slaves can continue and don't need to resync in turn.

2. However after a failed PSYNC the replication backlog was not reset, so a
slave was able to PSYNC successfully even if the instance did a full
sync with its master, containing now an entirely different data set.

Now instead chained slaves are not disconnected when the slave lose the
connection with its master, but only when it is forced to full SYNC with
its master. This means that if the slave having chained slaves does a
successful PSYNC all its slaves can continue without troubles.

See issue #2694 for more details.
2015-08-21 15:32:04 +02:00
antirez 730f7c5f5e replicationHandleMasterDisconnection() belongs to replication.c. 2015-08-21 15:32:01 +02:00
antirez 12d2a89410 flushSlavesOutputBuffers(): details clarified via comments.
Talking with @oranagra we had to reason a little bit to understand if
this function could ever flush the output buffers of the wrong slaves,
having online state but actually not being ready to receive writes
before the first ACK is received from them (this happens with diskless
replication).

Next time we'll just read this comment.
2015-08-21 15:31:59 +02:00
antirez 7a02677097 Log client details on SLAVEOF command having an effect. 2015-08-21 15:31:11 +02:00
antirez 5630eeb12b startBgsaveForReplication(): handle waiting slaves state change.
Before this commit, after triggering a BGSAVE it was up to the caller of
startBgsavForReplication() to handle slaves in WAIT_BGSAVE_START in
order to update them accordingly. However when the replication target is
the socket, this is not possible since the process of updating the
slaves and sending the FULLRESYNC reply must be coupled with the process
of starting an RDB save (the reason is, we need to send the FULLSYNC
command and spawn a child that will start to send RDB data to the slaves
ASAP).

This commit moves the responsibility of handling slaves in
WAIT_BGSAVE_START to startBgsavForReplication() so that for both
diskless and disk-based replication we have the same chain of
responsiblity. In order accomodate such change, the syncCommand() also
needs to put the client in the slave list ASAP (just after the initial
checks) and not at the end, so that startBgsavForReplication() can find
the new slave alrady in the list.

Another related change is what happens if the BGSAVE fails because of
fork() or other errors: we now remove the slave from the list of slaves
and send an error, scheduling the slave connection to be terminated.

As a side effect of this change the following errors found by
Oran Agra are fixed (thanks!):

1. rdbSaveToSlavesSockets() on failed fork will get the slaves cleaned
up, otherwise they remain in a wrong state forever since we setup them
for full resync before actually trying to fork.

2. updateSlavesWaitingBgsave() with replication target set as "socket"
was broken since the function changed the slaves state from
WAIT_BGSAVE_START to WAIT_BGSAVE_END via
replicationSetupSlaveForFullResync(), so later rdbSaveToSlavesSockets()
will not find any slave in the right state (WAIT_BGSAVE_START) to feed.
2015-08-21 11:55:30 +02:00
antirez a7e9b38a48 slaveTryPartialResynchronization and syncWithMaster: better synergy.
It is simpler if removing the read event handler from the FD is up to
slaveTryPartialResynchronization, after all it is only called in the
context of syncWithMaster.

This commit also makes sure that on error all the event handlers are
removed from the socket before closing it.
2015-08-07 12:19:21 +02:00
antirez d4e4bd039a syncWithMaster(): non blocking state machine. 2015-08-07 12:18:29 +02:00
antirez c9df63c103 Fixed issues introduced during last merge. 2015-08-06 17:33:04 +02:00
antirez ce3a2d085b startBgsaveForReplication(): log what you really do. 2015-08-06 12:36:13 +02:00
antirez 6974e69f35 Replication: add REPLCONF CAPA EOF support.
Add the concept of slaves capabilities to Redis, the slave now presents
to the Redis master with a set of capabilities in the form:

    REPLCONF capa SOMECAPA capa OTHERCAPA ...

This has the effect of setting slave->slave_capa with the corresponding
SLAVE_CAPA macros that the master can test later to understand if it
the slave will understand certain formats and protocols of the
replication process. This makes it much simpler to introduce new
replication capabilities in the future in a way that don't break old
slaves or masters.

This patch was designed and implemented together with Oran Agra
(@oranagra).
2015-08-06 12:36:13 +02:00
antirez be56e4cf33 Fix synchronous readline "\n" handling.
Our function to read a line with a timeout handles newlines as requests
to refresh the timeout, however the code kept subtracting the buffer
size left every time a newline was received, for a bug in the loop
logic. Fixed by this commit.
2015-08-05 17:00:04 +02:00
antirez a67d67b561 Fix replication slave pings period.
For PINGs we use the period configured by the user, but for the newlines
of slaves waiting for an RDB to be created (including slaves waiting for
the FULLRESYNC reply) we need to ping with frequency of 1 second, since
the timeout is fixed and needs to be refreshed.
2015-08-05 17:00:04 +02:00
antirez 9a5560f4c3 Fix RDB encoding test for new csvdump format. 2015-08-05 14:05:34 +02:00
antirez 6da198cdf1 Remove slave state change handled by replicationSetupSlaveForFullResync(). 2015-08-05 14:00:25 +02:00
antirez 39994c2493 Make sure we re-emit SELECT after each new slave full sync setup.
In previous commits we moved the FULLRESYNC to the moment we start the
BGSAVE, so that the offset we provide is the right one. However this
also means that we need to re-emit the SELECT statement every time a new
slave starts to accumulate the changes.

To obtian this effect in a more clean way, the function that sends the
FULLRESYNC reply was overloaded with a more important role of also doing
this and chanigng the slave state. So it was renamed to
replicationSetupSlaveForFullResync() to better reflect what it does now.
2015-08-05 14:00:22 +02:00
antirez a89326f0f6 Test: csvdump now scans all DBs. 2015-08-05 14:00:18 +02:00
antirez b2ff48ef19 Don't send SELECT to slaves in WAIT_BGSAVE_START state. 2015-08-05 14:00:15 +02:00
antirez 7967f1bca6 PSYNC test: also test the vanilla SYNC. 2015-08-05 14:00:11 +02:00
antirez e684e7266c syncCommand() comments improved. 2015-08-05 14:00:08 +02:00
antirez 4b010572cd PSYNC initial offset fix.
This commit attempts to fix a bug involving PSYNC and diskless
replication (currently experimental) found by Yuval Inbar from Redis Labs
and that was later found to have even more far reaching effects (the bug also
exists when diskstore is off).

The gist of the bug is that, a Redis master replies with +FULLRESYNC to
a PSYNC attempt that fails and requires a full resynchronization.
However, the baseline offset sent along with FULLRESYNC was always the
current master replication offset. This is not ok, because there are
many reasosn that may delay the RDB file creation. And... guess what,
the master offset we communicate must be the one of the time the RDB
was created. So for example:

1) When the BGSAVE for replication is delayed since there is one
   already but is not good for replication.
2) When the BGSAVE is not needed as we attach one currently ongoing.
3) When because of diskless replication the BGSAVE is delayed.

In all the above cases the PSYNC reply is wrong and the slave may
reconnect later claiming to need a wrong offset: this may cause
data curruption later.
2015-08-05 14:00:04 +02:00
antirez dc4d24440f Test PSYNC with diskless replication.
Thanks to Oran Agra from Redis Labs for providing this patch.
2015-08-05 14:00:00 +02:00
Salvatore Sanfilippo d3688e8b68 Merge pull request #2666 from Abioy/2.8
bugfix: errno might change before logging
2015-07-17 10:44:18 +02:00
Salvatore Sanfilippo 88a38fe3d3 Merge pull request #2678 from Kiemes/fix-config-resetstat
Fix: aof_delayed_fsync is not reset, fixes #2677
2015-07-17 10:39:02 +02:00
Tom Kiemes cb98ae12f6 Fix: aof_delayed_fsync is not reset
aof_delayed_fsync was not set to 0 when calling CONFIG RESETSTAT
2015-07-16 11:57:07 +02:00
Yongyue Sun 9c7f98521e bugfix: errno might change before logging
Signed-off-by: Yongyue Sun <abioy.sun@gmail.com>
2015-07-13 14:59:05 +08:00
antirez fbb9d619f7 Fix 2.8.21 release notes to give full credits. 2015-06-04 12:00:21 +02:00
antirez 7f8b865a65 Redis 2.8.21 2015-06-04 11:32:24 +02:00
Ben Murphy 700b863f13 hide access to debug table 2015-06-03 13:36:02 +02:00
Ben Murphy 5a1b22ad7f disable loading lua bytecode 2015-06-03 13:36:02 +02:00
antirez 1eeb9bd714 Scripting: Lua cmsgpack lib updated to include str8 support 2015-06-03 08:45:56 +02:00
therealbill 22ee2f9cd5 adding a sentinel command: "flushconfig"
This new command triggers a config flush to save the in-memory config to
disk. This is useful for cases of a configuration management system or a
package manager wiping out your sentinel config while the process is
still running - and has not yet been restarted. It can also be useful
for scripting a backup and migrate or clone of a running sentinel.
2015-05-25 12:06:57 +02:00
antirez cdedad2322 Sentinel: CKQUORUM tests 2015-05-19 12:27:26 +02:00
antirez 76412ec9c0 Sentinel: SENTINEL CKQUORUM command
A way for monitoring systems to check that Sentinel is technically able
to reach the quorum and failover, using the currently visible Sentinels.
2015-05-19 12:27:26 +02:00
antirez f42fcff6d2 Rewrite smoveCommand test with ternary operator 2015-05-15 17:39:39 +02:00
Glenn Nethercutt 19382c8be6 uphold the smove contract to return 0 when the element is not a member of the source set, even if source=dest 2015-05-15 17:39:39 +02:00
Jungtaek Lim bea224308b protocol error log should be seen debug/verbose level 2015-05-15 17:06:45 +02:00
antirez 22979836e5 Fix 2.8.20 changelog typo 2015-05-05 11:50:30 +02:00
antirez 11934a9f32 Redis 2.8.20 2015-05-05 11:14:33 +02:00
FuGangqiang 8db3969971 sdsfree x and y 2015-05-04 13:03:06 +02:00
FuGangqiang 98756d4c0d fix doc example 2015-05-04 13:03:06 +02:00
FuGangqiang 7316fda33a fix typo 2015-05-04 13:03:06 +02:00
Itamar Haber a5bada19be update copyright year 2015-05-04 12:56:52 +02:00
therealbill f0ab4fd6fd Making sentinel flush config on +slave
Originally, only the +slave event which occurs when a slave is
reconfigured during sentinelResetMasterAndChangeAddress triggers a flush
of the config to disk.  However, newly discovered slaves don't
apparently trigger this flush but do trigger the +slave event issuance.

So if you start up a sentinel, add a master, then add a slave to the
master (as a way to reproduce it) you'll see the +slave event issued,
but the sentinel config won't be updated with the known-slave entry.

This change makes sentinel do the flush of the config if a new slave is
deteted in sentinelRefreshInstanceInfo.
2015-05-04 12:55:30 +02:00
antirez e4c54498db Sentinel: remove useless sentinelFlushConfig() call
To rewrite the config in the loop that adds slaves back after a master
reset, in order to handle switching to another master, is useless: it
just adds latency since there is an fsync call in the inner loop,
without providing any additional guarantee, but the contrary, since if
after the first loop iteration the server crashes we end with just a
single slave entry losing all the other informations.

It is wiser to rewrite the config at the end when the full new
state is configured.
2015-05-04 12:55:30 +02:00
Yossi Gottlieb 81a8ebd7f0 Fix Redis server crash when Lua command exceeds client output buffer
limit.
2015-05-04 12:20:28 +02:00
clark.kang c4d4c1ed12 fix sentinel memory leak 2015-05-04 12:18:13 +02:00
antirez 081a0c943f Fix Sentinel memory leak (hiredis bug)
This fixes issue #2535, that was actually an hiredis library bug (I
submitted an issue and fix to the redis/hiredis repo as well).

When an asynchronous hiredis connection subscribes to a Pub/Sub channel
and gets an error, and in other related conditions, the function
redisProcessCallbacks() enters a code path where the link is
disconnected, however the function returns before freeing the allocated
reply object. This causes a memory leak. The memory leak was trivial to
trigger in Redis Sentinel, which uses hiredis, every time we tried to
subscribe to an instance that required a password, in case the Sentinel
was configured either with the wrong password or without password at
all. In this case, the -AUTH error caused the leaking code path to be
executed.

It was verified with Valgrind that after this change the leak no longer
happens in Sentinel with a misconfigured authentication password.
2015-04-28 22:15:12 +02:00
antirez d593ae8ab6 sha1.c: use standard uint32_t. 2015-04-27 12:08:03 +02:00
Oran Agra c72253ec54 fixes to diskless replication.
master was closing the connection if the RDB transfer took long time.
and also sent PINGs to the slave before it got the initial ACK, in which case the slave wouldn't be able to find the EOF marker.
2015-04-01 15:21:08 +02:00
antirez 83eb5e7f54 Test: regression for issue #2473. 2015-03-27 12:11:46 +01:00
antirez 96aa610666 Replication: disconnect blocked clients when switching to slave role.
Bug as old as Redis and blocking operations. It's hard to trigger since
only happens on instance role switch, but the results are quite bad
since an inconsistency between master and slave is created.

How to trigger the bug is a good description of the bug itself.

1. Client does "BLPOP mylist 0" in master.
2. Master is turned into slave, that replicates from New-Master.
3. Client does "LPUSH mylist foo" in New-Master.
4. New-Master propagates write to slave.
5. Slave receives the LPUSH, the blocked client get served.

Now Master "mylist" key has "foo", Slave "mylist" key is empty.

Highlights:

* At step "2" above, the client remains attached, basically escaping any
  check performed during command dispatch: read only slave, in that case.
* At step "5" the slave (that was the master), serves the blocked client
  consuming a list element, which is not consumed on the master side.

This scenario is technically likely to happen during failovers, however
since Redis Sentinel already disconnects clients using the CLIENT
command when changing the role of the instance, the bug is avoided in
Sentinel deployments.

Closes #2473.
2015-03-24 16:14:49 +01:00
antirez 1f5e7b8863 Config: activerehashing option support in CONFIG SET. 2015-03-08 15:34:00 +01:00
antirez f8f40afd5d Fix iterator for issue #2438.
Itereator misuse due to analyzeLatencyForEvent() accessing the
dictionary during the iteration, without the iterator being
reclared as safe.
2015-03-04 11:49:53 -08:00
Charles Hooper 1f4074e957 override histfile from env - fixes #831 and copies #833 2015-02-10 14:55:17 +01:00
antirez 9e83d2d26f redis-cli --lru-test implemented (cache workload simulator). 2015-02-10 14:55:17 +01:00
antirez ccbec34fad redis-cli: interactive reconnection for latency modes.
--stat mode already used to reconnect automatically if the server is no
longer available. This is useful since this is an interactive mode used
for debugging, however the same applies to --latency and --latency-dist
modes, so now both use the reconnecting command execution as well.

The reconnection code was modified to use basic VT100 escape sequences
in order to play better with different kinds of output on the screen
when the reconnection happens, and to hide the reconnection attempt
output when finally the reconnection happens.
2015-02-10 14:55:17 +01:00
antirez d29eb68f20 redis-cli --latecy-dist reverted to gray scale.
So far not able to find a color palette within the 256 colors which is
not confusing. However I believe it is a possible task, so will try
better later.
2015-02-10 14:55:17 +01:00
antirez 657f719760 redis-cli --latency-dist now uses a color palette.
Still not happy with the result but low grays are hard to see in certain
monitors with a non perfect gamma.
2015-02-10 14:55:17 +01:00
antirez ff13fa787d redis-cli latency dist: add new top HL. 2015-02-10 14:55:17 +01:00
antirez f6f3cb9458 Add missing latency-dest legend symbol. 2015-02-10 14:55:17 +01:00
antirez 9d9be0e09d Initial implementation of redis-cli --latency-dist. 2015-02-10 14:55:17 +01:00
antirez d4047f7261 Faster memory efficiency test.
This test on Linux was extremely slow, since in Tcl we can't enable
easily tcp-nodelay, so the busy loop used to take *a lot* with bigger
writes. Fixed using pipelining.
2015-02-10 14:48:22 +01:00
mattcollier 61290d819d Update redis-cli.c
Code was adding '\n'  (line 521) to the end of NIL values exlusively making csv output inconsistent.  Removed '\n'
2015-02-02 22:50:53 +01:00
antirez 18dc769104 luaRedisGenericCommand(): log error at WARNING level when re-entered.
Rationale is that when re-entering, it is likely due to Lua debugging
hooks. Returning an error will be ignored in most cases, going totally
unnoticed. With the log at least we leave a trace.

Related to issue #2302.
2015-01-22 10:36:23 +01:00
antirez 1dea8b5a29 luaRedisGenericCommand() recursion: just return an error.
Instead of calling redisPanic() to abort the server.

Related to issue #2302.
2015-01-22 10:36:19 +01:00
antirez a0ba7b3f00 Panic on recursive calls to luaRedisGenericCommand().
Related to issue #2302.
2015-01-22 10:36:15 +01:00
antirez e7aa5fb5ef AOF rewrite: set iterator var to NULL when freed.
The cleanup code expects that if 'di' is not NULL, it is a valid
iterator that should be freed.

The result of this bug was a crash of the AOF rewriting process if an
error occurred after the DBs data are written and the iterator is no
longer valid.
2015-01-21 16:42:56 +01:00
antirez 8a41dae49f Add "-lrt" in Makefile for Solaris.
This fix is from @NanXiao, however I was not able to retain authorship
because the Pull Request original repository was removed.
2015-01-09 11:54:52 +01:00
antirez 50cdedd127 Check for __sun macro in solarisfixes.h, not in includers. 2015-01-09 11:22:35 +01:00
antirez 77608ae5ac README section about make distclean reworded / extended. 2015-01-08 16:35:53 +01:00
Jungtaek Lim 5165fb98af Explain make distclean which seems not well known 2015-01-08 16:35:49 +01:00
antirez 22a0fe8de6 INFO loading stats: three fixes.
1. Server unxtime may remain not updated while loading AOF, so ETA is
not updated correctly.

2. Number of processed byte was not initialized.

3. Possible division by zero condition (likely cause of issue #1932).
2014-12-23 14:52:57 +01:00
Alon Diamant 585884da45 Fixed memory leaks in rdbSaveToSlavesSockets() 2014-12-22 11:01:21 +01:00
antirez ca3dd5e5ed Fix adjustOpenFilesLimit() logging to match real state.
Fixes issue #2225.
2014-12-19 10:05:09 +01:00
Rhommel Lamas 6f0a7353a6 Add redis-sentinel symlink to a relative path 2014-12-17 12:27:44 +01:00
antirez 902b877dc0 Redis 2.8.19. 2014-12-16 09:18:20 +01:00
antirez 02d465c8fe Don't log admin commands in MONITOR.
Otherwise there are security risks, especially when providing Redis as a
service, the user may "sniff" for admin commands renamed to an
unguessable string via rename-command in redis.conf.
2014-12-13 09:00:03 +01:00
antirez 4d8f4262a4 List of commands flagged as admin commands modified.
The old list did not made much sense... and the flag is currently not
used at all, so no side effects.
2014-12-13 08:59:43 +01:00
Salvatore Sanfilippo d4fc65309d Merge pull request #2092 from rhoml/rhoml-install-sentinel
Adds a symlink for redis-sentinel when Make install
2014-12-12 22:58:56 +01:00
antirez e47e460f1f Lua cmsgpack lib updated to latest version.
It fixes a bad bug that crashes the server in certain conditions
as shown in issue #2210.
2014-12-12 22:57:09 +01:00
Rhommel Lamas 5509c141f8 Add symlink to redis-sentinel during make install 2014-12-12 20:00:08 +01:00
antirez 7de1ef773e SORT: Don't sort Set elements if not needed.
Related to #2094.
2014-12-11 16:01:21 +01:00
Matt Stancliff e945a546af Fix zero-ordering SORT when called against lists
People mostly use SORT against lists, but our prior
behavior was pretending lists were an unordered bag
requiring a forced-sort when no sort was requested.

We can just use the native list ordering to ensure
consistency across replicaion and scripting calls.

Closes #2079
Closes #545 (again)
2014-12-11 16:01:09 +01:00
Ben Dowling d81c38316b Update redis_init_script.tpl
status command currently reports success when redis has crashed and the pid file still exists. Changing to check the actual process is running.
2014-12-11 15:21:53 +01:00
zhanghailei dba57ea910 FIXED redis-benchmark's idle mode.With idle mode shouldn't create write event 2014-12-11 15:21:53 +01:00
Sun He 888ea17548 zipmap.c: update comments above 2014-12-09 16:01:13 +01:00
Deepak Verma 86ebc139c2 replaced // comments #2150 2014-12-09 15:26:26 +01:00
azure provisioned user 3d73f088e1 redis-benchmark AUTH command to be discarded after the first send #2150 2014-12-09 15:26:26 +01:00
Sun He 76d53a6770 sds.c: Correct two spelling mistakes in comments 2014-12-09 15:14:14 +01:00
Sun He 4848cf9335 sds.c/sdscatvprintf: set va_end to finish va_list cpy 2014-12-09 15:14:14 +01:00
Sun He d2f584fa53 sds.c: Correct some comments 2014-12-09 15:14:13 +01:00
Serghei Iakovlev 2ed3f09cc3 Update whatisdoing.sh
Improved getting pid
2014-12-09 13:01:31 +01:00
antirez 77b997d429 Include stropts only if __sun is defined. 2014-12-09 12:59:43 +01:00
Jan-Erik Rediger d409371193 Fix implicit declaration of ioctl on Solaris 2014-12-09 12:57:59 +01:00
Johan Bergström 23b96c02a5 Silence _BSD_SOURCE warnings in glibc 2.20 and forward
See https://sourceware.org/glibc/wiki/Release/2.20#Packaging_Changes
2014-12-09 12:24:30 +01:00
antirez a47a042ea6 Mark whatisdoing.sh as deprecated in top-comment. 2014-12-09 12:18:47 +01:00
Serghei Iakovlev b5737d2d19 getting pid fixes
```sh
$ ~ pidof redis-server
# nothing

$ ~ ps aux | grep [r]edis
redis      593  0.0  0.0  36900  5564 ?        Ssl  Dec02   1:37 /usr/bin/redis-server 127.0.0.1:6379
klay     15927  0.0  0.0  16772  6068 pts/6    S+   13:58   0:00 redis-cli

$ ~ uname -a
Linux edge 3.17.4-1-ARCH #1 SMP PREEMPT Fri Nov 21 21:14:42 CET 2014 x86_64 GNU/Linux
```
2014-12-09 12:09:31 +01:00
Sun He a598e0894b sparkline.c: AddSample skip Empty label 2014-12-09 11:28:53 +01:00
Sun He 7d480abab4 sparkline.c: mov label-ini into the AddSample Function 2014-12-09 11:28:53 +01:00
Jan-Erik Rediger 2f3c860979 Only ignore sigpipe in interactive mode
This allows shell pipes to correctly end redis-cli.

Ref #2066
2014-12-09 09:01:10 +01:00
antirez 0c211a1953 Simplify lua_cmsgpack macro and fix build on old Linux distros.
Thanks to @badboy for the help in checking the build after the fix.
2014-12-05 10:52:56 +01:00
antirez d6ada4d1a8 Redis 2.8.18. 2014-12-04 11:36:52 +01:00
antirez 7c671c09a1 Check that tcp-backlog is matched by /proc/sys/net/core/somaxconn. 2014-12-04 11:07:55 +01:00
antirez d56ef62983 Network bandwidth tracking + refactoring.
Track bandwidth used by clients and replication (but diskless
replication is not tracked since the actual transfer happens in the
child process).

This includes a refactoring that makes tracking new instantaneous
metrics simpler.
2014-12-03 12:17:18 +01:00
antirez f60c8c0999 Don't show the ASCII logo if syslog is enabled.
Closes issue #1935.
2014-12-03 10:52:09 +01:00
antirez 04231cb7d7 Regression test for issue #2175. 2014-12-03 10:41:06 +01:00
antirez 6e0277769d Handle infinite explicitly in createStringObjectFromLongLong(). 2014-12-03 10:41:06 +01:00
antirez 42695fdfe6 Use exp format and more precision output for ZSCAN.
Ref: issue #2175
2014-12-03 10:41:06 +01:00
antirez 164ad2ead1 Over 80 chars comment trimmed in pfcountCommand(). 2014-12-02 17:03:15 +01:00
antirez 0e40124d0f Mark PFCOUNT as read-only, even if not true.
PFCOUNT is technically speaking a write command, since the cached value
of the HLL is exposed in the data structure (design error, mea culpa), and
can be modified by PFCOUNT.

However if we flag PFCOUNT as "w", read only slaves can't execute the
command, which is a problem since there are environments where slaves
are used to scale PFCOUNT reads.

Nor it is possible to just prevent PFCOUNT to modify the data structure
in slaves, since without the cache we lose too much efficiency.

So while this commit allows slaves to create a temporary inconsistency
(the strings representing the HLLs in the master and slave can be
different in certain moments) it is actually harmless.

In the long run this should be probably fixed by turning the HLL into a
more opaque representation, for example by storing the cached value in
the part of the string which is not exposed (this should be possible
with SDS strings).
2014-12-02 17:01:55 +01:00
antirez d0cc96f053 Mark diskless replication as experimental in redis.conf. 2014-12-02 14:13:44 +01:00
antirez a81043959a Test: wait for actual startup in start_server.
start_server now uses return value from Tcl exec to get the server pid,
however this introduces errors that depend from timing: a lot of the
testing code base assumed the server to be actually up and running when
server_start returns.

So the old code that waits to see the pid in the log file was restored.
2014-11-28 12:57:40 +01:00
antirez f0c00a14ed Test: try to cleanup still running Redis instances on exit.
It's hard to run the Redis test continuously if it leaks processes on
exceptions / errors.
2014-11-28 12:57:30 +01:00
antirez 3ac62dff23 Test framework: exit on timeout with report.
There was no sane way to detect tests that may never end because of
Redis bugs or tests bugs.
2014-11-28 12:54:17 +01:00
antirez 60c09dcf09 redis-benchmark: default num of requests is now 100000.
10000 completes in a too short time and may easily provide unreliable
figures because of tiny duration.
2014-11-28 10:38:58 +01:00
Matthias Petschick e689e93663 fix benchmark memleak in loop mode 2014-11-28 10:38:54 +01:00
antirez 01854f87e8 Document redis-cli --stat in --help output. 2014-11-25 18:25:58 +01:00
Matt Stancliff e305df099a Fix lua-cmsgpack 64 bit integer on 32 bit platform
This syncs lua-cmsgpack with the mattsta/lua-cmsgpack upstream.

Fixes #2161
2014-11-25 15:56:57 +01:00
antirez 9854d03fad Avoid valgrind memory leak false positive in processInlineBuffer().
zmalloc(0) cauesd to actually trigger a non-zero allocation since with
standard libc malloc we have our own zmalloc header for memory tracking,
but at the same time the returned pointer is at the end of the block and
not in the middle. This triggers a false positive when testing with
valgrind.

When the inline protocol args count is 0, we now avoid reallocating
c->argv, preventing the issue to happen.
2014-11-25 14:51:04 +01:00
antirez 5ddf56f9b1 Attempt to prevent false positives in replication test. 2014-11-24 11:55:16 +01:00
antirez b19e745780 scripting.tcl tests order fixed to match unstable branch. 2014-11-14 17:28:58 +01:00
antirez ce96069676 lua_cjson.c Lua includes: angled -> quoted. 2014-11-14 17:28:54 +01:00
antirez ead8e31b28 Fix non-linux builds error introduced with THP checks. 2014-11-14 17:28:49 +01:00
Matt Stancliff 5d8c88fdc1 Lua: add cmsgpack scripting tests
Basically: test to make sure we can load cmsgpack
and do some sanity checks to make sure pack/unpack works
properly.  We also have a bonus test for circular encoding
and decoding because I was curious how it worked.
2014-11-14 17:28:45 +01:00
Matt Stancliff 381863be5c Lua: upgrade cmsgpack to 0.4.0
Main reasons for upgrade:
  - Remove a warning when building Redis
  - Add multi pack/unpack
  - Improve memory usage and use Lua allocator properly
  - Fix some edge case encoding/decoding bugs
2014-11-14 17:28:40 +01:00
Matt Stancliff a7b58f1569 Lua: remove new warning added by cjson header
clang doesn't like "extern inline" when no definition
is given right away.
2014-11-14 17:28:35 +01:00
Matt Stancliff 56e1861398 Lua: Use Redis solaris compatability for cjson too
cjson calls isinf, but some Solaris versions don't have isinf
even with the attempted fix we have in deps/Makefile.

We can harmlessly include the Redis solarisfixes.h header to
give cjson proper isinf.

Note: cjson has a compile-time setting for using their own defined
isinf, but the Redis definition in solarisfixes.h is more complete.

Fixes antirez#1620
2014-11-14 17:28:32 +01:00
Matt Stancliff e2983dd03f Lua: Upgrade cjson to 2.1.0 (2012-03-01)
The new cjson has some improvements over our current version including
increased platform compatability, a new resource limit to restrict
decode depth, and better invalid number handling.

One minor change was required to deps/Makefile because this version
of cjson doesn't export itself globally, so we added a quick little
define of -DENABLE_CJSON_GLOBAL.

cjson now has an optional higher performing float parsing interface,
but we are not including it (g_fmt.c, dtoa.c) because it requires
endianness declaration during compile time.

This commit is exactly lua_cjson.c from 2.1.0 with one minor
change of altering the two Lua includes for local search
instead of system-wide importing.
2014-11-14 17:28:27 +01:00
Matt Stancliff 2828bbd511 Lua: add cjson scripting test
Two simple decode tests added mainly to check that
the 'cjson' global gets registered and is usable.
2014-11-14 17:28:23 +01:00
Matt Stancliff 1e501d9fd6 Lua: Add bitop
A few people have written custom C commands because bit
manipulation isn't exposed through Lua.  Let's give
them Mike Pall's bitop.

This adds bitop 1.0.2 (2012-05-08) from http://bitop.luajit.org/

bitop is imported as "bit" into the global namespace.

New Lua commands: bit.tobit, bit.tohex, bit.bnot, bit.band, bit.bor, bit.bxor,
bit.lshift, bit.rshift, bit.arshift, bit.rol, bit.ror, bit.bswap

Verification of working (the asserts would abort on error, so (nil) is correct):
127.0.0.1:6379> eval "assert(bit.tobit(1) == 1); assert(bit.band(1) == 1); assert(bit.bxor(1,2) == 3); assert(bit.bor(1,2,4,8,16,32,64,128) == 255)" 0
(nil)
127.0.0.1:6379> eval 'assert(0x7fffffff == 2147483647, "broken hex literals"); assert(0xffffffff == -1 or 0xffffffff == 2^32-1, "broken hex literals"); assert(tostring(-1) == "-1", "broken tostring()"); assert(tostring(0xffffffff) == "-1" or tostring(0xffffffff) == "4294967295", "broken tostring()")' 0
(nil)

Tests also integrated into the scripting tests and can be run with:
./runtest --single unit/scripting

Tests are excerpted from `bittest.lua` included in the bitop distribution.
2014-11-14 17:28:19 +01:00
antirez d9326cb83d THP detection for LATENCY DOCTOR. 2014-11-12 11:17:31 +01:00
antirez 011cfdfb6b Check THP support at startup and warn about it. 2014-11-12 11:17:27 +01:00
antirez c0266d87ae THP detection / reporting functions added. 2014-11-12 11:17:24 +01:00
antirez a5fcf44fcc Diskless SYNC: fix RDB EOF detection.
RDB EOF detection was relying on the final part of the RDB transfer to
be a magic 40 bytes EOF marker. However as the slave is put online
immediately, and because of sockets timeouts, the replication stream is
actually contiguous with the RDB file.

This means that to detect the EOF correctly we should either:

1) Scan all the stream searching for the mark. Sucks CPU-wise.
2) Start to send the replication stream only after an acknowledge.
3) Implement a proper chunked encoding.

For now solution "2" was picked, so the master does not start to send
ASAP the stream of commands in the case of diskless replication. We wait
for the first REPLCONF ACK command from the slave, that certifies us
that the slave correctly loaded the RDB file and is ready to get more
data.
2014-11-11 17:27:22 +01:00
antirez ec1ab0f18b Disconnect timedout slave: regression introduced with diskless repl. 2014-11-11 17:25:58 +01:00
Matt Stancliff eb67ca0f00 redis-cli: ignore SIGPIPE network errors
Closes #2066
2014-10-30 12:02:32 +01:00
antirez bb9ad32cb0 Fix DEBUG POPULATE warning for lack of casting. 2014-10-29 14:36:55 +01:00
antirez a37f8ec8b4 DEBUG POPULATE two args form implemented.
The old DEBUG POPULATE form for automatic creation of test keys is:

    DEBUG POPULATE <count>

Now an additional form is available:

    DEBUG POPULATE <count> <prefix>

When prefix is not specified, it defaults to "key", so the keys are
named incrementally from key:0 to key:<count-1>. Otherwise the specified
prefix is used instead of "key".

The command is useful in order to populate different Redis instances
with key names guaranteed to don't collide. There are other debugging
uses, for example it is possible to add additional N keys using a count
of N and a random prefix at every call.
2014-10-29 14:36:55 +01:00
antirez b2509d7759 redis-cli: add missing newline in error message. 2014-10-29 14:35:48 +01:00
antirez 9b7a190c82 Diskless replication: missing listRewind() added.
This caused BGSAVE to be triggered a second time without any need when
we switch from socket to disk target via the command

    CONFIG SET repl-diskless-sync no

and there is already a slave waiting for the BGSAVE to start.
Also comments clarified about what is happening.
2014-10-29 14:34:58 +01:00
antirez 49629a2e34 Log slave ip:port in more log messages. 2014-10-29 14:34:58 +01:00
antirez 081eeb9f6b Use new slave name function for diskless repl reporting. 2014-10-29 14:34:58 +01:00
antirez 72ea77af0b Added a function to get slave name for logs. 2014-10-29 14:34:51 +01:00
antirez 16319c69b1 Diskless replication: log BGSAVE delay only when it is non-zero. 2014-10-29 14:34:25 +01:00
antirez bafd3f646f Document repl-diskless-sync-delay in redis.conf. 2014-10-29 14:34:25 +01:00
antirez f71c4d54d1 Diskless sync delay is now configurable. 2014-10-29 14:34:18 +01:00
antirez 4b520177b3 Remove duplicated log message about starting BGSAVE. 2014-10-29 14:33:50 +01:00
antirez 7e8a471baf Diskless replication documented inside example redis.conf. 2014-10-29 14:33:50 +01:00
antirez d57ed95222 Diskless replication tested with the multiple slaves consistency test. 2014-10-29 14:33:50 +01:00
antirez 7d32dfc69f Diskless replication: child -> parent communication improved.
Child now reports full info to the parent including IDs of slaves in
failure state and exit code.
2014-10-29 14:33:50 +01:00
antirez 7728ee809d Translate rio fdset target EWOULDBLOCK error into ETIMEDOUT.
EWOULDBLOCK with the fdset rio target is returned when we try to write
but the send timeout socket option triggered an error. Better to
translate the error in something the user can actually recognize as a
timeout.
2014-10-29 14:33:50 +01:00
antirez 7cffd74b38 Diskless replication: set / reset socket send timeout.
We need to avoid that a child -> slaves transfer can continue forever.
We use the same timeout used as global replication timeout, which is
documented to also affect I/O operations during bulk transfers.
2014-10-29 14:33:50 +01:00
antirez eff8fb6f39 anet.c: new API anetSendTimeout(). 2014-10-29 14:33:50 +01:00
antirez c38bb5b2ea Diskless replication: less debugging printfs around. 2014-10-29 14:33:50 +01:00
antirez 348c4f6ce3 rio.c fdset write() method fixed: wrong type for return value. 2014-10-29 14:33:50 +01:00
antirez a7d0137075 rio fdset target: handle short writes.
While the socket is set in blocking mode, we still can get short writes
writing to a socket.
2014-10-29 14:33:50 +01:00
antirez 23bf435f54 anet.c: API to set sockets back to blocking mode. 2014-10-29 14:33:50 +01:00
antirez 9a521e6c74 Diskless replication: rio fdset target new supports buffering.
To perform a socket write() for each RDB rio API write call was
extremely unefficient, so now rio has minimal buffering capabilities.
Writes are accumulated into a buffer and only when a given limit is
reacehd are actually wrote to the N slaves FDs.

Trivia: rio lacked support for buffering since our targets were:

1) Memory buffers.
2) C standard I/O.

Both were buffered already.
2014-10-29 14:33:50 +01:00
antirez 9588973fda Diskless replication: Various fixes to backgroundSaveDoneHandlerSocket() 2014-10-29 14:33:50 +01:00
antirez 8a618971bd Diskless replication: don't send "\n" pings to slaves.
This is useful for normal replication in order to refresh the slave
when we are persisting on disk, but for diskless replication the
child is already receiving data while in WAIT_BGSAVE_END state.
2014-10-29 14:33:50 +01:00
antirez b794b94797 Diskless replication: remove 40 bytes EOF mark from end of RDB file. 2014-10-29 14:33:50 +01:00
antirez 3f1f29fdf3 Diskless replication: swap inverted branches to compute read len. 2014-10-29 14:33:50 +01:00
antirez dbc6e9a1ef Diskless replication: don't enter the read-payload branch forever. 2014-10-29 14:33:49 +01:00
antirez adf55b2337 syncReadLine(): actually enforce buffer size limits. 2014-10-29 14:33:49 +01:00
antirez 89f82a25e1 Diskless replication: EOF:<mark> streaming support slave side. 2014-10-29 14:33:49 +01:00
antirez 583a762cd9 Diskless replication: redis.conf and CONFIG SET/GET support. 2014-10-29 14:33:49 +01:00
antirez 31b0e13268 Diskless replication: trigger a BGSAVE after a config change.
If we turn from diskless to disk-based replication via CONFIG SET, we
need a way to start a BGSAVE if there are slaves alerady waiting for a
BGSAVE to start. Normally with disk-based replication we do it as soon
as the previous child exits, but when there is a configuration change
via CONFIG SET, we may have slaves in WAIT_BGSAVE_START state without
an RDB background process currently active.
2014-10-29 14:33:49 +01:00
antirez d403e4ae63 Diskless replication flag renamed repl_diskless -> repl_diskless_sync. 2014-10-29 14:33:49 +01:00
antirez 2f0b58e4ed Diskless replication: trigger diskless RDB transfer if needed. 2014-10-29 14:33:49 +01:00
antirez 82bfae5b70 Diskless replication: handle putting the slave online. 2014-10-29 14:33:49 +01:00
antirez 132c108f17 Diskless replication: read report from child. 2014-10-29 14:33:49 +01:00
antirez e82ec2c5d8 Diskless replication: child writes report to parent. 2014-10-29 14:33:49 +01:00
antirez 2ed67e4a9e rio.c fdset target: tolerate (and report) a subset of FDs in error.
Fdset target is used when we want to write an RDB file directly to
slave's sockets. In this setup as long as there is a single slave that
is still receiving our payload, we want to continue sennding instead of
aborting. However rio calls should abort of no FD is ok.

Also we want the errors reported so that we can signal the parent who is
ok and who is broken, so there is a new set integers with the state of
each fd. Zero is ok, non-zero is the errno of the failure, if avaialble,
or a generic EIO.
2014-10-29 14:33:49 +01:00
antirez 51864fcdd7 Diskless replication: parent-child pipe and a few TODOs. 2014-10-29 14:33:49 +01:00
antirez 1b4cadb664 Diskless replication: RDB -> slaves transfer draft implementation. 2014-10-29 14:33:49 +01:00
antirez 38dee9dc51 rio.c: draft implementation of fdset target implemented. 2014-10-29 14:33:49 +01:00
antirez 5d295fea12 rio.c refactoring before adding a new target. 2014-10-29 14:33:49 +01:00
antirez aef4c60c78 Add some comments in syncCommand() to clarify RDB target. 2014-10-29 14:33:49 +01:00
antirez 81abd1e178 Define different types of RDB childs.
We need to remember what is the saving strategy of the current RDB child
process, since the configuration may be modified at runtime via CONFIG
SET and still we'll need to understand, when the child exists, what to
do and for what goal the process was initiated: to create an RDB file
on disk or to write stuff directly to slave's sockets.
2014-10-29 14:33:49 +01:00
antirez 1e633f21c5 RDB file creation refactored to target non-disk target. 2014-10-29 14:33:49 +01:00
antirez ff8a3baaf7 Replication: better way to send a preamble before RDB payload.
During the replication full resynchronization process, the RDB file is
transfered from the master to the slave. However there is a short
preamble to send, that is currently just the bulk payload length of the
file in the usual Redis form $..length..<CR><LF>.

This preamble used to be sent with a direct write call, assuming that
there was alway room in the socket output buffer to hold the few bytes
needed, however this does not scale in case we'll need to send more
stuff, and is not very robust code in general.

This commit introduces a more general mechanism to send a preamble up to
2GB in size (the max length of an sds string) in a non blocking way.
2014-10-29 14:33:30 +01:00
antirez b6203138d3 Linenoise README updated to match source code. 2014-10-06 10:02:08 +02:00
Miguel Parramon 85b15acb44 redis.conf small grammar fix
😄

Closes #2034
2014-10-06 10:02:08 +02:00
Ezequiel Lovelle 09fc0fbbe4 Fix typo
Closes #2029
2014-10-06 10:02:08 +02:00
Gregory Petrosyan 03b36eef72 Fix typos in comments
Closes #2002
2014-10-06 10:01:36 +02:00
Matt Stancliff e1ccd7243f Add missing 'by' 2014-10-06 10:01:36 +02:00
Juarez Bochi 364aeed96d Fix typo in scripting.c comment
Closes #1960
2014-10-06 10:01:36 +02:00
T.J. Schuck a7a2f69bdb Fix typo radis -> redis
Closes #1938
2014-10-06 10:01:36 +02:00
Matt Stancliff 1adc4a7888 Cleanup wording of dictScan() comment
Some language in the comment was difficult
to understand, so this commit: clarifies wording, removes
unnecessary words, and relocates some dependent clauses
closer to what they actually describe.

I also tried to break up longer chains of thought
(if X, then Y, and Q, and also F, so obviously M)
into more manageable chunks for ease of understanding.
2014-10-06 10:01:36 +02:00
Matt Stancliff c3375fbdff Remove trailing spaces from tests 2014-10-06 10:01:27 +02:00
Matt Stancliff c4d32c03d0 Fix spelling in some test cases 2014-10-06 10:01:05 +02:00
Jan-Erik Rediger 919a4c3126 Fix sentinel.conf typo
Closes #1739
2014-10-06 10:00:48 +02:00
Xiaojie Zhang 8a1d16fe5c Fix comment inconsistencies in ziplist.c
Closes #1523
2014-10-06 10:00:48 +02:00
Matt Stancliff e0ffc863c6 Clean up text throughout project
- Remove trailing newlines from redis.conf
  - Fix comment misspelling
  - Clarifies zipEncodeLength usage and a C API mention (#1243, #1242)
  - Fix cluster typos (inspired by @papanikge #1507)
  - Fix rewite -> rewrite in a few places (inspired by #682)

Closes #1243, #1242, #1507
2014-10-06 10:00:43 +02:00
Matt Stancliff 22e6f434f8 Spell software correctly 2014-10-06 09:59:52 +02:00
Aaron Rutkovsky 3101760937 Fix typos
Closes #1513
2014-10-06 09:59:02 +02:00
Ben d3005539c5 Grammar and typo fixes in redis.conf
Closes #1441
2014-10-06 09:58:56 +02:00
xuxiang 3c99e429cf Fix typo in redis.h
Closes #1386
2014-10-06 09:58:18 +02:00
Ted Nyman 55597bbd67 Fix on-place -> in-place
Closes #1373
2014-10-06 09:58:18 +02:00
Michael Parker a5044c254d Fix hash table size in comment for dictScan
Closes #1351
2014-10-06 09:58:18 +02:00
Jan-Erik Rediger 33daf8e9a9 Fix many small typos
Closes #1871
2014-10-06 09:58:11 +02:00
zionwu b72422e100 Fix incorrect comments
error != success; and 0 != number of bytes written

Closes #1806
2014-10-06 09:57:01 +02:00
Jan-Erik Rediger 6cdd38f947 Fix typo in redis.conf
Closes #1713
2014-10-06 09:57:01 +02:00
Juarez Bochi 6b64fa49f8 Fix typo
Closes #1682
2014-10-06 09:57:01 +02:00
Agis Anastasopoulos fc9bce45c5 Fix typo
Closes #1544
2014-10-06 09:57:01 +02:00
Jan-Erik Rediger 575e293ddd Fix typo: ad -> and
Closes #1537
2014-10-06 09:57:01 +02:00
Matt Stancliff d05fcaffa0 Update linenoise accessory files
The core linenoise code was being backported, but not
the README or example.  It's less confusing for users
if everything matches across directories.

Fix inspired by @thrig

Closes #1872
2014-10-06 09:57:01 +02:00
Manuel Meurer a99d187bc4 Improve disable save comment in redis.conf
Closes #1897
2014-10-06 09:57:01 +02:00
Lynn 904bd0400c Remove surplus double quotes
Closes #1877
2014-10-06 09:57:01 +02:00
antirez 04b6945ebc Linenoise lib updated. 2014-09-22 14:56:01 +02:00
antirez ade6c5e782 Redis 2.8.17 2014-09-19 17:49:16 +02:00
Matt Stancliff cf143b571e Fix hiredis getaddrinfo leak
Fixed in Redis by 1a5e5b6, but since that part of code
is largely copy/paste from Redis, the fix needs to be
ported over too.

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

This commit fixes the issue by truncating the AOF to the last valid
opcode, and aborting if it is not possible to truncate the file
correctly.
2014-09-16 10:41:15 +02:00
antirez fbd9dc6039 Don't propagate SAVE.
This is a general fix (check that dirty delta is positive) but actually
should have as the only effect fixing the SAVE propagation to
AOF and slaves.
2014-09-16 10:41:15 +02:00
93 changed files with 4471 additions and 1334 deletions
+205
View File
@@ -14,6 +14,211 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
--------------------------------------------------------------------------------
--[ Redis 2.8.23 ] Release date: 15 Oct 2015
Upgrade urgency: MODERATE, the most important thing is a fix in the replication
code that may make the slave hanging forever if the master
remains with an open socket even if it is no longer able to
reply.
* [FIX] MOVE now moves the TTL as well. A bug lasting forever... finally
fixed thanks to Andy Grunwald that reported it.
(reported by Andy Grunwald, fixed by Salvatore Sanfilippo)
* [FIX] Fix a false positive in HSTRLEN test.
* [FIX] Fix a bug in redis-cli --pipe mode that was not able to read back
replies from the server incrementally. Now a mass import will use
a lot less memory, and you can use --pipe to do incremental streaming.
(reported by Twitter user @fsaintjacques, fixed by Salvatore
Sanfilippo)
* [FIX] Slave detection of master timeout. (fixed by Kevin McGehee, refactoring
and regression test by Salvatore Sanfilippo)
--[ Redis 2.8.22 ] Release date: 8 Sep 2015
Upgrade urgency: HIGH for Redis and Sentinel. However note that in order to
fix certain replication bugs, the replication internals were
modified in a very heavy way. So while this release is
conceptually saner, it may contain regressions. For this
reason, before the release, QA activities were performed by
me (antirez) and Redis Labs and no evident bug was found.
* [FIX] A number of bugs related to replication PSYNC and the (yet experimental)
diskless replication feature were fixed. The bugs could lead to
inconsistency between masters and slaves. (Salvatore Sanfilippo, Oran
Agra fixed the issue found by Yuval Inbar)
* [FIX] A replication bug in the context of PSYNC partial resynchonization was
found and fixed. This bug happens even when diskless replication is off
in the case different slaves connect at different times while the master
is creating an RDB file, and later a partial resynchronization is
attempted by a slave that connected not as the first one. (Salvatore
Sanfilippo, Oran Agra)
* [FIX] Chained replication and PSYNC interactions leading to potential stale
chained slaves data set, see issue #2694. (Salvatore Sanfilippo fixed
an issue reported by "GeorgeBJ" user at Github)
* [FIX] redis-cli --scan iteration fixed when returned cursor overflows
32 bit signed integer. (Ofir Luzon, Yuval Inbar)
* [FIX] Sentinel: fixed a bug during the master switch process, where for a
failed conditional check, the new configuration is rewritten, during
a small window of time, in a corrupted way where the master is
also reported to be one of the slaves. This bug is rare to trigger
but apparently it happens in the wild, and the effect is to see
a replication loop where the master will try to replicate with itself.
A detailed explanation of the bug and its effects can be found in
the commit message here: https://github.com/antirez/redis/commit/c20218eb5770b2cafb12bc7092313b8358fedc0a.
The bug was found by Jan-Erik Rediger using a static analyzer and
fixed by Salvatore Sanfilippo.
* [FIX] Sentinel lack of arity checks for certain commands.
(Rogerio Goncalves, Salvatore Sanfilippo)
* [NEW] Replication internals rewritten in order to be more resistant to bugs.
The replication handshake in the slave side was rewritten as a non
blocking state machine. (Salvatore Sanfilippo, Oran Agra)
* [NEW] New "replication capabilities" feature introduced in order to signal
from the master to the slave what are the features supported, so that
the master can choose the kind of replication to start (diskless or
not) when master and slave are of different versions. (Oran Agra,
Salvatore Sanfilippo)
* [NEW] Log clients details when SLAVEOF command is received. (Salvatore
Sanfilippo with inputs from Nick Craver and Marc Gravell).
--[ Redis 2.8.21 ] Release date: 4 Jun 2015
Upgrade urgency: HIGH for Redis because of a security issue.
LOW for Sentinel.
* [FIX] Critical security issue fix by Ben Murphy: http://t.co/LpGTyZmfS7
* [FIX] SMOVE reply fixed when src and dst keys are the same. (Glenn Nethercutt)
* [FIX] Lua cmsgpack lib updated to support str8 type. (Sebastian Waisbrot)
* [NEW] Senitnel: CKQUORUM and FLUSHCONFIG commands back ported.
(Salvatore Sanfilippo and Bill Anderson)
--[ Redis 2.8.20 ] Release date: 5 May 2015
Upgrade urgency: LOW for Redis, MODERATE for Sentinel.
* [FIX] Sentinel memory leak due to hiredis fixed. (Salvatore Sanfilippo)
* [FIX] Sentinel memory leak on duplicated instance. (Charsyam)
* [FIX] Redis crash on Lua reaching output buffer limits. (Yossi Gottlieb)
* [FIX] Sentinel flushes config on +slave events. (Bill Anderson)
* [FIX] Fixes to diskless replication. (Oran Agra)
* [FIX] Redis (non clustered & clustered) replication bug involving blocking
operations: see issue #2473. (Salvatore Sanfilippo)
* [FIX] Config: missing activerehashing option support in CONFIG SET added.
(Salvatore Sanfilippo, thx to Bill Anderson)
* [FIX] AOF bug unlikely to happen in practice and mostly harmless: child
process segfaults when parent is not reachable via pipe. (Sun He)
* [FIX] Scripting engine now reports an error when misused with Lua debug
hooks, instead of crashing. (Salvatore Sanfilippo)
* [FIX] INFO loading stats: three fixes.
* [FIX] Fixed memory leaks in rdbSaveToSlavesSockets(). (Alon Diamant)
* [NEW] Redis-cli --latency-dist backported from unstable.
(Salvatore Sanfilippo)
--[ Redis 2.8.19 ] Release date: 16 Dec 2014
# UPGRADE URGENCY: LOW for both Redis and Sentinel. This release mostly
fixes small issues.
02d465c Don't log admin commands in MONITOR. (antirez)
4d8f426 List of commands flagged as admin commands modified. (antirez)
e47e460 Lua cmsgpack lib updated to latest version. (antirez)
5509c14 Add symlink to redis-sentinel during make install (Rhommel Lamas)
7de1ef7 SORT: Don't sort Set elements if not needed. (antirez)
e945a54 Fix zero-ordering SORT when called against lists (Matt Stancliff)
d81c383 Update redis_init_script.tpl (Ben Dowling)
dba57ea FIXED redis-benchmark's idle mode.With idle mode shouldn't create write event (zhanghailei)
888ea17 zipmap.c: update comments above (Sun He)
86ebc13 replaced // comments #2150 (Deepak Verma)
3d73f08 redis-benchmark AUTH command to be discarded after the first send #2150 (azure provisioned user)
76d53a6 sds.c: Correct two spelling mistakes in comments (Sun He)
4848cf9 sds.c/sdscatvprintf: set va_end to finish va_list cpy (Sun He)
d2f584f sds.c: Correct some comments (Sun He)
2ed3f09 Update whatisdoing.sh (Serghei Iakovlev)
77b997d Include stropts only if __sun is defined. (antirez)
d409371 Fix implicit declaration of ioctl on Solaris (Jan-Erik Rediger)
23b96c0 Silence _BSD_SOURCE warnings in glibc 2.20 and forward (Johan Bergström)
a47a042 Mark whatisdoing.sh as deprecated in top-comment. (antirez)
b5737d2 getting pid fixes (Serghei Iakovlev)
a598e08 sparkline.c: AddSample skip Empty label (Sun He)
7d480ab sparkline.c: mov label-ini into the AddSample Function (Sun He)
2f3c860 Only ignore sigpipe in interactive mode (Jan-Erik Rediger)
0c211a1 Simplify lua_cmsgpack macro and fix build on old Linux distros. (antirez)
--[ Redis 2.8.18 ] Release date: 4 Dec 2014
# UPGRADE URGENCY: LOW for both Redis and Sentinel. This release mostly
adds new features to Redis, and contains non critical
fixes.
* [FIX] Linenoise updated to be more VT100 compatible. (Salvatore Sanfilippo)
* [FIX] A number of typos fixed inside comments. (Various authors)
* [FIX] redis-cli no longer quits after long timeouts. (Matt Stancliff)
* [FIX] Test framework improved to detect never terminating scripts, cleanup
instances on crashes. (Salvatore Sanfilippo)
* [FIX] PFCOUNT can be used on slaves now. (Salvatore Sanfilippo)
* [FIX] ZSCAN no longer report very small scores as 0. (Matt Stancliff,
Michael Grunder, Salvatore Sanfilippo)
* [FIX] Don't show the ASCII logo if syslog is enabled. Redis is now
an Enterprise Grade product. (Salvatore Sanfilippo)
* [NEW] EXPERIMENTAL: Diskless replication, for more info check the doc at
http://redis.io/topics/replication. (Salvatore Sanfilippo).
* [NEW] Transparent Huge Pages detection and reporting in logs and
LATENCY DOCTOR output. (Salvatore Sanfilippo)
* [NEW] Many Lua scripting enhancements: Bitops API, cjson upgrade and tests,
cmsgpack upgrade. (Matt Stancliff)
* [NEW] Total and instantaneous Network bandwidth tracking in INFO.
* [NEW] DEBUG POPULATE two args form implemented (old form still works).
The second argument is the key prefix. Default is "key:" (Salvatore
Sanfilippo)
* [NEW] Check that tcp-backlog is matched by /proc/sys/net/core/somaxconn, and
warn about it if not. (Salvatore Sanfilippo)
--[ Redis 2.8.17 ] Release date: 19 Sep 2014
# UPGRADE URGENCY: HIGH for Redis Sentinel.
LOW for Redis Server (unmodified compared to 2.8.16).
* [FIX] Resolved a memory leak in the hiredis library causing a memory leak
in Redis Sentinel when a monitored instance or another Sentinel is
unavailable. Every reconnection attempt will leak a small amount of
memory, but in the long run the process can reach a considerable size.
--[ Redis 2.8.16 ] Release date: 16 Sep 2014
# UPGRADE URGENCY: HIGH for Redis if you are using 2.8.15 + AOF.
LOW for Sentinel.
* [FIX] The ability to load truncated AOF files introduced with Redis 2.8.15
contains a bug fixed in this release: after loading the file was not
truncated to the last valid command, so the new commands are appended
after a non well formed command. This means that:
1) The first AOF rewrite triggered by the server will automatically
fix the problem.
2) However, if the server is restarted before the rewrite, Redis may
not be able to load the file and you need to manually fix it.
In order to fix a corrupted file you should start the redis-check-aof
utility WITHOUT the --fix option, just to check the offset where the
corruption is found. Around the offset reported by the check utility
you'll find, inside your AOF file, a command which is not complete
according to the Redis protocol. Just remove this incomplete command
leafing the file unaltered before and after the offending command,
and restart the server.
IMPORTANT #1: Redis 2.8.15 is the only stable version of Redis with
this bug so probably no actual real-world problem happened since the
problem is automatically fixed at the first automatic AOF rewrite.
IMPORTANT #2: Before upgrading to Redis 2.8.16, if you are using Redis
2.8.15 with AOF enabled, make sure to trigger a manual AOF rewrite
using the BGREWRITEAOF command.
* [FIX] SAVE is no longer propagated to AOF / slaves.
--[ Redis 2.8.15 ] Release date: 12 Sep 2014
# UPGRADE URGENCY: LOW for Redis, HIGH for Sentinel.
+1 -1
View File
@@ -1 +1 @@
Plese check https://github.com/antirez/redis/issues
Please check https://github.com/antirez/redis/issues
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2006-2014, Salvatore Sanfilippo
Copyright (c) 2006-2015, Salvatore Sanfilippo
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+19
View File
@@ -26,6 +26,25 @@ After building Redis is a good idea to test it, using:
% make test
Fixing build problems with dependencies or cached build options
—--------
Redis has some dependencies which are included into the "deps" directory.
"make" does not rebuild dependencies automatically, even if something in the
source code of dependencies is changes.
When you update the source code with `git pull` or when code inside the
dependencies tree is modified in any other way, make sure to use the following
command in order to really clean everything and rebuild from scratch:
make distclean
This will clean: jemalloc, lua, hiredis, linenoise.
Also if you force certain build options like 32bit target, no C compiler
optimizations (for debugging purposes), and other similar build time options,
those options are cached indefinitely until you issue a "make distclean"
command.
Fixing problems building 32 bit binaries
---------
+1 -1
View File
@@ -58,7 +58,7 @@ ifeq ($(uname_S),SunOS)
LUA_CFLAGS= -D__C99FEATURES__=1
endif
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL $(CFLAGS)
LUA_LDFLAGS+= $(LDFLAGS)
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
# challenging to cross-compile lua (and redis). These defines make it easier
+1
View File
@@ -443,6 +443,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
c->err = REDIS_ERR_OTHER;
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
c->reader->fn->freeObject(reply);
__redisAsyncDisconnect(ac);
return;
}
+1
View File
@@ -300,6 +300,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
break;
}
}
freeaddrinfo(bservinfo);
if (!bound) {
char buf[128];
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
+1 -1
View File
@@ -51,7 +51,7 @@ static redisContext *select_database(redisContext *c) {
assert(reply != NULL);
freeReplyObject(reply);
/* Make sure the DB is emtpy */
/* Make sure the DB is empty */
reply = redisCommand(c,"DBSIZE");
assert(reply != NULL);
if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
+3 -1
View File
@@ -1 +1,3 @@
linenoise_example*
linenoise_example
*.dSYM
history.txt
+15 -8
View File
@@ -1,8 +1,13 @@
# Linenoise
A minimal, zero-config, BSD licensed, readline replacement.
A minimal, zero-config, BSD licensed, readline replacement used in Redis,
MongoDB, and Android.
News: linenoise is now part of [Android](http://android.git.kernel.org/?p=platform/system/core.git;a=tree;f=liblinenoise;h=56450eaed7f783760e5e6a5993ef75cde2e29dea;hb=HEAD Android)!
* Single and multi line editing mode with the usual key bindings implemented.
* History handling.
* Completion.
* About 1,100 lines of BSD license source code.
* Only uses a subset of VT100 escapes (ANSI.SYS compatible).
## Can a line editing library be 20k lines of code?
@@ -10,7 +15,7 @@ Line editing with some support for history is a really important feature for com
So what usually happens is either:
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Readl world example of this problem: Tclsh).
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh).
* Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).
The result is a pollution of binaries without line editing support.
@@ -19,27 +24,29 @@ So I spent more or less two hours doing a reality check resulting in this little
## Terminals, in 2010.
Apparently almost every terminal you can happen to use today has some kind of support for VT100 alike escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it.
Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no
VT220 specific sequences are used anymore.
Since it's so young I guess there are a few bugs, or the lib may not compile or work with some operating system, but it's a matter of a few weeks and eventually we'll get it right, and there will be no excuses for not shipping command line tools without built-in line editing support.
The library is currently less than 400 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.
The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.
## Tested with...
* Linux text only console ($TERM = linux)
* Linux KDE terminal application ($TERM = xterm)
* Linux xterm ($TERM = xterm)
* Linux Buildroot ($TERM = vt100)
* Mac OS X iTerm ($TERM = xterm)
* Mac OS X default Terminal.app ($TERM = xterm)
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
* IBM AIX 6.1
* FreeBSD xterm ($TERM = xterm)
* ANSI.SYS
Please test it everywhere you can and report back!
## Let's push this forward!
Please fork it and add something interesting and send me a pull request. What's especially interesting are fixes, new key bindings, completion.
Patches should be provided in the respect of linenoise sensibility for small
easy to understand code.
Send feedbacks to antirez at gmail
+41 -4
View File
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linenoise.h"
@@ -10,16 +11,52 @@ void completion(const char *buf, linenoiseCompletions *lc) {
}
}
int main(void) {
int main(int argc, char **argv) {
char *line;
char *prgname = argv[0];
/* Parse options, with --multiline we enable multi line editing. */
while(argc > 1) {
argc--;
argv++;
if (!strcmp(*argv,"--multiline")) {
linenoiseSetMultiLine(1);
printf("Multi-line mode enabled.\n");
} else if (!strcmp(*argv,"--keycodes")) {
linenoisePrintKeyCodes();
exit(0);
} else {
fprintf(stderr, "Usage: %s [--multiline] [--keycodes]\n", prgname);
exit(1);
}
}
/* Set the completion callback. This will be called every time the
* user uses the <tab> key. */
linenoiseSetCompletionCallback(completion);
/* Load history from file. The history file is just a plain text file
* where entries are separated by newlines. */
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
/* Now this is the main loop of the typical linenoise-based application.
* The call to linenoise() will block as long as the user types something
* and presses enter.
*
* The typed string is returned as a malloc() allocated string by
* linenoise, so the user needs to free() it. */
while((line = linenoise("hello> ")) != NULL) {
if (line[0] != '\0') {
/* Do something with the string. */
if (line[0] != '\0' && line[0] != '/') {
printf("echo: '%s'\n", line);
linenoiseHistoryAdd(line);
linenoiseHistorySave("history.txt"); /* Save every new entry */
linenoiseHistoryAdd(line); /* Add to the history. */
linenoiseHistorySave("history.txt"); /* Save the history on disk. */
} else if (!strncmp(line,"/historylen",11)) {
/* The "/historylen" command will change the history len. */
int len = atoi(line+11);
linenoiseHistorySetMaxLen(len);
} else if (line[0] == '/') {
printf("Unreconized command: %s\n", line);
}
free(line);
}
+47 -32
View File
@@ -2,7 +2,7 @@
* line editing lib needs to be 20,000 lines of C code.
*
* You can find the latest source code at:
*
*
* http://github.com/antirez/linenoise
*
* Does a number of crazy assumptions that happen to be true in 99.9999% of
@@ -14,18 +14,18 @@
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -37,7 +37,7 @@
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* ------------------------------------------------------------------------
*
* References:
@@ -56,10 +56,6 @@
* flickering effect with some slow terminal, but the lesser sequences
* the more compatible.
*
* CHA (Cursor Horizontal Absolute)
* Sequence: ESC [ n G
* Effect: moves cursor to column n
*
* EL (Erase Line)
* Sequence: ESC [ n K
* Effect: if n is 0 or missing, clear from cursor to end of line
@@ -68,7 +64,19 @@
*
* CUF (CUrsor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward of n chars
* Effect: moves cursor forward n chars
*
* CUB (CUrsor Backward)
* Sequence: ESC [ n D
* Effect: moves cursor backward n chars
*
* The following is used to get the terminal width if getting
* the width with the TIOCGWINSZ ioctl fails
*
* DSR (Device Status Report)
* Sequence: ESC [ 6 n
* Effect: reports the current cusor position as ESC [ n ; m R
* where n is the row and m is the column
*
* When multi line mode is enabled, we also use an additional escape
* sequence. However multi line editing is disabled by default.
@@ -81,17 +89,18 @@
* Sequence: ESC [ n B
* Effect: moves cursor down of n chars.
*
* The following are used to clear the screen: ESC [ H ESC [ 2 J
* This is actually composed of two sequences:
* When linenoiseClearScreen() is called, two additional escape sequences
* are used in order to clear the screen and position the cursor at home
* position.
*
* cursorhome
* CUP (Cursor position)
* Sequence: ESC [ H
* Effect: moves the cursor to upper left corner
*
* ED2 (Clear entire screen)
* ED (Erase display)
* Sequence: ESC [ 2 J
* Effect: clear the whole screen
*
*
*/
#include <termios.h>
@@ -332,7 +341,7 @@ static void freeCompletions(linenoiseCompletions *lc) {
/* This is an helper function for linenoiseEdit() and is called when the
* user types the <tab> key in order to complete the string currently in the
* input.
*
*
* The state of the editing is encapsulated into the pointed linenoiseState
* structure as described in the structure definition. */
static int completeLine(struct linenoiseState *ls) {
@@ -459,7 +468,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
size_t len = l->len;
size_t pos = l->pos;
struct abuf ab;
while((plen+pos) >= l->cols) {
buf++;
len--;
@@ -471,7 +480,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
abInit(&ab);
/* Cursor to left edge */
snprintf(seq,64,"\x1b[0G");
snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq));
/* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt));
@@ -480,7 +489,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
snprintf(seq,64,"\x1b[0K");
abAppend(&ab,seq,strlen(seq));
/* Move cursor to original position. */
snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
abAppend(&ab,seq,strlen(seq));
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
abFree(&ab);
@@ -496,6 +505,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
int rpos2; /* rpos after refresh. */
int col; /* colum position, zero-based. */
int old_rows = l->maxrows;
int fd = l->ofd, j;
struct abuf ab;
@@ -515,15 +525,15 @@ static void refreshMultiLine(struct linenoiseState *l) {
/* Now for every row clear it, go up. */
for (j = 0; j < old_rows-1; j++) {
lndebug("clear+up");
snprintf(seq,64,"\x1b[0G\x1b[0K\x1b[1A");
snprintf(seq,64,"\r\x1b[0K\x1b[1A");
abAppend(&ab,seq,strlen(seq));
}
/* Clean the top line. */
lndebug("clear");
snprintf(seq,64,"\x1b[0G\x1b[0K");
snprintf(seq,64,"\r\x1b[0K");
abAppend(&ab,seq,strlen(seq));
/* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt));
abAppend(&ab,l->buf,l->len);
@@ -536,7 +546,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
{
lndebug("<newline>");
abAppend(&ab,"\n",1);
snprintf(seq,64,"\x1b[0G");
snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq));
rows++;
if (rows > (int)l->maxrows) l->maxrows = rows;
@@ -554,8 +564,12 @@ static void refreshMultiLine(struct linenoiseState *l) {
}
/* Set column. */
lndebug("set col %d", 1+((plen+(int)l->pos) % (int)l->cols));
snprintf(seq,64,"\x1b[%dG", 1+((plen+(int)l->pos) % (int)l->cols));
col = (plen+(int)l->pos) % (int)l->cols;
lndebug("set col %d", 1+col);
if (col)
snprintf(seq,64,"\r\x1b[%dC", col);
else
snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq));
lndebug("\n");
@@ -732,7 +746,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
linenoiseHistoryAdd("");
if (write(l.ofd,prompt,l.plen) == -1) return -1;
while(1) {
char c;
@@ -757,6 +771,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
case ENTER: /* enter */
history_len--;
free(history[history_len]);
if (mlmode) linenoiseEditMoveEnd(&l);
return (int)l.len;
case CTRL_C: /* ctrl-c */
errno = EAGAIN;
@@ -765,8 +780,8 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
case 8: /* ctrl-h */
linenoiseEditBackspace(&l);
break;
case CTRL_D: /* ctrl-d, remove char at right of cursor, or of the
line is empty, act as end-of-file. */
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
line is empty, act as end-of-file. */
if (l.len > 0) {
linenoiseEditDelete(&l);
} else {
@@ -904,7 +919,7 @@ void linenoisePrintKeyCodes(void) {
printf("'%c' %02x (%d) (type quit to exit)\n",
isprint(c) ? c : '?', (int)c, (int)c);
printf("\x1b[0G"); /* Go left edge manually, we are in raw mode. */
printf("\r"); /* Go left edge manually, we are in raw mode. */
fflush(stdout);
}
disableRawMode(STDIN_FILENO);
@@ -1058,7 +1073,7 @@ int linenoiseHistorySetMaxLen(int len) {
int linenoiseHistorySave(const char *filename) {
FILE *fp = fopen(filename,"w");
int j;
if (fp == NULL) return -1;
for (j = 0; j < history_len; j++)
fprintf(fp,"%s\n",history[j]);
@@ -1074,12 +1089,12 @@ int linenoiseHistorySave(const char *filename) {
int linenoiseHistoryLoad(const char *filename) {
FILE *fp = fopen(filename,"r");
char buf[LINENOISE_MAX_LINE];
if (fp == NULL) return -1;
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
char *p;
p = strchr(buf,'\r');
if (!p) p = strchr(buf,'\n');
if (p) *p = '\0';
+3 -3
View File
@@ -9,18 +9,18 @@
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+3 -2
View File
@@ -25,9 +25,10 @@ PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
LUA_A= liblua.a
CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \
lundump.o lvm.o lzio.o strbuf.o
lundump.o lvm.o lzio.o strbuf.o fpconv.o
LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
lstrlib.o loadlib.o linit.o lua_cjson.o lua_struct.o lua_cmsgpack.o
lstrlib.o loadlib.o linit.o lua_cjson.o lua_struct.o lua_cmsgpack.o \
lua_bit.o
LUA_T= lua
LUA_O= lua.o
+205
View File
@@ -0,0 +1,205 @@
/* fpconv - Floating point conversion routines
*
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
* with locale support will break when the decimal separator is a comma.
*
* fpconv_* will around these issues with a translation buffer if required.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "fpconv.h"
/* Lua CJSON assumes the locale is the same for all threads within a
* process and doesn't change after initialisation.
*
* This avoids the need for per thread storage or expensive checks
* for call. */
static char locale_decimal_point = '.';
/* In theory multibyte decimal_points are possible, but
* Lua CJSON only supports UTF-8 and known locales only have
* single byte decimal points ([.,]).
*
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
* not supported on some platforms. Use sprintf() instead - if the
* locale does change, at least Lua CJSON won't crash. */
static void fpconv_update_locale()
{
char buf[8];
snprintf(buf, sizeof(buf), "%g", 0.5);
/* Failing this test might imply the platform has a buggy dtoa
* implementation or wide characters */
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
fprintf(stderr, "Error: wide characters found or printf() bug.");
abort();
}
locale_decimal_point = buf[1];
}
/* Check for a valid number character: [-+0-9a-yA-Y.]
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
*
* Used to find the probable end of a number. It doesn't matter if
* invalid characters are counted - strtod() will find the valid
* number if it exists. The risk is that slightly more memory might
* be allocated before a parse error occurs. */
static inline int valid_number_character(char ch)
{
char lower_ch;
if ('0' <= ch && ch <= '9')
return 1;
if (ch == '-' || ch == '+' || ch == '.')
return 1;
/* Hex digits, exponent (e), base (p), "infinity",.. */
lower_ch = ch | 0x20;
if ('a' <= lower_ch && lower_ch <= 'y')
return 1;
return 0;
}
/* Calculate the size of the buffer required for a strtod locale
* conversion. */
static int strtod_buffer_size(const char *s)
{
const char *p = s;
while (valid_number_character(*p))
p++;
return p - s;
}
/* Similar to strtod(), but must be passed the current locale's decimal point
* character. Guaranteed to be called at the start of any valid number in a string */
double fpconv_strtod(const char *nptr, char **endptr)
{
char localbuf[FPCONV_G_FMT_BUFSIZE];
char *buf, *endbuf, *dp;
int buflen;
double value;
/* System strtod() is fine when decimal point is '.' */
if (locale_decimal_point == '.')
return strtod(nptr, endptr);
buflen = strtod_buffer_size(nptr);
if (!buflen) {
/* No valid characters found, standard strtod() return */
*endptr = (char *)nptr;
return 0;
}
/* Duplicate number into buffer */
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
/* Handle unusually large numbers */
buf = malloc(buflen + 1);
if (!buf) {
fprintf(stderr, "Out of memory");
abort();
}
} else {
/* This is the common case.. */
buf = localbuf;
}
memcpy(buf, nptr, buflen);
buf[buflen] = 0;
/* Update decimal point character if found */
dp = strchr(buf, '.');
if (dp)
*dp = locale_decimal_point;
value = strtod(buf, &endbuf);
*endptr = (char *)&nptr[endbuf - buf];
if (buflen >= FPCONV_G_FMT_BUFSIZE)
free(buf);
return value;
}
/* "fmt" must point to a buffer of at least 6 characters */
static void set_number_format(char *fmt, int precision)
{
int d1, d2, i;
assert(1 <= precision && precision <= 14);
/* Create printf format (%.14g) from precision */
d1 = precision / 10;
d2 = precision % 10;
fmt[0] = '%';
fmt[1] = '.';
i = 2;
if (d1) {
fmt[i++] = '0' + d1;
}
fmt[i++] = '0' + d2;
fmt[i++] = 'g';
fmt[i] = 0;
}
/* Assumes there is always at least 32 characters available in the target buffer */
int fpconv_g_fmt(char *str, double num, int precision)
{
char buf[FPCONV_G_FMT_BUFSIZE];
char fmt[6];
int len;
char *b;
set_number_format(fmt, precision);
/* Pass through when decimal point character is dot. */
if (locale_decimal_point == '.')
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
/* snprintf() to a buffer then translate for other decimal point characters */
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
/* Copy into target location. Translate decimal point if required */
b = buf;
do {
*str++ = (*b == locale_decimal_point ? '.' : *b);
} while(*b++);
return len;
}
void fpconv_init()
{
fpconv_update_locale();
}
/* vi:ai et sw=4 ts=4:
*/
+22
View File
@@ -0,0 +1,22 @@
/* Lua CJSON floating point conversion routines */
/* Buffer required to store the largest string representation of a double.
*
* Longest double printed with %.14g is 21 characters long:
* -1.7976931348623e+308 */
# define FPCONV_G_FMT_BUFSIZE 32
#ifdef USE_INTERNAL_FPCONV
static inline void fpconv_init()
{
/* Do nothing - not required */
}
#else
extern void fpconv_init();
#endif
extern int fpconv_g_fmt(char*, double, int);
extern double fpconv_strtod(const char*, char**);
/* vi:ai et sw=4 ts=4:
*/
+1 -1
View File
@@ -495,7 +495,7 @@ static void f_parser (lua_State *L, void *ud) {
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
tf = (luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
+189
View File
@@ -0,0 +1,189 @@
/*
** Lua BitOp -- a bit operations library for Lua 5.1/5.2.
** http://bitop.luajit.org/
**
** Copyright (C) 2008-2012 Mike Pall. All rights reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
*/
#define LUA_BITOP_VERSION "1.0.2"
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#ifdef _MSC_VER
/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
typedef int32_t SBits;
typedef uint32_t UBits;
typedef union {
lua_Number n;
#ifdef LUA_NUMBER_DOUBLE
uint64_t b;
#else
UBits b;
#endif
} BitNum;
/* Convert argument to bit type. */
static UBits barg(lua_State *L, int idx)
{
BitNum bn;
UBits b;
#if LUA_VERSION_NUM < 502
bn.n = lua_tonumber(L, idx);
#else
bn.n = luaL_checknumber(L, idx);
#endif
#if defined(LUA_NUMBER_DOUBLE)
bn.n += 6755399441055744.0; /* 2^52+2^51 */
#ifdef SWAPPED_DOUBLE
b = (UBits)(bn.b >> 32);
#else
b = (UBits)bn.b;
#endif
#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \
defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \
defined(LUA_NUMBER_LLONG)
if (sizeof(UBits) == sizeof(lua_Number))
b = bn.b;
else
b = (UBits)(SBits)bn.n;
#elif defined(LUA_NUMBER_FLOAT)
#error "A 'float' lua_Number type is incompatible with this library"
#else
#error "Unknown number type, check LUA_NUMBER_* in luaconf.h"
#endif
#if LUA_VERSION_NUM < 502
if (b == 0 && !lua_isnumber(L, idx)) {
luaL_typerror(L, idx, "number");
}
#endif
return b;
}
/* Return bit type. */
#define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;
static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }
static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }
#define BIT_OP(func, opr) \
static int func(lua_State *L) { int i; UBits b = barg(L, 1); \
for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }
BIT_OP(bit_band, &=)
BIT_OP(bit_bor, |=)
BIT_OP(bit_bxor, ^=)
#define bshl(b, n) (b << n)
#define bshr(b, n) (b >> n)
#define bsar(b, n) ((SBits)b >> n)
#define brol(b, n) ((b << n) | (b >> (32-n)))
#define bror(b, n) ((b << (32-n)) | (b >> n))
#define BIT_SH(func, fn) \
static int func(lua_State *L) { \
UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }
BIT_SH(bit_lshift, bshl)
BIT_SH(bit_rshift, bshr)
BIT_SH(bit_arshift, bsar)
BIT_SH(bit_rol, brol)
BIT_SH(bit_ror, bror)
static int bit_bswap(lua_State *L)
{
UBits b = barg(L, 1);
b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);
BRET(b)
}
static int bit_tohex(lua_State *L)
{
UBits b = barg(L, 1);
SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);
const char *hexdigits = "0123456789abcdef";
char buf[8];
int i;
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
if (n > 8) n = 8;
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
lua_pushlstring(L, buf, (size_t)n);
return 1;
}
static const struct luaL_Reg bit_funcs[] = {
{ "tobit", bit_tobit },
{ "bnot", bit_bnot },
{ "band", bit_band },
{ "bor", bit_bor },
{ "bxor", bit_bxor },
{ "lshift", bit_lshift },
{ "rshift", bit_rshift },
{ "arshift", bit_arshift },
{ "rol", bit_rol },
{ "ror", bit_ror },
{ "bswap", bit_bswap },
{ "tohex", bit_tohex },
{ NULL, NULL }
};
/* Signed right-shifts are implementation-defined per C89/C99.
** But the de facto standard are arithmetic right-shifts on two's
** complement CPUs. This behaviour is required here, so test for it.
*/
#define BAD_SAR (bsar(-8, 2) != (SBits)-2)
LUALIB_API int luaopen_bit(lua_State *L)
{
UBits b;
lua_pushnumber(L, (lua_Number)1437217655L);
b = barg(L, -1);
if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */
const char *msg = "compiled with incompatible luaconf.h";
#ifdef LUA_NUMBER_DOUBLE
#ifdef _WIN32
if (b == (UBits)1610612736L)
msg = "use D3DCREATE_FPU_PRESERVE with DirectX";
#endif
if (b == (UBits)1127743488L)
msg = "not compiled with SWAPPED_DOUBLE";
#endif
if (BAD_SAR)
msg = "arithmetic right-shift broken";
luaL_error(L, "bit library self-test failed (%s)", msg);
}
#if LUA_VERSION_NUM < 502
luaL_register(L, "bit", bit_funcs);
#else
luaL_newlib(L, bit_funcs);
#endif
return 1;
}
+434 -306
View File
File diff suppressed because it is too large Load Diff
+368 -127
View File
@@ -7,14 +7,38 @@
#include "lua.h"
#include "lauxlib.h"
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.3.0"
#define LUACMSGPACK_NAME "cmsgpack"
#define LUACMSGPACK_SAFE_NAME "cmsgpack_safe"
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.4.0"
#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2012, Salvatore Sanfilippo"
#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua"
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
/* Allows a preprocessor directive to override MAX_NESTING */
#ifndef LUACMSGPACK_MAX_NESTING
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
#endif
/* ==============================================================================
* MessagePack implementation and bindings for Lua 5.1.
/* Check if float or double can be an integer without loss of precision */
#define IS_INT_TYPE_EQUIVALENT(x, T) (!isinf(x) && (T)(x) == (x))
#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t)
#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int)
/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */
#if UINTPTR_MAX == UINT_MAX
#define BITS_32 1
#else
#define BITS_32 0
#endif
#if BITS_32
#define lua_pushunsigned(L, n) lua_pushnumber(L, n)
#else
#define lua_pushunsigned(L, n) lua_pushinteger(L, n)
#endif
/* =============================================================================
* MessagePack implementation and bindings for Lua 5.1/5.2.
* Copyright(C) 2012 Salvatore Sanfilippo <antirez@gmail.com>
*
* http://github.com/antirez/lua-cmsgpack
@@ -29,23 +53,27 @@
* 20-Feb-2012 (ver 0.2.0): Tables encoding improved.
* 20-Feb-2012 (ver 0.2.1): Minor bug fixing.
* 20-Feb-2012 (ver 0.3.0): Module renamed lua-cmsgpack (was lua-msgpack).
* ============================================================================ */
* 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.
* 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency.
* ========================================================================== */
/* --------------------------- Endian conversion --------------------------------
* We use it only for floats and doubles, all the other conversions are performed
/* -------------------------- Endian conversion --------------------------------
* We use it only for floats and doubles, all the other conversions performed
* in an endian independent fashion. So the only thing we need is a function
* that swaps a binary string if the arch is little endian (and left it untouched
* that swaps a binary string if arch is little endian (and left it untouched
* otherwise). */
/* Reverse memory bytes if arch is little endian. Given the conceptual
* simplicity of the Lua build system we prefer to check for endianess at runtime.
* simplicity of the Lua build system we prefer check for endianess at runtime.
* The performance difference should be acceptable. */
static void memrevifle(void *ptr, size_t len) {
unsigned char *p = ptr, *e = p+len-1, aux;
void memrevifle(void *ptr, size_t len) {
unsigned char *p = (unsigned char *)ptr,
*e = (unsigned char *)p+len-1,
aux;
int test = 1;
unsigned char *testp = (unsigned char*) &test;
if (testp[0] == 0) return; /* Big endian, nothign to do. */
if (testp[0] == 0) return; /* Big endian, nothing to do. */
len /= 2;
while(len--) {
aux = *p;
@@ -56,20 +84,34 @@ static void memrevifle(void *ptr, size_t len) {
}
}
/* ----------------------------- String buffer ----------------------------------
* This is a simple implementation of string buffers. The only opereation
/* ---------------------------- String buffer ----------------------------------
* This is a simple implementation of string buffers. The only operation
* supported is creating empty buffers and appending bytes to it.
* The string buffer uses 2x preallocation on every realloc for O(N) append
* behavior. */
typedef struct mp_buf {
lua_State *L;
unsigned char *b;
size_t len, free;
} mp_buf;
static mp_buf *mp_buf_new(void) {
mp_buf *buf = malloc(sizeof(*buf));
void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;
void *ud;
local_realloc = lua_getallocf(L, &ud);
return local_realloc(ud, target, osize, nsize);
}
mp_buf *mp_buf_new(lua_State *L) {
mp_buf *buf = NULL;
/* Old size = 0; new size = sizeof(*buf) */
buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf));
buf->L = L;
buf->b = NULL;
buf->len = buf->free = 0;
return buf;
@@ -79,7 +121,7 @@ void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
if (buf->free < len) {
size_t newlen = buf->len+len;
buf->b = realloc(buf->b,newlen*2);
buf->b = (unsigned char*)mp_realloc(buf->L, buf->b, buf->len, newlen*2);
buf->free = newlen;
}
memcpy(buf->b+buf->len,s,len);
@@ -88,11 +130,11 @@ void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
}
void mp_buf_free(mp_buf *buf) {
free(buf->b);
free(buf);
mp_realloc(buf->L, buf->b, buf->len, 0); /* realloc to 0 = free */
mp_realloc(buf->L, buf, sizeof(*buf), 0);
}
/* ------------------------------ String cursor ----------------------------------
/* ---------------------------- String cursor ----------------------------------
* This simple data structure is used for parsing. Basically you create a cursor
* using a string pointer and a length, then it is possible to access the
* current string position with cursor->p, check the remaining length
@@ -102,7 +144,7 @@ void mp_buf_free(mp_buf *buf) {
* be used to report errors. */
#define MP_CUR_ERROR_NONE 0
#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete the opereation. */
#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete operation. */
#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */
typedef struct mp_cur {
@@ -111,22 +153,15 @@ typedef struct mp_cur {
int err;
} mp_cur;
static mp_cur *mp_cur_new(const unsigned char *s, size_t len) {
mp_cur *cursor = malloc(sizeof(*cursor));
void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
cursor->p = s;
cursor->left = len;
cursor->err = MP_CUR_ERROR_NONE;
return cursor;
}
static void mp_cur_free(mp_cur *cursor) {
free(cursor);
}
#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)
/* When there is not enough room we set an error in the cursor and return, this
/* When there is not enough room we set an error in the cursor and return. This
* is very common across the code so we have a macro to make the code look
* a bit simpler. */
#define mp_cur_need(_c,_len) do { \
@@ -136,15 +171,19 @@ static void mp_cur_free(mp_cur *cursor) {
} \
} while(0)
/* --------------------------- Low level MP encoding -------------------------- */
/* ------------------------- Low level MP encoding -------------------------- */
static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
unsigned char hdr[5];
int hdrlen;
if (len < 32) {
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
hdrlen = 1;
} else if (len <= 0xff) {
hdr[0] = 0xd9;
hdr[1] = len;
hdrlen = 2;
} else if (len <= 0xffff) {
hdr[0] = 0xda;
hdr[1] = (len&0xff00)>>8;
@@ -163,7 +202,7 @@ static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
}
/* we assume IEEE 754 internal format for single and double precision floats. */
static void mp_encode_double(mp_buf *buf, double d) {
void mp_encode_double(mp_buf *buf, double d) {
unsigned char b[9];
float f = d;
@@ -181,7 +220,7 @@ static void mp_encode_double(mp_buf *buf, double d) {
}
}
static void mp_encode_int(mp_buf *buf, int64_t n) {
void mp_encode_int(mp_buf *buf, int64_t n) {
unsigned char b[9];
int enclen;
@@ -219,7 +258,7 @@ static void mp_encode_int(mp_buf *buf, int64_t n) {
}
} else {
if (n >= -32) {
b[0] = ((char)n); /* negative fixnum */
b[0] = ((signed char)n); /* negative fixnum */
enclen = 1;
} else if (n >= -128) {
b[0] = 0xd0; /* int 8 */
@@ -253,7 +292,7 @@ static void mp_encode_int(mp_buf *buf, int64_t n) {
mp_buf_append(buf,b,enclen);
}
static void mp_encode_array(mp_buf *buf, int64_t n) {
void mp_encode_array(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
@@ -276,7 +315,7 @@ static void mp_encode_array(mp_buf *buf, int64_t n) {
mp_buf_append(buf,b,enclen);
}
static void mp_encode_map(mp_buf *buf, int64_t n) {
void mp_encode_map(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
@@ -299,9 +338,9 @@ static void mp_encode_map(mp_buf *buf, int64_t n) {
mp_buf_append(buf,b,enclen);
}
/* ----------------------------- Lua types encoding --------------------------- */
/* --------------------------- Lua types encoding --------------------------- */
static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
size_t len;
const char *s;
@@ -309,26 +348,43 @@ static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
mp_encode_bytes(buf,(const unsigned char*)s,len);
}
static void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;
mp_buf_append(buf,&b,1);
}
static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
/* Lua 5.3 has a built in 64-bit integer type */
void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
#if (LUA_VERSION_NUM < 503) && BITS_32
lua_Number i = lua_tonumber(L,-1);
#else
lua_Integer i = lua_tointeger(L,-1);
#endif
mp_encode_int(buf, (int64_t)i);
}
/* Lua 5.2 and lower only has 64-bit doubles, so we need to
* detect if the double may be representable as an int
* for Lua < 5.3 */
void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
lua_Number n = lua_tonumber(L,-1);
if (floor(n) != n) {
mp_encode_double(buf,(double)n);
if (IS_INT64_EQUIVALENT(n)) {
mp_encode_lua_integer(L, buf);
} else {
mp_encode_int(buf,(int64_t)n);
mp_encode_double(buf,(double)n);
}
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
/* Convert a lua table into a message pack list. */
static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
#if LUA_VERSION_NUM < 502
size_t len = lua_objlen(L,-1), j;
#else
size_t len = lua_rawlen(L,-1), j;
#endif
mp_encode_array(buf,len);
for (j = 1; j <= len; j++) {
@@ -339,13 +395,13 @@ static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
}
/* Convert a lua table into a message pack key-value map. */
static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
size_t len = 0;
/* First step: count keys into table. No other way to do it with the
* Lua API, we need to iterate a first time. Note that an alternative
* would be to do a single run, and then hack the buffer to insert the
* map opcodes for message pack. Too hachish for this lib. */
* map opcodes for message pack. Too hackish for this lib. */
lua_pushnil(L);
while(lua_next(L,-2)) {
lua_pop(L,1); /* remove value, keep key for next iteration. */
@@ -366,80 +422,131 @@ static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
/* Returns true if the Lua table on top of the stack is exclusively composed
* of keys from numerical keys from 1 up to N, with N being the total number
* of elements, without any hole in the middle. */
static int table_is_an_array(lua_State *L) {
long count = 0, max = 0, idx = 0;
int table_is_an_array(lua_State *L) {
int count = 0, max = 0;
#if LUA_VERSION_NUM < 503
lua_Number n;
#else
lua_Integer n;
#endif
/* Stack top on function entry */
int stacktop;
stacktop = lua_gettop(L);
lua_pushnil(L);
while(lua_next(L,-2)) {
/* Stack: ... key value */
lua_pop(L,1); /* Stack: ... key */
if (lua_type(L,-1) != LUA_TNUMBER) goto not_array;
n = lua_tonumber(L,-1);
idx = n;
if (idx != n || idx < 1) goto not_array;
/* The <= 0 check is valid here because we're comparing indexes. */
#if LUA_VERSION_NUM < 503
if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 ||
!IS_INT_EQUIVALENT(n))
#else
if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0)
#endif
{
lua_settop(L, stacktop);
return 0;
}
max = (n > max ? n : max);
count++;
max = idx;
}
/* We have the total number of elements in "count". Also we have
* the max index encountered in "idx". We can't reach this code
* the max index encountered in "max". We can't reach this code
* if there are indexes <= 0. If you also note that there can not be
* repeated keys into a table, you have that if idx==count you are sure
* repeated keys into a table, you have that if max==count you are sure
* that there are all the keys form 1 to count (both included). */
return idx == count;
not_array:
lua_pop(L,1);
return 0;
lua_settop(L, stacktop);
return max == count;
}
/* If the length operator returns non-zero, that is, there is at least
* an object at key '1', we serialize to message pack list. Otherwise
* we use a map. */
static void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
if (table_is_an_array(L))
mp_encode_lua_table_as_array(L,buf,level);
else
mp_encode_lua_table_as_map(L,buf,level);
}
static void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
unsigned char b[1];
(void)L;
b[0] = 0xc0;
mp_buf_append(buf,b,1);
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
int t = lua_type(L,-1);
/* Limit the encoding of nested tables to a specfiied maximum depth, so that
/* Limit the encoding of nested tables to a specified maximum depth, so that
* we survive when called against circular references in tables. */
if (t == LUA_TTABLE && level == LUACMSGPACK_MAX_NESTING) t = LUA_TNIL;
switch(t) {
case LUA_TSTRING: mp_encode_lua_string(L,buf); break;
case LUA_TBOOLEAN: mp_encode_lua_bool(L,buf); break;
case LUA_TNUMBER: mp_encode_lua_number(L,buf); break;
case LUA_TNUMBER:
#if LUA_VERSION_NUM < 503
mp_encode_lua_number(L,buf); break;
#else
if (lua_isinteger(L, -1)) {
mp_encode_lua_integer(L, buf);
} else {
mp_encode_lua_number(L, buf);
}
break;
#endif
case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;
default: mp_encode_lua_null(L,buf); break;
}
lua_pop(L,1);
}
static int mp_pack(lua_State *L) {
mp_buf *buf = mp_buf_new();
/*
* Packs all arguments as a stream for multiple upacking later.
* Returns error if no arguments provided.
*/
int mp_pack(lua_State *L) {
int nargs = lua_gettop(L);
int i;
mp_buf *buf;
mp_encode_lua_type(L,buf,0);
lua_pushlstring(L,(char*)buf->b,buf->len);
if (nargs == 0)
return luaL_argerror(L, 0, "MessagePack pack needs input.");
buf = mp_buf_new(L);
for(i = 1; i <= nargs; i++) {
/* Copy argument i to top of stack for _encode processing;
* the encode function pops it from the stack when complete. */
lua_pushvalue(L, i);
mp_encode_lua_type(L,buf,0);
lua_pushlstring(L,(char*)buf->b,buf->len);
/* Reuse the buffer for the next operation by
* setting its free count to the total buffer size
* and the current position to zero. */
buf->free += buf->len;
buf->len = 0;
}
mp_buf_free(buf);
/* Concatenate all nargs buffers together */
lua_concat(L, nargs);
return 1;
}
/* --------------------------------- Decoding --------------------------------- */
/* ------------------------------- Decoding --------------------------------- */
void mp_decode_to_lua_type(lua_State *L, mp_cur *c);
void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {
assert(len <= UINT_MAX);
int index = 1;
lua_newtable(L);
@@ -452,6 +559,7 @@ void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {
}
void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {
assert(len <= UINT_MAX);
lua_newtable(L);
while(len--) {
mp_decode_to_lua_type(L,c); /* key */
@@ -466,34 +574,44 @@ void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {
* a Lua type, that is left as the only result on the stack. */
void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
mp_cur_need(c,1);
/* If we return more than 18 elements, we must resize the stack to
* fit all our return values. But, there is no way to
* determine how many objects a msgpack will unpack to up front, so
* we request a +1 larger stack on each iteration (noop if stack is
* big enough, and when stack does require resize it doubles in size) */
luaL_checkstack(L, 1,
"too many return values at once; "
"use unpack_one or unpack_limit instead.");
switch(c->p[0]) {
case 0xcc: /* uint 8 */
mp_cur_need(c,2);
lua_pushnumber(L,c->p[1]);
lua_pushunsigned(L,c->p[1]);
mp_cur_consume(c,2);
break;
case 0xd0: /* int 8 */
mp_cur_need(c,2);
lua_pushnumber(L,(char)c->p[1]);
lua_pushinteger(L,(signed char)c->p[1]);
mp_cur_consume(c,2);
break;
case 0xcd: /* uint 16 */
mp_cur_need(c,3);
lua_pushnumber(L,
lua_pushunsigned(L,
(c->p[1] << 8) |
c->p[2]);
mp_cur_consume(c,3);
break;
case 0xd1: /* int 16 */
mp_cur_need(c,3);
lua_pushnumber(L,(int16_t)
lua_pushinteger(L,(int16_t)
(c->p[1] << 8) |
c->p[2]);
mp_cur_consume(c,3);
break;
case 0xce: /* uint 32 */
mp_cur_need(c,5);
lua_pushnumber(L,
lua_pushunsigned(L,
((uint32_t)c->p[1] << 24) |
((uint32_t)c->p[2] << 16) |
((uint32_t)c->p[3] << 8) |
@@ -502,7 +620,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
break;
case 0xd2: /* int 32 */
mp_cur_need(c,5);
lua_pushnumber(L,
lua_pushinteger(L,
((int32_t)c->p[1] << 24) |
((int32_t)c->p[2] << 16) |
((int32_t)c->p[3] << 8) |
@@ -511,7 +629,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
break;
case 0xcf: /* uint 64 */
mp_cur_need(c,9);
lua_pushnumber(L,
lua_pushunsigned(L,
((uint64_t)c->p[1] << 56) |
((uint64_t)c->p[2] << 48) |
((uint64_t)c->p[3] << 40) |
@@ -524,7 +642,11 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
break;
case 0xd3: /* int 64 */
mp_cur_need(c,9);
#if LUA_VERSION_NUM < 503
lua_pushnumber(L,
#else
lua_pushinteger(L,
#endif
((int64_t)c->p[1] << 56) |
((int64_t)c->p[2] << 48) |
((int64_t)c->p[3] << 40) |
@@ -569,6 +691,15 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
mp_cur_consume(c,9);
}
break;
case 0xd9: /* raw 8 */
mp_cur_need(c,2);
{
size_t l = c->p[1];
mp_cur_need(c,2+l);
lua_pushlstring(L,(char*)c->p+2,l);
mp_cur_consume(c,2+l);
}
break;
case 0xda: /* raw 16 */
mp_cur_need(c,3);
{
@@ -581,13 +712,14 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
case 0xdb: /* raw 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
mp_cur_need(c,5+l);
lua_pushlstring(L,(char*)c->p+5,l);
mp_cur_consume(c,5+l);
size_t l = ((size_t)c->p[1] << 24) |
((size_t)c->p[2] << 16) |
((size_t)c->p[3] << 8) |
(size_t)c->p[4];
mp_cur_consume(c,5);
mp_cur_need(c,l);
lua_pushlstring(L,(char*)c->p,l);
mp_cur_consume(c,l);
}
break;
case 0xdc: /* array 16 */
@@ -601,10 +733,10 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
case 0xdd: /* array 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
size_t l = ((size_t)c->p[1] << 24) |
((size_t)c->p[2] << 16) |
((size_t)c->p[3] << 8) |
(size_t)c->p[4];
mp_cur_consume(c,5);
mp_decode_to_lua_array(L,c,l);
}
@@ -620,20 +752,20 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
case 0xdf: /* map 32 */
mp_cur_need(c,5);
{
size_t l = (c->p[1] << 24) |
(c->p[2] << 16) |
(c->p[3] << 8) |
c->p[4];
size_t l = ((size_t)c->p[1] << 24) |
((size_t)c->p[2] << 16) |
((size_t)c->p[3] << 8) |
(size_t)c->p[4];
mp_cur_consume(c,5);
mp_decode_to_lua_hash(L,c,l);
}
break;
default: /* types that can't be idenitified by first byte value. */
if ((c->p[0] & 0x80) == 0) { /* positive fixnum */
lua_pushnumber(L,c->p[0]);
lua_pushunsigned(L,c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */
lua_pushnumber(L,(signed char)c->p[0]);
lua_pushinteger(L,(signed char)c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xa0) { /* fix raw */
size_t l = c->p[0] & 0x1f;
@@ -654,54 +786,163 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
}
}
static int mp_unpack(lua_State *L) {
int mp_unpack_full(lua_State *L, int limit, int offset) {
size_t len;
const unsigned char *s;
mp_cur *c;
const char *s;
mp_cur c;
int cnt; /* Number of objects unpacked */
int decode_all = (!limit && !offset);
if (!lua_isstring(L,-1)) {
lua_pushstring(L,"MessagePack decoding needs a string as input.");
lua_error(L);
s = luaL_checklstring(L,1,&len); /* if no match, exits */
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
return luaL_error(L,
"Invalid request to unpack with offset of %d and limit of %d.",
offset, len);
else if (offset > len)
return luaL_error(L,
"Start offset %d greater than input length %d.", offset, len);
if (decode_all) limit = INT_MAX;
mp_cur_init(&c,(const unsigned char *)s+offset,len-offset);
/* We loop over the decode because this could be a stream
* of multiple top-level values serialized together */
for(cnt = 0; c.left > 0 && cnt < limit; cnt++) {
mp_decode_to_lua_type(L,&c);
if (c.err == MP_CUR_ERROR_EOF) {
return luaL_error(L,"Missing bytes in input.");
} else if (c.err == MP_CUR_ERROR_BADFMT) {
return luaL_error(L,"Bad data format in input.");
}
}
s = (const unsigned char*) lua_tolstring(L,-1,&len);
c = mp_cur_new(s,len);
mp_decode_to_lua_type(L,c);
if (c->err == MP_CUR_ERROR_EOF) {
mp_cur_free(c);
lua_pushstring(L,"Missing bytes in input.");
lua_error(L);
} else if (c->err == MP_CUR_ERROR_BADFMT) {
mp_cur_free(c);
lua_pushstring(L,"Bad data format in input.");
lua_error(L);
} else if (c->left != 0) {
mp_cur_free(c);
lua_pushstring(L,"Extra bytes in input.");
lua_error(L);
if (!decode_all) {
/* c->left is the remaining size of the input buffer.
* subtract the entire buffer size from the unprocessed size
* to get our next start offset */
int offset = len - c.left;
/* Return offset -1 when we have have processed the entire buffer. */
lua_pushinteger(L, c.left == 0 ? -1 : offset);
/* Results are returned with the arg elements still
* in place. Lua takes care of only returning
* elements above the args for us.
* In this case, we have one arg on the stack
* for this function, so we insert our first return
* value at position 2. */
lua_insert(L, 2);
cnt += 1; /* increase return count by one to make room for offset */
}
mp_cur_free(c);
return 1;
return cnt;
}
/* ---------------------------------------------------------------------------- */
int mp_unpack(lua_State *L) {
return mp_unpack_full(L, 0, 0);
}
static const struct luaL_reg thislib[] = {
int mp_unpack_one(lua_State *L) {
int offset = luaL_optinteger(L, 2, 0);
/* Variable pop because offset may not exist */
lua_pop(L, lua_gettop(L)-1);
return mp_unpack_full(L, 1, offset);
}
int mp_unpack_limit(lua_State *L) {
int limit = luaL_checkinteger(L, 2);
int offset = luaL_optinteger(L, 3, 0);
/* Variable pop because offset may not exist */
lua_pop(L, lua_gettop(L)-1);
return mp_unpack_full(L, limit, offset);
}
int mp_safe(lua_State *L) {
int argc, err, total_results;
argc = lua_gettop(L);
/* This adds our function to the bottom of the stack
* (the "call this function" position) */
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
err = lua_pcall(L, argc, LUA_MULTRET, 0);
total_results = lua_gettop(L);
if (!err) {
return total_results;
} else {
lua_pushnil(L);
lua_insert(L,-2);
return 2;
}
}
/* -------------------------------------------------------------------------- */
const struct luaL_Reg cmds[] = {
{"pack", mp_pack},
{"unpack", mp_unpack},
{NULL, NULL}
{"unpack_one", mp_unpack_one},
{"unpack_limit", mp_unpack_limit},
{0}
};
LUALIB_API int luaopen_cmsgpack (lua_State *L) {
luaL_register(L, "cmsgpack", thislib);
int luaopen_create(lua_State *L) {
int i;
/* Manually construct our module table instead of
* relying on _register or _newlib */
lua_newtable(L);
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
lua_pushcfunction(L, cmds[i].func);
lua_setfield(L, -2, cmds[i].name);
}
/* Add metadata */
lua_pushliteral(L, LUACMSGPACK_NAME);
lua_setfield(L, -2, "_NAME");
lua_pushliteral(L, LUACMSGPACK_VERSION);
lua_setfield(L, -2, "_VERSION");
lua_pushliteral(L, LUACMSGPACK_COPYRIGHT);
lua_setfield(L, -2, "_COPYRIGHT");
lua_pushliteral(L, LUACMSGPACK_DESCRIPTION);
lua_setfield(L, -2, "_DESCRIPTION");
lua_setfield(L, -2, "_DESCRIPTION");
return 1;
}
LUALIB_API int luaopen_cmsgpack(lua_State *L) {
luaopen_create(L);
#if LUA_VERSION_NUM < 502
/* Register name globally for 5.1 */
lua_pushvalue(L, -1);
lua_setglobal(L, LUACMSGPACK_NAME);
#endif
return 1;
}
LUALIB_API int luaopen_cmsgpack_safe(lua_State *L) {
int i;
luaopen_cmsgpack(L);
/* Wrap all functions in the safe handler */
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
lua_getfield(L, -1, cmds[i].name);
lua_pushcclosure(L, mp_safe, 1);
lua_setfield(L, -2, cmds[i].name);
}
#if LUA_VERSION_NUM < 502
/* Register name globally for 5.1 */
lua_pushvalue(L, -1);
lua_setglobal(L, LUACMSGPACK_SAFE_NAME);
#endif
return 1;
}
+3 -3
View File
@@ -1,6 +1,6 @@
/* strbuf - string buffer routines
/* strbuf - String buffer routines
*
* Copyright (c) 2010-2011 Mark Pulford <mark@kyne.com.au>
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -29,7 +29,7 @@
#include "strbuf.h"
void die(const char *fmt, ...)
static void die(const char *fmt, ...)
{
va_list arg;
+14 -2
View File
@@ -1,6 +1,6 @@
/* strbuf - String buffer routines
*
* Copyright (c) 2010-2011 Mark Pulford <mark@kyne.com.au>
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -62,7 +62,9 @@ extern void strbuf_resize(strbuf_t *s, int len);
static int strbuf_empty_length(strbuf_t *s);
static int strbuf_length(strbuf_t *s);
static char *strbuf_string(strbuf_t *s, int *len);
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
static char *strbuf_empty_ptr(strbuf_t *s);
static void strbuf_extend_length(strbuf_t *s, int len);
/* Update */
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
@@ -96,6 +98,16 @@ static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
strbuf_resize(s, s->length + len);
}
static inline char *strbuf_empty_ptr(strbuf_t *s)
{
return s->buf + s->length;
}
static inline void strbuf_extend_length(strbuf_t *s, int len)
{
s->length += len;
}
static inline int strbuf_length(strbuf_t *s)
{
return s->length;
+79 -32
View File
@@ -1,4 +1,9 @@
# Redis configuration file example
# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
@@ -15,7 +20,7 @@
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis server but also need
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
@@ -131,7 +136,7 @@ databases 16
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving at all commenting all the "save" lines.
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
@@ -180,9 +185,9 @@ dbfilename dump.rdb
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
#
# The Append Only File will also be created inside this directory.
#
#
# Note that you must specify a directory here, not a file name.
dir ./
@@ -240,6 +245,49 @@ slave-serve-stale-data yes
# administrative / dangerous commands.
slave-read-only yes
# Replication SYNC strategy: disk or socket.
#
# -------------------------------------------------------
# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY
# -------------------------------------------------------
#
# New slaves and reconnecting slaves that are not able to continue the replication
# process just receiving differences, need to do what is called a "full
# synchronization". An RDB file is transmitted from the master to the slaves.
# The transmission can happen in two different ways:
#
# 1) Disk-backed: The Redis master creates a new process that writes the RDB
# file on disk. Later the file is transferred by the parent
# process to the slaves incrementally.
# 2) Diskless: The Redis master creates a new process that directly writes the
# RDB file to slave sockets, without touching the disk at all.
#
# With disk-backed replication, while the RDB file is generated, more slaves
# can be queued and served with the RDB file as soon as the current child producing
# the RDB file finishes its work. With diskless replication instead once
# the transfer starts, new slaves arriving will be queued and a new transfer
# will start when the current one terminates.
#
# When diskless replication is used, the master waits a configurable amount of
# time (in seconds) before starting the transfer in the hope that multiple slaves
# will arrive and the transfer can be parallelized.
#
# With slow disks and fast (large bandwidth) networks, diskless replication
# works better.
repl-diskless-sync no
# When diskless replication is enabled, it is possible to configure the delay
# the server waits in order to spawn the child that trnasfers the RDB via socket
# to the slaves.
#
# This is important since once the transfer starts, it is not possible to serve
# new slaves arriving, that will be queued for the next RDB transfer, so the server
# waits a delay in order to let more slaves arrive.
#
# The delay is specified in seconds, and by default is 5 seconds. To disable
# it entirely just set it to 0 seconds and the transfer will start ASAP.
repl-diskless-sync-delay 5
# Slaves send PINGs to server in a predefined interval. It's possible to change
# this interval with the repl_ping_slave_period option. The default value is 10
# seconds.
@@ -279,7 +327,7 @@ repl-disable-tcp-nodelay no
# resync is enough, just passing the portion of data the slave missed while
# disconnected.
#
# The biggest the replication backlog, the longer the time the slave can be
# The bigger the replication backlog, the longer the time the slave can be
# disconnected and later be able to perform a partial resynchronization.
#
# The backlog is only allocated once there is at least a slave connected.
@@ -318,7 +366,7 @@ slave-priority 100
# The lag in seconds, that must be <= the specified value, is calculated from
# the last ping received from the slave, that is usually sent every second.
#
# This option does not GUARANTEES that N replicas will accept the write, but
# This option does not GUARANTEE that N replicas will accept the write, but
# will limit the window of exposure for lost writes in case not enough slaves
# are available, to the specified number of seconds.
#
@@ -340,7 +388,7 @@ slave-priority 100
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
@@ -406,18 +454,18 @@ slave-priority 100
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are not suitable keys for eviction.
#
# At the date of writing this commands are: set setnx setex append
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
@@ -462,13 +510,13 @@ appendonly no
appendfilename "appendonly.aof"
# The fsync() call tells the Operating System to actually write data on disk
# instead to wait for more data in the output buffer. Some OS will really flush
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log . Slow, Safest.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
@@ -503,7 +551,7 @@ appendfsync everysec
# the same as "appendfsync none". In practical terms, this means that it is
# possible to lose up to 30 seconds of log in the worst scenario (with the
# default Linux settings).
#
#
# If you have latency problems turn this to "yes". Otherwise leave it as
# "no" that is the safest pick from the point of view of durability.
@@ -512,7 +560,7 @@ no-appendfsync-on-rewrite no
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
@@ -561,11 +609,11 @@ aof-load-truncated yes
# still in execution after the maximum allowed time and will start to
# reply to queries with an error.
#
# When a long running script exceed the maximum execution time only the
# When a long running script exceeds the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# is the only way to shut down the server in the case a write commands was
# already issue by the script but the user don't want to wait for the natural
# is the only way to shut down the server in the case a write command was
# already issued by the script but the user doesn't want to wait for the natural
# termination of the script.
#
# Set it to 0 or a negative value for unlimited execution without warnings.
@@ -579,7 +627,7 @@ lua-time-limit 5000
# but just the time needed to actually execute the command (this is the only
# stage of command execution where the thread is blocked and can not serve
# other requests in the meantime).
#
#
# You can configure the slow log with two parameters: one tells Redis
# what is the execution time, in microseconds, to exceed in order for the
# command to get logged, and the other parameter is the length of the
@@ -620,7 +668,7 @@ latency-monitor-threshold 0
# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications
#
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
# messages will be published via Pub/Sub:
@@ -644,8 +692,8 @@ latency-monitor-threshold 0
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
#
# The "notify-keyspace-events" takes as argument a string that is composed
# by zero or multiple characters. The empty string means that notifications
# are disabled at all.
# of zero or multiple characters. The empty string means that notifications
# are disabled.
#
# Example: to enable list and generic events, from the point of view of the
# event name, use:
@@ -677,7 +725,7 @@ list-max-ziplist-entries 512
list-max-ziplist-value 64
# Sets have a special encoding in just one case: when a set is composed
# of just strings that happens to be integers in radix 10 in the range
# of just strings that happen to be integers in radix 10 in the range
# of 64 bit signed integers.
# The following configuration setting sets the limit in the size of the
# set in order to use this special memory saving encoding.
@@ -695,7 +743,7 @@ zset-max-ziplist-value 64
#
# A value greater than 16000 is totally useless, since at that point the
# dense representation is more memory efficient.
#
#
# The suggested value is ~ 3000 in order to have the benefits of
# the space efficient encoding without slowing down too much PFADD,
# which is O(N) with the sparse encoding. The value can be raised to
@@ -710,13 +758,13 @@ hll-sparse-max-bytes 3000
# that is rehashing, the more rehashing "steps" are performed, so if the
# server is idle the rehashing is never complete and some more memory is used
# by the hash table.
#
#
# The default is to use this millisecond 10 times every second in order to
# active rehashing the main dictionaries, freeing memory when possible.
# actively rehash the main dictionaries, freeing memory when possible.
#
# If unsure:
# use "activerehashing no" if you have hard latency requirements and it is
# not a good thing in your environment that Redis can reply form time to time
# not a good thing in your environment that Redis can reply from time to time
# to queries with 2 milliseconds delay.
#
# use "activerehashing yes" if you don't have such hard requirements but
@@ -765,7 +813,7 @@ client-output-buffer-limit pubsub 32mb 8mb 60
# never requested, and so forth.
#
# Not all tasks are performed with the same frequency, but Redis checks for
# tasks to perform accordingly to the specified "hz" value.
# tasks to perform according to the specified "hz" value.
#
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
@@ -782,4 +830,3 @@ hz 10
# in order to commit the file to the disk more incrementally and avoid
# big latency spikes.
aof-rewrite-incremental-fsync yes
+1 -1
View File
@@ -29,7 +29,7 @@ port 26379
# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interferer with administrative tasks such as
# for the process to don't interfere with administrative tasks such as
# unmounting filesystems.
dir /tmp
+2 -1
View File
@@ -58,7 +58,7 @@ ifeq ($(uname_S),SunOS)
# SunOS
INSTALL=cp -pf
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt
else
ifeq ($(uname_S),Darwin)
# Darwin (nothing to do)
@@ -251,3 +251,4 @@ install: all
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
+1 -1
View File
@@ -71,7 +71,7 @@ void listRelease(list *list)
zfree(list);
}
/* Add a new node to the list, to head, contaning the specified 'value'
/* Add a new node to the list, to head, containing the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
+32 -4
View File
@@ -34,6 +34,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
@@ -57,24 +58,37 @@ static void anetSetError(char *err, const char *fmt, ...)
va_end(ap);
}
int anetNonBlock(char *err, int fd)
{
int anetSetBlock(char *err, int fd, int non_block) {
int flags;
/* Set the socket non-blocking.
/* Set the socket blocking (if non_block is zero) or non-blocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno));
return ANET_ERR;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
if (non_block)
flags |= O_NONBLOCK;
else
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
int anetNonBlock(char *err, int fd) {
return anetSetBlock(err,fd,1);
}
int anetBlock(char *err, int fd) {
return anetSetBlock(err,fd,0);
}
/* Set TCP keep alive option to detect dead peers. The interval option
* is only used for Linux as we are using Linux-specific APIs to set
* the probe send time, interval, and count. */
@@ -165,6 +179,20 @@ int anetTcpKeepAlive(char *err, int fd)
return ANET_OK;
}
/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
* number of milliseconds, or disable it if the 'ms' argument is zero. */
int anetSendTimeout(char *err, int fd, long long ms) {
struct timeval tv;
tv.tv_sec = ms/1000;
tv.tv_usec = (ms%1000)*1000;
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
anetSetError(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
* do the actual work. It resolves the hostname "host" and set the string
* representation of the IP address into the buffer pointed by "ipbuf".
+2
View File
@@ -61,9 +61,11 @@ int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port)
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd);
int anetBlock(char *err, int fd);
int anetEnableTcpNoDelay(char *err, int fd);
int anetDisableTcpNoDelay(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd);
int anetSendTimeout(char *err, int fd, long long ms);
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);
int anetKeepAlive(char *err, int fd, int interval);
int anetSockName(int fd, char *ip, size_t ip_len, int *port);
+34 -13
View File
@@ -73,7 +73,7 @@ void aofRewriteBufferReset(void) {
listSetFreeMethod(server.aof_rewrite_buf_blocks,zfree);
}
/* Return the current size of the AOF rerwite buffer. */
/* Return the current size of the AOF rewrite buffer. */
unsigned long aofRewriteBufferSize(void) {
listNode *ln = listLast(server.aof_rewrite_buf_blocks);
aofrwblock *block = ln ? ln->value : NULL;
@@ -202,7 +202,7 @@ int startAppendOnly(void) {
redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.");
return REDIS_ERR;
}
/* We correctly switched on AOF, now wait for the rerwite to be complete
/* We correctly switched on AOF, now wait for the rewrite to be complete
* in order to append data on disk. */
server.aof_state = REDIS_AOF_WAIT_REWRITE;
return REDIS_OK;
@@ -243,7 +243,7 @@ void flushAppendOnlyFile(int force) {
* the write for a couple of seconds. */
if (sync_in_progress) {
if (server.aof_flush_postponed_start == 0) {
/* No previous write postponinig, remember that we are
/* No previous write postponing, remember that we are
* postponing the flush and return. */
server.aof_flush_postponed_start = server.unixtime;
return;
@@ -294,7 +294,7 @@ void flushAppendOnlyFile(int force) {
last_write_error_log = server.unixtime;
}
/* Lof the AOF write error and record the error code. */
/* Log the AOF write error and record the error code. */
if (nwritten == -1) {
if (can_log) {
redisLog(REDIS_WARNING,"Error writing to the AOF file: %s",
@@ -318,7 +318,7 @@ void flushAppendOnlyFile(int force) {
"ftruncate: %s", strerror(errno));
}
} else {
/* If the ftrunacate() succeeded we can set nwritten to
/* If the ftruncate() succeeded we can set nwritten to
* -1 since there is no longer partial data into the AOF. */
nwritten = -1;
}
@@ -330,7 +330,7 @@ void flushAppendOnlyFile(int force) {
/* We can't recover when the fsync policy is ALWAYS since the
* reply for the client is already in the output buffers, and we
* have the contract with the user that on acknowledged write data
* is synched on disk. */
* is synced on disk. */
redisLog(REDIS_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting...");
exit(1);
} else {
@@ -425,7 +425,7 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r
long long when;
robj *argv[3];
/* Make sure we can use strtol */
/* Make sure we can use strtoll */
seconds = getDecodedObject(seconds);
when = strtoll(seconds->ptr,NULL,10);
/* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */
@@ -456,7 +456,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
robj *tmpargv[3];
/* The DB this command was targeting is not the same as the last command
* we appendend. To issue a SELECT command is needed. */
* we appended. To issue a SELECT command is needed. */
if (dictid != server.aof_selected_db) {
char seldb[64];
@@ -549,7 +549,7 @@ void freeFakeClient(struct redisClient *c) {
zfree(c);
}
/* Replay the append log file. On error REDIS_OK is returned. On non fatal
/* Replay the append log file. On success REDIS_OK is returned. On non fatal
* error (the append only file is zero-length) REDIS_ERR is returned. On
* fatal error an error message is logged and the program exists. */
int loadAppendOnlyFile(char *filename) {
@@ -558,6 +558,7 @@ int loadAppendOnlyFile(char *filename) {
struct redis_stat sb;
int old_aof_state = server.aof_state;
long loops = 0;
off_t valid_up_to = 0; /* Offset of the latest well-formed command loaded. */
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.aof_current_size = 0;
@@ -647,6 +648,7 @@ int loadAppendOnlyFile(char *filename) {
/* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */
freeFakeClientArgv(fakeClient);
if (server.aof_load_truncated) valid_up_to = ftello(fp);
}
/* This point can only be reached when EOF is reached without errors.
@@ -671,9 +673,27 @@ readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */
uxeof: /* Unexpected AOF end of file. */
if (server.aof_load_truncated) {
redisLog(REDIS_WARNING,"!!! Warning: short read while loading the AOF file !!!");
redisLog(REDIS_WARNING,
"AOF loaded anyway because aof-load-truncated is enabled");
goto loaded_ok;
redisLog(REDIS_WARNING,"!!! Truncating the AOF at offset %llu !!!",
(unsigned long long) valid_up_to);
if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) {
if (valid_up_to == -1) {
redisLog(REDIS_WARNING,"Last valid command offset is invalid");
} else {
redisLog(REDIS_WARNING,"Error truncating the AOF file: %s",
strerror(errno));
}
} else {
/* Make sure the AOF file descriptor points to the end of the
* file after the truncate call. */
if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) {
redisLog(REDIS_WARNING,"Can't seek the end of the AOF file: %s",
strerror(errno));
} else {
redisLog(REDIS_WARNING,
"AOF loaded anyway because aof-load-truncated is enabled");
goto loaded_ok;
}
}
}
redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.");
exit(1);
@@ -1018,6 +1038,7 @@ int rewriteAppendOnlyFile(char *filename) {
}
}
dictReleaseIterator(di);
di = NULL;
}
/* Make sure data will not remain on the OS's output buffers */
@@ -1036,9 +1057,9 @@ int rewriteAppendOnlyFile(char *filename) {
return REDIS_OK;
werr:
redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));
fclose(fp);
unlink(tmpfile);
redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));
if (di) dictReleaseIterator(di);
return REDIS_ERR;
}
+31 -2
View File
@@ -267,6 +267,16 @@ void loadServerConfigFromString(char *config) {
if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-diskless-sync") && argc==2) {
if ((server.repl_diskless_sync = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-diskless-sync-delay") && argc==2) {
server.repl_diskless_sync_delay = atoi(argv[1]);
if (server.repl_diskless_sync_delay < 0) {
err = "repl-diskless-sync-delay can't be negative";
goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-backlog-size") && argc == 2) {
long long size = memtoll(argv[1],NULL);
if (size <= 0) {
@@ -281,7 +291,7 @@ void loadServerConfigFromString(char *config) {
goto loaderr;
}
} else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
server.masterauth = zstrdup(argv[1]);
server.masterauth = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
if ((server.repl_serve_stale_data = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
@@ -727,6 +737,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.repl_slave_ro = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"activerehashing")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.activerehashing = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
if (chdir((char*)o->ptr) == -1) {
addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
@@ -864,6 +879,15 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.repl_disable_tcp_nodelay = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"repl-diskless-sync")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.repl_diskless_sync = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"repl-diskless-sync-delay")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll < 0) goto badfmt;
server.repl_diskless_sync_delay = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll < 0) goto badfmt;
@@ -982,6 +1006,7 @@ void configGetCommand(redisClient *c) {
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("repl-diskless-sync-delay",server.repl_diskless_sync_delay);
/* Bool (yes/no) values */
config_get_bool_field("no-appendfsync-on-rewrite",
@@ -998,6 +1023,8 @@ void configGetCommand(redisClient *c) {
config_get_bool_field("activerehashing", server.activerehashing);
config_get_bool_field("repl-disable-tcp-nodelay",
server.repl_disable_tcp_nodelay);
config_get_bool_field("repl-diskless-sync",
server.repl_diskless_sync);
config_get_bool_field("aof-rewrite-incremental-fsync",
server.aof_rewrite_incremental_fsync);
config_get_bool_field("aof-load-truncated",
@@ -1424,7 +1451,7 @@ void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int
rewriteConfigRewriteLine(state,option,line,force);
}
/* Rewrite the syslog-fability option. */
/* Rewrite the syslog-facility option. */
void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
int value = server.syslog_facility, j;
int force = value != LOG_LOCAL0;
@@ -1722,6 +1749,8 @@ int rewriteConfig(char *path) {
rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE);
rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);
rewriteConfigYesNoOption(state,"repl-diskless-sync",server.repl_diskless_sync,REDIS_DEFAULT_REPL_DISKLESS_SYNC);
rewriteConfigNumericalOption(state,"repl-diskless-sync-delay",server.repl_diskless_sync_delay,REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY);
rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);
rewriteConfigNumericalOption(state,"min-slaves-to-write",server.repl_min_slaves_to_write,REDIS_DEFAULT_MIN_SLAVES_TO_WRITE);
rewriteConfigNumericalOption(state,"min-slaves-max-lag",server.repl_min_slaves_max_lag,REDIS_DEFAULT_MIN_SLAVES_MAX_LAG);
+1
View File
@@ -48,6 +48,7 @@
#define HAVE_PROC_STAT 1
#define HAVE_PROC_MAPS 1
#define HAVE_PROC_SMAPS 1
#define HAVE_PROC_SOMAXCONN 1
#endif
/* Test for task_info() */
+4 -2
View File
@@ -371,7 +371,7 @@ void scanCallback(void *privdata, const dictEntry *de) {
} else if (o->type == REDIS_ZSET) {
key = dictGetKey(de);
incrRefCount(key);
val = createStringObjectFromLongDouble(*(double*)dictGetVal(de));
val = createStringObjectFromLongDouble(*(double*)dictGetVal(de),0);
} else {
redisPanic("Type not handled in SCAN callback.");
}
@@ -700,7 +700,7 @@ void moveCommand(redisClient *c) {
robj *o;
redisDb *src, *dst;
int srcid;
long long dbid;
long long dbid, expire;
/* Obtain source and target DB pointers */
src = c->db;
@@ -729,6 +729,7 @@ void moveCommand(redisClient *c) {
addReply(c,shared.czero);
return;
}
expire = getExpire(c->db,c->argv[1]);
/* Return zero if the key already exists in the target DB */
if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
@@ -736,6 +737,7 @@ void moveCommand(redisClient *c) {
return;
}
dbAdd(dst,c->argv[1],o);
if (expire != -1) setExpire(dst,c->argv[1],expire);
incrRefCount(o);
/* OK! key moved, free the entry in the source DB */
+5 -3
View File
@@ -325,7 +325,8 @@ void debugCommand(redisClient *c) {
(long long) sdslen(val->ptr),
(long long) sdsavail(val->ptr));
}
} else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) {
} else if (!strcasecmp(c->argv[1]->ptr,"populate") &&
(c->argc == 3 || c->argc == 4)) {
long keys, j;
robj *key, *val;
char buf[128];
@@ -334,7 +335,8 @@ void debugCommand(redisClient *c) {
return;
dictExpand(c->db->dict,keys);
for (j = 0; j < keys; j++) {
snprintf(buf,sizeof(buf),"key:%lu",j);
snprintf(buf,sizeof(buf),"%s:%lu",
(c->argc == 3) ? "key" : (char*)c->argv[3]->ptr, j);
key = createStringObject(buf,strlen(buf));
if (lookupKeyRead(c->db,key) != NULL) {
decrRefCount(key);
@@ -852,7 +854,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
redisLog(REDIS_WARNING,
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash opening an issue on github:\n\n"
" Please report the crash by opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
" Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
);
+31 -31
View File
@@ -657,72 +657,72 @@ static unsigned long rev(unsigned long v) {
/* dictScan() is used to iterate over the elements of a dictionary.
*
* Iterating works in the following way:
* Iterating works the following way:
*
* 1) Initially you call the function using a cursor (v) value of 0.
* 2) The function performs one step of the iteration, and returns the
* new cursor value that you must use in the next call.
* new cursor value you must use in the next call.
* 3) When the returned cursor is 0, the iteration is complete.
*
* The function guarantees that all the elements that are present in the
* dictionary from the start to the end of the iteration are returned.
* However it is possible that some element is returned multiple time.
* The function guarantees all elements present in the
* dictionary get returned between the start and end of the iteration.
* However it is possible some elements get returned multiple times.
*
* For every element returned, the callback 'fn' passed as argument is
* called, with 'privdata' as first argument and the dictionar entry
* For every element returned, the callback argument 'fn' is
* called with 'privdata' as first argument and the dictionary entry
* 'de' as second argument.
*
* HOW IT WORKS.
*
* The algorithm used in the iteration was designed by Pieter Noordhuis.
* The iteration algorithm was designed by Pieter Noordhuis.
* The main idea is to increment a cursor starting from the higher order
* bits, that is, instead of incrementing the cursor normally, the bits
* bits. That is, instead of incrementing the cursor normally, the bits
* of the cursor are reversed, then the cursor is incremented, and finally
* the bits are reversed again.
*
* This strategy is needed because the hash table may be resized from one
* call to the other call of the same iteration.
* This strategy is needed because the hash table may be resized between
* iteration calls.
*
* dict.c hash tables are always power of two in size, and they
* use chaining, so the position of an element in a given table is given
* always by computing the bitwise AND between Hash(key) and SIZE-1
* by computing the bitwise AND between Hash(key) and SIZE-1
* (where SIZE-1 is always the mask that is equivalent to taking the rest
* of the division between the Hash of the key and SIZE).
*
* For example if the current hash table size is 16, the mask is
* (in binary) 1111. The position of a key in the hash table will be always
* (in binary) 1111. The position of a key in the hash table will always be
* the last four bits of the hash output, and so forth.
*
* WHAT HAPPENS IF THE TABLE CHANGES IN SIZE?
*
* If the hash table grows, elements can go anyway in one multiple of
* the old bucket: for example let's say that we already iterated with
* a 4 bit cursor 1100, since the mask is 1111 (hash table size = 16).
* If the hash table grows, elements can go anywhere in one multiple of
* the old bucket: for example let's say we already iterated with
* a 4 bit cursor 1100 (the mask is 1111 because hash table size = 16).
*
* If the hash table will be resized to 64 elements, and the new mask will
* be 111111, the new buckets that you obtain substituting in ??1100
* either 0 or 1, can be targeted only by keys that we already visited
* If the hash table will be resized to 64 elements, then the new mask will
* be 111111. The new buckets you obtain by substituting in ??1100
* with either 0 or 1 can be targeted only by keys we already visited
* when scanning the bucket 1100 in the smaller hash table.
*
* By iterating the higher bits first, because of the inverted counter, the
* cursor does not need to restart if the table size gets bigger, and will
* just continue iterating with cursors that don't have '1100' at the end,
* nor any other combination of final 4 bits already explored.
* cursor does not need to restart if the table size gets bigger. It will
* continue iterating using cursors without '1100' at the end, and also
* without any other combination of the final 4 bits already explored.
*
* Similarly when the table size shrinks over time, for example going from
* 16 to 8, If a combination of the lower three bits (the mask for size 8
* is 111) was already completely explored, it will not be visited again
* as we are sure that, we tried for example, both 0111 and 1111 (all the
* 16 to 8, if a combination of the lower three bits (the mask for size 8
* is 111) were already completely explored, it would not be visited again
* because we are sure we tried, for example, both 0111 and 1111 (all the
* variations of the higher bit) so we don't need to test it again.
*
* WAIT... YOU HAVE *TWO* TABLES DURING REHASHING!
*
* Yes, this is true, but we always iterate the smaller one of the tables,
* testing also all the expansions of the current cursor into the larger
* table. So for example if the current cursor is 101 and we also have a
* Yes, this is true, but we always iterate the smaller table first, then
* we test all the expansions of the current cursor into the larger
* table. For example if the current cursor is 101 and we also have a
* larger table of size 16, we also test (0)101 and (1)101 inside the larger
* table. This reduces the problem back to having only one table, where
* the larger one, if exists, is just an expansion of the smaller one.
* the larger one, if it exists, is just an expansion of the smaller one.
*
* LIMITATIONS
*
@@ -731,11 +731,11 @@ static unsigned long rev(unsigned long v) {
*
* The disadvantages resulting from this design are:
*
* 1) It is possible that we return duplicated elements. However this is usually
* 1) It is possible we return elements more than once. However this is usually
* easy to deal with in the application level.
* 2) The iterator must return multiple elements per call, as it needs to always
* return all the keys chained in a given bucket, and all the expansions, so
* we are sure we don't miss keys moving.
* we are sure we don't miss keys moving during rehashing.
* 3) The reverse cursor is somewhat hard to understand at first, but this
* comment is supposed to help.
*/
+1
View File
@@ -34,6 +34,7 @@
#if defined(__linux__)
#define _GNU_SOURCE
#define _DEFAULT_SOURCE
#endif
#if defined(_AIX)
+1 -1
View File
@@ -1213,7 +1213,7 @@ void pfcountCommand(redisClient *c) {
for (j = 1; j < c->argc; j++) {
/* Check type and size. */
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) continue; /* Assume empty HLL for non existing var. */
if (o == NULL) continue; /* Assume empty HLL for non existing var.*/
if (isHLLObjectOrReply(c,o) != REDIS_OK) return;
/* Merge with this HLL with our 'max' HHL by setting max[i]
+41 -6
View File
@@ -56,6 +56,32 @@ dictType latencyTimeSeriesDictType = {
dictVanillaFree /* val destructor */
};
/* ------------------------- Utility functions ------------------------------ */
#ifdef __linux__
/* Returns 1 if Transparent Huge Pages support is enabled in the kernel.
* Otherwise (or if we are unable to check) 0 is returned. */
int THPIsEnabled(void) {
char buf[1024];
FILE *fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled","r");
if (!fp) return 0;
if (fgets(buf,sizeof(buf),fp) == NULL) {
fclose(fp);
return 0;
}
fclose(fp);
return (strstr(buf,"[never]") == NULL) ? 1 : 0;
}
#endif
/* Report the amount of AnonHugePages in smap, in bytes. If the return
* value of the function is non-zero, the process is being targeted by
* THP support, and is likely to have memory usage / latency issues. */
int THPGetAnonHugePagesSize(void) {
return zmalloc_get_smap_bytes_by_field("AnonHugePages:");
}
/* ---------------------------- Latency API --------------------------------- */
/* Latency monitor initialization. We just need to create the dictionary
@@ -203,6 +229,7 @@ sds createLatencyReport(void) {
int advise_hz = 0; /* Use higher HZ. */
int advise_large_objects = 0; /* Deletion of large objects. */
int advise_relax_fsync_policy = 0; /* appendfsync always is slow. */
int advise_disable_thp = 0; /* AnonHugePages detected. */
int advices = 0;
/* Return ASAP if the latency engine is disabled and it looks like it
@@ -220,7 +247,7 @@ sds createLatencyReport(void) {
dictEntry *de;
int eventnum = 0;
di = dictGetIterator(server.latency_events);
di = dictGetSafeIterator(server.latency_events);
while((de = dictNext(di)) != NULL) {
char *event = dictGetKey(de);
struct latencyTimeSeries *ts = dictGetVal(de);
@@ -346,9 +373,15 @@ sds createLatencyReport(void) {
}
dictReleaseIterator(di);
if (eventnum == 0) {
/* Add non event based advices. */
if (THPGetAnonHugePagesSize() > 0) {
advise_disable_thp = 1;
advices++;
}
if (eventnum == 0 && advices == 0) {
report = sdscat(report,"Dave, no latency spike was observed during the lifetime of this Redis instance, not in the slightest bit. I honestly think you ought to sit down calmly, take a stress pill, and think things over.\n");
} else if (advices == 0) {
} else if (eventnum > 0 && advices == 0) {
report = sdscat(report,"\nWhile there are latency events logged, I'm not able to suggest any easy fix. Please use the Redis community to get some help, providing this report in your help request.\n");
} else {
/* Add all the suggestions accumulated so far. */
@@ -418,6 +451,10 @@ sds createLatencyReport(void) {
if (advise_large_objects) {
report = sdscat(report,"- Deleting, expiring or evicting (because of maxmemory policy) large objects is a blocking operation. If you have very large objects that are often deleted, expired, or evicted, try to fragment those objects into multiple smaller objects.\n");
}
if (advise_disable_thp) {
report = sdscat(report,"- I detected a non zero amount of anonymous huge pages used by your process. This creates very serious latency events in different conditions, especially when Redis is persisting on disk. To disable THP support use the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled', make sure to also add it into /etc/rc.local so that the command will be executed again after a reboot. Note that even if you have already disabled THP, you still need to restart the Redis process to get rid of the huge pages already created.\n");
}
}
return report;
@@ -475,7 +512,6 @@ sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {
for (j = 0; j < LATENCY_TS_LEN; j++) {
int i = (ts->idx + j) % LATENCY_TS_LEN;
int elapsed;
char *label;
char buf[64];
if (ts->samples[i].time == 0) continue;
@@ -497,8 +533,7 @@ sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {
snprintf(buf,sizeof(buf),"%dh",elapsed/3600);
else
snprintf(buf,sizeof(buf),"%dd",elapsed/(3600*24));
label = zstrdup(buf);
sparklineSequenceAddSample(seq,ts->samples[i].latency,label);
sparklineSequenceAddSample(seq,ts->samples[i].latency,buf);
}
graph = sdscatprintf(graph,
+1
View File
@@ -63,6 +63,7 @@ struct latencyStats {
void latencyMonitorInit(void);
void latencyAddSample(char *event, mstime_t latency);
int THPIsEnabled(void);
/* Latency monitoring macros. */
+3
View File
@@ -35,6 +35,9 @@
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#if defined(__sun)
#include <stropts.h>
#endif
#include "config.h"
#if (ULONG_MAX == 4294967295UL)
+26 -30
View File
@@ -89,10 +89,12 @@ redisClient *createClient(int fd) {
c->ctime = c->lastinteraction = server.unixtime;
c->authenticated = 0;
c->replstate = REDIS_REPL_NONE;
c->repl_put_online_on_ack = 0;
c->reploff = 0;
c->repl_ack_off = 0;
c->repl_ack_time = 0;
c->slave_listening_port = 0;
c->slave_capa = SLAVE_CAPA_NONE;
c->reply = listCreate();
c->reply_bytes = 0;
c->obuf_soft_limit_reached_time = 0;
@@ -133,7 +135,7 @@ int prepareClientToWrite(redisClient *c) {
if (c->fd <= 0) return REDIS_ERR; /* Fake client */
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||
c->replstate == REDIS_REPL_ONLINE) &&
c->replstate == REDIS_REPL_ONLINE) && !c->repl_put_online_on_ack &&
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
return REDIS_OK;
@@ -622,20 +624,6 @@ void disconnectSlaves(void) {
}
}
/* This function is called when the slave lose the connection with the
* master into an unexpected way. */
void replicationHandleMasterDisconnection(void) {
server.master = NULL;
server.repl_state = REDIS_REPL_CONNECT;
server.repl_down_since = server.unixtime;
/* We lost connection with our master, force our slaves to resync
* with us as well to load the new data set.
*
* If server.masterhost is NULL the user called SLAVEOF NO ONE so
* slave resync is not needed. */
if (server.masterhost != NULL) disconnectSlaves();
}
void freeClient(redisClient *c) {
listNode *ln;
@@ -661,12 +649,8 @@ void freeClient(redisClient *c) {
/* Log link disconnection with slave */
if ((c->flags & REDIS_SLAVE) && !(c->flags & REDIS_MONITOR)) {
char ip[REDIS_IP_STR_LEN];
if (anetPeerToString(c->fd,ip,sizeof(ip),NULL) != -1) {
redisLog(REDIS_WARNING,"Connection with slave %s:%d lost.",
ip, c->slave_listening_port);
}
redisLog(REDIS_WARNING,"Connection with slave %s lost.",
replicationGetSlaveName(c));
}
/* Free the query buffer */
@@ -716,8 +700,10 @@ void freeClient(redisClient *c) {
/* Master/slave cleanup Case 1:
* we lost the connection with a slave. */
if (c->flags & REDIS_SLAVE) {
if (c->replstate == REDIS_REPL_SEND_BULK && c->repldbfd != -1)
close(c->repldbfd);
if (c->replstate == REDIS_REPL_SEND_BULK) {
if (c->repldbfd != -1) close(c->repldbfd);
if (c->replpreamble) sdsfree(c->replpreamble);
}
list *l = (c->flags & REDIS_MONITOR) ? server.monitors : server.slaves;
ln = listSearchKey(l,c);
redisAssert(ln != NULL);
@@ -756,7 +742,7 @@ void freeClient(redisClient *c) {
* a context where calling freeClient() is not possible, because the client
* should be valid for the continuation of the flow of the program. */
void freeClientAsync(redisClient *c) {
if (c->flags & REDIS_CLOSE_ASAP) return;
if (c->flags & REDIS_CLOSE_ASAP || c->flags & REDIS_LUA_CLIENT) return;
c->flags |= REDIS_CLOSE_ASAP;
listAddNodeTail(server.clients_to_close,c);
}
@@ -823,6 +809,7 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
*
* However if we are over the maxmemory limit we ignore that and
* just deliver as much data as it is possible to deliver. */
server.stat_net_output_bytes += totwritten;
if (totwritten > REDIS_MAX_WRITE_PER_EVENT &&
(server.maxmemory == 0 ||
zmalloc_used_memory() < server.maxmemory)) break;
@@ -906,8 +893,10 @@ int processInlineBuffer(redisClient *c) {
sdsrange(c->querybuf,querylen+2,-1);
/* Setup argv array on client structure */
if (c->argv) zfree(c->argv);
c->argv = zmalloc(sizeof(robj*)*argc);
if (argc) {
if (c->argv) zfree(c->argv);
c->argv = zmalloc(sizeof(robj*)*argc);
}
/* Create redis objects for all arguments. */
for (c->argc = 0, j = 0; j < argc; j++) {
@@ -925,7 +914,7 @@ int processInlineBuffer(redisClient *c) {
/* Helper function. Trims query buffer to make the function that processes
* multi bulk requests idempotent. */
static void setProtocolError(redisClient *c, int pos) {
if (server.verbosity >= REDIS_VERBOSE) {
if (server.verbosity <= REDIS_VERBOSE) {
sds client = catClientInfoString(sdsempty(),c);
redisLog(REDIS_VERBOSE,
"Protocol error from client: %s", client);
@@ -1156,6 +1145,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
sdsIncrLen(c->querybuf,nread);
c->lastinteraction = server.unixtime;
if (c->flags & REDIS_MASTER) c->reploff += nread;
server.stat_net_input_bytes += nread;
} else {
server.current_client = NULL;
return;
@@ -1204,9 +1194,9 @@ void formatPeerId(char *peerid, size_t peerid_len, char *ip, int port) {
}
/* A Redis "Peer ID" is a colon separated ip:port pair.
* For IPv4 it's in the form x.y.z.k:pork, example: "127.0.0.1:1234".
* For IPv4 it's in the form x.y.z.k:port, example: "127.0.0.1:1234".
* For IPv6 addresses we use [] around the IP part, like in "[::1]:1234".
* For Unix socekts we use path:0, like in "/tmp/redis:0".
* For Unix sockets we use path:0, like in "/tmp/redis:0".
*
* A Peer ID always fits inside a buffer of REDIS_PEER_ID_LEN bytes, including
* the null term.
@@ -1233,7 +1223,7 @@ int genClientPeerId(redisClient *client, char *peerid, size_t peerid_len) {
}
/* This function returns the client peer id, by creating and caching it
* if client->perrid is NULL, otherwise returning the cached value.
* if client->peerid is NULL, otherwise returning the cached value.
* The Peer ID never changes during the life of the client, however it
* is expensive to compute. */
char *getClientPeerId(redisClient *c) {
@@ -1618,6 +1608,12 @@ void flushSlavesOutputBuffers(void) {
redisClient *slave = listNodeValue(ln);
int events;
/* Note that the following will not flush output buffers of slaves
* in STATE_ONLINE but having put_online_on_ack set to true: in this
* case the writable event is never installed, since the purpose
* of put_online_on_ack is to postpone the moment it is installed.
* This is what we want since slaves in this state should not receive
* writes before the first ACK. */
events = aeGetFileEvents(server.el,slave->fd);
if (events & AE_WRITABLE &&
slave->replstate == REDIS_REPL_ONLINE &&
+34 -16
View File
@@ -69,26 +69,44 @@ robj *createStringObjectFromLongLong(long long value) {
return o;
}
/* Note: this function is defined into object.c since here it is where it
* belongs but it is actually designed to be used just for INCRBYFLOAT */
robj *createStringObjectFromLongDouble(long double value) {
/* Create a string object from a long double. If humanfriendly is non-zero
* it does not use exponential format and trims trailing zeroes at the end,
* however this results in loss of precision. Otherwise exp format is used
* and the output of snprintf() is not modified.
*
* The 'humanfriendly' option is used for INCRBYFLOAT and HINCRBYFLOAT. */
robj *createStringObjectFromLongDouble(long double value, int humanfriendly) {
char buf[256];
int len;
/* We use 17 digits precision since with 128 bit floats that precision
* after rounding is able to represent most small decimal numbers in a way
* that is "non surprising" for the user (that is, most small decimal
* numbers will be represented in a way that when converted back into
* a string are exactly the same as what the user typed.) */
len = snprintf(buf,sizeof(buf),"%.17Lf", value);
/* Now remove trailing zeroes after the '.' */
if (strchr(buf,'.') != NULL) {
char *p = buf+len-1;
while(*p == '0') {
p--;
len--;
if (isinf(value)) {
/* Libc in odd systems (Hi Solaris!) will format infinite in a
* different way, so better to handle it in an explicit way. */
if (value > 0) {
memcpy(buf,"inf",3);
len = 3;
} else {
memcpy(buf,"-inf",4);
len = 4;
}
if (*p == '.') len--;
} else if (humanfriendly) {
/* We use 17 digits precision since with 128 bit floats that precision
* after rounding is able to represent most small decimal numbers in a
* way that is "non surprising" for the user (that is, most small
* decimal numbers will be represented in a way that when converted
* back into a string are exactly the same as what the user typed.) */
len = snprintf(buf,sizeof(buf),"%.17Lf", value);
/* Now remove trailing zeroes after the '.' */
if (strchr(buf,'.') != NULL) {
char *p = buf+len-1;
while(*p == '0') {
p--;
len--;
}
if (*p == '.') len--;
}
} else {
len = snprintf(buf,sizeof(buf),"%.17Lg", value);
}
return createStringObject(buf,len);
}
+364 -51
View File
@@ -474,7 +474,7 @@ int rdbLoadObjectType(rio *rdb) {
return type;
}
/* Save a Redis object. Returns -1 on error, 0 on success. */
/* Save a Redis object. Returns -1 on error, number of bytes written on success. */
int rdbSaveObject(rio *rdb, robj *o) {
int n, nwritten = 0;
@@ -626,17 +626,100 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
return 1;
}
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
int rdbSave(char *filename) {
/* Produces a dump of the database in RDB format sending it to the specified
* Redis I/O channel. On success REDIS_OK is returned, otherwise REDIS_ERR
* is returned and part of the output, or all the output, can be
* missing because of I/O errors.
*
* When the function returns REDIS_ERR and if 'error' is not NULL, the
* integer pointed by 'error' is set to the value of errno just after the I/O
* error. */
int rdbSaveRio(rio *rdb, int *error) {
dictIterator *di = NULL;
dictEntry *de;
char tmpfile[256];
char magic[10];
int j;
long long now = mstime();
uint64_t cksum;
if (server.rdb_checksum)
rdb->update_cksum = rioGenericUpdateChecksum;
snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
if (!di) return REDIS_ERR;
/* Write the SELECT DB opcode */
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_SELECTDB) == -1) goto werr;
if (rdbSaveLen(rdb,j) == -1) goto werr;
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
sds keystr = dictGetKey(de);
robj key, *o = dictGetVal(de);
long long expire;
initStaticStringObject(key,keystr);
expire = getExpire(db,&key);
if (rdbSaveKeyValuePair(rdb,&key,o,expire,now) == -1) goto werr;
}
dictReleaseIterator(di);
}
di = NULL; /* So that we don't release it again on error. */
/* EOF opcode */
if (rdbSaveType(rdb,REDIS_RDB_OPCODE_EOF) == -1) goto werr;
/* CRC64 checksum. It will be zero if checksum computation is disabled, the
* loading code skips the check in this case. */
cksum = rdb->cksum;
memrev64ifbe(&cksum);
if (rioWrite(rdb,&cksum,8) == 0) goto werr;
return REDIS_OK;
werr:
if (error) *error = errno;
if (di) dictReleaseIterator(di);
return REDIS_ERR;
}
/* This is just a wrapper to rdbSaveRio() that additionally adds a prefix
* and a suffix to the generated RDB dump. The prefix is:
*
* $EOF:<40 bytes unguessable hex string>\r\n
*
* While the suffix is the 40 bytes hex string we announced in the prefix.
* This way processes receiving the payload can understand when it ends
* without doing any processing of the content. */
int rdbSaveRioWithEOFMark(rio *rdb, int *error) {
char eofmark[REDIS_EOF_MARK_SIZE];
getRandomHexChars(eofmark,REDIS_EOF_MARK_SIZE);
if (error) *error = 0;
if (rioWrite(rdb,"$EOF:",5) == 0) goto werr;
if (rioWrite(rdb,eofmark,REDIS_EOF_MARK_SIZE) == 0) goto werr;
if (rioWrite(rdb,"\r\n",2) == 0) goto werr;
if (rdbSaveRio(rdb,error) == REDIS_ERR) goto werr;
if (rioWrite(rdb,eofmark,REDIS_EOF_MARK_SIZE) == 0) goto werr;
return REDIS_OK;
werr: /* Write error. */
/* Set 'error' only if not already set by rdbSaveRio() call. */
if (error && *error == 0) *error = errno;
return REDIS_ERR;
}
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
char tmpfile[256];
FILE *fp;
rio rdb;
uint64_t cksum;
int error;
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
@@ -647,47 +730,10 @@ int rdbSave(char *filename) {
}
rioInitWithFile(&rdb,fp);
if (server.rdb_checksum)
rdb.update_cksum = rioGenericUpdateChecksum;
snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
if (rdbWriteRaw(&rdb,magic,9) == -1) goto werr;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
if (!di) {
fclose(fp);
return REDIS_ERR;
}
/* Write the SELECT DB opcode */
if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_SELECTDB) == -1) goto werr;
if (rdbSaveLen(&rdb,j) == -1) goto werr;
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
sds keystr = dictGetKey(de);
robj key, *o = dictGetVal(de);
long long expire;
initStaticStringObject(key,keystr);
expire = getExpire(db,&key);
if (rdbSaveKeyValuePair(&rdb,&key,o,expire,now) == -1) goto werr;
}
dictReleaseIterator(di);
if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
errno = error;
goto werr;
}
di = NULL; /* So that we don't release it again on error. */
/* EOF opcode */
if (rdbSaveType(&rdb,REDIS_RDB_OPCODE_EOF) == -1) goto werr;
/* CRC64 checksum. It will be zero if checksum computation is disabled, the
* loading code skips the check in this case. */
cksum = rdb.cksum;
memrev64ifbe(&cksum);
if (rioWrite(&rdb,&cksum,8) == 0) goto werr;
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
@@ -708,10 +754,9 @@ int rdbSave(char *filename) {
return REDIS_OK;
werr:
redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
fclose(fp);
unlink(tmpfile);
redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
if (di) dictReleaseIterator(di);
return REDIS_ERR;
}
@@ -756,6 +801,7 @@ int rdbSaveBackground(char *filename) {
redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_DISK;
updateDictResizePolicy();
return REDIS_OK;
}
@@ -1041,8 +1087,9 @@ void startLoading(FILE *fp) {
/* Load the DB */
server.loading = 1;
server.loading_start_time = time(NULL);
server.loading_loaded_bytes = 0;
if (fstat(fileno(fp), &sb) == -1) {
server.loading_total_bytes = 1; /* just to avoid division by zero */
server.loading_total_bytes = 0;
} else {
server.loading_total_bytes = sb.st_size;
}
@@ -1191,8 +1238,9 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
return REDIS_ERR; /* Just to avoid warning */
}
/* A background saving child (BGSAVE) terminated its work. Handle this. */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {
/* A background saving child (BGSAVE) terminated its work. Handle this.
* This function covers the case of actual BGSAVEs. */
void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
if (!bysignal && exitcode == 0) {
redisLog(REDIS_NOTICE,
"Background saving terminated with success");
@@ -1217,11 +1265,276 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
server.lastbgsave_status = REDIS_ERR;
}
server.rdb_child_pid = -1;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_NONE;
server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;
server.rdb_save_time_start = -1;
/* Possibly there are slaves waiting for a BGSAVE in order to be served
* (the first stage of SYNC is a bulk transfer of dump.rdb) */
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? REDIS_OK : REDIS_ERR);
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? REDIS_OK : REDIS_ERR, REDIS_RDB_CHILD_TYPE_DISK);
}
/* A background saving child (BGSAVE) terminated its work. Handle this.
* This function covers the case of RDB -> Salves socket transfers for
* diskless replication. */
void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
uint64_t *ok_slaves;
if (!bysignal && exitcode == 0) {
redisLog(REDIS_NOTICE,
"Background RDB transfer terminated with success");
} else if (!bysignal && exitcode != 0) {
redisLog(REDIS_WARNING, "Background transfer error");
} else {
redisLog(REDIS_WARNING,
"Background transfer terminated by signal %d", bysignal);
}
server.rdb_child_pid = -1;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_NONE;
server.rdb_save_time_start = -1;
/* If the child returns an OK exit code, read the set of slave client
* IDs and the associated status code. We'll terminate all the slaves
* in error state.
*
* If the process returned an error, consider the list of slaves that
* can continue to be emtpy, so that it's just a special case of the
* normal code path. */
ok_slaves = zmalloc(sizeof(uint64_t)); /* Make space for the count. */
ok_slaves[0] = 0;
if (!bysignal && exitcode == 0) {
int readlen = sizeof(uint64_t);
if (read(server.rdb_pipe_read_result_from_child, ok_slaves, readlen) ==
readlen)
{
readlen = ok_slaves[0]*sizeof(uint64_t)*2;
/* Make space for enough elements as specified by the first
* uint64_t element in the array. */
ok_slaves = zrealloc(ok_slaves,sizeof(uint64_t)+readlen);
if (readlen &&
read(server.rdb_pipe_read_result_from_child, ok_slaves+1,
readlen) != readlen)
{
ok_slaves[0] = 0;
}
}
}
close(server.rdb_pipe_read_result_from_child);
close(server.rdb_pipe_write_result_to_parent);
/* We can continue the replication process with all the slaves that
* correctly received the full payload. Others are terminated. */
listNode *ln;
listIter li;
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
redisClient *slave = ln->value;
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {
uint64_t j;
int errorcode = 0;
/* Search for the slave ID in the reply. In order for a slave to
* continue the replication process, we need to find it in the list,
* and it must have an error code set to 0 (which means success). */
for (j = 0; j < ok_slaves[0]; j++) {
if (slave->id == ok_slaves[2*j+1]) {
errorcode = ok_slaves[2*j+2];
break; /* Found in slaves list. */
}
}
if (j == ok_slaves[0] || errorcode != 0) {
redisLog(REDIS_WARNING,
"Closing slave %s: child->slave RDB transfer failed: %s",
replicationGetSlaveName(slave),
(errorcode == 0) ? "RDB transfer child aborted"
: strerror(errorcode));
freeClient(slave);
} else {
redisLog(REDIS_WARNING,
"Slave %s correctly received the streamed RDB file.",
replicationGetSlaveName(slave));
/* Restore the socket as non-blocking. */
anetNonBlock(NULL,slave->fd);
anetSendTimeout(NULL,slave->fd,0);
}
}
}
zfree(ok_slaves);
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? REDIS_OK : REDIS_ERR, REDIS_RDB_CHILD_TYPE_SOCKET);
}
/* When a background RDB saving/transfer terminates, call the right handler. */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {
switch(server.rdb_child_type) {
case REDIS_RDB_CHILD_TYPE_DISK:
backgroundSaveDoneHandlerDisk(exitcode,bysignal);
break;
case REDIS_RDB_CHILD_TYPE_SOCKET:
backgroundSaveDoneHandlerSocket(exitcode,bysignal);
break;
default:
redisPanic("Unknown RDB child type.");
break;
}
}
/* Spawn an RDB child that writes the RDB to the sockets of the slaves
* that are currently in REDIS_REPL_WAIT_BGSAVE_START state. */
int rdbSaveToSlavesSockets(void) {
int *fds;
uint64_t *clientids;
int numfds;
listNode *ln;
listIter li;
pid_t childpid;
long long start;
int pipefds[2];
if (server.rdb_child_pid != -1) return REDIS_ERR;
/* Before to fork, create a pipe that will be used in order to
* send back to the parent the IDs of the slaves that successfully
* received all the writes. */
if (pipe(pipefds) == -1) return REDIS_ERR;
server.rdb_pipe_read_result_from_child = pipefds[0];
server.rdb_pipe_write_result_to_parent = pipefds[1];
/* Collect the file descriptors of the slaves we want to transfer
* the RDB to, which are i WAIT_BGSAVE_START state. */
fds = zmalloc(sizeof(int)*listLength(server.slaves));
/* We also allocate an array of corresponding client IDs. This will
* be useful for the child process in order to build the report
* (sent via unix pipe) that will be sent to the parent. */
clientids = zmalloc(sizeof(uint64_t)*listLength(server.slaves));
numfds = 0;
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
redisClient *slave = ln->value;
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) {
clientids[numfds] = slave->id;
fds[numfds++] = slave->fd;
replicationSetupSlaveForFullResync(slave,getPsyncInitialOffset());
/* Put the socket in non-blocking mode to simplify RDB transfer.
* We'll restore it when the children returns (since duped socket
* will share the O_NONBLOCK attribute with the parent). */
anetBlock(NULL,slave->fd);
anetSendTimeout(NULL,slave->fd,server.repl_timeout*1000);
}
}
/* Create the child process. */
start = ustime();
if ((childpid = fork()) == 0) {
/* Child */
int retval;
rio slave_sockets;
rioInitWithFdset(&slave_sockets,fds,numfds);
zfree(fds);
closeListeningSockets(0);
redisSetProcTitle("redis-rdb-to-slaves");
retval = rdbSaveRioWithEOFMark(&slave_sockets,NULL);
if (retval == REDIS_OK && rioFlush(&slave_sockets) == 0)
retval = REDIS_ERR;
if (retval == REDIS_OK) {
size_t private_dirty = zmalloc_get_private_dirty();
if (private_dirty) {
redisLog(REDIS_NOTICE,
"RDB: %zu MB of memory used by copy-on-write",
private_dirty/(1024*1024));
}
/* If we are returning OK, at least one slave was served
* with the RDB file as expected, so we need to send a report
* to the parent via the pipe. The format of the message is:
*
* <len> <slave[0].id> <slave[0].error> ...
*
* len, slave IDs, and slave errors, are all uint64_t integers,
* so basically the reply is composed of 64 bits for the len field
* plus 2 additional 64 bit integers for each entry, for a total
* of 'len' entries.
*
* The 'id' represents the slave's client ID, so that the master
* can match the report with a specific slave, and 'error' is
* set to 0 if the replication process terminated with a success
* or the error code if an error occurred. */
void *msg = zmalloc(sizeof(uint64_t)*(1+2*numfds));
uint64_t *len = msg;
uint64_t *ids = len+1;
int j, msglen;
*len = numfds;
for (j = 0; j < numfds; j++) {
*ids++ = clientids[j];
*ids++ = slave_sockets.io.fdset.state[j];
}
/* Write the message to the parent. If we have no good slaves or
* we are unable to transfer the message to the parent, we exit
* with an error so that the parent will abort the replication
* process with all the childre that were waiting. */
msglen = sizeof(uint64_t)*(1+2*numfds);
if (*len == 0 ||
write(server.rdb_pipe_write_result_to_parent,msg,msglen)
!= msglen)
{
retval = REDIS_ERR;
}
zfree(msg);
}
zfree(clientids);
exitFromChild((retval == REDIS_OK) ? 0 : 1);
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
if (childpid == -1) {
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno));
/* Undo the state change. The caller will perform cleanup on
* all the slaves in BGSAVE_START state, but an early call to
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
redisClient *slave = ln->value;
int j;
for (j = 0; j < numfds; j++) {
if (slave->id == clientids[j]) {
slave->replstate = REDIS_REPL_WAIT_BGSAVE_START;
break;
}
}
}
close(pipefds[0]);
close(pipefds[1]);
} else {
redisLog(REDIS_NOTICE,"Background RDB transfer started by pid %d",
childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_SOCKET;
updateDictResizePolicy();
}
zfree(clientids);
zfree(fds);
return (childpid == -1) ? REDIS_ERR : REDIS_OK;
}
return REDIS_OK; /* Unreached. */
}
void saveCommand(redisClient *c) {
+1
View File
@@ -101,6 +101,7 @@ int rdbSaveObjectType(rio *rdb, robj *o);
int rdbLoadObjectType(rio *rdb);
int rdbLoad(char *filename);
int rdbSaveBackground(char *filename);
int rdbSaveToSlavesSockets(void);
void rdbRemoveTempFile(pid_t childpid);
int rdbSave(char *filename);
int rdbSaveObject(rio *rdb, robj *o);
+41 -34
View File
@@ -90,9 +90,10 @@ typedef struct _client {
long long start; /* Start time of a request */
long long latency; /* Request latency */
int pending; /* Number of pending requests (replies to consume) */
int selectlen; /* If non-zero, a SELECT of 'selectlen' bytes is currently
used as a prefix of the pipline of commands. This gets
discarded the first time it's sent. */
int prefix_pending; /* If non-zero, number of pending prefix commands. Commands
such as auth and select are prefixed to the pipeline of
benchmark commands and discarded after the first send. */
int prefixlen; /* Size in bytes of the pending prefix commands */
} *client;
/* Prototypes */
@@ -212,20 +213,21 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
}
freeReplyObject(reply);
if (c->selectlen) {
size_t j;
/* This is the OK from SELECT. Just discard the SELECT
* from the buffer. */
/* This is an OK for prefix commands such as auth and select.*/
if (c->prefix_pending > 0) {
c->prefix_pending--;
c->pending--;
sdsrange(c->obuf,c->selectlen,-1);
/* We also need to fix the pointers to the strings
* we need to randomize. */
for (j = 0; j < c->randlen; j++)
c->randptr[j] -= c->selectlen;
c->selectlen = 0;
continue;
/* Discard prefix commands on first response.*/
if (c->prefixlen > 0) {
size_t j;
sdsrange(c->obuf, c->prefixlen, -1);
/* We also need to fix the pointers to the strings
* we need to randomize. */
for (j = 0; j < c->randlen; j++)
c->randptr[j] -= c->prefixlen;
c->prefixlen = 0;
}
continue;
}
if (config.requests_finished < config.requests)
@@ -299,8 +301,7 @@ static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
* 2) The offsets of the __rand_int__ elements inside the command line, used
* for arguments randomization.
*
* Even when cloning another client, the SELECT command is automatically prefixed
* if needed. */
* Even when cloning another client, prefix commands are applied if needed.*/
static client createClient(char *cmd, size_t len, client from) {
int j;
client c = zmalloc(sizeof(struct _client));
@@ -325,12 +326,16 @@ static client createClient(char *cmd, size_t len, client from) {
* Queue N requests accordingly to the pipeline size, or simply clone
* the example client buffer. */
c->obuf = sdsempty();
/* Prefix the request buffer with AUTH and/or SELECT commands, if applicable.
* These commands are discarded after the first response, so if the client is
* reused the commands will not be used again. */
c->prefix_pending = 0;
if (config.auth) {
char *buf = NULL;
int len = redisFormatCommand(&buf, "AUTH %s", config.auth);
c->obuf = sdscatlen(c->obuf, buf, len);
free(buf);
c->prefix_pending++;
}
/* If a DB number different than zero is selected, prefix our request
@@ -340,26 +345,23 @@ static client createClient(char *cmd, size_t len, client from) {
if (config.dbnum != 0) {
c->obuf = sdscatprintf(c->obuf,"*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n",
(int)sdslen(config.dbnumstr),config.dbnumstr);
c->selectlen = sdslen(c->obuf);
} else {
c->selectlen = 0;
c->prefix_pending++;
}
c->prefixlen = sdslen(c->obuf);
/* Append the request itself. */
if (from) {
c->obuf = sdscatlen(c->obuf,
from->obuf+from->selectlen,
sdslen(from->obuf)-from->selectlen);
from->obuf+from->prefixlen,
sdslen(from->obuf)-from->prefixlen);
} else {
for (j = 0; j < config.pipeline; j++)
c->obuf = sdscatlen(c->obuf,cmd,len);
}
c->written = 0;
c->pending = config.pipeline;
c->pending = config.pipeline+c->prefix_pending;
c->randptr = NULL;
c->randlen = 0;
if (c->selectlen) c->pending++;
/* Find substrings in the output buffer that need to be randomized. */
if (config.randomkeys) {
@@ -371,7 +373,7 @@ static client createClient(char *cmd, size_t len, client from) {
for (j = 0; j < (int)c->randlen; j++) {
c->randptr[j] = c->obuf + (from->randptr[j]-from->obuf);
/* Adjust for the different select prefix length. */
c->randptr[j] += c->selectlen - from->selectlen;
c->randptr[j] += c->prefixlen - from->prefixlen;
}
} else {
char *p = c->obuf;
@@ -390,7 +392,8 @@ static client createClient(char *cmd, size_t len, client from) {
}
}
}
aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c);
if (config.idlemode == 0)
aeCreateFileEvent(config.el,c->context->fd,AE_WRITABLE,writeHandler,c);
listAddNodeTail(config.clients,c);
config.liveclients++;
return c;
@@ -555,7 +558,7 @@ usage:
" -s <socket> Server socket (overrides host and port)\n"
" -a <password> Password for Redis Auth\n"
" -c <clients> Number of parallel connections (default 50)\n"
" -n <requests> Total number of requests (default 10000)\n"
" -n <requests> Total number of requests (default 100000)\n"
" -d <size> Data size of SET/GET value in bytes (default 2)\n"
" -dbnum <db> SELECT the specified db number (default 0)\n"
" -k <boolean> 1=keep alive 0=reconnect (default 1)\n"
@@ -599,9 +602,13 @@ int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData
if (config.liveclients == 0) {
fprintf(stderr,"All clients disconnected... aborting.\n");
exit(1);
}
}
if (config.csv) return 250;
if (config.idlemode == 1) {
printf("clients: %d\r", config.liveclients);
fflush(stdout);
return 250;
}
float dt = (float)(mstime()-config.start)/1000.0;
float rps = (float)config.requests_finished/dt;
printf("%s: %.2f\r", config.title, rps);
@@ -635,7 +642,7 @@ int main(int argc, const char **argv) {
signal(SIGPIPE, SIG_IGN);
config.numclients = 50;
config.requests = 10000;
config.requests = 100000;
config.liveclients = 0;
config.el = aeCreateEventLoop(1024*10);
aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
@@ -693,8 +700,8 @@ int main(int argc, const char **argv) {
}
/* Run default benchmark suite. */
data = zmalloc(config.datasize+1);
do {
data = zmalloc(config.datasize+1);
memset(data,'x',config.datasize);
data[config.datasize] = '\0';
+320 -17
View File
@@ -44,6 +44,7 @@
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include "hiredis.h"
#include "sds.h"
@@ -60,6 +61,8 @@
#define OUTPUT_CSV 2
#define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
#define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
#define REDIS_CLI_HISTFILE_ENV "REDISCLI_HISTFILE"
#define REDIS_CLI_HISTFILE_DEFAULT ".rediscli_history"
static redisContext *context;
static struct config {
@@ -74,7 +77,10 @@ static struct config {
int monitor_mode;
int pubsub_mode;
int latency_mode;
int latency_dist_mode;
int latency_history;
int lru_test_mode;
long long lru_test_sample_size;
int cluster_mode;
int cluster_reissue_command;
int slave_mode;
@@ -138,6 +144,30 @@ static void cliRefreshPrompt(void) {
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
}
static sds getHistoryPath() {
char *path = NULL;
sds historyPath = NULL;
/* check the env for a histfile override */
path = getenv(REDIS_CLI_HISTFILE_ENV);
if (path != NULL && *path != '\0') {
if (!strcmp("/dev/null", path)) {
return NULL;
}
/* if the env is set, return it */
historyPath = sdscatprintf(sdsempty(), "%s", path);
} else {
char *home = getenv("HOME");
if (home != NULL && *home != '\0') {
/* otherwise, return the default */
historyPath = sdscatprintf(sdsempty(), "%s/%s", home, REDIS_CLI_HISTFILE_DEFAULT);
}
}
return historyPath;
}
/*------------------------------------------------------------------------------
* Help functions
*--------------------------------------------------------------------------- */
@@ -493,7 +523,7 @@ static sds cliFormatReplyCSV(redisReply *r) {
out = sdscatrepr(out,r->str,r->len);
break;
case REDIS_REPLY_NIL:
out = sdscat(out,"NIL\n");
out = sdscat(out,"NIL");
break;
case REDIS_REPLY_ARRAY:
for (i = 0; i < r->elements; i++) {
@@ -524,7 +554,8 @@ static int cliReadReply(int output_raw_strings) {
}
if (config.interactive) {
/* Filter cases where we should reconnect */
if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
if (context->err == REDIS_ERR_IO &&
(errno == ECONNRESET || errno == EPIPE))
return REDIS_ERR;
if (context->err == REDIS_ERR_EOF)
return REDIS_ERR;
@@ -670,16 +701,17 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
return REDIS_OK;
}
/* Send the INFO command, reconnecting the link if needed. */
static redisReply *reconnectingInfo(void) {
redisContext *c = context;
/* Send a command reconnecting the link if needed. */
static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {
redisReply *reply = NULL;
int tries = 0;
va_list ap;
assert(!c->err);
while(reply == NULL) {
while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {
printf("Reconnecting (%d)...\r", ++tries);
printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
printf("Reconnecting... %d\r", ++tries);
fflush(stdout);
redisFree(c);
@@ -687,12 +719,15 @@ static redisReply *reconnectingInfo(void) {
usleep(1000000);
}
reply = redisCommand(c,"INFO");
va_start(ap,fmt);
reply = redisvCommand(c,fmt,ap);
va_end(ap);
if (c->err && !(c->err & (REDIS_ERR_IO | REDIS_ERR_EOF))) {
fprintf(stderr, "Error: %s\n", c->errstr);
exit(1);
} else if (tries > 0) {
printf("\n");
printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
}
}
@@ -740,9 +775,14 @@ static int parseOptions(int argc, char **argv) {
config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) {
config.latency_mode = 1;
} else if (!strcmp(argv[i],"--latency-dist")) {
config.latency_dist_mode = 1;
} else if (!strcmp(argv[i],"--latency-history")) {
config.latency_mode = 1;
config.latency_history = 1;
} else if (!strcmp(argv[i],"--lru-test") && !lastarg) {
config.lru_test_mode = 1;
config.lru_test_sample_size = strtoll(argv[++i],NULL,10);
} else if (!strcmp(argv[i],"--slave")) {
config.slave_mode = 1;
} else if (!strcmp(argv[i],"--stat")) {
@@ -828,9 +868,13 @@ static void usage(void) {
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
" --csv Output in CSV format.\n"
" --stat Print rolling stats about server: mem, clients, ...\n"
" --latency Enter a special mode continuously sampling latency.\n"
" --latency-history Like --latency but tracking latency changes over time.\n"
" Default time interval is 15 sec. Change it using -i.\n"
" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n"
" Default time interval is 1 sec. Change it using -i.\n"
" --lru-test <keys> Simulate a cache workload with an 80-20 distribution.\n"
" --slave Simulate a slave showing commands received from the master.\n"
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
" --pipe Transfer raw Redis protocol from stdin to server.\n"
@@ -888,10 +932,9 @@ static void repl(void) {
/* Only use history when stdin is a tty. */
if (isatty(fileno(stdin))) {
history = 1;
if (getenv("HOME") != NULL) {
historyfile = sdscatprintf(sdsempty(),"%s/.rediscli_history",getenv("HOME"));
historyfile = getHistoryPath();
if (historyfile != NULL) {
history = 1;
linenoiseHistoryLoad(historyfile);
}
}
@@ -1039,7 +1082,7 @@ static void latencyMode(void) {
if (!context) exit(1);
while(1) {
start = mstime();
reply = redisCommand(context,"PING");
reply = reconnectingRedisCommand(context,"PING");
if (reply == NULL) {
fprintf(stderr,"\nI/O error\n");
exit(1);
@@ -1069,6 +1112,155 @@ static void latencyMode(void) {
}
}
/*------------------------------------------------------------------------------
* Latency distribution mode -- requires 256 colors xterm
*--------------------------------------------------------------------------- */
#define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
#define LATENCY_DIST_MIN_GRAY 233 /* Less than that is too hard to see gray. */
#define LATENCY_DIST_MAX_GRAY 255
#define LATENCY_DIST_GRAYS (LATENCY_DIST_MAX_GRAY-LATENCY_DIST_MIN_GRAY+1)
/* Gray palette. */
int spectrum_palette_size = 24;
int spectrum_palette[] = {0, 233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
/* Structure to store samples distribution. */
struct distsamples {
long long max; /* Max latency to fit into this interval (usec). */
long long count; /* Number of samples in this interval. */
int character; /* Associated character in visualization. */
};
/* Helper function for latencyDistMode(). Performs the spectrum visualization
* of the collected samples targeting an xterm 256 terminal.
*
* Takes an array of distsamples structures, ordered from smaller to bigger
* 'max' value. Last sample max must be 0, to mean that it olds all the
* samples greater than the previous one, and is also the stop sentinel.
*
* "tot' is the total number of samples in the different buckets, so it
* is the SUM(samples[i].conut) for i to 0 up to the max sample.
*
* As a side effect the function sets all the buckets count to 0. */
void showLatencyDistSamples(struct distsamples *samples, long long tot) {
int j;
/* We convert samples into a index inside the palette
* proportional to the percentage a given bucket represents.
* This way intensity of the different parts of the spectrum
* don't change relative to the number of requests, which avoids to
* pollute the visualization with non-latency related info. */
printf("\033[38;5;0m"); /* Set foreground color to black. */
for (j = 0; ; j++) {
int coloridx =
ceil((float) samples[j].count / tot * (spectrum_palette_size-1));
int color = spectrum_palette[coloridx];
printf("\033[48;5;%dm%c", (int)color, samples[j].character);
samples[j].count = 0;
if (samples[j].max == 0) break; /* Last sample. */
}
printf("\033[0m\n");
fflush(stdout);
}
/* Show the legend: different buckets values and colors meaning, so
* that the spectrum is more easily readable. */
void showLatencyDistLegend(void) {
int j;
printf("---------------------------------------------\n");
printf(". - * # .01 .125 .25 .5 milliseconds\n");
printf("1,2,3,...,9 from 1 to 9 milliseconds\n");
printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n");
printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n");
printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n");
printf("From 0 to 100%%: ");
for (j = 0; j < spectrum_palette_size; j++) {
printf("\033[48;5;%dm ", spectrum_palette[j]);
}
printf("\033[0m\n");
printf("---------------------------------------------\n");
}
static void latencyDistMode(void) {
redisReply *reply;
long long start, latency, count = 0;
long long history_interval =
config.interval ? config.interval/1000 :
LATENCY_DIST_DEFAULT_INTERVAL;
long long history_start = ustime();
int j, outputs = 0;
struct distsamples samples[] = {
/* We use a mostly logarithmic scale, with certain linear intervals
* which are more interesting than others, like 1-10 milliseconds
* range. */
{10,0,'.'}, /* 0.01 ms */
{125,0,'-'}, /* 0.125 ms */
{250,0,'*'}, /* 0.25 ms */
{500,0,'#'}, /* 0.5 ms */
{1000,0,'1'}, /* 1 ms */
{2000,0,'2'}, /* 2 ms */
{3000,0,'3'}, /* 3 ms */
{4000,0,'4'}, /* 4 ms */
{5000,0,'5'}, /* 5 ms */
{6000,0,'6'}, /* 6 ms */
{7000,0,'7'}, /* 7 ms */
{8000,0,'8'}, /* 8 ms */
{9000,0,'9'}, /* 9 ms */
{10000,0,'A'}, /* 10 ms */
{20000,0,'B'}, /* 20 ms */
{30000,0,'C'}, /* 30 ms */
{40000,0,'D'}, /* 40 ms */
{50000,0,'E'}, /* 50 ms */
{100000,0,'F'}, /* 0.1 s */
{200000,0,'G'}, /* 0.2 s */
{300000,0,'H'}, /* 0.3 s */
{400000,0,'I'}, /* 0.4 s */
{500000,0,'J'}, /* 0.5 s */
{1000000,0,'K'}, /* 1 s */
{2000000,0,'L'}, /* 2 s */
{4000000,0,'M'}, /* 4 s */
{8000000,0,'N'}, /* 8 s */
{16000000,0,'O'}, /* 16 s */
{30000000,0,'P'}, /* 30 s */
{60000000,0,'Q'}, /* 1 minute */
{0,0,'?'}, /* > 1 minute */
};
if (!context) exit(1);
while(1) {
start = ustime();
reply = reconnectingRedisCommand(context,"PING");
if (reply == NULL) {
fprintf(stderr,"\nI/O error\n");
exit(1);
}
latency = ustime()-start;
freeReplyObject(reply);
count++;
/* Populate the relevant bucket. */
for (j = 0; ; j++) {
if (samples[j].max == 0 || latency <= samples[j].max) {
samples[j].count++;
break;
}
}
/* From time to time show the spectrum. */
if (count && (ustime()-history_start)/1000 > history_interval) {
if ((outputs++ % 20) == 0)
showLatencyDistLegend();
showLatencyDistSamples(samples,count);
history_start = ustime();
count = 0;
}
usleep(LATENCY_SAMPLE_RATE * 1000);
}
}
/*------------------------------------------------------------------------------
* Slave mode
*--------------------------------------------------------------------------- */
@@ -1189,6 +1381,7 @@ static void getRDB(void) {
* Bulk import (pipe) mode
*--------------------------------------------------------------------------- */
#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
static void pipeMode(void) {
int fd = context->fd;
long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
@@ -1265,6 +1458,8 @@ static void pipeMode(void) {
/* Handle the writable state: we can send protocol to the server. */
if (mask & AE_WRITABLE) {
ssize_t loop_nwritten = 0;
while(1) {
/* Transfer current buffer to server. */
if (obuf_len != 0) {
@@ -1281,6 +1476,7 @@ static void pipeMode(void) {
}
obuf_len -= nwritten;
obuf_pos += nwritten;
loop_nwritten += nwritten;
if (obuf_len != 0) break; /* Can't accept more data. */
}
/* If buffer is empty, load from stdin. */
@@ -1316,7 +1512,8 @@ static void pipeMode(void) {
obuf_pos = 0;
}
}
if (obuf_len == 0 && eof) break;
if ((obuf_len == 0 && eof) ||
loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) break;
}
}
@@ -1374,7 +1571,7 @@ static redisReply *sendScan(unsigned long long *it) {
assert(reply->element[1]->type == REDIS_REPLY_ARRAY);
/* Update iterator */
*it = atoi(reply->element[0]->str);
*it = strtoull(reply->element[0]->str, NULL, 10);
return reply;
}
@@ -1510,7 +1707,7 @@ static void findBigKeys(void) {
for(i=0;i<TYPE_NONE; i++) {
maxkeys[i] = sdsempty();
if(!maxkeys[i]) {
fprintf(stderr, "Failed to allocate memory for largest key names!");
fprintf(stderr, "Failed to allocate memory for largest key names!\n");
exit(1);
}
}
@@ -1688,7 +1885,7 @@ static void statMode(void) {
char buf[64];
int j;
reply = reconnectingInfo();
reply = reconnectingRedisCommand(context,"INFO");
if (reply->type == REDIS_REPLY_ERROR) {
printf("ERROR: %s\n", reply->str);
exit(1);
@@ -1794,6 +1991,94 @@ static void scanMode(void) {
exit(0);
}
/*------------------------------------------------------------------------------
* LRU test mode
*--------------------------------------------------------------------------- */
/* Return an integer from min to max (both inclusive) using a power-law
* distribution, depending on the value of alpha: the greater the alpha
* the more bias towards lower values.
*
* With alpha = 6.2 the output follows the 80-20 rule where 20% of
* the returned numbers will account for 80% of the frequency. */
long long powerLawRand(long long min, long long max, double alpha) {
double pl, r;
max += 1;
r = ((double)rand()) / RAND_MAX;
pl = pow(
((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),
(1.0/(alpha+1)));
return (max-1-(long long)pl)+min;
}
/* Generates a key name among a set of lru_test_sample_size keys, using
* an 80-20 distribution. */
void LRUTestGenKey(char *buf, size_t buflen) {
snprintf(buf, buflen, "lru:%lld\n",
powerLawRand(1, config.lru_test_sample_size, 6.2));
}
#define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */
#define LRU_CYCLE_PIPELINE_SIZE 250
static void LRUTestMode(void) {
redisReply *reply;
char key[128];
long long start_cycle;
int j;
srand(time(NULL)^getpid());
while(1) {
/* Perform cycles of 1 second with 50% writes and 50% reads.
* We use pipelining batching writes / reads N times per cycle in order
* to fill the target instance easily. */
start_cycle = mstime();
long long hits = 0, misses = 0;
while(mstime() - start_cycle < 1000) {
/* Write cycle. */
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
LRUTestGenKey(key,sizeof(key));
redisAppendCommand(context, "SET %s val",key);
}
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)
redisGetReply(context, (void**)&reply);
/* Read cycle. */
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
LRUTestGenKey(key,sizeof(key));
redisAppendCommand(context, "GET %s",key);
}
for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
if (redisGetReply(context, (void**)&reply) == REDIS_OK) {
switch(reply->type) {
case REDIS_REPLY_ERROR:
printf("%s\n", reply->str);
break;
case REDIS_REPLY_NIL:
misses++;
break;
default:
hits++;
break;
}
}
}
if (context->err) {
fprintf(stderr,"I/O error during LRU test\n");
exit(1);
}
}
/* Print stats. */
printf(
"%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
hits+misses,
hits, (double)hits/(hits+misses)*100,
misses, (double)misses/(hits+misses)*100);
}
exit(0);
}
/*------------------------------------------------------------------------------
* Intrisic latency mode.
*
@@ -1885,7 +2170,10 @@ int main(int argc, char **argv) {
config.monitor_mode = 0;
config.pubsub_mode = 0;
config.latency_mode = 0;
config.latency_dist_mode = 0;
config.latency_history = 0;
config.lru_test_mode = 0;
config.lru_test_sample_size = 0;
config.cluster_mode = 0;
config.slave_mode = 0;
config.getrdb_mode = 0;
@@ -1919,6 +2207,12 @@ int main(int argc, char **argv) {
latencyMode();
}
/* Latency distribution mode */
if (config.latency_dist_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
latencyDistMode();
}
/* Slave mode */
if (config.slave_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
@@ -1956,11 +2250,20 @@ int main(int argc, char **argv) {
scanMode();
}
/* LRU test mode */
if (config.lru_test_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
LRUTestMode();
}
/* Intrinsic latency mode */
if (config.intrinsic_latency_mode) intrinsicLatencyMode();
/* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) {
/* Ignore SIGPIPE in interactive mode to force a reconnect */
signal(SIGPIPE, SIG_IGN);
/* Note that in repl mode we don't abort on connection error.
* A new attempt will be performed for every command send. */
cliConnect(0);
+116 -53
View File
@@ -30,6 +30,7 @@
#include "redis.h"
#include "slowlog.h"
#include "bio.h"
#include "latency.h"
#include <time.h>
#include <signal.h>
@@ -243,7 +244,7 @@ struct redisCommand redisCommandTable[] = {
{"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0},
{"persist",persistCommand,2,"wF",0,NULL,1,1,1,0,0},
{"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},
{"role",roleCommand,1,"last",0,NULL,0,0,0,0,0},
{"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,"art",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
@@ -254,15 +255,15 @@ struct redisCommand redisCommandTable[] = {
{"pubsub",pubsubCommand,-2,"pltrR",0,NULL,0,0,0,0,0},
{"watch",watchCommand,-2,"rsF",0,NULL,1,-1,1,0,0},
{"unwatch",unwatchCommand,1,"rsF",0,NULL,0,0,0,0,0},
{"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
{"migrate",migrateCommand,6,"aw",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"ar",0,NULL,1,1,1,0,0},
{"restore",restoreCommand,4,"wm",0,NULL,1,1,1,0,0},
{"migrate",migrateCommand,6,"w",0,NULL,0,0,0,0,0},
{"dump",dumpCommand,2,"r",0,NULL,1,1,1,0,0},
{"object",objectCommand,3,"r",0,NULL,2,2,2,0,0},
{"client",clientCommand,-2,"ars",0,NULL,0,0,0,0,0},
{"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0},
{"eval",evalCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"evalsha",evalShaCommand,-3,"s",0,zunionInterGetKeys,0,0,0,0,0},
{"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
{"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0},
{"script",scriptCommand,-2,"rs",0,NULL,0,0,0,0,0},
{"time",timeCommand,1,"rRF",0,NULL,0,0,0,0,0},
{"bitop",bitopCommand,-4,"wm",0,NULL,2,-1,1,0,0},
{"bitcount",bitcountCommand,-2,"r",0,NULL,1,1,1,0,0},
@@ -270,7 +271,7 @@ struct redisCommand redisCommandTable[] = {
{"command",commandCommand,0,"rlt",0,NULL,0,0,0,0,0},
{"pfselftest",pfselftestCommand,1,"r",0,NULL,0,0,0,0,0},
{"pfadd",pfaddCommand,-2,"wmF",0,NULL,1,1,1,0,0},
{"pfcount",pfcountCommand,-2,"w",0,NULL,1,1,1,0,0},
{"pfcount",pfcountCommand,-2,"r",0,NULL,1,-1,1,0,0},
{"pfmerge",pfmergeCommand,-2,"wm",0,NULL,1,-1,1,0,0},
{"pfdebug",pfdebugCommand,-3,"w",0,NULL,0,0,0,0,0},
{"latency",latencyCommand,-2,"arslt",0,NULL,0,0,0,0,0}
@@ -387,7 +388,7 @@ void exitFromChild(int retcode) {
/*====================== Hash table type implementation ==================== */
/* This is a hash table type that uses the SDS dynamic strings library as
* keys and radis objects as values (objects can hold SDS strings,
* keys and redis objects as values (objects can hold SDS strings,
* lists, sets). */
void dictVanillaFree(void *privdata, void *val)
@@ -824,27 +825,30 @@ void updateLRUClock(void) {
/* Add a sample to the operations per second array of samples. */
void trackOperationsPerSecond(void) {
long long t = mstime() - server.ops_sec_last_sample_time;
long long ops = server.stat_numcommands - server.ops_sec_last_sample_ops;
void trackInstantaneousMetric(int metric, long long current_reading) {
long long t = mstime() - server.inst_metric[metric].last_sample_time;
long long ops = current_reading -
server.inst_metric[metric].last_sample_count;
long long ops_sec;
ops_sec = t > 0 ? (ops*1000/t) : 0;
server.ops_sec_samples[server.ops_sec_idx] = ops_sec;
server.ops_sec_idx = (server.ops_sec_idx+1) % REDIS_OPS_SEC_SAMPLES;
server.ops_sec_last_sample_time = mstime();
server.ops_sec_last_sample_ops = server.stat_numcommands;
server.inst_metric[metric].samples[server.inst_metric[metric].idx] =
ops_sec;
server.inst_metric[metric].idx++;
server.inst_metric[metric].idx %= REDIS_METRIC_SAMPLES;
server.inst_metric[metric].last_sample_time = mstime();
server.inst_metric[metric].last_sample_count = current_reading;
}
/* Return the mean of all the samples. */
long long getOperationsPerSecond(void) {
long long getInstantaneousMetric(int metric) {
int j;
long long sum = 0;
for (j = 0; j < REDIS_OPS_SEC_SAMPLES; j++)
sum += server.ops_sec_samples[j];
return sum / REDIS_OPS_SEC_SAMPLES;
for (j = 0; j < REDIS_METRIC_SAMPLES; j++)
sum += server.inst_metric[metric].samples[j];
return sum / REDIS_METRIC_SAMPLES;
}
/* Check for timeouts. Returns non-zero if the client was terminated */
@@ -1011,7 +1015,13 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Update the time cache. */
updateCachedTime();
run_with_period(100) trackOperationsPerSecond();
run_with_period(100) {
trackInstantaneousMetric(REDIS_METRIC_COMMAND,server.stat_numcommands);
trackInstantaneousMetric(REDIS_METRIC_NET_INPUT,
server.stat_net_input_bytes);
trackInstantaneousMetric(REDIS_METRIC_NET_OUTPUT,
server.stat_net_output_bytes);
}
/* We have just 22 bits per object for LRU information.
* So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
@@ -1394,6 +1404,8 @@ void initServerConfig(void) {
server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
server.repl_diskless_sync = REDIS_DEFAULT_REPL_DISKLESS_SYNC;
server.repl_diskless_sync_delay = REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
server.master_repl_offset = 0;
@@ -1465,33 +1477,33 @@ void adjustOpenFilesLimit(void) {
/* Set the max number of files if the current limit is not enough
* for our needs. */
if (oldlimit < maxfiles) {
rlim_t f;
rlim_t bestlimit;
int setrlimit_error = 0;
/* Try to set the file limit to match 'maxfiles' or at least
* to the higher value supported less than maxfiles. */
f = maxfiles;
while(f > oldlimit) {
bestlimit = maxfiles;
while(bestlimit > oldlimit) {
rlim_t decr_step = 16;
limit.rlim_cur = f;
limit.rlim_max = f;
limit.rlim_cur = bestlimit;
limit.rlim_max = bestlimit;
if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
setrlimit_error = errno;
/* We failed to set file limit to 'f'. Try with a
/* We failed to set file limit to 'bestlimit'. Try with a
* smaller limit decrementing by a few FDs per iteration. */
if (f < decr_step) break;
f -= decr_step;
if (bestlimit < decr_step) break;
bestlimit -= decr_step;
}
/* Assume that the limit we get initially is still valid if
* our last try was even lower. */
if (f < oldlimit) f = oldlimit;
if (bestlimit < oldlimit) bestlimit = oldlimit;
if (f != maxfiles) {
if (bestlimit < maxfiles) {
int old_maxclients = server.maxclients;
server.maxclients = f-REDIS_MIN_RESERVED_FDS;
server.maxclients = bestlimit-REDIS_MIN_RESERVED_FDS;
if (server.maxclients < 1) {
redisLog(REDIS_WARNING,"Your current 'ulimit -n' "
"of %llu is not enough for Redis to start. "
@@ -1512,7 +1524,7 @@ void adjustOpenFilesLimit(void) {
"maxclients has been reduced to %d to compensate for "
"low ulimit. "
"If you need higher maxclients increase 'ulimit -n'.",
(unsigned long long) oldlimit, server.maxclients);
(unsigned long long) bestlimit, server.maxclients);
} else {
redisLog(REDIS_NOTICE,"Increased maximum number of open files "
"to %llu (it was originally set to %llu).",
@@ -1523,6 +1535,23 @@ void adjustOpenFilesLimit(void) {
}
}
/* Check that server.tcp_backlog can be actually enforced in Linux according
* to the value of /proc/sys/net/core/somaxconn, or warn about it. */
void checkTcpBacklogSettings(void) {
#ifdef HAVE_PROC_SOMAXCONN
FILE *fp = fopen("/proc/sys/net/core/somaxconn","r");
char buf[1024];
if (!fp) return;
if (fgets(buf,sizeof(buf),fp) != NULL) {
int somaxconn = atoi(buf);
if (somaxconn > 0 && somaxconn < server.tcp_backlog) {
redisLog(REDIS_WARNING,"WARNING: The TCP backlog setting of %d cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of %d.", server.tcp_backlog, somaxconn);
}
}
fclose(fp);
#endif
}
/* Initialize a set of file descriptors to listen to the specified 'port'
* binding the addresses specified in the Redis server configuration.
*
@@ -1593,6 +1622,8 @@ int listenToPort(int port, int *fds, int *count) {
* to reset via CONFIG RESETSTAT. The function is also used in order to
* initialize these fields in initServer() at server startup. */
void resetServerStats(void) {
int j;
server.stat_numcommands = 0;
server.stat_numconnections = 0;
server.stat_expiredkeys = 0;
@@ -1605,10 +1636,16 @@ void resetServerStats(void) {
server.stat_sync_full = 0;
server.stat_sync_partial_ok = 0;
server.stat_sync_partial_err = 0;
memset(server.ops_sec_samples,0,sizeof(server.ops_sec_samples));
server.ops_sec_idx = 0;
server.ops_sec_last_sample_time = mstime();
server.ops_sec_last_sample_ops = 0;
for (j = 0; j < REDIS_METRIC_COUNT; j++) {
server.inst_metric[j].idx = 0;
server.inst_metric[j].last_sample_time = mstime();
server.inst_metric[j].last_sample_count = 0;
memset(server.inst_metric[j].samples,0,
sizeof(server.inst_metric[j].samples));
}
server.stat_net_input_bytes = 0;
server.stat_net_output_bytes = 0;
server.aof_delayed_fsync = 0;
}
void initServer(void) {
@@ -1677,6 +1714,7 @@ void initServer(void) {
server.cronloops = 0;
server.rdb_child_pid = -1;
server.aof_child_pid = -1;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_NONE;
aofRewriteBufferReset();
server.aof_buf = sdsempty();
server.lastsave = time(NULL); /* At startup we consider the DB saved. */
@@ -1885,7 +1923,7 @@ void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
}
/* It is possible to call the function forceCommandPropagation() inside a
* Redis command implementaiton in order to to force the propagation of a
* Redis command implementation in order to to force the propagation of a
* specific command execution into AOF / Replication. */
void forceCommandPropagation(redisClient *c, int flags) {
if (flags & REDIS_PROPAGATE_REPL) c->flags |= REDIS_FORCE_REPL;
@@ -1901,7 +1939,7 @@ void call(redisClient *c, int flags) {
* not generated from reading an AOF. */
if (listLength(server.monitors) &&
!server.loading &&
!(c->cmd->flags & REDIS_CMD_SKIP_MONITOR))
!(c->cmd->flags & (REDIS_CMD_SKIP_MONITOR|REDIS_CMD_ADMIN)))
{
replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
}
@@ -1914,6 +1952,7 @@ void call(redisClient *c, int flags) {
c->cmd->proc(c);
duration = ustime()-start;
dirty = server.dirty-dirty;
if (dirty < 0) dirty = 0;
/* When EVAL is called loading the AOF we don't want commands called
* from Lua to go into the slowlog or to populate statistics. */
@@ -2595,14 +2634,14 @@ sds genRedisInfoString(char *section) {
server.loading_loaded_bytes;
perc = ((double)server.loading_loaded_bytes /
server.loading_total_bytes) * 100;
(server.loading_total_bytes+1)) * 100;
elapsed = server.unixtime-server.loading_start_time;
elapsed = time(NULL)-server.loading_start_time;
if (elapsed == 0) {
eta = 1; /* A fake 1 second figure if we don't have
enough info */
} else {
eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes;
eta = (elapsed*remaining_bytes)/(server.loading_loaded_bytes+1);
}
info = sdscatprintf(info,
@@ -2628,6 +2667,10 @@ sds genRedisInfoString(char *section) {
"total_connections_received:%lld\r\n"
"total_commands_processed:%lld\r\n"
"instantaneous_ops_per_sec:%lld\r\n"
"total_net_input_bytes:%lld\r\n"
"total_net_output_bytes:%lld\r\n"
"instantaneous_input_kbps:%.2f\r\n"
"instantaneous_output_kbps:%.2f\r\n"
"rejected_connections:%lld\r\n"
"sync_full:%lld\r\n"
"sync_partial_ok:%lld\r\n"
@@ -2641,7 +2684,11 @@ sds genRedisInfoString(char *section) {
"latest_fork_usec:%lld\r\n",
server.stat_numconnections,
server.stat_numcommands,
getOperationsPerSecond(),
getInstantaneousMetric(REDIS_METRIC_COMMAND),
server.stat_net_input_bytes,
server.stat_net_output_bytes,
(float)getInstantaneousMetric(REDIS_METRIC_NET_INPUT)/1024,
(float)getInstantaneousMetric(REDIS_METRIC_NET_OUTPUT)/1024,
server.stat_rejected_conn,
server.stat_sync_full,
server.stat_sync_partial_ok,
@@ -3031,10 +3078,13 @@ int linuxOvercommitMemoryValue(void) {
return atoi(buf);
}
void linuxOvercommitMemoryWarning(void) {
void linuxMemoryWarnings(void) {
if (linuxOvercommitMemoryValue() == 0) {
redisLog(REDIS_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
}
if (THPIsEnabled()) {
redisLog(REDIS_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.");
}
}
#endif /* __linux__ */
@@ -3099,15 +3149,27 @@ void redisAsciiArt(void) {
if (server.sentinel_mode) mode = "sentinel";
snprintf(buf,1024*16,ascii_logo,
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
(sizeof(long) == 8) ? "64" : "32",
mode, server.port,
(long) getpid()
);
redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);
if (server.syslog_enabled) {
redisLog(REDIS_NOTICE,
"Redis %s (%s/%d) %s bit, %s mode, port %d, pid %ld ready to start.",
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
(sizeof(long) == 8) ? "64" : "32",
mode, server.port,
(long) getpid()
);
} else {
snprintf(buf,1024*16,ascii_logo,
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
(sizeof(long) == 8) ? "64" : "32",
mode, server.port,
(long) getpid()
);
redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);
}
zfree(buf);
}
@@ -3300,8 +3362,9 @@ int main(int argc, char **argv) {
/* Things not needed when running in Sentinel mode. */
redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
linuxOvercommitMemoryWarning();
linuxMemoryWarnings();
#endif
checkTcpBacklogSettings();
loadDataFromDisk();
if (server.ipfd_count > 0)
redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
+65 -21
View File
@@ -32,10 +32,7 @@
#include "fmacros.h"
#include "config.h"
#if defined(__sun)
#include "solarisfixes.h"
#endif
#include <stdio.h>
#include <stdlib.h>
@@ -96,7 +93,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_REPL_TIMEOUT 60
#define REDIS_REPL_PING_SLAVE_PERIOD 10
#define REDIS_RUN_ID_SIZE 40
#define REDIS_OPS_SEC_SAMPLES 16
#define REDIS_EOF_MARK_SIZE 40
#define REDIS_DEFAULT_REPL_BACKLOG_SIZE (1024*1024) /* 1mb */
#define REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT (60*60) /* 1 hour */
#define REDIS_REPL_BACKLOG_MIN_SIZE (1024*16) /* 16k */
@@ -113,6 +110,8 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_DEFAULT_RDB_COMPRESSION 1
#define REDIS_DEFAULT_RDB_CHECKSUM 1
#define REDIS_DEFAULT_RDB_FILENAME "dump.rdb"
#define REDIS_DEFAULT_REPL_DISKLESS_SYNC 0
#define REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define REDIS_DEFAULT_SLAVE_READ_ONLY 1
#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
@@ -137,6 +136,13 @@ typedef long long mstime_t; /* millisecond time type. */
#define ACTIVE_EXPIRE_CYCLE_SLOW 0
#define ACTIVE_EXPIRE_CYCLE_FAST 1
/* Instantaneous metrics tracking. */
#define REDIS_METRIC_SAMPLES 16 /* Number of samples per metric. */
#define REDIS_METRIC_COMMAND 0 /* Number of commands executed. */
#define REDIS_METRIC_NET_INPUT 1 /* Bytes read to network .*/
#define REDIS_METRIC_NET_OUTPUT 2 /* Bytes written to network. */
#define REDIS_METRIC_COUNT 3
/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
#define REDIS_IOBUF_LEN (1024*16) /* Generic I/O buffer size */
@@ -254,22 +260,37 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */
#define REDIS_CLIENT_TYPE_COUNT 3
/* Slave replication state - from the point of view of the slave. */
/* Slave replication state. Used in server.repl_state for slaves to remember
* what to do next. */
#define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_CONNECT 1 /* Must connect to master */
#define REDIS_REPL_CONNECTING 2 /* Connecting to master */
/* --- Handshake states, must be ordered --- */
#define REDIS_REPL_RECEIVE_PONG 3 /* Wait for PING reply */
#define REDIS_REPL_TRANSFER 4 /* Receiving .rdb from master */
#define REDIS_REPL_CONNECTED 5 /* Connected to master */
#define REDIS_REPL_SEND_AUTH 4 /* Send AUTH to master */
#define REDIS_REPL_RECEIVE_AUTH 5 /* Wait for AUTH reply */
#define REDIS_REPL_SEND_PORT 6 /* Send REPLCONF listening-port */
#define REDIS_REPL_RECEIVE_PORT 7 /* Wait for REPLCONF reply */
#define REDIS_REPL_SEND_CAPA 8 /* Send REPLCONF capa */
#define REDIS_REPL_RECEIVE_CAPA 9 /* Wait for REPLCONF reply */
#define REDIS_REPL_SEND_PSYNC 10 /* Send PSYNC */
#define REDIS_REPL_RECEIVE_PSYNC 11 /* Wait for PSYNC reply */
/* --- End of handshake states --- */
#define REDIS_REPL_TRANSFER 12 /* Receiving .rdb from master */
#define REDIS_REPL_CONNECTED 13 /* Connected to master */
/* Slave replication state - from the point of view of the master.
/* State of slaves from the POV of the master. Used in client->replstate.
* In SEND_BULK and ONLINE state the slave receives new updates
* in its output queue. In the WAIT_BGSAVE state instead the server is waiting
* to start the next background saving in order to send updates to it. */
#define REDIS_REPL_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */
#define REDIS_REPL_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */
#define REDIS_REPL_SEND_BULK 8 /* Sending RDB file to slave. */
#define REDIS_REPL_ONLINE 9 /* RDB file transmitted, sending just updates. */
#define REDIS_REPL_WAIT_BGSAVE_START 14 /* We need to produce a new RDB file. */
#define REDIS_REPL_WAIT_BGSAVE_END 15 /* Waiting RDB file creation to finish. */
#define REDIS_REPL_SEND_BULK 16 /* Sending RDB file to slave. */
#define REDIS_REPL_ONLINE 17 /* RDB file transmitted, sending just updates. */
/* Slave capabilities. */
#define SLAVE_CAPA_NONE 0
#define SLAVE_CAPA_EOF (1<<0) /* Can parse the RDB EOF streaming format. */
/* Synchronous read timeout - slave side */
#define REDIS_REPL_SYNCIO_TIMEOUT 5
@@ -354,6 +375,11 @@ typedef long long mstime_t; /* millisecond time type. */
#define REDIS_PROPAGATE_AOF 1
#define REDIS_PROPAGATE_REPL 2
/* RDB active child save type. */
#define REDIS_RDB_CHILD_TYPE_NONE 0
#define REDIS_RDB_CHILD_TYPE_DISK 1 /* RDB is written to disk. */
#define REDIS_RDB_CHILD_TYPE_SOCKET 2 /* RDB is written to slave socket. */
/* Keyspace changes notification classes. Every class is associated with a
* character for configuration purposes. */
#define REDIS_NOTIFY_KEYSPACE (1<<0) /* K */
@@ -460,7 +486,7 @@ typedef struct readyList {
} readyList;
/* With multiplexing we need to take per-client state.
* Clients are taken in a liked list. */
* Clients are taken in a linked list. */
typedef struct redisClient {
uint64_t id; /* Client incremental unique ID. */
int fd;
@@ -485,14 +511,20 @@ typedef struct redisClient {
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */
int authenticated; /* when requirepass is non-NULL */
int replstate; /* replication state if this is a slave */
int repl_put_online_on_ack; /* Install slave write handler on ACK. */
int repldbfd; /* replication DB file descriptor */
off_t repldboff; /* replication DB file offset */
off_t repldbsize; /* replication DB file size */
sds replpreamble; /* replication DB preamble. */
long long reploff; /* replication offset if this is our master */
long long repl_ack_off; /* replication ack offset, if this is a slave */
long long repl_ack_time;/* replication ack time, if this is a slave */
long long psync_initial_offset; /* FULLRESYNC reply offset other slaves
copying this slave output buffer
should use. */
char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
multiState mstate; /* MULTI/EXEC state */
blockingState bpop; /* blocking state */
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
@@ -651,12 +683,16 @@ struct redisServer {
long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */
size_t resident_set_size; /* RSS sampled in serverCron(). */
/* The following two are used to track instantaneous "load" in terms
* of operations per second. */
long long ops_sec_last_sample_time; /* Timestamp of last sample (in ms) */
long long ops_sec_last_sample_ops; /* numcommands in last sample */
long long ops_sec_samples[REDIS_OPS_SEC_SAMPLES];
int ops_sec_idx;
long long stat_net_input_bytes; /* Bytes read from network. */
long long stat_net_output_bytes; /* Bytes written to network. */
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */
struct {
long long last_sample_time; /* Timestamp of last sample in ms */
long long last_sample_count;/* Count in last sample */
long long samples[REDIS_METRIC_SAMPLES];
int idx;
} inst_metric[REDIS_METRIC_COUNT];
/* Configuration */
int verbosity; /* Loglevel in redis.conf */
int maxidletime; /* Client timeout in seconds */
@@ -704,8 +740,11 @@ struct redisServer {
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
time_t rdb_save_time_last; /* Time used by last RDB save run. */
time_t rdb_save_time_start; /* Current RDB save start time. */
int rdb_child_type; /* Type of save by active child. */
int lastbgsave_status; /* REDIS_OK or REDIS_ERR */
int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */
int rdb_pipe_write_result_to_parent; /* RDB pipes used to return the state */
int rdb_pipe_read_result_from_child; /* of each slave in diskless SYNC. */
/* Propagation of commands in AOF / replication */
redisOpArray also_propagate; /* Additional command to propagate. */
/* Logging */
@@ -730,6 +769,8 @@ struct redisServer {
int repl_min_slaves_to_write; /* Min number of slaves to write. */
int repl_min_slaves_max_lag; /* Max lag of <count> slaves to write. */
int repl_good_slaves_count; /* Number of slaves with lag <= max_lag. */
int repl_diskless_sync; /* Send RDB to slaves sockets directly. */
int repl_diskless_sync_delay; /* Delay to start a diskless repl BGSAVE. */
/* Replication (slave) */
char *masterauth; /* AUTH with this password with master */
char *masterhost; /* Hostname of master */
@@ -1022,7 +1063,7 @@ robj *tryObjectEncoding(robj *o);
robj *getDecodedObject(robj *o);
size_t stringObjectLen(robj *o);
robj *createStringObjectFromLongLong(long long value);
robj *createStringObjectFromLongDouble(long double value);
robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
robj *createListObject(void);
robj *createZiplistObject(void);
robj *createSetObject(void);
@@ -1051,7 +1092,7 @@ ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout);
/* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc);
void updateSlavesWaitingBgsave(int bgsaveerr);
void updateSlavesWaitingBgsave(int bgsaveerr, int type);
void replicationCron(void);
void replicationHandleMasterDisconnection(void);
void replicationCacheMaster(redisClient *c);
@@ -1064,6 +1105,9 @@ int replicationScriptCacheExists(sds sha1);
void replicationSetMaster(char *ip, int port);
void replicationUnsetMaster(void);
void replicationSendNewlineToMaster(void);
char *replicationGetSlaveName(redisClient *c);
long long getPsyncInitialOffset(void);
int replicationSetupSlaveForFullResync(redisClient *slave, long long offset);
/* Generic persistence functions */
void startLoading(FILE *fp);
+736 -237
View File
File diff suppressed because it is too large Load Diff
+162 -15
View File
@@ -55,6 +55,8 @@
#include "config.h"
#include "redis.h"
/* ------------------------- Buffer I/O implementation ----------------------- */
/* Returns 1 or 0 for success/failure. */
static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {
r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);
@@ -76,6 +78,33 @@ static off_t rioBufferTell(rio *r) {
return r->io.buffer.pos;
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
static int rioBufferFlush(rio *r) {
REDIS_NOTUSED(r);
return 1; /* Nothing to do, our write just appends to the buffer. */
}
static const rio rioBufferIO = {
rioBufferRead,
rioBufferWrite,
rioBufferTell,
rioBufferFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
void rioInitWithBuffer(rio *r, sds s) {
*r = rioBufferIO;
r->io.buffer.ptr = s;
r->io.buffer.pos = 0;
}
/* --------------------- Stdio file pointer implementation ------------------- */
/* Returns 1 or 0 for success/failure. */
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
size_t retval;
@@ -103,21 +132,17 @@ static off_t rioFileTell(rio *r) {
return ftello(r->io.file.fp);
}
static const rio rioBufferIO = {
rioBufferRead,
rioBufferWrite,
rioBufferTell,
NULL, /* update_checksum */
0, /* current checksum */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
static int rioFileFlush(rio *r) {
return (fflush(r->io.file.fp) == 0) ? 1 : 0;
}
static const rio rioFileIO = {
rioFileRead,
rioFileWrite,
rioFileTell,
rioFileFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* bytes read or written */
@@ -132,12 +157,133 @@ void rioInitWithFile(rio *r, FILE *fp) {
r->io.file.autosync = 0;
}
void rioInitWithBuffer(rio *r, sds s) {
*r = rioBufferIO;
r->io.buffer.ptr = s;
r->io.buffer.pos = 0;
/* ------------------- File descriptors set implementation ------------------- */
/* Returns 1 or 0 for success/failure.
* The function returns success as long as we are able to correctly write
* to at least one file descriptor.
*
* When buf is NULL adn len is 0, the function performs a flush operation
* if there is some pending buffer, so this function is also used in order
* to implement rioFdsetFlush(). */
static size_t rioFdsetWrite(rio *r, const void *buf, size_t len) {
ssize_t retval;
int j;
unsigned char *p = (unsigned char*) buf;
int doflush = (buf == NULL && len == 0);
/* To start we always append to our buffer. If it gets larger than
* a given size, we actually write to the sockets. */
if (len) {
r->io.fdset.buf = sdscatlen(r->io.fdset.buf,buf,len);
len = 0; /* Prevent entering the while belove if we don't flush. */
if (sdslen(r->io.fdset.buf) > REDIS_IOBUF_LEN) doflush = 1;
}
if (doflush) {
p = (unsigned char*) r->io.fdset.buf;
len = sdslen(r->io.fdset.buf);
}
/* Write in little chunchs so that when there are big writes we
* parallelize while the kernel is sending data in background to
* the TCP socket. */
while(len) {
size_t count = len < 1024 ? len : 1024;
int broken = 0;
for (j = 0; j < r->io.fdset.numfds; j++) {
if (r->io.fdset.state[j] != 0) {
/* Skip FDs alraedy in error. */
broken++;
continue;
}
/* Make sure to write 'count' bytes to the socket regardless
* of short writes. */
size_t nwritten = 0;
while(nwritten != count) {
retval = write(r->io.fdset.fds[j],p+nwritten,count-nwritten);
if (retval <= 0) {
/* With blocking sockets, which is the sole user of this
* rio target, EWOULDBLOCK is returned only because of
* the SO_SNDTIMEO socket option, so we translate the error
* into one more recognizable by the user. */
if (retval == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT;
break;
}
nwritten += retval;
}
if (nwritten != count) {
/* Mark this FD as broken. */
r->io.fdset.state[j] = errno;
if (r->io.fdset.state[j] == 0) r->io.fdset.state[j] = EIO;
}
}
if (broken == r->io.fdset.numfds) return 0; /* All the FDs in error. */
p += count;
len -= count;
r->io.fdset.pos += count;
}
if (doflush) sdsclear(r->io.fdset.buf);
return 1;
}
/* Returns 1 or 0 for success/failure. */
static size_t rioFdsetRead(rio *r, void *buf, size_t len) {
REDIS_NOTUSED(r);
REDIS_NOTUSED(buf);
REDIS_NOTUSED(len);
return 0; /* Error, this target does not support reading. */
}
/* Returns read/write position in file. */
static off_t rioFdsetTell(rio *r) {
return r->io.fdset.pos;
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
static int rioFdsetFlush(rio *r) {
/* Our flush is implemented by the write method, that recognizes a
* buffer set to NULL with a count of zero as a flush request. */
return rioFdsetWrite(r,NULL,0);
}
static const rio rioFdsetIO = {
rioFdsetRead,
rioFdsetWrite,
rioFdsetTell,
rioFdsetFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
void rioInitWithFdset(rio *r, int *fds, int numfds) {
int j;
*r = rioFdsetIO;
r->io.fdset.fds = zmalloc(sizeof(int)*numfds);
r->io.fdset.state = zmalloc(sizeof(int)*numfds);
memcpy(r->io.fdset.fds,fds,sizeof(int)*numfds);
for (j = 0; j < numfds; j++) r->io.fdset.state[j] = 0;
r->io.fdset.numfds = numfds;
r->io.fdset.pos = 0;
r->io.fdset.buf = sdsempty();
}
void rioFreeFdset(rio *r) {
zfree(r->io.fdset.fds);
zfree(r->io.fdset.state);
sdsfree(r->io.fdset.buf);
}
/* ---------------------------- Generic functions ---------------------------- */
/* This function can be installed both in memory and file streams when checksum
* computation is needed. */
void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {
@@ -157,7 +303,8 @@ void rioSetAutoSync(rio *r, off_t bytes) {
r->io.file.autosync = bytes;
}
/* ------------------------------ Higher level interface ---------------------------
/* --------------------------- Higher level interface --------------------------
*
* The following higher level functions use lower level rio.c functions to help
* generating the Redis protocol for the Append Only File. */
+16
View File
@@ -43,6 +43,7 @@ struct _rio {
size_t (*read)(struct _rio *, void *buf, size_t len);
size_t (*write)(struct _rio *, const void *buf, size_t len);
off_t (*tell)(struct _rio *);
int (*flush)(struct _rio *);
/* The update_cksum method if not NULL is used to compute the checksum of
* all the data that was read or written so far. The method should be
* designed so that can be called with the current checksum, and the buf
@@ -61,15 +62,25 @@ struct _rio {
/* Backend-specific vars. */
union {
/* In-memory buffer target. */
struct {
sds ptr;
off_t pos;
} buffer;
/* Stdio file pointer target. */
struct {
FILE *fp;
off_t buffered; /* Bytes written since last fsync. */
off_t autosync; /* fsync after 'autosync' bytes written. */
} file;
/* Multiple FDs target (used to write to N sockets). */
struct {
int *fds; /* File descriptors. */
int *state; /* Error state of each fd. 0 (if ok) or errno. */
int numfds;
off_t pos;
sds buf;
} fdset;
} io;
};
@@ -109,8 +120,13 @@ static inline off_t rioTell(rio *r) {
return r->tell(r);
}
static inline int rioFlush(rio *r) {
return r->flush(r);
}
void rioInitWithFile(rio *r, FILE *fp);
void rioInitWithBuffer(rio *r, sds s);
void rioInitWithFdset(rio *r, int *fds, int numfds);
size_t rioWriteBulkCount(rio *r, char prefix, int count);
size_t rioWriteBulkString(rio *r, const char *buf, size_t len);
+31 -7
View File
@@ -213,11 +213,27 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
static int argv_size = 0;
static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE];
static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
static int inuse = 0; /* Recursive calls detection. */
/* By using Lua debug hooks it is possible to trigger a recursive call
* to luaRedisGenericCommand(), which normally should never happen.
* To make this function reentrant is futile and makes it slower, but
* we should at least detect such a misuse, and abort. */
if (inuse) {
char *recursion_warning =
"luaRedisGenericCommand() recursive call detected. "
"Are you doing funny stuff with Lua debug hooks?";
redisLog(REDIS_WARNING,"%s",recursion_warning);
luaPushError(lua,recursion_warning);
return 1;
}
inuse++;
/* Require at least one argument */
if (argc == 0) {
luaPushError(lua,
"Please specify at least one argument for redis.call()");
inuse--;
return 1;
}
@@ -272,6 +288,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
luaPushError(lua,
"Lua redis() command arguments must be strings or integers");
inuse--;
return 1;
}
@@ -408,8 +425,10 @@ cleanup:
* return the plain error. */
lua_pushstring(lua,"err");
lua_gettable(lua,-2);
inuse--;
return lua_error(lua);
}
inuse--;
return 1;
}
@@ -536,6 +555,7 @@ void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
LUALIB_API int (luaopen_cjson) (lua_State *L);
LUALIB_API int (luaopen_struct) (lua_State *L);
LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
LUALIB_API int (luaopen_bit) (lua_State *L);
void luaLoadLibraries(lua_State *lua) {
luaLoadLib(lua, "", luaopen_base);
@@ -546,6 +566,7 @@ void luaLoadLibraries(lua_State *lua) {
luaLoadLib(lua, "cjson", luaopen_cjson);
luaLoadLib(lua, "struct", luaopen_struct);
luaLoadLib(lua, "cmsgpack", luaopen_cmsgpack);
luaLoadLib(lua, "bit", luaopen_bit);
#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);
@@ -572,11 +593,12 @@ void scriptingEnableGlobalsProtection(lua_State *lua) {
/* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
* Modified to be adapted to Redis. */
s[j++]="local dbg=debug\n";
s[j++]="local mt = {}\n";
s[j++]="setmetatable(_G, mt)\n";
s[j++]="mt.__newindex = function (t, n, v)\n";
s[j++]=" if debug.getinfo(2) then\n";
s[j++]=" local w = debug.getinfo(2, \"S\").what\n";
s[j++]=" if dbg.getinfo(2) then\n";
s[j++]=" local w = dbg.getinfo(2, \"S\").what\n";
s[j++]=" if w ~= \"main\" and w ~= \"C\" then\n";
s[j++]=" error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
s[j++]=" end\n";
@@ -584,11 +606,12 @@ void scriptingEnableGlobalsProtection(lua_State *lua) {
s[j++]=" rawset(t, n, v)\n";
s[j++]="end\n";
s[j++]="mt.__index = function (t, n)\n";
s[j++]=" if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n";
s[j++]=" if dbg.getinfo(2) and dbg.getinfo(2, \"S\").what ~= \"C\" then\n";
s[j++]=" error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
s[j++]=" end\n";
s[j++]=" return rawget(t, n)\n";
s[j++]="end\n";
s[j++]="debug = nil\n";
s[j++]=NULL;
for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
@@ -692,10 +715,11 @@ void scriptingInit(void) {
* information about the caller, that's what makes sense from the point
* of view of the user debugging a script. */
{
char *errh_func = "function __redis__err__handler(err)\n"
" local i = debug.getinfo(2,'nSl')\n"
char *errh_func = "local dbg = debug\n"
"function __redis__err__handler(err)\n"
" local i = dbg.getinfo(2,'nSl')\n"
" if i and i.what == 'C' then\n"
" i = debug.getinfo(3,'nSl')\n"
" i = dbg.getinfo(3,'nSl')\n"
" end\n"
" if i then\n"
" return i.source .. ':' .. i.currentline .. ': ' .. err\n"
@@ -716,7 +740,7 @@ void scriptingInit(void) {
server.lua_client->flags |= REDIS_LUA_CLIENT;
}
/* Lua beginners ofter don't use "local", this is likely to introduce
/* Lua beginners often don't use "local", this is likely to introduce
* subtle bugs in their code. To prevent problems we protect accesses
* to global variables. */
scriptingEnableGlobalsProtection(lua);
+13 -10
View File
@@ -43,7 +43,7 @@
* The string is always null-termined (all the sds strings are, always) so
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3");
* mystring = sdsnewlen("abc",3);
*
* You can print the string with printf() as there is an implicit \0 at the
* end of the string. However the string is binary safe and can contain
@@ -71,7 +71,7 @@ sds sdsempty(void) {
return sdsnewlen("",0);
}
/* Create a new sds string starting from a null termined C string. */
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
@@ -109,7 +109,7 @@ void sdsupdatelen(sds s) {
sh->len = reallen;
}
/* Modify an sds string on-place to make it empty (zero length).
/* Modify an sds string in-place to make it empty (zero length).
* However all the existing buffer is not discarded but set as free space
* so that next append operations will not require allocations up to the
* number of bytes previously available. */
@@ -295,7 +295,7 @@ sds sdscpy(sds s, const char *t) {
* conversion. 's' must point to a string with room for at least
* SDS_LLSTR_SIZE bytes.
*
* The function returns the lenght of the null-terminated string
* The function returns the length of the null-terminated string
* representation stored at 's'. */
#define SDS_LLSTR_SIZE 21
int sdsll2str(char *s, long long value) {
@@ -369,7 +369,7 @@ sds sdsfromlonglong(long long value) {
return sdsnewlen(buf,len);
}
/* Like sdscatpritf() but gets va_list instead of being variadic. */
/* Like sdscatprintf() but gets va_list instead of being variadic. */
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
char staticbuf[1024], *buf = staticbuf, *t;
@@ -390,7 +390,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
va_end(ap);
va_end(cpy);
if (buf[buflen-2] != '\0') {
if (buf != staticbuf) zfree(buf);
buflen *= 2;
@@ -415,7 +415,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
*
* Example:
*
* s = sdsempty("Sum is: ");
* s = sdsnew("Sum is: ");
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
*
* Often you need to create a string from scratch with the printf-alike
@@ -557,7 +557,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
* Example:
*
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
* s = sdstrim(s,"A. :");
* s = sdstrim(s,"Aa. :");
* printf("%s\n", s);
*
* Output will be just "Hello World".
@@ -643,8 +643,8 @@ void sdstoupper(sds s) {
*
* Return value:
*
* 1 if s1 > s2.
* -1 if s1 < s2.
* positive if s1 > s2.
* negative if s1 < s2.
* 0 if s1 and s2 are exactly the same binary string.
*
* If two strings share exactly the same prefix, but one of the two has
@@ -1083,6 +1083,7 @@ int main(void) {
int oldfree;
sdsfree(x);
sdsfree(y);
x = sdsnew("0");
sh = (void*) (x-(sizeof(struct sdshdr)));
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
@@ -1095,6 +1096,8 @@ int main(void) {
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
test_cond("sdsIncrLen() -- len", sh->len == 2);
test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
sdsfree(x);
}
}
test_report()
+67 -8
View File
@@ -922,6 +922,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
else if (flags & SRI_SENTINEL) table = master->sentinels;
sdsname = sdsnew(name);
if (dictFind(table,sdsname)) {
releaseSentinelAddr(addr);
sdsfree(sdsname);
errno = EBUSY;
return NULL;
@@ -1155,7 +1156,9 @@ void sentinelDelFlagsToDictOfRedisInstances(dict *instances, int flags) {
* 1) Remove all slaves.
* 2) Remove all sentinels.
* 3) Remove most of the flags resulting from runtime operations.
* 4) Reset timers to their default value.
* 4) Reset timers to their default value. For example after a reset it will be
* possible to failover again the same master ASAP, without waiting the
* failover timeout delay.
* 5) In the process of doing this undo the failover if in progress.
* 6) Disconnect the connections with the master (will reconnect automatically).
*/
@@ -1178,7 +1181,7 @@ void sentinelResetMaster(sentinelRedisInstance *ri, int flags) {
}
ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;
ri->failover_state_change_time = 0;
ri->failover_start_time = 0;
ri->failover_start_time = 0; /* We can failover again ASAP. */
ri->promoted_slave = NULL;
sdsfree(ri->runid);
sdsfree(ri->slave_master_host);
@@ -1268,10 +1271,7 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip,
slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip,
slaves[j]->port, master->quorum, master);
releaseSentinelAddr(slaves[j]);
if (slave) {
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
if (slave) sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
}
zfree(slaves);
@@ -1545,7 +1545,7 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
slave_addr = master->addr;
line = sdscatprintf(sdsempty(),
"sentinel known-slave %s %s %d",
master->name, ri->addr->ip, ri->addr->port);
master->name, slave_addr->ip, slave_addr->port);
rewriteConfigRewriteLine(state,"sentinel",line,1);
}
dictReleaseIterator(di2);
@@ -1843,6 +1843,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
atoi(port), ri->quorum, ri)) != NULL)
{
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
}
}
@@ -2106,7 +2107,7 @@ void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privd
* or sent directly to this sentinel via the (fake) PUBLISH command of Sentinel.
*
* If the master name specified in the message is not known, the message is
* discareded. */
* discarded. */
void sentinelProcessHelloMessage(char *hello, int hello_len) {
/* Format is composed of 8 tokens:
* 0=ip,1=port,2=runid,3=current_epoch,4=master_name,
@@ -2614,6 +2615,31 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,
return ri;
}
#define SENTINEL_ISQR_OK 0
#define SENTINEL_ISQR_NOQUORUM (1<<0)
#define SENTINEL_ISQR_NOAUTH (1<<1)
int sentinelIsQuorumReachable(sentinelRedisInstance *master, int *usableptr) {
dictIterator *di;
dictEntry *de;
int usable = 1; /* Number of usable Sentinels. Init to 1 to count myself. */
int result = SENTINEL_ISQR_OK;
int voters = dictSize(master->sentinels)+1; /* Known Sentinels + myself. */
di = dictGetIterator(master->sentinels);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
if (ri->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;
usable++;
}
dictReleaseIterator(di);
if (usable < (int)master->quorum) result |= SENTINEL_ISQR_NOQUORUM;
if (usable < voters/2+1) result |= SENTINEL_ISQR_NOAUTH;
if (usableptr) *usableptr = usable;
return result;
}
void sentinelCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"masters")) {
/* SENTINEL MASTERS */
@@ -2764,16 +2790,49 @@ void sentinelCommand(redisClient *c) {
sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum);
addReply(c,shared.ok);
}
} else if (!strcasecmp(c->argv[1]->ptr,"flushconfig")) {
if (c->argc != 2) goto numargserr;
sentinelFlushConfig();
addReply(c,shared.ok);
return;
} else if (!strcasecmp(c->argv[1]->ptr,"remove")) {
/* SENTINEL REMOVE <name> */
sentinelRedisInstance *ri;
if (c->argc != 3) goto numargserr;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
sentinelEvent(REDIS_WARNING,"-monitor",ri,"%@");
dictDelete(sentinel.masters,c->argv[2]->ptr);
sentinelFlushConfig();
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"ckquorum")) {
/* SENTINEL CKQUORUM <name> */
sentinelRedisInstance *ri;
int usable;
if (c->argc != 3) goto numargserr;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
int result = sentinelIsQuorumReachable(ri,&usable);
if (result == SENTINEL_ISQR_OK) {
addReplySds(c, sdscatfmt(sdsempty(),
"+OK %i usable Sentinels. Quorum and failover authorization "
"can be reached\r\n",usable));
} else {
sds e = sdscatfmt(sdsempty(),
"-NOQUORUM %i usable Sentinels. ",usable);
if (result & SENTINEL_ISQR_NOQUORUM)
e = sdscat(e,"Not enough available Sentinels to reach the"
" specified quorum for this master");
if (result & SENTINEL_ISQR_NOAUTH) {
if (result & SENTINEL_ISQR_NOQUORUM) e = sdscat(e,". ");
e = sdscat(e, "Not enough available Sentinels to reach the"
" majority and authorize a failover");
}
e = sdscat(e,"\r\n");
addReplySds(c,e);
}
} else if (!strcasecmp(c->argv[1]->ptr,"set")) {
if (c->argc < 3 || c->argc % 2 == 0) goto numargserr;
sentinelSetCommand(c);
+7 -9
View File
@@ -23,10 +23,8 @@ A million repetitions of "a"
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* for u_int*_t */
#if defined(__sun)
#include <stdint.h>
#include "solarisfixes.h"
#endif
#include "sha1.h"
#include "config.h"
@@ -55,12 +53,12 @@ A million repetitions of "a"
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
u_int32_t a, b, c, d, e;
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
@@ -130,9 +128,9 @@ void SHA1Init(SHA1_CTX* context)
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
u_int32_t i, j;
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
@@ -170,7 +168,7 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
for (i = 0; i < 2; i++)
{
u_int32_t t = context->count[i];
uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
+4 -4
View File
@@ -6,12 +6,12 @@ By Steve Reid <steve@edmweb.com>
*/
typedef struct {
u_int32_t state[5];
u_int32_t count[2];
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+4
View File
@@ -28,6 +28,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(__sun)
#if defined(__GNUC__)
#include <math.h>
#undef isnan
@@ -48,3 +50,5 @@
#define u_int uint
#define u_int32_t uint32_t
#endif /* __GNUC__ */
#endif /* __sun */
+8 -9
View File
@@ -264,16 +264,15 @@ void sortCommand(redisClient *c) {
j++;
}
/* For the STORE option, or when SORT is called from a Lua script,
* we want to force a specific ordering even when no explicit ordering
* was asked (SORT BY nosort). This guarantees that replication / AOF
* is deterministic.
/* When sorting a set with no sort specified, we must sort the output
* so the result is consistent across scripting and replication.
*
* However in the case 'dontsort' is true, but the type to sort is a
* sorted set, we don't need to do anything as ordering is guaranteed
* in this special case. */
if ((storekey || c->flags & REDIS_LUA_CLIENT) &&
(dontsort && sortval->type != REDIS_ZSET))
* The other types (list, sorted set) will retain their native order
* even if no sort order is requested, so they remain stable across
* scripting and replication. */
if (dontsort &&
sortval->type == REDIS_SET &&
(storekey || c->flags & REDIS_LUA_CLIENT))
{
/* Force ALPHA sorting */
dontsort = 0;
+2 -1
View File
@@ -49,7 +49,7 @@ static int label_margin_top = 1;
* sparklineSequenceAddSample(seq, 10, NULL);
* sparklineSequenceAddSample(seq, 20, NULL);
* sparklineSequenceAddSample(seq, 30, "last sample label");
* sds output = sparklineRender(seq, 80, 4);
* sds output = sparklineRender(sdsempty(), seq, 80, 4, SPARKLINE_FILL);
* freeSparklineSequence(seq);
* ------------------------------------------------------------------------- */
@@ -63,6 +63,7 @@ struct sequence *createSparklineSequence(void) {
/* Add a new sample into a sequence. */
void sparklineSequenceAddSample(struct sequence *seq, double value, char *label) {
label = (label == NULL || label[0] == '\0') ? NULL : zstrdup(label);
if (seq->length == 0) {
seq->min = seq->max = value;
} else {
+1
View File
@@ -139,6 +139,7 @@ ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout) {
*ptr = '\0';
nread++;
}
size--;
}
return nread;
}
+1 -1
View File
@@ -568,7 +568,7 @@ void hincrbyfloatCommand(redisClient *c) {
}
value += incr;
new = createStringObjectFromLongDouble(value);
new = createStringObjectFromLongDouble(value,1);
hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
hashTypeSet(o,c->argv[2],new);
addReplyBulk(c,new);
+1 -1
View File
@@ -321,7 +321,7 @@ void smoveCommand(redisClient *c) {
/* If srcset and dstset are equal, SMOVE is a no-op */
if (srcset == dstset) {
addReply(c,shared.cone);
addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero);
return;
}
+1 -1
View File
@@ -387,7 +387,7 @@ void incrbyfloatCommand(redisClient *c) {
addReplyError(c,"increment would produce NaN or Infinity");
return;
}
new = createStringObjectFromLongDouble(value);
new = createStringObjectFromLongDouble(value,1);
if (o)
dbOverwrite(c->db,c->argv[1],new);
else
+1 -1
View File
@@ -385,7 +385,7 @@ int string2l(const char *s, size_t slen, long *lval) {
}
/* Convert a double to a string representation. Returns the number of bytes
* required. The representation should always be parsable by stdtod(3). */
* required. The representation should always be parsable by strtod(3). */
int d2string(char *buf, size_t len, double value) {
if (isnan(value)) {
len = snprintf(buf,len,"nan");
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.8.15"
#define REDIS_VERSION "2.8.23"
+5 -4
View File
@@ -183,7 +183,7 @@ static unsigned int zipIntSize(unsigned char encoding) {
return 0;
}
/* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
/* Encode the length 'rawlen' writing it in 'p'. If p is NULL it just returns
* the amount of bytes required to encode such a length. */
static unsigned int zipEncodeLength(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
unsigned char len = 1, buf[5];
@@ -739,8 +739,8 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
}
}
/* Get entry pointed to by 'p' and store in either 'e' or 'v' depending
* on the encoding of the entry. 'e' is always set to NULL to be able
/* Get entry pointed to by 'p' and store in either '*sstr' or 'sval' depending
* on the encoding of the entry. '*sstr' is always set to NULL to be able
* to find out whether the string pointer or the integer value was set.
* Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */
unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
@@ -788,7 +788,8 @@ unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigne
return (p == NULL) ? zl : __ziplistDelete(zl,p,num);
}
/* Compare entry pointer to by 'p' with 'entry'. Return 1 if equal. */
/* Compare entry pointer to by 'p' with 'sstr' of length 'slen'. */
/* Return 1 if equal. */
unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int slen) {
zlentry entry;
unsigned char sencoding;
+2 -3
View File
@@ -51,10 +51,9 @@
* <len> is the length of the following string (key or value).
* <len> lengths are encoded in a single value or in a 5 bytes value.
* If the first byte value (as an unsigned 8 bit value) is between 0 and
* 252, it's a single-byte length. If it is 253 then a four bytes unsigned
* 253, it's a single-byte length. If it is 254 then a four bytes unsigned
* integer follows (in the host byte ordering). A value of 255 is used to
* signal the end of the hash. The special value 254 is used to mark
* empty space that can be used to add new key/value pairs.
* signal the end of the hash.
*
* <free> is the number of free unused bytes after the string, resulting
* from modification of values associated to a key. For instance if "foo"
+18 -6
View File
@@ -328,27 +328,39 @@ float zmalloc_get_fragmentation_ratio(size_t rss) {
return (float)rss/zmalloc_used_memory();
}
/* Get the sum of the specified field (converted form kb to bytes) in
* /proc/self/smaps. The field must be specified with trailing ":" as it
* apperas in the smaps output.
*
* Example: zmalloc_get_smap_bytes_by_field("Rss:");
*/
#if defined(HAVE_PROC_SMAPS)
size_t zmalloc_get_private_dirty(void) {
size_t zmalloc_get_smap_bytes_by_field(char *field) {
char line[1024];
size_t pd = 0;
size_t bytes = 0;
FILE *fp = fopen("/proc/self/smaps","r");
int flen = strlen(field);
if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) {
if (strncmp(line,"Private_Dirty:",14) == 0) {
if (strncmp(line,field,flen) == 0) {
char *p = strchr(line,'k');
if (p) {
*p = '\0';
pd += strtol(line+14,NULL,10) * 1024;
bytes += strtol(line+flen,NULL,10) * 1024;
}
}
}
fclose(fp);
return pd;
return bytes;
}
#else
size_t zmalloc_get_private_dirty(void) {
size_t zmalloc_get_smap_bytes_by_field(char *field) {
((void) field);
return 0;
}
#endif
size_t zmalloc_get_private_dirty(void) {
return zmalloc_get_smap_bytes_by_field("Private_Dirty:");
}
+1
View File
@@ -76,6 +76,7 @@ void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
float zmalloc_get_fragmentation_ratio(size_t rss);
size_t zmalloc_get_rss(void);
size_t zmalloc_get_private_dirty(void);
size_t zmalloc_get_smap_bytes_by_field(char *field);
void zlibc_free(void *ptr);
#ifndef HAVE_MALLOC_SIZE
+1 -1
View File
@@ -4,7 +4,7 @@
# instances.
#
# Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com
# This softare is released under the BSD License. See the COPYING file for
# This software is released under the BSD License. See the COPYING file for
# more information.
package require Tcl 8.5
+30 -3
View File
@@ -39,14 +39,41 @@ tags {"aof"} {
## Should also start with truncated AOF without incomplete MULTI block.
create_aof {
append_to_aof [formatCommand set foo hello]
append_to_aof [string range [formatCommand set bar world] 0 end-1]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [formatCommand incr foo]
append_to_aof [string range [formatCommand incr foo] 0 end-1]
}
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Short read: Server should start if load-truncated is yes" {
assert_equal 1 [is_alive $srv]
}
set client [redis [dict get $srv host] [dict get $srv port]]
test "Truncated AOF loaded: we expect foo to be equal to 5" {
assert {[$client get foo] eq "5"}
}
test "Append a new command after loading an incomplete AOF" {
$client incr foo
}
}
# Now the AOF file is expected to be correct
start_server_aof [list dir $server_path aof-load-truncated yes] {
test "Short read + command: Server should start" {
assert_equal 1 [is_alive $srv]
}
set client [redis [dict get $srv host] [dict get $srv port]]
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
assert {[$client get foo] eq "6"}
}
}
## Test that the server exits when the AOF contains a format error
@@ -142,7 +169,7 @@ tags {"aof"} {
assert_equal 1 [is_alive $srv]
}
test "Fixed AOF: Keyspace should contain values that were parsable" {
test "Fixed AOF: Keyspace should contain values that were parseable" {
set client [redis [dict get $srv host] [dict get $srv port]]
wait_for_condition 50 100 {
[catch {$client ping} e] == 0
+13 -13
View File
@@ -7,19 +7,19 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "encodings.rd
test "RDB encoding loading test" {
r select 0
csvdump r
} {"compressible","string","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"hash","hash","a","1","aa","10","aaa","100","b","2","bb","20","bbb","200","c","3","cc","30","ccc","300","ddd","400","eee","5000000000",
"hash_zipped","hash","a","1","b","2","c","3",
"list","list","1","2","3","a","b","c","100000","6000000000","1","2","3","a","b","c","100000","6000000000","1","2","3","a","b","c","100000","6000000000",
"list_zipped","list","1","2","3","a","b","c","100000","6000000000",
"number","string","10"
"set","set","1","100000","2","3","6000000000","a","b","c",
"set_zipped_1","set","1","2","3","4",
"set_zipped_2","set","100000","200000","300000","400000",
"set_zipped_3","set","1000000000","2000000000","3000000000","4000000000","5000000000","6000000000",
"string","string","Hello World"
"zset","zset","a","1","b","2","c","3","aa","10","bb","20","cc","30","aaa","100","bbb","200","ccc","300","aaaa","1000","cccc","123456789","bbbb","5000000000",
"zset_zipped","zset","a","1","b","2","c","3",
} {"0","compressible","string","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"0","hash","hash","a","1","aa","10","aaa","100","b","2","bb","20","bbb","200","c","3","cc","30","ccc","300","ddd","400","eee","5000000000",
"0","hash_zipped","hash","a","1","b","2","c","3",
"0","list","list","1","2","3","a","b","c","100000","6000000000","1","2","3","a","b","c","100000","6000000000","1","2","3","a","b","c","100000","6000000000",
"0","list_zipped","list","1","2","3","a","b","c","100000","6000000000",
"0","number","string","10"
"0","set","set","1","100000","2","3","6000000000","a","b","c",
"0","set_zipped_1","set","1","2","3","4",
"0","set_zipped_2","set","100000","200000","300000","400000",
"0","set_zipped_3","set","1000000000","2000000000","3000000000","4000000000","5000000000","6000000000",
"0","string","string","Hello World"
"0","zset","zset","a","1","b","2","c","3","aa","10","bb","20","cc","30","aaa","100","bbb","200","ccc","300","aaaa","1000","cccc","123456789","bbbb","5000000000",
"0","zset_zipped","zset","a","1","b","2","c","3",
}
}
+38 -25
View File
@@ -13,7 +13,11 @@ proc stop_bg_complex_data {handle} {
#
# You can specifiy backlog size, ttl, delay before reconnection, test duration
# in seconds, and an additional condition to verify at the end.
proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
#
# If reconnect is > 0, the test actually try to break the connection and
# reconnect with the master, otherwise just the initial synchronization is
# checked for consistency.
proc test_psync {descr duration backlog_size backlog_ttl delay cond diskless reconnect} {
start_server {tags {"repl"}} {
start_server {} {
@@ -24,6 +28,8 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
$master config set repl-backlog-size $backlog_size
$master config set repl-backlog-ttl $backlog_ttl
$master config set repl-diskless-sync $diskless
$master config set repl-diskless-sync-delay 1
set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
@@ -48,22 +54,24 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
}
}
test "Test replication partial resync: $descr" {
test "Test replication partial resync: $descr (diskless: $diskless, reconnect: $reconnect)" {
# Now while the clients are writing data, break the maste-slave
# link multiple times.
for {set j 0} {$j < $duration*10} {incr j} {
after 100
# catch {puts "MASTER [$master dbsize] keys, SLAVE [$slave dbsize] keys"}
if ($reconnect) {
for {set j 0} {$j < $duration*10} {incr j} {
after 100
# catch {puts "MASTER [$master dbsize] keys, SLAVE [$slave dbsize] keys"}
if {($j % 20) == 0} {
catch {
if {$delay} {
$slave multi
$slave client kill $master_host:$master_port
$slave debug sleep $delay
$slave exec
} else {
$slave client kill $master_host:$master_port
if {($j % 20) == 0} {
catch {
if {$delay} {
$slave multi
$slave client kill $master_host:$master_port
$slave debug sleep $delay
$slave exec
} else {
$slave client kill $master_host:$master_port
}
}
}
}
@@ -98,18 +106,23 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
}
}
test_psync {ok psync} 6 1000000 3600 0 {
assert {[s -1 sync_partial_ok] > 0}
}
foreach diskless {no yes} {
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
} $diskless 0
test_psync {no backlog} 6 100 3600 0.5 {
assert {[s -1 sync_partial_err] > 0}
}
test_psync {ok psync} 6 1000000 3600 0 {
assert {[s -1 sync_partial_ok] > 0}
} $diskless 1
test_psync {ok after delay} 3 100000000 3600 3 {
assert {[s -1 sync_partial_ok] > 0}
}
test_psync {no backlog} 6 100 3600 0.5 {
assert {[s -1 sync_partial_err] > 0}
} $diskless 1
test_psync {backlog expired} 3 100000000 1 3 {
assert {[s -1 sync_partial_err] > 0}
test_psync {ok after delay} 3 100000000 3600 3 {
assert {[s -1 sync_partial_ok] > 0}
} $diskless 1
test_psync {backlog expired} 3 100000000 1 3 {
assert {[s -1 sync_partial_err] > 0}
} $diskless 1
}
+172 -76
View File
@@ -1,10 +1,70 @@
proc log_file_matches {log pattern} {
set fp [open $log r]
set content [read $fp]
close $fp
string match $pattern $content
}
start_server {tags {"repl"}} {
set slave [srv 0 client]
set slave_host [srv 0 host]
set slave_port [srv 0 port]
set slave_log [srv 0 stdout]
start_server {} {
test {First server should have role slave after SLAVEOF} {
r -1 slaveof [srv 0 host] [srv 0 port]
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
# Configure the master in order to hang waiting for the BGSAVE
# operation, so that the slave remains in the handshake state.
$master config set repl-diskless-sync yes
$master config set repl-diskless-sync-delay 1000
# Use a short replication timeout on the slave, so that if there
# are no bugs the timeout is triggered in a reasonable amount
# of time.
$slave config set repl-timeout 5
# Start the replication process...
$slave slaveof $master_host $master_port
test {Slave enters handshake} {
wait_for_condition 50 1000 {
[string match *handshake* [$slave role]]
} else {
fail "Slave does not enter handshake state"
}
}
# But make the master unable to send
# the periodic newlines to refresh the connection. The slave
# should detect the timeout.
$master debug sleep 10
test {Slave is able to detect timeout during handshake} {
wait_for_condition 50 1000 {
[log_file_matches $slave_log "*Timeout connecting to the MASTER*"]
} else {
fail "Slave is not able to detect timeout"
}
}
}
}
start_server {tags {"repl"}} {
set A [srv 0 client]
set A_host [srv 0 host]
set A_port [srv 0 port]
start_server {} {
set B [srv 0 client]
set B_host [srv 0 host]
set B_port [srv 0 port]
test {Set instance A as slave of B} {
$A slaveof $B_host $B_port
wait_for_condition 50 100 {
[s -1 role] eq {slave} &&
[string match {*master_link_status:up*} [r -1 info replication]]
[lindex [$A role] 0] eq {slave} &&
[string match {*master_link_status:up*} [$A info replication]]
} else {
fail "Can't turn the instance into a slave"
}
@@ -15,9 +75,9 @@ start_server {tags {"repl"}} {
$rd brpoplpush a b 5
r lpush a foo
wait_for_condition 50 100 {
[r debug digest] eq [r -1 debug digest]
[$A debug digest] eq [$B debug digest]
} else {
fail "Master and slave have different digest: [r debug digest] VS [r -1 debug digest]"
fail "Master and slave have different digest: [$A debug digest] VS [$B debug digest]"
}
}
@@ -28,24 +88,53 @@ start_server {tags {"repl"}} {
r lpush c 3
$rd brpoplpush c d 5
after 1000
assert_equal [r debug digest] [r -1 debug digest]
assert_equal [$A debug digest] [$B debug digest]
}
test {BLPOP followed by role change, issue #2473} {
set rd [redis_deferring_client]
$rd blpop foo 0 ; # Block while B is a master
# Turn B into master of A
$A slaveof no one
$B slaveof $A_host $A_port
wait_for_condition 50 100 {
[lindex [$B role] 0] eq {slave} &&
[string match {*master_link_status:up*} [$B info replication]]
} else {
fail "Can't turn the instance into a slave"
}
# Push elements into the "foo" list of the new slave.
# If the client is still attached to the instance, we'll get
# a desync between the two instances.
$A rpush foo a b c
after 100
wait_for_condition 50 100 {
[$A debug digest] eq [$B debug digest] &&
[$A lrange foo 0 -1] eq {a b c} &&
[$B lrange foo 0 -1] eq {a b c}
} else {
fail "Master and slave have different digest: [$A debug digest] VS [$B debug digest]"
}
}
}
}
start_server {tags {"repl"}} {
r set mykey foo
start_server {} {
test {Second server should have role master at first} {
s role
} {master}
test {SLAVEOF should start with link status "down"} {
r slaveof [srv -1 host] [srv -1 port]
s master_link_status
} {down}
test {The role should immediately be changed to "slave"} {
s role
} {slave}
@@ -54,11 +143,11 @@ start_server {tags {"repl"}} {
test {Sync should have transferred keys from master} {
r get mykey
} {foo}
test {The link status should be up} {
s master_link_status
} {up}
test {SET on the master should immediately propagate} {
r -1 set mykey bar
@@ -94,79 +183,86 @@ start_server {tags {"repl"}} {
}
}
start_server {tags {"repl"}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
set slaves {}
set load_handle0 [start_write_load $master_host $master_port 3]
set load_handle1 [start_write_load $master_host $master_port 5]
set load_handle2 [start_write_load $master_host $master_port 20]
set load_handle3 [start_write_load $master_host $master_port 8]
set load_handle4 [start_write_load $master_host $master_port 4]
start_server {} {
lappend slaves [srv 0 client]
foreach dl {no yes} {
start_server {tags {"repl"}} {
set master [srv 0 client]
$master config set repl-diskless-sync $dl
set master_host [srv 0 host]
set master_port [srv 0 port]
set slaves {}
set load_handle0 [start_write_load $master_host $master_port 3]
set load_handle1 [start_write_load $master_host $master_port 5]
set load_handle2 [start_write_load $master_host $master_port 20]
set load_handle3 [start_write_load $master_host $master_port 8]
set load_handle4 [start_write_load $master_host $master_port 4]
start_server {} {
lappend slaves [srv 0 client]
start_server {} {
lappend slaves [srv 0 client]
test "Connect multiple slaves at the same time (issue #141)" {
# Send SALVEOF commands to slaves
[lindex $slaves 0] slaveof $master_host $master_port
[lindex $slaves 1] slaveof $master_host $master_port
[lindex $slaves 2] slaveof $master_host $master_port
start_server {} {
lappend slaves [srv 0 client]
test "Connect multiple slaves at the same time (issue #141), diskless=$dl" {
# Send SLAVEOF commands to slaves
[lindex $slaves 0] slaveof $master_host $master_port
[lindex $slaves 1] slaveof $master_host $master_port
[lindex $slaves 2] slaveof $master_host $master_port
# Wait for all the three slaves to reach the "online" state
set retry 500
while {$retry} {
set info [r -3 info]
if {[string match {*slave0:*state=online*slave1:*state=online*slave2:*state=online*} $info]} {
break
} else {
incr retry -1
after 100
# Wait for all the three slaves to reach the "online"
# state from the POV of the master.
set retry 500
while {$retry} {
set info [r -3 info]
if {[string match {*slave0:*state=online*slave1:*state=online*slave2:*state=online*} $info]} {
break
} else {
incr retry -1
after 100
}
}
if {$retry == 0} {
error "assertion:Slaves not correctly synchronized"
}
}
if {$retry == 0} {
error "assertion:Slaves not correctly synchronized"
}
# Stop the write load
stop_write_load $load_handle0
stop_write_load $load_handle1
stop_write_load $load_handle2
stop_write_load $load_handle3
stop_write_load $load_handle4
# Wait that slaves acknowledge they are online so
# we are sure that DBSIZE and DEBUG DIGEST will not
# fail because of timing issues.
wait_for_condition 500 100 {
[lindex [[lindex $slaves 0] role] 3] eq {connected} &&
[lindex [[lindex $slaves 1] role] 3] eq {connected} &&
[lindex [[lindex $slaves 2] role] 3] eq {connected}
} else {
fail "Slaves still not connected after some time"
}
# Wait that slaves exit the "loading" state
wait_for_condition 500 100 {
![string match {*loading:1*} [[lindex $slaves 0] info]] &&
![string match {*loading:1*} [[lindex $slaves 1] info]] &&
![string match {*loading:1*} [[lindex $slaves 2] info]]
} else {
fail "Slaves still loading data after too much time"
# Stop the write load
stop_write_load $load_handle0
stop_write_load $load_handle1
stop_write_load $load_handle2
stop_write_load $load_handle3
stop_write_load $load_handle4
# Make sure that slaves and master have same
# number of keys
wait_for_condition 500 100 {
[$master dbsize] == [[lindex $slaves 0] dbsize] &&
[$master dbsize] == [[lindex $slaves 1] dbsize] &&
[$master dbsize] == [[lindex $slaves 2] dbsize]
} else {
fail "Different number of keys between masted and slave after too long time."
}
# Check digests
set digest [$master debug digest]
set digest0 [[lindex $slaves 0] debug digest]
set digest1 [[lindex $slaves 1] debug digest]
set digest2 [[lindex $slaves 2] debug digest]
assert {$digest ne 0000000000000000000000000000000000000000}
assert {$digest eq $digest0}
assert {$digest eq $digest1}
assert {$digest eq $digest2}
}
# Make sure that slaves and master have same number of keys
wait_for_condition 500 100 {
[$master dbsize] == [[lindex $slaves 0] dbsize] &&
[$master dbsize] == [[lindex $slaves 1] dbsize] &&
[$master dbsize] == [[lindex $slaves 2] dbsize]
} else {
fail "Different number of keys between masted and slave after too long time."
}
# Check digests
set digest [$master debug digest]
set digest0 [[lindex $slaves 0] debug digest]
set digest1 [[lindex $slaves 1] debug digest]
set digest2 [[lindex $slaves 2] debug digest]
assert {$digest ne 0000000000000000000000000000000000000000}
assert {$digest eq $digest0}
assert {$digest eq $digest1}
assert {$digest eq $digest2}
}
}
}
}
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
# Sentinel test suite. Copyright (C) 2014 Salvatore Sanfilippo antirez@gmail.com
# This softare is released under the BSD License. See the COPYING file for
# This software is released under the BSD License. See the COPYING file for
# more information.
cd tests/sentinel
+34
View File
@@ -0,0 +1,34 @@
# Test for the SENTINEL CKQUORUM command
source "../tests/includes/init-tests.tcl"
set num_sentinels [llength $::sentinel_instances]
test "CKQUORUM reports OK and the right amount of Sentinels" {
foreach_sentinel_id id {
assert_match "*OK $num_sentinels usable*" [S $id SENTINEL CKQUORUM mymaster]
}
}
test "CKQUORUM detects quorum cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum [expr {$num_sentinels+1}]
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
}
test "CKQUORUM detects failover authorization cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum 1
kill_instance sentinel 1
kill_instance sentinel 2
kill_instance sentinel 3
after 5000
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
restart_instance sentinel 1
restart_instance sentinel 2
restart_instance sentinel 3
}
+1 -1
View File
@@ -18,7 +18,7 @@
# $r ping [list handlePong]
# }
# }
#
#
# set r [redis]
# $r blocking 0
# $r get fo [list handlePong]
+16 -10
View File
@@ -70,6 +70,9 @@ proc kill_server config {
if {$::valgrind} {
check_valgrind_errors [dict get $config stderr]
}
# Remove this pid from the set of active pids in the test server.
send_data_packet $::test_server_fd server-killed $pid
}
proc is_alive config {
@@ -178,10 +181,10 @@ proc start_server {options {code undefined}} {
dict set config $directive $arguments
}
}
# use a different directory every time a server is started
dict set config dir [tmpdir server]
# start every server on a different port
set ::port [find_available_port [expr {$::port+1}]]
dict set config port $::port
@@ -190,7 +193,7 @@ proc start_server {options {code undefined}} {
foreach {directive arguments} [concat $::global_overrides $overrides] {
dict set config $directive $arguments
}
# write new configuration to temporary file
set config_file [tmpfile redis.conf]
set fp [open $config_file w+]
@@ -204,11 +207,14 @@ proc start_server {options {code undefined}} {
set stderr [format "%s/%s" [dict get $config "dir"] "stderr"]
if {$::valgrind} {
exec valgrind --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &
set pid [exec valgrind --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &]
} else {
exec src/redis-server $config_file > $stdout 2> $stderr &
set pid [exec src/redis-server $config_file > $stdout 2> $stderr &]
}
# Tell the test server about this new instance.
send_data_packet $::test_server_fd server-spawned $pid
# check that the server actually started
# ugly but tries to be as fast as possible...
if {$::valgrind} {set retrynum 1000} else {set retrynum 100}
@@ -233,10 +239,10 @@ proc start_server {options {code undefined}} {
start_server_error $config_file $err
return
}
# find out the pid
while {![info exists pid]} {
regexp {\[(\d+)\]} [exec cat $stdout] _ pid
# Wait for actual startup
while {![info exists _pid]} {
regexp {PID:\s(\d+)} [exec cat $stdout] _ _pid
after 100
}
+1 -1
View File
@@ -29,7 +29,7 @@ proc assert_error {pattern code} {
if {[catch {uplevel 1 $code} error]} {
assert_match $pattern $error
} else {
error "assertion:Expected an error but nothing was catched"
error "assertion:Expected an error but nothing was caught"
}
}
+36 -32
View File
@@ -262,46 +262,50 @@ proc formatCommand {args} {
proc csvdump r {
set o {}
foreach k [lsort [{*}$r keys *]] {
set type [{*}$r type $k]
append o [csvstring $k] , [csvstring $type] ,
switch $type {
string {
append o [csvstring [{*}$r get $k]] "\n"
}
list {
foreach e [{*}$r lrange $k 0 -1] {
append o [csvstring $e] ,
for {set db 0} {$db < 16} {incr db} {
{*}$r select $db
foreach k [lsort [{*}$r keys *]] {
set type [{*}$r type $k]
append o [csvstring $db] , [csvstring $k] , [csvstring $type] ,
switch $type {
string {
append o [csvstring [{*}$r get $k]] "\n"
}
append o "\n"
}
set {
foreach e [lsort [{*}$r smembers $k]] {
append o [csvstring $e] ,
list {
foreach e [{*}$r lrange $k 0 -1] {
append o [csvstring $e] ,
}
append o "\n"
}
append o "\n"
}
zset {
foreach e [{*}$r zrange $k 0 -1 withscores] {
append o [csvstring $e] ,
set {
foreach e [lsort [{*}$r smembers $k]] {
append o [csvstring $e] ,
}
append o "\n"
}
append o "\n"
}
hash {
set fields [{*}$r hgetall $k]
set newfields {}
foreach {k v} $fields {
lappend newfields [list $k $v]
zset {
foreach e [{*}$r zrange $k 0 -1 withscores] {
append o [csvstring $e] ,
}
append o "\n"
}
set fields [lsort -index 0 $newfields]
foreach kv $fields {
append o [csvstring [lindex $kv 0]] ,
append o [csvstring [lindex $kv 1]] ,
hash {
set fields [{*}$r hgetall $k]
set newfields {}
foreach {k v} $fields {
lappend newfields [list $k $v]
}
set fields [lsort -index 0 $newfields]
foreach kv $fields {
append o [csvstring [lindex $kv 0]] ,
append o [csvstring [lindex $kv 1]] ,
}
append o "\n"
}
append o "\n"
}
}
}
{*}$r select 9
return $o
}
+56 -21
View File
@@ -1,5 +1,5 @@
# Redis test suite. Copyright (C) 2009 Salvatore Sanfilippo antirez@gmail.com
# This softare is released under the BSD License. See the COPYING file for
# This software is released under the BSD License. See the COPYING file for
# more information.
package require Tcl 8.5
@@ -65,6 +65,9 @@ set ::file ""; # If set, runs only the tests in this comma separated list
set ::curfile ""; # Hold the filename of the current suite
set ::accurate 0; # If true runs fuzz tests with more iterations
set ::force_failure 0
set ::timeout 600; # 10 minutes without progresses will quit the test.
set ::last_progress [clock seconds]
set ::active_servers {} ; # Pids of active Redis instances.
# 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
@@ -200,11 +203,19 @@ proc test_server_main {} {
vwait forever
}
# This function gets called 10 times per second, for now does nothing but
# may be used in the future in order to detect test clients taking too much
# time to execute the task.
# This function gets called 10 times per second.
proc test_server_cron {} {
# Do some work here.
set elapsed [expr {[clock seconds]-$::last_progress}]
if {$elapsed > $::timeout} {
set err "\[[colorstr red TIMEOUT]\]: clients state report follows."
puts $err
show_clients_state
kill_clients
force_kill_all_servers
the_end
}
after 100 test_server_cron
}
@@ -230,6 +241,8 @@ proc read_from_test_client fd {
set bytes [gets $fd]
set payload [read $fd $bytes]
foreach {status data} $payload break
set ::last_progress [clock seconds]
if {$status eq {ready}} {
if {!$::quiet} {
puts "\[$status\]: $data"
@@ -256,12 +269,15 @@ proc read_from_test_client fd {
set ::active_clients_task($fd) "(ERR) $data"
} elseif {$status eq {exception}} {
puts "\[[colorstr red $status]\]: $data"
foreach p $::clients_pids {
catch {exec kill -9 $p}
}
kill_clients
force_kill_all_servers
exit 1
} elseif {$status eq {testing}} {
set ::active_clients_task($fd) "(IN PROGRESS) $data"
} elseif {$status eq {server-spawned}} {
lappend ::active_servers $data
} elseif {$status eq {server-killed}} {
set ::active_servers [lsearch -all -inline -not -exact $::active_servers $data]
} else {
if {!$::quiet} {
puts "\[$status\]: $data"
@@ -269,6 +285,31 @@ proc read_from_test_client fd {
}
}
proc show_clients_state {} {
# The following loop is only useful for debugging tests that may
# enter an infinite loop. Commented out normally.
foreach x $::active_clients {
if {[info exist ::active_clients_task($x)]} {
puts "$x => $::active_clients_task($x)"
} else {
puts "$x => ???"
}
}
}
proc kill_clients {} {
foreach p $::clients_pids {
catch {exec kill $p}
}
}
proc force_kill_all_servers {} {
foreach p $::active_servers {
puts "Killing still running Redis server $p"
catch {exec kill -9 $p}
}
}
# A new client is idle. Remove it from the list of active clients and
# if there are still test units to run, launch them.
proc signal_idle_client fd {
@@ -276,17 +317,7 @@ proc signal_idle_client fd {
set ::active_clients \
[lsearch -all -inline -not -exact $::active_clients $fd]
if 0 {
# The following loop is only useful for debugging tests that may
# enter an infinite loop. Commented out normally.
foreach x $::active_clients {
if {[info exist ::active_clients_task($x)]} {
puts "$x => $::active_clients_task($x)"
} else {
puts "$x => ???"
}
}
}
if 0 {show_clients_state}
# New unit to process?
if {$::next_test != [llength $::all_tests]} {
@@ -306,7 +337,7 @@ proc signal_idle_client fd {
}
}
# The the_end funciton gets called when all the test units were already
# The the_end function gets called when all the test units were already
# executed, so the test finished.
proc the_end {} {
# TODO: print the status, exit with the rigth exit code.
@@ -361,7 +392,8 @@ proc print_help_screen {} {
"--quiet Don't show individual tests."
"--single <unit> Just execute the specified unit (see next option)."
"--list-tests List all the available test units."
"--clients <num> Number of test clients (16)."
"--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."
"--help Print this help screen."
} "\n"]
@@ -410,6 +442,9 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--clients}} {
set ::numclients $arg
incr j
} elseif {$opt eq {--timeout}} {
set ::timeout $arg
incr j
} elseif {$opt eq {--help}} {
print_help_screen
exit 0
+1 -1
View File
@@ -10,7 +10,7 @@ start_server {tags {"auth"} overrides {requirepass foobar}} {
catch {r auth wrong!} err
set _ $err
} {ERR*invalid password}
test {Arbitrary command gives an error when AUTH is required} {
catch {r set foo bar} err
set _ $err
+30 -4
View File
@@ -83,7 +83,7 @@ start_server {tags {"basic"}} {
for {set x 9999} {$x >= 0} {incr x -1} {
set val [r get $x]
if {$val ne $x} {
set err "Eleemnt at position $x is $val instead of $x"
set err "Element at position $x is $val instead of $x"
break
}
}
@@ -300,7 +300,7 @@ start_server {tags {"basic"}} {
catch {r foobaredcommand} err
string match ERR* $err
} {1}
test {RENAME basic usage} {
r set mykey hello
r rename mykey mykey1
@@ -410,6 +410,32 @@ start_server {tags {"basic"}} {
set e
} {*ERR*index out of range}
test {MOVE can move key expire metadata as well} {
r select 10
r flushdb
r select 9
r set mykey foo ex 100
r move mykey 10
assert {[r ttl mykey] == -2}
r select 10
assert {[r ttl mykey] > 0 && [r ttl mykey] <= 100}
assert {[r get mykey] eq "foo"}
r select 9
}
test {MOVE does not create an expire if it does not exist} {
r select 10
r flushdb
r select 9
r set mykey foo
r move mykey 10
assert {[r ttl mykey] == -2}
r select 10
assert {[r ttl mykey] == -1}
assert {[r get mykey] eq "foo"}
r select 9
}
test {SET/GET keys in different DBs} {
r set a hello
r set b world
@@ -426,7 +452,7 @@ start_server {tags {"basic"}} {
r select 9
format $res
} {hello world foo bared}
test {MGET} {
r flushdb
r set foo BAR
@@ -482,7 +508,7 @@ start_server {tags {"basic"}} {
r set foo bar
list [r getset foo xyz] [r get foo]
} {bar xyz}
test {MSET base case} {
r mset x 10 y "foo bar" z "x x x x x x x\n\n\r\n"
r mget x y z
+1 -1
View File
@@ -125,7 +125,7 @@ start_server {tags {"bitops"}} {
test {BITOP where dest and target are the same key} {
r set s "\xaa\x00\xff\x55"
r bitop not s s
r get s
r get s
} "\x55\xff\x00\xaa"
test {BITOP AND|OR|XOR don't change the string with single input key} {
+1 -1
View File
@@ -27,7 +27,7 @@ start_server {tags {"introspection"}} {
test {CLIENT LIST shows empty fields for unassigned names} {
r client list
} {*name= *}
test {CLIENT SETNAME does not accept spaces} {
catch {r client setname "foo bar"} e
set e
+3 -3
View File
@@ -28,7 +28,7 @@ start_server {tags {"maxmemory"}} {
} {
test "maxmemory - is the memory limit honoured? (policy $policy)" {
# make sure to start with a blank instance
r flushall
r flushall
# Get the current memory limit and calculate a new limit.
# We just add 100k to the current memory size so that it is
# fast for us to reach that limit.
@@ -60,7 +60,7 @@ start_server {tags {"maxmemory"}} {
} {
test "maxmemory - only allkeys-* should remove non-volatile keys ($policy)" {
# make sure to start with a blank instance
r flushall
r flushall
# Get the current memory limit and calculate a new limit.
# We just add 100k to the current memory size so that it is
# fast for us to reach that limit.
@@ -102,7 +102,7 @@ start_server {tags {"maxmemory"}} {
} {
test "maxmemory - policy $policy should only remove volatile keys." {
# make sure to start with a blank instance
r flushall
r flushall
# Get the current memory limit and calculate a new limit.
# We just add 100k to the current memory size so that it is
# fast for us to reach that limit.
+6 -1
View File
@@ -1,15 +1,20 @@
proc test_memory_efficiency {range} {
r flushall
set rd [redis_deferring_client]
set base_mem [s used_memory]
set written 0
for {set j 0} {$j < 10000} {incr j} {
set key key:$j
set val [string repeat A [expr {int(rand()*$range)}]]
r set $key $val
$rd set $key $val
incr written [string length $key]
incr written [string length $val]
incr written 2 ;# A separator is the minimum to store key-value data.
}
for {set j 0} {$j < 10000} {incr j} {
$rd read ; # Discard replies
}
set current_mem [s used_memory]
set used [expr {$current_mem-$base_mem}]
set efficiency [expr {double($written)/$used}]
+10
View File
@@ -226,4 +226,14 @@ start_server {tags {"scan"}} {
set res [r zscan mykey 0 MATCH foo* COUNT 10000]
lsort -unique [lindex $res 1]
}
test "ZSCAN scores: regression test for issue #2175" {
r del mykey
for {set j 0} {$j < 500} {incr j} {
r zadd mykey 9.8813129168249309e-323 $j
}
set res [lindex [r zscan mykey 0] 1]
set first_score [lindex $res 1]
assert {$first_score != 0}
}
}
+88
View File
@@ -184,6 +184,94 @@ start_server {tags {"scripting"}} {
set e
} {*against a key*}
test {EVAL - JSON numeric decoding} {
# We must return the table as a string because otherwise
# Redis converts floats to ints and we get 0 and 1023 instead
# of 0.0003 and 1023.2 as the parsed output.
r eval {return
table.concat(
cjson.decode(
"[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]"), " ")
} 0
} {0 -5000 -1 0.0003 1023.2 0}
test {EVAL - JSON string decoding} {
r eval {local decoded = cjson.decode('{"keya": "a", "keyb": "b"}')
return {decoded.keya, decoded.keyb}
} 0
} {a b}
test {EVAL - cmsgpack can pack double?} {
r eval {local encoded = cmsgpack.pack(0.1)
local h = ""
for i = 1, #encoded do
h = h .. string.format("%02x",string.byte(encoded,i))
end
return h
} 0
} {cb3fb999999999999a}
test {EVAL - cmsgpack can pack negative int64?} {
r eval {local encoded = cmsgpack.pack(-1099511627776)
local h = ""
for i = 1, #encoded do
h = h .. string.format("%02x",string.byte(encoded,i))
end
return h
} 0
} {d3ffffff0000000000}
test {EVAL - cmsgpack can pack and unpack circular references?} {
r eval {local a = {x=nil,y=5}
local b = {x=a}
a['x'] = b
local encoded = cmsgpack.pack(a)
local h = ""
-- cmsgpack encodes to a depth of 16, but can't encode
-- references, so the encoded object has a deep copy recusive
-- depth of 16.
for i = 1, #encoded do
h = h .. string.format("%02x",string.byte(encoded,i))
end
-- when unpacked, re.x.x != re because the unpack creates
-- individual tables down to a depth of 16.
-- (that's why the encoded output is so large)
local re = cmsgpack.unpack(encoded)
assert(re)
assert(re.x)
assert(re.x.x.y == re.y)
assert(re.x.x.x.x.y == re.y)
assert(re.x.x.x.x.x.x.y == re.y)
assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)
-- maximum working depth:
assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)
-- now the last x would be b above and has no y
assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)
-- so, the final x.x is at the depth limit and was assigned nil
assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)
return {h, re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}
} 0
} {82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0 1 1}
test {EVAL - Numerical sanity check from bitop} {
r eval {assert(0x7fffffff == 2147483647, "broken hex literals");
assert(0xffffffff == -1 or 0xffffffff == 2^32-1,
"broken hex literals");
assert(tostring(-1) == "-1", "broken tostring()");
assert(tostring(0xffffffff) == "-1" or
tostring(0xffffffff) == "4294967295",
"broken tostring()")
} 0
} {}
test {EVAL - Verify minimal bitop functionality} {
r eval {assert(bit.tobit(1) == 1);
assert(bit.band(1) == 1);
assert(bit.bxor(1,2) == 3);
assert(bit.bor(1,2,4,8,16,32,64,128) == 255)
} 0
} {}
test {SCRIPTING FLUSH - is able to clear the scripts cache?} {
r set mykey myval
set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]
+19 -1
View File
@@ -179,7 +179,7 @@ start_server {
assert_equal [lsort -real $floats] [r sort mylist]
}
test "SORT with STORE returns zero if result is empty (github isse 224)" {
test "SORT with STORE returns zero if result is empty (github issue 224)" {
r flushdb
r sort foo store bar
} {0}
@@ -238,6 +238,24 @@ start_server {
r sort mylist by num get x:*->
} {100}
test "SORT by nosort retains native order for lists" {
r del testa
r lpush testa 2 1 4 3 5
r sort testa by nosort
} {5 3 4 1 2}
test "SORT by nosort plus store retains native order for lists" {
r del testa
r lpush testa 2 1 4 3 5
r sort testa by nosort store testb
r lrange testb 0 -1
} {5 3 4 1 2}
test "SORT by nosort with limit returns based on original list order" {
r sort testa by nosort limit 0 3 store testb
r lrange testb 0 -1
} {5 3 4}
tags {"slow"} {
set num 100
set res [create_random_dataset $num lpush]
+5 -5
View File
@@ -2,8 +2,8 @@ start_server {tags {"hash"}} {
test {HSET/HLEN - Small hash creation} {
array set smallhash {}
for {set i 0} {$i < 8} {incr i} {
set key [randstring 0 8 alpha]
set val [randstring 0 8 alpha]
set key __avoid_collisions__[randstring 0 8 alpha]
set val __avoid_collisions__[randstring 0 8 alpha]
if {[info exists smallhash($key)]} {
incr i -1
continue
@@ -21,8 +21,8 @@ start_server {tags {"hash"}} {
test {HSET/HLEN - Big hash creation} {
array set bighash {}
for {set i 0} {$i < 1024} {incr i} {
set key [randstring 0 8 alpha]
set val [randstring 0 8 alpha]
set key __avoid_collisions__[randstring 0 8 alpha]
set val __avoid_collisions__[randstring 0 8 alpha]
if {[info exists bighash($key)]} {
incr i -1
continue
@@ -33,7 +33,7 @@ start_server {tags {"hash"}} {
list [r hlen bighash]
} {1024}
test {Is the big hash encoded with a ziplist?} {
test {Is the big hash encoded with an hash table?} {
assert_encoding hashtable bighash
}
+1
View File
@@ -450,6 +450,7 @@ start_server {
test "SMOVE non existing key" {
setup_move
assert_equal 0 [r smove myset1 myset2 foo]
assert_equal 0 [r smove myset1 myset1 foo]
assert_equal {1 a b} [lsort [r smembers myset1]]
assert_equal {2 3 4} [lsort [r smembers myset2]]
}
+3 -2
View File
@@ -26,11 +26,12 @@ case "$1" in
fi
;;
status)
if [ ! -f $PIDFILE ]
PID=$(cat $PIDFILE)
if [ ! -x /proc/${PID} ]
then
echo 'Redis is not running'
else
echo "Redis is running ($(<$PIDFILE))"
echo "Redis is running ($PID)"
fi
;;
restart)
+7 -1
View File
@@ -1,9 +1,15 @@
# This script is from http://poormansprofiler.org/
#
# NOTE: Instead of using this script, you should use the Redis
# Software Watchdog, which provides a similar functionality but in
# a more reliable / easy to use way.
#
# Check http://redis.io/topics/latency for more information.
#!/bin/bash
nsamples=1
sleeptime=0
pid=$(pidof redis-server)
pid=$(ps auxww | grep '[r]edis-server' | awk '{print $2}')
for x in $(seq 1 $nsamples)
do