Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7da5980848 | ||
|
|
062f60a36e | ||
|
|
4243483d3e | ||
|
|
d785413d86 | ||
|
|
e8a1a169dd | ||
|
|
dc0f13774b | ||
|
|
47911b8630 | ||
|
|
7ff729e192 | ||
|
|
02b9a72bae | ||
|
|
1862239136 | ||
|
|
f8791a158b | ||
|
|
26319d08b5 | ||
|
|
fc78d978de | ||
|
|
6cb7860658 | ||
|
|
611dcb56ee | ||
|
|
889b017403 | ||
|
|
d1369c3d9f | ||
|
|
6818905406 | ||
|
|
ef1cf15c14 | ||
|
|
dee8d84270 | ||
|
|
5576a28977 | ||
|
|
4f8b18f3dd | ||
|
|
c64255990e | ||
|
|
ba3c5494d8 | ||
|
|
684b61505a | ||
|
|
189e865c51 | ||
|
|
b9f8c2a5b0 | ||
|
|
18d16f8592 | ||
|
|
3a00520e5f | ||
|
|
f93d9929d8 | ||
|
|
6589821035 | ||
|
|
48f4f77189 | ||
|
|
b3281e93c3 | ||
|
|
2677c97d39 | ||
|
|
1d426bf53b | ||
|
|
da2dd8991b | ||
|
|
13f84841b5 | ||
|
|
ef3a95fa6f | ||
|
|
665b819eb4 | ||
|
|
bb562a6414 | ||
|
|
e166abad10 | ||
|
|
aec5ea5d07 | ||
|
|
2d24bf2f94 | ||
|
|
e7a61d287e | ||
|
|
78bae8b078 | ||
|
|
bf6c5d960d | ||
|
|
95fc9cc0a6 | ||
|
|
5de5efa364 | ||
|
|
1f2d5941a1 | ||
|
|
60aa3a67e8 | ||
|
|
921aec17b6 | ||
|
|
e174290307 | ||
|
|
a3273dbfa6 | ||
|
|
e9318d92db | ||
|
|
803c5b140d | ||
|
|
74373cd930 | ||
|
|
b5047d9b2f | ||
|
|
34b420dbf3 | ||
|
|
02ac84ddbd | ||
|
|
45bfab8596 | ||
|
|
948fdb5f7d | ||
|
|
fe97d7b1db | ||
|
|
e88517ac30 | ||
|
|
2d89c53d0d | ||
|
|
0fe052efaa | ||
|
|
e1e8b1cd29 | ||
|
|
609a3ec1be | ||
|
|
0b50d1ce9a | ||
|
|
c7b9a57fb2 | ||
|
|
63ad0e8867 | ||
|
|
d5d0b467db | ||
|
|
cf0191dc3c | ||
|
|
45b1b2f7ad | ||
|
|
eb07793b59 | ||
|
|
22ccc6db0f | ||
|
|
f9a8b0eced | ||
|
|
6da090e916 | ||
|
|
8c0b9f778e | ||
|
|
63dc34bd01 | ||
|
|
9ddd0f7796 | ||
|
|
c17a7f6fbc | ||
|
|
bfad381f57 | ||
|
|
742e580f40 | ||
|
|
1b0f244509 | ||
|
|
580f65a50b | ||
|
|
a154908a8f | ||
|
|
da540228d8 | ||
|
|
9e5b70162c | ||
|
|
dbde1d85ea | ||
|
|
560e049947 | ||
|
|
c2b43eac4f | ||
|
|
5e2aa2cafb | ||
|
|
a732d86fe0 | ||
|
|
0f4dbd9a11 | ||
|
|
21159d53f5 | ||
|
|
52f6a1dc16 | ||
|
|
f9c1263f31 | ||
|
|
1b2ac3b206 | ||
|
|
c5f23ca79b | ||
|
|
27abaa238f | ||
|
|
78b175a241 | ||
|
|
14cc0045dc | ||
|
|
023cbc3787 | ||
|
|
b70b9c6027 | ||
|
|
46dc3fbb53 | ||
|
|
c1c3e9b756 | ||
|
|
1f8ad7ae55 | ||
|
|
4468ba2317 | ||
|
|
d8a0fddd51 | ||
|
|
886c9ecb7c | ||
|
|
8e0a9f6027 | ||
|
|
d64a9cf514 | ||
|
|
d9301f05e2 | ||
|
|
6c9897f6cf | ||
|
|
984f6edf0a | ||
|
|
a2b3fff235 | ||
|
|
54b08c86a6 | ||
|
|
6bdcd50b56 | ||
|
|
cac49a9031 | ||
|
|
ed70955510 | ||
|
|
b41f4e5a48 | ||
|
|
21b26915ce | ||
|
|
3ccfb5a4d6 | ||
|
|
9120275dc9 | ||
|
|
de00a5a092 | ||
|
|
2b45ef020c | ||
|
|
52bd3d8a39 | ||
|
|
37d978d9b4 | ||
|
|
41f0f927c9 | ||
|
|
5ab4151d7f | ||
|
|
e95ca66320 | ||
|
|
681c191006 | ||
|
|
d85a09dfc6 | ||
|
|
26a48504cb | ||
|
|
ae3aeca828 | ||
|
|
5a8234d2c9 | ||
|
|
2861cd84f9 | ||
|
|
8e30092f06 | ||
|
|
d288ee655f | ||
|
|
89514f80e9 | ||
|
|
3f006679b1 | ||
|
|
36f026a3a0 | ||
|
|
cc017c5839 | ||
|
|
b6ffa85f11 | ||
|
|
4917a6a8b3 | ||
|
|
ed44a74e72 | ||
|
|
47ff443b53 | ||
|
|
4d9bd53530 | ||
|
|
5bf0997ff8 | ||
|
|
ec0f483de2 | ||
|
|
0dd6241314 | ||
|
|
3b1a41b172 | ||
|
|
9b3b110004 | ||
|
|
b25b0dc5f4 | ||
|
|
2b5fc529a4 | ||
|
|
be407c015c | ||
|
|
1eb9145089 |
+2
-7
@@ -1,13 +1,8 @@
|
||||
.*.swp
|
||||
*.o
|
||||
*.rdb
|
||||
*.log
|
||||
dump.rdb
|
||||
redis-benchmark
|
||||
redis-check-aof
|
||||
redis-check-dump
|
||||
redis-cli
|
||||
redis-sentinel
|
||||
redis-server
|
||||
redis-*
|
||||
doc-tools
|
||||
release
|
||||
misc/*
|
||||
|
||||
+342
-63
@@ -1,9 +1,9 @@
|
||||
Redis 2.8 release notes
|
||||
Redis 2.6 release notes
|
||||
=======================
|
||||
|
||||
** IMPORTANT ** Check the 'Migrating from 2.6 to 2.8' section at the end of
|
||||
this file for information about what changed between 2.6 and
|
||||
2.8 and how this may affect your application.
|
||||
** IMPORTANT ** Check the 'Migrating from 2.4 to 2.6' section at the end of
|
||||
this file for information about what changed between 2.4 and
|
||||
2.6 and how this may affect your application.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Upgrade urgency levels:
|
||||
@@ -14,84 +14,363 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
|
||||
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--[ Redis 2.8 Release Candidate 2 (2.7.102) ] Release date: 30 Jul 2013
|
||||
--[ Redis 2.6.12 ]
|
||||
|
||||
This is the second release candidate of Redis 2.8 (official version is 2.7.102).
|
||||
Important bugs fixed inside.
|
||||
UPGRADE URGENCY: MODERATE, nothing very critical but a few non trivial bugs.
|
||||
|
||||
# UPGRADE URGENCY: HIGH
|
||||
* [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.
|
||||
|
||||
* [FIX] Fixed a critical replication bug, see issue #1221.
|
||||
* [NEW] The new inline protocol now accepts quoted strings like, for example
|
||||
you can now type in a telnet session: set 'foo bar' "hello world\n".
|
||||
--[ Redis 2.6.11 ]
|
||||
|
||||
--[ Redis 2.8 Release Candidate 1 (2.7.101) ] Release date: 18 Jul 2013
|
||||
UPGRADE URGENCY: LOW, however updating is encouraged if you have many instances
|
||||
per server and you want to lower the CPU / energy usage.
|
||||
|
||||
This is the first release candidate of Redis 2.8 (official version is 2.7.101).
|
||||
* [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.
|
||||
|
||||
The following is a list of improvements in Redis 2.8, compared to Redis 2.6.
|
||||
--[ Redis 2.6.10 ]
|
||||
|
||||
* [NEW] Slaves are now able to partially resynchronize with the master, so most
|
||||
of the times a full resynchronization with the RDB creation in the master
|
||||
side is not needed when the master-slave link is disconnected for a short
|
||||
amount of time.
|
||||
* [NEW] Experimental IPv6 support.
|
||||
* [NEW] Slaves explicitly ping masters now, a master is able to detect a timed out
|
||||
slave independently.
|
||||
* [NEW] Masters can stop accepting writes if not enough slaves with a given
|
||||
maximum latency are connected.
|
||||
* [NEW] Keyspace changes notifications via Pub/Sub.
|
||||
* [NEW] CONFIG SET maxclients is now available.
|
||||
* [NEW] Ability to bind multiple IP addresses.
|
||||
* [NEW] Set process names so that you can recognize, in the "ps" command output,
|
||||
the listening port of an instance, or if it is a saving child.
|
||||
* [NEW] Automatic memory check on crash.
|
||||
* [NEW] CONFIG REWRITE is able to materialize the changes in the configuration
|
||||
operated using CONFIG SET into the redis.conf file.
|
||||
* [NEW] More NetBSD friendly code base.
|
||||
* [NEW] PUBSUB command for Pub/Sub introspection capabilities.
|
||||
* [NEW] EVALSHA can now be replicated as such, without requiring to be expanded
|
||||
to a full EVAL for the replication link.
|
||||
* [NEW] Better Lua scripts error reporting.
|
||||
* [NEW] SDIFF performance improved.
|
||||
* [FIX] A number of bugfixes.
|
||||
UPGRADE URGENCY: MODERATE, this release contains many non-critical fixes
|
||||
and many small improvements.
|
||||
|
||||
Migrating from 2.6 to 2.8
|
||||
* [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.
|
||||
MODERATE if Redis is running fine for you.
|
||||
|
||||
* [BUGFIX] Jemalloc updated to 3.2.0.
|
||||
|
||||
--[ Redis 2.6.5 ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE
|
||||
|
||||
Warning: this release of Redis introduces a different behavior in MULTI/EXEC
|
||||
handling of errors. This was done because the new behavior is safer
|
||||
compared to the old one, and should not break any code targeting
|
||||
Redis 2.6 in a critical way.
|
||||
|
||||
For more information check http://redis.io/topics/transactions
|
||||
|
||||
* [IMPROVED] RDB/AOF childern now log amount of additional memory used
|
||||
because of copy on write.
|
||||
* [BUGFIX] MIGRATE non critical fixes (see commits for details).
|
||||
* [BUGFIX] MULTI/EXEC: now EXEC aborts on errors before EXEC.
|
||||
* [BUGFIX] Fix integer overflow in zunionInterGenericCommand resulting
|
||||
into Z[INTER|UNION][STORE] commands to crash under extremely
|
||||
unlikely conditions (almost impossible in real world).
|
||||
* [BUGFIX] EVALSHA is now case insensitive (and will not crash).
|
||||
|
||||
--[ Redis 2.6.4 ]
|
||||
|
||||
UPGRADE URGENCY: LOW
|
||||
|
||||
* [IMPROVED] BSD license and copyright notice added to every .c and .h file.
|
||||
|
||||
--[ Redis 2.6.2 ]
|
||||
|
||||
UPGRADE URGENCY: LOW
|
||||
|
||||
* [BUGFIX] The compilation fix for RHLE5 in 2.6.1 was broken. Fixed.
|
||||
* [IMPROVED] Linenoise updated, now supports Ctrl+w.
|
||||
|
||||
---[ Redis 2.6.1 ]
|
||||
|
||||
UPGRADE URGENCY: LOW
|
||||
|
||||
* [BUGFIX] Compilation on Linux < 2.6.17 or glibc < 2.6 fixed (RHLE5 & co).
|
||||
|
||||
---[ Redis 2.6.0 ]
|
||||
|
||||
UPGRADE URGENCY: HIGH
|
||||
|
||||
* [BUGFIX] Allow AUTH when server is in -BUSY state because of a slow script.
|
||||
* [BUGFIX] MULTI/EXEC flow now makes sense when observed in MONITOR
|
||||
* [BUGFIX] SCRIPT KILL now uses different error prefixes for different errors.
|
||||
* [BUGFIX] Default memory limit for 32bit archs lowered from 3.5 to 3 GB.
|
||||
* [BUGFIX] redis-check-dump is now compatible with RDB files generated by 2.6.
|
||||
* [IMPROVED] New field in INFO: slave_read_only.
|
||||
|
||||
---[ Redis 2.5.14 (2.6 Release Candidate 8) ]
|
||||
|
||||
* [BUGFIX] Fixed compilation on FreeBSD.
|
||||
* [IMPROVED] SRANDMEMBER <count> that returns multiple random elements.
|
||||
* [IMPROVED] Sentinel backported to 2.6. It will be taken in sync with 2.8.
|
||||
* [IMPROVED] Helper function for scripting to return errors and status replies.
|
||||
* [IMPROVED] SORT by nosort [ASC|DESC] uses sorted set elements ordering.
|
||||
* [BUGFIX] Better resistence to system clock skew.
|
||||
* [IMPROVED] Warn the user when the configured maxmemory seems odd.
|
||||
* [BUGFIX] Hashing function is now murmurhash2 for security purposes.
|
||||
* [IMPROVED] Install script no longer uses a template but redis.conf itself.
|
||||
|
||||
---[ Redis 2.5.13 (2.6 Release Candidate 7) ]
|
||||
|
||||
UPGRADE URGENCY: HIGH
|
||||
|
||||
* [BUGFIX] Theoretical bug in ziplist fixed.
|
||||
* [BUGFIX] Better out of memory handling (Log produced in log file).
|
||||
* [BUGFIX] Incrementally flush RDB file on slave side while performing the
|
||||
first synchronization with the master. This makes Redis less
|
||||
blocking in environments where disk I/O is slow.
|
||||
* [BUGFIX] Don't crash with Lua's redis.call() without arguments.
|
||||
* [BUGFIX] Don't crash after a big number of Lua calls on 32 bit systems
|
||||
because of a failed assertion.
|
||||
* [BUGFIX] Fix SORT behaviour when called from scripting.
|
||||
* [BUGFIX] Adjust slave PING period accordingly to REDIS_HZ define.
|
||||
* [BUGFIX] BITCOUNT: fix crash on overflowing arguments.
|
||||
* [BUGFIX] Return an error when SELECT argument is not an integer.
|
||||
* [BUGFIX] Blocking operations on lists were completely reimplemented for
|
||||
correctness. Now blocking list ops and pushes originated from
|
||||
Lua scripts will play well together and will be replicated
|
||||
and transmitted to the AOF correctly.
|
||||
* [IMPROVED] Send async PING before starting replication to avoid blocking if
|
||||
master allows us to connect but it is actually not able to reply.
|
||||
* [IMPROVED] Support slave-priority for Redis Sentinel.
|
||||
* [IMPROVED] Hiredis library updated.
|
||||
|
||||
---[ Redis 2.5.12 (2.6 Release Candidate 6) ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE.
|
||||
|
||||
* [BUGFIX] Fixed a timing attack on AUTH (Issue #560).
|
||||
* [BUGFIX] Don't assume that "char" is signed.
|
||||
* [BUGFIX] Check that we have connection before enabling pipe mode.
|
||||
* [BUGFIX] Use the optimized version of the function to convert a double to
|
||||
its string representation. Compilation was disabled because of
|
||||
a typo in the #if statement.
|
||||
* [IMPROVED} REPLCONF internal command introduced, now INFO shows slaves with
|
||||
correct port numbers. This makes 2.5.12 Redis Sentinel compatible.
|
||||
* [IMPROVED] Truncate short write from the AOF for a cleaner restart. On short
|
||||
writes (for instance out of space) Redis will now try to remove
|
||||
the half-written data so that the next restart will work without
|
||||
the need for the "redis-check-aof" utility.
|
||||
* [IMPROVED] New in INFO: aof_last_bgrewrite_status
|
||||
* [IMPROVED] Allow Pub/Sub in contexts where other commands are blocked.
|
||||
* [BUGFIX] mark fd as writable when EPOLLERR or EPOLLHUP is returned by
|
||||
epoll_wait.
|
||||
|
||||
---[ Redis 2.5.11 (2.6 Release Candidate 5) ]
|
||||
|
||||
UPGRADE URGENCY: HIGH.
|
||||
|
||||
* [BUGFIX] Fixed Hash corruption when loading an RDB file generated by
|
||||
previous versions of Redis that encoded hashes using
|
||||
a different ziplist encoding format for small integers.
|
||||
All the fileds that are integers in the range 0-255 may not
|
||||
be recognized, or duplicated un updates, causing a crash
|
||||
when the ziplist is converted to a real hash. (Issue #547).
|
||||
* [BUGFIX] Fixed the count of memory used by output buffers in the
|
||||
setDeferredMultiBulkLength() function.
|
||||
|
||||
---[ Redis 2.5.10 (2.6 Release Candidate 4) ]
|
||||
|
||||
UPGRADE URGENCY: HIGH.
|
||||
|
||||
* [BUGFIX] Allow PREFIX to be overwritten on "make install".
|
||||
* [BUGFIX] Run the test with just one client if the computer is slow.
|
||||
* [BUGFIX] Event port support in our event driven libray.
|
||||
* [BUGFIX] Jemalloc updated to 3.0.0. This fixes a possibly AOF rewrite issue.
|
||||
See https://github.com/antirez/redis/issues/504 for info.
|
||||
* [BUGFIX] Fixed issue #516: ZINTERSTORE / ZUNIONSTORE with mixed sets/zsets.
|
||||
* [BUGFIX] Set fd to writable when poll(2) detects POLLERR or POLLHUP event.
|
||||
* [BUGFIX] Fixed RESTORE hash failure (Issue #532).
|
||||
* [IMPROVED] Allow an AOF rewrite buffer > 2GB (Related to issue #504).
|
||||
* [IMPROVED] Server cron function frequency is now configurable (REDIS_HZ).
|
||||
* [IMPROVED] Better, less blocking expired keys collection algorithm.
|
||||
* [FEATURE] New commands: BITOP and BITCOUNT.
|
||||
* [FEATURE] redis-cli --pipe for mass import.
|
||||
|
||||
What's new in Redis 2.5.9 (aka 2.6 Release Candidate 3)
|
||||
=======================================================
|
||||
|
||||
UPGRADE URGENCY: critical, upgrade ASAP.
|
||||
|
||||
* [BUGFIX] Fix for issue #500 (https://github.com/antirez/redis/pull/500).
|
||||
Redis 2.6-RC1 and RC2 may corrupt ziplist-encoded sorted sets
|
||||
produced by Redis 2.4.x.
|
||||
* [BUGFIX] Fixed several bugs in init.d script.
|
||||
* [BUGFIX] syncio.c functions modified for speed and correctness. On osx
|
||||
(and possibly other BSD-based systems) the slave would block on
|
||||
replication to send the SYNC command when the master was not
|
||||
available. This is fixed now, but was not affecting Linux installs.
|
||||
* Now when slave-serve-stale-data is set to yes and the master is down, instead
|
||||
of reporting a generic error Redis replies with -MASTERDOWN.
|
||||
|
||||
What's new in Redis 2.5.8 (aka 2.6 Release Candidate 2)
|
||||
=======================================================
|
||||
|
||||
UPGRADE URGENCY: high for all the users of the KEYS command, otherwise low.
|
||||
|
||||
* [BUGFIX] Fix for KEYS command: if the DB contains keys with expires the KEYS
|
||||
command may return the wrong output, having duplicated or missing
|
||||
keys. See issue #487 and #488 on github for details.
|
||||
|
||||
What's new in Redis 2.5.7 (aka 2.6 Release Candidate 1)
|
||||
=======================================================
|
||||
|
||||
UPGRADE URGENCY: upgrade not recommended because this is an RC release.
|
||||
|
||||
* This is the first release candidate for Redis 2.6. We are not aware of
|
||||
bugs, but part of this code is young and was never tested in production
|
||||
environments, so handle with care.
|
||||
|
||||
An overview of new features and changes in Redis 2.6.x
|
||||
======================================================
|
||||
|
||||
* Server side Lua scripting, see http://redis.io/commands/eval
|
||||
* Virtual Memory removed (was deprecated in 2.4)
|
||||
* Hardcoded limits about max number of clients removed.
|
||||
* AOF low level semantics is generally more sane, and especially when used
|
||||
in slaves.
|
||||
* Milliseconds resolution expires, also added new commands with milliseconds
|
||||
precision (PEXPIRE, PTTL, ...).
|
||||
* Better memory usage for "small" lists, ziplists and hashes when fields or
|
||||
values contain small integers.
|
||||
* Read only slaves.
|
||||
* New bit opeations: BITCOUNT and BITOP commands.
|
||||
* Clients max output buffer soft and hard limits. You can specifiy different
|
||||
limits for different classes of clients (normal,pubsub,slave).
|
||||
* More incremental (less blocking) expired keys collection algorithm, in
|
||||
practical terms this means that Redis is more responsive when a very
|
||||
big number of keys expire about at the same time.
|
||||
* AOF is now able to rewrite aggregate data types using variadic commands,
|
||||
often producing an AOF that is faster to save, load, and is smaller in size.
|
||||
* Every redis.conf directive is now accepted as a command line option for the
|
||||
redis-server binary, with the same name and number of arguments.
|
||||
* Hash table seed randomization for protection against collisions attacks.
|
||||
* Performances improved when writing large objects to Redis.
|
||||
* Integrated memory test, see redis-server --test-memory.
|
||||
* INCRBYFLOAT and HINCRBYFLOAT commands.
|
||||
* New DUMP, RESTORE, MIGRATE commands (back ported from Redis Cluster to 2.6).
|
||||
* CRC64 checksump in RDB files.
|
||||
* Better MONITOR output and behavior (now commands are logged before execution).
|
||||
* "Software Watchdog" feature to debug latency issues.
|
||||
* Significant parts of the core refactored or rewritten. New internal APIs
|
||||
and core changes allowed to develop Redis Cluster on top of the new code,
|
||||
however for 2.6 all the cluster code was removed, and will be released with
|
||||
Redis 3.0 when it is more complete and stable.
|
||||
* Redis ASCII art logo added at startup.
|
||||
* Crash report on memory violation or failed asserts improved significantly
|
||||
to make debugging of hard to catch bugs simpler.
|
||||
* redis-benchmark improvements: ability to run selected tests,
|
||||
CSV output, faster, better help.
|
||||
* redis-cli improvements: --eval for comfortable development of Lua scripts.
|
||||
* SHUTDOWN now supports two optional arguments: "SAVE" and "NOSAVE".
|
||||
* INFO output split into sections, the command is now able to just show
|
||||
pecific sections.
|
||||
* New statistics about how many time a command was called, and how much
|
||||
execution time it used (INFO commandstats).
|
||||
* More predictable SORT behavior in edge cases.
|
||||
* Better support for big endian and *BSD systems.
|
||||
* Build system improved.
|
||||
|
||||
Migrating from 2.4 to 2.6
|
||||
=========================
|
||||
|
||||
Redis 2.6 is mostly a strict subset of 2.8. However there are a few things
|
||||
Redis 2.4 is mostly a strict subset of 2.6. However there are a few things
|
||||
that you should be aware of:
|
||||
|
||||
The following commands changed behavior:
|
||||
* You can't use .rdb and AOF files generated with 2.6 into a 2.4 instance.
|
||||
* 2.6 slaves can be attached to 2.4 masters, but not the contrary, and only
|
||||
for the time needed to perform the version upgrade.
|
||||
|
||||
* SORT with ALPHA now sorts according to local collation locale if no STORE
|
||||
option is used.
|
||||
* ZADD/ZINCRBY are now able to accept a bigger range of values as valid
|
||||
scores, that is, all the values you may end having as a result of
|
||||
calling ZINCRBY multiple times.
|
||||
* Many errors are now prefixed by a more specific error code instead of
|
||||
the generic -ERR, for example -WRONGTYPE, -NOAUTH, ...
|
||||
* PUBLISH called inside Lua scripts is now correctly propagated to slaves.
|
||||
There are also a few API differences, that are unlikely to cause problems,
|
||||
but it is better to keep them in mind:
|
||||
|
||||
* SORT now will refuse to sort in numerical mode elements that can't be parsed
|
||||
as numbers.
|
||||
* EXPIREs now all have millisecond resolution (but this is very unlikely to
|
||||
break code that was not conceived exploting the previous resolution error
|
||||
in some way.)
|
||||
* INFO output is a bit different now, and contains empty lines and comments
|
||||
starting with '#'. All the major clients should be already fixed to work
|
||||
with the new INFO format.
|
||||
* Slaves are only read-only by default (but you can change this easily
|
||||
setting the "slave-read-only" configuration option to "no" editing your
|
||||
redis.conf or using CONFIG SET.
|
||||
|
||||
The following INFO fields were renamed for consistency:
|
||||
|
||||
changes_since_last_save -> rdb_changes_since_last_save
|
||||
bgsave_in_progress -> rdb_bgsave_in_progress
|
||||
last_save_time -> rdb_last_save_time
|
||||
last_bgsave_status -> rdb_last_bgsave_status
|
||||
bgrewriteaof_in_progress -> aof_rewrite_in_progress
|
||||
bgrewriteaof_scheduled -> aof_rewrite_scheduled
|
||||
|
||||
The following redis.conf and CONFIG GET / SET parameters changed:
|
||||
|
||||
* logfile now uses the empty string in order to log to standard output,
|
||||
so 'logfile stdout' is now invalid, use 'logfile ""' instead.
|
||||
|
||||
The following INFO fields changed format in a non-backward compatible way:
|
||||
|
||||
* The list of slaves in INFO is now in field=value format.
|
||||
|
||||
Replication:
|
||||
|
||||
Redis 2.8 can be used as slave for Redis 2.6, but doing this is only
|
||||
a good idea for the short amount of time needed to upgrade your servers.
|
||||
We suggest to update both master and slaves at about the same time.
|
||||
* hash-max-zipmap-entries, now replaced by hash-max-ziplist-entries
|
||||
* hash-max-zipmap-value, now replaced by hash-max-ziplist-value
|
||||
* glueoutputbuf option was now completely removed (was deprecated)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Credits: Where not specified the implementation and design is done by
|
||||
Salvatore Sanfilippo. Thanks to VMware and Pivotal for making all
|
||||
Credits: Where not specified the implementation and design are done by
|
||||
Salvatore Sanfilippo and Pieter Noordhuis. Thanks to VMware for making all
|
||||
this possible. Also many thanks to all the other contributors and the amazing
|
||||
community we have.
|
||||
|
||||
|
||||
@@ -26,20 +26,9 @@ After building Redis is a good idea to test it, using:
|
||||
|
||||
% make test
|
||||
|
||||
Fixing problems building 32 bit binaries
|
||||
---------
|
||||
|
||||
If after building Redis with a 32 bit target you need to rebuild it
|
||||
with a 64 bit target, or the other way around, you need to perform a
|
||||
"make distclean" in the root directory of the Redis distribution.
|
||||
|
||||
In case of build errors when trying to build a 32 bit binary of Redis, try
|
||||
the following steps:
|
||||
|
||||
* Install the packages libc6-dev-i386 (also try g++-multilib).
|
||||
* Try using the following command line instead of "make 32bit":
|
||||
|
||||
make CFLAGS="-m32 -march=native" LDFLAGS="-m32"
|
||||
NOTE: if after building Redis with a 32 bit target you need to rebuild it
|
||||
with a 64 bit target you need to perform a "make clean" in the root
|
||||
directory of the Redis distribution.
|
||||
|
||||
Allocator
|
||||
---------
|
||||
|
||||
Vendored
+2
-2
@@ -54,8 +54,8 @@ linenoise: .make-prerequisites
|
||||
.PHONY: linenoise
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
endif
|
||||
|
||||
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI $(CFLAGS)
|
||||
|
||||
Vendored
+2
-2
@@ -650,7 +650,7 @@ int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
||||
* doing unnecessary calls to memmove() in sds.c. */
|
||||
if (r->pos >= 1024) {
|
||||
sdsrange(r->buf,r->pos,-1);
|
||||
r->buf = sdsrange(r->buf,r->pos,-1);
|
||||
r->pos = 0;
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
@@ -1125,7 +1125,7 @@ int redisBufferWrite(redisContext *c, int *done) {
|
||||
sdsfree(c->obuf);
|
||||
c->obuf = sdsempty();
|
||||
} else {
|
||||
sdsrange(c->obuf,nwritten,-1);
|
||||
c->obuf = sdsrange(c->obuf,nwritten,-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-10
@@ -215,17 +215,9 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
|
||||
Vendored
+109
-386
@@ -1,6 +1,6 @@
|
||||
/* SDSLib, A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -32,76 +32,52 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include "sds.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
/* Create a new sds string with the content specified by the 'init' pointer
|
||||
* and 'initlen'.
|
||||
* If NULL is used for 'init' the string is initialized with zero bytes.
|
||||
*
|
||||
* The string is always null-termined (all the sds strings are, always) so
|
||||
* even if you create an sds string with:
|
||||
*
|
||||
* mystring = sdsnewlen("abc",3");
|
||||
*
|
||||
* You can print the string with printf() as there is an implicit \0 at the
|
||||
* end of the string. However the string is binary safe and can contain
|
||||
* \0 characters in the middle, as the length is stored in the sds header. */
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
static void sdsOomAbort(void) {
|
||||
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
if (init) {
|
||||
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
|
||||
} else {
|
||||
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
|
||||
}
|
||||
sh = malloc(sizeof(struct sdshdr)+initlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (sh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (sh == NULL) return NULL;
|
||||
#endif
|
||||
sh->len = initlen;
|
||||
sh->free = 0;
|
||||
if (initlen && init)
|
||||
memcpy(sh->buf, init, initlen);
|
||||
if (initlen) {
|
||||
if (init) memcpy(sh->buf, init, initlen);
|
||||
else memset(sh->buf,0,initlen);
|
||||
}
|
||||
sh->buf[initlen] = '\0';
|
||||
return (char*)sh->buf;
|
||||
}
|
||||
|
||||
/* Create an empty (zero length) sds string. Even in this case the string
|
||||
* always has an implicit null term. */
|
||||
sds sdsempty(void) {
|
||||
return sdsnewlen("",0);
|
||||
}
|
||||
|
||||
/* Create a new sds string starting from a null termined C string. */
|
||||
sds sdsnew(const char *init) {
|
||||
size_t initlen = (init == NULL) ? 0 : strlen(init);
|
||||
return sdsnewlen(init, initlen);
|
||||
}
|
||||
|
||||
/* Duplicate an sds string. */
|
||||
sds sdsdup(const sds s) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
|
||||
/* Free an sds string. No operation is performed if 's' is NULL. */
|
||||
void sdsfree(sds s) {
|
||||
if (s == NULL) return;
|
||||
zfree(s-sizeof(struct sdshdr));
|
||||
free(s-sizeof(struct sdshdr));
|
||||
}
|
||||
|
||||
/* Set the sds string length to the length as obtained with strlen(), so
|
||||
* considering as content only up to the first null term character.
|
||||
*
|
||||
* This function is useful when the sds string is hacked manually in some
|
||||
* way, like in the following example:
|
||||
*
|
||||
* s = sdsnew("foobar");
|
||||
* s[2] = '\0';
|
||||
* sdsupdatelen(s);
|
||||
* printf("%d\n", sdslen(s));
|
||||
*
|
||||
* The output will be "2", but if we comment out the call to sdsupdatelen()
|
||||
* the output will be "6" as the string was modified but the logical length
|
||||
* remains 6 bytes. */
|
||||
void sdsupdatelen(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
@@ -109,24 +85,7 @@ void sdsupdatelen(sds s) {
|
||||
sh->len = reallen;
|
||||
}
|
||||
|
||||
/* Modify an sds string on-place to make it empty (zero length).
|
||||
* However all the existing buffer is not discarded but set as free space
|
||||
* so that next append operations will not require allocations up to the
|
||||
* number of bytes previously available. */
|
||||
void sdsclear(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
sh->free += sh->len;
|
||||
sh->len = 0;
|
||||
sh->buf[0] = '\0';
|
||||
}
|
||||
|
||||
/* Enlarge the free space at the end of the sds string so that the caller
|
||||
* is sure that after calling this function can overwrite up to addlen
|
||||
* bytes after the end of the string, plus one more byte for nul term.
|
||||
*
|
||||
* Note: this does not change the *length* of the sds string as returned
|
||||
* by sdslen(), but only the free buffer space we have. */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
static sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
struct sdshdr *sh, *newsh;
|
||||
size_t free = sdsavail(s);
|
||||
size_t len, newlen;
|
||||
@@ -134,84 +93,20 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
if (free >= addlen) return s;
|
||||
len = sdslen(s);
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
newlen = (len+addlen);
|
||||
if (newlen < SDS_MAX_PREALLOC)
|
||||
newlen *= 2;
|
||||
else
|
||||
newlen += SDS_MAX_PREALLOC;
|
||||
newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
|
||||
newlen = (len+addlen)*2;
|
||||
newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (newsh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (newsh == NULL) return NULL;
|
||||
#endif
|
||||
|
||||
newsh->free = newlen - len;
|
||||
return newsh->buf;
|
||||
}
|
||||
|
||||
/* Reallocate the sds string so that it has no free space at the end. The
|
||||
* contained string remains not altered, but next concatenation operations
|
||||
* will require a reallocation.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdsRemoveFreeSpace(sds s) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
|
||||
sh->free = 0;
|
||||
return sh->buf;
|
||||
}
|
||||
|
||||
/* Return the total size of the allocation of the specifed sds string,
|
||||
* including:
|
||||
* 1) The sds header before the pointer.
|
||||
* 2) The string.
|
||||
* 3) The free buffer at the end if any.
|
||||
* 4) The implicit null term.
|
||||
*/
|
||||
size_t sdsAllocSize(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
|
||||
return sizeof(*sh)+sh->len+sh->free+1;
|
||||
}
|
||||
|
||||
/* Increment the sds length and decrements the left free space at the
|
||||
* end of the string according to 'incr'. Also set the null term
|
||||
* in the new end of the string.
|
||||
*
|
||||
* This function is used in order to fix the string length after the
|
||||
* user calls sdsMakeRoomFor(), writes something after the end of
|
||||
* the current string, and finally needs to set the new length.
|
||||
*
|
||||
* Note: it is possible to use a negative increment in order to
|
||||
* right-trim the string.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
|
||||
* following schema, to cat bytes coming from the kernel to the end of an
|
||||
* sds string without copying into an intermediate buffer:
|
||||
*
|
||||
* oldlen = sdslen(s);
|
||||
* s = sdsMakeRoomFor(s, BUFFER_SIZE);
|
||||
* nread = read(fd, s+oldlen, BUFFER_SIZE);
|
||||
* ... check for nread <= 0 and handle it ...
|
||||
* sdsIncrLen(s, nread);
|
||||
*/
|
||||
void sdsIncrLen(sds s, int incr) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
|
||||
assert(sh->free >= incr);
|
||||
sh->len += incr;
|
||||
sh->free -= incr;
|
||||
assert(sh->free >= 0);
|
||||
s[sh->len] = '\0';
|
||||
}
|
||||
|
||||
/* Grow the sds to have the specified length. Bytes that were not part of
|
||||
* the original length of the sds will be set to zero.
|
||||
*
|
||||
* if the specified length is smaller than the current length, no operation
|
||||
* is performed. */
|
||||
* the original length of the sds will be set to zero. */
|
||||
sds sdsgrowzero(sds s, size_t len) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
size_t totlen, curlen = sh->len;
|
||||
@@ -229,11 +124,6 @@ sds sdsgrowzero(sds s, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
|
||||
* end of the specified sds string 's'.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
struct sdshdr *sh;
|
||||
size_t curlen = sdslen(s);
|
||||
@@ -248,25 +138,11 @@ sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Append the specified null termianted C string to the sds string 's'.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscat(sds s, const char *t) {
|
||||
return sdscatlen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Append the specified sds 't' to the existing sds 's'.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatsds(sds s, const sds t) {
|
||||
return sdscatlen(s, t, sdslen(t));
|
||||
}
|
||||
|
||||
/* Destructively modify the sds string 's' to hold the specified binary
|
||||
* safe string pointed by 't' of length 'len' bytes. */
|
||||
sds sdscpylen(sds s, const char *t, size_t len) {
|
||||
sds sdscpylen(sds s, char *t, size_t len) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t totlen = sh->free+sh->len;
|
||||
|
||||
@@ -283,52 +159,37 @@ sds sdscpylen(sds s, const char *t, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Like sdscpylen() but 't' must be a null-termined string so that the length
|
||||
* of the string is obtained with strlen(). */
|
||||
sds sdscpy(sds s, const char *t) {
|
||||
sds sdscpy(sds s, char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Like sdscatpritf() but gets va_list instead of being variadic. */
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
va_list cpy;
|
||||
char *buf, *t;
|
||||
size_t buflen = 16;
|
||||
|
||||
while(1) {
|
||||
buf = zmalloc(buflen);
|
||||
buf = malloc(buflen);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (buf == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (buf == NULL) return NULL;
|
||||
#endif
|
||||
buf[buflen-2] = '\0';
|
||||
va_copy(cpy,ap);
|
||||
vsnprintf(buf, buflen, fmt, cpy);
|
||||
if (buf[buflen-2] != '\0') {
|
||||
zfree(buf);
|
||||
free(buf);
|
||||
buflen *= 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
t = sdscat(s, buf);
|
||||
zfree(buf);
|
||||
free(buf);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Append to the sds string 's' a string obtained using printf-alike format
|
||||
* specifier.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsempty("Sum is: ");
|
||||
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
|
||||
*
|
||||
* Often you need to create a string from scratch with the printf-alike
|
||||
* format. When this is the need, just use sdsempty() as the target string:
|
||||
*
|
||||
* s = sdscatprintf(sdsempty(), "... your format ...", args);
|
||||
*/
|
||||
sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char *t;
|
||||
@@ -338,20 +199,6 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Remove the part of the string from left and from right composed just of
|
||||
* contiguous characters found in 'cset', that is a null terminted C string.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
|
||||
* s = sdstrim(s,"A. :");
|
||||
* printf("%s\n", s);
|
||||
*
|
||||
* Output will be just "Hello World".
|
||||
*/
|
||||
sds sdstrim(sds s, const char *cset) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
char *start, *end, *sp, *ep;
|
||||
@@ -369,27 +216,11 @@ sds sdstrim(sds s, const char *cset) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Turn the string into a smaller (or equal) string containing only the
|
||||
* substring specified by the 'start' and 'end' indexes.
|
||||
*
|
||||
* start and end can be negative, where -1 means the last character of the
|
||||
* string, -2 the penultimate character, and so forth.
|
||||
*
|
||||
* The interval is inclusive, so the start and end characters will be part
|
||||
* of the resulting string.
|
||||
*
|
||||
* The string is modified in-place.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsnew("Hello World");
|
||||
* sdstrim(s,1,-1); => "ello Worl"
|
||||
*/
|
||||
void sdsrange(sds s, int start, int end) {
|
||||
sds sdsrange(sds s, int start, int end) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t newlen, len = sdslen(s);
|
||||
|
||||
if (len == 0) return;
|
||||
if (len == 0) return s;
|
||||
if (start < 0) {
|
||||
start = len+start;
|
||||
if (start < 0) start = 0;
|
||||
@@ -413,34 +244,22 @@ void sdsrange(sds s, int start, int end) {
|
||||
sh->buf[newlen] = 0;
|
||||
sh->free = sh->free+(sh->len-newlen);
|
||||
sh->len = newlen;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Apply tolower() to every character of the sds string 's'. */
|
||||
void sdstolower(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
||||
}
|
||||
|
||||
/* Apply toupper() to every character of the sds string 's'. */
|
||||
void sdstoupper(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
||||
}
|
||||
|
||||
/* Compare two sds strings s1 and s2 with memcmp().
|
||||
*
|
||||
* Return value:
|
||||
*
|
||||
* 1 if s1 > s2.
|
||||
* -1 if s1 < s2.
|
||||
* 0 if s1 and s2 are exactly the same binary string.
|
||||
*
|
||||
* If two strings share exactly the same prefix, but one of the two has
|
||||
* additional characters, the longer string is considered to be greater than
|
||||
* the smaller one. */
|
||||
int sdscmp(const sds s1, const sds s2) {
|
||||
int sdscmp(sds s1, sds s2) {
|
||||
size_t l1, l2, minlen;
|
||||
int cmp;
|
||||
|
||||
@@ -468,15 +287,14 @@ int sdscmp(const sds s1, const sds s2) {
|
||||
* requires length arguments. sdssplit() is just the
|
||||
* same function but for zero-terminated strings.
|
||||
*/
|
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5, start = 0, j;
|
||||
sds *tokens;
|
||||
|
||||
if (seplen < 1 || len < 0) return NULL;
|
||||
|
||||
tokens = zmalloc(sizeof(sds)*slots);
|
||||
if (tokens == NULL) return NULL;
|
||||
|
||||
sds *tokens = malloc(sizeof(sds)*slots);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (tokens == NULL) sdsOomAbort();
|
||||
#endif
|
||||
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
|
||||
if (len == 0) {
|
||||
*count = 0;
|
||||
return tokens;
|
||||
@@ -487,14 +305,26 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
|
||||
sds *newtokens;
|
||||
|
||||
slots *= 2;
|
||||
newtokens = zrealloc(tokens,sizeof(sds)*slots);
|
||||
if (newtokens == NULL) goto cleanup;
|
||||
newtokens = realloc(tokens,sizeof(sds)*slots);
|
||||
if (newtokens == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
tokens = newtokens;
|
||||
}
|
||||
/* search the separator */
|
||||
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
|
||||
tokens[elements] = sdsnewlen(s+start,j-start);
|
||||
if (tokens[elements] == NULL) goto cleanup;
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
start = j+seplen;
|
||||
j = j+seplen-1; /* skip the separator */
|
||||
@@ -502,33 +332,35 @@ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count
|
||||
}
|
||||
/* Add the final element. We are sure there is room in the tokens array. */
|
||||
tokens[elements] = sdsnewlen(s+start,len-start);
|
||||
if (tokens[elements] == NULL) goto cleanup;
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
*count = elements;
|
||||
return tokens;
|
||||
|
||||
#ifndef SDS_ABORT_ON_OOM
|
||||
cleanup:
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
|
||||
zfree(tokens);
|
||||
*count = 0;
|
||||
free(tokens);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
|
||||
void sdsfreesplitres(sds *tokens, int count) {
|
||||
if (!tokens) return;
|
||||
while(count--)
|
||||
sdsfree(tokens[count]);
|
||||
zfree(tokens);
|
||||
free(tokens);
|
||||
}
|
||||
|
||||
/* Create an sds string from a long long value. It is much faster than:
|
||||
*
|
||||
* sdscatprintf(sdsempty(),"%lld\n", value);
|
||||
*/
|
||||
sds sdsfromlonglong(long long value) {
|
||||
char buf[32], *p;
|
||||
unsigned long long v;
|
||||
@@ -544,14 +376,10 @@ sds sdsfromlonglong(long long value) {
|
||||
return sdsnewlen(p,32-(p-buf));
|
||||
}
|
||||
|
||||
/* Append to the sds string "s" an escaped string representation where
|
||||
* all the non-printable characters (tested with isprint()) are turned into
|
||||
* escapes in the form "\n\r\a...." or "\x<hex-number>".
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatrepr(sds s, const char *p, size_t len) {
|
||||
sds sdscatrepr(sds s, char *p, size_t len) {
|
||||
s = sdscatlen(s,"\"",1);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
while(len--) {
|
||||
switch(*p) {
|
||||
case '\\':
|
||||
@@ -571,64 +399,27 @@ sds sdscatrepr(sds s, const char *p, size_t len) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
if (s == NULL) return NULL;
|
||||
}
|
||||
return sdscatlen(s,"\"",1);
|
||||
}
|
||||
|
||||
/* Helper function for sdssplitargs() that returns non zero if 'c'
|
||||
* is a valid hex digit. */
|
||||
int is_hex_digit(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
/* Helper function for sdssplitargs() that converts an hex digit into an
|
||||
* integer from 0 to 15 */
|
||||
int hex_digit_to_int(char c) {
|
||||
switch(c) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Split a line into arguments, where every argument can be in the
|
||||
* following programming-language REPL-alike form:
|
||||
*
|
||||
* 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 free the resulting array of sds strings with
|
||||
* sdsfreesplitres().
|
||||
* of sds is returned. The caller should sdsfree() all the returned
|
||||
* strings and finally free() the array itself.
|
||||
*
|
||||
* 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;
|
||||
sds *sdssplitargs(char *line, int *argc) {
|
||||
char *p = line;
|
||||
char *current = NULL;
|
||||
char **vector = NULL;
|
||||
char **vector = NULL, **_vector = NULL;
|
||||
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
@@ -636,24 +427,17 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
while(*p && isspace(*p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
int insq=0; /* set to 1 if we are in 'single quotes' */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
int done=0;
|
||||
|
||||
if (current == NULL) current = sdsempty();
|
||||
if (current == NULL) {
|
||||
current = sdsempty();
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1) == 'x' &&
|
||||
is_hex_digit(*(p+2)) &&
|
||||
is_hex_digit(*(p+3)))
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
byte = (hex_digit_to_int(*(p+2))*16)+
|
||||
hex_digit_to_int(*(p+3));
|
||||
current = sdscatlen(current,(char*)&byte,1);
|
||||
p += 3;
|
||||
} else if (*p == '\\' && *(p+1)) {
|
||||
if (*p == '\\' && *(p+1)) {
|
||||
char c;
|
||||
|
||||
p++;
|
||||
@@ -667,23 +451,7 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
}
|
||||
current = sdscatlen(current,&c,1);
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
goto err;
|
||||
} else {
|
||||
current = sdscatlen(current,p,1);
|
||||
}
|
||||
} else if (insq) {
|
||||
if (*p == '\\' && *(p+1) == '\'') {
|
||||
p++;
|
||||
current = sdscatlen(current,"'",1);
|
||||
} else if (*p == '\'') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
/* closing quote must be followed by a space */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
@@ -704,24 +472,23 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
case '"':
|
||||
inq=1;
|
||||
break;
|
||||
case '\'':
|
||||
insq=1;
|
||||
break;
|
||||
default:
|
||||
current = sdscatlen(current,p,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*p) p++;
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
/* add the token to the vector */
|
||||
vector = zrealloc(vector,((*argc)+1)*sizeof(char*));
|
||||
_vector = realloc(vector,((*argc)+1)*sizeof(char*));
|
||||
if (_vector == NULL) goto err;
|
||||
|
||||
vector = _vector;
|
||||
vector[*argc] = current;
|
||||
(*argc)++;
|
||||
current = NULL;
|
||||
} else {
|
||||
/* Even on empty input string return something not NULL. */
|
||||
if (vector == NULL) vector = zmalloc(sizeof(void*));
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
@@ -729,55 +496,30 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
err:
|
||||
while((*argc)--)
|
||||
sdsfree(vector[*argc]);
|
||||
zfree(vector);
|
||||
if (current) sdsfree(current);
|
||||
*argc = 0;
|
||||
if (vector != NULL) free(vector);
|
||||
if (current != NULL) sdsfree(current);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Modify the string substituting all the occurrences of the set of
|
||||
* characters specified in the 'from' string to the corresponding character
|
||||
* in the 'to' array.
|
||||
*
|
||||
* For instance: sdsmapchars(mystring, "ho", "01", 2)
|
||||
* will have the effect of turning the string "hello" into "0ell1".
|
||||
*
|
||||
* The function returns the sds string pointer, that is always the same
|
||||
* as the input pointer since no resize is needed. */
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
|
||||
size_t j, i, l = sdslen(s);
|
||||
|
||||
for (j = 0; j < l; j++) {
|
||||
for (i = 0; i < setlen; i++) {
|
||||
if (s[j] == from[i]) {
|
||||
s[j] = to[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Join an array of C strings using the specified separator (also a C string).
|
||||
* Returns the result as an sds string. */
|
||||
sds sdsjoin(char **argv, int argc, char *sep) {
|
||||
sds join = sdsempty();
|
||||
int j;
|
||||
|
||||
for (j = 0; j < argc; j++) {
|
||||
join = sdscat(join, argv[j]);
|
||||
if (j != argc-1) join = sdscat(join,sep);
|
||||
}
|
||||
return join;
|
||||
}
|
||||
|
||||
#ifdef SDS_TEST_MAIN
|
||||
#include <stdio.h>
|
||||
#include "testhelp.h"
|
||||
|
||||
int __failed_tests = 0;
|
||||
int __test_num = 0;
|
||||
#define test_cond(descr,_c) do { \
|
||||
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
||||
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
||||
} while(0);
|
||||
#define test_report() do { \
|
||||
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
||||
__test_num-__failed_tests, __failed_tests); \
|
||||
if (__failed_tests) { \
|
||||
printf("=== WARNING === We have failed tests here...\n"); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
int main(void) {
|
||||
{
|
||||
struct sdshdr *sh;
|
||||
sds x = sdsnew("foo"), y;
|
||||
|
||||
test_cond("Create a string and obtain the length",
|
||||
@@ -857,26 +599,7 @@ int main(void) {
|
||||
x = sdsnew("aar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
|
||||
|
||||
{
|
||||
int oldfree;
|
||||
|
||||
sdsfree(x);
|
||||
x = sdsnew("0");
|
||||
sh = (void*) (x-(sizeof(struct sdshdr)));
|
||||
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
|
||||
x = sdsMakeRoomFor(x,1);
|
||||
sh = (void*) (x-(sizeof(struct sdshdr)));
|
||||
test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
|
||||
oldfree = sh->free;
|
||||
x[1] = '1';
|
||||
sdsIncrLen(x,1);
|
||||
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
|
||||
test_cond("sdsIncrLen() -- len", sh->len == 2);
|
||||
test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
|
||||
}
|
||||
}
|
||||
test_report()
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+9
-21
@@ -31,8 +31,6 @@
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -56,17 +54,16 @@ static inline size_t sdsavail(const sds s) {
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty();
|
||||
sds sdsempty(void);
|
||||
size_t sdslen(const sds s);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
size_t sdsavail(const sds s);
|
||||
size_t sdsavail(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscatsds(sds s, const sds t);
|
||||
sds sdscpylen(sds s, const char *t, size_t len);
|
||||
sds sdscpy(sds s, const char *t);
|
||||
sds sdscpylen(sds s, char *t, size_t len);
|
||||
sds sdscpy(sds s, char *t);
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
@@ -77,24 +74,15 @@ sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdsrange(sds s, int start, int end);
|
||||
sds sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
||||
int sdscmp(sds s1, sds s2);
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
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);
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
||||
sds sdsjoin(char **argv, int argc, char *sep);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
void sdsIncrLen(sds s, int incr);
|
||||
sds sdsRemoveFreeSpace(sds s);
|
||||
size_t sdsAllocSize(sds s);
|
||||
sds sdscatrepr(sds s, char *p, size_t len);
|
||||
sds *sdssplitargs(char *line, int *argc);
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
/* Drop in replacement for zmalloc.h in order to just use libc malloc without
|
||||
* any wrappering. */
|
||||
|
||||
#ifndef ZMALLOC_H
|
||||
#define ZMALLOC_H
|
||||
|
||||
#define zmalloc malloc
|
||||
#define zrealloc realloc
|
||||
#define zcalloc(x) calloc(x,1)
|
||||
#define zfree free
|
||||
#define zstrdup strdup
|
||||
|
||||
#endif
|
||||
@@ -2,19 +2,24 @@
|
||||
/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
|
||||
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ For details and rationale, see http://www.lua.org/license.html .
|
||||
|
||||
===============================================================================
|
||||
|
||||
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
|
||||
Copyright (C) 1994-2008 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
|
||||
|
||||
Vendored
+1
-1
@@ -48,7 +48,7 @@ TO_MAN= lua.1 luac.1
|
||||
|
||||
# Lua version and release.
|
||||
V= 5.1
|
||||
R= 5.1.5
|
||||
R= 5.1.4
|
||||
|
||||
all: $(PLAT)
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 797 B |
Vendored
+82
-80
@@ -3,10 +3,11 @@
|
||||
<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=utf-8">
|
||||
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
|
||||
<STYLE TYPE="text/css">
|
||||
ul {
|
||||
list-style-type: none ;
|
||||
list-style-position: outside ;
|
||||
}
|
||||
</STYLE>
|
||||
</HEAD>
|
||||
@@ -19,13 +20,7 @@ ul {
|
||||
Lua 5.1 Reference Manual
|
||||
</H1>
|
||||
|
||||
<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:
|
||||
This is an online version of
|
||||
<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>
|
||||
@@ -34,119 +29,128 @@ This manual is also available as a book:
|
||||
<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>
|
||||
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">Buy a copy</A>
|
||||
of this book and
|
||||
|
||||
Buy a copy 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>
|
||||
·
|
||||
<A HREF="#contents">contents</A>
|
||||
·
|
||||
<A HREF="#index">index</A>
|
||||
·
|
||||
<A HREF="http://www.lua.org/manual/">other versions</A>
|
||||
<A HREF="http://www.lua.org/manual/5.1/pt/">português</A>
|
||||
·
|
||||
<A HREF="http://www.lua.org/manual/5.1/es/">español</A>
|
||||
<HR>
|
||||
<SMALL>
|
||||
Copyright © 2006–2012 Lua.org, PUC-Rio.
|
||||
Copyright © 2006-2008 Lua.org, PUC-Rio.
|
||||
Freely available under the terms of the
|
||||
<A HREF="http://www.lua.org/license.html">Lua license</A>.
|
||||
<a href="http://www.lua.org/license.html#5">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 - Introduction</A>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#2">2 – The Language</A>
|
||||
<LI><A HREF="manual.html#2">2 - 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 - Lexical Conventions</A>
|
||||
<LI><A HREF="manual.html#2.2">2.2 - 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 - 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 - Variables</A>
|
||||
<LI><A HREF="manual.html#2.4">2.4 - 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 - 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>
|
||||
</UL>
|
||||
<LI><A HREF="manual.html#2.5">2.5 – Expressions</A>
|
||||
<LI><A HREF="manual.html#2.5">2.5 - 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 - 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>
|
||||
</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 - 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>
|
||||
<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 - Garbage-Collection Metamethods</A>
|
||||
<LI><A HREF="manual.html#2.10.2">2.10.2 - Weak Tables</A>
|
||||
</UL>
|
||||
<LI><A HREF="manual.html#2.11">2.11 – Coroutines</A>
|
||||
<LI><A HREF="manual.html#2.11">2.11 - Coroutines</A>
|
||||
</UL>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#3">3 – The Application Program Interface</A>
|
||||
<LI><A HREF="manual.html#3">3 - 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 - 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>
|
||||
</UL>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#4">4 – The Auxiliary Library</A>
|
||||
<LI><A HREF="manual.html#4">4 - 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 - Functions and Types</A>
|
||||
</UL>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#5">5 – Standard Libraries</A>
|
||||
<LI><A HREF="manual.html#5">5 - 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 - 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>
|
||||
<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 - 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 - 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>
|
||||
</UL>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#6">6 – Lua Stand-alone</A>
|
||||
<LI><A HREF="manual.html#6">6 - 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 - 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 - 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>
|
||||
</UL>
|
||||
<P>
|
||||
<LI><A HREF="manual.html#8">8 – The Complete Syntax of Lua</A>
|
||||
<LI><A HREF="manual.html#8">8 - The Complete Syntax of Lua</A>
|
||||
</UL>
|
||||
|
||||
<H2><A NAME="index">Index</A></H2>
|
||||
@@ -156,8 +160,6 @@ 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>
|
||||
@@ -485,12 +487,12 @@ Freely available under the terms of the
|
||||
<P>
|
||||
|
||||
<HR>
|
||||
<SMALL CLASS="footer">
|
||||
<SMALL>
|
||||
Last update:
|
||||
Mon Feb 13 18:53:32 BRST 2012
|
||||
Sat Jan 19 13:24:29 BRST 2008
|
||||
</SMALL>
|
||||
<!--
|
||||
Last change: revised for Lua 5.1.5
|
||||
Last change: revised for Lua 5.1.3
|
||||
-->
|
||||
|
||||
</BODY>
|
||||
|
||||
Vendored
+3
-45
@@ -1,37 +1,17 @@
|
||||
body {
|
||||
color: #000000 ;
|
||||
background-color: #FFFFFF ;
|
||||
font-family: Helvetica, Arial, sans-serif ;
|
||||
font-family: sans-serif ;
|
||||
text-align: justify ;
|
||||
margin-right: 30px ;
|
||||
margin-left: 30px ;
|
||||
margin-right: 20px ;
|
||||
margin-left: 20px ;
|
||||
}
|
||||
|
||||
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 ;
|
||||
@@ -59,25 +39,3 @@ 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 ;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-13
@@ -1,24 +1,13 @@
|
||||
h3 code {
|
||||
font-family: inherit ;
|
||||
font-size: inherit ;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-size: 12pt ;
|
||||
pre {
|
||||
font-size: 105% ;
|
||||
}
|
||||
|
||||
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 ;
|
||||
}
|
||||
|
||||
Vendored
+14
-17
@@ -19,9 +19,9 @@ Lua 5.1 Reference Manual
|
||||
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
|
||||
<p>
|
||||
<small>
|
||||
Copyright © 2006–2012 Lua.org, PUC-Rio.
|
||||
Copyright © 2006-2008 Lua.org, PUC-Rio.
|
||||
Freely available under the terms of the
|
||||
<a href="http://www.lua.org/license.html">Lua license</a>.
|
||||
<a href="http://www.lua.org/license.html#5">Lua license</a>.
|
||||
</small>
|
||||
<hr>
|
||||
<p>
|
||||
@@ -29,13 +29,11 @@ Freely available under the terms of the
|
||||
<a href="contents.html#contents">contents</A>
|
||||
·
|
||||
<a href="contents.html#index">index</A>
|
||||
·
|
||||
<A HREF="http://www.lua.org/manual/">other versions</A>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<p>
|
||||
|
||||
<!-- $Id: manual.of,v 1.49.1.2 2012/01/13 20:23:26 roberto Exp $ -->
|
||||
<!-- $Id: manual.of,v 1.48 2008/08/18 15:24:20 roberto Exp $ -->
|
||||
|
||||
|
||||
|
||||
@@ -5647,7 +5645,7 @@ Currently, Lua has the following standard libraries:
|
||||
|
||||
<ul>
|
||||
|
||||
<li>basic library, which includes the coroutine sub-library;</li>
|
||||
<li>basic library,</li> which includes the coroutine sub-library;
|
||||
|
||||
<li>package library;</li>
|
||||
|
||||
@@ -5711,7 +5709,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>
|
||||
@@ -5720,11 +5718,6 @@ 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>
|
||||
@@ -5733,6 +5726,10 @@ 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>
|
||||
@@ -5763,7 +5760,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>).
|
||||
@@ -8424,7 +8421,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>
|
||||
@@ -8792,12 +8789,12 @@ Here is the complete syntax of Lua in extended BNF.
|
||||
|
||||
|
||||
<HR>
|
||||
<SMALL CLASS="footer">
|
||||
<SMALL>
|
||||
Last update:
|
||||
Mon Feb 13 18:54:19 BRST 2012
|
||||
Mon Aug 18 13:25:46 BRT 2008
|
||||
</SMALL>
|
||||
<!--
|
||||
Last change: revised for Lua 5.1.5
|
||||
Last change: revised for Lua 5.1.4
|
||||
-->
|
||||
|
||||
</body></html>
|
||||
|
||||
Vendored
+2
-2
@@ -12,7 +12,7 @@
|
||||
Documentation
|
||||
</H1>
|
||||
|
||||
This is the documentation included in the source distribution of Lua 5.1.5.
|
||||
This is the documentation included in the source distribution of Lua 5.1.4.
|
||||
|
||||
<UL>
|
||||
<LI><A HREF="contents.html">Reference manual</A>
|
||||
@@ -33,7 +33,7 @@ especially the
|
||||
<HR>
|
||||
<SMALL>
|
||||
Last update:
|
||||
Fri Feb 3 09:44:42 BRST 2012
|
||||
Tue Aug 12 14:46:07 BRT 2008
|
||||
</SMALL>
|
||||
|
||||
</BODY>
|
||||
|
||||
Vendored
+1
-1
@@ -5,7 +5,7 @@
|
||||
# grep '^V=' ../Makefile
|
||||
V= 5.1
|
||||
# grep '^R=' ../Makefile
|
||||
R= 5.1.5
|
||||
R= 5.1.4
|
||||
|
||||
# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
|
||||
prefix= /usr/local
|
||||
|
||||
Vendored
+2
-2
@@ -7,7 +7,7 @@
|
||||
# Your platform. See PLATS for possible values.
|
||||
PLAT= none
|
||||
|
||||
CC?= gcc
|
||||
CC= gcc
|
||||
CFLAGS= -O2 -Wall $(MYCFLAGS)
|
||||
AR= ar rcu
|
||||
RANLIB= ranlib
|
||||
@@ -48,7 +48,7 @@ o: $(ALL_O)
|
||||
a: $(ALL_A)
|
||||
|
||||
$(LUA_A): $(CORE_O) $(LIB_O)
|
||||
$(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files
|
||||
$(AR) $@ $?
|
||||
$(RANLIB) $@
|
||||
|
||||
$(LUA_T): $(LUA_O) $(LUA_A)
|
||||
|
||||
Vendored
+1
-1
@@ -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 auxiliary functions as upvalues */
|
||||
/* `ipairs' and `pairs' need auxliliary functions as upvalues */
|
||||
auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
|
||||
auxopen(L, "pairs", luaB_pairs, luaB_next);
|
||||
/* `newproxy' needs a weaktable as upvalue */
|
||||
|
||||
Vendored
+9
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $
|
||||
** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
|
||||
** Code generator for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -544,6 +544,10 @@ 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;
|
||||
@@ -568,6 +572,10 @@ 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;
|
||||
|
||||
Vendored
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
|
||||
** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
|
||||
** Interface from Lua to its debug API
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -45,7 +45,6 @@ static int db_setmetatable (lua_State *L) {
|
||||
|
||||
|
||||
static int db_getfenv (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
lua_getfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $
|
||||
** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
|
||||
** Stack and Call structure of Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -217,7 +217,6 @@ 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);
|
||||
|
||||
Vendored
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $
|
||||
** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
|
||||
** Garbage Collector
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -627,6 +627,7 @@ void luaC_step (lua_State *L) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
lua_assert(g->totalbytes >= g->estimate);
|
||||
setthreshold(g);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $
|
||||
** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
|
||||
** Standard I/O (and system) library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -276,10 +276,7 @@ static int read_number (lua_State *L, FILE *f) {
|
||||
lua_pushnumber(L, d);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L); /* "result" to be removed */
|
||||
return 0; /* read fails */
|
||||
}
|
||||
else return 0; /* read fails */
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $
|
||||
** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -118,10 +118,8 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: loadlib.c,v 1.52.1.4 2009/09/09 13:17:16 roberto Exp $
|
||||
** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 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, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);
|
||||
lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
|
||||
/* fill it with pre-defined loaders */
|
||||
for (i=0; loaders[i] != NULL; i++) {
|
||||
lua_pushcfunction(L, loaders[i]);
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $
|
||||
** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 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 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $
|
||||
** $Id: lstrlib.c,v 1.132.1.4 2008/07/11 17:27:21 roberto Exp $
|
||||
** Standard library for string operations and pattern-matching
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -754,7 +754,6 @@ 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);
|
||||
@@ -769,8 +768,7 @@ 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 */
|
||||
if (++arg > top)
|
||||
luaL_argerror(L, arg, "no value");
|
||||
arg++;
|
||||
strfrmt = scanformat(L, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c': {
|
||||
|
||||
Vendored
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $
|
||||
** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 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.5"
|
||||
#define LUA_RELEASE "Lua 5.1.4"
|
||||
#define LUA_VERSION_NUM 501
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 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-2012 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 1994-2008 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
|
||||
|
||||
Vendored
+2
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $
|
||||
** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
|
||||
** Lua virtual machine
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@@ -133,7 +133,6 @@ 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? */
|
||||
@@ -142,7 +141,6 @@ 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;
|
||||
}
|
||||
@@ -154,9 +152,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
|
||||
callTM(L, tm, t, key, val);
|
||||
return;
|
||||
}
|
||||
/* else repeat with `tm' */
|
||||
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
|
||||
t = &temp;
|
||||
t = tm; /* else repeat with `tm' */
|
||||
}
|
||||
luaG_runerror(L, "loop in settable");
|
||||
}
|
||||
|
||||
+7
-111
@@ -24,14 +24,9 @@ pidfile /var/run/redis.pid
|
||||
# If port 0 is specified Redis will not listen on a TCP socket.
|
||||
port 6379
|
||||
|
||||
# By default Redis listens for connections from all the network interfaces
|
||||
# available on the server. It is possible to listen to just one or multiple
|
||||
# interfaces using the "bind" configuration directive, followed by one or
|
||||
# more IP addresses.
|
||||
# If you want you can bind a single interface, if the bind option is not
|
||||
# specified all the interfaces will listen for incoming connections.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# bind 192.168.1.100 10.0.0.1
|
||||
# bind 127.0.0.1
|
||||
|
||||
# Specify the path for the unix socket that will be used to listen for
|
||||
@@ -68,10 +63,10 @@ tcp-keepalive 0
|
||||
# warning (only very important / critical messages are logged)
|
||||
loglevel notice
|
||||
|
||||
# Specify the log file name. Also the emptry string can be used to force
|
||||
# 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 ""
|
||||
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.
|
||||
@@ -208,11 +203,8 @@ slave-read-only yes
|
||||
#
|
||||
# repl-ping-slave-period 10
|
||||
|
||||
# The following option sets the replication timeout for:
|
||||
#
|
||||
# 1) Bulk transfer I/O during SYNC, from the point of view of slave.
|
||||
# 2) Master timeout from the point of view of slaves (data, pings).
|
||||
# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings).
|
||||
# The following option sets a timeout for both Bulk transfer I/O timeout and
|
||||
# master data or ping response timeout. The default value is 60 seconds.
|
||||
#
|
||||
# It is important to make sure that this value is greater than the value
|
||||
# specified for repl-ping-slave-period otherwise a timeout will be detected
|
||||
@@ -235,28 +227,6 @@ slave-read-only yes
|
||||
# be a good idea.
|
||||
repl-disable-tcp-nodelay no
|
||||
|
||||
# Set the replication backlog size. The backlog is a buffer that accumulates
|
||||
# slave data when slaves are disconnected for some time, so that when a slave
|
||||
# wants to reconnect again, often a full resync is not needed, but a partial
|
||||
# resync is enough, just passing the portion of data the slave missed while
|
||||
# disconnected.
|
||||
#
|
||||
# The biggest the replication backlog, the longer the time the slave can be
|
||||
# disconnected and later be able to perform a partial resynchronization.
|
||||
#
|
||||
# The backlog is only allocated once there is at least a slave connected.
|
||||
#
|
||||
# repl-backlog-size 1mb
|
||||
|
||||
# After a master has no longer connected slaves for some time, the backlog
|
||||
# will be freed. The following option configures the amount of seconds that
|
||||
# need to elapse, starting from the time the last slave disconnected, for
|
||||
# the backlog buffer to be freed.
|
||||
#
|
||||
# A value of 0 means to never release the backlog.
|
||||
#
|
||||
# repl-backlog-ttl 3600
|
||||
|
||||
# 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.
|
||||
@@ -272,28 +242,6 @@ repl-disable-tcp-nodelay no
|
||||
# By default the priority is 100.
|
||||
slave-priority 100
|
||||
|
||||
# It is possible for a master to stop accepting writes if there are less than
|
||||
# N slaves connected, having a lag less or equal than M seconds.
|
||||
#
|
||||
# The N slaves need to be in "online" state.
|
||||
#
|
||||
# The lag in seconds, that must be <= the specified value, is calculated from
|
||||
# the last ping received from the slave, that is usually sent every second.
|
||||
#
|
||||
# This option does not GUARANTEES that N replicas will accept the write, but
|
||||
# will limit the window of exposure for lost writes in case not enough slaves
|
||||
# are available, to the specified number of seconds.
|
||||
#
|
||||
# For example to require at least 3 slaves with a lag <= 10 seconds use:
|
||||
#
|
||||
# min-slaves-to-write 3
|
||||
# min-slaves-max-lag 10
|
||||
#
|
||||
# Setting one or the other to 0 disables the feature.
|
||||
#
|
||||
# By default min-slaves-to-write is set to 0 (feature disabled) and
|
||||
# min-slaves-max-lag is set to 10.
|
||||
|
||||
################################## SECURITY ###################################
|
||||
|
||||
# Require clients to issue AUTH <PASSWORD> before processing any other
|
||||
@@ -531,52 +479,6 @@ slowlog-log-slower-than 10000
|
||||
# You can reclaim memory used by the slow log with SLOWLOG RESET.
|
||||
slowlog-max-len 128
|
||||
|
||||
############################# Event notification ##############################
|
||||
|
||||
# Redis can notify Pub/Sub clients about events happening in the key space.
|
||||
# This feature is documented at http://redis.io/topics/keyspace-events
|
||||
#
|
||||
# For instance if keyspace events notification is enabled, and a client
|
||||
# performs a DEL operation on key "foo" stored in the Database 0, two
|
||||
# messages will be published via Pub/Sub:
|
||||
#
|
||||
# PUBLISH __keyspace@0__:foo del
|
||||
# PUBLISH __keyevent@0__:del foo
|
||||
#
|
||||
# It is possible to select the events that Redis will notify among a set
|
||||
# of classes. Every class is identified by a single character:
|
||||
#
|
||||
# K Keyspace events, published with __keyspace@<db>__ prefix.
|
||||
# E Keyevent events, published with __keyevent@<db>__ prefix.
|
||||
# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
|
||||
# $ String commands
|
||||
# l List commands
|
||||
# s Set commands
|
||||
# h Hash commands
|
||||
# z Sorted set commands
|
||||
# x Expired events (events generated every time a key expires)
|
||||
# e Evicted events (events generated when a key is evicted for maxmemory)
|
||||
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
|
||||
#
|
||||
# The "notify-keyspace-events" takes as argument a string that is composed
|
||||
# by zero or multiple characters. The empty string means that notifications
|
||||
# are disabled at all.
|
||||
#
|
||||
# Example: to enable list and generic events, from the point of view of the
|
||||
# event name, use:
|
||||
#
|
||||
# notify-keyspace-events Elg
|
||||
#
|
||||
# Example 2: to get the stream of the expired keys subscribing to channel
|
||||
# name __keyevent@0__:expired use:
|
||||
#
|
||||
# notify-keyspace-events Ex
|
||||
#
|
||||
# By default all notifications are disabled because most users don't need
|
||||
# this feature and the feature has some overhead. Note that if you don't
|
||||
# specify at least one of K or E, no events will be delivered.
|
||||
notify-keyspace-events ""
|
||||
|
||||
############################### ADVANCED CONFIG ###############################
|
||||
|
||||
# Hashes are encoded using a memory efficient data structure when they have a
|
||||
@@ -665,7 +567,7 @@ client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
# closing connections of clients in timeot, purging expired keys that are
|
||||
# never requested, and so forth.
|
||||
#
|
||||
# Not all tasks are performed with the same frequency, but Redis checks for
|
||||
# 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
|
||||
@@ -678,12 +580,6 @@ client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
# 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
|
||||
|
||||
+39
-45
@@ -18,76 +18,72 @@ OPTIMIZATION?=-O2
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua
|
||||
|
||||
# Default settings
|
||||
STD=-std=c99 -pedantic
|
||||
WARN=-Wall
|
||||
OPT=$(OPTIMIZATION)
|
||||
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_BIN=$(PREFIX)/bin
|
||||
INSTALL=install
|
||||
STD= -std=c99 -pedantic
|
||||
WARN= -Wall
|
||||
OPT= $(OPTIMIZATION)
|
||||
|
||||
# Default allocator
|
||||
ifeq ($(uname_S),Linux)
|
||||
MALLOC=jemalloc
|
||||
MALLOC=jemalloc
|
||||
else
|
||||
MALLOC=libc
|
||||
MALLOC=libc
|
||||
endif
|
||||
|
||||
# Backwards compatibility for selecting an allocator
|
||||
ifeq ($(USE_TCMALLOC),yes)
|
||||
MALLOC=tcmalloc
|
||||
MALLOC=tcmalloc
|
||||
endif
|
||||
|
||||
ifeq ($(USE_TCMALLOC_MINIMAL),yes)
|
||||
MALLOC=tcmalloc_minimal
|
||||
MALLOC=tcmalloc_minimal
|
||||
endif
|
||||
|
||||
ifeq ($(USE_JEMALLOC),yes)
|
||||
MALLOC=jemalloc
|
||||
MALLOC=jemalloc
|
||||
endif
|
||||
|
||||
# Override default settings if possible
|
||||
-include .make-settings
|
||||
|
||||
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
INSTALL=cp -pf
|
||||
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
|
||||
FINAL_LIBS+= -ldl -lnsl -lsocket -lpthread
|
||||
else ifeq ($(uname_S),Darwin)
|
||||
|
||||
FINAL_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) -D__EXTENSIONS__ -D_XPG6
|
||||
FINAL_LDFLAGS= $(LDFLAGS) $(REDIS_LDFLAGS) -g -ggdb
|
||||
FINAL_LIBS= -ldl -lnsl -lsocket -lm -lpthread
|
||||
DEBUG= -g -ggdb
|
||||
else
|
||||
FINAL_LDFLAGS+= -rdynamic
|
||||
FINAL_LIBS+= -pthread
|
||||
FINAL_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS= $(LDFLAGS) $(REDIS_LDFLAGS) -g -rdynamic -ggdb
|
||||
FINAL_LIBS= -lm -pthread
|
||||
DEBUG= -g -rdynamic -ggdb
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
|
||||
|
||||
ifeq ($(MALLOC),tcmalloc)
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
FINAL_LIBS+= -ltcmalloc
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
FINAL_LIBS+= -ltcmalloc
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),tcmalloc_minimal)
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
FINAL_LIBS+= -ltcmalloc_minimal
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
FINAL_LIBS+= -ltcmalloc_minimal
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),jemalloc)
|
||||
DEPENDENCY_TARGETS+= jemalloc
|
||||
FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
|
||||
FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl
|
||||
DEPENDENCY_TARGETS+= jemalloc
|
||||
FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
|
||||
FINAL_LIBS+= ../deps/jemalloc/lib/libjemalloc.a -ldl
|
||||
endif
|
||||
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
|
||||
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
|
||||
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
|
||||
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_BIN= $(PREFIX)/bin
|
||||
INSTALL= cp -pf
|
||||
|
||||
CCCOLOR="\033[34m"
|
||||
LINKCOLOR="\033[34;1m"
|
||||
SRCCOLOR="\033[33m"
|
||||
@@ -101,17 +97,17 @@ QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(EN
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
endif
|
||||
|
||||
REDIS_SERVER_NAME=redis-server
|
||||
REDIS_SENTINEL_NAME=redis-sentinel
|
||||
REDIS_SERVER_OBJ=adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o
|
||||
REDIS_CLI_NAME=redis-cli
|
||||
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o
|
||||
REDIS_CHECK_DUMP_NAME=redis-check-dump
|
||||
REDIS_CHECK_DUMP_OBJ=redis-check-dump.o lzf_c.o lzf_d.o crc64.o
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof
|
||||
REDIS_CHECK_AOF_OBJ=redis-check-aof.o
|
||||
REDIS_SERVER_NAME= redis-server
|
||||
REDIS_SENTINEL_NAME= redis-sentinel
|
||||
REDIS_SERVER_OBJ= adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o migrate.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o
|
||||
REDIS_CLI_NAME= redis-cli
|
||||
REDIS_CLI_OBJ= anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o
|
||||
REDIS_BENCHMARK_NAME= redis-benchmark
|
||||
REDIS_BENCHMARK_OBJ= ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o
|
||||
REDIS_CHECK_DUMP_NAME= redis-check-dump
|
||||
REDIS_CHECK_DUMP_OBJ= redis-check-dump.o lzf_c.o lzf_d.o crc64.o
|
||||
REDIS_CHECK_AOF_NAME= redis-check-aof
|
||||
REDIS_CHECK_AOF_OBJ= redis-check-aof.o
|
||||
|
||||
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
@echo ""
|
||||
@@ -200,8 +196,6 @@ distclean: clean
|
||||
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
@(cd ..; ./runtest)
|
||||
|
||||
check: test
|
||||
|
||||
lcov:
|
||||
$(MAKE) gcov
|
||||
@(set -e; cd ..; ./runtest --clients 1)
|
||||
@@ -232,7 +226,7 @@ src/help.h:
|
||||
@../utils/generate-command-help.rb > help.h
|
||||
|
||||
install: all
|
||||
@mkdir -p $(INSTALL_BIN)
|
||||
mkdir -p $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
|
||||
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
|
||||
|
||||
+5
-9
@@ -23,13 +23,13 @@ db.o: db.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h crc64.h bio.h
|
||||
ziplist.h intset.h version.h util.h 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 config.h
|
||||
lzf_c.o: lzf_c.c lzfP.h
|
||||
lzf_d.o: lzf_d.c lzfP.h
|
||||
memtest.o: memtest.c config.h
|
||||
memtest.o: memtest.c
|
||||
migrate.o: migrate.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h endianconv.h
|
||||
@@ -40,9 +40,6 @@ networking.o: networking.c redis.h fmacros.h config.h \
|
||||
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
|
||||
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
|
||||
rio.h
|
||||
notify.o: notify.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
object.o: object.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
@@ -58,19 +55,19 @@ rdb.o: rdb.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
|
||||
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h
|
||||
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
|
||||
redis-check-dump.o: redis-check-dump.c lzf.h crc64.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 anet.h ae.h
|
||||
redis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h bio.h \
|
||||
asciilogo.h
|
||||
release.o: release.c release.h version.h crc64.h
|
||||
release.o: release.c release.h
|
||||
replication.o: replication.c redis.h fmacros.h config.h \
|
||||
../deps/lua/src/lua.h ../deps/lua/src/luaconf.h ae.h sds.h dict.h \
|
||||
adlist.h zmalloc.h anet.h ziplist.h intset.h version.h util.h rdb.h \
|
||||
rio.h
|
||||
rio.o: rio.c fmacros.h rio.h sds.h util.h crc64.h
|
||||
rio.o: rio.c fmacros.h rio.h sds.h util.h
|
||||
scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h rand.h \
|
||||
@@ -82,7 +79,6 @@ sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.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
|
||||
setproctitle.o: setproctitle.c
|
||||
sha1.o: sha1.c sha1.h config.h
|
||||
slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
|
||||
@@ -91,36 +91,6 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the current set size. */
|
||||
int aeGetSetSize(aeEventLoop *eventLoop) {
|
||||
return eventLoop->setsize;
|
||||
}
|
||||
|
||||
/* Resize the maximum set size of the event loop.
|
||||
* If the requested set size is smaller than the current set size, but
|
||||
* there is already a file descriptor in use that is >= the requested
|
||||
* set size minus one, AE_ERR is returned and the operation is not
|
||||
* performed at all.
|
||||
*
|
||||
* Otherwise AE_OK is returned and the operation is successful. */
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
|
||||
int i;
|
||||
|
||||
if (setsize == eventLoop->setsize) return AE_OK;
|
||||
if (eventLoop->maxfd >= setsize) return AE_ERR;
|
||||
if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
|
||||
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->setsize = setsize;
|
||||
|
||||
/* Make sure that if we created new slots, they are initialized with
|
||||
* an AE_NONE mask. */
|
||||
for (i = eventLoop->maxfd+1; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
void aeDeleteEventLoop(aeEventLoop *eventLoop) {
|
||||
aeApiFree(eventLoop);
|
||||
zfree(eventLoop->events);
|
||||
|
||||
@@ -114,7 +114,5 @@ int aeWait(int fd, int mask, long long milliseconds);
|
||||
void aeMain(aeEventLoop *eventLoop);
|
||||
char *aeGetApiName(void);
|
||||
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
|
||||
int aeGetSetSize(aeEventLoop *eventLoop);
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -55,13 +55,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
|
||||
@@ -94,11 +94,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
/* Nothing to resize here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
|
||||
+1
-7
@@ -54,16 +54,10 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
return -1;
|
||||
}
|
||||
eventLoop->apidata = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
state->events = zrealloc(state->events, sizeof(struct kevent)*setsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
aeApiState *state = eventLoop->apidata;
|
||||
|
||||
|
||||
@@ -48,12 +48,6 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
|
||||
/* Just ensure we have enough room in the fd_set type. */
|
||||
if (setsize >= FD_SETSIZE) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aeApiFree(aeEventLoop *eventLoop) {
|
||||
zfree(eventLoop->apidata);
|
||||
}
|
||||
|
||||
+70
-150
@@ -163,44 +163,27 @@ int anetTcpKeepAlive(char *err, int fd)
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len)
|
||||
int anetResolve(char *err, char *host, char *ipbuf)
|
||||
{
|
||||
struct addrinfo hints, *info;
|
||||
int rv;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
|
||||
sa.sin_family = AF_INET;
|
||||
if (inet_aton(host, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (info->ai_family == AF_INET) {
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr;
|
||||
inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len);
|
||||
} else {
|
||||
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr;
|
||||
inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len);
|
||||
}
|
||||
|
||||
freeaddrinfo(info);
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int anetSetReuseAddr(char *err, int fd) {
|
||||
int yes = 1;
|
||||
/* Make sure connection-intensive things like the redis benckmark
|
||||
* will be able to close/open sockets a zillion of times */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
he = gethostbyname(host);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s", host);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
strcpy(ipbuf,inet_ntoa(sa.sin_addr));
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int anetCreateSocket(char *err, int domain) {
|
||||
int s;
|
||||
int s, on = 1;
|
||||
if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {
|
||||
anetSetError(err, "creating socket: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
@@ -208,8 +191,8 @@ static int anetCreateSocket(char *err, int domain) {
|
||||
|
||||
/* Make sure connection-intensive things like the redis benchmark
|
||||
* will be able to close/open sockets a zillion of times */
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) {
|
||||
close(s);
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return s;
|
||||
@@ -219,45 +202,38 @@ static int anetCreateSocket(char *err, int domain) {
|
||||
#define ANET_CONNECT_NONBLOCK 1
|
||||
static int anetTcpGenericConnect(char *err, char *addr, int port, int flags)
|
||||
{
|
||||
int s, rv;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
int s;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
snprintf(_port,6,"%d",port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
if (inet_aton(addr, &sa.sin_addr) == 0) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
anetSetError(err, "can't resolve: %s", addr);
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
if (flags & ANET_CONNECT_NONBLOCK) {
|
||||
if (anetNonBlock(err,s) != ANET_OK)
|
||||
return ANET_ERR;
|
||||
}
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS &&
|
||||
flags & ANET_CONNECT_NONBLOCK)
|
||||
return s;
|
||||
|
||||
anetSetError(err, "connect: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
/* if we set err then goto cleanup, otherwise next */
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK)
|
||||
goto error;
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) goto end;
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* break with the socket */
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
anetSetError(err, "creating socket: %s", strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
s = ANET_ERR;
|
||||
end:
|
||||
freeaddrinfo(servinfo);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -355,63 +331,28 @@ static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) {
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int anetV6Only(char *err, int s) {
|
||||
int yes = 1;
|
||||
if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {
|
||||
anetSetError(err, "setsockopt: %s", strerror(errno));
|
||||
int anetTcpServer(char *err, int port, char *bindaddr)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) {
|
||||
anetSetError(err, "invalid bind address");
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int _anetTcpServer(char *err, int port, char *bindaddr, int af)
|
||||
{
|
||||
int s, rv;
|
||||
char _port[6]; /* strlen("65535") */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
|
||||
snprintf(_port,6,"%d",port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */
|
||||
|
||||
if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen) == ANET_ERR) goto error;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
anetSetError(err, "unable to bind socket");
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
s = ANET_ERR;
|
||||
end:
|
||||
freeaddrinfo(servinfo);
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetTcpServer(char *err, int port, char *bindaddr)
|
||||
{
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET);
|
||||
}
|
||||
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr)
|
||||
{
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET6);
|
||||
}
|
||||
|
||||
int anetUnixServer(char *err, char *path, mode_t perm)
|
||||
{
|
||||
int s;
|
||||
@@ -447,22 +388,15 @@ static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *l
|
||||
return fd;
|
||||
}
|
||||
|
||||
int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {
|
||||
int anetTcpAccept(char *err, int s, char *ip, int *port) {
|
||||
int fd;
|
||||
struct sockaddr_storage sa;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
|
||||
if (sa.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
||||
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
||||
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin6_port);
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -476,8 +410,8 @@ int anetUnixAccept(char *err, int s) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
|
||||
struct sockaddr_storage sa;
|
||||
int anetPeerToString(int fd, char *ip, int *port) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) {
|
||||
@@ -486,20 +420,13 @@ int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
|
||||
ip[1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
if (sa.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
||||
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
||||
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin6_port);
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int anetSockName(int fd, char *ip, size_t ip_len, int *port) {
|
||||
struct sockaddr_storage sa;
|
||||
int anetSockName(int fd, char *ip, int *port) {
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {
|
||||
@@ -508,14 +435,7 @@ int anetSockName(int fd, char *ip, size_t ip_len, int *port) {
|
||||
ip[1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
if (sa.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
||||
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
||||
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
||||
if (port) *port = ntohs(s->sin6_port);
|
||||
}
|
||||
if (ip) strcpy(ip,inet_ntoa(sa.sin_addr));
|
||||
if (port) *port = ntohs(sa.sin_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-5
@@ -44,19 +44,17 @@ int anetTcpNonBlockConnect(char *err, char *addr, int port);
|
||||
int anetUnixConnect(char *err, char *path);
|
||||
int anetUnixNonBlockConnect(char *err, char *path);
|
||||
int anetRead(int fd, char *buf, int count);
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);
|
||||
int anetResolve(char *err, char *host, char *ipbuf);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr);
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr);
|
||||
int anetUnixServer(char *err, char *path, mode_t perm);
|
||||
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
|
||||
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 anetEnableTcpNoDelay(char *err, int fd);
|
||||
int anetDisableTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);
|
||||
int anetPeerToString(int fd, char *ip, int *port);
|
||||
int anetKeepAlive(char *err, int fd, int interval);
|
||||
int anetSockName(int fd, char *ip, size_t ip_len, int *port);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -456,7 +456,7 @@ struct redisClient *createFakeClient(void) {
|
||||
c->reply_bytes = 0;
|
||||
c->obuf_soft_limit_reached_time = 0;
|
||||
c->watched_keys = listCreate();
|
||||
listSetFreeMethod(c->reply,decrRefCountVoid);
|
||||
listSetFreeMethod(c->reply,decrRefCount);
|
||||
listSetDupMethod(c->reply,dupClientReplyValue);
|
||||
initClientMultiState(c);
|
||||
return c;
|
||||
@@ -536,7 +536,7 @@ int loadAppendOnlyFile(char *filename) {
|
||||
/* Command lookup */
|
||||
cmd = lookupCommand(argv[0]->ptr);
|
||||
if (!cmd) {
|
||||
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr);
|
||||
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr);
|
||||
exit(1);
|
||||
}
|
||||
/* Run the command in the context of a fake client */
|
||||
@@ -855,8 +855,6 @@ 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;
|
||||
@@ -884,9 +882,6 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
|
||||
expiretime = getExpire(db,&key);
|
||||
|
||||
/* If this key is already expired skip it */
|
||||
if (expiretime != -1 && expiretime < now) continue;
|
||||
|
||||
/* Save the key and associated value */
|
||||
if (o->type == REDIS_STRING) {
|
||||
/* Emit a SET command */
|
||||
@@ -909,6 +904,8 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
/* Save the expire time */
|
||||
if (expiretime != -1) {
|
||||
char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";
|
||||
/* If this key is already expired skip it */
|
||||
if (expiretime < now) continue;
|
||||
if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkObject(&aof,&key) == 0) goto werr;
|
||||
if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr;
|
||||
@@ -962,8 +959,8 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
char tmpfile[256];
|
||||
|
||||
/* Child */
|
||||
closeListeningSockets(0);
|
||||
redisSetProcTitle("redis-aof-rewrite");
|
||||
if (server.ipfd > 0) close(server.ipfd);
|
||||
if (server.sofd > 0) close(server.sofd);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
|
||||
size_t private_dirty = zmalloc_get_private_dirty();
|
||||
@@ -997,7 +994,6 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
* accumulated by the parent into server.aof_rewrite_buf will start
|
||||
* with a SELECT statement and it will be safe to merge. */
|
||||
server.aof_selected_db = -1;
|
||||
replicationScriptCacheFlush();
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_OK; /* unreached */
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
#include "redis.h"
|
||||
#include "bio.h"
|
||||
|
||||
static pthread_t bio_threads[REDIS_BIO_NUM_OPS];
|
||||
static pthread_mutex_t bio_mutex[REDIS_BIO_NUM_OPS];
|
||||
static pthread_cond_t bio_condvar[REDIS_BIO_NUM_OPS];
|
||||
static list *bio_jobs[REDIS_BIO_NUM_OPS];
|
||||
@@ -119,7 +118,6 @@ void bioInit(void) {
|
||||
redisLog(REDIS_WARNING,"Fatal: Can't initialize Background Jobs.");
|
||||
exit(1);
|
||||
}
|
||||
bio_threads[j] = thread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,11 +140,7 @@ void *bioProcessBackgroundJobs(void *arg) {
|
||||
unsigned long type = (unsigned long) arg;
|
||||
sigset_t sigset;
|
||||
|
||||
/* Make the thread killable at any time, so that bioKillThreads()
|
||||
* can work reliably. */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
|
||||
pthread_detach(pthread_self());
|
||||
pthread_mutex_lock(&bio_mutex[type]);
|
||||
/* Block SIGALRM so we are sure that only the main thread will
|
||||
* receive the watchdog signal. */
|
||||
@@ -198,23 +192,56 @@ unsigned long long bioPendingJobsOfType(int type) {
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Kill the running bio threads in an unclean way. This function should be
|
||||
* used only when it's critical to stop the threads for some reason.
|
||||
* Currently Redis does this only on crash (for instance on SIGSEGV) in order
|
||||
* to perform a fast memory check without other threads messing with memory. */
|
||||
void bioKillThreads(void) {
|
||||
int err, j;
|
||||
#if 0 /* We don't use the following code for now, and bioWaitPendingJobsLE
|
||||
probably needs a rewrite using conditional variables instead of the
|
||||
current implementation. */
|
||||
|
||||
|
||||
for (j = 0; j < REDIS_BIO_NUM_OPS; j++) {
|
||||
if (pthread_cancel(bio_threads[j]) == 0) {
|
||||
if ((err = pthread_join(bio_threads[j],NULL)) != 0) {
|
||||
redisLog(REDIS_WARNING,
|
||||
"Bio thread for job type #%d can be joined: %s",
|
||||
j, strerror(err));
|
||||
} else {
|
||||
redisLog(REDIS_WARNING,
|
||||
"Bio thread for job type #%d terminated",j);
|
||||
}
|
||||
/* Wait until the number of pending jobs of the specified type are
|
||||
* less or equal to the specified number.
|
||||
*
|
||||
* This function may block for long time, it should only be used to perform
|
||||
* the following tasks:
|
||||
*
|
||||
* 1) To avoid that the main thread is pushing jobs of a given time so fast
|
||||
* that the background thread can't process them at the same speed.
|
||||
* So before creating a new job of a given type the main thread should
|
||||
* call something like: bioWaitPendingJobsLE(job_type,10000);
|
||||
* 2) In order to perform special operations that make it necessary to be sure
|
||||
* no one is touching shared resourced in the background.
|
||||
*/
|
||||
void bioWaitPendingJobsLE(int type, unsigned long long num) {
|
||||
unsigned long long iteration = 0;
|
||||
|
||||
/* We poll the jobs queue aggressively to start, and gradually relax
|
||||
* the polling speed if it is going to take too much time. */
|
||||
while(1) {
|
||||
iteration++;
|
||||
if (iteration > 1000 && iteration <= 10000) {
|
||||
usleep(100);
|
||||
} else if (iteration > 10000) {
|
||||
usleep(1000);
|
||||
}
|
||||
if (bioPendingJobsOfType(type) <= num) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the older job of the specified type. */
|
||||
time_t bioOlderJobOfType(int type) {
|
||||
time_t time;
|
||||
listNode *ln;
|
||||
struct bio_job *job;
|
||||
|
||||
pthread_mutex_lock(&bio_mutex[type]);
|
||||
ln = listFirst(bio_jobs[type]);
|
||||
if (ln == NULL) {
|
||||
pthread_mutex_unlock(&bio_mutex[type]);
|
||||
return 0;
|
||||
}
|
||||
job = ln->value;
|
||||
time = job->time;
|
||||
pthread_mutex_unlock(&bio_mutex[type]);
|
||||
return time;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -33,7 +33,6 @@ void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);
|
||||
unsigned long long bioPendingJobsOfType(int type);
|
||||
void bioWaitPendingJobsLE(int type, unsigned long long num);
|
||||
time_t bioOlderJobOfType(int type);
|
||||
void bioKillThreads(void);
|
||||
|
||||
/* Background job opcodes */
|
||||
#define REDIS_BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */
|
||||
|
||||
+3
-6
@@ -58,8 +58,8 @@ static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
|
||||
/* Count number of bits set in the binary array pointed by 's' and long
|
||||
* 'count' bytes. The implementation of this function is required to
|
||||
* work with a input string length up to 512 MB. */
|
||||
size_t redisPopcount(void *s, long count) {
|
||||
size_t bits = 0;
|
||||
long popcount(void *s, long count) {
|
||||
long bits = 0;
|
||||
unsigned char *p;
|
||||
uint32_t *p4 = s;
|
||||
static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
|
||||
@@ -153,7 +153,6 @@ void setbitCommand(redisClient *c) {
|
||||
byteval |= ((on & 0x1) << bit);
|
||||
((uint8_t*)o->ptr)[byte] = byteval;
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
addReply(c, bitval ? shared.cone : shared.czero);
|
||||
}
|
||||
@@ -347,11 +346,9 @@ void bitopCommand(redisClient *c) {
|
||||
if (maxlen) {
|
||||
o = createObject(REDIS_STRING,res);
|
||||
setKey(c->db,targetkey,o);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",targetkey,c->db->id);
|
||||
decrRefCount(o);
|
||||
} else if (dbDelete(c->db,targetkey)) {
|
||||
signalModifiedKey(c->db,targetkey);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",targetkey,c->db->id);
|
||||
}
|
||||
server.dirty++;
|
||||
addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
|
||||
@@ -407,6 +404,6 @@ void bitcountCommand(redisClient *c) {
|
||||
} else {
|
||||
long bytes = end-start+1;
|
||||
|
||||
addReplyLongLong(c,redisPopcount(p+start,bytes));
|
||||
addReplyLongLong(c,popcount(p+start,bytes));
|
||||
}
|
||||
}
|
||||
|
||||
+37
-784
@@ -28,33 +28,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "redis.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
const int value;
|
||||
} validSyslogFacilities[] = {
|
||||
{"user", LOG_USER},
|
||||
{"local0", LOG_LOCAL0},
|
||||
{"local1", LOG_LOCAL1},
|
||||
{"local2", LOG_LOCAL2},
|
||||
{"local3", LOG_LOCAL3},
|
||||
{"local4", LOG_LOCAL4},
|
||||
{"local5", LOG_LOCAL5},
|
||||
{"local6", LOG_LOCAL6},
|
||||
{"local7", LOG_LOCAL7},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES] = {
|
||||
{0, 0, 0}, /* normal */
|
||||
{1024*1024*256, 1024*1024*64, 60}, /* slave */
|
||||
{1024*1024*32, 1024*1024*8, 60} /* pubsub */
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Config file parsing
|
||||
*----------------------------------------------------------------------------*/
|
||||
@@ -105,7 +81,7 @@ void loadServerConfigFromString(char *config) {
|
||||
/* Skip this line if the resulting command vector is empty. */
|
||||
if (argc == 0) {
|
||||
sdsfreesplitres(argv,argc);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
sdstolower(argv[0]);
|
||||
|
||||
@@ -125,15 +101,8 @@ void loadServerConfigFromString(char *config) {
|
||||
if (server.port < 0 || server.port > 65535) {
|
||||
err = "Invalid port"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"bind") && argc >= 2) {
|
||||
int j, addresses = argc-1;
|
||||
|
||||
if (addresses > REDIS_BINDADDR_MAX) {
|
||||
err = "Too many bind addresses specified"; goto loaderr;
|
||||
}
|
||||
for (j = 0; j < addresses; j++)
|
||||
server.bindaddr[j] = zstrdup(argv[j+1]);
|
||||
server.bindaddr_count = addresses;
|
||||
} else if (!strcasecmp(argv[0],"bind") && argc == 2) {
|
||||
server.bindaddr = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
|
||||
server.unixsocket = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
|
||||
@@ -171,9 +140,12 @@ void loadServerConfigFromString(char *config) {
|
||||
} else if (!strcasecmp(argv[0],"logfile") && argc == 2) {
|
||||
FILE *logfp;
|
||||
|
||||
zfree(server.logfile);
|
||||
server.logfile = zstrdup(argv[1]);
|
||||
if (server.logfile[0] != '\0') {
|
||||
if (!strcasecmp(server.logfile,"stdout")) {
|
||||
zfree(server.logfile);
|
||||
server.logfile = NULL;
|
||||
}
|
||||
if (server.logfile) {
|
||||
/* Test if we are able to open the file. The server will not
|
||||
* be able to abort just for this problem later... */
|
||||
logfp = fopen(server.logfile,"a");
|
||||
@@ -192,6 +164,21 @@ void loadServerConfigFromString(char *config) {
|
||||
if (server.syslog_ident) zfree(server.syslog_ident);
|
||||
server.syslog_ident = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) {
|
||||
struct {
|
||||
const char *name;
|
||||
const int value;
|
||||
} validSyslogFacilities[] = {
|
||||
{"user", LOG_USER},
|
||||
{"local0", LOG_LOCAL0},
|
||||
{"local1", LOG_LOCAL1},
|
||||
{"local2", LOG_LOCAL2},
|
||||
{"local3", LOG_LOCAL3},
|
||||
{"local4", LOG_LOCAL4},
|
||||
{"local5", LOG_LOCAL5},
|
||||
{"local6", LOG_LOCAL6},
|
||||
{"local7", LOG_LOCAL7},
|
||||
{NULL, 0}
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; validSyslogFacilities[i].name; i++) {
|
||||
@@ -262,19 +249,6 @@ void loadServerConfigFromString(char *config) {
|
||||
if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
|
||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"repl-backlog-size") && argc == 2) {
|
||||
long long size = memtoll(argv[1],NULL);
|
||||
if (size <= 0) {
|
||||
err = "repl-backlog-size must be 1 or greater.";
|
||||
goto loaderr;
|
||||
}
|
||||
resizeReplicationBacklog(size);
|
||||
} else if (!strcasecmp(argv[0],"repl-backlog-ttl") && argc == 2) {
|
||||
server.repl_backlog_time_limit = atoi(argv[1]);
|
||||
if (server.repl_backlog_time_limit < 0) {
|
||||
err = "repl-backlog-ttl can't be negative ";
|
||||
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) {
|
||||
@@ -313,10 +287,6 @@ void loadServerConfigFromString(char *config) {
|
||||
}
|
||||
server.aof_state = yes ? REDIS_AOF_ON : REDIS_AOF_OFF;
|
||||
} else if (!strcasecmp(argv[0],"appendfilename") && argc == 2) {
|
||||
if (!pathIsBaseName(argv[1])) {
|
||||
err = "appendfilename can't be a path, just a filename";
|
||||
goto loaderr;
|
||||
}
|
||||
zfree(server.aof_filename);
|
||||
server.aof_filename = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"no-appendfsync-on-rewrite")
|
||||
@@ -347,12 +317,6 @@ 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";
|
||||
@@ -363,10 +327,6 @@ void loadServerConfigFromString(char *config) {
|
||||
zfree(server.pidfile);
|
||||
server.pidfile = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
|
||||
if (!pathIsBaseName(argv[1])) {
|
||||
err = "dbfilename can't be a path, just a filename";
|
||||
goto loaderr;
|
||||
}
|
||||
zfree(server.rdb_filename);
|
||||
server.rdb_filename = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"hash-max-ziplist-entries") && argc == 2) {
|
||||
@@ -443,24 +403,6 @@ void loadServerConfigFromString(char *config) {
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
|
||||
server.slave_priority = atoi(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"min-slaves-to-write") && argc == 2) {
|
||||
server.repl_min_slaves_to_write = atoi(argv[1]);
|
||||
if (server.repl_min_slaves_to_write < 0) {
|
||||
err = "Invalid value for min-slaves-to-write."; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"min-slaves-max-lag") && argc == 2) {
|
||||
server.repl_min_slaves_max_lag = atoi(argv[1]);
|
||||
if (server.repl_min_slaves_max_lag < 0) {
|
||||
err = "Invalid value for min-slaves-max-lag."; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
|
||||
int flags = keyspaceEventsStringToFlags(argv[1]);
|
||||
|
||||
if (flags == -1) {
|
||||
err = "Invalid event class character. Use 'g$lshzxeA'.";
|
||||
goto loaderr;
|
||||
}
|
||||
server.notify_keyspace_events = flags;
|
||||
} else if (!strcasecmp(argv[0],"sentinel")) {
|
||||
/* argc == 1 is handled by main() as we need to enter the sentinel
|
||||
* mode ASAP. */
|
||||
@@ -526,7 +468,7 @@ void loadServerConfig(char *filename, char *options) {
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* CONFIG SET implementation
|
||||
* CONFIG command for remote configuration
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
void configSetCommand(redisClient *c) {
|
||||
@@ -537,10 +479,6 @@ void configSetCommand(redisClient *c) {
|
||||
o = c->argv[3];
|
||||
|
||||
if (!strcasecmp(c->argv[2]->ptr,"dbfilename")) {
|
||||
if (!pathIsBaseName(o->ptr)) {
|
||||
addReplyError(c, "dbfilename can't be a path, just a filename");
|
||||
return;
|
||||
}
|
||||
zfree(server.rdb_filename);
|
||||
server.rdb_filename = zstrdup(o->ptr);
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"requirepass")) {
|
||||
@@ -549,7 +487,7 @@ void configSetCommand(redisClient *c) {
|
||||
server.requirepass = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"masterauth")) {
|
||||
zfree(server.masterauth);
|
||||
server.masterauth = ((char*)o->ptr)[0] ? zstrdup(o->ptr) : NULL;
|
||||
server.masterauth = zstrdup(o->ptr);
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0) goto badfmt;
|
||||
@@ -560,35 +498,10 @@ void configSetCommand(redisClient *c) {
|
||||
}
|
||||
freeMemoryIfNeeded();
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"maxclients")) {
|
||||
int orig_value = server.maxclients;
|
||||
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
|
||||
/* Try to check if the OS is capable of supporting so many FDs. */
|
||||
server.maxclients = ll;
|
||||
if (ll > orig_value) {
|
||||
adjustOpenFilesLimit();
|
||||
if (server.maxclients != ll) {
|
||||
addReplyErrorFormat(c,"The operating system is not able to handle the specified number of clients, try with %d", server.maxclients);
|
||||
server.maxclients = orig_value;
|
||||
return;
|
||||
}
|
||||
if (aeGetSetSize(server.el) <
|
||||
server.maxclients + REDIS_EVENTLOOP_FDSET_INCR)
|
||||
{
|
||||
if (aeResizeSetSize(server.el,
|
||||
server.maxclients + REDIS_EVENTLOOP_FDSET_INCR) == AE_ERR)
|
||||
{
|
||||
addReplyError(c,"The event loop API used by Redis is not able to handle the specified number of clients");
|
||||
server.maxclients = orig_value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"hz")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.hz = ll;
|
||||
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")) {
|
||||
@@ -653,11 +566,6 @@ 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);
|
||||
@@ -806,12 +714,6 @@ void configSetCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"repl-timeout")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
|
||||
server.repl_timeout = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-size")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt;
|
||||
resizeReplicationBacklog(ll);
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"repl-backlog-ttl")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.repl_backlog_time_limit = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"watchdog-period")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
if (ll)
|
||||
@@ -823,11 +725,11 @@ void configSetCommand(redisClient *c) {
|
||||
|
||||
if (yn == -1) goto badfmt;
|
||||
server.rdb_compression = yn;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) {
|
||||
int flags = keyspaceEventsStringToFlags(o->ptr);
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"rdbchecksum")) {
|
||||
int yn = yesnotoi(o->ptr);
|
||||
|
||||
if (flags == -1) goto badfmt;
|
||||
server.notify_keyspace_events = flags;
|
||||
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);
|
||||
|
||||
@@ -835,18 +737,8 @@ void configSetCommand(redisClient *c) {
|
||||
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;
|
||||
ll <= 0) goto badfmt;
|
||||
server.slave_priority = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"min-slaves-to-write")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0) goto badfmt;
|
||||
server.repl_min_slaves_to_write = ll;
|
||||
refreshGoodSlavesCount();
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"min-slaves-max-lag")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0) goto badfmt;
|
||||
server.repl_min_slaves_max_lag = ll;
|
||||
refreshGoodSlavesCount();
|
||||
} else {
|
||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||
(char*)c->argv[2]->ptr);
|
||||
@@ -861,10 +753,6 @@ badfmt: /* Bad format errors */
|
||||
(char*)c->argv[2]->ptr);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* CONFIG GET implementation
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#define config_get_string_field(_name,_var) do { \
|
||||
if (stringmatch(pattern,_name,0)) { \
|
||||
addReplyBulkCString(c,_name); \
|
||||
@@ -902,6 +790,7 @@ void configGetCommand(redisClient *c) {
|
||||
config_get_string_field("dbfilename",server.rdb_filename);
|
||||
config_get_string_field("requirepass",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);
|
||||
config_get_string_field("pidfile",server.pidfile);
|
||||
@@ -938,13 +827,9 @@ void configGetCommand(redisClient *c) {
|
||||
config_get_numerical_field("databases",server.dbnum);
|
||||
config_get_numerical_field("repl-ping-slave-period",server.repl_ping_slave_period);
|
||||
config_get_numerical_field("repl-timeout",server.repl_timeout);
|
||||
config_get_numerical_field("repl-backlog-size",server.repl_backlog_size);
|
||||
config_get_numerical_field("repl-backlog-ttl",server.repl_backlog_time_limit);
|
||||
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("min-slaves-to-write",server.repl_min_slaves_to_write);
|
||||
config_get_numerical_field("min-slaves-max-lag",server.repl_min_slaves_max_lag);
|
||||
config_get_numerical_field("hz",server.hz);
|
||||
|
||||
/* Bool (yes/no) values */
|
||||
@@ -962,8 +847,6 @@ void configGetCommand(redisClient *c) {
|
||||
config_get_bool_field("activerehashing", server.activerehashing);
|
||||
config_get_bool_field("repl-disable-tcp-nodelay",
|
||||
server.repl_disable_tcp_nodelay);
|
||||
config_get_bool_field("aof-rewrite-incremental-fsync",
|
||||
server.aof_rewrite_incremental_fsync);
|
||||
|
||||
/* Everything we can't handle with macros follows. */
|
||||
|
||||
@@ -1016,8 +899,8 @@ void configGetCommand(redisClient *c) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < server.saveparamslen; j++) {
|
||||
buf = sdscatprintf(buf,"%jd %d",
|
||||
(intmax_t)server.saveparams[j].seconds,
|
||||
buf = sdscatprintf(buf,"%ld %d",
|
||||
server.saveparams[j].seconds,
|
||||
server.saveparams[j].changes);
|
||||
if (j != server.saveparamslen-1)
|
||||
buf = sdscatlen(buf," ",1);
|
||||
@@ -1078,628 +961,9 @@ void configGetCommand(redisClient *c) {
|
||||
addReplyBulkCString(c,buf);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"notify-keyspace-events",0)) {
|
||||
robj *flagsobj = createObject(REDIS_STRING,
|
||||
keyspaceEventsFlagsToString(server.notify_keyspace_events));
|
||||
|
||||
addReplyBulkCString(c,"notify-keyspace-events");
|
||||
addReplyBulk(c,flagsobj);
|
||||
decrRefCount(flagsobj);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"bind",0)) {
|
||||
sds aux = sdsjoin(server.bindaddr,server.bindaddr_count," ");
|
||||
|
||||
addReplyBulkCString(c,"bind");
|
||||
addReplyBulkCString(c,aux);
|
||||
sdsfree(aux);
|
||||
matches++;
|
||||
}
|
||||
setDeferredMultiBulkLength(c,replylen,matches*2);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* CONFIG REWRITE implementation
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/* We use the following dictionary type to store where a configuration
|
||||
* option is mentioned in the old configuration file, so it's
|
||||
* like "maxmemory" -> list of line numbers (first line is zero). */
|
||||
unsigned int dictSdsHash(const void *key);
|
||||
int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
|
||||
void dictSdsDestructor(void *privdata, void *val);
|
||||
void dictListDestructor(void *privdata, void *val);
|
||||
|
||||
dictType optionToLineDictType = {
|
||||
dictSdsHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictSdsKeyCompare, /* key compare */
|
||||
dictSdsDestructor, /* key destructor */
|
||||
dictListDestructor /* val destructor */
|
||||
};
|
||||
|
||||
/* The config rewrite state. */
|
||||
struct rewriteConfigState {
|
||||
dict *option_to_line; /* Option -> list of config file lines map */
|
||||
int numlines; /* Number of lines in current config */
|
||||
sds *lines; /* Current lines as an array of sds strings */
|
||||
int has_tail; /* True if we already added directives that were
|
||||
not present in the original config file. */
|
||||
};
|
||||
|
||||
/* Append the new line to the current configuration state. */
|
||||
void rewriteConfigAppendLine(struct rewriteConfigState *state, sds line) {
|
||||
state->lines = zrealloc(state->lines, sizeof(char*) * (state->numlines+1));
|
||||
state->lines[state->numlines++] = line;
|
||||
}
|
||||
|
||||
/* Populate the option -> list of line numbers map. */
|
||||
void rewriteConfigAddLineNumberToOption(struct rewriteConfigState *state, sds option, int linenum) {
|
||||
list *l = dictFetchValue(state->option_to_line,option);
|
||||
|
||||
if (l == NULL) {
|
||||
l = listCreate();
|
||||
dictAdd(state->option_to_line,sdsdup(option),l);
|
||||
}
|
||||
listAddNodeTail(l,(void*)(long)linenum);
|
||||
}
|
||||
|
||||
/* Read the old file, split it into lines to populate a newly created
|
||||
* config rewrite state, and return it to the caller.
|
||||
*
|
||||
* If it is impossible to read the old file, NULL is returned.
|
||||
* If the old file does not exist at all, an empty state is returned. */
|
||||
struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
|
||||
FILE *fp = fopen(path,"r");
|
||||
struct rewriteConfigState *state = zmalloc(sizeof(*state));
|
||||
char buf[REDIS_CONFIGLINE_MAX+1];
|
||||
int linenum = -1;
|
||||
|
||||
if (fp == NULL && errno != ENOENT) return NULL;
|
||||
|
||||
state->option_to_line = dictCreate(&optionToLineDictType,NULL);
|
||||
state->numlines = 0;
|
||||
state->lines = NULL;
|
||||
state->has_tail = 0;
|
||||
if (fp == NULL) return state;
|
||||
|
||||
/* Read the old file line by line, populate the state. */
|
||||
while(fgets(buf,REDIS_CONFIGLINE_MAX+1,fp) != NULL) {
|
||||
int argc;
|
||||
sds *argv;
|
||||
sds line = sdstrim(sdsnew(buf),"\r\n\t ");
|
||||
|
||||
linenum++; /* Zero based, so we init at -1 */
|
||||
|
||||
/* Handle comments and empty lines. */
|
||||
if (line[0] == '#' || line[0] == '\0') {
|
||||
rewriteConfigAppendLine(state,line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not a comment, split into arguments. */
|
||||
argv = sdssplitargs(line,&argc);
|
||||
if (argv == NULL) {
|
||||
/* Apparently the line is unparsable for some reason, for
|
||||
* instance it may have unbalanced quotes. Load it as a
|
||||
* comment. */
|
||||
sds aux = sdsnew("# ??? ");
|
||||
aux = sdscatsds(aux,line);
|
||||
sdsfree(line);
|
||||
rewriteConfigAppendLine(state,aux);
|
||||
continue;
|
||||
}
|
||||
|
||||
sdstolower(argv[0]); /* We only want lowercase config directives. */
|
||||
|
||||
/* Now we populate the state according to the content of this line.
|
||||
* Append the line and populate the option -> line numbers map. */
|
||||
rewriteConfigAppendLine(state,line);
|
||||
rewriteConfigAddLineNumberToOption(state,argv[0],linenum);
|
||||
|
||||
sdsfreesplitres(argv,argc);
|
||||
}
|
||||
fclose(fp);
|
||||
return state;
|
||||
}
|
||||
|
||||
/* Rewrite the specified configuration option with the new "line".
|
||||
* It progressively uses lines of the file that were already used for the same
|
||||
* configuraiton option in the old version of the file, removing that line from
|
||||
* the map of options -> line numbers.
|
||||
*
|
||||
* If there are lines associated with a given configuration option and
|
||||
* "force" is non-zero, the line is appended to the configuration file.
|
||||
* Usually "force" is true when an option has not its default value, so it
|
||||
* must be rewritten even if not present previously.
|
||||
*
|
||||
* The first time a line is appended into a configuration file, a comment
|
||||
* is added to show that starting from that point the config file was generated
|
||||
* by CONFIG REWRITE.
|
||||
*
|
||||
* "line" is either used, or freed, so the caller does not need to free it
|
||||
* in any way. */
|
||||
void rewriteConfigRewriteLine(struct rewriteConfigState *state, char *option, sds line, int force) {
|
||||
sds o = sdsnew(option);
|
||||
list *l = dictFetchValue(state->option_to_line,o);
|
||||
|
||||
if (!l && !force) {
|
||||
/* Option not used previously, and we are not forced to use it. */
|
||||
sdsfree(line);
|
||||
sdsfree(o);
|
||||
return;
|
||||
}
|
||||
|
||||
if (l) {
|
||||
listNode *ln = listFirst(l);
|
||||
int linenum = (long) ln->value;
|
||||
|
||||
/* There are still lines in the old configuration file we can reuse
|
||||
* for this option. Replace the line with the new one. */
|
||||
listDelNode(l,ln);
|
||||
if (listLength(l) == 0) dictDelete(state->option_to_line,o);
|
||||
sdsfree(state->lines[linenum]);
|
||||
state->lines[linenum] = line;
|
||||
} else {
|
||||
/* Append a new line. */
|
||||
if (!state->has_tail) {
|
||||
rewriteConfigAppendLine(state,
|
||||
sdsnew("# Generated by CONFIG REWRITE"));
|
||||
state->has_tail = 1;
|
||||
}
|
||||
rewriteConfigAppendLine(state,line);
|
||||
}
|
||||
sdsfree(o);
|
||||
}
|
||||
|
||||
/* Write the long long 'bytes' value as a string in a way that is parsable
|
||||
* inside redis.conf. If possible uses the GB, MB, KB notation. */
|
||||
int rewriteConfigFormatMemory(char *buf, size_t len, long long bytes) {
|
||||
int gb = 1024*1024*1024;
|
||||
int mb = 1024*1024;
|
||||
int kb = 1024;
|
||||
|
||||
if (bytes && (bytes % gb) == 0) {
|
||||
return snprintf(buf,len,"%lldgb",bytes/gb);
|
||||
} else if (bytes && (bytes % mb) == 0) {
|
||||
return snprintf(buf,len,"%lldmb",bytes/mb);
|
||||
} else if (bytes && (bytes % kb) == 0) {
|
||||
return snprintf(buf,len,"%lldkb",bytes/kb);
|
||||
} else {
|
||||
return snprintf(buf,len,"%lld",bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rewrite a simple "option-name <bytes>" configuration option. */
|
||||
void rewriteConfigBytesOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
|
||||
char buf[64];
|
||||
int force = value != defvalue;
|
||||
sds line;
|
||||
|
||||
rewriteConfigFormatMemory(buf,sizeof(buf),value);
|
||||
line = sdscatprintf(sdsempty(),"%s %s",option,buf);
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
|
||||
}
|
||||
|
||||
/* Rewrite a yes/no option. */
|
||||
void rewriteConfigYesNoOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
|
||||
int force = value != defvalue;
|
||||
sds line = sdscatprintf(sdsempty(),"%s %s",option,
|
||||
value ? "yes" : "no");
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite a string option. */
|
||||
void rewriteConfigStringOption(struct rewriteConfigState *state, char *option, char *value, char *defvalue) {
|
||||
int force = 1;
|
||||
sds line;
|
||||
|
||||
/* String options set to NULL need to be not present at all in the
|
||||
* configuration file to be set to NULL again at the next reboot. */
|
||||
if (value == NULL) return;
|
||||
|
||||
/* Compare the strings as sds strings to have a binary safe comparison. */
|
||||
if (defvalue && strcmp(value,defvalue) == 0) force = 0;
|
||||
|
||||
line = sdsnew(option);
|
||||
line = sdscatlen(line, " ", 1);
|
||||
line = sdscatrepr(line, value, strlen(value));
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite a numerical (long long range) option. */
|
||||
void rewriteConfigNumericalOption(struct rewriteConfigState *state, char *option, long long value, long long defvalue) {
|
||||
int force = value != defvalue;
|
||||
sds line = sdscatprintf(sdsempty(),"%s %lld",option,value);
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite a octal option. */
|
||||
void rewriteConfigOctalOption(struct rewriteConfigState *state, char *option, int value, int defvalue) {
|
||||
int force = value != defvalue;
|
||||
sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite an enumeration option, after the "value" every enum/value pair
|
||||
* is specified, terminated by NULL. After NULL the default value is
|
||||
* specified. See how the function is used for more information. */
|
||||
void rewriteConfigEnumOption(struct rewriteConfigState *state, char *option, int value, ...) {
|
||||
va_list ap;
|
||||
char *enum_name, *matching_name = NULL;
|
||||
int enum_val, def_val, force;
|
||||
sds line;
|
||||
|
||||
va_start(ap, value);
|
||||
while(1) {
|
||||
enum_name = va_arg(ap,char*);
|
||||
enum_val = va_arg(ap,int);
|
||||
if (enum_name == NULL) {
|
||||
def_val = enum_val;
|
||||
break;
|
||||
}
|
||||
if (value == enum_val) matching_name = enum_name;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
force = value != def_val;
|
||||
line = sdscatprintf(sdsempty(),"%s %s",option,matching_name);
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the syslog-fability option. */
|
||||
void rewriteConfigSyslogfacilityOption(struct rewriteConfigState *state) {
|
||||
int value = server.syslog_facility, j;
|
||||
int force = value != LOG_LOCAL0;
|
||||
char *name = NULL, *option = "syslog-facility";
|
||||
sds line;
|
||||
|
||||
for (j = 0; validSyslogFacilities[j].name; j++) {
|
||||
if (validSyslogFacilities[j].value == value) {
|
||||
name = (char*) validSyslogFacilities[j].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
line = sdscatprintf(sdsempty(),"%s %s",option,name);
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the save option. */
|
||||
void rewriteConfigSaveOption(struct rewriteConfigState *state) {
|
||||
int j;
|
||||
sds line;
|
||||
|
||||
/* Note that if there are no save parameters at all, all the current
|
||||
* config line with "save" will be detected as orphaned and deleted,
|
||||
* resulting into no RDB persistence as expected. */
|
||||
for (j = 0; j < server.saveparamslen; j++) {
|
||||
line = sdscatprintf(sdsempty(),"save %ld %d",
|
||||
server.saveparams[j].seconds, server.saveparams[j].changes);
|
||||
rewriteConfigRewriteLine(state,"save",line,1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rewrite the dir option, always using absolute paths.*/
|
||||
void rewriteConfigDirOption(struct rewriteConfigState *state) {
|
||||
char cwd[1024];
|
||||
|
||||
if (getcwd(cwd,sizeof(cwd)) == NULL) return; /* no rewrite on error. */
|
||||
rewriteConfigStringOption(state,"dir",cwd,NULL);
|
||||
}
|
||||
|
||||
/* Rewrite the slaveof option. */
|
||||
void rewriteConfigSlaveofOption(struct rewriteConfigState *state) {
|
||||
char *option = "slaveof";
|
||||
sds line;
|
||||
|
||||
/* If this is a master, we want all the slaveof config options
|
||||
* in the file to be removed. */
|
||||
if (server.masterhost == NULL) return;
|
||||
line = sdscatprintf(sdsempty(),"%s %s %d", option,
|
||||
server.masterhost, server.masterport);
|
||||
rewriteConfigRewriteLine(state,option,line,1);
|
||||
}
|
||||
|
||||
/* Rewrite the appendonly option. */
|
||||
void rewriteConfigAppendonlyOption(struct rewriteConfigState *state) {
|
||||
int force = server.aof_state != REDIS_AOF_OFF;
|
||||
char *option = "appendonly";
|
||||
sds line;
|
||||
|
||||
line = sdscatprintf(sdsempty(),"%s %s", option,
|
||||
(server.aof_state == REDIS_AOF_OFF) ? "no" : "yes");
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the notify-keyspace-events option. */
|
||||
void rewriteConfigNotifykeyspaceeventsOption(struct rewriteConfigState *state) {
|
||||
int force = server.notify_keyspace_events != 0;
|
||||
char *option = "notify-keyspace-events";
|
||||
sds line, flags;
|
||||
|
||||
flags = keyspaceEventsFlagsToString(server.notify_keyspace_events);
|
||||
line = sdsnew(option);
|
||||
line = sdscatlen(line, " ", 1);
|
||||
line = sdscatrepr(line, flags, sdslen(flags));
|
||||
sdsfree(flags);
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the client-output-buffer-limit option. */
|
||||
void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state) {
|
||||
int j;
|
||||
char *option = "client-output-buffer-limit";
|
||||
|
||||
for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) {
|
||||
int force = (server.client_obuf_limits[j].hard_limit_bytes !=
|
||||
clientBufferLimitsDefaults[j].hard_limit_bytes) ||
|
||||
(server.client_obuf_limits[j].soft_limit_bytes !=
|
||||
clientBufferLimitsDefaults[j].soft_limit_bytes) ||
|
||||
(server.client_obuf_limits[j].soft_limit_seconds !=
|
||||
clientBufferLimitsDefaults[j].soft_limit_seconds);
|
||||
sds line;
|
||||
char hard[64], soft[64];
|
||||
|
||||
rewriteConfigFormatMemory(hard,sizeof(hard),
|
||||
server.client_obuf_limits[j].hard_limit_bytes);
|
||||
rewriteConfigFormatMemory(soft,sizeof(soft),
|
||||
server.client_obuf_limits[j].soft_limit_bytes);
|
||||
|
||||
line = sdscatprintf(sdsempty(),"%s %s %s %s %ld",
|
||||
option, getClientLimitClassName(j), hard, soft,
|
||||
(long) server.client_obuf_limits[j].soft_limit_seconds);
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rewrite the bind option. */
|
||||
void rewriteConfigBindOption(struct rewriteConfigState *state) {
|
||||
int force = 1;
|
||||
sds line, addresses;
|
||||
char *option = "bind";
|
||||
|
||||
/* Nothing to rewrite if we don't have bind addresses. */
|
||||
if (server.bindaddr_count == 0) return;
|
||||
|
||||
/* Rewrite as bind <addr1> <addr2> ... <addrN> */
|
||||
addresses = sdsjoin(server.bindaddr,server.bindaddr_count," ");
|
||||
line = sdsnew(option);
|
||||
line = sdscatlen(line, " ", 1);
|
||||
line = sdscatsds(line, addresses);
|
||||
sdsfree(addresses);
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Glue together the configuration lines in the current configuration
|
||||
* rewrite state into a single string, stripping multiple empty lines. */
|
||||
sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
|
||||
sds content = sdsempty();
|
||||
int j, was_empty = 0;
|
||||
|
||||
for (j = 0; j < state->numlines; j++) {
|
||||
/* Every cluster of empty lines is turned into a single empty line. */
|
||||
if (sdslen(state->lines[j]) == 0) {
|
||||
if (was_empty) continue;
|
||||
was_empty = 1;
|
||||
} else {
|
||||
was_empty = 0;
|
||||
}
|
||||
content = sdscatsds(content,state->lines[j]);
|
||||
content = sdscatlen(content,"\n",1);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/* Free the configuration rewrite state. */
|
||||
void rewriteConfigReleaseState(struct rewriteConfigState *state) {
|
||||
sdsfreesplitres(state->lines,state->numlines);
|
||||
dictRelease(state->option_to_line);
|
||||
zfree(state);
|
||||
}
|
||||
|
||||
/* At the end of the rewrite process the state contains the remaining
|
||||
* map between "option name" => "lines in the original config file".
|
||||
* Lines used by the rewrite process were removed by the function
|
||||
* rewriteConfigRewriteLine(), all the other lines are "orphaned" and
|
||||
* should be replaced by empty lines.
|
||||
*
|
||||
* This function does just this, iterating all the option names and
|
||||
* blanking all the lines still associated. */
|
||||
void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
|
||||
dictIterator *di = dictGetIterator(state->option_to_line);
|
||||
dictEntry *de;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
list *l = dictGetVal(de);
|
||||
|
||||
while(listLength(l)) {
|
||||
listNode *ln = listFirst(l);
|
||||
int linenum = (long) ln->value;
|
||||
|
||||
sdsfree(state->lines[linenum]);
|
||||
state->lines[linenum] = sdsempty();
|
||||
listDelNode(l,ln);
|
||||
}
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* This function overwrites the old configuration file with the new content.
|
||||
*
|
||||
* 1) The old file length is obtained.
|
||||
* 2) If the new content is smaller, padding is added.
|
||||
* 3) A single write(2) call is used to replace the content of the file.
|
||||
* 4) Later the file is truncated to the length of the new content.
|
||||
*
|
||||
* This way we are sure the file is left in a consistent state even if the
|
||||
* process is stopped between any of the four operations.
|
||||
*
|
||||
* The function returns 0 on success, otherwise -1 is returned and errno
|
||||
* set accordingly. */
|
||||
int rewriteConfigOverwriteFile(char *configfile, sds content) {
|
||||
int retval = 0;
|
||||
int fd = open(configfile,O_RDWR|O_CREAT,0644);
|
||||
int content_size = sdslen(content), padding = 0;
|
||||
struct stat sb;
|
||||
sds content_padded;
|
||||
|
||||
/* 1) Open the old file (or create a new one if it does not
|
||||
* exist), get the size. */
|
||||
if (fd == -1) return -1; /* errno set by open(). */
|
||||
if (fstat(fd,&sb) == -1) {
|
||||
close(fd);
|
||||
return -1; /* errno set by fstat(). */
|
||||
}
|
||||
|
||||
/* 2) Pad the content at least match the old file size. */
|
||||
content_padded = sdsdup(content);
|
||||
if (content_size < sb.st_size) {
|
||||
/* If the old file was bigger, pad the content with
|
||||
* a newline plus as many "#" chars as required. */
|
||||
padding = sb.st_size - content_size;
|
||||
content_padded = sdsgrowzero(content_padded,sb.st_size);
|
||||
content_padded[content_size] = '\n';
|
||||
memset(content_padded+content_size+1,'#',padding-1);
|
||||
}
|
||||
|
||||
/* 3) Write the new content using a single write(2). */
|
||||
if (write(fd,content_padded,strlen(content_padded)) == -1) {
|
||||
retval = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* 4) Truncate the file to the right length if we used padding. */
|
||||
if (padding) {
|
||||
if (ftruncate(fd,content_size) == -1) {
|
||||
/* Non critical error... */
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
sdsfree(content_padded);
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Rewrite the configuration file at "path".
|
||||
* If the configuration file already exists, we try at best to retain comments
|
||||
* and overall structure.
|
||||
*
|
||||
* Configuration parameters that are at their default value, unless already
|
||||
* explicitly included in the old configuration file, are not rewritten.
|
||||
*
|
||||
* On error -1 is returned and errno is set accordingly, otherwise 0. */
|
||||
int rewriteConfig(char *path) {
|
||||
struct rewriteConfigState *state;
|
||||
sds newcontent;
|
||||
int retval;
|
||||
|
||||
/* Step 1: read the old config into our rewrite state. */
|
||||
if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
|
||||
|
||||
/* Step 2: rewrite every single option, replacing or appending it inside
|
||||
* the rewrite state. */
|
||||
|
||||
/* TODO: Turn every default into a define, use it also in
|
||||
* initServerConfig(). */
|
||||
rewriteConfigYesNoOption(state,"daemonize",server.daemonize,0);
|
||||
rewriteConfigStringOption(state,"pidfile",server.pidfile,REDIS_DEFAULT_PID_FILE);
|
||||
rewriteConfigNumericalOption(state,"port",server.port,REDIS_SERVERPORT);
|
||||
rewriteConfigBindOption(state);
|
||||
rewriteConfigStringOption(state,"unixsocket",server.unixsocket,NULL);
|
||||
rewriteConfigOctalOption(state,"unixsocketperm",server.unixsocketperm,REDIS_DEFAULT_UNIX_SOCKET_PERM);
|
||||
rewriteConfigNumericalOption(state,"timeout",server.maxidletime,REDIS_MAXIDLETIME);
|
||||
rewriteConfigNumericalOption(state,"tcp-keepalive",server.tcpkeepalive,REDIS_DEFAULT_TCP_KEEPALIVE);
|
||||
rewriteConfigEnumOption(state,"loglevel",server.verbosity,
|
||||
"debug", REDIS_DEBUG,
|
||||
"verbose", REDIS_VERBOSE,
|
||||
"notice", REDIS_NOTICE,
|
||||
"warning", REDIS_WARNING,
|
||||
NULL, REDIS_DEFAULT_VERBOSITY);
|
||||
rewriteConfigStringOption(state,"logfile",server.logfile,REDIS_DEFAULT_LOGFILE);
|
||||
rewriteConfigYesNoOption(state,"syslog-enabled",server.syslog_enabled,REDIS_DEFAULT_SYSLOG_ENABLED);
|
||||
rewriteConfigStringOption(state,"syslog-ident",server.syslog_ident,REDIS_DEFAULT_SYSLOG_IDENT);
|
||||
rewriteConfigSyslogfacilityOption(state);
|
||||
rewriteConfigSaveOption(state);
|
||||
rewriteConfigNumericalOption(state,"databases",server.dbnum,REDIS_DEFAULT_DBNUM);
|
||||
rewriteConfigYesNoOption(state,"stop-writes-on-bgsave-error",server.stop_writes_on_bgsave_err,REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR);
|
||||
rewriteConfigYesNoOption(state,"rdbcompression",server.rdb_compression,REDIS_DEFAULT_RDB_COMPRESSION);
|
||||
rewriteConfigYesNoOption(state,"rdbchecksum",server.rdb_checksum,REDIS_DEFAULT_RDB_CHECKSUM);
|
||||
rewriteConfigStringOption(state,"dbfilename",server.rdb_filename,REDIS_DEFAULT_RDB_FILENAME);
|
||||
rewriteConfigDirOption(state);
|
||||
rewriteConfigSlaveofOption(state);
|
||||
rewriteConfigStringOption(state,"masterauth",server.masterauth,NULL);
|
||||
rewriteConfigYesNoOption(state,"slave-serve-stale-data",server.repl_serve_stale_data,REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA);
|
||||
rewriteConfigYesNoOption(state,"slave-read-only",server.repl_slave_ro,REDIS_DEFAULT_SLAVE_READ_ONLY);
|
||||
rewriteConfigNumericalOption(state,"repl-ping-slave-period",server.repl_ping_slave_period,REDIS_REPL_PING_SLAVE_PERIOD);
|
||||
rewriteConfigNumericalOption(state,"repl-timeout",server.repl_timeout,REDIS_REPL_TIMEOUT);
|
||||
rewriteConfigBytesOption(state,"repl-backlog-size",server.repl_backlog_size,REDIS_DEFAULT_REPL_BACKLOG_SIZE);
|
||||
rewriteConfigBytesOption(state,"repl-backlog-ttl",server.repl_backlog_time_limit,REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT);
|
||||
rewriteConfigYesNoOption(state,"repl-disable-tcp-nodelay",server.repl_disable_tcp_nodelay,REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY);
|
||||
rewriteConfigNumericalOption(state,"slave-priority",server.slave_priority,REDIS_DEFAULT_SLAVE_PRIORITY);
|
||||
rewriteConfigStringOption(state,"requirepass",server.requirepass,NULL);
|
||||
rewriteConfigNumericalOption(state,"maxclients",server.maxclients,REDIS_MAX_CLIENTS);
|
||||
rewriteConfigBytesOption(state,"maxmemory",server.maxmemory,REDIS_DEFAULT_MAXMEMORY);
|
||||
rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,
|
||||
"volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU,
|
||||
"allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU,
|
||||
"volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM,
|
||||
"allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM,
|
||||
"volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL,
|
||||
"noeviction", REDIS_MAXMEMORY_NO_EVICTION,
|
||||
NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);
|
||||
rewriteConfigNumericalOption(state,"maxmemory-samples",server.maxmemory_samples,REDIS_DEFAULT_MAXMEMORY_SAMPLES);
|
||||
rewriteConfigAppendonlyOption(state);
|
||||
rewriteConfigEnumOption(state,"appendfsync",server.aof_fsync,
|
||||
"everysec", AOF_FSYNC_EVERYSEC,
|
||||
"always", AOF_FSYNC_ALWAYS,
|
||||
"no", AOF_FSYNC_NO,
|
||||
NULL, REDIS_DEFAULT_AOF_FSYNC);
|
||||
rewriteConfigYesNoOption(state,"no-appendfsync-on-rewrite",server.aof_no_fsync_on_rewrite,REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE);
|
||||
rewriteConfigNumericalOption(state,"auto-aof-rewrite-percentage",server.aof_rewrite_perc,REDIS_AOF_REWRITE_PERC);
|
||||
rewriteConfigBytesOption(state,"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,REDIS_AOF_REWRITE_MIN_SIZE);
|
||||
rewriteConfigNumericalOption(state,"lua-time-limit",server.lua_time_limit,REDIS_LUA_TIME_LIMIT);
|
||||
rewriteConfigNumericalOption(state,"slowlog-log-slower-than",server.slowlog_log_slower_than,REDIS_SLOWLOG_LOG_SLOWER_THAN);
|
||||
rewriteConfigNumericalOption(state,"slowlog-max-len",server.slowlog_max_len,REDIS_SLOWLOG_MAX_LEN);
|
||||
rewriteConfigNotifykeyspaceeventsOption(state);
|
||||
rewriteConfigNumericalOption(state,"hash-max-ziplist-entries",server.hash_max_ziplist_entries,REDIS_HASH_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"hash-max-ziplist-value",server.hash_max_ziplist_value,REDIS_HASH_MAX_ZIPLIST_VALUE);
|
||||
rewriteConfigNumericalOption(state,"list-max-ziplist-entries",server.list_max_ziplist_entries,REDIS_LIST_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"list-max-ziplist-value",server.list_max_ziplist_value,REDIS_LIST_MAX_ZIPLIST_VALUE);
|
||||
rewriteConfigNumericalOption(state,"set-max-intset-entries",server.set_max_intset_entries,REDIS_SET_MAX_INTSET_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"zset-max-ziplist-entries",server.zset_max_ziplist_entries,REDIS_ZSET_MAX_ZIPLIST_ENTRIES);
|
||||
rewriteConfigNumericalOption(state,"zset-max-ziplist-value",server.zset_max_ziplist_value,REDIS_ZSET_MAX_ZIPLIST_VALUE);
|
||||
rewriteConfigYesNoOption(state,"activerehashing",server.activerehashing,REDIS_DEFAULT_ACTIVE_REHASHING);
|
||||
rewriteConfigClientoutputbufferlimitOption(state);
|
||||
rewriteConfigNumericalOption(state,"hz",server.hz,REDIS_DEFAULT_HZ);
|
||||
rewriteConfigYesNoOption(state,"aof-rewrite-incremental-fsync",server.aof_rewrite_incremental_fsync,REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC);
|
||||
|
||||
/* Step 3: remove all the orphaned lines in the old file, that is, lines
|
||||
* that were used by a config option and are no longer used, like in case
|
||||
* of multiple "save" options or duplicated options. */
|
||||
rewriteConfigRemoveOrphaned(state);
|
||||
|
||||
/* Step 4: generate a new configuration file from the modified state
|
||||
* and write it into the original file. */
|
||||
newcontent = rewriteConfigGetContentFromState(state);
|
||||
retval = rewriteConfigOverwriteFile(server.configfile,newcontent);
|
||||
|
||||
sdsfree(newcontent);
|
||||
rewriteConfigReleaseState(state);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* CONFIG command entry point
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
void configCommand(redisClient *c) {
|
||||
if (!strcasecmp(c->argv[1]->ptr,"set")) {
|
||||
if (c->argc != 4) goto badarity;
|
||||
@@ -1719,17 +983,6 @@ void configCommand(redisClient *c) {
|
||||
server.aof_delayed_fsync = 0;
|
||||
resetCommandTableStats();
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"rewrite")) {
|
||||
if (c->argc != 2) goto badarity;
|
||||
if (server.configfile == NULL) {
|
||||
addReplyError(c,"The server is running without a config file");
|
||||
return;
|
||||
}
|
||||
if (rewriteConfig(server.configfile) == -1) {
|
||||
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
|
||||
} else {
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
} else {
|
||||
addReplyError(c,
|
||||
"CONFIG subcommand must be one of GET, SET, RESETSTAT");
|
||||
|
||||
+1
-17
@@ -45,9 +45,7 @@
|
||||
|
||||
/* Test for proc filesystem */
|
||||
#ifdef __linux__
|
||||
#define HAVE_PROC_STAT 1
|
||||
#define HAVE_PROC_MAPS 1
|
||||
#define HAVE_PROC_SMAPS 1
|
||||
#define HAVE_PROCFS 1
|
||||
#endif
|
||||
|
||||
/* Test for task_info() */
|
||||
@@ -105,20 +103,6 @@
|
||||
#define rdb_fsync_range(fd,off,size) fsync(fd)
|
||||
#endif
|
||||
|
||||
/* Check if we can use setproctitle().
|
||||
* BSD systems have support for it, we provide an implementation for
|
||||
* Linux and osx. */
|
||||
#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
|
||||
#define USE_SETPROCTITLE
|
||||
#endif
|
||||
|
||||
#if (defined __linux || defined __APPLE__)
|
||||
#define USE_SETPROCTITLE
|
||||
#define INIT_SETPROCTITLE_REPLACEMENT
|
||||
void spt_init(int argc, char *argv[]);
|
||||
void setproctitle(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
/* Byte ordering detection */
|
||||
#include <sys/types.h> /* This will likely define BYTE_ORDER */
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef CRC64_H
|
||||
#define CRC64_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
#endif
|
||||
@@ -238,8 +238,6 @@ void delCommand(redisClient *c) {
|
||||
for (j = 1; j < c->argc; j++) {
|
||||
if (dbDelete(c->db,c->argv[j])) {
|
||||
signalModifiedKey(c->db,c->argv[j]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
|
||||
"del",c->argv[j],c->db->id);
|
||||
server.dirty++;
|
||||
deleted++;
|
||||
}
|
||||
@@ -353,12 +351,6 @@ void shutdownCommand(redisClient *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* SHUTDOWN can be called even while the server is in "loading" state.
|
||||
* When this happens we need to make sure no attempt is performed to save
|
||||
* the dataset on shutdown (otherwise it could overwrite the current DB
|
||||
* with half-read data). */
|
||||
if (server.loading)
|
||||
flags = (flags & ~REDIS_SHUTDOWN_SAVE) | REDIS_SHUTDOWN_NOSAVE;
|
||||
if (prepareForShutdown(flags) == REDIS_OK) exit(0);
|
||||
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
|
||||
}
|
||||
@@ -384,8 +376,7 @@ void renameGenericCommand(redisClient *c, int nx) {
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
/* Overwrite: delete the old key before creating the new one
|
||||
* with the same name. */
|
||||
/* Overwrite: delete the old key before creating the new one with the same name. */
|
||||
dbDelete(c->db,c->argv[2]);
|
||||
}
|
||||
dbAdd(c->db,c->argv[2],o);
|
||||
@@ -393,10 +384,6 @@ void renameGenericCommand(redisClient *c, int nx) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[2]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"rename_from",
|
||||
c->argv[1],c->db->id);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"rename_to",
|
||||
c->argv[2],c->db->id);
|
||||
server.dirty++;
|
||||
addReply(c,nx ? shared.cone : shared.ok);
|
||||
}
|
||||
@@ -506,7 +493,8 @@ void propagateExpire(redisDb *db, robj *key) {
|
||||
|
||||
if (server.aof_state != REDIS_AOF_OFF)
|
||||
feedAppendOnlyFile(server.delCommand,db->id,argv,2);
|
||||
replicationFeedSlaves(server.slaves,db->id,argv,2);
|
||||
if (listLength(server.slaves))
|
||||
replicationFeedSlaves(server.slaves,db->id,argv,2);
|
||||
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[1]);
|
||||
@@ -537,8 +525,6 @@ int expireIfNeeded(redisDb *db, robj *key) {
|
||||
/* Delete the key */
|
||||
server.stat_expiredkeys++;
|
||||
propagateExpire(db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
|
||||
"expired",key,db->id);
|
||||
return dbDelete(db,key);
|
||||
}
|
||||
|
||||
@@ -586,14 +572,12 @@ void expireGenericCommand(redisClient *c, long long basetime, int unit) {
|
||||
rewriteClientCommandVector(c,2,aux,key);
|
||||
decrRefCount(aux);
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
addReply(c, shared.cone);
|
||||
return;
|
||||
} else {
|
||||
setExpire(c->db,key,when);
|
||||
addReply(c,shared.cone);
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id);
|
||||
server.dirty++;
|
||||
return;
|
||||
}
|
||||
@@ -618,17 +602,10 @@ void pexpireatCommand(redisClient *c) {
|
||||
void ttlGenericCommand(redisClient *c, int output_ms) {
|
||||
long long expire, ttl = -1;
|
||||
|
||||
/* If the key does not exist at all, return -2 */
|
||||
if (lookupKeyRead(c->db,c->argv[1]) == NULL) {
|
||||
addReplyLongLong(c,-2);
|
||||
return;
|
||||
}
|
||||
/* The key exists. Return -1 if it has no expire, or the actual
|
||||
* TTL value otherwise. */
|
||||
expire = getExpire(c->db,c->argv[1]);
|
||||
if (expire != -1) {
|
||||
ttl = expire-mstime();
|
||||
if (ttl < 0) ttl = 0;
|
||||
if (ttl < 0) ttl = -1;
|
||||
}
|
||||
if (ttl == -1) {
|
||||
addReplyLongLong(c,-1);
|
||||
|
||||
+84
-192
@@ -29,7 +29,6 @@
|
||||
|
||||
#include "redis.h"
|
||||
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
|
||||
#include "crc64.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
@@ -38,7 +37,6 @@
|
||||
#include <execinfo.h>
|
||||
#include <ucontext.h>
|
||||
#include <fcntl.h>
|
||||
#include "bio.h"
|
||||
#endif /* HAVE_BACKTRACE */
|
||||
|
||||
/* ================================= Debugging ============================== */
|
||||
@@ -329,11 +327,8 @@ void debugCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
|
||||
double dtime = strtod(c->argv[2]->ptr,NULL);
|
||||
long long utime = dtime*1000000;
|
||||
struct timespec tv;
|
||||
|
||||
tv.tv_sec = utime / 1000000;
|
||||
tv.tv_nsec = (utime % 1000000) * 1000;
|
||||
nanosleep(&tv, NULL);
|
||||
usleep(utime);
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") &&
|
||||
c->argc == 3)
|
||||
@@ -392,12 +387,9 @@ void redisLogObjectDebugInfo(robj *o) {
|
||||
redisLog(REDIS_WARNING,"Object encoding: %d", o->encoding);
|
||||
redisLog(REDIS_WARNING,"Object refcount: %d", o->refcount);
|
||||
if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_RAW) {
|
||||
redisLog(REDIS_WARNING,"Object raw string len: %zu", sdslen(o->ptr));
|
||||
if (sdslen(o->ptr) < 4096) {
|
||||
sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr));
|
||||
redisLog(REDIS_WARNING,"Object raw string content: %s", repr);
|
||||
sdsfree(repr);
|
||||
}
|
||||
redisLog(REDIS_WARNING,"Object raw string len: %d", sdslen(o->ptr));
|
||||
if (sdslen(o->ptr) < 4096)
|
||||
redisLog(REDIS_WARNING,"Object raw string content: \"%s\"", (char*)o->ptr);
|
||||
} else if (o->type == REDIS_LIST) {
|
||||
redisLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o));
|
||||
} else if (o->type == REDIS_SET) {
|
||||
@@ -478,13 +470,10 @@ static void *getMcontextEip(ucontext_t *uc) {
|
||||
void logStackContent(void **sp) {
|
||||
int i;
|
||||
for (i = 15; i >= 0; i--) {
|
||||
unsigned long addr = (unsigned long) sp+i;
|
||||
unsigned long val = (unsigned long) sp[i];
|
||||
|
||||
if (sizeof(long) == 4)
|
||||
redisLog(REDIS_WARNING, "(%08lx) -> %08lx", addr, val);
|
||||
redisLog(REDIS_WARNING, "(%08lx) -> %08lx", sp+i, sp[i]);
|
||||
else
|
||||
redisLog(REDIS_WARNING, "(%016lx) -> %016lx", addr, val);
|
||||
redisLog(REDIS_WARNING, "(%016lx) -> %016lx", sp+i, sp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,27 +491,27 @@ void logRegisters(ucontext_t *uc) {
|
||||
"R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
|
||||
"R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
|
||||
"RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx",
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rax,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rbx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rcx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rdx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rdi,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rsi,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rbp,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rsp,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r8,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r9,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r10,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r11,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r12,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r13,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r14,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__r15,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rip,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__rflags,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__cs,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__fs,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__gs
|
||||
uc->uc_mcontext->__ss.__rax,
|
||||
uc->uc_mcontext->__ss.__rbx,
|
||||
uc->uc_mcontext->__ss.__rcx,
|
||||
uc->uc_mcontext->__ss.__rdx,
|
||||
uc->uc_mcontext->__ss.__rdi,
|
||||
uc->uc_mcontext->__ss.__rsi,
|
||||
uc->uc_mcontext->__ss.__rbp,
|
||||
uc->uc_mcontext->__ss.__rsp,
|
||||
uc->uc_mcontext->__ss.__r8,
|
||||
uc->uc_mcontext->__ss.__r9,
|
||||
uc->uc_mcontext->__ss.__r10,
|
||||
uc->uc_mcontext->__ss.__r11,
|
||||
uc->uc_mcontext->__ss.__r12,
|
||||
uc->uc_mcontext->__ss.__r13,
|
||||
uc->uc_mcontext->__ss.__r14,
|
||||
uc->uc_mcontext->__ss.__r15,
|
||||
uc->uc_mcontext->__ss.__rip,
|
||||
uc->uc_mcontext->__ss.__rflags,
|
||||
uc->uc_mcontext->__ss.__cs,
|
||||
uc->uc_mcontext->__ss.__fs,
|
||||
uc->uc_mcontext->__ss.__gs
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
|
||||
#else
|
||||
@@ -533,22 +522,22 @@ void logRegisters(ucontext_t *uc) {
|
||||
"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
|
||||
"SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n"
|
||||
"DS:%08lx ES:%08lx FS :%08lx GS :%08lx",
|
||||
(unsigned long) uc->uc_mcontext->__ss.__eax,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__ebx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__ecx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__edx,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__edi,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__esi,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__ebp,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__esp,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__ss,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__eflags,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__eip,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__cs,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__ds,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__es,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__fs,
|
||||
(unsigned long) uc->uc_mcontext->__ss.__gs
|
||||
uc->uc_mcontext->__ss.__eax,
|
||||
uc->uc_mcontext->__ss.__ebx,
|
||||
uc->uc_mcontext->__ss.__ecx,
|
||||
uc->uc_mcontext->__ss.__edx,
|
||||
uc->uc_mcontext->__ss.__edi,
|
||||
uc->uc_mcontext->__ss.__esi,
|
||||
uc->uc_mcontext->__ss.__ebp,
|
||||
uc->uc_mcontext->__ss.__esp,
|
||||
uc->uc_mcontext->__ss.__ss,
|
||||
uc->uc_mcontext->__ss.__eflags,
|
||||
uc->uc_mcontext->__ss.__eip,
|
||||
uc->uc_mcontext->__ss.__cs,
|
||||
uc->uc_mcontext->__ss.__ds,
|
||||
uc->uc_mcontext->__ss.__es,
|
||||
uc->uc_mcontext->__ss.__fs,
|
||||
uc->uc_mcontext->__ss.__gs
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
|
||||
#endif
|
||||
@@ -562,22 +551,22 @@ void logRegisters(ucontext_t *uc) {
|
||||
"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
|
||||
"SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
|
||||
"DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
|
||||
(unsigned long) uc->uc_mcontext.gregs[11],
|
||||
(unsigned long) uc->uc_mcontext.gregs[8],
|
||||
(unsigned long) uc->uc_mcontext.gregs[10],
|
||||
(unsigned long) uc->uc_mcontext.gregs[9],
|
||||
(unsigned long) uc->uc_mcontext.gregs[4],
|
||||
(unsigned long) uc->uc_mcontext.gregs[5],
|
||||
(unsigned long) uc->uc_mcontext.gregs[6],
|
||||
(unsigned long) uc->uc_mcontext.gregs[7],
|
||||
(unsigned long) uc->uc_mcontext.gregs[18],
|
||||
(unsigned long) uc->uc_mcontext.gregs[17],
|
||||
(unsigned long) uc->uc_mcontext.gregs[14],
|
||||
(unsigned long) uc->uc_mcontext.gregs[15],
|
||||
(unsigned long) uc->uc_mcontext.gregs[3],
|
||||
(unsigned long) uc->uc_mcontext.gregs[2],
|
||||
(unsigned long) uc->uc_mcontext.gregs[1],
|
||||
(unsigned long) uc->uc_mcontext.gregs[0]
|
||||
uc->uc_mcontext.gregs[11],
|
||||
uc->uc_mcontext.gregs[8],
|
||||
uc->uc_mcontext.gregs[10],
|
||||
uc->uc_mcontext.gregs[9],
|
||||
uc->uc_mcontext.gregs[4],
|
||||
uc->uc_mcontext.gregs[5],
|
||||
uc->uc_mcontext.gregs[6],
|
||||
uc->uc_mcontext.gregs[7],
|
||||
uc->uc_mcontext.gregs[18],
|
||||
uc->uc_mcontext.gregs[17],
|
||||
uc->uc_mcontext.gregs[14],
|
||||
uc->uc_mcontext.gregs[15],
|
||||
uc->uc_mcontext.gregs[3],
|
||||
uc->uc_mcontext.gregs[2],
|
||||
uc->uc_mcontext.gregs[1],
|
||||
uc->uc_mcontext.gregs[0]
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext.gregs[7]);
|
||||
#elif defined(__X86_64__) || defined(__x86_64__)
|
||||
@@ -589,25 +578,25 @@ void logRegisters(ucontext_t *uc) {
|
||||
"R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
|
||||
"R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
|
||||
"RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
|
||||
(unsigned long) uc->uc_mcontext.gregs[13],
|
||||
(unsigned long) uc->uc_mcontext.gregs[11],
|
||||
(unsigned long) uc->uc_mcontext.gregs[14],
|
||||
(unsigned long) uc->uc_mcontext.gregs[12],
|
||||
(unsigned long) uc->uc_mcontext.gregs[8],
|
||||
(unsigned long) uc->uc_mcontext.gregs[9],
|
||||
(unsigned long) uc->uc_mcontext.gregs[10],
|
||||
(unsigned long) uc->uc_mcontext.gregs[15],
|
||||
(unsigned long) uc->uc_mcontext.gregs[0],
|
||||
(unsigned long) uc->uc_mcontext.gregs[1],
|
||||
(unsigned long) uc->uc_mcontext.gregs[2],
|
||||
(unsigned long) uc->uc_mcontext.gregs[3],
|
||||
(unsigned long) uc->uc_mcontext.gregs[4],
|
||||
(unsigned long) uc->uc_mcontext.gregs[5],
|
||||
(unsigned long) uc->uc_mcontext.gregs[6],
|
||||
(unsigned long) uc->uc_mcontext.gregs[7],
|
||||
(unsigned long) uc->uc_mcontext.gregs[16],
|
||||
(unsigned long) uc->uc_mcontext.gregs[17],
|
||||
(unsigned long) uc->uc_mcontext.gregs[18]
|
||||
uc->uc_mcontext.gregs[13],
|
||||
uc->uc_mcontext.gregs[11],
|
||||
uc->uc_mcontext.gregs[14],
|
||||
uc->uc_mcontext.gregs[12],
|
||||
uc->uc_mcontext.gregs[8],
|
||||
uc->uc_mcontext.gregs[9],
|
||||
uc->uc_mcontext.gregs[10],
|
||||
uc->uc_mcontext.gregs[15],
|
||||
uc->uc_mcontext.gregs[0],
|
||||
uc->uc_mcontext.gregs[1],
|
||||
uc->uc_mcontext.gregs[2],
|
||||
uc->uc_mcontext.gregs[3],
|
||||
uc->uc_mcontext.gregs[4],
|
||||
uc->uc_mcontext.gregs[5],
|
||||
uc->uc_mcontext.gregs[6],
|
||||
uc->uc_mcontext.gregs[7],
|
||||
uc->uc_mcontext.gregs[16],
|
||||
uc->uc_mcontext.gregs[17],
|
||||
uc->uc_mcontext.gregs[18]
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext.gregs[15]);
|
||||
#endif
|
||||
@@ -622,12 +611,11 @@ void logRegisters(ucontext_t *uc) {
|
||||
void logStackTrace(ucontext_t *uc) {
|
||||
void *trace[100];
|
||||
int trace_size = 0, fd;
|
||||
int log_to_stdout = server.logfile[0] == '\0';
|
||||
|
||||
/* Open the log file in append mode. */
|
||||
fd = log_to_stdout ?
|
||||
STDOUT_FILENO :
|
||||
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
|
||||
fd = server.logfile ?
|
||||
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
|
||||
STDOUT_FILENO;
|
||||
if (fd == -1) return;
|
||||
|
||||
/* Generate the stack trace */
|
||||
@@ -641,7 +629,7 @@ void logStackTrace(ucontext_t *uc) {
|
||||
backtrace_symbols_fd(trace, trace_size, fd);
|
||||
|
||||
/* Cleanup */
|
||||
if (!log_to_stdout) close(fd);
|
||||
if (server.logfile) close(fd);
|
||||
}
|
||||
|
||||
/* Log information about the "current" client, that is, the client that is
|
||||
@@ -675,96 +663,13 @@ void logCurrentClient(void) {
|
||||
de = dictFind(cc->db->dict, key->ptr);
|
||||
if (de) {
|
||||
val = dictGetVal(de);
|
||||
redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", (char*)key->ptr);
|
||||
redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", key->ptr);
|
||||
redisLogObjectDebugInfo(val);
|
||||
}
|
||||
decrRefCount(key);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_PROC_MAPS)
|
||||
void memtest_non_destructive_invert(void *addr, size_t size);
|
||||
void memtest_non_destructive_swap(void *addr, size_t size);
|
||||
#define MEMTEST_MAX_REGIONS 128
|
||||
|
||||
int memtest_test_linux_anonymous_maps(void) {
|
||||
FILE *fp = fopen("/proc/self/maps","r");
|
||||
char line[1024];
|
||||
size_t start_addr, end_addr, size;
|
||||
size_t start_vect[MEMTEST_MAX_REGIONS];
|
||||
size_t size_vect[MEMTEST_MAX_REGIONS];
|
||||
int regions = 0, j;
|
||||
uint64_t crc1 = 0, crc2 = 0, crc3 = 0;
|
||||
|
||||
while(fgets(line,sizeof(line),fp) != NULL) {
|
||||
char *start, *end, *p = line;
|
||||
|
||||
start = p;
|
||||
p = strchr(p,'-');
|
||||
if (!p) continue;
|
||||
*p++ = '\0';
|
||||
end = p;
|
||||
p = strchr(p,' ');
|
||||
if (!p) continue;
|
||||
*p++ = '\0';
|
||||
if (strstr(p,"stack") ||
|
||||
strstr(p,"vdso") ||
|
||||
strstr(p,"vsyscall")) continue;
|
||||
if (!strstr(p,"00:00")) continue;
|
||||
if (!strstr(p,"rw")) continue;
|
||||
|
||||
start_addr = strtoul(start,NULL,16);
|
||||
end_addr = strtoul(end,NULL,16);
|
||||
size = end_addr-start_addr;
|
||||
|
||||
start_vect[regions] = start_addr;
|
||||
size_vect[regions] = size;
|
||||
printf("Testing %lx %lu\n", (unsigned long) start_vect[regions],
|
||||
(unsigned long) size_vect[regions]);
|
||||
regions++;
|
||||
}
|
||||
|
||||
/* Test all the regions as an unique sequential region.
|
||||
* 1) Take the CRC64 of the memory region. */
|
||||
for (j = 0; j < regions; j++) {
|
||||
crc1 = crc64(crc1,(void*)start_vect[j],size_vect[j]);
|
||||
}
|
||||
|
||||
/* 2) Invert bits, swap adjacent words, swap again, invert bits.
|
||||
* This is the error amplification step. */
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]);
|
||||
|
||||
/* 3) Take the CRC64 sum again. */
|
||||
for (j = 0; j < regions; j++)
|
||||
crc2 = crc64(crc2,(void*)start_vect[j],size_vect[j]);
|
||||
|
||||
/* 4) Swap + Swap again */
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
||||
for (j = 0; j < regions; j++)
|
||||
memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]);
|
||||
|
||||
/* 5) Take the CRC64 sum again. */
|
||||
for (j = 0; j < regions; j++)
|
||||
crc3 = crc64(crc3,(void*)start_vect[j],size_vect[j]);
|
||||
|
||||
/* NOTE: It is very important to close the file descriptor only now
|
||||
* because closing it before may result into unmapping of some memory
|
||||
* region that we are testing. */
|
||||
fclose(fp);
|
||||
|
||||
/* If the two CRC are not the same, we trapped a memory error. */
|
||||
return crc1 != crc2 || crc2 != crc3;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
|
||||
ucontext_t *uc = (ucontext_t*) secret;
|
||||
sds infostring, clients;
|
||||
@@ -800,19 +705,6 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
|
||||
/* Log dump of processor registers */
|
||||
logRegisters(uc);
|
||||
|
||||
#if defined(HAVE_PROC_MAPS)
|
||||
/* Test memory */
|
||||
redisLog(REDIS_WARNING, "--- FAST MEMORY TEST");
|
||||
bioKillThreads();
|
||||
if (memtest_test_linux_anonymous_maps()) {
|
||||
redisLog(REDIS_WARNING,
|
||||
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!");
|
||||
} else {
|
||||
redisLog(REDIS_WARNING,
|
||||
"Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.");
|
||||
}
|
||||
#endif
|
||||
|
||||
redisLog(REDIS_WARNING,
|
||||
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
|
||||
" Please report the crash opening an issue on github:\n\n"
|
||||
@@ -890,7 +782,7 @@ void watchdogScheduleSignal(int period) {
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
}
|
||||
|
||||
/* Enable the software watchdog with the specified period in milliseconds. */
|
||||
/* Enable the software watchdong with the specified period in milliseconds. */
|
||||
void enableWatchdog(int period) {
|
||||
int min_period;
|
||||
|
||||
|
||||
+2
-1
@@ -621,7 +621,8 @@ static int _dictExpandIfNeeded(dict *d)
|
||||
(dict_can_resize ||
|
||||
d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
|
||||
{
|
||||
return dictExpand(d, d->ht[0].used*2);
|
||||
return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
|
||||
d->ht[0].size : d->ht[0].used)*2);
|
||||
}
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
+2
-6
@@ -36,13 +36,9 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__OpenBSD__)
|
||||
#if defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#define _XOPEN_SOURCE 700
|
||||
/*
|
||||
* On NetBSD, _XOPEN_SOURCE undefines _NETBSD_SOURCE and
|
||||
* thus hides inet_aton etc.
|
||||
*/
|
||||
#elif !defined(__NetBSD__)
|
||||
#else
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
+3
-13
@@ -69,11 +69,6 @@ struct commandHelp {
|
||||
"Pop a value from a list, push it to another list and return it; or block until one is available",
|
||||
2,
|
||||
"2.2.0" },
|
||||
{ "CLIENT GETNAME",
|
||||
"-",
|
||||
"Get the current connection name",
|
||||
9,
|
||||
"2.6.9" },
|
||||
{ "CLIENT KILL",
|
||||
"ip:port",
|
||||
"Kill the connection of a client",
|
||||
@@ -84,11 +79,6 @@ struct commandHelp {
|
||||
"Get the list of client connections",
|
||||
9,
|
||||
"2.4.0" },
|
||||
{ "CLIENT SETNAME",
|
||||
"connection-name",
|
||||
"Set the current connection name",
|
||||
9,
|
||||
"2.6.9" },
|
||||
{ "CONFIG GET",
|
||||
"parameter",
|
||||
"Get the value of a configuration parameter",
|
||||
@@ -290,7 +280,7 @@ struct commandHelp {
|
||||
1,
|
||||
"2.6.0" },
|
||||
{ "INFO",
|
||||
"[section]",
|
||||
"-",
|
||||
"Get information and statistics about the server",
|
||||
9,
|
||||
"1.0.0" },
|
||||
@@ -535,7 +525,7 @@ struct commandHelp {
|
||||
8,
|
||||
"1.0.0" },
|
||||
{ "SET",
|
||||
"key value [EX seconds] [PX milliseconds] [NX|XX]",
|
||||
"key value",
|
||||
"Set the string value of a key",
|
||||
1,
|
||||
"1.0.0" },
|
||||
@@ -675,7 +665,7 @@ struct commandHelp {
|
||||
7,
|
||||
"2.2.0" },
|
||||
{ "ZADD",
|
||||
"key score member [score member ...]",
|
||||
"key score member [score] [member]",
|
||||
"Add one or more members to a sorted set, or update its score if it already exists",
|
||||
4,
|
||||
"1.2.0" },
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "config.h"
|
||||
|
||||
#if (ULONG_MAX == 4294967295UL)
|
||||
#define MEMTEST_32BIT
|
||||
@@ -239,32 +238,6 @@ void memtest_test(size_t megabytes, int passes) {
|
||||
}
|
||||
}
|
||||
|
||||
void memtest_non_destructive_invert(void *addr, size_t size) {
|
||||
volatile unsigned long *p = addr;
|
||||
size_t words = size / sizeof(unsigned long);
|
||||
size_t j;
|
||||
|
||||
/* Invert */
|
||||
for (j = 0; j < words; j++)
|
||||
p[j] = ~p[j];
|
||||
}
|
||||
|
||||
void memtest_non_destructive_swap(void *addr, size_t size) {
|
||||
volatile unsigned long *p = addr;
|
||||
size_t words = size / sizeof(unsigned long);
|
||||
size_t j;
|
||||
|
||||
/* Swap */
|
||||
for (j = 0; j < words; j += 2) {
|
||||
unsigned long a, b;
|
||||
|
||||
a = p[j];
|
||||
b = p[j+1];
|
||||
p[j] = b;
|
||||
p[j+1] = a;
|
||||
}
|
||||
}
|
||||
|
||||
void memtest(size_t megabytes, int passes) {
|
||||
if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
|
||||
ws.ws_col = 80;
|
||||
|
||||
+3
-2
@@ -139,7 +139,7 @@ void migrateCommand(redisClient *c) {
|
||||
return;
|
||||
if (getLongFromObjectOrReply(c,c->argv[4],&dbid,NULL) != REDIS_OK)
|
||||
return;
|
||||
if (timeout <= 0) timeout = 1;
|
||||
if (timeout <= 0) timeout = 1000;
|
||||
|
||||
/* Check if the key is here. If not we reply with success as there is
|
||||
* nothing to migrate (for instance the key expired in the meantime), but
|
||||
@@ -157,7 +157,8 @@ void migrateCommand(redisClient *c) {
|
||||
server.neterr);
|
||||
return;
|
||||
}
|
||||
if ((aeWait(fd,AE_WRITABLE,timeout*1000) & AE_WRITABLE) == 0) {
|
||||
if ((aeWait(fd,AE_WRITABLE,timeout) & AE_WRITABLE) == 0) {
|
||||
close(fd);
|
||||
addReplySds(c,sdsnew("-IOERR error or timeout connecting to the client\r\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#!/bin/sh
|
||||
GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1`
|
||||
GIT_DIRTY=`git diff --no-ext-diff 2> /dev/null | wc -l`
|
||||
BUILD_ID=`uname -n`"-"`date +%s`
|
||||
test -f release.h || touch release.h
|
||||
(cat release.h | grep SHA1 | grep $GIT_SHA1) && \
|
||||
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date
|
||||
echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h
|
||||
echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h
|
||||
echo "#define REDIS_BUILD_ID \"$BUILD_ID\"" >> release.h
|
||||
touch release.c # Force recompile of release.c
|
||||
|
||||
+62
-134
@@ -29,7 +29,6 @@
|
||||
|
||||
#include "redis.h"
|
||||
#include <sys/uio.h>
|
||||
#include <math.h>
|
||||
|
||||
static void setProtocolError(redisClient *c, int pos);
|
||||
|
||||
@@ -88,24 +87,21 @@ redisClient *createClient(int fd) {
|
||||
c->ctime = c->lastinteraction = server.unixtime;
|
||||
c->authenticated = 0;
|
||||
c->replstate = REDIS_REPL_NONE;
|
||||
c->reploff = 0;
|
||||
c->repl_ack_off = 0;
|
||||
c->repl_ack_time = 0;
|
||||
c->slave_listening_port = 0;
|
||||
c->reply = listCreate();
|
||||
c->reply_bytes = 0;
|
||||
c->obuf_soft_limit_reached_time = 0;
|
||||
listSetFreeMethod(c->reply,decrRefCountVoid);
|
||||
listSetFreeMethod(c->reply,decrRefCount);
|
||||
listSetDupMethod(c->reply,dupClientReplyValue);
|
||||
c->bpop.keys = dictCreate(&setDictType,NULL);
|
||||
c->bpop.timeout = 0;
|
||||
c->bpop.target = NULL;
|
||||
c->io_keys = listCreate();
|
||||
c->watched_keys = listCreate();
|
||||
listSetFreeMethod(c->io_keys,decrRefCountVoid);
|
||||
listSetFreeMethod(c->io_keys,decrRefCount);
|
||||
c->pubsub_channels = dictCreate(&setDictType,NULL);
|
||||
c->pubsub_patterns = listCreate();
|
||||
listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid);
|
||||
listSetFreeMethod(c->pubsub_patterns,decrRefCount);
|
||||
listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
|
||||
if (fd != -1) listAddNodeTail(server.clients,c);
|
||||
initClientMultiState(c);
|
||||
@@ -119,17 +115,15 @@ redisClient *createClient(int fd) {
|
||||
* returns REDIS_OK, and make sure to install the write handler in our event
|
||||
* loop so that when the socket is writable new data gets written.
|
||||
*
|
||||
* If the client should not receive new data, because it is a fake client,
|
||||
* a master, a slave not yet online, or because the setup of the write handler
|
||||
* failed, the function returns REDIS_ERR.
|
||||
* If the client should not receive new data, because it is a fake client
|
||||
* or a slave, or because the setup of the write handler failed, the function
|
||||
* returns REDIS_ERR.
|
||||
*
|
||||
* Typically gets called every time a reply is built, before adding more
|
||||
* data to the clients output buffers. If the function returns REDIS_ERR no
|
||||
* data should be appended to the output buffers. */
|
||||
int prepareClientToWrite(redisClient *c) {
|
||||
if (c->flags & REDIS_LUA_CLIENT) return REDIS_OK;
|
||||
if ((c->flags & REDIS_MASTER) &&
|
||||
!(c->flags & REDIS_MASTER_FORCE_REPLY)) return REDIS_ERR;
|
||||
if (c->fd <= 0) return REDIS_ERR; /* Fake client */
|
||||
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
|
||||
(c->replstate == REDIS_REPL_NONE ||
|
||||
@@ -416,15 +410,9 @@ void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
|
||||
void addReplyDouble(redisClient *c, double d) {
|
||||
char dbuf[128], sbuf[128];
|
||||
int dlen, slen;
|
||||
if (isinf(d)) {
|
||||
/* Libc in odd systems (Hi Solaris!) will format infinite in a
|
||||
* different way, so better to handle it in an explicit way. */
|
||||
addReplyBulkCString(c, d > 0 ? "inf" : "-inf");
|
||||
} else {
|
||||
dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
|
||||
slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
|
||||
addReplyString(c,sbuf,slen);
|
||||
}
|
||||
dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
|
||||
slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
|
||||
addReplyString(c,sbuf,slen);
|
||||
}
|
||||
|
||||
/* Add a long long as integer reply or bulk len / multi bulk count.
|
||||
@@ -559,12 +547,12 @@ static void acceptCommonHandler(int fd, int flags) {
|
||||
|
||||
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
int cport, cfd;
|
||||
char cip[REDIS_IP_STR_LEN];
|
||||
char cip[128];
|
||||
REDIS_NOTUSED(el);
|
||||
REDIS_NOTUSED(mask);
|
||||
REDIS_NOTUSED(privdata);
|
||||
|
||||
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
|
||||
cfd = anetTcpAccept(server.neterr, fd, cip, &cport);
|
||||
if (cfd == AE_ERR) {
|
||||
redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
|
||||
return;
|
||||
@@ -607,42 +595,12 @@ void disconnectSlaves(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called when the slave lose the connection with the
|
||||
* master into an unexpected way. */
|
||||
void replicationHandleMasterDisconnection(void) {
|
||||
server.master = NULL;
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
server.repl_down_since = server.unixtime;
|
||||
/* We lost connection with our master, force our slaves to resync
|
||||
* with us as well to load the new data set.
|
||||
*
|
||||
* If server.masterhost is NULL the user called SLAVEOF NO ONE so
|
||||
* slave resync is not needed. */
|
||||
if (server.masterhost != NULL) disconnectSlaves();
|
||||
}
|
||||
|
||||
void freeClient(redisClient *c) {
|
||||
listNode *ln;
|
||||
|
||||
/* If this is marked as current client unset it */
|
||||
if (server.current_client == c) server.current_client = NULL;
|
||||
|
||||
/* If it is our master that's beging disconnected we should make sure
|
||||
* to cache the state to try a partial resynchronization later.
|
||||
*
|
||||
* Note that before doing this we make sure that the client is not in
|
||||
* some unexpected state, by checking its flags. */
|
||||
if (server.master &&
|
||||
(c->flags & REDIS_MASTER) &&
|
||||
!(c->flags & (REDIS_CLOSE_AFTER_REPLY|
|
||||
REDIS_CLOSE_ASAP|
|
||||
REDIS_BLOCKED|
|
||||
REDIS_UNBLOCKED)))
|
||||
{
|
||||
replicationCacheMaster(c);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note that if the client we are freeing is blocked into a blocking
|
||||
* call, we have to set querybuf to NULL *before* to call
|
||||
* unblockClientWaitingData() to avoid processInputBuffer() will get
|
||||
@@ -662,21 +620,16 @@ void freeClient(redisClient *c) {
|
||||
pubsubUnsubscribeAllPatterns(c,0);
|
||||
dictRelease(c->pubsub_channels);
|
||||
listRelease(c->pubsub_patterns);
|
||||
/* Close socket, unregister events, and remove list of replies and
|
||||
* accumulated arguments. */
|
||||
if (c->fd != -1) {
|
||||
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
|
||||
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
|
||||
close(c->fd);
|
||||
}
|
||||
/* Obvious cleanup */
|
||||
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
|
||||
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
|
||||
listRelease(c->reply);
|
||||
freeClientArgv(c);
|
||||
close(c->fd);
|
||||
/* Remove from the list of clients */
|
||||
if (c->fd != -1) {
|
||||
ln = listSearchKey(server.clients,c);
|
||||
redisAssert(ln != NULL);
|
||||
listDelNode(server.clients,ln);
|
||||
}
|
||||
ln = listSearchKey(server.clients,c);
|
||||
redisAssert(ln != NULL);
|
||||
listDelNode(server.clients,ln);
|
||||
/* When client was just unblocked because of a blocking operation,
|
||||
* remove it from the list with unblocked clients. */
|
||||
if (c->flags & REDIS_UNBLOCKED) {
|
||||
@@ -694,16 +647,20 @@ void freeClient(redisClient *c) {
|
||||
ln = listSearchKey(l,c);
|
||||
redisAssert(ln != NULL);
|
||||
listDelNode(l,ln);
|
||||
/* We need to remember the time when we started to have zero
|
||||
* attached slaves, as after some time we'll free the replication
|
||||
* backlog. */
|
||||
if (c->flags & REDIS_SLAVE && listLength(server.slaves) == 0)
|
||||
server.repl_no_slaves_since = server.unixtime;
|
||||
refreshGoodSlavesCount();
|
||||
}
|
||||
|
||||
/* Case 2: we lost the connection with the master. */
|
||||
if (c->flags & REDIS_MASTER) replicationHandleMasterDisconnection();
|
||||
if (c->flags & REDIS_MASTER) {
|
||||
server.master = NULL;
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
server.repl_down_since = server.unixtime;
|
||||
/* We lost connection with our master, force our slaves to resync
|
||||
* with us as well to load the new data set.
|
||||
*
|
||||
* If server.masterhost is NULL the user called SLAVEOF NO ONE so
|
||||
* slave resync is not needed. */
|
||||
if (server.masterhost != NULL) disconnectSlaves();
|
||||
}
|
||||
|
||||
/* If this client was scheduled for async freeing we need to remove it
|
||||
* from the queue. */
|
||||
@@ -751,8 +708,13 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
|
||||
while(c->bufpos > 0 || listLength(c->reply)) {
|
||||
if (c->bufpos > 0) {
|
||||
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
|
||||
if (nwritten <= 0) break;
|
||||
if (c->flags & REDIS_MASTER) {
|
||||
/* Don't reply to a master */
|
||||
nwritten = c->bufpos - c->sentlen;
|
||||
} else {
|
||||
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
|
||||
if (nwritten <= 0) break;
|
||||
}
|
||||
c->sentlen += nwritten;
|
||||
totwritten += nwritten;
|
||||
|
||||
@@ -772,8 +734,13 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
|
||||
if (nwritten <= 0) break;
|
||||
if (c->flags & REDIS_MASTER) {
|
||||
/* Don't reply to a master */
|
||||
nwritten = objlen - c->sentlen;
|
||||
} else {
|
||||
nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
|
||||
if (nwritten <= 0) break;
|
||||
}
|
||||
c->sentlen += nwritten;
|
||||
totwritten += nwritten;
|
||||
|
||||
@@ -829,7 +796,7 @@ void resetClient(redisClient *c) {
|
||||
int processInlineBuffer(redisClient *c) {
|
||||
char *newline = strstr(c->querybuf,"\r\n");
|
||||
int argc, j;
|
||||
sds *argv, aux;
|
||||
sds *argv;
|
||||
size_t querylen;
|
||||
|
||||
/* Nothing to do without a \r\n */
|
||||
@@ -843,12 +810,10 @@ int processInlineBuffer(redisClient *c) {
|
||||
|
||||
/* Split the input buffer up to the \r\n */
|
||||
querylen = newline-(c->querybuf);
|
||||
aux = sdsnewlen(c->querybuf,querylen);
|
||||
argv = sdssplitargs(aux,&argc);
|
||||
sdsfree(aux);
|
||||
argv = sdssplitlen(c->querybuf,querylen," ",1,&argc);
|
||||
|
||||
/* Leave data after the first line of the query in the buffer */
|
||||
sdsrange(c->querybuf,querylen+2,-1);
|
||||
c->querybuf = sdsrange(c->querybuf,querylen+2,-1);
|
||||
|
||||
/* Setup argv array on client structure */
|
||||
if (c->argv) zfree(c->argv);
|
||||
@@ -877,7 +842,7 @@ static void setProtocolError(redisClient *c, int pos) {
|
||||
sdsfree(client);
|
||||
}
|
||||
c->flags |= REDIS_CLOSE_AFTER_REPLY;
|
||||
sdsrange(c->querybuf,pos,-1);
|
||||
c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
}
|
||||
|
||||
int processMultibulkBuffer(redisClient *c) {
|
||||
@@ -915,7 +880,7 @@ int processMultibulkBuffer(redisClient *c) {
|
||||
|
||||
pos = (newline-c->querybuf)+2;
|
||||
if (ll <= 0) {
|
||||
sdsrange(c->querybuf,pos,-1);
|
||||
c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -964,7 +929,7 @@ int processMultibulkBuffer(redisClient *c) {
|
||||
* try to make it likely that it will start at c->querybuf
|
||||
* boundary so that we can optimized object creation
|
||||
* avoiding a large copy of data. */
|
||||
sdsrange(c->querybuf,pos,-1);
|
||||
c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
pos = 0;
|
||||
/* Hint the sds library about the amount of bytes this string is
|
||||
* going to contain. */
|
||||
@@ -1003,7 +968,7 @@ int processMultibulkBuffer(redisClient *c) {
|
||||
}
|
||||
|
||||
/* Trim to pos */
|
||||
if (pos) sdsrange(c->querybuf,pos,-1);
|
||||
if (pos) c->querybuf = sdsrange(c->querybuf,pos,-1);
|
||||
|
||||
/* We're done when c->multibulk == 0 */
|
||||
if (c->multibulklen == 0) return REDIS_OK;
|
||||
@@ -1094,7 +1059,6 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
if (nread) {
|
||||
sdsIncrLen(c->querybuf,nread);
|
||||
c->lastinteraction = server.unixtime;
|
||||
if (c->flags & REDIS_MASTER) c->reploff += nread;
|
||||
} else {
|
||||
server.current_client = NULL;
|
||||
return;
|
||||
@@ -1131,52 +1095,14 @@ void getClientsMaxBuffers(unsigned long *longest_output_list,
|
||||
*biggest_input_buffer = bib;
|
||||
}
|
||||
|
||||
/* This is an helper function for getClientPeerId().
|
||||
* It writes the specified ip/port to "peerid" as a null termiated string
|
||||
* in the form ip:port if ip does not contain ":" itself, otherwise
|
||||
* [ip]:port format is used (for IPv6 addresses basically). */
|
||||
void formatPeerId(char *peerid, size_t peerid_len, char *ip, int port) {
|
||||
if (strchr(ip,':'))
|
||||
snprintf(peerid,peerid_len,"[%s]:%d",ip,port);
|
||||
else
|
||||
snprintf(peerid,peerid_len,"%s:%d",ip,port);
|
||||
}
|
||||
|
||||
/* A Redis "Peer ID" is a colon separated ip:port pair.
|
||||
* For IPv4 it's in the form x.y.z.k:pork, example: "127.0.0.1:1234".
|
||||
* For IPv6 addresses we use [] around the IP part, like in "[::1]:1234".
|
||||
* For Unix socekts we use path:0, like in "/tmp/redis:0".
|
||||
*
|
||||
* A Peer ID always fits inside a buffer of REDIS_PEER_ID_LEN bytes, including
|
||||
* the null term.
|
||||
*
|
||||
* The function returns REDIS_OK on succcess, and REDIS_ERR on failure.
|
||||
*
|
||||
* On failure the function still populates 'peerid' with the "?:0" string
|
||||
* in case you want to relax error checking or need to display something
|
||||
* anyway (see anetPeerToString implementation for more info). */
|
||||
int getClientPeerId(redisClient *client, char *peerid, size_t peerid_len) {
|
||||
char ip[REDIS_IP_STR_LEN];
|
||||
int port;
|
||||
|
||||
if (client->flags & REDIS_UNIX_SOCKET) {
|
||||
/* Unix socket client. */
|
||||
snprintf(peerid,peerid_len,"%s:0",server.unixsocket);
|
||||
return REDIS_OK;
|
||||
} else {
|
||||
/* TCP client. */
|
||||
int retval = anetPeerToString(client->fd,ip,sizeof(ip),&port);
|
||||
formatPeerId(peerid,peerid_len,ip,port);
|
||||
return (retval == -1) ? REDIS_ERR : REDIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn a Redis client into an sds string representing its state. */
|
||||
sds getClientInfoString(redisClient *client) {
|
||||
char peerid[REDIS_PEER_ID_LEN], flags[16], events[3], *p;
|
||||
char ip[32], flags[16], events[3], *p;
|
||||
int port = 0; /* initialized to zero for the unix socket case. */
|
||||
int emask;
|
||||
|
||||
getClientPeerId(client,peerid,sizeof(peerid));
|
||||
if (!(client->flags & REDIS_UNIX_SOCKET))
|
||||
anetPeerToString(client->fd,ip,&port);
|
||||
p = flags;
|
||||
if (client->flags & REDIS_SLAVE) {
|
||||
if (client->flags & REDIS_MONITOR)
|
||||
@@ -1201,8 +1127,9 @@ sds getClientInfoString(redisClient *client) {
|
||||
if (emask & AE_WRITABLE) *p++ = 'w';
|
||||
*p = '\0';
|
||||
return sdscatprintf(sdsempty(),
|
||||
"addr=%s 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",
|
||||
peerid,
|
||||
"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,
|
||||
client->name ? (char*)client->name->ptr : "",
|
||||
(long)(server.unixtime - client->ctime),
|
||||
@@ -1252,12 +1179,13 @@ void clientCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"kill") && c->argc == 3) {
|
||||
listRewind(server.clients,&li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
char peerid[REDIS_PEER_ID_LEN];
|
||||
char ip[32], addr[64];
|
||||
int port;
|
||||
|
||||
client = listNodeValue(ln);
|
||||
if (getClientPeerId(client,peerid,sizeof(peerid)) == REDIS_ERR)
|
||||
continue;
|
||||
if (strcmp(peerid,c->argv[2]->ptr) == 0) {
|
||||
if (anetPeerToString(client->fd,ip,&port) == -1) continue;
|
||||
snprintf(addr,sizeof(addr),"%s:%d",ip,port);
|
||||
if (strcmp(addr,c->argv[2]->ptr) == 0) {
|
||||
addReply(c,shared.ok);
|
||||
if (c == client) {
|
||||
client->flags |= REDIS_CLOSE_AFTER_REPLY;
|
||||
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "redis.h"
|
||||
|
||||
/* This file implements keyspace events notification via Pub/Sub ad
|
||||
* described at http://redis.io/topics/keyspace-events. */
|
||||
|
||||
/* Turn a string representing notification classes into an integer
|
||||
* representing notification classes flags xored.
|
||||
*
|
||||
* The function returns -1 if the input contains characters not mapping to
|
||||
* any class. */
|
||||
int keyspaceEventsStringToFlags(char *classes) {
|
||||
char *p = classes;
|
||||
int c, flags = 0;
|
||||
|
||||
while((c = *p++) != '\0') {
|
||||
switch(c) {
|
||||
case 'A': flags |= REDIS_NOTIFY_ALL; break;
|
||||
case 'g': flags |= REDIS_NOTIFY_GENERIC; break;
|
||||
case '$': flags |= REDIS_NOTIFY_STRING; break;
|
||||
case 'l': flags |= REDIS_NOTIFY_LIST; break;
|
||||
case 's': flags |= REDIS_NOTIFY_SET; break;
|
||||
case 'h': flags |= REDIS_NOTIFY_HASH; break;
|
||||
case 'z': flags |= REDIS_NOTIFY_ZSET; break;
|
||||
case 'x': flags |= REDIS_NOTIFY_EXPIRED; break;
|
||||
case 'e': flags |= REDIS_NOTIFY_EVICTED; break;
|
||||
case 'K': flags |= REDIS_NOTIFY_KEYSPACE; break;
|
||||
case 'E': flags |= REDIS_NOTIFY_KEYEVENT; break;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* This function does exactly the revese of the function above: it gets
|
||||
* as input an integer with the xored flags and returns a string representing
|
||||
* the selected classes. The string returned is an sds string that needs to
|
||||
* be released with sdsfree(). */
|
||||
sds keyspaceEventsFlagsToString(int flags) {
|
||||
sds res;
|
||||
|
||||
if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL)
|
||||
return sdsnew("A");
|
||||
res = sdsempty();
|
||||
if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,"g",1);
|
||||
if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,"$",1);
|
||||
if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,"l",1);
|
||||
if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,"s",1);
|
||||
if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,"h",1);
|
||||
if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,"z",1);
|
||||
if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
|
||||
if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
|
||||
if (flags & REDIS_NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
|
||||
if (flags & REDIS_NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* The API provided to the rest of the Redis core is a simple function:
|
||||
*
|
||||
* notifyKeyspaceEvent(char *event, robj *key, int dbid);
|
||||
*
|
||||
* 'event' is a C string representing the event name.
|
||||
* 'key' is a Redis object representing the key name.
|
||||
* 'dbid' is the database ID where the key lives. */
|
||||
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {
|
||||
sds chan;
|
||||
robj *chanobj, *eventobj;
|
||||
int len = -1;
|
||||
char buf[24];
|
||||
|
||||
/* If notifications for this class of events are off, return ASAP. */
|
||||
if (!(server.notify_keyspace_events & type)) return;
|
||||
|
||||
eventobj = createStringObject(event,strlen(event));
|
||||
|
||||
/* __keyspace@<db>__:<key> <event> notifications. */
|
||||
if (server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE) {
|
||||
chan = sdsnewlen("__keyspace@",11);
|
||||
len = ll2string(buf,sizeof(buf),dbid);
|
||||
chan = sdscatlen(chan, buf, len);
|
||||
chan = sdscatlen(chan, "__:", 3);
|
||||
chan = sdscatsds(chan, key->ptr);
|
||||
chanobj = createObject(REDIS_STRING, chan);
|
||||
pubsubPublishMessage(chanobj, eventobj);
|
||||
decrRefCount(chanobj);
|
||||
}
|
||||
|
||||
/* __keyevente@<db>__:<event> <key> notifications. */
|
||||
if (server.notify_keyspace_events & REDIS_NOTIFY_KEYEVENT) {
|
||||
chan = sdsnewlen("__keyevent@",11);
|
||||
if (len == -1) len = ll2string(buf,sizeof(buf),dbid);
|
||||
chan = sdscatlen(chan, buf, len);
|
||||
chan = sdscatlen(chan, "__:", 3);
|
||||
chan = sdscatsds(chan, eventobj->ptr);
|
||||
chanobj = createObject(REDIS_STRING, chan);
|
||||
pubsubPublishMessage(chanobj, key);
|
||||
decrRefCount(chanobj);
|
||||
}
|
||||
decrRefCount(eventobj);
|
||||
}
|
||||
+17
-48
@@ -97,7 +97,7 @@ robj *dupStringObject(robj *o) {
|
||||
robj *createListObject(void) {
|
||||
list *l = listCreate();
|
||||
robj *o = createObject(REDIS_LIST,l);
|
||||
listSetFreeMethod(l,decrRefCountVoid);
|
||||
listSetFreeMethod(l,decrRefCount);
|
||||
o->encoding = REDIS_ENCODING_LINKEDLIST;
|
||||
return o;
|
||||
}
|
||||
@@ -215,7 +215,9 @@ void incrRefCount(robj *o) {
|
||||
o->refcount++;
|
||||
}
|
||||
|
||||
void decrRefCount(robj *o) {
|
||||
void decrRefCount(void *obj) {
|
||||
robj *o = obj;
|
||||
|
||||
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
|
||||
if (o->refcount == 1) {
|
||||
switch(o->type) {
|
||||
@@ -232,13 +234,6 @@ void decrRefCount(robj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
/* This variant of decrRefCount() gets its argument as void, and is useful
|
||||
* as free method in data structures that expect a 'void free_object(void*)'
|
||||
* prototype for the free method. */
|
||||
void decrRefCountVoid(void *o) {
|
||||
decrRefCount(o);
|
||||
}
|
||||
|
||||
/* This function set the ref count to zero without freeing the object.
|
||||
* It is useful in order to pass a new object to functions incrementing
|
||||
* the ref count of the received object. Example:
|
||||
@@ -332,57 +327,35 @@ robj *getDecodedObject(robj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare two string objects via strcmp() or strcoll() depending on flags.
|
||||
/* Compare two string objects via strcmp() or alike.
|
||||
* Note that the objects may be integer-encoded. In such a case we
|
||||
* use ll2string() to get a string representation of the numbers on the stack
|
||||
* and compare the strings, it's much faster than calling getDecodedObject().
|
||||
*
|
||||
* Important note: when REDIS_COMPARE_BINARY is used a binary-safe comparison
|
||||
* is used. */
|
||||
|
||||
#define REDIS_COMPARE_BINARY (1<<0)
|
||||
#define REDIS_COMPARE_COLL (1<<1)
|
||||
|
||||
int compareStringObjectsWithFlags(robj *a, robj *b, int flags) {
|
||||
* Important note: if objects are not integer encoded, but binary-safe strings,
|
||||
* sdscmp() from sds.c will apply memcmp() so this function ca be considered
|
||||
* binary safe. */
|
||||
int compareStringObjects(robj *a, robj *b) {
|
||||
redisAssertWithInfo(NULL,a,a->type == REDIS_STRING && b->type == REDIS_STRING);
|
||||
char bufa[128], bufb[128], *astr, *bstr;
|
||||
size_t alen, blen, minlen;
|
||||
int bothsds = 1;
|
||||
|
||||
if (a == b) return 0;
|
||||
if (a->encoding != REDIS_ENCODING_RAW) {
|
||||
alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);
|
||||
ll2string(bufa,sizeof(bufa),(long) a->ptr);
|
||||
astr = bufa;
|
||||
bothsds = 0;
|
||||
} else {
|
||||
astr = a->ptr;
|
||||
alen = sdslen(astr);
|
||||
}
|
||||
if (b->encoding != REDIS_ENCODING_RAW) {
|
||||
blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
|
||||
ll2string(bufb,sizeof(bufb),(long) b->ptr);
|
||||
bstr = bufb;
|
||||
bothsds = 0;
|
||||
} else {
|
||||
bstr = b->ptr;
|
||||
blen = sdslen(bstr);
|
||||
}
|
||||
if (flags & REDIS_COMPARE_COLL) {
|
||||
return strcoll(astr,bstr);
|
||||
} else {
|
||||
int cmp;
|
||||
|
||||
minlen = (alen < blen) ? alen : blen;
|
||||
cmp = memcmp(astr,bstr,minlen);
|
||||
if (cmp == 0) return alen-blen;
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wrapper for compareStringObjectsWithFlags() using binary comparison. */
|
||||
int compareStringObjects(robj *a, robj *b) {
|
||||
return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_BINARY);
|
||||
}
|
||||
|
||||
/* Wrapper for compareStringObjectsWithFlags() using collation. */
|
||||
int collateStringObjects(robj *a, robj *b) {
|
||||
return compareStringObjectsWithFlags(a,b,REDIS_COMPARE_COLL);
|
||||
return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
|
||||
}
|
||||
|
||||
/* Equal string objects return 1 if the two objects are the same from the
|
||||
@@ -419,12 +392,8 @@ int getDoubleFromObject(robj *o, double *target) {
|
||||
if (o->encoding == REDIS_ENCODING_RAW) {
|
||||
errno = 0;
|
||||
value = strtod(o->ptr, &eptr);
|
||||
if (isspace(((char*)o->ptr)[0]) ||
|
||||
eptr[0] != '\0' ||
|
||||
(errno == ERANGE &&
|
||||
(value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
|
||||
errno == EINVAL ||
|
||||
isnan(value))
|
||||
if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||
|
||||
errno == ERANGE || isnan(value))
|
||||
return REDIS_ERR;
|
||||
} else if (o->encoding == REDIS_ENCODING_INT) {
|
||||
value = (long)o->ptr;
|
||||
|
||||
+12
-1
@@ -102,9 +102,10 @@ _pqsort(void *a, size_t n, size_t es,
|
||||
{
|
||||
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||
size_t d, r;
|
||||
int swaptype, cmp_result;
|
||||
int swaptype, swap_cnt, cmp_result;
|
||||
|
||||
loop: SWAPINIT(a, es);
|
||||
swap_cnt = 0;
|
||||
if (n < 7) {
|
||||
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
@@ -131,6 +132,7 @@ loop: SWAPINIT(a, es);
|
||||
for (;;) {
|
||||
while (pb <= pc && (cmp_result = cmp(pb, a)) <= 0) {
|
||||
if (cmp_result == 0) {
|
||||
swap_cnt = 1;
|
||||
swap(pa, pb);
|
||||
pa += es;
|
||||
}
|
||||
@@ -138,6 +140,7 @@ loop: SWAPINIT(a, es);
|
||||
}
|
||||
while (pb <= pc && (cmp_result = cmp(pc, a)) >= 0) {
|
||||
if (cmp_result == 0) {
|
||||
swap_cnt = 1;
|
||||
swap(pc, pd);
|
||||
pd -= es;
|
||||
}
|
||||
@@ -146,9 +149,17 @@ loop: SWAPINIT(a, es);
|
||||
if (pb > pc)
|
||||
break;
|
||||
swap(pb, pc);
|
||||
swap_cnt = 1;
|
||||
pb += es;
|
||||
pc -= es;
|
||||
}
|
||||
if (swap_cnt == 0) { /* Switch to insertion sort */
|
||||
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||
pl -= es)
|
||||
swap(pl, pl - es);
|
||||
return;
|
||||
}
|
||||
|
||||
pn = (char *) a + n * es;
|
||||
r = min(pa - (char *) a, pb - pa);
|
||||
|
||||
@@ -306,53 +306,5 @@ void punsubscribeCommand(redisClient *c) {
|
||||
|
||||
void publishCommand(redisClient *c) {
|
||||
int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
|
||||
forceCommandPropagation(c,REDIS_PROPAGATE_REPL);
|
||||
addReplyLongLong(c,receivers);
|
||||
}
|
||||
|
||||
/* PUBSUB command for Pub/Sub introspection. */
|
||||
void pubsubCommand(redisClient *c) {
|
||||
if (!strcasecmp(c->argv[1]->ptr,"channels") &&
|
||||
(c->argc == 2 || c->argc ==3))
|
||||
{
|
||||
/* PUBSUB CHANNELS [<pattern>] */
|
||||
sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr;
|
||||
dictIterator *di = dictGetIterator(server.pubsub_channels);
|
||||
dictEntry *de;
|
||||
long mblen = 0;
|
||||
void *replylen;
|
||||
|
||||
replylen = addDeferredMultiBulkLength(c);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *cobj = dictGetKey(de);
|
||||
sds channel = cobj->ptr;
|
||||
|
||||
if (!pat || stringmatchlen(pat, sdslen(pat),
|
||||
channel, sdslen(channel),0))
|
||||
{
|
||||
addReplyBulk(c,cobj);
|
||||
mblen++;
|
||||
}
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
setDeferredMultiBulkLength(c,replylen,mblen);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) {
|
||||
/* PUBSUB NUMSUB [Channel_1 ... Channel_N] */
|
||||
int j;
|
||||
|
||||
addReplyMultiBulkLen(c,(c->argc-2)*2);
|
||||
for (j = 2; j < c->argc; j++) {
|
||||
list *l = dictFetchValue(server.pubsub_channels,c->argv[j]);
|
||||
|
||||
addReplyBulk(c,c->argv[j]);
|
||||
addReplyBulkLongLong(c,l ? listLength(l) : 0);
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) {
|
||||
/* PUBSUB NUMPAT */
|
||||
addReplyLongLong(c,listLength(server.pubsub_patterns));
|
||||
} else {
|
||||
addReplyErrorFormat(c,
|
||||
"Unknown PUBSUB subcommand or wrong number of arguments for '%s'",
|
||||
(char*)c->argv[1]->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,15 +722,14 @@ 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) {
|
||||
int retval;
|
||||
|
||||
/* Child */
|
||||
closeListeningSockets(0);
|
||||
redisSetProcTitle("redis-rdb-bgsave");
|
||||
if (server.ipfd > 0) close(server.ipfd);
|
||||
if (server.sofd > 0) close(server.sofd);
|
||||
retval = rdbSave(filename);
|
||||
if (retval == REDIS_OK) {
|
||||
size_t private_dirty = zmalloc_get_private_dirty();
|
||||
@@ -773,6 +772,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
|
||||
size_t len;
|
||||
unsigned int i;
|
||||
|
||||
redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",rdbtype,rioTell(rdb));
|
||||
if (rdbtype == REDIS_RDB_TYPE_STRING) {
|
||||
/* Read string value */
|
||||
if ((o = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;
|
||||
@@ -1057,32 +1057,21 @@ void stopLoading(void) {
|
||||
server.loading = 0;
|
||||
}
|
||||
|
||||
/* Track loading progress in order to serve client's from time to time
|
||||
and if needed calculate rdb checksum */
|
||||
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
|
||||
if (server.rdb_checksum)
|
||||
rioGenericUpdateChecksum(r, buf, len);
|
||||
if (server.loading_process_events_interval_bytes &&
|
||||
(r->processed_bytes + len)/server.loading_process_events_interval_bytes > r->processed_bytes/server.loading_process_events_interval_bytes) {
|
||||
loadingProgress(r->processed_bytes);
|
||||
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
int rdbLoad(char *filename) {
|
||||
uint32_t dbid;
|
||||
int type, rdbver;
|
||||
redisDb *db = server.db+0;
|
||||
char buf[1024];
|
||||
long long expiretime, now = mstime();
|
||||
long loops = 0;
|
||||
FILE *fp;
|
||||
rio rdb;
|
||||
|
||||
if ((fp = fopen(filename,"r")) == NULL) return REDIS_ERR;
|
||||
|
||||
rioInitWithFile(&rdb,fp);
|
||||
rdb.update_cksum = rdbLoadProgressCallback;
|
||||
rdb.max_processing_chunk = server.loading_process_events_interval_bytes;
|
||||
if (server.rdb_checksum)
|
||||
rdb.update_cksum = rioGenericUpdateChecksum;
|
||||
if (rioRead(&rdb,buf,9) == 0) goto eoferr;
|
||||
buf[9] = '\0';
|
||||
if (memcmp(buf,"REDIS",5) != 0) {
|
||||
@@ -1104,6 +1093,12 @@ int rdbLoad(char *filename) {
|
||||
robj *key, *val;
|
||||
expiretime = -1;
|
||||
|
||||
/* Serve the clients from time to time */
|
||||
if (!(loops++ % 1000)) {
|
||||
loadingProgress(rioTell(&rdb));
|
||||
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
|
||||
}
|
||||
|
||||
/* Read type. */
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
if (type == REDIS_RDB_OPCODE_EXPIRETIME) {
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
@@ -511,7 +510,6 @@ int main(int argc, const char **argv) {
|
||||
|
||||
client c;
|
||||
|
||||
srandom(time(NULL));
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include "lzf.h"
|
||||
#include "crc64.h"
|
||||
|
||||
/* Object types */
|
||||
#define REDIS_STRING 0
|
||||
@@ -141,6 +140,9 @@ static double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
||||
/* store string types for output */
|
||||
static char types[256][16];
|
||||
|
||||
/* Prototypes */
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
/* Return true if 't' is a valid object type. */
|
||||
int checkType(unsigned char t) {
|
||||
/* In case a new object type is added, update the following
|
||||
|
||||
+27
-84
@@ -58,7 +58,6 @@
|
||||
#define OUTPUT_RAW 1
|
||||
#define OUTPUT_CSV 2
|
||||
#define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
#define REDIS_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
|
||||
|
||||
static redisContext *context;
|
||||
static struct config {
|
||||
@@ -73,12 +72,10 @@ 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 pipe_timeout;
|
||||
int getrdb_mode;
|
||||
int stat_mode;
|
||||
char *rdb_filename;
|
||||
@@ -116,8 +113,7 @@ static void cliRefreshPrompt(void) {
|
||||
len = snprintf(config.prompt,sizeof(config.prompt),"redis %s",
|
||||
config.hostsocket);
|
||||
else
|
||||
len = snprintf(config.prompt,sizeof(config.prompt),
|
||||
strchr(config.hostip,':') ? "[%s]:%d" : "%s:%d",
|
||||
len = snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d",
|
||||
config.hostip, config.hostport);
|
||||
/* Add [dbnum] if needed */
|
||||
if (config.dbnum != 0)
|
||||
@@ -704,9 +700,6 @@ 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")) {
|
||||
@@ -716,8 +709,6 @@ static int parseOptions(int argc, char **argv) {
|
||||
config.rdb_filename = argv[++i];
|
||||
} else if (!strcmp(argv[i],"--pipe")) {
|
||||
config.pipe_mode = 1;
|
||||
} else if (!strcmp(argv[i],"--pipe-timeout") && !lastarg) {
|
||||
config.pipe_timeout = atoi(argv[++i]);
|
||||
} else if (!strcmp(argv[i],"--bigkeys")) {
|
||||
config.bigkeys = 1;
|
||||
} else if (!strcmp(argv[i],"--eval") && !lastarg) {
|
||||
@@ -733,15 +724,7 @@ static int parseOptions(int argc, char **argv) {
|
||||
sdsfree(version);
|
||||
exit(0);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
@@ -770,32 +753,26 @@ 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\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"
|
||||
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data\n"
|
||||
" no reply is received within <n> seconds.\n"
|
||||
" Default timeout: %d. Use 0 to wait forever.\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 not a tty)\n"
|
||||
" --latency Enter a special mode continuously sampling latency\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"
|
||||
@@ -808,7 +785,7 @@ static void usage() {
|
||||
"When no command is given, redis-cli starts in interactive mode.\n"
|
||||
"Type \"help\" in interactive mode for information on available commands.\n"
|
||||
"\n",
|
||||
version, REDIS_DEFAULT_PIPE_TIMEOUT);
|
||||
version);
|
||||
sdsfree(version);
|
||||
exit(1);
|
||||
}
|
||||
@@ -966,16 +943,10 @@ 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) {
|
||||
@@ -1000,13 +971,7 @@ static void latencyMode(void) {
|
||||
printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)",
|
||||
min, max, avg, count);
|
||||
fflush(stdout);
|
||||
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);
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1126,7 +1091,6 @@ static void pipeMode(void) {
|
||||
int eof = 0; /* True once we consumed all the standard input. */
|
||||
int done = 0;
|
||||
char magic[20]; /* Special reply we recognize. */
|
||||
time_t last_read_time = time(NULL);
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
@@ -1157,10 +1121,7 @@ static void pipeMode(void) {
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (nread > 0) {
|
||||
redisReaderFeed(reader,ibuf,nread);
|
||||
last_read_time = time(NULL);
|
||||
}
|
||||
if (nread > 0) redisReaderFeed(reader,ibuf,nread);
|
||||
} while(nread > 0);
|
||||
|
||||
/* Consume replies. */
|
||||
@@ -1215,12 +1176,8 @@ static void pipeMode(void) {
|
||||
ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));
|
||||
|
||||
if (nread == 0) {
|
||||
/* The ECHO sequence starts with a "\r\n" so that if there
|
||||
* is garbage in the protocol we read from stdin, the ECHO
|
||||
* will likely still be properly formatted.
|
||||
* CRLF is ignored by Redis, so it has no effects. */
|
||||
char echo[] =
|
||||
"\r\n*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n";
|
||||
"*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n";
|
||||
int j;
|
||||
|
||||
eof = 1;
|
||||
@@ -1229,7 +1186,7 @@ static void pipeMode(void) {
|
||||
* to make sure everything was read from the server. */
|
||||
for (j = 0; j < 20; j++)
|
||||
magic[j] = rand() & 0xff;
|
||||
memcpy(echo+21,magic,20);
|
||||
memcpy(echo+19,magic,20);
|
||||
memcpy(obuf,echo,sizeof(echo)-1);
|
||||
obuf_len = sizeof(echo)-1;
|
||||
obuf_pos = 0;
|
||||
@@ -1246,18 +1203,6 @@ static void pipeMode(void) {
|
||||
if (obuf_len == 0 && eof) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle timeout, that is, we reached EOF, and we are not getting
|
||||
* replies from the server for a few seconds, nor the final ECHO is
|
||||
* received. */
|
||||
if (eof && config.pipe_timeout > 0 &&
|
||||
time(NULL)-last_read_time > config.pipe_timeout)
|
||||
{
|
||||
fprintf(stderr,"No replies for %d seconds: exiting.\n",
|
||||
config.pipe_timeout);
|
||||
errors++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
redisReaderFree(reader);
|
||||
printf("errors: %lld, replies: %lld\n", errors, replies);
|
||||
@@ -1507,13 +1452,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.pipe_timeout = REDIS_DEFAULT_PIPE_TIMEOUT;
|
||||
config.bigkeys = 0;
|
||||
config.stdinarg = 0;
|
||||
config.auth = NULL;
|
||||
|
||||
+108
-306
@@ -49,7 +49,6 @@
|
||||
#include <math.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <locale.h>
|
||||
|
||||
/* Our shared "common" objects */
|
||||
|
||||
@@ -214,15 +213,14 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"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,"arl",0,NULL,0,0,0,0,0},
|
||||
{"shutdown",shutdownCommand,-1,"ar",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},
|
||||
{"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
|
||||
{"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
|
||||
{"psync",syncCommand,3,"ars",0,NULL,0,0,0,0,0},
|
||||
{"replconf",replconfCommand,-1,"arslt",0,NULL,0,0,0,0,0},
|
||||
{"replconf",replconfCommand,-1,"ars",0,NULL,0,0,0,0,0},
|
||||
{"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
|
||||
{"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
|
||||
{"sort",sortCommand,-2,"wm",0,NULL,1,1,1,0,0},
|
||||
@@ -238,8 +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,"pltr",0,NULL,0,0,0,0,0},
|
||||
{"pubsub",pubsubCommand,-2,"pltrR",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},
|
||||
@@ -266,12 +263,11 @@ void redisLogRaw(int level, const char *msg) {
|
||||
FILE *fp;
|
||||
char buf[64];
|
||||
int rawmode = (level & REDIS_LOG_RAW);
|
||||
int log_to_stdout = server.logfile[0] == '\0';
|
||||
|
||||
level &= 0xff; /* clear flags */
|
||||
if (level < server.verbosity) return;
|
||||
|
||||
fp = log_to_stdout ? stdout : fopen(server.logfile,"a");
|
||||
fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a");
|
||||
if (!fp) return;
|
||||
|
||||
if (rawmode) {
|
||||
@@ -287,7 +283,8 @@ void redisLogRaw(int level, const char *msg) {
|
||||
}
|
||||
fflush(fp);
|
||||
|
||||
if (!log_to_stdout) fclose(fp);
|
||||
if (server.logfile) fclose(fp);
|
||||
|
||||
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
|
||||
}
|
||||
|
||||
@@ -315,13 +312,13 @@ void redisLog(int level, const char *fmt, ...) {
|
||||
* where we need printf-alike features are served by redisLog(). */
|
||||
void redisLogFromHandler(int level, const char *msg) {
|
||||
int fd;
|
||||
int log_to_stdout = server.logfile[0] == '\0';
|
||||
char buf[64];
|
||||
|
||||
if ((level&0xff) < server.verbosity || (log_to_stdout && server.daemonize))
|
||||
return;
|
||||
fd = log_to_stdout ? STDOUT_FILENO :
|
||||
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
|
||||
if ((level&0xff) < server.verbosity ||
|
||||
(server.logfile == NULL && server.daemonize)) return;
|
||||
fd = server.logfile ?
|
||||
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644) :
|
||||
STDOUT_FILENO;
|
||||
if (fd == -1) return;
|
||||
ll2string(buf,sizeof(buf),getpid());
|
||||
if (write(fd,"[",1) == -1) goto err;
|
||||
@@ -333,7 +330,7 @@ void redisLogFromHandler(int level, const char *msg) {
|
||||
if (write(fd,msg,strlen(msg)) == -1) goto err;
|
||||
if (write(fd,"\n",1) == -1) goto err;
|
||||
err:
|
||||
if (!log_to_stdout) close(fd);
|
||||
if (server.logfile) close(fd);
|
||||
}
|
||||
|
||||
/* Return the UNIX time in microseconds */
|
||||
@@ -562,18 +559,6 @@ dictType keylistDictType = {
|
||||
dictListDestructor /* val destructor */
|
||||
};
|
||||
|
||||
/* Replication cached script dict (server.repl_scriptcache_dict).
|
||||
* Keys are sds SHA1 strings, while values are not used at all in the current
|
||||
* implementation. */
|
||||
dictType replScriptCacheDictType = {
|
||||
dictSdsCaseHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictSdsKeyCaseCompare, /* key compare */
|
||||
dictSdsDestructor, /* key destructor */
|
||||
NULL /* val destructor */
|
||||
};
|
||||
|
||||
int htNeedsResize(dict *dict) {
|
||||
long long size, used;
|
||||
|
||||
@@ -706,8 +691,6 @@ void activeExpireCycle(void) {
|
||||
|
||||
propagateExpire(db,keyobj);
|
||||
dbDelete(db,keyobj);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
|
||||
"expired",keyobj,db->id);
|
||||
decrRefCount(keyobj);
|
||||
expired++;
|
||||
server.stat_expiredkeys++;
|
||||
@@ -963,7 +946,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
if (!server.sentinel_mode) {
|
||||
run_with_period(5000) {
|
||||
redisLog(REDIS_VERBOSE,
|
||||
"%lu clients connected (%lu slaves), %zu bytes in use",
|
||||
"%d clients connected (%d slaves), %zu bytes in use",
|
||||
listLength(server.clients)-listLength(server.slaves),
|
||||
listLength(server.slaves),
|
||||
zmalloc_used_memory());
|
||||
@@ -1012,18 +995,10 @@ 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.lastbgsave_try >
|
||||
REDIS_BGSAVE_RETRY_DELAY ||
|
||||
server.lastbgsave_status == REDIS_OK))
|
||||
{
|
||||
server.unixtime-server.lastsave > sp->seconds) {
|
||||
redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
|
||||
sp->changes, (int)sp->seconds);
|
||||
sp->changes, sp->seconds);
|
||||
rdbSaveBackground(server.rdb_filename);
|
||||
break;
|
||||
}
|
||||
@@ -1112,7 +1087,7 @@ void createSharedObjects(void) {
|
||||
shared.pong = createObject(REDIS_STRING,sdsnew("+PONG\r\n"));
|
||||
shared.queued = createObject(REDIS_STRING,sdsnew("+QUEUED\r\n"));
|
||||
shared.wrongtypeerr = createObject(REDIS_STRING,sdsnew(
|
||||
"-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"));
|
||||
"-ERR Operation against a key holding the wrong kind of value\r\n"));
|
||||
shared.nokeyerr = createObject(REDIS_STRING,sdsnew(
|
||||
"-ERR no such key\r\n"));
|
||||
shared.syntaxerr = createObject(REDIS_STRING,sdsnew(
|
||||
@@ -1133,27 +1108,17 @@ void createSharedObjects(void) {
|
||||
"-MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.\r\n"));
|
||||
shared.roslaveerr = createObject(REDIS_STRING,sdsnew(
|
||||
"-READONLY You can't write against a read only slave.\r\n"));
|
||||
shared.noautherr = createObject(REDIS_STRING,sdsnew(
|
||||
"-NOAUTH Authentication required.\r\n"));
|
||||
shared.oomerr = createObject(REDIS_STRING,sdsnew(
|
||||
"-OOM command not allowed when used memory > 'maxmemory'.\r\n"));
|
||||
shared.execaborterr = createObject(REDIS_STRING,sdsnew(
|
||||
"-EXECABORT Transaction discarded because of previous errors.\r\n"));
|
||||
shared.noreplicaserr = createObject(REDIS_STRING,sdsnew(
|
||||
"-NOREPLICAS Not enough good slaves to write.\r\n"));
|
||||
shared.space = createObject(REDIS_STRING,sdsnew(" "));
|
||||
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
|
||||
shared.plus = createObject(REDIS_STRING,sdsnew("+"));
|
||||
|
||||
for (j = 0; j < REDIS_SHARED_SELECT_CMDS; j++) {
|
||||
char dictid_str[64];
|
||||
int dictid_len;
|
||||
|
||||
dictid_len = ll2string(dictid_str,sizeof(dictid_str),j);
|
||||
shared.select[j] = createObject(REDIS_STRING,
|
||||
sdscatprintf(sdsempty(),
|
||||
"*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n",
|
||||
dictid_len, dictid_str));
|
||||
sdscatprintf(sdsempty(),"select %d\r\n", j));
|
||||
}
|
||||
shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
|
||||
shared.pmessagebulk = createStringObject("$8\r\npmessage\r\n",14);
|
||||
@@ -1178,35 +1143,32 @@ void createSharedObjects(void) {
|
||||
}
|
||||
|
||||
void initServerConfig() {
|
||||
int j;
|
||||
|
||||
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
|
||||
server.configfile = NULL;
|
||||
server.hz = REDIS_DEFAULT_HZ;
|
||||
server.runid[REDIS_RUN_ID_SIZE] = '\0';
|
||||
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
|
||||
server.port = REDIS_SERVERPORT;
|
||||
server.bindaddr_count = 0;
|
||||
server.bindaddr = NULL;
|
||||
server.unixsocket = NULL;
|
||||
server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;
|
||||
server.ipfd_count = 0;
|
||||
server.unixsocketperm = 0;
|
||||
server.ipfd = -1;
|
||||
server.sofd = -1;
|
||||
server.dbnum = REDIS_DEFAULT_DBNUM;
|
||||
server.verbosity = REDIS_DEFAULT_VERBOSITY;
|
||||
server.verbosity = REDIS_NOTICE;
|
||||
server.maxidletime = REDIS_MAXIDLETIME;
|
||||
server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE;
|
||||
server.tcpkeepalive = 0;
|
||||
server.active_expire_enabled = 1;
|
||||
server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
|
||||
server.saveparams = NULL;
|
||||
server.loading = 0;
|
||||
server.logfile = zstrdup(REDIS_DEFAULT_LOGFILE);
|
||||
server.syslog_enabled = REDIS_DEFAULT_SYSLOG_ENABLED;
|
||||
server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT);
|
||||
server.logfile = NULL; /* NULL = log on standard output */
|
||||
server.syslog_enabled = 0;
|
||||
server.syslog_ident = zstrdup("redis");
|
||||
server.syslog_facility = LOG_LOCAL0;
|
||||
server.daemonize = REDIS_DEFAULT_DAEMONIZE;
|
||||
server.daemonize = 0;
|
||||
server.aof_state = REDIS_AOF_OFF;
|
||||
server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC;
|
||||
server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE;
|
||||
server.aof_fsync = AOF_FSYNC_EVERYSEC;
|
||||
server.aof_no_fsync_on_rewrite = 0;
|
||||
server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC;
|
||||
server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE;
|
||||
server.aof_rewrite_base_size = 0;
|
||||
@@ -1219,21 +1181,19 @@ 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 = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
|
||||
server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
|
||||
server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
|
||||
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 = REDIS_DEFAULT_RDB_COMPRESSION;
|
||||
server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM;
|
||||
server.stop_writes_on_bgsave_err = REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR;
|
||||
server.activerehashing = REDIS_DEFAULT_ACTIVE_REHASHING;
|
||||
server.notify_keyspace_events = 0;
|
||||
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;
|
||||
server.maxmemory = REDIS_DEFAULT_MAXMEMORY;
|
||||
server.maxmemory_policy = REDIS_DEFAULT_MAXMEMORY_POLICY;
|
||||
server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES;
|
||||
server.maxmemory = 0;
|
||||
server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
|
||||
server.maxmemory_samples = 3;
|
||||
server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
|
||||
server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
|
||||
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
|
||||
@@ -1244,13 +1204,10 @@ void initServerConfig() {
|
||||
server.shutdown_asap = 0;
|
||||
server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;
|
||||
server.repl_timeout = REDIS_REPL_TIMEOUT;
|
||||
server.repl_min_slaves_to_write = REDIS_DEFAULT_MIN_SLAVES_TO_WRITE;
|
||||
server.repl_min_slaves_max_lag = REDIS_DEFAULT_MIN_SLAVES_MAX_LAG;
|
||||
server.lua_caller = NULL;
|
||||
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
|
||||
server.lua_client = NULL;
|
||||
server.lua_timedout = 0;
|
||||
server.loading_process_events_interval_bytes = (1024*1024*2);
|
||||
|
||||
updateLRUClock();
|
||||
resetServerSaveParams();
|
||||
@@ -1263,29 +1220,24 @@ void initServerConfig() {
|
||||
server.masterhost = NULL;
|
||||
server.masterport = 6379;
|
||||
server.master = NULL;
|
||||
server.cached_master = NULL;
|
||||
server.repl_master_initial_offset = -1;
|
||||
server.repl_state = REDIS_REPL_NONE;
|
||||
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
|
||||
server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;
|
||||
server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
|
||||
server.repl_serve_stale_data = 1;
|
||||
server.repl_slave_ro = 1;
|
||||
server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
|
||||
server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
|
||||
server.repl_disable_tcp_nodelay = 0;
|
||||
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
|
||||
server.master_repl_offset = 0;
|
||||
|
||||
/* Replication partial resync backlog */
|
||||
server.repl_backlog = NULL;
|
||||
server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE;
|
||||
server.repl_backlog_histlen = 0;
|
||||
server.repl_backlog_idx = 0;
|
||||
server.repl_backlog_off = 0;
|
||||
server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT;
|
||||
server.repl_no_slaves_since = time(NULL);
|
||||
|
||||
/* Client output buffer limits */
|
||||
for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++)
|
||||
server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_bytes = 0;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].soft_limit_seconds = 0;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].hard_limit_bytes = 1024*1024*256;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_bytes = 1024*1024*64;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_SLAVE].soft_limit_seconds = 60;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].hard_limit_bytes = 1024*1024*32;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_bytes = 1024*1024*8;
|
||||
server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_PUBSUB].soft_limit_seconds = 60;
|
||||
|
||||
/* Double constants initialization */
|
||||
R_Zero = 0.0;
|
||||
@@ -1378,45 +1330,22 @@ void initServer() {
|
||||
server.clients_to_close = listCreate();
|
||||
server.slaves = listCreate();
|
||||
server.monitors = listCreate();
|
||||
server.slaveseldb = -1; /* Force to emit the first SELECT command. */
|
||||
server.unblocked_clients = listCreate();
|
||||
server.ready_keys = listCreate();
|
||||
|
||||
createSharedObjects();
|
||||
adjustOpenFilesLimit();
|
||||
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
|
||||
server.el = aeCreateEventLoop(server.maxclients+1024);
|
||||
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
|
||||
|
||||
/* Open the TCP listening sockets. */
|
||||
if (server.port != 0) {
|
||||
/* Force binding of 0.0.0.0 if no bind address is specified, always
|
||||
* entering the loop if j == 0. */
|
||||
if (server.bindaddr_count == 0) server.bindaddr[0] = NULL;
|
||||
for (j = 0; j < server.bindaddr_count || j == 0; j++) {
|
||||
if (server.bindaddr[j] == NULL) {
|
||||
/* Bind * for both IPv6 and IPv4. */
|
||||
server.ipfd[0] = anetTcp6Server(server.neterr,server.port,NULL);
|
||||
if (server.ipfd[0] != ANET_ERR) server.ipfd_count++;
|
||||
server.ipfd[1] = anetTcpServer(server.neterr,server.port,NULL);
|
||||
} else if (strchr(server.bindaddr[j],':')) {
|
||||
/* Bind IPv6 address. */
|
||||
server.ipfd[server.ipfd_count] = anetTcp6Server(server.neterr,server.port,server.bindaddr[j]);
|
||||
} else {
|
||||
/* Bind IPv4 address. */
|
||||
server.ipfd[server.ipfd_count] = anetTcpServer(server.neterr,server.port,server.bindaddr[j]);
|
||||
}
|
||||
if (server.ipfd[server.ipfd_count] == ANET_ERR) {
|
||||
redisLog(REDIS_WARNING,
|
||||
"Creating Server TCP listening socket %s:%d: %s",
|
||||
server.bindaddr[j] ? server.bindaddr[j] : "*",
|
||||
server.port, server.neterr);
|
||||
exit(1);
|
||||
}
|
||||
server.ipfd_count++;
|
||||
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
|
||||
if (server.ipfd == ANET_ERR) {
|
||||
redisLog(REDIS_WARNING, "Opening port %d: %s",
|
||||
server.port, server.neterr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the listening Unix domain socket. */
|
||||
if (server.unixsocket != NULL) {
|
||||
unlink(server.unixsocket); /* don't care if this fails */
|
||||
server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
|
||||
@@ -1425,14 +1354,10 @@ void initServer() {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Abort if there are no listening sockets at all. */
|
||||
if (server.ipfd_count == 0 && server.sofd < 0) {
|
||||
if (server.ipfd < 0 && server.sofd < 0) {
|
||||
redisLog(REDIS_WARNING, "Configured to not listen anywhere, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Create the Redis databases, and initialize other internal state. */
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
server.db[j].dict = dictCreate(&dbDictType,NULL);
|
||||
server.db[j].expires = dictCreate(&keyptrDictType,NULL);
|
||||
@@ -1450,8 +1375,7 @@ void initServer() {
|
||||
server.aof_child_pid = -1;
|
||||
aofRewriteBufferReset();
|
||||
server.aof_buf = sdsempty();
|
||||
server.lastsave = time(NULL); /* At startup we consider the DB saved. */
|
||||
server.lastbgsave_try = 0; /* At startup we never tried to BGSAVE. */
|
||||
server.lastsave = time(NULL);
|
||||
server.rdb_save_time_last = -1;
|
||||
server.rdb_save_time_start = -1;
|
||||
server.dirty = 0;
|
||||
@@ -1465,38 +1389,21 @@ void initServer() {
|
||||
server.stat_peak_memory = 0;
|
||||
server.stat_fork_time = 0;
|
||||
server.stat_rejected_conn = 0;
|
||||
server.stat_sync_full = 0;
|
||||
server.stat_sync_partial_ok = 0;
|
||||
server.stat_sync_partial_err = 0;
|
||||
memset(server.ops_sec_samples,0,sizeof(server.ops_sec_samples));
|
||||
server.ops_sec_idx = 0;
|
||||
server.ops_sec_last_sample_time = mstime();
|
||||
server.ops_sec_last_sample_ops = 0;
|
||||
server.unixtime = time(NULL);
|
||||
server.lastbgsave_status = REDIS_OK;
|
||||
server.repl_good_slaves_count = 0;
|
||||
|
||||
/* Create the serverCron() time event, that's our main way to process
|
||||
* background operations. */
|
||||
if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
|
||||
redisPanic("Can't create the serverCron time event.");
|
||||
redisPanic("create time event failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Create an event handler for accepting new connections in TCP and Unix
|
||||
* domain sockets. */
|
||||
for (j = 0; j < server.ipfd_count; j++) {
|
||||
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
|
||||
acceptTcpHandler,NULL) == AE_ERR)
|
||||
{
|
||||
redisPanic(
|
||||
"Unrecoverable error creating server.ipfd file event.");
|
||||
}
|
||||
}
|
||||
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,
|
||||
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
|
||||
|
||||
/* Open the AOF file if needed. */
|
||||
if (server.aof_state == REDIS_AOF_ON) {
|
||||
server.aof_fd = open(server.aof_filename,
|
||||
O_WRONLY|O_APPEND|O_CREAT,0644);
|
||||
@@ -1517,7 +1424,6 @@ void initServer() {
|
||||
server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
|
||||
}
|
||||
|
||||
replicationScriptCacheInit();
|
||||
scriptingInit();
|
||||
slowlogInit();
|
||||
bioInit();
|
||||
@@ -1541,6 +1447,7 @@ void populateCommandTable(void) {
|
||||
case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
|
||||
case 'a': c->flags |= REDIS_CMD_ADMIN; break;
|
||||
case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
|
||||
case 'f': c->flags |= REDIS_CMD_FORCE_REPLICATION; break;
|
||||
case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
|
||||
case 'R': c->flags |= REDIS_CMD_RANDOM; break;
|
||||
case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
|
||||
@@ -1651,7 +1558,7 @@ void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
|
||||
{
|
||||
if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)
|
||||
feedAppendOnlyFile(cmd,dbid,argv,argc);
|
||||
if (flags & REDIS_PROPAGATE_REPL)
|
||||
if (flags & REDIS_PROPAGATE_REPL && listLength(server.slaves))
|
||||
replicationFeedSlaves(server.slaves,dbid,argv,argc);
|
||||
}
|
||||
|
||||
@@ -1663,18 +1570,9 @@ void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
|
||||
redisOpArrayAppend(&server.also_propagate,cmd,dbid,argv,argc,target);
|
||||
}
|
||||
|
||||
/* It is possible to call the function forceCommandPropagation() inside a
|
||||
* Redis command implementaiton in order to to force the propagation of a
|
||||
* specific command execution into AOF / Replication. */
|
||||
void forceCommandPropagation(redisClient *c, int flags) {
|
||||
if (flags & REDIS_PROPAGATE_REPL) c->flags |= REDIS_FORCE_REPL;
|
||||
if (flags & REDIS_PROPAGATE_AOF) c->flags |= REDIS_FORCE_AOF;
|
||||
}
|
||||
|
||||
/* Call() is the core of Redis execution of a command */
|
||||
void call(redisClient *c, int flags) {
|
||||
long long dirty, start = ustime(), duration;
|
||||
int client_old_flags = c->flags;
|
||||
|
||||
/* Sent the command to clients in MONITOR mode, only if the commands are
|
||||
* not generated from reading an AOF. */
|
||||
@@ -1686,7 +1584,6 @@ void call(redisClient *c, int flags) {
|
||||
}
|
||||
|
||||
/* Call the command. */
|
||||
c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);
|
||||
redisOpArrayInit(&server.also_propagate);
|
||||
dirty = server.dirty;
|
||||
c->cmd->proc(c);
|
||||
@@ -1698,16 +1595,6 @@ void call(redisClient *c, int flags) {
|
||||
if (server.loading && c->flags & REDIS_LUA_CLIENT)
|
||||
flags &= ~(REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);
|
||||
|
||||
/* If the caller is Lua, we want to force the EVAL caller to propagate
|
||||
* the script if the command flag or client flag are forcing the
|
||||
* propagation. */
|
||||
if (c->flags & REDIS_LUA_CLIENT && server.lua_caller) {
|
||||
if (c->flags & REDIS_FORCE_REPL)
|
||||
server.lua_caller->flags |= REDIS_FORCE_REPL;
|
||||
if (c->flags & REDIS_FORCE_AOF)
|
||||
server.lua_caller->flags |= REDIS_FORCE_AOF;
|
||||
}
|
||||
|
||||
/* Log the command into the Slow log if needed, and populate the
|
||||
* per-command statistics that we show in INFO commandstats. */
|
||||
if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)
|
||||
@@ -1721,19 +1608,14 @@ void call(redisClient *c, int flags) {
|
||||
if (flags & REDIS_CALL_PROPAGATE) {
|
||||
int flags = REDIS_PROPAGATE_NONE;
|
||||
|
||||
if (c->flags & REDIS_FORCE_REPL) flags |= REDIS_PROPAGATE_REPL;
|
||||
if (c->flags & REDIS_FORCE_AOF) flags |= REDIS_PROPAGATE_AOF;
|
||||
if (c->cmd->flags & REDIS_CMD_FORCE_REPLICATION)
|
||||
flags |= REDIS_PROPAGATE_REPL;
|
||||
if (dirty)
|
||||
flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);
|
||||
if (flags != REDIS_PROPAGATE_NONE)
|
||||
propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
|
||||
}
|
||||
|
||||
/* Restore the old FORCE_AOF/REPL flags, since call can be executed
|
||||
* recursively. */
|
||||
c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);
|
||||
c->flags |= client_old_flags & (REDIS_FORCE_AOF|REDIS_FORCE_REPL);
|
||||
|
||||
/* Handle the alsoPropagate() API to handle commands that want to propagate
|
||||
* multiple separated commands. */
|
||||
if (server.also_propagate.numops) {
|
||||
@@ -1788,7 +1670,7 @@ int processCommand(redisClient *c) {
|
||||
if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
|
||||
{
|
||||
flagTransaction(c);
|
||||
addReply(c,shared.noautherr);
|
||||
addReplyError(c,"operation not permitted");
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
@@ -1817,18 +1699,6 @@ int processCommand(redisClient *c) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Don't accept write commands if there are not enough good slaves and
|
||||
* used configured the min-slaves-to-write option. */
|
||||
if (server.repl_min_slaves_to_write &&
|
||||
server.repl_min_slaves_max_lag &&
|
||||
c->cmd->flags & REDIS_CMD_WRITE &&
|
||||
server.repl_good_slaves_count < server.repl_min_slaves_to_write)
|
||||
{
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.noreplicaserr);
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Don't accept write commands if this is a read only slave. But
|
||||
* accept write commands if this is our master. */
|
||||
if (server.masterhost && server.repl_slave_ro &&
|
||||
@@ -1868,10 +1738,9 @@ int processCommand(redisClient *c) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Lua script too slow? Only allow a limited number of commands. */
|
||||
/* Lua script too slow? Only allow commands with REDIS_CMD_STALE flag. */
|
||||
if (server.lua_timedout &&
|
||||
c->cmd->proc != authCommand &&
|
||||
c->cmd->proc != replconfCommand &&
|
||||
!(c->cmd->proc == shutdownCommand &&
|
||||
c->argc == 2 &&
|
||||
tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
|
||||
@@ -1901,19 +1770,6 @@ int processCommand(redisClient *c) {
|
||||
|
||||
/*================================== Shutdown =============================== */
|
||||
|
||||
/* Close listening sockets. Also unlink the unix domain socket if
|
||||
* unlink_unix_socket is non-zero. */
|
||||
void closeListeningSockets(int unlink_unix_socket) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]);
|
||||
if (server.sofd != -1) close(server.sofd);
|
||||
if (unlink_unix_socket && server.unixsocket) {
|
||||
redisLog(REDIS_NOTICE,"Removing the unix socket file.");
|
||||
unlink(server.unixsocket); /* don't care if this fails */
|
||||
}
|
||||
}
|
||||
|
||||
int prepareForShutdown(int flags) {
|
||||
int save = flags & REDIS_SHUTDOWN_SAVE;
|
||||
int nosave = flags & REDIS_SHUTDOWN_NOSAVE;
|
||||
@@ -1957,7 +1813,13 @@ int prepareForShutdown(int flags) {
|
||||
unlink(server.pidfile);
|
||||
}
|
||||
/* Close the listening sockets. Apparently this allows faster restarts. */
|
||||
closeListeningSockets(1);
|
||||
if (server.ipfd != -1) close(server.ipfd);
|
||||
if (server.sofd != -1) close(server.sofd);
|
||||
if (server.unixsocket) {
|
||||
redisLog(REDIS_NOTICE,"Removing the unix socket file.");
|
||||
unlink(server.unixsocket); /* don't care if this fails */
|
||||
}
|
||||
|
||||
redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
|
||||
return REDIS_OK;
|
||||
}
|
||||
@@ -2094,7 +1956,6 @@ sds genRedisInfoString(char *section) {
|
||||
"redis_version:%s\r\n"
|
||||
"redis_git_sha1:%s\r\n"
|
||||
"redis_git_dirty:%d\r\n"
|
||||
"redis_build_id:%llx\r\n"
|
||||
"redis_mode:%s\r\n"
|
||||
"os:%s %s %s\r\n"
|
||||
"arch_bits:%d\r\n"
|
||||
@@ -2103,15 +1964,13 @@ sds genRedisInfoString(char *section) {
|
||||
"process_id:%ld\r\n"
|
||||
"run_id:%s\r\n"
|
||||
"tcp_port:%d\r\n"
|
||||
"uptime_in_seconds:%jd\r\n"
|
||||
"uptime_in_days:%jd\r\n"
|
||||
"uptime_in_seconds:%ld\r\n"
|
||||
"uptime_in_days:%ld\r\n"
|
||||
"hz:%d\r\n"
|
||||
"lru_clock:%ld\r\n"
|
||||
"config_file:%s\r\n",
|
||||
"lru_clock:%ld\r\n",
|
||||
REDIS_VERSION,
|
||||
redisGitSHA1(),
|
||||
strtol(redisGitDirty(),NULL,10) > 0,
|
||||
(unsigned long long) redisBuildId(),
|
||||
mode,
|
||||
name.sysname, name.release, name.machine,
|
||||
server.arch_bits,
|
||||
@@ -2124,11 +1983,10 @@ sds genRedisInfoString(char *section) {
|
||||
(long) getpid(),
|
||||
server.runid,
|
||||
server.port,
|
||||
(intmax_t)uptime,
|
||||
(intmax_t)(uptime/(3600*24)),
|
||||
uptime,
|
||||
uptime/(3600*24),
|
||||
server.hz,
|
||||
(unsigned long) server.lruclock,
|
||||
server.configfile ? server.configfile : "");
|
||||
(unsigned long) server.lruclock);
|
||||
}
|
||||
|
||||
/* Clients */
|
||||
@@ -2182,30 +2040,30 @@ sds genRedisInfoString(char *section) {
|
||||
"loading:%d\r\n"
|
||||
"rdb_changes_since_last_save:%lld\r\n"
|
||||
"rdb_bgsave_in_progress:%d\r\n"
|
||||
"rdb_last_save_time:%jd\r\n"
|
||||
"rdb_last_save_time:%ld\r\n"
|
||||
"rdb_last_bgsave_status:%s\r\n"
|
||||
"rdb_last_bgsave_time_sec:%jd\r\n"
|
||||
"rdb_current_bgsave_time_sec:%jd\r\n"
|
||||
"rdb_last_bgsave_time_sec:%ld\r\n"
|
||||
"rdb_current_bgsave_time_sec:%ld\r\n"
|
||||
"aof_enabled:%d\r\n"
|
||||
"aof_rewrite_in_progress:%d\r\n"
|
||||
"aof_rewrite_scheduled:%d\r\n"
|
||||
"aof_last_rewrite_time_sec:%jd\r\n"
|
||||
"aof_current_rewrite_time_sec:%jd\r\n"
|
||||
"aof_last_rewrite_time_sec:%ld\r\n"
|
||||
"aof_current_rewrite_time_sec:%ld\r\n"
|
||||
"aof_last_bgrewrite_status:%s\r\n",
|
||||
server.loading,
|
||||
server.dirty,
|
||||
server.rdb_child_pid != -1,
|
||||
(intmax_t)server.lastsave,
|
||||
server.lastsave,
|
||||
(server.lastbgsave_status == REDIS_OK) ? "ok" : "err",
|
||||
(intmax_t)server.rdb_save_time_last,
|
||||
(intmax_t)((server.rdb_child_pid == -1) ?
|
||||
-1 : time(NULL)-server.rdb_save_time_start),
|
||||
server.rdb_save_time_last,
|
||||
(server.rdb_child_pid == -1) ?
|
||||
-1 : time(NULL)-server.rdb_save_time_start,
|
||||
server.aof_state != REDIS_AOF_OFF,
|
||||
server.aof_child_pid != -1,
|
||||
server.aof_rewrite_scheduled,
|
||||
(intmax_t)server.aof_rewrite_time_last,
|
||||
(intmax_t)((server.aof_child_pid == -1) ?
|
||||
-1 : time(NULL)-server.aof_rewrite_time_start),
|
||||
server.aof_rewrite_time_last,
|
||||
(server.aof_child_pid == -1) ?
|
||||
-1 : time(NULL)-server.aof_rewrite_time_start,
|
||||
(server.aof_lastbgrewrite_status == REDIS_OK) ? "ok" : "err");
|
||||
|
||||
if (server.aof_state != REDIS_AOF_OFF) {
|
||||
@@ -2244,16 +2102,16 @@ sds genRedisInfoString(char *section) {
|
||||
}
|
||||
|
||||
info = sdscatprintf(info,
|
||||
"loading_start_time:%jd\r\n"
|
||||
"loading_start_time:%ld\r\n"
|
||||
"loading_total_bytes:%llu\r\n"
|
||||
"loading_loaded_bytes:%llu\r\n"
|
||||
"loading_loaded_perc:%.2f\r\n"
|
||||
"loading_eta_seconds:%jd\r\n",
|
||||
(intmax_t) server.loading_start_time,
|
||||
"loading_eta_seconds:%ld\r\n"
|
||||
,(unsigned long) server.loading_start_time,
|
||||
(unsigned long long) server.loading_total_bytes,
|
||||
(unsigned long long) server.loading_loaded_bytes,
|
||||
perc,
|
||||
(intmax_t)eta
|
||||
eta
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2267,9 +2125,6 @@ sds genRedisInfoString(char *section) {
|
||||
"total_commands_processed:%lld\r\n"
|
||||
"instantaneous_ops_per_sec:%lld\r\n"
|
||||
"rejected_connections:%lld\r\n"
|
||||
"sync_full:%lld\r\n"
|
||||
"sync_partial_ok:%lld\r\n"
|
||||
"sync_partial_err:%lld\r\n"
|
||||
"expired_keys:%lld\r\n"
|
||||
"evicted_keys:%lld\r\n"
|
||||
"keyspace_hits:%lld\r\n"
|
||||
@@ -2281,9 +2136,6 @@ sds genRedisInfoString(char *section) {
|
||||
server.stat_numcommands,
|
||||
getOperationsPerSecond(),
|
||||
server.stat_rejected_conn,
|
||||
server.stat_sync_full,
|
||||
server.stat_sync_partial_ok,
|
||||
server.stat_sync_partial_err,
|
||||
server.stat_expiredkeys,
|
||||
server.stat_evictedkeys,
|
||||
server.stat_keyspace_hits,
|
||||
@@ -2307,15 +2159,13 @@ sds genRedisInfoString(char *section) {
|
||||
"master_link_status:%s\r\n"
|
||||
"master_last_io_seconds_ago:%d\r\n"
|
||||
"master_sync_in_progress:%d\r\n"
|
||||
"slave_repl_offset:%lld\r\n"
|
||||
,server.masterhost,
|
||||
server.masterport,
|
||||
(server.repl_state == REDIS_REPL_CONNECTED) ?
|
||||
"up" : "down",
|
||||
server.master ?
|
||||
((int)(server.unixtime-server.master->lastinteraction)) : -1,
|
||||
server.repl_state == REDIS_REPL_TRANSFER,
|
||||
server.master ? server.master->reploff : -1
|
||||
server.repl_state == REDIS_REPL_TRANSFER
|
||||
);
|
||||
|
||||
if (server.repl_state == REDIS_REPL_TRANSFER) {
|
||||
@@ -2330,8 +2180,8 @@ sds genRedisInfoString(char *section) {
|
||||
|
||||
if (server.repl_state != REDIS_REPL_CONNECTED) {
|
||||
info = sdscatprintf(info,
|
||||
"master_link_down_since_seconds:%jd\r\n",
|
||||
(intmax_t)server.unixtime-server.repl_down_since);
|
||||
"master_link_down_since_seconds:%ld\r\n",
|
||||
(long)server.unixtime-server.repl_down_since);
|
||||
}
|
||||
info = sdscatprintf(info,
|
||||
"slave_priority:%d\r\n"
|
||||
@@ -2339,20 +2189,9 @@ sds genRedisInfoString(char *section) {
|
||||
server.slave_priority,
|
||||
server.repl_slave_ro);
|
||||
}
|
||||
|
||||
info = sdscatprintf(info,
|
||||
"connected_slaves:%lu\r\n",
|
||||
listLength(server.slaves));
|
||||
|
||||
/* If min-slaves-to-write is active, write the number of slaves
|
||||
* currently considered 'good'. */
|
||||
if (server.repl_min_slaves_to_write &&
|
||||
server.repl_min_slaves_max_lag) {
|
||||
info = sdscatprintf(info,
|
||||
"min_slaves_good_slaves:%d\r\n",
|
||||
server.repl_good_slaves_count);
|
||||
}
|
||||
|
||||
if (listLength(server.slaves)) {
|
||||
int slaveid = 0;
|
||||
listNode *ln;
|
||||
@@ -2362,11 +2201,10 @@ sds genRedisInfoString(char *section) {
|
||||
while((ln = listNext(&li))) {
|
||||
redisClient *slave = listNodeValue(ln);
|
||||
char *state = NULL;
|
||||
char ip[REDIS_IP_STR_LEN];
|
||||
char ip[32];
|
||||
int port;
|
||||
long lag = 0;
|
||||
|
||||
if (anetPeerToString(slave->fd,ip,sizeof(ip),&port) == -1) continue;
|
||||
if (anetPeerToString(slave->fd,ip,&port) == -1) continue;
|
||||
switch(slave->replstate) {
|
||||
case REDIS_REPL_WAIT_BGSAVE_START:
|
||||
case REDIS_REPL_WAIT_BGSAVE_END:
|
||||
@@ -2380,28 +2218,11 @@ sds genRedisInfoString(char *section) {
|
||||
break;
|
||||
}
|
||||
if (state == NULL) continue;
|
||||
if (slave->replstate == REDIS_REPL_ONLINE)
|
||||
lag = time(NULL) - slave->repl_ack_time;
|
||||
|
||||
info = sdscatprintf(info,
|
||||
"slave%d:ip=%s,port=%d,state=%s,"
|
||||
"offset=%lld,lag=%ld\r\n",
|
||||
slaveid,ip,slave->slave_listening_port,state,
|
||||
slave->repl_ack_off, lag);
|
||||
info = sdscatprintf(info,"slave%d:%s,%d,%s\r\n",
|
||||
slaveid,ip,slave->slave_listening_port,state);
|
||||
slaveid++;
|
||||
}
|
||||
}
|
||||
info = sdscatprintf(info,
|
||||
"master_repl_offset:%lld\r\n"
|
||||
"repl_backlog_active:%d\r\n"
|
||||
"repl_backlog_size:%lld\r\n"
|
||||
"repl_backlog_first_byte_offset:%lld\r\n"
|
||||
"repl_backlog_histlen:%lld\r\n",
|
||||
server.master_repl_offset,
|
||||
server.repl_backlog != NULL,
|
||||
server.repl_backlog_size,
|
||||
server.repl_backlog_off,
|
||||
server.repl_backlog_histlen);
|
||||
}
|
||||
|
||||
/* CPU */
|
||||
@@ -2472,6 +2293,7 @@ void monitorCommand(redisClient *c) {
|
||||
if (c->flags & REDIS_SLAVE) return;
|
||||
|
||||
c->flags |= (REDIS_SLAVE|REDIS_MONITOR);
|
||||
c->slaveseldb = 0;
|
||||
listAddNodeTail(server.monitors,c);
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
@@ -2619,8 +2441,6 @@ int freeMemoryIfNeeded(void) {
|
||||
delta -= (long long) zmalloc_used_memory();
|
||||
mem_freed += delta;
|
||||
server.stat_evictedkeys++;
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_EVICTED, "evicted",
|
||||
keyobj, db->id);
|
||||
decrRefCount(keyobj);
|
||||
keys_freed++;
|
||||
|
||||
@@ -2687,13 +2507,12 @@ void daemonize(void) {
|
||||
}
|
||||
|
||||
void version() {
|
||||
printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d build=%llx\n",
|
||||
printf("Redis server v=%s sha=%s:%d malloc=%s bits=%d\n",
|
||||
REDIS_VERSION,
|
||||
redisGitSHA1(),
|
||||
atoi(redisGitDirty()) > 0,
|
||||
ZMALLOC_LIB,
|
||||
sizeof(long) == 4 ? 32 : 64,
|
||||
(unsigned long long) redisBuildId());
|
||||
sizeof(long) == 4 ? 32 : 64);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -2795,28 +2614,13 @@ void loadDataFromDisk(void) {
|
||||
void redisOutOfMemoryHandler(size_t allocation_size) {
|
||||
redisLog(REDIS_WARNING,"Out Of Memory allocating %zu bytes!",
|
||||
allocation_size);
|
||||
redisPanic("Redis aborting for OUT OF MEMORY");
|
||||
}
|
||||
|
||||
void redisSetProcTitle(char *title) {
|
||||
#ifdef USE_SETPROCTITLE
|
||||
setproctitle("%s %s:%d",
|
||||
title,
|
||||
server.bindaddr_count ? server.bindaddr[0] : "*",
|
||||
server.port);
|
||||
#else
|
||||
REDIS_NOTUSED(title);
|
||||
#endif
|
||||
redisPanic("OOM");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct timeval tv;
|
||||
|
||||
/* We need to initialize our libraries, and the server configuration. */
|
||||
#ifdef INIT_SETPROCTITLE_REPLACEMENT
|
||||
spt_init(argc, argv);
|
||||
#endif
|
||||
setlocale(LC_COLLATE,"");
|
||||
zmalloc_enable_thread_safeness();
|
||||
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
|
||||
srand(time(NULL)^getpid());
|
||||
@@ -2877,24 +2681,22 @@ int main(int argc, char **argv) {
|
||||
resetServerSaveParams();
|
||||
loadServerConfig(configfile,options);
|
||||
sdsfree(options);
|
||||
if (configfile) server.configfile = getAbsolutePath(configfile);
|
||||
} else {
|
||||
redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
|
||||
}
|
||||
if (server.daemonize) daemonize();
|
||||
initServer();
|
||||
if (server.daemonize) createPidFile();
|
||||
redisSetProcTitle(argv[0]);
|
||||
redisAsciiArt();
|
||||
|
||||
if (!server.sentinel_mode) {
|
||||
/* Things not needed when running in Sentinel mode. */
|
||||
/* Things only needed when not running in Sentinel mode. */
|
||||
redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
|
||||
#ifdef __linux__
|
||||
linuxOvercommitMemoryWarning();
|
||||
#endif
|
||||
loadDataFromDisk();
|
||||
if (server.ipfd_count > 0)
|
||||
if (server.ipfd > 0)
|
||||
redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
|
||||
if (server.sofd > 0)
|
||||
redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
|
||||
|
||||
+20
-150
@@ -94,35 +94,6 @@
|
||||
#define REDIS_REPL_PING_SLAVE_PERIOD 10
|
||||
#define REDIS_RUN_ID_SIZE 40
|
||||
#define REDIS_OPS_SEC_SAMPLES 16
|
||||
#define REDIS_DEFAULT_REPL_BACKLOG_SIZE (1024*1024) /* 1mb */
|
||||
#define REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT (60*60) /* 1 hour */
|
||||
#define REDIS_REPL_BACKLOG_MIN_SIZE (1024*16) /* 16k */
|
||||
#define REDIS_BGSAVE_RETRY_DELAY 5 /* Wait a few secs before trying again. */
|
||||
#define REDIS_DEFAULT_PID_FILE "/var/run/redis.pid"
|
||||
#define REDIS_DEFAULT_SYSLOG_IDENT "redis"
|
||||
#define REDIS_DEFAULT_CLUSTER_CONFIG_FILE "nodes.conf"
|
||||
#define REDIS_DEFAULT_DAEMONIZE 0
|
||||
#define REDIS_DEFAULT_UNIX_SOCKET_PERM 0
|
||||
#define REDIS_DEFAULT_TCP_KEEPALIVE 0
|
||||
#define REDIS_DEFAULT_LOGFILE ""
|
||||
#define REDIS_DEFAULT_SYSLOG_ENABLED 0
|
||||
#define REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR 1
|
||||
#define REDIS_DEFAULT_RDB_COMPRESSION 1
|
||||
#define REDIS_DEFAULT_RDB_CHECKSUM 1
|
||||
#define REDIS_DEFAULT_RDB_FILENAME "dump.rdb"
|
||||
#define REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA 1
|
||||
#define REDIS_DEFAULT_SLAVE_READ_ONLY 1
|
||||
#define REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY 0
|
||||
#define REDIS_DEFAULT_MAXMEMORY 0
|
||||
#define REDIS_DEFAULT_MAXMEMORY_SAMPLES 3
|
||||
#define REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE 0
|
||||
#define REDIS_DEFAULT_ACTIVE_REHASHING 1
|
||||
#define REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
|
||||
#define REDIS_DEFAULT_MIN_SLAVES_TO_WRITE 0
|
||||
#define REDIS_DEFAULT_MIN_SLAVES_MAX_LAG 10
|
||||
#define REDIS_IP_STR_LEN INET6_ADDRSTRLEN
|
||||
#define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */
|
||||
#define REDIS_BINDADDR_MAX 16
|
||||
|
||||
/* Protocol and I/O related defines */
|
||||
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
|
||||
@@ -130,12 +101,6 @@
|
||||
#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_LONGSTR_SIZE 21 /* Bytes needed for long -> str */
|
||||
#define REDIS_AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */
|
||||
/* When configuring the Redis eventloop, we setup it so that the total number
|
||||
* of file descriptors we can handle are server.maxclients + FDSET_INCR
|
||||
* that is our safety margin. */
|
||||
#define REDIS_EVENTLOOP_FDSET_INCR 128
|
||||
|
||||
/* Hash table parameters */
|
||||
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
|
||||
@@ -145,7 +110,7 @@
|
||||
#define REDIS_CMD_WRITE 1 /* "w" flag */
|
||||
#define REDIS_CMD_READONLY 2 /* "r" flag */
|
||||
#define REDIS_CMD_DENYOOM 4 /* "m" flag */
|
||||
#define REDIS_CMD_NOT_USED_1 8 /* no longer used flag */
|
||||
#define REDIS_CMD_FORCE_REPLICATION 8 /* "f" flag */
|
||||
#define REDIS_CMD_ADMIN 16 /* "a" flag */
|
||||
#define REDIS_CMD_PUBSUB 32 /* "p" flag */
|
||||
#define REDIS_CMD_NOSCRIPT 64 /* "s" flag */
|
||||
@@ -221,10 +186,6 @@
|
||||
#define REDIS_CLOSE_ASAP (1<<10)/* Close this client ASAP */
|
||||
#define REDIS_UNIX_SOCKET (1<<11) /* Client connected via Unix domain socket */
|
||||
#define REDIS_DIRTY_EXEC (1<<12) /* EXEC will fail for errors while queueing */
|
||||
#define REDIS_MASTER_FORCE_REPLY (1<<13) /* Queue replies even if is master */
|
||||
#define REDIS_FORCE_AOF (1<<14) /* Force AOF propagation of current cmd. */
|
||||
#define REDIS_FORCE_REPL (1<<15) /* Force replication of current cmd. */
|
||||
#define REDIS_PRE_PSYNC_SLAVE (1<<16) /* Slave don't understand PSYNC. */
|
||||
|
||||
/* Client request types */
|
||||
#define REDIS_REQ_INLINE 1
|
||||
@@ -237,7 +198,7 @@
|
||||
#define REDIS_CLIENT_LIMIT_CLASS_PUBSUB 2
|
||||
#define REDIS_CLIENT_LIMIT_NUM_CLASSES 3
|
||||
|
||||
/* Slave replication state - from the point of view of the slave. */
|
||||
/* Slave replication state - slave side */
|
||||
#define REDIS_REPL_NONE 0 /* No active replication */
|
||||
#define REDIS_REPL_CONNECT 1 /* Must connect to master */
|
||||
#define REDIS_REPL_CONNECTING 2 /* Connecting to master */
|
||||
@@ -245,18 +206,18 @@
|
||||
#define REDIS_REPL_TRANSFER 4 /* Receiving .rdb from master */
|
||||
#define REDIS_REPL_CONNECTED 5 /* Connected to master */
|
||||
|
||||
/* Slave replication state - from the point of view of the master.
|
||||
* In SEND_BULK and ONLINE state the slave receives new updates
|
||||
* in its output queue. In the WAIT_BGSAVE state instead the server is waiting
|
||||
* to start the next background saving in order to send updates to it. */
|
||||
#define REDIS_REPL_WAIT_BGSAVE_START 6 /* We need to produce a new RDB file. */
|
||||
#define REDIS_REPL_WAIT_BGSAVE_END 7 /* Waiting RDB file creation to finish. */
|
||||
#define REDIS_REPL_SEND_BULK 8 /* Sending RDB file to slave. */
|
||||
#define REDIS_REPL_ONLINE 9 /* RDB file transmitted, sending just updates. */
|
||||
|
||||
/* Synchronous read timeout - slave side */
|
||||
#define REDIS_REPL_SYNCIO_TIMEOUT 5
|
||||
|
||||
/* Slave replication state - from the point of view of master
|
||||
* Note that in SEND_BULK and ONLINE state the slave receives new updates
|
||||
* in its output queue. In the WAIT_BGSAVE state instead the server is waiting
|
||||
* to start the next background saving in order to send updates to it. */
|
||||
#define REDIS_REPL_WAIT_BGSAVE_START 3 /* master waits bgsave to start feeding it */
|
||||
#define REDIS_REPL_WAIT_BGSAVE_END 4 /* master waits bgsave to start bulk DB transmission */
|
||||
#define REDIS_REPL_SEND_BULK 5 /* master is sending the bulk DB */
|
||||
#define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates */
|
||||
|
||||
/* List related stuff */
|
||||
#define REDIS_HEAD 0
|
||||
#define REDIS_TAIL 1
|
||||
@@ -273,7 +234,6 @@
|
||||
#define REDIS_NOTICE 2
|
||||
#define REDIS_WARNING 3
|
||||
#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */
|
||||
#define REDIS_DEFAULT_VERBOSITY REDIS_NOTICE
|
||||
|
||||
/* Anti-warning macro... */
|
||||
#define REDIS_NOTUSED(V) ((void) V)
|
||||
@@ -285,7 +245,6 @@
|
||||
#define AOF_FSYNC_NO 0
|
||||
#define AOF_FSYNC_ALWAYS 1
|
||||
#define AOF_FSYNC_EVERYSEC 2
|
||||
#define REDIS_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC
|
||||
|
||||
/* Zip structure related defaults */
|
||||
#define REDIS_HASH_MAX_ZIPLIST_ENTRIES 512
|
||||
@@ -308,7 +267,6 @@
|
||||
#define REDIS_MAXMEMORY_ALLKEYS_LRU 3
|
||||
#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4
|
||||
#define REDIS_MAXMEMORY_NO_EVICTION 5
|
||||
#define REDIS_DEFAULT_MAXMEMORY_POLICY REDIS_MAXMEMORY_VOLATILE_LRU
|
||||
|
||||
/* Scripting */
|
||||
#define REDIS_LUA_TIME_LIMIT 5000 /* milliseconds */
|
||||
@@ -334,20 +292,6 @@
|
||||
#define REDIS_PROPAGATE_AOF 1
|
||||
#define REDIS_PROPAGATE_REPL 2
|
||||
|
||||
/* Keyspace changes notification classes. Every class is associated with a
|
||||
* character for configuration purposes. */
|
||||
#define REDIS_NOTIFY_KEYSPACE (1<<0) /* K */
|
||||
#define REDIS_NOTIFY_KEYEVENT (1<<1) /* E */
|
||||
#define REDIS_NOTIFY_GENERIC (1<<2) /* g */
|
||||
#define REDIS_NOTIFY_STRING (1<<3) /* $ */
|
||||
#define REDIS_NOTIFY_LIST (1<<4) /* l */
|
||||
#define REDIS_NOTIFY_SET (1<<5) /* s */
|
||||
#define REDIS_NOTIFY_HASH (1<<6) /* h */
|
||||
#define REDIS_NOTIFY_ZSET (1<<7) /* z */
|
||||
#define REDIS_NOTIFY_EXPIRED (1<<8) /* x */
|
||||
#define REDIS_NOTIFY_EVICTED (1<<9) /* e */
|
||||
#define REDIS_NOTIFY_ALL (REDIS_NOTIFY_GENERIC | REDIS_NOTIFY_STRING | REDIS_NOTIFY_LIST | REDIS_NOTIFY_SET | REDIS_NOTIFY_HASH | REDIS_NOTIFY_ZSET | REDIS_NOTIFY_EXPIRED | REDIS_NOTIFY_EVICTED) /* A */
|
||||
|
||||
/* Using the following macro you can run code inside serverCron() with the
|
||||
* specified period, specified in milliseconds.
|
||||
* The actual resolution depends on server.hz. */
|
||||
@@ -406,15 +350,13 @@ typedef struct multiCmd {
|
||||
typedef struct multiState {
|
||||
multiCmd *commands; /* Array of MULTI commands */
|
||||
int count; /* Total number of MULTI commands */
|
||||
int minreplicas; /* MINREPLICAS for synchronous replication */
|
||||
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
|
||||
} multiState;
|
||||
|
||||
typedef struct blockingState {
|
||||
dict *keys; /* The keys we are waiting to terminate a blocking
|
||||
* operation such as BLPOP. Otherwise NULL. */
|
||||
time_t timeout; /* Blocking operation timeout. If UNIX current time
|
||||
* is > timeout then the operation timed out. */
|
||||
* is >= timeout then the operation timed out. */
|
||||
robj *target; /* The key that should receive the element,
|
||||
* for BRPOPLPUSH. */
|
||||
} blockingState;
|
||||
@@ -452,21 +394,17 @@ typedef struct redisClient {
|
||||
long bulklen; /* length of bulk argument in multi bulk request */
|
||||
list *reply;
|
||||
unsigned long reply_bytes; /* Tot bytes of objects in reply list */
|
||||
int sentlen; /* Amount of bytes already sent in the current
|
||||
buffer or object being sent. */
|
||||
int sentlen;
|
||||
time_t ctime; /* Client creation time */
|
||||
time_t lastinteraction; /* time of the last interaction, used for timeout */
|
||||
time_t obuf_soft_limit_reached_time;
|
||||
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */
|
||||
int slaveseldb; /* slave selected db, if this client is a slave */
|
||||
int authenticated; /* when requirepass is non-NULL */
|
||||
int replstate; /* replication state if this is a slave */
|
||||
int repldbfd; /* replication DB file descriptor */
|
||||
long repldboff; /* replication DB file offset */
|
||||
off_t repldbsize; /* replication DB file size */
|
||||
long long reploff; /* replication offset if this is our master */
|
||||
long long repl_ack_off; /* replication ack offset, if this is a slave */
|
||||
long long repl_ack_time;/* replication ack time, if this is a slave */
|
||||
char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */
|
||||
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
|
||||
multiState mstate; /* MULTI/EXEC state */
|
||||
blockingState bpop; /* blocking state */
|
||||
@@ -491,7 +429,7 @@ struct sharedObjectsStruct {
|
||||
*colon, *nullbulk, *nullmultibulk, *queued,
|
||||
*emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
|
||||
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
|
||||
*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,
|
||||
*masterdownerr, *roslaveerr, *execaborterr,
|
||||
*oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
|
||||
*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *rpop, *lpop,
|
||||
*lpush,
|
||||
@@ -529,8 +467,6 @@ typedef struct clientBufferLimitsConfig {
|
||||
time_t soft_limit_seconds;
|
||||
} clientBufferLimitsConfig;
|
||||
|
||||
extern clientBufferLimitsConfig clientBufferLimitsDefaults[REDIS_CLIENT_LIMIT_NUM_CLASSES];
|
||||
|
||||
/* The redisOp structure defines a Redis Operation, that is an instance of
|
||||
* a command with an argument vector, database ID, propagation target
|
||||
* (REDIS_PROPAGATE_*), and command pointer.
|
||||
@@ -561,7 +497,6 @@ typedef struct redisOpArray {
|
||||
|
||||
struct redisServer {
|
||||
/* General */
|
||||
char *configfile; /* Absolute config file path, or NULL */
|
||||
int hz; /* serverCron() calls frequency in hertz */
|
||||
redisDb *db;
|
||||
dict *commands; /* Command table */
|
||||
@@ -579,12 +514,10 @@ struct redisServer {
|
||||
int sentinel_mode; /* True if this instance is a Sentinel. */
|
||||
/* Networking */
|
||||
int port; /* TCP listening port */
|
||||
char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */
|
||||
int bindaddr_count; /* Number of addresses in server.bindaddr[] */
|
||||
char *bindaddr; /* Bind address or NULL */
|
||||
char *unixsocket; /* UNIX socket path */
|
||||
mode_t unixsocketperm; /* UNIX socket permission */
|
||||
int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */
|
||||
int ipfd_count; /* Used slots in ipfd[] */
|
||||
int ipfd; /* TCP socket file descriptor */
|
||||
int sofd; /* Unix socket file descriptor */
|
||||
list *clients; /* List of active clients */
|
||||
list *clients_to_close; /* Clients to close asynchronously */
|
||||
@@ -596,7 +529,6 @@ struct redisServer {
|
||||
off_t loading_total_bytes;
|
||||
off_t loading_loaded_bytes;
|
||||
time_t loading_start_time;
|
||||
off_t loading_process_events_interval_bytes;
|
||||
/* Fast pointers to often looked up command */
|
||||
struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,
|
||||
*rpopCommand;
|
||||
@@ -611,9 +543,6 @@ struct redisServer {
|
||||
size_t stat_peak_memory; /* Max used memory record */
|
||||
long long stat_fork_time; /* Time needed to perform latest fork() */
|
||||
long long stat_rejected_conn; /* Clients rejected because of maxclients */
|
||||
long long stat_sync_full; /* Number of full resyncs with slaves. */
|
||||
long long stat_sync_partial_ok; /* Number of accepted PSYNC requests. */
|
||||
long long stat_sync_partial_err;/* Number of unaccepted PSYNC requests. */
|
||||
list *slowlog; /* SLOWLOG list of commands */
|
||||
long long slowlog_entry_id; /* SLOWLOG current entry ID */
|
||||
long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
|
||||
@@ -654,7 +583,6 @@ 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 */
|
||||
@@ -665,7 +593,6 @@ struct redisServer {
|
||||
int rdb_compression; /* Use compression in RDB? */
|
||||
int rdb_checksum; /* Use RDB checksum? */
|
||||
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 */
|
||||
@@ -677,30 +604,13 @@ struct redisServer {
|
||||
int syslog_enabled; /* Is syslog enabled? */
|
||||
char *syslog_ident; /* Syslog ident */
|
||||
int syslog_facility; /* Syslog facility */
|
||||
/* Replication (master) */
|
||||
int slaveseldb; /* Last SELECTed DB in replication output */
|
||||
long long master_repl_offset; /* Global replication offset */
|
||||
int repl_ping_slave_period; /* Master pings the slave every N seconds */
|
||||
char *repl_backlog; /* Replication backlog for partial syncs */
|
||||
long long repl_backlog_size; /* Backlog circular buffer size */
|
||||
long long repl_backlog_histlen; /* Backlog actual data length */
|
||||
long long repl_backlog_idx; /* Backlog circular buffer current offset */
|
||||
long long repl_backlog_off; /* Replication offset of first byte in the
|
||||
backlog buffer. */
|
||||
time_t repl_backlog_time_limit; /* Time without slaves after the backlog
|
||||
gets released. */
|
||||
time_t repl_no_slaves_since; /* We have no slaves since that time.
|
||||
Only valid if server.slaves len is 0. */
|
||||
int repl_min_slaves_to_write; /* Min number of slaves to write. */
|
||||
int repl_min_slaves_max_lag; /* Max lag of <count> slaves to write. */
|
||||
int repl_good_slaves_count; /* Number of slaves with lag <= max_lag. */
|
||||
/* Replication (slave) */
|
||||
/* Slave specific fields */
|
||||
char *masterauth; /* AUTH with this password with master */
|
||||
char *masterhost; /* Hostname of master */
|
||||
int masterport; /* Port of master */
|
||||
int repl_ping_slave_period; /* Master pings the slave every N seconds */
|
||||
int repl_timeout; /* Timeout after N seconds of master idle */
|
||||
redisClient *master; /* Client that is master for this slave */
|
||||
redisClient *cached_master; /* Cached master to be reused for PSYNC. */
|
||||
int repl_syncio_timeout; /* Timeout for synchronous I/O calls */
|
||||
int repl_state; /* Replication status if the instance is a slave */
|
||||
off_t repl_transfer_size; /* Size of RDB to read from master during sync. */
|
||||
@@ -715,12 +625,6 @@ struct redisServer {
|
||||
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. */
|
||||
char repl_master_runid[REDIS_RUN_ID_SIZE+1]; /* Master run id for PSYNC. */
|
||||
long long repl_master_initial_offset; /* Master PSYNC offset. */
|
||||
/* Replication script cache. */
|
||||
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
|
||||
list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */
|
||||
int repl_scriptcache_size; /* Max number of elements. */
|
||||
/* Limits */
|
||||
unsigned int maxclients; /* Max number of simultaneous clients */
|
||||
unsigned long long maxmemory; /* Max number of memory bytes to use */
|
||||
@@ -735,7 +639,6 @@ struct redisServer {
|
||||
int sort_desc;
|
||||
int sort_alpha;
|
||||
int sort_bypattern;
|
||||
int sort_store;
|
||||
/* Zip structure config, see redis.conf for more information */
|
||||
size_t hash_max_ziplist_entries;
|
||||
size_t hash_max_ziplist_value;
|
||||
@@ -748,8 +651,6 @@ struct redisServer {
|
||||
/* Pubsub */
|
||||
dict *pubsub_channels; /* Map channels to list of subscribed clients */
|
||||
list *pubsub_patterns; /* A list of pubsub_patterns */
|
||||
int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
|
||||
xor of REDIS_NOTIFY... flags. */
|
||||
/* Scripting */
|
||||
lua_State *lua; /* The Lua interpreter. We use just one for all clients */
|
||||
redisClient *lua_client; /* The "fake client" to query Redis from Lua */
|
||||
@@ -865,7 +766,6 @@ extern dictType dbDictType;
|
||||
extern dictType shaScriptObjectDictType;
|
||||
extern double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
||||
extern dictType hashDictType;
|
||||
extern dictType replScriptCacheDictType;
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Functions prototypes
|
||||
@@ -877,14 +777,11 @@ long long mstime(void);
|
||||
void getRandomHexChars(char *p, unsigned int len);
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
void exitFromChild(int retcode);
|
||||
size_t redisPopcount(void *s, long count);
|
||||
void redisSetProcTitle(char *title);
|
||||
|
||||
/* networking.c -- Networking and Client related operations */
|
||||
redisClient *createClient(int fd);
|
||||
void closeTimedoutClients(void);
|
||||
void freeClient(redisClient *c);
|
||||
void freeClientAsync(redisClient *c);
|
||||
void resetClient(redisClient *c);
|
||||
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||
void addReply(redisClient *c, robj *obj);
|
||||
@@ -911,8 +808,6 @@ void copyClientOutputBuffer(redisClient *dst, redisClient *src);
|
||||
void *dupClientReplyValue(void *o);
|
||||
void getClientsMaxBuffers(unsigned long *longest_output_list,
|
||||
unsigned long *biggest_input_buffer);
|
||||
void formatPeerId(char *peerid, size_t peerid_len, char *ip, int port);
|
||||
int getClientPeerId(redisClient *client, char *peerid, size_t peerid_len);
|
||||
sds getClientInfoString(redisClient *client);
|
||||
sds getAllClientsInfoString(void);
|
||||
void rewriteClientCommandVector(redisClient *c, int argc, ...);
|
||||
@@ -963,8 +858,7 @@ void discardTransaction(redisClient *c);
|
||||
void flagTransaction(redisClient *c);
|
||||
|
||||
/* Redis object implementation */
|
||||
void decrRefCount(robj *o);
|
||||
void decrRefCountVoid(void *o);
|
||||
void decrRefCount(void *o);
|
||||
void incrRefCount(robj *o);
|
||||
robj *resetRefCount(robj *obj);
|
||||
void freeStringObject(robj *o);
|
||||
@@ -997,7 +891,6 @@ int getLongDoubleFromObject(robj *o, long double *target);
|
||||
int getLongDoubleFromObjectOrReply(redisClient *c, robj *o, long double *target, const char *msg);
|
||||
char *strEncoding(int encoding);
|
||||
int compareStringObjects(robj *a, robj *b);
|
||||
int collateStringObjects(robj *a, robj *b);
|
||||
int equalStringObjects(robj *a, robj *b);
|
||||
unsigned long estimateObjectIdleTime(robj *o);
|
||||
|
||||
@@ -1011,14 +904,6 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
|
||||
void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc);
|
||||
void updateSlavesWaitingBgsave(int bgsaveerr);
|
||||
void replicationCron(void);
|
||||
void replicationHandleMasterDisconnection(void);
|
||||
void replicationCacheMaster(redisClient *c);
|
||||
void resizeReplicationBacklog(long long newsize);
|
||||
void refreshGoodSlavesCount(void);
|
||||
void replicationScriptCacheInit(void);
|
||||
void replicationScriptCacheFlush(void);
|
||||
void replicationScriptCacheAdd(sds sha1);
|
||||
int replicationScriptCacheExists(sds sha1);
|
||||
|
||||
/* Generic persistence functions */
|
||||
void startLoading(FILE *fp);
|
||||
@@ -1070,14 +955,8 @@ 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);
|
||||
void forceCommandPropagation(redisClient *c, int flags);
|
||||
int prepareForShutdown();
|
||||
#ifdef __GNUC__
|
||||
void redisLog(int level, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
void redisLog(int level, const char *fmt, ...);
|
||||
#endif
|
||||
void redisLogRaw(int level, const char *msg);
|
||||
void redisLogFromHandler(int level, const char *msg);
|
||||
void usage();
|
||||
@@ -1086,8 +965,6 @@ int htNeedsResize(dict *dict);
|
||||
void oom(const char *msg);
|
||||
void populateCommandTable(void);
|
||||
void resetCommandTableStats(void);
|
||||
void adjustOpenFilesLimit(void);
|
||||
void closeListeningSockets(int unlink_unix_socket);
|
||||
|
||||
/* Set data type */
|
||||
robj *setTypeCreate(robj *value);
|
||||
@@ -1129,11 +1006,6 @@ void freePubsubPattern(void *p);
|
||||
int listMatchPubsubPattern(void *a, void *b);
|
||||
int pubsubPublishMessage(robj *channel, robj *message);
|
||||
|
||||
/* Keyspace events notification */
|
||||
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid);
|
||||
int keyspaceEventsStringToFlags(char *classes);
|
||||
sds keyspaceEventsFlagsToString(int flags);
|
||||
|
||||
/* Configuration */
|
||||
void loadServerConfig(char *filename, char *options);
|
||||
void appendServerSaveParams(time_t seconds, int changes);
|
||||
@@ -1183,7 +1055,6 @@ void scriptingInit(void);
|
||||
/* Git SHA1 */
|
||||
char *redisGitSHA1(void);
|
||||
char *redisGitDirty(void);
|
||||
uint64_t redisBuildId(void);
|
||||
|
||||
/* Commands prototypes */
|
||||
void authCommand(redisClient *c);
|
||||
@@ -1307,7 +1178,6 @@ void unsubscribeCommand(redisClient *c);
|
||||
void psubscribeCommand(redisClient *c);
|
||||
void punsubscribeCommand(redisClient *c);
|
||||
void publishCommand(redisClient *c);
|
||||
void pubsubCommand(redisClient *c);
|
||||
void watchCommand(redisClient *c);
|
||||
void unwatchCommand(redisClient *c);
|
||||
void restoreCommand(redisClient *c);
|
||||
|
||||
@@ -31,11 +31,7 @@
|
||||
* file is recompiled, as we access this information in all the other
|
||||
* files using this functions. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "release.h"
|
||||
#include "version.h"
|
||||
#include "crc64.h"
|
||||
|
||||
char *redisGitSHA1(void) {
|
||||
return REDIS_GIT_SHA1;
|
||||
@@ -44,9 +40,3 @@ char *redisGitSHA1(void) {
|
||||
char *redisGitDirty(void) {
|
||||
return REDIS_GIT_DIRTY;
|
||||
}
|
||||
|
||||
uint64_t redisBuildId(void) {
|
||||
char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1;
|
||||
|
||||
return crc64(0,(unsigned char*)buildid,strlen(buildid));
|
||||
}
|
||||
|
||||
+59
-863
File diff suppressed because it is too large
Load Diff
@@ -48,12 +48,10 @@
|
||||
#include "fmacros.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "rio.h"
|
||||
#include "util.h"
|
||||
#include "crc64.h"
|
||||
#include "config.h"
|
||||
#include "redis.h"
|
||||
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
/* Returns 1 or 0 for success/failure. */
|
||||
static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {
|
||||
@@ -78,18 +76,7 @@ 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) {
|
||||
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;
|
||||
return fwrite(buf,len,1,r->io.file.fp);
|
||||
}
|
||||
|
||||
/* Returns 1 or 0 for success/failure. */
|
||||
@@ -108,8 +95,6 @@ static const rio rioBufferIO = {
|
||||
rioBufferTell,
|
||||
NULL, /* update_checksum */
|
||||
0, /* current checksum */
|
||||
0, /* bytes read or written */
|
||||
0, /* read/write chunk size */
|
||||
{ { NULL, 0 } } /* union for io-specific vars */
|
||||
};
|
||||
|
||||
@@ -119,16 +104,12 @@ static const rio rioFileIO = {
|
||||
rioFileTell,
|
||||
NULL, /* update_checksum */
|
||||
0, /* current checksum */
|
||||
0, /* bytes read or written */
|
||||
0, /* read/write chunk size */
|
||||
{ { NULL, 0 } } /* union for io-specific vars */
|
||||
};
|
||||
|
||||
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) {
|
||||
@@ -143,19 +124,6 @@ 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. */
|
||||
|
||||
@@ -43,22 +43,15 @@ 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 */
|
||||
uint64_t cksum;
|
||||
|
||||
/* number of bytes read or written */
|
||||
size_t processed_bytes;
|
||||
|
||||
/* maximum single read or write chunk size */
|
||||
size_t max_processing_chunk;
|
||||
|
||||
/* Backend-specific vars. */
|
||||
union {
|
||||
struct {
|
||||
@@ -67,8 +60,6 @@ struct _rio {
|
||||
} buffer;
|
||||
struct {
|
||||
FILE *fp;
|
||||
off_t buffered; /* Bytes written since last fsync. */
|
||||
off_t autosync; /* fsync after 'autosync' bytes written. */
|
||||
} file;
|
||||
} io;
|
||||
};
|
||||
@@ -80,29 +71,16 @@ typedef struct _rio rio;
|
||||
* if needed. */
|
||||
|
||||
static inline size_t rioWrite(rio *r, const void *buf, size_t len) {
|
||||
while (len) {
|
||||
size_t bytes_to_write = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;
|
||||
if (r->update_cksum) r->update_cksum(r,buf,bytes_to_write);
|
||||
if (r->write(r,buf,bytes_to_write) == 0)
|
||||
return 0;
|
||||
buf = (char*)buf + bytes_to_write;
|
||||
len -= bytes_to_write;
|
||||
r->processed_bytes += bytes_to_write;
|
||||
}
|
||||
return 1;
|
||||
if (r->update_cksum) r->update_cksum(r,buf,len);
|
||||
return r->write(r,buf,len);
|
||||
}
|
||||
|
||||
static inline size_t rioRead(rio *r, void *buf, size_t len) {
|
||||
while (len) {
|
||||
size_t bytes_to_read = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;
|
||||
if (r->read(r,buf,bytes_to_read) == 0)
|
||||
return 0;
|
||||
if (r->update_cksum) r->update_cksum(r,buf,bytes_to_read);
|
||||
buf = (char*)buf + bytes_to_read;
|
||||
len -= bytes_to_read;
|
||||
r->processed_bytes += bytes_to_read;
|
||||
if (r->read(r,buf,len) == 1) {
|
||||
if (r->update_cksum) r->update_cksum(r,buf,len);
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline off_t rioTell(rio *r) {
|
||||
@@ -118,6 +96,5 @@ 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
|
||||
|
||||
+20
-65
@@ -152,20 +152,9 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) {
|
||||
}
|
||||
|
||||
void luaPushError(lua_State *lua, char *error) {
|
||||
lua_Debug dbg;
|
||||
|
||||
lua_newtable(lua);
|
||||
lua_pushstring(lua,"err");
|
||||
|
||||
/* Attempt to figure out where this function was called, if possible */
|
||||
if(lua_getstack(lua, 1, &dbg) && lua_getinfo(lua, "nSl", &dbg)) {
|
||||
sds msg = sdscatprintf(sdsempty(), "%s: %d: %s",
|
||||
dbg.source, dbg.currentline, error);
|
||||
lua_pushstring(lua, msg);
|
||||
sdsfree(msg);
|
||||
} else {
|
||||
lua_pushstring(lua, error);
|
||||
}
|
||||
lua_pushstring(lua, error);
|
||||
lua_settable(lua,-3);
|
||||
}
|
||||
|
||||
@@ -269,7 +258,6 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
|
||||
"Write commands not allowed after non deterministic commands");
|
||||
goto cleanup;
|
||||
} else if (server.masterhost && server.repl_slave_ro &&
|
||||
!server.loading &&
|
||||
!(server.lua_caller->flags & REDIS_MASTER))
|
||||
{
|
||||
luaPushError(lua, shared.roslaveerr->ptr);
|
||||
@@ -621,26 +609,6 @@ void scriptingInit(void) {
|
||||
lua_pcall(lua,0,0,0);
|
||||
}
|
||||
|
||||
/* Add a helper function we use for pcall error reporting.
|
||||
* Note that when the error is in the C function we want to report the
|
||||
* information about the caller, that's what makes sense from the point
|
||||
* of view of the user debugging a script. */
|
||||
{
|
||||
char *errh_func = "function __redis__err__handler(err)\n"
|
||||
" local i = debug.getinfo(2,'nSl')\n"
|
||||
" if i and i.what == 'C' then\n"
|
||||
" i = debug.getinfo(3,'nSl')\n"
|
||||
" end\n"
|
||||
" if i then\n"
|
||||
" return i.source .. ':' .. i.currentline .. ': ' .. err\n"
|
||||
" else\n"
|
||||
" return err\n"
|
||||
" end\n"
|
||||
"end\n";
|
||||
luaL_loadbuffer(lua,errh_func,strlen(errh_func),"@err_handler_def");
|
||||
lua_pcall(lua,0,0,0);
|
||||
}
|
||||
|
||||
/* Create the (non connected) client that we use to execute Redis commands
|
||||
* inside the Lua interpreter.
|
||||
* Note: there is no need to create it again when this function is called
|
||||
@@ -861,25 +829,21 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
funcname[42] = '\0';
|
||||
}
|
||||
|
||||
/* Push the pcall error handler function on the stack. */
|
||||
lua_getglobal(lua, "__redis__err__handler");
|
||||
|
||||
/* Try to lookup the Lua function */
|
||||
lua_getglobal(lua, funcname);
|
||||
if (lua_isnil(lua,-1)) {
|
||||
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 function. If this is an EVALSHA call we can just
|
||||
* return an error. */
|
||||
if (evalsha) {
|
||||
lua_pop(lua,1); /* remove the error handler from the stack. */
|
||||
addReply(c, shared.noscripterr);
|
||||
return;
|
||||
}
|
||||
if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) return;
|
||||
/* Now the following is guaranteed to return non nil */
|
||||
lua_getglobal(lua, funcname);
|
||||
redisAssert(!lua_isnil(lua,-1));
|
||||
redisAssert(!lua_isnil(lua,1));
|
||||
}
|
||||
|
||||
/* Populate the argv and keys table accordingly to the arguments that
|
||||
@@ -902,11 +866,10 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
delhook = 1;
|
||||
}
|
||||
|
||||
/* At this point whether this script was never seen before or if it was
|
||||
/* 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. */
|
||||
|
||||
err = lua_pcall(lua,0,1,-2);
|
||||
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 */
|
||||
@@ -931,29 +894,23 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
luaReplyToRedisReply(c,lua);
|
||||
}
|
||||
|
||||
/* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
|
||||
* we are sure that the script was already in the context of all the
|
||||
* attached slaves *and* the current AOF file if enabled.
|
||||
/* 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
|
||||
* and in the replication link.
|
||||
*
|
||||
* To do so we use a cache of SHA1s of scripts that we already propagated
|
||||
* as full EVAL, that's called the Replication Script Cache.
|
||||
*
|
||||
* For repliation, everytime a new slave attaches to the master, we need to
|
||||
* flush our cache of scripts that can be replicated as EVALSHA, while
|
||||
* for AOF we need to do so every time we rewrite the AOF file. */
|
||||
* IMPROVEMENT POSSIBLE:
|
||||
* 1) Replicate this command as EVALSHA in the AOF.
|
||||
* 2) Remember what slave already received a given script, and replicate
|
||||
* the EVALSHA against this slaves when possible.
|
||||
*/
|
||||
if (evalsha) {
|
||||
if (!replicationScriptCacheExists(c->argv[1]->ptr)) {
|
||||
/* This script is not in our script cache, replicate it as
|
||||
* EVAL, then add it into the script cache, as from now on
|
||||
* slaves and AOF know about it. */
|
||||
robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);
|
||||
robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);
|
||||
|
||||
replicationScriptCacheAdd(c->argv[1]->ptr);
|
||||
redisAssertWithInfo(c,NULL,script != NULL);
|
||||
rewriteClientCommandArgument(c,0,
|
||||
resetRefCount(createStringObject("EVAL",4)));
|
||||
rewriteClientCommandArgument(c,1,script);
|
||||
}
|
||||
redisAssertWithInfo(c,NULL,script != NULL);
|
||||
rewriteClientCommandArgument(c,0,
|
||||
resetRefCount(createStringObject("EVAL",4)));
|
||||
rewriteClientCommandArgument(c,1,script);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,8 +977,7 @@ void scriptCommand(redisClient *c) {
|
||||
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"flush")) {
|
||||
scriptingReset();
|
||||
addReply(c,shared.ok);
|
||||
replicationScriptCacheFlush();
|
||||
server.dirty++; /* Propagating this command is a good idea. */
|
||||
server.dirty++; /* Replicating this command is a good idea. */
|
||||
} else if (c->argc >= 2 && !strcasecmp(c->argv[1]->ptr,"exists")) {
|
||||
int j;
|
||||
|
||||
@@ -1049,7 +1005,6 @@ void scriptCommand(redisClient *c) {
|
||||
}
|
||||
addReplyBulkCBuffer(c,funcname+2,40);
|
||||
sdsfree(sha);
|
||||
forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF);
|
||||
} else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"kill")) {
|
||||
if (server.lua_caller == NULL) {
|
||||
addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n"));
|
||||
|
||||
@@ -36,18 +36,6 @@
|
||||
#include "sds.h"
|
||||
#include "zmalloc.h"
|
||||
|
||||
/* Create a new sds string with the content specified by the 'init' pointer
|
||||
* and 'initlen'.
|
||||
* If NULL is used for 'init' the string is initialized with zero bytes.
|
||||
*
|
||||
* The string is always null-termined (all the sds strings are, always) so
|
||||
* even if you create an sds string with:
|
||||
*
|
||||
* mystring = sdsnewlen("abc",3");
|
||||
*
|
||||
* You can print the string with printf() as there is an implicit \0 at the
|
||||
* end of the string. However the string is binary safe and can contain
|
||||
* \0 characters in the middle, as the length is stored in the sds header. */
|
||||
sds sdsnewlen(const void *init, size_t initlen) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
@@ -65,43 +53,24 @@ sds sdsnewlen(const void *init, size_t initlen) {
|
||||
return (char*)sh->buf;
|
||||
}
|
||||
|
||||
/* Create an empty (zero length) sds string. Even in this case the string
|
||||
* always has an implicit null term. */
|
||||
sds sdsempty(void) {
|
||||
return sdsnewlen("",0);
|
||||
}
|
||||
|
||||
/* Create a new sds string starting from a null termined C string. */
|
||||
sds sdsnew(const char *init) {
|
||||
size_t initlen = (init == NULL) ? 0 : strlen(init);
|
||||
return sdsnewlen(init, initlen);
|
||||
}
|
||||
|
||||
/* Duplicate an sds string. */
|
||||
sds sdsdup(const sds s) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
|
||||
/* Free an sds string. No operation is performed if 's' is NULL. */
|
||||
void sdsfree(sds s) {
|
||||
if (s == NULL) return;
|
||||
zfree(s-sizeof(struct sdshdr));
|
||||
}
|
||||
|
||||
/* Set the sds string length to the length as obtained with strlen(), so
|
||||
* considering as content only up to the first null term character.
|
||||
*
|
||||
* This function is useful when the sds string is hacked manually in some
|
||||
* way, like in the following example:
|
||||
*
|
||||
* s = sdsnew("foobar");
|
||||
* s[2] = '\0';
|
||||
* sdsupdatelen(s);
|
||||
* printf("%d\n", sdslen(s));
|
||||
*
|
||||
* The output will be "2", but if we comment out the call to sdsupdatelen()
|
||||
* the output will be "6" as the string was modified but the logical length
|
||||
* remains 6 bytes. */
|
||||
void sdsupdatelen(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
@@ -109,10 +78,6 @@ void sdsupdatelen(sds s) {
|
||||
sh->len = reallen;
|
||||
}
|
||||
|
||||
/* Modify an sds string on-place to make it empty (zero length).
|
||||
* However all the existing buffer is not discarded but set as free space
|
||||
* so that next append operations will not require allocations up to the
|
||||
* number of bytes previously available. */
|
||||
void sdsclear(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
sh->free += sh->len;
|
||||
@@ -124,7 +89,7 @@ void sdsclear(sds s) {
|
||||
* is sure that after calling this function can overwrite up to addlen
|
||||
* bytes after the end of the string, plus one more byte for nul term.
|
||||
*
|
||||
* Note: this does not change the *length* of the sds string as returned
|
||||
* Note: this does not change the *size* of the sds string as returned
|
||||
* by sdslen(), but only the free buffer space we have. */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
struct sdshdr *sh, *newsh;
|
||||
@@ -148,10 +113,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
|
||||
/* Reallocate the sds string so that it has no free space at the end. The
|
||||
* contained string remains not altered, but next concatenation operations
|
||||
* will require a reallocation.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
* will require a reallocation. */
|
||||
sds sdsRemoveFreeSpace(sds s) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
@@ -161,13 +123,6 @@ sds sdsRemoveFreeSpace(sds s) {
|
||||
return sh->buf;
|
||||
}
|
||||
|
||||
/* Return the total size of the allocation of the specifed sds string,
|
||||
* including:
|
||||
* 1) The sds header before the pointer.
|
||||
* 2) The string.
|
||||
* 3) The free buffer at the end if any.
|
||||
* 4) The implicit null term.
|
||||
*/
|
||||
size_t sdsAllocSize(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
|
||||
@@ -175,7 +130,7 @@ size_t sdsAllocSize(sds s) {
|
||||
}
|
||||
|
||||
/* Increment the sds length and decrements the left free space at the
|
||||
* end of the string according to 'incr'. Also set the null term
|
||||
* end of the string accordingly to 'incr'. Also set the null term
|
||||
* in the new end of the string.
|
||||
*
|
||||
* This function is used in order to fix the string length after the
|
||||
@@ -185,17 +140,15 @@ size_t sdsAllocSize(sds s) {
|
||||
* Note: it is possible to use a negative increment in order to
|
||||
* right-trim the string.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
|
||||
* following schema, to cat bytes coming from the kernel to the end of an
|
||||
* sds string without copying into an intermediate buffer:
|
||||
* 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);
|
||||
* s = sdsMakeRoomFor(s, BUFFER_SIZE);
|
||||
* nread = read(fd, s+oldlen, BUFFER_SIZE);
|
||||
* ... check for nread <= 0 and handle it ...
|
||||
* sdsIncrLen(s, nread);
|
||||
* sdsIncrLen(s, nhread);
|
||||
*/
|
||||
void sdsIncrLen(sds s, int incr) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
@@ -208,10 +161,7 @@ void sdsIncrLen(sds s, int incr) {
|
||||
}
|
||||
|
||||
/* Grow the sds to have the specified length. Bytes that were not part of
|
||||
* the original length of the sds will be set to zero.
|
||||
*
|
||||
* if the specified length is smaller than the current length, no operation
|
||||
* is performed. */
|
||||
* the original length of the sds will be set to zero. */
|
||||
sds sdsgrowzero(sds s, size_t len) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
size_t totlen, curlen = sh->len;
|
||||
@@ -229,11 +179,6 @@ sds sdsgrowzero(sds s, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
|
||||
* end of the specified sds string 's'.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
struct sdshdr *sh;
|
||||
size_t curlen = sdslen(s);
|
||||
@@ -248,24 +193,14 @@ sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Append the specified null termianted C string to the sds string 's'.
|
||||
*
|
||||
* After the call, the passed sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscat(sds s, const char *t) {
|
||||
return sdscatlen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Append the specified sds 't' to the existing sds 's'.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatsds(sds s, const sds t) {
|
||||
return sdscatlen(s, t, sdslen(t));
|
||||
}
|
||||
|
||||
/* Destructively modify the sds string 's' to hold the specified binary
|
||||
* safe string pointed by 't' of length 'len' bytes. */
|
||||
sds sdscpylen(sds s, const char *t, size_t len) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t totlen = sh->free+sh->len;
|
||||
@@ -283,13 +218,10 @@ sds sdscpylen(sds s, const char *t, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Like sdscpylen() but 't' must be a null-termined string so that the length
|
||||
* of the string is obtained with strlen(). */
|
||||
sds sdscpy(sds s, const char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Like sdscatpritf() but gets va_list instead of being variadic. */
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
va_list cpy;
|
||||
char *buf, *t;
|
||||
@@ -313,22 +245,6 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Append to the sds string 's' a string obtained using printf-alike format
|
||||
* specifier.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsempty("Sum is: ");
|
||||
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
|
||||
*
|
||||
* Often you need to create a string from scratch with the printf-alike
|
||||
* format. When this is the need, just use sdsempty() as the target string:
|
||||
*
|
||||
* s = sdscatprintf(sdsempty(), "... your format ...", args);
|
||||
*/
|
||||
sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char *t;
|
||||
@@ -338,20 +254,6 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Remove the part of the string from left and from right composed just of
|
||||
* contiguous characters found in 'cset', that is a null terminted C string.
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
|
||||
* s = sdstrim(s,"A. :");
|
||||
* printf("%s\n", s);
|
||||
*
|
||||
* Output will be just "Hello World".
|
||||
*/
|
||||
sds sdstrim(sds s, const char *cset) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
char *start, *end, *sp, *ep;
|
||||
@@ -369,27 +271,11 @@ sds sdstrim(sds s, const char *cset) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Turn the string into a smaller (or equal) string containing only the
|
||||
* substring specified by the 'start' and 'end' indexes.
|
||||
*
|
||||
* start and end can be negative, where -1 means the last character of the
|
||||
* string, -2 the penultimate character, and so forth.
|
||||
*
|
||||
* The interval is inclusive, so the start and end characters will be part
|
||||
* of the resulting string.
|
||||
*
|
||||
* The string is modified in-place.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsnew("Hello World");
|
||||
* sdstrim(s,1,-1); => "ello Worl"
|
||||
*/
|
||||
void sdsrange(sds s, int start, int end) {
|
||||
sds sdsrange(sds s, int start, int end) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t newlen, len = sdslen(s);
|
||||
|
||||
if (len == 0) return;
|
||||
if (len == 0) return s;
|
||||
if (start < 0) {
|
||||
start = len+start;
|
||||
if (start < 0) start = 0;
|
||||
@@ -413,33 +299,21 @@ void sdsrange(sds s, int start, int end) {
|
||||
sh->buf[newlen] = 0;
|
||||
sh->free = sh->free+(sh->len-newlen);
|
||||
sh->len = newlen;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Apply tolower() to every character of the sds string 's'. */
|
||||
void sdstolower(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
||||
}
|
||||
|
||||
/* Apply toupper() to every character of the sds string 's'. */
|
||||
void sdstoupper(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
||||
}
|
||||
|
||||
/* Compare two sds strings s1 and s2 with memcmp().
|
||||
*
|
||||
* Return value:
|
||||
*
|
||||
* 1 if s1 > s2.
|
||||
* -1 if s1 < s2.
|
||||
* 0 if s1 and s2 are exactly the same binary string.
|
||||
*
|
||||
* If two strings share exactly the same prefix, but one of the two has
|
||||
* additional characters, the longer string is considered to be greater than
|
||||
* the smaller one. */
|
||||
int sdscmp(const sds s1, const sds s2) {
|
||||
size_t l1, l2, minlen;
|
||||
int cmp;
|
||||
@@ -517,7 +391,6 @@ cleanup:
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
|
||||
void sdsfreesplitres(sds *tokens, int count) {
|
||||
if (!tokens) return;
|
||||
while(count--)
|
||||
@@ -525,10 +398,6 @@ void sdsfreesplitres(sds *tokens, int count) {
|
||||
zfree(tokens);
|
||||
}
|
||||
|
||||
/* Create an sds string from a long long value. It is much faster than:
|
||||
*
|
||||
* sdscatprintf(sdsempty(),"%lld\n", value);
|
||||
*/
|
||||
sds sdsfromlonglong(long long value) {
|
||||
char buf[32], *p;
|
||||
unsigned long long v;
|
||||
@@ -544,12 +413,6 @@ sds sdsfromlonglong(long long value) {
|
||||
return sdsnewlen(p,32-(p-buf));
|
||||
}
|
||||
|
||||
/* Append to the sds string "s" an escaped string representation where
|
||||
* all the non-printable characters (tested with isprint()) are turned into
|
||||
* escapes in the form "\n\r\a...." or "\x<hex-number>".
|
||||
*
|
||||
* After the call, the modified sds string is no longer valid and all the
|
||||
* references must be substituted with the new pointer returned by the call. */
|
||||
sds sdscatrepr(sds s, const char *p, size_t len) {
|
||||
s = sdscatlen(s,"\"",1);
|
||||
while(len--) {
|
||||
@@ -758,19 +621,6 @@ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Join an array of C strings using the specified separator (also a C string).
|
||||
* Returns the result as an sds string. */
|
||||
sds sdsjoin(char **argv, int argc, char *sep) {
|
||||
sds join = sdsempty();
|
||||
int j;
|
||||
|
||||
for (j = 0; j < argc; j++) {
|
||||
join = sdscat(join, argv[j]);
|
||||
if (j != argc-1) join = sdscat(join,sep);
|
||||
}
|
||||
return join;
|
||||
}
|
||||
|
||||
#ifdef SDS_TEST_MAIN
|
||||
#include <stdio.h>
|
||||
#include "testhelp.h"
|
||||
|
||||
@@ -77,7 +77,7 @@ sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdsrange(sds s, int start, int end);
|
||||
sds sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
@@ -89,7 +89,6 @@ sds sdsfromlonglong(long long value);
|
||||
sds sdscatrepr(sds s, const char *p, size_t len);
|
||||
sds *sdssplitargs(const char *line, int *argc);
|
||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
||||
sds sdsjoin(char **argv, int argc, char *sep);
|
||||
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
|
||||
+50
-151
@@ -74,8 +74,6 @@ 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
|
||||
@@ -405,7 +403,7 @@ void initSentinel(void) {
|
||||
/* Initialize various data structures. */
|
||||
sentinel.masters = dictCreate(&instancesDictType,NULL);
|
||||
sentinel.tilt = 0;
|
||||
sentinel.tilt_start_time = 0;
|
||||
sentinel.tilt_start_time = mstime();
|
||||
sentinel.previous_time = mstime();
|
||||
sentinel.running_scripts = 0;
|
||||
sentinel.scripts_queue = listCreate();
|
||||
@@ -426,7 +424,7 @@ sentinelAddr *createSentinelAddr(char *hostname, int port) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) {
|
||||
if (anetResolve(NULL,hostname,buf) == ANET_ERR) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
@@ -835,9 +833,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
|
||||
|
||||
/* For slaves and sentinel we use ip:port as name. */
|
||||
if (flags & (SRI_SLAVE|SRI_SENTINEL)) {
|
||||
snprintf(slavename,sizeof(slavename),
|
||||
strchr(hostname,':') ? "[%s]:%d" : "%s:%d",
|
||||
hostname,port);
|
||||
snprintf(slavename,sizeof(slavename),"%s:%d",hostname,port);
|
||||
name = slavename;
|
||||
}
|
||||
|
||||
@@ -945,9 +941,7 @@ sentinelRedisInstance *sentinelRedisInstanceLookupSlave(
|
||||
sentinelRedisInstance *slave;
|
||||
|
||||
redisAssert(ri->flags & SRI_MASTER);
|
||||
key = sdscatprintf(sdsempty(),
|
||||
strchr(ip,':') ? "[%s]:%d" : "%s:%d",
|
||||
ip,port);
|
||||
key = sdscatprintf(sdsempty(),"%s:%d",ip,port);
|
||||
slave = dictFetchValue(ri->slaves,key);
|
||||
sdsfree(key);
|
||||
return slave;
|
||||
@@ -1136,6 +1130,7 @@ 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;
|
||||
|
||||
@@ -1144,26 +1139,12 @@ 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;
|
||||
@@ -1401,39 +1382,26 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
|
||||
}
|
||||
}
|
||||
|
||||
/* old versions: slave0:<ip>,<port>,<state>
|
||||
* new versions: slave0:ip=127.0.0.1,port=9999,... */
|
||||
/* slave0:<ip>,<port>,<state> */
|
||||
if ((ri->flags & SRI_MASTER) &&
|
||||
sdslen(l) >= 7 &&
|
||||
!memcmp(l,"slave",5) && isdigit(l[5]))
|
||||
{
|
||||
char *ip, *port, *end;
|
||||
|
||||
if (strstr(l,"ip=") == NULL) {
|
||||
/* Old format. */
|
||||
ip = strchr(l,':'); if (!ip) continue;
|
||||
ip++; /* Now ip points to start of ip address. */
|
||||
port = strchr(ip,','); if (!port) continue;
|
||||
*port = '\0'; /* nul term for easy access. */
|
||||
port++; /* Now port points to start of port number. */
|
||||
end = strchr(port,','); if (!end) continue;
|
||||
*end = '\0'; /* nul term for easy access. */
|
||||
} else {
|
||||
/* New format. */
|
||||
ip = strstr(l,"ip="); if (!ip) continue;
|
||||
ip += 3; /* Now ip points to start of ip address. */
|
||||
port = strstr(l,"port="); if (!port) continue;
|
||||
port += 5; /* Now port points to start of port number. */
|
||||
/* Nul term both fields for easy access. */
|
||||
end = strchr(ip,','); if (end) *end = '\0';
|
||||
end = strchr(port,','); if (end) *end = '\0';
|
||||
}
|
||||
ip = strchr(l,':'); if (!ip) continue;
|
||||
ip++; /* Now ip points to start of ip address. */
|
||||
port = strchr(ip,','); if (!port) continue;
|
||||
*port = '\0'; /* nul term for easy access. */
|
||||
port++; /* Now port points to start of port number. */
|
||||
end = strchr(port,','); if (!end) continue;
|
||||
*end = '\0'; /* nul term for easy access. */
|
||||
|
||||
/* Check if we already have this slave into our table,
|
||||
* 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,"%@");
|
||||
}
|
||||
@@ -1478,80 +1446,44 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
|
||||
ri->info_refresh = mstime();
|
||||
sdsfreesplitres(lines,numlines);
|
||||
|
||||
/* ---------------------------- Acting half -----------------------------
|
||||
* Some things will not happen if sentinel.tilt is true, but some will
|
||||
* still be processed. */
|
||||
/* ---------------------------- Acting half ----------------------------- */
|
||||
if (sentinel.tilt) 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle slave -> master role switch. */
|
||||
/* Act if a slave turned into a master. */
|
||||
if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) {
|
||||
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 (!(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 has changed or its the first time we see an INFO output.
|
||||
* 2) RunID hs 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. 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. */
|
||||
* Log the event and remove the slave. */
|
||||
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 (!sentinel.tilt && ri->flags & SRI_PROMOTED) {
|
||||
} else if (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) &&
|
||||
@@ -1567,12 +1499,11 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
|
||||
sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,
|
||||
"start",ri->master->addr,ri->addr);
|
||||
}
|
||||
} 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)))
|
||||
} 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))
|
||||
{
|
||||
/* No failover in progress? Then it is the start of a failover
|
||||
* and we are an observer.
|
||||
@@ -1592,7 +1523,6 @@ 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
|
||||
@@ -1604,10 +1534,6 @@ 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 &&
|
||||
@@ -1638,13 +1564,6 @@ 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) {
|
||||
@@ -1745,13 +1664,9 @@ void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privd
|
||||
|
||||
{
|
||||
int numtokens, port, removed, canfailover;
|
||||
/* Separator changed from ":" to "," in recent versions in order to
|
||||
* play well with IPv6 addresses. For now we make sure to parse both
|
||||
* correctly detecting if there is "," inside the string. */
|
||||
char *sep = strchr(r->element[2]->str,',') ? "," : ":";
|
||||
char **token = sdssplitlen(r->element[2]->str,
|
||||
r->element[2]->len,
|
||||
sep,1,&numtokens);
|
||||
":",1,&numtokens);
|
||||
sentinelRedisInstance *sentinel;
|
||||
|
||||
if (numtokens == 4) {
|
||||
@@ -1845,12 +1760,14 @@ void sentinelPingInstance(sentinelRedisInstance *ri) {
|
||||
(now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD)
|
||||
{
|
||||
/* PUBLISH hello messages only to masters. */
|
||||
char ip[REDIS_IP_STR_LEN];
|
||||
if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) != -1) {
|
||||
char myaddr[REDIS_IP_STR_LEN+128];
|
||||
struct sockaddr_in sa;
|
||||
socklen_t salen = sizeof(sa);
|
||||
|
||||
snprintf(myaddr,sizeof(myaddr),"%s,%d,%s,%d",
|
||||
ip, server.port, server.runid,
|
||||
if (getsockname(ri->cc->c.fd,(struct sockaddr*)&sa,&salen) != -1) {
|
||||
char myaddr[128];
|
||||
|
||||
snprintf(myaddr,sizeof(myaddr),"%s:%d:%s:%d",
|
||||
inet_ntoa(sa.sin_addr), server.port, server.runid,
|
||||
(ri->flags & SRI_CAN_FAILOVER) != 0);
|
||||
retval = redisAsyncCommand(ri->cc,
|
||||
sentinelPublishReplyCallback, NULL, "PUBLISH %s %s",
|
||||
@@ -1918,9 +1835,8 @@ 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) sdsrange(flags,0,-2); /* remove last "," */
|
||||
if (sdslen(flags) != 0) flags = sdsrange(flags,0,-2); /* remove last "," */
|
||||
addReplyBulkCString(c,flags);
|
||||
sdsfree(flags);
|
||||
fields++;
|
||||
@@ -2676,7 +2592,6 @@ 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;
|
||||
|
||||
@@ -2922,28 +2837,12 @@ 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) {
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
/* ==========================================================================
|
||||
* setproctitle.c - Linux/Darwin setproctitle.
|
||||
* --------------------------------------------------------------------------
|
||||
* Copyright (C) 2010 William Ahern
|
||||
* Copyright (C) 2013 Salvatore Sanfilippo
|
||||
* Copyright (C) 2013 Stam He
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do so, subject to the
|
||||
* following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ==========================================================================
|
||||
*/
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stddef.h> /* NULL size_t */
|
||||
#include <stdarg.h> /* va_list va_start va_end */
|
||||
#include <stdlib.h> /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */
|
||||
#include <stdio.h> /* vsnprintf(3) snprintf(3) */
|
||||
|
||||
#include <string.h> /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */
|
||||
|
||||
#include <errno.h> /* errno program_invocation_name program_invocation_short_name */
|
||||
|
||||
#if !defined(HAVE_SETPROCTITLE)
|
||||
#define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
|
||||
#endif
|
||||
|
||||
|
||||
#if !HAVE_SETPROCTITLE
|
||||
#if (defined __linux || defined __APPLE__)
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static struct {
|
||||
/* original value */
|
||||
const char *arg0;
|
||||
|
||||
/* title space available */
|
||||
char *base, *end;
|
||||
|
||||
/* pointer to original nul character within base */
|
||||
char *nul;
|
||||
|
||||
_Bool reset;
|
||||
int error;
|
||||
} SPT;
|
||||
|
||||
|
||||
#ifndef SPT_MIN
|
||||
#define SPT_MIN(a, b) (((a) < (b))? (a) : (b))
|
||||
#endif
|
||||
|
||||
static inline size_t spt_min(size_t a, size_t b) {
|
||||
return SPT_MIN(a, b);
|
||||
} /* spt_min() */
|
||||
|
||||
|
||||
/*
|
||||
* For discussion on the portability of the various methods, see
|
||||
* http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
|
||||
*/
|
||||
static int spt_clearenv(void) {
|
||||
#if __GLIBC__
|
||||
clearenv();
|
||||
|
||||
return 0;
|
||||
#else
|
||||
extern char **environ;
|
||||
static char **tmp;
|
||||
|
||||
if (!(tmp = malloc(sizeof *tmp)))
|
||||
return errno;
|
||||
|
||||
tmp[0] = NULL;
|
||||
environ = tmp;
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
} /* spt_clearenv() */
|
||||
|
||||
|
||||
static int spt_copyenv(char *oldenv[]) {
|
||||
extern char **environ;
|
||||
char *eq;
|
||||
int i, error;
|
||||
|
||||
if (environ != oldenv)
|
||||
return 0;
|
||||
|
||||
if ((error = spt_clearenv()))
|
||||
goto error;
|
||||
|
||||
for (i = 0; oldenv[i]; i++) {
|
||||
if (!(eq = strchr(oldenv[i], '=')))
|
||||
continue;
|
||||
|
||||
*eq = '\0';
|
||||
error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;
|
||||
*eq = '=';
|
||||
|
||||
if (error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
environ = oldenv;
|
||||
|
||||
return error;
|
||||
} /* spt_copyenv() */
|
||||
|
||||
|
||||
static int spt_copyargs(int argc, char *argv[]) {
|
||||
char *tmp;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc || (i >= argc && argv[i]); i++) {
|
||||
if (!argv[i])
|
||||
continue;
|
||||
|
||||
if (!(tmp = strdup(argv[i])))
|
||||
return errno;
|
||||
|
||||
argv[i] = tmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} /* spt_copyargs() */
|
||||
|
||||
|
||||
void spt_init(int argc, char *argv[]) {
|
||||
char **envp = environ;
|
||||
char *base, *end, *nul, *tmp;
|
||||
int i, error;
|
||||
|
||||
if (!(base = argv[0]))
|
||||
return;
|
||||
|
||||
nul = &base[strlen(base)];
|
||||
end = nul + 1;
|
||||
|
||||
for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
|
||||
if (!argv[i] || argv[i] < end)
|
||||
continue;
|
||||
|
||||
end = argv[i] + strlen(argv[i]) + 1;
|
||||
}
|
||||
|
||||
for (i = 0; envp[i]; i++) {
|
||||
if (envp[i] < end)
|
||||
continue;
|
||||
|
||||
end = envp[i] + strlen(envp[i]) + 1;
|
||||
}
|
||||
|
||||
if (!(SPT.arg0 = strdup(argv[0])))
|
||||
goto syerr;
|
||||
|
||||
#if __GLIBC__
|
||||
if (!(tmp = strdup(program_invocation_name)))
|
||||
goto syerr;
|
||||
|
||||
program_invocation_name = tmp;
|
||||
|
||||
if (!(tmp = strdup(program_invocation_short_name)))
|
||||
goto syerr;
|
||||
|
||||
program_invocation_short_name = tmp;
|
||||
#elif __APPLE__
|
||||
if (!(tmp = strdup(getprogname())))
|
||||
goto syerr;
|
||||
|
||||
setprogname(tmp);
|
||||
#endif
|
||||
|
||||
|
||||
if ((error = spt_copyenv(envp)))
|
||||
goto error;
|
||||
|
||||
if ((error = spt_copyargs(argc, argv)))
|
||||
goto error;
|
||||
|
||||
SPT.nul = nul;
|
||||
SPT.base = base;
|
||||
SPT.end = end;
|
||||
|
||||
return;
|
||||
syerr:
|
||||
error = errno;
|
||||
error:
|
||||
SPT.error = error;
|
||||
} /* spt_init() */
|
||||
|
||||
|
||||
#ifndef SPT_MAXTITLE
|
||||
#define SPT_MAXTITLE 255
|
||||
#endif
|
||||
|
||||
void setproctitle(const char *fmt, ...) {
|
||||
char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */
|
||||
va_list ap;
|
||||
char *nul;
|
||||
int len, error;
|
||||
|
||||
if (!SPT.base)
|
||||
return;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof buf, fmt, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
len = snprintf(buf, sizeof buf, "%s", SPT.arg0);
|
||||
}
|
||||
|
||||
if (len <= 0)
|
||||
{ error = errno; goto error; }
|
||||
|
||||
if (!SPT.reset) {
|
||||
memset(SPT.base, 0, SPT.end - SPT.base);
|
||||
SPT.reset = 1;
|
||||
} else {
|
||||
memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base));
|
||||
}
|
||||
|
||||
len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1);
|
||||
memcpy(SPT.base, buf, len);
|
||||
nul = &SPT.base[len];
|
||||
|
||||
if (nul < SPT.nul) {
|
||||
*SPT.nul = '.';
|
||||
} else if (nul == SPT.nul && &nul[1] < SPT.end) {
|
||||
*SPT.nul = ' ';
|
||||
*++nul = '\0';
|
||||
}
|
||||
|
||||
return;
|
||||
error:
|
||||
SPT.error = error;
|
||||
} /* setproctitle() */
|
||||
|
||||
|
||||
#endif /* __linux || __APPLE__ */
|
||||
#endif /* !HAVE_SETPROCTITLE */
|
||||
+5
-17
@@ -163,22 +163,12 @@ int sortCompare(const void *s1, const void *s2) {
|
||||
else
|
||||
cmp = 1;
|
||||
} else {
|
||||
/* We have both the objects, compare them. */
|
||||
if (server.sort_store) {
|
||||
cmp = compareStringObjects(so1->u.cmpobj,so2->u.cmpobj);
|
||||
} else {
|
||||
/* Here we can use strcoll() directly as we are sure that
|
||||
* the objects are decoded string objects. */
|
||||
cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
|
||||
}
|
||||
/* We have both the objects, use strcoll */
|
||||
cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
|
||||
}
|
||||
} else {
|
||||
/* Compare elements directly. */
|
||||
if (server.sort_store) {
|
||||
cmp = compareStringObjects(so1->obj,so2->obj);
|
||||
} else {
|
||||
cmp = collateStringObjects(so1->obj,so2->obj);
|
||||
}
|
||||
cmp = compareStringObjects(so1->obj,so2->obj);
|
||||
}
|
||||
}
|
||||
return server.sort_desc ? -cmp : cmp;
|
||||
@@ -442,7 +432,6 @@ void sortCommand(redisClient *c) {
|
||||
server.sort_desc = desc;
|
||||
server.sort_alpha = alpha;
|
||||
server.sort_bypattern = sortby ? 1 : 0;
|
||||
server.sort_store = storekey ? 1 : 0;
|
||||
if (sortby && (start != 0 || end != vectorlen-1))
|
||||
pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
|
||||
else
|
||||
@@ -515,12 +504,9 @@ void sortCommand(redisClient *c) {
|
||||
}
|
||||
if (outputlen) {
|
||||
setKey(c->db,storekey,sobj);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"sortstore",storekey,
|
||||
c->db->id);
|
||||
server.dirty += outputlen;
|
||||
} else if (dbDelete(c->db,storekey)) {
|
||||
signalModifiedKey(c->db,storekey);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",storekey,c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
decrRefCount(sobj);
|
||||
@@ -539,3 +525,5 @@ void sortCommand(redisClient *c) {
|
||||
}
|
||||
zfree(vector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-11
@@ -474,7 +474,6 @@ void hsetCommand(redisClient *c) {
|
||||
update = hashTypeSet(o,c->argv[2],c->argv[3]);
|
||||
addReply(c, update ? shared.czero : shared.cone);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
|
||||
@@ -490,7 +489,6 @@ void hsetnxCommand(redisClient *c) {
|
||||
hashTypeSet(o,c->argv[2],c->argv[3]);
|
||||
addReply(c, shared.cone);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
}
|
||||
@@ -512,7 +510,6 @@ void hmsetCommand(redisClient *c) {
|
||||
}
|
||||
addReply(c, shared.ok);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
|
||||
@@ -546,7 +543,6 @@ void hincrbyCommand(redisClient *c) {
|
||||
decrRefCount(new);
|
||||
addReplyLongLong(c,value);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
|
||||
@@ -573,7 +569,6 @@ void hincrbyfloatCommand(redisClient *c) {
|
||||
hashTypeSet(o,c->argv[2],new);
|
||||
addReplyBulk(c,new);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
|
||||
/* Always replicate HINCRBYFLOAT as an HSET command with the final value
|
||||
@@ -654,7 +649,7 @@ void hmgetCommand(redisClient *c) {
|
||||
|
||||
void hdelCommand(redisClient *c) {
|
||||
robj *o;
|
||||
int j, deleted = 0, keyremoved = 0;
|
||||
int j, deleted = 0;
|
||||
|
||||
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,REDIS_HASH)) return;
|
||||
@@ -664,17 +659,12 @@ void hdelCommand(redisClient *c) {
|
||||
deleted++;
|
||||
if (hashTypeLength(o) == 0) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
keyremoved = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleted) {
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
|
||||
if (keyremoved)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],
|
||||
c->db->id);
|
||||
server.dirty += deleted;
|
||||
}
|
||||
addReplyLongLong(c,deleted);
|
||||
|
||||
+10
-43
@@ -275,7 +275,7 @@ void listTypeConvert(robj *subject, int enc) {
|
||||
|
||||
if (enc == REDIS_ENCODING_LINKEDLIST) {
|
||||
list *l = listCreate();
|
||||
listSetFreeMethod(l,decrRefCountVoid);
|
||||
listSetFreeMethod(l,decrRefCount);
|
||||
|
||||
/* listTypeGet returns a robj with incremented refcount */
|
||||
li = listTypeInitIterator(subject,0,REDIS_TAIL);
|
||||
@@ -316,12 +316,7 @@ void pushGenericCommand(redisClient *c, int where) {
|
||||
pushed++;
|
||||
}
|
||||
addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0));
|
||||
if (pushed) {
|
||||
char *event = (where == REDIS_HEAD) ? "lpush" : "rpush";
|
||||
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);
|
||||
}
|
||||
if (pushed) signalModifiedKey(c->db,c->argv[1]);
|
||||
server.dirty += pushed;
|
||||
}
|
||||
|
||||
@@ -343,6 +338,10 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
||||
checkType(c,subject,REDIS_LIST)) return;
|
||||
|
||||
if (refval != NULL) {
|
||||
/* Note: we expect refval to be string-encoded because it is *not* the
|
||||
* last argument of the multi-bulk LINSERT. */
|
||||
redisAssertWithInfo(c,refval,refval->encoding == REDIS_ENCODING_RAW);
|
||||
|
||||
/* We're not sure if this value can be inserted yet, but we cannot
|
||||
* convert the list inside the iterator. We don't want to loop over
|
||||
* the list twice (once to see if the value can be inserted and once
|
||||
@@ -367,8 +366,6 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
||||
ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
|
||||
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"linsert",
|
||||
c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
} else {
|
||||
/* Notify client of a failed insert */
|
||||
@@ -376,11 +373,8 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
char *event = (where == REDIS_HEAD) ? "lpush" : "rpush";
|
||||
|
||||
listTypePush(subject,val,where);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
|
||||
@@ -475,7 +469,6 @@ void lsetCommand(redisClient *c) {
|
||||
decrRefCount(value);
|
||||
addReply(c,shared.ok);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
|
||||
@@ -488,7 +481,6 @@ void lsetCommand(redisClient *c) {
|
||||
incrRefCount(value);
|
||||
addReply(c,shared.ok);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
} else {
|
||||
@@ -504,16 +496,9 @@ void popGenericCommand(redisClient *c, int where) {
|
||||
if (value == NULL) {
|
||||
addReply(c,shared.nullbulk);
|
||||
} else {
|
||||
char *event = (where == REDIS_HEAD) ? "lpop" : "rpop";
|
||||
|
||||
addReplyBulk(c,value);
|
||||
decrRefCount(value);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);
|
||||
if (listTypeLength(o) == 0) {
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
|
||||
c->argv[1],c->db->id);
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
}
|
||||
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
server.dirty++;
|
||||
}
|
||||
@@ -633,12 +618,7 @@ void ltrimCommand(redisClient *c) {
|
||||
} else {
|
||||
redisPanic("Unknown list encoding");
|
||||
}
|
||||
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"ltrim",c->argv[1],c->db->id);
|
||||
if (listTypeLength(o) == 0) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
|
||||
}
|
||||
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
server.dirty++;
|
||||
addReply(c,shared.ok);
|
||||
@@ -713,7 +693,6 @@ void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value
|
||||
}
|
||||
signalModifiedKey(c->db,dstkey);
|
||||
listTypePush(dstobj,value,REDIS_HEAD);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lpush",dstkey,c->db->id);
|
||||
/* Always send the pushed value to the client. */
|
||||
addReplyBulk(c,value);
|
||||
}
|
||||
@@ -743,12 +722,7 @@ void rpoplpushCommand(redisClient *c) {
|
||||
decrRefCount(value);
|
||||
|
||||
/* Delete the source list when it is empty */
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"rpop",touchedkey,c->db->id);
|
||||
if (listTypeLength(sobj) == 0) {
|
||||
dbDelete(c->db,touchedkey);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
|
||||
touchedkey,c->db->id);
|
||||
}
|
||||
if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey);
|
||||
signalModifiedKey(c->db,touchedkey);
|
||||
decrRefCount(touchedkey);
|
||||
server.dirty++;
|
||||
@@ -1072,7 +1046,6 @@ void blockingPopGenericCommand(redisClient *c, int where) {
|
||||
} else {
|
||||
if (listTypeLength(o) != 0) {
|
||||
/* Non empty list, this is like a non normal [LR]POP. */
|
||||
char *event = (where == REDIS_HEAD) ? "lpop" : "rpop";
|
||||
robj *value = listTypePop(o,where);
|
||||
redisAssert(value != NULL);
|
||||
|
||||
@@ -1080,13 +1053,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
|
||||
addReplyBulk(c,c->argv[j]);
|
||||
addReplyBulk(c,value);
|
||||
decrRefCount(value);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,
|
||||
c->argv[j],c->db->id);
|
||||
if (listTypeLength(o) == 0) {
|
||||
dbDelete(c->db,c->argv[j]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
|
||||
c->argv[j],c->db->id);
|
||||
}
|
||||
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[j]);
|
||||
signalModifiedKey(c->db,c->argv[j]);
|
||||
server.dirty++;
|
||||
|
||||
|
||||
+23
-134
@@ -266,17 +266,14 @@ void saddCommand(redisClient *c) {
|
||||
c->argv[j] = tryObjectEncoding(c->argv[j]);
|
||||
if (setTypeAdd(set,c->argv[j])) added++;
|
||||
}
|
||||
if (added) {
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
|
||||
}
|
||||
if (added) signalModifiedKey(c->db,c->argv[1]);
|
||||
server.dirty += added;
|
||||
addReplyLongLong(c,added);
|
||||
}
|
||||
|
||||
void sremCommand(redisClient *c) {
|
||||
robj *set;
|
||||
int j, deleted = 0, keyremoved = 0;
|
||||
int j, deleted = 0;
|
||||
|
||||
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,set,REDIS_SET)) return;
|
||||
@@ -286,17 +283,12 @@ void sremCommand(redisClient *c) {
|
||||
deleted++;
|
||||
if (setTypeSize(set) == 0) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
keyremoved = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleted) {
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
|
||||
if (keyremoved)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],
|
||||
c->db->id);
|
||||
server.dirty += deleted;
|
||||
}
|
||||
addReplyLongLong(c,deleted);
|
||||
@@ -330,13 +322,9 @@ void smoveCommand(redisClient *c) {
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
|
||||
|
||||
/* Remove the src set from the database when empty */
|
||||
if (setTypeSize(srcset) == 0) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
|
||||
}
|
||||
if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[2]);
|
||||
server.dirty++;
|
||||
@@ -348,10 +336,7 @@ void smoveCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
/* An extra key has changed when ele was successfully added to dstset */
|
||||
if (setTypeAdd(dstset,ele)) {
|
||||
server.dirty++;
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[2],c->db->id);
|
||||
}
|
||||
if (setTypeAdd(dstset,ele)) server.dirty++;
|
||||
addReply(c,shared.cone);
|
||||
}
|
||||
|
||||
@@ -393,7 +378,6 @@ void spopCommand(redisClient *c) {
|
||||
incrRefCount(ele);
|
||||
setTypeRemove(set,ele);
|
||||
}
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id);
|
||||
|
||||
/* Replicate/AOF this command as an SREM operation */
|
||||
aux = createStringObject("SREM",4);
|
||||
@@ -402,10 +386,7 @@ void spopCommand(redisClient *c) {
|
||||
decrRefCount(aux);
|
||||
|
||||
addReplyBulk(c,ele);
|
||||
if (setTypeSize(set) == 0) {
|
||||
dbDelete(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
|
||||
}
|
||||
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
server.dirty++;
|
||||
}
|
||||
@@ -584,14 +565,6 @@ int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
||||
return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);
|
||||
}
|
||||
|
||||
/* This is used by SDIFF and in this case we can receive NULL that should
|
||||
* be handled as empty sets. */
|
||||
int qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {
|
||||
robj *o1 = *(robj**)s1, *o2 = *(robj**)s2;
|
||||
|
||||
return (o2 ? setTypeSize(o2) : 0) - (o1 ? setTypeSize(o1) : 0);
|
||||
}
|
||||
|
||||
void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
|
||||
robj **sets = zmalloc(sizeof(robj*)*setnum);
|
||||
setTypeIterator *si;
|
||||
@@ -706,18 +679,13 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
|
||||
if (dstkey) {
|
||||
/* Store the resulting set into the target, if the intersection
|
||||
* is not an empty set. */
|
||||
int deleted = dbDelete(c->db,dstkey);
|
||||
dbDelete(c->db,dstkey);
|
||||
if (setTypeSize(dstset) > 0) {
|
||||
dbAdd(c->db,dstkey,dstset);
|
||||
addReplyLongLong(c,setTypeSize(dstset));
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sinterstore",
|
||||
dstkey,c->db->id);
|
||||
} else {
|
||||
decrRefCount(dstset);
|
||||
addReply(c,shared.czero);
|
||||
if (deleted)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
|
||||
dstkey,c->db->id);
|
||||
}
|
||||
signalModifiedKey(c->db,dstkey);
|
||||
server.dirty++;
|
||||
@@ -744,7 +712,6 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
|
||||
setTypeIterator *si;
|
||||
robj *ele, *dstset = NULL;
|
||||
int j, cardinality = 0;
|
||||
int diff_algo = 1;
|
||||
|
||||
for (j = 0; j < setnum; j++) {
|
||||
robj *setobj = dstkey ?
|
||||
@@ -761,106 +728,34 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
|
||||
sets[j] = setobj;
|
||||
}
|
||||
|
||||
/* Select what DIFF algorithm to use.
|
||||
*
|
||||
* Algorithm 1 is O(N*M) where N is the size of the element first set
|
||||
* and M the total number of sets.
|
||||
*
|
||||
* Algorithm 2 is O(N) where N is the total number of elements in all
|
||||
* the sets.
|
||||
*
|
||||
* We compute what is the best bet with the current input here. */
|
||||
if (op == REDIS_OP_DIFF && sets[0]) {
|
||||
long long algo_one_work = 0, algo_two_work = 0;
|
||||
|
||||
for (j = 0; j < setnum; j++) {
|
||||
if (sets[j] == NULL) continue;
|
||||
|
||||
algo_one_work += setTypeSize(sets[0]);
|
||||
algo_two_work += setTypeSize(sets[j]);
|
||||
}
|
||||
|
||||
/* Algorithm 1 has better constant times and performs less operations
|
||||
* if there are elements in common. Give it some advantage. */
|
||||
algo_one_work /= 2;
|
||||
diff_algo = (algo_one_work <= algo_two_work) ? 1 : 2;
|
||||
|
||||
if (diff_algo == 1 && setnum > 1) {
|
||||
/* With algorithm 1 it is better to order the sets to subtract
|
||||
* by decreasing size, so that we are more likely to find
|
||||
* duplicated elements ASAP. */
|
||||
qsort(sets+1,setnum-1,sizeof(robj*),
|
||||
qsortCompareSetsByRevCardinality);
|
||||
}
|
||||
}
|
||||
|
||||
/* We need a temp set object to store our union. If the dstkey
|
||||
* is not NULL (that is, we are inside an SUNIONSTORE operation) then
|
||||
* this set object will be the resulting object to set into the target key*/
|
||||
dstset = createIntsetObject();
|
||||
|
||||
if (op == REDIS_OP_UNION) {
|
||||
/* Union is trivial, just add every element of every set to the
|
||||
* temporary set. */
|
||||
for (j = 0; j < setnum; j++) {
|
||||
if (!sets[j]) continue; /* non existing keys are like empty sets */
|
||||
/* Iterate all the elements of all the sets, add every element a single
|
||||
* time to the result set */
|
||||
for (j = 0; j < setnum; j++) {
|
||||
if (op == REDIS_OP_DIFF && j == 0 && !sets[j]) break; /* result set is empty */
|
||||
if (!sets[j]) continue; /* non existing keys are like empty sets */
|
||||
|
||||
si = setTypeInitIterator(sets[j]);
|
||||
while((ele = setTypeNextObject(si)) != NULL) {
|
||||
if (setTypeAdd(dstset,ele)) cardinality++;
|
||||
decrRefCount(ele);
|
||||
}
|
||||
setTypeReleaseIterator(si);
|
||||
}
|
||||
} else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 1) {
|
||||
/* DIFF Algorithm 1:
|
||||
*
|
||||
* We perform the diff by iterating all the elements of the first set,
|
||||
* and only adding it to the target set if the element does not exist
|
||||
* into all the other sets.
|
||||
*
|
||||
* This way we perform at max N*M operations, where N is the size of
|
||||
* the first set, and M the number of sets. */
|
||||
si = setTypeInitIterator(sets[0]);
|
||||
si = setTypeInitIterator(sets[j]);
|
||||
while((ele = setTypeNextObject(si)) != NULL) {
|
||||
for (j = 1; j < setnum; j++) {
|
||||
if (!sets[j]) continue; /* no key is an empty set. */
|
||||
if (setTypeIsMember(sets[j],ele)) break;
|
||||
}
|
||||
if (j == setnum) {
|
||||
/* There is no other set with this element. Add it. */
|
||||
setTypeAdd(dstset,ele);
|
||||
cardinality++;
|
||||
if (op == REDIS_OP_UNION || j == 0) {
|
||||
if (setTypeAdd(dstset,ele)) {
|
||||
cardinality++;
|
||||
}
|
||||
} else if (op == REDIS_OP_DIFF) {
|
||||
if (setTypeRemove(dstset,ele)) {
|
||||
cardinality--;
|
||||
}
|
||||
}
|
||||
decrRefCount(ele);
|
||||
}
|
||||
setTypeReleaseIterator(si);
|
||||
} else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 2) {
|
||||
/* DIFF Algorithm 2:
|
||||
*
|
||||
* Add all the elements of the first set to the auxiliary set.
|
||||
* Then remove all the elements of all the next sets from it.
|
||||
*
|
||||
* This is O(N) where N is the sum of all the elements in every
|
||||
* set. */
|
||||
for (j = 0; j < setnum; j++) {
|
||||
if (!sets[j]) continue; /* non existing keys are like empty sets */
|
||||
|
||||
si = setTypeInitIterator(sets[j]);
|
||||
while((ele = setTypeNextObject(si)) != NULL) {
|
||||
if (j == 0) {
|
||||
if (setTypeAdd(dstset,ele)) cardinality++;
|
||||
} else {
|
||||
if (setTypeRemove(dstset,ele)) cardinality--;
|
||||
}
|
||||
decrRefCount(ele);
|
||||
}
|
||||
setTypeReleaseIterator(si);
|
||||
|
||||
/* Exit if result set is empty as any additional removal
|
||||
* of elements will have no effect. */
|
||||
if (cardinality == 0) break;
|
||||
}
|
||||
/* Exit when result set is empty. */
|
||||
if (op == REDIS_OP_DIFF && cardinality == 0) break;
|
||||
}
|
||||
|
||||
/* Output the content of the resulting set, if not in STORE mode */
|
||||
@@ -876,19 +771,13 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
|
||||
} else {
|
||||
/* If we have a target key where to store the resulting set
|
||||
* create this key with the result set inside */
|
||||
int deleted = dbDelete(c->db,dstkey);
|
||||
dbDelete(c->db,dstkey);
|
||||
if (setTypeSize(dstset) > 0) {
|
||||
dbAdd(c->db,dstkey,dstset);
|
||||
addReplyLongLong(c,setTypeSize(dstset));
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_SET,
|
||||
op == REDIS_OP_UNION ? "sunionstore" : "sdiffstore",
|
||||
dstkey,c->db->id);
|
||||
} else {
|
||||
decrRefCount(dstset);
|
||||
addReply(c,shared.czero);
|
||||
if (deleted)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
|
||||
dstkey,c->db->id);
|
||||
}
|
||||
signalModifiedKey(c->db,dstkey);
|
||||
server.dirty++;
|
||||
|
||||
@@ -84,9 +84,6 @@ void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *ex
|
||||
setKey(c->db,key,val);
|
||||
server.dirty++;
|
||||
if (expire) setExpire(c->db,key,mstime()+milliseconds);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id);
|
||||
if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
|
||||
"expire",key,c->db->id);
|
||||
addReply(c, ok_reply ? ok_reply : shared.ok);
|
||||
}
|
||||
|
||||
@@ -165,7 +162,6 @@ void getsetCommand(redisClient *c) {
|
||||
if (getGenericCommand(c) == REDIS_ERR) return;
|
||||
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||
setKey(c->db,c->argv[1],c->argv[2]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
|
||||
@@ -227,8 +223,6 @@ void setrangeCommand(redisClient *c) {
|
||||
o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
|
||||
memcpy((char*)o->ptr+offset,value,sdslen(value));
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,
|
||||
"setrange",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
addReplyLongLong(c,sdslen(o->ptr));
|
||||
@@ -313,7 +307,6 @@ void msetGenericCommand(redisClient *c, int nx) {
|
||||
for (j = 1; j < c->argc; j += 2) {
|
||||
c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
|
||||
setKey(c->db,c->argv[j],c->argv[j+1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",c->argv[j],c->db->id);
|
||||
}
|
||||
server.dirty += (c->argc-1)/2;
|
||||
addReply(c, nx ? shared.cone : shared.ok);
|
||||
@@ -348,7 +341,6 @@ void incrDecrCommand(redisClient *c, long long incr) {
|
||||
else
|
||||
dbAdd(c->db,c->argv[1],new);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
addReply(c,shared.colon);
|
||||
addReply(c,new);
|
||||
@@ -398,7 +390,6 @@ void incrbyfloatCommand(redisClient *c) {
|
||||
else
|
||||
dbAdd(c->db,c->argv[1],new);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
addReplyBulk(c,new);
|
||||
|
||||
@@ -446,7 +437,6 @@ void appendCommand(redisClient *c) {
|
||||
totlen = sdslen(o->ptr);
|
||||
}
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"append",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
addReplyLongLong(c,totlen);
|
||||
}
|
||||
|
||||
+33
-57
@@ -844,9 +844,9 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
robj *ele;
|
||||
robj *zobj;
|
||||
robj *curobj;
|
||||
double score = 0, *scores = NULL, curscore = 0.0;
|
||||
double score = 0, *scores, curscore = 0.0;
|
||||
int j, elements = (c->argc-2)/2;
|
||||
int added = 0, updated = 0;
|
||||
int added = 0;
|
||||
|
||||
if (c->argc % 2) {
|
||||
addReply(c,shared.syntaxerr);
|
||||
@@ -859,7 +859,11 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
scores = zmalloc(sizeof(double)*elements);
|
||||
for (j = 0; j < elements; j++) {
|
||||
if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)
|
||||
!= REDIS_OK) goto cleanup;
|
||||
!= REDIS_OK)
|
||||
{
|
||||
zfree(scores);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lookup the key and create the sorted set if does not exist. */
|
||||
@@ -876,7 +880,8 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
} else {
|
||||
if (zobj->type != REDIS_ZSET) {
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
goto cleanup;
|
||||
zfree(scores);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,7 +898,10 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
score += curscore;
|
||||
if (isnan(score)) {
|
||||
addReplyError(c,nanerr);
|
||||
goto cleanup;
|
||||
/* Don't need to check if the sorted set is empty
|
||||
* because we know it has at least one element. */
|
||||
zfree(scores);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,8 +909,9 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
if (score != curscore) {
|
||||
zobj->ptr = zzlDelete(zobj->ptr,eptr);
|
||||
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
|
||||
|
||||
signalModifiedKey(c->db,key);
|
||||
server.dirty++;
|
||||
updated++;
|
||||
}
|
||||
} else {
|
||||
/* Optimize: check if the element is too large or the list
|
||||
@@ -912,8 +921,10 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
|
||||
if (sdslen(ele->ptr) > server.zset_max_ziplist_value)
|
||||
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
|
||||
|
||||
signalModifiedKey(c->db,key);
|
||||
server.dirty++;
|
||||
added++;
|
||||
if (!incr) added++;
|
||||
}
|
||||
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = zobj->ptr;
|
||||
@@ -932,7 +943,8 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
addReplyError(c,nanerr);
|
||||
/* Don't need to check if the sorted set is empty
|
||||
* because we know it has at least one element. */
|
||||
goto cleanup;
|
||||
zfree(scores);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,33 +956,29 @@ void zaddGenericCommand(redisClient *c, int incr) {
|
||||
znode = zslInsert(zs->zsl,score,curobj);
|
||||
incrRefCount(curobj); /* Re-inserted in skiplist. */
|
||||
dictGetVal(de) = &znode->score; /* Update score ptr. */
|
||||
|
||||
signalModifiedKey(c->db,key);
|
||||
server.dirty++;
|
||||
updated++;
|
||||
}
|
||||
} else {
|
||||
znode = zslInsert(zs->zsl,score,ele);
|
||||
incrRefCount(ele); /* Inserted in skiplist. */
|
||||
redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
|
||||
incrRefCount(ele); /* Added to dictionary. */
|
||||
|
||||
signalModifiedKey(c->db,key);
|
||||
server.dirty++;
|
||||
added++;
|
||||
if (!incr) added++;
|
||||
}
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
}
|
||||
zfree(scores);
|
||||
if (incr) /* ZINCRBY */
|
||||
addReplyDouble(c,score);
|
||||
else /* ZADD */
|
||||
addReplyLongLong(c,added);
|
||||
|
||||
cleanup:
|
||||
zfree(scores);
|
||||
if (added || updated) {
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,
|
||||
incr ? "zincr" : "zadd", key, c->db->id);
|
||||
}
|
||||
}
|
||||
|
||||
void zaddCommand(redisClient *c) {
|
||||
@@ -984,7 +992,7 @@ void zincrbyCommand(redisClient *c) {
|
||||
void zremCommand(redisClient *c) {
|
||||
robj *key = c->argv[1];
|
||||
robj *zobj;
|
||||
int deleted = 0, keyremoved = 0, j;
|
||||
int deleted = 0, j;
|
||||
|
||||
if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
|
||||
checkType(c,zobj,REDIS_ZSET)) return;
|
||||
@@ -1030,9 +1038,6 @@ void zremCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrem",key,c->db->id);
|
||||
if (keyremoved)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
signalModifiedKey(c->db,key);
|
||||
server.dirty += deleted;
|
||||
}
|
||||
@@ -1043,7 +1048,6 @@ void zremrangebyscoreCommand(redisClient *c) {
|
||||
robj *key = c->argv[1];
|
||||
robj *zobj;
|
||||
zrangespec range;
|
||||
int keyremoved = 0;
|
||||
unsigned long deleted;
|
||||
|
||||
/* Parse the range arguments. */
|
||||
@@ -1057,28 +1061,17 @@ void zremrangebyscoreCommand(redisClient *c) {
|
||||
|
||||
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,range,&deleted);
|
||||
if (zzlLength(zobj->ptr) == 0) {
|
||||
dbDelete(c->db,key);
|
||||
keyremoved = 1;
|
||||
}
|
||||
if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key);
|
||||
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = zobj->ptr;
|
||||
deleted = zslDeleteRangeByScore(zs->zsl,range,zs->dict);
|
||||
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
|
||||
if (dictSize(zs->dict) == 0) {
|
||||
dbDelete(c->db,key);
|
||||
keyremoved = 1;
|
||||
}
|
||||
if (dictSize(zs->dict) == 0) dbDelete(c->db,key);
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrembyscore",key,c->db->id);
|
||||
if (keyremoved)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
}
|
||||
if (deleted) signalModifiedKey(c->db,key);
|
||||
server.dirty += deleted;
|
||||
addReplyLongLong(c,deleted);
|
||||
}
|
||||
@@ -1090,7 +1083,6 @@ void zremrangebyrankCommand(redisClient *c) {
|
||||
long end;
|
||||
int llen;
|
||||
unsigned long deleted;
|
||||
int keyremoved = 0;
|
||||
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
|
||||
(getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
|
||||
@@ -1115,30 +1107,19 @@ void zremrangebyrankCommand(redisClient *c) {
|
||||
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
/* Correct for 1-based rank. */
|
||||
zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted);
|
||||
if (zzlLength(zobj->ptr) == 0) {
|
||||
dbDelete(c->db,key);
|
||||
keyremoved = 1;
|
||||
}
|
||||
if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key);
|
||||
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = zobj->ptr;
|
||||
|
||||
/* Correct for 1-based rank. */
|
||||
deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
|
||||
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
|
||||
if (dictSize(zs->dict) == 0) {
|
||||
dbDelete(c->db,key);
|
||||
keyremoved = 1;
|
||||
}
|
||||
if (dictSize(zs->dict) == 0) dbDelete(c->db,key);
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrembyrank",key,c->db->id);
|
||||
if (keyremoved)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
}
|
||||
if (deleted) signalModifiedKey(c->db,key);
|
||||
server.dirty += deleted;
|
||||
addReplyLongLong(c,deleted);
|
||||
}
|
||||
@@ -1692,15 +1673,10 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
|
||||
dbAdd(c->db,dstkey,dstobj);
|
||||
addReplyLongLong(c,zsetLength(dstobj));
|
||||
if (!touched) signalModifiedKey(c->db,dstkey);
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,
|
||||
(op == REDIS_OP_UNION) ? "zunionstore" : "zinterstore",
|
||||
dstkey,c->db->id);
|
||||
server.dirty++;
|
||||
} else {
|
||||
decrRefCount(dstobj);
|
||||
addReply(c,shared.czero);
|
||||
if (touched)
|
||||
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",dstkey,c->db->id);
|
||||
}
|
||||
zfree(src);
|
||||
}
|
||||
|
||||
-60
@@ -405,66 +405,6 @@ void getRandomHexChars(char *p, unsigned int len) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Given the filename, return the absolute path as an SDS string, or NULL
|
||||
* if it fails for some reason. Note that "filename" may be an absolute path
|
||||
* already, this will be detected and handled correctly.
|
||||
*
|
||||
* The function does not try to normalize everything, but only the obvious
|
||||
* case of one or more "../" appearning at the start of "filename"
|
||||
* relative path. */
|
||||
sds getAbsolutePath(char *filename) {
|
||||
char cwd[1024];
|
||||
sds abspath;
|
||||
sds relpath = sdsnew(filename);
|
||||
|
||||
relpath = sdstrim(relpath," \r\n\t");
|
||||
if (relpath[0] == '/') return relpath; /* Path is already absolute. */
|
||||
|
||||
/* If path is relative, join cwd and relative path. */
|
||||
if (getcwd(cwd,sizeof(cwd)) == NULL) {
|
||||
sdsfree(relpath);
|
||||
return NULL;
|
||||
}
|
||||
abspath = sdsnew(cwd);
|
||||
if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/')
|
||||
abspath = sdscat(abspath,"/");
|
||||
|
||||
/* At this point we have the current path always ending with "/", and
|
||||
* the trimmed relative path. Try to normalize the obvious case of
|
||||
* trailing ../ elements at the start of the path.
|
||||
*
|
||||
* For every "../" we find in the filename, we remove it and also remove
|
||||
* the last element of the cwd, unless the current cwd is "/". */
|
||||
while (sdslen(relpath) >= 3 &&
|
||||
relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/')
|
||||
{
|
||||
sdsrange(relpath,3,-1);
|
||||
if (sdslen(abspath) > 1) {
|
||||
char *p = abspath + sdslen(abspath)-2;
|
||||
int trimlen = 1;
|
||||
|
||||
while(*p != '/') {
|
||||
p--;
|
||||
trimlen++;
|
||||
}
|
||||
sdsrange(abspath,0,-(trimlen+1));
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally glue the two parts together. */
|
||||
abspath = sdscatsds(abspath,relpath);
|
||||
sdsfree(relpath);
|
||||
return abspath;
|
||||
}
|
||||
|
||||
/* Return true if the specified path is just a file basename without any
|
||||
* relative or absolute path. This function just checks that no / or \
|
||||
* character exists inside the specified path, that's enough in the
|
||||
* environments where Redis runs. */
|
||||
int pathIsBaseName(char *path) {
|
||||
return strchr(path,'/') == NULL && strchr(path,'\\') == NULL;
|
||||
}
|
||||
|
||||
#ifdef UTIL_TEST_MAIN
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
#ifndef __REDIS_UTIL_H
|
||||
#define __REDIS_UTIL_H
|
||||
|
||||
#include "sds.h"
|
||||
|
||||
int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase);
|
||||
int stringmatch(const char *p, const char *s, int nocase);
|
||||
long long memtoll(const char *p, int *err);
|
||||
@@ -39,7 +37,5 @@ int ll2string(char *s, size_t len, long long value);
|
||||
int string2ll(const char *s, size_t slen, long long *value);
|
||||
int string2l(const char *s, size_t slen, long *value);
|
||||
int d2string(char *buf, size_t len, double value);
|
||||
sds getAbsolutePath(char *filename);
|
||||
int pathIsBaseName(char *path);
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.7.102"
|
||||
#define REDIS_VERSION "2.6.12"
|
||||
|
||||
+1
-2
@@ -1041,8 +1041,7 @@ void pop(unsigned char *zl, int where) {
|
||||
}
|
||||
|
||||
int randstring(char *target, unsigned int min, unsigned int max) {
|
||||
int p = 0;
|
||||
int len = min+rand()%(max-min+1);
|
||||
int p, len = min+rand()%(max-min+1);
|
||||
int minval, maxval;
|
||||
switch(rand() % 3) {
|
||||
case 0:
|
||||
|
||||
+2
-2
@@ -252,7 +252,7 @@ void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
|
||||
* function RedisEstimateRSS() that is a much faster (and less precise)
|
||||
* version of the function. */
|
||||
|
||||
#if defined(HAVE_PROC_STAT)
|
||||
#if defined(HAVE_PROCFS)
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -325,7 +325,7 @@ float zmalloc_get_fragmentation_ratio(void) {
|
||||
return (float)zmalloc_get_rss()/zmalloc_used_memory();
|
||||
}
|
||||
|
||||
#if defined(HAVE_PROC_SMAPS)
|
||||
#if defined(HAVE_PROCFS)
|
||||
size_t zmalloc_get_private_dirty(void) {
|
||||
char line[1024];
|
||||
size_t pd = 0;
|
||||
|
||||
+323
-4
@@ -1,25 +1,344 @@
|
||||
# Redis configuration for testing.
|
||||
# Redis configuration file example
|
||||
|
||||
notify-keyspace-events KEA
|
||||
# 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 0
|
||||
bind 127.0.0.1
|
||||
|
||||
# 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)
|
||||
loglevel verbose
|
||||
logfile ''
|
||||
|
||||
# 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
|
||||
|
||||
@@ -98,7 +98,7 @@ tags {"aof"} {
|
||||
}
|
||||
}
|
||||
|
||||
## Test that SPOP (that modifies the client's argc/argv) is correctly free'd
|
||||
## Test that SPOP (that modifies the client its argc/argv) is correctly free'd
|
||||
create_aof {
|
||||
append_to_aof [formatCommand sadd set foo]
|
||||
append_to_aof [formatCommand sadd set bar]
|
||||
|
||||
+12
-27
@@ -53,31 +53,18 @@ proc start_server_and_kill_it {overrides code} {
|
||||
# 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!"
|
||||
}
|
||||
}
|
||||
start_server_and_kill_it [list "dir" $server_path] {
|
||||
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.
|
||||
# Fix permissions of the RDB file, but corrupt its CRC64 checksum.
|
||||
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
|
||||
@@ -87,12 +74,10 @@ 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!"
|
||||
}
|
||||
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,12 +2,9 @@ start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
r -1 slaveof [srv 0 host] [srv 0 port]
|
||||
wait_for_condition 50 100 {
|
||||
[s -1 master_link_status] eq {up}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
after 1000
|
||||
s -1 role
|
||||
} {slave}
|
||||
|
||||
if {$::accurate} {set numops 50000} else {set numops 5000}
|
||||
|
||||
@@ -32,70 +29,3 @@ start_server {tags {"repl"}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
r -1 slaveof [srv 0 host] [srv 0 port]
|
||||
wait_for_condition 50 100 {
|
||||
[s -1 master_link_status] eq {up}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
|
||||
set numops 20000 ;# Enough to trigger the Script Cache LRU eviction.
|
||||
|
||||
# While we are at it, enable AOF to test it will be consistent as well
|
||||
# after the test.
|
||||
r config set appendonly yes
|
||||
|
||||
test {MASTER and SLAVE consistency with EVALSHA replication} {
|
||||
array set oldsha {}
|
||||
for {set j 0} {$j < $numops} {incr j} {
|
||||
set key "key:$j"
|
||||
# Make sure to create scripts that have different SHA1s
|
||||
set script "return redis.call('incr','$key')"
|
||||
set sha1 [r eval "return redis.sha1hex(\"$script\")" 0]
|
||||
set oldsha($j) $sha1
|
||||
r eval $script 0
|
||||
set res [r evalsha $sha1 0]
|
||||
assert {$res == 2}
|
||||
# Additionally call one of the old scripts as well, at random.
|
||||
set res [r evalsha $oldsha([randomInt $j]) 0]
|
||||
assert {$res > 2}
|
||||
|
||||
# Trigger an AOF rewrite while we are half-way, this also
|
||||
# forces the flush of the script cache, and we will cover
|
||||
# more code as a result.
|
||||
if {$j == $numops / 2} {
|
||||
catch {r bgrewriteaof}
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_condition 50 100 {
|
||||
[r dbsize] == $numops &&
|
||||
[r -1 dbsize] == $numops &&
|
||||
[r debug digest] eq [r -1 debug digest]
|
||||
} else {
|
||||
set csv1 [csvdump r]
|
||||
set csv2 [csvdump {r -1}]
|
||||
set fd [open /tmp/repldump1.txt w]
|
||||
puts -nonewline $fd $csv1
|
||||
close $fd
|
||||
set fd [open /tmp/repldump2.txt w]
|
||||
puts -nonewline $fd $csv2
|
||||
close $fd
|
||||
puts "Master - Slave inconsistency"
|
||||
puts "Run diff -u against /tmp/repldump*.txt for more info"
|
||||
|
||||
}
|
||||
|
||||
set old_digest [r debug digest]
|
||||
r config set appendonly no
|
||||
r debug loadaof
|
||||
set new_digest [r debug digest]
|
||||
assert {$old_digest eq $new_digest}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,21 @@ proc stop_bg_complex_data {handle} {
|
||||
start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
|
||||
set master [srv 0 client]
|
||||
set master_host [srv 0 host]
|
||||
set master_port [srv 0 port]
|
||||
set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
|
||||
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
|
||||
set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
|
||||
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
$slave slaveof $master_host $master_port
|
||||
r -1 slaveof [srv 0 host] [srv 0 port]
|
||||
after 1000
|
||||
s 0 role
|
||||
s -1 role
|
||||
} {slave}
|
||||
|
||||
test {Test replication with parallel clients writing in differnet DBs} {
|
||||
lappend slave [srv 0 client]
|
||||
after 5000
|
||||
stop_bg_complex_data $load_handle0
|
||||
stop_bg_complex_data $load_handle1
|
||||
@@ -38,7 +37,7 @@ start_server {tags {"repl"}} {
|
||||
}
|
||||
assert {[$master dbsize] > 0}
|
||||
|
||||
if {[$master debug digest] ne [$slave debug digest]} {
|
||||
if {[r debug digest] ne [r -1 debug digest]} {
|
||||
set csv1 [csvdump r]
|
||||
set csv2 [csvdump {r -1}]
|
||||
set fd [open /tmp/repldump1.txt w]
|
||||
@@ -54,83 +53,3 @@ start_server {tags {"repl"}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
$slave slaveof $master_host $master_port
|
||||
wait_for_condition 50 100 {
|
||||
[s 0 master_link_status] eq {up}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
|
||||
test {With min-slaves-to-write (1,3): master should be writable} {
|
||||
$master config set min-slaves-max-lag 3
|
||||
$master config set min-slaves-to-write 1
|
||||
$master set foo bar
|
||||
} {OK}
|
||||
|
||||
test {With min-slaves-to-write (2,3): master should not be writable} {
|
||||
$master config set min-slaves-max-lag 3
|
||||
$master config set min-slaves-to-write 2
|
||||
catch {$master set foo bar} e
|
||||
set e
|
||||
} {NOREPLICAS*}
|
||||
|
||||
test {With min-slaves-to-write: master not writable with lagged slave} {
|
||||
$master config set min-slaves-max-lag 2
|
||||
$master config set min-slaves-to-write 1
|
||||
assert {[$master set foo bar] eq {OK}}
|
||||
$slave deferred 1
|
||||
$slave debug sleep 6
|
||||
after 4000
|
||||
catch {$master set foo bar} e
|
||||
set e
|
||||
} {NOREPLICAS*}
|
||||
}
|
||||
}
|
||||
|
||||
start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
$slave slaveof $master_host $master_port
|
||||
wait_for_condition 50 100 {
|
||||
[s 0 role] eq {slave}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
|
||||
test {Replication: commands with many arguments (issue #1221)} {
|
||||
# We now issue large MSET commands, that may trigger a specific
|
||||
# class of bugs, see issue #1221.
|
||||
for {set j 0} {$j < 100} {incr j} {
|
||||
set cmd [list mset]
|
||||
for {set x 0} {$x < 1000} {incr x} {
|
||||
lappend cmd [randomKey] [randomValue]
|
||||
}
|
||||
$master {*}$cmd
|
||||
}
|
||||
|
||||
set retry 10
|
||||
while {$retry && ([$master debug digest] ne [$slave debug digest])}\
|
||||
{
|
||||
after 1000
|
||||
incr retry -1
|
||||
}
|
||||
assert {[$master dbsize] > 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
proc start_bg_complex_data {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} {
|
||||
catch {exec /bin/kill -9 $handle}
|
||||
}
|
||||
|
||||
# Creates a master-slave pair and breaks the link continuously to force
|
||||
# partial resyncs attempts, all this while flooding the master with
|
||||
# write queries.
|
||||
#
|
||||
# You can specifiy backlog size, ttl, delay before reconnection, test duration
|
||||
# in seconds, and an additional condition to verify at the end.
|
||||
proc test_psync {descr duration backlog_size backlog_ttl delay cond} {
|
||||
start_server {tags {"repl"}} {
|
||||
start_server {} {
|
||||
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
|
||||
$master config set repl-backlog-size $backlog_size
|
||||
$master config set repl-backlog-ttl $backlog_ttl
|
||||
|
||||
set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
|
||||
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
|
||||
set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
|
||||
|
||||
test {First server should have role slave after SLAVEOF} {
|
||||
$slave slaveof $master_host $master_port
|
||||
wait_for_condition 50 100 {
|
||||
[s 0 role] eq {slave}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
|
||||
test "Test replication partial resync: $descr" {
|
||||
# Now while the clients are writing data, break the maste-slave
|
||||
# link multiple times.
|
||||
for {set j 0} {$j < $duration*10} {incr j} {
|
||||
after 100
|
||||
# catch {puts "MASTER [$master dbsize] keys, SLAVE [$slave dbsize] keys"}
|
||||
|
||||
if {($j % 20) == 0} {
|
||||
catch {
|
||||
if {$delay} {
|
||||
$slave multi
|
||||
$slave client kill $master_host:$master_port
|
||||
$slave debug sleep $delay
|
||||
$slave exec
|
||||
} else {
|
||||
$slave client kill $master_host:$master_port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stop_bg_complex_data $load_handle0
|
||||
stop_bg_complex_data $load_handle1
|
||||
stop_bg_complex_data $load_handle2
|
||||
set retry 10
|
||||
while {$retry && ([$master debug digest] ne [$slave debug digest])}\
|
||||
{
|
||||
after 1000
|
||||
incr retry -1
|
||||
}
|
||||
assert {[$master dbsize] > 0}
|
||||
|
||||
if {[$master debug digest] ne [$slave debug digest]} {
|
||||
set csv1 [csvdump r]
|
||||
set csv2 [csvdump {r -1}]
|
||||
set fd [open /tmp/repldump1.txt w]
|
||||
puts -nonewline $fd $csv1
|
||||
close $fd
|
||||
set fd [open /tmp/repldump2.txt w]
|
||||
puts -nonewline $fd $csv2
|
||||
close $fd
|
||||
puts "Master - Slave inconsistency"
|
||||
puts "Run diff -u against /tmp/repldump*.txt for more info"
|
||||
}
|
||||
assert_equal [r debug digest] [r -1 debug digest]
|
||||
eval $cond
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_psync {ok psync} 6 1000000 3600 0 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
}
|
||||
|
||||
test_psync {no backlog} 6 100 3600 0 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
}
|
||||
|
||||
test_psync {ok after delay} 3 100000000 3600 3 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
}
|
||||
|
||||
test_psync {backlog expired} 3 100000000 1 3 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ start_server {tags {"repl"}} {
|
||||
set retry 500
|
||||
while {$retry} {
|
||||
set info [r -3 info]
|
||||
if {[string match {*slave0:*state=online*slave1:*state=online*slave2:*state=online*} $info]} {
|
||||
if {[string match {*slave0:*,online*slave1:*,online*slave2:*,online*} $info]} {
|
||||
break
|
||||
} else {
|
||||
incr retry -1
|
||||
|
||||
@@ -115,10 +115,6 @@ proc ::redis::__method__channel {id fd} {
|
||||
return $fd
|
||||
}
|
||||
|
||||
proc ::redis::__method__deferred {id fd val} {
|
||||
set ::redis::deferred($id) $val
|
||||
}
|
||||
|
||||
proc ::redis::redis_write {fd buf} {
|
||||
puts -nonewline $fd $buf
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ proc ping_server {host port} {
|
||||
puts $fd "PING\r\n"
|
||||
flush $fd
|
||||
set reply [gets $fd]
|
||||
if {[string range $reply 0 0] eq {+} ||
|
||||
[string range $reply 0 0] eq {-}} {
|
||||
if {[string range $reply 0 4] eq {+PONG} ||
|
||||
[string range $reply 0 3] eq {-ERR}} {
|
||||
set retval 1
|
||||
}
|
||||
close $fd
|
||||
|
||||
@@ -91,12 +91,10 @@ proc wait_for_sync r {
|
||||
}
|
||||
}
|
||||
|
||||
# Random integer between 0 and max (excluded).
|
||||
proc randomInt {max} {
|
||||
expr {int(rand()*$max)}
|
||||
}
|
||||
|
||||
# Random signed integer between -max and max (both extremes excluded).
|
||||
proc randomSignedInt {max} {
|
||||
set i [randomInt $max]
|
||||
if {rand() > 0.5} {
|
||||
|
||||
@@ -32,7 +32,6 @@ set ::all_tests {
|
||||
integration/replication-2
|
||||
integration/replication-3
|
||||
integration/replication-4
|
||||
integration/replication-psync
|
||||
integration/aof
|
||||
integration/rdb
|
||||
integration/convert-zipmap-hash-on-load
|
||||
@@ -186,7 +185,7 @@ proc test_server_main {} {
|
||||
if {!$::quiet} {
|
||||
puts "Starting test server at port $port"
|
||||
}
|
||||
socket -server accept_test_clients -myaddr 127.0.0.1 $port
|
||||
socket -server accept_test_clients $port
|
||||
|
||||
# Start the client instances
|
||||
set ::clients_pids {}
|
||||
@@ -439,9 +438,15 @@ proc read_from_replication_stream {s} {
|
||||
after 100
|
||||
}
|
||||
fconfigure $s -blocking 1
|
||||
set count [string range $count 1 end]
|
||||
|
||||
# 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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user