Compare commits

..
139 Commits
Author SHA1 Message Date
antirez d96497961c Redis 2.6.13 2013-04-30 15:15:56 +02:00
antirez 4f38d03254 Sentinel: changes to tilt mode.
Tilt mode was too aggressive (not processing INFO output), this
resulted in a few problems:

1) Redirections were not followed when in tilt mode. This opened a
   window to misinform clients about the current master when a Sentinel
   was in tilt mode and a fail over happened during the time it was not
   able to update the state.

2) It was possible for a Sentinel exiting tilt mode to detect a false
   fail over start, if a slave rebooted with a wrong configuration
   about at the same time. This used to happen since in tilt mode we
   lose the information that the runid changed (reboot).

   Now instead the Sentinel in tilt mode will still remove the instance
   from the list of slaves if it changes state AND runid at the same
   time.

Both are edge conditions but the changes should overall improve the
reliability of Sentinel.
2013-04-30 15:09:34 +02:00
antirez 59ff2fe9a4 Sentinel: more sensible delay in master demote after tilt. 2013-04-30 15:09:31 +02:00
antirez fb0d08e35f Sentinel: only demote old master into slave under certain conditions.
We used to always turn a master into a slave if the DEMOTE flag was set,
as this was a resurrecting master instance.

However the following race condition is possible for a Sentinel that
got partitioned or internal issues (tilt mode), and was not able to
refresh the state in the meantime:

1) Sentinel X is running, master is instance "A".
3) "A" fails, sentinels will promote slave "B" as master.
2) Sentinel X goes down because of a network partition.
4) "A" returns available, Sentinels will demote it as a slave.
5) "B" fails, other Sentinels will promote slave "A" as master.
6) At this point Sentinel X comes back.

When "X" comes back he thinks that:

"B" is the master.
"A" is the slave to demote.

We want to avoid that Sentinel "X" will demote "A" into a slave.
We also want that Sentinel "X" will detect that the conditions changed
and will reconfigure itself to monitor the right master.

There are two main ways for the Sentinel to reconfigure itself after
this event:

1) If "B" is reachable and already configured as a slave by other
sentinels, "X" will perform a redirection to "A".
2) If there are not the conditions to demote "A", the fact that "A"
reports to be a master will trigger a failover detection in "X", that
will end into a reconfiguraiton to monitor "A".

However if the Sentinel was not reachable, its state may not be updated,
so in case it titled, or was partiitoned from the master instance of the
slave to demote, the new implementation waits some time (enough to
guarantee we can detect the new INFO, and new DOWN conditions).

If after some time still there are not the right condiitons to demote
the instance, the DEMOTE flag is cleared.
2013-04-30 15:09:27 +02:00
antirez 395e6a5872 Lua updated to version 5.1.5. 2013-04-29 12:02:04 +02:00
antirez 34a57a5b52 Sentinel: always redirect on master->slave transition.
Sentinel redirected to the master if the instance changed runid or it
was the first time we got INFO, and a role change was detected from
master to slave.

While this is a good idea in case of slave->master, since otherwise we
could detect a failover without good reasons just after a reboot with a
slave with a wrong configuration, in the case of master->slave
transition is much better to always perform the redirection for the
following reasons:

1) A Sentinel may go down for some time. When it is back online there is
no other way to understand there was a failover.
2) Pointing clients to a slave seems to be always the wrong thing to do.
3) There is no good rationale about handling things differently once an
instance is rebooted (runid change) in that case.
2013-04-24 11:34:05 +02:00
antirez 967ae8ca67 Config option to turn AOF rewrite incremental fsync on/off. 2013-04-24 10:57:38 +02:00
antirez c735116c87 AOF: sync data on disk every 32MB when rewriting.
This prevents the kernel from putting too much stuff in the output
buffers, doing too heavy I/O all at once. So the goal of this commit is
to split the disk pressure due to the AOF rewrite process into smaller
spikes.

Please see issue #1019 for more information.
2013-04-24 10:28:43 +02:00
antirez 8b41e19e47 rio.c: added ability to fdatasync() from time to time while writing. 2013-04-24 10:27:55 +02:00
antirez cf7b2c329b Test: fix RDB test checking file permissions.
When the test is executed using the root account, setting the permission
to 222 does not work as expected, as root can read files with 222
permission.

Now we skip the test if root is detected.

This fixes issue #1034 and the duplicated #1040 issue.

Thanks to Jan-Erik Rediger (@badboy on Github) for finding a way to reproduce the issue.
2013-04-23 14:16:58 +02:00
antirez bac29c8b7c Sentinel: turn old master into a slave when it comes back. 2013-04-22 11:26:33 +02:00
antirez 4848af1b9e Test: split conceptually unrelated comments in RDB test. 2013-04-22 11:25:59 +02:00
antirez 172eac504a More explicit panic message on out of memory. 2013-04-19 15:11:59 +02:00
antirez 5bf9b53a83 Redis/Jemalloc Gitignore were too aggressive.
Redis gitignore was too aggressive since simply broken.

Jemalloc gitignore was too agressive because it is conceived to just
keep the files that allow to generate all the rest in development
environments (so for instance the "configure" file is excluded).
2013-04-18 16:21:32 +02:00
antirez 40861516ac redis-cli: raise error on bad command line switch.
Previously redis-cli never tried to raise an error when an unrecognized
switch was encountered, as everything after the initial options is to be
transmitted to the server.

However this is too liberal, as there are no commands starting with "-".
So the new behavior is to produce an error if there is an unrecognized
switch starting with "-". This should not break past redis-cli usages
but should prevent broken options to be silently discarded.

As far the first token not starting with "-" is encountered, all the
rest is considered to be part of the command, so you cna still use
strings starting with "-" as values, like in:

    redis-cli --port 6380 set foo --my-value
2013-04-11 13:19:39 +02:00
antirez 92a7b0102c redis-cli: --latency-history mode implemented. 2013-04-11 13:19:33 +02:00
antirez 9f2e6ac0ed Test: remove useless statements and comments from test default config. 2013-04-04 14:32:17 +02:00
antirez 63978d338d Make rio.c comment 80-columns friendly. 2013-04-03 12:41:31 +02:00
antirez ed2d988192 Throttle BGSAVE attempt on saving error.
When a BGSAVE fails, Redis used to flood itself trying to BGSAVE at
every next cron call, that is either 10 or 100 times per second
depending on configuration and server version.

This commit does not allow a new automatic BGSAVE attempt to be
performed before a few seconds delay (currently 5).

This avoids both the auto-flood problem and filling the disk with
logs at a serious rate.

The five seconds limit, considering a log entry of 200 bytes, will use
less than 4 MB of disk space per day that is reasonable, the sysadmin
should notice before of catastrofic events especially since by default
Redis will stop serving write queries after the first failed BGSAVE.

This fixes issue #849
2013-04-02 14:13:03 +02:00
antirez 7da5980848 Redis 2.6.12 2013-03-29 17:42:39 +01:00
charsyam 062f60a36e Support for case unsensitive SET options. 2013-03-29 10:37:33 +01:00
antirez 4243483d3e Test: Extended SET tests. 2013-03-28 16:45:45 +01:00
antirez d785413d86 Extended SET command implemented (issue #931). 2013-03-28 16:45:37 +01:00
antirez e8a1a169dd EXPIRE should not resurrect keys. Issue #1026. 2013-03-28 12:49:03 +01:00
antirez dc0f13774b Test: regression test for issue #1026. 2013-03-28 12:48:59 +01:00
antirez 47911b8630 Better DEBUG error message when num of arguments is wrong. 2013-03-28 12:48:55 +01:00
antirez 7ff729e192 Test: verify that lazy-expire works. 2013-03-28 12:48:51 +01:00
antirez 02b9a72bae DEBUG set-active-expire added.
We need the ability to disable the activeExpireCycle() (active
expired key collection) call for testing purposes.
2013-03-28 12:48:47 +01:00
antirez 1862239136 Test: read_from_replication_stream, workaround for 2.6 2013-03-27 12:03:46 +01:00
antirez f8791a158b Test: test replication of MULTI/EXEC. 2013-03-27 11:57:32 +01:00
antirez 26319d08b5 Test: Restore DB back to 9 after testing MULTI/EXEC with DB 5. 2013-03-27 11:57:28 +01:00
antirez fc78d978de Test: new functions to capture and analyze the replication stream. 2013-03-27 11:57:23 +01:00
antirez 6cb7860658 Flag PUBLISH as read-only in the command table. 2013-03-27 09:07:43 +01:00
antirez 611dcb56ee Transactions: propagate MULTI/EXEC only when needed.
MULTI/EXEC is now propagated to the AOF / Slaves only once we encounter
the first command that is not a read-only one inside the transaction.

The old behavior was to always propagate an empty MULTI/EXEC block when
the transaction was composed just of read only commands, or even
completely empty. This created two problems:

1) It's a bandwidth waste in the replication link and a space waste
   inside the AOF file.

2) We used to always increment server.dirty to force the propagation of
   the EXEC command, resulting into triggering RDB saves more often
   than needed.

Note: even read-only commands may also trigger writes that will be
propagated, when we access a key that is found expired and Redis will
synthesize a DEL operation. However there is no need for this to stay
inside the transaction itself, but only to be ordered.

So for instance something like:

    MULTI
    GET foo
    SET key zap
    EXEC

May be propagated into:

    DEL foo
    MULTI
    SET key zap
    EXEC

While the DEL is outside the transaction, the commands are delivered in
the right order and it is not possible for other commands to be inserted
between DEL and MULTI.
2013-03-27 09:07:23 +01:00
antirez 889b017403 Transactions: use discardTransaction() in EXEC implementation. 2013-03-27 09:07:18 +01:00
antirez d1369c3d9f Transactions: use the propagate() API to propagate MULTI.
The behavior is the same, but the code is now cleaner and uses the
proper interface instead of dealing directly with AOF/replication
functions.
2013-03-27 09:06:59 +01:00
antirez 6818905406 Allow SELECT while loading the DB.
Fixes issue #1024.
2013-03-26 13:59:00 +01:00
NanXiao ef1cf15c14 Update config.c
Fix bug in configGetCommand function: get correct masterauth value.
2013-03-25 19:32:24 +01:00
antirez dee8d84270 Test: obuf-limits test false positive removed.
Fixes #621.
2013-03-25 11:56:08 +01:00
antirez 5576a28977 redis-cli --stat, stolen from redis-tools.
Redis-tools is a connection of tools no longer mantained that was
intented as a way to economically make sense of Redis in the pre-vmware
sponsorship era. However there was a nice redis-stat utility, this
commit imports one of the functionalities of this tool here in redis-cli
as it seems to be pretty useful.

Usage: redis-cli --stat

The output is similar to vmstat in the format, but with Redis specific
stuff of course.

From the point of view of the monitored instance, only INFO is used in
order to grab data.
2013-03-25 11:52:32 +01:00
antirez 4f8b18f3dd Replication: master_link_down_since_seconds initial value should be huge.
server.repl_down_since used to be initialized to the current time at
startup. This is wrong since the replication never started. Clients
testing this filed to check if data is uptodate should never believe
data is recent if we never ever connected to our master.
2013-03-13 12:55:06 +01:00
antirez c64255990e Test: make sure broken RDB checksum is detected. 2013-03-13 11:15:33 +01:00
antirez ba3c5494d8 Test: more RDB loading checks.
A test for issue #1001 is included.
2013-03-13 10:09:21 +01:00
antirez 684b61505a Test: check that Redis starts empty without an RDB file. 2013-03-13 10:09:16 +01:00
antirez 189e865c51 rdbLoad(): rework code to save vertical space. 2013-03-13 10:09:09 +01:00
Damian Janowski b9f8c2a5b0 Abort when opening the RDB file results in an error other than ENOENT.
This fixes cases where the RDB file does exist but can't be accessed for
any reason. For instance, when the Redis process doesn't have enough
permissions on the file.
2013-03-13 10:08:47 +01:00
antirez 18d16f8592 Set default for stop_writes_on_bgsave_err in initServerConfig().
It was placed for error in initServer() that's called after the
configuation is already loaded, causing issue #1000.
2013-03-12 18:36:07 +01:00
antirez 3a00520e5f Add a missing bugfix entry in 2.6.11 release notes. 2013-03-12 17:55:23 +01:00
antirez f93d9929d8 redis-cli --bigkeys: don't crash with empty DBs. 2013-03-12 09:57:49 +01:00
antirez 6589821035 Redis 2.6.11 2013-03-11 19:51:10 +01:00
antirez 48f4f77189 activeExpireCycle() smarter with many DBs and under expire pressure.
activeExpireCycle() tries to test just a few DBs per iteration so that
it scales if there are many configured DBs in the Redis instance.
However this commit makes it a bit smarter when one a few of those DBs
are under expiration pressure and there are many many keys to expire.

What we do is to remember if in the last iteration had to return because
we ran out of time. In that case the next iteration we'll test all the
configured DBs so that we are sure we'll test again the DB under
pressure.

Before of this commit after some mass-expire in a given DB the function
tested just a few of the next DBs, possibly empty, a few per iteration,
so it took a long time for the function to reach again the DB under
pressure. This resulted in a lot of memory being used by already expired
keys and never accessed by clients.
2013-03-11 11:34:49 +01:00
antirez b3281e93c3 In databasesCron() never test more DBs than we have. 2013-03-11 11:34:45 +01:00
antirez 2677c97d39 Make comment name match var name in activeExpireCycle(). 2013-03-11 11:34:34 +01:00
antirez 1d426bf53b Optimize inner loop of activeExpireCycle() for no-expires case. 2013-03-11 11:34:18 +01:00
antirez da2dd8991b REDIS_DBCRON_DBS_PER_SEC -> REDIS_DBCRON_DBS_PER_CALL 2013-03-11 11:34:14 +01:00
antirez 13f84841b5 activeExpireCycle(): process only a small number of DBs per iteration.
This small number of DBs is set to 16 so actually in the default
configuraiton Redis should behave exactly like in the past.
However the difference is that when the user configures a very large
number of DBs we don't do an O(N) operation, consuming a non trivial
amount of CPU per serverCron() iteration.
2013-03-11 11:34:10 +01:00
antirez ef3a95fa6f Use unsigned integers for DB ids, for defined wrap-to-zero. 2013-03-11 11:34:05 +01:00
antirez 665b819eb4 Only resize/rehash a few databases per cron iteration.
This is the first step to lower the CPU usage when many databases are
configured. The other is to also process a limited number of DBs per
call in the active expire cycle.
2013-03-11 11:33:59 +01:00
antirez bb562a6414 Actually call databasesCron() inside serverCron(). 2013-03-11 11:30:26 +01:00
antirez e166abad10 Move Redis databases background processing to databasesCron(). 2013-03-11 11:30:21 +01:00
antirez aec5ea5d07 serverCron() frequency is now a runtime parameter (was REDIS_HZ).
REDIS_HZ is the frequency our serverCron() function is called with.
A more frequent call to this function results into less latency when the
server is trying to handle very expansive background operations like
mass expires of a lot of keys at the same time.

Redis 2.4 used to have an HZ of 10. This was good enough with almost
every setup, but the incremental key expiration algorithm was working a
bit better under *extreme* pressure when HZ was set to 100 for Redis
2.6.

However for most users a latency spike of 30 milliseconds when million
of keys are expiring at the same time is acceptable, on the other hand a
default HZ of 100 in Redis 2.6 was causing idle instances to use some
CPU time compared to Redis 2.4. The CPU usage was in the order of 0.3%
for an idle instance, however this is a shame as more energy is consumed
by the server, if not important resources.

This commit introduces HZ as a runtime parameter, that can be queried by
INFO or CONFIG GET, and can be modified with CONFIG SET. At the same
time the default frequency is set back to 10.

In this way we default to a sane value of 10, but allows users to
easily switch to values up to 500 for near real-time applications if
needed and if they are willing to pay this small CPU usage penalty.
2013-03-11 11:28:55 +01:00
Gengliang Wang 2d24bf2f94 Removed useless "return" statements in pubsub.c
(original commit message edited)
2013-03-06 16:50:56 +01:00
antirez e7a61d287e API to lookup commands with their original name.
A new server.orig_commands table was added to the server structure, this
contains a copy of the commant table unaffected by rename-command
statements in redis.conf.

A new API lookupCommandOrOriginal() was added that checks both tables,
new first, old later, so that rewriteClientCommandVector() and friends
can lookup commands with their new or original name in order to fix the
client->cmd pointer when the argument vector is renamed.

This fixes the segfault of issue #986, but does not fix a wider range of
problems resulting from renaming commands that actually operate on data
and are registered into the AOF file or propagated to slaves... That is
command renaming should be handled with care.
2013-03-06 16:36:52 +01:00
antirez 78bae8b078 Add a warning about command renaming in redis.conf. 2013-03-06 16:36:52 +01:00
antirez bf6c5d960d Handle a non-impossible empty argv in loadServerConfigFromString().
Usually this does not happens since we trim for " \t\r\n", but if there
are other chars that return true with isspace(), we may end with an
empty argv. Better to handle the condition in an explicit way.
2013-03-06 12:44:28 +01:00
antirez 95fc9cc0a6 redis-cli: use sdsfreesplitres() instead of hand-coding it. 2013-03-06 12:44:23 +01:00
antirez 5de5efa364 sds.c: sdssplitargs_free() removed as it was a duplicate. 2013-03-06 12:44:19 +01:00
antirez 1f2d5941a1 More specific error message in loadServerConfigFromString(). 2013-03-06 12:44:16 +01:00
antirez 60aa3a67e8 sdssplitargs(): on error set *argc to 0.
This makes programs not checking the return value for NULL much safer
since with this change:

1) It is still possible to iterate the zero-length result without
crashes.
2) sdssplitargs_free will work against NULL and 0 count.
2013-03-06 12:44:10 +01:00
antirez 921aec17b6 sdssplitargs(): now returns NULL only on error.
An empty input string also resulted into the function returning NULL
making it harder for the caller to distinguish between error and empty
string without checking the original input string length.
2013-03-06 12:44:06 +01:00
charsyam e174290307 Don't segfault on unbalanced quotes. 2013-03-06 12:44:02 +01:00
antirez a3273dbfa6 Allow AUTH while loading the DB in memory.
While Redis is loading the AOF or RDB file in memory only a subset of
commands are allowed. This commit adds AUTH to this subset.
2013-03-06 11:51:26 +01:00
antirez e9318d92db redis-cli: use keepalive socket option.
This should improve things in two ways:

1) Prevent timeouts caused by the execution of long commands.
2) Improve detection of real connection errors.

This is mostly effective only on Linux because of the bogus default
keepalive settings. In Linux we have OS-specific calls to set the
keepalive interval to reasonable values.
2013-03-04 11:16:24 +01:00
0x20h 803c5b140d suppress external diff program when using git diff. 2013-03-04 10:59:22 +01:00
Stam He 74373cd930 add a check for aeCreateTimeEvent
1) Add a check for aeCreateTimeEvent in function initServer.
2013-03-04 10:52:53 +01:00
Stam He b5047d9b2f point 2 of slave-serve-stale-data miss '-' between 'stale' and 'data' 2013-03-04 10:49:30 +01:00
antirez 34b420dbf3 Replication: more strict error checking for master PING reply. 2013-02-12 16:59:38 +01:00
antirez 02ac84ddbd Test: avoid false positives in CLIENT SETNAME closed connection test. 2013-02-12 13:27:32 +01:00
Steven Penny 45bfab8596 Format to fit 80 columns
This makes it readable on GitHub and editors without auto wrapping.
2013-02-12 13:11:47 +01:00
antirez 948fdb5f7d Redis 2.6.10 2013-02-11 13:24:39 +01:00
antirez fe97d7b1db Makefile: valgrind target added (forces -O0 and libc malloc). 2013-02-11 12:11:21 +01:00
antirez e88517ac30 TCP keep-alive. Better documentation in redis.conf. 2013-02-11 11:47:35 +01:00
antirez 2d89c53d0d Tcp keep-alive: send three probes before detectin an error.
Otherwise we end with less reliable connections because it's too easy
that a single packet gets lost.
2013-02-11 11:47:31 +01:00
antirez 0fe052efaa tcp-keepalive option documented in redis.conf. 2013-02-11 11:47:28 +01:00
antirez e1e8b1cd29 Set SO_KEEPALIVE on client sockets if configured to do so. 2013-02-11 11:47:23 +01:00
antirez 609a3ec1be Add SO_KEEPALIVE support to anet.c. 2013-02-11 11:47:20 +01:00
antirez 0b50d1ce9a Sentinel: advertise the promoted slave address only after successful setup. 2013-02-11 11:47:16 +01:00
Pierre Chapuis c7b9a57fb2 fix comments forgotten in #285 (zipmap -> ziplist) 2013-02-11 11:47:10 +01:00
antirez 63ad0e8867 Make all WATCHers dirty when the slave reloads the DB. 2013-02-08 10:27:26 +01:00
antirez d5d0b467db LASTSAVE is a "random" command. 2013-02-07 19:13:09 +01:00
antirez cf0191dc3c TCP_NODELAY after SYNC: changes to the implementation. 2013-02-05 12:05:04 +01:00
charsyam 45b1b2f7ad Turn off TCP_NODELAY on the slave socket after SYNC.
Further details from @antirez:

It was reported by @StopForumSpam on Twitter that the Redis replication
link was strangely using multiple TCP packets for multiple commands.
This wastes a lot of bandwidth and is due to the TCP_NODELAY option we
enable on the socket after accepting a new connection.

However the master -> slave channel is a one-way channel since Redis
replication is asynchronous, so there is no point in trying to reduce
the latency, we should aim to reduce the bandwidth. For this reason this
commit introduces the ability to disable the nagle algorithm on the
socket after a successful SYNC.

This feature is off by default because the delay can be up to 40
milliseconds with normally configured Linux kernels.
2013-02-05 12:05:01 +01:00
antirez eb07793b59 Test: No clients timeout while testing. 2013-02-05 12:02:36 +01:00
Johan Bergström 22ccc6db0f Use info nameofexectuable to find current executable 2013-02-05 11:45:11 +01:00
Johan Bergström f9a8b0eced Enforce tcl 8.5 or newer 2013-02-05 11:45:08 +01:00
Johan Bergström 6da090e916 Check available tcl versions 2013-02-05 11:45:05 +01:00
Rock Li 8c0b9f778e retval doesn't initalized
If each if conditions are all fail, variable retval will under uninitlized
2013-02-05 10:49:20 +01:00
David Celis 63dc34bd01 Fix a few typos and improve grammar of redis.conf
Make several edits to the example redis.conf configuration file for
improved flow and grammar.

Signed-off-by: David Celis <me@davidcel.is>
2013-02-04 10:36:24 +01:00
Gengliang Wang 9ddd0f7796 Fix a bug in srandmemberWithCountCommand()
In CASE 2, the call sunionDiffGenericCommand will involve the string "srandmember"
> sadd foo one
(integer 1)
> sadd srandmember two
(integer 2)
> srandmember foo 3
1)"one"
2)"two"
2013-02-04 10:33:24 +01:00
antirez c17a7f6fbc Lua struct library updated to version 0.2.
There was a bug in the previous version of this library that caused a
crash under the circumstances described in issue #901.

The newer version of the library appears to be fixed (I tested it
manually with valgrind and everything seems fine now).

For more information about this library please visit this web site:

    http://www.inf.puc-rio.br/~roberto/struct/
2013-01-23 10:53:28 +01:00
antirez bfad381f57 redis-cli --bigkeys output is now simpler to understand. 2013-01-21 19:16:14 +01:00
antirez 742e580f40 UNSUBSCRIBE and PUNSUBSCRIBE: always provide a reply.
UNSUBSCRIBE and PUNSUBSCRIBE commands are designed to mass-unsubscribe
the client respectively all the channels and patters if called without
arguments.

However when these functions are called without arguments, but there are
no channels or patters we are subscribed to, the old behavior was to
don't reply at all.

This behavior is broken, as every command should always reply.
Also it is possible that we are no longer subscribed to a channels but we
are subscribed to patters or the other way around, and the client should
be notified with the correct number of subscriptions.

Also it is not pretty that sometimes we did not receive a reply at all
in a redis-cli session from these commands, blocking redis-cli trying
to read the reply.

This fixes issue #714.
2013-01-21 18:59:52 +01:00
antirez 1b0f244509 Fixed a bug in memtest progress bar, that had no actual effects.
This closes issue #859, thanks to @erbenmo.
2013-01-21 12:38:43 +01:00
antirez 580f65a50b Not every __sun has backtrace().
I don't know how to test for Open Solaris that has support for
backtrace() so for now removing the #ifdef that breaks compilation under
other Solaris flavors.
2013-01-21 12:07:44 +01:00
antirez a154908a8f Additionally two typos fixed thanks to @jodal 2013-01-19 13:46:24 +01:00
antirez da540228d8 Whitelist SIGUSR1 to avoid auto-triggering errors.
This commit fixes issue #875 that was caused by the following events:

1) There is an active child doing BGSAVE.
2) flushall is called (or any other condition that makes Redis killing
the saving child process).
3) An error is sensed by Redis as the child exited with an error (killed
by a singal), that stops accepting write commands until a BGSAVE happens
to be executed with success.

Whitelisting SIGUSR1 and making sure Redis always uses this signal in
order to kill its own children fixes the issue.
2013-01-19 13:30:41 +01:00
antirez 9e5b70162c Clear server.shutdown_asap on failed shutdown.
When a SIGTERM is received Redis schedules a shutdown. However if it
fails to perform the shutdown it must be clear the shutdown_asap flag
otehrwise it will try again and again possibly making the server
unusable.
2013-01-19 13:20:58 +01:00
antirez dbde1d85ea Slowlog: don't log EXEC but just the executed commands.
The Redis Slow Log always used to log the slow commands executed inside
a MULTI/EXEC block. However also EXEC was logged at the end, which is
perfectly useless.

Now EXEC is no longer logged and a test was added to test this behavior.

This fixes issue #759.
2013-01-19 12:55:12 +01:00
guiquanz 560e049947 Fixed many typos. 2013-01-19 11:08:43 +01:00
charsyam c2b43eac4f redis-cli prompt bug fix 2013-01-19 10:34:25 +01:00
Jan-Erik Rediger 5e2aa2cafb Always exit if connection fails.
This avoids unnecessary core dumps. Fixes antirez/redis#894
2013-01-19 10:29:51 +01:00
bitterb a732d86fe0 Fix an error reply for CLIENT command 2013-01-19 10:26:25 +01:00
Nathan Parry 0f4dbd9a11 redis-cli --rdb fails if server sends a ping
Redis pings slaves in "pre-synchronization stage" with newlines. (See
https://github.com/antirez/redis/blob/2.6.9/src/replication.c#L814)
However, redis-cli does not expect this - it sees the newline as the end
of the bulk length line, and ends up returning 0 as bulk the length.
This manifests as the following when running redis-cli:

    $ ./src/redis-cli --rdb some_file
    SYNC sent to master, writing 0 bytes to 'some_file'
    Transfer finished with success.

With this commit, we just ignore leading newlines while reading the bulk
length line.

To reproduce the problem, load enough data into Redis so that the
preparation of the RDB snapshot takes long enough for a ping to occur
while redis-cli is waiting for the data.
2013-01-18 11:49:58 +01:00
antirez 21159d53f5 Redis 2.6.9 2013-01-16 19:54:56 +01:00
antirez 52f6a1dc16 redis-cli: save an RDB dump from remote server to local file. 2013-01-16 19:48:26 +01:00
antirez f9c1263f31 Tests for CLIENT GETNAME/SETNAME. 2013-01-15 13:34:37 +01:00
antirez 1b2ac3b206 Typo fixed, ASCI -> ASCII. 2013-01-15 13:34:35 +01:00
antirez c5f23ca79b CLIENT GETNAME and CLIENT SETNAME introduced.
Sometimes it is much simpler to debug complex Redis installations if it
is possible to assign clients a name that is displayed in the CLIENT
LIST output.

This is the case, for example, for "leaked" connections. The ability to
provide a name to the client makes it quite trivial to understand what
is the part of the code implementing the client not releasing the
resources appropriately.

Behavior:

    CLIENT SETNAME: set a name for the client, or remove the current
                    name if an empty name is set.
    CLIENT GETNAME: get the current name, or a nil.
    CLIENT LIST: now displays the client name if any.

Thanks to Mark Gravell for pushing this idea forward.
2013-01-15 13:34:34 +01:00
antirez 27abaa238f Undo slave-master handshake when SLAVEOF sets a new slave.
Issue #828 shows how Redis was not correctly undoing a non-blocking
connection attempt with the previous master when the master was set to a
new address using the SLAVEOF command.

This was also a result of lack of refactoring, so now there is a
function to cancel the non blocking handshake with the master.
The new function is now used when SLAVEOF NO ONE is called or when
SLAVEOF is used to set the master to a different address.
2013-01-15 13:33:30 +01:00
antirez 78b175a241 Makefile.dep updated. 2013-01-11 23:51:00 +01:00
antirez 14cc0045dc Redis 2.6.8 2013-01-10 17:15:16 +01:00
antirez 023cbc3787 Comment in the call() function clarified a bit. 2013-01-10 12:04:52 +01:00
antirez b70b9c6027 Test: added regression for issue #872. 2013-01-10 12:04:48 +01:00
antirez 46dc3fbb53 Multiple fixes for EVAL (issue #872).
1) The event handler was no restored after a timeout condition if the
   command was eventually executed with success.
2) The command was not converted to EVAL in case of errors in the middle
   of the execution.
3) Terrible duplication of code without any apparent reason.
2013-01-10 12:04:45 +01:00
antirez c1c3e9b756 Better error reporting when fd event creation fails. 2013-01-03 14:32:11 +01:00
antirez 1f8ad7ae55 ae.c: set errno when error is not a failing syscall.
In this way the caller is able to perform better error checking or to
use strerror() without the risk of meaningless error messages being
displayed.
2013-01-03 14:32:06 +01:00
antirez 4468ba2317 Fix overflow in mstime() in redis-cli and benchmark.
The problem does not exist in the Redis server implementation of mstime()
but is only limited to redis-cli and redis-benchmark.

Thix fixes issue #839.
2012-12-20 15:21:37 +01:00
antirez d8a0fddd51 CONTRIBUTING updated with request to add BSD license. 2012-12-17 11:17:52 +01:00
antirez 886c9ecb7c Added missing license and copyright in deps/hiredis. 2012-12-17 11:11:27 +01:00
Patrick TJ McPhee 8e0a9f6027 Define _XOPEN_SOURCE appropriately on NetBSD. 2012-12-12 18:42:36 +01:00
antirez d64a9cf514 Fix config.h endianess detection to work on Linux / PPC64.
Config.h performs endianess detection including OS-specific headers to
define the endianess macros, or when this is not possible, checking the
processor type via ifdefs.

Sometimes when the OS-specific macro is included, only __BYTE_ORDER is
defined, while BYTE_ORDER remains undefined. There is code at the end of
config.h endianess detection in order to define the macros without the
underscore, but it was not working correctly.

This commit fixes endianess detection fixing Redis on Linux / PPC64 and
possibly other systems.
2012-12-11 17:19:58 +01:00
antirez d9301f05e2 Redis 2.6.7 2012-12-03 12:53:09 +01:00
Brian J. McManus 6c9897f6cf Issue 804 Add Default-Start and Default-Stop LSB tags for RedHat startup and update-rc.d compatability. 2012-12-03 12:31:10 +01:00
antirez 984f6edf0a Memory leak fixed: release client's bpop->keys dictionary.
Refactoring performed after issue #801 resolution (see commit
2f87cf8b01) introduced a memory leak that
is fixed by this commit.

I simply forgot to free the new allocated dictionary in the client
structure trusting the output of "make test" on OSX.

However due to changes in the "leaks" utility the test was no longer
testing memory leaks. This problem was also fixed.

Fortunately the CI test running at ci.redis.io spotted the bug in the
valgrind run.

The leak never ended into a stable release.
2012-12-03 12:17:56 +01:00
antirez a2b3fff235 Test: fixed osx "leaks" support in test.
Due to changes in recent releases of osx leaks utility, the osx leak
detection no longer worked. Now it is fixed in a way that should be
backward compatible.
2012-12-03 12:17:52 +01:00
antirez 54b08c86a6 Blocking POP: use a dictionary to store keys clinet side.
To store the keys we block for during a blocking pop operation, in the
case the client is blocked for more data to arrive, we used a simple
linear array of redis objects, in the blockingState structure:

    robj **keys;
    int count;

However in order to fix issue #801 we also use a dictionary in order to
avoid to end in the blocked clients queue for the same key multiple
times with the same client.

The dictionary was only temporary, just to avoid duplicates, but since
we create / destroy it there is no point in doing this duplicated work,
so this commit simply use a dictionary as the main structure to store
the keys we are blocked for. So instead of the previous fields we now
just have:

    dict *keys;

This simplifies the code and reduces the work done by the server during
a blocking POP operation.
2012-12-02 20:36:18 +01:00
antirez 6bdcd50b56 Test: regression for issue #801. 2012-12-01 23:07:09 +01:00
antirez cac49a9031 Client should not block multiple times on the same key.
Sending a command like:

BLPOP foo foo foo foo 0

Resulted into a crash before this commit since the client ended being
inserted in the waiting list for this key multiple times.
This resulted into the function handleClientsBlockedOnLists() to fail
because we have code like that:

    if (de) {
        list *clients = dictGetVal(de);
        int numclients = listLength(clients);

        while(numclients--) {
            listNode *clientnode = listFirst(clients);

            /* server clients here... */
        }
    }

The code to serve clients used to remove the served client from the
waiting list, so if a client is blocking multiple times, eventually the
call to listFirst() will return NULL or worse will access random memory
since the list may no longer exist as it is removed by the function
unblockClientWaitingData() if there are no more clients waiting for this
list.

To avoid making the rest of the implementation more complex, this commit
modifies blockForKeys() so that a client will be put just a single time
into the waiting list for a given key.

Since it is Saturday, I hope this fixes issue #801.
2012-12-01 12:26:07 +01:00
antirez ed70955510 Make an EXEC test more latency proof. 2012-11-29 16:12:26 +01:00
99 changed files with 2301 additions and 1006 deletions
+7 -2
View File
@@ -1,8 +1,13 @@
.*.swp
*.o
*.rdb
*.log
redis-*
dump.rdb
redis-benchmark
redis-check-aof
redis-check-dump
redis-cli
redis-sentinel
redis-server
doc-tools
release
misc/*
+106
View File
@@ -14,6 +14,112 @@ 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.6.13 ]
UPGRADE URGENCY: MODERATE, nothing very critical but upgrading is suggested
if you experienced:
1) Strange issues with Lua scripting.
2) Not reconfigured reappearing master using Sentinel.
3) Server continusly trying to save on save error.
This version of Redis may also help with AOF and slow / busy
disks and latency issues.
* [FIX] Throttle BGSAVE attempt on saving error.
* [FIX] redis-cli: raise error on bad command line switch.
* [FIX] Redis/Jemalloc Gitignore were too aggressive.
* [FIX] Test: fix RDB test checking file permissions.
* [FIX] Sentinel: always redirect on master->slave transition.
* [FIX] Lua updated to version 5.1.5. Fixes rare scripting issues.
* [NEW] AOF: improved latency figures with slow/busy disks.
* [NEW] Sentinel: turn old master into a slave when it comes back.
* [NEW] More explicit panic message on out of memory.
* [NEW] redis-cli: --latency-history mode implemented.
--[ Redis 2.6.12 ]
UPGRADE URGENCY: MODERATE, nothing very critical but a few non trivial bugs.
* [BUGFIX] redis-cli --bigkeys: don't crash with empty DB.
* [BUGFIX] stop-writes-on-bgsave-error now works in redis.conf
* [BUGFIX] Don't crash at startup if RDB is there but can't be opened.
* [BUGFIX] Initial value for master_link_down_since_seconds is now huge.
* [BUGFIX] Allow SELECT while loading the DB.
* [BUGFIX] Don't replicate/AOF an empty MULTI/EXEC if the transaction
is empty or containing just read-only commands.
* [BUGFIX] EXPIRE should not be able to resurrect keys (see issue #1026).
* [IMPROVED] Extended SET back ported from Redis 2.8 / unstable
See http://redis.io/commands/set for more information.
* [IMPROVED] Test suite improved.
--[ Redis 2.6.11 ]
UPGRADE URGENCY: LOW, however updating is encouraged if you have many instances
per server and you want to lower the CPU / energy usage.
* [BUGFIX] Replication: more strict error checking for master PING reply.
* [BUGFIX] redis-cli: use keepalive socket option for improved reliability.
* [BUGFIX] Allow AUTH while loading the DB in memory.
* [BUGFIX] Don't segfault on unbalanced quotes while parsing config file.
* [BUGFIX] Don't segfault if command gets propagated to AOF / replication
link as another command name that was renamed in redis.conf
* [IMPROVED] serverCron() frequency is now a runtime parameter (was REDIS_HZ).
* [IMPROVED] Use a lot less CPU when idle, even with many configured DBs.
--[ Redis 2.6.10 ]
UPGRADE URGENCY: MODERATE, this release contains many non-critical fixes
and many small improvements.
* [BUGFIX] redis-cli --rdb, fixed when the server sends newlines to ping.
* [BUGFIX] redis-cli, minor fixes on connection handling, prompt.
* [BUGFIX] Slow log: don't log EXEC, just executed commands.
* [BUGFIX] On failed shutdown don't try again and again compulsively.
* [BUGFIX] Fix build on sunos without backtrace().
* [BUGFIX] UNSUBSCRIBE and PUNSUBSCRIBE: always provide a reply (see 742e580)
* [BUGFIX] Lua struct library was broken, upgraded.
* [BUGFIX] Fix a bug in srandmemberWithCountCommand() with count argument.
* [BUGFIX] Test: disable clients timeout to prevent issues on slow systems.
* [BUGFIX] Sentinel: don't advertise the promoted slave as master too early.
* [IMPROVED] Whitelist SIGUSR1, see http://redis.io/topics/signals.
* [IMPROVED] Simpler to understand redis-cli --bigkeys output.
* [IMPROVED] Test now works with tclsh > 8.5.
* [IMPROVED] Added option to turn of the Nagle algorithm in slave socket.
* [IMPROVED] Optionally use SO_KEEPALIVE to detect dead peers.
--[ Redis 2.6.9 ]
UPGRADE URGENCY: MODERATE if you use replication.
* [BUGFIX] Changing master at runtime (SLAVEOF command) in presence of
network problems, or in very rapid succession, could result
in non-critical problems (GitHub Issue #828).
* [IMPROVED] CLINGET GETNAME and SETNAME to set and query connection names
reported by CLIENT LIST. Very useful for debugging of
problems.
* [IMPROVED] redis-cli is now able to transfer an RDB file from a remote
server to a local file using the --rdb <filename> command
line option.
--[ Redis 2.6.8 ]
UPGRADE URGENCY: MODERATE if you use Lua scripting. Otherwise LOW.
* [BUGFIX] Multiple fixes for EVAL (issue #872).
* [BUGFIX] Fix overflow in mstime() in redis-cli and benchmark.
* [BUGFIX] Fix Linux / PPC64 behavior by correcting endianess detection.
* [BUGFIX] Fix NetBSD build by defining _XOPEN_SOURCE appropriately.
* [BUGFIX] Added missing license and copyright in a few places.
* [BUGFIX] Better error reporting when fd event creation fails.
--[ Redis 2.6.7 ]
UPGRADE URGENCY: MODERATE (unless you BLPOP using the same key multiple times).
* [BUGFIX] Don't crash if BLPOP & co are called with the same key repeated
multiple times (Issue #801).
--[ Redis 2.6.6 ]
UPGRADE URGENCY: CRITICAL if you experienced one more more crashes.
+2 -1
View File
@@ -2,7 +2,8 @@ Note: by contributing code to the Redis project in any form, including sending
a pull request via Github, a code fragment or patch via private email or
public discussion groups, you agree to release your code under the terms
of the BSD license that you can find in the COPYING file included in the Redis
source distribution.
source distribution. You will include BSD license in the COPYING file within
each source file that you contribute.
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
+55 -8
View File
@@ -4,17 +4,64 @@
Redis Manifesto
===============
1 - A DSL for Abstract Data Types. Redis is a DSL (Domain Specific Language) that manipulates abstract data types and implemented as a TCP daemon. Commands manipulate a key space where keys are binary-safe strings and values are different kinds of abstract data types. Every data type represents an abstract version of a fundamental data structure. For instance Redis Lists are an abstract representation of linked lists. In Redis, the essence of a data type isn't just the kind of operations that the data types support, but also the space and time complexity of the data type and the operations performed upon it.
1 - A DSL for Abstract Data Types. Redis is a DSL (Domain Specific Language)
that manipulates abstract data types and implemented as a TCP daemon.
Commands manipulate a key space where keys are binary-safe strings and
values are different kinds of abstract data types. Every data type
represents an abstract version of a fundamental data structure. For instance
Redis Lists are an abstract representation of linked lists. In Redis, the
essence of a data type isn't just the kind of operations that the data types
support, but also the space and time complexity of the data type and the
operations performed upon it.
2 - Memory storage is #1. The Redis data set, composed of defined key-value pairs, is primarily stored in the computer's memory. The amount of memory in all kinds of computers, including entry-level servers, is increasing significantly each year. Memory is fast, and allows Redis to have very predictable performance. Datasets composed of 10k or 40 millions keys will perform similarly. Complex data types like Redis Sorted Sets are easy to implement and manipulate in memory with good performance, making Redis very simple. Redis will continue to explore alternative options (where data can be optionally stored on disk, say) but the main goal of the project remains the development of an in-memory database.
2 - Memory storage is #1. The Redis data set, composed of defined key-value
pairs, is primarily stored in the computer's memory. The amount of memory in
all kinds of computers, including entry-level servers, is increasing
significantly each year. Memory is fast, and allows Redis to have very
predictable performance. Datasets composed of 10k or 40 millions keys will
perform similarly. Complex data types like Redis Sorted Sets are easy to
implement and manipulate in memory with good performance, making Redis very
simple. Redis will continue to explore alternative options (where data can
be optionally stored on disk, say) but the main goal of the project remains
the development of an in-memory database.
3 - Fundamental data structures for a fundamental API. The Redis API is a direct consequence of fundamental data structures. APIs can often be arbitrary but not an API that resembles the nature of fundamental data structures. If we ever meet intelligent life forms from another part of the universe, they'll likely know, understand and recognize the same basic data structures we have in our computer science books. Redis will avoid intermediate layers in API, so that the complexity is obvious and more complex operations can be performed as the sum of the basic operations.
3 - Fundamental data structures for a fundamental API. The Redis API is a direct
consequence of fundamental data structures. APIs can often be arbitrary but
not an API that resembles the nature of fundamental data structures. If we
ever meet intelligent life forms from another part of the universe, they'll
likely know, understand and recognize the same basic data structures we have
in our computer science books. Redis will avoid intermediate layers in API,
so that the complexity is obvious and more complex operations can be
performed as the sum of the basic operations.
4 - Code is like a poem; it's not just something we write to reach some practical result. Sometimes people that are far from the Redis philosophy suggest using other code written by other authors (frequently in other languages) in order to implement something Redis currently lacks. But to us this is like if Shakespeare decided to end Enrico IV using the Paradiso from the Divina Commedia. Is using any external code a bad idea? Not at all. Like in "One Thousand and One Nights" smaller self contained stories are embedded in a bigger story, we'll be happy to use beautiful self contained libraries when needed. At the same time, when writing the Redis story we're trying to write smaller stories that will fit in to other code.
4 - Code is like a poem; it's not just something we write to reach some
practical result. Sometimes people that are far from the Redis philosophy
suggest using other code written by other authors (frequently in other
languages) in order to implement something Redis currently lacks. But to us
this is like if Shakespeare decided to end Enrico IV using the Paradiso from
the Divina Commedia. Is using any external code a bad idea? Not at all. Like
in "One Thousand and One Nights" smaller self contained stories are embedded
in a bigger story, we'll be happy to use beautiful self contained libraries
when needed. At the same time, when writing the Redis story we're trying to
write smaller stories that will fit in to other code.
5 - We're against complexity. We believe designing systems is a fight against complexity. We'll accept to fight the complexity when it's worthwhile but we'll try hard to recognize when a small feature is not worth 1000s of lines of code. Most of the time the best way to fight complexity is by not creating it at all.
5 - We're against complexity. We believe designing systems is a fight against
complexity. We'll accept to fight the complexity when it's worthwhile but
we'll try hard to recognize when a small feature is not worth 1000s of lines
of code. Most of the time the best way to fight complexity is by not
creating it at all.
6 - Two levels of API. The Redis API has two levels: 1) a subset of the API fits naturally into a distributed version of Redis and 2) a more complex API that supports multi-key operations. Both are useful if used judiciously but there's no way to make the more complex multi-keys API distributed in an opaque way without violating our other principles. We don't want to provide the illusion of something that will work magically when actually it can't in all cases. Instead we'll provide commands to quickly migrate keys from one instance to another to perform multi-key operations and expose the tradeoffs to the user.
7 - We optimize for joy. We believe writing code is a lot of hard work, and the only way it can be worth is by enjoying it. When there is no longer joy in writing code, the best thing to do is stop. To prevent this, we'll avoid taking paths that will make Redis less of a joy to develop.
6 - Two levels of API. The Redis API has two levels: 1) a subset of the API fits
naturally into a distributed version of Redis and 2) a more complex API that
supports multi-key operations. Both are useful if used judiciously but
there's no way to make the more complex multi-keys API distributed in an
opaque way without violating our other principles. We don't want to provide
the illusion of something that will work magically when actually it can't in
all cases. Instead we'll provide commands to quickly migrate keys from one
instance to another to perform multi-key operations and expose the tradeoffs
to the user.
7 - We optimize for joy. We believe writing code is a lot of hard work, and the
only way it can be worth is by enjoying it. When there is no longer joy in
writing code, the best thing to do is stop. To prevent this, we'll avoid
taking paths that will make Redis less of a joy to develop.
+30
View File
@@ -1,3 +1,33 @@
/*
* Copyright (c) 2010-2011, 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.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_AE_H__
#define __HIREDIS_AE_H__
#include <sys/types.h>
+30
View File
@@ -1,3 +1,33 @@
/*
* Copyright (c) 2010-2011, 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.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_LIBEV_H__
#define __HIREDIS_LIBEV_H__
#include <stdlib.h>
+30
View File
@@ -1,3 +1,33 @@
/*
* Copyright (c) 2010-2011, 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.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_LIBEVENT_H__
#define __HIREDIS_LIBEVENT_H__
#include <event.h>
+1 -1
View File
@@ -7,7 +7,7 @@
#if defined(__sun__)
#define _POSIX_C_SOURCE 200112L
#elif defined(__linux__)
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE
-5
View File
@@ -2,24 +2,19 @@
/config.stamp
/config.log
/config.status
/configure
/doc/html.xsl
/doc/manpages.xsl
/doc/jemalloc.xml
/doc/jemalloc.html
/doc/jemalloc.3
/lib/
/Makefile
/include/jemalloc/internal/jemalloc_internal\.h
/include/jemalloc/internal/size_classes\.h
/include/jemalloc/jemalloc\.h
/include/jemalloc/jemalloc_defs\.h
/test/jemalloc_test\.h
/src/*.[od]
/test/*.[od]
/test/*.out
/test/[a-zA-Z_]*
!test/*.c
!test/*.exp
/VERSION
/bin/jemalloc.sh
+1 -1
View File
@@ -9,7 +9,7 @@ For details and rationale, see http://www.lua.org/license.html .
===============================================================================
Copyright (C) 1994-2008 Lua.org, PUC-Rio.
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -48,7 +48,7 @@ TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.1
R= 5.1.4
R= 5.1.5
all: $(PLAT)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

+79 -81
View File
@@ -3,11 +3,10 @@
<HEAD>
<TITLE>Lua 5.1 Reference Manual - contents</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=utf-8">
<STYLE TYPE="text/css">
ul {
list-style-type: none ;
list-style-position: outside ;
}
</STYLE>
</HEAD>
@@ -20,7 +19,13 @@ ul {
Lua 5.1 Reference Manual
</H1>
This is an online version of
<P>
The reference manual is the official definition of the Lua language.
For a complete introduction to Lua programming, see the book
<A HREF="http://www.lua.org/docs.html#pil">Programming in Lua</A>.
<P>
This manual is also available as a book:
<BLOCKQUOTE>
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
<IMG SRC="cover.png" ALT="" TITLE="buy from Amazon" BORDER=1 ALIGN="left" HSPACE=12>
@@ -29,128 +34,119 @@ This is an online version of
<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes
<BR>Lua.org, August 2006
<BR>ISBN 85-903798-3-3
<BR><A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
<IMG SRC="amazon.gif" ALT="[Buy from Amazon]" BORDER=0></A>
<BR CLEAR="all">
</BLOCKQUOTE>
<P>
Buy a copy of this book and
<P>
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">Buy a copy</A>
of this book and
<A HREF="http://www.lua.org/donations.html">help to support</A>
the Lua project.
<P>
The reference manual is the official definition of the Lua language.
For a complete introduction to Lua programming, see the book
<A HREF="http://www.lua.org/docs.html#books">Programming in Lua</A>.
<P>
<A HREF="manual.html">start</A>
&middot;
<A HREF="#contents">contents</A>
&middot;
<A HREF="#index">index</A>
&middot;
<A HREF="http://www.lua.org/manual/5.1/pt/">português</A>
&middot;
<A HREF="http://www.lua.org/manual/5.1/es/">español</A>
<A HREF="http://www.lua.org/manual/">other versions</A>
<HR>
<SMALL>
Copyright &copy; 2006-2008 Lua.org, PUC-Rio.
Copyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.
Freely available under the terms of the
<a href="http://www.lua.org/license.html#5">Lua license</a>.
<A HREF="http://www.lua.org/license.html">Lua license</A>.
</SMALL>
<P>
<H2><A NAME="contents">Contents</A></H2>
<UL style="padding: 0">
<LI><A HREF="manual.html">1 - Introduction</A>
<LI><A HREF="manual.html">1 &ndash; Introduction</A>
<P>
<LI><A HREF="manual.html#2">2 - The Language</A>
<LI><A HREF="manual.html#2">2 &ndash; The Language</A>
<UL>
<LI><A HREF="manual.html#2.1">2.1 - Lexical Conventions</A>
<LI><A HREF="manual.html#2.2">2.2 - Values and Types</A>
<LI><A HREF="manual.html#2.1">2.1 &ndash; Lexical Conventions</A>
<LI><A HREF="manual.html#2.2">2.2 &ndash; Values and Types</A>
<UL>
<LI><A HREF="manual.html#2.2.1">2.2.1 - Coercion</A>
<LI><A HREF="manual.html#2.2.1">2.2.1 &ndash; Coercion</A>
</UL>
<LI><A HREF="manual.html#2.3">2.3 - Variables</A>
<LI><A HREF="manual.html#2.4">2.4 - Statements</A>
<LI><A HREF="manual.html#2.3">2.3 &ndash; Variables</A>
<LI><A HREF="manual.html#2.4">2.4 &ndash; Statements</A>
<UL>
<LI><A HREF="manual.html#2.4.1">2.4.1 - Chunks</A>
<LI><A HREF="manual.html#2.4.2">2.4.2 - Blocks</A>
<LI><A HREF="manual.html#2.4.3">2.4.3 - Assignment</A>
<LI><A HREF="manual.html#2.4.4">2.4.4 - Control Structures</A>
<LI><A HREF="manual.html#2.4.5">2.4.5 - For Statement</A>
<LI><A HREF="manual.html#2.4.6">2.4.6 - Function Calls as Statements</A>
<LI><A HREF="manual.html#2.4.7">2.4.7 - Local Declarations</A>
<LI><A HREF="manual.html#2.4.1">2.4.1 &ndash; Chunks</A>
<LI><A HREF="manual.html#2.4.2">2.4.2 &ndash; Blocks</A>
<LI><A HREF="manual.html#2.4.3">2.4.3 &ndash; Assignment</A>
<LI><A HREF="manual.html#2.4.4">2.4.4 &ndash; Control Structures</A>
<LI><A HREF="manual.html#2.4.5">2.4.5 &ndash; For Statement</A>
<LI><A HREF="manual.html#2.4.6">2.4.6 &ndash; Function Calls as Statements</A>
<LI><A HREF="manual.html#2.4.7">2.4.7 &ndash; Local Declarations</A>
</UL>
<LI><A HREF="manual.html#2.5">2.5 - Expressions</A>
<LI><A HREF="manual.html#2.5">2.5 &ndash; Expressions</A>
<UL>
<LI><A HREF="manual.html#2.5.1">2.5.1 - Arithmetic Operators</A>
<LI><A HREF="manual.html#2.5.2">2.5.2 - Relational Operators</A>
<LI><A HREF="manual.html#2.5.3">2.5.3 - Logical Operators</A>
<LI><A HREF="manual.html#2.5.4">2.5.4 - Concatenation</A>
<LI><A HREF="manual.html#2.5.5">2.5.5 - The Length Operator</A>
<LI><A HREF="manual.html#2.5.6">2.5.6 - Precedence</A>
<LI><A HREF="manual.html#2.5.7">2.5.7 - Table Constructors</A>
<LI><A HREF="manual.html#2.5.8">2.5.8 - Function Calls</A>
<LI><A HREF="manual.html#2.5.9">2.5.9 - Function Definitions</A>
<LI><A HREF="manual.html#2.5.1">2.5.1 &ndash; Arithmetic Operators</A>
<LI><A HREF="manual.html#2.5.2">2.5.2 &ndash; Relational Operators</A>
<LI><A HREF="manual.html#2.5.3">2.5.3 &ndash; Logical Operators</A>
<LI><A HREF="manual.html#2.5.4">2.5.4 &ndash; Concatenation</A>
<LI><A HREF="manual.html#2.5.5">2.5.5 &ndash; The Length Operator</A>
<LI><A HREF="manual.html#2.5.6">2.5.6 &ndash; Precedence</A>
<LI><A HREF="manual.html#2.5.7">2.5.7 &ndash; Table Constructors</A>
<LI><A HREF="manual.html#2.5.8">2.5.8 &ndash; Function Calls</A>
<LI><A HREF="manual.html#2.5.9">2.5.9 &ndash; Function Definitions</A>
</UL>
<LI><A HREF="manual.html#2.6">2.6 - Visibility Rules</A>
<LI><A HREF="manual.html#2.7">2.7 - Error Handling</A>
<LI><A HREF="manual.html#2.8">2.8 - Metatables</A>
<LI><A HREF="manual.html#2.9">2.9 - Environments</A>
<LI><A HREF="manual.html#2.10">2.10 - Garbage Collection</A>
<LI><A HREF="manual.html#2.6">2.6 &ndash; Visibility Rules</A>
<LI><A HREF="manual.html#2.7">2.7 &ndash; Error Handling</A>
<LI><A HREF="manual.html#2.8">2.8 &ndash; Metatables</A>
<LI><A HREF="manual.html#2.9">2.9 &ndash; Environments</A>
<LI><A HREF="manual.html#2.10">2.10 &ndash; Garbage Collection</A>
<UL>
<LI><A HREF="manual.html#2.10.1">2.10.1 - Garbage-Collection Metamethods</A>
<LI><A HREF="manual.html#2.10.2">2.10.2 - Weak Tables</A>
<LI><A HREF="manual.html#2.10.1">2.10.1 &ndash; Garbage-Collection Metamethods</A>
<LI><A HREF="manual.html#2.10.2">2.10.2 &ndash; Weak Tables</A>
</UL>
<LI><A HREF="manual.html#2.11">2.11 - Coroutines</A>
<LI><A HREF="manual.html#2.11">2.11 &ndash; Coroutines</A>
</UL>
<P>
<LI><A HREF="manual.html#3">3 - The Application Program Interface</A>
<LI><A HREF="manual.html#3">3 &ndash; The Application Program Interface</A>
<UL>
<LI><A HREF="manual.html#3.1">3.1 - The Stack</A>
<LI><A HREF="manual.html#3.2">3.2 - Stack Size</A>
<LI><A HREF="manual.html#3.3">3.3 - Pseudo-Indices</A>
<LI><A HREF="manual.html#3.4">3.4 - C Closures</A>
<LI><A HREF="manual.html#3.5">3.5 - Registry</A>
<LI><A HREF="manual.html#3.6">3.6 - Error Handling in C</A>
<LI><A HREF="manual.html#3.7">3.7 - Functions and Types</A>
<LI><A HREF="manual.html#3.8">3.8 - The Debug Interface</A>
<LI><A HREF="manual.html#3.1">3.1 &ndash; The Stack</A>
<LI><A HREF="manual.html#3.2">3.2 &ndash; Stack Size</A>
<LI><A HREF="manual.html#3.3">3.3 &ndash; Pseudo-Indices</A>
<LI><A HREF="manual.html#3.4">3.4 &ndash; C Closures</A>
<LI><A HREF="manual.html#3.5">3.5 &ndash; Registry</A>
<LI><A HREF="manual.html#3.6">3.6 &ndash; Error Handling in C</A>
<LI><A HREF="manual.html#3.7">3.7 &ndash; Functions and Types</A>
<LI><A HREF="manual.html#3.8">3.8 &ndash; The Debug Interface</A>
</UL>
<P>
<LI><A HREF="manual.html#4">4 - The Auxiliary Library</A>
<LI><A HREF="manual.html#4">4 &ndash; The Auxiliary Library</A>
<UL>
<LI><A HREF="manual.html#4.1">4.1 - Functions and Types</A>
<LI><A HREF="manual.html#4.1">4.1 &ndash; Functions and Types</A>
</UL>
<P>
<LI><A HREF="manual.html#5">5 - Standard Libraries</A>
<LI><A HREF="manual.html#5">5 &ndash; Standard Libraries</A>
<UL>
<LI><A HREF="manual.html#5.1">5.1 - Basic Functions</A>
<LI><A HREF="manual.html#5.2">5.2 - Coroutine Manipulation</A>
<LI><A HREF="manual.html#5.3">5.3 - Modules</A>
<LI><A HREF="manual.html#5.4">5.4 - String Manipulation</A>
<LI><A HREF="manual.html#5.1">5.1 &ndash; Basic Functions</A>
<LI><A HREF="manual.html#5.2">5.2 &ndash; Coroutine Manipulation</A>
<LI><A HREF="manual.html#5.3">5.3 &ndash; Modules</A>
<LI><A HREF="manual.html#5.4">5.4 &ndash; String Manipulation</A>
<UL>
<LI><A HREF="manual.html#5.4.1">5.4.1 - Patterns</A>
<LI><A HREF="manual.html#5.4.1">5.4.1 &ndash; Patterns</A>
</UL>
<LI><A HREF="manual.html#5.5">5.5 - Table Manipulation</A>
<LI><A HREF="manual.html#5.6">5.6 - Mathematical Functions</A>
<LI><A HREF="manual.html#5.7">5.7 - Input and Output Facilities</A>
<LI><A HREF="manual.html#5.8">5.8 - Operating System Facilities</A>
<LI><A HREF="manual.html#5.9">5.9 - The Debug Library</A>
<LI><A HREF="manual.html#5.5">5.5 &ndash; Table Manipulation</A>
<LI><A HREF="manual.html#5.6">5.6 &ndash; Mathematical Functions</A>
<LI><A HREF="manual.html#5.7">5.7 &ndash; Input and Output Facilities</A>
<LI><A HREF="manual.html#5.8">5.8 &ndash; Operating System Facilities</A>
<LI><A HREF="manual.html#5.9">5.9 &ndash; The Debug Library</A>
</UL>
<P>
<LI><A HREF="manual.html#6">6 - Lua Stand-alone</A>
<LI><A HREF="manual.html#6">6 &ndash; Lua Stand-alone</A>
<P>
<LI><A HREF="manual.html#7">7 - Incompatibilities with the Previous Version</A>
<LI><A HREF="manual.html#7">7 &ndash; Incompatibilities with the Previous Version</A>
<UL>
<LI><A HREF="manual.html#7.1">7.1 - Changes in the Language</A>
<LI><A HREF="manual.html#7.2">7.2 - Changes in the Libraries</A>
<LI><A HREF="manual.html#7.3">7.3 - Changes in the API</A>
<LI><A HREF="manual.html#7.1">7.1 &ndash; Changes in the Language</A>
<LI><A HREF="manual.html#7.2">7.2 &ndash; Changes in the Libraries</A>
<LI><A HREF="manual.html#7.3">7.3 &ndash; Changes in the API</A>
</UL>
<P>
<LI><A HREF="manual.html#8">8 - The Complete Syntax of Lua</A>
<LI><A HREF="manual.html#8">8 &ndash; The Complete Syntax of Lua</A>
</UL>
<H2><A NAME="index">Index</A></H2>
@@ -160,6 +156,8 @@ Freely available under the terms of the
<H3><A NAME="functions">Lua functions</A></H3>
<A HREF="manual.html#pdf-_G">_G</A><BR>
<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
<P>
<A HREF="manual.html#pdf-assert">assert</A><BR>
<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
<A HREF="manual.html#pdf-dofile">dofile</A><BR>
@@ -487,12 +485,12 @@ Freely available under the terms of the
<P>
<HR>
<SMALL>
<SMALL CLASS="footer">
Last update:
Sat Jan 19 13:24:29 BRST 2008
Mon Feb 13 18:53:32 BRST 2012
</SMALL>
<!--
Last change: revised for Lua 5.1.3
Last change: revised for Lua 5.1.5
-->
</BODY>
+45 -3
View File
@@ -1,17 +1,37 @@
body {
color: #000000 ;
background-color: #FFFFFF ;
font-family: sans-serif ;
font-family: Helvetica, Arial, sans-serif ;
text-align: justify ;
margin-right: 20px ;
margin-left: 20px ;
margin-right: 30px ;
margin-left: 30px ;
}
h1, h2, h3, h4 {
font-family: Verdana, Geneva, sans-serif ;
font-weight: normal ;
font-style: italic ;
}
h2 {
padding-top: 0.4em ;
padding-bottom: 0.4em ;
padding-left: 30px ;
padding-right: 30px ;
margin-left: -30px ;
background-color: #E0E0FF ;
}
h3 {
padding-left: 0.5em ;
border-left: solid #E0E0FF 1em ;
}
table h3 {
padding-left: 0px ;
border-left: none ;
}
a:link {
color: #000080 ;
background-color: inherit ;
@@ -39,3 +59,25 @@ hr {
background-color: #a0a0a0 ;
}
:target {
background-color: #F8F8F8 ;
padding: 8px ;
border: solid #a0a0a0 2px ;
}
.footer {
color: gray ;
font-size: small ;
}
input[type=text] {
border: solid #a0a0a0 2px ;
border-radius: 2em ;
-moz-border-radius: 2em ;
background-image: url('images/search.png') ;
background-repeat: no-repeat;
background-position: 4px center ;
padding-left: 20px ;
height: 2em ;
}
+13 -2
View File
@@ -1,13 +1,24 @@
h3 code {
font-family: inherit ;
font-size: inherit ;
}
pre {
font-size: 105% ;
pre, code {
font-size: 12pt ;
}
span.apii {
float: right ;
font-family: inherit ;
font-style: normal ;
font-size: small ;
color: gray ;
}
p+h1, ul+h1 {
padding-top: 0.4em ;
padding-bottom: 0.4em ;
padding-left: 30px ;
margin-left: -30px ;
background-color: #E0E0FF ;
}
+17 -14
View File
@@ -19,9 +19,9 @@ Lua 5.1 Reference Manual
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<p>
<small>
Copyright &copy; 2006-2008 Lua.org, PUC-Rio.
Copyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.
Freely available under the terms of the
<a href="http://www.lua.org/license.html#5">Lua license</a>.
<a href="http://www.lua.org/license.html">Lua license</a>.
</small>
<hr>
<p>
@@ -29,11 +29,13 @@ Freely available under the terms of the
<a href="contents.html#contents">contents</A>
&middot;
<a href="contents.html#index">index</A>
&middot;
<A HREF="http://www.lua.org/manual/">other versions</A>
<!-- ====================================================================== -->
<p>
<!-- $Id: manual.of,v 1.48 2008/08/18 15:24:20 roberto Exp $ -->
<!-- $Id: manual.of,v 1.49.1.2 2012/01/13 20:23:26 roberto Exp $ -->
@@ -5645,7 +5647,7 @@ Currently, Lua has the following standard libraries:
<ul>
<li>basic library,</li> which includes the coroutine sub-library;
<li>basic library, which includes the coroutine sub-library;</li>
<li>package library;</li>
@@ -5709,7 +5711,7 @@ when absent, it defaults to "assertion failed!"
<p>
<hr><h3><a name="pdf-collectgarbage"><code>collectgarbage (opt [, arg])</code></a></h3>
<hr><h3><a name="pdf-collectgarbage"><code>collectgarbage ([opt [, arg]])</code></a></h3>
<p>
@@ -5718,6 +5720,11 @@ It performs different functions according to its first argument, <code>opt</code
<ul>
<li><b>"collect":</b>
performs a full garbage-collection cycle.
This is the default option.
</li>
<li><b>"stop":</b>
stops the garbage collector.
</li>
@@ -5726,10 +5733,6 @@ stops the garbage collector.
restarts the garbage collector.
</li>
<li><b>"collect":</b>
performs a full garbage-collection cycle.
</li>
<li><b>"count":</b>
returns the total memory in use by Lua (in Kbytes).
</li>
@@ -5760,7 +5763,7 @@ Returns the previous value for <em>step</em>.
<p>
<hr><h3><a name="pdf-dofile"><code>dofile (filename)</code></a></h3>
<hr><h3><a name="pdf-dofile"><code>dofile ([filename])</code></a></h3>
Opens the named file and executes its contents as a Lua chunk.
When called without arguments,
<code>dofile</code> executes the contents of the standard input (<code>stdin</code>).
@@ -8421,7 +8424,7 @@ Otherwise, it returns the name of the upvalue.
<p>
<hr><h3><a name="pdf-debug.traceback"><code>debug.traceback ([thread,] [message] [, level])</code></a></h3>
<hr><h3><a name="pdf-debug.traceback"><code>debug.traceback ([thread,] [message [, level]])</code></a></h3>
<p>
@@ -8789,12 +8792,12 @@ Here is the complete syntax of Lua in extended BNF.
<HR>
<SMALL>
<SMALL CLASS="footer">
Last update:
Mon Aug 18 13:25:46 BRT 2008
Mon Feb 13 18:54:19 BRST 2012
</SMALL>
<!--
Last change: revised for Lua 5.1.4
Last change: revised for Lua 5.1.5
-->
</body></html>
+2 -2
View File
@@ -12,7 +12,7 @@
Documentation
</H1>
This is the documentation included in the source distribution of Lua 5.1.4.
This is the documentation included in the source distribution of Lua 5.1.5.
<UL>
<LI><A HREF="contents.html">Reference manual</A>
@@ -33,7 +33,7 @@ especially the
<HR>
<SMALL>
Last update:
Tue Aug 12 14:46:07 BRT 2008
Fri Feb 3 09:44:42 BRST 2012
</SMALL>
</BODY>
+1 -1
View File
@@ -5,7 +5,7 @@
# grep '^V=' ../Makefile
V= 5.1
# grep '^R=' ../Makefile
R= 5.1.4
R= 5.1.5
# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
prefix= /usr/local
+1 -1
View File
@@ -48,7 +48,7 @@ o: $(ALL_O)
a: $(ALL_A)
$(LUA_A): $(CORE_O) $(LIB_O)
$(AR) $@ $?
$(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A)
+1 -1
View File
@@ -631,7 +631,7 @@ static void base_open (lua_State *L) {
luaL_register(L, "_G", base_funcs);
lua_pushliteral(L, LUA_VERSION);
lua_setglobal(L, "_VERSION"); /* set global _VERSION */
/* `ipairs' and `pairs' need auxliliary functions as upvalues */
/* `ipairs' and `pairs' need auxiliary functions as upvalues */
auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
auxopen(L, "pairs", luaB_pairs, luaB_next);
/* `newproxy' needs a weaktable as upvalue */
+1 -9
View File
@@ -1,5 +1,5 @@
/*
** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
@@ -544,10 +544,6 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
pc = NO_JUMP; /* always true; do nothing */
break;
}
case VFALSE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
invertjump(fs, e);
pc = e->u.s.info;
@@ -572,10 +568,6 @@ static void luaK_goiffalse (FuncState *fs, expdesc *e) {
pc = NO_JUMP; /* always false; do nothing */
break;
}
case VTRUE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
pc = e->u.s.info;
break;
+2 -1
View File
@@ -1,5 +1,5 @@
/*
** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
@@ -45,6 +45,7 @@ static int db_setmetatable (lua_State *L) {
static int db_getfenv (lua_State *L) {
luaL_checkany(L, 1);
lua_getfenv(L, 1);
return 1;
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@@ -217,6 +217,7 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
int nvar = actual - nfixargs; /* number of extra arguments */
lua_assert(p->is_vararg & VARARG_HASARG);
luaC_checkGC(L);
luaD_checkstack(L, p->maxstacksize);
htab = luaH_new(L, nvar, 1); /* create `arg' table */
for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
+1 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -627,7 +627,6 @@ void luaC_step (lua_State *L) {
}
}
else {
lua_assert(g->totalbytes >= g->estimate);
setthreshold(g);
}
}
+5 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
@@ -276,7 +276,10 @@ static int read_number (lua_State *L, FILE *f) {
lua_pushnumber(L, d);
return 1;
}
else return 0; /* read fails */
else {
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
}
}
+4 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
@@ -118,8 +118,10 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TString *ts = luaS_newlstr(L, str, l);
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
if (ttisnil(o))
if (ttisnil(o)) {
setbvalue(o, 1); /* make sure `str' will not be collected */
luaC_checkGC(L);
}
return ts;
}
+2 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 roberto Exp $
** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
@@ -639,7 +639,7 @@ LUALIB_API int luaopen_package (lua_State *L) {
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
/* create `loaders' table */
lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);
/* fill it with pre-defined loaders */
for (i=0; loaders[i] != NULL; i++) {
lua_pushcfunction(L, loaders[i]);
+2 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $
** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -374,9 +374,9 @@ static void close_func (LexState *ls) {
lua_assert(luaG_checkcode(f));
lua_assert(fs->bl == NULL);
ls->fs = fs->prev;
L->top -= 2; /* remove table and prototype from the stack */
/* last token read was anchored in defunct function; must reanchor it */
if (fs) anchor_token(ls);
L->top -= 2; /* remove table and prototype from the stack */
}
+4 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: lstrlib.c,v 1.132.1.4 2008/07/11 17:27:21 roberto Exp $
** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
@@ -754,6 +754,7 @@ static void addintlen (char *form) {
static int str_format (lua_State *L) {
int top = lua_gettop(L);
int arg = 1;
size_t sfl;
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
@@ -768,7 +769,8 @@ static int str_format (lua_State *L) {
else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
arg++;
if (++arg > top)
luaL_argerror(L, arg, "no value");
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
+4 -4
View File
@@ -1,5 +1,5 @@
/*
** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $
** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
@@ -17,9 +17,9 @@
#define LUA_VERSION "Lua 5.1"
#define LUA_RELEASE "Lua 5.1.4"
#define LUA_RELEASE "Lua 5.1.5"
#define LUA_VERSION_NUM 501
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
@@ -362,7 +362,7 @@ struct lua_Debug {
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
+119 -52
View File
@@ -1,18 +1,7 @@
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
/*
** {======================================================
** Library for packing/unpacking structures.
** $Id: struct.c,v 1.2 2008/04/18 20:06:01 roberto Exp $
** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
** See Copyright Notice at the end of this file
** =======================================================
*/
@@ -25,6 +14,7 @@
** b/B - signed/unsigned byte
** h/H - signed/unsigned short
** l/L - signed/unsigned long
** T - size_t
** i/In - signed/unsigned integer with size `n' (default is size of int)
** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
the whole string; when unpacking, n==0 means use the previous
@@ -36,6 +26,38 @@
*/
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#if (LUA_VERSION_NUM >= 502)
#define luaL_register(L,n,f) luaL_newlib(L,f)
#endif
/* basic integer type */
#if !defined(STRUCT_INT)
#define STRUCT_INT long
#endif
typedef STRUCT_INT Inttype;
/* corresponding unsigned version */
typedef unsigned STRUCT_INT Uinttype;
/* maximum size (in bytes) for integral types */
#define MAXINTSIZE 32
/* is 'x' a power of 2? */
#define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0)
@@ -67,11 +89,11 @@ typedef struct Header {
} Header;
static size_t getnum (const char **fmt, size_t df) {
static int getnum (const char **fmt, int df) {
if (!isdigit(**fmt)) /* no number? */
return df; /* return default value */
else {
size_t a = 0;
int a = 0;
do {
a = a*10 + *((*fmt)++) - '0';
} while (isdigit(**fmt));
@@ -89,33 +111,40 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
case 'B': case 'b': return sizeof(char);
case 'H': case 'h': return sizeof(short);
case 'L': case 'l': return sizeof(long);
case 'T': return sizeof(size_t);
case 'f': return sizeof(float);
case 'd': return sizeof(double);
case 'x': return 1;
case 'c': return getnum(fmt, 1);
case 's': case ' ': case '<': case '>': case '!': return 0;
case 'i': case 'I': {
int sz = getnum(fmt, sizeof(int));
if (!isp2(sz))
luaL_error(L, "integral size %d is not a power of 2", sz);
if (sz > MAXINTSIZE)
luaL_error(L, "integral size %d is larger than limit of %d",
sz, MAXINTSIZE);
return sz;
}
default: {
const char *msg = lua_pushfstring(L, "invalid format option [%c]", opt);
return luaL_argerror(L, 1, msg);
}
default: return 0; /* other cases do not need alignment */
}
}
/*
** return number of bytes needed to align an element of size 'size'
** at current position 'len'
*/
static int gettoalign (size_t len, Header *h, int opt, size_t size) {
if (size == 0 || opt == 'c') return 0;
if (size > (size_t)h->align) size = h->align; /* respect max. alignment */
return (size - (len & (size - 1))) & (size - 1);
if (size > (size_t)h->align)
size = h->align; /* respect max. alignment */
return (size - (len & (size - 1))) & (size - 1);
}
static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
/*
** options to control endianess and alignment
*/
static void controloptions (lua_State *L, int opt, const char **fmt,
Header *h) {
switch (opt) {
case ' ': return; /* ignore white spaces */
case '>': h->endian = BIG; return;
@@ -127,7 +156,10 @@ static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
h->align = a;
return;
}
default: assert(0);
default: {
const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
luaL_argerror(L, 1, msg);
}
}
}
@@ -135,21 +167,27 @@ static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
int size) {
lua_Number n = luaL_checknumber(L, arg);
unsigned long value;
if (n < (lua_Number)LONG_MAX)
value = (long)n;
Uinttype value;
char buff[MAXINTSIZE];
if (n < 0)
value = (Uinttype)(Inttype)n;
else
value = (unsigned long)n;
value = (Uinttype)n;
if (endian == LITTLE) {
int i;
for (i = 0; i < size; i++)
luaL_addchar(b, (value >> 8*i) & 0xff);
for (i = 0; i < size; i++) {
buff[i] = (value & 0xff);
value >>= 8;
}
}
else {
int i;
for (i = size - 1; i >= 0; i--)
luaL_addchar(b, (value >> 8*i) & 0xff);
for (i = size - 1; i >= 0; i--) {
buff[i] = (value & 0xff);
value >>= 8;
}
}
luaL_addlstring(b, buff, size);
}
@@ -179,15 +217,15 @@ static int b_pack (lua_State *L) {
size_t size = optsize(L, opt, &fmt);
int toalign = gettoalign(totalsize, &h, opt, size);
totalsize += toalign;
while (toalign-- > 0) luaL_putchar(&b, '\0');
while (toalign-- > 0) luaL_addchar(&b, '\0');
switch (opt) {
case 'b': case 'B': case 'h': case 'H':
case 'l': case 'L': case 'i': case 'I': { /* integer types */
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
putinteger(L, &b, arg++, h.endian, size);
break;
}
case 'x': {
luaL_putchar(&b, '\0');
luaL_addchar(&b, '\0');
break;
}
case 'f': {
@@ -209,12 +247,12 @@ static int b_pack (lua_State *L) {
luaL_argcheck(L, l >= (size_t)size, arg, "string too short");
luaL_addlstring(&b, s, size);
if (opt == 's') {
luaL_putchar(&b, '\0'); /* add zero at the end */
luaL_addchar(&b, '\0'); /* add zero at the end */
size++;
}
break;
}
default: commoncases(L, opt, &fmt, &h);
default: controloptions(L, opt, &fmt, &h);
}
totalsize += size;
}
@@ -225,24 +263,27 @@ static int b_pack (lua_State *L) {
static lua_Number getinteger (const char *buff, int endian,
int issigned, int size) {
unsigned long l = 0;
Uinttype l = 0;
int i;
if (endian == BIG) {
int i;
for (i = 0; i < size; i++)
l |= (unsigned long)(unsigned char)buff[size - i - 1] << (i*8);
for (i = 0; i < size; i++) {
l <<= 8;
l |= (Uinttype)(unsigned char)buff[i];
}
}
else {
int i;
for (i = 0; i < size; i++)
l |= (unsigned long)(unsigned char)buff[i] << (i*8);
for (i = size - 1; i >= 0; i--) {
l <<= 8;
l |= (Uinttype)(unsigned char)buff[i];
}
}
if (!issigned)
return (lua_Number)l;
else { /* signed format */
unsigned long mask = ~(0UL) << (size*8 - 1);
Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
if (l & mask) /* negative value? */
l |= mask; /* signal extension */
return (lua_Number)(long)l;
return (lua_Number)(Inttype)l;
}
}
@@ -260,9 +301,10 @@ static int b_unpack (lua_State *L) {
size_t size = optsize(L, opt, &fmt);
pos += gettoalign(pos, &h, opt, size);
luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
luaL_checkstack(L, 1, "too many results");
switch (opt) {
case 'b': case 'B': case 'h': case 'H':
case 'l': case 'L': case 'i': case 'I': { /* integer types */
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
int issigned = islower(opt);
lua_Number res = getinteger(data+pos, h.endian, issigned, size);
lua_pushnumber(L, res);
@@ -304,7 +346,7 @@ static int b_unpack (lua_State *L) {
lua_pushlstring(L, data+pos, size - 1);
break;
}
default: commoncases(L, opt, &fmt, &h);
default: controloptions(L, opt, &fmt, &h);
}
pos += size;
}
@@ -312,26 +354,50 @@ static int b_unpack (lua_State *L) {
return lua_gettop(L) - 2;
}
static int b_size (lua_State *L) {
Header h;
const char *fmt = luaL_checkstring(L, 1);
size_t pos = 0;
defaultoptions(&h);
while (*fmt) {
int opt = *fmt++;
size_t size = optsize(L, opt, &fmt);
pos += gettoalign(pos, &h, opt, size);
if (opt == 's')
luaL_argerror(L, 1, "option 's' has no fixed size");
else if (opt == 'c' && size == 0)
luaL_argerror(L, 1, "option 'c0' has no fixed size");
if (!isalnum(opt))
controloptions(L, opt, &fmt, &h);
pos += size;
}
lua_pushinteger(L, pos);
return 1;
}
/* }====================================================== */
static const struct luaL_reg thislib[] = {
static const struct luaL_Reg thislib[] = {
{"pack", b_pack},
{"unpack", b_unpack},
{"size", b_size},
{NULL, NULL}
};
LUALIB_API int luaopen_struct (lua_State *L);
LUALIB_API int luaopen_struct (lua_State *L) {
luaL_register(L, "struct", thislib);
return 1;
}
/******************************************************************************
* Copyright (C) 2010 Lua.org, PUC-Rio. All rights reserved.
* Copyright (C) 2010-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -352,3 +418,4 @@ LUALIB_API int luaopen_struct (lua_State *L) {
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
+6 -2
View File
@@ -1,5 +1,5 @@
/*
** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@@ -133,6 +133,7 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
TValue temp;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
@@ -141,6 +142,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
if (!ttisnil(oldval) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
setobj2t(L, oldval, val);
h->flags = 0;
luaC_barriert(L, h, val);
return;
}
@@ -152,7 +154,9 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
callTM(L, tm, t, key, val);
return;
}
t = tm; /* else repeat with `tm' */
/* else repeat with `tm' */
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
t = &temp;
}
luaG_runerror(L, "loop in settable");
}
+77 -20
View File
@@ -39,8 +39,24 @@ port 6379
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0
# Set server verbosity to 'debug'
# it can be one of:
# TCP keepalive.
#
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Take the connection alive from the point of view of network
# equipment in the middle.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
# On other kernels the period depends on the kernel configuration.
#
# A reasonable value for this option is 60 seconds.
tcp-keepalive 0
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
@@ -59,7 +75,7 @@ logfile stdout
# Specify the syslog identity.
# syslog-ident redis
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
# Set the number of databases. The default database is DB 0, you can select
@@ -114,7 +130,7 @@ stop-writes-on-bgsave-error yes
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
# Since verison 5 of RDB a CRC64 checksum is placed at the end of the file.
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
@@ -131,7 +147,7 @@ dbfilename dump.rdb
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# Also the Append Only File will be created inside this directory.
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
@@ -152,14 +168,14 @@ dir ./
#
# masterauth <master-password>
# When a slave lost the connection with the master, or when the replication
# When a slave loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
#
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
# still reply to client requests, possibly with out of date data, or the
# data set may just be empty if this is the first synchronization.
#
# 2) if slave-serve-stale data is set to 'no' the slave will reply with
# 2) if slave-serve-stale-data is set to 'no' the slave will reply with
# an error "SYNC with master in progress" to all the kind of commands
# but to INFO and SLAVEOF.
#
@@ -196,6 +212,21 @@ slave-read-only yes
#
# repl-timeout 60
# Disable TCP_NODELAY on the slave socket after SYNC?
#
# If you select "yes" Redis will use a smaller number of TCP packets and
# less bandwidth to send data to slaves. But this can add a delay for
# the data to appear on the slave side, up to 40 milliseconds with
# Linux kernels using a default configuration.
#
# If you select "no" the delay for data to appear on the slave side will
# be reduced but more bandwidth will be used for replication.
#
# By default we optimize for low latency, but in very high traffic conditions
# or when the master and slaves are many hops away, turning this to "yes" may
# be a good idea.
repl-disable-tcp-nodelay no
# The slave priority is an integer number published by Redis in the INFO output.
# It is used by Redis Sentinel in order to select a slave to promote into a
# master if the master is no longer working correctly.
@@ -230,23 +261,26 @@ slave-priority 100
#
# It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# of hard to guess so that it will be still available for internal-use
# tools but not available for general clients.
# hard to guess so that it will still be available for internal-use tools
# but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command renaming it into
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG ""
#
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to slaves may cause problems.
################################### LIMITS ####################################
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able ot configure the process file limit to allow for the specified limit
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
@@ -281,7 +315,7 @@ slave-priority 100
# maxmemory <bytes>
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached? You can select among five behavior:
# 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
@@ -290,7 +324,7 @@ slave-priority 100
# 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 all the kind of policies, Redis will return an error on write
# 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
@@ -346,7 +380,7 @@ appendonly no
# always: fsync after every write to the append only log . Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec" that's usually the right compromise between
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
@@ -374,9 +408,9 @@ appendfsync everysec
# that will prevent fsync() from being called in the main process while a
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving the durability of Redis is
# the same as "appendfsync none", that in practical terms means that it is
# possible to lost up to 30 seconds of log in the worst scenario (with the
# This means that while another child is saving, the durability of Redis is
# 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
@@ -385,10 +419,10 @@ 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 will growth by the specified percentage.
# 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 (or if no rewrite happened since the restart, the size of
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
@@ -524,11 +558,34 @@ activerehashing yes
# Instead there is a default limit for pubsub and slave clients, since
# subscribers and slaves receive data in a push fashion.
#
# Both the hard or the soft limit can be disabled just setting it to zero.
# Both the hard or the soft limit can be disabled by setting them to zero.
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeot, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are perforemd with the same frequency, but Redis checks for
# tasks to perform accordingly 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
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10
# When a child rewrites the AOF file, if the following option is enabled
# the file will be fsync-ed every 32 MB of data generated. This is useful
# in order to commit the file to the disk more incrementally and avoid
# big latency spikes.
aof-rewrite-incremental-fsync yes
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
+10 -5
View File
@@ -1,9 +1,14 @@
#!/bin/sh
TCL=tclsh8.5
which $TCL
if [ "$?" != "0" ]
TCL_VERSIONS="8.5 8.6"
TCLSH=""
for VERSION in $TCL_VERSIONS; do
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
done
if [ -z $TCLSH ]
then
echo "You need '$TCL' in order to run the Redis test"
echo "You need tcl 8.5 or newer in order to run the Redis test"
exit 1
fi
$TCL tests/test_helper.tcl $*
$TCLSH tests/test_helper.tcl $*
+1 -1
View File
@@ -71,7 +71,7 @@ sentinel parallel-syncs mymaster 1
# Default is 15 minutes.
sentinel failover-timeout mymaster 900000
# SCRIPTS EXECTION
# SCRIPTS EXECUTION
#
# sentinel notification-script and sentinel reconfig-script are used in order
# to configure scripts that are called to notify the system administrator
+4 -1
View File
@@ -217,7 +217,10 @@ gcov:
$(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
noopt:
$(MAKE) OPT="-O0"
$(MAKE) OPTIMIZATION="-O0"
valgrind:
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
src/help.h:
@../utils/generate-command-help.rb > help.h
+13 -4
View File
@@ -1,6 +1,7 @@
adlist.o: adlist.c adlist.h zmalloc.h
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
ae_epoll.o: ae_epoll.c
ae_evport.o: ae_evport.c
ae_kqueue.o: ae_kqueue.c
ae_select.o: ae_select.c
anet.o: anet.c fmacros.h anet.h
@@ -10,6 +11,9 @@ aof.o: aof.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
bio.o: bio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
bitops.o: bitops.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
config.o: config.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
@@ -22,7 +26,7 @@ debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h
dict.o: dict.c fmacros.h dict.h zmalloc.h
endianconv.o: endianconv.c
intset.o: intset.c intset.h zmalloc.h endianconv.h
intset.o: intset.c intset.h zmalloc.h endianconv.h config.h
lzf_c.o: lzf_c.c lzfP.h
lzf_d.o: lzf_d.c lzfP.h
memtest.o: memtest.c
@@ -53,7 +57,7 @@ redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
redis-check-dump.o: redis-check-dump.c lzf.h
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h anet.h ae.h
redis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h bio.h \
@@ -70,6 +74,11 @@ scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/lauxlib.h ../deps/lua/src/lua.h \
../deps/lua/src/lualib.h
sds.o: sds.c sds.h zmalloc.h
sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h \
../deps/hiredis/hiredis.h ../deps/hiredis/async.h \
../deps/hiredis/hiredis.h
sha1.o: sha1.c sha1.h config.h
slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
@@ -96,6 +105,6 @@ t_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
ziplist.h intset.h version.h util.h rdb.h rio.h
util.o: util.c fmacros.h util.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h
zipmap.o: zipmap.c zmalloc.h endianconv.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h config.h
zipmap.o: zipmap.c zmalloc.h endianconv.h config.h
zmalloc.o: zmalloc.c config.h zmalloc.h
+3 -3
View File
@@ -97,7 +97,7 @@ list *listAddNodeHead(list *list, void *value)
return list;
}
/* Add a new node to the list, to tail, contaning the specified 'value'
/* Add a new node to the list, to tail, containing the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
@@ -308,7 +308,7 @@ listNode *listSearchKey(list *list, void *key)
/* Return the element at the specified zero-based index
* where 0 is the head, 1 is the element next to head
* and so on. Negative integers are used in order to count
* from the tail, -1 is the last element, -2 the penultimante
* from the tail, -1 is the last element, -2 the penultimate
* and so on. If the index is out of range NULL is returned. */
listNode *listIndex(list *list, long index) {
listNode *n;
@@ -330,7 +330,7 @@ void listRotate(list *list) {
if (listLength(list) <= 1) return;
/* Detatch current tail */
/* Detach current tail */
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
+8 -4
View File
@@ -38,6 +38,7 @@
#include <poll.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "ae.h"
#include "zmalloc.h"
@@ -104,7 +105,10 @@ void aeStop(aeEventLoop *eventLoop) {
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
if (fd >= eventLoop->setsize) return AE_ERR;
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
}
aeFileEvent *fe = &eventLoop->events[fd];
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
@@ -305,7 +309,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
/* Process every pending time event, then every pending file event
* (that may be registered by time event callbacks just processed).
* Without special flags the function sleeps until some file event
* fires, or when the next time event occurrs (if any).
* fires, or when the next time event occurs (if any).
*
* If flags is 0, the function does nothing and returns.
* if flags has AE_ALL_EVENTS set, all the kind of events are processed.
@@ -352,7 +356,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
} else {
/* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to se the timeout
* ASAP because of AE_DONT_WAIT we need to set the timeout
* to zero */
if (flags & AE_DONT_WAIT) {
tv.tv_sec = tv.tv_usec = 0;
@@ -391,7 +395,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
return processed; /* return the number of processed file/time events */
}
/* Wait for millseconds until the given file descriptor becomes
/* Wait for milliseconds until the given file descriptor becomes
* writable/readable/exception */
int aeWait(int fd, int mask, long long milliseconds) {
struct pollfd pfd;
+6 -6
View File
@@ -50,15 +50,15 @@ static int evport_debug = 0;
* aeApiPoll, the corresponding file descriptors become dissociated from the
* port. This is necessary because poll events are level-triggered, so if the
* fd didn't become dissociated, it would immediately fire another event since
* the underlying state hasn't changed yet. We must reassociate the file
* the underlying state hasn't changed yet. We must re-associate the file
* descriptor, but only after we know that our caller has actually read from it.
* The ae API does not tell us exactly when that happens, but we do know that
* it must happen by the time aeApiPoll is called again. Our solution is to
* keep track of the last fds returned by aeApiPoll and reassociate them next
* keep track of the last fds returned by aeApiPoll and re-associate them next
* time aeApiPoll is invoked.
*
* To summarize, in this module, each fd association is EITHER (a) represented
* only via the in-kernel assocation OR (b) represented by pending_fds and
* only via the in-kernel association OR (b) represented by pending_fds and
* pending_masks. (b) is only true for the last fds we returned from aeApiPoll,
* and only until we enter aeApiPoll again (at which point we restore the
* in-kernel association).
@@ -164,7 +164,7 @@ static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
* This fd was recently returned from aeApiPoll. It should be safe to
* assume that the consumer has processed that poll event, but we play
* it safer by simply updating pending_mask. The fd will be
* reassociated as usual when aeApiPoll is called again.
* re-associated as usual when aeApiPoll is called again.
*/
if (evport_debug)
fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd);
@@ -228,7 +228,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
* ENOMEM is a potentially transient condition, but the kernel won't
* generally return it unless things are really bad. EAGAIN indicates
* we've reached an resource limit, for which it doesn't make sense to
* retry (counterintuitively). All other errors indicate a bug. In any
* retry (counter-intuitively). All other errors indicate a bug. In any
* of these cases, the best we can do is to abort.
*/
abort(); /* will not return */
@@ -243,7 +243,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
port_event_t event[MAX_EVENT_BATCHSZ];
/*
* If we've returned fd events before, we must reassociate them with the
* If we've returned fd events before, we must re-associate them with the
* port now, before calling port_get(). See the block comment at the top of
* this file for an explanation of why.
*/
+62 -5
View File
@@ -61,7 +61,7 @@ int anetNonBlock(char *err, int fd)
{
int flags;
/* Set the socket nonblocking.
/* Set the socket 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) {
@@ -75,10 +75,56 @@ int anetNonBlock(char *err, int fd)
return ANET_OK;
}
int anetTcpNoDelay(char *err, int fd)
/* 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. */
int anetKeepAlive(char *err, int fd, int interval)
{
int yes = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
int val = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
{
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
return ANET_ERR;
}
#ifdef __linux__
/* Default settings are more or less garbage, with the keepalive time
* set to 7200 by default on Linux. Modify settings to make the feature
* actually useful. */
/* Send first probe after interval. */
val = interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
return ANET_ERR;
}
/* Send next probes after the specified interval. Note that we set the
* delay as interval / 3, as we send three probes before detecting
* an error (see the next setsockopt call). */
val = interval/3;
if (val == 0) val = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
return ANET_ERR;
}
/* Consider the socket in error state after three we send three ACK
* probes without getting a reply. */
val = 3;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
return ANET_ERR;
}
#endif
return ANET_OK;
}
static int anetSetTcpNoDelay(char *err, int fd, int val)
{
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)
{
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
return ANET_ERR;
@@ -86,6 +132,17 @@ int anetTcpNoDelay(char *err, int fd)
return ANET_OK;
}
int anetEnableTcpNoDelay(char *err, int fd)
{
return anetSetTcpNoDelay(err, fd, 1);
}
int anetDisableTcpNoDelay(char *err, int fd)
{
return anetSetTcpNoDelay(err, fd, 0);
}
int anetSetSendBuffer(char *err, int fd, int buffsize)
{
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
@@ -132,7 +189,7 @@ static int anetCreateSocket(char *err, int domain) {
return ANET_ERR;
}
/* Make sure connection-intensive things like the redis benckmark
/* Make sure connection-intensive things like the redis benchmark
* will be able to close/open sockets a zillion of times */
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
+3 -1
View File
@@ -51,8 +51,10 @@ int anetTcpAccept(char *err, int serversock, char *ip, int *port);
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd);
int anetTcpNoDelay(char *err, int fd);
int anetEnableTcpNoDelay(char *err, int fd);
int anetDisableTcpNoDelay(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd);
int anetPeerToString(int fd, char *ip, int *port);
int anetKeepAlive(char *err, int fd, int interval);
#endif
+5 -2
View File
@@ -177,7 +177,7 @@ void stopAppendOnly(void) {
redisLog(REDIS_NOTICE,"Killing running AOF rewrite child: %ld",
(long) server.aof_child_pid);
if (kill(server.aof_child_pid,SIGKILL) != -1)
if (kill(server.aof_child_pid,SIGUSR1) != -1)
wait3(&statloc,0,NULL);
/* reset the buffer accumulating changes while the child saves */
aofRewriteBufferReset();
@@ -385,7 +385,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
sds buf = sdsempty();
robj *tmpargv[3];
/* The DB this command was targetting is not the same as the last command
/* The DB this command was targeting is not the same as the last command
* we appendend. To issue a SELECT command is needed. */
if (dictid != server.aof_selected_db) {
char seldb[64];
@@ -442,6 +442,7 @@ struct redisClient *createFakeClient(void) {
selectDb(c,0);
c->fd = -1;
c->name = NULL;
c->querybuf = sdsempty();
c->querybuf_peak = 0;
c->argc = 0;
@@ -854,6 +855,8 @@ int rewriteAppendOnlyFile(char *filename) {
}
rioInitWithFile(&aof,fp);
if (server.aof_rewrite_incremental_fsync)
rioSetAutoSync(&aof,REDIS_AOF_AUTOSYNC_BYTES);
for (j = 0; j < server.dbnum; j++) {
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
redisDb *db = server.db+j;
+1 -1
View File
@@ -73,7 +73,7 @@ static list *bio_jobs[REDIS_BIO_NUM_OPS];
static unsigned long long bio_pending[REDIS_BIO_NUM_OPS];
/* This structure represents a background Job. It is only used locally to this
* file as the API deos not expose the internals at all. */
* file as the API does not expose the internals at all. */
struct bio_job {
time_t time; /* Time at which the job was created. */
/* Job specific arguments pointers. If we need to pass more than three
+2 -2
View File
@@ -34,7 +34,7 @@
* Helpers and low level bit functions.
* -------------------------------------------------------------------------- */
/* This helper function used by GETBIT / SETBIT parses the bit offset arguemnt
/* This helper function used by GETBIT / SETBIT parses the bit offset argument
* making sure an error is returned if it is negative or if it overflows
* Redis 512 MB limit for the string value. */
static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
@@ -189,7 +189,7 @@ void bitopCommand(redisClient *c) {
char *opname = c->argv[1]->ptr;
robj *o, *targetkey = c->argv[2];
long op, j, numkeys;
robj **objects; /* Array of soruce objects. */
robj **objects; /* Array of source objects. */
unsigned char **src; /* Array of source strings pointers. */
long *len, maxlen = 0; /* Array of length of src strings, and max len. */
long minlen = 0; /* Min len among the input keys. */
+60 -5
View File
@@ -68,11 +68,21 @@ void loadServerConfigFromString(char *config) {
linenum = i+1;
lines[i] = sdstrim(lines[i]," \t\r\n");
/* Skip comments and blank lines*/
/* Skip comments and blank lines */
if (lines[i][0] == '#' || lines[i][0] == '\0') continue;
/* Split into arguments */
argv = sdssplitargs(lines[i],&argc);
if (argv == NULL) {
err = "Unbalanced quotes in configuration line";
goto loaderr;
}
/* Skip this line if the resulting command vector is empty. */
if (argc == 0) {
sdsfreesplitres(argv,argc);
return;
}
sdstolower(argv[0]);
/* Execute config directives */
@@ -81,6 +91,11 @@ void loadServerConfigFromString(char *config) {
if (server.maxidletime < 0) {
err = "Invalid timeout value"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) {
server.tcpkeepalive = atoi(argv[1]);
if (server.tcpkeepalive < 0) {
err = "Invalid tcp-keepalive value"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"port") && argc == 2) {
server.port = atoi(argv[1]);
if (server.port < 0 || server.port > 65535) {
@@ -230,6 +245,10 @@ void loadServerConfigFromString(char *config) {
err = "repl-timeout must be 1 or greater";
goto loaderr;
}
} else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) {
if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
server.masterauth = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
@@ -256,6 +275,10 @@ void loadServerConfigFromString(char *config) {
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"hz") && argc == 2) {
server.hz = atoi(argv[1]);
if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
} else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
int yes;
@@ -294,6 +317,12 @@ void loadServerConfigFromString(char *config) {
argc == 2)
{
server.aof_rewrite_min_size = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"aof-rewrite-incremental-fsync") &&
argc == 2)
{
if ((server.aof_rewrite_incremental_fsync = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
if (strlen(argv[1]) > REDIS_AUTHPASS_MAX_LEN) {
err = "Password is longer than REDIS_AUTHPASS_MAX_LEN";
@@ -329,7 +358,7 @@ void loadServerConfigFromString(char *config) {
goto loaderr;
}
/* If the target command name is the emtpy string we just
/* If the target command name is the empty string we just
* remove it from the command table. */
retval = dictDelete(server.commands, argv[1]);
redisAssert(retval == DICT_OK);
@@ -367,7 +396,7 @@ void loadServerConfigFromString(char *config) {
soft = memtoll(argv[3],NULL);
soft_seconds = atoi(argv[4]);
if (soft_seconds < 0) {
err = "Negative number of seconds in soft limt is invalid";
err = "Negative number of seconds in soft limit is invalid";
goto loaderr;
}
server.client_obuf_limits[class].hard_limit_bytes = hard;
@@ -412,7 +441,7 @@ loaderr:
* in the 'options' string to the config file before loading.
*
* Both filename and options can be NULL, in such a case are considered
* emtpy. This way loadServerConfig can be used to just load a file or
* empty. This way loadServerConfig can be used to just load a file or
* just load a string. */
void loadServerConfig(char *filename, char *options) {
sds config = sdsempty();
@@ -475,6 +504,12 @@ void configSetCommand(redisClient *c) {
}
freeMemoryIfNeeded();
}
} else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll < 0) goto badfmt;
server.hz = (int) ll;
if (server.hz < REDIS_MIN_HZ) server.hz = REDIS_MIN_HZ;
if (server.hz > REDIS_MAX_HZ) server.hz = REDIS_MAX_HZ;
} else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
if (!strcasecmp(o->ptr,"volatile-lru")) {
server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
@@ -499,6 +534,10 @@ void configSetCommand(redisClient *c) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll < 0 || ll > LONG_MAX) goto badfmt;
server.maxidletime = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll < 0 || ll > INT_MAX) goto badfmt;
server.tcpkeepalive = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
if (!strcasecmp(o->ptr,"no")) {
server.aof_fsync = AOF_FSYNC_NO;
@@ -533,6 +572,11 @@ void configSetCommand(redisClient *c) {
} else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.aof_rewrite_min_size = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"aof-rewrite-incremental-fsync")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.aof_rewrite_incremental_fsync = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"save")) {
int vlen, j;
sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
@@ -697,6 +741,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.rdb_checksum = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
int yn = yesnotoi(o->ptr);
if (yn == -1) goto badfmt;
server.repl_disable_tcp_nodelay = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll <= 0) goto badfmt;
@@ -751,7 +800,7 @@ void configGetCommand(redisClient *c) {
/* String values */
config_get_string_field("dbfilename",server.rdb_filename);
config_get_string_field("requirepass",server.requirepass);
config_get_string_field("masterauth",server.requirepass);
config_get_string_field("masterauth",server.masterauth);
config_get_string_field("bind",server.bindaddr);
config_get_string_field("unixsocket",server.unixsocket);
config_get_string_field("logfile",server.logfile);
@@ -761,6 +810,7 @@ void configGetCommand(redisClient *c) {
config_get_numerical_field("maxmemory",server.maxmemory);
config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
config_get_numerical_field("timeout",server.maxidletime);
config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
config_get_numerical_field("auto-aof-rewrite-percentage",
server.aof_rewrite_perc);
config_get_numerical_field("auto-aof-rewrite-min-size",
@@ -791,6 +841,7 @@ void configGetCommand(redisClient *c) {
config_get_numerical_field("maxclients",server.maxclients);
config_get_numerical_field("watchdog-period",server.watchdog_period);
config_get_numerical_field("slave-priority",server.slave_priority);
config_get_numerical_field("hz",server.hz);
/* Bool (yes/no) values */
config_get_bool_field("no-appendfsync-on-rewrite",
@@ -805,6 +856,10 @@ void configGetCommand(redisClient *c) {
config_get_bool_field("rdbcompression", server.rdb_compression);
config_get_bool_field("rdbchecksum", server.rdb_checksum);
config_get_bool_field("activerehashing", server.activerehashing);
config_get_bool_field("repl-disable-tcp-nodelay",
server.repl_disable_tcp_nodelay);
config_get_bool_field("aof-rewrite-incremental-fsync",
server.aof_rewrite_incremental_fsync);
/* Everything we can't handle with macros follows. */
+16 -3
View File
@@ -54,7 +54,7 @@
#endif
/* Test for backtrace() */
#if defined(__APPLE__) || defined(__linux__) || defined(__sun)
#if defined(__APPLE__) || defined(__linux__)
#define HAVE_BACKTRACE 1
#endif
@@ -137,13 +137,27 @@
#endif /* BSD */
#endif /* BYTE_ORDER */
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
/* Sometimes after including an OS-specific header that defines the
* endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what
* the Redis code uses. In this case let's define everything without the
* underscores. */
#ifndef BYTE_ORDER
#ifdef __BYTE_ORDER
#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN __BIG_ENDIAN
#endif
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN)
@@ -162,5 +176,4 @@
#endif
#endif
#endif
+7 -7
View File
@@ -44,7 +44,7 @@ robj *lookupKey(redisDb *db, robj *key) {
if (de) {
robj *val = dictGetVal(de);
/* Update the access time for the aging algorithm.
/* Update the access time for the ageing algorithm.
* Don't do it if we have a saving child, as this will trigger
* a copy on write madness. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
@@ -85,7 +85,7 @@ robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
}
/* Add the key to the DB. It's up to the caller to increment the reference
* counte of the value if needed.
* counter of the value if needed.
*
* The program is aborted if the key already exists. */
void dbAdd(redisDb *db, robj *key, robj *val) {
@@ -219,7 +219,7 @@ void flushallCommand(redisClient *c) {
server.dirty += emptyDb();
addReply(c,shared.ok);
if (server.rdb_child_pid != -1) {
kill(server.rdb_child_pid,SIGKILL);
kill(server.rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(server.rdb_child_pid);
}
if (server.saveparamslen > 0) {
@@ -538,9 +538,8 @@ int expireIfNeeded(redisDb *db, robj *key) {
* for *AT variants of the command, or the current time for relative expires).
*
* unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for
* the argv[2] parameter. The basetime is always specified in milliesconds. */
* the argv[2] parameter. The basetime is always specified in milliseconds. */
void expireGenericCommand(redisClient *c, long long basetime, int unit) {
dictEntry *de;
robj *key = c->argv[1], *param = c->argv[2];
long long when; /* unix time in milliseconds when the key will expire. */
@@ -550,11 +549,12 @@ void expireGenericCommand(redisClient *c, long long basetime, int unit) {
if (unit == UNIT_SECONDS) when *= 1000;
when += basetime;
de = dictFind(c->db->dict,key->ptr);
if (de == NULL) {
/* No key, return zero. */
if (lookupKeyRead(c->db,key) == NULL) {
addReply(c,shared.czero);
return;
}
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
* should never be executed as a DEL when load the AOF or in the context
* of a slave instance.
+10 -5
View File
@@ -42,7 +42,7 @@
/* ================================= Debugging ============================== */
/* Compute the sha1 of string at 's' with 'len' bytes long.
* The SHA1 is then xored againt the string pointed by digest.
* The SHA1 is then xored against the string pointed by digest.
* Since xor is commutative, this operation is used in order to
* "add" digests relative to unordered elements.
*
@@ -67,7 +67,7 @@ void xorObjectDigest(unsigned char *digest, robj *o) {
}
/* This function instead of just computing the SHA1 and xoring it
* against diget, also perform the digest of "digest" itself and
* against digest, also perform the digest of "digest" itself and
* replace the old value with the new one.
*
* So the final digest will be:
@@ -330,9 +330,14 @@ void debugCommand(redisClient *c) {
usleep(utime);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") &&
c->argc == 3)
{
server.active_expire_enabled = atoi(c->argv[2]->ptr);
addReply(c,shared.ok);
} else {
addReplyError(c,
"Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'",
(char*)c->argv[1]->ptr);
}
}
@@ -794,7 +799,7 @@ void enableWatchdog(int period) {
/* If the configured period is smaller than twice the timer period, it is
* too short for the software watchdog to work reliably. Fix it now
* if needed. */
min_period = (1000/REDIS_HZ)*2;
min_period = (1000/server.hz)*2;
if (period < min_period) period = min_period;
watchdogScheduleSignal(period); /* Adjust the current timer. */
server.watchdog_period = period;
+1 -1
View File
@@ -610,7 +610,7 @@ static int _dictExpandIfNeeded(dict *d)
/* Incremental rehashing already in progress. Return. */
if (dictIsRehashing(d)) return DICT_OK;
/* If the hash table is empty expand it to the intial size. */
/* If the hash table is empty expand it to the initial size. */
if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
/* If we reached the 1:1 ratio, and we are allowed to resize the hash
+1
View File
@@ -33,6 +33,7 @@
#ifndef __ENDIANCONV_H
#define __ENDIANCONV_H
#include "config.h"
#include <stdint.h>
void memrev16(void *p);
+1 -1
View File
@@ -36,7 +36,7 @@
#define _GNU_SOURCE
#endif
#if defined(__linux__) || defined(__OpenBSD__)
#if defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
#define _XOPEN_SOURCE 700
#else
#define _XOPEN_SOURCE
+4 -4
View File
@@ -93,7 +93,7 @@
/*
* Avoid assigning values to errno variable? for some embedding purposes
* (linux kernel for example), this is neccessary. NOTE: this breaks
* (linux kernel for example), this is necessary. NOTE: this breaks
* the documentation in lzf.h.
*/
#ifndef AVOID_ERRNO
@@ -101,7 +101,7 @@
#endif
/*
* Wether to pass the LZF_STATE variable as argument, or allocate it
* Whether to pass the LZF_STATE variable as argument, or allocate it
* on the stack. For small-stack environments, define this to 1.
* NOTE: this breaks the prototype in lzf.h.
*/
@@ -110,11 +110,11 @@
#endif
/*
* Wether to add extra checks for input validity in lzf_decompress
* Whether to add extra checks for input validity in lzf_decompress
* and return EINVAL if the input stream has been corrupted. This
* only shields against overflowing the input buffer and will not
* detect most corrupted streams.
* This check is not normally noticable on modern hardware
* This check is not normally noticeable on modern hardware
* (<1% slowdown), but might slow down older cpus considerably.
*/
#ifndef CHECK_INPUT
+2 -4
View File
@@ -78,10 +78,8 @@ void memtest_progress_end(void) {
void memtest_progress_step(size_t curr, size_t size, char c) {
size_t chars = ((unsigned long long)curr*progress_full)/size, j;
for (j = 0; j < chars-progress_printed; j++) {
printf("%c",c);
progress_printed++;
}
for (j = 0; j < chars-progress_printed; j++) printf("%c",c);
progress_printed = chars;
fflush(stdout);
}
+2 -2
View File
@@ -1,9 +1,9 @@
#!/bin/sh
GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1`
GIT_DIRTY=`git diff 2> /dev/null | wc -l`
GIT_DIRTY=`git diff --no-ext-diff 2> /dev/null | wc -l`
test -f release.h || touch release.h
(cat release.h | grep SHA1 | grep $GIT_SHA1) && \
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already uptodate
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date
echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h
echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h
touch release.c # Force recompile of release.c
+21 -24
View File
@@ -102,14 +102,12 @@ void discardCommand(redisClient *c) {
}
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand
* implememntation for more information. */
void execCommandReplicateMulti(redisClient *c) {
* implementation for more information. */
void execCommandPropagateMulti(redisClient *c) {
robj *multistring = createStringObject("MULTI",5);
if (server.aof_state != REDIS_AOF_OFF)
feedAppendOnlyFile(server.multiCommand,c->db->id,&multistring,1);
if (listLength(server.slaves))
replicationFeedSlaves(server.slaves,c->db->id,&multistring,1);
propagate(server.multiCommand,c->db->id,&multistring,1,
REDIS_PROPAGATE_AOF|REDIS_PROPAGATE_REPL);
decrRefCount(multistring);
}
@@ -118,6 +116,7 @@ void execCommand(redisClient *c) {
robj **orig_argv;
int orig_argc;
struct redisCommand *orig_cmd;
int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */
if (!(c->flags & REDIS_MULTI)) {
addReplyError(c,"EXEC without MULTI");
@@ -133,19 +132,10 @@ void execCommand(redisClient *c) {
if (c->flags & (REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC)) {
addReply(c, c->flags & REDIS_DIRTY_EXEC ? shared.execaborterr :
shared.nullmultibulk);
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);
unwatchAllKeys(c);
discardTransaction(c);
goto handle_monitor;
}
/* Replicate a MULTI request now that we are sure the block is executed.
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* and atomicity guarantees. */
execCommandReplicateMulti(c);
/* Exec all the queued commands */
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
orig_argv = c->argv;
@@ -156,6 +146,16 @@ void execCommand(redisClient *c) {
c->argc = c->mstate.commands[j].argc;
c->argv = c->mstate.commands[j].argv;
c->cmd = c->mstate.commands[j].cmd;
/* Propagate a MULTI request once we encounter the first write op.
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* and atomicity guarantees. */
if (!must_propagate && !(c->cmd->flags & REDIS_CMD_READONLY)) {
execCommandPropagateMulti(c);
must_propagate = 1;
}
call(c,REDIS_CALL_FULL);
/* Commands may alter argc/argv, restore mstate. */
@@ -166,13 +166,10 @@ void execCommand(redisClient *c) {
c->argv = orig_argv;
c->argc = orig_argc;
c->cmd = orig_cmd;
freeClientMultiState(c);
initClientMultiState(c);
c->flags &= ~(REDIS_MULTI|REDIS_DIRTY_CAS|REDIS_DIRTY_EXEC);
/* Make sure the EXEC command is always replicated / AOF, since we
* always send the MULTI command (we can't know beforehand if the
* next operations will contain at least a modification to the DB). */
server.dirty++;
discardTransaction(c);
/* Make sure the EXEC command will be propagated as well if MULTI
* was already propagated. */
if (must_propagate) server.dirty++;
handle_monitor:
/* Send EXEC to clients waiting data from MONITOR. We do it here
@@ -223,7 +220,7 @@ void watchForKey(redisClient *c, robj *key) {
incrRefCount(key);
}
listAddNodeTail(clients,c);
/* Add the new key to the lits of keys watched by this client */
/* Add the new key to the list of keys watched by this client */
wk = zmalloc(sizeof(*wk));
wk->key = key;
wk->db = c->db;
+54 -13
View File
@@ -58,7 +58,9 @@ redisClient *createClient(int fd) {
* contexts (for instance a Lua script) we need a non connected client. */
if (fd != -1) {
anetNonBlock(NULL,fd);
anetTcpNoDelay(NULL,fd);
anetEnableTcpNoDelay(NULL,fd);
if (server.tcpkeepalive)
anetKeepAlive(NULL,fd,server.tcpkeepalive);
if (aeCreateFileEvent(server.el,fd,AE_READABLE,
readQueryFromClient, c) == AE_ERR)
{
@@ -70,6 +72,7 @@ redisClient *createClient(int fd) {
selectDb(c,0);
c->fd = fd;
c->name = NULL;
c->bufpos = 0;
c->querybuf = sdsempty();
c->querybuf_peak = 0;
@@ -90,8 +93,7 @@ redisClient *createClient(int fd) {
c->obuf_soft_limit_reached_time = 0;
listSetFreeMethod(c->reply,decrRefCount);
listSetDupMethod(c->reply,dupClientReplyValue);
c->bpop.keys = NULL;
c->bpop.count = 0;
c->bpop.keys = dictCreate(&setDictType,NULL);
c->bpop.timeout = 0;
c->bpop.target = NULL;
c->io_keys = listCreate();
@@ -378,7 +380,7 @@ void *addDeferredMultiBulkLength(redisClient *c) {
return listLast(c->reply);
}
/* Populate the length object and try glueing it to the next chunk. */
/* Populate the length object and try gluing it to the next chunk. */
void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
listNode *ln = (listNode*)node;
robj *len, *next;
@@ -404,7 +406,7 @@ void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
asyncCloseClientOnOutputBufferLimitReached(c);
}
/* Add a duble as a bulk reply */
/* Add a double as a bulk reply */
void addReplyDouble(redisClient *c, double d) {
char dbuf[128], sbuf[128];
int dlen, slen;
@@ -518,13 +520,15 @@ void copyClientOutputBuffer(redisClient *dst, redisClient *src) {
static void acceptCommonHandler(int fd, int flags) {
redisClient *c;
if ((c = createClient(fd)) == NULL) {
redisLog(REDIS_WARNING,"Error allocating resources for the client");
redisLog(REDIS_WARNING,
"Error registering fd event for the new client: %s (fd=%d)",
strerror(errno),fd);
close(fd); /* May be already closed, just ignore errors */
return;
}
/* If maxclient directive is set and this is one client more... close the
* connection. Note that we create the client instead to check before
* for this condition, since now the socket is already set in nonblocking
* for this condition, since now the socket is already set in non-blocking
* mode and we can send an error for free using the Kernel I/O */
if (listLength(server.clients) > server.maxclients) {
char *err = "-ERR max number of clients reached\r\n";
@@ -606,6 +610,7 @@ void freeClient(redisClient *c) {
c->querybuf = NULL;
if (c->flags & REDIS_BLOCKED)
unblockClientWaitingData(c);
dictRelease(c->bpop.keys);
/* UNWATCH all the keys */
unwatchAllKeys(c);
@@ -666,6 +671,7 @@ void freeClient(redisClient *c) {
}
/* Release memory */
if (c->name) decrRefCount(c->name);
zfree(c->argv);
freeClientMultiState(c);
zfree(c);
@@ -937,7 +943,7 @@ int processMultibulkBuffer(redisClient *c) {
/* Not enough data (+2 == trailing \r\n) */
break;
} else {
/* Optimization: if the buffer contanins JUST our bulk element
/* Optimization: if the buffer contains JUST our bulk element
* instead of creating a new object by *copying* the sds we
* just use the current sds string. */
if (pos == 0 &&
@@ -1121,9 +1127,11 @@ sds getClientInfoString(redisClient *client) {
if (emask & AE_WRITABLE) *p++ = 'w';
*p = '\0';
return sdscatprintf(sdsempty(),
"addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
"addr=%s:%d fd=%d name=%s age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
(client->flags & REDIS_UNIX_SOCKET) ? server.unixsocket : ip,
port,client->fd,
port,
client->fd,
client->name ? (char*)client->name->ptr : "",
(long)(server.unixtime - client->ctime),
(long)(server.unixtime - client->lastinteraction),
flags,
@@ -1188,8 +1196,41 @@ void clientCommand(redisClient *c) {
}
}
addReplyError(c,"No such client");
} else if (!strcasecmp(c->argv[1]->ptr,"setname") && c->argc == 3) {
int j, len = sdslen(c->argv[2]->ptr);
char *p = c->argv[2]->ptr;
/* Setting the client name to an empty string actually removes
* the current name. */
if (len == 0) {
if (c->name) decrRefCount(c->name);
c->name = NULL;
addReply(c,shared.ok);
return;
}
/* Otherwise check if the charset is ok. We need to do this otherwise
* CLIENT LIST format will break. You should always be able to
* split by space to get the different fields. */
for (j = 0; j < len; j++) {
if (p[j] < '!' || p[j] > '~') { /* ASCII is assumed. */
addReplyError(c,
"Client names cannot contain spaces, "
"newlines or special characters.");
return;
}
}
if (c->name) decrRefCount(c->name);
c->name = c->argv[2];
incrRefCount(c->name);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"getname") && c->argc == 2) {
if (c->name)
addReplyBulk(c,c->name);
else
addReply(c,shared.nullbulk);
} else {
addReplyError(c, "Syntax error, try CLIENT (LIST | KILL ip:port)");
addReplyError(c, "Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)");
}
}
@@ -1218,7 +1259,7 @@ void rewriteClientCommandVector(redisClient *c, int argc, ...) {
/* Replace argv and argc with our new versions. */
c->argv = argv;
c->argc = argc;
c->cmd = lookupCommand(c->argv[0]->ptr);
c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);
redisAssertWithInfo(c,NULL,c->cmd != NULL);
va_end(ap);
}
@@ -1236,7 +1277,7 @@ void rewriteClientCommandArgument(redisClient *c, int i, robj *newval) {
/* If this is the command name make sure to fix c->cmd. */
if (i == 0) {
c->cmd = lookupCommand(c->argv[0]->ptr);
c->cmd = lookupCommandOrOriginal(c->argv[0]->ptr);
redisAssertWithInfo(c,NULL,c->cmd != NULL);
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ robj *createStringObjectFromLongDouble(long double value) {
int len;
/* We use 17 digits precision since with 128 bit floats that precision
* after rouding is able to represent most small decimal numbers in a way
* 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.) */
+17 -3
View File
@@ -117,7 +117,7 @@ int pubsubUnsubscribeChannel(redisClient *c, robj *channel, int notify) {
return retval;
}
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the clinet was already subscribed to that pattern. */
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */
int pubsubSubscribePattern(redisClient *c, robj *pattern) {
int retval = 0;
@@ -179,6 +179,14 @@ int pubsubUnsubscribeAllChannels(redisClient *c, int notify) {
count += pubsubUnsubscribeChannel(c,channel,notify);
}
/* We were subscribed to nothing? Still reply to the client. */
if (notify && count == 0) {
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.unsubscribebulk);
addReply(c,shared.nullbulk);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
dictReleaseIterator(di);
return count;
}
@@ -196,6 +204,14 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify) {
count += pubsubUnsubscribePattern(c,pattern,notify);
}
if (notify && count == 0) {
/* We were subscribed to nothing? Still reply to the client. */
addReply(c,shared.mbulkhdr[3]);
addReply(c,shared.punsubscribebulk);
addReply(c,shared.nullbulk);
addReplyLongLong(c,dictSize(c->pubsub_channels)+
listLength(c->pubsub_patterns));
}
return count;
}
@@ -262,7 +278,6 @@ void subscribeCommand(redisClient *c) {
void unsubscribeCommand(redisClient *c) {
if (c->argc == 1) {
pubsubUnsubscribeAllChannels(c,1);
return;
} else {
int j;
@@ -281,7 +296,6 @@ void psubscribeCommand(redisClient *c) {
void punsubscribeCommand(redisClient *c) {
if (c->argc == 1) {
pubsubUnsubscribeAllPatterns(c,1);
return;
} else {
int j;
+12 -11
View File
@@ -265,7 +265,7 @@ err:
return NULL;
}
/* Save a string objet as [len][data] on disk. If the object is a string
/* Save a string object as [len][data] on disk. If the object is a string
* representation of an integer value we try to save it in a special form */
int rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
int enclen;
@@ -321,7 +321,7 @@ int rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
/* Like rdbSaveStringObjectRaw() but handle encoded objects */
int rdbSaveStringObject(rio *rdb, robj *obj) {
/* Avoid to decode the object, then encode it again, if the
* object is alrady integer encoded. */
* object is already integer encoded. */
if (obj->encoding == REDIS_ENCODING_INT) {
return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);
} else {
@@ -367,7 +367,7 @@ robj *rdbLoadEncodedStringObject(rio *rdb) {
}
/* Save a double value. Doubles are saved as strings prefixed by an unsigned
* 8 bit integer specifing the length of the representation.
* 8 bit integer specifying the length of the representation.
* This 8 bit integer has special values in order to specify the following
* conditions:
* 253: not a number
@@ -606,7 +606,7 @@ off_t rdbSavedObjectLen(robj *o) {
/* Save a key-value pair, with expire time, type, key, value.
* On error -1 is returned.
* On success if the key was actaully saved 1 is returned, otherwise 0
* On success if the key was actually saved 1 is returned, otherwise 0
* is returned (the key was already expired). */
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
long long expiretime, long long now)
@@ -722,6 +722,7 @@ int rdbSaveBackground(char *filename) {
if (server.rdb_child_pid != -1) return REDIS_ERR;
server.dirty_before_bgsave = server.dirty;
server.lastbgsave_try = time(NULL);
start = ustime();
if ((childpid = fork()) == 0) {
@@ -1067,11 +1068,8 @@ int rdbLoad(char *filename) {
FILE *fp;
rio rdb;
fp = fopen(filename,"r");
if (!fp) {
errno = ENOENT;
return REDIS_ERR;
}
if ((fp = fopen(filename,"r")) == NULL) return REDIS_ERR;
rioInitWithFile(&rdb,fp);
if (server.rdb_checksum)
rdb.update_cksum = rioGenericUpdateChecksum;
@@ -1109,7 +1107,7 @@ int rdbLoad(char *filename) {
/* We read the time so we need to read the object type again. */
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
/* the EXPIRETIME opcode specifies time in seconds, so convert
* into milliesconds. */
* into milliseconds. */
expiretime *= 1000;
} else if (type == REDIS_RDB_OPCODE_EXPIRETIME_MS) {
/* Milliseconds precision expire times introduced with RDB
@@ -1194,7 +1192,10 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
redisLog(REDIS_WARNING,
"Background saving terminated by signal %d", bysignal);
rdbRemoveTempFile(server.rdb_child_pid);
server.lastbgsave_status = REDIS_ERR;
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error conditon. */
if (bysignal != SIGUSR1)
server.lastbgsave_status = REDIS_ERR;
}
server.rdb_child_pid = -1;
server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;
+1 -1
View File
@@ -51,7 +51,7 @@
* number specify the kind of object that follows.
* See the REDIS_RDB_ENC_* defines.
*
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
* Lengths up to 63 are stored using a single byte, most DB keys, and may
* values, will fit inside. */
#define REDIS_RDB_6BITLEN 0
#define REDIS_RDB_14BITLEN 1
+1 -1
View File
@@ -106,7 +106,7 @@ static long long mstime(void) {
long long mst;
gettimeofday(&tv, NULL);
mst = ((long)tv.tv_sec)*1000;
mst = ((long long)tv.tv_sec)*1000;
mst += tv.tv_usec/1000;
return mst;
}
+2 -2
View File
@@ -78,7 +78,7 @@
* number specify the kind of object that follows.
* See the REDIS_RDB_ENC_* defines.
*
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
* Lengths up to 63 are stored using a single byte, most DB keys, and may
* values, will fit inside. */
#define REDIS_RDB_6BITLEN 0
#define REDIS_RDB_14BITLEN 1
@@ -132,7 +132,7 @@ typedef struct {
char success;
} entry;
/* Global vars that are actally used as constants. The following double
/* Global vars that are actually used as constants. The following double
* values are used for double on-disk serialization, and are initialized
* at runtime to avoid strange compiler optimizations. */
static double R_Zero, R_PosInf, R_NegInf, R_Nan;
+335 -41
View File
@@ -41,6 +41,8 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include "hiredis.h"
#include "sds.h"
@@ -55,6 +57,7 @@
#define OUTPUT_STANDARD 0
#define OUTPUT_RAW 1
#define OUTPUT_CSV 2
#define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
static redisContext *context;
static struct config {
@@ -69,10 +72,14 @@ static struct config {
int monitor_mode;
int pubsub_mode;
int latency_mode;
int latency_history;
int cluster_mode;
int cluster_reissue_command;
int slave_mode;
int pipe_mode;
int getrdb_mode;
int stat_mode;
char *rdb_filename;
int bigkeys;
int stdinarg; /* get last arg from stdin. (-x option) */
char *auth;
@@ -95,7 +102,7 @@ static long long mstime(void) {
long long mst;
gettimeofday(&tv, NULL);
mst = ((long)tv.tv_sec)*1000;
mst = ((long long)tv.tv_sec)*1000;
mst += tv.tv_usec/1000;
return mst;
}
@@ -305,7 +312,7 @@ static int cliSelect() {
return REDIS_ERR;
}
/* Connect to the client. If force is not zero the connection is performed
/* Connect to the server. If force is not zero the connection is performed
* even if there is already a connected socket. */
static int cliConnect(int force) {
if (context == NULL || force) {
@@ -329,6 +336,12 @@ static int cliConnect(int force) {
return REDIS_ERR;
}
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
* in order to prevent timeouts caused by the execution of long
* commands. At the same time this improves the detection of real
* errors. */
anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);
/* Do AUTH and select the right DB. */
if (cliAuth() != REDIS_OK)
return REDIS_ERR;
@@ -620,6 +633,36 @@ 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;
redisReply *reply = NULL;
int tries = 0;
assert(!c->err);
while(reply == NULL) {
while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {
printf("Reconnecting (%d)...\r", ++tries);
fflush(stdout);
redisFree(c);
c = redisConnect(config.hostip,config.hostport);
usleep(1000000);
}
reply = redisCommand(c,"INFO");
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");
}
}
context = c;
return reply;
}
/*------------------------------------------------------------------------------
* User interface
*--------------------------------------------------------------------------- */
@@ -658,8 +701,16 @@ 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-history")) {
config.latency_mode = 1;
config.latency_history = 1;
} else if (!strcmp(argv[i],"--slave")) {
config.slave_mode = 1;
} else if (!strcmp(argv[i],"--stat")) {
config.stat_mode = 1;
} else if (!strcmp(argv[i],"--rdb") && !lastarg) {
config.getrdb_mode = 1;
config.rdb_filename = argv[++i];
} else if (!strcmp(argv[i],"--pipe")) {
config.pipe_mode = 1;
} else if (!strcmp(argv[i],"--bigkeys")) {
@@ -677,7 +728,15 @@ static int parseOptions(int argc, char **argv) {
sdsfree(version);
exit(0);
} else {
break;
if (argv[i][0] == '-') {
fprintf(stderr,
"Unrecognized option or bad number of args for: '%s'\n",
argv[i]);
exit(1);
} else {
/* Likely the command name, stop here. */
break;
}
}
}
return i;
@@ -706,25 +765,29 @@ static void usage() {
"redis-cli %s\n"
"\n"
"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
" -h <hostname> Server hostname (default: 127.0.0.1)\n"
" -p <port> Server port (default: 6379)\n"
" -s <socket> Server socket (overrides hostname and port)\n"
" -a <password> Password to use when connecting to the server\n"
" -r <repeat> Execute specified command N times\n"
" -i <interval> When -r is used, waits <interval> seconds per command.\n"
" It is possible to specify sub-second times like -i 0.1\n"
" -n <db> Database number\n"
" -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections)\n"
" --raw Use raw formatting for replies (default when STDOUT is not a tty)\n"
" --latency Enter a special mode continuously sampling latency\n"
" --slave Simulate a slave showing commands received from the master\n"
" --pipe Transfer raw Redis protocol from stdin to server\n"
" --bigkeys Sample Redis keys looking for big keys\n"
" --eval <file> Send an EVAL command using the Lua script at <file>\n"
" --help Output this help and exit\n"
" --version Output version and exit\n"
" -h <hostname> Server hostname (default: 127.0.0.1)\n"
" -p <port> Server port (default: 6379)\n"
" -s <socket> Server socket (overrides hostname and port)\n"
" -a <password> Password to use when connecting to the server\n"
" -r <repeat> Execute specified command N times\n"
" -i <interval> When -r is used, waits <interval> seconds per command.\n"
" It is possible to specify sub-second times like -i 0.1\n"
" -n <db> Database number\n"
" -x Read last argument from STDIN\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n)\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections)\n"
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty)\n"
" --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"
" --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"
" --bigkeys Sample Redis keys looking for big keys\n"
" --eval <file> Send an EVAL command using the Lua script at <file>\n"
" --help Output this help and exit\n"
" --version Output version and exit\n"
"\n"
"Examples:\n"
" cat /etc/passwd | redis-cli -x set mypasswd\n"
@@ -794,6 +857,7 @@ static void repl() {
sdsfree(config.hostip);
config.hostip = sdsnew(argv[1]);
config.hostport = atoi(argv[2]);
cliRefreshPrompt();
cliConnect(1);
} else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
linenoiseClearScreen();
@@ -835,8 +899,7 @@ static void repl() {
}
}
/* Free the argument vector */
while(argc--) sdsfree(argv[argc]);
zfree(argv);
sdsfreesplitres(argv,argc);
}
/* linenoise() returns malloc-ed lines like readline() */
free(line);
@@ -895,10 +958,16 @@ static int evalMode(int argc, char **argv) {
return cliSendCommand(argc+3-got_comma, argv2, config.repeat);
}
#define LATENCY_SAMPLE_RATE 10 /* milliseconds. */
#define LATENCY_HISTORY_DEFAULT_INTERVAL 15000 /* milliseconds. */
static void latencyMode(void) {
redisReply *reply;
long long start, latency, min = 0, max = 0, tot = 0, count = 0;
long long history_interval =
config.interval ? config.interval/1000 :
LATENCY_HISTORY_DEFAULT_INTERVAL;
double avg;
long long history_start = mstime();
if (!context) exit(1);
while(1) {
@@ -923,19 +992,25 @@ static void latencyMode(void) {
printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)",
min, max, avg, count);
fflush(stdout);
usleep(10000);
if (config.latency_history && mstime()-history_start > history_interval)
{
printf(" -- %.2f seconds range\n", (float)(mstime()-history_start)/1000);
history_start = mstime();
min = max = tot = count = 0;
}
usleep(LATENCY_SAMPLE_RATE * 1000);
}
}
static void slaveMode(void) {
/* Sends SYNC and reads the number of bytes in the payload. Used both by
* slaveMode() and getRDB(). */
unsigned long long sendSync(int fd) {
/* To start we need to send the SYNC command and return the payload.
* The hiredis client lib does not understand this part of the protocol
* and we don't want to mess with its buffers, so everything is performed
* using direct low-level I/O. */
int fd = context->fd;
char buf[1024], *p;
char buf[4096], *p;
ssize_t nread;
unsigned long long payload;
/* Send the SYNC command. */
if (write(fd,"SYNC\r\n",6) != 6) {
@@ -951,16 +1026,29 @@ static void slaveMode(void) {
fprintf(stderr,"Error reading bulk length while SYNCing\n");
exit(1);
}
if (*p == '\n') break;
p++;
if (*p == '\n' && p != buf) break;
if (*p != '\n') p++;
}
*p = '\0';
payload = strtoull(buf+1,NULL,10);
fprintf(stderr,"SYNC with master, discarding %lld bytes of bulk tranfer...\n",
payload);
if (buf[0] == '-') {
printf("SYNC with master failed: %s\n", buf);
exit(1);
}
return strtoull(buf+1,NULL,10);
}
static void slaveMode(void) {
int fd = context->fd;
unsigned long long payload = sendSync(fd);
char buf[1024];
fprintf(stderr,"SYNC with master, discarding %llu "
"bytes of bulk transfer...\n", payload);
/* Discard the payload. */
while(payload) {
ssize_t nread;
nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"Error reading RDB payload while SYNCing\n");
@@ -970,11 +1058,56 @@ static void slaveMode(void) {
}
fprintf(stderr,"SYNC done. Logging commands from master.\n");
/* Now we can use the hiredis to read the incoming protocol. */
/* Now we can use hiredis to read the incoming protocol. */
config.output = OUTPUT_CSV;
while (cliReadReply(0) == REDIS_OK);
}
/* This function implements --rdb, so it uses the replication protocol in order
* to fetch the RDB file from a remote server. */
static void getRDB(void) {
int s = context->fd;
int fd;
unsigned long long payload = sendSync(s);
char buf[4096];
fprintf(stderr,"SYNC sent to master, writing %llu bytes to '%s'\n",
payload, config.rdb_filename);
/* Write to file. */
if (!strcmp(config.rdb_filename,"-")) {
fd = STDOUT_FILENO;
} else {
fd = open(config.rdb_filename, O_CREAT|O_WRONLY, 0644);
if (fd == -1) {
fprintf(stderr, "Error opening '%s': %s\n", config.rdb_filename,
strerror(errno));
exit(1);
}
}
while(payload) {
ssize_t nread, nwritten;
nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"I/O Error reading RDB payload from socket\n");
exit(1);
}
nwritten = write(fd, buf, nread);
if (nwritten != nread) {
fprintf(stderr,"Error writing data to file: %s\n",
strerror(errno));
exit(1);
}
payload -= nread;
}
close(s); /* Close the file descriptor ASAP as fsync() may take time. */
fsync(fd);
fprintf(stderr,"Transfer finished with success.\n");
exit(0);
}
static void pipeMode(void) {
int fd = context->fd;
long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
@@ -1075,7 +1208,7 @@ static void pipeMode(void) {
int j;
eof = 1;
/* Everything transfered, so we queue a special
/* Everything transferred, so we queue a special
* ECHO command that we can match in the replies
* to make sure everything was read from the server. */
for (j = 0; j < 20; j++)
@@ -1117,6 +1250,7 @@ static void findBigKeys(void) {
unsigned long long samples = 0;
redisReply *reply1, *reply2, *reply3 = NULL;
char *sizecmd, *typename[] = {"string","list","set","hash","zset"};
char *typeunit[] = {"bytes","items","members","fields","members"};
int type;
printf("\n# Press ctrl+c when you have had enough of it... :)\n");
@@ -1132,7 +1266,11 @@ static void findBigKeys(void) {
fprintf(stderr, "RANDOMKEY error: %s\n",
reply1->str);
exit(1);
} else if (reply1->type == REDIS_REPLY_NIL) {
fprintf(stderr, "It looks like the database is empty!\n");
exit(1);
}
/* Get the key type */
reply2 = redisCommand(context,"TYPE %s",reply1->str);
assert(reply2 && reply2->type == REDIS_REPLY_STATUS);
@@ -1168,9 +1306,10 @@ static void findBigKeys(void) {
reply3 = redisCommand(context,"%s %s", sizecmd, reply1->str);
if (reply3 && reply3->type == REDIS_REPLY_INTEGER) {
if (biggest[type] < reply3->integer) {
printf("[%6s] %s | biggest so far with size %llu\n",
printf("Biggest %-6s found so far '%s' with %llu %s.\n",
typename[type], reply1->str,
(unsigned long long) reply3->integer);
(unsigned long long) reply3->integer,
typeunit[type]);
biggest[type] = reply3->integer;
}
}
@@ -1187,6 +1326,145 @@ static void findBigKeys(void) {
}
}
/* Return the specified INFO field from the INFO command output "info".
* A new buffer is allocated for the result, that needs to be free'd.
* If the field is not found NULL is returned. */
static char *getInfoField(char *info, char *field) {
char *p = strstr(info,field);
char *n1, *n2;
char *result;
if (!p) return NULL;
p += strlen(field)+1;
n1 = strchr(p,'\r');
n2 = strchr(p,',');
if (n2 && n2 < n1) n1 = n2;
result = malloc(sizeof(char)*(n1-p)+1);
memcpy(result,p,(n1-p));
result[n1-p] = '\0';
return result;
}
/* Like the above function but automatically convert the result into
* a long. On error (missing field) LONG_MIN is returned. */
static long getLongInfoField(char *info, char *field) {
char *value = getInfoField(info,field);
long l;
if (!value) return LONG_MIN;
l = strtol(value,NULL,10);
free(value);
return l;
}
/* Convert number of bytes into a human readable string of the form:
* 100B, 2G, 100M, 4K, and so forth. */
void bytesToHuman(char *s, long long n) {
double d;
if (n < 0) {
*s = '-';
s++;
n = -n;
}
if (n < 1024) {
/* Bytes */
sprintf(s,"%lluB",n);
return;
} else if (n < (1024*1024)) {
d = (double)n/(1024);
sprintf(s,"%.2fK",d);
} else if (n < (1024LL*1024*1024)) {
d = (double)n/(1024*1024);
sprintf(s,"%.2fM",d);
} else if (n < (1024LL*1024*1024*1024)) {
d = (double)n/(1024LL*1024*1024);
sprintf(s,"%.2fG",d);
}
}
static void statMode() {
redisReply *reply;
long aux, requests = 0;
int i = 0;
while(1) {
char buf[64];
int j;
reply = reconnectingInfo();
if (reply->type == REDIS_REPLY_ERROR) {
printf("ERROR: %s\n", reply->str);
exit(1);
}
if ((i++ % 20) == 0) {
printf(
"------- data ------ --------------------- load -------------------- - child -\n"
"keys mem clients blocked requests connections \n");
}
/* Keys */
aux = 0;
for (j = 0; j < 20; j++) {
long k;
sprintf(buf,"db%d:keys",j);
k = getLongInfoField(reply->str,buf);
if (k == LONG_MIN) continue;
aux += k;
}
sprintf(buf,"%ld",aux);
printf("%-11s",buf);
/* Used memory */
aux = getLongInfoField(reply->str,"used_memory");
bytesToHuman(buf,aux);
printf("%-8s",buf);
/* Clients */
aux = getLongInfoField(reply->str,"connected_clients");
sprintf(buf,"%ld",aux);
printf(" %-8s",buf);
/* Blocked (BLPOPPING) Clients */
aux = getLongInfoField(reply->str,"blocked_clients");
sprintf(buf,"%ld",aux);
printf("%-8s",buf);
/* Requets */
aux = getLongInfoField(reply->str,"total_commands_processed");
sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
printf("%-19s",buf);
requests = aux;
/* Connections */
aux = getLongInfoField(reply->str,"total_connections_received");
sprintf(buf,"%ld",aux);
printf(" %-12s",buf);
/* Children */
aux = getLongInfoField(reply->str,"bgsave_in_progress");
aux |= getLongInfoField(reply->str,"aof_rewrite_in_progress") << 1;
switch(aux) {
case 0: break;
case 1:
printf("SAVE");
break;
case 2:
printf("AOF");
break;
case 3:
printf("SAVE+AOF");
break;
}
printf("\n");
freeReplyObject(reply);
usleep(config.interval);
}
}
int main(int argc, char **argv) {
int firstarg;
@@ -1201,8 +1479,11 @@ int main(int argc, char **argv) {
config.monitor_mode = 0;
config.pubsub_mode = 0;
config.latency_mode = 0;
config.latency_history = 0;
config.cluster_mode = 0;
config.slave_mode = 0;
config.getrdb_mode = 0;
config.rdb_filename = NULL;
config.pipe_mode = 0;
config.bigkeys = 0;
config.stdinarg = 0;
@@ -1221,16 +1502,22 @@ int main(int argc, char **argv) {
/* Latency mode */
if (config.latency_mode) {
cliConnect(0);
if (cliConnect(0) == REDIS_ERR) exit(1);
latencyMode();
}
/* Slave mode */
if (config.slave_mode) {
cliConnect(0);
if (cliConnect(0) == REDIS_ERR) exit(1);
slaveMode();
}
/* Get RDB mode. */
if (config.getrdb_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
getRDB();
}
/* Pipe mode */
if (config.pipe_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
@@ -1239,10 +1526,17 @@ int main(int argc, char **argv) {
/* Find big keys */
if (config.bigkeys) {
cliConnect(0);
if (cliConnect(0) == REDIS_ERR) exit(1);
findBigKeys();
}
/* Stat mode */
if (config.stat_mode) {
if (cliConnect(0) == REDIS_ERR) exit(1);
if (config.interval == 0) config.interval = 1000000;
statMode();
}
/* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) {
/* Note that in repl mode we don't abort on connection error.
+190 -90
View File
@@ -99,7 +99,7 @@ struct redisCommand *commandTable;
* m: may increase memory usage once called. Don't allow if out of memory.
* a: admin command, like SAVE or SHUTDOWN.
* p: Pub/Sub related command.
* f: force replication of this command, regarless of server.dirty.
* f: force replication of this command, regardless of server.dirty.
* s: command not allowed in scripts.
* R: random command. Command is not deterministic, that is, the same command
* with the same arguments, with the same key space, may have different
@@ -114,7 +114,7 @@ struct redisCommand *commandTable;
*/
struct redisCommand redisCommandTable[] = {
{"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
{"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
{"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,1,1,1,0,0},
@@ -197,7 +197,7 @@ struct redisCommand redisCommandTable[] = {
{"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
{"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
{"select",selectCommand,2,"r",0,NULL,0,0,0,0,0},
{"select",selectCommand,2,"rl",0,NULL,0,0,0,0,0},
{"move",moveCommand,3,"w",0,NULL,1,1,1,0,0},
{"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
{"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
@@ -207,14 +207,14 @@ struct redisCommand redisCommandTable[] = {
{"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0},
{"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},
{"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"rs",0,NULL,0,0,0,0,0},
{"auth",authCommand,2,"rsl",0,NULL,0,0,0,0,0},
{"ping",pingCommand,1,"r",0,NULL,0,0,0,0,0},
{"echo",echoCommand,2,"r",0,NULL,0,0,0,0,0},
{"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
{"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
{"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
{"shutdown",shutdownCommand,-1,"ar",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
{"lastsave",lastsaveCommand,1,"rR",0,NULL,0,0,0,0,0},
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
{"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
{"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0},
@@ -236,7 +236,7 @@ struct redisCommand redisCommandTable[] = {
{"unsubscribe",unsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
{"punsubscribe",punsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"pflt",0,NULL,0,0,0,0,0},
{"publish",publishCommand,3,"pfltr",0,NULL,0,0,0,0,0},
{"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
{"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
{"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
@@ -288,7 +288,7 @@ void redisLogRaw(int level, const char *msg) {
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
}
/* Like redisLogRaw() but with printf-alike support. This is the funciton that
/* Like redisLogRaw() but with printf-alike support. This is the function that
* is used across the code. The raw version is only used in order to dump
* the INFO output on crash. */
void redisLog(int level, const char *fmt, ...) {
@@ -363,7 +363,7 @@ void exitFromChild(int retcode) {
/*====================== Hash table type implementation ==================== */
/* This is an hash table type that uses the SDS dynamic strings libary as
/* This is an hash table type that uses the SDS dynamic strings library as
* keys and radis objects as values (objects can hold SDS strings,
* lists, sets). */
@@ -537,7 +537,7 @@ dictType commandTableDictType = {
NULL /* val destructor */
};
/* Hash type hash table (note that small hashes are represented with zimpaps) */
/* Hash type hash table (note that small hashes are represented with ziplists) */
dictType hashDictType = {
dictEncObjHash, /* hash function */
NULL, /* key dup */
@@ -570,36 +570,32 @@ int htNeedsResize(dict *dict) {
/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
* we resize the hash table to save memory */
void tryResizeHashTables(void) {
int j;
for (j = 0; j < server.dbnum; j++) {
if (htNeedsResize(server.db[j].dict))
dictResize(server.db[j].dict);
if (htNeedsResize(server.db[j].expires))
dictResize(server.db[j].expires);
}
void tryResizeHashTables(int dbid) {
if (htNeedsResize(server.db[dbid].dict))
dictResize(server.db[dbid].dict);
if (htNeedsResize(server.db[dbid].expires))
dictResize(server.db[dbid].expires);
}
/* Our hash table implementation performs rehashing incrementally while
* we write/read from the hash table. Still if the server is idle, the hash
* table will use two tables for a long time. So we try to use 1 millisecond
* of CPU time at every serverCron() loop in order to rehash some key. */
void incrementallyRehash(void) {
int j;
for (j = 0; j < server.dbnum; j++) {
/* Keys dictionary */
if (dictIsRehashing(server.db[j].dict)) {
dictRehashMilliseconds(server.db[j].dict,1);
break; /* already used our millisecond for this loop... */
}
/* Expires */
if (dictIsRehashing(server.db[j].expires)) {
dictRehashMilliseconds(server.db[j].expires,1);
break; /* already used our millisecond for this loop... */
}
* of CPU time at every call of this function to perform some rehahsing.
*
* The function returns 1 if some rehashing was performed, otherwise 0
* is returned. */
int incrementallyRehash(int dbid) {
/* Keys dictionary */
if (dictIsRehashing(server.db[dbid].dict)) {
dictRehashMilliseconds(server.db[dbid].dict,1);
return 1; /* already used our millisecond for this loop... */
}
/* Expires */
if (dictIsRehashing(server.db[dbid].expires)) {
dictRehashMilliseconds(server.db[dbid].expires,1);
return 1; /* already used our millisecond for this loop... */
}
return 0;
}
/* This function is called once a background process of some kind terminates,
@@ -620,28 +616,57 @@ void updateDictResizePolicy(void) {
/* Try to expire a few timed out keys. The algorithm used is adaptive and
* will use few CPU cycles if there are few expiring keys, otherwise
* it will get more aggressive to avoid that too much memory is used by
* keys that can be removed from the keyspace. */
* keys that can be removed from the keyspace.
*
* No more than REDIS_DBCRON_DBS_PER_CALL databases are tested at every
* iteration. */
void activeExpireCycle(void) {
int j, iteration = 0;
/* This function has some global state in order to continue the work
* incrementally across calls. */
static unsigned int current_db = 0; /* Last DB tested. */
static int timelimit_exit = 0; /* Time limit hit in previous call? */
unsigned int j, iteration = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
long long start = ustime(), timelimit;
/* We usually should test REDIS_DBCRON_DBS_PER_CALL per iteration, with
* two exceptions:
*
* 1) Don't test more DBs than we have.
* 2) If last time we hit the time limit, we want to scan all DBs
* in this iteration, as there is work to do in some DB and we don't want
* expired keys to use memory for too much time. */
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
/* We can use at max REDIS_EXPIRELOOKUPS_TIME_PERC percentage of CPU time
* per iteration. Since this function gets called with a frequency of
* REDIS_HZ times per second, the following is the max amount of
* server.hz times per second, the following is the max amount of
* microseconds we can spend in this function. */
timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/REDIS_HZ/100;
timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/server.hz/100;
timelimit_exit = 0;
if (timelimit <= 0) timelimit = 1;
for (j = 0; j < server.dbnum; j++) {
for (j = 0; j < dbs_per_call; j++) {
int expired;
redisDb *db = server.db+j;
redisDb *db = server.db+(current_db % server.dbnum);
/* Increment the DB now so we are sure if we run out of time
* in the current DB we'll restart from the next. This allows to
* distribute the time evenly across DBs. */
current_db++;
/* Continue to expire if at the end of the cycle more than 25%
* of the keys were expired. */
do {
unsigned long num = dictSize(db->expires);
unsigned long slots = dictSlots(db->expires);
long long now = mstime();
unsigned long num, slots;
long long now;
/* If there is nothing to expire try next DB ASAP. */
if ((num = dictSize(db->expires)) == 0) break;
slots = dictSlots(db->expires);
now = mstime();
/* When there are less than 1% filled slots getting random
* keys is expensive, so stop here waiting for better times...
@@ -675,8 +700,12 @@ void activeExpireCycle(void) {
* expire. So after a given amount of milliseconds return to the
* caller waiting for the other active expire cycle. */
iteration++;
if ((iteration & 0xf) == 0 && /* check once every 16 cycles. */
(ustime()-start) > timelimit) return;
if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */
(ustime()-start) > timelimit)
{
timelimit_exit = 1;
return;
}
} while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4);
}
}
@@ -738,7 +767,7 @@ int clientsCronHandleTimeout(redisClient *c) {
/* The client query buffer is an sds.c string that can end with a lot of
* free space not used, this function reclaims space if needed.
*
* The funciton always returns 0 as it never terminates the client. */
* The function always returns 0 as it never terminates the client. */
int clientsCronResizeQueryBuffer(redisClient *c) {
size_t querybuf_size = sdsAllocSize(c->querybuf);
time_t idletime = server.unixtime - c->lastinteraction;
@@ -762,13 +791,13 @@ int clientsCronResizeQueryBuffer(redisClient *c) {
}
void clientsCron(void) {
/* Make sure to process at least 1/(REDIS_HZ*10) of clients per call.
* Since this function is called REDIS_HZ times per second we are sure that
/* Make sure to process at least 1/(server.hz*10) of clients per call.
* Since this function is called server.hz times per second we are sure that
* in the worst case we process all the clients in 10 seconds.
* In normal conditions (a reasonable number of clients) we process
* all the clients in a shorter time. */
int numclients = listLength(server.clients);
int iterations = numclients/(REDIS_HZ*10);
int iterations = numclients/(server.hz*10);
if (iterations < 50)
iterations = (numclients < 50) ? numclients : 50;
@@ -790,21 +819,66 @@ void clientsCron(void) {
}
}
/* This is our timer interrupt, called REDIS_HZ times per second.
/* This function handles 'background' operations we are required to do
* incrementally in Redis databases, such as active key expiring, resizing,
* rehashing. */
void databasesCron(void) {
/* Expire keys by random sampling. Not required for slaves
* as master will synthesize DELs for us. */
if (server.active_expire_enabled && server.masterhost == NULL)
activeExpireCycle();
/* Perform hash tables rehashing if needed, but only if there are no
* other processes saving the DB on disk. Otherwise rehashing is bad
* as will cause a lot of copy-on-write of memory pages. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {
/* We use global counters so if we stop the computation at a given
* DB we'll be able to start from the successive in the next
* cron loop iteration. */
static unsigned int resize_db = 0;
static unsigned int rehash_db = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
unsigned int j;
/* Don't test more DBs than we have. */
if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;
/* Resize */
for (j = 0; j < dbs_per_call; j++) {
tryResizeHashTables(resize_db % server.dbnum);
resize_db++;
}
/* Rehash */
if (server.activerehashing) {
for (j = 0; j < dbs_per_call; j++) {
int work_done = incrementallyRehash(rehash_db % server.dbnum);
rehash_db++;
if (work_done) {
/* If the function did some work, stop here, we'll do
* more at the next cron loop. */
break;
}
}
}
}
}
/* This is our timer interrupt, called server.hz times per second.
* Here is where we do a number of things that need to be done asynchronously.
* For instance:
*
* - Active expired keys collection (it is also performed in a lazy way on
* lookup).
* - Software watchdong.
* - Software watchdog.
* - Update some statistic.
* - Incremental rehashing of the DBs hash tables.
* - Triggering BGSAVE / AOF rewrite, and handling of terminated children.
* - Clients timeout of differnet kinds.
* - Clients timeout of different kinds.
* - Replication reconnection.
* - Many more...
*
* Everything directly called here will be called REDIS_HZ times per second,
* Everything directly called here will be called server.hz times per second,
* so in order to throttle execution of things we want to do less frequently
* a macro is used: run_with_period(milliseconds) { .... }
*/
@@ -829,7 +903,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* We have just 22 bits per object for LRU information.
* So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
* 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
* 2^22 bits with 10 seconds resolution is more or less 1.5 years.
*
* Note that even if this will wrap after 1.5 years it's not a problem,
* everything will still work but just some object will appear younger
@@ -850,6 +924,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
if (server.shutdown_asap) {
if (prepareForShutdown(0) == REDIS_OK) exit(0);
redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
server.shutdown_asap = 0;
}
/* Show some info about non-empty databases */
@@ -867,17 +942,6 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
}
}
/* We don't want to resize the hash tables while a bacground saving
* is in progress: the saving child is created using fork() that is
* implemented with a copy-on-write semantic in most modern systems, so
* if we resize the HT while there is the saving child at work actually
* a lot of memory movements in the parent will cause a lot of pages
* copied. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {
tryResizeHashTables();
if (server.activerehashing) incrementallyRehash();
}
/* Show information about connected clients */
if (!server.sentinel_mode) {
run_with_period(5000) {
@@ -892,6 +956,9 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* We need to do a few operations on clients asynchronously. */
clientsCron();
/* Handle background operations on Redis databases. */
databasesCron();
/* Start a scheduled AOF rewrite if this was requested by the user while
* a BGSAVE was in progress. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
@@ -928,8 +995,16 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
/* Save if we reached the given amount of changes,
* the given amount of seconds, and if the latest bgsave was
* successful or if, in case of an error, at least
* REDIS_BGSAVE_RETRY_DELAY seconds already elapsed. */
if (server.dirty >= sp->changes &&
server.unixtime-server.lastsave > sp->seconds) {
server.unixtime-server.lastsave > sp->seconds &&
(server.unixtime-server.lastbgsave_try >
REDIS_BGSAVE_RETRY_DELAY ||
server.lastbgsave_status == REDIS_OK))
{
redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
sp->changes, sp->seconds);
rdbSaveBackground(server.rdb_filename);
@@ -958,11 +1033,6 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* cron function is called. */
if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);
/* Expire a few keys per cycle, only if this is a master.
* On slaves we wait for DEL operations synthesized by the master
* in order to guarantee a strict consistency. */
if (server.masterhost == NULL) activeExpireCycle();
/* Close clients that need to be closed asynchronous */
freeClientsInAsyncFreeQueue();
@@ -976,7 +1046,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
}
server.cronloops++;
return 1000/REDIS_HZ;
return 1000/server.hz;
}
/* This function gets called every time Redis is entering the
@@ -1082,6 +1152,7 @@ void createSharedObjects(void) {
void initServerConfig() {
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
server.hz = REDIS_DEFAULT_HZ;
server.runid[REDIS_RUN_ID_SIZE] = '\0';
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
server.port = REDIS_SERVERPORT;
@@ -1093,6 +1164,8 @@ void initServerConfig() {
server.dbnum = REDIS_DEFAULT_DBNUM;
server.verbosity = REDIS_NOTICE;
server.maxidletime = REDIS_MAXIDLETIME;
server.tcpkeepalive = 0;
server.active_expire_enabled = 1;
server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
server.saveparams = NULL;
server.loading = 0;
@@ -1116,12 +1189,14 @@ void initServerConfig() {
server.aof_fd = -1;
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_rewrite_incremental_fsync = 1;
server.pidfile = zstrdup("/var/run/redis.pid");
server.rdb_filename = zstrdup("dump.rdb");
server.aof_filename = zstrdup("appendonly.aof");
server.requirepass = NULL;
server.rdb_compression = 1;
server.rdb_checksum = 1;
server.stop_writes_on_bgsave_err = 1;
server.activerehashing = 1;
server.maxclients = REDIS_MAX_CLIENTS;
server.bpop_blocked_clients = 0;
@@ -1158,7 +1233,8 @@ void initServerConfig() {
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = 1;
server.repl_slave_ro = 1;
server.repl_down_since = time(NULL);
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
server.repl_disable_tcp_nodelay = 0;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
/* Client output buffer limits */
@@ -1178,10 +1254,11 @@ void initServerConfig() {
R_NegInf = -1.0/R_Zero;
R_Nan = R_Zero/R_Zero;
/* Command table -- we intiialize it here as it is part of the
/* Command table -- we initiialize it here as it is part of the
* initial configuration, since command names may be changed via
* redis.conf using the rename-command directive. */
server.commands = dictCreate(&commandTableDictType,NULL);
server.orig_commands = dictCreate(&commandTableDictType,NULL);
populateCommandTable();
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
@@ -1307,7 +1384,8 @@ void initServer() {
server.aof_child_pid = -1;
aofRewriteBufferReset();
server.aof_buf = sdsempty();
server.lastsave = time(NULL);
server.lastsave = time(NULL); /* At startup we consider the DB saved. */
server.lastbgsave_try = 0; /* At startup we never tried to BGSAVE. */
server.rdb_save_time_last = -1;
server.rdb_save_time_start = -1;
server.dirty = 0;
@@ -1327,8 +1405,10 @@ void initServer() {
server.ops_sec_last_sample_ops = 0;
server.unixtime = time(NULL);
server.lastbgsave_status = REDIS_OK;
server.stop_writes_on_bgsave_err = 1;
aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL);
if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
redisPanic("create time event failed");
exit(1);
}
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.ipfd file event.");
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
@@ -1368,7 +1448,7 @@ void populateCommandTable(void) {
for (j = 0; j < numcommands; j++) {
struct redisCommand *c = redisCommandTable+j;
char *f = c->sflags;
int retval;
int retval1, retval2;
while(*f != '\0') {
switch(*f) {
@@ -1389,8 +1469,11 @@ void populateCommandTable(void) {
f++;
}
retval = dictAdd(server.commands, sdsnew(c->name), c);
assert(retval == DICT_OK);
retval1 = dictAdd(server.commands, sdsnew(c->name), c);
/* Populate an additional dictionary that will be unaffected
* by rename-command statements in redis.conf. */
retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);
}
}
@@ -1458,6 +1541,20 @@ struct redisCommand *lookupCommandByCString(char *s) {
return cmd;
}
/* Lookup the command in the current table, if not found also check in
* the original table containing the original command names unaffected by
* redis.conf rename-command statement.
*
* This is used by functions rewriting the argument vector such as
* rewriteClientCommandVector() in order to set client->cmd pointer
* correctly even if the command was renamed. */
struct redisCommand *lookupCommandOrOriginal(sds name) {
struct redisCommand *cmd = dictFetchValue(server.commands, name);
if (!cmd) cmd = dictFetchValue(server.orig_commands,name);
return cmd;
}
/* Propagate the specified command (in the context of the specified database id)
* to AOF and Slaves.
*
@@ -1488,7 +1585,7 @@ void call(redisClient *c, int flags) {
long long dirty, start = ustime(), duration;
/* Sent the command to clients in MONITOR mode, only if the commands are
* not geneated from reading an AOF. */
* not generated from reading an AOF. */
if (listLength(server.monitors) &&
!server.loading &&
!(c->cmd->flags & REDIS_CMD_SKIP_MONITOR))
@@ -1510,7 +1607,7 @@ void call(redisClient *c, int flags) {
/* Log the command into the Slow log if needed, and populate the
* per-command statistics that we show in INFO commandstats. */
if (flags & REDIS_CALL_SLOWLOG)
if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)
slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
if (flags & REDIS_CALL_STATS) {
c->cmd->microseconds += duration;
@@ -1528,8 +1625,9 @@ void call(redisClient *c, int flags) {
if (flags != REDIS_PROPAGATE_NONE)
propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
}
/* Commands such as LPUSH or BRPOPLPUSH may propagate an additional
* PUSH command. */
/* Handle the alsoPropagate() API to handle commands that want to propagate
* multiple separated commands. */
if (server.also_propagate.numops) {
int j;
redisOp *rop;
@@ -1549,8 +1647,8 @@ void call(redisClient *c, int flags) {
* server for a bulk read from the client.
*
* If 1 is returned the client is still alive and valid and
* and other operations can be performed by the caller. Otherwise
* if 0 is returned the client was destroied (i.e. after QUIT). */
* other operations can be performed by the caller. Otherwise
* if 0 is returned the client was destroyed (i.e. after QUIT). */
int processCommand(redisClient *c) {
/* The QUIT command is handled separately. Normal command procs will
* go through checking for replication and QUIT will cause trouble
@@ -1692,7 +1790,7 @@ int prepareForShutdown(int flags) {
overwrite the synchronous saving did by SHUTDOWN. */
if (server.rdb_child_pid != -1) {
redisLog(REDIS_WARNING,"There is a child saving an .rdb. Killing it!");
kill(server.rdb_child_pid,SIGKILL);
kill(server.rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(server.rdb_child_pid);
}
if (server.aof_state != REDIS_AOF_OFF) {
@@ -1701,7 +1799,7 @@ int prepareForShutdown(int flags) {
if (server.aof_child_pid != -1) {
redisLog(REDIS_WARNING,
"There is a child rewriting the AOF. Killing it!");
kill(server.aof_child_pid,SIGKILL);
kill(server.aof_child_pid,SIGUSR1);
}
/* Append only file: fsync() the AOF and exit */
redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file.");
@@ -1803,7 +1901,7 @@ void echoCommand(redisClient *c) {
void timeCommand(redisClient *c) {
struct timeval tv;
/* gettimeofday() can only fail if &tv is a bad addresss so we
/* gettimeofday() can only fail if &tv is a bad address so we
* don't check for errors. */
gettimeofday(&tv,NULL);
addReplyMultiBulkLen(c,2);
@@ -1878,6 +1976,7 @@ sds genRedisInfoString(char *section) {
"tcp_port:%d\r\n"
"uptime_in_seconds:%ld\r\n"
"uptime_in_days:%ld\r\n"
"hz:%d\r\n"
"lru_clock:%ld\r\n",
REDIS_VERSION,
redisGitSHA1(),
@@ -1896,6 +1995,7 @@ sds genRedisInfoString(char *section) {
server.port,
uptime,
uptime/(3600*24),
server.hz,
(unsigned long) server.lruclock);
}
@@ -2515,7 +2615,7 @@ void loadDataFromDisk(void) {
redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
(float)(ustime()-start)/1000000);
} else if (errno != ENOENT) {
redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
exit(1);
}
}
@@ -2524,7 +2624,7 @@ void loadDataFromDisk(void) {
void redisOutOfMemoryHandler(size_t allocation_size) {
redisLog(REDIS_WARNING,"Out Of Memory allocating %zu bytes!",
allocation_size);
redisPanic("OOM");
redisPanic("Redis aborting for OUT OF MEMORY");
}
int main(int argc, char **argv) {
+30 -17
View File
@@ -67,13 +67,16 @@
#define REDIS_ERR -1
/* Static server configuration */
#define REDIS_HZ 100 /* Time interrupt calls/sec. */
#define REDIS_DEFAULT_HZ 10 /* Time interrupt calls/sec. */
#define REDIS_MIN_HZ 1
#define REDIS_MAX_HZ 500
#define REDIS_SERVERPORT 6379 /* TCP port */
#define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */
#define REDIS_DEFAULT_DBNUM 16
#define REDIS_CONFIGLINE_MAX 1024
#define REDIS_EXPIRELOOKUPS_PER_CRON 10 /* lookup 10 expires per loop */
#define REDIS_EXPIRELOOKUPS_TIME_PERC 25 /* CPU max % for keys collection */
#define REDIS_DBCRON_DBS_PER_CALL 16
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
#define REDIS_SHARED_SELECT_CMDS 10
#define REDIS_SHARED_INTEGERS 10000
@@ -91,6 +94,7 @@
#define REDIS_REPL_PING_SLAVE_PERIOD 10
#define REDIS_RUN_ID_SIZE 40
#define REDIS_OPS_SEC_SAMPLES 16
#define REDIS_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */
/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
@@ -98,6 +102,7 @@
#define REDIS_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */
#define REDIS_INLINE_MAX_SIZE (1024*64) /* Max size of inline reads */
#define REDIS_MBULK_BIG_ARG (1024*32)
#define REDIS_AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */
/* Hash table parameters */
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
@@ -142,12 +147,12 @@
*
* 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
* 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
* 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
* 10|000000 [32 bit integer] => if it's 10, a full 32 bit len will follow
* 11|000000 this means: specially encoded object will follow. The six bits
* number specify the kind of object that follows.
* See the REDIS_RDB_ENC_* defines.
*
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
* Lengths up to 63 are stored using a single byte, most DB keys, and may
* values, will fit inside. */
#define REDIS_RDB_6BITLEN 0
#define REDIS_RDB_14BITLEN 1
@@ -291,8 +296,8 @@
/* Using the following macro you can run code inside serverCron() with the
* specified period, specified in milliseconds.
* The actual resolution depends on REDIS_HZ. */
#define run_with_period(_ms_) if (!(server.cronloops%((_ms_)/(1000/REDIS_HZ))))
* The actual resolution depends on server.hz. */
#define run_with_period(_ms_) if ((_ms_ <= 1000/server.hz) || !(server.cronloops%((_ms_)/(1000/server.hz))))
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))
@@ -317,7 +322,7 @@ typedef struct redisObject {
void *ptr;
} robj;
/* Macro used to initalize a Redis object allocated on the stack.
/* Macro used to initialize a Redis object allocated on the stack.
* Note that this macro is taken near the structure definition to make sure
* we'll update it when the structure is changed, to avoid bugs like
* bug #85 introduced exactly in this way. */
@@ -350,9 +355,8 @@ typedef struct multiState {
} multiState;
typedef struct blockingState {
robj **keys; /* The key we are waiting to terminate a blocking
dict *keys; /* The keys we are waiting to terminate a blocking
* operation such as BLPOP. Otherwise NULL. */
int count; /* Number of blocking keys */
time_t timeout; /* Blocking operation timeout. If UNIX current time
* is >= timeout then the operation timed out. */
robj *target; /* The key that should receive the element,
@@ -375,12 +379,13 @@ typedef struct readyList {
robj *key;
} readyList;
/* With multiplexing we need to take per-clinet state.
/* With multiplexing we need to take per-client state.
* Clients are taken in a liked list. */
typedef struct redisClient {
int fd;
redisDb *db;
int dictid;
robj *name; /* As set by CLIENT SETNAME */
sds querybuf;
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */
int argc;
@@ -494,8 +499,10 @@ typedef struct redisOpArray {
struct redisServer {
/* General */
int hz; /* serverCron() calls frequency in hertz */
redisDb *db;
dict *commands; /* Command table hash table */
dict *commands; /* Command table */
dict *orig_commands; /* Command table before command renaming. */
aeEventLoop *el;
unsigned lruclock:22; /* Clock incrementing every minute, for LRU */
unsigned lruclock_padding:10;
@@ -536,7 +543,7 @@ struct redisServer {
long long stat_keyspace_hits; /* Number of successful lookups of keys */
long long stat_keyspace_misses; /* Number of failed lookups of keys */
size_t stat_peak_memory; /* Max used memory record */
long long stat_fork_time; /* Time needed to perform latets fork() */
long long stat_fork_time; /* Time needed to perform latest fork() */
long long stat_rejected_conn; /* Clients rejected because of maxclients */
list *slowlog; /* SLOWLOG list of commands */
long long slowlog_entry_id; /* SLOWLOG current entry ID */
@@ -551,6 +558,8 @@ struct redisServer {
/* Configuration */
int verbosity; /* Loglevel in redis.conf */
int maxidletime; /* Client timeout in seconds */
int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */
int active_expire_enabled; /* Can be disabled for testing purposes. */
size_t client_max_querybuf_len; /* Limit for client query buffer length */
int dbnum; /* Total number of configured DBs */
int daemonize; /* True if running as a daemon */
@@ -576,6 +585,7 @@ struct redisServer {
time_t aof_rewrite_time_start; /* Current AOF rewrite start time. */
int aof_lastbgrewrite_status; /* REDIS_OK or REDIS_ERR */
unsigned long aof_delayed_fsync; /* delayed AOF fsync() counter */
int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */
/* RDB persistence */
long long dirty; /* Changes to DB from the last save */
long long dirty_before_bgsave; /* Used to restore dirty on failed BGSAVE */
@@ -585,7 +595,8 @@ struct redisServer {
char *rdb_filename; /* Name of RDB file */
int rdb_compression; /* Use compression in RDB? */
int rdb_checksum; /* Use RDB checksum? */
time_t lastsave; /* Unix time of last save succeeede */
time_t lastsave; /* Unix time of last successful save */
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 lastbgsave_status; /* REDIS_OK or REDIS_ERR */
@@ -616,11 +627,12 @@ struct redisServer {
int repl_serve_stale_data; /* Serve stale data when link is down? */
int repl_slave_ro; /* Slave is read only? */
time_t repl_down_since; /* Unix time at which link with master went down */
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
int slave_priority; /* Reported in INFO and used by Sentinel. */
/* Limits */
unsigned int maxclients; /* Max number of simultaneous clients */
unsigned long long maxmemory; /* Max number of memory bytes to use */
int maxmemory_policy; /* Policy for key evition */
int maxmemory_policy; /* Policy for key eviction */
int maxmemory_samples; /* Pricision of random sampling */
/* Blocked clients */
unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */
@@ -657,7 +669,7 @@ struct redisServer {
int lua_timedout; /* True if we reached the time limit for script
execution. */
int lua_kill; /* Kill the script if true. */
/* Assert & bug reportign */
/* Assert & bug reporting */
char *assert_failed;
char *assert_file;
int assert_line;
@@ -676,13 +688,13 @@ struct redisCommand {
char *name;
redisCommandProc *proc;
int arity;
char *sflags; /* Flags as string represenation, one char per flag. */
char *sflags; /* Flags as string representation, one char per flag. */
int flags; /* The actual flags, obtained from the 'sflags' field. */
/* Use a function to determine keys arguments in a command line. */
redisGetKeysProc *getkeys_proc;
/* What keys should be loaded in background when calling this command? */
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey; /* THe last argument that's a key */
int lastkey; /* The last argument that's a key */
int keystep; /* The step between first and last key */
long long microseconds, calls;
};
@@ -729,7 +741,7 @@ typedef struct {
dictIterator *di;
} setTypeIterator;
/* Structure to hold hash iteration abstration. Note that iteration over
/* Structure to hold hash iteration abstraction. Note that iteration over
* hashes involves both fields and values. Because it is possible that
* not both are required, store pointers in the iterator to avoid
* unnecessary memory allocation for fields/values. */
@@ -943,6 +955,7 @@ int processCommand(redisClient *c);
void setupSignalHandlers(void);
struct redisCommand *lookupCommand(sds name);
struct redisCommand *lookupCommandByCString(char *s);
struct redisCommand *lookupCommandOrOriginal(sds name);
void call(redisClient *c, int flags);
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags);
void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int target);
+2 -2
View File
@@ -27,8 +27,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Every time the Redis Git SHA1 or Dirty status changes only this file
* small file is recompiled, as we access this information in all the other
/* Every time the Redis Git SHA1 or Dirty status changes only this small
* file is recompiled, as we access this information in all the other
* files using this functions. */
#include "release.h"
+46 -19
View File
@@ -52,7 +52,7 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue;
/* Feed slaves that are waiting for the initial SYNC (so these commands
* are queued in the output buffer until the intial SYNC completes),
* are queued in the output buffer until the initial SYNC completes),
* or are already in sync with the master. */
if (slave->slaveseldb != dictid) {
robj *selectcmd;
@@ -115,7 +115,7 @@ void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **
}
void syncCommand(redisClient *c) {
/* ignore SYNC if aleady slave or in monitor mode */
/* ignore SYNC if already slave or in monitor mode */
if (c->flags & REDIS_SLAVE) return;
/* Refuse SYNC requests if we are a slave but the link with our master
@@ -172,6 +172,9 @@ void syncCommand(redisClient *c) {
}
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
}
if (server.repl_disable_tcp_nodelay)
anetDisableTcpNoDelay(NULL, c->fd); /* Non critical if it fails. */
c->repldbfd = -1;
c->flags |= REDIS_SLAVE;
c->slaveseldb = 0;
@@ -229,7 +232,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
if (slave->repldboff == 0) {
/* Write the bulk write count before to transfer the DB. In theory here
* we don't know how much room there is in the output buffer of the
* socket, but in pratice SO_SNDLOWAT (the minimum count for output
* socket, but in practice SO_SNDLOWAT (the minimum count for output
* operations) will never be smaller than the few bytes we need. */
sds bulkcount;
@@ -272,7 +275,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
}
}
/* This function is called at the end of every backgrond saving.
/* This function is called at the end of every background saving.
* The argument bgsaveerr is REDIS_OK if the background saving succeeded
* otherwise REDIS_ERR is passed to the function.
*
@@ -424,6 +427,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
}
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
signalFlushedDb(-1);
emptyDb();
/* Before loading the DB into memory we need to delete the readable
* handler, otherwise it will get called recursively since
@@ -451,7 +455,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
stopAppendOnly();
while (retry-- && startAppendOnly() == REDIS_ERR) {
redisLog(REDIS_WARNING,"Failed enabling the AOF after successful master synchrnization! Trying it again in one second.");
redisLog(REDIS_WARNING,"Failed enabling the AOF after successful master synchronization! Trying it again in one second.");
sleep(1);
}
if (!retry) {
@@ -575,11 +579,16 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
goto error;
}
/* We don't care about the reply, it can be +PONG or an error since
* the server requires AUTH. As long as it replies correctly, it's
* fine from our point of view. */
if (buf[0] != '-' && buf[0] != '+') {
redisLog(REDIS_WARNING,"Unexpected reply to PING from master.");
/* We accept only two replies as valid, a positive +PONG reply
* (we just check for "+") or an authentication error.
* Note that older versions of Redis replied with "operation not
* permitted" instead of using a proper error code, so we test
* both. */
if (buf[0] != '+' &&
strncmp(buf,"-NOAUTH",7) != 0 &&
strncmp(buf,"-ERR operation not permitted",28) != 0)
{
redisLog(REDIS_WARNING,"Error reply to PING from master: '%s'",buf);
goto error;
} else {
redisLog(REDIS_NOTICE,
@@ -636,7 +645,9 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)
== AE_ERR)
{
redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
redisLog(REDIS_WARNING,
"Can't create readable event for SYNC: %s (fd=%d)",
strerror(errno),fd);
goto error;
}
@@ -693,6 +704,27 @@ void undoConnectWithMaster(void) {
server.repl_state = REDIS_REPL_CONNECT;
}
/* This function aborts a non blocking replication attempt if there is one
* in progress, by canceling the non-blocking connect attempt or
* the initial bulk transfer.
*
* If there was a replication handshake in progress 1 is returned and
* the replication state (server.repl_state) set to REDIS_REPL_CONNECT.
*
* Otherwise zero is returned and no operation is perforemd at all. */
int cancelReplicationHandshake(void) {
if (server.repl_state == REDIS_REPL_TRANSFER) {
replicationAbortSyncTransfer();
} else if (server.repl_state == REDIS_REPL_CONNECTING ||
server.repl_state == REDIS_REPL_RECEIVE_PONG)
{
undoConnectWithMaster();
} else {
return 0;
}
return 1;
}
void slaveofCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"no") &&
!strcasecmp(c->argv[2]->ptr,"one")) {
@@ -700,11 +732,7 @@ void slaveofCommand(redisClient *c) {
sdsfree(server.masterhost);
server.masterhost = NULL;
if (server.master) freeClient(server.master);
if (server.repl_state == REDIS_REPL_TRANSFER)
replicationAbortSyncTransfer();
else if (server.repl_state == REDIS_REPL_CONNECTING ||
server.repl_state == REDIS_REPL_RECEIVE_PONG)
undoConnectWithMaster();
cancelReplicationHandshake();
server.repl_state = REDIS_REPL_NONE;
redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)");
}
@@ -728,8 +756,7 @@ void slaveofCommand(redisClient *c) {
server.masterport = port;
if (server.master) freeClient(server.master);
disconnectSlaves(); /* Force our slaves to resync with us as well. */
if (server.repl_state == REDIS_REPL_TRANSFER)
replicationAbortSyncTransfer();
cancelReplicationHandshake();
server.repl_state = REDIS_REPL_CONNECT;
redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)",
server.masterhost, server.masterport);
@@ -778,7 +805,7 @@ void replicationCron(void) {
* So slaves can implement an explicit timeout to masters, and will
* be able to detect a link disconnection even if the TCP connection
* will not actually go down. */
if (!(server.cronloops % (server.repl_ping_slave_period * REDIS_HZ))) {
if (!(server.cronloops % (server.repl_ping_slave_period * server.hz))) {
listIter li;
listNode *ln;
+30 -1
View File
@@ -48,8 +48,11 @@
#include "fmacros.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "rio.h"
#include "util.h"
#include "config.h"
#include "redis.h"
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
@@ -76,7 +79,18 @@ static off_t rioBufferTell(rio *r) {
/* Returns 1 or 0 for success/failure. */
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
return fwrite(buf,len,1,r->io.file.fp);
size_t retval;
retval = fwrite(buf,len,1,r->io.file.fp);
r->io.file.buffered += len;
if (r->io.file.autosync &&
r->io.file.buffered >= r->io.file.autosync)
{
aof_fsync(fileno(r->io.file.fp));
r->io.file.buffered = 0;
}
return retval;
}
/* Returns 1 or 0 for success/failure. */
@@ -110,6 +124,8 @@ static const rio rioFileIO = {
void rioInitWithFile(rio *r, FILE *fp) {
*r = rioFileIO;
r->io.file.fp = fp;
r->io.file.buffered = 0;
r->io.file.autosync = 0;
}
void rioInitWithBuffer(rio *r, sds s) {
@@ -124,6 +140,19 @@ void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {
r->cksum = crc64(r->cksum,buf,len);
}
/* Set the file-based rio object to auto-fsync every 'bytes' file written.
* By default this is set to zero that means no automatic file sync is
* performed.
*
* This feature is useful in a few contexts since when we rely on OS write
* buffers sometimes the OS buffers way too much, resulting in too many
* disk I/O concentrated in very little time. When we fsync in an explicit
* way instead the I/O pressure is more distributed across time. */
void rioSetAutoSync(rio *r, off_t bytes) {
redisAssert(r->read == rioFileIO.read);
r->io.file.autosync = bytes;
}
/* ------------------------------ 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. */
+8 -4
View File
@@ -43,10 +43,11 @@ 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 *);
/* 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 and len fields pointing
* to the new block of data to add to the checksum computation. */
/* 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
* and len fields pointing to the new block of data to add to the checksum
* computation. */
void (*update_cksum)(struct _rio *, const void *buf, size_t len);
/* The current checksum */
@@ -60,6 +61,8 @@ struct _rio {
} buffer;
struct {
FILE *fp;
off_t buffered; /* Bytes written since last fsync. */
off_t autosync; /* fsync after 'autosync' bytes written. */
} file;
} io;
};
@@ -96,5 +99,6 @@ size_t rioWriteBulkLongLong(rio *r, long long l);
size_t rioWriteBulkDouble(rio *r, double d);
void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);
void rioSetAutoSync(rio *r, off_t bytes);
#endif
+27 -26
View File
@@ -48,7 +48,7 @@ void sha1hex(char *digest, char *script, size_t len);
/* Take a Redis reply in the Redis protocol format and convert it into a
* Lua type. Thanks to this function, and the introduction of not connected
* clients, it is trvial to implement the redis() lua function.
* clients, it is trivial to implement the redis() lua function.
*
* Basically we take the arguments, execute the Redis command in the context
* of a non connected client, then take the generated reply and convert it
@@ -58,7 +58,7 @@ void sha1hex(char *digest, char *script, size_t len);
*
* Note: in this function we do not do any sanity check as the reply is
* generated by Redis directly. This allows us to go faster.
* The reply string can be altered during the parsing as it is discared
* The reply string can be altered during the parsing as it is discarded
* after the conversion is completed.
*
* Errors are returned as a table with a single 'err' field set to the
@@ -597,7 +597,7 @@ void scriptingInit(void) {
lua_setglobal(lua,"math");
/* Add a helper funciton that we use to sort the multi bulk output of non
/* Add a helper function that we use to sort the multi bulk output of non
* deterministic commands, when containing 'false' elements. */
{
char *compare_func = "function __redis__compare_helper(a,b)\n"
@@ -638,7 +638,7 @@ void scriptingReset(void) {
scriptingInit();
}
/* Perform the SHA1 of the input string. We use this both for hasing script
/* Perform the SHA1 of the input string. We use this both for hashing script
* bodies in order to obtain the Lua function name, and in the implementation
* of redis.sha1().
*
@@ -677,7 +677,7 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
case LUA_TTABLE:
/* We need to check if it is an array, an error, or a status reply.
* Error are returned as a single element table with 'err' field.
* Status replies are returned as single elment table with 'ok' field */
* Status replies are returned as single element table with 'ok' field */
lua_pushstring(lua,"err");
lua_gettable(lua,-2);
t = lua_type(lua,-1);
@@ -787,7 +787,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
lua_State *lua = server.lua;
char funcname[43];
long long numkeys;
int delhook = 0;
int delhook = 0, err;
/* We want the same PRNG sequence at every call so that our PRNG is
* not affected by external state. */
@@ -834,7 +834,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
if (lua_isnil(lua,1)) {
lua_pop(lua,1); /* remove the nil from the stack */
/* Function not defined... let's define it if we have the
* body of the funciton. If this is an EVALSHA call we can just
* body of the function. If this is an EVALSHA call we can just
* return an error. */
if (evalsha) {
addReply(c, shared.noscripterr);
@@ -869,30 +869,31 @@ void evalGenericCommand(redisClient *c, int evalsha) {
/* At this point whatever this script was never seen before or if it was
* already defined, we can call it. We have zero arguments and expect
* a single return value. */
if (lua_pcall(lua,0,1,0)) {
if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
if (server.lua_timedout) {
server.lua_timedout = 0;
/* Restore the readable handler that was unregistered when the
* script timeout was detected. */
aeCreateFileEvent(server.el,c->fd,AE_READABLE,
readQueryFromClient,c);
}
server.lua_caller = NULL;
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
funcname, lua_tostring(lua,-1));
lua_pop(lua,1);
lua_gc(lua,LUA_GCCOLLECT,0);
return;
}
err = lua_pcall(lua,0,1,0);
/* Perform some cleanup that we need to do both on error and success. */
if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
server.lua_timedout = 0;
if (server.lua_timedout) {
server.lua_timedout = 0;
/* Restore the readable handler that was unregistered when the
* script timeout was detected. */
aeCreateFileEvent(server.el,c->fd,AE_READABLE,
readQueryFromClient,c);
}
server.lua_caller = NULL;
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
luaReplyToRedisReply(c,lua);
lua_gc(lua,LUA_GCSTEP,1);
if (err) {
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
funcname, lua_tostring(lua,-1));
lua_pop(lua,1); /* Consume the Lua reply. */
} else {
/* On success convert the Lua return value into Redis protocol, and
* send it to * the client. */
luaReplyToRedisReply(c,lua);
}
/* If we have slaves attached we want to replicate this command as
* EVAL instead of EVALSHA. We do this also in the AOF as currently there
* is no easy way to propagate a command in a different way in the AOF
+14 -11
View File
@@ -141,7 +141,7 @@ size_t sdsAllocSize(sds s) {
* right-trim the string.
*
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
* following schema to cat bytes coming from the kerenl to the end of an
* following schema to cat bytes coming from the kernel to the end of an
* sds string new things without copying into an intermediate buffer:
*
* oldlen = sdslen(s);
@@ -475,11 +475,18 @@ int hex_digit_to_int(char c) {
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
*
* The number of arguments is stored into *argc, and an array
* of sds is returned. The caller should sdsfree() all the returned
* strings and finally zfree() the array itself.
* of sds is returned.
*
* The caller should free the resulting array of sds strings with
* sdsfreesplitres().
*
* Note that sdscatrepr() is able to convert back a string into
* a quoted string in the same format sdssplitargs() is able to parse.
*
* The function returns the allocated tokens on success, even when the
* input string is empty, or NULL if the input contains unbalanced
* quotes or closed quotes followed by non space characters
* as in: "foo"bar or "foo'
*/
sds *sdssplitargs(const char *line, int *argc) {
const char *p = line;
@@ -576,6 +583,8 @@ sds *sdssplitargs(const char *line, int *argc) {
(*argc)++;
current = NULL;
} else {
/* Even on empty input string return something not NULL. */
if (vector == NULL) vector = zmalloc(sizeof(void*));
return vector;
}
}
@@ -585,18 +594,12 @@ err:
sdsfree(vector[*argc]);
zfree(vector);
if (current) sdsfree(current);
*argc = 0;
return NULL;
}
void sdssplitargs_free(sds *argv, int argc) {
int j;
for (j = 0 ;j < argc; j++) sdsfree(argv[j]);
zfree(argv);
}
/* Modify the string substituting all the occurrences of the set of
* characters specifed in the 'from' string to the corresponding character
* characters specified in the 'from' string to the corresponding character
* in the 'to' array.
*
* For instance: sdsmapchars(mystring, "ho", "01", 2)
-1
View File
@@ -88,7 +88,6 @@ void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, const char *p, size_t len);
sds *sdssplitargs(const char *line, int *argc);
void sdssplitargs_free(sds *argv, int argc);
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
/* Low level functions exposed to the user API */
+131 -41
View File
@@ -74,6 +74,8 @@ typedef struct sentinelAddr {
#define SRI_RECONF_DONE (1<<13) /* Slave synchronized with new master. */
#define SRI_FORCE_FAILOVER (1<<14) /* Force failover with master up. */
#define SRI_SCRIPT_KILL_SENT (1<<15) /* SCRIPT KILL already sent on -BUSY */
#define SRI_DEMOTE (1<<16) /* If the instance claims to be a master, demote
it into a slave sending SLAVEOF. */
#define SENTINEL_INFO_PERIOD 10000
#define SENTINEL_PING_PERIOD 1000
@@ -403,7 +405,7 @@ void initSentinel(void) {
/* Initialize various data structures. */
sentinel.masters = dictCreate(&instancesDictType,NULL);
sentinel.tilt = 0;
sentinel.tilt_start_time = mstime();
sentinel.tilt_start_time = 0;
sentinel.previous_time = mstime();
sentinel.running_scripts = 0;
sentinel.scripts_queue = listCreate();
@@ -969,9 +971,9 @@ const char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri) {
* a master's Sentinels dictionary, we want to be very sure about not
* having duplicated instances for any reason. This is so important because
* we use those other sentinels in order to run our quorum protocol to
* understand if it's time to proceeed with the fail over.
* understand if it's time to proceed with the fail over.
*
* Making sure no duplication is possible we greately improve the robustness
* Making sure no duplication is possible we greatly improve the robustness
* of the quorum (otherwise we may end counting the same instance multiple
* times for some reason).
*
@@ -1130,7 +1132,6 @@ int sentinelResetMastersByPattern(char *pattern, int flags) {
* TODO: make this reset so that original sentinels are re-added with
* same ip / port / runid.
*/
int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, int port) {
sentinelAddr *oldaddr, *newaddr;
@@ -1139,12 +1140,26 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip,
sentinelResetMaster(master,SENTINEL_NO_FLAGS);
oldaddr = master->addr;
master->addr = newaddr;
master->o_down_since_time = 0;
master->s_down_since_time = 0;
/* Release the old address at the end so we are safe even if the function
* gets the master->addr->ip and master->addr->port as arguments. */
releaseSentinelAddr(oldaddr);
return REDIS_OK;
}
/* Return non-zero if there was no SDOWN or ODOWN error associated to this
* instance in the latest 'ms' milliseconds. */
int sentinelRedisInstanceNoDownFor(sentinelRedisInstance *ri, mstime_t ms) {
mstime_t most_recent;
most_recent = ri->s_down_since_time;
if (ri->o_down_since_time > most_recent)
most_recent = ri->o_down_since_time;
return most_recent == 0 || (mstime() - most_recent) > ms;
}
/* ============================ Config handling ============================= */
char *sentinelHandleConfiguration(char **argv, int argc) {
sentinelRedisInstance *ri;
@@ -1238,7 +1253,7 @@ void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) {
* cleanup needed.
*
* Note: we don't free the hiredis context as hiredis will do it for us
* for async conenctions. */
* for async connections. */
void sentinelDisconnectInstanceFromContext(const redisAsyncContext *c) {
sentinelRedisInstance *ri = c->data;
int pubsub;
@@ -1401,7 +1416,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
* otherwise add it. */
if (sentinelRedisInstanceLookupSlave(ri,ip,atoi(port)) == NULL) {
if ((slave = createSentinelRedisInstance(NULL,SRI_SLAVE,ip,
atoi(port), ri->quorum,ri)) != NULL)
atoi(port), ri->quorum, ri)) != NULL)
{
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
}
@@ -1446,44 +1461,80 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
ri->info_refresh = mstime();
sdsfreesplitres(lines,numlines);
/* ---------------------------- Acting half ----------------------------- */
if (sentinel.tilt) return;
/* ---------------------------- Acting half -----------------------------
* Some things will not happen if sentinel.tilt is true, but some will
* still be processed. */
/* Act if a master turned into a slave. */
if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE) {
if ((first_runid || runid_changed) && ri->slave_master_host) {
/* If it is the first time we receive INFO from it, but it's
* a slave while it was configured as a master, we want to monitor
* its master instead. */
sentinelEvent(REDIS_WARNING,"+redirect-to-master",ri,
"%s %s %d %s %d",
ri->name, ri->addr->ip, ri->addr->port,
ri->slave_master_host, ri->slave_master_port);
sentinelResetMasterAndChangeAddress(ri,ri->slave_master_host,
ri->slave_master_port);
return;
}
/* When what we believe is our master, turned into a slave, the wiser
* thing we can do is to follow the events and redirect to the new
* master, always. */
if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE && ri->slave_master_host)
{
sentinelEvent(REDIS_WARNING,"+redirect-to-master",ri,
"%s %s %d %s %d",
ri->name, ri->addr->ip, ri->addr->port,
ri->slave_master_host, ri->slave_master_port);
sentinelResetMasterAndChangeAddress(ri,ri->slave_master_host,
ri->slave_master_port);
return; /* Don't process anything after this event. */
}
/* Act if a slave turned into a master. */
/* Handle slave -> master role switch. */
if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) {
if (!(ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
(runid_changed || first_runid))
if (!sentinel.tilt && ri->flags & SRI_DEMOTE) {
/* If this sentinel was partitioned from the slave's master,
* or tilted recently, wait some time before to act,
* so that DOWN and roles INFO will be refreshed. */
mstime_t wait_time = SENTINEL_INFO_PERIOD*2 +
ri->master->down_after_period*2;
if (!sentinelRedisInstanceNoDownFor(ri->master,wait_time) ||
(mstime()-sentinel.tilt_start_time) < wait_time)
return;
/* Old master returned back? Turn it into a slave ASAP if
* we can reach what we believe is the new master now, and
* have a recent role information for it.
*
* Note: we'll clear the DEMOTE flag only when we have the
* acknowledge that it's a slave again. */
if (ri->master->flags & SRI_MASTER &&
(ri->master->flags & (SRI_S_DOWN|SRI_O_DOWN)) == 0 &&
(mstime() - ri->master->info_refresh) < SENTINEL_INFO_PERIOD*2)
{
int retval;
retval = redisAsyncCommand(ri->cc,
sentinelDiscardReplyCallback, NULL, "SLAVEOF %s %d",
ri->master->addr->ip,
ri->master->addr->port);
if (retval == REDIS_OK)
sentinelEvent(REDIS_NOTICE,"+demote-old-slave",ri,"%@");
} else {
/* Otherwise if there are not the conditions to demote, we
* no longer trust the DEMOTE flag and remove it. */
ri->flags &= ~SRI_DEMOTE;
sentinelEvent(REDIS_NOTICE,"-demote-flag-cleared",ri,"%@");
}
} else if (!(ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
(runid_changed || first_runid))
{
/* If a slave turned into master but:
*
* 1) Failover not in progress.
* 2) RunID hs changed, or its the first time we see an INFO output.
* 2) RunID has changed or its the first time we see an INFO output.
*
* We assume this is a reboot with a wrong configuration.
* Log the event and remove the slave. */
* Log the event and remove the slave. Note that this is processed
* in tilt mode as well, otherwise we lose the information that the
* runid changed (reboot?) and when the tilt mode ends a fake
* failover will be detected. */
int retval;
sentinelEvent(REDIS_WARNING,"-slave-restart-as-master",ri,"%@ #removing it from the attached slaves");
retval = dictDelete(ri->master->slaves,ri->name);
redisAssert(retval == REDIS_OK);
return;
} else if (ri->flags & SRI_PROMOTED) {
} else if (!sentinel.tilt && ri->flags & SRI_PROMOTED) {
/* If this is a promoted slave we can change state to the
* failover state machine. */
if ((ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
@@ -1499,11 +1550,12 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,
"start",ri->master->addr,ri->addr);
}
} else if (!(ri->master->flags & SRI_FAILOVER_IN_PROGRESS) ||
((ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
(ri->master->flags & SRI_I_AM_THE_LEADER) &&
ri->master->failover_state ==
SENTINEL_FAILOVER_STATE_WAIT_START))
} else if (!sentinel.tilt && (
!(ri->master->flags & SRI_FAILOVER_IN_PROGRESS) ||
((ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
(ri->master->flags & SRI_I_AM_THE_LEADER) &&
ri->master->failover_state ==
SENTINEL_FAILOVER_STATE_WAIT_START)))
{
/* No failover in progress? Then it is the start of a failover
* and we are an observer.
@@ -1523,6 +1575,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
ri->master->failover_state_change_time = mstime();
ri->master->promoted_slave = ri;
ri->flags |= SRI_PROMOTED;
ri->flags &= ~SRI_DEMOTE;
sentinelCallClientReconfScript(ri->master,SENTINEL_OBSERVER,
"start", ri->master->addr,ri->addr);
/* We are an observer, so we can only assume that the leader
@@ -1534,6 +1587,10 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
}
}
/* None of the following conditions are processed when in tilt mode, so
* return asap. */
if (sentinel.tilt) return;
/* Detect if the slave that is in the process of being reconfigured
* changed state. */
if ((ri->flags & SRI_SLAVE) && role == SRI_SLAVE &&
@@ -1564,6 +1621,13 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
ri->failover_state_change_time = mstime();
}
}
/* Detect if the old master was demoted as slave and generate the
* +slave event. */
if (role == SRI_SLAVE && ri->flags & SRI_DEMOTE) {
sentinelEvent(REDIS_NOTICE,"+slave",ri,"%@");
ri->flags &= ~SRI_DEMOTE;
}
}
void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) {
@@ -1647,7 +1711,7 @@ void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privd
/* Update the last activity in the pubsub channel. Note that since we
* receive our messages as well this timestamp can be used to detect
* if the link is probably diconnected even if it seems otherwise. */
* if the link is probably disconnected even if it seems otherwise. */
ri->pc_last_activity = mstime();
/* Sanity check in the reply we expect, so that the code that follows
@@ -1835,6 +1899,7 @@ void addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) {
if (ri->flags & SRI_RECONF_SENT) flags = sdscat(flags,"reconf_sent,");
if (ri->flags & SRI_RECONF_INPROG) flags = sdscat(flags,"reconf_inprog,");
if (ri->flags & SRI_RECONF_DONE) flags = sdscat(flags,"reconf_done,");
if (ri->flags & SRI_DEMOTE) flags = sdscat(flags,"demote,");
if (sdslen(flags) != 0) flags = sdsrange(flags,0,-2); /* remove last "," */
addReplyBulkCString(c,flags);
@@ -1939,7 +2004,7 @@ void addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) {
setDeferredMultiBulkLength(c,mbl,fields*2);
}
/* Output a number of instances contanined inside a dictionary as
/* Output a number of instances contained inside a dictionary as
* Redis protocol. */
void addReplyDictOfRedisInstances(redisClient *c, dict *instances) {
dictIterator *di;
@@ -2035,8 +2100,16 @@ void sentinelCommand(redisClient *c) {
} else {
sentinelAddr *addr = ri->addr;
if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) && ri->promoted_slave)
/* If we are in the middle of a failover, and the slave was
* already successfully switched to master role, we can advertise
* the new address as slave in order to allow clients to talk
* with the new master ASAP. */
if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) &&
ri->promoted_slave &&
ri->failover_state >= SENTINEL_FAILOVER_STATE_RECONF_SLAVES)
{
addr = ri->promoted_slave->addr;
}
addReplyMultiBulkLen(c,2);
addReplyBulkCString(c,addr->ip);
addReplyBulkLongLong(c,addr->port);
@@ -2535,7 +2608,7 @@ void sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) {
* 3) info_refresh more recent than SENTINEL_INFO_VALIDITY_TIME.
* 4) master_link_down_time no more than:
* (now - master->s_down_since_time) + (master->down_after_period * 10).
* 5) Slave priority can't be zero, otherwise the slave is discareded.
* 5) Slave priority can't be zero, otherwise the slave is discarded.
*
* Among all the slaves matching the above conditions we select the slave
* with lower slave_priority. If priority is the same we select the slave
@@ -2584,6 +2657,7 @@ sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) {
mstime_t info_validity_time = mstime()-SENTINEL_INFO_VALIDITY_TIME;
if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN|SRI_DISCONNECTED)) continue;
if (slave->flags & SRI_DEMOTE) continue; /* Old master not yet ready. */
if (slave->last_avail_time < info_validity_time) continue;
if (slave->slave_priority == 0) continue;
@@ -2611,10 +2685,10 @@ void sentinelFailoverWaitStart(sentinelRedisInstance *ri) {
/* If we in "wait start" but the master is no longer in ODOWN nor in
* SDOWN condition we abort the failover. This is important as it
* prevents a useless failover in a a notable case of netsplit, where
* the senitnels are split from the redis instances. In this case
* the sentinels are split from the redis instances. In this case
* the failover will not start while there is the split because no
* good slave can be reached. However when the split is resolved, we
* can go to waitstart if the slave is back rechable a few milliseconds
* can go to waitstart if the slave is back reachable a few milliseconds
* before the master is. In that case when the master is back online
* we cancel the failover. */
if ((ri->flags & (SRI_S_DOWN|SRI_O_DOWN|SRI_FORCE_FAILOVER)) == 0) {
@@ -2829,12 +2903,28 @@ void sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) {
void sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {
sentinelRedisInstance *ref = master->promoted_slave ?
master->promoted_slave : master;
sds old_master_ip;
int old_master_port;
sentinelEvent(REDIS_WARNING,"+switch-master",master,"%s %s %d %s %d",
master->name, master->addr->ip, master->addr->port,
ref->addr->ip, ref->addr->port);
old_master_ip = sdsdup(master->addr->ip);
old_master_port = master->addr->port;
sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);
/* If this is a real switch, that is, we have master->promoted_slave not
* NULL, then we want to add the old master as a slave of the new master,
* but flagging it with SRI_DEMOTE so that we know we'll need to send
* SLAVEOF once the old master is reachable again. */
if (master != ref) {
/* Add the new slave, but don't generate a Sentinel event as it will
* happen later when finally the instance will claim to be a slave
* in the INFO output. */
createSentinelRedisInstance(NULL,SRI_SLAVE|SRI_DEMOTE,
old_master_ip, old_master_port, master->quorum, master);
}
sdsfree(old_master_ip);
}
void sentinelFailoverStateMachine(sentinelRedisInstance *ri) {
@@ -3026,13 +3116,13 @@ void sentinelHandleDictOfRedisInstances(dict *instances) {
* following conditions happen:
*
* 1) The Sentiel process for some time is blocked, for every kind of
* random reason: the load is huge, the computer was freezed for some time
* random reason: the load is huge, the computer was frozen for some time
* in I/O or alike, the process was stopped by a signal. Everything.
* 2) The system clock was altered significantly.
*
* Under both this conditions we'll see everything as timed out and failing
* without good reasons. Instead we enter the TILT mode and wait
* for SENTIENL_TILT_PERIOD to elapse before starting to act again.
* for SENTINEL_TILT_PERIOD to elapse before starting to act again.
*
* During TILT time we still collect information, we just do not act. */
void sentinelCheckTiltCondition(void) {
+18 -18
View File
@@ -57,13 +57,13 @@ A million repetitions of "a"
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
{
u_int32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
} CHAR64LONG16;
u_int32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
memcpy(block, buffer, 64);
#else
/* The following had better never be used because it causes the
@@ -71,7 +71,7 @@ CHAR64LONG16 block[1]; /* use array to appear as a pointer */
* And the result is written through. I threw a "const" in, hoping
* this will cause a diagnostic.
*/
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
@@ -132,12 +132,11 @@ void SHA1Init(SHA1_CTX* context)
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
{
u_int32_t i;
u_int32_t j;
u_int32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
@@ -157,9 +156,9 @@ u_int32_t j;
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
unsigned i;
unsigned char finalcount[8];
unsigned char c;
unsigned i;
unsigned char finalcount[8];
unsigned char c;
#if 0 /* untested "improvement" by DHR */
/* Convert context->count to a sequence of bytes
@@ -170,12 +169,12 @@ unsigned char c;
unsigned char *fcp = &finalcount[8];
for (i = 0; i < 2; i++)
{
u_int32_t t = context->count[i];
int j;
{
u_int32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
*--fcp = (unsigned char) t
for (j = 0; j < 4; t >>= 8, j++)
*--fcp = (unsigned char) t;
}
#else
for (i = 0; i < 8; i++) {
@@ -226,3 +225,4 @@ main(int argc, char **argv)
}
#endif
+3 -3
View File
@@ -45,7 +45,7 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
/* Return the value associated to the key with a name obtained using
* the following rules:
*
* 1) The first occurence of '*' in 'pattern' is substituted with 'subst'.
* 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.
*
* 2) If 'pattern' matches the "->" string, everything on the left of
* the arrow is treated as the name of an hash field, and the part on the
@@ -147,7 +147,7 @@ int sortCompare(const void *s1, const void *s2) {
cmp = -1;
} else {
/* Objects have the same score, but we don't want the comparison
* to be undefined, so we compare objects lexicographycally.
* to be undefined, so we compare objects lexicographically.
* This way the result of SORT is deterministic. */
cmp = compareStringObjects(so1->obj,so2->obj);
}
@@ -205,7 +205,7 @@ void sortCommand(redisClient *c) {
/* Now we need to protect sortval incrementing its count, in the future
* SORT may have options able to overwrite/delete keys during the sorting
* and the sorted key itself may get destroied */
* and the sorted key itself may get destroyed */
if (sortval)
incrRefCount(sortval);
else
+24 -24
View File
@@ -45,10 +45,10 @@ void listTypeTryConversion(robj *subject, robj *value) {
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
}
/* The function pushes an elmenet to the specified list object 'subject',
/* The function pushes an element to the specified list object 'subject',
* at head or tail position as specified by 'where'.
*
* There is no need for the caller to incremnet the refcount of 'value' as
* There is no need for the caller to increment the refcount of 'value' as
* the function takes care of it if needed. */
void listTypePush(robj *subject, robj *value, int where) {
/* Check if we need to convert the ziplist */
@@ -757,18 +757,14 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
list *l;
int j;
c->bpop.keys = zmalloc(sizeof(robj*)*numkeys);
c->bpop.count = numkeys;
c->bpop.timeout = timeout;
c->bpop.target = target;
if (target != NULL) {
incrRefCount(target);
}
if (target != NULL) incrRefCount(target);
for (j = 0; j < numkeys; j++) {
/* Add the key in the client structure, to map clients -> keys */
c->bpop.keys[j] = keys[j];
/* If the key already exists in the dict ignore it. */
if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue;
incrRefCount(keys[j]);
/* And in the other "side", to map keys -> clients */
@@ -786,6 +782,7 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
}
listAddNodeTail(l,c);
}
/* Mark the client as a blocked client */
c->flags |= REDIS_BLOCKED;
server.bpop_blocked_clients++;
@@ -794,28 +791,31 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
/* Unblock a client that's waiting in a blocking operation such as BLPOP */
void unblockClientWaitingData(redisClient *c) {
dictEntry *de;
dictIterator *di;
list *l;
int j;
redisAssertWithInfo(c,NULL,c->bpop.keys != NULL);
redisAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);
di = dictGetIterator(c->bpop.keys);
/* The client may wait for multiple keys, so unblock it for every key. */
for (j = 0; j < c->bpop.count; j++) {
while((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
/* Remove this client from the list of clients waiting for this key. */
de = dictFind(c->db->blocking_keys,c->bpop.keys[j]);
redisAssertWithInfo(c,c->bpop.keys[j],de != NULL);
l = dictGetVal(de);
l = dictFetchValue(c->db->blocking_keys,key);
redisAssertWithInfo(c,key,l != NULL);
listDelNode(l,listSearchKey(l,c));
/* If the list is empty we need to remove it to avoid wasting memory */
if (listLength(l) == 0)
dictDelete(c->db->blocking_keys,c->bpop.keys[j]);
decrRefCount(c->bpop.keys[j]);
dictDelete(c->db->blocking_keys,key);
}
dictReleaseIterator(di);
/* Cleanup the client structure */
zfree(c->bpop.keys);
c->bpop.keys = NULL;
if (c->bpop.target) decrRefCount(c->bpop.target);
c->bpop.target = NULL;
dictEmpty(c->bpop.keys);
if (c->bpop.target) {
decrRefCount(c->bpop.target);
c->bpop.target = NULL;
}
c->flags &= ~REDIS_BLOCKED;
c->flags |= REDIS_UNBLOCKED;
server.bpop_blocked_clients--;
@@ -825,7 +825,7 @@ void unblockClientWaitingData(redisClient *c) {
/* If the specified key has clients blocked waiting for list pushes, this
* function will put the key reference into the server.ready_keys list.
* Note that db->ready_keys is an hash table that allows us to avoid putting
* the same key agains and again in the list in case of multiple pushes
* the same key again and again in the list in case of multiple pushes
* made by a script or in the context of MULTI/EXEC.
*
* The list will be finally processed by handleClientsBlockedOnLists() */
@@ -858,7 +858,7 @@ void signalListAsReady(redisClient *c, robj *key) {
*
* 1) Provide the client with the 'value' element.
* 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the
* 'value' element on the destionation list (the LPUSH side of the command).
* 'value' element on the destination list (the LPUSH side of the command).
* 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into
* the AOF and replication channel.
*
@@ -868,7 +868,7 @@ void signalListAsReady(redisClient *c, robj *key) {
*
* The function returns REDIS_OK if we are able to serve the client, otherwise
* REDIS_ERR is returned to signal the caller that the list POP operation
* should be undoed as the client was not served: This only happens for
* should be undone as the client was not served: This only happens for
* BRPOPLPUSH that fails to push the value to the destination key as it is
* of the wrong type. */
int serveClientBlockedOnList(redisClient *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)
+4 -4
View File
@@ -188,7 +188,7 @@ robj *setTypeNextObject(setTypeIterator *si) {
* The caller provides both pointers to be populated with the right
* object. The return value of the function is the object->encoding
* field of the object and is used by the caller to check if the
* int64_t pointer or the redis object pointere was populated.
* int64_t pointer or the redis object pointer was populated.
*
* When an object is returned (the set was a real set) the ref count
* of the object is not incremented so this function can be considered
@@ -450,7 +450,7 @@ void srandmemberWithCountCommand(redisClient *c) {
* The number of requested elements is greater than the number of
* elements inside the set: simply return the whole set. */
if (count >= size) {
sunionDiffGenericCommand(c,c->argv,c->argc-1,NULL,REDIS_OP_UNION);
sunionDiffGenericCommand(c,c->argv+1,1,NULL,REDIS_OP_UNION);
return;
}
@@ -472,7 +472,7 @@ void srandmemberWithCountCommand(redisClient *c) {
/* Add all the elements into the temporary dictionary. */
si = setTypeInitIterator(set);
while((encoding = setTypeNext(si,&ele,&llele)) != -1) {
int retval;
int retval = DICT_ERR;
if (encoding == REDIS_ENCODING_INTSET) {
retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);
@@ -598,7 +598,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
sets[j] = setobj;
}
/* Sort sets from the smallest to largest, this will improve our
* algorithm's performace */
* algorithm's performance */
qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
/* The first thing we should output is the total number of elements...
+64 -10
View File
@@ -42,8 +42,28 @@ static int checkStringLength(redisClient *c, long long size) {
return REDIS_OK;
}
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) {
long long milliseconds = 0; /* initialized to avoid an harmness warning */
/* The setGenericCommand() function implements the SET operation with different
* options and variants. This function is called in order to implement the
* following commands: SET, SETEX, PSETEX, SETNX.
*
* 'flags' changes the behavior of the command (NX or XX, see belove).
*
* 'expire' represents an expire to set in form of a Redis object as passed
* by the user. It is interpreted according to the specified 'unit'.
*
* 'ok_reply' and 'abort_reply' is what the function will reply to the client
* if the operation is performed, or when it is not because of NX or
* XX flags.
*
* If ok_reply is NULL "+OK" is used.
* If abort_reply is NULL, "$-1" is used. */
#define REDIS_SET_NO_FLAGS 0
#define REDIS_SET_NX (1<<0) /* Set if key not exists. */
#define REDIS_SET_XX (1<<1) /* Set if key exists. */
void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
long long milliseconds = 0; /* initialized to avoid any harmness warning */
if (expire) {
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
@@ -55,34 +75,68 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
if (unit == UNIT_SECONDS) milliseconds *= 1000;
}
if (nx && lookupKeyWrite(c->db,key) != NULL) {
addReply(c,shared.czero);
if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
(flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL))
{
addReply(c, abort_reply ? abort_reply : shared.nullbulk);
return;
}
setKey(c->db,key,val);
server.dirty++;
if (expire) setExpire(c->db,key,mstime()+milliseconds);
addReply(c, nx ? shared.cone : shared.ok);
addReply(c, ok_reply ? ok_reply : shared.ok);
}
/* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */
void setCommand(redisClient *c) {
int j;
robj *expire = NULL;
int unit = UNIT_SECONDS;
int flags = REDIS_SET_NO_FLAGS;
for (j = 3; j < c->argc; j++) {
char *a = c->argv[j]->ptr;
robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
if ((a[0] == 'n' || a[0] == 'N') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {
flags |= REDIS_SET_NX;
} else if ((a[0] == 'x' || a[0] == 'X') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {
flags |= REDIS_SET_XX;
} else if ((a[0] == 'e' || a[0] == 'E') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {
unit = UNIT_SECONDS;
expire = next;
j++;
} else if ((a[0] == 'p' || a[0] == 'P') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {
unit = UNIT_MILLISECONDS;
expire = next;
j++;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
c->argv[2] = tryObjectEncoding(c->argv[2]);
setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0);
setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
}
void setnxCommand(redisClient *c) {
c->argv[2] = tryObjectEncoding(c->argv[2]);
setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0);
setGenericCommand(c,REDIS_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);
}
void setexCommand(redisClient *c) {
c->argv[3] = tryObjectEncoding(c->argv[3]);
setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS);
setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);
}
void psetexCommand(redisClient *c) {
c->argv[3] = tryObjectEncoding(c->argv[3]);
setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS);
setGenericCommand(c,REDIS_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);
}
int getGenericCommand(redisClient *c) {
@@ -340,7 +394,7 @@ void incrbyfloatCommand(redisClient *c) {
addReplyBulk(c,new);
/* Always replicate INCRBYFLOAT as a SET command with the final value
* in order to make sure that differences in float pricision or formatting
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart. */
aux = createStringObject("SET",3);
rewriteClientCommandArgument(c,0,aux);
+2 -2
View File
@@ -125,7 +125,7 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {
}
/* we assume the key is not already inside, since we allow duplicated
* scores, and the re-insertion of score and redis object should never
* happpen since the caller of zslInsert() should test in the hash table
* happen since the caller of zslInsert() should test in the hash table
* if the element is already inside or not. */
level = zslRandomLevel();
if (level > zsl->level) {
@@ -285,7 +285,7 @@ zskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec range) {
}
/* Delete all the elements with score between min and max from the skiplist.
* Min and mx are inclusive, so a score >= min || score <= max is deleted.
* Min and max are inclusive, so a score >= min || score <= max is deleted.
* Note that this function takes the reference to the hash table view of the
* sorted set, in order to remove the elements from the hash table too. */
unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec range, dict *dict) {
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.6.6"
#define REDIS_VERSION "2.6.13"
+2 -2
View File
@@ -739,10 +739,10 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
}
}
/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
/* 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
* to find out whether the string pointer or the integer value was set.
* Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
* 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) {
zlentry entry;
if (p == NULL || p[0] == ZIP_END) return 0;
+1 -1
View File
@@ -250,7 +250,7 @@ void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
*
* For this kind of "fast RSS reporting" usages use instead the
* function RedisEstimateRSS() that is a much faster (and less precise)
* version of the funciton. */
* version of the function. */
#if defined(HAVE_PROCFS)
#include <unistd.h>
+2 -323
View File
@@ -1,344 +1,23 @@
# Redis configuration file example
# Redis configuration for testing.
# Note on units: when memory size is needed, it is possible to specifiy
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize no
# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile /var/run/redis.pid
# Accept connections on the specified port, default is 6379.
port 6379
# If you want you can bind a single interface, if the bind option is not
# specified all the interfaces will listen for incoming connections.
#
# bind 127.0.0.1
# Specify the path for the unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 300
# Set server verbosity to 'debug'
# it can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
timeout 0
loglevel verbose
# Specify the log file name. Also 'stdout' can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile stdout
# To enable logging to the system logger, just set 'syslog-enabled' to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no
# Specify the syslog identity.
# syslog-ident redis
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16
################################ SNAPSHOTTING #################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# 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.
save 900 1
save 300 10
save 60 10000
# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
# The filename where to dump the DB
dbfilename dump.rdb
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# Also the Append Only File will be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
################################# REPLICATION #################################
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. Note that the configuration is local to the slave
# so for example it is possible to configure the slave to save the DB with a
# different interval, or to listen to another port, and so on.
#
# slaveof <masterip> <masterport>
# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
# masterauth <master-password>
# When a slave lost the connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
#
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
# still reply to client requests, possibly with out of data data, or the
# data set may just be empty if this is the first synchronization.
#
# 2) if slave-serve-stale data is set to 'no' the slave will reply with
# an error "SYNC with master in progress" to all the kind of commands
# but to INFO and SLAVEOF.
#
slave-serve-stale-data yes
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# 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.
#
# requirepass foobared
# Command renaming.
#
# It is possilbe to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# of hard to guess so that it will be still available for internal-use
# tools but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possilbe to completely kill a command renaming it into
# an empty string:
#
# rename-command CONFIG ""
################################### LIMITS ####################################
# Set the max number of connected clients at the same time. By default there
# is no limit, and it's up to the number of file descriptors the Redis process
# is able to open. The special value '0' means no limits.
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# maxclients 128
# Don't use more memory than the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys with an
# EXPIRE set. It will try to start freeing keys that are going to expire
# in little time and preserve keys with a longer time to live.
# Redis will also try to remove objects from free lists if possible.
#
# If all this fails, Redis will start to reply with errors to commands
# that will use more memory, like SET, LPUSH, and so on, and will continue
# to reply to most read-only commands like GET.
#
# WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
# 'state' server or cache, not as a real DB. When Redis is used as a real
# database the memory usage will grow over the weeks, it will be obvious if
# it is going to use too much memory in the long run, and you'll have the time
# to upgrade. With maxmemory after the limit is reached you'll start to get
# errors for write operations, and this may even lead to DB inconsistency.
#
# maxmemory <bytes>
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached? You can select among five behavior:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly 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 all the kind of 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
# 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
# getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy volatile-lru
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can select as well the sample
# size to check. For instance for default Redis will check three keys and
# pick the one that was used less recently, you can change the sample size
# using the following configuration directive.
#
# maxmemory-samples 3
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. If you can live
# with the idea that the latest records will be lost if something like a crash
# happens this is the preferred way to run Redis. If instead you care a lot
# about your data and don't want to that a single record can get lost you should
# enable the append only mode: when this mode is enabled Redis will append
# every write operation received in the file appendonly.aof. This file will
# be read on startup in order to rebuild the full dataset in memory.
#
# Note that you can have both the async dumps and the append only file if you
# like (you have to comment the "save" statements above to disable the dumps).
# Still if append only mode is enabled Redis will load the data from the
# log file at startup ignoring the dump.rdb file.
#
# IMPORTANT: Check the BGREWRITEAOF to check how to rewrite the append
# log file in background when it gets too big.
appendonly no
# The name of the append only file (default: "appendonly.aof")
# 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
# 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.
# everysec: fsync only if one second passed since the last fsync. Compromise.
#
# The default is "everysec" that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
# When the AOF fsync policy is set to always or everysec, and a background
# saving process (a background save or AOF log background rewriting) is
# performing a lot of I/O against the disk, in some Linux configurations
# Redis may block too long on the fsync() call. Note that there is no fix for
# this currently, as even performing fsync in a different thread will block
# our synchronous write(2) call.
#
# In order to mitigate this problem it's possible to use the following option
# that will prevent fsync() from being called in the main process while a
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving the durability of Redis is
# the same as "appendfsync none", that in pratical terms means that it is
# possible to lost 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.
no-appendfsync-on-rewrite no
############################### ADVANCED CONFIG ###############################
# Hashes are encoded in a special way (much more memory efficient) when they
# have at max a given numer of elements, and the biggest element does not
# exceed a given threshold. You can configure this limits with the following
# configuration directives.
hash-max-ziplist-entries 64
hash-max-ziplist-value 512
# Similarly to hashes, small lists are also encoded in a special way in order
# to save a lot of space. The special representation is only used when
# you are under the following limits:
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 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.
set-max-intset-entries 512
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
# order to help rehashing the main Redis hash table (the one mapping top-level
# keys to values). The hash table implementation redis uses (see dict.c)
# performs a lazy rehashing: the more operation you run into an hash table
# that is rhashing, 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.
#
# 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
# to queries with 2 milliseconds delay.
#
# use "activerehashing yes" if you don't have such hard requirements but
# want to free memory asap when possible.
activerehashing yes
################################## 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
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# include /path/to/local.conf
# include /path/to/other.conf
+73
View File
@@ -23,3 +23,76 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "encodings.rd
}
}
set server_path [tmpdir "server.rdb-startup-test"]
start_server [list overrides [list "dir" $server_path]] {
test {Server started empty with non-existing RDB file} {
r debug digest
} {0000000000000000000000000000000000000000}
# Save an RDB file, needed for the next test.
r save
}
start_server [list overrides [list "dir" $server_path]] {
test {Server started empty with empty RDB file} {
r debug digest
} {0000000000000000000000000000000000000000}
}
# Helper function to start a server and kill it, just to check the error
# logged.
set defaults {}
proc start_server_and_kill_it {overrides code} {
upvar defaults defaults srv srv server_path server_path
set config [concat $defaults $overrides]
set srv [start_server [list overrides $config]]
uplevel 1 $code
kill_server $srv
}
# Make the RDB file unreadable
file attributes [file join $server_path dump.rdb] -permissions 0222
# Detect root account (it is able to read the file even with 002 perm)
set isroot 0
catch {
open [file join $server_path dump.rdb]
set isroot 1
}
# Now make sure the server aborted with an error
if {!$isroot} {
start_server_and_kill_it [list "dir" $server_path] {
test {Server should not start if RDB file can't be open} {
wait_for_condition 50 100 {
[string match {*Fatal error loading*} \
[exec tail -n1 < [dict get $srv stdout]]]
} else {
fail "Server started even if RDB was unreadable!"
}
}
}
}
# Fix permissions of the RDB file.
file attributes [file join $server_path dump.rdb] -permissions 0666
# Corrupt its CRC64 checksum.
set filesize [file size [file join $server_path dump.rdb]]
set fd [open [file join $server_path dump.rdb] r+]
fconfigure $fd -translation binary
seek $fd -8 end
puts -nonewline $fd "foobar00"; # Corrupt the checksum
close $fd
# Now make sure the server aborted with an error
start_server_and_kill_it [list "dir" $server_path] {
test {Server should not start if RDB is corrupted} {
wait_for_condition 50 100 {
[string match {*RDB checksum*} \
[exec tail -n1 < [dict get $srv stdout]]]
} else {
fail "Server started even if RDB was corrupted!"
}
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
proc start_bg_complex_data {host port db ops} {
exec tclsh8.5 tests/helpers/bg_complex_data.tcl $host $port $db $ops &
set tclsh [info nameofexecutable]
exec $tclsh tests/helpers/bg_complex_data.tcl $host $port $db $ops &
}
proc stop_bg_complex_data {handle} {
+2 -1
View File
@@ -78,7 +78,8 @@ start_server {tags {"repl"}} {
}
proc start_write_load {host port seconds} {
exec tclsh8.5 tests/helpers/gen_write_load.tcl $host $port $seconds &
set tclsh [info nameofexecutable]
exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
}
proc stop_write_load {handle} {
+3 -1
View File
@@ -38,7 +38,9 @@ proc kill_server config {
if {[string match {*Darwin*} [exec uname -a]]} {
tags {"leaks"} {
test "Check for memory leaks (pid $pid)" {
exec leaks $pid
set output {0 leaks}
catch {exec leaks $pid} output
set output
} {*0 leaks*}
}
}
+63 -1
View File
@@ -2,6 +2,8 @@
# This softare is released under the BSD License. See the COPYING file for
# more information.
package require Tcl 8.5
set tcl_precision 17
source tests/support/redis.tcl
source tests/support/server.tcl
@@ -176,6 +178,7 @@ proc find_available_port start {
proc test_server_main {} {
cleanup
set tclsh [info nameofexecutable]
# Open a listening socket, trying different ports in order to find a
# non busy one.
set port [find_available_port 11111]
@@ -189,7 +192,7 @@ proc test_server_main {} {
set start_port [expr {$::port+100}]
for {set j 0} {$j < $::numclients} {incr j} {
set start_port [find_available_port $start_port]
set p [exec tclsh8.5 [info script] {*}$::argv \
set p [exec $tclsh [info script] {*}$::argv \
--client $port --port $start_port &]
lappend ::clients_pids $p
incr start_port 10
@@ -405,6 +408,65 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
}
}
proc attach_to_replication_stream {} {
set s [socket [srv 0 "host"] [srv 0 "port"]]
fconfigure $s -translation binary
puts -nonewline $s "SYNC\r\n"
flush $s
# Get the count
set count [gets $s]
set prefix [string range $count 0 0]
if {$prefix ne {$}} {
error "attach_to_replication_stream error. Received '$count' as count."
}
set count [string range $count 1 end]
# Consume the bulk payload
while {$count} {
set buf [read $s $count]
set count [expr {$count-[string length $buf]}]
}
return $s
}
proc read_from_replication_stream {s} {
fconfigure $s -blocking 0
set attempt 0
while {[gets $s count] == -1} {
if {[incr attempt] == 10} return ""
after 100
}
fconfigure $s -blocking 1
# Workaround for Redis 2.6, not always using the new protocol in the
# replication channel (this was fixed in >= 2.8).
if {[string tolower [lindex [split $count] 0]] eq {select}} {
return $count
}
# Return a list of arguments for the command.
set count [string range $count 1 end]
set res {}
for {set j 0} {$j < $count} {incr j} {
read $s 1
set arg [::redis::redis_bulk_read $s]
if {$j == 0} {set arg [string tolower $arg]}
lappend res $arg
}
return $res
}
proc assert_replication_stream {s patterns} {
for {set j 0} {$j < [llength $patterns]} {incr j} {
assert_match [lindex $patterns $j] [read_from_replication_stream $s]
}
}
proc close_replication_stream {s} {
close $s
}
# With the parallel test running multiple Redis instances at the same time
# we need a fast enough computer, otherwise a lot of tests may generate
# false positives.
+42
View File
@@ -712,4 +712,46 @@ start_server {tags {"basic"}} {
assert_equal [string range $bin $_start $_end] [r getrange bin $start $end]
}
}
test {Extended SET can detect syntax errors} {
set e {}
catch {r set foo bar non-existing-option} e
set e
} {*syntax*}
test {Extended SET NX option} {
r del foo
set v1 [r set foo 1 nx]
set v2 [r set foo 2 nx]
list $v1 $v2 [r get foo]
} {OK {} 1}
test {Extended SET XX option} {
r del foo
set v1 [r set foo 1 xx]
r set foo bar
set v2 [r set foo 2 xx]
list $v1 $v2 [r get foo]
} {{} OK 2}
test {Extended SET EX option} {
r del foo
r set foo bar ex 10
set ttl [r ttl foo]
assert {$ttl <= 10 && $ttl > 5}
}
test {Extended SET PX option} {
r del foo
r set foo bar px 10000
set ttl [r ttl foo]
assert {$ttl <= 10 && $ttl > 5}
}
test {Extended SET using multiple options at once} {
r set foo val
assert {[r set foo bar xx px 10000] eq {OK}}
set ttl [r ttl foo]
assert {$ttl <= 10 && $ttl > 5}
}
}
+28
View File
@@ -142,6 +142,34 @@ start_server {tags {"expire"}} {
list $size1 $size2
} {3 0}
test {Redis should lazy expire keys} {
r flushdb
r debug set-active-expire 0
r psetex key1 500 a
r psetex key2 500 a
r psetex key3 500 a
set size1 [r dbsize]
# Redis expires random keys ten times every second so we are
# fairly sure that all the three keys should be evicted after
# one second.
after 1000
set size2 [r dbsize]
r mget key1 key2 key3
set size3 [r dbsize]
r debug set-active-expire 1
list $size1 $size2 $size3
} {3 3 0}
test {EXPIRE should not resurrect keys (issue #1026)} {
r debug set-active-expire 0
r set foo bar
r pexpire foo 500
after 1000
r expire foo 10
r debug set-active-expire 1
r exists foo
} {0}
test {5 keys in, 5 keys out} {
r flushdb
r set a c
+37
View File
@@ -19,4 +19,41 @@ start_server {tags {"introspection"}} {
assert_match {*eval*} [$rd read]
assert_match {*lua*"set"*"foo"*"bar"*} [$rd read]
}
test {CLIENT GETNAME should return NIL if name is not assigned} {
r client getname
} {}
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
} {ERR*}
test {CLIENT SETNAME can assign a name to this connection} {
assert_equal [r client setname myname] {OK}
r client list
} {*name=myname*}
test {CLIENT SETNAME can change the name of an existing connection} {
assert_equal [r client setname someothername] {OK}
r client list
} {*name=someothername*}
test {After CLIENT SETNAME, connection can still be closed} {
set rd [redis_deferring_client]
$rd client setname foobar
assert_equal [$rd read] "OK"
assert_match {*foobar*} [r client list]
$rd close
# Now the client should no longer be listed
wait_for_condition 50 100 {
[string match {*foobar*} [r client list]] == 0
} else {
fail "Client still listed in CLIENT LIST after SETNAME."
}
}
}
+60 -3
View File
@@ -63,13 +63,13 @@ start_server {tags {"multi"}} {
r multi
r set foo1 bar1
$rd config set maxmemory 1
assert {[$rd read] eq {OK}}
catch {r lpush mylist myvalue}
$rd config set maxmemory 0
assert {[$rd read] eq {OK}}
r set foo2 bar2
catch {r exec} e
assert_match {EXECABORT*} $e
assert {[$rd read] eq {OK}}
assert {[$rd read] eq {OK}}
$rd close
list [r exists foo1] [r exists foo2]
} {0 0}
@@ -205,7 +205,10 @@ start_server {tags {"multi"}} {
r select 5
r multi
r ping
r exec
set res [r exec]
# Restore original DB
r select 9
set res
} {PONG}
test {WATCH will consider touched keys target of EXPIRE} {
@@ -249,4 +252,58 @@ start_server {tags {"multi"}} {
r incr x
r exec
} {11}
test {MULTI / EXEC is propagated correctly (single write command)} {
set repl [attach_to_replication_stream]
r multi
r set foo bar
r exec
assert_replication_stream $repl {
{select *}
{multi}
{set foo bar}
{exec}
}
close_replication_stream $repl
}
test {MULTI / EXEC is propagated correctly (empty transaction)} {
set repl [attach_to_replication_stream]
r multi
r exec
r set foo bar
assert_replication_stream $repl {
{select *}
{set foo bar}
}
close_replication_stream $repl
}
test {MULTI / EXEC is propagated correctly (read-only commands)} {
r set foo value1
set repl [attach_to_replication_stream]
r multi
r get foo
r exec
r set foo value2
assert_replication_stream $repl {
{select *}
{set foo value2}
}
close_replication_stream $repl
}
test {MULTI / EXEC is propagated correctly (write command, no effect)} {
r del bar foo bar
set repl [attach_to_replication_stream]
r multi
r del foo
r exec
assert_replication_stream $repl {
{select *}
{multi}
{exec}
}
close_replication_stream $repl
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ start_server {tags {"obuf-limits"}} {
if {![regexp {omem=([0-9]+)} $c - omem]} break
if {$omem > 200000} break
}
assert {$omem >= 99000 && $omem < 200000}
assert {$omem >= 90000 && $omem < 200000}
$rd1 close
}
+11 -1
View File
@@ -192,4 +192,14 @@ start_server {tags {"pubsub"}} {
# clean up clients
$rd1 close
}
}
test "PUNSUBSCRIBE and UNSUBSCRIBE should always reply." {
# Make sure we are not subscribed to any channel at all.
r punsubscribe
r unsubscribe
# Now check if the commands still reply correctly.
set reply1 [r punsubscribe]
set reply2 [r unsubscribe]
concat $reply1 $reply2
} {punsubscribe {} 0 unsubscribe {} 0}
}
+28 -8
View File
@@ -34,7 +34,7 @@ start_server {tags {"scripting"}} {
r eval {return {1,2,3,'ciao',{1,2}}} 0
} {1 2 3 ciao {1 2}}
test {EVAL - Are the KEYS and ARGS arrays populated correctly?} {
test {EVAL - Are the KEYS and ARGV arrays populated correctly?} {
r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d
} {a b c d}
@@ -297,6 +297,12 @@ start_server {tags {"scripting"}} {
assert_equal [r ping] "PONG"
}
test {Timedout script link is still usable after Lua returns} {
r config set lua-time-limit 10
r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0
r ping
} {PONG}
test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
set rd [redis_deferring_client]
r config set lua-time-limit 10
@@ -310,6 +316,8 @@ start_server {tags {"scripting"}} {
assert_match {BUSY*} $e
}
# Note: keep this test at the end of this server stanza because it
# kills the server.
test {SHUTDOWN NOSAVE can kill a timedout script anyway} {
# The server sould be still unresponding to normal commands.
catch {r ping} e
@@ -323,9 +331,16 @@ start_server {tags {"scripting"}} {
start_server {tags {"scripting repl"}} {
start_server {} {
test {Before the slave connects we issue an EVAL command} {
test {Before the slave connects we issue two EVAL commands} {
# One with an error, but still executing a command.
# SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56
catch {
r eval {redis.call('incr','x'); redis.call('nonexisting')} 0
}
# One command is correct:
# SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2
r eval {return redis.call('incr','x')} 0
} {1}
} {2}
test {Connect a slave to the main instance} {
r -1 slaveof [srv 0 host] [srv 0 port]
@@ -337,15 +352,20 @@ start_server {tags {"scripting repl"}} {
}
}
test {Now use EVALSHA against the master} {
test {Now use EVALSHA against the master, with both SHAs} {
# The server should replicate successful and unsuccessful
# commands as EVAL instead of EVALSHA.
catch {
r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0
}
r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0
} {2}
} {4}
test {If EVALSHA was replicated as EVAL the slave should be ok} {
test {If EVALSHA was replicated as EVAL, 'x' should be '4'} {
wait_for_condition 50 100 {
[r -1 get x] eq {2}
[r -1 get x] eq {4}
} else {
fail "Expected 2 in x, but value is '[r -1 get x]'"
fail "Expected 4 in x, but value is '[r -1 get x]'"
}
}
+12
View File
@@ -55,4 +55,16 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
set e [lindex [r slowlog get] 0]
lindex $e 3
} {sadd set foo {AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... (1 more bytes)}}
test {SLOWLOG - EXEC is not logged, just executed commands} {
r config set slowlog-log-slower-than 100000
r slowlog reset
assert_equal [r slowlog len] 0
r multi
r debug sleep 0.2
r exec
assert_equal [r slowlog len] 1
set e [lindex [r slowlog get] 0]
assert_equal [lindex $e 3] {debug sleep 0.2}
}
}
+21
View File
@@ -190,6 +190,27 @@ start_server {
$rd read
} {list b}
test "BLPOP with same key multiple times should work (issue #801)" {
set rd [redis_deferring_client]
r del list1 list2
# Data arriving after the BLPOP.
$rd blpop list1 list2 list2 list1 0
r lpush list1 a
assert_equal [$rd read] {list1 a}
$rd blpop list1 list2 list2 list1 0
r lpush list2 b
assert_equal [$rd read] {list2 b}
# Data already there.
r lpush list1 a
r lpush list2 b
$rd blpop list1 list2 list2 list1 0
assert_equal [$rd read] {list1 a}
$rd blpop list1 list2 list2 list1 0
assert_equal [$rd read] {list2 b}
}
test "MULTI/EXEC is isolated from the point of view of BLPOP" {
set rd [redis_deferring_client]
r del list
+2
View File
@@ -151,6 +151,8 @@ REDIS_CHKCONFIG_INFO=\
# Provides: redis_6379\n
# Required-Start: $network $local_fs $remote_fs\n
# Required-Stop: $network $local_fs $remote_fs\n
# Default-Start: 2 3 4 5\n
# Default-Stop: 0 1 6\n
# Should-Start: $syslog $named\n
# Should-Stop: $syslog $named\n
# Short-Description: start and stop redis_6379\n