Compare commits

..
Author SHA1 Message Date
antirez 6c194dae58 VM tests commented out for 2.4 2011-06-09 12:35:56 +02:00
antirez 7a4c4682f2 Warn the user in redis.conf that VM is deprecated and discouraged 2011-06-09 12:31:16 +02:00
antirez 054ed87d85 removed no longer useful files from utils directory 2011-06-09 12:29:05 +02:00
antirez 8dcec3bf25 INSTALL now points the user to the README file 2011-06-09 12:27:54 +02:00
antirez 7dc811b8cb Release notes updated for 2.4 2011-06-09 12:25:21 +02:00
antirez 0f3be53d7d README updated for 2.4 release 2011-06-09 12:00:28 +02:00
antirez 4e5b1d3851 version bumped to 2.3.5 2011-06-08 23:21:09 +02:00
antirez 27c8f84c66 INFO now contains the time (in usecs) needed to fork() in order to persist. The info is available in the stats section of INFO. 2011-06-08 23:20:11 +02:00
antirez 32463852be Add ustime() from unstable into utils.c 2011-06-08 23:13:47 +02:00
antirez 567d575dbe Color make backported to 2.4 2011-06-08 22:53:33 +02:00
antirez a7982fed9e Warn the user that will try to enable VM that VM sucks. But still allows him to enable VM with a special option. 2011-06-06 18:41:54 +02:00
antirez b590cedff1 no more client-libraries directory 2011-06-05 20:51:07 +02:00
antirez 02d71527d8 no more html documentation 2011-06-05 20:50:22 +02:00
antirez 112569d12d touch less pages in decrRefCount 2011-06-02 17:41:42 +02:00
Pieter Noordhuis 82c6e9c297 Ignore jemalloc build artifacts 2011-06-01 18:37:40 +02:00
antirez fcf862d22b Merge remote branch 'pietern/2.4-bench' into 2.4 2011-06-01 18:17:25 +02:00
antirez 16d227ef2d Removed useless stuff from config.h that are now moved into zmalloc.h 2011-06-01 18:07:40 +02:00
antirez ac1eda60b0 Fixed warning on Linux 2011-06-01 18:06:30 +02:00
antirez 598387b970 encoding agnostic intsets 2011-06-01 18:04:50 +02:00
antirez 8789dc82aa fmacros in utils.c to avoid warning about strcasecmp() 2011-06-01 17:58:20 +02:00
antirez 7f90bcfaee Added -ldl when linking against jemalloc, needed on Linux 2011-06-01 17:56:50 +02:00
antirez 860e3ade2d It is now possible to build with: make 32bit USE_JEMALLOC=yes 2011-06-01 17:08:12 +02:00
antirez 0042a2d737 jemalloc support backported in 2.4 using part of the Pieter Noordhuis and Didier Spezia original implementations 2011-06-01 16:52:53 +02:00
antirez 0811e89c80 jemalloc source added 2011-06-01 16:16:13 +02:00
Pieter Noordhuis fe71355df9 There is no debug mode in benchmark tool 2011-05-31 17:42:48 -07:00
Pieter Noordhuis 33d00ff7d5 Provide custom command to benchmark against via args 2011-05-31 17:42:47 -07:00
Pieter Noordhuis c60a0f588e More const's in redis-benchmark.c 2011-05-31 17:42:47 -07:00
antirez 942354f925 version bumped to 2.3.4 2011-05-31 20:46:38 +02:00
antirez cd7a497164 revert ZADD implementation to 2.4 WATCH API. 2011-05-31 20:40:24 +02:00
antirez 5fca35da3f disabled development test entry, tests moved in the right place 2011-05-31 20:37:59 +02:00
antirez d260b9a499 Variadic ZADD tests 2011-05-31 20:37:52 +02:00
antirez 45df42c9fe Variadic ZADD 2011-05-31 20:37:08 +02:00
antirez 6e326c81dc prepare to cherry pick variadic ZADD without conflicts 2011-05-31 20:36:17 +02:00
antirez 83533b622e ZREM tests 2011-05-31 20:34:02 +02:00
antirez 913090ec63 Variadic ZREM 2011-05-31 20:33:47 +02:00
antirez 51501514e9 Fix for the variadic version of SREM. Regression test added. 2011-05-31 20:15:25 +02:00
antirez 09843df1dd test for the BRPOPLPUSH issue 561 related issues 2011-05-31 12:16:16 +02:00
antirez 4f8cf6a23c use the new rewriteClientCommandVector() function for SPOP -> SREM replication translation as well. 2011-05-31 12:15:55 +02:00
antirez 0d119ad3a3 modified the BRPOPLPUSH target field cleanup strategy to fix it the proper way. 2011-05-31 12:14:26 +02:00
antirez 6a1275bcab Removed a leak in the BRPOPLPUSH unrelated to issue 561 2011-05-31 12:14:13 +02:00
antirez 3769dbd722 Fix for issue 516, rewriting the command vector to correctly repliate BRPOPLPUSH. Still to test everything, especially edge cases 2011-05-31 12:14:07 +02:00
Pieter Noordhuis afc151d3ba Make replication faster (biggest gain for small number of slaves) 2011-05-29 17:55:13 -07:00
antirez 6ac57f16cb Merge remote branch 'pietern/2.4-replnonblock' into 2.4 2011-05-29 14:24:50 +02:00
antirez 61a707af39 version bumped to 2.3.3 2011-05-28 23:04:03 +02:00
antirez 5cb0558e8c 2.4 redis-cli synched with the latest changes into unstable. 2011-05-28 23:02:49 +02:00
antirez e5dbbc20f1 Fix for Pub/Sub system, introduced in Redis 2.2.6 with the new copy-on-write safe iterator semantics. In the hope this is the last bug I introduced this way. 2011-05-25 12:31:32 +02:00
antirez 1f036c6587 Comment typo fixed 2011-05-24 10:43:19 +02:00
antirez 7dd1e95288 Fixed typo in comment 2011-05-24 10:35:48 +02:00
Pieter Noordhuis ecfc0583da Configurable synchronous I/O timeout 2011-05-22 12:57:56 +02:00
Pieter Noordhuis ff8d85885e Minor changes in non-blocking repl. connect 2011-05-22 12:48:10 +02:00
antirez d89db584fa Merge branch '2.4' of github.com:antirez/redis into 2.4 2011-05-20 11:44:38 +02:00
antirez 1f2af715cb Print version info before running the test 2011-05-20 11:44:32 +02:00
Pieter Noordhuis 78c05caf5e Non-blocking connect with master 2011-05-19 18:53:31 +02:00
antirez 10e4b58312 Fix for ZUNIONSTORE bug when there is an empty set among input sets. Regression test added. 2011-05-19 17:59:13 +02:00
antirez de6ba77bc4 Fixed misuse of the new iterator semantics in ZUNIONSTORE 2011-05-15 17:28:22 +02:00
antirez 5d64f21792 Fix for a possible bug related to ZINTER/UNIONSTORE called with the same source set more than one time. 2011-05-15 17:23:59 +02:00
antirez 86277eee4c Fixed SINTER[STORE] problem related to the new copy on write safe iterator 2011-05-15 12:18:56 +02:00
antirez 24430cb820 Fix for DEBUG DIGEST, key may expire on lookup 2011-05-13 11:44:14 +02:00
antirez 604d26ffde ZINTERSTORE regressiont test with two sets, intset+hashtable 2011-05-12 20:20:25 +02:00
antirez 338e44a6e7 replication with expire test modified to produce no or less false failures 2011-05-12 20:17:07 +02:00
antirez de77addb36 ziplist are now endianess agnostic 2011-05-12 13:26:55 +02:00
antirez 9f517b920e removed assert causing an illegal memory access. This was responsible of crashes during BLPOP and other list blocking operations. 2011-05-11 09:50:43 +02:00
antirez d0f710ea09 RDB version signature bumped to 2, now that direct saving of specially encoded types is implemented 2.2 instances are no longer able to read rdb files produced by 2.4 or unstable. 2011-05-10 11:33:29 +02:00
antirez d0d72c19d3 use safe Dict iteration in DEBUG DIGEST 2011-05-10 10:42:20 +02:00
antirez da49700d5a Introduced a safe iterator interface that can be used to iterate while accessing the dictionary at the same time. Now the default interface is consireded unsafe and should be used only with dictNext() 2011-05-10 10:22:14 +02:00
antirez ba07e1f505 Merge branch '2.4' of github.com:antirez/redis into 2.4 2011-05-07 12:10:11 +02:00
antirez 8ec9d6ef69 when Redis fails accepting a new connection reports the error at WARNING and not VERBOSE error level. Thanks to offby1 for proposing this in the Redis mailing list. #backport-candidate 2011-05-07 12:09:55 +02:00
antirez deff2338fa Merge remote branch 'pietern/2.4-misc' into 2.4 2011-05-05 16:50:28 +02:00
antirez 2ee7dd362c Merge branch '2.4' of github.com:antirez/redis into 2.4 2011-05-05 15:17:31 +02:00
Pieter Noordhuis fddd64e610 Fix ziplist regression and update stresser 2011-05-05 15:17:13 +02:00
antirez d275513b04 Merge branch '2.4' of github.com:antirez/redis into 2.4 2011-05-01 15:42:53 +02:00
antirez 2618476032 Fixed a problem with string2ll() 2011-05-01 15:40:43 +02:00
antirez c924df8c83 Fixed a bug with replication where SLAVEOF NO ONE caused a slave to close the connection with its slaves 2011-04-29 14:18:45 +02:00
Pieter Noordhuis 01f36192c8 Extra tests for string2ll 2011-04-27 14:38:58 +02:00
Pieter Noordhuis 6e258c76bf Check seplen and len before malloc'ing "tokens" 2011-04-27 14:33:21 +02:00
Pieter Noordhuis 355d8cb0b5 Use correct replacement length in sdscatrepr() 2011-04-27 14:31:18 +02:00
Pieter Noordhuis f1e072c57d Check for \n after finding \r 2011-04-27 14:29:27 +02:00
Pieter Noordhuis a244985d1d Show memory allocator in INFO output 2011-04-27 13:35:49 +02:00
Pieter Noordhuis b83a52c6f8 Use string2ll in ziplist code (faster) 2011-04-27 13:30:32 +02:00
Pieter Noordhuis 20dad91bdf Tests for string2ll; move isObject* to object.c 2011-04-27 13:26:33 +02:00
Pieter Noordhuis d548f3daad Move code 2011-04-27 13:16:23 +02:00
Pieter Noordhuis 992fe21706 Use correct argc/argv for cleanup when loading AOF 2011-04-22 09:42:38 +02:00
Pieter Noordhuis 2f170ad733 Reformat AOF tests 2011-04-22 09:42:38 +02:00
antirez 29a87f23b1 Variadic SREM backported from unstable 2011-04-21 17:31:10 +02:00
antirez 0882fe5e43 addReplyLongLong optimized to return shared objects when the value to reply is 0 or 1 2011-04-21 17:15:47 +02:00
antirez 7e5cf2660c CLIENT KILL implemented 2011-04-21 17:11:24 +02:00
antirez 91867a5ea2 CLIENT LIST implemented 2011-04-21 17:09:46 +02:00
antirez 1c30cad1ab peak mem in INFO backported from unstable branch 2011-04-21 16:55:27 +02:00
antirez bd50301f3e removed check for zmalloc return NULL in createClient(). The check was misplaced, and zmalloc never returns NULL. 2011-04-21 16:44:00 +02:00
antirez 760dec3a6c VM/direct-saving fixes 2011-04-20 17:48:46 +02:00
antirez e8852d782d encoding.c/h added, this new functions make sure that even our in memory represenation of encoded types is endianess agnostic, so we can save this stuff directly on disk without problems when reloading on a different arch. 2011-04-20 15:58:36 +02:00
antirez 1f84b0648b yet more manual backporting from unstable 2011-04-20 15:57:35 +02:00
antirez 73f01b0606 Some manual merging of encoded values direct saving 2011-04-20 15:49:32 +02:00
antirez 552a6fb39d fix type id for specially encoded data types, for direct blob saving of intsets, ziplists and zipmaps 2011-04-20 15:41:13 +02:00
antirez 5045ff843a direct saving of specially encoded types implemented for lists and sets too 2011-04-20 15:41:13 +02:00
antirez c92283770d added new RDB codes for ziplist encoded lists and intset encodeed sets 2011-04-20 15:41:13 +02:00
antirez 44d4b87f1f convert the zipmap into hash in rdb loading if the zipmap has too many elements 2011-04-20 15:41:13 +02:00
antirez cebac46589 API change function name fix 2011-04-20 15:41:13 +02:00
antirez 68495c46fe changed names of APIs to get blob len of special encoded data types 2011-04-20 15:41:13 +02:00
antirez 3dffb8e21e save zipmap encoded hashes as blobs. Work in progress. 2011-04-20 15:41:13 +02:00
Pieter Noordhuis 1c53928fca Functions to get size of specially encoded value in bytes 2011-04-20 15:32:44 +02:00
Pieter Noordhuis 660a0cdf02 Don't export unused function 2011-04-20 15:32:44 +02:00
Pieter Noordhuis 01fc59b787 More allocators 2011-04-19 23:54:43 +02:00
Pieter Noordhuis ff8187844a Inline sdslen and sdsavail (thanks to @bitbckt) 2011-04-19 23:10:45 +02:00
Pieter Noordhuis 1dabf116d8 Update hiredis 2011-04-19 23:07:36 +02:00
antirez 5b97a708fc variadic HDEL backported from unstable to 2.4 2011-04-19 17:14:44 +02:00
antirez 17f41d49b4 variadic SADD backported from unstable 2011-04-19 16:41:27 +02:00
antirez 8514f3c035 prepare to cherry pick 2011-04-19 16:35:07 +02:00
Pieter Noordhuis 3fa46ef069 Use custom string2ll and strchr 2011-04-18 21:25:52 +02:00
antirez 3c2fe3f88d version bumped to 2.3.2 after optimized sorted set merge 2011-04-15 17:51:03 +02:00
Pieter Noordhuis 687054849e Merge branch '2.4' into 2.4-zset 2011-04-15 17:47:10 +02:00
antirez 577b084e3c version bumped to 2.3.1 (2.4 unstable) 2011-04-15 17:43:21 +02:00
antirez 6a9764d183 Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-15 17:28:03 +02:00
antirez c138dc7da4 variadic LPUSH/RPUSH backported to 2.2 2011-04-15 17:27:20 +02:00
antirez 1b905277bb tests for variadic list push 2011-04-15 17:15:59 +02:00
Pieter Noordhuis cf7a138684 Explicitly zero zval since it is stored on the stack 2011-04-06 16:57:08 +02:00
Pieter Noordhuis edfd6ae76b Test for ENCODING_SKIPLIST instead of ENCODING_RAW 2011-04-06 16:10:40 +02:00
Pieter Noordhuis 408e12dcaa Typo 2011-04-06 15:53:52 +02:00
Pieter Noordhuis dfeb899b2d Merge branch '2.2' into 2.2-zset
Conflicts:
	src/object.c
2011-04-06 15:46:33 +02:00
antirez 2b886275e9 Redis 2.2.4 2011-04-06 15:39:54 +02:00
antirez 93db956e06 make sure that OBJECT ENCODING returns skiplist for sorted sets, and not raw, so that once we will merge specially encoded sorted sets everything will make sense. 2011-04-06 15:38:08 +02:00
antirez 27fc6199ca CHANGELOG updated 2011-04-06 12:46:39 +02:00
antirez 0b01578d2e Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-06 12:23:44 +02:00
antirez 920c45b818 version is now 2.2.3 2011-04-06 12:23:16 +02:00
antirez cf6c3f4b04 OBJECT command implemented 2011-04-06 12:22:09 +02:00
antirez fb90934c47 fixed memory leak introduced with the previous commit. Many thanks to Pieter Noordhuis for spotting it in no time 2011-03-31 19:54:08 +02:00
antirez 207ca3cedd Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-03-31 16:45:27 +02:00
antirez 97d3b7dc8d Fixed issue #503. MONITOR + QUIT could crash the server, there are actually other interactions that could have the same effect (for instance Pub/Sub). 2011-03-31 16:45:05 +02:00
antirez abdbfc14c0 Fixed sdssplitargs() handling of hex-style escapes. 2011-03-22 22:49:33 +01:00
Pieter Noordhuis 724376a0df Backport patch: object swappability for encoded sorted sets 2011-03-22 16:35:50 +01:00
Pieter Noordhuis 476f044db6 Backport patch: don't use blob-saving 2011-03-22 16:30:29 +01:00
Pieter Noordhuis 8b2449dca0 Backport patch: signalModifiedKey (unstable) -> touchWatchedKey (2.2) 2011-03-22 16:12:26 +01:00
Pieter Noordhuis 763d6165ea Test that sorted sets are removed when empty 2011-03-22 16:09:22 +01:00
Pieter Noordhuis 2e18193f5b Remove sorted set when empty after ZREMRANGEBY* 2011-03-22 16:09:22 +01:00
Pieter Noordhuis 7f9c973467 Fix DEBUG DIGEST, SORT and AOF rewrite 2011-03-22 16:09:22 +01:00
Pieter Noordhuis 366e872d4d Test both sorted set encodings for every test 2011-03-22 16:09:22 +01:00
Pieter Noordhuis 4d8f13ddc3 Offset should be size_t 2011-03-22 16:09:21 +01:00
Pieter Noordhuis 3e518093b6 Remove unused function 2011-03-22 16:09:21 +01:00
Pieter Noordhuis 99cec798bb Test for empty inner range when looking for elements in range 2011-03-22 16:09:21 +01:00
Pieter Noordhuis ccf96e197b Make zzl API unaware of the robj where the ziplist is stored 2011-03-22 16:09:21 +01:00
Pieter Noordhuis 8f0a41be98 Encode sorted set after loading from dump 2011-03-22 16:09:21 +01:00
Pieter Noordhuis 63b29390b2 Convert encoding of result when in limits 2011-03-22 16:08:42 +01:00
Pieter Noordhuis 46aa0e2472 Remove comment 2011-03-22 16:08:03 +01:00
Pieter Noordhuis 07d9e35637 Generic iterator code for usage in ZUNIONSTORE/ZINTERSTORE 2011-03-22 16:08:03 +01:00
Pieter Noordhuis 2e12082b3e Make zzlLength take a ziplist argument 2011-03-22 16:08:03 +01:00
Pieter Noordhuis 08532b1e99 Add new string to long long function 2011-03-22 16:08:03 +01:00
Pieter Noordhuis 771ea6e1a6 Fix used function in ZCARD 2011-03-22 16:08:03 +01:00
Pieter Noordhuis c5802e3f4a Convert encoding when thresholds overflow 2011-03-22 16:08:03 +01:00
Pieter Noordhuis eab8382d18 Configurable thresholds for encoded sorted sets 2011-03-22 16:08:03 +01:00
Pieter Noordhuis efc77ad91c Persistence code for encoded sorted sets 2011-03-22 16:07:37 +01:00
Pieter Noordhuis a3886e29a6 Support dual encoding for more commands 2011-03-22 16:06:07 +01:00
Pieter Noordhuis db912c5a93 Support dual encoding for ZRANGEBYSCORE et al 2011-03-22 16:06:07 +01:00
Pieter Noordhuis 2165ac5573 Helpers to move around in encoded sorted set 2011-03-22 16:06:07 +01:00
Pieter Noordhuis f8224a4f90 Support dual encoding for ZREMRANGEBYRANK 2011-03-22 16:06:07 +01:00
Pieter Noordhuis 679de7ae2f Support dual encoding for ZRANGE 2011-03-22 16:05:45 +01:00
Pieter Noordhuis efc3f1783d Properly free encoded sorted set 2011-03-22 16:05:45 +01:00
Pieter Noordhuis 0b8c9eac68 Look up and remove elements by range 2011-03-22 16:05:45 +01:00
Pieter Noordhuis 4c36024f51 Typo 2011-03-22 16:05:05 +01:00
Pieter Noordhuis 8e1db6a05f Little less obfuscation 2011-03-22 16:05:05 +01:00
Pieter Noordhuis d3882605c0 Support dual encoding in ZREM 2011-03-22 16:05:05 +01:00
Pieter Noordhuis 3d6eade781 Don't encode element argument when dealing with ziplist 2011-03-22 16:04:33 +01:00
Pieter Noordhuis 6b017e615f Initial work for ziplist backed sorted sets 2011-03-22 16:04:33 +01:00
Pieter Noordhuis 3164869d78 Add function to create ziplist-backed sorted set 2011-03-22 16:03:23 +01:00
Pieter Noordhuis b38c299886 Fast conversion of double when representable as long long 2011-03-22 16:03:23 +01:00
Pieter Noordhuis 5b40d6e656 Rename zset range functions 2011-03-22 16:03:22 +01:00
Pieter Noordhuis 8c3b630561 Test for ranges where min > max 2011-03-22 16:03:22 +01:00
Pieter Noordhuis 4484e3b591 Compiler should decide on inlining 2011-03-22 16:03:22 +01:00
Pieter Noordhuis 453a906fc9 Move logic concerned with zset ranges
This also optimizes ZREVRANGEBYSCORE for pathological cases where a
sorted set contains many elements with the same score. Previously,
it would traverse the list from back to front in such a case.
2011-03-22 16:03:22 +01:00
antirez de5d4be994 Merge remote branch 'pietern/2.2-sigs' into 2.2 2011-03-07 12:21:41 +01:00
Pieter Noordhuis 69bfdcf7d4 Show database number in cli prompt when non-zero 2011-03-06 21:10:50 +01:00
Pieter Noordhuis 699465c31e Store SELECTed database for reconnect (issue #468) 2011-03-06 20:53:13 +01:00
Pieter Noordhuis 45332cc47b Minor memory leak in redis-cli (issue #464) 2011-03-06 20:53:12 +01:00
Pieter Noordhuis 2d2c17b075 Save RDB on SIGTERM (see issue #471) 2011-03-06 17:49:22 +01:00
antirez 048c0f0f37 RELEASE NOTES updated 2011-03-04 16:31:20 +01:00
antirez 3eb3cc9da4 version is now 2.2.2 2011-03-04 16:29:38 +01:00
antirez 74f6ee8dd5 Fixed return value of GETRANGE / SUBSTR 2011-03-04 16:23:18 +01:00
antirez 7a55d72f28 AOF file descriptor leak fixed 2011-03-04 16:14:09 +01:00
antirez 4e4b3b943c Redis 2.1.1, release notes updated 2011-02-23 15:48:24 +01:00
Pieter Noordhuis 61e2547675 Restore argc/argv in EXEC after command is executed 2011-02-23 09:39:29 +01:00
antirez 595b5974f8 fixed client-libraries/README to reflect the new site path instead of google code 2011-02-22 18:22:00 +01:00
antirez 58418d7c3e linenoise updated 2011-02-22 17:40:33 +01:00
antirez c02bda324f fixed color support for test suite. Now colors are shown as long as there is xterm somewhere inside the TERM env var 2011-02-22 16:40:24 +01:00
Paul Gideon Dann 1afef16d88 Allow port to be 0 in configuration, in order to allow disabling of TCP. 2011-02-22 12:12:30 +01:00
antirez 7377aacd84 seed the random number generator at startup 2011-02-22 00:01:48 +01:00
antirez eece2d528c suppress a Linux warning, for 2.2 sake 2011-02-21 17:50:54 +01:00
antirez eb5aeaf138 check return value of getcwd() 2011-02-21 17:41:25 +01:00
antirez 1a6281f52f version bumped to 2.2.0 and release notes added 2011-02-17 17:15:52 +01:00
antirez c2571b314a CONFIG SET/GET for all the special encoding parameters of sets, lists, hashes 2011-02-17 12:21:22 +01:00
Pieter Noordhuis 97aeda9828 Morph SPOP to SREM for correct behavior on log replay/replication 2011-02-16 00:24:54 +01:00
antirez ca9d961073 CONFIG GET/SET dir implemented 2011-02-14 02:51:49 +01:00
antirez 2446fbb3bb Merge remote branch 'pietern/2.2-brpoplpush' into 2.2 2011-02-07 15:27:45 +01:00
antirez 970e813be2 Fix for the previous port 0 patch, thanks to Pieter for noticing the error 2011-02-07 13:43:55 +01:00
antirez a36d524562 If port zero is specified, Redis will not listen for TCP connections 2011-02-07 12:52:40 +01:00
Pieter Noordhuis bbaf76ea2f Clarify comment 2011-01-31 16:50:17 +01:00
Pieter Noordhuis 669f302e16 Reply with single null bulk for unsuccesful BRPOPLPUSH 2011-01-31 16:50:00 +01:00
antirez 71791e7a8e propagate key eviction to slaves and AOF, like for expires, so that replicas and AOFs are always consistent even in maxmemory mode. 2011-01-27 16:53:23 +01:00
antirez 2f91d7ada4 add tarball script added 2011-01-25 10:45:53 +01:00
antirez 5ef8f9531d other minor README changes 2011-01-25 10:26:00 +01:00
antirez 3ce30a6906 README updated 2011-01-25 10:25:05 +01:00
antirez 65e79a8272 release noted updated, version bumped to 2.1.12 (2.2.0 RC4) 2011-01-25 10:21:13 +01:00
antirez ef8bc13d0d Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-01-24 10:07:18 +01:00
antirez 0ffeadd290 portability fix in redis benchmark, using zu format specified with size_t instead of lu 2011-01-24 10:06:09 +01:00
antirez db0e263b42 Fixed issue #435 and at the same time introduced explicit ping in the master-slave channel that will detect a blocked master or a broken even if apparently connected TCP link. 2011-01-20 18:02:51 +01:00
antirez d526d09e41 Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-01-20 16:50:02 +01:00
antirez 9738e587da Fixed compilation on FreeBSD 2011-01-20 10:59:25 +01:00
antirez da67b5f59a typo fixed in release notes for 2.2.0 RC3 2011-01-17 12:21:59 +01:00
antirez 825d2904d1 release notes updated for 2.2.0 RC3 2011-01-17 12:19:15 +01:00
Pieter Noordhuis 3becef9ee6 Reverse commits changing sorted set code for 2.2 2011-01-17 11:15:50 +01:00
antirez 2ccf6e81a6 version bumped to 2.1.11 2011-01-17 10:12:57 +01:00
Pieter Noordhuis e18b59ae7e Remove client from list of unblocked clients when it is free'd 2011-01-17 10:03:21 +01:00
Pieter Noordhuis 8ff1353766 Merge branch '2.2' of git://github.com/antirez/redis into 2.2 2011-01-17 09:44:12 +01:00
antirez 99677e89d0 default hash encoding settings also fixed in redis.conf 2011-01-15 22:32:34 +01:00
antirez f43502fb0e lame typo causing bad hashes memory characteristic 2011-01-15 22:24:48 +01:00
antirez ebb07fb4bb implemented two new INFO fields showing the size of clients max input and output buffers. 2011-01-14 10:20:35 +01:00
antirez c13c4080e5 useless expression removed in zmalloc.c 2011-01-14 09:57:07 +01:00
antirez 360664c9c8 master-slave replication fixed, it was not listing any key using KEYS command in the slave. 2011-01-14 09:54:41 +01:00
Pieter Noordhuis 7ac1b364fe Merge branch 'zset-range' into 2.2 2011-01-13 16:30:02 +01:00
Pieter Noordhuis 1b2b8cbbde Compiler should decide on inlining 2011-01-13 16:06:03 +01:00
antirez f487bbbfe2 allocation stats in INFO 2011-01-09 15:57:46 +01:00
Pieter Noordhuis 4b2499cf2f Remove newlines for networking errors 2011-01-05 10:50:47 +01:00
Pieter Noordhuis 01a0b09886 Remove glueoutputbuf from redis.conf 2011-01-05 10:44:54 +01:00
Pieter Noordhuis 90419b5681 Remove glueoutputbuf option and broken code 2011-01-05 10:37:23 +01:00
antirez ae7db3fe83 Merge remote branch 'pietern/bench-fix' into 2.2 2010-12-23 13:17:54 +01:00
Pieter Noordhuis 454f4bfd28 Fix compiler warnings on Solaris 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 1408d147e4 limits.h is already included from redis.h 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 78055bd4ca Solaris 10 doesn't know AF_LOCAL 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 736d447380 Solaris doesn't support -rdynamic 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 71b6f64f2a Update hiredis to 0.9.2 2010-12-23 13:02:21 +01:00
antirez c4e93cadfb Merge branch '2.2' of github.com:antirez/redis into 2.2 2010-12-20 13:35:13 +01:00
antirez 0540df22ba version bumped to 2.1.10 2010-12-20 13:34:07 +01:00
antirez 9ac5be2ebe Improved release notes. Added RC2 changes 2010-12-20 13:32:54 +01:00
antirez b902c1413e TODO file just pointing to the master branch TODO file 2010-12-20 13:16:38 +01:00
antirez 54e9f28922 Merge branch '2.2' of github.com:antirez/redis into 2.2 2010-12-20 13:14:36 +01:00
antirez dd889d884f removed some dead code, added evicted keys counter separated from expired keys. 2010-12-19 15:15:33 +01:00
antirez 1443a814d0 overflow detection in INCR family functions 2010-12-19 12:23:35 +01:00
antirez 076ff11c06 fixed typo in release notes 2010-12-15 18:10:15 +01:00
Pieter Noordhuis 4769dc7826 Undo rename of function names where something went wrong 2010-12-09 10:37:35 +01:00
Pieter Noordhuis 039357e471 Move logic concerned with zset ranges
This also optimizes ZREVRANGEBYSCORE for pathological cases where a
sorted set contains many elements with the same score. Previously,
it would traverse the list from back to front in such a case.
2010-12-07 23:21:07 +01:00
56 changed files with 2648 additions and 6243 deletions
+1 -3
View File
@@ -1,4 +1,3 @@
.*.swp
*.o
*.rdb
*.log
@@ -8,6 +7,7 @@ redis-benchmark
redis-check-dump
redis-check-aof
doc-tools
mkrelease.sh
release
myredis.conf
misc/*
@@ -19,5 +19,3 @@ release.h
src/transfer.sh
src/configs
src/redis-server.dSYM
redis.ds
src/redis.conf
+48 -8
View File
@@ -1,15 +1,55 @@
This is Redis 2.3.x.
Redis 2.4 release notes
It was forked from 2.2 branch a few weeks ago, and is going to be the playground
for many new developments, especially related to replacing the VM with something
better.
Migrating from 2.2 to 2.4
=========================
VM is already completely removed from this release, and instead diskstore was
implemented. You can read more about it here:
Redis 2.2 is mostly a strict subset of 2.4.
The only thing you should be aware is that you can't use .rdb and AOF files
generated with 2.4 into a 2.2 instance.
http://groups.google.com/group/redis-db/browse_thread/thread/d444bc786689bde9
2.4 slaves can be attached to 2.2 masters, but not the contrary.
This Redis version is not intented for production environments.
From the point of view of the API Redis 2.4 only adds new commands
(other commands now accepts a variable number of arguments) so you don't need
to modify your program in order to use Redis 2.4.
---------
CHANGELOG
---------
What's new in Redis 2.3.5 (2.4 Release Candidate 1)
===================================================
This is the first Release Candidate of Redis 2.4, in our experience the
server is very stable, however it is always better to closely monitor your
server if you switch to 2.4 RC1. Also make sure to perform a backup of your
old data set before switching from 2.2 to 2.4.
WHAT'S NEW IN REDIS 2.4 compared to the 2.2 version?
====================================================
* Specially encoded sorted sets, now small sorted sets will use little memory.
* Native persistence of specially encoded data types (ziplists, zipmaps,
intsets). Many data sets will be saved and loaded an order of magnitude
faster.
* Variadic versions of commands: SADD, HDEL, SREM, ZREM, ZADD, L/RPUSH.
* Jemalloc support (enabled by default for Linux build) in order to
avoid fragmentation issues.
* Reduced memory usage while saving.
* More info fields (peak memory, fork time, ...)
* OBJECT command for objects introspection.
* CLIENT command for clients introspection.
* Non blocking slave -> master connection.
* Better redis-cli connection handling. New redis-cli features.
* Better redis-benchmark, now able to benchmark user provided commands.
* Colorized Make.
* VM deprecated. Still supported but with a big warning... don't use it.
* Many speed optimizations and bug fixes.
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.
Cheers,
Salvatore
-156
View File
@@ -1,156 +0,0 @@
CLUSTER README
==============
Redis Cluster is currenty a work in progress, however there are a few things
that you can do already with it to see how it works.
The following guide show you how to setup a three nodes cluster and issue some
basic command against it.
... WORK IN PROGRESS ...
1) Show MIGRATE
2) Show CLUSTER MEET
3) Show link status detection with CLUSTER NODES
4) Show how to add slots with CLUSTER ADDSLOTS
5) Show redirection
6) Show cluster down
... WORK IN PROGRESS ...
TODO
====
*** WARNING: all the following problably has some meaning only for
*** me (antirez), most info are not updated, so please consider this file
*** as a private TODO list / brainstorming.
- disconnect FAIL clients after some pong idle time.
---------------------------------
* Majority rule: the cluster con continue when there are all the hash slots covered AND when there are the majority of masters.
* Shutdown on request rule: when a node sees many connections closed or even a timeout longer than usual on almost all the other nodes, it will usually wait for the normal timeout before to change the state, unless it receives a query from a client: in such a case it will put itself into error status.
--------------------------------
* When asked for a key that is not in a node's business it will reply:
-ASK 1.2.3.4:6379 (in case we want the client to ask just one time)
-MOVED <slotid> 1.2.3.4:6379 (in case the hash slot is permanently moved)
So with -ASK a client should just retry the query against this new node, a single time.
With -MOVED the client should update its hash slots table to reflect the fact that now the specified node is the one to contact for the specified hash slot.
* Nodes communicate using a binary protocol.
* Node failure detection.
1) Every node contains information about all the other nodes:
- If this node is believed to work ok or not
- The hash slots for which this node is responsible
- If the node is a master or a slave
- If it is a slave, the slave of which node
- if it is a master, the list of slave nodes
- The slaves are ordered for "<ip>:<port>" string from lower to higher
ordered lexicographically. When a master is down, the cluster will
try to elect the first slave in the list.
2) Every node also contains the unix time where every other node was
reported to work properly (that is, it replied to a ping or any other
protocol request correctly). For every node we also store the timestamp
at which we sent the latest ping, so we can easily compute the current
lag.
3) From time to time a node pings a random node, selected among the nodes
with the least recent "alive" time stamp. Three random nodes are selected
and the one with lower alive time stamp is pinged.
4) The ping packet contains also information about a few random nodes
alive time stamp. So that the receiver of the ping will update the
alive table if the received alive timestamp is more recent the
one present in the node local table.
In the ping packet every node "gossip" information is somethig like
this:
<ip>:<port>:<status>:<pingsent_timestamp>:<pongreceived_timestamp>
status is OK, POSSIBLE_FAILURE, FAILURE.
5) The node replies to ping with a pong packet, that also contains a random
selections of nodes timestamps.
A given node thinks another node may be in a failure state once there is a
ping timeout bigger than 30 seconds (configurable).
When a possible failure is detected the node performs the following action:
1) Is the average between all the other nodes big? For instance bigger
than 30 seconds / 2 = 15 seconds? Probably *we* are disconnected.
In such a case we don't trust our lag data, and reset all the
timestamps of sent ping to zero. This way when we'll reconnect there
is no risk that we'll claim many nodes are down, taking inappropriate
actions.
2) Messages from nodes marked as failed are *always* ignored by the other
nodes. A new node needs to be "introduced" by a good online node.
3) If we are well connected (that is, condition "1" is not true) and a
node timeout is > 30 seconds, we mark the node as POSSIBLE_FAILURE
(a flat in the cluster node structure). Every time we sent a ping
to another node we inform this other nodes that we detected this
condition, as already stated.
4) Once a node receives a POSSIBLE_FAILURE status for a node that is
already marked as POSSIBLE_FAILURE locally, it sends a message
to all the other nodes of type NODE_FAILURE_DETECTED, communicating the
ip/port of the specified node.
All the nodes need to update the status of this node setting it into
FAILURE.
5) If the computer in FAILURE state is a master node, what is needed is
to perform a Slave Election.
SLAVE ELECTION
1) The slave election is performed by the first slave (with slaves ordered
lexicographically). Actually it is the first functioning slave, so if
the first slave is marked as failing the next slave will perform the
election and so forth. Such a slave is called the "Successor".
2) The Successor starts checking that all the nodes in the cluster already
marked the master in FAILURE state. If at least one node does not agree
no action is performed.
3) If all the nodes agree that the master is failing, the Successor does
the following:
a) It will send a SUCCESSION message to all the other nodes, that will
upgrade the hash slot tables accordingly. It will make sure that all
the nodes are updated and if some node did not received the message
it will keep trying.
b) Once all nodes not marked as FAILURE accepted the SUCCESSION message
it will update his own table and will start acting as a master
accepting write queries.
c) Every node receiving the succession message, if not already informed
of the change will broadcast the same message to other three random
nodes. No action is performed if the specified host was already marked
as the master node.
d) A node that was a slave of the original master that failed will
switch master to the new one once the SUCCESSION message is received.
RANDOM
1) When selecting a slave, the system will try to pick one with an IP different than the master and other slaves, if possible.
2) The PING packet also contains information about the local configuration checksum. This is the SHA1 of the current configuration, without the bits that normally change form one node to another (like latest ping reply, failure status of nodes, and so forth). From time to time the local config SHA1 is checked against the list of the other nodes, and if there is a mismatch between our configuration and the most common one that lasts for more than N seconds, the most common configuration is asked and retrieved from another node. The event is logged.
3) Every time a node updates its internal cluster configuration, it dumps such a config in the cluster.conf file. On startup the configuration is reloaded.
Nodes can share the cluster configuration when needed (for instance if SHA1 does not match) using this exact same format.
CLIENTS
- Clients may be configured to use slaves to perform reads, when read-after-write consistency is not required.
+1 -30
View File
@@ -1,30 +1 @@
To compile Redis, do the following:
cd src; make
The compilation will produce a redis-server binary.
To install Redis, use
make install
and all the binaries will be installed on /usr/local/bin.
Alternatively:
make PREFIX=/some/other/directory install
to have the binaries in /some/other/directory/bin.
Run the server using the following command line:
/path/to/redis-server
This will start a Redis server with the default configuration.
Otherwise if you want to provide your configuration use:
/path/to/redis-server /path/to/redis.conf
You can find an example redis.conf file in the root directory
of this source distribution.
Please check the README file.
+25 -17
View File
@@ -2,10 +2,7 @@ Where to find complete Redis documentation?
-------------------------------------------
This README is just a fast "quick start" document. You can find more detailed
documentation here:
1) http://code.google.com/p/redis
2) Check the 'doc' directory. doc/README.html is a good starting point :)
documentation at http://redis.io
Building Redis
--------------
@@ -23,26 +20,37 @@ You can run a 32 bit Redis binary using:
% make 32bit
After you build Redis is a good idea to test it, using:
After building Redis is a good idea to test it, using:
% make test
Buliding using tcmalloc
-----------------------
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.
tcmalloc is a fast and space efficient implementation (for little objects)
of malloc(). Compiling Redis with it can improve performances and memeory
usage. You can read more about it here:
Allocator
---------
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
By default Redis compiles and links against jemalloc under Linux, since
glibc malloc() has memory fragmentation problems.
In order to compile Redis with tcmalloc support install tcmalloc on your system
and then use:
To force a libc malloc() build use:
% make USE_TCMALLOC=yes
make FORCE_LIBC_MALLOC=yes
Note that you can pass any other target to make, as long as you append
USE_TCMALLOC=yes at the end.
In all the other non Linux systems the libc malloc() is used by default.
On Mac OS X you can force a jemalloc based build using the following:
make USE_JEMALLOC=yes
Verbose build
-------------
Redis will build with a user friendly colorized output by default.
If you want to see a more verbose output use the following:
make V=1
Running Redis
-------------
@@ -80,7 +88,7 @@ then in another terminal try the following:
You can find the list of all the available commands here:
http://code.google.com/p/redis/wiki/CommandReference
http://redis.io/commands
Enjoy!
+3 -84
View File
@@ -1,85 +1,4 @@
Redis TODO
----------
This is a stable release! No TODO file here.
Please check the TODO file in the master branch on github.
WARNING: are you a possible Redis contributor?
Before implementing what is listed what is listed in this file
please drop a message in the Redis google group or chat with
antirez or pietern on irc.freenode.org #redis to check if the work
is already in progress and if the feature is still interesting for
us, and *how* exactly this can be implemented to have good changes
of a merge. Otherwise it is probably wasted work! Thank you
API CHANGES
===========
* Turn commands into variadic versions when it makes sense, that is, when
the variable number of arguments represent values, and there is no conflict
with the return value of the command.
CLUSTER
=======
* Implement rehashing and cluster check in redis-trib.
* Reimplement MIGRATE / RESTORE to use just in memory buffers (no disk at
all). This will require touching a lot of the RDB stuff around, but we may
hand with faster persistence for RDB.
* Implement the slave nodes semantics and election.
* Allow redis-trib to create a cluster-wide snapshot (using SYNC).
* Allow redis-trib to restore a cluster-wide snapshot (implement UPLOAD?).
APPEND ONLY FILE
================
* in AOF rewirte use HMSET to rewrite small hashes instead of multiple calls
to HSET.
OPTIMIZATIONS
=============
* Avoid COW due to incrementing the dict iterators counter.
* SORT: Don't copy the list into a vector when BY argument is constant.
* Write the hash table size of every db in the dump, so that Redis can resize the hash table just one time when loading a big DB.
* Read-only mode for slaves.
* Redis big lists as linked lists of small ziplists?
Possibly a simple heuristic that join near nodes when some node gets smaller than the low_level, and split it into two if gets bigger than high_level.
REPORTING
=========
* Better INFO output with sections.
RANDOM
======
* Clients should be closed as far as the output buffer list is bigger than a given number of elements (configurable in redis.conf)
* Should the redis default configuration, and the default redis.conf, just bind 127.0.0.1?
KNOWN BUGS
==========
* What happens in the following scenario:
1) We are reading an AOF file.
2) SETEX FOO 5 BAR
3) APPEND FOO ZAP
What happens if between 1 and 2 for some reason (system under huge load
or alike) too many time passes? We should prevent expires while the
AOF is loading.
* #519: Slave may have expired keys that were never read in the master (so a DEL
is not sent in the replication channel) but are already expired since
a lot of time. Maybe after a given delay that is undoubltly greater than
the replication link latency we should expire this key on the slave on
access?
DISKSTORE TODO
==============
* Fix FLUSHALL/FLUSHDB: the queue of pending reads/writes should be handled.
* Check that 00/00 and ff/ff exist at startup, otherwise exit with error.
* Implement sync flush option, where data is written synchronously on disk when a command is executed.
* Implement MULTI/EXEC as transaction abstract API to diskstore.c, with transaction_start, transaction_end, and a journal to recover.
* Stop BGSAVE thread on shutdown and any other condition where the child is killed during normal bgsave.
* Fix RANDOMKEY to really do something interesting
* Fix DBSIZE to really do something interesting
* Add a DEBUG command to check if an entry is or not in memory currently
* dscache.c near 236, kobj = createStringObject... we could use static obj.
https://github.com/antirez/redis/raw/master/TODO
+70 -41
View File
@@ -292,51 +292,80 @@ appendfsync everysec
# "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size will growth by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (or if no rewrite happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a precentage of zero in order to disable the automatic AOF
# rewrite feature.
################################ VIRTUAL MEMORY ###############################
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
### WARNING! Virtual Memory is deprecated in Redis 2.4
### The use of Virtual Memory is strongly discouraged.
#################################### DISK STORE ###############################
# Virtual Memory allows Redis to work with datasets bigger than the actual
# amount of RAM needed to hold the whole dataset in memory.
# In order to do so very used keys are taken in memory while the other keys
# are swapped into a swap file, similarly to what operating systems do
# with memory pages.
#
# To enable VM just set 'vm-enabled' to yes, and set the following three
# VM parameters accordingly to your needs.
# When disk store is active Redis works as an on-disk database, where memory
# is only used as a object cache.
#
# This mode is good for datasets that are bigger than memory, and in general
# when you want to trade speed for:
#
# - less memory used
# - immediate server restart
# - per key durability, without need for backgrond savig
#
# On the other hand, with disk store enabled MULTI/EXEC are no longer
# transactional from the point of view of the persistence on disk, that is,
# Redis transactions will still guarantee that commands are either processed
# all or nothing, but there is no guarantee that all the keys are flushed
# on disk in an atomic way.
#
# Of course with disk store enabled Redis is not as fast as it is when
# working with just the memory back end.
vm-enabled no
# vm-enabled yes
diskstore-enabled no
diskstore-path redis.ds
cache-max-memory 0
cache-flush-delay 0
# This is the path of the Redis swap file. As you can guess, swap files
# can't be shared by different Redis instances, so make sure to use a swap
# file for every redis process you are running. Redis will complain if the
# swap file is already in use.
#
# The best kind of storage for the Redis swap file (that's accessed at random)
# is a Solid State Disk (SSD).
#
# *** WARNING *** if you are using a shared hosting the default of putting
# the swap file under /tmp is not secure. Create a dir with access granted
# only to Redis user and configure Redis to create the swap file there.
vm-swap-file /tmp/redis.swap
# vm-max-memory configures the VM to use at max the specified amount of
# RAM. Everything that deos not fit will be swapped on disk *if* possible, that
# is, if there is still enough contiguous space in the swap file.
#
# With vm-max-memory 0 the system will swap everything it can. Not a good
# default, just specify the max amount of RAM you can in bytes, but it's
# better to leave some margin. For instance specify an amount of RAM
# that's more or less between 60 and 80% of your free RAM.
vm-max-memory 0
# Redis swap files is split into pages. An object can be saved using multiple
# contiguous pages, but pages can't be shared between different objects.
# So if your page is too big, small objects swapped out on disk will waste
# a lot of space. If you page is too small, there is less space in the swap
# file (assuming you configured the same number of total swap file pages).
#
# If you use a lot of small objects, use a page size of 64 or 32 bytes.
# If you use a lot of big objects, use a bigger page size.
# If unsure, use the default :)
vm-page-size 32
# Number of total memory pages in the swap file.
# Given that the page table (a bitmap of free/used pages) is taken in memory,
# every 8 pages on disk will consume 1 byte of RAM.
#
# The total swap size is vm-page-size * vm-pages
#
# With the default of 32-bytes memory pages and 134217728 pages Redis will
# use a 4 GB swap file, that will use 16 MB of RAM for the page table.
#
# It's better to use the smallest acceptable value for your application,
# but the default is large in order to work in most conditions.
vm-pages 134217728
# Max number of VM I/O threads running at the same time.
# This threads are used to read/write data from/to swap file, since they
# also encode and decode objects from disk to memory or the reverse, a bigger
# number of threads can help with big objects even if they can't help with
# I/O itself as the physical device may not be able to couple with many
# reads/writes operations at the same time.
#
# The special value of 0 turn off threaded I/O and enables the blocking
# Virtual Memory implementation.
vm-max-threads 4
############################### ADVANCED CONFIG ###############################
-20
View File
@@ -1,20 +0,0 @@
[Note: this is the Redis manifesto, for general information about
installing and running Redis read the README file instead.]
Redis Manifesto
===============
1 - A DSL for Abstract Data Types. Redis is a DSL (Domain Specific Language) that manipulates abstract data types and implemented as a TCP daemon. Commands manipulate a key space where keys are binary-safe strings and values are different kinds of abstract data types. Every data type represents an abstract version of a fundamental data structure. For instance Redis Lists are an abstract representation of linked lists. In Redis, the essence of a data type isn't just the kind of operations that the data types support, but also the space and time complexity of the data type and the operations performed upon it.
2 - Memory storage is #1. The Redis data set, composed of defined key-value pairs, is primarily stored in the computer's memory. The amount of memory in all kinds of computers, including entry-level servers, is increasing significantly each year. Memory is fast, and allows Redis to have very predictable performance. Datasets composed of 10k or 40 millions keys will perform similarly. Complex data types like Redis Sorted Sets are easy to implement and manipulate in memory with good performance, making Redis very simple. Redis will continue to explore alternative options (where data can be optionally stored on disk, say) but the main goal of the project remains the development of an in-memory database.
3 - Fundamental data structures for a fundamental API. The Redis API is a direct consequence of fundamental data structures. APIs can often be arbitrary but not an API that resembles the nature of fundamental data structures. If we ever meet intelligent life forms from another part of the universe, they'll likely know, understand and recognize the same basic data structures we have in our computer science books. Redis will avoid intermediate layers in API, so that the complexity is obvious and more complex operations can be performed as the sum of the basic operations.
4 - Code is like a poem; it's not just something we write to reach some practical result. Sometimes people that are far from the Redis philosophy suggest using other code written by other authors (frequently in other languages) in order to implement something Redis currently lacks. But to us this is like if Shakespeare decided to end Enrico IV using the Paradiso from the Divina Commedia. Is using any external code a bad idea? Not at all. Like in "One Thousand and One Nights" smaller self contained stories are embedded in a bigger story, we'll be happy to use beautiful self contained libraries when needed. At the same time, when writing the Redis story we're trying to write smaller stories that will fit in to other code.
5 - We're against complexity. We believe designing systems is a fight against complexity. We'll accept to fight the complexity when it's worthwhile but we'll try hard to recognize when a small feature is not worth 1000s of lines of code. Most of the time the best way to fight complexity is by not creating it at all.
6 - Two levels of API. The Redis API has two levels: 1) a subset of the API fits naturally into a distributed version of Redis and 2) a more complex API that supports multi-key operations. Both are useful if used judiciously but there's no way to make the more complex multi-keys API distributed in an opaque way without violating our other principles. We don't want to provide the illusion of something that will work magically when actually it can't in all cases. Instead we'll provide commands to quickly migrate keys from one instance to another to perform multi-key operations and expose the tradeoffs to the user.
7 - We optimize for joy. We believe writing code is a lot of hard work, and the only way it can be worth is by enjoying it. When there is no longer joy in writing code, the best thing to do is stop. To prevent this, we'll avoid taking paths that will make Redis less of a joy to develop.
+73 -75
View File
@@ -6,6 +6,18 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O2
CCCOLOR="\033[34m"
LINKCOLOR="\033[34;1m"
SRCCOLOR="\033[33m"
BINCOLOR="\033[37;1m"
MAKECOLOR="\033[32;1m"
ENDCOLOR="\033[0m"
ifndef V
QUIET_CC = @echo ' ' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
QUIET_LINK = @echo ' ' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
endif
ifeq ($(uname_S),Linux)
ifneq ($(FORCE_LIBC_MALLOC),yes)
USE_JEMALLOC=yes
@@ -15,21 +27,21 @@ endif
ifeq ($(uname_S),SunOS)
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
DEBUG?=-g -ggdb
DEBUG?=-g -ggdb
else
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
CCLINK?=-lm -pthread
DEBUG?=-g -rdynamic -ggdb
DEBUG?=-g -rdynamic -ggdb
endif
ifeq ($(USE_TCMALLOC),yes)
ALLOD_DEPS=
ALLOC_DEP=
ALLOC_LINK=-ltcmalloc
ALLOC_FLAGS=-DUSE_TCMALLOC
endif
ifeq ($(USE_TCMALLOC_MINIMAL),yes)
ALLOD_DEPS=
ALLOC_DEP=
ALLOC_LINK=-ltcmalloc_minimal
ALLOC_FLAGS=-DUSE_TCMALLOC
endif
@@ -40,28 +52,13 @@ ifeq ($(USE_JEMALLOC),yes)
ALLOC_FLAGS=-DUSE_JEMALLOC -I../deps/jemalloc/include
endif
CCLINK+= $(ALLOC_LINK)
CFLAGS+= $(ALLOC_FLAGS)
CCOPT= $(CFLAGS) $(ARCH) $(PROF)
PREFIX= /usr/local
INSTALL_BIN= $(PREFIX)/bin
INSTALL= cp -p
CCCOLOR="\033[34m"
LINKCOLOR="\033[34;1m"
SRCCOLOR="\033[33m"
BINCOLOR="\033[37;1m"
MAKECOLOR="\033[32;1m"
ENDCOLOR="\033[0m"
ifndef V
QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
endif
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 dscache.o pubsub.o multi.o debug.o sort.o intset.o syncio.o diskstore.o cluster.o crc16.o endian.o
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 vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
@@ -80,123 +77,124 @@ all: redis-benchmark redis-cli redis-check-dump redis-check-aof redis-server
# Deps (use make dep to generate this)
adlist.o: adlist.c adlist.h zmalloc.h
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
ae.o: ae.c ae.h config.h zmalloc.h ae_epoll.c ae_kqueue.c ae_select.c
ae_epoll.o: ae_epoll.c
ae_kqueue.o: ae_kqueue.c
ae_select.o: ae_select.c
anet.o: anet.c fmacros.h anet.h
aof.o: aof.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
cluster.o: cluster.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
config.o: config.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
crc16.o: crc16.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
db.o: db.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
debug.o: debug.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h sha1.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h sha1.h
dict.o: dict.c fmacros.h dict.h zmalloc.h
diskstore.o: diskstore.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
sha1.h
dscache.o: dscache.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
intset.o: intset.c intset.h zmalloc.h
endian.o: endian.c
intset.o: intset.c intset.h zmalloc.h endian.h
lzf_c.o: lzf_c.c lzfP.h
lzf_d.o: lzf_d.c lzfP.h
multi.o: multi.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
networking.o: networking.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
object.o: object.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
pqsort.o: pqsort.c
pubsub.o: pubsub.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
rdb.o: rdb.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h lzf.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
../deps/hiredis/hiredis.h sds.h adlist.h zmalloc.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h lzf.h
redis-benchmark.o: redis-benchmark.c fmacros.h ae.c ae.h ae_epoll.c \
ae_kqueue.c ae_select.c config.h zmalloc.h ae_kqueue.c adlist.c adlist.h \
sds.c sds.h
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
redis-check-dump.o: redis-check-dump.c lzf.h
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
redis-cli.o: redis-cli.c fmacros.h version.h help.h sds.c sds.h
redis.o: redis.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h \
asciilogo.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
release.o: release.c release.h
replication.o: replication.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
sds.o: sds.c sds.h zmalloc.h
sha1.o: sha1.c sha1.h config.h
sha1.o: sha1.c sha1.h
sort.o: sort.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h pqsort.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h pqsort.h
syncio.o: syncio.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
t_hash.o: t_hash.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
t_list.o: t_list.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
t_set.o: t_set.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
t_string.o: t_string.c redis.h fmacros.h config.h ae.h sds.h dict.h \
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
adlist.h zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
t_zset.o: t_zset.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h util.h
util.o: util.c fmacros.h util.h
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endian.h
zipmap.o: zipmap.c zmalloc.h endian.h
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
util.o: util.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
vm.o: vm.c redis.h fmacros.h config.h ae.h sds.h dict.h adlist.h \
zmalloc.h anet.h zipmap.h ziplist.h intset.h version.h
ziplist.o: ziplist.c zmalloc.h ziplist.h
zipmap.o: zipmap.c zmalloc.h
zmalloc.o: zmalloc.c config.h zmalloc.h
.PHONY: dependencies
dependencies:
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
@echo $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
@cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)"
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)linenoise$(ENDCOLOR)
@echo $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)linenoise$(ENDCOLOR)
@cd ../deps/linenoise && $(MAKE) ARCH="$(ARCH)"
../deps/jemalloc/lib/libjemalloc.a:
cd ../deps/jemalloc && ./configure $(JEMALLOC_CFLAGS) --with-jemalloc-prefix=je_ --enable-cc-silence && $(MAKE) lib/libjemalloc.a
redis-server: $(OBJ)
$(QUIET_CC)$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) $(CCLINK) $(ALLOC_LINK)
redis-benchmark: dependencies $(BENCHOBJ)
@cd ../deps/hiredis && $(MAKE) static
$(QUIET_LINK)$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a $(CCLINK) $(ALLOC_LINK)
@echo $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)jemalloc$(ENDCOLOR)
@cd ../deps/jemalloc && ./configure $(JEMALLOC_CFLAGS) --with-jemalloc-prefix=je_ --enable-cc-silence && $(MAKE) lib/libjemalloc.a
redis-benchmark.o:
$(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis $(DEBUG) $(COMPILE_TIME) $<
redis-cli: dependencies $(CLIOBJ)
$(QUIET_LINK)$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ) ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(CCLINK) $(ALLOC_LINK)
redis-benchmark: dependencies $(BENCHOBJ)
@echo $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)hiredis$(ENDCOLOR)
@cd ../deps/hiredis && $(MAKE) static ARCH="$(ARCH)"
$(QUIET_LINK)$(CC) -o $(BENCHPRGNAME) $(CCOPT) $(DEBUG) $(BENCHOBJ) ../deps/hiredis/libhiredis.a $(CCLINK) $(ALLOC_LINK)
redis-cli.o:
$(QUIET_CC)$(CC) -c $(CFLAGS) -I../deps/hiredis -I../deps/linenoise $(DEBUG) $(COMPILE_TIME) $<
redis-cli: dependencies $(CLIOBJ)
$(QUIET_LINK)$(CC) -o $(CLIPRGNAME) $(CCOPT) $(DEBUG) $(CLIOBJ) ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(CCLINK) $(ALLOC_LINK)
redis-check-dump.o:
$(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
redis-check-dump: $(CHECKDUMPOBJ)
$(QUIET_LINK)$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) $(CCLINK) $(ALLOC_LINK)
$(QUIET_LINK)$(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ) $(CCLINK)
redis-check-aof.o:
$(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
redis-check-aof: $(CHECKAOFOBJ)
$(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) $(CCLINK) $(ALLOC_LINK)
$(QUIET_LINK)$(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ) $(CCLINK)
redis-server: $(OBJ)
$(QUIET_LINK)$(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) $(CCLINK) $(ALLOC_LINK)
# Because the jemalloc.h header is generated as a part of the jemalloc build
# process, building it should complete before building any other object.
%.o: %.c $(ALLOC_DEP)
$(QUIET_CC)$(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
$(QUIET_CC)$(CC) -c $(CFLAGS) $(ALLOC_FLAGS) $(DEBUG) $(COMPILE_TIME) $<
clean:
rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c -I ../deps/hiredis -I ../deps/linenoise
$(CC) -MM *.c
test: redis-server
@(cd ..; (which tclsh >/dev/null && tclsh tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}") || echo "You need to install Tcl in order to run tests.")
(cd ..; tclsh8.5 tests/test_helper.tcl --tags "${TAGS}" --file "${FILE}")
bench:
./redis-benchmark
+40 -39
View File
@@ -8,8 +8,6 @@
#include <sys/resource.h>
#include <sys/wait.h>
void aofUpdateCurrentSize(void);
/* Called when the user switches from "appendonly yes" to "appendonly no"
* at runtime using the CONFIG command. */
void stopAppendOnly(void) {
@@ -21,15 +19,15 @@ void stopAppendOnly(void) {
server.appendseldb = -1;
server.appendonly = 0;
/* rewrite operation in progress? kill it, wait child exit */
if (server.bgrewritechildpid != -1) {
if (server.bgsavechildpid != -1) {
int statloc;
if (kill(server.bgrewritechildpid,SIGKILL) != -1)
if (kill(server.bgsavechildpid,SIGKILL) != -1)
wait3(&statloc,0,NULL);
/* reset the buffer accumulating changes while the child saves */
sdsfree(server.bgrewritebuf);
server.bgrewritebuf = sdsempty();
server.bgrewritechildpid = -1;
server.bgsavechildpid = -1;
}
}
@@ -84,7 +82,6 @@ void flushAppendOnlyFile(void) {
}
sdsfree(server.aofbuf);
server.aofbuf = sdsempty();
server.appendonly_current_size += nwritten;
/* Don't Fsync if no-appendfsync-on-rewrite is set to yes and we have
* childs performing heavy I/O on disk. */
@@ -224,7 +221,6 @@ int loadAppendOnlyFile(char *filename) {
long loops = 0;
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.appendonly_current_size = 0;
fclose(fp);
return REDIS_ERR;
}
@@ -248,6 +244,7 @@ int loadAppendOnlyFile(char *filename) {
char buf[128];
sds argsds;
struct redisCommand *cmd;
int force_swapout;
/* Serve the clients from time to time */
if (!(loops++ % 1000)) {
@@ -293,6 +290,17 @@ int loadAppendOnlyFile(char *filename) {
for (j = 0; j < fakeClient->argc; j++)
decrRefCount(fakeClient->argv[j]);
zfree(fakeClient->argv);
/* Handle swapping while loading big datasets when VM is on */
force_swapout = 0;
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
force_swapout = 1;
if (server.vm_enabled && force_swapout) {
while (zmalloc_used_memory() > server.vm_max_memory) {
if (vmSwapOneObjectBlocking() == REDIS_ERR) break;
}
}
}
/* This point can only be reached when EOF is reached without errors.
@@ -303,8 +311,6 @@ int loadAppendOnlyFile(char *filename) {
freeFakeClient(fakeClient);
server.appendonly = appendonly;
stopLoading();
aofUpdateCurrentSize();
server.auto_aofrewrite_base_size = server.appendonly_current_size;
return REDIS_OK;
readerr:
@@ -342,7 +348,7 @@ int rewriteAppendOnlyFile(char *filename) {
redisDb *db = server.db+j;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
di = dictGetIterator(d);
if (!di) {
fclose(fp);
return REDIS_ERR;
@@ -354,14 +360,25 @@ int rewriteAppendOnlyFile(char *filename) {
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
sds keystr;
sds keystr = dictGetEntryKey(de);
robj key, *o;
time_t expiretime;
int swapped;
keystr = dictGetEntryKey(de);
o = dictGetEntryVal(de);
initStaticStringObject(key,keystr);
/* If the value for this key is swapped, load a preview in memory.
* We use a "swapped" flag to remember if we need to free the
* value object instead to just increment the ref count anyway
* in order to avoid copy-on-write of pages if we are forked() */
if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||
o->storage == REDIS_VM_SWAPPING) {
swapped = 0;
} else {
o = vmPreviewObject(o);
swapped = 1;
}
expiretime = getExpire(db,&key);
/* Save the key and associated value */
@@ -530,6 +547,7 @@ int rewriteAppendOnlyFile(char *filename) {
if (fwriteBulkObject(fp,&key) == 0) goto werr;
if (fwriteBulkLongLong(fp,expiretime) == 0) goto werr;
}
if (swapped) decrRefCount(o);
}
dictReleaseIterator(di);
}
@@ -574,15 +592,13 @@ int rewriteAppendOnlyFileBackground(void) {
long long start;
if (server.bgrewritechildpid != -1) return REDIS_ERR;
if (server.ds_enabled != 0) {
redisLog(REDIS_WARNING,"BGREWRITEAOF called with diskstore enabled: AOF is not supported when diskstore is enabled. Operation not performed.");
return REDIS_ERR;
}
if (server.vm_enabled) waitEmptyIOJobsQueue();
start = ustime();
if ((childpid = fork()) == 0) {
char tmpfile[256];
/* Child */
if (server.vm_enabled) vmReopenSwapFile();
if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
@@ -617,10 +633,9 @@ int rewriteAppendOnlyFileBackground(void) {
void bgrewriteaofCommand(redisClient *c) {
if (server.bgrewritechildpid != -1) {
addReplyError(c,"Background append only file rewriting already in progress");
} else if (server.bgsavechildpid != -1) {
server.aofrewrite_scheduled = 1;
addReplyStatus(c,"Background append only file rewriting scheduled");
} else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
return;
}
if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
addReplyStatus(c,"Background append only file rewriting started");
} else {
addReply(c,shared.err);
@@ -634,24 +649,12 @@ void aofRemoveTempFile(pid_t childpid) {
unlink(tmpfile);
}
/* Update the server.appendonly_current_size filed explicitly using stat(2)
* to check the size of the file. This is useful after a rewrite or after
* a restart, normally the size is updated just adding the write length
* to the current lenght, that is much faster. */
void aofUpdateCurrentSize(void) {
struct redis_stat sb;
if (redis_fstat(server.appendfd,&sb) == -1) {
redisLog(REDIS_WARNING,"Unable to check the AOF length: %s",
strerror(errno));
} else {
server.appendonly_current_size = sb.st_size;
}
}
/* A background append only file rewriting (BGREWRITEAOF) terminated its work.
* Handle this. */
void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
void backgroundRewriteDoneHandler(int statloc) {
int exitcode = WEXITSTATUS(statloc);
int bysignal = WIFSIGNALED(statloc);
if (!bysignal && exitcode == 0) {
int fd;
char tmpfile[256];
@@ -689,8 +692,6 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
if (server.appendfsync != APPENDFSYNC_NO) aof_fsync(fd);
server.appendseldb = -1; /* Make sure it will issue SELECT */
redisLog(REDIS_NOTICE,"The new append only file was selected for future appends.");
aofUpdateCurrentSize();
server.auto_aofrewrite_base_size = server.appendonly_current_size;
} else {
/* If append only is disabled we just generate a dump in this
* format. Why not? */
@@ -701,7 +702,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
} else {
redisLog(REDIS_WARNING,
"Background append only file rewriting terminated by signal %d",
bysignal);
WTERMSIG(statloc));
}
cleanup:
sdsfree(server.bgrewritebuf);
-18
View File
@@ -1,18 +0,0 @@
char *ascii_logo =
" _._ \n"
" _.-``__ ''-._ \n"
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
" ( ' , .-` | `, ) Running in %s mode\n"
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
" | `-._ `._ / _.-' | PID: %ld\n"
" `-._ `-._ `-./ _.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | http://redis.io \n"
" `-._ `-._`-.__.-'_.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | \n"
" `-._ `-._`-.__.-'_.-' _.-' \n"
" `-._ `-.__.-' _.-' \n"
" `-._ _.-' \n"
" `-.__.-' \n\n";
-1766
View File
File diff suppressed because it is too large Load Diff
+28 -46
View File
@@ -30,6 +30,7 @@ void loadServerConfig(char *filename) {
char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
int linenum = 0;
sds line = NULL;
int really_use_vm = 0;
if (filename[0] == '-' && filename[1] == '\0')
fp = stdin;
@@ -231,18 +232,6 @@ void loadServerConfig(char *filename) {
err = "argument must be 'no', 'always' or 'everysec'";
goto loaderr;
}
} else if (!strcasecmp(argv[0],"auto-aof-rewrite-percentage") &&
argc == 2)
{
server.auto_aofrewrite_perc = atoi(argv[1]);
if (server.auto_aofrewrite_perc < 0) {
err = "Invalid negative percentage for AOF auto rewrite";
goto loaderr;
}
} else if (!strcasecmp(argv[0],"auto-aof-rewrite-min-size") &&
argc == 2)
{
server.auto_aofrewrite_min_size = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
server.requirepass = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"pidfile") && argc == 2) {
@@ -251,18 +240,25 @@ void loadServerConfig(char *filename) {
} else if (!strcasecmp(argv[0],"dbfilename") && argc == 2) {
zfree(server.dbfilename);
server.dbfilename = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"diskstore-enabled") && argc == 2) {
if ((server.ds_enabled = yesnotoi(argv[1])) == -1) {
} else if (!strcasecmp(argv[0],"vm-enabled") && argc == 2) {
if ((server.vm_enabled = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"diskstore-path") && argc == 2) {
sdsfree(server.ds_path);
server.ds_path = sdsnew(argv[1]);
} else if (!strcasecmp(argv[0],"cache-max-memory") && argc == 2) {
server.cache_max_memory = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"cache-flush-delay") && argc == 2) {
server.cache_flush_delay = atoi(argv[1]);
if (server.cache_flush_delay < 0) server.cache_flush_delay = 0;
} else if (!strcasecmp(argv[0],"really-use-vm") && argc == 2) {
if ((really_use_vm = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"vm-swap-file") && argc == 2) {
zfree(server.vm_swap_file);
server.vm_swap_file = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"vm-max-memory") && argc == 2) {
server.vm_max_memory = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"vm-page-size") && argc == 2) {
server.vm_page_size = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"vm-pages") && argc == 2) {
server.vm_pages = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"vm-max-threads") && argc == 2) {
server.vm_max_threads = strtoll(argv[1], NULL, 10);
} else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2) {
server.hash_max_zipmap_entries = memtoll(argv[1], NULL);
} else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2) {
@@ -301,13 +297,6 @@ void loadServerConfig(char *filename) {
err = "Target command name already exists"; goto loaderr;
}
}
} else if (!strcasecmp(argv[0],"cluster-enabled") && argc == 2) {
if ((server.cluster_enabled = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"cluster-config-file") && argc == 2) {
zfree(server.cluster.configfile);
server.cluster.configfile = zstrdup(argv[1]);
} else {
err = "Bad directive or wrong number of arguments"; goto loaderr;
}
@@ -317,6 +306,7 @@ void loadServerConfig(char *filename) {
sdsfree(line);
}
if (fp != stdin) fclose(fp);
if (server.vm_enabled && !really_use_vm) goto vm_warning;
return;
loaderr:
@@ -325,6 +315,15 @@ loaderr:
fprintf(stderr, ">>> '%s'\n", line);
fprintf(stderr, "%s\n", err);
exit(1);
vm_warning:
fprintf(stderr, "\nARE YOU SURE YOU WANT TO USE VM?\n\n");
fprintf(stderr, "Redis Virtual Memory is going to be deprecated soon,\n");
fprintf(stderr, "we think you should NOT use it, but use Redis only if\n");
fprintf(stderr, "your data is suitable for an in-memory database.\n");
fprintf(stderr, "If you *really* want VM add this in the config file:\n");
fprintf(stderr, "\n really-use-vm yes\n\n");
exit(1);
}
/*-----------------------------------------------------------------------------
@@ -407,12 +406,6 @@ void configSetCommand(redisClient *c) {
}
}
}
} else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-percentage")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.auto_aofrewrite_perc = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"auto-aof-rewrite-min-size")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.auto_aofrewrite_min_size = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"save")) {
int vlen, j;
sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
@@ -599,16 +592,6 @@ void configGetCommand(redisClient *c) {
sdsfree(buf);
matches++;
}
if (stringmatch(pattern,"auto-aof-rewrite-percentage",0)) {
addReplyBulkCString(c,"auto-aof-rewrite-percentage");
addReplyBulkLongLong(c,server.auto_aofrewrite_perc);
matches++;
}
if (stringmatch(pattern,"auto-aof-rewrite-min-size",0)) {
addReplyBulkCString(c,"auto-aof-rewrite-min-size");
addReplyBulkLongLong(c,server.auto_aofrewrite_min_size);
matches++;
}
if (stringmatch(pattern,"slave-serve-stale-data",0)) {
addReplyBulkCString(c,"slave-serve-stale-data");
addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no");
@@ -666,7 +649,6 @@ void configCommand(redisClient *c) {
server.stat_numcommands = 0;
server.stat_numconnections = 0;
server.stat_expiredkeys = 0;
resetCommandTableStats();
addReply(c,shared.ok);
} else {
addReplyError(c,
+6 -57
View File
@@ -5,7 +5,7 @@
#include <AvailabilityMacros.h>
#endif
/* Define redis_fstat to fstat or fstat64() */
/* define redis_fstat to fstat or fstat64() */
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
#define redis_fstat fstat64
#define redis_stat stat64
@@ -14,22 +14,22 @@
#define redis_stat stat
#endif
/* Test for proc filesystem */
/* test for proc filesystem */
#ifdef __linux__
#define HAVE_PROCFS 1
#endif
/* Test for task_info() */
/* test for task_info() */
#if defined(__APPLE__)
#define HAVE_TASKINFO 1
#endif
/* Test for backtrace() */
/* test for backtrace() */
#if defined(__APPLE__) || defined(__linux__)
#define HAVE_BACKTRACE 1
#endif
/* Test for polling API */
/* test for polling API */
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
@@ -38,62 +38,11 @@
#define HAVE_KQUEUE 1
#endif
/* Define aof_fsync to fdatasync() in Linux and fsync() for all the rest */
/* define aof_fsync to fdatasync() in Linux and fsync() for all the rest */
#ifdef __linux__
#define aof_fsync fdatasync
#else
#define aof_fsync fsync
#endif
/* Byte ordering detection */
#include <sys/types.h> /* This will likely define BYTE_ORDER */
#ifndef BYTE_ORDER
#if (BSD >= 199103)
# include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__)
# include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
defined(__alpha__) || defined(__alpha)
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
defined(__hppa) || defined(__hp9000) || \
defined(__hp9000s300) || defined(__hp9000s700) || \
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
#error "Undefined or invalid BYTE_ORDER"
#endif
#endif
-74
View File
@@ -1,74 +0,0 @@
#include "redis.h"
/*
* Copyright 2001-2010 Georges Menie (www.menie.org)
* Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style)
* 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 the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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.
*/
/* CRC16 implementation acording to CCITT standards */
static const uint16_t crc16tab[256]= {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};
uint16_t crc16(const char *buf, int len) {
int counter;
uint16_t crc = 0;
for (counter = 0; counter < len; counter++)
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
return crc;
}
+85 -291
View File
@@ -2,42 +2,10 @@
#include <signal.h>
void SlotToKeyAdd(robj *key);
void SlotToKeyDel(robj *key);
/*-----------------------------------------------------------------------------
* C-level DB API
*----------------------------------------------------------------------------*/
/* Important notes on lookup and disk store.
*
* When disk store is enabled on lookup we can have different cases.
*
* a) The key is in memory:
* - If the key is not in IO_SAVEINPROG state we can access it.
* As if it's just IO_SAVE this means we have the key in the IO queue
* but can't be accessed by the IO thread (it requires to be
* translated into an IO Job by the cache cron function.)
* - If the key is in IO_SAVEINPROG we can't touch the key and have
* to blocking wait completion of operations.
* b) The key is not in memory:
* - If it's marked as non existing on disk as well (negative cache)
* we don't need to perform the disk access.
* - if the key MAY EXIST, but is not in memory, and it is marked as IO_SAVE
* then the key can only be a deleted one. As IO_SAVE keys are never
* evicted (dirty state), so the only possibility is that key was deleted.
* - if the key MAY EXIST we need to blocking load it.
* We check that the key is not in IO_SAVEINPROG state before accessing
* the disk object. If it is in this state, we wait.
*/
void lookupWaitBusyKey(redisDb *db, robj *key) {
/* FIXME: wait just for this key, not everything */
waitEmptyIOJobsQueue();
processAllPendingIOJobs();
redisAssert((cacheScheduleIOGetFlags(db,key) & REDIS_IO_SAVEINPROG) == 0);
}
robj *lookupKey(redisDb *db, robj *key) {
dictEntry *de = dictFind(db->dict,key->ptr);
if (de) {
@@ -49,51 +17,29 @@ robj *lookupKey(redisDb *db, robj *key) {
if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1)
val->lru = server.lruclock;
if (server.ds_enabled &&
cacheScheduleIOGetFlags(db,key) & REDIS_IO_SAVEINPROG)
{
/* Need to wait for the key to get unbusy */
redisLog(REDIS_DEBUG,"Lookup found a key in SAVEINPROG state. Waiting. (Key was in the cache)");
lookupWaitBusyKey(db,key);
if (server.vm_enabled) {
if (val->storage == REDIS_VM_MEMORY ||
val->storage == REDIS_VM_SWAPPING)
{
/* If we were swapping the object out, cancel the operation */
if (val->storage == REDIS_VM_SWAPPING)
vmCancelThreadedIOJob(val);
} else {
int notify = (val->storage == REDIS_VM_LOADING);
/* Our value was swapped on disk. Bring it at home. */
redisAssert(val->type == REDIS_VMPOINTER);
val = vmLoadObject(val);
dictGetEntryVal(de) = val;
/* Clients blocked by the VM subsystem may be waiting for
* this key... */
if (notify) handleClientsBlockedOnSwappedKey(db,key);
}
}
server.stat_keyspace_hits++;
return val;
} else {
time_t expire;
robj *val;
/* Key not found in the in memory hash table, but if disk store is
* enabled we may have this key on disk. If so load it in memory
* in a blocking way. */
if (server.ds_enabled && cacheKeyMayExist(db,key)) {
long flags = cacheScheduleIOGetFlags(db,key);
/* They key is not in cache, but it has a SAVE op in queue?
* The only possibility is that the key was deleted, since
* dirty keys are not evicted. */
if (flags & REDIS_IO_SAVE) {
server.stat_keyspace_misses++;
return NULL;
}
/* At this point we need to blocking load the key in memory.
* The first thing we do is waiting here if the key is busy. */
if (flags & REDIS_IO_SAVEINPROG) {
redisLog(REDIS_DEBUG,"Lookup found a key in SAVEINPROG state. Waiting (while force loading).");
lookupWaitBusyKey(db,key);
}
redisLog(REDIS_DEBUG,"Force loading key %s via lookup", key->ptr);
val = dsGet(db,key,&expire);
if (val) {
dbAdd(db,key,val);
if (expire != -1) setExpire(db,key,expire);
server.stat_keyspace_hits++;
return val;
} else {
cacheSetKeyDoesNotExist(db,key);
}
}
server.stat_keyspace_misses++;
return NULL;
}
@@ -121,47 +67,34 @@ robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
return o;
}
/* Add the key to the DB. It's up to the caller to increment the reference
* counte of the value if needed.
*
* The program is aborted if the key already exists. */
void dbAdd(redisDb *db, robj *key, robj *val) {
sds copy = sdsdup(key->ptr);
int retval = dictAdd(db->dict, copy, val);
redisAssert(retval == REDIS_OK);
if (server.ds_enabled) cacheSetKeyMayExist(db,key);
if (server.cluster_enabled) SlotToKeyAdd(key);
}
/* Overwrite an existing key with a new value. Incrementing the reference
* count of the new value is up to the caller.
* This function does not modify the expire time of the existing key.
*
* The program is aborted if the key was not already present. */
void dbOverwrite(redisDb *db, robj *key, robj *val) {
struct dictEntry *de = dictFind(db->dict,key->ptr);
redisAssert(de != NULL);
dictReplace(db->dict, key->ptr, val);
if (server.ds_enabled) cacheSetKeyMayExist(db,key);
/* Add the key to the DB. If the key already exists REDIS_ERR is returned,
* otherwise REDIS_OK is returned, and the caller should increment the
* refcount of 'val'. */
int dbAdd(redisDb *db, robj *key, robj *val) {
/* Perform a lookup before adding the key, as we need to copy the
* key value. */
if (dictFind(db->dict, key->ptr) != NULL) {
return REDIS_ERR;
} else {
sds copy = sdsdup(key->ptr);
dictAdd(db->dict, copy, val);
return REDIS_OK;
}
}
/* High level Set operation. This function can be used in order to set
* a key, whatever it was existing or not, to a new object.
/* If the key does not exist, this is just like dbAdd(). Otherwise
* the value associated to the key is replaced with the new one.
*
* 1) The ref count of the value object is incremented.
* 2) clients WATCHing for the destination key notified.
* 3) The expire time of the key is reset (the key is made persistent). */
void setKey(redisDb *db, robj *key, robj *val) {
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
* On update (key already existed) 0 is returned. Otherwise 1. */
int dbReplace(redisDb *db, robj *key, robj *val) {
if (dictFind(db->dict,key->ptr) == NULL) {
sds copy = sdsdup(key->ptr);
dictAdd(db->dict, copy, val);
return 1;
} else {
dbOverwrite(db,key,val);
dictReplace(db->dict, key->ptr, val);
return 0;
}
incrRefCount(val);
removeExpire(db,key);
touchWatchedKey(db,key);
}
int dbExists(redisDb *db, robj *key) {
@@ -196,27 +129,18 @@ robj *dbRandomKey(redisDb *db) {
/* Delete a key, value, and associated expiration entry if any, from the DB */
int dbDelete(redisDb *db, robj *key) {
/* If diskstore is enabled make sure to awake waiting clients for this key
* as it is not really useful to wait for a key already deleted to be
* loaded from disk. */
if (server.ds_enabled) {
handleClientsBlockedOnSwappedKey(db,key);
cacheSetKeyDoesNotExist(db,key);
}
/* If VM is enabled make sure to awake waiting clients for this key:
* deleting the key will kill the I/O thread bringing the key from swap
* to memory, so the client will never be notified and unblocked if we
* don't do it now. */
if (server.vm_enabled) handleClientsBlockedOnSwappedKey(db,key);
/* Deleting an entry from the expires dict will not free the sds of
* the key, because it is shared with the main dictionary. */
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
if (dictDelete(db->dict,key->ptr) == DICT_OK) {
if (server.cluster_enabled) SlotToKeyDel(key);
return 1;
} else {
return 0;
}
return dictDelete(db->dict,key->ptr) == DICT_OK;
}
/* Empty the whole database.
* If diskstore is enabled this function will just flush the in-memory cache. */
/* Empty the whole database */
long long emptyDb() {
int j;
long long removed = 0;
@@ -225,7 +149,6 @@ long long emptyDb() {
removed += dictSize(server.db[j].dict);
dictEmpty(server.db[j].dict);
dictEmpty(server.db[j].expires);
if (server.ds_enabled) dictEmpty(server.db[j].io_negcache);
}
return removed;
}
@@ -237,50 +160,27 @@ int selectDb(redisClient *c, int id) {
return REDIS_OK;
}
/*-----------------------------------------------------------------------------
* Hooks for key space changes.
*
* Every time a key in the database is modified the function
* signalModifiedKey() is called.
*
* Every time a DB is flushed the function signalFlushDb() is called.
*----------------------------------------------------------------------------*/
void signalModifiedKey(redisDb *db, robj *key) {
touchWatchedKey(db,key);
if (server.ds_enabled)
cacheScheduleIO(db,key,REDIS_IO_SAVE);
}
void signalFlushedDb(int dbid) {
touchWatchedKeysOnFlush(dbid);
}
/*-----------------------------------------------------------------------------
* Type agnostic commands operating on the key space
*----------------------------------------------------------------------------*/
void flushdbCommand(redisClient *c) {
server.dirty += dictSize(c->db->dict);
signalFlushedDb(c->db->id);
touchWatchedKeysOnFlush(c->db->id);
dictEmpty(c->db->dict);
dictEmpty(c->db->expires);
if (server.ds_enabled) dsFlushDb(c->db->id);
addReply(c,shared.ok);
}
void flushallCommand(redisClient *c) {
signalFlushedDb(-1);
touchWatchedKeysOnFlush(-1);
server.dirty += emptyDb();
addReply(c,shared.ok);
if (server.bgsavechildpid != -1) {
kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid);
}
if (server.ds_enabled)
dsFlushDb(-1);
else
rdbSave(server.dbfilename);
rdbSave(server.dbfilename);
server.dirty++;
}
@@ -288,22 +188,10 @@ void delCommand(redisClient *c) {
int deleted = 0, j;
for (j = 1; j < c->argc; j++) {
if (server.ds_enabled) {
lookupKeyRead(c->db,c->argv[j]);
/* FIXME: this can be optimized a lot, no real need to load
* a possibly huge value. */
}
if (dbDelete(c->db,c->argv[j])) {
signalModifiedKey(c->db,c->argv[j]);
touchWatchedKey(c->db,c->argv[j]);
server.dirty++;
deleted++;
} else if (server.ds_enabled) {
if (cacheKeyMayExist(c->db,c->argv[j]) &&
dsExists(c->db,c->argv[j]))
{
cacheScheduleIO(c->db,c->argv[j],REDIS_IO_SAVE);
deleted = 1;
}
}
}
addReplyLongLong(c,deleted);
@@ -321,10 +209,6 @@ void existsCommand(redisClient *c) {
void selectCommand(redisClient *c) {
int id = atoi(c->argv[1]->ptr);
if (server.cluster_enabled && id != 0) {
addReplyError(c,"SELECT is not allowed in cluster mode");
return;
}
if (selectDb(c,id) == REDIS_ERR) {
addReplyError(c,"invalid DB index");
} else {
@@ -399,6 +283,30 @@ void typeCommand(redisClient *c) {
addReplyStatus(c,type);
}
void saveCommand(redisClient *c) {
if (server.bgsavechildpid != -1) {
addReplyError(c,"Background save already in progress");
return;
}
if (rdbSave(server.dbfilename) == REDIS_OK) {
addReply(c,shared.ok);
} else {
addReply(c,shared.err);
}
}
void bgsaveCommand(redisClient *c) {
if (server.bgsavechildpid != -1) {
addReplyError(c,"Background save already in progress");
return;
}
if (rdbSaveBackground(server.dbfilename) == REDIS_OK) {
addReplyStatus(c,"Background saving started");
} else {
addReply(c,shared.err);
}
}
void shutdownCommand(redisClient *c) {
if (prepareForShutdown() == REDIS_OK)
exit(0);
@@ -418,19 +326,17 @@ void renameGenericCommand(redisClient *c, int nx) {
return;
incrRefCount(o);
if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {
if (dbAdd(c->db,c->argv[2],o) == REDIS_ERR) {
if (nx) {
decrRefCount(o);
addReply(c,shared.czero);
return;
}
dbOverwrite(c->db,c->argv[2],o);
} else {
dbAdd(c->db,c->argv[2],o);
dbReplace(c->db,c->argv[2],o);
}
dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
touchWatchedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[2]);
server.dirty++;
addReply(c,nx ? shared.cone : shared.ok);
}
@@ -448,11 +354,6 @@ void moveCommand(redisClient *c) {
redisDb *src, *dst;
int srcid;
if (server.cluster_enabled) {
addReplyError(c,"MOVE is not allowed in cluster mode");
return;
}
/* Obtain source and target DB pointers */
src = c->db;
srcid = c->db->id;
@@ -477,12 +378,11 @@ void moveCommand(redisClient *c) {
return;
}
/* Return zero if the key already exists in the target DB */
if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
/* Try to add the element to the target DB */
if (dbAdd(dst,c->argv[1],o) == REDIS_ERR) {
addReply(c,shared.czero);
return;
}
dbAdd(dst,c->argv[1],o);
incrRefCount(o);
/* OK! key moved, free the entry in the source DB */
@@ -595,13 +495,13 @@ void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) {
if (seconds <= 0) {
if (dbDelete(c->db,key)) server.dirty++;
addReply(c, shared.cone);
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
return;
} else {
time_t when = time(NULL)+seconds;
setExpire(c->db,key,when);
addReply(c,shared.cone);
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty++;
return;
}
@@ -618,7 +518,6 @@ void expireatCommand(redisClient *c) {
void ttlCommand(redisClient *c) {
time_t expire, ttl = -1;
if (server.ds_enabled) lookupKeyRead(c->db,c->argv[1]);
expire = getExpire(c->db,c->argv[1]);
if (expire != -1) {
ttl = (expire-time(NULL));
@@ -642,108 +541,3 @@ void persistCommand(redisClient *c) {
}
}
}
/* -----------------------------------------------------------------------------
* API to get key arguments from commands
* ---------------------------------------------------------------------------*/
int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {
int j, i = 0, last, *keys;
REDIS_NOTUSED(argv);
if (cmd->firstkey == 0) {
*numkeys = 0;
return NULL;
}
last = cmd->lastkey;
if (last < 0) last = argc+last;
keys = zmalloc(sizeof(int)*((last - cmd->firstkey)+1));
for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
redisAssert(j < argc);
keys[i++] = j;
}
*numkeys = i;
return keys;
}
int *getKeysFromCommand(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
if (cmd->getkeys_proc) {
return cmd->getkeys_proc(cmd,argv,argc,numkeys,flags);
} else {
return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
}
}
void getKeysFreeResult(int *result) {
zfree(result);
}
int *noPreloadGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
if (flags & REDIS_GETKEYS_PRELOAD) {
*numkeys = 0;
return NULL;
} else {
return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
}
}
int *renameGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
if (flags & REDIS_GETKEYS_PRELOAD) {
int *keys = zmalloc(sizeof(int));
*numkeys = 1;
keys[0] = 1;
return keys;
} else {
return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
}
}
int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
int i, num, *keys;
REDIS_NOTUSED(cmd);
REDIS_NOTUSED(flags);
num = atoi(argv[2]->ptr);
/* Sanity check. Don't return any key if the command is going to
* reply with syntax error. */
if (num > (argc-3)) {
*numkeys = 0;
return NULL;
}
keys = zmalloc(sizeof(int)*num);
for (i = 0; i < num; i++) keys[i] = 3+i;
*numkeys = num;
return keys;
}
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
* a fast way a key that belongs to a specified hash slot. This is useful
* while rehashing the cluster. */
void SlotToKeyAdd(robj *key) {
unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));
zslInsert(server.cluster.slots_to_keys,hashslot,key);
incrRefCount(key);
}
void SlotToKeyDel(robj *key) {
unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));
zslDelete(server.cluster.slots_to_keys,hashslot,key);
}
unsigned int GetKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) {
zskiplistNode *n;
zrangespec range;
int j = 0;
range.min = range.max = hashslot;
range.minex = range.maxex = 0;
n = zslFirstInRange(server.cluster.slots_to_keys, range);
while(n && n->score == hashslot && count--) {
keys[j++] = n->obj;
n = n->level[0].forward;
}
return j;
}
+56 -35
View File
@@ -80,7 +80,7 @@ void computeDatasetDigest(unsigned char *final) {
redisDb *db = server.db+j;
if (dictSize(db->dict) == 0) continue;
di = dictGetIterator(db->dict);
di = dictGetSafeIterator(db->dict);
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
@@ -100,7 +100,12 @@ void computeDatasetDigest(unsigned char *final) {
mixDigest(digest,key,sdslen(key));
/* Make sure the key is loaded if VM is active */
o = dictGetEntryVal(de);
o = lookupKeyRead(db,keyobj);
if (o == NULL) {
/* Key expired on lookup? Try the next one. */
decrRefCount(keyobj);
continue;
}
aux = htonl(o->type);
mixDigest(digest,&aux,sizeof(aux));
@@ -212,26 +217,7 @@ void computeDatasetDigest(unsigned char *final) {
void debugCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"segfault")) {
*((char*)-1) = 'x';
} else if (!strcasecmp(c->argv[1]->ptr,"flushcache")) {
if (!server.ds_enabled) {
addReplyError(c, "DEBUG FLUSHCACHE called with diskstore off.");
return;
} else if (server.bgsavethread != (pthread_t) -1) {
addReplyError(c, "Can't flush cache while BGSAVE is in progress.");
return;
} else {
/* To flush the whole cache we need to wait for everything to
* be flushed on disk... */
cacheForcePointInTime();
emptyDb();
addReply(c,shared.ok);
return;
}
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
if (server.ds_enabled) {
addReply(c,shared.ok);
return;
}
if (rdbSave(server.dbfilename) != REDIS_OK) {
addReply(c,shared.err);
return;
@@ -252,25 +238,62 @@ void debugCommand(redisClient *c) {
redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF");
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) {
dictEntry *de;
dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
robj *val;
char *strenc;
if (server.ds_enabled) lookupKeyRead(c->db,c->argv[2]);
if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
if (!de) {
addReply(c,shared.nokeyerr);
return;
}
val = dictGetEntryVal(de);
strenc = strEncoding(val->encoding);
if (!server.vm_enabled || (val->storage == REDIS_VM_MEMORY ||
val->storage == REDIS_VM_SWAPPING)) {
char *strenc;
addReplyStatusFormat(c,
"Value at:%p refcount:%d "
"encoding:%s serializedlength:%lld "
"lru:%d lru_seconds_idle:%lu",
(void*)val, val->refcount,
strenc, (long long) rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val));
strenc = strEncoding(val->encoding);
addReplyStatusFormat(c,
"Value at:%p refcount:%d "
"encoding:%s serializedlength:%lld "
"lru:%d lru_seconds_idle:%lu",
(void*)val, val->refcount,
strenc, (long long) rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val));
} else {
vmpointer *vp = (vmpointer*) val;
addReplyStatusFormat(c,
"Value swapped at: page %llu "
"using %llu pages",
(unsigned long long) vp->page,
(unsigned long long) vp->usedpages);
}
} else if (!strcasecmp(c->argv[1]->ptr,"swapin") && c->argc == 3) {
lookupKeyRead(c->db,c->argv[2]);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"swapout") && c->argc == 3) {
dictEntry *de = dictFind(c->db->dict,c->argv[2]->ptr);
robj *val;
vmpointer *vp;
if (!server.vm_enabled) {
addReplyError(c,"Virtual Memory is disabled");
return;
}
if (!de) {
addReply(c,shared.nokeyerr);
return;
}
val = dictGetEntryVal(de);
/* Swap it */
if (val->storage != REDIS_VM_MEMORY) {
addReplyError(c,"This key is not in memory");
} else if (val->refcount != 1) {
addReplyError(c,"Object is shared");
} else if ((vp = vmSwapObjectBlocking(val)) != NULL) {
dictGetEntryVal(de) = vp;
addReply(c,shared.ok);
} else {
addReply(c,shared.err);
}
} else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) {
long keys, j;
robj *key, *val;
@@ -317,12 +340,10 @@ void _redisAssert(char *estr, char *file, int line) {
}
void _redisPanic(char *msg, char *file, int line) {
redisLog(REDIS_WARNING,"------------------------------------------------");
redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
#ifdef HAVE_BACKTRACE
redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
redisLog(REDIS_WARNING,"------------------------------------------------");
*((char*)-1) = 'x';
#endif
}
-1
View File
@@ -203,7 +203,6 @@ int dictRehash(dict *d, int n) {
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned)d->rehashidx);
while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
-509
View File
@@ -1,509 +0,0 @@
/* diskstore.c implements a very simple disk backed key-value store used
* by Redis for the "disk" backend. This implementation uses the filesystem
* to store key/value pairs. Every file represents a given key.
*
* The key path is calculated using the SHA1 of the key name. For instance
* the key "foo" is stored as a file name called:
*
* /0b/ee/0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
*
* The couples of characters from the hex output of SHA1 are also used
* to locate two two levels of directories to store the file (as most
* filesystems are not able to handle too many files in a single dir).
*
* In the end there are 65536 final directories (256 directories inside
* every 256 top level directories), so that with 1 billion of files every
* directory will contain in the average 15258 entires, that is ok with
* most filesystems implementation.
*
* Note that since Redis supports multiple databases, the actual key name
* is:
*
* /0b/ee/<dbid>_0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
*
* so for instance if the key is inside DB 0:
*
* /0b/ee/0_0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
*
* The actaul implementation of this disk store is highly dependant to the
* filesystem implementation itself. This implementation may be replaced by
* a B+TREE implementation in future implementations.
*
* Data ok every key is serialized using the same format used for .rdb
* serialization. Everything is serialized on every entry: key name,
* ttl information in case of keys with an associated expire time, and the
* serialized value itself.
*
* Because the format is the same of the .rdb files it is trivial to create
* an .rdb file starting from this format just by mean of scanning the
* directories and concatenating entries, with the sole addition of an
* .rdb header at the start and the end-of-db opcode at the end.
*
* -------------------------------------------------------------------------
*
* Copyright (c) 2010-2011, 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"
#include "sha1.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
int create256dir(char *prefix) {
char buf[1024];
int j;
for (j = 0; j < 256; j++) {
snprintf(buf,sizeof(buf),"%s%02x",prefix,j);
if (mkdir(buf,0755) == -1) {
redisLog(REDIS_WARNING,"Error creating dir %s for diskstore: %s",
buf,strerror(errno));
return REDIS_ERR;
}
}
return REDIS_OK;
}
int dsOpen(void) {
struct stat sb;
int retval, j;
char *path = server.ds_path;
char buf[1024];
if ((retval = stat(path,&sb) == -1) && errno != ENOENT) {
redisLog(REDIS_WARNING, "Error opening disk store at %s: %s",
path, strerror(errno));
return REDIS_ERR;
}
/* Directory already in place. Assume everything is ok. */
if (retval == 0 && S_ISDIR(sb.st_mode)) {
redisLog(REDIS_NOTICE,"Disk store %s exists", path);
return REDIS_OK;
}
/* File exists but it's not a directory */
if (retval == 0 && !S_ISDIR(sb.st_mode)) {
redisLog(REDIS_WARNING,"Disk store at %s is not a directory", path);
return REDIS_ERR;
}
/* New disk store, create the directory structure now, as creating
* them in a lazy way is not a good idea, after very few insertions
* we'll need most of the 65536 directories anyway. */
redisLog(REDIS_NOTICE,"Disk store %s does not exist: creating", path);
if (mkdir(path,0755) == -1) {
redisLog(REDIS_WARNING,"Disk store init failed creating dir %s: %s",
path, strerror(errno));
return REDIS_ERR;
}
/* Create the top level 256 directories */
snprintf(buf,sizeof(buf),"%s/",path);
if (create256dir(buf) == REDIS_ERR) return REDIS_ERR;
/* For every 256 top level dir, create 256 nested dirs */
for (j = 0; j < 256; j++) {
snprintf(buf,sizeof(buf),"%s/%02x/",path,j);
if (create256dir(buf) == REDIS_ERR) return REDIS_ERR;
}
return REDIS_OK;
}
int dsClose(void) {
return REDIS_OK;
}
/* Convert key into full path for this object. Dirty but hopefully
* is fast enough. Returns the length of the returned path. */
int dsKeyToPath(redisDb *db, char *buf, robj *key) {
SHA1_CTX ctx;
unsigned char hash[20];
char hex[40], digits[] = "0123456789abcdef";
int j, l;
char *origbuf = buf;
SHA1Init(&ctx);
SHA1Update(&ctx,key->ptr,sdslen(key->ptr));
SHA1Final(hash,&ctx);
/* Convert the hash into hex format */
for (j = 0; j < 20; j++) {
hex[j*2] = digits[(hash[j]&0xF0)>>4];
hex[(j*2)+1] = digits[hash[j]&0x0F];
}
/* Create the object path. Start with server.ds_path that's the root dir */
l = sdslen(server.ds_path);
memcpy(buf,server.ds_path,l);
buf += l;
*buf++ = '/';
/* Then add xx/yy/ that is the two level directories */
buf[0] = hex[0];
buf[1] = hex[1];
buf[2] = '/';
buf[3] = hex[2];
buf[4] = hex[3];
buf[5] = '/';
buf += 6;
/* Add the database number followed by _ and finall the SHA1 hex */
l = ll2string(buf,64,db->id);
buf += l;
buf[0] = '_';
memcpy(buf+1,hex,40);
buf[41] = '\0';
return (buf-origbuf)+41;
}
int dsSet(redisDb *db, robj *key, robj *val, time_t expire) {
char buf[1024], buf2[1024];
FILE *fp;
int retval, len;
len = dsKeyToPath(db,buf,key);
memcpy(buf2,buf,len);
snprintf(buf2+len,sizeof(buf2)-len,"-%ld-%ld",(long)time(NULL),(long)val);
while ((fp = fopen(buf2,"w")) == NULL) {
if (errno == ENOSPC) {
redisLog(REDIS_WARNING,"Diskstore: No space left on device. Please make room and wait 30 seconds for Redis to continue.");
sleep(30);
} else {
redisLog(REDIS_WARNING,"diskstore error opening %s: %s",
buf2, strerror(errno));
redisPanic("Unrecoverable diskstore error. Exiting.");
}
}
if ((retval = rdbSaveKeyValuePair(fp,key,val,expire,time(NULL))) == -1)
return REDIS_ERR;
fclose(fp);
if (retval == 0) {
/* Expired key. Unlink failing not critical */
unlink(buf);
unlink(buf2);
} else {
/* Use rename for atomic updadte of value */
if (rename(buf2,buf) == -1) {
redisLog(REDIS_WARNING,"rename(2) returned an error: %s",
strerror(errno));
redisPanic("Unrecoverable diskstore error. Exiting.");
}
}
return REDIS_OK;
}
robj *dsGet(redisDb *db, robj *key, time_t *expire) {
char buf[1024];
int type;
time_t expiretime = -1; /* -1 means: no expire */
robj *dskey; /* Key as loaded from disk. */
robj *val;
FILE *fp;
dsKeyToPath(db,buf,key);
fp = fopen(buf,"r");
if (fp == NULL && errno == ENOENT) return NULL; /* No such key */
if (fp == NULL) {
redisLog(REDIS_WARNING,"Disk store failed opening %s: %s",
buf, strerror(errno));
goto readerr;
}
if ((type = rdbLoadType(fp)) == -1) goto readerr;
if (type == REDIS_EXPIRETIME) {
if ((expiretime = rdbLoadTime(fp)) == -1) goto readerr;
/* We read the time so we need to read the object type again */
if ((type = rdbLoadType(fp)) == -1) goto readerr;
}
/* Read key */
if ((dskey = rdbLoadStringObject(fp)) == NULL) goto readerr;
/* Read value */
if ((val = rdbLoadObject(type,fp)) == NULL) goto readerr;
fclose(fp);
/* The key we asked, and the key returned, must be the same */
redisAssert(equalStringObjects(key,dskey));
/* Check if the key already expired */
decrRefCount(dskey);
if (expiretime != -1 && expiretime < time(NULL)) {
decrRefCount(val);
unlink(buf); /* This failing is non critical here */
return NULL;
}
/* Everything ok... */
*expire = expiretime;
return val;
readerr:
redisLog(REDIS_WARNING,"Read error reading reading %s. Corrupted key?",
buf);
redisPanic("Unrecoverable error reading from disk store");
return NULL; /* unreached */
}
int dsDel(redisDb *db, robj *key) {
char buf[1024];
dsKeyToPath(db,buf,key);
if (unlink(buf) == -1) {
if (errno == ENOENT) {
return REDIS_ERR;
} else {
redisLog(REDIS_WARNING,"Disk store can't remove %s: %s",
buf, strerror(errno));
redisPanic("Unrecoverable Disk store errore. Existing.");
return REDIS_ERR; /* unreached */
}
} else {
return REDIS_OK;
}
}
int dsExists(redisDb *db, robj *key) {
char buf[1024];
dsKeyToPath(db,buf,key);
return access(buf,R_OK) == 0;
}
int dsGetDbidFromFilename(char *path) {
char id[64];
char *p = strchr(path,'_');
int len = (p - path);
redisAssert(p != NULL && len < 64);
memcpy(id,path,len);
id[len] = '\0';
return atoi(id);
}
void dsFlushOneDir(char *path, int dbid) {
DIR *dir;
struct dirent *dp, de;
dir = opendir(path);
if (dir == NULL) {
redisLog(REDIS_WARNING,"Disk store can't open dir %s: %s",
path, strerror(errno));
redisPanic("Unrecoverable Disk store errore. Existing.");
}
while(1) {
char buf[1024];
readdir_r(dir,&de,&dp);
if (dp == NULL) break;
if (dp->d_name[0] == '.') continue;
/* Check if we need to remove this entry accordingly to the
* DB number. */
if (dbid != -1 && dsGetDbidFromFilename(dp->d_name)) continue;
/* Finally unlink the file */
snprintf(buf,1024,"%s/%s",path,dp->d_name);
if (unlink(buf) == -1) {
redisLog(REDIS_WARNING,
"Can't unlink %s: %s", buf, strerror(errno));
redisPanic("Unrecoverable Disk store errore. Existing.");
}
}
closedir(dir);
}
void dsFlushDb(int dbid) {
char buf[1024];
int j, i;
redisLog(REDIS_NOTICE,"Flushing diskstore DB (%d)",dbid);
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
snprintf(buf,1024,"%s/%02x/%02x",server.ds_path,j,i);
dsFlushOneDir(buf,dbid);
}
}
}
void dsRdbSaveSetState(int state) {
pthread_mutex_lock(&server.bgsavethread_mutex);
server.bgsavethread_state = state;
pthread_mutex_unlock(&server.bgsavethread_mutex);
}
void *dsRdbSave_thread(void *arg) {
char tmpfile[256], *filename = (char*)arg;
struct dirent *dp, de;
int j, i, last_dbid = -1;
FILE *fp;
/* Change state to ACTIVE, to signal there is a saving thead working. */
redisLog(REDIS_NOTICE,"Diskstore BGSAVE thread started");
dsRdbSaveSetState(REDIS_BGSAVE_THREAD_ACTIVE);
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
if (!fp) {
redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
strerror(errno));
dsRdbSaveSetState(REDIS_BGSAVE_THREAD_DONE_ERR);
return NULL;
}
if (fwrite("REDIS0001",9,1,fp) == 0) goto werr;
sleep(5);
/* Scan all diskstore dirs looking for keys */
for (j = 0; j < 256; j++) {
for (i = 0; i < 256; i++) {
DIR *dir;
char buf[1024];
/* For every directory, collect all the keys */
snprintf(buf,sizeof(buf),"%s/%02x/%02x",server.ds_path,j,i);
if ((dir = opendir(buf)) == NULL) {
redisLog(REDIS_WARNING,"Disk store can't open dir %s: %s",
buf, strerror(errno));
goto werr;
}
while(1) {
char buf[1024];
int dbid;
FILE *entryfp;
readdir_r(dir,&de,&dp);
if (dp == NULL) break;
if (dp->d_name[0] == '.') continue;
/* If there is a '-' char in the file name, it's a temp file */
if (strchr(dp->d_name,'-') != NULL) continue;
/* Emit the SELECT DB opcode if needed. */
dbid = dsGetDbidFromFilename(dp->d_name);
if (dbid != last_dbid) {
last_dbid = dbid;
if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr;
if (rdbSaveLen(fp,dbid) == -1) goto werr;
}
/* Let's copy this file into the target .rdb */
snprintf(buf,sizeof(buf),"%s/%02x/%02x/%s",
server.ds_path,j,i,dp->d_name);
if ((entryfp = fopen(buf,"r")) == NULL) {
redisLog(REDIS_WARNING,"Can't open %s: %s",
buf,strerror(errno));
closedir(dir);
goto werr;
}
while(1) {
int nread = fread(buf,1,sizeof(buf),entryfp);
if (nread == 0) {
if (ferror(entryfp)) {
redisLog(REDIS_WARNING,"Error reading from file entry while performing BGSAVE for diskstore: %s", strerror(errno));
closedir(dir);
goto werr;
} else {
break;
}
}
if (fwrite(buf,1,nread,fp) != (unsigned)nread) {
closedir(dir);
goto werr;
}
}
fclose(entryfp);
}
closedir(dir);
}
}
/* Output the end of file opcode */
if (rdbSaveType(fp,REDIS_EOF) == -1) goto werr;
/* Make sure data will not remain on the OS's output buffers */
fflush(fp);
fsync(fileno(fp));
fclose(fp);
zfree(filename);
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
if (rename(tmpfile,filename) == -1) {
redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s (diskstore)", strerror(errno));
unlink(tmpfile);
dsRdbSaveSetState(REDIS_BGSAVE_THREAD_DONE_ERR);
return NULL;
}
redisLog(REDIS_NOTICE,"DB saved on disk by diskstore thread");
dsRdbSaveSetState(REDIS_BGSAVE_THREAD_DONE_OK);
return NULL;
werr:
zfree(filename);
fclose(fp);
unlink(tmpfile);
dsRdbSaveSetState(REDIS_BGSAVE_THREAD_DONE_ERR);
redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
return NULL;
}
int dsRdbSaveBackground(char *filename) {
pthread_t thread;
if (pthread_create(&thread,NULL,dsRdbSave_thread,zstrdup(filename)) != 0) {
redisLog(REDIS_WARNING,"Can't create diskstore BGSAVE thread: %s",
strerror(errno));
return REDIS_ERR;
} else {
server.bgsavethread = thread;
return REDIS_OK;
}
}
int dsRdbSave(char *filename) {
/* A blocking save is actually a non blocking save... just we wait
* for it to terminate in a non-busy loop. */
redisLog(REDIS_NOTICE,"Starting a blocking SAVE (BGSAVE + blocking wait)");
server.dirty_before_bgsave = server.dirty;
if (dsRdbSaveBackground(filename) == REDIS_ERR) return REDIS_ERR;
while(1) {
usleep(1000);
int state;
pthread_mutex_lock(&server.bgsavethread_mutex);
state = server.bgsavethread_state;
pthread_mutex_unlock(&server.bgsavethread_mutex);
if (state == REDIS_BGSAVE_THREAD_DONE_OK ||
state == REDIS_BGSAVE_THREAD_DONE_ERR) break;
}
return REDIS_OK;
}
-1028
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -186,6 +186,7 @@ void _addReplyStringToList(redisClient *c, char *s, size_t len) {
void addReply(redisClient *c, robj *obj) {
if (_installWriteEvent(c) != REDIS_OK) return;
redisAssert(!server.vm_enabled || obj->storage == REDIS_VM_MEMORY);
/* This is an important place where we can avoid copy-on-write
* when there is a saving child running, avoiding touching the
@@ -490,7 +491,7 @@ void freeClient(redisClient *c) {
/* Remove from the list of clients waiting for swapped keys, or ready
* to be restarted, but not yet woken up again. */
if (c->flags & REDIS_IO_WAIT) {
redisAssert(server.ds_enabled);
redisAssert(server.vm_enabled);
if (listLength(c->io_keys) == 0) {
ln = listSearchKey(server.io_ready_clients,c);
@@ -504,7 +505,7 @@ void freeClient(redisClient *c) {
dontWaitForSwappedKey(c,ln->value);
}
}
server.cache_blocked_clients--;
server.vm_blocked_clients--;
}
listRelease(c->io_keys);
/* Master/slave cleanup.
@@ -522,7 +523,6 @@ void freeClient(redisClient *c) {
if (c->flags & REDIS_MASTER) {
server.master = NULL;
server.replstate = REDIS_REPL_CONNECT;
server.repl_down_since = time(NULL);
/* Since we lost the connection with the master, we should also
* close the connection with all our slaves if we have any, so
* when we'll resync with the master the other slaves will sync again
+32 -8
View File
@@ -21,6 +21,7 @@ robj *createObject(int type, void *ptr) {
/* The following is only needed if VM is active, but since the conditional
* is probably more costly than initializing the field it's better to
* have every field properly initialized anyway. */
o->storage = REDIS_VM_MEMORY;
return o;
}
@@ -31,7 +32,6 @@ robj *createStringObject(char *ptr, size_t len) {
robj *createStringObjectFromLongLong(long long value) {
robj *o;
if (value >= 0 && value < REDIS_SHARED_INTEGERS &&
!server.ds_enabled &&
pthread_equal(pthread_self(),server.mainthread)) {
incrRefCount(shared.integers[value]);
o = shared.integers[value];
@@ -179,7 +179,30 @@ void incrRefCount(robj *o) {
void decrRefCount(void *obj) {
robj *o = obj;
/* Object is a swapped out value, or in the process of being loaded. */
if (server.vm_enabled &&
(o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING))
{
vmpointer *vp = obj;
if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o);
vmMarkPagesFree(vp->page,vp->usedpages);
server.vm_stats_swapped_objects--;
zfree(vp);
return;
}
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
/* Object is in memory, or in the process of being swapped out.
*
* If the object is being swapped out, abort the operation on
* decrRefCount even if the refcount does not drop to 0: the object
* is referenced at least two times, as value of the key AND as
* job->val in the iojob. So if we don't invalidate the iojob, when it is
* done but the relevant key was removed in the meantime, the
* complete jobs handler will not find the key about the job and the
* assert will fail. */
if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING)
vmCancelThreadedIOJob(o);
if (o->refcount == 1) {
switch(o->type) {
case REDIS_STRING: freeStringObject(o); break;
@@ -235,16 +258,16 @@ robj *tryObjectEncoding(robj *o) {
/* Ok, this object can be encoded...
*
* Can I use a shared object? Only if the object is inside a given
* range and if the back end in use is in-memory. For disk store every
* object in memory used as value should be independent.
* range and if this is the main thread, since when VM is enabled we
* have the constraint that I/O thread should only handle non-shared
* objects, in order to avoid race conditions (we don't have per-object
* locking).
*
* Note that we also avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* because very object needs to have a private LRU field for the LRU
* algorithm to work well. */
if (!server.ds_enabled &&
server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
pthread_equal(pthread_self(),server.mainthread))
{
if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&
pthread_equal(pthread_self(),server.mainthread)) {
decrRefCount(o);
incrRefCount(shared.integers[value]);
return shared.integers[value];
@@ -453,6 +476,7 @@ unsigned long estimateObjectIdleTime(robj *o) {
robj *objectCommandLookup(redisClient *c, robj *key) {
dictEntry *de;
if (server.vm_enabled) lookupKeyRead(c->db,key);
if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
return (robj*) dictGetEntryVal(de);
}
+117 -86
View File
@@ -245,7 +245,7 @@ int rdbSaveDoubleValue(FILE *fp, double val) {
return rdbWriteRaw(fp,buf,len);
}
/* Save a Redis object. Returns -1 on error, 0 on success. */
/* Save a Redis object. */
int rdbSaveObject(FILE *fp, robj *o) {
int n, nwritten = 0;
@@ -370,38 +370,24 @@ off_t rdbSavedObjectLen(robj *o) {
return len;
}
/* Save a key-value pair, with expire time, type, key, value.
* On error -1 is returned.
* On success if the key was actaully saved 1 is returned, otherwise 0
* is returned (the key was already expired). */
int rdbSaveKeyValuePair(FILE *fp, robj *key, robj *val,
time_t expiretime, time_t now)
{
int vtype;
/* Return the number of pages required to save this object in the swap file */
off_t rdbSavedObjectPages(robj *o) {
off_t bytes = rdbSavedObjectLen(o);
return (bytes+(server.vm_page_size-1))/server.vm_page_size;
}
/* Save the expire time */
if (expiretime != -1) {
/* If this key is already expired skip it */
if (expiretime < now) return 0;
if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) return -1;
if (rdbSaveTime(fp,expiretime) == -1) return -1;
}
/* Fix the object type if needed, to support saving zipmaps, ziplists,
* and intsets, directly as blobs of bytes: they are already serialized. */
vtype = val->type;
if (vtype == REDIS_HASH && val->encoding == REDIS_ENCODING_ZIPMAP)
vtype = REDIS_HASH_ZIPMAP;
else if (vtype == REDIS_LIST && val->encoding == REDIS_ENCODING_ZIPLIST)
vtype = REDIS_LIST_ZIPLIST;
else if (vtype == REDIS_SET && val->encoding == REDIS_ENCODING_INTSET)
vtype = REDIS_SET_INTSET;
else if (vtype == REDIS_ZSET && val->encoding == REDIS_ENCODING_ZIPLIST)
vtype = REDIS_ZSET_ZIPLIST;
/* Save type, key, value */
if (rdbSaveType(fp,vtype) == -1) return -1;
if (rdbSaveStringObject(fp,key) == -1) return -1;
if (rdbSaveObject(fp,val) == -1) return -1;
return 1;
int getObjectSaveType(robj *o) {
/* Fix the type id for specially encoded data types */
if (o->type == REDIS_HASH && o->encoding == REDIS_ENCODING_ZIPMAP)
return REDIS_HASH_ZIPMAP;
else if (o->type == REDIS_LIST && o->encoding == REDIS_ENCODING_ZIPLIST)
return REDIS_LIST_ZIPLIST;
else if (o->type == REDIS_SET && o->encoding == REDIS_ENCODING_INTSET)
return REDIS_SET_INTSET;
else if (o->type == REDIS_ZSET && o->encoding == REDIS_ENCODING_ZIPLIST)
return REDIS_ZSET_ZIPLIST;
else
return o->type;
}
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
@@ -413,16 +399,16 @@ int rdbSave(char *filename) {
int j;
time_t now = time(NULL);
if (server.ds_enabled) {
cacheForcePointInTime();
return dsRdbSave(filename);
}
/* Wait for I/O therads to terminate, just in case this is a
* foreground-saving, to avoid seeking the swap file descriptor at the
* same time. */
if (server.vm_enabled)
waitEmptyIOJobsQueue();
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
if (!fp) {
redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
strerror(errno));
redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));
return REDIS_ERR;
}
if (fwrite("REDIS0002",9,1,fp) == 0) goto werr;
@@ -430,7 +416,7 @@ int rdbSave(char *filename) {
redisDb *db = server.db+j;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
di = dictGetIterator(d);
if (!di) {
fclose(fp);
return REDIS_ERR;
@@ -444,11 +430,41 @@ int rdbSave(char *filename) {
while((de = dictNext(di)) != NULL) {
sds keystr = dictGetEntryKey(de);
robj key, *o = dictGetEntryVal(de);
time_t expire;
time_t expiretime;
initStaticStringObject(key,keystr);
expire = getExpire(db,&key);
if (rdbSaveKeyValuePair(fp,&key,o,expire,now) == -1) goto werr;
expiretime = getExpire(db,&key);
/* Save the expire time */
if (expiretime != -1) {
/* If this key is already expired skip it */
if (expiretime < now) continue;
if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr;
if (rdbSaveTime(fp,expiretime) == -1) goto werr;
}
/* Save the key and associated value. This requires special
* handling if the value is swapped out. */
if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||
o->storage == REDIS_VM_SWAPPING) {
int otype = getObjectSaveType(o);
/* Save type, key, value */
if (rdbSaveType(fp,otype) == -1) goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,o) == -1) goto werr;
} else {
/* REDIS_VM_SWAPPED or REDIS_VM_LOADING */
robj *po;
/* Get a preview of the object in memory */
po = vmPreviewObject(o);
/* Save type, key, value */
if (rdbSaveType(fp,getObjectSaveType(po)) == -1)
goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,po) == -1) goto werr;
/* Remove the loaded object from memory */
decrRefCount(po);
}
}
dictReleaseIterator(di);
}
@@ -484,25 +500,20 @@ int rdbSaveBackground(char *filename) {
pid_t childpid;
long long start;
if (server.bgsavechildpid != -1 ||
server.bgsavethread != (pthread_t) -1) return REDIS_ERR;
if (server.bgsavechildpid != -1) return REDIS_ERR;
if (server.vm_enabled) waitEmptyIOJobsQueue();
server.dirty_before_bgsave = server.dirty;
if (server.ds_enabled) {
cacheForcePointInTime();
return dsRdbSaveBackground(filename);
}
start = ustime();
if ((childpid = fork()) == 0) {
int retval;
/* Child */
if (server.vm_enabled) vmReopenSwapFile();
if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd);
retval = rdbSave(filename);
_exit((retval == REDIS_OK) ? 0 : 1);
if (rdbSave(filename) == REDIS_OK) {
_exit(0);
} else {
_exit(1);
}
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
@@ -918,7 +929,8 @@ void stopLoading(void) {
int rdbLoad(char *filename) {
FILE *fp;
uint32_t dbid;
int type, rdbver;
int type, retval, rdbver;
int swap_all_values = 0;
redisDb *db = server.db+0;
char buf[1024];
time_t expiretime, now = time(NULL);
@@ -943,6 +955,8 @@ int rdbLoad(char *filename) {
startLoading(fp);
while(1) {
robj *key, *val;
int force_swapout;
expiretime = -1;
/* Serve the clients from time to time */
@@ -981,12 +995,52 @@ int rdbLoad(char *filename) {
continue;
}
/* Add the new object in the hash table */
dbAdd(db,key,val);
retval = dbAdd(db,key,val);
if (retval == REDIS_ERR) {
redisLog(REDIS_WARNING,"Loading DB, duplicated key (%s) found! Unrecoverable error, exiting now.", key->ptr);
exit(1);
}
/* Set the expire time if needed */
if (expiretime != -1) setExpire(db,key,expiretime);
/* Handle swapping while loading big datasets when VM is on */
/* If we detecter we are hopeless about fitting something in memory
* we just swap every new key on disk. Directly...
* Note that's important to check for this condition before resorting
* to random sampling, otherwise we may try to swap already
* swapped keys. */
if (swap_all_values) {
dictEntry *de = dictFind(db->dict,key->ptr);
/* de may be NULL since the key already expired */
if (de) {
vmpointer *vp;
val = dictGetEntryVal(de);
if (val->refcount == 1 &&
(vp = vmSwapObjectBlocking(val)) != NULL)
dictGetEntryVal(de) = vp;
}
decrRefCount(key);
continue;
}
decrRefCount(key);
/* Flush data on disk once 32 MB of additional RAM are used... */
force_swapout = 0;
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
force_swapout = 1;
/* If we have still some hope of having some value fitting memory
* then we try random sampling. */
if (!swap_all_values && server.vm_enabled && force_swapout) {
while (zmalloc_used_memory() > server.vm_max_memory) {
if (vmSwapOneObjectBlocking() == REDIS_ERR) break;
}
if (zmalloc_used_memory() > server.vm_max_memory)
swap_all_values = 1; /* We are already using too much mem */
}
}
fclose(fp);
stopLoading();
@@ -999,7 +1053,10 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
}
/* A background saving child (BGSAVE) terminated its work. Handle this. */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {
void backgroundSaveDoneHandler(int statloc) {
int exitcode = WEXITSTATUS(statloc);
int bysignal = WIFSIGNALED(statloc);
if (!bysignal && exitcode == 0) {
redisLog(REDIS_NOTICE,
"Background saving terminated with success");
@@ -1009,37 +1066,11 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
redisLog(REDIS_WARNING, "Background saving error");
} else {
redisLog(REDIS_WARNING,
"Background saving terminated by signal %d", bysignal);
"Background saving terminated by signal %d", WTERMSIG(statloc));
rdbRemoveTempFile(server.bgsavechildpid);
}
server.bgsavechildpid = -1;
server.bgsavethread = (pthread_t) -1;
server.bgsavethread_state = REDIS_BGSAVE_THREAD_UNACTIVE;
/* Possibly there are slaves waiting for a BGSAVE in order to be served
* (the first stage of SYNC is a bulk transfer of dump.rdb) */
updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR);
}
void saveCommand(redisClient *c) {
if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t)-1) {
addReplyError(c,"Background save already in progress");
return;
}
if (rdbSave(server.dbfilename) == REDIS_OK) {
addReply(c,shared.ok);
} else {
addReply(c,shared.err);
}
}
void bgsaveCommand(redisClient *c) {
if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t)-1) {
addReplyError(c,"Background save already in progress");
} else if (server.bgrewritechildpid != -1) {
addReplyError(c,"Can't BGSAVE while AOF log rewriting is in progress");
} else if (rdbSaveBackground(server.dbfilename) == REDIS_OK) {
addReplyStatus(c,"Background saving started");
} else {
addReply(c,shared.err);
}
}
-3
View File
@@ -469,9 +469,6 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
output_raw = 0;
if (!strcasecmp(command,"info") ||
(argc == 2 && !strcasecmp(command,"cluster") &&
(!strcasecmp(argv[1],"nodes") ||
!strcasecmp(argv[1],"info"))) ||
(argc == 2 && !strcasecmp(command,"client") &&
!strcasecmp(argv[1],"list")))
-258
View File
@@ -1,258 +0,0 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'redis'
ClusterHashSlots = 4096
def xputs(s)
printf s
STDOUT.flush
end
class ClusterNode
def initialize(addr)
s = addr.split(":")
if s.length != 2
puts "Invalid node name #{addr}"
exit 1
end
@r = nil
@host = s[0]
@port = s[1]
@slots = {}
@dirty = false
end
def to_s
"#{@host}:#{@port}"
end
def connect(o={})
xputs "Connecting to node #{self}: "
begin
@r = Redis.new(:host => @host, :port => @port)
@r.ping
rescue
puts "ERROR"
puts "Sorry, can't connect to node #{self}"
exit 1 if o[:abort]
@r = nil
end
puts "OK"
end
def assert_cluster
info = @r.info
if !info["cluster_enabled"] || info["cluster_enabled"].to_i == 0
puts "Error: Node #{self} is not configured as a cluster node."
exit 1
end
end
def assert_empty
if !(@r.cluster("info").split("\r\n").index("cluster_known_nodes:1")) ||
(@r.info['db0'])
puts "Error: Node #{self} is not empty. Either the node already knows other nodes (check with nodes-info) or contains some key in database 0."
exit 1
end
end
def add_slots(slots)
slots.each{|s|
@slots[s] = :new
}
@dirty = true
end
def flush_node_config
return if !@dirty
new = []
@slots.each{|s,val|
if val == :new
new << s
@slots[s] = true
end
}
@r.cluster("addslots",*new)
@dirty = false
end
def info_string
# We want to display the hash slots assigned to this node
# as ranges, like in: "1-5,8-9,20-25,30"
#
# Note: this could be easily written without side effects,
# we use 'slots' just to split the computation into steps.
# First step: we want an increasing array of integers
# for instance: [1,2,3,4,5,8,9,20,21,22,23,24,25,30]
slots = @slots.keys.sort
# As we want to aggregate adiacent slots we convert all the
# slot integers into ranges (with just one element)
# So we have something like [1..1,2..2, ... and so forth.
slots.map!{|x| x..x}
# Finally we group ranges with adiacent elements.
slots = slots.reduce([]) {|a,b|
if !a.empty? && b.first == (a[-1].last)+1
a[0..-2] + [(a[-1].first)..(b.last)]
else
a + [b]
end
}
# Now our task is easy, we just convert ranges with just one
# element into a number, and a real range into a start-end format.
# Finally we join the array using the comma as separator.
slots = slots.map{|x|
x.count == 1 ? x.first.to_s : "#{x.first}-#{x.last}"
}.join(",")
"#{self.to_s.ljust(25)} slots:#{slots}"
end
def info
{
:host => @host,
:port => @port,
:slots => @slots,
:dirty => @dirty
}
end
def is_dirty?
@dirty
end
def r
@r
end
end
class RedisTrib
def initialize
@nodes = []
end
def check_arity(req_args, num_args)
if ((req_args > 0 and num_args != req_args) ||
(req_args < 0 and num_args < req_args.abs))
puts "Wrong number of arguments for specified sub command"
exit 1
end
end
def add_node(node)
@nodes << node
end
def check_cluster
puts "Performing Cluster Check (using node #{@nodes[0]})"
show_nodes
end
def alloc_slots
slots_per_node = ClusterHashSlots/@nodes.length
i = 0
@nodes.each{|n|
first = i*slots_per_node
last = first+slots_per_node-1
last = ClusterHashSlots-1 if i == @nodes.length-1
n.add_slots first..last
i += 1
}
end
def flush_nodes_config
@nodes.each{|n|
n.flush_node_config
}
end
def show_nodes
@nodes.each{|n|
puts n.info_string
}
end
def join_cluster
# We use a brute force approach to make sure the node will meet
# each other, that is, sending CLUSTER MEET messages to all the nodes
# about the very same node.
# Thanks to gossip this information should propagate across all the
# cluster in a matter of seconds.
first = false
@nodes.each{|n|
if !first then first = n.info; next; end # Skip the first node
n.r.cluster("meet",first[:host],first[:port])
}
end
def yes_or_die(msg)
print "#{msg} (type 'yes' to accept): "
STDOUT.flush
if !(STDIN.gets.chomp.downcase == "yes")
puts "Aborting..."
exit 1
end
end
# redis-trib subcommands implementations
def check_cluster_cmd
node = ClusterNode.new(ARGV[1])
node.connect(:abort => true)
node.assert_cluster
add_node(node)
check_cluster
end
def create_cluster_cmd
puts "Creating cluster"
ARGV[1..-1].each{|n|
node = ClusterNode.new(n)
node.connect(:abort => true)
node.assert_cluster
node.assert_empty
add_node(node)
}
puts "Performing hash slots allocation on #{@nodes.length} nodes..."
alloc_slots
show_nodes
yes_or_die "Can I set the above configuration?"
flush_nodes_config
puts "** Nodes configuration updated"
puts "** Sending CLUSTER MEET messages to join the cluster"
join_cluster
check_cluster
end
end
COMMANDS={
"create" => ["create_cluster_cmd", -2, "host1:port host2:port ... hostN:port"],
"check" => ["check_cluster_cmd", 2, "host:port"]
}
# Sanity check
if ARGV.length == 0
puts "Usage: redis-trib <command> <arguments ...>"
puts
COMMANDS.each{|k,v|
puts " #{k.ljust(20)} #{v[2]}"
}
puts
exit 1
end
rt = RedisTrib.new
cmd_spec = COMMANDS[ARGV[0].downcase]
if !cmd_spec
puts "Unknown redis-trib subcommand '#{ARGV[0]}'"
exit 1
end
rt.check_arity(cmd_spec[1],ARGV.length)
# Dispatch
rt.send(cmd_spec[0])
+372 -627
View File
File diff suppressed because it is too large Load Diff
+239 -429
View File
@@ -18,7 +18,6 @@
#include <inttypes.h>
#include <pthread.h>
#include <syslog.h>
#include <netinet/in.h>
#include "ae.h" /* Event driven programming library */
#include "sds.h" /* Dynamic safe strings */
@@ -50,8 +49,6 @@
#define REDIS_SHARED_INTEGERS 10000
#define REDIS_REPLY_CHUNK_BYTES (5*1500) /* 5 TCP packets with default MTU */
#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */
#define REDIS_AUTO_AOFREWRITE_PERC 100
#define REDIS_AUTO_AOFREWRITE_MIN_SIZE (1024*1024)
/* Hash table parameters */
#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */
@@ -124,18 +121,23 @@
#define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */
#define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ */
/* Scheduled IO opeations flags. */
#define REDIS_IO_LOAD 1
#define REDIS_IO_SAVE 2
#define REDIS_IO_LOADINPROG 4
#define REDIS_IO_SAVEINPROG 8
/* Virtual memory object->where field. */
#define REDIS_VM_MEMORY 0 /* The object is on memory */
#define REDIS_VM_SWAPPED 1 /* The object is on disk */
#define REDIS_VM_SWAPPING 2 /* Redis is swapping this object on disk */
#define REDIS_VM_LOADING 3 /* Redis is loading this object from disk */
/* Generic IO flags */
#define REDIS_IO_ONLYLOADS 1
#define REDIS_IO_ASAP 2
#define REDIS_MAX_COMPLETED_JOBS_PROCESSED 1
/* Virtual memory static configuration stuff.
* Check vmFindContiguousPages() to know more about this magic numbers. */
#define REDIS_VM_MAX_NEAR_PAGES 65536
#define REDIS_VM_MAX_RANDOM_JUMP 4096
#define REDIS_VM_MAX_THREADS 32
#define REDIS_THREAD_STACK_SIZE (1024*1024*4)
/* The following is the *percentage* of completed I/O jobs to process when the
* handelr is called. While Virtual Memory I/O operations are performed by
* threads, this operations must be processed by the main thread when completed
* in order to take effect. */
#define REDIS_MAX_COMPLETED_JOBS_PROCESSED 1
/* Client flags */
#define REDIS_SLAVE 1 /* This client is a slave server */
@@ -187,7 +189,6 @@
#define REDIS_VERBOSE 1
#define REDIS_NOTICE 2
#define REDIS_WARNING 3
#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */
/* Anti-warning macro... */
#define REDIS_NOTUSED(V) ((void) V)
@@ -222,12 +223,6 @@
#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4
#define REDIS_MAXMEMORY_NO_EVICTION 5
/* Diskstore background saving thread states */
#define REDIS_BGSAVE_THREAD_UNACTIVE 0
#define REDIS_BGSAVE_THREAD_ACTIVE 1
#define REDIS_BGSAVE_THREAD_DONE_OK 2
#define REDIS_BGSAVE_THREAD_DONE_ERR 3
/* We can print the stacktrace, so our assert is defined this way: */
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)
@@ -245,7 +240,7 @@ void _redisPanic(char *msg, char *file, int line);
#define REDIS_LRU_CLOCK_RESOLUTION 10 /* LRU clock resolution in seconds */
typedef struct redisObject {
unsigned type:4;
unsigned notused:2; /* Not used */
unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
unsigned encoding:4;
unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
@@ -286,15 +281,14 @@ typedef struct vmPointer {
_var.type = REDIS_STRING; \
_var.encoding = REDIS_ENCODING_RAW; \
_var.ptr = _ptr; \
_var.storage = REDIS_VM_MEMORY; \
} while(0);
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */
dict *io_keys; /* Keys with clients waiting for DS I/O */
dict *io_negcache; /* Negative caching for disk store */
dict *io_queued; /* Queued IO operations hash table */
dict *io_keys; /* Keys with clients waiting for VM I/O */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id;
} redisDb;
@@ -373,6 +367,191 @@ struct sharedObjectsStruct {
*integers[REDIS_SHARED_INTEGERS];
};
/* Global server state structure */
struct redisServer {
pthread_t mainthread;
int port;
char *bindaddr;
char *unixsocket;
int ipfd;
int sofd;
redisDb *db;
long long dirty; /* changes to DB from the last save */
long long dirty_before_bgsave; /* used to restore dirty on failed BGSAVE */
list *clients;
dict *commands; /* Command table hahs table */
/* RDB / AOF loading information */
int loading;
off_t loading_total_bytes;
off_t loading_loaded_bytes;
time_t loading_start_time;
/* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand;
list *slaves, *monitors;
char neterr[ANET_ERR_LEN];
aeEventLoop *el;
int cronloops; /* number of times the cron function run */
time_t lastsave; /* Unix time of last save succeeede */
/* Fields used only for stats */
time_t stat_starttime; /* server start time */
long long stat_numcommands; /* number of processed commands */
long long stat_numconnections; /* number of connections received */
long long stat_expiredkeys; /* number of expired keys */
long long stat_evictedkeys; /* number of evicted keys (maxmemory) */
long long stat_keyspace_hits; /* number of successful lookups of keys */
long long stat_keyspace_misses; /* number of failed lookups of keys */
size_t stat_peak_memory; /* max used memory record */
long long stat_fork_time; /* time needed to perform latets fork() */
/* Configuration */
int verbosity;
int maxidletime;
int dbnum;
int daemonize;
int appendonly;
int appendfsync;
int no_appendfsync_on_rewrite;
int shutdown_asap;
time_t lastfsync;
int appendfd;
int appendseldb;
char *pidfile;
pid_t bgsavechildpid;
pid_t bgrewritechildpid;
sds bgrewritebuf; /* buffer taken by parent during oppend only rewrite */
sds aofbuf; /* AOF buffer, written before entering the event loop */
struct saveparam *saveparams;
int saveparamslen;
char *logfile;
int syslog_enabled;
char *syslog_ident;
int syslog_facility;
char *dbfilename;
char *appendfilename;
char *requirepass;
int rdbcompression;
int activerehashing;
/* Replication related */
int isslave;
/* Slave specific fields */
char *masterauth;
char *masterhost;
int masterport;
redisClient *master; /* client that is master for this slave */
int repl_syncio_timeout; /* timeout for synchronous I/O calls */
int replstate; /* replication status if the instance is a slave */
off_t repl_transfer_left; /* bytes left reading .rdb */
int repl_transfer_s; /* slave -> master SYNC socket */
int repl_transfer_fd; /* slave -> master SYNC temp file descriptor */
char *repl_transfer_tmpfile; /* slave-> master SYNC temp file name */
time_t repl_transfer_lastio; /* unix time of the latest read, for timeout */
int repl_serve_stale_data; /* Serve stale data when link is down? */
/* Limits */
unsigned int maxclients;
unsigned long long maxmemory;
int maxmemory_policy;
int maxmemory_samples;
/* Blocked clients */
unsigned int bpop_blocked_clients;
unsigned int vm_blocked_clients;
list *unblocked_clients;
/* Sort parameters - qsort_r() is only available under BSD so we
* have to take this state global, in order to pass it to sortCompare() */
int sort_desc;
int sort_alpha;
int sort_bypattern;
/* Virtual memory configuration */
int vm_enabled;
char *vm_swap_file;
off_t vm_page_size;
off_t vm_pages;
unsigned long long vm_max_memory;
/* Zip structure config */
size_t hash_max_zipmap_entries;
size_t hash_max_zipmap_value;
size_t list_max_ziplist_entries;
size_t list_max_ziplist_value;
size_t set_max_intset_entries;
size_t zset_max_ziplist_entries;
size_t zset_max_ziplist_value;
/* Virtual memory state */
FILE *vm_fp;
int vm_fd;
off_t vm_next_page; /* Next probably empty page */
off_t vm_near_pages; /* Number of pages allocated sequentially */
unsigned char *vm_bitmap; /* Bitmap of free/used pages */
time_t unixtime; /* Unix time sampled every second. */
/* Virtual memory I/O threads stuff */
/* An I/O thread process an element taken from the io_jobs queue and
* put the result of the operation in the io_done list. While the
* job is being processed, it's put on io_processing queue. */
list *io_newjobs; /* List of VM I/O jobs yet to be processed */
list *io_processing; /* List of VM I/O jobs being processed */
list *io_processed; /* List of VM I/O jobs already processed */
list *io_ready_clients; /* Clients ready to be unblocked. All keys loaded */
pthread_mutex_t io_mutex; /* lock to access io_jobs/io_done/io_thread_job */
pthread_mutex_t io_swapfile_mutex; /* So we can lseek + write */
pthread_attr_t io_threads_attr; /* attributes for threads creation */
int io_active_threads; /* Number of running I/O threads */
int vm_max_threads; /* Max number of I/O threads running at the same time */
/* Our main thread is blocked on the event loop, locking for sockets ready
* to be read or written, so when a threaded I/O operation is ready to be
* processed by the main thread, the I/O thread will use a unix pipe to
* awake the main thread. The followings are the two pipe FDs. */
int io_ready_pipe_read;
int io_ready_pipe_write;
/* Virtual memory stats */
unsigned long long vm_stats_used_pages;
unsigned long long vm_stats_swapped_objects;
unsigned long long vm_stats_swapouts;
unsigned long long vm_stats_swapins;
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
/* Misc */
unsigned lruclock:22; /* clock incrementing every minute, for LRU */
unsigned lruclock_padding:10;
};
typedef struct pubsubPattern {
redisClient *client;
robj *pattern;
} pubsubPattern;
typedef void redisCommandProc(redisClient *c);
typedef void redisVmPreloadProc(redisClient *c, struct redisCommand *cmd, int argc, robj **argv);
struct redisCommand {
char *name;
redisCommandProc *proc;
int arity;
int flags;
/* Use a function to determine which keys need to be loaded
* in the background prior to executing this command. Takes precedence
* over vm_firstkey and others, ignored when NULL */
redisVmPreloadProc *vm_preload_proc;
/* What keys should be loaded in background when calling this command? */
int vm_firstkey; /* The first argument that's a key (0 = no keys) */
int vm_lastkey; /* THe last argument that's a key */
int vm_keystep; /* The step between first and last key */
};
struct redisFunctionSym {
char *name;
unsigned long pointer;
};
typedef struct _redisSortObject {
robj *obj;
union {
double score;
robj *cmpobj;
} u;
} redisSortObject;
typedef struct _redisSortOperation {
int type;
robj *pattern;
} redisSortOperation;
/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
robj *obj;
@@ -395,341 +574,25 @@ typedef struct zset {
zskiplist *zsl;
} zset;
/*-----------------------------------------------------------------------------
* Redis cluster data structures
*----------------------------------------------------------------------------*/
#define REDIS_CLUSTER_SLOTS 4096
#define REDIS_CLUSTER_OK 0 /* Everything looks ok */
#define REDIS_CLUSTER_FAIL 1 /* The cluster can't work */
#define REDIS_CLUSTER_NEEDHELP 2 /* The cluster works, but needs some help */
#define REDIS_CLUSTER_NAMELEN 40 /* sha1 hex length */
#define REDIS_CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */
struct clusterNode;
/* clusterLink encapsulates everything needed to talk with a remote node. */
typedef struct clusterLink {
int fd; /* TCP socket file descriptor */
sds sndbuf; /* Packet send buffer */
sds rcvbuf; /* Packet reception buffer */
struct clusterNode *node; /* Node related to this link if any, or NULL */
} clusterLink;
/* Node flags */
#define REDIS_NODE_MASTER 1 /* The node is a master */
#define REDIS_NODE_SLAVE 2 /* The node is a slave */
#define REDIS_NODE_PFAIL 4 /* Failure? Need acknowledge */
#define REDIS_NODE_FAIL 8 /* The node is believed to be malfunctioning */
#define REDIS_NODE_MYSELF 16 /* This node is myself */
#define REDIS_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */
#define REDIS_NODE_NOADDR 64 /* We don't know the address of this node */
#define REDIS_NODE_MEET 128 /* Send a MEET message to this node */
#define REDIS_NODE_NULL_NAME "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
struct clusterNode {
char name[REDIS_CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
int flags; /* REDIS_NODE_... */
unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* slots handled by this node */
int numslaves; /* Number of slave nodes, if this is a master */
struct clusterNode **slaves; /* pointers to slave nodes */
struct clusterNode *slaveof; /* pointer to the master node */
time_t ping_sent; /* Unix time we sent latest ping */
time_t pong_received; /* Unix time we received the pong */
char *configdigest; /* Configuration digest of this node */
time_t configdigest_ts; /* Configuration digest timestamp */
char ip[16]; /* Latest known IP address of this node */
int port; /* Latest known port of this node */
clusterLink *link; /* TCP/IP link with this node */
};
typedef struct clusterNode clusterNode;
typedef struct {
char *configfile;
clusterNode *myself; /* This node */
int state; /* REDIS_CLUSTER_OK, REDIS_CLUSTER_FAIL, ... */
int node_timeout;
dict *nodes; /* Hash table of name -> clusterNode structures */
clusterNode *migrating_slots_to[REDIS_CLUSTER_SLOTS];
clusterNode *importing_slots_from[REDIS_CLUSTER_SLOTS];
clusterNode *slots[REDIS_CLUSTER_SLOTS];
zskiplist *slots_to_keys;
} clusterState;
/* Redis cluster messages header */
/* Note that the PING, PONG and MEET messages are actually the same exact
* kind of packet. PONG is the reply to ping, in the extact format as a PING,
* while MEET is a special PING that forces the receiver to add the sender
* as a node (if it is not already in the list). */
#define CLUSTERMSG_TYPE_PING 0 /* Ping */
#define CLUSTERMSG_TYPE_PONG 1 /* Pong (reply to Ping) */
#define CLUSTERMSG_TYPE_MEET 2 /* Meet "let's join" message */
#define CLUSTERMSG_TYPE_FAIL 3 /* Mark node xxx as failing */
/* Initially we don't know our "name", but we'll find it once we connect
* to the first node, using the getsockname() function. Then we'll use this
* address for all the next messages. */
typedef struct {
char nodename[REDIS_CLUSTER_NAMELEN];
uint32_t ping_sent;
uint32_t pong_received;
char ip[16]; /* IP address last time it was seen */
uint16_t port; /* port last time it was seen */
uint16_t flags;
uint32_t notused; /* for 64 bit alignment */
} clusterMsgDataGossip;
typedef struct {
char nodename[REDIS_CLUSTER_NAMELEN];
} clusterMsgDataFail;
union clusterMsgData {
/* PING, MEET and PONG */
struct {
/* Array of N clusterMsgDataGossip structures */
clusterMsgDataGossip gossip[1];
} ping;
/* FAIL */
struct {
clusterMsgDataFail about;
} fail;
};
typedef struct {
uint32_t totlen; /* Total length of this message */
uint16_t type; /* Message type */
uint16_t count; /* Only used for some kind of messages. */
char sender[REDIS_CLUSTER_NAMELEN]; /* Name of the sender node */
unsigned char myslots[REDIS_CLUSTER_SLOTS/8];
char slaveof[REDIS_CLUSTER_NAMELEN];
char configdigest[32];
uint16_t port; /* Sender TCP base port */
unsigned char state; /* Cluster state from the POV of the sender */
unsigned char notused[5]; /* Reserved for future use. For alignment. */
union clusterMsgData data;
} clusterMsg;
/*-----------------------------------------------------------------------------
* Global server state
*----------------------------------------------------------------------------*/
struct redisServer {
/* General */
pthread_t mainthread;
redisDb *db;
dict *commands; /* Command table hahs table */
aeEventLoop *el;
/* Networking */
int port;
char *bindaddr;
char *unixsocket;
int ipfd;
int sofd;
int cfd;
list *clients;
list *slaves, *monitors;
char neterr[ANET_ERR_LEN];
/* RDB / AOF loading information */
int loading;
off_t loading_total_bytes;
off_t loading_loaded_bytes;
time_t loading_start_time;
/* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand;
int cronloops; /* number of times the cron function run */
time_t lastsave; /* Unix time of last save succeeede */
/* Fields used only for stats */
time_t stat_starttime; /* server start time */
long long stat_numcommands; /* number of processed commands */
long long stat_numconnections; /* number of connections received */
long long stat_expiredkeys; /* number of expired keys */
long long stat_evictedkeys; /* number of evicted keys (maxmemory) */
long long stat_keyspace_hits; /* number of successful lookups of keys */
long long stat_keyspace_misses; /* number of failed lookups of keys */
size_t stat_peak_memory; /* max used memory record */
long long stat_fork_time; /* time needed to perform latets fork() */
/* Configuration */
int verbosity;
int maxidletime;
int dbnum;
int daemonize;
int appendonly;
int appendfsync;
int no_appendfsync_on_rewrite;
int auto_aofrewrite_perc; /* Rewrite AOF if % growth is > M and... */
off_t auto_aofrewrite_min_size; /* the AOF file is at least N bytes. */
off_t auto_aofrewrite_base_size;/* AOF size on latest startup or rewrite. */
off_t appendonly_current_size; /* AOF current size. */
int aofrewrite_scheduled; /* Rewrite once BGSAVE terminates. */
int shutdown_asap;
int activerehashing;
char *requirepass;
/* Persistence */
long long dirty; /* changes to DB from the last save */
long long dirty_before_bgsave; /* used to restore dirty on failed BGSAVE */
time_t lastfsync;
int appendfd;
int appendseldb;
char *pidfile;
pid_t bgsavechildpid;
pid_t bgrewritechildpid;
int bgsavethread_state;
pthread_mutex_t bgsavethread_mutex;
pthread_t bgsavethread;
sds bgrewritebuf; /* buffer taken by parent during oppend only rewrite */
sds aofbuf; /* AOF buffer, written before entering the event loop */
struct saveparam *saveparams;
int saveparamslen;
char *dbfilename;
int rdbcompression;
char *appendfilename;
/* Logging */
char *logfile;
int syslog_enabled;
char *syslog_ident;
int syslog_facility;
/* Replication related */
int isslave;
/* Slave specific fields */
char *masterauth;
char *masterhost;
int masterport;
redisClient *master; /* client that is master for this slave */
int repl_syncio_timeout; /* timeout for synchronous I/O calls */
int replstate; /* replication status if the instance is a slave */
off_t repl_transfer_left; /* bytes left reading .rdb */
int repl_transfer_s; /* slave -> master SYNC socket */
int repl_transfer_fd; /* slave -> master SYNC temp file descriptor */
char *repl_transfer_tmpfile; /* slave-> master SYNC temp file name */
time_t repl_transfer_lastio; /* unix time of the latest read, for timeout */
int repl_serve_stale_data; /* Serve stale data when link is down? */
time_t repl_down_since; /* unix time at which link with master went down */
/* Limits */
unsigned int maxclients;
unsigned long long maxmemory;
int maxmemory_policy;
int maxmemory_samples;
/* Blocked clients */
unsigned int bpop_blocked_clients;
unsigned int cache_blocked_clients;
list *unblocked_clients; /* list of clients to unblock before next loop */
list *cache_io_queue; /* IO operations queue */
int cache_flush_delay; /* seconds to wait before flushing keys */
/* Sort parameters - qsort_r() is only available under BSD so we
* have to take this state global, in order to pass it to sortCompare() */
int sort_desc;
int sort_alpha;
int sort_bypattern;
/* Virtual memory configuration */
int ds_enabled; /* backend disk in redis.conf */
char *ds_path; /* location of the disk store on disk */
unsigned long long cache_max_memory;
/* Zip structure config */
size_t hash_max_zipmap_entries;
size_t hash_max_zipmap_value;
size_t list_max_ziplist_entries;
size_t list_max_ziplist_value;
size_t set_max_intset_entries;
size_t zset_max_ziplist_entries;
size_t zset_max_ziplist_value;
time_t unixtime; /* Unix time sampled every second. */
/* Virtual memory I/O threads stuff */
/* An I/O thread process an element taken from the io_jobs queue and
* put the result of the operation in the io_done list. While the
* job is being processed, it's put on io_processing queue. */
list *io_newjobs; /* List of VM I/O jobs yet to be processed */
list *io_processing; /* List of VM I/O jobs being processed */
list *io_processed; /* List of VM I/O jobs already processed */
list *io_ready_clients; /* Clients ready to be unblocked. All keys loaded */
pthread_mutex_t io_mutex; /* lock to access io_jobs/io_done/io_thread_job */
pthread_cond_t io_condvar; /* I/O threads conditional variable */
pthread_attr_t io_threads_attr; /* attributes for threads creation */
int io_active_threads; /* Number of running I/O threads */
int vm_max_threads; /* Max number of I/O threads running at the same time */
/* Our main thread is blocked on the event loop, locking for sockets ready
* to be read or written, so when a threaded I/O operation is ready to be
* processed by the main thread, the I/O thread will use a unix pipe to
* awake the main thread. The followings are the two pipe FDs. */
int io_ready_pipe_read;
int io_ready_pipe_write;
/* Virtual memory stats */
unsigned long long vm_stats_used_pages;
unsigned long long vm_stats_swapped_objects;
unsigned long long vm_stats_swapouts;
unsigned long long vm_stats_swapins;
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
/* Misc */
unsigned lruclock:22; /* clock incrementing every minute, for LRU */
unsigned lruclock_padding:10;
/* Cluster */
int cluster_enabled;
clusterState cluster;
};
typedef struct pubsubPattern {
redisClient *client;
robj *pattern;
} pubsubPattern;
typedef void redisCommandProc(redisClient *c);
typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys, int flags);
struct redisCommand {
char *name;
redisCommandProc *proc;
int arity;
int flags;
/* Use a function to determine keys arguments in a command line.
* Used both for diskstore preloading and Redis Cluster. */
redisGetKeysProc *getkeys_proc;
/* What keys should be loaded in background when calling this command? */
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey; /* THe last argument that's a key */
int keystep; /* The step between first and last key */
long long microseconds, calls;
};
struct redisFunctionSym {
char *name;
unsigned long pointer;
};
typedef struct _redisSortObject {
robj *obj;
union {
double score;
robj *cmpobj;
} u;
} redisSortObject;
typedef struct _redisSortOperation {
int type;
robj *pattern;
} redisSortOperation;
/* DIsk store threaded I/O request message */
#define REDIS_IOJOB_LOAD 0
#define REDIS_IOJOB_SAVE 1
/* VM threaded I/O request message */
#define REDIS_IOJOB_LOAD 0 /* Load from disk to memory */
#define REDIS_IOJOB_PREPARE_SWAP 1 /* Compute needed pages */
#define REDIS_IOJOB_DO_SWAP 2 /* Swap from memory to disk */
typedef struct iojob {
int type; /* Request type, REDIS_IOJOB_* */
redisDb *db;/* Redis database */
robj *key; /* This I/O request is about this key */
robj *val; /* the value to swap for REDIS_IOJOB_SAVE, otherwise this
* field is populated by the I/O thread for REDIS_IOJOB_LOAD. */
time_t expire; /* Expire time for this key on REDIS_IOJOB_LOAD */
robj *key; /* This I/O request is about swapping this key */
robj *id; /* Unique identifier of this job:
this is the object to swap for REDIS_IOREQ_*_SWAP, or the
vmpointer objct for REDIS_IOREQ_LOAD. */
robj *val; /* the value to swap for REDIS_IOREQ_*_SWAP, otherwise this
* field is populated by the I/O thread for REDIS_IOREQ_LOAD. */
off_t page; /* Swap page where to read/write the object */
off_t pages; /* Swap pages needed to save object. PREPARE_SWAP return val */
int canceled; /* True if this command was canceled by blocking side of VM */
pthread_t thread; /* ID of the thread processing this entry */
} iojob;
/* IO operations scheduled -- check dscache.c for more info */
typedef struct ioop {
int type;
redisDb *db;
robj *key;
time_t ctime; /* This is the creation time of the entry. */
} ioop;
/* Structure to hold list iteration abstraction. */
typedef struct {
robj *subject;
@@ -779,7 +642,6 @@ extern struct redisServer server;
extern struct sharedObjectsStruct shared;
extern dictType setDictType;
extern dictType zsetDictType;
extern dictType clusterNodesDictType;
extern double R_Zero, R_PosInf, R_NegInf, R_Nan;
dictType hashDictType;
@@ -787,9 +649,6 @@ dictType hashDictType;
* Functions prototypes
*----------------------------------------------------------------------------*/
/* Utils */
long long ustime(void);
/* networking.c -- Networking and Client related operations */
redisClient *createClient(int fd);
void closeTimedoutClients(void);
@@ -897,7 +756,6 @@ int fwriteBulkString(FILE *fp, char *s, unsigned long len);
int fwriteBulkDouble(FILE *fp, double d);
int fwriteBulkLongLong(FILE *fp, long long l);
int fwriteBulkObject(FILE *fp, robj *obj);
int fwriteBulkCount(FILE *fp, char prefix, int count);
/* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
@@ -919,13 +777,8 @@ int rdbSaveObject(FILE *fp, robj *o);
off_t rdbSavedObjectLen(robj *o);
off_t rdbSavedObjectPages(robj *o);
robj *rdbLoadObject(int type, FILE *fp);
void backgroundSaveDoneHandler(int exitcode, int bysignal);
int rdbSaveKeyValuePair(FILE *fp, robj *key, robj *val, time_t expireitme, time_t now);
int rdbLoadType(FILE *fp);
time_t rdbLoadTime(FILE *fp);
robj *rdbLoadStringObject(FILE *fp);
int rdbSaveType(FILE *fp, unsigned char type);
int rdbSaveLen(FILE *fp, uint32_t len);
void backgroundSaveDoneHandler(int statloc);
int getObjectSaveType(robj *o);
/* AOF persistence */
void flushAppendOnlyFile(void);
@@ -935,22 +788,13 @@ int rewriteAppendOnlyFileBackground(void);
int loadAppendOnlyFile(char *filename);
void stopAppendOnly(void);
int startAppendOnly(void);
void backgroundRewriteDoneHandler(int exitcode, int bysignal);
void backgroundRewriteDoneHandler(int statloc);
/* Sorted sets data type */
/* Struct to hold a inclusive/exclusive range spec. */
typedef struct {
double min, max;
int minex, maxex; /* are min or max exclusive? */
} zrangespec;
zskiplist *zslCreate(void);
void zslFree(zskiplist *zsl);
zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);
unsigned char *zzlInsert(unsigned char *zl, robj *ele, double score);
int zslDelete(zskiplist *zsl, double score, robj *obj);
zskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec range);
double zzlGetScore(unsigned char *sptr);
void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
@@ -971,41 +815,33 @@ void updateDictResizePolicy(void);
int htNeedsResize(dict *dict);
void oom(const char *msg);
void populateCommandTable(void);
void resetCommandTableStats(void);
/* Disk store */
int dsOpen(void);
int dsClose(void);
int dsSet(redisDb *db, robj *key, robj *val, time_t expire);
robj *dsGet(redisDb *db, robj *key, time_t *expire);
int dsDel(redisDb *db, robj *key);
int dsExists(redisDb *db, robj *key);
void dsFlushDb(int dbid);
int dsRdbSaveBackground(char *filename);
int dsRdbSave(char *filename);
/* Disk Store Cache */
void dsInit(void);
/* Virtual Memory */
void vmInit(void);
void vmMarkPagesFree(off_t page, off_t count);
robj *vmLoadObject(robj *o);
robj *vmPreviewObject(robj *o);
int vmSwapOneObjectBlocking(void);
int vmSwapOneObjectThreaded(void);
int vmCanSwapOut(void);
void vmThreadedIOCompletedJob(aeEventLoop *el, int fd, void *privdata, int mask);
void vmCancelThreadedIOJob(robj *o);
void lockThreadedIO(void);
void unlockThreadedIO(void);
int vmSwapObjectThreaded(robj *key, robj *val, redisDb *db);
void freeIOJob(iojob *j);
void queueIOJob(iojob *j);
int vmWriteObjectOnSwap(robj *o, off_t page);
robj *vmReadObjectFromSwap(off_t page, int type);
void waitEmptyIOJobsQueue(void);
void processAllPendingIOJobs(void);
void vmReopenSwapFile(void);
int vmFreePage(off_t page);
void zunionInterBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv);
void execBlockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd, int argc, robj **argv);
int blockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd);
int dontWaitForSwappedKey(redisClient *c, robj *key);
void handleClientsBlockedOnSwappedKey(redisDb *db, robj *key);
int cacheFreeOneEntry(void);
void cacheScheduleIOAddFlag(redisDb *db, robj *key, long flag);
void cacheScheduleIODelFlag(redisDb *db, robj *key, long flag);
int cacheScheduleIOGetFlags(redisDb *db, robj *key);
void cacheScheduleIO(redisDb *db, robj *key, int type);
void cacheCron(void);
int cacheKeyMayExist(redisDb *db, robj *key);
void cacheSetKeyMayExist(redisDb *db, robj *key);
void cacheSetKeyDoesNotExist(redisDb *db, robj *key);
void cacheForcePointInTime(void);
vmpointer *vmSwapObjectBlocking(robj *val);
/* Set data type */
robj *setTypeCreate(robj *value);
@@ -1059,35 +895,13 @@ robj *lookupKeyRead(redisDb *db, robj *key);
robj *lookupKeyWrite(redisDb *db, robj *key);
robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply);
robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply);
void dbAdd(redisDb *db, robj *key, robj *val);
void dbOverwrite(redisDb *db, robj *key, robj *val);
void setKey(redisDb *db, robj *key, robj *val);
int dbAdd(redisDb *db, robj *key, robj *val);
int dbReplace(redisDb *db, robj *key, robj *val);
int dbExists(redisDb *db, robj *key);
robj *dbRandomKey(redisDb *db);
int dbDelete(redisDb *db, robj *key);
long long emptyDb();
int selectDb(redisClient *c, int id);
void signalModifiedKey(redisDb *db, robj *key);
void signalFlushedDb(int dbid);
unsigned int GetKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count);
/* API to get key arguments from commands */
#define REDIS_GETKEYS_ALL 0
#define REDIS_GETKEYS_PRELOAD 1
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys, int flags);
void getKeysFreeResult(int *result);
int *noPreloadGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags);
int *renameGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags);
int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags);
/* Cluster */
void clusterInit(void);
unsigned short crc16(const char *buf, int len);
unsigned int keyHashSlot(char *key, int keylen);
clusterNode *createClusterNode(char *nodename, int flags);
int clusterAddNode(clusterNode *node);
void clusterCron(void);
clusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
/* Git SHA1 */
char *redisGitSHA1(void);
@@ -1211,10 +1025,6 @@ void punsubscribeCommand(redisClient *c);
void publishCommand(redisClient *c);
void watchCommand(redisClient *c);
void unwatchCommand(redisClient *c);
void clusterCommand(redisClient *c);
void restoreCommand(redisClient *c);
void migrateCommand(redisClient *c);
void dumpCommand(redisClient *c);
void objectCommand(redisClient *c);
void clientCommand(redisClient *c);
+3 -5
View File
@@ -358,8 +358,6 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
server.master->authenticated = 1;
server.replstate = REDIS_REPL_CONNECTED;
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
/* Rewrite the AOF file now that the dataset changed. */
if (server.appendonly) rewriteAppendOnlyFileBackground();
}
return;
@@ -376,11 +374,10 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);
redisLog(REDIS_NOTICE,"Non blocking connect for SYNC fired the event.");
/* This event should only be triggered once since it is used to have a
* non-blocking connect(2) to the master. It has been triggered when this
* function is called, so we can delete it. */
aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);
aeDeleteFileEvent(server.el,fd,AE_WRITABLE);
/* AUTH with the master if required. */
if(server.masterauth) {
@@ -456,7 +453,7 @@ int connectWithMaster(void) {
return REDIS_ERR;
}
if (aeCreateFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE,syncWithMaster,NULL) ==
if (aeCreateFileEvent(server.el,fd,AE_WRITABLE,syncWithMaster,NULL) ==
AE_ERR)
{
close(fd);
@@ -522,6 +519,7 @@ void replicationCron(void) {
redisLog(REDIS_NOTICE,"Connecting to MASTER...");
if (connectWithMaster() == REDIS_OK) {
redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started");
if (server.appendonly) rewriteAppendOnlyFileBackground();
}
}
+5 -15
View File
@@ -51,11 +51,7 @@ static void sdsOomAbort(void) {
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 = zmalloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if (sh == NULL) sdsOomAbort();
#else
@@ -63,8 +59,10 @@ sds sdsnewlen(const void *init, size_t initlen) {
#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;
}
@@ -363,7 +361,6 @@ cleanup:
int i;
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
zfree(tokens);
*count = 0;
return NULL;
}
#endif
@@ -546,13 +543,6 @@ err:
return NULL;
}
void sdssplitargs_free(sds *argv, int argc) {
int j;
for (j = 0 ;j < argc; j++) sdsfree(argv[j]);
zfree(argv);
}
#ifdef SDS_TEST_MAIN
#include <stdio.h>
#include "testhelp.h"
-1
View File
@@ -84,6 +84,5 @@ void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, char *p, size_t len);
sds *sdssplitargs(char *line, int *argc);
void sdssplitargs_free(sds *argv, int argc);
#endif
+49 -1
View File
@@ -28,7 +28,55 @@ A million repetitions of "a"
#include "solarisfixes.h"
#endif
#include "sha1.h"
#include "config.h"
#ifndef BYTE_ORDER
#if (BSD >= 199103)
# include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__)
# include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
defined(__alpha__) || defined(__alpha)
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
defined(__hppa) || defined(__hp9000) || \
defined(__hp9000s300) || defined(__hp9000s700) || \
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \
BYTE_ORDER != PDP_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
#error "Undefined or invalid BYTE_ORDER"
#endif
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+2 -2
View File
@@ -366,12 +366,12 @@ void sortCommand(redisClient *c) {
}
}
}
setKey(c->db,storekey,sobj);
decrRefCount(sobj);
dbReplace(c->db,storekey,sobj);
/* Note: we add 1 because the DB is dirty anyway since even if the
* SORT result is empty a new key is set and maybe the old content
* replaced. */
server.dirty += 1+outputlen;
touchWatchedKey(c->db,storekey);
addReplyLongLong(c,outputlen);
}
-14
View File
@@ -107,7 +107,6 @@ int syncReadLine(int fd, char *ptr, ssize_t size, int timeout) {
int fwriteBulkString(FILE *fp, char *s, unsigned long len) {
char cbuf[128];
int clen;
cbuf[0] = '$';
clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,len);
cbuf[clen++] = '\r';
@@ -118,19 +117,6 @@ int fwriteBulkString(FILE *fp, char *s, unsigned long len) {
return 1;
}
/* Write a multi bulk count in the form "*<count>\r\n" */
int fwriteBulkCount(FILE *fp, char prefix, int count) {
char cbuf[128];
int clen;
cbuf[0] = prefix;
clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count);
cbuf[clen++] = '\r';
cbuf[clen++] = '\n';
if (fwrite(cbuf,clen,1,fp) == 0) return 0;
return 1;
}
/* Write a double value in bulk format $<count>\r\n<payload>\r\n */
int fwriteBulkDouble(FILE *fp, double d) {
char buf[128], dbuf[128];
+5 -5
View File
@@ -279,7 +279,7 @@ void hsetCommand(redisClient *c) {
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
update = hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, update ? shared.czero : shared.cone);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -294,7 +294,7 @@ void hsetnxCommand(redisClient *c) {
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, shared.cone);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
}
@@ -315,7 +315,7 @@ void hmsetCommand(redisClient *c) {
hashTypeSet(o,c->argv[i],c->argv[i+1]);
}
addReply(c, shared.ok);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -342,7 +342,7 @@ void hincrbyCommand(redisClient *c) {
hashTypeSet(o,c->argv[2],new);
decrRefCount(new);
addReplyLongLong(c,value);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -408,7 +408,7 @@ void hdelCommand(redisClient *c) {
}
}
if (deleted) {
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
+10 -11
View File
@@ -286,7 +286,7 @@ void pushGenericCommand(redisClient *c, int where) {
pushed++;
}
addReplyLongLong(c,addlen + (lobj ? listTypeLength(lobj) : 0));
if (pushed) signalModifiedKey(c->db,c->argv[1]);
if (pushed) touchWatchedKey(c->db,c->argv[1]);
server.dirty += pushed;
}
@@ -335,7 +335,7 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
if (subject->encoding == REDIS_ENCODING_ZIPLIST &&
ziplistLen(subject->ptr) > server.list_max_ziplist_entries)
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
} else {
/* Notify client of a failed insert */
@@ -344,7 +344,7 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
}
} else {
listTypePush(subject,val,where);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -432,7 +432,7 @@ void lsetCommand(redisClient *c) {
o->ptr = ziplistInsert(o->ptr,p,value->ptr,sdslen(value->ptr));
decrRefCount(value);
addReply(c,shared.ok);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
} else if (o->encoding == REDIS_ENCODING_LINKEDLIST) {
@@ -444,7 +444,7 @@ void lsetCommand(redisClient *c) {
listNodeValue(ln) = value;
incrRefCount(value);
addReply(c,shared.ok);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
} else {
@@ -463,7 +463,7 @@ void popGenericCommand(redisClient *c, int where) {
addReplyBulk(c,value);
decrRefCount(value);
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
}
@@ -578,7 +578,7 @@ void ltrimCommand(redisClient *c) {
redisPanic("Unknown list encoding");
}
if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
addReply(c,shared.ok);
}
@@ -621,7 +621,7 @@ void lremCommand(redisClient *c) {
if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
addReplyLongLong(c,removed);
if (removed) signalModifiedKey(c->db,c->argv[1]);
if (removed) touchWatchedKey(c->db,c->argv[1]);
}
/* This is the semantic of this command:
@@ -649,7 +649,7 @@ void rpoplpushHandlePush(redisClient *origclient, redisClient *c, robj *dstkey,
dstobj = createZiplistObject();
dbAdd(c->db,dstkey,dstobj);
} else {
signalModifiedKey(c->db,dstkey);
touchWatchedKey(c->db,dstkey);
}
listTypePush(dstobj,value,REDIS_HEAD);
/* If we are pushing as a result of LPUSH against a key
@@ -698,7 +698,7 @@ void rpoplpushCommand(redisClient *c) {
/* Delete the source list when it is empty */
if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey);
signalModifiedKey(c->db,touchedkey);
touchWatchedKey(c->db,touchedkey);
decrRefCount(touchedkey);
server.dirty++;
}
@@ -848,7 +848,6 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
/* Protect receiver->bpop.target, that will be freed by
* the next unblockClientWaitingData() call. */
if (dstkey) incrRefCount(dstkey);
/* This should remove the first element of the "clients" list. */
unblockClientWaitingData(receiver);
+8 -8
View File
@@ -235,7 +235,7 @@ 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]);
if (added) touchWatchedKey(c->db,c->argv[1]);
server.dirty += added;
addReplyLongLong(c,added);
}
@@ -257,7 +257,7 @@ void sremCommand(redisClient *c) {
}
}
if (deleted) {
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
@@ -294,8 +294,8 @@ void smoveCommand(redisClient *c) {
/* Remove the src set from the database when empty */
if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
touchWatchedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[2]);
server.dirty++;
/* Create the destination set when it doesn't exist */
@@ -356,7 +356,7 @@ void spopCommand(redisClient *c) {
addReplyBulk(c,ele);
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -397,7 +397,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
zfree(sets);
if (dstkey) {
if (dbDelete(c->db,dstkey)) {
signalModifiedKey(c->db,dstkey);
touchWatchedKey(c->db,dstkey);
server.dirty++;
}
addReply(c,shared.czero);
@@ -502,7 +502,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
decrRefCount(dstset);
addReply(c,shared.czero);
}
signalModifiedKey(c->db,dstkey);
touchWatchedKey(c->db,dstkey);
server.dirty++;
} else {
setDeferredMultiBulkLength(c,replylen,cardinality);
@@ -594,7 +594,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
decrRefCount(dstset);
addReply(c,shared.czero);
}
signalModifiedKey(c->db,dstkey);
touchWatchedKey(c->db,dstkey);
server.dirty++;
}
zfree(sets);
+37 -24
View File
@@ -13,6 +13,7 @@ static int checkStringLength(redisClient *c, long long size) {
}
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
int retval;
long seconds = 0; /* initialized to avoid an harmness warning */
if (expire) {
@@ -24,12 +25,21 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
}
}
if (lookupKeyWrite(c->db,key) != NULL && nx) {
addReply(c,shared.czero);
return;
retval = dbAdd(c->db,key,val);
if (retval == REDIS_ERR) {
if (!nx) {
dbReplace(c->db,key,val);
incrRefCount(val);
} else {
addReply(c,shared.czero);
return;
}
} else {
incrRefCount(val);
}
setKey(c->db,key,val);
touchWatchedKey(c->db,key);
server.dirty++;
removeExpire(c->db,key);
if (expire) setExpire(c->db,key,time(NULL)+seconds);
addReply(c, nx ? shared.cone : shared.ok);
}
@@ -71,8 +81,11 @@ void getCommand(redisClient *c) {
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]);
dbReplace(c->db,c->argv[1],c->argv[2]);
incrRefCount(c->argv[2]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
removeExpire(c->db,c->argv[1]);
}
static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
@@ -125,7 +138,7 @@ void setbitCommand(redisClient *c) {
robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded);
dbOverwrite(c->db,c->argv[1],o);
dbReplace(c->db,c->argv[1],o);
}
}
@@ -142,7 +155,7 @@ void setbitCommand(redisClient *c) {
byteval &= ~(1 << bit);
byteval |= ((on & 0x1) << bit);
((char*)o->ptr)[byte] = byteval;
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
addReply(c, bitval ? shared.cone : shared.czero);
}
@@ -223,14 +236,14 @@ void setrangeCommand(redisClient *c) {
robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded);
dbOverwrite(c->db,c->argv[1],o);
dbReplace(c->db,c->argv[1],o);
}
}
if (sdslen(value) > 0) {
o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
memcpy((char*)o->ptr+offset,value,sdslen(value));
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
}
addReplyLongLong(c,sdslen(o->ptr));
@@ -306,15 +319,18 @@ void msetGenericCommand(redisClient *c, int nx) {
busykeys++;
}
}
if (busykeys) {
addReply(c, shared.czero);
return;
}
}
if (busykeys) {
addReply(c, shared.czero);
return;
}
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]);
dbReplace(c->db,c->argv[j],c->argv[j+1]);
incrRefCount(c->argv[j+1]);
removeExpire(c->db,c->argv[j]);
touchWatchedKey(c->db,c->argv[j]);
}
server.dirty += (c->argc-1)/2;
addReply(c, nx ? shared.cone : shared.ok);
@@ -330,7 +346,7 @@ void msetnxCommand(redisClient *c) {
void incrDecrCommand(redisClient *c, long long incr) {
long long value, oldvalue;
robj *o, *new;
robj *o;
o = lookupKeyWrite(c->db,c->argv[1]);
if (o != NULL && checkType(c,o,REDIS_STRING)) return;
@@ -342,15 +358,12 @@ void incrDecrCommand(redisClient *c, long long incr) {
addReplyError(c,"increment or decrement would overflow");
return;
}
new = createStringObjectFromLongLong(value);
if (o)
dbOverwrite(c->db,c->argv[1],new);
else
dbAdd(c->db,c->argv[1],new);
signalModifiedKey(c->db,c->argv[1]);
o = createStringObjectFromLongLong(value);
dbReplace(c->db,c->argv[1],o);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
addReply(c,shared.colon);
addReply(c,new);
addReply(c,o);
addReply(c,shared.crlf);
}
@@ -403,14 +416,14 @@ void appendCommand(redisClient *c) {
robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded);
dbOverwrite(c->db,c->argv[1],o);
dbReplace(c->db,c->argv[1],o);
}
/* Append the value */
o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
totlen = sdslen(o->ptr);
}
signalModifiedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
addReplyLongLong(c,totlen);
}
+16 -10
View File
@@ -174,6 +174,12 @@ int zslDelete(zskiplist *zsl, double score, robj *obj) {
return 0; /* not found */
}
/* Struct to hold a inclusive/exclusive range spec. */
typedef struct {
double min, max;
int minex, maxex; /* are min or max exclusive? */
} zrangespec;
static int zslValueGteMin(double value, zrangespec *spec) {
return spec->minex ? (value > spec->min) : (value >= spec->min);
}
@@ -876,7 +882,7 @@ void zaddGenericCommand(redisClient *c, int incr) {
zobj->ptr = zzlDelete(zobj->ptr,eptr);
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty++;
}
} else {
@@ -888,7 +894,7 @@ void zaddGenericCommand(redisClient *c, int incr) {
if (sdslen(ele->ptr) > server.zset_max_ziplist_value)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty++;
if (!incr) added++;
}
@@ -923,7 +929,7 @@ void zaddGenericCommand(redisClient *c, int incr) {
incrRefCount(curobj); /* Re-inserted in skiplist. */
dictGetEntryVal(de) = &znode->score; /* Update score ptr. */
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty++;
}
} else {
@@ -932,7 +938,7 @@ void zaddGenericCommand(redisClient *c, int incr) {
redisAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty++;
if (!incr) added++;
}
@@ -1004,7 +1010,7 @@ void zremCommand(redisClient *c) {
}
if (deleted) {
signalModifiedKey(c->db,key);
touchWatchedKey(c->db,key);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
@@ -1037,7 +1043,7 @@ void zremrangebyscoreCommand(redisClient *c) {
redisPanic("Unknown sorted set encoding");
}
if (deleted) signalModifiedKey(c->db,key);
if (deleted) touchWatchedKey(c->db,key);
server.dirty += deleted;
addReplyLongLong(c,deleted);
}
@@ -1085,7 +1091,7 @@ void zremrangebyrankCommand(redisClient *c) {
redisPanic("Unknown sorted set encoding");
}
if (deleted) signalModifiedKey(c->db,key);
if (deleted) touchWatchedKey(c->db,key);
server.dirty += deleted;
addReplyLongLong(c,deleted);
}
@@ -1530,7 +1536,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
dstobj = createZsetObject();
dstzset = dstobj->ptr;
memset(&zval, 0, sizeof(zval));
memset(&zval,0,sizeof(zval));
if (op == REDIS_OP_INTER) {
/* Skip everything if the smallest input is empty. */
@@ -1617,7 +1623,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
zuiClearIterator(&src[i]);
if (dbDelete(c->db,dstkey)) {
signalModifiedKey(c->db,dstkey);
touchWatchedKey(c->db,dstkey);
touched = 1;
server.dirty++;
}
@@ -1629,7 +1635,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
dbAdd(c->db,dstkey,dstobj);
addReplyLongLong(c,zsetLength(dstobj));
if (!touched) signalModifiedKey(c->db,dstkey);
if (!touched) touchWatchedKey(c->db,dstkey);
server.dirty++;
} else {
decrRefCount(dstobj);
+12
View File
@@ -5,6 +5,7 @@
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <sys/time.h>
#include "util.h"
@@ -327,6 +328,17 @@ int d2string(char *buf, size_t len, double value) {
return len;
}
/* Return the UNIX time in microseconds */
long long ustime(void) {
struct timeval tv;
long long ust;
gettimeofday(&tv, NULL);
ust = ((long long)tv.tv_sec)*1000000;
ust += tv.tv_usec;
return ust;
}
#ifdef UTIL_TEST_MAIN
#include <assert.h>
+1
View File
@@ -8,5 +8,6 @@ int ll2string(char *s, size_t len, long long value);
int string2ll(char *s, size_t slen, long long *value);
int string2l(char *s, size_t slen, long *value);
int d2string(char *buf, size_t len, double value);
long long ustime(void);
#endif
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.9.0"
#define REDIS_VERSION "2.3.5"
+1159
View File
File diff suppressed because it is too large Load Diff
+72 -134
View File
@@ -18,9 +18,9 @@ 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
pidfile redis.pid
# Accept connections on the specified port, default is 6379.
# 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
@@ -28,12 +28,6 @@ port 6379
#
# bind 127.0.0.1
# Specify the path for the unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 300
@@ -50,16 +44,6 @@ loglevel verbose
# 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
@@ -102,7 +86,7 @@ dbfilename dump.rdb
# Also the Append Only File will be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
dir ./test/tmp
################################# REPLICATION #################################
@@ -120,19 +104,6 @@ dir ./
#
# 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
@@ -148,22 +119,6 @@ slave-serve-stale-data yes
#
# 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
@@ -193,37 +148,6 @@ slave-serve-stale-data yes
#
# 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
@@ -271,54 +195,81 @@ appendonly no
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
################################ VIRTUAL MEMORY ###############################
#################################### DISK STORE ###############################
# Virtual Memory allows Redis to work with datasets bigger than the actual
# amount of RAM needed to hold the whole dataset in memory.
# In order to do so very used keys are taken in memory while the other keys
# are swapped into a swap file, similarly to what operating systems do
# with memory pages.
#
# To enable VM just set 'vm-enabled' to yes, and set the following three
# VM parameters accordingly to your needs.
# When disk store is active Redis works as an on-disk database, where memory
# is only used as a object cache.
#
# This mode is good for datasets that are bigger than memory, and in general
# when you want to trade speed for:
#
# - less memory used
# - immediate server restart
# - per key durability, without need for backgrond savig
#
# On the other hand, with disk store enabled MULTI/EXEC are no longer
# transactional from the point of view of the persistence on disk, that is,
# Redis transactions will still guarantee that commands are either processed
# all or nothing, but there is no guarantee that all the keys are flushed
# on disk in an atomic way.
#
# Of course with disk store enabled Redis is not as fast as it is when
# working with just the memory back end.
vm-enabled no
# vm-enabled yes
diskstore-enabled no
diskstore-path redis.ds
cache-max-memory 0
cache-flush-delay 0
# This is the path of the Redis swap file. As you can guess, swap files
# can't be shared by different Redis instances, so make sure to use a swap
# file for every redis process you are running. Redis will complain if the
# swap file is already in use.
#
# The best kind of storage for the Redis swap file (that's accessed at random)
# is a Solid State Disk (SSD).
#
# *** WARNING *** if you are using a shared hosting the default of putting
# the swap file under /tmp is not secure. Create a dir with access granted
# only to Redis user and configure Redis to create the swap file there.
vm-swap-file redis.swap
# vm-max-memory configures the VM to use at max the specified amount of
# RAM. Everything that deos not fit will be swapped on disk *if* possible, that
# is, if there is still enough contiguous space in the swap file.
#
# With vm-max-memory 0 the system will swap everything it can. Not a good
# default, just specify the max amount of RAM you can in bytes, but it's
# better to leave some margin. For instance specify an amount of RAM
# that's more or less between 60 and 80% of your free RAM.
vm-max-memory 0
# Redis swap files is split into pages. An object can be saved using multiple
# contiguous pages, but pages can't be shared between different objects.
# So if your page is too big, small objects swapped out on disk will waste
# a lot of space. If you page is too small, there is less space in the swap
# file (assuming you configured the same number of total swap file pages).
#
# If you use a lot of small objects, use a page size of 64 or 32 bytes.
# If you use a lot of big objects, use a bigger page size.
# If unsure, use the default :)
vm-page-size 32
# Number of total memory pages in the swap file.
# Given that the page table (a bitmap of free/used pages) is taken in memory,
# every 8 pages on disk will consume 1 byte of RAM.
#
# The total swap size is vm-page-size * vm-pages
#
# 32M swap should be enough for testing.
vm-pages 1048576
# Max number of VM I/O threads running at the same time.
# This threads are used to read/write data from/to swap file, since they
# also encode and decode objects from disk to memory or the reverse, a bigger
# number of threads can help with big objects even if they can't help with
# I/O itself as the physical device may not be able to couple with many
# reads/writes operations at the same time.
#
# The special value of 0 turn off threaded I/O and enables the blocking
# Virtual Memory implementation.
vm-max-threads 4
############################### ADVANCED CONFIG ###############################
# Glue small output buffers together in order to send small replies in a
# single TCP packet. Uses a bit more CPU but most of the times it is a win
# in terms of number of queries per second. Use 'yes' if unsure.
glueoutputbuf yes
# 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
@@ -326,19 +277,6 @@ cache-flush-delay 0
hash-max-zipmap-entries 64
hash-max-zipmap-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)
+1 -1
View File
@@ -214,7 +214,7 @@ proc start_server {options {code undefined}} {
# find out the pid
while {![info exists pid]} {
regexp {\[(\d+)\]} [exec cat $stdout] _ pid
regexp {^\[(\d+)\]} [exec head -n1 $stdout] _ pid
after 100
}
+1 -7
View File
@@ -4,13 +4,7 @@ file mkdir $::tmproot
# returns a dirname unique to this process to write to
proc tmpdir {basename} {
if {$::diskstore} {
# For diskstore we want to use the same dir again and again
# otherwise everything is too slow.
set dir [file join $::tmproot $basename.diskstore]
} else {
set dir [file join $::tmproot $basename.[pid].[incr ::tmpcounter]]
}
set dir [file join $::tmproot $basename.[pid].[incr ::tmpcounter]]
file mkdir $dir
set _ $dir
}
+4 -8
View File
@@ -19,7 +19,6 @@ set ::allowtags {}
set ::external 0; # If "1" this means, we are running against external instance
set ::file ""; # If set, runs only the tests in this comma separated list
set ::curfile ""; # Hold the filename of the current suite
set ::diskstore 0; # Don't touch this by hand. The test itself will toggle it.
proc execute_tests name {
set path "tests/$name.tcl"
@@ -104,7 +103,6 @@ proc s {args} {
}
proc cleanup {} {
puts "Cleanup: warning may take some time..."
catch {exec rm -rf {*}[glob tests/tmp/redis.conf.*]}
catch {exec rm -rf {*}[glob tests/tmp/server.*]}
}
@@ -135,12 +133,9 @@ proc execute_everything {} {
# execute_tests "integration/redis-cli"
execute_tests "unit/pubsub"
return; # No diskstore tests for now...
# run tests with diskstore enabled
puts "\nRunning diskstore tests... this is slow, press Ctrl+C if not interested.."
set ::diskstore 1
lappend ::denytags nodiskstore
set ::global_overrides {diskstore-enabled yes}
# run tests with VM enabled
if 0 {
set ::global_overrides {vm-enabled yes}
execute_tests "unit/protocol"
execute_tests "unit/basic"
execute_tests "unit/type/list"
@@ -151,6 +146,7 @@ proc execute_everything {} {
execute_tests "unit/expire"
execute_tests "unit/other"
execute_tests "unit/cas"
}
}
proc main {} {
+11 -38
View File
@@ -138,48 +138,21 @@ start_server {tags {"basic"}} {
r decrby novar 17179869185
} {-1}
test "SETNX target key missing" {
r del novar
assert_equal 1 [r setnx novar foobared]
assert_equal "foobared" [r get novar]
}
test {SETNX target key missing} {
r setnx novar2 foobared
r get novar2
} {foobared}
test "SETNX target key exists" {
r set novar foobared
assert_equal 0 [r setnx novar blabla]
assert_equal "foobared" [r get novar]
}
test {SETNX target key exists} {
r setnx novar2 blabla
r get novar2
} {foobared}
test "SETNX against not-expired volatile key" {
test {SETNX against volatile key} {
r set x 10
r expire x 10000
assert_equal 0 [r setnx x 20]
assert_equal 10 [r get x]
}
test "SETNX against expired volatile key" {
# Make it very unlikely for the key this test uses to be expired by the
# active expiry cycle. This is tightly coupled to the implementation of
# active expiry and dbAdd() but currently the only way to test that
# SETNX expires a key when it should have been.
for {set x 0} {$x < 9999} {incr x} {
r setex key-$x 3600 value
}
# This will be one of 10000 expiring keys. A cycle is executed every
# 100ms, sampling 10 keys for being expired or not. This key will be
# expired for at most 1s when we wait 2s, resulting in a total sample
# of 100 keys. The probability of the success of this test being a
# false positive is therefore approx. 1%.
r set x 10
r expire x 1
# Wait for the key to expire
after 2000
assert_equal 1 [r setnx x 20]
assert_equal 20 [r get x]
}
list [r setnx x 20] [r get x]
} {0 10}
test {EXISTS} {
set res {}
+32 -41
View File
@@ -12,7 +12,7 @@ start_server {tags {"other"}} {
r save
} {OK}
tags {slow nodiskstore} {
tags {"slow"} {
foreach fuzztype {binary alpha compr} {
test "FUZZ stresser with data model $fuzztype" {
set err 0
@@ -46,7 +46,7 @@ start_server {tags {"other"}} {
set _ $err
} {*invalid*}
tags {consistency nodiskstore} {
tags {consistency} {
if {![catch {package require sha1}]} {
test {Check consistency of different data types after a reload} {
r flushdb
@@ -102,54 +102,45 @@ start_server {tags {"other"}} {
r flushdb
r set x 10
r expire x 1000
if {$::diskstore} {
r debug flushcache
} else {
r save
r debug reload
}
r save
r debug reload
set ttl [r ttl x]
set e1 [expr {$ttl > 900 && $ttl <= 1000}]
if {!$::diskstore} {
r bgrewriteaof
waitForBgrewriteaof r
r debug loadaof
}
r bgrewriteaof
waitForBgrewriteaof r
set ttl [r ttl x]
set e2 [expr {$ttl > 900 && $ttl <= 1000}]
list $e1 $e2
} {1 1}
tags {protocol nodiskstore} {
test {PIPELINING stresser (also a regression for the old epoll bug)} {
set fd2 [socket $::host $::port]
fconfigure $fd2 -encoding binary -translation binary
puts -nonewline $fd2 "SELECT 9\r\n"
flush $fd2
gets $fd2
test {PIPELINING stresser (also a regression for the old epoll bug)} {
set fd2 [socket $::host $::port]
fconfigure $fd2 -encoding binary -translation binary
puts -nonewline $fd2 "SELECT 9\r\n"
flush $fd2
gets $fd2
for {set i 0} {$i < 100000} {incr i} {
set q {}
set val "0000${i}0000"
append q "SET key:$i $val\r\n"
puts -nonewline $fd2 $q
set q {}
append q "GET key:$i\r\n"
puts -nonewline $fd2 $q
}
flush $fd2
for {set i 0} {$i < 100000} {incr i} {
set q {}
set val "0000${i}0000"
append q "SET key:$i $val\r\n"
puts -nonewline $fd2 $q
set q {}
append q "GET key:$i\r\n"
puts -nonewline $fd2 $q
}
flush $fd2
for {set i 0} {$i < 100000} {incr i} {
gets $fd2 line
gets $fd2 count
set count [string range $count 1 end]
set val [read $fd2 $count]
read $fd2 2
}
close $fd2
set _ 1
} {1}
}
for {set i 0} {$i < 100000} {incr i} {
gets $fd2 line
gets $fd2 count
set count [string range $count 1 end]
set val [read $fd2 $count]
read $fd2 2
}
close $fd2
set _ 1
} {1}
test {MUTLI / EXEC basics} {
r del mylist
-7
View File
@@ -59,13 +59,6 @@ start_server {
assert_encoding hashtable myset
}
test {Variadic SADD} {
r del myset
assert_equal 3 [r sadd myset a b c]
assert_equal 2 [r sadd myset A a b c B]
assert_equal [lsort {A a b c B}] [lsort [r smembers myset]]
}
test "Set encoding after DEBUG RELOAD" {
r del myintset myhashset mylargeintset
for {set i 0} {$i < 100} {incr i} { r sadd myintset $i }
-22
View File
@@ -1,22 +0,0 @@
# Build a symbol table for static symbols of redis.c
# Useful to get stack traces on segfault without a debugger. See redis.c
# for more information.
#
# Copyright(C) 2009 Salvatore Sanfilippo, under the BSD license.
set fd [open redis.c]
set symlist {}
while {[gets $fd line] != -1} {
if {[regexp {^static +[A-z0-9]+[ *]+([A-z0-9]*)\(} $line - sym]} {
lappend symlist $sym
}
}
set symlist [lsort -unique $symlist]
puts "static struct redisFunctionSym symsTable\[\] = {"
foreach sym $symlist {
puts "{\"$sym\",(unsigned long)$sym},"
}
puts "{NULL,0}"
puts "};"
close $fd
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
if [ $# != "1" ]
then
echo "Usage: ./mkrelease.sh <git-ref>"
exit 1
fi
TAG=$1
TARNAME="redis-${TAG}.tar"
echo "Generating /tmp/${TARNAME}"
git archive $TAG --prefix redis-${TAG}/ > /tmp/$TARNAME || exit 1
echo "Gizipping the archive"
rm -f /tmp/$TARNAME.gz
gzip -9 /tmp/$TARNAME
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
if [ "$1" = "" ]
then
echo "Usage: mktarball.sh <git tag, branch or commit>"
echo "Example: mktarball.sh 2.2-rc4"
exit 1
fi
PREFIX="redis-${1}/"
TARBALL="/tmp/redis-${1}.tar.gz"
git archive --format=tar --prefix=$PREFIX $1 | gzip -c > $TARBALL
echo "File created: $TARBALL"
-78
View File
@@ -1,78 +0,0 @@
# redis-copy.rb - Copyright (C) 2009-2010 Salvatore Sanfilippo
# BSD license, See the COPYING file for more information.
#
# Copy the whole dataset from one Redis instance to another one
#
# WARNING: currently hashes and sorted sets are not supported! This
# program should be updated.
require 'rubygems'
require 'redis'
require 'digest/sha1'
def redisCopy(opts={})
sha1=""
src = Redis.new(:host => opts[:srchost], :port => opts[:srcport])
dst = Redis.new(:host => opts[:dsthost], :port => opts[:dstport])
puts "Loading key names..."
keys = src.keys('*')
puts "Copying #{keys.length} keys..."
c = 0
keys.each{|k|
vtype = src.type?(k)
ttl = src.ttl(k).to_i if vtype != "none"
if vtype == "string"
dst[k] = src[k]
elsif vtype == "list"
list = src.lrange(k,0,-1)
if list.length == 0
# Empty list special case
dst.lpush(k,"")
dst.lpop(k)
else
list.each{|ele|
dst.rpush(k,ele)
}
end
elsif vtype == "set"
set = src.smembers(k)
if set.length == 0
# Empty set special case
dst.sadd(k,"")
dst.srem(k,"")
else
set.each{|ele|
dst.sadd(k,ele)
}
end
elsif vtype == "none"
puts "WARNING: key '#{k}' was removed in the meanwhile."
end
# Handle keys with an expire time set
if ttl != -1 and vtype != "none"
dst.expire(k,ttl)
end
c = c+1
if (c % 1000) == 0
puts "#{c}/#{keys.length} completed"
end
}
puts "DONE!"
end
if ARGV.length != 4
puts "Usage: redis-copy.rb <srchost> <srcport> <dsthost> <dstport>"
exit 1
end
puts "WARNING: it's up to you to FLUSHDB the destination host before to continue, press any key when ready."
STDIN.gets
srchost = ARGV[0]
srcport = ARGV[1]
dsthost = ARGV[2]
dstport = ARGV[3]
puts "Copying #{srchost}:#{srcport} into #{dsthost}:#{dstport}"
redisCopy(:srchost => srchost, :srcport => srcport.to_i,
:dsthost => dsthost, :dstport => dstport.to_i)
-52
View File
@@ -1,52 +0,0 @@
# redis-sha1.rb - Copyright (C) 2009 Salvatore Sanfilippo
# BSD license, See the COPYING file for more information.
#
# Performs the SHA1 sum of the whole datset.
# This is useful to spot bugs in persistence related code and to make sure
# Slaves and Masters are in SYNC.
#
# If you hack this code make sure to sort keys and set elements as this are
# unsorted elements. Otherwise the sum may differ with equal dataset.
require 'rubygems'
require 'redis'
require 'digest/sha1'
def redisSha1(opts={})
sha1=""
r = Redis.new(opts)
r.keys('*').sort.each{|k|
vtype = r.type?(k)
if vtype == "string"
len = 1
sha1 = Digest::SHA1.hexdigest(sha1+k)
sha1 = Digest::SHA1.hexdigest(sha1+r.get(k))
elsif vtype == "list"
len = r.llen(k)
if len != 0
sha1 = Digest::SHA1.hexdigest(sha1+k)
sha1 = Digest::SHA1.hexdigest(sha1+r.list_range(k,0,-1).join("\x01"))
end
elsif vtype == "set"
len = r.scard(k)
if len != 0
sha1 = Digest::SHA1.hexdigest(sha1+k)
sha1 = Digest::SHA1.hexdigest(sha1+r.set_members(k).to_a.sort.join("\x02"))
end
elsif vtype == "zset"
len = r.zcard(k)
if len != 0
sha1 = Digest::SHA1.hexdigest(sha1+k)
sha1 = Digest::SHA1.hexdigest(sha1+r.zrange(k,0,-1).join("\x01"))
end
end
# puts "#{k} => #{sha1}" if len != 0
}
sha1
end
host = ARGV[0] || "127.0.0.1"
port = ARGV[1] || "6379"
db = ARGV[2] || "0"
puts "Performing SHA1 of Redis server #{host} #{port} DB: #{db}"
p "Dataset SHA1: #{redisSha1(:host => host, :port => port.to_i, :db => db)}"
+7 -14
View File
@@ -1,11 +1,7 @@
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
REDISPORT=6379
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"
@@ -14,21 +10,21 @@ case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
echo -n "$PIDFILE exists, process is already running or crashed\n"
else
echo "Starting Redis server..."
echo -n "Starting Redis server...\n"
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
echo -n "$PIDFILE does not exist, process is not running\n"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
PID=$(cat $PIDFILE)
echo -n "Stopping ...\n"
echo -n "SHUTDOWN\r\n" | nc localhost $REDISPORT &
while [ -x /proc/${PIDFILE} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
@@ -36,7 +32,4 @@ case "$1" in
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
-18
View File
@@ -1,18 +0,0 @@
# This script is from http://poormansprofiler.org/
#!/bin/bash
nsamples=1
sleeptime=0
pid=$(pidof redis-server)
for x in $(seq 1 $nsamples)
do
gdb -ex "set pagination 0" -ex "thread apply all bt" -batch -p $pid
sleep $sleeptime
done | \
awk '
BEGIN { s = ""; }
/Thread/ { print s; s = ""; }
/^\#/ { if (s != "" ) { s = s "," $4} else { s = $4 } }
END { print s }' | \
sort | uniq -c | sort -r -n -k 1,1