Compare commits

...
168 Commits
Author SHA1 Message Date
John Sully f83a89f82a Acquire the lock for modules that don't acquire it before calling like they are supposed to 2019-08-29 15:41:03 -04:00
John Sully bf905a78f0 Bump version 2019-08-27 15:32:24 -04:00
John Sully fad9483fc9 Fix race condition in PUB/SUB and other async reply commands where the client can be freed before our handler is executed on the client thread. When this occurs the client pointer is dangling 2019-08-27 15:31:47 -04:00
John Sully 6b6e6f6c1e Fix crash in RediSearch 2019-07-29 15:08:57 -04:00
John Sully b0a0f03b96 RREPLAY failures should be logged 2019-07-24 22:49:55 -04:00
John Sully f1982ca63d Issue #64 RREPLAY isn't binary safe. Add fix and test. 2019-07-24 22:31:44 -04:00
John Sully 25e18a7de9 Release version 5 2019-07-18 20:50:40 -04:00
John Sully bb090b6d20 Active Replicas should always use their own slaveseldb variable when sending/saving rdbs - never those of their masters. This is because the Active Replica is also simultaneously a master 2019-07-18 20:50:20 -04:00
John Sully a9c4e3970f remove unnecessary variables 2019-07-18 19:37:49 -04:00
John Sully d5ecc2347a Fix issue #59 - Test replicaof config variable 2019-07-18 19:22:49 -04:00
John Sully 58971a430d Add support for sanitizers and fix issues they have found 2019-07-18 18:39:42 -04:00
John Sully 1512274f4c Resolve race on neterr variable 2019-07-18 17:11:28 -04:00
John Sully e310d33c12 Make object refcounts atomic, technically it doesn't need to be currently but the semantics are complicated and error prone. This is much safer with little performance impact 2019-07-18 15:49:12 -04:00
John Sully ad32ba0343 Suppress initialized data sanitizer warnings 2019-07-18 15:49:03 -04:00
John Sully 4e30a5b4e5 Another RESP3 regression 2019-07-17 22:57:52 -04:00
John Sully c8516f3dc4 Fix Issue #58 2019-07-17 02:34:02 -04:00
John Sully aae0fdc114 Shared objects should be marked as such 2019-07-16 18:32:01 -04:00
John Sully a19d0f67b9 Crash when aborting SYNC with a master on a thread other than main 2019-07-15 14:55:41 -04:00
John Sully 971f69fe1f Implement test mode to make finding bugs easier 2019-07-15 14:55:18 -04:00
John Sully 7468c691ad Blocked clients can stall when under low load 2019-07-13 16:44:11 -04:00
John Sully 620c2ec141 Give the active-rep more time to sync for test reliability 2019-07-12 23:52:07 -04:00
John Sully 3081b6f98b Fix more locking deadlocks 2019-07-12 23:51:45 -04:00
John Sully 8630339e43 Fix deadlock with client and ae locks 2019-07-12 20:46:50 -04:00
John Sully 528d10091f Add Active Replication tests 2019-07-12 03:54:41 -04:00
John Sully 149c3cf3dd FIX: The dabase count configuration is not respected 2019-07-12 02:13:37 -04:00
John Sully f12c3cf9dd Update README.md 2019-07-11 20:29:47 -04:00
John Sully 1f08a0efb3 Fix compile errors on GCC v5 2019-07-11 20:20:01 -04:00
John Sully 6cf85d1d01 Fix more accounting errors with active replication and the replication offset. Specifically we don't update repl_ack_off as frequently as we do reploff_skipped 2019-07-11 19:20:12 -04:00
John Sully b5101a460d Active Replicas are not slaves 2019-07-11 18:51:20 -04:00
John Sully a5784ef09e Fix active replication offset synchronization accounting, and enable the wait command 2019-07-11 17:00:23 -04:00
John Sully 122ec25bf0 lazyfree needs to consume more memory to be detectable 2019-07-06 00:53:40 -04:00
John Sully d013ad9eba Redis only does this in beforeSleep function - doing extra calls like we do causes test issues 2019-07-06 00:53:20 -04:00
John Sully d918ca6fa5 Add back user space buffering of RDB save 2019-07-06 00:36:23 -04:00
John Sully 852819180f We can use less memory in the client output buffers. Its OK 2019-07-05 23:43:01 -04:00
John Sully 218111eb72 Fix additional warning related to new() overloads 2019-07-05 23:42:39 -04:00
John Sully 1531cfb7ee Fix additional warnings 2019-06-27 16:29:36 -04:00
John Sully 16a89f21dc Fix mac build breaks 2019-06-27 15:04:09 -04:00
John Sully 79e5160e37 Fix libcxxabi clang compile failure in travis, as well as bool issue due to old clang 2019-06-18 00:42:01 -04:00
John Sully 510e966dc0 Bump travis compiler version 2019-06-18 00:42:01 -04:00
John Sully 8238d3207e Fix Travis CI issue 2019-06-17 23:56:23 -04:00
John Sully e37bc4ad40 Fix issues with relaxed memory model architectures 2019-06-17 23:00:12 -04:00
John Sully 0f78b8b917 Remove unnecessary recursive lock 2019-06-17 23:00:12 -04:00
John Sully 19061fe04f missing file :) 2019-06-17 23:00:12 -04:00
John Sully 9c901eb65f Fix test failures due to assert 2019-06-17 23:00:12 -04:00
John Sully ae33ff3a6e convert the pending write list to a vector for better performance and less cache thrashing 2019-06-17 23:00:12 -04:00
John Sully 59dbd625c1 Move more code out of the GIL 2019-06-17 23:00:12 -04:00
John Sully 8eb4e355b9 Remove an unnecessary lock that eats performance 2019-06-17 23:00:12 -04:00
John Sully 8b76b01bfe Even the next up thread should sleep in the futex 2019-06-17 23:00:12 -04:00
John Sully ec57b4b024 Fallback to a futex if we spin for a long time 2019-06-17 23:00:12 -04:00
John Sully 46d6caa47c Enable load balancing of UNIX sockets across threads 2019-06-11 01:34:36 -04:00
John Sully 97b65ce19c Fix crash adding duplicate replica. Just send an error instead 2019-06-02 15:50:50 -04:00
John Sully 55d0ef40b0 Merge branch 'unstable' of https://github.com/JohnSully/redis2 into unstable 2019-06-01 03:23:21 -04:00
John Sully a6c56416fc Implement MOTD feature in keydb-cli 2019-05-21 15:12:12 -04:00
John Sully cd3c482d5e Update README.md 2019-05-21 00:04:12 -04:00
John Sully c5cbb77fc7 Fix regressions from RESP3 changes in commit 317f8b9d38 2019-05-20 23:39:44 -04:00
John Sully d7f21f02b3 The unstable branch doesn't have a version # 2019-05-20 16:12:26 -04:00
John Sully 87dc8ce9b3 Fix double unlock 2019-05-20 15:52:04 -04:00
John Sully 2e22aa1a9c Merge branch 'unstable' of https://github.com/JohnSully/redis2 into unstable 2019-05-20 10:57:59 -04:00
John Sully 8c56dbf971 Protocol regression from RESP3 work 2019-05-20 10:57:37 -04:00
Qix a78169e9e0 cast void data pointer to const char * to fix gcc 2019-05-19 16:24:39 -04:00
John Sully 39a60c8120 Fix build breaks on some older compilers 2019-05-18 20:58:48 -04:00
John Sully c57d8760bd Fix build break on some compilers 2019-05-18 20:41:05 -04:00
John Sully 3eed2a8780 Merge branch 'unstable' of https://github.com/JohnSully/redis2 into unstable 2019-05-18 20:25:23 -04:00
John Sully 23b0268a7f Print nicer error messages for uncaught exceptions 2019-05-18 20:24:29 -04:00
John Sully 3d0de946bb Fix: Active replication falls behind under high load 2019-05-18 20:23:20 -04:00
John Sully 866b8ead91 Update Active Replication wiki links 2019-05-11 21:53:30 -04:00
John Sully aa274960ff Fix a few issues from the merge 2019-05-11 19:26:59 -04:00
John Sully 54d6eb851b Make lazy free test more reliable 2019-05-11 17:57:35 -04:00
John Sully 86d7276f24 Merge branch 'unstable' of https://github.com/antirez/redis into MergeRedis
Note: some tests failing
2019-05-11 02:20:34 -04:00
John Sully 0918651438 Add byline to logo 2019-05-10 23:48:14 -04:00
Salvatore Sanfilippo e633254ccf Merge pull request #6053 from soloestoy/enhance-aof-fsync-everysec
aof: enhance AOF_FSYNC_EVERYSEC, more details in #5985
2019-05-10 18:06:40 +02:00
Salvatore Sanfilippo 34f6d94701 Merge pull request #6074 from trevor211/fixActiveExpire
Do not active expire keys in the background when the switch is off.
2019-05-10 12:39:35 +02:00
Salvatore Sanfilippo 1e9b8d853d Merge pull request #6080 from soloestoy/fix-makefile
Makefile: 1TD -> STD
2019-05-10 12:37:07 +02:00
Salvatore Sanfilippo 2903c56bda Merge pull request #6073 from yossigo/fix/blocked-client-id
Preserve client->id for blocked clients.
2019-05-10 12:32:47 +02:00
Salvatore Sanfilippo 6b490e33c4 Merge pull request #6081 from Happy4Code/mod_comments
change the comments there may have a mis type
2019-05-10 12:28:12 +02:00
Salvatore Sanfilippo 891f4a38f6 Merge pull request #6077 from zltl/patch-7
delete sdsTest() from REDIS_TEST in server.c to fix build failed
2019-05-10 12:27:47 +02:00
Salvatore Sanfilippo 433f763814 Merge pull request #6083 from tangquanawj/fix-inappropriate-add-operation-code
fix-inappropriate-add-operation-code
2019-05-10 12:25:18 +02:00
Salvatore Sanfilippo 402b0d0bd1 Merge pull request #6086 from yongman/fix-uint64-in-defrag
Fix uint64_t hash value in active defrag
2019-05-10 12:23:59 +02:00
Salvatore Sanfilippo 19c3d5b743 Merge pull request #6087 from AngusP/linux-build-warnings
[Minor] Fix build warnings on Linux
2019-05-10 12:23:42 +02:00
Salvatore Sanfilippo 1212f22858 Merge pull request #6091 from soloestoy/fix-test-skip-till
test cases: skiptill -> skip-till
2019-05-10 12:22:19 +02:00
zhaozhao.zz c64aec9ce7 test cases: skiptill -> skip-till 2019-05-10 16:27:25 +08:00
John Sully 272d9297b6 Fix CentOS build failure 2019-05-09 19:54:34 -04:00
John Sully f5bfc71899 Merge branch 'unstable' of https://github.com/JohnSully/redis2 into unstable 2019-05-09 19:16:50 -04:00
John Sully 42e1ded55f build break fix 2019-05-09 19:16:31 -04:00
John Sully 6c410af5a8 Fix issue #11 2019-05-09 19:14:10 -04:00
John Sully 57ae676181 Fix typo 2019-05-09 19:06:58 -04:00
John Sully 7896ca6e52 Add missing file 2019-05-09 19:05:09 -04:00
John Sully c4a315dd5e Change debug messages to reference KeyDB 2019-05-09 19:03:50 -04:00
John Sully bf26959b72 Fix module locking issues 2019-05-09 19:00:27 -04:00
Angus Pearson a1fb0be1d7 Enlarge error buffer in redis-check-aof.c to remove compiler warning of output truncation through snprintf format string 2019-05-08 12:13:45 +01:00
Angus Pearson bea09a7fa6 Add <strings.h> include to deps/hiredis/read.c to fix Implicit Declaration of strcasecmp warning 2019-05-08 11:36:31 +01:00
yongman fb4ee7f0c5 Fix uint64_t hash value in active defrag 2019-05-08 16:29:16 +08:00
唐权 842dd85b26 Update ziplist.c
Hi, @antirez

In the code, to get the size of ziplist, "unsigned int bytes = ZIPLIST_HEADER_SIZE+1;" is correct, 
but why not make it more readable and easy to understand
2019-05-08 12:53:56 +08:00
Salvatore Sanfilippo f674e83215 Merge pull request #6082 from soloestoy/fix-config-rewrite-memleak
fix memory leak when rewrite config file
2019-05-07 10:25:13 +02:00
zhaozhao.zz 48d591a010 fix memory leak when rewrite config file 2019-05-07 15:59:16 +08:00
stan011 d9d3d3065b change the comments there may have a mis type 2019-05-07 14:22:40 +08:00
zhaozhao.zz 340a723b87 Makefile: 1TD -> STD 2019-05-07 13:35:27 +08:00
Salvatore Sanfilippo a72ab70fee Merge pull request #6070 from oranagra/stabilize_repl_tests
make replication tests more stable on slow machines
2019-05-06 18:05:33 +02:00
antirez 3d053dbb6d Threaded IO: handleClientsWithPendingReadsUsingThreads top comment. 2019-05-06 18:02:51 +02:00
antirez 5baeb14cf3 Threaded IO: configuration directive for turning on/off reads. 2019-05-06 18:02:51 +02:00
antirez 1c0c436757 Threaded IO: ability to disable reads from threaded path. 2019-05-06 18:02:51 +02:00
antirez 90d720e7a5 Threaded IO: put fflush() inside tio_debug conditional. 2019-05-06 18:02:51 +02:00
antirez 6ab6a97fe6 Threaded IO: parsing WIP 2: refactoring to parse from thread. 2019-05-06 18:02:51 +02:00
antirez 647a66ebba Threaded IO: parsing WIP 1: set current_client in a better scoped way. 2019-05-06 18:02:51 +02:00
antirez 463ccf8664 Threaded IO: logging should be safe in I/O threads.
Potentially it is possible that we get interleaved writes, even if
serverLog() makes sure to write into a buffer and then use printf(), so
even this should be ok. However in general POSIX guarantees that writing
to the same file pointer object from multiple threads is safe. Anyway
currently we *reopen* the file at each call, but for the standard output
logging.

The logging functions actually also access global configuration while
performing the log (for instance in order to check the log level, the
log filename and so forth), however dunring the I/O threads execution
we cannot alter such shared state in any way.
2019-05-06 18:02:51 +02:00
antirez 8d7d2be24f Threaded IO: process read queue before stopping threads. 2019-05-06 18:02:51 +02:00
antirez 63a0ffd36a Threaded IO: read side WIP 3. 2019-05-06 18:02:51 +02:00
antirez a2245f8ff1 Threaded IO: read side WIP 2. 2019-05-06 18:02:51 +02:00
antirez dd5b105c73 Threaded IO: read side WIP. 2019-05-06 18:02:51 +02:00
antirez 74591fb5bd Threaded IO: hide more debugging printfs under conditional. 2019-05-06 18:02:51 +02:00
antirez 9814b2a5f3 Threaded IO: make num of I/O threads configurable. 2019-05-06 18:02:51 +02:00
antirez 30091dc29f Threaded IO: use main thread if num of threads is 1. 2019-05-06 18:02:51 +02:00
Ubuntu 9bf7f302a7 Threaded IO: stop threads when no longer needed + C11 in Makefile.
Now threads are stopped even when the connections drop immediately to
zero, not allowing the networking code to detect the condition and stop
the threads. serverCron() will handle that.
2019-05-06 18:02:51 +02:00
antirez ea35a81c42 Threaded IO: 3rd version: use the mutex only to stop the thread. 2019-05-06 18:02:51 +02:00
antirez 6f4f36c0fb Threaded IO: second attempt without signaling conditions. 2019-05-06 18:02:51 +02:00
antirez a2dbd9bd97 Threaded IO: allow to disable debug printf. 2019-05-06 18:02:51 +02:00
antirez f468e653b5 Threaded IO: implement handleClientsWithPendingWritesUsingThreads().
This is just an experiment for now, there are a couple of race
conditions, mostly harmless for the performance gain experiment that
this commit represents so far.

The general idea here is to take Redis single threaded and instead
fan-out on expansive kernel calls: write(2) in this case, but the same
concept could be easily implemented for read(2) and protcol parsing.

However just threading writes like in this commit, is enough to evaluate
if the approach is sounding.
2019-05-06 18:02:51 +02:00
liaotonglang b1c7e3393d delete sdsTest() from REDIS_TEST
sdsTest() defined in sds.c dit not match the call in server.c.
remove it from REDIS_TEST, since test-sds defined in Makefile.
2019-05-06 19:24:00 +08:00
WuYunlong 1c0913dc4e Do not active expire keys in the background when the switch is off. 2019-05-06 11:46:07 +08:00
Yossi Gottlieb 9f3679880a Preserve client->id for blocked clients. 2019-05-05 20:32:53 +03:00
Oran Agra ba809f26d4 make replication tests more stable on slow machines
solving few replication related tests race conditions which fail on slow machines

bugfix in slave buffers test: since the test is executed twice, each time with
a different commands count, the threshold for the delta can't be a constant.
2019-05-05 08:25:01 +03:00
Salvatore Sanfilippo 0a6090bfd8 Merge pull request #6051 from vattezhang/unstable
fix: benchmark auth fails when server have requirepass
2019-05-02 09:48:02 +02:00
zhaozhao.zz bcac165fab aof: enhance AOF_FSYNC_EVERYSEC, more details in #5985 2019-04-29 14:38:28 +08:00
Salvatore Sanfilippo 843de8b786 Merge pull request #5971 from devnexen/unstable
build fix
2019-04-26 17:32:21 +02:00
Salvatore Sanfilippo 5ba908fa5f Merge pull request #6002 from yongman/fix-memleak-in-bitfield
Fix memleak in bitfieldCommand
2019-04-26 17:31:42 +02:00
Salvatore Sanfilippo 5c5197fe4f Merge pull request #6013 from rouzier/fix/stream_iterator_init
Fix stream interator start and end key initialize
2019-04-26 17:25:20 +02:00
Salvatore Sanfilippo 2d450d1315 Merge pull request #6039 from git-hulk/fix/benchmark-coredump-in-arguments
FIX: coredump in redis-benchmark
2019-04-26 17:17:55 +02:00
abhay 162208f94d removed obsolete warning as per - https://github.com/antirez/redis/issues/5291 2019-04-26 17:03:16 +02:00
vattezhang 4e38ced488 fix: benchmark auth fails when server have requirepass 2019-04-26 19:47:07 +08:00
vattezhang bc36404c79 update 2019-04-26 18:50:51 +08:00
vattezhang d3d96b041a Merge github.com:antirez/redis into unstable 2019-04-26 18:47:11 +08:00
git-hulk fa97ef34ca FIX: core dump in redis-benchmark when the -r is the last arg 2019-04-24 20:05:35 +08:00
John Sully 37b2d046be refactor server global into a pointer (so that threadsafe commands can set this to NULL to prove they don't rely on it 2019-04-21 14:01:10 -04:00
John Sully fa2e407873 Factor out some normally const globals 2019-04-21 13:20:45 -04:00
John Sully c61cf57db5 additional fix to rreplay forward compat 2019-04-20 01:36:23 -04:00
John Sully ef3690f2db Make rreplay forward compatible with extra future args 2019-04-20 01:15:01 -04:00
John Sully 14d01ec7bd Tweak mvcc time format so rollovers are not an issue 2019-04-20 01:03:08 -04:00
John Sully 5dc9f70386 Timestamp writes to objects so we can be smarter about merging databases 2019-04-20 00:52:07 -04:00
John Sully 88e593bf96 Fix use uninitialized 2019-04-19 23:41:40 -04:00
John Sully efde2e6be6 Additional MVCC work and fix memory leak loading objects from rdb 2019-04-19 22:54:42 -04:00
John Sully 5a78a3bc46 Add version override command for users that need compatibility with Redis 2019-04-19 19:57:11 -04:00
John Sully b276d803d4 RREPLAY PERF: The UUID stack is unnecessary, RREPLAY itself can detect the cycle and call cancel() 2019-04-19 19:36:47 -04:00
John Sully da51ac430b Detect cycles in mesh networks with multi master 2019-04-19 19:31:11 -04:00
John Sully 01eb0a3c7f Fix crash near boot when adding replicas and dumping database 2019-04-19 17:08:54 -04:00
John Sully dc2b3cf4f8 Replication fake connection should inhereit the user and permission of the real connection 2019-04-18 22:07:35 -04:00
John Sully c4621a5ed2 Start of MVCC support (and more C++) 2019-04-16 23:16:03 -04:00
John Sully 1c953a4d5b Fix module linking and crashing issues 2019-04-16 13:34:22 -04:00
John Sully c64ce7aa9c Fix replica authentication failure when masterauth is below replicaof in the config file 2019-04-15 22:31:02 -04:00
antirez 4a5057000a Merge branch 'unstable' of github.com:/antirez/redis into unstable 2019-04-15 16:50:48 +02:00
antirez 487601d85d Test: disable module testing for now. 2019-04-15 16:50:26 +02:00
John Sully acdc4fdfec Fix memory leak unsetting master 2019-04-11 19:55:44 -04:00
James Rouzier 3ccdcbc088 Fix start and end key initialize 2019-04-11 12:19:02 -04:00
Salvatore Sanfilippo d044e33c20 Merge pull request #5961 from yossigo/modules-tests
Modules tests
2019-04-11 07:56:23 -07:00
antirez 9e67691ffb Aesthetic change to #5962 to conform to Redis style. 2019-04-10 18:53:35 +02:00
Salvatore Sanfilippo 6de5d25062 Merge pull request #5962 from oranagra/module_blocked_reply
slave corrupts replication stream when module blocked client uses large reply (or POSTPONED_ARRAY)
2019-04-10 09:41:36 -07:00
yongman d490752d58 Fix memleak in bitfieldCommand 2019-04-09 09:24:22 +08:00
antirez c24e32041b ACL: Fix memory leak in ACLResetSubcommandsForCommand().
This commit fixes bug reported at #5998. Thanks to @tomcat1102.
2019-04-08 18:08:37 +02:00
antirez f8a9708aa7 ACL: regression test for #5998. 2019-04-08 18:06:50 +02:00
antirez 75648f99a5 Fix assert comparison in fetchClusterSlotsConfiguration(). 2019-04-08 17:39:26 +02:00
David Carlier 4de88828d9 build fix 2019-03-28 06:38:16 +00:00
Oran Agra acba2fc9b4 slave corrupts replication stream when module blocked client uses large reply (or POSTPONED_ARRAY)
when redis appends the blocked client reply list to the real client, it didn't
bother to check if it is in fact the master client. so a slave executing that
module command will send replies to the master, causing the master to send the
slave error responses, which will mess up the replication offset
(slave will advance it's replication offset, and the master does not)
2019-03-24 14:17:37 +02:00
Yossi Gottlieb ec0b6bd2c3 Add runtest-moduleapi with commandfilter coverage. 2019-03-24 12:03:03 +02:00
Yossi Gottlieb 822a992f91 fix: missing initialization. 2019-03-24 12:00:33 +02:00
vattezhang ad223e2042 fix: fix benchmark cannot exit when NOAUTH err happens 2019-03-13 20:46:33 +08:00
vattezhang 31110f1caf Merge branch 'unstable' of github.com:antirez/redis into unstable 2019-03-13 20:26:54 +08:00
vattezhang 270a111433 Merge branch 'unstable' of github.com:antirez/redis into unstable 2019-03-12 21:52:20 +08:00
vattezhang 0f0f787a37 fix: fix sentinel command table and new flags format 2019-02-27 21:20:00 +08:00
vattezhang 4cc43a96f6 benchmark: add auth check in benchmark
When we run benchmark but forget to set the right requirepass, benchmark should return error.
2019-02-18 22:48:55 +08:00
88 changed files with 5282 additions and 4035 deletions

No files matched your search

+4 -3
View File
@@ -4,11 +4,11 @@ matrix:
include:
- os: linux
script: make
env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
env: COMPILER_NAME=g++-6 CXX=g++-6 CC=gcc-6
addons:
apt:
packages:
- g++-5
- g++-6
- nasm
- uuid-dev
sources: &sources
@@ -16,10 +16,11 @@ matrix:
- ubuntu-toolchain-r-test
- os: linux
script: make MALLOC=libc
env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-stdlib=libc++"
env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 CXXFLAGS="-I/usr/include/libcxxabi/" LDFLAGS="-lc++"
addons:
apt:
packages:
- libc++abi-dev
- clang-3.8
- libc++-dev
- libc++abi-dev
-56
View File
@@ -1,56 +0,0 @@
{
"files.associations": {
"zmalloc.h": "c",
"stat.h": "c",
"array": "cpp",
"atomic": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"scoped_allocator": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"utility": "cpp"
}
}
+6 -4
View File
@@ -1,10 +1,11 @@
![Current Release](https://img.shields.io/github/release/JohnSully/KeyDB.svg)
[![Build Status](https://travis-ci.org/JohnSully/KeyDB.svg?branch=unstable)](https://travis-ci.org/JohnSully/KeyDB) [![Join the chat at https://gitter.im/KeyDB/community](https://badges.gitter.im/KeyDB/community.svg)](https://gitter.im/KeyDB/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![StackShare](http://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](https://stackshare.io/eq-alpha-technology-inc/eq-alpha-technology-inc)
What is KeyDB?
--------------
KeyDB is a high performance fork of Redis focusing on multithreading, memory efficiency, and high throughput. In addition to multithreading KeyDB also has features only available in Redis Enterprise such as [Active Replication](https://github.com/JohnSully/KeyDB/wiki/KeyDB-(Redis-Fork):-Active-Replica-Support), [FLASH storage](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) support, and some not available at all such as direct backup to AWS S3.
KeyDB is a high performance fork of Redis focusing on multithreading, memory efficiency, and high throughput. In addition to multithreading KeyDB also has features only available in Redis Enterprise such as [Active Replication](https://github.com/JohnSully/KeyDB/wiki/Active-Replication), [FLASH storage](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) support, and some not available at all such as direct backup to AWS S3.
On the same hardware KeyDB can perform twice as many queries per second as Redis, with 60% lower latency.
@@ -14,12 +15,14 @@ Try our docker container: https://hub.docker.com/r/eqalpha/keydb
Talk on Gitter: https://gitter.im/KeyDB
[Subscribe to the KeyDB mailing list](https://eqalpha.us20.list-manage.com/subscribe/post?u=978f486c2f95589b24591a9cc&id=4ab9220500)
Management GUI: We recommend [FastoNoSQL](https://fastonosql.com/) which has official KeyDB support.
New: Active Replica Support
---------------------------
New! KeyDB now has support for Active Replicas. This feature greatly simplifies hot-spare failover and allows you to distribute writes over replicas instead of just a single master. For more information [see the wiki page](https://github.com/JohnSully/KeyDB/wiki/KeyDB-(Redis-Fork):-Active-Replica-Support).
New! KeyDB now has support for Active Replicas. This feature greatly simplifies hot-spare failover and allows you to distribute writes over replicas instead of just a single master. For more information [see the wiki page](https://github.com/JohnSully/KeyDB/wiki/Active-Replication).
Why fork Redis?
---------------
@@ -60,7 +63,7 @@ All other configuration options behave as you'd expect. Your existing configura
Building KeyDB
--------------
KeyDB can be compiled and is tested for use on Linux. KeyDB currently relies on SO_REUSEADDR's load balancing behavior which is available only in Linux. When we support marshalling connections across threads we plan to support other operating systems such as FreeBSD.
KeyDB can be compiled and is tested for use on Linux. KeyDB currently relies on SO_REUSEPORT's load balancing behavior which is available only in Linux. When we support marshalling connections across threads we plan to support other operating systems such as FreeBSD.
Install dependencies:
@@ -249,4 +252,3 @@ source distribution.
Please see the CONTRIBUTING file in this source distribution for more
information.
+1
View File
@@ -31,6 +31,7 @@
#include "fmacros.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h>
+1 -7
View File
@@ -942,13 +942,7 @@ aof-use-rdb-preamble yes
lua-time-limit 5000
################################ REDIS CLUSTER ###############################
#
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however
# in order to mark it as "mature" we need to wait for a non trivial percentage
# of users to deploy it in production.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# Normal Redis instances can't be part of a Redis Cluster; only nodes that are
# started as cluster nodes can. In order to start a Redis instance as a
# cluster node enable the cluster support uncommenting the following:
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
TCL_VERSIONS="8.5 8.6"
TCLSH=""
for VERSION in $TCL_VERSIONS; do
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
done
if [ -z $TCLSH ]
then
echo "You need tcl 8.5 or newer in order to run the Redis test"
exit 1
fi
make -C tests/modules && \
$TCLSH tests/test_helper.tcl --single unit/moduleapi/commandfilter "${@}"
+20 -8
View File
@@ -20,8 +20,8 @@ DEPENDENCY_TARGETS=hiredis linenoise lua
NODEPS:=clean distclean
# Default settings
STD=-std=c99 -pedantic -DREDIS_STATIC=''
CXX_STD=-std=c++14 -pedantic -fno-rtti
STD=-std=c11 -pedantic -DREDIS_STATIC=''
CXX_STD=-std=c++14 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
ifneq (,$(findstring clang,$(CC)))
ifneq (,$(findstring FreeBSD,$(uname_S)))
STD+=-Wno-c11-extensions
@@ -45,6 +45,17 @@ endif
endif
USEASM?=true
ifneq ($(SANITIZE),)
CC=clang
CXX=clang++
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
CXXFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
LDFLAGS+= -fsanitize=$(SANITIZE)
MALLOC=libc
USEASM=false
endif
# Do we use our assembly spinlock? X64 only
ifeq ($(uname_S),Linux)
ifeq ($(uname_M),x86_64)
@@ -140,6 +151,7 @@ else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+=-ldl -pthread -lrt -luuid
FINAL_CFLAGS += -DMOTD
endif
endif
endif
@@ -197,11 +209,11 @@ endif
REDIS_SERVER_NAME=keydb-server
REDIS_SENTINEL_NAME=keydb-sentinel
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o $(ASM_OBJ)
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o $(ASM_OBJ)
REDIS_CLI_NAME=keydb-cli
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o $(ASM_OBJ)
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o $(ASM_OBJ)
REDIS_BENCHMARK_NAME=keydb-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o storage-lite.o fastlock.o $(ASM_OBJ)
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o storage-lite.o fastlock.o new.o $(ASM_OBJ)
REDIS_CHECK_RDB_NAME=keydb-check-rdb
REDIS_CHECK_AOF_NAME=keydb-check-aof
@@ -264,13 +276,13 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)
# keydb-cli
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) -lcurl
# keydb-benchmark
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
dict-benchmark: dict.cpp zmalloc.c sds.c siphash.c
dict-benchmark: dict.cpp zmalloc.cpp sds.c siphash.c
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
# Because the jemalloc.h header is generated as a part of the jemalloc build,
@@ -312,7 +324,7 @@ lcov:
@genhtml --legend -o lcov-html redis.info
test-sds: sds.c sds.h
$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
$(REDIS_CC) sds.c zmalloc.cpp -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
/tmp/sds_test
.PHONY: lcov
+18 -16
View File
@@ -161,7 +161,7 @@ int ACLListMatchSds(void *a, void *b) {
}
/* Method to free list elements from ACL users password/ptterns lists. */
void ACLListFreeSds(void *item) {
void ACLListFreeSds(const void *item) {
sdsfree((sds)item);
}
@@ -227,7 +227,7 @@ void ACLFreeUser(user *u) {
void ACLFreeUserAndKillClients(user *u) {
listIter li;
listNode *ln;
listRewind(server.clients,&li);
listRewind(g_pserver->clients,&li);
while ((ln = listNext(&li)) != NULL) {
client *c = (client*)listNodeValue(ln);
if (c->puser == u) {
@@ -334,7 +334,7 @@ void ACLSetUserCommandBit(user *u, unsigned long id, int value) {
int ACLSetUserCommandBitsForCategory(user *u, const char *category, int value) {
uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
if (!cflag) return C_ERR;
dictIterator *di = dictGetIterator(server.orig_commands);
dictIterator *di = dictGetIterator(g_pserver->orig_commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct redisCommand *cmd = (redisCommand*)dictGetVal(de);
@@ -359,7 +359,7 @@ int ACLCountCategoryBitsForUser(user *u, unsigned long *on, unsigned long *off,
if (!cflag) return C_ERR;
*on = *off = 0;
dictIterator *di = dictGetIterator(server.orig_commands);
dictIterator *di = dictGetIterator(g_pserver->orig_commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct redisCommand *cmd = (redisCommand*)dictGetVal(de);
@@ -428,7 +428,7 @@ sds ACLDescribeUserCommandRules(user *u) {
}
/* Fix the final ACLs with single commands differences. */
dictIterator *di = dictGetIterator(server.orig_commands);
dictIterator *di = dictGetIterator(g_pserver->orig_commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct redisCommand *cmd = (redisCommand*)dictGetVal(de);
@@ -533,7 +533,7 @@ sds ACLDescribeUser(user *u) {
struct redisCommand *ACLLookupCommand(const char *name) {
struct redisCommand *cmd;
sds sdsname = sdsnew(name);
cmd = (redisCommand*)dictFetchValue(server.orig_commands, sdsname);
cmd = (redisCommand*)dictFetchValue(g_pserver->orig_commands, sdsname);
sdsfree(sdsname);
return cmd;
}
@@ -542,6 +542,8 @@ struct redisCommand *ACLLookupCommand(const char *name) {
* and command ID. */
void ACLResetSubcommandsForCommand(user *u, unsigned long id) {
if (u->allowed_subcommands && u->allowed_subcommands[id]) {
for (int i = 0; u->allowed_subcommands[id][i]; i++)
sdsfree(u->allowed_subcommands[id][i]);
zfree(u->allowed_subcommands[id]);
u->allowed_subcommands[id] = NULL;
}
@@ -1194,7 +1196,7 @@ sds ACLLoadFromFile(const char *filename) {
if (argv == NULL) {
errors = sdscatprintf(errors,
"%s:%d: unbalanced quotes in acl line. ",
server.acl_filename, linenum);
g_pserver->acl_filename, linenum);
continue;
}
@@ -1208,7 +1210,7 @@ sds ACLLoadFromFile(const char *filename) {
if (strcmp(argv[0],"user") || argc < 2) {
errors = sdscatprintf(errors,
"%s:%d should start with user keyword followed "
"by the username. ", server.acl_filename,
"by the username. ", g_pserver->acl_filename,
linenum);
sdsfreesplitres(argv,argc);
continue;
@@ -1223,7 +1225,7 @@ sds ACLLoadFromFile(const char *filename) {
const char *errmsg = ACLSetUserStringError();
errors = sdscatprintf(errors,
"%s:%d: %s. ",
server.acl_filename, linenum, errmsg);
g_pserver->acl_filename, linenum, errmsg);
continue;
}
}
@@ -1349,7 +1351,7 @@ cleanup:
* The function will just exit with an error if the user is trying to mix
* both the loading methods. */
void ACLLoadUsersAtStartup(void) {
if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
if (g_pserver->acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
serverLog(LL_WARNING,
"Configuring Redis with users defined in redis.conf and at "
"the same setting an ACL file path is invalid. This setup "
@@ -1365,8 +1367,8 @@ void ACLLoadUsersAtStartup(void) {
exit(1);
}
if (server.acl_filename[0] != '\0') {
sds errors = ACLLoadFromFile(server.acl_filename);
if (g_pserver->acl_filename[0] != '\0') {
sds errors = ACLLoadFromFile(g_pserver->acl_filename);
if (errors) {
serverLog(LL_WARNING,
"Aborting Redis startup because of ACL errors: %s", errors);
@@ -1526,13 +1528,13 @@ void aclCommand(client *c) {
} else {
addReplyNull(c);
}
} else if (server.acl_filename[0] == '\0' &&
} else if (g_pserver->acl_filename[0] == '\0' &&
(!strcasecmp(sub,"load") || !strcasecmp(sub,"save")))
{
addReplyError(c,"This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.");
return;
} else if (!strcasecmp(sub,"load") && c->argc == 2) {
sds errors = ACLLoadFromFile(server.acl_filename);
sds errors = ACLLoadFromFile(g_pserver->acl_filename);
if (errors == NULL) {
addReply(c,shared.ok);
} else {
@@ -1540,7 +1542,7 @@ void aclCommand(client *c) {
sdsfree(errors);
}
} else if (!strcasecmp(sub,"save") && c->argc == 2) {
if (ACLSaveToFile(server.acl_filename) == C_OK) {
if (ACLSaveToFile(g_pserver->acl_filename) == C_OK) {
addReply(c,shared.ok);
} else {
addReplyError(c,"There was an error trying to save the ACLs. "
@@ -1561,7 +1563,7 @@ void aclCommand(client *c) {
}
int arraylen = 0;
void *dl = addReplyDeferredLen(c);
dictIterator *di = dictGetIterator(server.orig_commands);
dictIterator *di = dictGetIterator(g_pserver->orig_commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct redisCommand *cmd = (redisCommand*)dictGetVal(de);
+1 -1
View File
@@ -52,7 +52,7 @@ typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
void (*free)(const void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
+40 -11
View File
@@ -46,10 +46,8 @@
#include "ae.h"
#include "fastlock.h"
extern "C" {
#include "zmalloc.h"
#include "config.h"
}
#ifdef USE_MUTEX
thread_local int cOwnLock = 0;
@@ -193,6 +191,36 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
}
}
// Unlike write() this is an all or nothing thing. We will block if a partial write is hit
ssize_t safe_write(int fd, const void *pv, size_t cb)
{
const char *pcb = (const char*)pv;
ssize_t written = 0;
do
{
ssize_t rval = write(fd, pcb, cb);
if (rval > 0)
{
pcb += rval;
cb -= rval;
written += rval;
}
else if (errno == EAGAIN)
{
if (written == 0)
break;
// if we've already written something then we're committed so keep trying
}
else
{
if (rval == 0)
return written;
return rval;
}
} while (cb);
return written;
}
int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData, int fSynchronous)
{
@@ -201,7 +229,7 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
int ret = AE_OK;
aeCommand cmd;
aeCommand cmd = {};
cmd.op = AE_ASYNC_OP::CreateFileEvent;
cmd.fd = fd;
cmd.mask = mask;
@@ -209,14 +237,15 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
cmd.clientData = clientData;
cmd.pctl = nullptr;
if (fSynchronous)
cmd.pctl = new aeCommandControl();
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
if (fSynchronous)
cmd.pctl->mutexcv.lock();
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
auto size = safe_write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
if (size != sizeof(cmd))
{
AE_ASSERT(size == sizeof(cmd) || size <= 0);
AE_ASSERT(errno == EAGAIN);
ret = AE_ERR;
}
@@ -238,7 +267,7 @@ int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg)
proc(arg);
return AE_OK;
}
aeCommand cmd;
aeCommand cmd = {};
cmd.op = AE_ASYNC_OP::PostFunction;
cmd.proc = proc;
cmd.clientData = arg;
@@ -255,12 +284,12 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynch
return AE_OK;
}
aeCommand cmd;
aeCommand cmd = {};
cmd.op = AE_ASYNC_OP::PostCppFunction;
cmd.pfn = new std::function<void()>(fn);
cmd.pfn = new (MALLOC_LOCAL) std::function<void()>(fn);
cmd.pctl = nullptr;
if (fSynchronous)
cmd.pctl = new aeCommandControl();
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
if (fSynchronous)
cmd.pctl->mutexcv.lock();
@@ -805,9 +834,9 @@ void aeAcquireLock()
g_lock.lock();
}
int aeTryAcquireLock()
int aeTryAcquireLock(int fWeak)
{
return g_lock.try_lock();
return g_lock.try_lock(!!fWeak);
}
void aeReleaseLock()
+1 -1
View File
@@ -159,7 +159,7 @@ int aeGetSetSize(aeEventLoop *eventLoop);
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
void aeAcquireLock();
int aeTryAcquireLock();
int aeTryAcquireLock(int fWeak);
void aeReleaseLock();
int aeThreadOwnsLock();
+69
View File
@@ -0,0 +1,69 @@
#pragma once
class AeLocker
{
bool m_fArmed = false;
public:
AeLocker()
{
}
void arm(client *c) // if a client is passed, then the client is already locked
{
if (c != nullptr)
{
serverAssert(!m_fArmed);
serverAssert(c->lock.fOwnLock());
if (!aeTryAcquireLock(true /*fWeak*/)) // avoid locking the client if we can
{
bool fOwnClientLock = true;
int clientNesting = 1;
for (;;)
{
if (fOwnClientLock)
{
clientNesting = c->lock.unlock_recursive();
fOwnClientLock = false;
}
aeAcquireLock();
if (!c->lock.try_lock(false)) // ensure a strong try because aeAcquireLock is expensive
{
aeReleaseLock();
}
else
{
break;
}
}
c->lock.lock_recursive(clientNesting);
}
m_fArmed = true;
}
else if (!m_fArmed)
{
m_fArmed = true;
aeAcquireLock();
}
}
void disarm()
{
serverAssert(m_fArmed);
m_fArmed = false;
aeReleaseLock();
}
bool isArmed() const
{
return m_fArmed;
}
~AeLocker()
{
if (m_fArmed)
aeReleaseLock();
}
};
+202 -175
View File
@@ -65,14 +65,14 @@ typedef struct aofrwblock {
} aofrwblock;
/* This function free the old AOF rewrite buffer if needed, and initialize
* a fresh new one. It tests for server.aof_rewrite_buf_blocks equal to NULL
* a fresh new one. It tests for g_pserver->aof_rewrite_buf_blocks equal to NULL
* so can be used for the first initialization as well. */
void aofRewriteBufferReset(void) {
if (server.aof_rewrite_buf_blocks)
listRelease(server.aof_rewrite_buf_blocks);
if (g_pserver->aof_rewrite_buf_blocks)
listRelease(g_pserver->aof_rewrite_buf_blocks);
server.aof_rewrite_buf_blocks = listCreate();
listSetFreeMethod(server.aof_rewrite_buf_blocks,zfree);
g_pserver->aof_rewrite_buf_blocks = listCreate();
listSetFreeMethod(g_pserver->aof_rewrite_buf_blocks,zfree);
}
/* Return the current size of the AOF rewrite buffer. */
@@ -81,7 +81,7 @@ unsigned long aofRewriteBufferSize(void) {
listIter li;
unsigned long size = 0;
listRewind(server.aof_rewrite_buf_blocks,&li);
listRewind(g_pserver->aof_rewrite_buf_blocks,&li);
while((ln = listNext(&li))) {
aofrwblock *block = (aofrwblock*)listNodeValue(ln);
size += block->used;
@@ -104,28 +104,28 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {
UNUSED(mask);
while(1) {
ln = listFirst(server.aof_rewrite_buf_blocks);
ln = listFirst(g_pserver->aof_rewrite_buf_blocks);
block = (aofrwblock*)(ln ? ln->value : NULL);
if (server.aof_stop_sending_diff || !block) {
aeDeleteFileEvent(el,server.aof_pipe_write_data_to_child,
if (g_pserver->aof_stop_sending_diff || !block) {
aeDeleteFileEvent(el,g_pserver->aof_pipe_write_data_to_child,
AE_WRITABLE);
return;
}
if (block->used > 0) {
nwritten = write(server.aof_pipe_write_data_to_child,
nwritten = write(g_pserver->aof_pipe_write_data_to_child,
block->buf,block->used);
if (nwritten <= 0) return;
memmove(block->buf,block->buf+nwritten,block->used-nwritten);
block->used -= nwritten;
block->free += nwritten;
}
if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln);
if (block->used == 0) listDelNode(g_pserver->aof_rewrite_buf_blocks,ln);
}
}
/* Append data to the AOF rewrite buffer, allocating new blocks if needed. */
void aofRewriteBufferAppend(unsigned char *s, unsigned long len) {
listNode *ln = listLast(server.aof_rewrite_buf_blocks);
listNode *ln = listLast(g_pserver->aof_rewrite_buf_blocks);
aofrwblock *block = (aofrwblock*)(ln ? ln->value : NULL);
while(len) {
@@ -148,11 +148,11 @@ void aofRewriteBufferAppend(unsigned char *s, unsigned long len) {
block = (aofrwblock*)zmalloc(sizeof(*block), MALLOC_LOCAL);
block->free = AOF_RW_BUF_BLOCK_SIZE;
block->used = 0;
listAddNodeTail(server.aof_rewrite_buf_blocks,block);
listAddNodeTail(g_pserver->aof_rewrite_buf_blocks,block);
/* Log every time we cross more 10 or 100 blocks, respectively
* as a notice or warning. */
numblocks = listLength(server.aof_rewrite_buf_blocks);
numblocks = listLength(g_pserver->aof_rewrite_buf_blocks);
if (((numblocks+1) % 10) == 0) {
int level = ((numblocks+1) % 100) == 0 ? LL_WARNING :
LL_NOTICE;
@@ -164,8 +164,8 @@ void aofRewriteBufferAppend(unsigned char *s, unsigned long len) {
/* Install a file event to send data to the rewrite child if there is
* not one already. */
if (aeGetFileEvents(serverTL->el,server.aof_pipe_write_data_to_child) == 0) {
aeCreateFileEvent(serverTL->el, server.aof_pipe_write_data_to_child,
if (aeGetFileEvents(serverTL->el,g_pserver->aof_pipe_write_data_to_child) == 0) {
aeCreateFileEvent(serverTL->el, g_pserver->aof_pipe_write_data_to_child,
AE_WRITABLE, aofChildWriteDiffData, NULL);
}
}
@@ -178,7 +178,7 @@ ssize_t aofRewriteBufferWrite(int fd) {
listIter li;
ssize_t count = 0;
listRewind(server.aof_rewrite_buf_blocks,&li);
listRewind(g_pserver->aof_rewrite_buf_blocks,&li);
while((ln = listNext(&li))) {
aofrwblock *block = (aofrwblock*)listNodeValue(ln);
ssize_t nwritten;
@@ -199,6 +199,12 @@ ssize_t aofRewriteBufferWrite(int fd) {
* AOF file implementation
* ------------------------------------------------------------------------- */
/* Return true if an AOf fsync is currently already in progress in a
* BIO thread. */
int aofFsyncInProgress(void) {
return bioPendingJobsOfType(BIO_AOF_FSYNC) != 0;
}
/* Starts a background task that performs fsync() against the specified
* file descriptor (the one of the AOF file) in another thread. */
void aof_background_fsync(int fd) {
@@ -209,18 +215,18 @@ void aof_background_fsync(int fd) {
void killAppendOnlyChild(void) {
int statloc;
/* No AOFRW child? return. */
if (server.aof_child_pid == -1) return;
if (g_pserver->aof_child_pid == -1) return;
/* Kill AOFRW child, wait for child exit. */
serverLog(LL_NOTICE,"Killing running AOF rewrite child: %ld",
(long) server.aof_child_pid);
if (kill(server.aof_child_pid,SIGUSR1) != -1) {
while(wait3(&statloc,0,NULL) != server.aof_child_pid);
(long) g_pserver->aof_child_pid);
if (kill(g_pserver->aof_child_pid,SIGUSR1) != -1) {
while(wait3(&statloc,0,NULL) != g_pserver->aof_child_pid);
}
/* Reset the buffer accumulating changes while the child saves. */
aofRewriteBufferReset();
aofRemoveTempFile(server.aof_child_pid);
server.aof_child_pid = -1;
server.aof_rewrite_time_start = -1;
aofRemoveTempFile(g_pserver->aof_child_pid);
g_pserver->aof_child_pid = -1;
g_pserver->aof_rewrite_time_start = -1;
/* Close pipes used for IPC between the two processes. */
aofClosePipes();
closeChildInfoPipe();
@@ -230,14 +236,14 @@ void killAppendOnlyChild(void) {
/* Called when the user switches from "appendonly yes" to "appendonly no"
* at runtime using the CONFIG command. */
void stopAppendOnly(void) {
serverAssert(server.aof_state != AOF_OFF);
serverAssert(g_pserver->aof_state != AOF_OFF);
flushAppendOnlyFile(1);
redis_fsync(server.aof_fd);
close(server.aof_fd);
redis_fsync(g_pserver->aof_fd);
close(g_pserver->aof_fd);
server.aof_fd = -1;
server.aof_selected_db = -1;
server.aof_state = AOF_OFF;
g_pserver->aof_fd = -1;
g_pserver->aof_selected_db = -1;
g_pserver->aof_state = AOF_OFF;
killAppendOnlyChild();
}
@@ -247,27 +253,27 @@ int startAppendOnly(void) {
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
int newfd;
newfd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);
serverAssert(server.aof_state == AOF_OFF);
newfd = open(g_pserver->aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);
serverAssert(g_pserver->aof_state == AOF_OFF);
if (newfd == -1) {
char *cwdp = getcwd(cwd,MAXPATHLEN);
serverLog(LL_WARNING,
"Redis needs to enable the AOF but can't open the "
"append only file %s (in server root dir %s): %s",
server.aof_filename,
g_pserver->aof_filename,
cwdp ? cwdp : "unknown",
strerror(errno));
return C_ERR;
}
if (server.rdb_child_pid != -1) {
server.aof_rewrite_scheduled = 1;
if (g_pserver->rdb_child_pid != -1) {
g_pserver->aof_rewrite_scheduled = 1;
serverLog(LL_WARNING,"AOF was enabled but there is already a child process saving an RDB file on disk. An AOF background was scheduled to start when possible.");
} else {
/* If there is a pending AOF rewrite, we need to switch it off and
* start a new one: the old one cannot be reused because it is not
* accumulating the AOF buffer. */
if (server.aof_child_pid != -1) {
if (g_pserver->aof_child_pid != -1) {
serverLog(LL_WARNING,"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.");
killAppendOnlyChild();
}
@@ -279,9 +285,9 @@ int startAppendOnly(void) {
}
/* We correctly switched on AOF, now wait for the rewrite to be complete
* in order to append data on disk. */
server.aof_state = AOF_WAIT_REWRITE;
server.aof_last_fsync = server.unixtime;
server.aof_fd = newfd;
g_pserver->aof_state = AOF_WAIT_REWRITE;
g_pserver->aof_last_fsync = g_pserver->unixtime;
g_pserver->aof_fd = newfd;
return C_OK;
}
@@ -337,29 +343,43 @@ void flushAppendOnlyFile(int force) {
int sync_in_progress = 0;
mstime_t latency;
if (sdslen(server.aof_buf) == 0) return;
if (sdslen(g_pserver->aof_buf) == 0) {
/* Check if we need to do fsync even the aof buffer is empty,
* because previously in AOF_FSYNC_EVERYSEC mode, fsync is
* called only when aof buffer is not empty, so if users
* stop write commands before fsync called in one second,
* the data in page cache cannot be flushed in time. */
if (g_pserver->aof_fsync == AOF_FSYNC_EVERYSEC &&
g_pserver->aof_fsync_offset != g_pserver->aof_current_size &&
g_pserver->unixtime > g_pserver->aof_last_fsync &&
!(sync_in_progress = aofFsyncInProgress())) {
goto try_fsync;
} else {
return;
}
}
if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
sync_in_progress = bioPendingJobsOfType(BIO_AOF_FSYNC) != 0;
if (g_pserver->aof_fsync == AOF_FSYNC_EVERYSEC)
sync_in_progress = aofFsyncInProgress();
if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {
if (g_pserver->aof_fsync == AOF_FSYNC_EVERYSEC && !force) {
/* With this append fsync policy we do background fsyncing.
* If the fsync is still in progress we can try to delay
* the write for a couple of seconds. */
if (sync_in_progress) {
if (server.aof_flush_postponed_start == 0) {
if (g_pserver->aof_flush_postponed_start == 0) {
/* No previous write postponing, remember that we are
* postponing the flush and return. */
server.aof_flush_postponed_start = server.unixtime;
g_pserver->aof_flush_postponed_start = g_pserver->unixtime;
return;
} else if (server.unixtime - server.aof_flush_postponed_start < 2) {
} else if (g_pserver->unixtime - g_pserver->aof_flush_postponed_start < 2) {
/* We were already waiting for fsync to finish, but for less
* than two seconds this is still ok. Postpone again. */
return;
}
/* Otherwise fall trough, and go write since we can't wait
* over two seconds. */
server.aof_delayed_fsync++;
g_pserver->aof_delayed_fsync++;
serverLog(LL_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.");
}
}
@@ -370,7 +390,7 @@ void flushAppendOnlyFile(int force) {
* or alike */
latencyStartMonitor(latency);
nwritten = aofWrite(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));
nwritten = aofWrite(g_pserver->aof_fd,g_pserver->aof_buf,sdslen(g_pserver->aof_buf));
latencyEndMonitor(latency);
/* We want to capture different events for delayed writes:
* when the delay happens with a pending fsync, or with a saving child
@@ -379,7 +399,7 @@ void flushAppendOnlyFile(int force) {
* useful for graphing / monitoring purposes. */
if (sync_in_progress) {
latencyAddSampleIfNeeded("aof-write-pending-fsync",latency);
} else if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) {
} else if (g_pserver->aof_child_pid != -1 || g_pserver->rdb_child_pid != -1) {
latencyAddSampleIfNeeded("aof-write-active-child",latency);
} else {
latencyAddSampleIfNeeded("aof-write-alone",latency);
@@ -387,16 +407,16 @@ void flushAppendOnlyFile(int force) {
latencyAddSampleIfNeeded("aof-write",latency);
/* We performed the write so reset the postponed flush sentinel to zero. */
server.aof_flush_postponed_start = 0;
g_pserver->aof_flush_postponed_start = 0;
if (nwritten != (ssize_t)sdslen(server.aof_buf)) {
if (nwritten != (ssize_t)sdslen(g_pserver->aof_buf)) {
static time_t last_write_error_log = 0;
int can_log = 0;
/* Limit logging rate to 1 line per AOF_WRITE_LOG_ERROR_RATE seconds. */
if ((server.unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) {
if ((g_pserver->unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) {
can_log = 1;
last_write_error_log = server.unixtime;
last_write_error_log = g_pserver->unixtime;
}
/* Log the AOF write error and record the error code. */
@@ -404,7 +424,7 @@ void flushAppendOnlyFile(int force) {
if (can_log) {
serverLog(LL_WARNING,"Error writing to the AOF file: %s",
strerror(errno));
server.aof_last_write_errno = errno;
g_pserver->aof_last_write_errno = errno;
}
} else {
if (can_log) {
@@ -412,10 +432,10 @@ void flushAppendOnlyFile(int force) {
"the AOF file: (nwritten=%lld, "
"expected=%lld)",
(long long)nwritten,
(long long)sdslen(server.aof_buf));
(long long)sdslen(g_pserver->aof_buf));
}
if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {
if (ftruncate(g_pserver->aof_fd, g_pserver->aof_current_size) == -1) {
if (can_log) {
serverLog(LL_WARNING, "Could not remove short write "
"from the append-only file. Redis may refuse "
@@ -427,11 +447,11 @@ void flushAppendOnlyFile(int force) {
* -1 since there is no longer partial data into the AOF. */
nwritten = -1;
}
server.aof_last_write_errno = ENOSPC;
g_pserver->aof_last_write_errno = ENOSPC;
}
/* Handle the AOF write error. */
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
if (g_pserver->aof_fsync == AOF_FSYNC_ALWAYS) {
/* We can't recover when the fsync policy is ALWAYS since the
* reply for the client is already in the output buffers, and we
* have the contract with the user that on acknowledged write data
@@ -442,55 +462,60 @@ void flushAppendOnlyFile(int force) {
/* Recover from failed write leaving data into the buffer. However
* set an error to stop accepting writes as long as the error
* condition is not cleared. */
server.aof_last_write_status = C_ERR;
g_pserver->aof_last_write_status = C_ERR;
/* Trim the sds buffer if there was a partial write, and there
* was no way to undo it with ftruncate(2). */
if (nwritten > 0) {
server.aof_current_size += nwritten;
sdsrange(server.aof_buf,nwritten,-1);
g_pserver->aof_current_size += nwritten;
sdsrange(g_pserver->aof_buf,nwritten,-1);
}
return; /* We'll try again on the next call... */
}
} else {
/* Successful write(2). If AOF was in error state, restore the
* OK state and log the event. */
if (server.aof_last_write_status == C_ERR) {
if (g_pserver->aof_last_write_status == C_ERR) {
serverLog(LL_WARNING,
"AOF write error looks solved, Redis can write again.");
server.aof_last_write_status = C_OK;
g_pserver->aof_last_write_status = C_OK;
}
}
server.aof_current_size += nwritten;
g_pserver->aof_current_size += nwritten;
/* Re-use AOF buffer when it is small enough. The maximum comes from the
* arena size of 4k minus some overhead (but is otherwise arbitrary). */
if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {
sdsclear(server.aof_buf);
if ((sdslen(g_pserver->aof_buf)+sdsavail(g_pserver->aof_buf)) < 4000) {
sdsclear(g_pserver->aof_buf);
} else {
sdsfree(server.aof_buf);
server.aof_buf = sdsempty();
sdsfree(g_pserver->aof_buf);
g_pserver->aof_buf = sdsempty();
}
try_fsync:
/* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are
* children doing I/O in the background. */
if (server.aof_no_fsync_on_rewrite &&
(server.aof_child_pid != -1 || server.rdb_child_pid != -1))
if (g_pserver->aof_no_fsync_on_rewrite &&
(g_pserver->aof_child_pid != -1 || g_pserver->rdb_child_pid != -1))
return;
/* Perform the fsync if needed. */
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
if (g_pserver->aof_fsync == AOF_FSYNC_ALWAYS) {
/* redis_fsync is defined as fdatasync() for Linux in order to avoid
* flushing metadata. */
latencyStartMonitor(latency);
redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */
redis_fsync(g_pserver->aof_fd); /* Let's try to get this data on the disk */
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fsync-always",latency);
server.aof_last_fsync = server.unixtime;
} else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&
server.unixtime > server.aof_last_fsync)) {
if (!sync_in_progress) aof_background_fsync(server.aof_fd);
server.aof_last_fsync = server.unixtime;
g_pserver->aof_fsync_offset = g_pserver->aof_current_size;
g_pserver->aof_last_fsync = g_pserver->unixtime;
} else if ((g_pserver->aof_fsync == AOF_FSYNC_EVERYSEC &&
g_pserver->unixtime > g_pserver->aof_last_fsync)) {
if (!sync_in_progress) {
aof_background_fsync(g_pserver->aof_fd);
g_pserver->aof_fsync_offset = g_pserver->aof_current_size;
}
g_pserver->aof_last_fsync = g_pserver->unixtime;
}
}
@@ -562,13 +587,13 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
/* The DB this command was targeting is not the same as the last command
* we appended. To issue a SELECT command is needed. */
if (dictid != server.aof_selected_db) {
if (dictid != g_pserver->aof_selected_db) {
char seldb[64];
snprintf(seldb,sizeof(seldb),"%d",dictid);
buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
(unsigned long)strlen(seldb),seldb);
server.aof_selected_db = dictid;
g_pserver->aof_selected_db = dictid;
}
if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||
@@ -594,10 +619,10 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
}
serverAssert(!(exarg && pxarg));
if (exarg)
buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],
buf = catAppendOnlyExpireAtCommand(buf,cserver.expireCommand,argv[1],
exarg);
if (pxarg)
buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],
buf = catAppendOnlyExpireAtCommand(buf,cserver.pexpireCommand,argv[1],
pxarg);
} else {
/* All the other commands don't need translation or need the
@@ -609,14 +634,14 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
/* Append to the AOF buffer. This will be flushed on disk just before
* of re-entering the event loop, so before the client will get a
* positive reply about the operation performed. */
if (server.aof_state == AOF_ON)
server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));
if (g_pserver->aof_state == AOF_ON)
g_pserver->aof_buf = sdscatlen(g_pserver->aof_buf,buf,sdslen(buf));
/* If a background append only file rewriting is in progress we want to
* accumulate the differences between the child DB and the current one
* in a buffer, so that when the child process will do its work we
* can append the differences to the new append only file. */
if (server.aof_child_pid != -1)
if (g_pserver->aof_child_pid != -1)
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
sdsfree(buf);
@@ -686,7 +711,7 @@ int loadAppendOnlyFile(char *filename) {
struct client *fakeClient;
FILE *fp = fopen(filename,"r");
struct redis_stat sb;
int old_aof_state = server.aof_state;
int old_aof_state = g_pserver->aof_state;
long loops = 0;
off_t valid_up_to = 0; /* Offset of latest well-formed command loaded. */
off_t valid_before_multi = 0; /* Offset before MULTI command loaded. */
@@ -702,14 +727,15 @@ int loadAppendOnlyFile(char *filename) {
* a zero length file at startup, that will remain like that if no write
* operation is received. */
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.aof_current_size = 0;
g_pserver->aof_current_size = 0;
g_pserver->aof_fsync_offset = g_pserver->aof_current_size;
fclose(fp);
return C_ERR;
}
/* Temporarily disable AOF, to prevent EXEC from feeding a MULTI
* to the same file we're about to read. */
server.aof_state = AOF_OFF;
g_pserver->aof_state = AOF_OFF;
fakeClient = createFakeClient();
startLoading(fp);
@@ -726,7 +752,7 @@ int loadAppendOnlyFile(char *filename) {
serverLog(LL_NOTICE,"Reading RDB preamble from AOF file...");
if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
rioInitWithFile(&rdb,fileno(fp));
rioInitWithFile(&rdb,fp);
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
if (rdbLoadRio(&rdb,&rsi,1) != C_OK) {
serverLog(LL_WARNING,"Error reading the RDB preamble of the AOF file, AOF loading aborted");
@@ -748,7 +774,7 @@ int loadAppendOnlyFile(char *filename) {
/* Serve the clients from time to time */
if (!(loops++ % 1000)) {
loadingProgress(ftello(fp));
processEventsWhileBlocked(serverTL - server.rgthreadvar);
processEventsWhileBlocked(serverTL - g_pserver->rgthreadvar);
}
if (fgets(buf,sizeof(buf),fp) == NULL) {
@@ -798,7 +824,7 @@ int loadAppendOnlyFile(char *filename) {
exit(1);
}
if (cmd == server.multiCommand) valid_before_multi = valid_up_to;
if (cmd == cserver.multiCommand) valid_before_multi = valid_up_to;
/* Run the command in the context of a fake client */
fakeClient->cmd = cmd;
@@ -821,7 +847,7 @@ int loadAppendOnlyFile(char *filename) {
* argv/argc of the client instead of the local variables. */
freeFakeClientArgv(fakeClient);
fakeClient->cmd = NULL;
if (server.aof_load_truncated) valid_up_to = ftello(fp);
if (g_pserver->aof_load_truncated) valid_up_to = ftello(fp);
}
/* This point can only be reached when EOF is reached without errors.
@@ -838,10 +864,11 @@ int loadAppendOnlyFile(char *filename) {
loaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */
fclose(fp);
freeFakeClient(fakeClient);
server.aof_state = old_aof_state;
g_pserver->aof_state = old_aof_state;
stopLoading();
aofUpdateCurrentSize();
server.aof_rewrite_base_size = server.aof_current_size;
g_pserver->aof_rewrite_base_size = g_pserver->aof_current_size;
g_pserver->aof_fsync_offset = g_pserver->aof_current_size;
return C_OK;
readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */
@@ -852,7 +879,7 @@ readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */
}
uxeof: /* Unexpected AOF end of file. */
if (server.aof_load_truncated) {
if (g_pserver->aof_load_truncated) {
serverLog(LL_WARNING,"!!! Warning: short read while loading the AOF file !!!");
serverLog(LL_WARNING,"!!! Truncating the AOF at offset %llu !!!",
(unsigned long long) valid_up_to);
@@ -866,7 +893,7 @@ uxeof: /* Unexpected AOF end of file. */
} else {
/* Make sure the AOF file descriptor points to the end of the
* file after the truncate call. */
if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) {
if (g_pserver->aof_fd != -1 && lseek(g_pserver->aof_fd,0,SEEK_END) == -1) {
serverLog(LL_WARNING,"Can't seek the end of the AOF file: %s",
strerror(errno));
} else {
@@ -877,7 +904,7 @@ uxeof: /* Unexpected AOF end of file. */
}
}
if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */
serverLog(LL_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./keydb-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server.");
serverLog(LL_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./keydb-check-aof --fix <filename>. 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the g_pserver->");
exit(1);
fmterr: /* Format error. */
@@ -891,7 +918,7 @@ fmterr: /* Format error. */
* ------------------------------------------------------------------------- */
/* Delegate writing an object to writing a bulk string or bulk long long.
* This is not placed in rio.c since that adds the server.h dependency. */
* This is not placed in rio.c since that adds the g_pserver->h dependency. */
int rioWriteBulkObject(rio *r, robj *obj) {
/* Avoid using getDecodedObject to help copy-on-write (we are often
* in a child process when this function is called). */
@@ -1266,8 +1293,8 @@ ssize_t aofReadDiffFromParent(void) {
ssize_t nread, total = 0;
while ((nread =
read(server.aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) {
server.aof_child_diff = sdscatlen(server.aof_child_diff,buf,nread);
read(g_pserver->aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) {
g_pserver->aof_child_diff = sdscatlen(g_pserver->aof_child_diff,buf,nread);
total += nread;
}
return total;
@@ -1279,9 +1306,9 @@ int rewriteAppendOnlyFileRio(rio *aof) {
size_t processed = 0;
int j;
for (j = 0; j < server.dbnum; j++) {
for (j = 0; j < cserver.dbnum; j++) {
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
redisDb *db = server.db+j;
redisDb *db = g_pserver->db+j;
dict *d = db->pdict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
@@ -1372,13 +1399,13 @@ int rewriteAppendOnlyFile(char *filename) {
return C_ERR;
}
server.aof_child_diff = sdsempty();
rioInitWithFile(&aof,fileno(fp));
g_pserver->aof_child_diff = sdsempty();
rioInitWithFile(&aof,fp);
if (server.aof_rewrite_incremental_fsync)
if (g_pserver->aof_rewrite_incremental_fsync)
rioSetAutoSync(&aof,REDIS_AUTOSYNC_BYTES);
if (server.aof_use_rdb_preamble) {
if (g_pserver->aof_use_rdb_preamble) {
int error;
if (rdbSaveRio(&aof,&error,RDB_SAVE_AOF_PREAMBLE,NULL) == C_ERR) {
errno = error;
@@ -1401,7 +1428,7 @@ int rewriteAppendOnlyFile(char *filename) {
* happens after 20 ms without new data). */
start = mstime();
while(mstime()-start < 1000 && nodata < 20) {
if (aeWait(server.aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0)
if (aeWait(g_pserver->aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0)
{
nodata++;
continue;
@@ -1412,13 +1439,13 @@ int rewriteAppendOnlyFile(char *filename) {
}
/* Ask the master to stop sending diffs. */
if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr;
if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)
if (write(g_pserver->aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr;
if (anetNonBlock(NULL,g_pserver->aof_pipe_read_ack_from_parent) != ANET_OK)
goto werr;
/* We read the ACK from the server using a 10 seconds timeout. Normally
* it should reply ASAP, but just in case we lose its reply, we are sure
* the child will eventually get terminated. */
if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||
if (syncRead(g_pserver->aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||
byte != '!') goto werr;
serverLog(LL_NOTICE,"Parent agreed to stop sending diffs. Finalizing AOF...");
@@ -1428,8 +1455,8 @@ int rewriteAppendOnlyFile(char *filename) {
/* Write the received diff to the file. */
serverLog(LL_NOTICE,
"Concatenating %.2f MB of AOF diff received from parent.",
(double) sdslen(server.aof_child_diff) / (1024*1024));
if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0)
(double) sdslen(g_pserver->aof_child_diff) / (1024*1024));
if (rioWrite(&aof,g_pserver->aof_child_diff,sdslen(g_pserver->aof_child_diff)) == 0)
goto werr;
/* Make sure data will not remain on the OS's output buffers */
@@ -1469,8 +1496,8 @@ void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {
if (read(fd,&byte,1) == 1 && byte == '!') {
serverLog(LL_NOTICE,"AOF rewrite child asks to stop sending diffs.");
server.aof_stop_sending_diff = 1;
if (write(server.aof_pipe_write_ack_to_child,"!",1) != 1) {
g_pserver->aof_stop_sending_diff = 1;
if (write(g_pserver->aof_pipe_write_ack_to_child,"!",1) != 1) {
/* If we can't send the ack, inform the user, but don't try again
* since in the other side the children will use a timeout if the
* kernel can't buffer our write, or, the children was
@@ -1481,7 +1508,7 @@ void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) {
}
/* Remove the handler since this can be called only one time during a
* rewrite. */
aeDeleteFileEventAsync(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,server.aof_pipe_read_ack_from_child,AE_READABLE);
aeDeleteFileEventAsync(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,g_pserver->aof_pipe_read_ack_from_child,AE_READABLE);
}
/* Create the pipes used for parent - child process IPC during rewrite.
@@ -1501,14 +1528,14 @@ int aofCreatePipes(void) {
if (anetNonBlock(NULL,fds[1]) != ANET_OK) goto error;
if (aeCreateFileEvent(serverTL->el, fds[2], AE_READABLE, aofChildPipeReadable, NULL) == AE_ERR) goto error;
server.aof_pipe_write_data_to_child = fds[1];
server.aof_pipe_read_data_from_parent = fds[0];
server.aof_pipe_write_ack_to_parent = fds[3];
server.aof_pipe_read_ack_from_child = fds[2];
server.el_alf_pip_read_ack_from_child = serverTL->el;
server.aof_pipe_write_ack_to_child = fds[5];
server.aof_pipe_read_ack_from_parent = fds[4];
server.aof_stop_sending_diff = 0;
g_pserver->aof_pipe_write_data_to_child = fds[1];
g_pserver->aof_pipe_read_data_from_parent = fds[0];
g_pserver->aof_pipe_write_ack_to_parent = fds[3];
g_pserver->aof_pipe_read_ack_from_child = fds[2];
g_pserver->el_alf_pip_read_ack_from_child = serverTL->el;
g_pserver->aof_pipe_write_ack_to_child = fds[5];
g_pserver->aof_pipe_read_ack_from_parent = fds[4];
g_pserver->aof_stop_sending_diff = 0;
return C_OK;
error:
@@ -1519,14 +1546,14 @@ error:
}
void aofClosePipes(void) {
aeDeleteFileEventAsync(server.el_alf_pip_read_ack_from_child,server.aof_pipe_read_ack_from_child,AE_READABLE);
aeDeleteFileEventAsync(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,server.aof_pipe_write_data_to_child,AE_WRITABLE);
close(server.aof_pipe_write_data_to_child);
close(server.aof_pipe_read_data_from_parent);
close(server.aof_pipe_write_ack_to_parent);
close(server.aof_pipe_read_ack_from_child);
close(server.aof_pipe_write_ack_to_child);
close(server.aof_pipe_read_ack_from_parent);
aeDeleteFileEventAsync(g_pserver->el_alf_pip_read_ack_from_child,g_pserver->aof_pipe_read_ack_from_child,AE_READABLE);
aeDeleteFileEventAsync(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,g_pserver->aof_pipe_write_data_to_child,AE_WRITABLE);
close(g_pserver->aof_pipe_write_data_to_child);
close(g_pserver->aof_pipe_read_data_from_parent);
close(g_pserver->aof_pipe_write_ack_to_parent);
close(g_pserver->aof_pipe_read_ack_from_child);
close(g_pserver->aof_pipe_write_ack_to_child);
close(g_pserver->aof_pipe_read_ack_from_parent);
}
/* ----------------------------------------------------------------------------
@@ -1538,10 +1565,10 @@ void aofClosePipes(void) {
* 1) The user calls BGREWRITEAOF
* 2) Redis calls this function, that forks():
* 2a) the child rewrite the append only file in a temp file.
* 2b) the parent accumulates differences in server.aof_rewrite_buf.
* 2b) the parent accumulates differences in g_pserver->aof_rewrite_buf.
* 3) When the child finished '2a' exists.
* 4) The parent will trap the exit code, if it's OK, will append the
* data accumulated into server.aof_rewrite_buf into the temp file, and
* data accumulated into g_pserver->aof_rewrite_buf into the temp file, and
* finally will rename(2) the temp file in the actual file name.
* The the new file is reopened as the new append only file. Profit!
*/
@@ -1549,7 +1576,7 @@ int rewriteAppendOnlyFileBackground(void) {
pid_t childpid;
long long start;
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
if (g_pserver->aof_child_pid != -1 || g_pserver->rdb_child_pid != -1) return C_ERR;
if (aofCreatePipes() != C_OK) return C_ERR;
openChildInfoPipe();
start = ustime();
@@ -1569,7 +1596,7 @@ int rewriteAppendOnlyFileBackground(void) {
private_dirty/(1024*1024));
}
server.child_info_data.cow_size = private_dirty;
g_pserver->child_info_data.cow_size = private_dirty;
sendChildInfo(CHILD_INFO_TYPE_AOF);
exitFromChild(0);
} else {
@@ -1577,9 +1604,9 @@ int rewriteAppendOnlyFileBackground(void) {
}
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",g_pserver->stat_fork_time/1000);
if (childpid == -1) {
closeChildInfoPipe();
serverLog(LL_WARNING,
@@ -1590,15 +1617,15 @@ int rewriteAppendOnlyFileBackground(void) {
}
serverLog(LL_NOTICE,
"Background append only file rewriting started by pid %d",childpid);
server.aof_rewrite_scheduled = 0;
server.aof_rewrite_time_start = time(NULL);
server.aof_child_pid = childpid;
g_pserver->aof_rewrite_scheduled = 0;
g_pserver->aof_rewrite_time_start = time(NULL);
g_pserver->aof_child_pid = childpid;
updateDictResizePolicy();
/* We set appendseldb to -1 in order to force the next call to the
* feedAppendOnlyFile() to issue a SELECT command, so the differences
* accumulated by the parent into server.aof_rewrite_buf will start
* accumulated by the parent into g_pserver->aof_rewrite_buf will start
* with a SELECT statement and it will be safe to merge. */
server.aof_selected_db = -1;
g_pserver->aof_selected_db = -1;
replicationScriptCacheFlush();
return C_OK;
}
@@ -1606,10 +1633,10 @@ int rewriteAppendOnlyFileBackground(void) {
}
void bgrewriteaofCommand(client *c) {
if (server.aof_child_pid != -1) {
if (g_pserver->aof_child_pid != -1) {
addReplyError(c,"Background append only file rewriting already in progress");
} else if (server.rdb_child_pid != -1) {
server.aof_rewrite_scheduled = 1;
} else if (g_pserver->rdb_child_pid != -1) {
g_pserver->aof_rewrite_scheduled = 1;
addReplyStatus(c,"Background append only file rewriting scheduled");
} else if (rewriteAppendOnlyFileBackground() == C_OK) {
addReplyStatus(c,"Background append only file rewriting started");
@@ -1628,7 +1655,7 @@ void aofRemoveTempFile(pid_t childpid) {
unlink(tmpfile);
}
/* Update the server.aof_current_size field explicitly using stat(2)
/* Update the g_pserver->aof_current_size field 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 length, that is much faster. */
@@ -1637,11 +1664,11 @@ void aofUpdateCurrentSize(void) {
mstime_t latency;
latencyStartMonitor(latency);
if (redis_fstat(server.aof_fd,&sb) == -1) {
if (redis_fstat(g_pserver->aof_fd,&sb) == -1) {
serverLog(LL_WARNING,"Unable to obtain the AOF file length. stat: %s",
strerror(errno));
} else {
server.aof_current_size = sb.st_size;
g_pserver->aof_current_size = sb.st_size;
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fstat",latency);
@@ -1663,7 +1690,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
* rewritten AOF. */
latencyStartMonitor(latency);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
(int)server.aof_child_pid);
(int)g_pserver->aof_child_pid);
newfd = open(tmpfile,O_WRONLY|O_APPEND);
if (newfd == -1) {
serverLog(LL_WARNING,
@@ -1692,14 +1719,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
*
* 1) AOF is DISABLED and this was a one time rewrite. The temporary
* file will be renamed to the configured file. When this file already
* exists, it will be unlinked, which may block the server.
* exists, it will be unlinked, which may block the g_pserver->
*
* 2) AOF is ENABLED and the rewritten AOF will immediately start
* receiving writes. After the temporary file is renamed to the
* configured file, the original AOF file descriptor will be closed.
* Since this will be the last reference to that file, closing it
* causes the underlying file to be unlinked, which may block the
* server.
* g_pserver->
*
* To mitigate the blocking effect of the unlink operation (either
* caused by rename(2) in scenario 1, or by close(2) in scenario 2), we
@@ -1710,13 +1737,13 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
* guarantee atomicity for this switch has already happened by then, so
* we don't care what the outcome or duration of that close operation
* is, as long as the file descriptor is released again. */
if (server.aof_fd == -1) {
if (g_pserver->aof_fd == -1) {
/* AOF disabled */
/* Don't care if this fails: oldfd will be -1 and we handle that.
* One notable case of -1 return is if the old file does
* not exist. */
oldfd = open(server.aof_filename,O_RDONLY|O_NONBLOCK);
oldfd = open(g_pserver->aof_filename,O_RDONLY|O_NONBLOCK);
} else {
/* AOF enabled */
oldfd = -1; /* We'll set this to the current AOF filedes later. */
@@ -1725,11 +1752,11 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* Rename the temporary file. This will not unlink the target file if
* it exists, because we reference it with "oldfd". */
latencyStartMonitor(latency);
if (rename(tmpfile,server.aof_filename) == -1) {
if (rename(tmpfile,g_pserver->aof_filename) == -1) {
serverLog(LL_WARNING,
"Error trying to rename the temporary AOF file %s into %s: %s",
tmpfile,
server.aof_filename,
g_pserver->aof_filename,
strerror(errno));
close(newfd);
if (oldfd != -1) close(oldfd);
@@ -1738,34 +1765,34 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-rename",latency);
if (server.aof_fd == -1) {
if (g_pserver->aof_fd == -1) {
/* AOF disabled, we don't need to set the AOF file descriptor
* to this new file, so we can close it. */
close(newfd);
} else {
/* AOF enabled, replace the old fd with the new one. */
oldfd = server.aof_fd;
server.aof_fd = newfd;
if (server.aof_fsync == AOF_FSYNC_ALWAYS)
oldfd = g_pserver->aof_fd;
g_pserver->aof_fd = newfd;
if (g_pserver->aof_fsync == AOF_FSYNC_ALWAYS)
redis_fsync(newfd);
else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
else if (g_pserver->aof_fsync == AOF_FSYNC_EVERYSEC)
aof_background_fsync(newfd);
server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
g_pserver->aof_selected_db = -1; /* Make sure SELECT is re-issued */
aofUpdateCurrentSize();
server.aof_rewrite_base_size = server.aof_current_size;
g_pserver->aof_rewrite_base_size = g_pserver->aof_current_size;
/* Clear regular AOF buffer since its contents was just written to
* the new AOF from the background rewrite buffer. */
sdsfree(server.aof_buf);
server.aof_buf = sdsempty();
sdsfree(g_pserver->aof_buf);
g_pserver->aof_buf = sdsempty();
}
server.aof_lastbgrewrite_status = C_OK;
g_pserver->aof_lastbgrewrite_status = C_OK;
serverLog(LL_NOTICE, "Background AOF rewrite finished successfully");
/* Change state from WAIT_REWRITE to ON if needed */
if (server.aof_state == AOF_WAIT_REWRITE)
server.aof_state = AOF_ON;
if (g_pserver->aof_state == AOF_WAIT_REWRITE)
g_pserver->aof_state = AOF_ON;
/* Asynchronously close the overwritten AOF. */
if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);
@@ -1776,11 +1803,11 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error condition. */
if (bysignal != SIGUSR1)
server.aof_lastbgrewrite_status = C_ERR;
g_pserver->aof_lastbgrewrite_status = C_ERR;
serverLog(LL_WARNING,
"Background AOF rewrite terminated with error");
} else {
server.aof_lastbgrewrite_status = C_ERR;
g_pserver->aof_lastbgrewrite_status = C_ERR;
serverLog(LL_WARNING,
"Background AOF rewrite terminated by signal %d", bysignal);
@@ -1789,11 +1816,11 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
cleanup:
aofClosePipes();
aofRewriteBufferReset();
aofRemoveTempFile(server.aof_child_pid);
server.aof_child_pid = -1;
server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;
server.aof_rewrite_time_start = -1;
aofRemoveTempFile(g_pserver->aof_child_pid);
g_pserver->aof_child_pid = -1;
g_pserver->aof_rewrite_time_last = time(NULL)-g_pserver->aof_rewrite_time_start;
g_pserver->aof_rewrite_time_start = -1;
/* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */
if (server.aof_state == AOF_WAIT_REWRITE)
server.aof_rewrite_scheduled = 1;
if (g_pserver->aof_state == AOF_WAIT_REWRITE)
g_pserver->aof_rewrite_scheduled = 1;
}
+3
View File
@@ -36,4 +36,7 @@ const char *ascii_logo =
" Port: %d\n"
" PID: %ld\n"
" \n"
" Like KeyDB? Star us on GitHub! \n"
" \n"
" https://github.com/JohnSully/KeyDB \n"
" \n\n";
+1 -1
View File
@@ -4,7 +4,7 @@
* Currently there is only a single operation, that is a background close(2)
* system call. This is needed as when the process is the last owner of a
* reference to a file closing it means unlinking it, and the deletion of the
* file is slow, blocking the server.
* file is slow, blocking the g_pserver->
*
* In the future we'll either continue implementing new things we need or
* we'll switch to libeio. However there are probably long term uses for this
+35 -28
View File
@@ -37,7 +37,7 @@
/* Count number of bits set in the binary array pointed by 's' and long
* 'count' bytes. The implementation of this function is required to
* work with a input string length up to 512 MB. */
size_t redisPopcount(void *s, long count) {
size_t redisPopcount(const void *s, long count) {
size_t bits = 0;
unsigned char *p = (unsigned char*)s;
uint32_t *p4;
@@ -98,7 +98,7 @@ size_t redisPopcount(void *s, long count) {
* no zero bit is found, it returns count*8 assuming the string is zero
* padded on the right. However if 'bit' is 1 it is possible that there is
* not a single set bit in the bitmap. In this special case -1 is returned. */
long redisBitpos(void *s, unsigned long count, int bit) {
long redisBitpos(const void *s, unsigned long count, int bit) {
unsigned long *l;
unsigned char *c;
unsigned long skipval, word = 0, one;
@@ -503,17 +503,17 @@ robj *lookupStringForBitCommand(client *c, size_t maxbit) {
*
* If the source object is NULL the function is guaranteed to return NULL
* and set 'len' to 0. */
unsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) {
const unsigned char *getObjectReadOnlyString(robj_roptr o, long *len, char *llbuf) {
serverAssert(o->type == OBJ_STRING);
unsigned char *p = NULL;
const unsigned char *p = NULL;
/* Set the 'p' pointer to the string, that can be just a stack allocated
* array if our string was integer encoded. */
if (o && o->encoding == OBJ_ENCODING_INT) {
p = (unsigned char*) llbuf;
p = (const unsigned char*) llbuf;
if (len) *len = ll2string(llbuf,LONG_STR_SIZE,(long)ptrFromObj(o));
} else if (o) {
p = (unsigned char*) ptrFromObj(o);
p = (const unsigned char*) ptrFromObj(o);
if (len) *len = sdslen(szFromObj(o));
} else {
if (len) *len = 0;
@@ -556,13 +556,13 @@ void setbitCommand(client *c) {
((uint8_t*)ptrFromObj(o))[byte] = byteval;
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReply(c, bitval ? shared.cone : shared.czero);
}
/* GETBIT key offset */
void getbitCommand(client *c) {
robj *o;
robj_roptr o;
char llbuf[32];
size_t bitoffset;
size_t byte, bit;
@@ -571,7 +571,7 @@ void getbitCommand(client *c) {
if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_STRING)) return;
byte = bitoffset >> 3;
@@ -590,9 +590,10 @@ void getbitCommand(client *c) {
/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */
void bitopCommand(client *c) {
char *opname = szFromObj(c->argv[1]);
robj *o, *targetkey = c->argv[2];
robj *targetkey = c->argv[2];
robj_roptr o;
unsigned long op, j, numkeys;
robj **objects; /* Array of source objects. */
robj_roptr *objects; /* Array of source objects. */
unsigned char **src; /* Array of source strings pointers. */
unsigned long *len, maxlen = 0; /* Array of length of src strings,
and max len. */
@@ -623,12 +624,12 @@ void bitopCommand(client *c) {
numkeys = c->argc - 3;
src = (unsigned char**)zmalloc(sizeof(unsigned char*) * numkeys, MALLOC_LOCAL);
len = (unsigned long*)zmalloc(sizeof(long) * numkeys, MALLOC_LOCAL);
objects = (robj**)zmalloc(sizeof(robj*) * numkeys, MALLOC_LOCAL);
objects = (robj_roptr*)zmalloc(sizeof(robj_roptr) * numkeys, MALLOC_LOCAL);
for (j = 0; j < numkeys; j++) {
o = lookupKeyRead(c->db,c->argv[j+3]);
/* Handle non-existing keys as empty strings. */
if (o == NULL) {
objects[j] = NULL;
if (o == nullptr) {
objects[j] = nullptr;
src[j] = NULL;
len[j] = 0;
minlen = 0;
@@ -753,7 +754,7 @@ void bitopCommand(client *c) {
/* Store the computed value into the target key */
if (maxlen) {
o = createObject(OBJ_STRING,res);
robj *o = createObject(OBJ_STRING,res);
setKey(c->db,targetkey,o);
notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id);
decrRefCount(o);
@@ -761,19 +762,19 @@ void bitopCommand(client *c) {
signalModifiedKey(c->db,targetkey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",targetkey,c->db->id);
}
server.dirty++;
g_pserver->dirty++;
addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
}
/* BITCOUNT key [start end] */
void bitcountCommand(client *c) {
robj *o;
robj_roptr o;
long start, end, strlen;
unsigned char *p;
const unsigned char *p;
char llbuf[LONG_STR_SIZE];
/* Lookup, check for type, and return 0 for non existing keys. */
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_STRING)) return;
p = getObjectReadOnlyString(o,&strlen,llbuf);
@@ -816,9 +817,9 @@ void bitcountCommand(client *c) {
/* BITPOS key bit [start [end]] */
void bitposCommand(client *c) {
robj *o;
robj_roptr o;
long bit, start, end, strlen;
unsigned char *p;
const unsigned char *p;
char llbuf[LONG_STR_SIZE];
int end_given = 0;
@@ -834,7 +835,7 @@ void bitposCommand(client *c) {
/* If the key does not exist, from our point of view it is an infinite
* array of 0 bits. If the user is looking for the fist clear bit return 0,
* If the user is looking for the first set bit, return -1. */
if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) {
addReplyLongLong(c, bit ? -1 : 0);
return;
}
@@ -912,7 +913,7 @@ struct bitfieldOp {
};
void bitfieldCommand(client *c) {
robj *o;
robj_roptr o;
size_t bitoffset;
int j, numops = 0, changes = 0;
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
@@ -994,12 +995,18 @@ void bitfieldCommand(client *c) {
/* Lookup for read is ok if key doesn't exit, but errors
* if it's not a string. */
o = lookupKeyRead(c->db,c->argv[1]);
if (o != NULL && checkType(c,o,OBJ_STRING)) return;
if (o != nullptr && checkType(c,o,OBJ_STRING)) {
zfree(ops);
return;
}
} else {
/* Lookup by making room up to the farest bit reached by
* this operation. */
if ((o = lookupStringForBitCommand(c,
highest_write_offset)) == NULL) return;
highest_write_offset)) == nullptr) {
zfree(ops);
return;
}
}
addReplyArrayLen(c,numops);
@@ -1084,10 +1091,10 @@ void bitfieldCommand(client *c) {
/* GET */
unsigned char buf[9];
long strlen = 0;
unsigned char *src = NULL;
const unsigned char *src = NULL;
char llbuf[LONG_STR_SIZE];
if (o != NULL)
if (o != nullptr)
src = getObjectReadOnlyString(o,&strlen,llbuf);
/* For GET we use a trick: before executing the operation
@@ -1119,7 +1126,7 @@ void bitfieldCommand(client *c) {
if (changes) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
server.dirty += changes;
g_pserver->dirty += changes;
}
zfree(ops);
}
+22 -19
View File
@@ -64,6 +64,7 @@
*/
#include "server.h"
#include <mutex>
int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);
@@ -110,8 +111,8 @@ void blockClient(client *c, int btype) {
serverAssert(GlobalLocksAcquired());
c->flags |= CLIENT_BLOCKED;
c->btype = btype;
server.blocked_clients++;
server.blocked_clients_by_type[btype]++;
g_pserver->blocked_clients++;
g_pserver->blocked_clients_by_type[btype]++;
}
/* This function is called in the beforeSleep() function of the event loop
@@ -122,8 +123,8 @@ void processUnblockedClients(int iel) {
listNode *ln;
client *c;
list *unblocked_clients = server.rgthreadvar[iel].unblocked_clients;
serverAssert(iel == (serverTL - server.rgthreadvar));
list *unblocked_clients = g_pserver->rgthreadvar[iel].unblocked_clients;
serverAssert(iel == (serverTL - g_pserver->rgthreadvar));
while (listLength(unblocked_clients)) {
ln = listFirst(unblocked_clients);
@@ -171,7 +172,7 @@ void queueClientForReprocessing(client *c) {
fastlock_lock(&c->lock);
if (!(c->flags & CLIENT_UNBLOCKED)) {
c->flags |= CLIENT_UNBLOCKED;
listAddNodeTail(server.rgthreadvar[c->iel].unblocked_clients,c);
listAddNodeTail(g_pserver->rgthreadvar[c->iel].unblocked_clients,c);
}
fastlock_unlock(&c->lock);
}
@@ -180,6 +181,7 @@ void queueClientForReprocessing(client *c) {
* of operation the client is blocking for. */
void unblockClient(client *c) {
serverAssert(GlobalLocksAcquired());
serverAssert(c->lock.fOwnLock());
if (c->btype == BLOCKED_LIST ||
c->btype == BLOCKED_ZSET ||
c->btype == BLOCKED_STREAM) {
@@ -193,8 +195,8 @@ void unblockClient(client *c) {
}
/* Clear the flags, and put the client in the unblocked list so that
* we'll process new commands in its query buffer ASAP. */
server.blocked_clients--;
server.blocked_clients_by_type[c->btype]--;
g_pserver->blocked_clients--;
g_pserver->blocked_clients_by_type[c->btype]--;
c->flags &= ~CLIENT_BLOCKED;
c->btype = BLOCKED_NONE;
queueClientForReprocessing(c);
@@ -229,7 +231,7 @@ void disconnectAllBlockedClients(void) {
listNode *ln;
listIter li;
listRewind(server.clients,&li);
listRewind(g_pserver->clients,&li);
while((ln = listNext(&li))) {
client *c = (client*)listNodeValue(ln);
@@ -252,7 +254,7 @@ void disconnectAllBlockedClients(void) {
*
* All the keys with at least one client blocked that received at least
* one new element via some write operation are accumulated into
* the server.ready_keys list. This function will run the list and will
* the g_pserver->ready_keys list. This function will run the list and will
* serve clients accordingly. Note that the function will iterate again and
* again as a result of serving BRPOPLPUSH we can have new blocking clients
* to serve because of the PUSH side of BRPOPLPUSH.
@@ -268,15 +270,15 @@ void disconnectAllBlockedClients(void) {
* do, the function is already fair. */
void handleClientsBlockedOnKeys(void) {
serverAssert(GlobalLocksAcquired());
while(listLength(server.ready_keys) != 0) {
while(listLength(g_pserver->ready_keys) != 0) {
list *l;
/* Point server.ready_keys to a fresh list and save the current one
/* Point g_pserver->ready_keys to a fresh list and save the current one
* locally. This way as we run the old list we are free to call
* signalKeyAsReady() that may push new elements in server.ready_keys
* signalKeyAsReady() that may push new elements in g_pserver->ready_keys
* when handling clients blocked into BRPOPLPUSH. */
l = server.ready_keys;
server.ready_keys = listCreate();
l = g_pserver->ready_keys;
g_pserver->ready_keys = listCreate();
while(listLength(l) != 0) {
listNode *ln = listFirst(l);
@@ -301,6 +303,7 @@ void handleClientsBlockedOnKeys(void) {
while(numclients--) {
listNode *clientnode = listFirst(clients);
client *receiver = (client*)clientnode->value;
std::unique_lock<decltype(client::lock)> lock(receiver->lock);
if (receiver->btype != BLOCKED_LIST) {
/* Put at the tail, so that at the next call
@@ -385,8 +388,8 @@ void handleClientsBlockedOnKeys(void) {
/* Replicate the command. */
robj *argv[2];
struct redisCommand *cmd = where == ZSET_MIN ?
server.zpopminCommand :
server.zpopmaxCommand;
cserver.zpopminCommand :
cserver.zpopmaxCommand;
argv[0] = createStringObject(cmd->name,strlen(cmd->name));
argv[1] = rl->key;
incrRefCount(rl->key);
@@ -613,7 +616,7 @@ void unblockClientWaitingData(client *c) {
}
/* If the specified key has clients blocked waiting for list pushes, this
* function will put the key reference into the server.ready_keys list.
* function will put the key reference into the g_pserver->ready_keys list.
* Note that db->ready_keys is a hash table that allows us to avoid putting
* the same key again and again in the list in case of multiple pushes
* made by a script or in the context of MULTI/EXEC.
@@ -628,12 +631,12 @@ void signalKeyAsReady(redisDb *db, robj *key) {
/* Key was already signaled? No need to queue it again. */
if (dictFind(db->ready_keys,key) != NULL) return;
/* Ok, we need to queue this key into server.ready_keys. */
/* Ok, we need to queue this key into g_pserver->ready_keys. */
rl = (readyList*)zmalloc(sizeof(*rl), MALLOC_SHARED);
rl->key = key;
rl->db = db;
incrRefCount(key);
listAddNodeTail(server.ready_keys,rl);
listAddNodeTail(g_pserver->ready_keys,rl);
/* We also add the key in the db->ready_keys dictionary in order
* to avoid adding it multiple times into a list with a simple O(1)
+22 -22
View File
@@ -34,52 +34,52 @@
* RDB / AOF saving process from the child to the parent (for instance
* the amount of copy on write memory used) */
void openChildInfoPipe(void) {
if (pipe(server.child_info_pipe) == -1) {
if (pipe(g_pserver->child_info_pipe) == -1) {
/* On error our two file descriptors should be still set to -1,
* but we call anyway cloesChildInfoPipe() since can't hurt. */
closeChildInfoPipe();
} else if (anetNonBlock(NULL,server.child_info_pipe[0]) != ANET_OK) {
} else if (anetNonBlock(NULL,g_pserver->child_info_pipe[0]) != ANET_OK) {
closeChildInfoPipe();
} else {
memset(&server.child_info_data,0,sizeof(server.child_info_data));
memset(&g_pserver->child_info_data,0,sizeof(g_pserver->child_info_data));
}
}
/* Close the pipes opened with openChildInfoPipe(). */
void closeChildInfoPipe(void) {
if (server.child_info_pipe[0] != -1 ||
server.child_info_pipe[1] != -1)
if (g_pserver->child_info_pipe[0] != -1 ||
g_pserver->child_info_pipe[1] != -1)
{
close(server.child_info_pipe[0]);
close(server.child_info_pipe[1]);
server.child_info_pipe[0] = -1;
server.child_info_pipe[1] = -1;
close(g_pserver->child_info_pipe[0]);
close(g_pserver->child_info_pipe[1]);
g_pserver->child_info_pipe[0] = -1;
g_pserver->child_info_pipe[1] = -1;
}
}
/* Send COW data to parent. The child should call this function after populating
* the corresponding fields it want to sent (according to the process type). */
void sendChildInfo(int ptype) {
if (server.child_info_pipe[1] == -1) return;
server.child_info_data.magic = CHILD_INFO_MAGIC;
server.child_info_data.process_type = ptype;
ssize_t wlen = sizeof(server.child_info_data);
if (write(server.child_info_pipe[1],&server.child_info_data,wlen) != wlen) {
if (g_pserver->child_info_pipe[1] == -1) return;
g_pserver->child_info_data.magic = CHILD_INFO_MAGIC;
g_pserver->child_info_data.process_type = ptype;
ssize_t wlen = sizeof(g_pserver->child_info_data);
if (write(g_pserver->child_info_pipe[1],&g_pserver->child_info_data,wlen) != wlen) {
/* Nothing to do on error, this will be detected by the other side. */
}
}
/* Receive COW data from parent. */
void receiveChildInfo(void) {
if (server.child_info_pipe[0] == -1) return;
ssize_t wlen = sizeof(server.child_info_data);
if (read(server.child_info_pipe[0],&server.child_info_data,wlen) == wlen &&
server.child_info_data.magic == CHILD_INFO_MAGIC)
if (g_pserver->child_info_pipe[0] == -1) return;
ssize_t wlen = sizeof(g_pserver->child_info_data);
if (read(g_pserver->child_info_pipe[0],&g_pserver->child_info_data,wlen) == wlen &&
g_pserver->child_info_data.magic == CHILD_INFO_MAGIC)
{
if (server.child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
server.stat_rdb_cow_bytes = server.child_info_data.cow_size;
} else if (server.child_info_data.process_type == CHILD_INFO_TYPE_AOF) {
server.stat_aof_cow_bytes = server.child_info_data.cow_size;
if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
g_pserver->stat_rdb_cow_bytes = g_pserver->child_info_data.cow_size;
} else if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_AOF) {
g_pserver->stat_aof_cow_bytes = g_pserver->child_info_data.cow_size;
}
}
}
+350 -350
View File
File diff suppressed because it is too large. Load diff
+539 -528
View File
File diff suppressed because it is too large. Load diff
+127 -105
View File
@@ -52,7 +52,7 @@ void updateLFU(robj *val) {
/* Low level key lookup API, not actually called directly from commands
* implementations that should instead rely on lookupKeyRead(),
* lookupKeyWrite() and lookupKeyReadWithFlags(). */
robj *lookupKey(redisDb *db, robj *key, int flags) {
static robj *lookupKey(redisDb *db, robj *key, int flags) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
if (de) {
robj *val = (robj*)dictGetVal(de);
@@ -60,16 +60,20 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
/* Update the access time for the ageing algorithm.
* Don't do it if we have a saving child, as this will trigger
* a copy on write madness. */
if (server.rdb_child_pid == -1 &&
server.aof_child_pid == -1 &&
if (g_pserver->rdb_child_pid == -1 &&
g_pserver->aof_child_pid == -1 &&
!(flags & LOOKUP_NOTOUCH))
{
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
updateLFU(val);
} else {
val->lru = LRU_CLOCK();
}
}
if (flags & LOOKUP_UPDATEMVCC) {
val->mvcc_tstamp = getMvccTstamp();
}
return val;
} else {
return NULL;
@@ -98,7 +102,7 @@ robj *lookupKey(redisDb *db, robj *key, int flags) {
* for read operations. Even if the key expiry is master-driven, we can
* correctly report a key is expired on slaves even if the master is lagging
* expiring our key via DELs in the replication link. */
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
robj *val;
serverAssert(GlobalLocksAcquired());
@@ -106,8 +110,8 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
/* Key expired. If we are in the context of a master, expireIfNeeded()
* returns 0 only when the key does not exist at all, so it's safe
* to return NULL ASAP. */
if (listLength(server.masters) == 0) {
server.stat_keyspace_misses++;
if (listLength(g_pserver->masters) == 0) {
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
}
@@ -124,29 +128,29 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
* will say the key as non existing.
*
* Notably this covers GETs when slaves are used to scale reads. */
if (server.current_client &&
!FActiveMaster(server.current_client) &&
server.current_client->cmd &&
server.current_client->cmd->flags & CMD_READONLY)
if (serverTL->current_client &&
!FActiveMaster(serverTL->current_client) &&
serverTL->current_client->cmd &&
serverTL->current_client->cmd->flags & CMD_READONLY)
{
server.stat_keyspace_misses++;
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
}
}
val = lookupKey(db,key,flags);
if (val == NULL) {
server.stat_keyspace_misses++;
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
}
else
server.stat_keyspace_hits++;
g_pserver->stat_keyspace_hits++;
return val;
}
/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the
* common case. */
robj *lookupKeyRead(redisDb *db, robj *key) {
robj_roptr lookupKeyRead(redisDb *db, robj *key) {
return lookupKeyReadWithFlags(db,key,LOOKUP_NONE);
}
@@ -157,11 +161,11 @@ robj *lookupKeyRead(redisDb *db, robj *key) {
* does not exist in the specified DB. */
robj *lookupKeyWrite(redisDb *db, robj *key) {
expireIfNeeded(db,key);
return lookupKey(db,key,LOOKUP_NONE);
return lookupKey(db,key,LOOKUP_UPDATEMVCC);
}
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
robj *o = lookupKeyRead(c->db, key);
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
robj_roptr o = lookupKeyRead(c->db, key);
if (!o) addReply(c,reply);
return o;
}
@@ -175,13 +179,14 @@ robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) {
int dbAddCore(redisDb *db, robj *key, robj *val) {
sds copy = sdsdup(szFromObj(key));
int retval = dictAdd(db->pdict, copy, val);
val->mvcc_tstamp = key->mvcc_tstamp = getMvccTstamp();
if (retval == DICT_OK)
{
if (val->type == OBJ_LIST ||
val->type == OBJ_ZSET)
signalKeyAsReady(db, key);
if (server.cluster_enabled) slotToKeyAdd(key);
if (g_pserver->cluster_enabled) slotToKeyAdd(key);
}
else
{
@@ -201,18 +206,24 @@ void dbAdd(redisDb *db, robj *key, robj *val)
serverAssertWithInfo(NULL,key,retval == DICT_OK);
}
/* Insert a key, handling duplicate keys according to fReplace */
int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
void dbOverwriteCore(redisDb *db, dictEntry *de, robj *val, bool fUpdateMvcc)
{
if (fReplace)
{
setKey(db, key, val);
return TRUE;
dictEntry auxentry = *de;
robj *old = (robj*)dictGetVal(de);
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
val->lru = old->lru;
}
else
{
return (dbAddCore(db, key, val) == DICT_OK);
if (fUpdateMvcc)
val->mvcc_tstamp = getMvccTstamp();
dictSetVal(db->pdict, de, val);
if (g_pserver->lazyfree_lazy_server_del) {
freeObjAsync(old);
dictSetVal(db->pdict, &auxentry, NULL);
}
dictFreeVal(db->pdict, &auxentry);
}
/* Overwrite an existing key with a new value. Incrementing the reference
@@ -224,19 +235,31 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
serverAssertWithInfo(NULL,key,de != NULL);
dictEntry auxentry = *de;
robj *old = (robj*)dictGetVal(de);
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
val->lru = old->lru;
}
dictSetVal(db->pdict, de, val);
dbOverwriteCore(db, de, val, true);
}
if (server.lazyfree_lazy_server_del) {
freeObjAsync(old);
dictSetVal(db->pdict, &auxentry, NULL);
}
/* Insert a key, handling duplicate keys according to fReplace */
int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
{
if (fReplace)
{
dictEntry *de = dictFind(db->pdict, ptrFromObj(key));
if (de == nullptr)
return (dbAddCore(db, key, val) == DICT_OK);
dictFreeVal(db->pdict, &auxentry);
robj *old = (robj*)dictGetVal(de);
if (old->mvcc_tstamp <= val->mvcc_tstamp)
{
dbOverwriteCore(db, de, val, false);
return true;
}
return false;
}
else
{
return (dbAddCore(db, key, val) == DICT_OK);
}
}
/* High level Set operation. This function can be used in order to set
@@ -281,7 +304,7 @@ robj *dbRandomKey(redisDb *db) {
key = (sds)dictGetKey(de);
keyobj = createStringObject(key,sdslen(key));
if (dictFind(db->expires,key)) {
if (allvolatile && listLength(server.masters) && --maxtries == 0) {
if (allvolatile && listLength(g_pserver->masters) && --maxtries == 0) {
/* If the DB is composed only of keys with an expire set,
* it could happen that all the keys are already logically
* expired in the slave, so the function cannot stop because
@@ -307,7 +330,7 @@ int dbSyncDelete(redisDb *db, robj *key) {
* the key, because it is shared with the main dictionary. */
if (dictSize(db->expires) > 0) dictDelete(db->expires,ptrFromObj(key));
if (dictDelete(db->pdict,ptrFromObj(key)) == DICT_OK) {
if (server.cluster_enabled) slotToKeyDel(key);
if (g_pserver->cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
@@ -317,7 +340,7 @@ int dbSyncDelete(redisDb *db, robj *key) {
/* This is a wrapper whose behavior depends on the Redis lazy free
* configuration. Deletes the key synchronously or asynchronously. */
int dbDelete(redisDb *db, robj *key) {
return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key) :
return g_pserver->lazyfree_lazy_server_del ? dbAsyncDelete(db,key) :
dbSyncDelete(db,key);
}
@@ -359,7 +382,7 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
return o;
}
/* Remove all keys from all the databases in a Redis server.
/* Remove all keys from all the databases in a Redis g_pserver->
* If callback is given the function is called from time to time to
* signal that work is in progress.
*
@@ -377,7 +400,7 @@ long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
int async = (flags & EMPTYDB_ASYNC);
long long removed = 0;
if (dbnum < -1 || dbnum >= server.dbnum) {
if (dbnum < -1 || dbnum >= cserver.dbnum) {
errno = EINVAL;
return -1;
}
@@ -385,21 +408,21 @@ long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
int startdb, enddb;
if (dbnum == -1) {
startdb = 0;
enddb = server.dbnum-1;
enddb = cserver.dbnum-1;
} else {
startdb = enddb = dbnum;
}
for (int j = startdb; j <= enddb; j++) {
removed += dictSize(server.db[j].pdict);
removed += dictSize(g_pserver->db[j].pdict);
if (async) {
emptyDbAsync(&server.db[j]);
emptyDbAsync(&g_pserver->db[j]);
} else {
dictEmpty(server.db[j].pdict,callback);
dictEmpty(server.db[j].expires,callback);
dictEmpty(g_pserver->db[j].pdict,callback);
dictEmpty(g_pserver->db[j].expires,callback);
}
}
if (server.cluster_enabled) {
if (g_pserver->cluster_enabled) {
if (async) {
slotToKeyFlushAsync();
} else {
@@ -411,9 +434,9 @@ long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
}
int selectDb(client *c, int id) {
if (id < 0 || id >= server.dbnum)
if (id < 0 || id >= cserver.dbnum)
return C_ERR;
c->db = &server.db[id];
c->db = &g_pserver->db[id];
return C_OK;
}
@@ -468,7 +491,7 @@ void flushdbCommand(client *c) {
if (getFlushCommandFlags(c,&flags) == C_ERR) return;
signalFlushedDb(c->db->id);
server.dirty += emptyDb(c->db->id,flags,NULL);
g_pserver->dirty += emptyDb(c->db->id,flags,NULL);
addReply(c,shared.ok);
}
@@ -480,19 +503,19 @@ void flushallCommand(client *c) {
if (getFlushCommandFlags(c,&flags) == C_ERR) return;
signalFlushedDb(-1);
server.dirty += emptyDb(-1,flags,NULL);
g_pserver->dirty += emptyDb(-1,flags,NULL);
addReply(c,shared.ok);
if (server.rdb_child_pid != -1) killRDBChild();
if (server.saveparamslen > 0) {
if (g_pserver->rdb_child_pid != -1) killRDBChild();
if (g_pserver->saveparamslen > 0) {
/* Normally rdbSave() will reset dirty, but we don't want this here
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
int saved_dirty = server.dirty;
int saved_dirty = g_pserver->dirty;
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
rdbSave(rsiptr);
server.dirty = saved_dirty;
g_pserver->dirty = saved_dirty;
}
server.dirty++;
g_pserver->dirty++;
}
/* This command implements DEL and LAZYDEL. */
@@ -507,7 +530,7 @@ void delGenericCommand(client *c, int lazy) {
signalModifiedKey(c->db,c->argv[j]);
notifyKeyspaceEvent(NOTIFY_GENERIC,
"del",c->argv[j],c->db->id);
server.dirty++;
g_pserver->dirty++;
numdel++;
}
}
@@ -541,7 +564,7 @@ void selectCommand(client *c) {
"invalid DB index") != C_OK)
return;
if (server.cluster_enabled && id != 0) {
if (g_pserver->cluster_enabled && id != 0) {
addReplyError(c,"SELECT is not allowed in cluster mode");
return;
}
@@ -652,7 +675,7 @@ int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor) {
*
* In the case of a Hash object the function returns both the field and value
* of every element on the Hash. */
void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) {
int i, j;
list *keys = listCreate();
listNode *node, *nextnode;
@@ -663,11 +686,11 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
/* Object must be NULL (to iterate keys names), or the type of the object
* must be Set, Sorted Set, or Hash. */
serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||
serverAssert(o == nullptr || o->type == OBJ_SET || o->type == OBJ_HASH ||
o->type == OBJ_ZSET);
/* Set i to the first option argument. The previous one is the cursor. */
i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */
i = (o == nullptr) ? 2 : 3; /* Skip the key argument if needed. */
/* Step 1: Parse options. */
while (i < c->argc) {
@@ -710,7 +733,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
/* Handle the case of a hash table. */
ht = NULL;
if (o == NULL) {
if (o == nullptr) {
ht = c->db->pdict;
} else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {
ht = (dict*)ptrFromObj(o);
@@ -735,7 +758,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
* add new elements, and the object containing the dictionary so that
* it is possible to fetch more data in a type-dependent way. */
privdata[0] = keys;
privdata[1] = o;
privdata[1] = o.unsafe_robjcast();
do {
cursor = dictScan(ht, cursor, scanCallback, NULL, privdata);
} while (cursor &&
@@ -789,7 +812,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
}
/* Filter element if it is an expired key. */
if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1;
if (!filter && o == nullptr && expireIfNeeded(c->db, kobj)) filter = 1;
/* Remove the element and its associted value if needed. */
if (filter) {
@@ -833,7 +856,7 @@ cleanup:
void scanCommand(client *c) {
unsigned long cursor;
if (parseScanCursorOrReply(c,c->argv[1],&cursor) == C_ERR) return;
scanGenericCommand(c,NULL,cursor);
scanGenericCommand(c,nullptr,cursor);
}
void dbsizeCommand(client *c) {
@@ -841,15 +864,14 @@ void dbsizeCommand(client *c) {
}
void lastsaveCommand(client *c) {
addReplyLongLong(c,server.lastsave);
addReplyLongLong(c,g_pserver->lastsave);
}
void typeCommand(client *c) {
robj *o;
const char *type;
o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH);
if (o == NULL) {
robj_roptr o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH);
if (o == nullptr) {
type = "none";
} else {
switch(o->type) {
@@ -891,7 +913,7 @@ void shutdownCommand(client *c) {
* with half-read data).
*
* Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */
if (server.loading || server.sentinel_mode)
if (g_pserver->loading || g_pserver->sentinel_mode)
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
if (prepareForShutdown(flags) == C_OK) exit(0);
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
@@ -935,7 +957,7 @@ void renameGenericCommand(client *c, int nx) {
c->argv[1],c->db->id);
notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_to",
c->argv[2],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReply(c,nx ? shared.cone : shared.ok);
}
@@ -953,7 +975,7 @@ void moveCommand(client *c) {
int srcid;
long long dbid, expire;
if (server.cluster_enabled) {
if (g_pserver->cluster_enabled) {
addReplyError(c,"MOVE is not allowed in cluster mode");
return;
}
@@ -998,7 +1020,7 @@ void moveCommand(client *c) {
/* OK! key moved, free the entry in the source DB */
dbDelete(src,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
addReply(c,shared.cone);
}
@@ -1029,11 +1051,11 @@ void scanDatabaseForReadyLists(redisDb *db) {
* Returns C_ERR if at least one of the DB ids are out of range, otherwise
* C_OK is returned. */
int dbSwapDatabases(int id1, int id2) {
if (id1 < 0 || id1 >= server.dbnum ||
id2 < 0 || id2 >= server.dbnum) return C_ERR;
if (id1 < 0 || id1 >= cserver.dbnum ||
id2 < 0 || id2 >= cserver.dbnum) return C_ERR;
if (id1 == id2) return C_OK;
redisDb aux = server.db[id1];
redisDb *db1 = &server.db[id1], *db2 = &server.db[id2];
redisDb aux = g_pserver->db[id1];
redisDb *db1 = &g_pserver->db[id1], *db2 = &g_pserver->db[id2];
/* Swap hash tables. Note that we don't swap blocking_keys,
* ready_keys and watched_keys, since we want clients to
@@ -1065,7 +1087,7 @@ void swapdbCommand(client *c) {
long id1, id2;
/* Not allowed in cluster mode: we have just DB 0 there. */
if (server.cluster_enabled) {
if (g_pserver->cluster_enabled) {
addReplyError(c,"SWAPDB is not allowed in cluster mode");
return;
}
@@ -1084,7 +1106,7 @@ void swapdbCommand(client *c) {
addReplyError(c,"DB index is out of range");
return;
} else {
server.dirty++;
g_pserver->dirty++;
addReply(c,shared.ok);
}
}
@@ -1114,14 +1136,14 @@ void setExpire(client *c, redisDb *db, robj *key, long long when) {
de = dictAddOrFind(db->expires,dictGetKey(kde));
dictSetSignedIntegerVal(de,when);
int writable_slave = listLength(server.masters) && server.repl_slave_ro == 0;
int writable_slave = listLength(g_pserver->masters) && g_pserver->repl_slave_ro == 0;
if (c && writable_slave && !(c->flags & CLIENT_MASTER))
rememberSlaveKeyWithExpire(db,key);
}
/* Return the expire time of the specified key, or -1 if no expire
* is associated with this key (i.e. the key is non volatile) */
long long getExpire(redisDb *db, robj *key) {
long long getExpire(redisDb *db, robj_roptr key) {
dictEntry *de;
/* No expire? return ASAP */
@@ -1151,9 +1173,9 @@ void propagateExpire(redisDb *db, robj *key, int lazy) {
incrRefCount(argv[0]);
incrRefCount(argv[1]);
if (server.aof_state != AOF_OFF)
feedAppendOnlyFile(server.delCommand,db->id,argv,2);
replicationFeedSlaves(server.slaves,db->id,argv,2);
if (g_pserver->aof_state != AOF_OFF)
feedAppendOnlyFile(cserver.delCommand,db->id,argv,2);
replicationFeedSlaves(g_pserver->slaves,db->id,argv,2);
decrRefCount(argv[0]);
decrRefCount(argv[1]);
@@ -1166,14 +1188,14 @@ int keyIsExpired(redisDb *db, robj *key) {
if (when < 0) return 0; /* No expire for this key */
/* Don't expire anything while loading. It will be done later. */
if (server.loading) return 0;
if (g_pserver->loading) return 0;
/* If we are in the context of a Lua script, we pretend that time is
* blocked to when the Lua script started. This way a key can expire
* only the first time it is accessed and not in the middle of the
* script execution, making propagation to slaves / AOF consistent.
* See issue #1525 on Github for more information. */
mstime_t now = server.lua_caller ? server.lua_time_start : mstime();
mstime_t now = g_pserver->lua_caller ? g_pserver->lua_time_start : mstime();
return now > when;
}
@@ -1208,14 +1230,14 @@ int expireIfNeeded(redisDb *db, robj *key) {
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
if (listLength(server.masters)) return 1;
if (listLength(g_pserver->masters)) return 1;
/* Delete the key */
server.stat_expiredkeys++;
propagateExpire(db,key,server.lazyfree_lazy_expire);
g_pserver->stat_expiredkeys++;
propagateExpire(db,key,g_pserver->lazyfree_lazy_expire);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",key,db->id);
return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
return g_pserver->lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
dbSyncDelete(db,key);
}
@@ -1506,15 +1528,15 @@ void slotToKeyUpdateKey(robj *key, int add) {
unsigned char buf[64];
unsigned char *indexed = buf;
server.cluster->slots_keys_count[hashslot] += add ? 1 : -1;
g_pserver->cluster->slots_keys_count[hashslot] += add ? 1 : -1;
if (keylen+2 > 64) indexed = (unsigned char*)zmalloc(keylen+2, MALLOC_SHARED);
indexed[0] = (hashslot >> 8) & 0xff;
indexed[1] = hashslot & 0xff;
memcpy(indexed+2,ptrFromObj(key),keylen);
if (add) {
raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);
raxInsert(g_pserver->cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);
} else {
raxRemove(server.cluster->slots_to_keys,indexed,keylen+2,NULL);
raxRemove(g_pserver->cluster->slots_to_keys,indexed,keylen+2,NULL);
}
if (indexed != buf) zfree(indexed);
}
@@ -1528,10 +1550,10 @@ void slotToKeyDel(robj *key) {
}
void slotToKeyFlush(void) {
raxFree(server.cluster->slots_to_keys);
server.cluster->slots_to_keys = raxNew();
memset(server.cluster->slots_keys_count,0,
sizeof(server.cluster->slots_keys_count));
raxFree(g_pserver->cluster->slots_to_keys);
g_pserver->cluster->slots_to_keys = raxNew();
memset(g_pserver->cluster->slots_keys_count,0,
sizeof(g_pserver->cluster->slots_keys_count));
}
/* Pupulate the specified array of objects with keys in the specified slot.
@@ -1544,7 +1566,7 @@ unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int coun
indexed[0] = (hashslot >> 8) & 0xff;
indexed[1] = hashslot & 0xff;
raxStart(&iter,server.cluster->slots_to_keys);
raxStart(&iter,g_pserver->cluster->slots_to_keys);
raxSeek(&iter,">=",indexed,2);
while(count-- && raxNext(&iter)) {
if (iter.key[0] != indexed[0] || iter.key[1] != indexed[1]) break;
@@ -1563,13 +1585,13 @@ unsigned int delKeysInSlot(unsigned int hashslot) {
indexed[0] = (hashslot >> 8) & 0xff;
indexed[1] = hashslot & 0xff;
raxStart(&iter,server.cluster->slots_to_keys);
while(server.cluster->slots_keys_count[hashslot]) {
raxStart(&iter,g_pserver->cluster->slots_to_keys);
while(g_pserver->cluster->slots_keys_count[hashslot]) {
raxSeek(&iter,">=",indexed,2);
raxNext(&iter);
robj *key = createStringObject((char*)iter.key+2,iter.key_len-2);
dbDelete(&server.db[0],key);
dbDelete(&g_pserver->db[0],key);
decrRefCount(key);
j++;
}
@@ -1578,5 +1600,5 @@ unsigned int delKeysInSlot(unsigned int hashslot) {
}
unsigned int countKeysInSlot(unsigned int hashslot) {
return server.cluster->slots_keys_count[hashslot];
return g_pserver->cluster->slots_keys_count[hashslot];
}
+108 -48
View File
@@ -27,6 +27,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#define NO_DEPRECATE_FREE 1 // we are required to call the real free() in this CU
#include "server.h"
#include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
#include "crc64.h"
@@ -45,6 +46,7 @@ typedef ucontext_t sigcontext_t;
#include <fcntl.h>
#include "bio.h"
#include <unistd.h>
#include <cxxabi.h>
#endif /* HAVE_BACKTRACE */
#ifdef __CYGWIN__
@@ -53,6 +55,8 @@ typedef ucontext_t sigcontext_t;
#endif
#endif
bool g_fInCrash = false;
/* ================================= Debugging ============================== */
/* Compute the sha1 of string at 's' with 'len' bytes long.
@@ -61,7 +65,7 @@ typedef ucontext_t sigcontext_t;
* "add" digests relative to unordered elements.
*
* So digest(a,b,c,d) will be the same of digest(b,a,c,d) */
extern "C" void xorDigest(unsigned char *digest, const void *ptr, size_t len) {
void xorDigest(unsigned char *digest, const void *ptr, size_t len) {
SHA1_CTX ctx;
unsigned char hash[20];
const unsigned char *s = (const unsigned char*)ptr;
@@ -95,9 +99,9 @@ void xorStringObjectDigest(unsigned char *digest, robj *o) {
* Also note that mixdigest("foo") followed by mixdigest("bar")
* will lead to a different digest compared to "fo", "obar".
*/
void mixDigest(unsigned char *digest, void *ptr, size_t len) {
void mixDigest(unsigned char *digest, const void *ptr, size_t len) {
SHA1_CTX ctx;
char *s = (char*)ptr;
const char *s = (const char*)ptr;
xorDigest(digest,s,len);
SHA1Init(&ctx);
@@ -105,7 +109,7 @@ void mixDigest(unsigned char *digest, void *ptr, size_t len) {
SHA1Final(digest,&ctx);
}
void mixStringObjectDigest(unsigned char *digest, robj *o) {
void mixStringObjectDigest(unsigned char *digest, robj_roptr o) {
o = getDecodedObject(o);
mixDigest(digest,ptrFromObj(o),sdslen(szFromObj(o)));
decrRefCount(o);
@@ -119,7 +123,7 @@ void mixStringObjectDigest(unsigned char *digest, robj *o) {
* Note that this function does not reset the initial 'digest' passed, it
* will continue mixing this object digest to anything that was already
* present. */
void xorObjectDigest(redisDb *db, robj *keyobj, unsigned char *digest, robj *o) {
void xorObjectDigest(redisDb *db, robj_roptr keyobj, unsigned char *digest, robj_roptr o) {
uint32_t aux = htonl(o->type);
mixDigest(digest,&aux,sizeof(aux));
long long expiretime = getExpire(db,keyobj);
@@ -265,8 +269,8 @@ void computeDatasetDigest(unsigned char *final) {
memset(final,0,20); /* Start with a clean result */
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
if (dictSize(db->pdict) == 0) continue;
di = dictGetSafeIterator(db->pdict);
@@ -344,7 +348,7 @@ NULL
(RESTART_SERVER_GRACEFULLY|RESTART_SERVER_CONFIG_REWRITE) :
RESTART_SERVER_NONE;
restartServer(flags,delay);
addReplyError(c,"failed to restart the server. Check server logs.");
addReplyError(c,"failed to restart the g_pserver-> Check server logs.");
} else if (!strcasecmp(szFromObj(c->argv[1]),"oom")) {
void *ptr = zmalloc(ULONG_MAX, MALLOC_LOCAL); /* Should trigger an out of memory. */
zfree(ptr);
@@ -373,16 +377,16 @@ NULL
serverLog(LL_WARNING,"DB reloaded by DEBUG RELOAD");
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"loadaof")) {
if (server.aof_state != AOF_OFF) flushAppendOnlyFile(1);
if (g_pserver->aof_state != AOF_OFF) flushAppendOnlyFile(1);
emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
protectClient(c);
int ret = loadAppendOnlyFile(server.aof_filename);
int ret = loadAppendOnlyFile(g_pserver->aof_filename);
unprotectClient(c);
if (ret != C_OK) {
addReply(c,shared.err);
return;
}
server.dirty = 0; /* Prevent AOF / replication */
g_pserver->dirty = 0; /* Prevent AOF / replication */
serverLog(LL_WARNING,"Append Only File loaded by DEBUG LOADAOF");
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"object") && c->argc == 3) {
@@ -434,7 +438,7 @@ NULL
"Value at:%p refcount:%d "
"encoding:%s serializedlength:%zu "
"lru:%d lru_seconds_idle:%llu%s",
(void*)val, val->refcount,
(void*)val, static_cast<int>(val->refcount),
strenc, rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val)/1000, extra);
} else if (!strcasecmp(szFromObj(c->argv[1]),"sdslen") && c->argc == 3) {
@@ -523,7 +527,7 @@ NULL
for (int j = 2; j < c->argc; j++) {
unsigned char digest[20];
memset(digest,0,20); /* Start with a clean result */
robj *o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);
robj_roptr o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);
if (o) xorObjectDigest(c->db,c->argv[j],digest,o);
sds d = sdsempty();
@@ -595,12 +599,12 @@ NULL
} else if (!strcasecmp(szFromObj(c->argv[1]),"set-active-expire") &&
c->argc == 3)
{
server.active_expire_enabled = atoi(szFromObj(c->argv[2]));
g_pserver->active_expire_enabled = atoi(szFromObj(c->argv[2]));
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"lua-always-replicate-commands") &&
c->argc == 3)
{
server.lua_always_replicate_commands = atoi(szFromObj(c->argv[2]));
g_pserver->lua_always_replicate_commands = atoi(szFromObj(c->argv[2]));
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"error") && c->argc == 3) {
sds errstr = sdsnewlen("-",1);
@@ -627,17 +631,17 @@ NULL
if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK)
return;
if (dbid < 0 || dbid >= server.dbnum) {
if (dbid < 0 || dbid >= cserver.dbnum) {
addReplyError(c,"Out of range database");
return;
}
stats = sdscatprintf(stats,"[Dictionary HT]\n");
dictGetStats(buf,sizeof(buf),server.db[dbid].pdict);
dictGetStats(buf,sizeof(buf),g_pserver->db[dbid].pdict);
stats = sdscat(stats,buf);
stats = sdscatprintf(stats,"[Expires HT]\n");
dictGetStats(buf,sizeof(buf),server.db[dbid].expires);
dictGetStats(buf,sizeof(buf),g_pserver->db[dbid].expires);
stats = sdscat(stats,buf);
addReplyBulkSds(c,stats);
@@ -691,9 +695,9 @@ void _serverAssert(const char *estr, const char *file, int line) {
serverLog(LL_WARNING,"=== ASSERTION FAILED ===");
serverLog(LL_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
#ifdef HAVE_BACKTRACE
server.assert_failed = estr;
server.assert_file = file;
server.assert_line = line;
g_pserver->assert_failed = estr;
g_pserver->assert_file = file;
g_pserver->assert_line = line;
serverLog(LL_WARNING,"(forcing SIGSEGV to print the bug report.)");
#endif
*((char*)-1) = 'x';
@@ -704,7 +708,7 @@ void _serverAssertPrintClientInfo(const client *c) {
bugReportStart();
serverLog(LL_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ===");
serverLog(LL_WARNING,"client->flags = %d", c->flags);
serverLog(LL_WARNING,"client->flags = %d", static_cast<int>(c->flags));
serverLog(LL_WARNING,"client->fd = %d", c->fd);
serverLog(LL_WARNING,"client->argc = %d", c->argc);
for (j=0; j < c->argc; j++) {
@@ -719,14 +723,14 @@ void _serverAssertPrintClientInfo(const client *c) {
arg = buf;
}
serverLog(LL_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)",
j, arg, c->argv[j]->refcount);
j, arg, static_cast<int>(c->argv[j]->refcount));
}
}
void serverLogObjectDebugInfo(const robj *o) {
void serverLogObjectDebugInfo(robj_roptr o) {
serverLog(LL_WARNING,"Object type: %d", o->type);
serverLog(LL_WARNING,"Object encoding: %d", o->encoding);
serverLog(LL_WARNING,"Object refcount: %d", o->refcount);
serverLog(LL_WARNING,"Object refcount: %d", static_cast<int>(o->refcount));
if (o->type == OBJ_STRING && sdsEncodedObject(o)) {
serverLog(LL_WARNING,"Object raw string len: %zu", sdslen(szFromObj(o)));
if (sdslen(szFromObj(o)) < 4096) {
@@ -747,13 +751,13 @@ void serverLogObjectDebugInfo(const robj *o) {
}
}
void _serverAssertPrintObject(const robj *o) {
void _serverAssertPrintObject(robj_roptr o) {
bugReportStart();
serverLog(LL_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ===");
serverLogObjectDebugInfo(o);
}
void _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line) {
void _serverAssertWithInfo(const client *c, robj_roptr o, const char *estr, const char *file, int line) {
if (c) _serverAssertPrintClientInfo(c);
if (o) _serverAssertPrintObject(o);
_serverAssert(estr,file,line);
@@ -778,10 +782,10 @@ void _serverPanic(const char *file, int line, const char *msg, ...) {
}
void bugReportStart(void) {
if (server.bug_report_start == 0) {
if (g_pserver->bug_report_start == 0) {
serverLogRaw(LL_WARNING|LL_RAW,
"\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ===\n");
server.bug_report_start = 1;
"\n\n=== KEYDB BUG REPORT START: Cut & paste starting from here ===\n");
g_pserver->bug_report_start = 1;
}
}
@@ -1125,19 +1129,74 @@ void logRegisters(ucontext_t *uc) {
*
* Close it with closeDirectLogFiledes(). */
int openDirectLogFiledes(void) {
int log_to_stdout = server.logfile[0] == '\0';
int log_to_stdout = g_pserver->logfile[0] == '\0';
int fd = log_to_stdout ?
STDOUT_FILENO :
open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
open(g_pserver->logfile, O_APPEND|O_CREAT|O_WRONLY, 0644);
return fd;
}
/* Used to close what closeDirectLogFiledes() returns. */
void closeDirectLogFiledes(int fd) {
int log_to_stdout = server.logfile[0] == '\0';
int log_to_stdout = g_pserver->logfile[0] == '\0';
if (!log_to_stdout) close(fd);
}
void safe_write(int fd, const void *pv, ssize_t cb)
{
ssize_t offset = 0;
do
{
ssize_t cbWrite = write(fd, reinterpret_cast<const char*>(pv)+offset, cb-offset);
if (cbWrite <= 0)
return;
offset += cbWrite;
} while (offset < cb);
}
void backtrace_symbols_demangle_fd(void **trace, size_t csym, int fd)
{
char **syms = backtrace_symbols(trace, csym);
char symbuf[1024];
for (size_t itrace = 0; itrace < csym; ++itrace)
{
int status = 0;
// First find the symbol (preceded by a '(')
char *pchSymStart = syms[itrace];
while (*pchSymStart != '(' && *pchSymStart != '\0')
++pchSymStart;
if (*pchSymStart != '\0')
++pchSymStart; // skip the '('
char *pchSymEnd = pchSymStart;
while (*pchSymEnd != '+' && *pchSymEnd != '\0')
++pchSymEnd;
if ((pchSymEnd - pchSymStart) < 1023)
{
memcpy(symbuf, pchSymStart, pchSymEnd - pchSymStart);
symbuf[pchSymEnd - pchSymStart] = '\0';
char *sz = abi::__cxa_demangle(symbuf, nullptr, nullptr, &status);
if (status == 0)
{
safe_write(fd, syms[itrace], pchSymStart - syms[itrace]);
safe_write(fd, sz, strlen(sz));
safe_write(fd, pchSymEnd, (syms[itrace] + strlen(syms[itrace])-pchSymEnd));
}
else
{
safe_write(fd, syms[itrace], strlen(syms[itrace]));
}
free(sz);
}
else {
safe_write(fd, syms[itrace], strlen(syms[itrace]));
}
safe_write(fd, "\n", 1);
}
free(syms);
}
/* Logs the stack trace using the backtrace() call. This function is designed
* to be called from signal handlers safely. */
void logStackTrace(ucontext_t *uc) {
@@ -1154,12 +1213,12 @@ void logStackTrace(ucontext_t *uc) {
const char *msg2 = "\nBacktrace:\n";
if (write(fd,msg1,strlen(msg1)) == -1) {/* Avoid warning. */};
trace[0] = getMcontextEip(uc);
backtrace_symbols_fd(trace, 1, fd);
backtrace_symbols_demangle_fd(trace, 1, fd);
if (write(fd,msg2,strlen(msg2)) == -1) {/* Avoid warning. */};
}
/* Write symbols to log file */
backtrace_symbols_fd(trace+1, trace_size, fd);
backtrace_symbols_demangle_fd(trace+1, trace_size, fd);
/* Cleanup */
closeDirectLogFiledes(fd);
@@ -1169,9 +1228,9 @@ void logStackTrace(ucontext_t *uc) {
* currently being served by Redis. May be NULL if Redis is not serving a
* client right now. */
void logCurrentClient(void) {
if (server.current_client == NULL) return;
if (serverTL->current_client == NULL) return;
client *cc = server.current_client;
client *cc = serverTL->current_client;
sds client;
int j;
@@ -1299,6 +1358,7 @@ void dumpX86Calls(void *addr, size_t len) {
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
ucontext_t *uc = (ucontext_t*) secret;
g_fInCrash = true;
void *eip = getMcontextEip(uc);
sds infostring, clients;
struct sigaction act;
@@ -1306,7 +1366,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
bugReportStart();
serverLog(LL_WARNING,
"Redis %s crashed by signal: %d", REDIS_VERSION, sig);
"KeyDB %s crashed by signal: %d", KEYDB_REAL_VERSION, sig);
if (eip != NULL) {
serverLog(LL_WARNING,
"Crashed running the instruction at: %p", eip);
@@ -1316,8 +1376,8 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
"Accessing address: %p", (void*)info->si_addr);
}
serverLog(LL_WARNING,
"Failed assertion: %s (%s:%d)", server.assert_failed,
server.assert_file, server.assert_line);
"Failed assertion: %s (%s:%d)", g_pserver->assert_failed,
g_pserver->assert_file, g_pserver->assert_line);
/* Log the stack trace */
serverLogRaw(LL_WARNING|LL_RAW, "\n------ STACK TRACE ------\n");
@@ -1382,14 +1442,14 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
}
serverLogRaw(LL_WARNING|LL_RAW,
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
"\n=== KEYDB BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash by opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
" https://github.com/JohnSully/KeyDB/issues\n\n"
" Suspect RAM error? Use keydb-server --test-memory to verify it.\n\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize && server.supervised == 0) unlink(server.pidfile);
if (cserver.daemonize && cserver.supervised == 0) unlink(cserver.pidfile);
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled. */
@@ -1465,7 +1525,7 @@ void watchdogScheduleSignal(int period) {
void enableWatchdog(int period) {
int min_period;
if (server.watchdog_period == 0) {
if (g_pserver->watchdog_period == 0) {
struct sigaction act;
/* Watchdog was actually disabled, so we have to setup the signal
@@ -1478,16 +1538,16 @@ void enableWatchdog(int period) {
/* If the configured period is smaller than twice the timer period, it is
* too short for the software watchdog to work reliably. Fix it now
* if needed. */
min_period = (1000/server.hz)*2;
min_period = (1000/g_pserver->hz)*2;
if (period < min_period) period = min_period;
watchdogScheduleSignal(period); /* Adjust the current timer. */
server.watchdog_period = period;
g_pserver->watchdog_period = period;
}
/* Disable the software watchdog. */
void disableWatchdog(void) {
struct sigaction act;
if (server.watchdog_period == 0) return; /* Already disabled. */
if (g_pserver->watchdog_period == 0) return; /* Already disabled. */
watchdogScheduleSignal(0); /* Stop the current timer. */
/* Set the signal handler to SIG_IGN, this will also remove pending
@@ -1496,5 +1556,5 @@ void disableWatchdog(void) {
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
sigaction(SIGALRM, &act, NULL);
server.watchdog_period = 0;
g_pserver->watchdog_period = 0;
}
+64 -64
View File
@@ -47,7 +47,7 @@ extern "C" int je_get_defrag_hint(void* ptr, int *bin_util, int *run_util);
/* forward declarations*/
void defragDictBucketCallback(void *privdata, dictEntry **bucketref);
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, unsigned int hash, long *defragged);
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged);
/* Defrag helper for generic allocations.
*
@@ -59,14 +59,14 @@ void* activeDefragAlloc(void *ptr) {
size_t size;
void *newptr;
if(!je_get_defrag_hint(ptr, &bin_util, &run_util)) {
server.stat_active_defrag_misses++;
g_pserver->stat_active_defrag_misses++;
return NULL;
}
/* if this run is more utilized than the average utilization in this bin
* (or it is full), skip it. This will eventually move all the allocations
* from relatively empty runs into relatively full runs. */
if (run_util > bin_util || run_util == 1<<16) {
server.stat_active_defrag_misses++;
g_pserver->stat_active_defrag_misses++;
return NULL;
}
/* move this allocation to a new allocation.
@@ -353,7 +353,7 @@ long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
sdsele = (sds)ln->value;
if ((newsds = activeDefragSds(sdsele))) {
/* When defragging an sds value, we need to update the dict key */
unsigned int hash = dictGetHash(d, sdsele);
uint64_t hash = dictGetHash(d, sdsele);
replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);
ln->value = newsds;
defragged++;
@@ -390,7 +390,7 @@ long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
* moved. Return value is the the dictEntry if found, or NULL if not found.
* NOTE: this is very ugly code, but it let's us avoid the complication of
* doing a scan on another dict. */
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, unsigned int hash, long *defragged) {
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) {
dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);
if (deref) {
dictEntry *de = *deref;
@@ -442,7 +442,7 @@ long scanLaterList(robj *ob) {
quicklist *ql = (quicklist*)ptrFromObj(ob);
if (ob->type != OBJ_LIST || ob->encoding != OBJ_ENCODING_QUICKLIST)
return 0;
server.stat_active_defrag_scanned+=ql->len;
g_pserver->stat_active_defrag_scanned+=ql->len;
return activeDefragQuickListNodes(ql);
}
@@ -455,7 +455,7 @@ void scanLaterZsetCallback(void *privdata, const dictEntry *_de) {
dictEntry *de = (dictEntry*)_de;
scanLaterZsetData *data = (scanLaterZsetData*)privdata;
data->defragged += activeDefragZsetEntry(data->zs, de);
server.stat_active_defrag_scanned++;
g_pserver->stat_active_defrag_scanned++;
}
long scanLaterZset(robj *ob, unsigned long *cursor) {
@@ -474,7 +474,7 @@ void scanLaterSetCallback(void *privdata, const dictEntry *_de) {
sds sdsele = (sds)dictGetKey(de), newsds;
if ((newsds = activeDefragSds(sdsele)))
(*defragged)++, de->key = newsds;
server.stat_active_defrag_scanned++;
g_pserver->stat_active_defrag_scanned++;
}
long scanLaterSet(robj *ob, unsigned long *cursor) {
@@ -495,7 +495,7 @@ void scanLaterHashCallback(void *privdata, const dictEntry *_de) {
sdsele = (sds)dictGetVal(de);
if ((newsds = activeDefragSds(sdsele)))
(*defragged)++, de->v.val = newsds;
server.stat_active_defrag_scanned++;
g_pserver->stat_active_defrag_scanned++;
}
long scanLaterHash(robj *ob, unsigned long *cursor) {
@@ -514,7 +514,7 @@ long defragQuicklist(redisDb *db, dictEntry *kde) {
serverAssert(ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST);
if ((newql = (quicklist*)activeDefragAlloc(ql)))
defragged++, ob->m_ptr = ql = newql;
if (ql->len > server.active_defrag_max_scan_fields)
if (ql->len > cserver.active_defrag_max_scan_fields)
defragLater(db, kde);
else
defragged += activeDefragQuickListNodes(ql);
@@ -537,7 +537,7 @@ long defragZsetSkiplist(redisDb *db, dictEntry *kde) {
defragged++, zs->zsl = newzsl;
if ((newheader = (zskiplistNode*)activeDefragAlloc(zs->zsl->header)))
defragged++, zs->zsl->header = newheader;
if (dictSize(zs->pdict) > server.active_defrag_max_scan_fields)
if (dictSize(zs->pdict) > cserver.active_defrag_max_scan_fields)
defragLater(db, kde);
else {
dictIterator *di = dictGetIterator(zs->pdict);
@@ -560,7 +560,7 @@ long defragHash(redisDb *db, dictEntry *kde) {
dict *d, *newd;
serverAssert(ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HT);
d = (dict*)ptrFromObj(ob);
if (dictSize(d) > server.active_defrag_max_scan_fields)
if (dictSize(d) > cserver.active_defrag_max_scan_fields)
defragLater(db, kde);
else
defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_VAL_IS_SDS);
@@ -578,7 +578,7 @@ long defragSet(redisDb *db, dictEntry *kde) {
dict *d, *newd;
serverAssert(ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HT);
d = (dict*)ptrFromObj(ob);
if (dictSize(d) > server.active_defrag_max_scan_fields)
if (dictSize(d) > cserver.active_defrag_max_scan_fields)
defragLater(db, kde);
else
defragged += activeDefragSdsDict(d, DEFRAG_SDS_DICT_NO_VAL);
@@ -742,7 +742,7 @@ long defragStream(redisDb *db, dictEntry *kde) {
if ((news = (stream*)activeDefragAlloc(s)))
defragged++, ob->m_ptr = s = news;
if (raxSize(s->prax) > server.active_defrag_max_scan_fields) {
if (raxSize(s->prax) > cserver.active_defrag_max_scan_fields) {
rax *newrax = (rax*)activeDefragAlloc(s->prax);
if (newrax)
defragged++, s->prax = newrax;
@@ -837,12 +837,12 @@ long defragKey(redisDb *db, dictEntry *de) {
/* Defrag scan callback for the main db dictionary. */
void defragScanCallback(void *privdata, const dictEntry *de) {
long defragged = defragKey((redisDb*)privdata, (dictEntry*)de);
server.stat_active_defrag_hits += defragged;
g_pserver->stat_active_defrag_hits += defragged;
if(defragged)
server.stat_active_defrag_key_hits++;
g_pserver->stat_active_defrag_key_hits++;
else
server.stat_active_defrag_key_misses++;
server.stat_active_defrag_scanned++;
g_pserver->stat_active_defrag_key_misses++;
g_pserver->stat_active_defrag_scanned++;
}
/* Defrag scan callback for each hash table bicket,
@@ -887,8 +887,8 @@ long defragOtherGlobals() {
/* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc.
* but we assume most of these are short lived, we only need to defrag allocations
* that remain static for a long time */
defragged += activeDefragSdsDict(server.lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB);
defragged += activeDefragSdsListAndDict(server.repl_scriptcache_fifo, server.repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL);
defragged += activeDefragSdsDict(g_pserver->lua_scripts, DEFRAG_SDS_DICT_VAL_IS_STROB);
defragged += activeDefragSdsListAndDict(g_pserver->repl_scriptcache_fifo, g_pserver->repl_scriptcache_dict, DEFRAG_SDS_DICT_NO_VAL);
return defragged;
}
@@ -898,16 +898,16 @@ int defragLaterItem(dictEntry *de, unsigned long *cursor, long long endtime) {
if (de) {
robj *ob = (robj*)dictGetVal(de);
if (ob->type == OBJ_LIST) {
server.stat_active_defrag_hits += scanLaterList(ob);
g_pserver->stat_active_defrag_hits += scanLaterList(ob);
*cursor = 0; /* list has no scan, we must finish it in one go */
} else if (ob->type == OBJ_SET) {
server.stat_active_defrag_hits += scanLaterSet(ob, cursor);
g_pserver->stat_active_defrag_hits += scanLaterSet(ob, cursor);
} else if (ob->type == OBJ_ZSET) {
server.stat_active_defrag_hits += scanLaterZset(ob, cursor);
g_pserver->stat_active_defrag_hits += scanLaterZset(ob, cursor);
} else if (ob->type == OBJ_HASH) {
server.stat_active_defrag_hits += scanLaterHash(ob, cursor);
g_pserver->stat_active_defrag_hits += scanLaterHash(ob, cursor);
} else if (ob->type == OBJ_STREAM) {
return scanLaterStraemListpacks(ob, cursor, endtime, &server.stat_active_defrag_hits);
return scanLaterStraemListpacks(ob, cursor, endtime, &g_pserver->stat_active_defrag_hits);
} else {
*cursor = 0; /* object type may have changed since we schedule it for later */
}
@@ -922,8 +922,8 @@ int defragLaterStep(redisDb *db, long long endtime) {
static sds current_key = NULL;
static unsigned long cursor = 0;
unsigned int iterations = 0;
unsigned long long prev_defragged = server.stat_active_defrag_hits;
unsigned long long prev_scanned = server.stat_active_defrag_scanned;
unsigned long long prev_defragged = g_pserver->stat_active_defrag_hits;
unsigned long long prev_scanned = g_pserver->stat_active_defrag_scanned;
long long key_defragged;
do {
@@ -952,7 +952,7 @@ int defragLaterStep(redisDb *db, long long endtime) {
/* each time we enter this function we need to fetch the key from the dict again (if it still exists) */
dictEntry *de = dictFind(db->pdict, current_key);
key_defragged = server.stat_active_defrag_hits;
key_defragged = g_pserver->stat_active_defrag_hits;
do {
int quit = 0;
if (defragLaterItem(de, &cursor, endtime))
@@ -967,24 +967,24 @@ int defragLaterStep(redisDb *db, long long endtime) {
* (if we have a lot of pointers in one hash bucket, or rehashing),
* check if we reached the time limit. */
if (quit || (++iterations > 16 ||
server.stat_active_defrag_hits - prev_defragged > 512 ||
server.stat_active_defrag_scanned - prev_scanned > 64)) {
g_pserver->stat_active_defrag_hits - prev_defragged > 512 ||
g_pserver->stat_active_defrag_scanned - prev_scanned > 64)) {
if (quit || ustime() > endtime) {
if(key_defragged != server.stat_active_defrag_hits)
server.stat_active_defrag_key_hits++;
if(key_defragged != g_pserver->stat_active_defrag_hits)
g_pserver->stat_active_defrag_key_hits++;
else
server.stat_active_defrag_key_misses++;
g_pserver->stat_active_defrag_key_misses++;
return 1;
}
iterations = 0;
prev_defragged = server.stat_active_defrag_hits;
prev_scanned = server.stat_active_defrag_scanned;
prev_defragged = g_pserver->stat_active_defrag_hits;
prev_scanned = g_pserver->stat_active_defrag_scanned;
}
} while(cursor);
if(key_defragged != server.stat_active_defrag_hits)
server.stat_active_defrag_key_hits++;
if(key_defragged != g_pserver->stat_active_defrag_hits)
g_pserver->stat_active_defrag_key_hits++;
else
server.stat_active_defrag_key_misses++;
g_pserver->stat_active_defrag_key_misses++;
} while(1);
}
@@ -996,26 +996,26 @@ void computeDefragCycles() {
size_t frag_bytes;
float frag_pct = getAllocatorFragmentation(&frag_bytes);
/* If we're not already running, and below the threshold, exit. */
if (!server.active_defrag_running) {
if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes)
if (!g_pserver->active_defrag_running) {
if(frag_pct < cserver.active_defrag_threshold_lower || frag_bytes < cserver.active_defrag_ignore_bytes)
return;
}
/* Calculate the adaptive aggressiveness of the defrag */
int cpu_pct = INTERPOLATE(frag_pct,
server.active_defrag_threshold_lower,
server.active_defrag_threshold_upper,
server.active_defrag_cycle_min,
server.active_defrag_cycle_max);
cserver.active_defrag_threshold_lower,
cserver.active_defrag_threshold_upper,
cserver.active_defrag_cycle_min,
cserver.active_defrag_cycle_max);
cpu_pct = LIMIT(cpu_pct,
server.active_defrag_cycle_min,
server.active_defrag_cycle_max);
cserver.active_defrag_cycle_min,
cserver.active_defrag_cycle_max);
/* We allow increasing the aggressiveness during a scan, but don't
* reduce it. */
if (!server.active_defrag_running ||
cpu_pct > server.active_defrag_running)
if (!g_pserver->active_defrag_running ||
cpu_pct > g_pserver->active_defrag_running)
{
server.active_defrag_running = cpu_pct;
g_pserver->active_defrag_running = cpu_pct;
serverLog(LL_VERBOSE,
"Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%",
frag_pct, frag_bytes, cpu_pct);
@@ -1031,13 +1031,13 @@ void activeDefragCycle(void) {
static redisDb *db = NULL;
static long long start_scan, start_stat;
unsigned int iterations = 0;
unsigned long long prev_defragged = server.stat_active_defrag_hits;
unsigned long long prev_scanned = server.stat_active_defrag_scanned;
unsigned long long prev_defragged = g_pserver->stat_active_defrag_hits;
unsigned long long prev_scanned = g_pserver->stat_active_defrag_scanned;
long long start, timelimit, endtime;
mstime_t latency;
int quit = 0;
if (server.aof_child_pid!=-1 || server.rdb_child_pid!=-1)
if (g_pserver->aof_child_pid!=-1 || g_pserver->rdb_child_pid!=-1)
return; /* Defragging memory while there's a fork will just do damage. */
/* Once a second, check if we the fragmentation justfies starting a scan
@@ -1045,12 +1045,12 @@ void activeDefragCycle(void) {
run_with_period(1000) {
computeDefragCycles();
}
if (!server.active_defrag_running)
if (!g_pserver->active_defrag_running)
return;
/* See activeExpireCycle for how timelimit is handled. */
start = ustime();
timelimit = 1000000*server.active_defrag_running/server.hz/100;
timelimit = 1000000*g_pserver->active_defrag_running/g_pserver->hz/100;
if (timelimit <= 0) timelimit = 1;
endtime = start + timelimit;
latencyStartMonitor(latency);
@@ -1065,7 +1065,7 @@ void activeDefragCycle(void) {
}
/* Move on to next database, and stop if we reached the last one. */
if (++current_db >= server.dbnum) {
if (++current_db >= cserver.dbnum) {
/* defrag other items not part of the db / keys */
defragOtherGlobals();
@@ -1074,26 +1074,26 @@ void activeDefragCycle(void) {
float frag_pct = getAllocatorFragmentation(&frag_bytes);
serverLog(LL_VERBOSE,
"Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu",
(int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);
(int)((now - start_scan)/1000), (int)(g_pserver->stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);
start_scan = now;
current_db = -1;
cursor = 0;
db = NULL;
server.active_defrag_running = 0;
g_pserver->active_defrag_running = 0;
computeDefragCycles(); /* if another scan is needed, start it right away */
if (server.active_defrag_running != 0 && ustime() < endtime)
if (g_pserver->active_defrag_running != 0 && ustime() < endtime)
continue;
break;
}
else if (current_db==0) {
/* Start a scan from the first database. */
start_scan = ustime();
start_stat = server.stat_active_defrag_hits;
start_stat = g_pserver->stat_active_defrag_hits;
}
db = &server.db[current_db];
db = &g_pserver->db[current_db];
cursor = 0;
}
@@ -1112,15 +1112,15 @@ void activeDefragCycle(void) {
* But regardless, don't start a new db in this loop, this is because after
* the last db we call defragOtherGlobals, which must be done in once cycle */
if (!cursor || (++iterations > 16 ||
server.stat_active_defrag_hits - prev_defragged > 512 ||
server.stat_active_defrag_scanned - prev_scanned > 64)) {
g_pserver->stat_active_defrag_hits - prev_defragged > 512 ||
g_pserver->stat_active_defrag_scanned - prev_scanned > 64)) {
if (!cursor || ustime() > endtime) {
quit = 1;
break;
}
iterations = 0;
prev_defragged = server.stat_active_defrag_hits;
prev_scanned = server.stat_active_defrag_scanned;
prev_defragged = g_pserver->stat_active_defrag_hits;
prev_scanned = g_pserver->stat_active_defrag_scanned;
}
} while(cursor && !quit);
} while(!quit);
+47 -47
View File
@@ -77,8 +77,8 @@ unsigned int getLRUClock(void) {
* precomputed value, otherwise we need to resort to a system call. */
unsigned int LRU_CLOCK(void) {
unsigned int lruclock;
if (1000/server.hz <= LRU_CLOCK_RESOLUTION) {
atomicGet(server.lruclock,lruclock);
if (1000/g_pserver->hz <= LRU_CLOCK_RESOLUTION) {
lruclock = g_pserver->lruclock;
} else {
lruclock = getLRUClock();
}
@@ -111,7 +111,7 @@ unsigned long long estimateObjectIdleTime(robj *o) {
* If all the bytes needed to return back under the limit were freed the
* function returns C_OK, otherwise C_ERR is returned, and the caller
* should block the execution of commands that will result in more memory
* used by the server.
* used by the g_pserver->
*
* ------------------------------------------------------------------------
*
@@ -161,13 +161,13 @@ void evictionPoolAlloc(void) {
void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {
int j, k, count;
dictEntry **samples = (dictEntry**)alloca(server.maxmemory_samples * sizeof(dictEntry*));
dictEntry **samples = (dictEntry**)alloca(g_pserver->maxmemory_samples * sizeof(dictEntry*));
count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);
count = dictGetSomeKeys(sampledict,samples,g_pserver->maxmemory_samples);
for (j = 0; j < count; j++) {
unsigned long long idle;
sds key;
robj *o;
robj *o = nullptr;
dictEntry *de;
de = samples[j];
@@ -176,7 +176,7 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
/* If the dictionary we are sampling from is not the main
* dictionary (but the expires one) we need to lookup the key
* again in the key dictionary to obtain the value object. */
if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
if (g_pserver->maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {
if (sampledict != keydict) de = dictFind(keydict, key);
o = (robj*)dictGetVal(de);
}
@@ -184,9 +184,9 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
/* Calculate the idle time according to the policy. This is called
* idle just because the code initially handled LRU, but is in fact
* just a score where an higher score means better candidate. */
if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
idle = estimateObjectIdleTime(o);
} else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU) {
idle = (o != nullptr) ? estimateObjectIdleTime(o) : 0;
} else if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
/* When we use an LRU policy, we sort the keys by idle time
* so that we expire keys starting from greater idle time.
* However when the policy is an LFU one, we have a frequency
@@ -195,7 +195,7 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
* frequency subtracting the actual frequency to the maximum
* frequency of 255. */
idle = 255-LFUDecrAndReturn(o);
} else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
} else if (g_pserver->maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
/* In this case the sooner the expire the better. */
idle = ULLONG_MAX - (long)dictGetVal(de);
} else {
@@ -297,7 +297,7 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
* 16 bits. The returned time is suitable to be stored as LDT (last decrement
* time) for the LFU implementation. */
unsigned long LFUGetTimeInMinutes(void) {
return (server.unixtime/60) & 65535;
return (g_pserver->unixtime/60) & 65535;
}
/* Given an object last access time, compute the minimum number of minutes
@@ -317,7 +317,7 @@ uint8_t LFULogIncr(uint8_t counter) {
double r = (double)rand()/RAND_MAX;
double baseval = counter - LFU_INIT_VAL;
if (baseval < 0) baseval = 0;
double p = 1.0/(baseval*server.lfu_log_factor+1);
double p = 1.0/(baseval*g_pserver->lfu_log_factor+1);
if (r < p) counter++;
return counter;
}
@@ -326,7 +326,7 @@ uint8_t LFULogIncr(uint8_t counter) {
* do not update LFU fields of the object, we update the access time
* and counter in an explicit way when the object is really accessed.
* And we will times halve the counter according to the times of
* elapsed time than server.lfu_decay_time.
* elapsed time than g_pserver->lfu_decay_time.
* Return the object frequency counter.
*
* This function is used in order to scan the dataset for the best object
@@ -335,7 +335,7 @@ uint8_t LFULogIncr(uint8_t counter) {
unsigned long LFUDecrAndReturn(robj *o) {
unsigned long ldt = o->lru >> 8;
unsigned long counter = o->lru & 255;
unsigned long num_periods = server.lfu_decay_time ? LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;
unsigned long num_periods = g_pserver->lfu_decay_time ? LFUTimeElapsed(ldt) / g_pserver->lfu_decay_time : 0;
if (num_periods)
counter = (num_periods > counter) ? 0 : counter - num_periods;
return counter;
@@ -352,20 +352,20 @@ unsigned long LFUDecrAndReturn(robj *o) {
size_t freeMemoryGetNotCountedMemory(void) {
serverAssert(GlobalLocksAcquired());
size_t overhead = 0;
int slaves = listLength(server.slaves);
int slaves = listLength(g_pserver->slaves);
if (slaves) {
listIter li;
listNode *ln;
listRewind(server.slaves,&li);
listRewind(g_pserver->slaves,&li);
while((ln = listNext(&li))) {
client *slave = (client*)listNodeValue(ln);
overhead += getClientOutputBufferMemoryUsage(slave);
}
}
if (server.aof_state != AOF_OFF) {
overhead += sdsalloc(server.aof_buf)+aofRewriteBufferSize();
if (g_pserver->aof_state != AOF_OFF) {
overhead += sdsalloc(g_pserver->aof_buf)+aofRewriteBufferSize();
}
return overhead;
}
@@ -403,7 +403,7 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
if (total) *total = mem_reported;
/* We may return ASAP if there is no need to compute the level. */
int return_ok_asap = !server.maxmemory || mem_reported <= server.maxmemory;
int return_ok_asap = !g_pserver->maxmemory || mem_reported <= g_pserver->maxmemory;
if (return_ok_asap && !level) return C_OK;
/* Remove the size of slaves output buffers and AOF buffer from the
@@ -414,20 +414,20 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
/* Compute the ratio of memory usage. */
if (level) {
if (!server.maxmemory) {
if (!g_pserver->maxmemory) {
*level = 0;
} else {
*level = (float)mem_used / (float)server.maxmemory;
*level = (float)mem_used / (float)g_pserver->maxmemory;
}
}
if (return_ok_asap) return C_OK;
/* Check if we are still over the memory limit. */
if (mem_used <= server.maxmemory) return C_OK;
if (mem_used <= g_pserver->maxmemory) return C_OK;
/* Compute how much memory we need to free. */
mem_tofree = mem_used - server.maxmemory;
mem_tofree = mem_used - g_pserver->maxmemory;
if (logical) *logical = mem_used;
if (tofree) *tofree = mem_tofree;
@@ -448,12 +448,12 @@ int freeMemoryIfNeeded(void) {
serverAssert(GlobalLocksAcquired());
/* By default replicas should ignore maxmemory
* and just be masters exact copies. */
if (listLength(server.masters) && server.repl_slave_ignore_maxmemory) return C_OK;
if (listLength(g_pserver->masters) && g_pserver->repl_slave_ignore_maxmemory) return C_OK;
size_t mem_reported, mem_tofree, mem_freed;
mstime_t latency, eviction_latency;
long long delta;
int slaves = listLength(server.slaves);
int slaves = listLength(g_pserver->slaves);
/* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of
@@ -464,7 +464,7 @@ int freeMemoryIfNeeded(void) {
mem_freed = 0;
if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)
if (g_pserver->maxmemory_policy == MAXMEMORY_NO_EVICTION)
goto cant_free; /* We need to free memory, but policy forbids. */
latencyStartMonitor(latency);
@@ -477,8 +477,8 @@ int freeMemoryIfNeeded(void) {
dict *dict;
dictEntry *de;
if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)
if (g_pserver->maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||
g_pserver->maxmemory_policy == MAXMEMORY_VOLATILE_TTL)
{
struct evictionPoolEntry *pool = EvictionPoolLRU;
@@ -488,9 +488,9 @@ int freeMemoryIfNeeded(void) {
/* We don't want to make local-db choices when expiring keys,
* so to start populate the eviction pool sampling keys from
* every DB. */
for (i = 0; i < server.dbnum; i++) {
db = server.db+i;
dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?
for (i = 0; i < cserver.dbnum; i++) {
db = g_pserver->db+i;
dict = (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?
db->pdict : db->expires;
if ((keys = dictSize(dict)) != 0) {
evictionPoolPopulate(i, dict, db->pdict, pool);
@@ -504,11 +504,11 @@ int freeMemoryIfNeeded(void) {
if (pool[k].key == NULL) continue;
bestdbid = pool[k].dbid;
if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
de = dictFind(server.db[pool[k].dbid].pdict,
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
de = dictFind(g_pserver->db[pool[k].dbid].pdict,
pool[k].key);
} else {
de = dictFind(server.db[pool[k].dbid].expires,
de = dictFind(g_pserver->db[pool[k].dbid].expires,
pool[k].key);
}
@@ -531,16 +531,16 @@ int freeMemoryIfNeeded(void) {
}
/* volatile-random and allkeys-random policy */
else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)
else if (g_pserver->maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||
g_pserver->maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)
{
/* When evicting a random key, we try to evict a key for
* each DB, so we use the static 'next_db' variable to
* incrementally visit all DBs. */
for (i = 0; i < server.dbnum; i++) {
j = (++next_db) % server.dbnum;
db = server.db+j;
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ?
for (i = 0; i < cserver.dbnum; i++) {
j = (++next_db) % cserver.dbnum;
db = g_pserver->db+j;
dict = (g_pserver->maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ?
db->pdict : db->expires;
if (dictSize(dict) != 0) {
de = dictGetRandomKey(dict);
@@ -553,9 +553,9 @@ int freeMemoryIfNeeded(void) {
/* Finally remove the selected key. */
if (bestkey) {
db = server.db+bestdbid;
db = g_pserver->db+bestdbid;
robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
propagateExpire(db,keyobj,server.lazyfree_lazy_eviction);
propagateExpire(db,keyobj,g_pserver->lazyfree_lazy_eviction);
/* We compute the amount of memory freed by db*Delete() alone.
* It is possible that actually the memory needed to propagate
* the DEL in AOF and replication link is greater than the one
@@ -566,7 +566,7 @@ int freeMemoryIfNeeded(void) {
* we only care about memory used by the key space. */
delta = (long long) zmalloc_used_memory();
latencyStartMonitor(eviction_latency);
if (server.lazyfree_lazy_eviction)
if (g_pserver->lazyfree_lazy_eviction)
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
@@ -575,7 +575,7 @@ int freeMemoryIfNeeded(void) {
latencyRemoveNestedEvent(latency,eviction_latency);
delta -= (long long) zmalloc_used_memory();
mem_freed += delta;
server.stat_evictedkeys++;
g_pserver->stat_evictedkeys++;
notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted",
keyobj, db->id);
decrRefCount(keyobj);
@@ -594,7 +594,7 @@ int freeMemoryIfNeeded(void) {
* memory, since the "mem_freed" amount is computed only
* across the dbAsyncDelete() call, while the thread can
* release the memory all the time. */
if (server.lazyfree_lazy_eviction && !(keys_freed % 16)) {
if (g_pserver->lazyfree_lazy_eviction && !(keys_freed % 16)) {
if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
/* Let's satisfy our stop condition. */
mem_freed = mem_tofree;
@@ -632,6 +632,6 @@ cant_free:
*
*/
int freeMemoryIfNeededAndSafe(void) {
if (server.lua_timedout || server.loading) return C_OK;
if (g_pserver->lua_timedout || g_pserver->loading) return C_OK;
return freeMemoryIfNeeded();
}
+24 -24
View File
@@ -47,7 +47,7 @@
* If the key is found to be expired, it is removed from the database and
* 1 is returned. Otherwise no operation is performed and 0 is returned.
*
* When a key is expired, server.stat_expiredkeys is incremented.
* When a key is expired, g_pserver->stat_expiredkeys is incremented.
*
* The parameter 'now' is the current time in milliseconds as is passed
* to the function to avoid too many gettimeofday() syscalls. */
@@ -57,15 +57,15 @@ int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
sds key = (sds)dictGetKey(de);
robj *keyobj = createStringObject(key,sdslen(key));
propagateExpire(db,keyobj,server.lazyfree_lazy_expire);
if (server.lazyfree_lazy_expire)
propagateExpire(db,keyobj,g_pserver->lazyfree_lazy_expire);
if (g_pserver->lazyfree_lazy_expire)
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",keyobj,db->id);
decrRefCount(keyobj);
server.stat_expiredkeys++;
g_pserver->stat_expiredkeys++;
return 1;
} else {
return 0;
@@ -126,14 +126,14 @@ void activeExpireCycle(int type) {
* 2) If last time we hit the time limit, we want to scan all DBs
* in this iteration, as there is work to do in some DB and we don't want
* expired keys to use memory for too much time. */
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
if (dbs_per_call > cserver.dbnum || timelimit_exit)
dbs_per_call = cserver.dbnum;
/* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time
* per iteration. Since this function gets called with a frequency of
* server.hz times per second, the following is the max amount of
* g_pserver->hz times per second, the following is the max amount of
* microseconds we can spend in this function. */
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/g_pserver->hz/100;
timelimit_exit = 0;
if (timelimit <= 0) timelimit = 1;
@@ -148,7 +148,7 @@ void activeExpireCycle(int type) {
for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) {
int expired;
redisDb *db = server.db+(current_db % server.dbnum);
redisDb *db = g_pserver->db+(current_db % cserver.dbnum);
/* Increment the DB now so we are sure if we run out of time
* in the current DB we'll restart from the next. This allows to
@@ -220,7 +220,7 @@ void activeExpireCycle(int type) {
elapsed = ustime()-start;
if (elapsed > timelimit) {
timelimit_exit = 1;
server.stat_expired_time_cap_reached_count++;
g_pserver->stat_expired_time_cap_reached_count++;
break;
}
}
@@ -239,8 +239,8 @@ void activeExpireCycle(int type) {
current_perc = (double)total_expired/total_sampled;
} else
current_perc = 0;
server.stat_expired_stale_perc = (current_perc*0.05)+
(server.stat_expired_stale_perc*0.95);
g_pserver->stat_expired_stale_perc = (current_perc*0.05)+
(g_pserver->stat_expired_stale_perc*0.95);
}
/*-----------------------------------------------------------------------------
@@ -297,14 +297,14 @@ void expireSlaveKeys(void) {
/* Check the key against every database corresponding to the
* bits set in the value bitmap. */
int dbid = 0;
while(dbids && dbid < server.dbnum) {
while(dbids && dbid < cserver.dbnum) {
if ((dbids & 1) != 0) {
redisDb *db = server.db+dbid;
redisDb *db = g_pserver->db+dbid;
dictEntry *expire = dictFind(db->expires,keyname);
int expired = 0;
if (expire &&
activeExpireCycleTryExpire(server.db+dbid,expire,start))
activeExpireCycleTryExpire(g_pserver->db+dbid,expire,start))
{
expired = 1;
}
@@ -377,7 +377,7 @@ size_t getSlaveKeyWithExpireCount(void) {
}
/* Remove the keys in the hash table. We need to do that when data is
* flushed from the server. We may receive new keys from the master with
* flushed from the g_pserver-> We may receive new keys from the master with
* the same name/db and it is no longer a good idea to expire them.
*
* Note: technically we should handle the case of a single DB being flushed
@@ -424,16 +424,16 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
*
* Instead we take the other branch of the IF statement setting an expire
* (possibly in the past) and wait for an explicit DEL from the master. */
if (when <= mstime() && !server.loading && !listLength(server.masters)) {
if (when <= mstime() && !g_pserver->loading && !listLength(g_pserver->masters)) {
robj *aux;
int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
int deleted = g_pserver->lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
dbSyncDelete(c->db,key);
serverAssertWithInfo(c,key,deleted);
server.dirty++;
g_pserver->dirty++;
/* Replicate/AOF this as an explicit DEL or UNLINK. */
aux = server.lazyfree_lazy_expire ? shared.unlink : shared.del;
aux = g_pserver->lazyfree_lazy_expire ? shared.unlink : shared.del;
rewriteClientCommandVector(c,2,aux,key);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
@@ -444,7 +444,7 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
addReply(c,shared.cone);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id);
server.dirty++;
g_pserver->dirty++;
return;
}
}
@@ -474,7 +474,7 @@ void ttlGenericCommand(client *c, int output_ms) {
long long expire, ttl = -1;
/* If the key does not exist at all, return -2 */
if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == NULL) {
if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == nullptr) {
addReplyLongLong(c,-2);
return;
}
@@ -507,7 +507,7 @@ void persistCommand(client *c) {
if (lookupKeyWrite(c->db,c->argv[1])) {
if (removeExpire(c->db,c->argv[1])) {
addReply(c,shared.cone);
server.dirty++;
g_pserver->dirty++;
} else {
addReply(c,shared.czero);
}
@@ -520,7 +520,7 @@ void persistCommand(client *c) {
void touchCommand(client *c) {
int touched = 0;
for (int j = 1; j < c->argc; j++)
if (lookupKeyRead(c->db,c->argv[j]) != NULL) touched++;
if (lookupKeyRead(c->db,c->argv[j]) != nullptr) touched++;
addReplyLongLong(c,touched);
}
+178 -23
View File
@@ -36,6 +36,10 @@
#include <assert.h>
#include <pthread.h>
#include <limits.h>
#ifdef __linux__
#include <linux/futex.h>
#endif
#include <string.h>
#ifdef __APPLE__
#include <TargetConditionals.h>
@@ -49,6 +53,11 @@
#endif
#endif
#ifndef UNUSED
#define UNUSED(x) ((void)x)
#endif
/****************************************************
*
* Implementation of a fair spinlock. To promote fairness we
@@ -56,14 +65,86 @@
*
****************************************************/
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
#if __has_feature(thread_sanitizer)
/* Report that a lock has been created at address "lock". */
#define ANNOTATE_RWLOCK_CREATE(lock) \
AnnotateRWLockCreate(__FILE__, __LINE__, lock)
/* Report that the lock at address "lock" is about to be destroyed. */
#define ANNOTATE_RWLOCK_DESTROY(lock) \
AnnotateRWLockDestroy(__FILE__, __LINE__, lock)
/* Report that the lock at address "lock" has been acquired.
is_w=1 for writer lock, is_w=0 for reader lock. */
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
/* Report that the lock at address "lock" is about to be released. */
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
#if defined(DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK)
#if defined(__GNUC__)
#define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak))
#else
/* TODO(glider): for Windows support we may want to change this macro in order
to prepend __declspec(selectany) to the annotations' declarations. */
#error weak annotations are not supported for your compiler
#endif
#else
#define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK
#endif
extern "C" {
void AnnotateRWLockCreate(
const char *file, int line,
const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockDestroy(
const char *file, int line,
const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockAcquired(
const char *file, int line,
const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockReleased(
const char *file, int line,
const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
}
#else
#define ANNOTATE_RWLOCK_CREATE(lock)
#define ANNOTATE_RWLOCK_DESTROY(lock)
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w)
#endif
static_assert(sizeof(pid_t) <= sizeof(fastlock::m_pidOwner), "fastlock::m_pidOwner not large enough");
uint64_t g_longwaits = 0;
uint64_t fastlock_getlongwaitcount()
{
return g_longwaits;
uint64_t rval;
__atomic_load(&g_longwaits, &rval, __ATOMIC_RELAXED);
return rval;
}
#ifndef ASM_SPINLOCK
#ifdef __linux__
static int futex(volatile unsigned *uaddr, int futex_op, int val,
const struct timespec *timeout, int val3)
{
return syscall(SYS_futex, uaddr, futex_op, val,
timeout, uaddr, val3);
}
#endif
#endif
extern "C" pid_t gettid()
{
@@ -88,72 +169,129 @@ extern "C" void fastlock_init(struct fastlock *lock)
lock->m_ticket.m_avail = 0;
lock->m_depth = 0;
lock->m_pidOwner = -1;
lock->futex = 0;
ANNOTATE_RWLOCK_CREATE(lock);
}
#ifndef ASM_SPINLOCK
extern "C" void fastlock_lock(struct fastlock *lock)
{
if ((int)__atomic_load_4(&lock->m_pidOwner, __ATOMIC_ACQUIRE) == gettid())
int pidOwner;
__atomic_load(&lock->m_pidOwner, &pidOwner, __ATOMIC_ACQUIRE);
if (pidOwner == gettid())
{
++lock->m_depth;
return;
}
unsigned myticket = __atomic_fetch_add(&lock->m_ticket.m_avail, 1, __ATOMIC_RELEASE);
#ifdef __linux__
unsigned mask = (1U << (myticket % 32));
#endif
int cloops = 0;
while (__atomic_load_2(&lock->m_ticket.m_active, __ATOMIC_ACQUIRE) != myticket)
ticket ticketT;
for (;;)
{
if ((++cloops % 1024*1024) == 0)
{
sched_yield();
++g_longwaits;
}
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if ((ticketT.u & 0xffff) == myticket)
break;
#if defined(__i386__) || defined(__amd64__)
__asm__ ("pause");
#endif
if ((++cloops % 1024*1024) == 0)
{
#ifdef __linux__
__atomic_fetch_or(&lock->futex, mask, __ATOMIC_ACQUIRE);
futex(&lock->m_ticket.u, FUTEX_WAIT_BITSET_PRIVATE, ticketT.u, nullptr, mask);
__atomic_fetch_and(&lock->futex, ~mask, __ATOMIC_RELEASE);
#endif
__atomic_fetch_add(&g_longwaits, 1, __ATOMIC_RELAXED);
}
}
lock->m_depth = 1;
__atomic_store_4(&lock->m_pidOwner, gettid(), __ATOMIC_RELEASE);
int tid = gettid();
__atomic_store(&lock->m_pidOwner, &tid, __ATOMIC_RELEASE);
ANNOTATE_RWLOCK_ACQUIRED(lock, true);
std::atomic_thread_fence(std::memory_order_acquire);
}
extern "C" int fastlock_trylock(struct fastlock *lock)
extern "C" int fastlock_trylock(struct fastlock *lock, int fWeak)
{
if ((int)__atomic_load_4(&lock->m_pidOwner, __ATOMIC_ACQUIRE) == gettid())
int tid;
__atomic_load(&lock->m_pidOwner, &tid, __ATOMIC_ACQUIRE);
if (tid == gettid())
{
++lock->m_depth;
return true;
}
// cheap test
if (lock->m_ticket.m_active != lock->m_ticket.m_avail)
struct ticket ticketT;
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if (ticketT.m_active != ticketT.m_avail)
return false;
uint16_t active = __atomic_load_2(&lock->m_ticket.m_active, __ATOMIC_RELAXED);
uint16_t active = ticketT.m_active;
uint16_t next = active + 1;
struct ticket ticket_expect { active, active };
struct ticket ticket_setiflocked { active, next };
if (__atomic_compare_exchange(&lock->m_ticket, &ticket_expect, &ticket_setiflocked, false /*weak*/, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
struct ticket ticket_expect { { { active, active } } };
struct ticket ticket_setiflocked { { { active, next } } };
if (__atomic_compare_exchange(&lock->m_ticket, &ticket_expect, &ticket_setiflocked, fWeak /*weak*/, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
{
lock->m_depth = 1;
__atomic_store_4(&lock->m_pidOwner, gettid(), __ATOMIC_RELEASE);
tid = gettid();
__atomic_store(&lock->m_pidOwner, &tid, __ATOMIC_RELEASE);
ANNOTATE_RWLOCK_ACQUIRED(lock, true);
return true;
}
return false;
}
#ifdef __linux__
#define ROL32(v, shift) ((v << shift) | (v >> (32-shift)))
void unlock_futex(struct fastlock *lock, uint16_t ifutex)
{
unsigned mask = (1U << (ifutex % 32));
unsigned futexT;
__atomic_load(&lock->futex, &futexT, __ATOMIC_RELAXED);
futexT &= mask;
if (futexT == 0)
return;
for (;;)
{
__atomic_load(&lock->futex, &futexT, __ATOMIC_ACQUIRE);
futexT &= mask;
if (!futexT)
break;
if (futex(&lock->m_ticket.u, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, mask) == 1)
break;
}
}
#endif
extern "C" void fastlock_unlock(struct fastlock *lock)
{
--lock->m_depth;
if (lock->m_depth == 0)
{
assert((int)__atomic_load_4(&lock->m_pidOwner, __ATOMIC_RELAXED) >= 0); // unlock after free
lock->m_pidOwner = -1;
std::atomic_thread_fence(std::memory_order_acquire);
__atomic_fetch_add(&lock->m_ticket.m_active, 1, __ATOMIC_ACQ_REL); // on x86 the atomic is not required here, but ASM handles that case
int pidT;
__atomic_load(&lock->m_pidOwner, &pidT, __ATOMIC_RELAXED);
assert(pidT >= 0); // unlock after free
int t = -1;
__atomic_store(&lock->m_pidOwner, &t, __ATOMIC_RELEASE);
std::atomic_thread_fence(std::memory_order_release);
ANNOTATE_RWLOCK_RELEASED(lock, true);
uint16_t activeNew = __atomic_add_fetch(&lock->m_ticket.m_active, 1, __ATOMIC_RELEASE); // on x86 the atomic is not required here, but ASM handles that case
#ifdef __linux__
unlock_futex(lock, activeNew);
#else
UNUSED(activeNew);
#endif
}
}
#endif
@@ -164,10 +302,27 @@ extern "C" void fastlock_free(struct fastlock *lock)
assert((lock->m_ticket.m_active == lock->m_ticket.m_avail) // Asser the lock is unlocked
|| (lock->m_pidOwner == gettid() && (lock->m_ticket.m_active == lock->m_ticket.m_avail-1))); // OR we own the lock and nobody else is waiting
lock->m_pidOwner = -2; // sentinal value indicating free
ANNOTATE_RWLOCK_DESTROY(lock);
}
bool fastlock::fOwnLock()
{
return gettid() == m_pidOwner;
int tid;
__atomic_load(&m_pidOwner, &tid, __ATOMIC_RELAXED);
return gettid() == tid;
}
int fastlock_unlock_recursive(struct fastlock *lock)
{
int rval = lock->m_depth;
lock->m_depth = 1;
fastlock_unlock(lock);
return rval;
}
void fastlock_lock_recursive(struct fastlock *lock, int nesting)
{
fastlock_lock(lock);
lock->m_depth = nesting;
}
+29 -5
View File
@@ -9,9 +9,11 @@ extern "C" {
struct fastlock;
void fastlock_init(struct fastlock *lock);
void fastlock_lock(struct fastlock *lock);
int fastlock_trylock(struct fastlock *lock);
int fastlock_trylock(struct fastlock *lock, int fWeak);
void fastlock_unlock(struct fastlock *lock);
void fastlock_free(struct fastlock *lock);
int fastlock_unlock_recursive(struct fastlock *lock);
void fastlock_lock_recursive(struct fastlock *lock, int nesting);
uint64_t fastlock_getlongwaitcount(); // this is a global value
@@ -20,17 +22,29 @@ uint64_t fastlock_getlongwaitcount(); // this is a global value
}
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
struct ticket
{
uint16_t m_active;
uint16_t m_avail;
union
{
struct
{
uint16_t m_active;
uint16_t m_avail;
};
unsigned u;
};
};
#pragma GCC diagnostic pop
struct fastlock
{
volatile struct ticket m_ticket;
volatile int m_pidOwner;
volatile int m_depth;
unsigned futex;
#ifdef __cplusplus
fastlock()
@@ -43,9 +57,9 @@ struct fastlock
fastlock_lock(this);
}
bool try_lock()
bool try_lock(bool fWeak = false)
{
return !!fastlock_trylock(this);
return !!fastlock_trylock(this, fWeak);
}
void unlock()
@@ -53,6 +67,16 @@ struct fastlock
fastlock_unlock(this);
}
int unlock_recursive()
{
return fastlock_unlock_recursive(this);
}
void lock_recursive(int nesting)
{
fastlock_lock_recursive(this, nesting);
}
bool fOwnLock(); // true if this thread owns the lock, NOTE: not 100% reliable, use for debugging only
#endif
};
+46 -11
View File
@@ -22,7 +22,7 @@ fastlock_lock:
push rdi ; we need our struct pointer (also balance the stack for the call)
call gettid ; get our thread ID (TLS is nasty in ASM so don't bother inlining)
mov esi, eax ; back it up in esi
mov rdi, [rsp] ; get our pointer back
pop rdi ; get our pointer back
cmp [rdi+4], esi ; Is the TID we got back the owner of the lock?
je .LLocked ; Don't spin in that case
@@ -30,11 +30,11 @@ fastlock_lock:
xor eax, eax ; eliminate partial register dependency
inc eax ; we want to add one
lock xadd [rdi+2], ax ; do the xadd, ax contains the value before the addition
; eax now contains the ticket
xor ecx, ecx
; ax now contains the ticket
ALIGN 16
.LLoop:
cmp [rdi], ax ; is our ticket up?
mov edx, [rdi]
cmp dx, ax ; is our ticket up?
je .LLocked ; leave the loop
pause
add ecx, 1000h ; Have we been waiting a long time? (oflow if we have)
@@ -44,22 +44,34 @@ ALIGN 16
; But the compiler doesn't know that we rarely hit this, and when we do we know the lock is
; taking a long time to be released anyways. We optimize for the common case of short
; lock intervals. That's why we're using a spinlock in the first place
; If we get here we're going to sleep in the kernel with a futex
push rsi
push rax
mov rax, 24 ; sys_sched_yield
syscall ; give up our timeslice we'll be here a while
pop rax
pop rsi
; Setup the syscall args
; rdi ARG1 futex (already in rdi)
mov esi, (9 | 128) ; rsi ARG2 FUTEX_WAIT_BITSET_PRIVATE
; rdx ARG3 ticketT.u (already in edx)
xor r10d, r10d ; r10 ARG4 NULL
mov r8, rdi ; r8 ARG5 dup rdi
xor r9d, r9d
bts r9d, eax ; r9 ARG6 mask
mov eax, 202 ; sys_futex
; Do the syscall
lock or [rdi+12], r9d ; inform the unlocking thread we're waiting
syscall ; wait for the futex
not r9d ; convert our flag into a mask of bits not to touch
lock and [rdi+12], r9d ; clear the flag in the futex control mask
; cleanup and continue
mov rcx, g_longwaits
inc qword [rcx] ; increment our long wait counter
mov rdi, [rsp] ; our struct pointer is on the stack already
pop rax
pop rsi
xor ecx, ecx ; Reset our loop counter
jmp .LLoop ; Get back in the game
ALIGN 16
.LLocked:
mov [rdi+4], esi ; lock->m_pidOwner = gettid()
inc dword [rdi+8] ; lock->m_depth++
add rsp, 8 ; fix stack
ret
ALIGN 16
@@ -114,9 +126,32 @@ fastlock_unlock:
; uint16_t avail
; int32_t m_pidOwner
; int32_t m_depth
push r11
sub dword [rdi+8], 1 ; decrement m_depth, don't use dec because it partially writes the flag register and we don't know its state
jnz .LDone ; if depth is non-zero this is a recursive unlock, and we still hold it
mov dword [rdi+4], -1 ; pidOwner = -1 (we don't own it anymore)
inc word [rdi] ; give up our ticket (note: lock is not required here because the spinlock itself guards this variable)
mov ecx, [rdi] ; get current active (this one)
inc ecx ; bump it to the next thread
mov [rdi], cx ; give up our ticket (note: lock is not required here because the spinlock itself guards this variable)
; At this point the lock is removed, however we must wake up any pending futexs
mov r9d, 1 ; eax is the bitmask for 2 threads
rol r9d, cl ; place the mask in the right spot for the next 2 threads
ALIGN 16
.LRetryWake:
mov r11d, [rdi+12] ; load the futex mask
and r11d, r9d ; are any threads waiting on a futex?
jz .LDone ; if not we're done.
; we have to wake the futexs
; rdi ARG1 futex (already in rdi)
mov esi, (10 | 128) ; rsi ARG2 FUTEX_WAKE_BITSET_PRIVATE
mov edx, 0x7fffffff ; rdx ARG3 INT_MAX (number of threads to wake)
xor r10d, r10d ; r10 ARG4 NULL
mov r8, rdi ; r8 ARG5 dup rdi
; r9 ARG6 mask (already set above)
mov eax, 202 ; sys_futex
syscall
cmp eax, 1 ; did we wake as many as we expected?
jnz .LRetryWake
.LDone:
pop r11
ret
+12 -12
View File
@@ -110,7 +110,7 @@ int extractLongLatOrReply(client *c, robj **argv, double *xy) {
/* Input Argument Helper */
/* Decode lat/long from a zset member's score.
* Returns C_OK on successful decoding, otherwise C_ERR is returned. */
int longLatFromMember(robj *zobj, robj *member, double *xy) {
int longLatFromMember(robj_roptr zobj, robj *member, double *xy) {
double score = 0;
if (zsetScore(zobj, szFromObj(member), &score) == C_ERR) return C_ERR;
@@ -222,7 +222,7 @@ int geoAppendIfWithinRadius(geoArray *ga, double lon, double lat, double radius,
* using multiple queries to the sorted set, that we later need to sort
* via qsort. Similarly we need to be able to reject points outside the search
* radius area ASAP in order to allocate and process more points than needed. */
int geoGetPointsInRange(robj *zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) {
int geoGetPointsInRange(robj_roptr zobj, double min, double max, double lon, double lat, double radius, geoArray *ga) {
/* minex 0 = include min in range; maxex 1 = exclude max in range */
/* That's: min <= val < max */
zrangespec range = { min, max, 0, 1 };
@@ -315,7 +315,7 @@ void scoresOfGeoHashBox(GeoHashBits hash, GeoHashFix52Bits *min, GeoHashFix52Bit
/* Obtain all members between the min/max of this geohash bounding box.
* Populate a geoArray of GeoPoints by calling geoGetPointsInRange().
* Return the number of points added to the array. */
int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) {
int membersOfGeoHashBox(robj_roptr zobj, GeoHashBits hash, geoArray *ga, double lon, double lat, double radius) {
GeoHashFix52Bits min, max;
scoresOfGeoHashBox(hash,&min,&max);
@@ -323,7 +323,7 @@ int membersOfGeoHashBox(robj *zobj, GeoHashBits hash, geoArray *ga, double lon,
}
/* Search all eight neighbors + self geohash box */
int membersOfAllNeighbors(robj *zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {
int membersOfAllNeighbors(robj_roptr zobj, GeoHashRadius n, double lon, double lat, double radius, geoArray *ga) {
GeoHashBits neighbors[9];
unsigned int i, count = 0, last_processed = 0;
int debugmsg = 0;
@@ -465,8 +465,8 @@ void georadiusGeneric(client *c, int flags) {
int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */
/* Look up the requested zset */
robj *zobj = NULL;
if ((zobj = lookupKeyReadOrReply(c, key, shared.null[c->resp])) == NULL ||
robj_roptr zobj;
if ((zobj = lookupKeyReadOrReply(c, key, shared.null[c->resp])) == nullptr ||
checkType(c, zobj, OBJ_ZSET)) {
return;
}
@@ -661,11 +661,11 @@ void georadiusGeneric(client *c, int flags) {
decrRefCount(zobj);
notifyKeyspaceEvent(NOTIFY_ZSET,"georadiusstore",storekey,
c->db->id);
server.dirty += returned_items;
g_pserver->dirty += returned_items;
} else if (dbDelete(c->db,storekey)) {
signalModifiedKey(c->db,storekey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",storekey,c->db->id);
server.dirty++;
g_pserver->dirty++;
}
addReplyLongLong(c, returned_items);
}
@@ -701,7 +701,7 @@ void geohashCommand(client *c) {
int j;
/* Look up the requested zset */
robj *zobj = lookupKeyRead(c->db, c->argv[1]);
robj_roptr zobj = lookupKeyRead(c->db, c->argv[1]);
if (zobj && checkType(c, zobj, OBJ_ZSET)) return;
/* Geohash elements one after the other, using a null bulk reply for
@@ -754,7 +754,7 @@ void geoposCommand(client *c) {
int j;
/* Look up the requested zset */
robj *zobj = lookupKeyRead(c->db, c->argv[1]);
robj_roptr zobj = lookupKeyRead(c->db, c->argv[1]);
if (zobj && checkType(c, zobj, OBJ_ZSET)) return;
/* Report elements one after the other, using a null bulk reply for
@@ -796,9 +796,9 @@ void geodistCommand(client *c) {
}
/* Look up the requested zset */
robj *zobj = NULL;
robj_roptr zobj = NULL;
if ((zobj = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp]))
== NULL || checkType(c, zobj, OBJ_ZSET)) return;
== nullptr || checkType(c, zobj, OBJ_ZSET)) return;
/* Get the scores. We need both otherwise NULL is returned. */
double score1, score2, xyxy[4];
+16 -16
View File
@@ -176,7 +176,7 @@
* involved in updating the sparse representation is not justified by the
* memory savings. The exact maximum length of the sparse representation
* when this implementation switches to the dense representation is
* configured via the define server.hll_sparse_max_bytes.
* configured via the define g_pserver->hll_sparse_max_bytes.
*/
struct hllhdr {
@@ -652,7 +652,7 @@ int hllSparseToDense(robj *o) {
* As a side effect the function may promote the HLL representation from
* sparse to dense: this happens when a register requires to be set to a value
* not representable with the sparse representation, or when the resulting
* size would be greater than server.hll_sparse_max_bytes. */
* size would be greater than g_pserver->hll_sparse_max_bytes. */
int hllSparseSet(robj *o, long index, uint8_t count) {
struct hllhdr *hdr;
uint8_t oldcount, *sparse, *end, *p, *prev, *next;
@@ -837,7 +837,7 @@ int hllSparseSet(robj *o, long index, uint8_t count) {
deltalen = seqlen-oldlen;
if (deltalen > 0 &&
sdslen(szFromObj(o))+deltalen > server.hll_sparse_max_bytes) goto promote;
sdslen(szFromObj(o))+deltalen > g_pserver->hll_sparse_max_bytes) goto promote;
if (deltalen && next) memmove(next+deltalen,next,end-next);
sdsIncrLen(szFromObj(o),deltalen);
memcpy(p,seq,seqlen);
@@ -1073,7 +1073,7 @@ int hllAdd(robj *o, unsigned char *ele, size_t elesize) {
*
* If the HyperLogLog is sparse and is found to be invalid, C_ERR
* is returned, otherwise the function always succeeds. */
int hllMerge(uint8_t *max, size_t cmax, robj *hll) {
int hllMerge(uint8_t *max, size_t cmax, robj_roptr hll) {
struct hllhdr *hdr = (hllhdr*)ptrFromObj(hll);
int i;
@@ -1085,7 +1085,7 @@ int hllMerge(uint8_t *max, size_t cmax, robj *hll) {
if (val > max[i]) max[i] = val;
}
} else {
uint8_t *p = (uint8_t*)ptrFromObj(hll), *end = p + sdslen(szFromObj(hll));
const uint8_t *p = (const uint8_t*)ptrFromObj(hll), *end = p + sdslen(szFromObj(hll));
long runlen, regval;
p += HLL_HDR_SIZE;
@@ -1156,7 +1156,7 @@ robj *createHLLObject(void) {
/* Check if the object is a String with a valid HLL representation.
* Return C_OK if this is true, otherwise reply to the client
* with an error and return C_ERR. */
int isHLLObjectOrReply(client *c, robj *o) {
int isHLLObjectOrReply(client *c, robj_roptr o) {
struct hllhdr *hdr;
/* Key exists, check type */
@@ -1221,7 +1221,7 @@ void pfaddCommand(client *c) {
if (updated) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
HLL_INVALIDATE_CACHE(hdr);
}
addReply(c, updated ? shared.cone : shared.czero);
@@ -1248,8 +1248,8 @@ void pfcountCommand(client *c) {
registers = max + HLL_HDR_SIZE;
for (j = 1; j < c->argc; j++) {
/* Check type and size. */
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) continue; /* Assume empty HLL for non existing var.*/
robj_roptr o = lookupKeyRead(c->db,c->argv[j]);
if (o == nullptr) continue; /* Assume empty HLL for non existing var.*/
if (isHLLObjectOrReply(c,o) != C_OK) return;
/* Merge with this HLL with our 'max' HHL by setting max[i]
@@ -1311,7 +1311,7 @@ void pfcountCommand(client *c) {
* may be modified and given that the HLL is a Redis string
* we need to propagate the change. */
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
}
addReplyLongLong(c,card);
}
@@ -1330,8 +1330,8 @@ void pfmergeCommand(client *c) {
memset(max,0,sizeof(max));
for (j = 1; j < c->argc; j++) {
/* Check type and size. */
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) continue; /* Assume empty HLL for non existing var. */
robj_roptr o = lookupKeyRead(c->db,c->argv[j]);
if (o == nullptr) continue; /* Assume empty HLL for non existing var. */
if (isHLLObjectOrReply(c,o) != C_OK) return;
/* If at least one involved HLL is dense, use the dense representation
@@ -1387,7 +1387,7 @@ void pfmergeCommand(client *c) {
/* We generate a PFADD event for PFMERGE for semantical simplicity
* since in theory this is a mass-add of elements. */
notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReply(c,shared.ok);
}
@@ -1457,7 +1457,7 @@ void pfselftestCommand(client *c) {
/* Make sure that for small cardinalities we use sparse
* encoding. */
if (j == checkpoint && j < server.hll_sparse_max_bytes/2) {
if (j == checkpoint && j < g_pserver->hll_sparse_max_bytes/2) {
hdr2 = (hllhdr*)ptrFromObj(o);
if (hdr2->encoding != HLL_SPARSE) {
addReplyError(c, "TESTFAILED sparse encoding not used");
@@ -1528,7 +1528,7 @@ void pfdebugCommand(client *c) {
addReplySds(c,sdsnew(invalid_hll_err));
return;
}
server.dirty++; /* Force propagation on encoding change. */
g_pserver->dirty++; /* Force propagation on encoding change. */
}
hdr = (hllhdr*)ptrFromObj(o);
@@ -1593,7 +1593,7 @@ void pfdebugCommand(client *c) {
return;
}
conv = 1;
server.dirty++; /* Force propagation on encoding change. */
g_pserver->dirty++; /* Force propagation on encoding change. */
}
addReply(c,conv ? shared.cone : shared.czero);
} else {
+25 -25
View File
@@ -88,15 +88,15 @@ int THPGetAnonHugePagesSize(void) {
* of time series, each time serie is craeted on demand in order to avoid
* having a fixed list to maintain. */
void latencyMonitorInit(void) {
server.latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);
g_pserver->latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);
}
/* Add the specified sample to the specified time series "event".
* This function is usually called via latencyAddSampleIfNeeded(), that
* is a macro that only adds the sample if the latency is higher than
* server.latency_monitor_threshold. */
* g_pserver->latency_monitor_threshold. */
void latencyAddSample(const char *event, mstime_t latency) {
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictFetchValue(server.latency_events,event);
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictFetchValue(g_pserver->latency_events,event);
time_t now = time(NULL);
int prev;
@@ -106,7 +106,7 @@ void latencyAddSample(const char *event, mstime_t latency) {
ts->idx = 0;
ts->max = 0;
memset(ts->samples,0,sizeof(ts->samples));
dictAdd(server.latency_events,zstrdup(event),ts);
dictAdd(g_pserver->latency_events,zstrdup(event),ts);
}
if (latency > ts->max) ts->max = latency;
@@ -137,12 +137,12 @@ int latencyResetEvent(char *event_to_reset) {
dictEntry *de;
int resets = 0;
di = dictGetSafeIterator(server.latency_events);
di = dictGetSafeIterator(g_pserver->latency_events);
while((de = dictNext(di)) != NULL) {
char *event = (char*)dictGetKey(de);
if (event_to_reset == NULL || strcasecmp(event,event_to_reset) == 0) {
dictDelete(server.latency_events, event);
dictDelete(g_pserver->latency_events, event);
resets++;
}
}
@@ -158,7 +158,7 @@ int latencyResetEvent(char *event_to_reset) {
* If the specified event has no elements the structure is populate with
* zero values. */
void analyzeLatencyForEvent(char *event, struct latencyStats *ls) {
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictFetchValue(server.latency_events,event);
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictFetchValue(g_pserver->latency_events,event);
int j;
uint64_t sum;
@@ -236,8 +236,8 @@ sds createLatencyReport(void) {
/* Return ASAP if the latency engine is disabled and it looks like it
* was never enabled so far. */
if (dictSize(server.latency_events) == 0 &&
server.latency_monitor_threshold == 0)
if (dictSize(g_pserver->latency_events) == 0 &&
g_pserver->latency_monitor_threshold == 0)
{
report = sdscat(report,"I'm sorry, Dave, I can't do that. Latency monitoring is disabled in this Redis instance. You may use \"CONFIG SET latency-monitor-threshold <milliseconds>.\" in order to enable it. If we weren't in a deep space mission I'd suggest to take a look at http://redis.io/topics/latency-monitor.\n");
return report;
@@ -249,7 +249,7 @@ sds createLatencyReport(void) {
dictEntry *de;
int eventnum = 0;
di = dictGetSafeIterator(server.latency_events);
di = dictGetSafeIterator(g_pserver->latency_events);
while((de = dictNext(di)) != NULL) {
char *event = (char*)dictGetKey(de);
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictGetVal(de);
@@ -274,31 +274,31 @@ sds createLatencyReport(void) {
/* Fork */
if (!strcasecmp(event,"fork")) {
const char *fork_quality;
if (server.stat_fork_rate < 10) {
if (g_pserver->stat_fork_rate < 10) {
fork_quality = "terrible";
advise_better_vm = 1;
advices++;
} else if (server.stat_fork_rate < 25) {
} else if (g_pserver->stat_fork_rate < 25) {
fork_quality = "poor";
advise_better_vm = 1;
advices++;
} else if (server.stat_fork_rate < 100) {
} else if (g_pserver->stat_fork_rate < 100) {
fork_quality = "good";
} else {
fork_quality = "excellent";
}
report = sdscatprintf(report,
" Fork rate is %.2f GB/sec (%s).", server.stat_fork_rate,
" Fork rate is %.2f GB/sec (%s).", g_pserver->stat_fork_rate,
fork_quality);
}
/* Potentially commands. */
if (!strcasecmp(event,"command")) {
if (server.slowlog_log_slower_than < 0) {
if (g_pserver->slowlog_log_slower_than < 0) {
advise_slowlog_enabled = 1;
advices++;
} else if (server.slowlog_log_slower_than/1000 >
server.latency_monitor_threshold)
} else if (g_pserver->slowlog_log_slower_than/1000 >
g_pserver->latency_monitor_threshold)
{
advise_slowlog_tuning = 1;
advices++;
@@ -401,11 +401,11 @@ sds createLatencyReport(void) {
/* Slow log. */
if (advise_slowlog_enabled) {
report = sdscatprintf(report,"- There are latency issues with potentially slow commands you are using. Try to enable the Slow Log Redis feature using the command 'CONFIG SET slowlog-log-slower-than %llu'. If the Slow log is disabled Redis is not able to log slow commands execution for you.\n", (unsigned long long)server.latency_monitor_threshold*1000);
report = sdscatprintf(report,"- There are latency issues with potentially slow commands you are using. Try to enable the Slow Log Redis feature using the command 'CONFIG SET slowlog-log-slower-than %llu'. If the Slow log is disabled Redis is not able to log slow commands execution for you.\n", (unsigned long long)g_pserver->latency_monitor_threshold*1000);
}
if (advise_slowlog_tuning) {
report = sdscatprintf(report,"- Your current Slow Log configuration only logs events that are slower than your configured latency monitor threshold. Please use 'CONFIG SET slowlog-log-slower-than %llu'.\n", (unsigned long long)server.latency_monitor_threshold*1000);
report = sdscatprintf(report,"- Your current Slow Log configuration only logs events that are slower than your configured latency monitor threshold. Please use 'CONFIG SET slowlog-log-slower-than %llu'.\n", (unsigned long long)g_pserver->latency_monitor_threshold*1000);
}
if (advise_slowlog_inspect) {
@@ -443,7 +443,7 @@ sds createLatencyReport(void) {
report = sdscat(report,"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing an RDB file (the moment where there is high disk contention).\n");
}
if (advise_relax_fsync_policy && server.aof_fsync == AOF_FSYNC_ALWAYS) {
if (advise_relax_fsync_policy && g_pserver->aof_fsync == AOF_FSYNC_ALWAYS) {
report = sdscat(report,"- Your fsync policy is set to 'always'. It is very hard to get good performances with such a setup, if possible try to relax the fsync policy to 'onesec'.\n");
}
@@ -451,7 +451,7 @@ sds createLatencyReport(void) {
report = sdscat(report,"- Latency during the AOF atomic rename operation or when the final difference is flushed to the AOF file at the end of the rewrite, sometimes is caused by very high write load, causing the AOF buffer to get very large. If possible try to send less commands to accomplish the same work, or use Lua scripts to group multiple operations into a single EVALSHA call.\n");
}
if (advise_hz && server.hz < 100) {
if (advise_hz && g_pserver->hz < 100) {
report = sdscat(report,"- In order to make the Redis keys expiring process more incremental, try to set the 'hz' configuration parameter to 100 using 'CONFIG SET hz 100'.\n");
}
@@ -497,8 +497,8 @@ void latencyCommandReplyWithLatestEvents(client *c) {
dictIterator *di;
dictEntry *de;
addReplyArrayLen(c,dictSize(server.latency_events));
di = dictGetIterator(server.latency_events);
addReplyArrayLen(c,dictSize(g_pserver->latency_events));
di = dictGetIterator(g_pserver->latency_events);
while((de = dictNext(di)) != NULL) {
char *event = (char*)dictGetKey(de);
struct latencyTimeSeries *ts = (latencyTimeSeries*)dictGetVal(de);
@@ -581,7 +581,7 @@ NULL
if (!strcasecmp(szFromObj(c->argv[1]),"history") && c->argc == 3) {
/* LATENCY HISTORY <event> */
ts = (latencyTimeSeries*)dictFetchValue(server.latency_events,ptrFromObj(c->argv[2]));
ts = (latencyTimeSeries*)dictFetchValue(g_pserver->latency_events,ptrFromObj(c->argv[2]));
if (ts == NULL) {
addReplyArrayLen(c,0);
} else {
@@ -593,7 +593,7 @@ NULL
dictEntry *de;
char *event;
de = dictFind(server.latency_events,ptrFromObj(c->argv[2]));
de = dictFind(g_pserver->latency_events,ptrFromObj(c->argv[2]));
if (de == NULL) goto nodataerr;
ts = (latencyTimeSeries*)dictGetVal(de);
event = (char*)dictGetKey(de);
+4 -4
View File
@@ -72,7 +72,7 @@ int THPIsEnabled(void);
/* Latency monitoring macros. */
/* Start monitoring an event. We just set the current time. */
#define latencyStartMonitor(var) if (server.latency_monitor_threshold) { \
#define latencyStartMonitor(var) if (g_pserver->latency_monitor_threshold) { \
var = mstime(); \
} else { \
var = 0; \
@@ -80,14 +80,14 @@ int THPIsEnabled(void);
/* End monitoring an event, compute the difference with the current time
* to check the amount of time elapsed. */
#define latencyEndMonitor(var) if (server.latency_monitor_threshold) { \
#define latencyEndMonitor(var) if (g_pserver->latency_monitor_threshold) { \
var = mstime() - var; \
}
/* Add the sample only if the elapsed time is >= to the configured threshold. */
#define latencyAddSampleIfNeeded(event,var) \
if (server.latency_monitor_threshold && \
(var) >= server.latency_monitor_threshold) \
if (g_pserver->latency_monitor_threshold && \
(var) >= g_pserver->latency_monitor_threshold) \
latencyAddSample((event),(var));
/* Remove time from a nested event. */
+5 -5
View File
@@ -83,7 +83,7 @@ int dbAsyncDelete(redisDb *db, robj *key) {
* field to NULL in order to lazy free it later. */
if (de) {
dictFreeUnlinkedEntry(db->pdict,de);
if (server.cluster_enabled) slotToKeyDel(key);
if (g_pserver->cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
@@ -115,11 +115,11 @@ void emptyDbAsync(redisDb *db) {
/* Empty the slots-keys map of Redis CLuster by creating a new empty one
* and scheduiling the old for lazy freeing. */
void slotToKeyFlushAsync(void) {
rax *old = server.cluster->slots_to_keys;
rax *old = g_pserver->cluster->slots_to_keys;
server.cluster->slots_to_keys = raxNew();
memset(server.cluster->slots_keys_count,0,
sizeof(server.cluster->slots_keys_count));
g_pserver->cluster->slots_to_keys = raxNew();
memset(g_pserver->cluster->slots_keys_count,0,
sizeof(g_pserver->cluster->slots_keys_count));
atomicIncr(lazyfree_objects,old->numele);
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,old);
}
+8
View File
@@ -44,6 +44,10 @@
#define LP_AFTER 1
#define LP_REPLACE 2
#ifdef __cplusplus
extern "C" {
#endif
unsigned char *lpNew(void);
void lpFree(unsigned char *lp);
unsigned char *lpInsert(unsigned char *lp, unsigned char *ele, uint32_t size, unsigned char *p, int where, unsigned char **newp);
@@ -58,4 +62,8 @@ unsigned char *lpPrev(unsigned char *lp, unsigned char *p);
uint32_t lpBytes(unsigned char *lp);
unsigned char *lpSeek(unsigned char *lp, long index);
#ifdef __cplusplus
}
#endif
#endif
+121 -89
View File
@@ -30,6 +30,8 @@
#include "server.h"
#include "cluster.h"
#include <dlfcn.h>
#include <mutex>
#include <condition_variable>
#define REDISMODULE_CORE 1
#include "redismodule.h"
@@ -55,7 +57,7 @@ struct RedisModule {
typedef struct RedisModule RedisModule;
/* This represents a shared API. Shared APIs will be used to populate
* the server.sharedapi dictionary, mapping names of APIs exported by
* the g_pserver->sharedapi dictionary, mapping names of APIs exported by
* modules for other modules to use, to their structure specifying the
* function pointer that can be called. */
struct RedisModuleSharedAPI {
@@ -235,7 +237,6 @@ static list *moduleUnblockedClients;
/* We need a mutex that is unlocked / relocked in beforeSleep() in order to
* allow thread safe contexts to execute commands at a safe moment. */
static pthread_rwlock_t moduleGIL = PTHREAD_RWLOCK_INITIALIZER;
int fModuleGILWlocked = FALSE;
/* Function pointer type for keyspace event notification subscriptions from modules. */
@@ -293,6 +294,12 @@ typedef struct RedisModuleCommandFilter {
/* Registered filters */
static list *moduleCommandFilters;
/* Module GIL Variables */
static int s_cAcquisitionsServer = 0;
static int s_cAcquisitionsModule = 0;
static std::mutex s_mutex;
static std::condition_variable s_cv;
/* --------------------------------------------------------------------------
* Prototypes
* -------------------------------------------------------------------------- */
@@ -427,8 +434,8 @@ int moduleCreateEmptyKey(RedisModuleKey *key, int type) {
switch(type) {
case REDISMODULE_KEYTYPE_LIST:
obj = createQuicklistObject();
quicklistSetOptions((quicklist*)obj->m_ptr, server.list_max_ziplist_size,
server.list_compress_depth);
quicklistSetOptions((quicklist*)obj->m_ptr, g_pserver->list_max_ziplist_size,
g_pserver->list_compress_depth);
break;
case REDISMODULE_KEYTYPE_ZSET:
obj = createZsetZiplistObject();
@@ -492,7 +499,7 @@ int moduleDelKeyIfEmpty(RedisModuleKey *key) {
* This function is not meant to be used by modules developer, it is only
* used implicitly by including redismodule.h. */
int RM_GetApi(const char *funcname, void **targetPtrPtr) {
dictEntry *he = dictFind(server.moduleapi, funcname);
dictEntry *he = dictFind(g_pserver->moduleapi, funcname);
if (!he) return REDISMODULE_ERR;
*targetPtrPtr = dictGetVal(he);
return REDISMODULE_OK;
@@ -528,7 +535,7 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {
if (ctx->flags & REDISMODULE_CTX_MULTI_EMITTED) {
robj *propargv[1];
propargv[0] = createStringObject("EXEC",4);
alsoPropagate(server.execCommand,c->db->id,propargv,1,
alsoPropagate(cserver.execCommand,c->db->id,propargv,1,
PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(propargv[0]);
}
@@ -700,7 +707,7 @@ int commandFlagsFromString(char *s) {
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {
int flags = strflags ? commandFlagsFromString((char*)strflags) : 0;
if (flags == -1) return REDISMODULE_ERR;
if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)
if ((flags & CMD_MODULE_NO_CLUSTER) && g_pserver->cluster_enabled)
return REDISMODULE_ERR;
struct redisCommand *rediscmd;
@@ -734,8 +741,8 @@ int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc c
cp->rediscmd->keystep = keystep;
cp->rediscmd->microseconds = 0;
cp->rediscmd->calls = 0;
dictAdd(server.commands,sdsdup(cmdname),cp->rediscmd);
dictAdd(server.orig_commands,sdsdup(cmdname),cp->rediscmd);
dictAdd(g_pserver->commands,sdsdup(cmdname),cp->rediscmd);
dictAdd(g_pserver->orig_commands,sdsdup(cmdname),cp->rediscmd);
cp->rediscmd->id = ACLGetCommandID(cmdname); /* ID used for ACL. */
return REDISMODULE_OK;
}
@@ -756,6 +763,7 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
module->usedby = listCreate();
module->usingMods = listCreate();
module->filters = listCreate();
module->in_call = 0;
ctx->module = module;
}
@@ -1355,7 +1363,7 @@ int RM_Replicate(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...)
/* Release the argv. */
for (j = 0; j < argc; j++) decrRefCount(argv[j]);
zfree(argv);
server.dirty++;
g_pserver->dirty++;
return REDISMODULE_OK;
}
@@ -1374,7 +1382,7 @@ int RM_ReplicateVerbatim(RedisModuleCtx *ctx) {
alsoPropagate(ctx->client->cmd,ctx->client->db->id,
ctx->client->argv,ctx->client->argc,
PROPAGATE_AOF|PROPAGATE_REPL);
server.dirty++;
g_pserver->dirty++;
return REDISMODULE_OK;
}
@@ -1454,29 +1462,29 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) {
flags |= REDISMODULE_CTX_FLAGS_REPLICATED;
}
if (server.cluster_enabled)
if (g_pserver->cluster_enabled)
flags |= REDISMODULE_CTX_FLAGS_CLUSTER;
/* Maxmemory and eviction policy */
if (server.maxmemory > 0) {
if (g_pserver->maxmemory > 0) {
flags |= REDISMODULE_CTX_FLAGS_MAXMEMORY;
if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION)
if (g_pserver->maxmemory_policy != MAXMEMORY_NO_EVICTION)
flags |= REDISMODULE_CTX_FLAGS_EVICT;
}
/* Persistence flags */
if (server.aof_state != AOF_OFF)
if (g_pserver->aof_state != AOF_OFF)
flags |= REDISMODULE_CTX_FLAGS_AOF;
if (server.saveparamslen > 0)
if (g_pserver->saveparamslen > 0)
flags |= REDISMODULE_CTX_FLAGS_RDB;
/* Replication flags */
if (listLength(server.masters) == 0) {
if (listLength(g_pserver->masters) == 0) {
flags |= REDISMODULE_CTX_FLAGS_MASTER;
} else {
flags |= REDISMODULE_CTX_FLAGS_SLAVE;
if (server.repl_slave_ro)
if (g_pserver->repl_slave_ro)
flags |= REDISMODULE_CTX_FLAGS_READONLY;
}
@@ -1525,7 +1533,7 @@ void *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
if (mode & REDISMODULE_WRITE) {
value = lookupKeyWrite(ctx->client->db,keyname);
} else {
value = lookupKeyRead(ctx->client->db,keyname);
value = lookupKeyRead(ctx->client->db,keyname).unsafe_robjcast();
if (value == NULL) {
return NULL;
}
@@ -2750,7 +2758,7 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
int replicate = 0; /* Replicate this command? */
int call_flags;
sds proto = nullptr;
/* Create the client and dispatch the command. */
va_start(ap, fmt);
c = createClient(-1, IDX_EVENT_LOOP_MAIN);
@@ -2792,12 +2800,12 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
/* If this is a Redis Cluster node, we need to make sure the module is not
* trying to access non-local keys, with the exception of commands
* received from our master. */
if (server.cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {
if (g_pserver->cluster_enabled && !(ctx->client->flags & CLIENT_MASTER)) {
/* Duplicate relevant flags in the module client. */
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
c->flags |= ctx->client->flags & (CLIENT_READONLY|CLIENT_ASKING);
if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,NULL) !=
server.cluster->myself)
g_pserver->cluster->myself)
{
errno = EPERM;
goto cleanup;
@@ -3507,7 +3515,7 @@ void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_li
else if (!strcasecmp(levelstr,"warning")) level = LL_WARNING;
else level = LL_VERBOSE; /* Default. */
if (level < server.verbosity) return;
if (level < cserver.verbosity) return;
name_len = snprintf(msg, sizeof(msg),"<%s> ", module->name);
vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap);
@@ -3663,9 +3671,22 @@ int RM_UnblockClient(RedisModuleBlockedClient *bc, void *privdata) {
pthread_mutex_lock(&moduleUnblockedClientsMutex);
bc->privdata = privdata;
listAddNodeTail(moduleUnblockedClients,bc);
if (write(server.module_blocked_pipe[1],"A",1) != 1) {
/* Ignore the error, this is best-effort. */
if (bc->client != nullptr)
{
if (write(g_pserver->rgthreadvar[bc->client->iel].module_blocked_pipe[1],"A",1) != 1) {
/* Ignore the error, this is best-effort. */
}
}
else
{
for (int iel = 0; iel < cserver.cthreads; ++iel)
{
if (write(g_pserver->rgthreadvar[iel].module_blocked_pipe[1],"A",1) != 1) {
/* Ignore the error, this is best-effort. */
}
}
}
pthread_mutex_unlock(&moduleUnblockedClientsMutex);
return REDISMODULE_OK;
}
@@ -3706,8 +3727,7 @@ void RM_SetDisconnectCallback(RedisModuleBlockedClient *bc, RedisModuleDisconnec
* blocked client, it was terminated by Redis (for timeout or other reasons).
* When this happens the RedisModuleBlockedClient structure in the queue
* will have the 'client' field set to NULL. */
void moduleHandleBlockedClients(void) {
listNode *ln;
void moduleHandleBlockedClients(int iel) {
RedisModuleBlockedClient *bc;
serverAssert(GlobalLocksAcquired());
@@ -3715,12 +3735,16 @@ void moduleHandleBlockedClients(void) {
/* Here we unblock all the pending clients blocked in modules operations
* so we can read every pending "awake byte" in the pipe. */
char buf[1];
while (read(server.module_blocked_pipe[0],buf,1) == 1);
while (listLength(moduleUnblockedClients)) {
ln = listFirst(moduleUnblockedClients);
while (read(serverTL->module_blocked_pipe[0],buf,1) == 1);
listIter li;
listNode *ln;
listRewind(moduleUnblockedClients, &li);
while ((ln = listNext(&li))) {
bc = (RedisModuleBlockedClient*)ln->value;
client *c = bc->client;
serverAssert(c->iel == IDX_EVENT_LOOP_MAIN);
if ((c != nullptr) && (iel != c->iel))
continue;
listDelNode(moduleUnblockedClients,ln);
pthread_mutex_unlock(&moduleUnblockedClientsMutex);
@@ -3763,14 +3787,7 @@ void moduleHandleBlockedClients(void) {
* replies to send to the client in a thread safe context.
* We need to glue such replies to the client output buffer and
* free the temporary client we just used for the replies. */
if (c) {
if (bc->reply_client->bufpos)
addReplyProto(c,bc->reply_client->buf,
bc->reply_client->bufpos);
if (listLength(bc->reply_client->reply))
listJoin(c->reply,bc->reply_client->reply);
c->reply_bytes += bc->reply_client->reply_bytes;
}
if (c) AddReplyFromClient(c, bc->reply_client);
freeClient(bc->reply_client);
if (c != NULL) {
@@ -3788,9 +3805,9 @@ void moduleHandleBlockedClients(void) {
c->flags |= CLIENT_PENDING_WRITE;
AssertCorrectThread(c);
fastlock_lock(&server.rgthreadvar[c->iel].lockPendingWrite);
listAddNodeHead(server.rgthreadvar[c->iel].clients_pending_write,c);
fastlock_unlock(&server.rgthreadvar[c->iel].lockPendingWrite);
fastlock_lock(&g_pserver->rgthreadvar[c->iel].lockPendingWrite);
g_pserver->rgthreadvar[c->iel].clients_pending_write.push_back(c);
fastlock_unlock(&g_pserver->rgthreadvar[c->iel].lockPendingWrite);
}
}
@@ -3894,13 +3911,18 @@ RedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {
* in order to keep things like the currently selected database and similar
* things. */
ctx->client = createClient(-1, IDX_EVENT_LOOP_MAIN);
if (bc) selectDb(ctx->client,bc->dbid);
if (bc) {
selectDb(ctx->client,bc->dbid);
ctx->client->id = bc->client->id;
}
return ctx;
}
/* Release a thread safe context. */
void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
moduleAcquireGIL(false /*fServerThread*/);
moduleFreeContext(ctx);
moduleReleaseGIL(false /*fServerThread*/);
zfree(ctx);
}
@@ -3910,6 +3932,8 @@ void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
void RM_ThreadSafeContextLock(RedisModuleCtx *ctx) {
UNUSED(ctx);
moduleAcquireGIL(FALSE /*fServerThread*/);
if (serverTL == nullptr)
serverTL = &g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN]; // arbitrary module threads get the main thread context
}
/* Release the server lock after a thread safe API call was executed. */
@@ -3919,23 +3943,36 @@ void RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {
}
void moduleAcquireGIL(int fServerThread) {
std::unique_lock<std::mutex> lock(s_mutex);
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
while (*pcheck > 0)
s_cv.wait(lock);
if (fServerThread)
{
pthread_rwlock_rdlock(&moduleGIL);
++s_cAcquisitionsServer;
}
else
{
pthread_rwlock_wrlock(&moduleGIL);
fModuleGILWlocked = TRUE;
++s_cAcquisitionsModule;
fModuleGILWlocked++;
}
}
void moduleReleaseGIL(int fServerThread) {
pthread_rwlock_unlock(&moduleGIL);
if (!fServerThread)
std::unique_lock<std::mutex> lock(s_mutex);
if (fServerThread)
{
fModuleGILWlocked = FALSE;
--s_cAcquisitionsServer;
}
else
{
--s_cAcquisitionsModule;
fModuleGILWlocked--;
}
s_cv.notify_all();
}
int moduleGILAcquiredByModule(void) {
@@ -4107,7 +4144,7 @@ void moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8
* is already a callback for this function, the callback is unregistered
* (so this API call is also used in order to delete the receiver). */
void RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) {
if (!server.cluster_enabled) return;
if (!g_pserver->cluster_enabled) return;
uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);
moduleClusterReceiver *r = clusterReceivers[type], *prev = NULL;
@@ -4151,7 +4188,7 @@ void RM_RegisterClusterMessageReceiver(RedisModuleCtx *ctx, uint8_t type, RedisM
* otherwise if the node is not connected or such node ID does not map to any
* known cluster node, REDISMODULE_ERR is returned. */
int RM_SendClusterMessage(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) {
if (!server.cluster_enabled) return REDISMODULE_ERR;
if (!g_pserver->cluster_enabled) return REDISMODULE_ERR;
uint64_t module_id = moduleTypeEncodeId(ctx->module->name,0);
if (clusterSendModuleMessageToTarget(target_id,module_id,type,msg,len) == C_OK)
return REDISMODULE_OK;
@@ -4184,10 +4221,10 @@ int RM_SendClusterMessage(RedisModuleCtx *ctx, char *target_id, uint8_t type, un
char **RM_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes) {
UNUSED(ctx);
if (!server.cluster_enabled) return NULL;
size_t count = dictSize(server.cluster->nodes);
if (!g_pserver->cluster_enabled) return NULL;
size_t count = dictSize(g_pserver->cluster->nodes);
char **ids = (char**)zmalloc((count+1)*REDISMODULE_NODE_ID_LEN, MALLOC_LOCAL);
dictIterator *di = dictGetIterator(server.cluster->nodes);
dictIterator *di = dictGetIterator(g_pserver->cluster->nodes);
dictEntry *de;
int j = 0;
while((de = dictNext(di)) != NULL) {
@@ -4214,8 +4251,8 @@ void RM_FreeClusterNodesList(char **ids) {
/* Return this node ID (REDISMODULE_CLUSTER_ID_LEN bytes) or NULL if the cluster
* is disabled. */
const char *RM_GetMyClusterID(void) {
if (!server.cluster_enabled) return NULL;
return server.cluster->myself->name;
if (!g_pserver->cluster_enabled) return NULL;
return g_pserver->cluster->myself->name;
}
/* Return the number of nodes in the cluster, regardless of their state
@@ -4223,8 +4260,8 @@ const char *RM_GetMyClusterID(void) {
* be smaller, but not greater than this number. If the instance is not in
* cluster mode, zero is returned. */
size_t RM_GetClusterSize(void) {
if (!server.cluster_enabled) return 0;
return dictSize(server.cluster->nodes);
if (!g_pserver->cluster_enabled) return 0;
return dictSize(g_pserver->cluster->nodes);
}
/* Populate the specified info for the node having as ID the specified 'id',
@@ -4304,9 +4341,9 @@ int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *m
void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) {
UNUSED(ctx);
if (flags & REDISMODULE_CLUSTER_FLAG_NO_FAILOVER)
server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_FAILOVER;
g_pserver->cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_FAILOVER;
if (flags & REDISMODULE_CLUSTER_FLAG_NO_REDIRECTION)
server.cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION;
g_pserver->cluster_module_flags |= CLUSTER_MODULE_FLAG_NO_REDIRECTION;
}
/* --------------------------------------------------------------------------
@@ -4415,7 +4452,7 @@ RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisMod
if (memcmp(ri.key,&key,sizeof(key)) == 0) {
/* This is the first key, we need to re-install the timer according
* to the just added event. */
aeDeleteTimeEvent(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,aeTimer);
aeDeleteTimeEvent(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,aeTimer);
aeTimer = -1;
}
raxStop(&ri);
@@ -4424,7 +4461,7 @@ RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisMod
/* If we have no main timer (the old one was invalidated, or this is the
* first module timer we have), install one. */
if (aeTimer == -1)
aeTimer = aeCreateTimeEvent(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,period,moduleTimerHandler,NULL,NULL);
aeTimer = aeCreateTimeEvent(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,period,moduleTimerHandler,NULL,NULL);
return key;
}
@@ -4752,7 +4789,7 @@ int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {
RedisModuleSharedAPI *sapi = (RedisModuleSharedAPI*)zmalloc(sizeof(*sapi), MALLOC_LOCAL);
sapi->module = ctx->module;
sapi->func = func;
if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) {
if (dictAdd(g_pserver->sharedapi, (char*)apiname, sapi) != DICT_OK) {
zfree(sapi);
return REDISMODULE_ERR;
}
@@ -4793,7 +4830,7 @@ int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {
* }
*/
void *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {
dictEntry *de = dictFind(server.sharedapi, apiname);
dictEntry *de = dictFind(g_pserver->sharedapi, apiname);
if (de == NULL) return NULL;
RedisModuleSharedAPI *sapi = (RedisModuleSharedAPI*)dictGetVal(de);
if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) {
@@ -4811,13 +4848,13 @@ void *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {
* The number of unregistered APIs is returned. */
int moduleUnregisterSharedAPI(RedisModule *module) {
int count = 0;
dictIterator *di = dictGetSafeIterator(server.sharedapi);
dictIterator *di = dictGetSafeIterator(g_pserver->sharedapi);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
const char *apiname = (const char*)dictGetKey(de);
RedisModuleSharedAPI *sapi = (RedisModuleSharedAPI*)dictGetVal(de);
if (sapi->module == module) {
dictDelete(server.sharedapi,apiname);
dictDelete(g_pserver->sharedapi,apiname);
zfree(sapi);
count++;
}
@@ -4955,6 +4992,12 @@ int RM_UnregisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilter *fi
return REDISMODULE_OK;
}
int moduleHasCommandFilters()
{
// Note: called outside the global lock
return listLength(moduleCommandFilters);
}
void moduleCallCommandFilters(client *c) {
if (listLength(moduleCommandFilters) == 0) return;
@@ -5056,7 +5099,7 @@ int RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos)
* Modules API internals
* -------------------------------------------------------------------------- */
/* server.moduleapi dictionary type. Only uses plain C strings since
/* g_pserver->moduleapi dictionary type. Only uses plain C strings since
* this gets queries from modules. */
uint64_t dictCStringKeyHash(const void *key) {
@@ -5077,8 +5120,8 @@ dictType moduleAPIDictType = {
NULL /* val destructor */
};
int moduleRegisterApi(const char *funcname, void *funcptr) {
return dictAdd(server.moduleapi, (char*)funcname, funcptr);
extern "C" int moduleRegisterApi(const char *funcname, void *funcptr) {
return dictAdd(g_pserver->moduleapi, (char*)funcname, funcptr);
}
#define REGISTER_API(name) \
@@ -5089,7 +5132,7 @@ void moduleRegisterCoreAPI(void);
void moduleInitModulesSystem(void) {
moduleUnblockedClients = listCreate();
server.loadmodule_queue = listCreate();
g_pserver->loadmodule_queue = listCreate();
modules = dictCreate(&modulesDictType,NULL);
/* Set up the keyspace notification susbscriber list and static client */
@@ -5102,27 +5145,16 @@ void moduleInitModulesSystem(void) {
moduleCommandFilters = listCreate();
moduleRegisterCoreAPI();
if (pipe(server.module_blocked_pipe) == -1) {
serverLog(LL_WARNING,
"Can't create the pipe for module blocking commands: %s",
strerror(errno));
exit(1);
}
/* Make the pipe non blocking. This is just a best effort aware mechanism
* and we do not want to block not in the read nor in the write half. */
anetNonBlock(NULL,server.module_blocked_pipe[0]);
anetNonBlock(NULL,server.module_blocked_pipe[1]);
/* Create the timers radix tree. */
Timers = raxNew();
/* Our thread-safe contexts GIL must start with already locked:
* it is just unlocked when it's safe. */
pthread_rwlock_init(&moduleGIL, NULL);
pthread_rwlock_rdlock(&moduleGIL);
moduleAcquireGIL(true);
}
/* Load all the modules in the server.loadmodule_queue list, which is
/* Load all the modules in the g_pserver->loadmodule_queue list, which is
* populated by `loadmodule` directives in the configuration file.
* We can't load modules directly when processing the configuration file
* because the server must be fully initialized before loading modules.
@@ -5135,7 +5167,7 @@ void moduleLoadFromQueue(void) {
listIter li;
listNode *ln;
listRewind(server.loadmodule_queue,&li);
listRewind(g_pserver->loadmodule_queue,&li);
while((ln = listNext(&li))) {
struct moduleLoadQueueEntry *loadmod = (moduleLoadQueueEntry*)ln->value;
if (moduleLoad(loadmod->path,(void **)loadmod->argv,loadmod->argc)
@@ -5158,7 +5190,7 @@ void moduleFreeModuleStructure(struct RedisModule *module) {
void moduleUnregisterCommands(struct RedisModule *module) {
/* Unregister all the commands registered by this module. */
dictIterator *di = dictGetSafeIterator(server.commands);
dictIterator *di = dictGetSafeIterator(g_pserver->commands);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct redisCommand *cmd = (redisCommand*)dictGetVal(de);
@@ -5167,8 +5199,8 @@ void moduleUnregisterCommands(struct RedisModule *module) {
(RedisModuleCommandProxy*)(unsigned long)cmd->getkeys_proc;
sds cmdname = (sds)cp->rediscmd->name;
if (cp->module == module) {
dictDelete(server.commands,cmdname);
dictDelete(server.orig_commands,cmdname);
dictDelete(g_pserver->commands,cmdname);
dictDelete(g_pserver->orig_commands,cmdname);
sdsfree(cmdname);
zfree(cp->rediscmd);
zfree(cp);
@@ -5353,8 +5385,8 @@ size_t moduleCount(void) {
/* Register all the APIs we export. Keep this function at the end of the
* file so that's easy to seek it to add new entries. */
void moduleRegisterCoreAPI(void) {
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
server.sharedapi = dictCreate(&moduleAPIDictType,NULL);
g_pserver->moduleapi = dictCreate(&moduleAPIDictType,NULL);
g_pserver->sharedapi = dictCreate(&moduleAPIDictType,NULL);
REGISTER_API(Alloc);
REGISTER_API(Calloc);
REGISTER_API(Realloc);
+1 -6
View File
@@ -13,7 +13,7 @@ endif
.SUFFIXES: .c .so .xo .o
all: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellofilter.so
all: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so
.c.xo:
$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
@@ -47,11 +47,6 @@ hellodict.xo: ../redismodule.h
hellodict.so: hellodict.xo
hellofilter.xo: ../redismodule.h
hellofilter.so: hellofilter.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
testmodule.xo: ../redismodule.h
testmodule.so: testmodule.xo
+1 -1
View File
@@ -40,7 +40,7 @@
/* Timer callback. */
void timerHandler(RedisModuleCtx *ctx, void *data) {
REDISMODULE_NOT_USED(ctx);
printf("Fired %s!\n", data);
printf("Fired %s!\n", (const char *) data);
RedisModule_Free(data);
}
+10 -11
View File
@@ -82,7 +82,6 @@ void discardTransaction(client *c) {
/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.
* Should be called every time there is an error while queueing a command. */
void flagTransaction(client *c) {
serverAssert(GlobalLocksAcquired());
if (c->flags & CLIENT_MULTI)
c->flags |= CLIENT_DIRTY_EXEC;
}
@@ -111,7 +110,7 @@ void discardCommand(client *c) {
void execCommandPropagateMulti(client *c) {
robj *multistring = createStringObject("MULTI",5);
propagate(server.multiCommand,c->db->id,&multistring,1,
propagate(cserver.multiCommand,c->db->id,&multistring,1,
PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(multistring);
}
@@ -122,7 +121,7 @@ void execCommand(client *c) {
int orig_argc;
struct redisCommand *orig_cmd;
int must_propagate = 0; /* Need to propagate MULTI/EXEC to AOF / slaves? */
int was_master = listLength(server.masters) == 0;
int was_master = listLength(g_pserver->masters) == 0;
if (!(c->flags & CLIENT_MULTI)) {
addReplyError(c,"EXEC without MULTI");
@@ -147,7 +146,7 @@ void execCommand(client *c) {
* was initiated when the instance was a master or a writable replica and
* then the configuration changed (for example instance was turned into
* a replica). */
if (!server.loading && listLength(server.masters) && server.repl_slave_ro &&
if (!g_pserver->loading && listLength(g_pserver->masters) && g_pserver->repl_slave_ro &&
!(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)
{
addReplyError(c,
@@ -178,7 +177,7 @@ void execCommand(client *c) {
must_propagate = 1;
}
call(c,server.loading ? CMD_CALL_NONE : CMD_CALL_FULL);
call(c,g_pserver->loading ? CMD_CALL_NONE : CMD_CALL_FULL);
/* Commands may alter argc/argv, restore mstate. */
c->mstate.commands[j].argc = c->argc;
@@ -193,14 +192,14 @@ void execCommand(client *c) {
/* Make sure the EXEC command will be propagated as well if MULTI
* was already propagated. */
if (must_propagate) {
int is_master = listLength(server.masters) == 0;
server.dirty++;
int is_master = listLength(g_pserver->masters) == 0;
g_pserver->dirty++;
/* If inside the MULTI/EXEC block this instance was suddenly
* switched from master to slave (using the SLAVEOF command), the
* initial MULTI was propagated into the replication backlog, but the
* rest was not. We need to make sure to at least terminate the
* backlog with the final EXEC. */
if (server.repl_backlog && was_master && !is_master) {
if (g_pserver->repl_backlog && was_master && !is_master) {
const char *execcmd = "*1\r\n$4\r\nEXEC\r\n";
feedReplicationBacklog(execcmd,strlen(execcmd));
}
@@ -212,8 +211,8 @@ handle_monitor:
* MUTLI, EXEC, ... commands inside transaction ...
* Instead EXEC is flagged as CMD_SKIP_MONITOR in the command
* table, and we do it here with correct ordering. */
if (listLength(server.monitors) && !server.loading)
replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
if (listLength(g_pserver->monitors) && !g_pserver->loading)
replicationFeedMonitors(c,g_pserver->monitors,c->db->id,c->argv,c->argc);
}
/* ===================== WATCH (CAS alike for MULTI/EXEC) ===================
@@ -323,7 +322,7 @@ void touchWatchedKeysOnFlush(int dbid) {
serverAssert(GlobalLocksAcquired());
/* For every client, check all the waited keys */
listRewind(server.clients,&li1);
listRewind(g_pserver->clients,&li1);
while((ln = listNext(&li1))) {
client *c = (client*)listNodeValue(ln);
listRewind(c->watched_keys,&li2);
+355 -279
View File
File diff suppressed because it is too large. Load diff
+34
View File
@@ -0,0 +1,34 @@
#include <cstddef> // std::size_t
#include "server.h"
#include "new.h"
#ifdef SANITIZE
void *operator new(size_t size, enum MALLOC_CLASS mclass)
{
(void)mclass;
return ::operator new(size);
}
#else
[[deprecated]]
void *operator new(size_t size)
{
return zmalloc(size, MALLOC_LOCAL);
}
void *operator new(size_t size, enum MALLOC_CLASS mclass)
{
return zmalloc(size, mclass);
}
void operator delete(void * p) noexcept
{
zfree(p);
}
void operator delete(void *p, std::size_t) noexcept
{
zfree(p);
}
#endif
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <cstddef> // std::size_t
void *operator new(size_t size, enum MALLOC_CLASS mclass);
#ifndef SANITIZE
[[deprecated]]
void *operator new(size_t size);
void operator delete(void * p) noexcept;
void operator delete(void *p, std::size_t) noexcept;
#endif
+3 -3
View File
@@ -109,12 +109,12 @@ void notifyKeyspaceEvent(int type, const char *event, robj *key, int dbid) {
moduleNotifyKeyspaceEvent(type, event, key, dbid);
/* If notifications for this class of events are off, return ASAP. */
if (!(server.notify_keyspace_events & type)) return;
if (!(g_pserver->notify_keyspace_events & type)) return;
eventobj = createStringObject(event,strlen(event));
/* __keyspace@<db>__:<key> <event> notifications. */
if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {
if (g_pserver->notify_keyspace_events & NOTIFY_KEYSPACE) {
chan = sdsnewlen("__keyspace@",11);
len = ll2string(buf,sizeof(buf),dbid);
chan = sdscatlen(chan, buf, len);
@@ -126,7 +126,7 @@ void notifyKeyspaceEvent(int type, const char *event, robj *key, int dbid) {
}
/* __keyevent@<db>__:<event> <key> notifications. */
if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {
if (g_pserver->notify_keyspace_events & NOTIFY_KEYEVENT) {
chan = sdsnewlen("__keyevent@",11);
if (len == -1) len = ll2string(buf,sizeof(buf),dbid);
chan = sdscatlen(chan, buf, len);
+76 -69
View File
@@ -43,11 +43,12 @@ robj *createObject(int type, void *ptr) {
o->type = type;
o->encoding = OBJ_ENCODING_RAW;
o->m_ptr = ptr;
o->refcount = 1;
o->refcount.store(1, std::memory_order_relaxed);
o->mvcc_tstamp = OBJ_MVCC_INVALID;
/* Set the LRU to the current lruclock (minutes resolution), or
* alternatively the LFU counter. */
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
@@ -68,7 +69,7 @@ robj *createObject(int type, void *ptr) {
*/
robj *makeObjectShared(robj *o) {
serverAssert(o->refcount == 1);
o->refcount = OBJ_SHARED_REFCOUNT;
o->refcount.store(OBJ_SHARED_REFCOUNT, std::memory_order_relaxed);
return o;
}
@@ -90,8 +91,10 @@ robj *createEmbeddedStringObject(const char *ptr, size_t len) {
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
o->refcount = 1;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->refcount.store(1, std::memory_order_relaxed);
o->mvcc_tstamp = OBJ_MVCC_INVALID;
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
@@ -135,8 +138,8 @@ robj *createStringObject(const char *ptr, size_t len) {
robj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {
robj *o;
if (server.maxmemory == 0 ||
!(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))
if (g_pserver->maxmemory == 0 ||
!(g_pserver->maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))
{
/* If the maxmemory policy permits, we can still return shared integers
* even if valueobj is true. */
@@ -280,13 +283,13 @@ robj *createModuleObject(moduleType *mt, void *value) {
return createObject(OBJ_MODULE,mv);
}
void freeStringObject(robj *o) {
void freeStringObject(robj_roptr o) {
if (o->encoding == OBJ_ENCODING_RAW) {
sdsfree(szFromObj(o));
}
}
void freeListObject(robj *o) {
void freeListObject(robj_roptr o) {
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
quicklistRelease((quicklist*)ptrFromObj(o));
} else {
@@ -294,7 +297,7 @@ void freeListObject(robj *o) {
}
}
void freeSetObject(robj *o) {
void freeSetObject(robj_roptr o) {
switch (o->encoding) {
case OBJ_ENCODING_HT:
dictRelease((dict*) ptrFromObj(o));
@@ -307,7 +310,7 @@ void freeSetObject(robj *o) {
}
}
void freeZsetObject(robj *o) {
void freeZsetObject(robj_roptr o) {
zset *zs;
switch (o->encoding) {
case OBJ_ENCODING_SKIPLIST:
@@ -324,7 +327,7 @@ void freeZsetObject(robj *o) {
}
}
void freeHashObject(robj *o) {
void freeHashObject(robj_roptr o) {
switch (o->encoding) {
case OBJ_ENCODING_HT:
dictRelease((dict*) ptrFromObj(o));
@@ -338,22 +341,22 @@ void freeHashObject(robj *o) {
}
}
void freeModuleObject(robj *o) {
void freeModuleObject(robj_roptr o) {
moduleValue *mv = (moduleValue*)ptrFromObj(o);
mv->type->free(mv->value);
zfree(mv);
}
void freeStreamObject(robj *o) {
void freeStreamObject(robj_roptr o) {
freeStream((stream*)ptrFromObj(o));
}
void incrRefCount(robj *o) {
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;
void incrRefCount(robj_roptr o) {
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount.fetch_add(1, std::memory_order_acquire);
}
void decrRefCount(robj *o) {
if (o->refcount == 1) {
void decrRefCount(robj_roptr o) {
if (o->refcount.load(std::memory_order_acquire) == 1) {
switch(o->type) {
case OBJ_STRING: freeStringObject(o); break;
case OBJ_LIST: freeListObject(o); break;
@@ -364,17 +367,17 @@ void decrRefCount(robj *o) {
case OBJ_STREAM: freeStreamObject(o); break;
default: serverPanic("Unknown object type"); break;
}
zfree(o);
zfree(o.unsafe_robjcast());
} else {
if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount.fetch_sub(1, std::memory_order_acquire);
}
}
/* This variant of decrRefCount() gets its argument as void, and is useful
* as free method in data structures that expect a 'void free_object(void*)'
* prototype for the free method. */
void decrRefCountVoid(void *o) {
void decrRefCountVoid(const void *o) {
decrRefCount((robj*)o);
}
@@ -395,7 +398,7 @@ robj *resetRefCount(robj *obj) {
return obj;
}
int checkType(client *c, robj *o, int type) {
int checkType(client *c, robj_roptr o, int type) {
if (o->type != type) {
addReplyAsync(c,shared.wrongtypeerr);
return 1;
@@ -403,7 +406,7 @@ int checkType(client *c, robj *o, int type) {
return 0;
}
int isSdsRepresentableAsLongLong(sds s, long long *llval) {
int isSdsRepresentableAsLongLong(const char *s, long long *llval) {
return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;
}
@@ -460,8 +463,8 @@ robj *tryObjectEncoding(robj *o) {
* Note that we avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* algorithm to work well. */
if ((server.maxmemory == 0 ||
!(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &&
if ((g_pserver->maxmemory == 0 ||
!(g_pserver->maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &&
value >= 0 &&
value < OBJ_SHARED_INTEGERS)
{
@@ -524,6 +527,10 @@ robj *getDecodedObject(robj *o) {
}
}
robj_roptr getDecodedObject(robj_roptr o) {
return getDecodedObject(o.unsafe_robjcast());
}
/* Compare two string objects via strcmp() or strcoll() depending on flags.
* Note that the objects may be integer-encoded. In such a case we
* use ll2string() to get a string representation of the numbers on the stack
@@ -592,7 +599,7 @@ int equalStringObjects(robj *a, robj *b) {
}
}
size_t stringObjectLen(robj *o) {
size_t stringObjectLen(robj_roptr o) {
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
if (sdsEncodedObject(o)) {
return sdslen(szFromObj(o));
@@ -617,7 +624,7 @@ int getDoubleFromObject(const robj *o, double *target) {
(size_t)(eptr-(char*)szFromObj(o)) != sdslen(szFromObj(o)) ||
(errno == ERANGE &&
(value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
isnan(value))
std::isnan(value))
return C_ERR;
} else if (o->encoding == OBJ_ENCODING_INT) {
value = (long)ptrFromObj(o);
@@ -659,7 +666,7 @@ int getLongDoubleFromObject(robj *o, long double *target) {
(size_t)(eptr-(char*)szFromObj(o)) != sdslen(szFromObj(o)) ||
(errno == ERANGE &&
(value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
isnan(value))
std::isnan(value))
return C_ERR;
} else if (o->encoding == OBJ_ENCODING_INT) {
value = (long)szFromObj(o);
@@ -959,39 +966,39 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
struct redisMemOverhead *mh = (redisMemOverhead*)zcalloc(sizeof(*mh), MALLOC_LOCAL);
mh->total_allocated = zmalloc_used;
mh->startup_allocated = server.initial_memory_usage;
mh->peak_allocated = server.stat_peak_memory;
mh->startup_allocated = g_pserver->initial_memory_usage;
mh->peak_allocated = g_pserver->stat_peak_memory;
mh->total_frag =
(float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.zmalloc_used;
(float)g_pserver->cron_malloc_stats.process_rss / g_pserver->cron_malloc_stats.zmalloc_used;
mh->total_frag_bytes =
server.cron_malloc_stats.process_rss - server.cron_malloc_stats.zmalloc_used;
g_pserver->cron_malloc_stats.process_rss - g_pserver->cron_malloc_stats.zmalloc_used;
mh->allocator_frag =
(float)server.cron_malloc_stats.allocator_active / server.cron_malloc_stats.allocator_allocated;
(float)g_pserver->cron_malloc_stats.allocator_active / g_pserver->cron_malloc_stats.allocator_allocated;
mh->allocator_frag_bytes =
server.cron_malloc_stats.allocator_active - server.cron_malloc_stats.allocator_allocated;
g_pserver->cron_malloc_stats.allocator_active - g_pserver->cron_malloc_stats.allocator_allocated;
mh->allocator_rss =
(float)server.cron_malloc_stats.allocator_resident / server.cron_malloc_stats.allocator_active;
(float)g_pserver->cron_malloc_stats.allocator_resident / g_pserver->cron_malloc_stats.allocator_active;
mh->allocator_rss_bytes =
server.cron_malloc_stats.allocator_resident - server.cron_malloc_stats.allocator_active;
g_pserver->cron_malloc_stats.allocator_resident - g_pserver->cron_malloc_stats.allocator_active;
mh->rss_extra =
(float)server.cron_malloc_stats.process_rss / server.cron_malloc_stats.allocator_resident;
(float)g_pserver->cron_malloc_stats.process_rss / g_pserver->cron_malloc_stats.allocator_resident;
mh->rss_extra_bytes =
server.cron_malloc_stats.process_rss - server.cron_malloc_stats.allocator_resident;
g_pserver->cron_malloc_stats.process_rss - g_pserver->cron_malloc_stats.allocator_resident;
mem_total += server.initial_memory_usage;
mem_total += g_pserver->initial_memory_usage;
mem = 0;
if (server.repl_backlog)
mem += zmalloc_size(server.repl_backlog);
if (g_pserver->repl_backlog)
mem += zmalloc_size(g_pserver->repl_backlog);
mh->repl_backlog = mem;
mem_total += mem;
mem = 0;
if (listLength(server.slaves)) {
if (listLength(g_pserver->slaves)) {
listIter li;
listNode *ln;
listRewind(server.slaves,&li);
listRewind(g_pserver->slaves,&li);
while((ln = listNext(&li))) {
client *c = (client*)listNodeValue(ln);
if (c->flags & CLIENT_CLOSE_ASAP)
@@ -1005,11 +1012,11 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mem_total+=mem;
mem = 0;
if (listLength(server.clients)) {
if (listLength(g_pserver->clients)) {
listIter li;
listNode *ln;
listRewind(server.clients,&li);
listRewind(g_pserver->clients,&li);
while((ln = listNext(&li))) {
client *c = (client*)listNodeValue(ln);
if (c->flags & CLIENT_SLAVE && !(c->flags & CLIENT_MONITOR))
@@ -1023,27 +1030,27 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mem_total+=mem;
mem = 0;
if (server.aof_state != AOF_OFF) {
mem += sdsalloc(server.aof_buf);
if (g_pserver->aof_state != AOF_OFF) {
mem += sdsalloc(g_pserver->aof_buf);
mem += aofRewriteBufferSize();
}
mh->aof_buffer = mem;
mem_total+=mem;
mem = server.lua_scripts_mem;
mem += dictSize(server.lua_scripts) * sizeof(dictEntry) +
dictSlots(server.lua_scripts) * sizeof(dictEntry*);
mem += dictSize(server.repl_scriptcache_dict) * sizeof(dictEntry) +
dictSlots(server.repl_scriptcache_dict) * sizeof(dictEntry*);
if (listLength(server.repl_scriptcache_fifo) > 0) {
mem += listLength(server.repl_scriptcache_fifo) * (sizeof(listNode) +
sdsZmallocSize((sds)listNodeValue(listFirst(server.repl_scriptcache_fifo))));
mem = g_pserver->lua_scripts_mem;
mem += dictSize(g_pserver->lua_scripts) * sizeof(dictEntry) +
dictSlots(g_pserver->lua_scripts) * sizeof(dictEntry*);
mem += dictSize(g_pserver->repl_scriptcache_dict) * sizeof(dictEntry) +
dictSlots(g_pserver->repl_scriptcache_dict) * sizeof(dictEntry*);
if (listLength(g_pserver->repl_scriptcache_fifo) > 0) {
mem += listLength(g_pserver->repl_scriptcache_fifo) * (sizeof(listNode) +
sdsZmallocSize((sds)listNodeValue(listFirst(g_pserver->repl_scriptcache_fifo))));
}
mh->lua_caches = mem;
mem_total+=mem;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
long long keyscount = dictSize(db->pdict);
if (keyscount==0) continue;
@@ -1139,8 +1146,8 @@ sds getMemoryDoctorReport(void) {
}
/* Clients using more than 200k each average? */
long numslaves = listLength(server.slaves);
long numclients = listLength(server.clients)-numslaves;
long numslaves = listLength(g_pserver->slaves);
long numclients = listLength(g_pserver->clients)-numslaves;
if ((numclients > 0) && mh->clients_normal / numclients > (1024*200)) {
big_client_buf = 1;
num_reports++;
@@ -1153,7 +1160,7 @@ sds getMemoryDoctorReport(void) {
}
/* Too many scripts are cached? */
if (dictSize(server.lua_scripts) > 1000) {
if (dictSize(g_pserver->lua_scripts) > 1000) {
many_scripts = 1;
num_reports++;
}
@@ -1203,14 +1210,14 @@ sds getMemoryDoctorReport(void) {
return s;
}
/* Set the object LRU/LFU depending on server.maxmemory_policy.
/* Set the object LRU/LFU depending on g_pserver->maxmemory_policy.
* The lfu_freq arg is only relevant if policy is MAXMEMORY_FLAG_LFU.
* The lru_idle and lru_clock args are only relevant if policy
* is MAXMEMORY_FLAG_LRU.
* Either or both of them may be <0, in that case, nothing is set. */
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
long long lru_clock) {
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (lfu_freq >= 0) {
serverAssert(lfu_freq <= 255);
val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;
@@ -1276,7 +1283,7 @@ NULL
} else if (!strcasecmp(szFromObj(c->argv[1]),"idletime") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
return;
}
@@ -1284,7 +1291,7 @@ NULL
} else if (!strcasecmp(szFromObj(c->argv[1]),"freq") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
if (!(g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
return;
}
@@ -1308,7 +1315,7 @@ void memoryCommand(client *c) {
"DOCTOR - Return memory problems reports.",
"MALLOC-STATS -- Return internal statistics report from the memory allocator.",
"PURGE -- Attempt to purge dirty pages for reclamation by the allocator.",
"STATS -- Return information about the memory usage of the server.",
"STATS -- Return information about the memory usage of the g_pserver->",
"USAGE <key> [SAMPLES <count>] -- Return memory in bytes used by <key> and its value. Nested values are sampled up to <count> times (default: 5).",
NULL
};
@@ -1334,7 +1341,7 @@ NULL
}
}
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
addReplyNull(c);
addReplyNull(c, shared.nullbulk);
return;
}
size_t usage = objectComputeSize((robj*)dictGetVal(de),samples);
@@ -1402,13 +1409,13 @@ NULL
addReplyDouble(c,mh->peak_perc);
addReplyBulkCString(c,"allocator.allocated");
addReplyLongLong(c,server.cron_malloc_stats.allocator_allocated);
addReplyLongLong(c,g_pserver->cron_malloc_stats.allocator_allocated);
addReplyBulkCString(c,"allocator.active");
addReplyLongLong(c,server.cron_malloc_stats.allocator_active);
addReplyLongLong(c,g_pserver->cron_malloc_stats.allocator_active);
addReplyBulkCString(c,"allocator.resident");
addReplyLongLong(c,server.cron_malloc_stats.allocator_resident);
addReplyLongLong(c,g_pserver->cron_malloc_stats.allocator_resident);
addReplyBulkCString(c,"allocator-fragmentation.ratio");
addReplyDouble(c,mh->allocator_frag);
+27 -17
View File
@@ -84,7 +84,7 @@ void addReplyPubsubUnsubscribed(client *c, robj *channel) {
if (channel)
addReplyBulk(c,channel);
else
addReplyNull(c);
addReplyNull(c, shared.nullbulk);
addReplyLongLong(c,clientSubscriptionsCount(c));
}
@@ -112,7 +112,7 @@ void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {
if (pattern)
addReplyBulk(c,pattern);
else
addReplyNull(c);
addReplyNull(c, shared.nullbulk);
addReplyLongLong(c,clientSubscriptionsCount(c));
}
@@ -120,7 +120,7 @@ void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {
* Pubsub low level API
*----------------------------------------------------------------------------*/
void freePubsubPattern(void *p) {
void freePubsubPattern(const void *p) {
pubsubPattern *pat = (pubsubPattern *)p;
decrRefCount(pat->pattern);
@@ -143,6 +143,8 @@ int clientSubscriptionsCount(client *c) {
/* Subscribe a client to a channel. Returns 1 if the operation succeeded, or
* 0 if the client was already subscribed to that channel. */
int pubsubSubscribeChannel(client *c, robj *channel) {
serverAssert(GlobalLocksAcquired());
serverAssert(c->lock.fOwnLock());
dictEntry *de;
list *clients = NULL;
int retval = 0;
@@ -152,10 +154,10 @@ int pubsubSubscribeChannel(client *c, robj *channel) {
retval = 1;
incrRefCount(channel);
/* Add the client to the channel -> list of clients hash table */
de = dictFind(server.pubsub_channels,channel);
de = dictFind(g_pserver->pubsub_channels,channel);
if (de == NULL) {
clients = listCreate();
dictAdd(server.pubsub_channels,channel,clients);
dictAdd(g_pserver->pubsub_channels,channel,clients);
incrRefCount(channel);
} else {
clients = (list*)dictGetVal(de);
@@ -181,7 +183,7 @@ int pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {
if (dictDelete(c->pubsub_channels,channel) == DICT_OK) {
retval = 1;
/* Remove the client from the channel -> clients list hash table */
de = dictFind(server.pubsub_channels,channel);
de = dictFind(g_pserver->pubsub_channels,channel);
serverAssertWithInfo(c,NULL,de != NULL);
clients = (list*)dictGetVal(de);
ln = listSearchKey(clients,c);
@@ -191,7 +193,7 @@ int pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {
/* Free the list and associated hash entry at all if this was
* the latest client, so that it will be possible to abuse
* Redis PUBSUB creating millions of channels. */
dictDelete(server.pubsub_channels,channel);
dictDelete(g_pserver->pubsub_channels,channel);
}
}
/* Notify the client */
@@ -202,6 +204,7 @@ int pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */
int pubsubSubscribePattern(client *c, robj *pattern) {
serverAssert(GlobalLocksAcquired());
int retval = 0;
if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {
@@ -212,7 +215,7 @@ int pubsubSubscribePattern(client *c, robj *pattern) {
pat = (pubsubPattern*)zmalloc(sizeof(*pat), MALLOC_LOCAL);
pat->pattern = getDecodedObject(pattern);
pat->pclient = c;
listAddNodeTail(server.pubsub_patterns,pat);
listAddNodeTail(g_pserver->pubsub_patterns,pat);
}
/* Notify the client */
addReplyPubsubPatSubscribed(c,pattern);
@@ -232,8 +235,8 @@ int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
listDelNode(c->pubsub_patterns,ln);
pat.pclient = c;
pat.pattern = pattern;
ln = listSearchKey(server.pubsub_patterns,&pat);
listDelNode(server.pubsub_patterns,ln);
ln = listSearchKey(g_pserver->pubsub_patterns,&pat);
listDelNode(g_pserver->pubsub_patterns,ln);
}
/* Notify the client */
if (notify) addReplyPubsubPatUnsubscribed(c,pattern);
@@ -244,6 +247,7 @@ int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
/* Unsubscribe from all the channels. Return the number of channels the
* client was subscribed to. */
int pubsubUnsubscribeAllChannels(client *c, int notify) {
serverAssert(GlobalLocksAcquired());
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
dictEntry *de;
int count = 0;
@@ -262,6 +266,7 @@ int pubsubUnsubscribeAllChannels(client *c, int notify) {
/* Unsubscribe from all the patterns. Return the number of patterns the
* client was subscribed from. */
int pubsubUnsubscribeAllPatterns(client *c, int notify) {
serverAssert(GlobalLocksAcquired());
listNode *ln;
listIter li;
int count = 0;
@@ -278,13 +283,14 @@ int pubsubUnsubscribeAllPatterns(client *c, int notify) {
/* Publish a message */
int pubsubPublishMessage(robj *channel, robj *message) {
serverAssert(GlobalLocksAcquired());
int receivers = 0;
dictEntry *de;
listNode *ln;
listIter li;
/* Send to clients listening for that channel */
de = dictFind(server.pubsub_channels,channel);
de = dictFind(g_pserver->pubsub_channels,channel);
if (de) {
list *list = reinterpret_cast<::list*>(dictGetVal(de));
listNode *ln;
@@ -293,6 +299,8 @@ int pubsubPublishMessage(robj *channel, robj *message) {
listRewind(list,&li);
while ((ln = listNext(&li)) != NULL) {
client *c = reinterpret_cast<client*>(ln->value);
if (c->flags & CLIENT_CLOSE_ASAP) // avoid blocking if the write will be ignored
continue;
fastlock_lock(&c->lock);
addReplyPubsubMessage(c,channel,message);
fastlock_unlock(&c->lock);
@@ -300,8 +308,8 @@ int pubsubPublishMessage(robj *channel, robj *message) {
}
}
/* Send to clients listening to matching channels */
if (listLength(server.pubsub_patterns)) {
listRewind(server.pubsub_patterns,&li);
if (listLength(g_pserver->pubsub_patterns)) {
listRewind(g_pserver->pubsub_patterns,&li);
channel = getDecodedObject(channel);
while ((ln = listNext(&li)) != NULL) {
pubsubPattern *pat = (pubsubPattern*)ln->value;
@@ -311,6 +319,8 @@ int pubsubPublishMessage(robj *channel, robj *message) {
(char*)ptrFromObj(channel),
sdslen(szFromObj(channel)),0))
{
if (pat->pclient->flags & CLIENT_CLOSE_ASAP)
continue;
fastlock_lock(&pat->pclient->lock);
addReplyPubsubPatMessage(pat->pclient,
pat->pattern,channel,message);
@@ -371,7 +381,7 @@ void punsubscribeCommand(client *c) {
void publishCommand(client *c) {
int receivers = pubsubPublishMessage(c->argv[1],c->argv[2]);
if (server.cluster_enabled)
if (g_pserver->cluster_enabled)
clusterPropagatePublish(c->argv[1],c->argv[2]);
else
forceCommandPropagation(c,PROPAGATE_REPL);
@@ -393,7 +403,7 @@ NULL
{
/* PUBSUB CHANNELS [<pattern>] */
sds pat = (c->argc == 2) ? NULL : szFromObj(c->argv[2]);
dictIterator *di = dictGetIterator(server.pubsub_channels);
dictIterator *di = dictGetIterator(g_pserver->pubsub_channels);
dictEntry *de;
long mblen = 0;
void *replylen;
@@ -418,14 +428,14 @@ NULL
addReplyArrayLen(c,(c->argc-2)*2);
for (j = 2; j < c->argc; j++) {
list *l = (list*)dictFetchValue(server.pubsub_channels,c->argv[j]);
list *l = (list*)dictFetchValue(g_pserver->pubsub_channels,c->argv[j]);
addReplyBulk(c,c->argv[j]);
addReplyLongLong(c,l ? listLength(l) : 0);
}
} else if (!strcasecmp(szFromObj(c->argv[1]),"numpat") && c->argc == 2) {
/* PUBSUB NUMPAT */
addReplyLongLong(c,listLength(server.pubsub_patterns));
addReplyLongLong(c,listLength(g_pserver->pubsub_patterns));
} else {
addReplySubcommandSyntaxError(c);
}
+12 -5
View File
@@ -6,7 +6,7 @@ extern "C" {
#include <sys/wait.h>
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
extern "C" int rdbSaveS3(char *s3bucket, rdbSaveInfo *rsi)
int rdbSaveS3(char *s3bucket, rdbSaveInfo *rsi)
{
int status = EXIT_FAILURE;
int fd[2];
@@ -33,12 +33,19 @@ extern "C" int rdbSaveS3(char *s3bucket, rdbSaveInfo *rsi)
else
{
close(fd[0]);
if (rdbSaveFd(fd[1], rsi) != C_OK)
FILE *fp = fdopen(fd[1], "w");
if (fp == NULL)
{
close(fd[1]);
close (fd[1]);
return C_ERR;
}
close(fd[1]);
if (rdbSaveFp(fp, rsi) != C_OK)
{
fclose(fp);
return C_ERR;
}
fclose(fp);
waitpid(pid, &status, 0);
}
@@ -59,7 +66,7 @@ int rdbLoadS3Core(int fd, rdbSaveInfo *rsi)
if ((fp = fdopen(fd, "rb")) == NULL) return C_ERR;
startLoading(fp);
rioInitWithFile(&rdb,fileno(fp));
rioInitWithFile(&rdb,fp);
retval = rdbLoadRio(&rdb,rsi,0);
fclose(fp);
stopLoading();
+165 -155
View File
@@ -62,7 +62,7 @@ void rdbCheckThenExit(int linenum, const char *reason, ...) {
if (!rdbCheckMode) {
serverLog(LL_WARNING, "%s", msg);
const char * argv[2] = {"",server.rdb_filename};
const char * argv[2] = {"",g_pserver->rdb_filename};
redis_check_rdb_main(2,argv,NULL);
} else {
rdbCheckError("%s",msg);
@@ -418,7 +418,7 @@ ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len) {
/* Try LZF compression - under 20 bytes it's unable to compress even
* aaaaaaaaaaaaaaaaaa so skip it */
if (server.rdb_compression && len > 20) {
if (g_pserver->rdb_compression && len > 20) {
n = rdbSaveLzfStringObject(rdb,(const unsigned char*)s,len);
if (n == -1) return -1;
if (n > 0) return n;
@@ -455,7 +455,7 @@ ssize_t rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
}
/* Like rdbSaveRawString() gets a Redis object instead. */
ssize_t rdbSaveStringObject(rio *rdb, robj *obj) {
ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj) {
/* Avoid to decode the object, then encode it again, if the
* object is already integer encoded. */
if (obj->encoding == OBJ_ENCODING_INT) {
@@ -543,10 +543,10 @@ int rdbSaveDoubleValue(rio *rdb, double val) {
unsigned char buf[128];
int len;
if (isnan(val)) {
if (std::isnan(val)) {
buf[0] = 253;
len = 1;
} else if (!isfinite(val)) {
} else if (!std::isfinite(val)) {
len = 1;
buf[0] = (val < 0) ? 255 : 254;
} else {
@@ -623,7 +623,7 @@ int rdbLoadBinaryFloatValue(rio *rdb, float *val) {
}
/* Save the object type of object "o". */
int rdbSaveObjectType(rio *rdb, robj *o) {
int rdbSaveObjectType(rio *rdb, robj_roptr o) {
switch (o->type) {
case OBJ_STRING:
return rdbSaveType(rdb,RDB_TYPE_STRING);
@@ -752,7 +752,7 @@ size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {
/* Save a Redis object.
* Returns -1 on error, number of bytes written on success. */
ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
ssize_t n = 0, nwritten = 0;
if (o->type == OBJ_STRING) {
@@ -992,6 +992,31 @@ ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {
return nwritten;
}
/* Save an AUX field. */
ssize_t rdbSaveAuxField(rio *rdb, const void *key, size_t keylen, const void *val, size_t vallen) {
ssize_t ret, len = 0;
if ((ret = rdbSaveType(rdb,RDB_OPCODE_AUX)) == -1) return -1;
len += ret;
if ((ret = rdbSaveRawString(rdb,(const unsigned char*)key,keylen)) == -1) return -1;
len += ret;
if ((ret = rdbSaveRawString(rdb,(const unsigned char*)val,vallen)) == -1) return -1;
len += ret;
return len;
}
/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained
* with strlen(). */
ssize_t rdbSaveAuxFieldStrStr(rio *rdb, const char *key, const char *val) {
return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));
}
/* Wrapper for strlen(key) + integer type (up to long long range). */
ssize_t rdbSaveAuxFieldStrInt(rio *rdb, const char *key, long long val) {
char buf[LONG_STR_SIZE];
int vlen = ll2string(buf,sizeof(buf),val);
return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);
}
/* Return the length the object will have on disk if saved with
* the rdbSaveObject() function. Currently we use a trick to get
* this length with very little changes to the code. In the future
@@ -1007,8 +1032,8 @@ size_t rdbSavedObjectLen(robj *o) {
* On success if the key was actually saved 1 is returned, otherwise 0
* is returned (the key was already expired). */
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {
int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU;
int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU;
int savelru = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU;
int savelfu = g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU;
/* Save the expire time */
if (expiretime != -1) {
@@ -1036,6 +1061,10 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {
if (rdbWriteRaw(rdb,buf,1) == -1) return -1;
}
char szMvcc[32];
snprintf(szMvcc, 32, "%" PRIu64, val->mvcc_tstamp);
if (rdbSaveAuxFieldStrStr(rdb,"mvcc-tstamp", szMvcc) == -1) return -1;
/* Save type, key, value */
if (rdbSaveObjectType(rdb,val) == -1) return -1;
if (rdbSaveStringObject(rdb,key) == -1) return -1;
@@ -1043,38 +1072,13 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {
return 1;
}
/* Save an AUX field. */
ssize_t rdbSaveAuxField(rio *rdb, const void *key, size_t keylen, const void *val, size_t vallen) {
ssize_t ret, len = 0;
if ((ret = rdbSaveType(rdb,RDB_OPCODE_AUX)) == -1) return -1;
len += ret;
if ((ret = rdbSaveRawString(rdb,(const unsigned char*)key,keylen)) == -1) return -1;
len += ret;
if ((ret = rdbSaveRawString(rdb,(const unsigned char*)val,vallen)) == -1) return -1;
len += ret;
return len;
}
/* Wrapper for rdbSaveAuxField() used when key/val length can be obtained
* with strlen(). */
ssize_t rdbSaveAuxFieldStrStr(rio *rdb, const char *key, const char *val) {
return rdbSaveAuxField(rdb,key,strlen(key),val,strlen(val));
}
/* Wrapper for strlen(key) + integer type (up to long long range). */
ssize_t rdbSaveAuxFieldStrInt(rio *rdb, const char *key, long long val) {
char buf[LONG_STR_SIZE];
int vlen = ll2string(buf,sizeof(buf),val);
return rdbSaveAuxField(rdb,key,strlen(key),buf,vlen);
}
/* Save a few default AUX fields with information about the RDB generated. */
int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {
int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
int aof_preamble = (flags & RDB_SAVE_AOF_PREAMBLE) != 0;
/* Add a few fields about the state when the RDB was created. */
if (rdbSaveAuxFieldStrStr(rdb,"redis-ver",REDIS_VERSION) == -1) return -1;
if (rdbSaveAuxFieldStrStr(rdb,"redis-ver",KEYDB_REAL_VERSION) == -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1;
@@ -1083,9 +1087,9 @@ int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {
if (rsi) {
if (rdbSaveAuxFieldStrInt(rdb,"repl-stream-db",rsi->repl_stream_db)
== -1) return -1;
if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid)
if (rdbSaveAuxFieldStrStr(rdb,"repl-id",g_pserver->replid)
== -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset)
if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",g_pserver->master_repl_offset)
== -1) return -1;
}
if (rdbSaveAuxFieldStrInt(rdb,"aof-preamble",aof_preamble) == -1) return -1;
@@ -1108,14 +1112,14 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
uint64_t cksum;
size_t processed = 0;
if (server.rdb_checksum)
if (g_pserver->rdb_checksum)
rdb->update_cksum = rioGenericUpdateChecksum;
snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr;
for (j = 0; j < server.dbnum; j++) {
redisDb *db = server.db+j;
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
dict *d = db->pdict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
@@ -1163,8 +1167,8 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
* the script cache as well: on successful PSYNC after a restart, we need
* to be able to process any EVALSHA inside the replication backlog the
* master will send us. */
if (rsi && dictSize(server.lua_scripts)) {
di = dictGetIterator(server.lua_scripts);
if (rsi && dictSize(g_pserver->lua_scripts)) {
di = dictGetIterator(g_pserver->lua_scripts);
while((de = dictNext(di)) != NULL) {
robj *body = (robj*)dictGetVal(de);
if (rdbSaveAuxField(rdb,"lua",3,szFromObj(body),sdslen(szFromObj(body))) == -1)
@@ -1216,14 +1220,14 @@ werr: /* Write error. */
return C_ERR;
}
int rdbSaveFd(int fd, rdbSaveInfo *rsi)
int rdbSaveFp(FILE *fp, rdbSaveInfo *rsi)
{
int error = 0;
rio rdb;
rioInitWithFile(&rdb,fd);
rioInitWithFile(&rdb,fp);
if (server.rdb_save_incremental_fsync)
if (g_pserver->rdb_save_incremental_fsync)
rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
@@ -1236,11 +1240,11 @@ int rdbSaveFd(int fd, rdbSaveInfo *rsi)
int rdbSave(rdbSaveInfo *rsi)
{
int err = C_OK;
if (server.rdb_filename != NULL)
err = rdbSaveFile(server.rdb_filename, rsi);
if (g_pserver->rdb_filename != NULL)
err = rdbSaveFile(g_pserver->rdb_filename, rsi);
if (err == C_OK && server.rdb_s3bucketpath != NULL)
err = rdbSaveS3(server.rdb_s3bucketpath, rsi);
if (err == C_OK && g_pserver->rdb_s3bucketpath != NULL)
err = rdbSaveS3(g_pserver->rdb_s3bucketpath, rsi);
return err;
}
@@ -1263,7 +1267,7 @@ int rdbSaveFile(char *filename, rdbSaveInfo *rsi) {
return C_ERR;
}
if (rdbSaveFd(fileno(fp), rsi) == C_ERR){
if (rdbSaveFp(fp, rsi) == C_ERR){
goto werr;
}
@@ -1288,9 +1292,9 @@ int rdbSaveFile(char *filename, rdbSaveInfo *rsi) {
}
serverLog(LL_NOTICE,"DB saved on disk");
server.dirty = 0;
server.lastsave = time(NULL);
server.lastbgsave_status = C_OK;
g_pserver->dirty = 0;
g_pserver->lastsave = time(NULL);
g_pserver->lastbgsave_status = C_OK;
return C_OK;
werr:
@@ -1304,10 +1308,10 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
pid_t childpid;
long long start;
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
if (g_pserver->aof_child_pid != -1 || g_pserver->rdb_child_pid != -1) return C_ERR;
server.dirty_before_bgsave = server.dirty;
server.lastbgsave_try = time(NULL);
g_pserver->dirty_before_bgsave = g_pserver->dirty;
g_pserver->lastbgsave_try = time(NULL);
openChildInfoPipe();
start = ustime();
@@ -1327,26 +1331,26 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
private_dirty/(1024*1024));
}
server.child_info_data.cow_size = private_dirty;
g_pserver->child_info_data.cow_size = private_dirty;
sendChildInfo(CHILD_INFO_TYPE_RDB);
}
exitFromChild((retval == C_OK) ? 0 : 1);
} else {
/* Parent */
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",g_pserver->stat_fork_time/1000);
if (childpid == -1) {
closeChildInfoPipe();
server.lastbgsave_status = C_ERR;
g_pserver->lastbgsave_status = C_ERR;
serverLog(LL_WARNING,"Can't save in background: fork: %s",
strerror(errno));
return C_ERR;
}
serverLog(LL_NOTICE,"Background saving started by pid %d",childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = RDB_CHILD_TYPE_DISK;
g_pserver->rdb_save_time_start = time(NULL);
g_pserver->rdb_child_pid = childpid;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_DISK;
updateDictResizePolicy();
return C_OK;
}
@@ -1401,7 +1405,7 @@ robj *rdbLoadCheckModuleValue(rio *rdb, char *modulename) {
/* Load a Redis object of the specified type from the specified file.
* On success a newly allocated object is returned, otherwise NULL. */
robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key, uint64_t mvcc_tstamp) {
robj *o = NULL, *ele, *dec;
uint64_t len;
unsigned int i;
@@ -1415,8 +1419,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;
o = createQuicklistObject();
quicklistSetOptions((quicklist*)ptrFromObj(o), server.list_max_ziplist_size,
server.list_compress_depth);
quicklistSetOptions((quicklist*)ptrFromObj(o), g_pserver->list_max_ziplist_size,
g_pserver->list_compress_depth);
/* Load every single element of the list */
while(len--) {
@@ -1432,7 +1436,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;
/* Use a regular set when there are too many entries. */
if (len > server.set_max_intset_entries) {
if (len > g_pserver->set_max_intset_entries) {
o = createSetObject();
/* It's faster to expand the dict to the right size asap in order
* to avoid rehashing */
@@ -1504,8 +1508,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
}
/* Convert *after* loading, since sorted sets are not stored ordered. */
if (zsetLength(o) <= server.zset_max_ziplist_entries &&
maxelelen <= server.zset_max_ziplist_value)
if (zsetLength(o) <= g_pserver->zset_max_ziplist_entries &&
maxelelen <= g_pserver->zset_max_ziplist_value)
zsetConvert(o,OBJ_ENCODING_ZIPLIST);
} else if (rdbtype == RDB_TYPE_HASH) {
uint64_t len;
@@ -1518,7 +1522,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
o = createHashObject();
/* Too many entries? Use a hash table. */
if (len > server.hash_max_ziplist_entries)
if (len > g_pserver->hash_max_ziplist_entries)
hashTypeConvert(o, OBJ_ENCODING_HT);
/* Load every field and value into the ziplist */
@@ -1537,8 +1541,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
sdslen(value), ZIPLIST_TAIL);
/* Convert to hash table if size threshold is exceeded */
if (sdslen(field) > server.hash_max_ziplist_value ||
sdslen(value) > server.hash_max_ziplist_value)
if (sdslen(field) > g_pserver->hash_max_ziplist_value ||
sdslen(value) > g_pserver->hash_max_ziplist_value)
{
sdsfree(field);
sdsfree(value);
@@ -1573,8 +1577,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
} else if (rdbtype == RDB_TYPE_LIST_QUICKLIST) {
if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL;
o = createQuicklistObject();
quicklistSetOptions((quicklist*)ptrFromObj(o), server.list_max_ziplist_size,
server.list_compress_depth);
quicklistSetOptions((quicklist*)ptrFromObj(o), g_pserver->list_max_ziplist_size,
g_pserver->list_compress_depth);
while (len--) {
unsigned char *zl = (unsigned char*)
@@ -1622,8 +1626,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
o->type = OBJ_HASH;
o->encoding = OBJ_ENCODING_ZIPLIST;
if (hashTypeLength(o) > server.hash_max_ziplist_entries ||
maxlen > server.hash_max_ziplist_value)
if (hashTypeLength(o) > g_pserver->hash_max_ziplist_entries ||
maxlen > g_pserver->hash_max_ziplist_value)
{
hashTypeConvert(o, OBJ_ENCODING_HT);
}
@@ -1637,19 +1641,19 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
case RDB_TYPE_SET_INTSET:
o->type = OBJ_SET;
o->encoding = OBJ_ENCODING_INTSET;
if (intsetLen((intset*)ptrFromObj(o)) > server.set_max_intset_entries)
if (intsetLen((intset*)ptrFromObj(o)) > g_pserver->set_max_intset_entries)
setTypeConvert(o,OBJ_ENCODING_HT);
break;
case RDB_TYPE_ZSET_ZIPLIST:
o->type = OBJ_ZSET;
o->encoding = OBJ_ENCODING_ZIPLIST;
if (zsetLength(o) > server.zset_max_ziplist_entries)
if (zsetLength(o) > g_pserver->zset_max_ziplist_entries)
zsetConvert(o,OBJ_ENCODING_SKIPLIST);
break;
case RDB_TYPE_HASH_ZIPLIST:
o->type = OBJ_HASH;
o->encoding = OBJ_ENCODING_ZIPLIST;
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
if (hashTypeLength(o) > g_pserver->hash_max_ziplist_entries)
hashTypeConvert(o, OBJ_ENCODING_HT);
break;
default:
@@ -1816,6 +1820,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
} else {
rdbExitReportCorruptRDB("Unknown RDB encoding type %d",rdbtype);
}
o->mvcc_tstamp = mvcc_tstamp;
return o;
}
@@ -1825,35 +1831,35 @@ void startLoading(FILE *fp) {
struct stat sb;
/* Load the DB */
server.loading = 1;
server.loading_start_time = time(NULL);
server.loading_loaded_bytes = 0;
g_pserver->loading = 1;
g_pserver->loading_start_time = time(NULL);
g_pserver->loading_loaded_bytes = 0;
if (fstat(fileno(fp), &sb) == -1) {
server.loading_total_bytes = 0;
g_pserver->loading_total_bytes = 0;
} else {
server.loading_total_bytes = sb.st_size;
g_pserver->loading_total_bytes = sb.st_size;
}
}
/* Refresh the loading progress info */
void loadingProgress(off_t pos) {
server.loading_loaded_bytes = pos;
if (server.stat_peak_memory < zmalloc_used_memory())
server.stat_peak_memory = zmalloc_used_memory();
g_pserver->loading_loaded_bytes = pos;
if (g_pserver->stat_peak_memory < zmalloc_used_memory())
g_pserver->stat_peak_memory = zmalloc_used_memory();
}
/* Loading finished */
void stopLoading(void) {
server.loading = 0;
g_pserver->loading = 0;
}
/* Track loading progress in order to serve client's from time to time
and if needed calculate rdb checksum */
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
if (server.rdb_checksum)
if (g_pserver->rdb_checksum)
rioGenericUpdateChecksum(r, buf, len);
if (server.loading_process_events_interval_bytes &&
(r->processed_bytes + len)/server.loading_process_events_interval_bytes > r->processed_bytes/server.loading_process_events_interval_bytes)
if (g_pserver->loading_process_events_interval_bytes &&
(r->processed_bytes + len)/g_pserver->loading_process_events_interval_bytes > r->processed_bytes/g_pserver->loading_process_events_interval_bytes)
{
/* The DB can take some non trivial amount of time to load. Update
* our cached time since it is used to create and update the last
@@ -1861,7 +1867,7 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
updateCachedTime();
listIter li;
listNode *ln;
listRewind(server.masters, &li);
listRewind(g_pserver->masters, &li);
while ((ln = listNext(&li)))
{
struct redisMaster *mi = (struct redisMaster*)listNodeValue(ln);
@@ -1869,7 +1875,7 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
replicationSendNewlineToMaster(mi);
}
loadingProgress(r->processed_bytes);
processEventsWhileBlocked(serverTL - server.rgthreadvar);
processEventsWhileBlocked(serverTL - g_pserver->rgthreadvar);
}
}
@@ -1878,14 +1884,15 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
uint64_t dbid;
int type, rdbver;
redisDb *db = server.db+0;
redisDb *db = g_pserver->db+0;
char buf[1024];
/* Key-specific attributes, set by opcodes before the key type. */
long long lru_idle = -1, lfu_freq = -1, expiretime = -1, now = mstime();
long long lru_clock = LRU_CLOCK();
long long lru_clock = 0;
uint64_t mvcc_tstamp = OBJ_MVCC_INVALID;
rdb->update_cksum = rdbLoadProgressCallback;
rdb->max_processing_chunk = server.loading_process_events_interval_bytes;
rdb->max_processing_chunk = g_pserver->loading_process_events_interval_bytes;
if (rioRead(rdb,buf,9) == 0) goto eoferr;
buf[9] = '\0';
if (memcmp(buf,"REDIS",5) != 0) {
@@ -1940,14 +1947,14 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
} else if (type == RDB_OPCODE_SELECTDB) {
/* SELECTDB: Select the specified database. */
if ((dbid = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;
if (dbid >= (unsigned)server.dbnum) {
if (dbid >= (unsigned)cserver.dbnum) {
serverLog(LL_WARNING,
"FATAL: Data file was created with a Redis "
"server configured to handle more than %d "
"databases. Exiting\n", server.dbnum);
"databases. Exiting\n", cserver.dbnum);
exit(1);
}
db = server.db+dbid;
db = g_pserver->db+dbid;
continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_RESIZEDB) {
/* RESIZEDB: Hint about the size of the keys in the currently
@@ -1988,7 +1995,7 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
if (rsi) rsi->repl_offset = strtoll(szFromObj(auxval),NULL,10);
} else if (!strcasecmp(szFromObj(auxkey),"lua")) {
/* Load the script back in memory. */
if (luaCreateFunction(NULL,server.lua,auxval) == NULL) {
if (luaCreateFunction(NULL,g_pserver->lua,auxval) == NULL) {
rdbExitReportCorruptRDB(
"Can't load Lua script from RDB file! "
"BODY: %s", ptrFromObj(auxval));
@@ -2010,6 +2017,9 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
if (haspreamble) serverLog(LL_NOTICE,"RDB has an AOF tail");
} else if (!strcasecmp(szFromObj(auxkey),"redis-bits")) {
/* Just ignored. */
} else if (!strcasecmp(szFromObj(auxkey),"mvcc-tstamp")) {
static_assert(sizeof(unsigned long long) == sizeof(uint64_t), "Ensure long long is 64-bits");
mvcc_tstamp = strtoull(szFromObj(auxval), nullptr, 10);
} else {
/* We ignore fields we don't understand, as by AUX field
* contract. */
@@ -2053,13 +2063,13 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
/* Read key */
if ((key = rdbLoadStringObject(rdb)) == NULL) goto eoferr;
/* Read value */
if ((val = rdbLoadObject(type,rdb,key)) == NULL) goto eoferr;
if ((val = rdbLoadObject(type,rdb,key, mvcc_tstamp)) == NULL) goto eoferr;
/* Check if the key already expired. This function is used when loading
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave. */
if (listLength(server.masters) == 0 && !loading_aof && expiretime != -1 && expiretime < now) {
if (listLength(g_pserver->masters) == 0 && !loading_aof && expiretime != -1 && expiretime < now) {
decrRefCount(key);
decrRefCount(val);
} else {
@@ -2096,7 +2106,7 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
uint64_t cksum, expected = rdb->cksum;
if (rioRead(rdb,&cksum,8) == 0) goto eoferr;
if (server.rdb_checksum) {
if (g_pserver->rdb_checksum) {
memrev64ifbe(&cksum);
if (cksum == 0) {
serverLog(LL_WARNING,"RDB file was saved with checksum disabled: no check performed.");
@@ -2118,11 +2128,11 @@ int rdbLoadFile(char *filename, rdbSaveInfo *rsi);
int rdbLoad(rdbSaveInfo *rsi)
{
int err = C_ERR;
if (server.rdb_filename != NULL)
err = rdbLoadFile(server.rdb_filename, rsi);
if (g_pserver->rdb_filename != NULL)
err = rdbLoadFile(g_pserver->rdb_filename, rsi);
if ((err == C_ERR) && server.rdb_s3bucketpath != NULL)
err = rdbLoadS3(server.rdb_s3bucketpath, rsi);
if ((err == C_ERR) && g_pserver->rdb_s3bucketpath != NULL)
err = rdbLoadS3(g_pserver->rdb_s3bucketpath, rsi);
return err;
}
@@ -2141,7 +2151,7 @@ int rdbLoadFile(char *filename, rdbSaveInfo *rsi) {
if ((fp = fopen(filename,"r")) == NULL) return C_ERR;
startLoading(fp);
rioInitWithFile(&rdb,fileno(fp));
rioInitWithFile(&rdb,fp);
retval = rdbLoadRio(&rdb,rsi,0);
fclose(fp);
stopLoading();
@@ -2154,30 +2164,30 @@ void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
if (!bysignal && exitcode == 0) {
serverLog(LL_NOTICE,
"Background saving terminated with success");
server.dirty = server.dirty - server.dirty_before_bgsave;
server.lastsave = time(NULL);
server.lastbgsave_status = C_OK;
g_pserver->dirty = g_pserver->dirty - g_pserver->dirty_before_bgsave;
g_pserver->lastsave = time(NULL);
g_pserver->lastbgsave_status = C_OK;
} else if (!bysignal && exitcode != 0) {
serverLog(LL_WARNING, "Background saving error");
server.lastbgsave_status = C_ERR;
g_pserver->lastbgsave_status = C_ERR;
} else {
mstime_t latency;
serverLog(LL_WARNING,
"Background saving terminated by signal %d", bysignal);
latencyStartMonitor(latency);
rdbRemoveTempFile(server.rdb_child_pid);
rdbRemoveTempFile(g_pserver->rdb_child_pid);
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("rdb-unlink-temp-file",latency);
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error condition. */
if (bysignal != SIGUSR1)
server.lastbgsave_status = C_ERR;
g_pserver->lastbgsave_status = C_ERR;
}
server.rdb_child_pid = -1;
server.rdb_child_type = RDB_CHILD_TYPE_NONE;
server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;
server.rdb_save_time_start = -1;
g_pserver->rdb_child_pid = -1;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_NONE;
g_pserver->rdb_save_time_last = time(NULL)-g_pserver->rdb_save_time_start;
g_pserver->rdb_save_time_start = -1;
/* Possibly there are slaves waiting for a BGSAVE in order to be served
* (the first stage of SYNC is a bulk transfer of dump.rdb) */
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_DISK);
@@ -2199,9 +2209,9 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
serverLog(LL_WARNING,
"Background transfer terminated by signal %d", bysignal);
}
server.rdb_child_pid = -1;
server.rdb_child_type = RDB_CHILD_TYPE_NONE;
server.rdb_save_time_start = -1;
g_pserver->rdb_child_pid = -1;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_NONE;
g_pserver->rdb_save_time_start = -1;
/* If the child returns an OK exit code, read the set of slave client
* IDs and the associated status code. We'll terminate all the slaves
@@ -2215,7 +2225,7 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
if (!bysignal && exitcode == 0) {
int readlen = sizeof(uint64_t);
if (read(server.rdb_pipe_read_result_from_child, ok_slaves, readlen) ==
if (read(g_pserver->rdb_pipe_read_result_from_child, ok_slaves, readlen) ==
readlen)
{
readlen = ok_slaves[0]*sizeof(uint64_t)*2;
@@ -2224,7 +2234,7 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
* uint64_t element in the array. */
ok_slaves = (uint64_t*)zrealloc(ok_slaves,sizeof(uint64_t)+readlen, MALLOC_LOCAL);
if (readlen &&
read(server.rdb_pipe_read_result_from_child, ok_slaves+1,
read(g_pserver->rdb_pipe_read_result_from_child, ok_slaves+1,
readlen) != readlen)
{
ok_slaves[0] = 0;
@@ -2232,15 +2242,15 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
}
}
close(server.rdb_pipe_read_result_from_child);
close(server.rdb_pipe_write_result_to_parent);
close(g_pserver->rdb_pipe_read_result_from_child);
close(g_pserver->rdb_pipe_write_result_to_parent);
/* We can continue the replication process with all the slaves that
* correctly received the full payload. Others are terminated. */
listNode *ln;
listIter li;
listRewind(server.slaves,&li);
listRewind(g_pserver->slaves,&li);
while((ln = listNext(&li))) {
client *slave = (client*)ln->value;
@@ -2281,7 +2291,7 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
/* When a background RDB saving/transfer terminates, call the right handler. */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {
switch(server.rdb_child_type) {
switch(g_pserver->rdb_child_type) {
case RDB_CHILD_TYPE_DISK:
backgroundSaveDoneHandlerDisk(exitcode,bysignal);
break;
@@ -2298,8 +2308,8 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
* the child did not exit for an error, but because we wanted), and performs
* the cleanup needed. */
void killRDBChild(void) {
kill(server.rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(server.rdb_child_pid);
kill(g_pserver->rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(g_pserver->rdb_child_pid);
closeChildInfoPipe();
updateDictResizePolicy();
}
@@ -2317,25 +2327,25 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
long long start;
int pipefds[2];
if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
if (g_pserver->aof_child_pid != -1 || g_pserver->rdb_child_pid != -1) return C_ERR;
/* Before to fork, create a pipe that will be used in order to
* send back to the parent the IDs of the slaves that successfully
* received all the writes. */
if (pipe(pipefds) == -1) return C_ERR;
server.rdb_pipe_read_result_from_child = pipefds[0];
server.rdb_pipe_write_result_to_parent = pipefds[1];
g_pserver->rdb_pipe_read_result_from_child = pipefds[0];
g_pserver->rdb_pipe_write_result_to_parent = pipefds[1];
/* Collect the file descriptors of the slaves we want to transfer
* the RDB to, which are i WAIT_BGSAVE_START state. */
fds = (int*)zmalloc(sizeof(int)*listLength(server.slaves), MALLOC_LOCAL);
fds = (int*)zmalloc(sizeof(int)*listLength(g_pserver->slaves), MALLOC_LOCAL);
/* We also allocate an array of corresponding client IDs. This will
* be useful for the child process in order to build the report
* (sent via unix pipe) that will be sent to the parent. */
clientids = (uint64_t*)zmalloc(sizeof(uint64_t)*listLength(server.slaves), MALLOC_LOCAL);
clientids = (uint64_t*)zmalloc(sizeof(uint64_t)*listLength(g_pserver->slaves), MALLOC_LOCAL);
numfds = 0;
listRewind(server.slaves,&li);
listRewind(g_pserver->slaves,&li);
while((ln = listNext(&li))) {
client *slave = (client*)ln->value;
@@ -2347,7 +2357,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
* We'll restore it when the children returns (since duped socket
* will share the O_NONBLOCK attribute with the parent). */
anetBlock(NULL,slave->fd);
anetSendTimeout(NULL,slave->fd,server.repl_timeout*1000);
anetSendTimeout(NULL,slave->fd,g_pserver->repl_timeout*1000);
}
}
@@ -2378,7 +2388,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
private_dirty/(1024*1024));
}
server.child_info_data.cow_size = private_dirty;
g_pserver->child_info_data.cow_size = private_dirty;
sendChildInfo(CHILD_INFO_TYPE_RDB);
/* If we are returning OK, at least one slave was served
@@ -2413,7 +2423,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
* process with all the childre that were waiting. */
msglen = sizeof(uint64_t)*(1+2*numfds);
if (*len == 0 ||
write(server.rdb_pipe_write_result_to_parent,msg,msglen)
write(g_pserver->rdb_pipe_write_result_to_parent,msg,msglen)
!= msglen)
{
retval = C_ERR;
@@ -2432,7 +2442,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
/* Undo the state change. The caller will perform cleanup on
* all the slaves in BGSAVE_START state, but an early call to
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
listRewind(server.slaves,&li);
listRewind(g_pserver->slaves,&li);
while((ln = listNext(&li))) {
client *slave = (client*)ln->value;
int j;
@@ -2448,15 +2458,15 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
close(pipefds[1]);
closeChildInfoPipe();
} else {
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
g_pserver->stat_fork_time = ustime()-start;
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",g_pserver->stat_fork_time/1000);
serverLog(LL_NOTICE,"Background RDB transfer started by pid %d",
childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = RDB_CHILD_TYPE_SOCKET;
g_pserver->rdb_save_time_start = time(NULL);
g_pserver->rdb_child_pid = childpid;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_SOCKET;
updateDictResizePolicy();
}
zfree(clientids);
@@ -2467,7 +2477,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
}
void saveCommand(client *c) {
if (server.rdb_child_pid != -1) {
if (g_pserver->rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
return;
}
@@ -2498,11 +2508,11 @@ void bgsaveCommand(client *c) {
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
if (server.rdb_child_pid != -1) {
if (g_pserver->rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
} else if (server.aof_child_pid != -1) {
} else if (g_pserver->aof_child_pid != -1) {
if (schedule) {
server.rdb_bgsave_scheduled = 1;
g_pserver->rdb_bgsave_scheduled = 1;
addReplyStatus(c,"Background saving scheduled");
} else {
addReplyError(c,
@@ -2537,22 +2547,22 @@ rdbSaveInfo *rdbPopulateSaveInfo(rdbSaveInfo *rsi) {
* connects to us, the NULL repl_backlog will trigger a full
* synchronization, at the same time we will use a new replid and clear
* replid2. */
if (!listLength(server.masters) && server.repl_backlog) {
/* Note that when server.slaveseldb is -1, it means that this master
if (g_pserver->fActiveReplica || (!listLength(g_pserver->masters) && g_pserver->repl_backlog)) {
/* Note that when g_pserver->slaveseldb is -1, it means that this master
* didn't apply any write commands after a full synchronization.
* So we can let repl_stream_db be 0, this allows a restarted slave
* to reload replication ID/offset, it's safe because the next write
* command must generate a SELECT statement. */
rsi->repl_stream_db = server.slaveseldb == -1 ? 0 : server.slaveseldb;
rsi->repl_stream_db = g_pserver->slaveseldb == -1 ? 0 : g_pserver->slaveseldb;
return rsi;
}
if (listLength(server.masters) > 1)
if (listLength(g_pserver->masters) > 1)
{
// BUGBUG, warn user about this incomplete implementation
serverLog(LL_WARNING, "Warning: Only backing up first master's information in RDB");
}
struct redisMaster *miFirst = (redisMaster*)(listLength(server.masters) ? listNodeValue(listFirst(server.masters)) : NULL);
struct redisMaster *miFirst = (redisMaster*)(listLength(g_pserver->masters) ? listNodeValue(listFirst(g_pserver->masters)) : NULL);
/* If the instance is a slave we need a connected master
* in order to fetch the currently selected DB. */
+5 -5
View File
@@ -133,7 +133,7 @@ int rdbSaveMillisecondTime(rio *rdb, long long t);
long long rdbLoadMillisecondTime(rio *rdb, int rdbver);
uint64_t rdbLoadLen(rio *rdb, int *isencoded);
int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);
int rdbSaveObjectType(rio *rdb, robj *o);
int rdbSaveObjectType(rio *rdb, robj_roptr o);
int rdbLoadObjectType(rio *rdb);
int rdbLoad(rdbSaveInfo *rsi);
int rdbSaveBackground(rdbSaveInfo *rsi);
@@ -141,16 +141,16 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
void rdbRemoveTempFile(pid_t childpid);
int rdbSave(rdbSaveInfo *rsi);
int rdbSaveFile(char *filename, rdbSaveInfo *rsi);
int rdbSaveFd(int fd, rdbSaveInfo *rsi);
int rdbSaveFp(FILE *pf, rdbSaveInfo *rsi);
int rdbSaveS3(char *path, rdbSaveInfo *rsi);
int rdbLoadS3(char *path, rdbSaveInfo *rsi);
ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key);
ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key);
size_t rdbSavedObjectLen(robj *o);
robj *rdbLoadObject(int type, rio *rdb, robj *key);
robj *rdbLoadObject(int type, rio *rdb, robj *key, uint64_t mvcc_tstamp);
void backgroundSaveDoneHandler(int exitcode, int bysignal);
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);
robj *rdbLoadStringObject(rio *rdb);
ssize_t rdbSaveStringObject(rio *rdb, robj *obj);
ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj);
ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len);
void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr);
int rdbSaveBinaryDoubleValue(rio *rdb, double val);
+15 -2
View File
@@ -258,6 +258,19 @@ static redisConfig *getRedisConfig(const char *ip, int port,
else fprintf(stderr,"%s: %s\n",hostsocket,err);
goto fail;
}
if(config.auth){
void *authReply = NULL;
redisAppendCommand(c, "AUTH %s", config.auth);
if (REDIS_OK != redisGetReply(c, &authReply)) goto fail;
if (reply) freeReplyObject(reply);
reply = ((redisReply *) authReply);
if (reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr, "ERROR: %s\n", reply->str);
goto fail;
}
}
redisAppendCommand(c, "CONFIG GET %s", "save");
redisAppendCommand(c, "CONFIG GET %s", "appendonly");
@@ -1196,7 +1209,7 @@ static int fetchClusterSlotsConfiguration(client c) {
assert(reply->type == REDIS_REPLY_ARRAY);
for (i = 0; i < reply->elements; i++) {
redisReply *r = reply->element[i];
assert(r->type = REDIS_REPLY_ARRAY);
assert(r->type == REDIS_REPLY_ARRAY);
assert(r->elements >= 3);
int from, to, slot;
from = r->element[0]->integer;
@@ -1298,7 +1311,7 @@ int parseOptions(int argc, const char **argv) {
if (*p < '0' || *p > '9') goto invalid;
}
config.randomkeys = 1;
config.randomkeys_keyspacelen = atoi(argv[++i]);
config.randomkeys_keyspacelen = atoi(next);
if (config.randomkeys_keyspacelen < 0)
config.randomkeys_keyspacelen = 0;
} else if (!strcmp(argv[i],"-q")) {
+1 -1
View File
@@ -37,7 +37,7 @@
snprintf(error, sizeof(error), "0x%16llx: %s", (long long)epos, __buf); \
}
static char error[1024];
static char error[1044];
static off_t epos;
int consumeNewline(char *buf) {
+4 -4
View File
@@ -186,7 +186,7 @@ int redis_check_rdb(const char *rdbfilename, FILE *fp) {
int closefile = (fp == NULL);
if (fp == NULL && (fp = fopen(rdbfilename,"r")) == NULL) return 1;
rioInitWithFile(&rdb,fileno(fp));
rioInitWithFile(&rdb,fp);
rdbstate.rio = &rdb;
rdb.update_cksum = rdbLoadProgressCallback;
if (rioRead(&rdb,buf,9) == 0) goto eoferr;
@@ -285,7 +285,7 @@ int redis_check_rdb(const char *rdbfilename, FILE *fp) {
rdbstate.keys++;
/* Read value */
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
if ((val = rdbLoadObject(type,&rdb,key)) == NULL) goto eoferr;
if ((val = rdbLoadObject(type,&rdb,key,OBJ_MVCC_INVALID)) == NULL) goto eoferr;
/* Check if the key already expired. */
if (expiretime != -1 && expiretime < now)
rdbstate.already_expired++;
@@ -297,7 +297,7 @@ int redis_check_rdb(const char *rdbfilename, FILE *fp) {
expiretime = -1;
}
/* Verify the checksum if RDB version is >= 5 */
if (rdbver >= 5 && server.rdb_checksum) {
if (rdbver >= 5 && g_pserver->rdb_checksum) {
uint64_t cksum, expected = rdb.cksum;
rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;
@@ -349,7 +349,7 @@ int redis_check_rdb_main(int argc, const char **argv, FILE *fp) {
* an already initialized Redis instance, check if we really need to. */
if (shared.integers[0] == NULL)
createSharedObjects();
server.loading_process_events_interval_bytes = 0;
g_pserver->loading_process_events_interval_bytes = 0;
rdbCheckMode = 1;
rdbCheckInfo("Checking RDB file %s", argv[1]);
rdbCheckSetupSignals();
+123 -1
View File
@@ -45,6 +45,8 @@
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <sys/types.h>
#include <pwd.h>
#include <hiredis.h>
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
@@ -282,7 +284,7 @@ static int helpEntriesLen;
static sds cliVersion(void) {
sds version;
version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
version = sdscatprintf(sdsempty(), "%s", KEYDB_REAL_VERSION);
/* Add git commit and working tree status when available */
if (strtoll(redisGitSHA1(),NULL,16)) {
@@ -6532,6 +6534,116 @@ static void intrinsicLatencyMode(void) {
}
}
/*------------------------------------------------------------------------------
* Message of the day
*--------------------------------------------------------------------------- */
#ifdef MOTD
#include <curl/curl.h>
static const char *szMotdCachePath()
{
static sds sdsMotdCachePath = NULL;
if (sdsMotdCachePath != NULL)
return sdsMotdCachePath;
struct passwd *pw = getpwuid(getuid());
if (pw == NULL)
return "";
const char *homedir = pw->pw_dir;
sdsMotdCachePath = sdsnew(homedir);
sdsMotdCachePath = sdscat(sdsMotdCachePath, "/.keydb-cli-motd");
return sdsMotdCachePath;
}
static size_t motd_write_callback(void *ptr, size_t size, size_t nmemb, sds *str)
{
*str = sdscatlen(*str, ptr, size*nmemb);
return (size*nmemb);
}
static char *fetchMOTDFromCache()
{
struct stat attrib;
if (stat(szMotdCachePath(), &attrib) != 0)
return NULL;
time_t t = attrib.st_mtim.tv_sec;
time_t now = time(NULL);
if ((now - t) < 14400)
{
// If our cache was updated no more than 4 hours ago use it instead of fetching the MOTD
FILE *pf = fopen(szMotdCachePath(), "rb");
if (pf == NULL)
return NULL;
fseek(pf, 0L, SEEK_END);
long cb = ftell(pf);
fseek(pf, 0L, SEEK_SET); // rewind
sds str = sdsnewlen(NULL, cb);
size_t cbRead = fread(str, 1, cb, pf);
fclose(pf);
if ((long)cbRead != cb)
{
sdsfree(str);
return NULL;
}
return str;
}
return NULL;
}
static void setMOTDCache(const char *sz)
{
FILE *pf = fopen(szMotdCachePath(), "wb");
size_t celem = fwrite(sz, strlen(sz), 1, pf);
(void)celem; // best effort
fclose(pf);
}
static char *fetchMOTD()
{
sds str;
CURL *curl;
CURLcode res;
/* First try and get the string from the cache */
str = fetchMOTDFromCache();
if (str != NULL)
return str;
str = sdsnew("");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://api.keydb.dev/motd/motd.txt");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // follow redirects
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2); // take no more than two seconds
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, motd_write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
{
sdsfree(str);
str = NULL;
}
/* always cleanup */
curl_easy_cleanup(curl);
if (str != NULL)
setMOTDCache(str);
}
return str;
}
#else
static char *fetchMOTD()
{
return NULL;
}
#endif
/*------------------------------------------------------------------------------
* Program main()
*--------------------------------------------------------------------------- */
@@ -6700,6 +6812,15 @@ int main(int argc, char **argv) {
/* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) {
/* Show the message of the day if we are interactive */
if (config.output == OUTPUT_STANDARD) {
char *szMotd = fetchMOTD();
if (szMotd != NULL) {
printf("Message of the day:\n %s\n", szMotd);
sdsfree(szMotd);
}
}
/* Ignore SIGPIPE in interactive mode to force a reconnect */
signal(SIGPIPE, SIG_IGN);
@@ -6711,6 +6832,7 @@ int main(int argc, char **argv) {
/* Otherwise, we have some arguments to execute */
if (cliConnect(0) != REDIS_OK) exit(1);
if (config.eval) {
return evalMode(argc,argv);
} else {
+2 -1
View File
@@ -276,4 +276,5 @@ redisReply *sendScan(unsigned long long *it);
#ifdef __cplusplus
}
#endif
#endif
+9
View File
@@ -5,6 +5,10 @@
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ---------------- Defines common between core and modules --------------- */
/* Error status return values. */
@@ -538,4 +542,9 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
#define RedisModuleString robj
#endif /* REDISMODULE_CORE */
#ifdef __cplusplus
}
#endif
#endif /* REDISMOUDLE_H */
+1 -1
View File
@@ -46,7 +46,7 @@ char *redisGitDirty(void) {
}
uint64_t redisBuildId(void) {
char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1;
char *buildid = KEYDB_REAL_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1;
return crc64(0,(unsigned char*)buildid,strlen(buildid));
}
+453 -336
View File
File diff suppressed because it is too large. Load diff
+8 -7
View File
@@ -109,13 +109,14 @@ void rioInitWithBuffer(rio *r, sds s) {
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
size_t retval;
retval = write(r->io.file.fd,buf,len);
retval = fwrite(buf,len,1,r->io.file.fp);
r->io.file.buffered += len;
if (r->io.file.autosync &&
r->io.file.buffered >= r->io.file.autosync)
{
redis_fsync(r->io.file.fd);
fflush(r->io.file.fp);
redis_fsync(fileno(r->io.file.fp));
r->io.file.buffered = 0;
}
return retval;
@@ -123,18 +124,18 @@ static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
/* Returns 1 or 0 for success/failure. */
static size_t rioFileRead(rio *r, void *buf, size_t len) {
return read(r->io.file.fd,buf,len);
return fread(buf,len,1,r->io.file.fp);
}
/* Returns read/write position in file. */
static off_t rioFileTell(rio *r) {
return lseek(r->io.file.fd, 0, SEEK_CUR);
return ftello(r->io.file.fp);
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
static int rioFileFlush(rio *r) {
return (fsync(r->io.file.fd) == 0) ? 1 : 0;
return (fflush(r->io.file.fp) == 0) ? 1 : 0;
}
static const rio rioFileIO = {
@@ -149,9 +150,9 @@ static const rio rioFileIO = {
{ { NULL, 0 } } /* union for io-specific vars */
};
void rioInitWithFile(rio *r, int fd) {
void rioInitWithFile(rio *r, FILE *fp) {
*r = rioFileIO;
r->io.file.fd = fd;
r->io.file.fp = fp;
r->io.file.buffered = 0;
r->io.file.autosync = 0;
}
+2 -2
View File
@@ -73,7 +73,7 @@ struct _rio {
} buffer;
/* Stdio file pointer target. */
struct {
int fd;
FILE *fp;
off_t buffered; /* Bytes written since last fsync. */
off_t autosync; /* fsync after 'autosync' bytes written. */
} file;
@@ -128,7 +128,7 @@ static inline int rioFlush(rio *r) {
return r->flush(r);
}
void rioInitWithFile(rio *r, int fd);
void rioInitWithFile(rio *r, FILE *fp);
void rioInitWithBuffer(rio *r, sds s);
void rioInitWithFdset(rio *r, int *fds, int numfds);
+96 -102
View File
@@ -191,7 +191,7 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
int j = 0;
string2ll(reply+1,p-reply-1,&mbulklen);
if (server.lua_caller->resp == 2 || atype == '*') {
if (g_pserver->lua_caller->resp == 2 || atype == '*') {
p += 2;
if (mbulklen == -1) {
lua_pushboolean(lua,0);
@@ -203,7 +203,7 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
p = redisProtocolToLuaType(lua,p);
lua_settable(lua,-3);
}
} else if (server.lua_caller->resp == 3) {
} else if (g_pserver->lua_caller->resp == 3) {
/* Here we handle only Set and Map replies in RESP3 mode, since arrays
* follow the above RESP2 code path. */
p += 2;
@@ -371,14 +371,14 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
int acl_retval = 0;
int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;
struct redisCommand *cmd;
client *c = server.lua_client;
client *c = serverTL->lua_client;
sds reply;
// Ensure our client is on the right thread
serverAssert(!(c->flags & CLIENT_PENDING_WRITE));
serverAssert(!(c->flags & CLIENT_UNBLOCKED));
serverAssert(GlobalLocksAcquired());
c->iel = serverTL - server.rgthreadvar;
c->iel = serverTL - g_pserver->rgthreadvar;
/* Cached across calls. */
static robj **argv = NULL;
@@ -388,7 +388,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
static int inuse = 0; /* Recursive calls detection. */
/* Reflect MULTI state */
if (server.lua_multi_emitted || (server.lua_caller->flags & CLIENT_MULTI)) {
if (g_pserver->lua_multi_emitted || (g_pserver->lua_caller->flags & CLIENT_MULTI)) {
c->flags |= CLIENT_MULTI;
} else {
c->flags &= ~CLIENT_MULTI;
@@ -472,7 +472,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
/* Setup our fake client for command execution */
c->argv = argv;
c->argc = argc;
c->puser = server.lua_caller->puser;
c->puser = g_pserver->lua_caller->puser;
/* Process module hooks */
moduleCallCommandFilters(c);
@@ -533,13 +533,13 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
* of this script. */
if (cmd->flags & CMD_WRITE) {
int deny_write_type = writeCommandsDeniedByDiskError();
if (server.lua_random_dirty && !server.lua_replicate_commands) {
if (g_pserver->lua_random_dirty && !g_pserver->lua_replicate_commands) {
luaPushError(lua,
"Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode.");
goto cleanup;
} else if (listLength(server.masters) && server.repl_slave_ro &&
!server.loading &&
!(server.lua_caller->flags & CLIENT_MASTER))
} else if (listLength(g_pserver->masters) && g_pserver->repl_slave_ro &&
!g_pserver->loading &&
!(g_pserver->lua_caller->flags & CLIENT_MASTER))
{
luaPushError(lua, (char*)ptrFromObj(shared.roslaveerr));
goto cleanup;
@@ -549,7 +549,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
} else {
sds aof_write_err = sdscatfmt(sdsempty(),
"-MISCONF Errors writing to the AOF file: %s\r\n",
strerror(server.aof_last_write_errno));
strerror(g_pserver->aof_last_write_errno));
luaPushError(lua, aof_write_err);
sdsfree(aof_write_err);
}
@@ -561,10 +561,10 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
* could enlarge the memory usage are not allowed, but only if this is the
* first write in the context of this script, otherwise we can't stop
* in the middle. */
if (server.maxmemory && /* Maxmemory is actually enabled. */
!server.loading && /* Don't care about mem if loading. */
!listLength(server.masters) && /* Slave must execute the script. */
server.lua_write_dirty == 0 && /* Script had no side effects so far. */
if (g_pserver->maxmemory && /* Maxmemory is actually enabled. */
!g_pserver->loading && /* Don't care about mem if loading. */
!listLength(g_pserver->masters) && /* Slave must execute the script. */
g_pserver->lua_write_dirty == 0 && /* Script had no side effects so far. */
(cmd->flags & CMD_DENYOOM))
{
if (getMaxmemoryState(NULL,NULL,NULL,NULL) != C_OK) {
@@ -573,20 +573,20 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
}
if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1;
if (cmd->flags & CMD_WRITE) server.lua_write_dirty = 1;
if (cmd->flags & CMD_RANDOM) g_pserver->lua_random_dirty = 1;
if (cmd->flags & CMD_WRITE) g_pserver->lua_write_dirty = 1;
/* If this is a Redis Cluster node, we need to make sure Lua is not
* trying to access non-local keys, with the exception of commands
* received from our master or when loading the AOF back in memory. */
if (server.cluster_enabled && !server.loading &&
!(server.lua_caller->flags & CLIENT_MASTER))
if (g_pserver->cluster_enabled && !g_pserver->loading &&
!(g_pserver->lua_caller->flags & CLIENT_MASTER))
{
/* Duplicate relevant flags in the lua client. */
c->flags &= ~(CLIENT_READONLY|CLIENT_ASKING);
c->flags |= server.lua_caller->flags & (CLIENT_READONLY|CLIENT_ASKING);
c->flags |= g_pserver->lua_caller->flags & (CLIENT_READONLY|CLIENT_ASKING);
if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,NULL) !=
server.cluster->myself)
g_pserver->cluster->myself)
{
luaPushError(lua,
"Lua script attempted to access a non local key in a "
@@ -598,22 +598,22 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
/* If we are using single commands replication, we need to wrap what
* we propagate into a MULTI/EXEC block, so that it will be atomic like
* a Lua script in the context of AOF and slaves. */
if (server.lua_replicate_commands &&
!server.lua_multi_emitted &&
!(server.lua_caller->flags & CLIENT_MULTI) &&
server.lua_write_dirty &&
server.lua_repl != PROPAGATE_NONE)
if (g_pserver->lua_replicate_commands &&
!g_pserver->lua_multi_emitted &&
!(g_pserver->lua_caller->flags & CLIENT_MULTI) &&
g_pserver->lua_write_dirty &&
g_pserver->lua_repl != PROPAGATE_NONE)
{
execCommandPropagateMulti(server.lua_caller);
server.lua_multi_emitted = 1;
execCommandPropagateMulti(g_pserver->lua_caller);
g_pserver->lua_multi_emitted = 1;
}
/* Run the command */
if (server.lua_replicate_commands) {
if (g_pserver->lua_replicate_commands) {
/* Set flags according to redis.set_repl() settings. */
if (server.lua_repl & PROPAGATE_AOF)
if (g_pserver->lua_repl & PROPAGATE_AOF)
call_flags |= CMD_CALL_PROPAGATE_AOF;
if (server.lua_repl & PROPAGATE_REPL)
if (g_pserver->lua_repl & PROPAGATE_REPL)
call_flags |= CMD_CALL_PROPAGATE_REPL;
}
call(c,call_flags);
@@ -648,7 +648,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
/* Sort the output array if needed, assuming it is a non-null multi bulk
* reply as expected. */
if ((cmd->flags & CMD_SORT_FOR_SCRIPT) &&
(server.lua_replicate_commands == 0) &&
(g_pserver->lua_replicate_commands == 0) &&
(reply[0] == '*' && reply[1] != '-')) {
luaSortArray(lua);
}
@@ -764,10 +764,10 @@ int luaRedisStatusReplyCommand(lua_State *lua) {
* already started to write, returns false and stick to whole scripts
* replication, which is our default. */
int luaRedisReplicateCommandsCommand(lua_State *lua) {
if (server.lua_write_dirty) {
if (g_pserver->lua_write_dirty) {
lua_pushboolean(lua,0);
} else {
server.lua_replicate_commands = 1;
g_pserver->lua_replicate_commands = 1;
/* When we switch to single commands replication, we can provide
* different math.random() sequences at every call, which is what
* the user normally expects. */
@@ -817,7 +817,7 @@ int luaRedisSetReplCommand(lua_State *lua) {
int argc = lua_gettop(lua);
int flags;
if (server.lua_replicate_commands == 0) {
if (g_pserver->lua_replicate_commands == 0) {
lua_pushstring(lua, "You can set the replication behavior only after turning on single commands replication with redis.replicate_commands().");
return lua_error(lua);
} else if (argc != 1) {
@@ -830,7 +830,7 @@ int luaRedisSetReplCommand(lua_State *lua) {
lua_pushstring(lua, "Invalid replication flags. Use REPL_AOF, REPL_REPLICA, REPL_ALL or REPL_NONE.");
return lua_error(lua);
}
server.lua_repl = flags;
g_pserver->lua_repl = flags;
return 0;
}
@@ -966,9 +966,12 @@ void scriptingInit(int setup) {
lua_State *lua = lua_open();
if (setup) {
server.lua_client = NULL;
server.lua_caller = NULL;
server.lua_timedout = 0;
for (int iel = 0; iel < cserver.cthreads; ++iel)
{
g_pserver->rgthreadvar[iel].lua_client = createClient(-1, iel);
g_pserver->rgthreadvar[iel].lua_client->flags |= CLIENT_LUA;
}
g_pserver->lua_timedout = 0;
ldbInit();
}
@@ -978,8 +981,8 @@ void scriptingInit(int setup) {
/* Initialize a dictionary we use to map SHAs to scripts.
* This is useful for replication, as we need to replicate EVALSHA
* as EVAL, so we need to remember the associated script. */
server.lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);
server.lua_scripts_mem = 0;
g_pserver->lua_scripts = dictCreate(&shaScriptObjectDictType,NULL);
g_pserver->lua_scripts_mem = 0;
/* Register the redis commands table and fields */
lua_newtable(lua);
@@ -1117,29 +1120,20 @@ void scriptingInit(int setup) {
lua_pcall(lua,0,0,0);
}
/* Create the (non connected) client that we use to execute Redis commands
* inside the Lua interpreter.
* Note: there is no need to create it again when this function is called
* by scriptingReset(). */
if (server.lua_client == NULL) {
server.lua_client = createClient(-1, IDX_EVENT_LOOP_MAIN);
server.lua_client->flags |= CLIENT_LUA;
}
/* Lua beginners often don't use "local", this is likely to introduce
* subtle bugs in their code. To prevent problems we protect accesses
* to global variables. */
scriptingEnableGlobalsProtection(lua);
server.lua = lua;
g_pserver->lua = lua;
}
/* Release resources related to Lua scripting.
* This function is used in order to reset the scripting environment. */
void scriptingRelease(void) {
dictRelease(server.lua_scripts);
server.lua_scripts_mem = 0;
lua_close(server.lua);
dictRelease(g_pserver->lua_scripts);
g_pserver->lua_scripts_mem = 0;
lua_close(g_pserver->lua);
}
void scriptingReset(void) {
@@ -1233,7 +1227,7 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) {
sha1hex(funcname+2,(char*)ptrFromObj(body),sdslen((sds)ptrFromObj(body)));
sds sha = sdsnewlen(funcname+2,40);
if ((de = dictFind(server.lua_scripts,sha)) != NULL) {
if ((de = dictFind(g_pserver->lua_scripts,sha)) != NULL) {
sdsfree(sha);
return (sds)dictGetKey(de);
}
@@ -1271,33 +1265,33 @@ sds luaCreateFunction(client *c, lua_State *lua, robj *body) {
/* We also save a SHA1 -> Original script map in a dictionary
* so that we can replicate / write in the AOF all the
* EVALSHA commands as EVAL using the original script. */
int retval = dictAdd(server.lua_scripts,sha,body);
serverAssertWithInfo(c ? c : server.lua_client,NULL,retval == DICT_OK);
server.lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body);
int retval = dictAdd(g_pserver->lua_scripts,sha,body);
serverAssertWithInfo(c ? c : serverTL->lua_client,NULL,retval == DICT_OK);
g_pserver->lua_scripts_mem += sdsZmallocSize(sha) + getStringObjectSdsUsedMemory(body);
incrRefCount(body);
return sha;
}
/* This is the Lua script "count" hook that we use to detect scripts timeout. */
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
long long elapsed = mstime() - server.lua_time_start;
long long elapsed = mstime() - g_pserver->lua_time_start;
UNUSED(ar);
UNUSED(lua);
/* Set the timeout condition if not already set and the maximum
* execution time was reached. */
if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
if (elapsed >= g_pserver->lua_time_limit && g_pserver->lua_timedout == 0) {
serverLog(LL_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
server.lua_timedout = 1;
g_pserver->lua_timedout = 1;
/* Once the script timeouts we reenter the event loop to permit others
* to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason
* we need to mask the client executing the script from the event loop.
* If we don't do that the client may disconnect and could no longer be
* here when the EVAL command will return. */
protectClient(server.lua_caller);
protectClient(g_pserver->lua_caller);
}
if (server.lua_timedout) processEventsWhileBlocked(serverTL - server.rgthreadvar);
if (server.lua_kill) {
if (g_pserver->lua_timedout) processEventsWhileBlocked(serverTL - g_pserver->rgthreadvar);
if (g_pserver->lua_kill) {
serverLog(LL_WARNING,"Lua script killed by user with SCRIPT KILL.");
lua_pushstring(lua,"Script killed by user with SCRIPT KILL...");
lua_error(lua);
@@ -1305,10 +1299,10 @@ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
}
void evalGenericCommand(client *c, int evalsha) {
lua_State *lua = server.lua;
lua_State *lua = g_pserver->lua;
char funcname[43];
long long numkeys;
long long initial_server_dirty = server.dirty;
long long initial_server_dirty = g_pserver->dirty;
int delhook = 0, err;
/* When we replicate whole scripts, we want the same PRNG sequence at
@@ -1323,11 +1317,11 @@ void evalGenericCommand(client *c, int evalsha) {
*
* Thanks to this flag we'll raise an error every time a write command
* is called after a random command was used. */
server.lua_random_dirty = 0;
server.lua_write_dirty = 0;
server.lua_replicate_commands = server.lua_always_replicate_commands;
server.lua_multi_emitted = 0;
server.lua_repl = PROPAGATE_AOF|PROPAGATE_REPL;
g_pserver->lua_random_dirty = 0;
g_pserver->lua_write_dirty = 0;
g_pserver->lua_replicate_commands = g_pserver->lua_always_replicate_commands;
g_pserver->lua_multi_emitted = 0;
g_pserver->lua_repl = PROPAGATE_AOF|PROPAGATE_REPL;
/* Get the number of arguments that are keys */
if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != C_OK)
@@ -1393,7 +1387,7 @@ void evalGenericCommand(client *c, int evalsha) {
luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);
/* Select the right DB in the context of the Lua client */
selectDb(server.lua_client,c->db->id);
selectDb(serverTL->lua_client,c->db->id);
/* Set a hook in order to be able to stop the script execution if it
* is running for too much time.
@@ -1402,14 +1396,14 @@ void evalGenericCommand(client *c, int evalsha) {
*
* If we are debugging, we set instead a "line" hook so that the
* debugger is call-back at every line executed by the script. */
server.lua_caller = c;
server.lua_time_start = mstime();
server.lua_kill = 0;
if (server.lua_time_limit > 0 && ldb.active == 0) {
g_pserver->lua_caller = c;
g_pserver->lua_time_start = mstime();
g_pserver->lua_kill = 0;
if (g_pserver->lua_time_limit > 0 && ldb.active == 0) {
lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
delhook = 1;
} else if (ldb.active) {
lua_sethook(server.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000);
lua_sethook(g_pserver->lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000);
delhook = 1;
}
@@ -1420,14 +1414,14 @@ void evalGenericCommand(client *c, int evalsha) {
/* Perform some cleanup that we need to do both on error and success. */
if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */
if (server.lua_timedout) {
server.lua_timedout = 0;
if (g_pserver->lua_timedout) {
g_pserver->lua_timedout = 0;
/* Restore the client that was protected when the script timeout
* was detected. */
unprotectClient(c);
listIter li;
listNode *ln;
listRewind(server.masters, &li);
listRewind(g_pserver->masters, &li);
while ((ln = listNext(&li)))
{
struct redisMaster *mi = (struct redisMaster*)listNodeValue(ln);
@@ -1435,7 +1429,7 @@ void evalGenericCommand(client *c, int evalsha) {
queueClientForReprocessing(mi->master);
}
}
server.lua_caller = NULL;
g_pserver->lua_caller = NULL;
/* Call the Lua garbage collector from time to time to avoid a
* full cycle performed by Lua, which adds too latency.
@@ -1467,12 +1461,12 @@ void evalGenericCommand(client *c, int evalsha) {
/* If we are using single commands replication, emit EXEC if there
* was at least a write. */
if (server.lua_replicate_commands) {
if (g_pserver->lua_replicate_commands) {
preventCommandPropagation(c);
if (server.lua_multi_emitted) {
if (g_pserver->lua_multi_emitted) {
robj *propargv[1];
propargv[0] = createStringObject("EXEC",4);
alsoPropagate(server.execCommand,c->db->id,propargv,1,
alsoPropagate(cserver.execCommand,c->db->id,propargv,1,
PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(propargv[0]);
}
@@ -1488,12 +1482,12 @@ void evalGenericCommand(client *c, int evalsha) {
* For repliation, everytime a new slave attaches to the master, we need to
* flush our cache of scripts that can be replicated as EVALSHA, while
* for AOF we need to do so every time we rewrite the AOF file. */
if (evalsha && !server.lua_replicate_commands) {
if (evalsha && !g_pserver->lua_replicate_commands) {
if (!replicationScriptCacheExists((sds)ptrFromObj(c->argv[1]))) {
/* This script is not in our script cache, replicate it as
* EVAL, then add it into the script cache, as from now on
* slaves and AOF know about it. */
robj *script = (robj*)dictFetchValue(server.lua_scripts,ptrFromObj(c->argv[1]));
robj *script = (robj*)dictFetchValue(g_pserver->lua_scripts,ptrFromObj(c->argv[1]));
replicationScriptCacheAdd((sds)ptrFromObj(c->argv[1]));
serverAssertWithInfo(c,NULL,script != NULL);
@@ -1502,7 +1496,7 @@ void evalGenericCommand(client *c, int evalsha) {
* just to replicate it as SCRIPT LOAD, otherwise we risk running
* an aborted script on slaves (that may then produce results there)
* or just running a CPU costly read-only script on the slaves. */
if (server.dirty == initial_server_dirty) {
if (g_pserver->dirty == initial_server_dirty) {
rewriteClientCommandVector(c,3,
resetRefCount(createStringObject("SCRIPT",6)),
resetRefCount(createStringObject("LOAD",4)),
@@ -1556,31 +1550,31 @@ NULL
scriptingReset();
addReply(c,shared.ok);
replicationScriptCacheFlush();
server.dirty++; /* Propagating this command is a good idea. */
g_pserver->dirty++; /* Propagating this command is a good idea. */
} else if (c->argc >= 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"exists")) {
int j;
addReplyArrayLen(c, c->argc-2);
for (j = 2; j < c->argc; j++) {
if (dictFind(server.lua_scripts,ptrFromObj(c->argv[j])))
if (dictFind(g_pserver->lua_scripts,ptrFromObj(c->argv[j])))
addReply(c,shared.cone);
else
addReply(c,shared.czero);
}
} else if (c->argc == 3 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"load")) {
sds sha = luaCreateFunction(c,server.lua,c->argv[2]);
sds sha = luaCreateFunction(c,g_pserver->lua,c->argv[2]);
if (sha == NULL) return; /* The error was sent by luaCreateFunction(). */
addReplyBulkCBuffer(c,sha,40);
forceCommandPropagation(c,PROPAGATE_REPL|PROPAGATE_AOF);
} else if (c->argc == 2 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"kill")) {
if (server.lua_caller == NULL) {
if (g_pserver->lua_caller == NULL) {
addReplySds(c,sdsnew("-NOTBUSY No scripts in execution right now.\r\n"));
} else if (server.lua_caller->flags & CLIENT_MASTER) {
} else if (g_pserver->lua_caller->flags & CLIENT_MASTER) {
addReplySds(c,sdsnew("-UNKILLABLE The busy script was sent by a master instance in the context of replication and cannot be killed.\r\n"));
} else if (server.lua_write_dirty) {
} else if (g_pserver->lua_write_dirty) {
addReplySds(c,sdsnew("-UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.\r\n"));
} else {
server.lua_kill = 1;
g_pserver->lua_kill = 1;
addReply(c,shared.ok);
}
} else if (c->argc == 3 && !strcasecmp((const char*)ptrFromObj(c->argv[1]),"debug")) {
@@ -1616,7 +1610,7 @@ void ldbInit(void) {
ldb.fd = -1;
ldb.active = 0;
ldb.logs = listCreate();
listSetFreeMethod(ldb.logs,(void (*)(void*))sdsfree);
listSetFreeMethod(ldb.logs,(void (*)(const void*))sdsfree);
ldb.children = listCreate();
ldb.src = NULL;
ldb.lines = 0;
@@ -2289,7 +2283,7 @@ void ldbEval(lua_State *lua, sds *argv, int argc) {
* implementation, with ldb.step enabled, so as a side effect the Redis command
* and its reply are logged. */
void ldbRedis(lua_State *lua, sds *argv, int argc) {
int j, saved_rc = server.lua_replicate_commands;
int j, saved_rc = g_pserver->lua_replicate_commands;
lua_getglobal(lua,"redis");
lua_pushstring(lua,"call");
@@ -2297,10 +2291,10 @@ void ldbRedis(lua_State *lua, sds *argv, int argc) {
for (j = 1; j < argc; j++)
lua_pushlstring(lua,argv[j],sdslen(argv[j]));
ldb.step = 1; /* Force redis.call() to log. */
server.lua_replicate_commands = 1;
g_pserver->lua_replicate_commands = 1;
lua_pcall(lua,argc-1,1,0); /* Stack: redis, result */
ldb.step = 0; /* Disable logging. */
server.lua_replicate_commands = saved_rc;
g_pserver->lua_replicate_commands = saved_rc;
lua_pop(lua,2); /* Discard the result and clean the stack. */
}
@@ -2474,9 +2468,9 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
/* Check if a timeout occurred. */
if (ar->event == LUA_HOOKCOUNT && ldb.step == 0 && bp == 0) {
mstime_t elapsed = mstime() - server.lua_time_start;
mstime_t timelimit = server.lua_time_limit ?
server.lua_time_limit : 5000;
mstime_t elapsed = mstime() - g_pserver->lua_time_start;
mstime_t timelimit = g_pserver->lua_time_limit ?
g_pserver->lua_time_limit : 5000;
if (elapsed >= timelimit) {
timeout = 1;
ldb.step = 1;
@@ -2504,7 +2498,7 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
lua_pushstring(lua, "timeout during Lua debugging with client closing connection");
lua_error(lua);
}
server.lua_time_start = mstime();
g_pserver->lua_time_start = mstime();
}
}
+2 -2
View File
@@ -157,12 +157,12 @@ sds sdsnew(const char *init) {
}
/* Duplicate an sds string. */
sds sdsdup(const sds s) {
sds sdsdup(const char *s) {
return sdsnewlen(s, sdslen(s));
}
/* Free an sds string. No operation is performed if 's' is NULL. */
void sdsfree(sds s) {
void sdsfree(const char *s) {
if (s == NULL) return;
s_free((char*)s-sdsHdrSize(s[-1]));
}
+4 -4
View File
@@ -102,7 +102,7 @@ struct __attribute__ ((__packed__)) sdshdr64 {
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
static inline size_t sdslen(const sds s) {
static inline size_t sdslen(const char *s) {
unsigned char flags = s[-1];
int type = flags & SDS_TYPE_MASK;
@@ -126,7 +126,7 @@ static inline size_t sdslen(const sds s) {
return 0;
}
static inline size_t sdsavail(const sds s) {
static inline size_t sdsavail(const char * s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5: {
@@ -243,8 +243,8 @@ static inline void sdssetalloc(sds s, size_t newlen) {
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty(void);
sds sdsdup(const sds s);
void sdsfree(sds s);
sds sdsdup(const char *s);
void sdsfree(const char *s);
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
+17 -17
View File
@@ -460,8 +460,8 @@ struct redisCommand sentinelcmds[] = {
/* This function overwrites a few normal Redis config default with Sentinel
* specific defaults. */
void initSentinelConfig(void) {
server.port = REDIS_SENTINEL_PORT;
server.protected_mode = 0; /* Sentinel must be exposed. */
g_pserver->port = REDIS_SENTINEL_PORT;
g_pserver->protected_mode = 0; /* Sentinel must be exposed. */
}
/* Perform the Sentinel mode initialization. */
@@ -470,12 +470,12 @@ void initSentinel(void) {
/* Remove usual Redis commands from the command table, then just add
* the SENTINEL command. */
dictEmpty(server.commands,NULL);
dictEmpty(g_pserver->commands,NULL);
for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
int retval;
struct redisCommand *cmd = sentinelcmds+j;
retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
retval = dictAdd(g_pserver->commands, sdsnew(cmd->name), cmd);
serverAssert(retval == DICT_OK);
/* Translate the command string flags description into an actual
@@ -504,14 +504,14 @@ void initSentinel(void) {
void sentinelIsRunning(void) {
int j;
if (server.configfile == NULL) {
if (cserver.configfile == NULL) {
serverLog(LL_WARNING,
"Sentinel started without a config file. Exiting...");
exit(1);
} else if (access(server.configfile,W_OK) == -1) {
} else if (access(cserver.configfile,W_OK) == -1) {
serverLog(LL_WARNING,
"Sentinel config file %s is not writable: %s. Exiting...",
server.configfile,strerror(errno));
cserver.configfile,strerror(errno));
exit(1);
}
@@ -641,7 +641,7 @@ void sentinelEvent(int level, const char *type, sentinelRedisInstance *ri,
}
/* Log the message if the log level allows it to be logged. */
if (level >= server.verbosity)
if (level >= cserver.verbosity)
serverLog(level,"%s %s",type,msg);
/* Publish the message via Pub/Sub if it's not a debugging one. */
@@ -1926,15 +1926,15 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
* On failure the function logs a warning on the Redis log. */
void sentinelFlushConfig(void) {
int fd = -1;
int saved_hz = server.hz;
int saved_hz = g_pserver->hz;
int rewrite_status;
server.hz = CONFIG_DEFAULT_HZ;
rewrite_status = rewriteConfig(server.configfile);
server.hz = saved_hz;
g_pserver->hz = CONFIG_DEFAULT_HZ;
rewrite_status = rewriteConfig(cserver.configfile);
g_pserver->hz = saved_hz;
if (rewrite_status == -1) goto werr;
if ((fd = open(server.configfile,O_RDONLY)) == -1) goto werr;
if ((fd = open(cserver.configfile,O_RDONLY)) == -1) goto werr;
if (fsync(fd) == -1) goto werr;
if (close(fd) == EOF) goto werr;
return;
@@ -2018,7 +2018,7 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
link->pending_commands = 0;
link->cc_conn_time = mstime();
link->cc->data = link;
redisAeAttach(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,link->cc);
redisAeAttach(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,link->cc);
redisAsyncSetConnectCallback(link->cc,
sentinelLinkEstablishedCallback);
redisAsyncSetDisconnectCallback(link->cc,
@@ -2042,7 +2042,7 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
link->pc_conn_time = mstime();
link->pc->data = link;
redisAeAttach(server.rgthreadvar[IDX_EVENT_LOOP_MAIN].el,link->pc);
redisAeAttach(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el,link->pc);
redisAsyncSetConnectCallback(link->pc,
sentinelLinkEstablishedCallback);
redisAsyncSetDisconnectCallback(link->pc,
@@ -2585,7 +2585,7 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
announce_ip = ip;
}
announce_port = sentinel.announce_port ?
sentinel.announce_port : server.port;
sentinel.announce_port : g_pserver->port;
/* Format and send the Hello message. */
snprintf(payload,sizeof(payload),
@@ -4521,6 +4521,6 @@ void sentinelTimer(void) {
* exactly continue to stay synchronized asking to be voted at the
* same time again and again (resulting in nobody likely winning the
* election because of split brain voting). */
server.hz = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;
g_pserver->hz = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;
}
+936 -834
View File
File diff suppressed because it is too large. Load diff
+245 -150
View File
@@ -40,6 +40,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <string.h>
#include <time.h>
#include <limits.h>
@@ -49,6 +50,9 @@
#include <pthread.h>
#include <syslog.h>
#include <netinet/in.h>
#include <atomic>
#include <vector>
#include <algorithm>
#ifdef __cplusplus
extern "C" {
#include <lua.h>
@@ -84,9 +88,59 @@ typedef long long mstime_t; /* millisecond time type. */
#include "endianconv.h"
#include "crc64.h"
#ifdef __cplusplus
extern "C" {
#endif
extern int g_fTestMode;
struct redisObject;
class robj_roptr
{
const redisObject *m_ptr;
public:
robj_roptr()
: m_ptr(nullptr)
{}
robj_roptr(const redisObject *ptr)
: m_ptr(ptr)
{}
robj_roptr(const robj_roptr&) = default;
robj_roptr(robj_roptr&&) = default;
robj_roptr &operator=(const robj_roptr&) = default;
robj_roptr &operator=(const redisObject *ptr)
{
m_ptr = ptr;
return *this;
}
bool operator==(const robj_roptr &other) const
{
return m_ptr == other.m_ptr;
}
bool operator!=(const robj_roptr &other) const
{
return m_ptr != other.m_ptr;
}
const redisObject* operator->() const
{
return m_ptr;
}
bool operator!() const
{
return !m_ptr;
}
operator bool() const{
return !!m_ptr;
}
redisObject *unsafe_robjcast()
{
return (redisObject*)m_ptr;
}
};
/* Error codes */
#define C_OK 0
@@ -214,7 +268,7 @@ extern "C" {
#define LIMIT_PENDING_QUERYBUF (4*1024*1024) /* 4mb */
/* When configuring the server eventloop, we setup it so that the total number
* of file descriptors we can handle are server.maxclients + RESERVED_FDS +
* of file descriptors we can handle are g_pserver->maxclients + RESERVED_FDS +
* a few more to stay safe. Since RESERVED_FDS defaults to 32, we add 96
* in order to make sure of not over provisioning more than 128 fds. */
#define CONFIG_FDSET_INCR (CONFIG_MIN_RESERVED_FDS+96)
@@ -281,7 +335,7 @@ extern "C" {
#define CLIENT_DIRTY_CAS (1<<5) /* Watched keys modified. EXEC will fail. */
#define CLIENT_CLOSE_AFTER_REPLY (1<<6) /* Close after writing entire reply. */
#define CLIENT_UNBLOCKED (1<<7) /* This client was unblocked and is stored in
server.unblocked_clients */
g_pserver->unblocked_clients */
#define CLIENT_LUA (1<<8) /* This is a non connected client used by Lua */
#define CLIENT_ASKING (1<<9) /* Client issued the ASKING command */
#define CLIENT_CLOSE_ASAP (1<<10)/* Close this client ASAP */
@@ -331,7 +385,7 @@ extern "C" {
buffer configuration. Just the first
three: normal, slave, pubsub. */
/* Slave replication state. Used in server.repl_state for slaves to remember
/* Slave replication state. Used in g_pserver->repl_state for slaves to remember
* what to do next. */
#define REPL_STATE_NONE 0 /* No active replication */
#define REPL_STATE_CONNECT 1 /* Must connect to master */
@@ -496,12 +550,12 @@ extern "C" {
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_KEY_MISS) /* A flag */
/* Get the first bind addr or NULL */
#define NET_FIRST_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)
#define NET_FIRST_BIND_ADDR (g_pserver->bindaddr_count ? g_pserver->bindaddr[0] : NULL)
/* Using the following macro you can run code inside serverCron() with the
* specified period, specified in milliseconds.
* The actual resolution depends on server.hz. */
#define run_with_period(_ms_) if ((_ms_ <= 1000/server.hz) || !(server.cronloops%((_ms_)/(1000/server.hz))))
* The actual resolution depends on g_pserver->hz. */
#define run_with_period(_ms_) if ((_ms_ <= 1000/g_pserver->hz) || !(g_pserver->cronloops%((_ms_)/(1000/g_pserver->hz))))
/* We can print the stacktrace, so our assert is defined this way: */
#define serverAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_serverAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))
@@ -654,16 +708,27 @@ typedef struct RedisModuleDigest {
#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
#define OBJ_SHARED_REFCOUNT INT_MAX
#define OBJ_MVCC_INVALID (0xFFFFFFFFFFFFFFFFULL)
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount;
mutable std::atomic<int> refcount;
uint64_t mvcc_tstamp;
void *m_ptr;
} robj;
__attribute__((always_inline)) inline const void *ptrFromObj(robj_roptr &o)
{
if (o->encoding == OBJ_ENCODING_EMBSTR)
return ((char*)&(o)->m_ptr) + sizeof(struct sdshdr8);
return o->m_ptr;
}
__attribute__((always_inline)) inline void *ptrFromObj(const robj *o)
{
if (o->encoding == OBJ_ENCODING_EMBSTR)
@@ -671,6 +736,11 @@ __attribute__((always_inline)) inline void *ptrFromObj(const robj *o)
return o->m_ptr;
}
__attribute__((always_inline)) inline const char *szFromObj(robj_roptr o)
{
return (const char*)ptrFromObj(o);
}
__attribute__((always_inline)) inline char *szFromObj(const robj *o)
{
return (char*)ptrFromObj(o);
@@ -764,17 +834,17 @@ typedef struct blockingState {
handled in module.c. */
} blockingState;
/* The following structure represents a node in the server.ready_keys list,
/* The following structure represents a node in the g_pserver->ready_keys list,
* where we accumulate all the keys that had clients blocked with a blocking
* operation such as B[LR]POP, but received new data in the context of the
* last executed command.
*
* After the execution of every command or script, we run this list to check
* if as a result we should serve data to clients blocked, unblocking them.
* Note that server.ready_keys will not have duplicates as there dictionary
* Note that g_pserver->ready_keys will not have duplicates as there dictionary
* also called ready_keys in every structure representing a Redis database,
* where we make sure to remember if a given key was already added in the
* server.ready_keys list. */
* g_pserver->ready_keys list. */
typedef struct readyList {
redisDb *db;
robj *key;
@@ -854,7 +924,8 @@ typedef struct client {
time_t ctime; /* Client creation time. */
time_t lastinteraction; /* Time of the last interaction, used for timeout */
time_t obuf_soft_limit_reached_time;
int flags; /* Client flags: CLIENT_* macros. */
std::atomic<int> flags; /* Client flags: CLIENT_* macros. */
int casyncOpsPending;
int fPendingAsyncWrite; /* NOTE: Not a flag because it is written to outside of the client lock (locked by the global lock instead) */
int authenticated; /* Needed when the default user requires auth. */
int replstate; /* Replication state if this is a slave. */
@@ -865,6 +936,7 @@ typedef struct client {
sds replpreamble; /* Replication DB preamble. */
long long read_reploff; /* Read replication offset if this is a master. */
long long reploff; /* Applied replication offset if this is a master. */
long long reploff_skipped; /* Repl backlog we did not send to this client */
long long repl_ack_off; /* Replication ack offset, if this is a slave. */
long long repl_ack_time;/* Replication ack time, if this is a slave. */
long long psync_initial_offset; /* FULLRESYNC reply offset other slaves
@@ -914,7 +986,7 @@ struct moduleLoadQueueEntry {
};
struct sharedObjectsStruct {
robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
robj *crlf, *ok, *err, *emptybulk, *emptymultibulk, *nullbulk, *czero, *cone, *pong, *space,
*colon, *queued, *null[4], *nullarray[4],
*emptyarray, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,
*outofrangeerr, *noscripterr, *loadingerr, *slowscripterr, *bgsaveerr,
@@ -1034,7 +1106,7 @@ struct redisMemOverhead {
* top-level master. */
typedef struct rdbSaveInfo {
/* Used saving and loading. */
int repl_stream_db; /* DB to select in server.master client. */
int repl_stream_db; /* DB to select in g_pserver->master client. */
/* Used only loading. */
int repl_id_is_set; /* True if repl_id field is set. */
@@ -1077,11 +1149,18 @@ struct redisServerThreadVars {
aeEventLoop *el;
int ipfd[CONFIG_BINDADDR_MAX]; /* TCP socket file descriptors */
int ipfd_count; /* Used slots in ipfd[] */
list *clients_pending_write; /* There is to write or install handler. */
std::vector<client*> clients_pending_write; /* There is to write or install handler. */
list *unblocked_clients; /* list of clients to unblock before next loop NOT THREADSAFE */
list *clients_pending_asyncwrite;
int cclients;
client *current_client; /* Current client */
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
client blocked on a module command needs
to be processed. */
client *lua_client = nullptr; /* The "fake client" to query Redis from Lua */
struct fastlock lockPendingWrite;
char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */
long unsigned commandsExecuted = 0;
};
struct redisMaster {
@@ -1089,7 +1168,7 @@ struct redisMaster {
char *masterauth; /* AUTH with this password with master */
char *masterhost; /* Hostname of master */
int masterport; /* Port of master */
client *cached_master; /* Cached master to be reused for PSYNC. */
client *cached_master; /* Cached master to be reused for PSYNC. */
client *master;
/* The following two fields is where we store master PSYNC replid/offset
* while the PSYNC is in progress. At the end we'll copy the fields into
@@ -1108,15 +1187,56 @@ struct redisMaster {
time_t repl_down_since; /* Unix time at which link with master went down */
unsigned char master_uuid[UUID_BINARY_LEN]; /* Used during sync with master, this is our master's UUID */
/* After we've connected with our master use the UUID in server.master */
/* After we've connected with our master use the UUID in g_pserver->master */
};
// Const vars are not changed after worker threads are launched
struct redisServerConst {
pid_t pid; /* Main process pid. */
time_t stat_starttime; /* Server start time */
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
char **exec_argv; /* Executable argv vector (copy). */
int cthreads; /* Number of main worker threads */
int fThreadAffinity; /* Should we pin threads to cores? */
char *pidfile; /* PID file path */
/* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand, *lpushCommand,
*lpopCommand, *rpopCommand, *zpopminCommand,
*zpopmaxCommand, *sremCommand, *execCommand,
*expireCommand, *pexpireCommand, *xclaimCommand,
*xgroupCommand, *rreplayCommand;
/* Configuration */
char *default_masteruser; /* AUTH with this user and masterauth with master */
char *default_masterauth; /* AUTH with this password with master */
int verbosity; /* Loglevel in redis.conf */
int maxidletime; /* Client timeout in seconds */
int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */
int active_defrag_enabled;
size_t active_defrag_ignore_bytes; /* minimum amount of fragmentation waste to start active defrag */
int active_defrag_threshold_lower; /* minimum percentage of fragmentation to start active defrag */
int active_defrag_threshold_upper; /* maximum percentage of fragmentation at which we use maximum effort */
int active_defrag_cycle_min; /* minimal effort for defrag in CPU percentage */
int active_defrag_cycle_max; /* maximal effort for defrag in CPU percentage */
unsigned long active_defrag_max_scan_fields; /* maximum number of fields of set/hash/zset/list to process from within the main dict scan */
size_t client_max_querybuf_len; /* Limit for client query buffer length */
int dbnum; /* Total number of configured DBs */
int supervised; /* 1 if supervised, 0 otherwise. */
int supervised_mode; /* See SUPERVISED_* */
int daemonize; /* True if running as a daemon */
clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];
/* System hardware info */
size_t system_memory_size; /* Total memory in system as reported by OS */
unsigned char uuid[UUID_BINARY_LEN]; /* This server's UUID - populated on boot */
};
struct redisServer {
/* General */
pid_t pid; /* Main process pid. */
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
char **exec_argv; /* Executable argv vector (copy). */
int dynamic_hz; /* Change hz value depending on # of clients. */
int config_hz; /* Configured HZ value. May be different than
the actual 'hz' field value if dynamic-hz
@@ -1126,16 +1246,12 @@ struct redisServer {
dict *commands; /* Command table */
dict *orig_commands; /* Command table before command renaming. */
int cthreads; /* Number of main worker threads */
int fThreadAffinity; /* Should we pin threads to cores? */
struct redisServerThreadVars rgthreadvar[MAX_EVENT_LOOPS];
unsigned int lruclock; /* Clock for LRU eviction */
std::atomic<unsigned int> lruclock; /* Clock for LRU eviction */
int shutdown_asap; /* SHUTDOWN needed ASAP */
int activerehashing; /* Incremental rehash in serverCron() */
int active_defrag_running; /* Active defragmentation running (holds current scan aggressiveness) */
char *pidfile; /* PID file path */
int arch_bits; /* 32 or 64 depending on sizeof(long) */
int cronloops; /* Number of times the cron function run */
char runid[CONFIG_RUN_ID_SIZE+1]; /* ID always different at every exec. */
int sentinel_mode; /* True if this instance is a Sentinel. */
@@ -1146,14 +1262,11 @@ struct redisServer {
dict *sharedapi; /* Like moduleapi but containing the APIs that
modules share with each other. */
list *loadmodule_queue; /* List of modules to load at startup. */
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
client blocked on a module command needs
to be processed. */
/* Networking */
int port; /* TCP listening port */
int tcp_backlog; /* TCP listen() backlog */
char *bindaddr[CONFIG_BINDADDR_MAX]; /* Addresses we should bind to */
int bindaddr_count; /* Number of addresses in server.bindaddr[] */
int bindaddr_count; /* Number of addresses in g_pserver->bindaddr[] */
char *unixsocket; /* UNIX socket path */
mode_t unixsocketperm; /* UNIX socket permission */
int sofd; /* Unix socket file descriptor */
@@ -1162,13 +1275,11 @@ struct redisServer {
list *clients; /* List of active clients */
list *clients_to_close; /* Clients to close asynchronously */
list *slaves, *monitors; /* List of slaves and MONITORs */
client *current_client; /* Current client, only used on crash report */
rax *clients_index; /* Active clients dictionary by client ID. */
int clients_paused; /* True if clients are currently paused */
mstime_t clients_pause_end_time; /* Time when we undo clients_paused */
char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */
dict *migrate_cached_sockets;/* MIGRATE cached sockets */
uint64_t next_client_id; /* Next client unique ID. Incremental. */
std::atomic<uint64_t> next_client_id; /* Next client unique ID. Incremental. */
int protected_mode; /* Don't accept external connections. */
/* RDB / AOF loading information */
int loading; /* We are loading data from disk if true */
@@ -1176,14 +1287,10 @@ struct redisServer {
off_t loading_loaded_bytes;
time_t loading_start_time;
off_t loading_process_events_interval_bytes;
/* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand, *lpushCommand,
*lpopCommand, *rpopCommand, *zpopminCommand,
*zpopmaxCommand, *sremCommand, *execCommand,
*expireCommand, *pexpireCommand, *xclaimCommand,
*xgroupCommand, *rreplayCommand;
int active_expire_enabled; /* Can be disabled for testing purposes. */
/* 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 */
@@ -1209,8 +1316,8 @@ struct redisServer {
long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */
struct malloc_stats cron_malloc_stats; /* sampled in serverCron(). */
long long stat_net_input_bytes; /* Bytes read from network. */
long long stat_net_output_bytes; /* Bytes written to network. */
std::atomic<long long> stat_net_input_bytes; /* Bytes read from network. */
std::atomic<long long> stat_net_output_bytes; /* Bytes written to network. */
size_t stat_rdb_cow_bytes; /* Copy on write bytes during RDB saving. */
size_t stat_aof_cow_bytes; /* Copy on write bytes during AOF rewrite. */
/* The following two are used to track instantaneous metrics, like
@@ -1221,26 +1328,6 @@ struct redisServer {
long long samples[STATS_METRIC_SAMPLES];
int idx;
} inst_metric[STATS_METRIC_COUNT];
/* Configuration */
char *default_masteruser; /* AUTH with this user and masterauth with master */
char *default_masterauth; /* AUTH with this password with master */
int verbosity; /* Loglevel in redis.conf */
int maxidletime; /* Client timeout in seconds */
int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */
int active_expire_enabled; /* Can be disabled for testing purposes. */
int active_defrag_enabled;
size_t active_defrag_ignore_bytes; /* minimum amount of fragmentation waste to start active defrag */
int active_defrag_threshold_lower; /* minimum percentage of fragmentation to start active defrag */
int active_defrag_threshold_upper; /* maximum percentage of fragmentation at which we use maximum effort */
int active_defrag_cycle_min; /* minimal effort for defrag in CPU percentage */
int active_defrag_cycle_max; /* maximal effort for defrag in CPU percentage */
unsigned long active_defrag_max_scan_fields; /* maximum number of fields of set/hash/zset/list to process from within the main dict scan */
size_t client_max_querybuf_len; /* Limit for client query buffer length */
int dbnum; /* Total number of configured DBs */
int supervised; /* 1 if supervised, 0 otherwise. */
int supervised_mode; /* See SUPERVISED_* */
int daemonize; /* True if running as a daemon */
clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT];
/* AOF persistence */
int aof_state; /* AOF_(ON|OFF|WAIT_REWRITE) */
int aof_fsync; /* Kind of fsync() policy */
@@ -1250,6 +1337,7 @@ struct redisServer {
off_t aof_rewrite_min_size; /* the AOF file is at least N bytes. */
off_t aof_rewrite_base_size; /* AOF size on latest startup or rewrite. */
off_t aof_current_size; /* AOF current size. */
off_t aof_fsync_offset; /* AOF offset which is already synced to disk. */
int aof_rewrite_scheduled; /* Rewrite once BGSAVE terminates. */
pid_t aof_child_pid; /* PID if rewriting process */
list *aof_rewrite_buf_blocks; /* Hold changes during an AOF rewrite. */
@@ -1330,7 +1418,7 @@ struct redisServer {
time_t repl_backlog_time_limit; /* Time without slaves after the backlog
gets released. */
time_t repl_no_slaves_since; /* We have no slaves since that time.
Only valid if server.slaves len is 0. */
Only valid if g_pserver->slaves len is 0. */
int repl_min_slaves_to_write; /* Min number of slaves to write. */
int repl_min_slaves_max_lag; /* Max lag of <count> slaves to write. */
int repl_good_slaves_count; /* Number of slaves with lag <= max_lag. */
@@ -1387,10 +1475,10 @@ struct redisServer {
int list_max_ziplist_size;
int list_compress_depth;
/* time cache */
time_t unixtime; /* Unix time sampled every cron cycle. */
time_t timezone; /* Cached timezone. As set by tzset(). */
int daylight_active; /* Currently in daylight saving time. */
long long mstime; /* Like 'unixtime' but with milliseconds resolution. */
std::atomic<time_t> unixtime; /* Unix time sampled every cron cycle. */
time_t timezone; /* Cached timezone. As set by tzset(). */
int daylight_active; /* Currently in daylight saving time. */
long long mstime; /* 'unixtime' with milliseconds resolution. */
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
@@ -1416,8 +1504,7 @@ struct redisServer {
REDISMODULE_CLUSTER_FLAG_*. */
/* Scripting */
lua_State *lua; /* The Lua interpreter. We use just one for all clients */
client *lua_client; /* The "fake client" to query Redis from Lua */
client *lua_caller; /* The client running EVAL right now, or NULL */
client *lua_caller = nullptr; /* The client running EVAL right now, or NULL */
dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
unsigned long long lua_scripts_mem; /* Cached scripts' memory + oh */
mstime_t lua_time_limit; /* Script timeout in milliseconds */
@@ -1448,19 +1535,18 @@ struct redisServer {
int assert_line;
int bug_report_start; /* True if bug report header was already logged. */
int watchdog_period; /* Software watchdog period in ms. 0 = off */
/* System hardware info */
size_t system_memory_size; /* Total memory in system as reported by OS */
/* Mutexes used to protect atomic variables when atomic builtins are
* not available. */
pthread_mutex_t lruclock_mutex;
pthread_mutex_t next_client_id_mutex;
pthread_mutex_t unixtime_mutex;
int fActiveReplica; /* Can this replica also be a master? */
unsigned char uuid[UUID_BINARY_LEN]; /* This server's UUID - populated on boot */
struct fastlock flock;
// Format:
// Lower 20 bits: a counter incrementing for each command executed in the same millisecond
// Upper 44 bits: mstime (least significant 44-bits) enough for ~500 years before rollover from date of addition
uint64_t mvcc_tstamp;
/* System hardware info */
size_t system_memory_size; /* Total memory in system as reported by OS */
};
typedef struct pubsubPattern {
@@ -1511,7 +1597,7 @@ typedef struct _redisSortOperation {
/* Structure to hold list iteration abstraction. */
typedef struct {
robj *subject;
robj_roptr subject;
unsigned char encoding;
unsigned char direction; /* Iteration direction */
quicklistIter *iter;
@@ -1525,7 +1611,7 @@ typedef struct {
/* Structure to hold set iteration abstraction. */
typedef struct {
robj *subject;
robj_roptr subject;
int encoding;
int ii; /* intset iterator */
dictIterator *di;
@@ -1536,7 +1622,7 @@ typedef struct {
* not both are required, store pointers in the iterator to avoid
* unnecessary memory allocation for fields/values. */
typedef struct {
robj *subject;
robj_roptr subject;
int encoding;
unsigned char *fptr, *vptr;
@@ -1554,7 +1640,9 @@ typedef struct {
* Extern declarations
*----------------------------------------------------------------------------*/
extern struct redisServer server;
//extern struct redisServer server;
extern redisServer *g_pserver;
extern struct redisServerConst cserver;
extern __thread struct redisServerThreadVars *serverTL; // thread local server vars
extern struct sharedObjectsStruct shared;
extern dictType objectKeyPointerValueDictType;
@@ -1584,7 +1672,7 @@ moduleType *moduleTypeLookupModuleByID(uint64_t id);
void moduleTypeNameByID(char *name, uint64_t moduleid);
void moduleFreeContext(struct RedisModuleCtx *ctx);
void unblockClientFromModule(client *c);
void moduleHandleBlockedClients(void);
void moduleHandleBlockedClients(int iel);
void moduleBlockedClientTimedOut(client *c);
void moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);
size_t moduleCount(void);
@@ -1592,21 +1680,22 @@ void moduleAcquireGIL(int fServerThread);
void moduleReleaseGIL(int fServerThread);
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
void moduleCallCommandFilters(client *c);
int moduleHasCommandFilters();
/* Utils */
long long ustime(void);
long long mstime(void);
void getRandomHexChars(char *p, size_t len);
void getRandomBytes(unsigned char *p, size_t len);
extern "C" void getRandomHexChars(char *p, size_t len);
extern "C" void getRandomBytes(unsigned char *p, size_t len);
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
void exitFromChild(int retcode);
size_t redisPopcount(void *s, long count);
size_t redisPopcount(const void *s, long count);
void redisSetProcTitle(const char *title);
/* networking.c -- Networking and Client related operations */
client *createClient(int fd, int iel);
void closeTimedoutClients(void);
void freeClient(client *c);
bool freeClient(client *c);
void freeClientAsync(client *c);
void resetClient(client *c);
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
@@ -1622,16 +1711,17 @@ void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);
void addReplyNull(client *c);
void addReplyNull(client *c, robj_roptr objOldProtocol = nullptr);
void addReplyNullArray(client *c);
void addReplyBool(client *c, int b);
void addReplyVerbatim(client *c, const char *s, size_t len, const char *ext);
void addReplyProto(client *c, const char *s, size_t len);
void addReplyBulk(client *c, robj *obj);
void addReplyBulk(client *c, robj_roptr obj);
void AddReplyFromClient(client *c, client *src);
void addReplyBulkCString(client *c, const char *s);
void addReplyBulkCBuffer(client *c, const void *p, size_t len);
void addReplyBulkLongLong(client *c, long long ll);
void addReply(client *c, robj *obj);
void addReply(client *c, robj_roptr obj);
void addReplySds(client *c, sds s);
void addReplyBulkSds(client *c, sds s);
void addReplyError(client *c, const char *err);
@@ -1653,7 +1743,7 @@ void addReplyLoadedModules(client *c);
void copyClientOutputBuffer(client *dst, client *src);
size_t sdsZmallocSize(sds s);
size_t getStringObjectSdsUsedMemory(robj *o);
void freeClientReplyValue(void *o);
void freeClientReplyValue(const void *o);
void *dupClientReplyValue(void *o);
void getClientsMaxBuffers(unsigned long *longest_output_list,
unsigned long *biggest_input_buffer);
@@ -1685,10 +1775,10 @@ void protectClient(client *c);
void unprotectClient(client *c);
// Special Thread-safe addReply() commands for posting messages to clients from a different thread
void addReplyAsync(client *c, robj *obj);
void addReplyAsync(client *c, robj_roptr obj);
void addReplyArrayLenAsync(client *c, long length);
void addReplyProtoAsync(client *c, const char *s, size_t len);
void addReplyBulkAsync(client *c, robj *obj);
void addReplyBulkAsync(client *c, robj_roptr obj);
void addReplyBulkCBufferAsync(client *c, const void *p, size_t len);
void addReplyErrorAsync(client *c, const char *err);
void addReplyMapLenAsync(client *c, long length);
@@ -1717,8 +1807,8 @@ void addReplyStatusFormat(client *c, const char *fmt, ...);
void listTypeTryConversion(robj *subject, robj *value);
void listTypePush(robj *subject, robj *value, int where);
robj *listTypePop(robj *subject, int where);
unsigned long listTypeLength(const robj *subject);
listTypeIterator *listTypeInitIterator(robj *subject, long index, unsigned char direction);
unsigned long listTypeLength(robj_roptr subject);
listTypeIterator *listTypeInitIterator(robj_roptr subject, long index, unsigned char direction);
void listTypeReleaseIterator(listTypeIterator *li);
int listTypeNext(listTypeIterator *li, listTypeEntry *entry);
robj *listTypeGet(listTypeEntry *entry);
@@ -1741,9 +1831,9 @@ void flagTransaction(client *c);
void execCommandPropagateMulti(client *c);
/* Redis object implementation */
void decrRefCount(robj *o);
void decrRefCountVoid(void *o);
void incrRefCount(robj *o);
void decrRefCount(robj_roptr o);
void decrRefCountVoid(const void *o);
void incrRefCount(robj_roptr o);
robj *makeObjectShared(robj *o);
robj *resetRefCount(robj *obj);
void freeStringObject(robj *o);
@@ -1756,11 +1846,12 @@ robj *createStringObject(const char *ptr, size_t len);
robj *createRawStringObject(const char *ptr, size_t len);
robj *createEmbeddedStringObject(const char *ptr, size_t len);
robj *dupStringObject(const robj *o);
int isSdsRepresentableAsLongLong(sds s, long long *llval);
int isSdsRepresentableAsLongLong(const char *s, long long *llval);
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
robj *tryObjectEncoding(robj *o);
robj *getDecodedObject(robj *o);
size_t stringObjectLen(robj *o);
robj_roptr getDecodedObject(robj_roptr o);
size_t stringObjectLen(robj_roptr o);
robj *createStringObjectFromLongLong(long long value);
robj *createStringObjectFromLongLongForValue(long long value);
robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
@@ -1774,7 +1865,7 @@ robj *createZsetZiplistObject(void);
robj *createStreamObject(void);
robj *createModuleObject(moduleType *mt, void *value);
int getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg);
int checkType(client *c, robj *o, int type);
int checkType(client *c, robj_roptr o, int type);
int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg);
int getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg);
int getDoubleFromObject(const robj *o, double *target);
@@ -1824,6 +1915,7 @@ void clearReplicationId2(void);
void chopReplicationBacklog(void);
void replicationCacheMasterUsingMyself(struct redisMaster *mi);
void feedReplicationBacklog(const void *ptr, size_t len);
void updateMasterAuth();
/* Generic persistence functions */
void startLoading(FILE *fp);
@@ -1924,13 +2016,13 @@ void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);
unsigned long zsetLength(const robj *zobj);
unsigned long zsetLength(robj_roptr zobj);
void zsetConvert(robj *zobj, int encoding);
void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);
int zsetScore(robj *zobj, sds member, double *score);
int zsetScore(robj_roptr zobj, sds member, double *score);
unsigned long zslGetRank(zskiplist *zsl, double score, sds o);
int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore);
long zsetRank(robj *zobj, sds ele, int reverse);
long zsetRank(robj_roptr zobj, sds ele, int reverse);
int zsetDel(robj *zobj, sds ele);
void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey, robj *countarg);
sds ziplistGetObject(unsigned char *sptr);
@@ -1995,17 +2087,17 @@ void freeMemoryOverheadData(struct redisMemOverhead *mh);
int restartServer(int flags, mstime_t delay);
/* Set data type */
robj *setTypeCreate(sds value);
int setTypeAdd(robj *subject, sds value);
int setTypeRemove(robj *subject, sds value);
int setTypeIsMember(robj *subject, sds value);
setTypeIterator *setTypeInitIterator(robj *subject);
robj *setTypeCreate(const char *value);
int setTypeAdd(robj *subject, const char *value);
int setTypeRemove(robj *subject, const char *value);
int setTypeIsMember(robj_roptr subject, const char *value);
setTypeIterator *setTypeInitIterator(robj_roptr subject);
void setTypeReleaseIterator(setTypeIterator *si);
int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele);
int setTypeNext(setTypeIterator *si, const char **sdsele, int64_t *llele);
sds setTypeNextObject(setTypeIterator *si);
int setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele);
unsigned long setTypeRandomElements(robj *set, unsigned long count, robj *aux_set);
unsigned long setTypeSize(const robj *subject);
unsigned long setTypeSize(robj_roptr subject);
void setTypeConvert(robj *subject, int enc);
/* Hash data type */
@@ -2015,10 +2107,10 @@ void setTypeConvert(robj *subject, int enc);
void hashTypeConvert(robj *o, int enc);
void hashTypeTryConversion(robj *subject, robj **argv, int start, int end);
int hashTypeExists(robj *o, sds key);
int hashTypeExists(robj_roptr o, const char *key);
int hashTypeDelete(robj *o, sds key);
unsigned long hashTypeLength(const robj *o);
hashTypeIterator *hashTypeInitIterator(robj *subject);
unsigned long hashTypeLength(robj_roptr o);
hashTypeIterator *hashTypeInitIterator(robj_roptr subject);
void hashTypeReleaseIterator(hashTypeIterator *hi);
int hashTypeNext(hashTypeIterator *hi);
void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
@@ -2029,13 +2121,13 @@ sds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what);
void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll);
sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what);
robj *hashTypeLookupWriteOrCreate(client *c, robj *key);
robj *hashTypeGetValueObject(robj *o, sds field);
robj *hashTypeGetValueObject(robj_roptr o, sds field);
int hashTypeSet(robj *o, sds field, sds value, int flags);
/* Pub / Sub */
int pubsubUnsubscribeAllChannels(client *c, int notify);
int pubsubUnsubscribeAllPatterns(client *c, int notify);
void freePubsubPattern(void *p);
void freePubsubPattern(const void *p);
int listMatchPubsubPattern(void *a, void *b);
int pubsubPublishMessage(robj *channel, robj *message);
@@ -2056,20 +2148,20 @@ int rewriteConfig(char *path);
int removeExpire(redisDb *db, robj *key);
void propagateExpire(redisDb *db, robj *key, int lazy);
int expireIfNeeded(redisDb *db, robj *key);
long long getExpire(redisDb *db, robj *key);
long long getExpire(redisDb *db, robj_roptr key);
void setExpire(client *c, redisDb *db, robj *key, long long when);
robj *lookupKey(redisDb *db, robj *key, int flags);
robj *lookupKeyRead(redisDb *db, robj *key);
robj_roptr lookupKeyRead(redisDb *db, robj *key);
robj *lookupKeyWrite(redisDb *db, robj *key);
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply);
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
robj *objectCommandLookup(client *c, robj *key);
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
long long lru_clock);
#define LOOKUP_NONE 0
#define LOOKUP_NOTOUCH (1<<0)
#define LOOKUP_UPDATEMVCC (1<<1)
void dbAdd(redisDb *db, robj *key, robj *val);
void dbOverwrite(redisDb *db, robj *key, robj *val);
int dbMerge(redisDb *db, robj *key, robj *val, int fReplace);
@@ -2091,7 +2183,7 @@ unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int coun
unsigned int countKeysInSlot(unsigned int hashslot);
unsigned int delKeysInSlot(unsigned int hashslot);
int verifyClusterConfigWithData(void);
void scanGenericCommand(client *c, robj *o, unsigned long cursor);
void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor);
int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);
void slotToKeyAdd(robj *key);
void slotToKeyDel(robj *key);
@@ -2114,7 +2206,7 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
/* Cluster */
void clusterInit(void);
unsigned short crc16(const char *buf, int len);
extern "C" unsigned short crc16(const char *buf, int len);
unsigned int keyHashSlot(char *key, int keylen);
void clusterCron(void);
void clusterPropagatePublish(robj *channel, robj *message);
@@ -2173,9 +2265,9 @@ int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2);
void dictSdsDestructor(void *privdata, void *val);
/* Git SHA1 */
char *redisGitSHA1(void);
char *redisGitDirty(void);
uint64_t redisBuildId(void);
extern "C" char *redisGitSHA1(void);
extern "C" char *redisGitDirty(void);
extern "C" uint64_t redisBuildId(void);
/* Commands prototypes */
void authCommand(client *c);
@@ -2379,53 +2471,60 @@ int FBrokenLinkToMaster();
int FActiveMaster(client *c);
struct redisMaster *MasterInfoFromClient(client *c);
#if defined(__GNUC__)
#ifndef __cplusplus
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
void free(void *ptr) __attribute__ ((deprecated));
void *malloc(size_t size) __attribute__ ((deprecated));
void *realloc(void *ptr, size_t size) __attribute__ ((deprecated));
#endif
/* MVCC */
uint64_t getMvccTstamp();
void incrementMvccTstamp();
#if __GNUC__ >= 7 && !defined(NO_DEPRECATE_FREE)
[[deprecated]]
void *calloc(size_t count, size_t size);
[[deprecated]]
void free(void *ptr);
[[deprecated]]
void *malloc(size_t size);
[[deprecated]]
void *realloc(void *ptr, size_t size);
#endif
/* Debugging stuff */
void _serverAssertWithInfo(const client *c, const robj *o, const char *estr, const char *file, int line);
void _serverAssert(const char *estr, const char *file, int line);
void _serverPanic(const char *file, int line, const char *msg, ...);
void _serverAssertWithInfo(const client *c, robj_roptr o, const char *estr, const char *file, int line);
extern "C" void _serverAssert(const char *estr, const char *file, int line);
extern "C" void _serverPanic(const char *file, int line, const char *msg, ...);
void bugReportStart(void);
void serverLogObjectDebugInfo(const robj *o);
void serverLogObjectDebugInfo(robj_roptr o);
void sigsegvHandler(int sig, siginfo_t *info, void *secret);
sds genRedisInfoString(const char *section);
void enableWatchdog(int period);
void disableWatchdog(void);
void watchdogScheduleSignal(int period);
void serverLogHexDump(int level, const char *descr, void *value, size_t len);
int memtest_preserving_test(unsigned long *m, size_t bytes, int passes);
void mixDigest(unsigned char *digest, void *ptr, size_t len);
extern "C" int memtest_preserving_test(unsigned long *m, size_t bytes, int passes);
void mixDigest(unsigned char *digest, const void *ptr, size_t len);
void xorDigest(unsigned char *digest, const void *ptr, size_t len);
int populateCommandTableParseFlags(struct redisCommand *c, const char *strflags);
int moduleGILAcquiredByModule(void);
extern bool g_fInCrash;
static inline int GlobalLocksAcquired(void) // Used in asserts to verify all global locks are correctly acquired for a server-thread to operate
{
return aeThreadOwnsLock() || moduleGILAcquiredByModule();
return aeThreadOwnsLock() || moduleGILAcquiredByModule() || g_fInCrash;
}
inline int ielFromEventLoop(const aeEventLoop *eventLoop)
{
int iel = 0;
for (; iel < server.cthreads; ++iel)
for (; iel < cserver.cthreads; ++iel)
{
if (server.rgthreadvar[iel].el == eventLoop)
if (g_pserver->rgthreadvar[iel].el == eventLoop)
break;
}
serverAssert(iel < server.cthreads);
serverAssert(iel < cserver.cthreads);
return iel;
}
inline int FCorrectThread(client *c)
{
return (serverTL != NULL && (server.rgthreadvar[c->iel].el == serverTL->el))
return (serverTL != NULL && (g_pserver->rgthreadvar[c->iel].el == serverTL->el))
|| (c->iel == IDX_EVENT_LOOP_MAIN && moduleGILAcquiredByModule())
|| (c->fd == -1);
}
@@ -2436,8 +2535,4 @@ inline int FCorrectThread(client *c)
#define redisDebugMark() \
printf("-- MARK %s:%d --\n", __FILE__, __LINE__)
#ifdef __cplusplus
}
#endif
#endif
+14 -14
View File
@@ -87,7 +87,7 @@ slowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long dur
}
se->time = time(NULL);
se->duration = duration;
se->id = server.slowlog_entry_id++;
se->id = g_pserver->slowlog_entry_id++;
se->peerid = sdsnew(getClientPeerId(c));
se->cname = c->name ? sdsnew(szFromObj(c->name)) : sdsempty();
return se;
@@ -97,7 +97,7 @@ slowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long dur
* function matches the one of the 'free' method of adlist.c.
*
* This function will take care to release all the retained object. */
void slowlogFreeEntry(void *septr) {
void slowlogFreeEntry(const void *septr) {
slowlogEntry *se = (slowlogEntry*)septr;
int j;
@@ -112,29 +112,29 @@ void slowlogFreeEntry(void *septr) {
/* Initialize the slow log. This function should be called a single time
* at server startup. */
void slowlogInit(void) {
server.slowlog = listCreate();
server.slowlog_entry_id = 0;
listSetFreeMethod(server.slowlog,slowlogFreeEntry);
g_pserver->slowlog = listCreate();
g_pserver->slowlog_entry_id = 0;
listSetFreeMethod(g_pserver->slowlog,slowlogFreeEntry);
}
/* Push a new entry into the slow log.
* This function will make sure to trim the slow log accordingly to the
* configured max length. */
void slowlogPushEntryIfNeeded(client *c, robj **argv, int argc, long long duration) {
if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
if (duration >= server.slowlog_log_slower_than)
listAddNodeHead(server.slowlog,
if (g_pserver->slowlog_log_slower_than < 0) return; /* Slowlog disabled */
if (duration >= g_pserver->slowlog_log_slower_than)
listAddNodeHead(g_pserver->slowlog,
slowlogCreateEntry(c,argv,argc,duration));
/* Remove old entries if needed. */
while (listLength(server.slowlog) > server.slowlog_max_len)
listDelNode(server.slowlog,listLast(server.slowlog));
while (listLength(g_pserver->slowlog) > g_pserver->slowlog_max_len)
listDelNode(g_pserver->slowlog,listLast(g_pserver->slowlog));
}
/* Remove all the entries from the current slow log. */
void slowlogReset(void) {
while (listLength(server.slowlog) > 0)
listDelNode(server.slowlog,listLast(server.slowlog));
while (listLength(g_pserver->slowlog) > 0)
listDelNode(g_pserver->slowlog,listLast(g_pserver->slowlog));
}
/* The SLOWLOG command. Implements all the subcommands needed to handle the
@@ -154,7 +154,7 @@ NULL
slowlogReset();
addReply(c,shared.ok);
} else if (c->argc == 2 && !strcasecmp(szFromObj(c->argv[1]),"len")) {
addReplyLongLong(c,listLength(server.slowlog));
addReplyLongLong(c,listLength(g_pserver->slowlog));
} else if ((c->argc == 2 || c->argc == 3) &&
!strcasecmp(szFromObj(c->argv[1]),"get"))
{
@@ -168,7 +168,7 @@ NULL
getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != C_OK)
return;
listRewind(server.slowlog,&li);
listRewind(g_pserver->slowlog,&li);
totentries = addReplyDeferredLen(c);
while(count-- && (ln = listNext(&li))) {
int j;
+19 -18
View File
@@ -61,7 +61,8 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag) {
char *p, *f, *k;
sds spat, ssub;
robj *keyobj, *fieldobj = NULL, *o;
robj *keyobj, *fieldobj = NULL;
robj_roptr o;
int prefixlen, sublen, postfixlen, fieldlen;
/* If the pattern is "#" return the substitution object itself in order
@@ -110,7 +111,7 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag)
o = lookupKeyRead(db,keyobj);
else
o = lookupKeyWrite(db,keyobj);
if (o == NULL) goto noobj;
if (o == nullptr) goto noobj;
if (fieldobj) {
if (o->type != OBJ_HASH) goto noobj;
@@ -127,7 +128,7 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag)
}
decrRefCount(keyobj);
if (fieldobj) decrRefCount(fieldobj);
return o;
return o.unsafe_robjcast();
noobj:
decrRefCount(keyobj);
@@ -142,7 +143,7 @@ int sortCompare(const void *s1, const void *s2) {
const redisSortObject *so1 = (redisSortObject*)s1, *so2 = (redisSortObject*)s2;
int cmp;
if (!server.sort_alpha) {
if (!g_pserver->sort_alpha) {
/* Numeric sorting. Here it's trivial as we precomputed scores */
if (so1->u.score > so2->u.score) {
cmp = 1;
@@ -156,7 +157,7 @@ int sortCompare(const void *s1, const void *s2) {
}
} else {
/* Alphanumeric sorting */
if (server.sort_bypattern) {
if (g_pserver->sort_bypattern) {
if (!so1->u.cmpobj || !so2->u.cmpobj) {
/* At least one compare object is NULL */
if (so1->u.cmpobj == so2->u.cmpobj)
@@ -167,7 +168,7 @@ int sortCompare(const void *s1, const void *s2) {
cmp = 1;
} else {
/* We have both the objects, compare them. */
if (server.sort_store) {
if (g_pserver->sort_store) {
cmp = compareStringObjects(so1->u.cmpobj,so2->u.cmpobj);
} else {
/* Here we can use strcoll() directly as we are sure that
@@ -177,14 +178,14 @@ int sortCompare(const void *s1, const void *s2) {
}
} else {
/* Compare elements directly. */
if (server.sort_store) {
if (g_pserver->sort_store) {
cmp = compareStringObjects(so1->obj,so2->obj);
} else {
cmp = collateStringObjects(so1->obj,so2->obj);
}
}
}
return server.sort_desc ? -cmp : cmp;
return g_pserver->sort_desc ? -cmp : cmp;
}
/* The SORT command is the most complex command in Redis. Warning: this code
@@ -238,7 +239,7 @@ void sortCommand(client *c) {
} else {
/* If BY is specified with a real patter, we can't accept
* it in cluster mode. */
if (server.cluster_enabled) {
if (g_pserver->cluster_enabled) {
addReplyError(c,"BY option of SORT denied in Cluster mode.");
syntax_error++;
break;
@@ -246,7 +247,7 @@ void sortCommand(client *c) {
}
j++;
} else if (!strcasecmp(szFromObj(c->argv[j]),"get") && leftargs >= 1) {
if (server.cluster_enabled) {
if (g_pserver->cluster_enabled) {
addReplyError(c,"GET option of SORT denied in Cluster mode.");
syntax_error++;
break;
@@ -271,7 +272,7 @@ void sortCommand(client *c) {
/* Lookup the key to sort. It must be of the right types */
if (storekey)
sortval = lookupKeyRead(c->db,c->argv[1]);
sortval = lookupKeyRead(c->db,c->argv[1]).unsafe_robjcast();
else
sortval = lookupKeyWrite(c->db,c->argv[1]);
if (sortval && sortval->type != OBJ_SET &&
@@ -474,7 +475,7 @@ void sortCommand(client *c) {
vector[j].u.score = strtod(szFromObj(byval),&eptr);
if (eptr[0] != '\0' || errno == ERANGE ||
isnan(vector[j].u.score))
std::isnan(vector[j].u.score))
{
int_conversion_error = 1;
}
@@ -495,10 +496,10 @@ void sortCommand(client *c) {
}
}
server.sort_desc = desc;
server.sort_alpha = alpha;
server.sort_bypattern = sortby ? 1 : 0;
server.sort_store = storekey ? 1 : 0;
g_pserver->sort_desc = desc;
g_pserver->sort_alpha = alpha;
g_pserver->sort_bypattern = sortby ? 1 : 0;
g_pserver->sort_store = storekey ? 1 : 0;
if (sortby && (start != 0 || end != vectorlen-1))
pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
else
@@ -573,11 +574,11 @@ void sortCommand(client *c) {
setKey(c->db,storekey,sobj);
notifyKeyspaceEvent(NOTIFY_LIST,"sortstore",storekey,
c->db->id);
server.dirty += outputlen;
g_pserver->dirty += outputlen;
} else if (dbDelete(c->db,storekey)) {
signalModifiedKey(c->db,storekey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",storekey,c->db->id);
server.dirty++;
g_pserver->dirty++;
}
decrRefCount(sobj);
addReplyLongLong(c,outputlen);
+45 -45
View File
@@ -44,7 +44,7 @@ void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
for (i = start; i <= end; i++) {
if (sdsEncodedObject(argv[i]) &&
sdslen(szFromObj(argv[i])) > server.hash_max_ziplist_value)
sdslen(szFromObj(argv[i])) > g_pserver->hash_max_ziplist_value)
{
hashTypeConvert(o, OBJ_ENCODING_HT);
break;
@@ -54,8 +54,8 @@ void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
/* Get the value from a ziplist encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
int hashTypeGetFromZiplist(robj *o, sds field,
unsigned char **vstr,
int hashTypeGetFromZiplist(robj_roptr o, const char *field,
const unsigned char **vstr,
unsigned int *vlen,
long long *vll)
{
@@ -64,7 +64,7 @@ int hashTypeGetFromZiplist(robj *o, sds field,
serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
zl = (unsigned char*)ptrFromObj(o);
zl = (unsigned char*)(ptrFromObj(o));
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
@@ -76,7 +76,7 @@ int hashTypeGetFromZiplist(robj *o, sds field,
}
if (vptr != NULL) {
ret = ziplistGet(vptr, vstr, vlen, vll);
ret = ziplistGet(vptr, (unsigned char**)vstr, vlen, vll);
serverAssert(ret);
return 0;
}
@@ -87,7 +87,7 @@ int hashTypeGetFromZiplist(robj *o, sds field,
/* Get the value from a hash table encoded hash, identified by field.
* Returns NULL when the field cannot be found, otherwise the SDS value
* is returned. */
sds hashTypeGetFromHashTable(robj *o, sds field) {
const char *hashTypeGetFromHashTable(robj_roptr o, const char *field) {
dictEntry *de;
serverAssert(o->encoding == OBJ_ENCODING_HT);
@@ -106,15 +106,15 @@ sds hashTypeGetFromHashTable(robj *o, sds field) {
* If *vll is populated *vstr is set to NULL, so the caller
* can always check the function return by checking the return value
* for C_OK and checking if vll (or vstr) is NULL. */
int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
int hashTypeGetValue(robj_roptr o, sds field, const unsigned char **vstr, unsigned int *vlen, long long *vll) {
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
*vstr = NULL;
if (hashTypeGetFromZiplist(o, field, vstr, vlen, vll) == 0)
return C_OK;
} else if (o->encoding == OBJ_ENCODING_HT) {
sds value;
const char *value;
if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
*vstr = (unsigned char*) value;
*vstr = (const unsigned char*) value;
*vlen = sdslen(value);
return C_OK;
}
@@ -128,8 +128,8 @@ int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vle
* interaction with the hash type outside t_hash.c.
* The function returns NULL if the field is not found in the hash. Otherwise
* a newly allocated string object with the value is returned. */
robj *hashTypeGetValueObject(robj *o, sds field) {
unsigned char *vstr;
robj *hashTypeGetValueObject(robj_roptr o, sds field) {
const unsigned char *vstr;
unsigned int vlen;
long long vll;
@@ -141,17 +141,17 @@ robj *hashTypeGetValueObject(robj *o, sds field) {
/* Higher level function using hashTypeGet*() to return the length of the
* object associated with the requested field, or 0 if the field does not
* exist. */
size_t hashTypeGetValueLength(robj *o, sds field) {
size_t hashTypeGetValueLength(robj_roptr o, const char *field) {
size_t len = 0;
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
const unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)
len = vstr ? vlen : sdigits10(vll);
} else if (o->encoding == OBJ_ENCODING_HT) {
sds aux;
const char *aux;
if ((aux = hashTypeGetFromHashTable(o, field)) != NULL)
len = sdslen(aux);
@@ -163,9 +163,9 @@ size_t hashTypeGetValueLength(robj *o, sds field) {
/* Test if the specified field exists in the given hash. Returns 1 if the field
* exists, and 0 when it doesn't. */
int hashTypeExists(robj *o, sds field) {
int hashTypeExists(robj_roptr o, const char *field) {
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
const unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
@@ -234,7 +234,7 @@ int hashTypeSet(robj *o, sds field, sds value, int flags) {
o->m_ptr = zl;
/* Check if the ziplist needs to be converted to a hash table */
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
if (hashTypeLength(o) > g_pserver->hash_max_ziplist_entries)
hashTypeConvert(o, OBJ_ENCODING_HT);
} else if (o->encoding == OBJ_ENCODING_HT) {
dictEntry *de = dictFind((dict*)ptrFromObj(o),field);
@@ -308,7 +308,7 @@ int hashTypeDelete(robj *o, sds field) {
}
/* Return the number of elements in a hash. */
unsigned long hashTypeLength(const robj *o) {
unsigned long hashTypeLength(robj_roptr o) {
unsigned long length = ULONG_MAX;
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
@@ -321,7 +321,7 @@ unsigned long hashTypeLength(const robj *o) {
return length;
}
hashTypeIterator *hashTypeInitIterator(robj *subject) {
hashTypeIterator *hashTypeInitIterator(robj_roptr subject) {
hashTypeIterator *hi = (hashTypeIterator*)zmalloc(sizeof(hashTypeIterator), MALLOC_LOCAL);
hi->subject = subject;
hi->encoding = subject->encoding;
@@ -523,7 +523,7 @@ void hsetnxCommand(client *c) {
addReply(c, shared.cone);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
}
@@ -553,14 +553,14 @@ void hsetCommand(client *c) {
}
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
void hincrbyCommand(client *c) {
long long value, incr, oldvalue;
robj *o;
sds newstr;
unsigned char *vstr;
const unsigned char *vstr;
unsigned int vlen;
if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
@@ -588,7 +588,7 @@ void hincrbyCommand(client *c) {
addReplyLongLong(c,value);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
void hincrbyfloatCommand(client *c) {
@@ -596,7 +596,7 @@ void hincrbyfloatCommand(client *c) {
long long ll;
robj *o;
sds newstr;
unsigned char *vstr;
const unsigned char *vstr;
unsigned int vlen;
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
@@ -615,7 +615,7 @@ void hincrbyfloatCommand(client *c) {
}
value += incr;
if (isnan(value) || isinf(value)) {
if (std::isnan(value) || std::isinf(value)) {
addReplyError(c,"increment would produce NaN or Infinity");
return;
}
@@ -627,7 +627,7 @@ void hincrbyfloatCommand(client *c) {
addReplyBulkCBuffer(c,buf,len);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
/* Always replicate HINCRBYFLOAT as an HSET command with the final value
* in order to make sure that differences in float pricision or formatting
@@ -641,16 +641,16 @@ void hincrbyfloatCommand(client *c) {
decrRefCount(newobj);
}
static void addHashFieldToReply(client *c, robj *o, sds field) {
static void addHashFieldToReply(client *c, robj_roptr o, sds field) {
int ret;
if (o == NULL) {
if (o == nullptr) {
addReplyNull(c);
return;
}
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
const unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
@@ -666,7 +666,7 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
}
} else if (o->encoding == OBJ_ENCODING_HT) {
sds value = hashTypeGetFromHashTable(o, field);
const char* value = hashTypeGetFromHashTable(o, field);
if (value == NULL)
addReplyNull(c);
else
@@ -677,22 +677,22 @@ static void addHashFieldToReply(client *c, robj *o, sds field) {
}
void hgetCommand(client *c) {
robj *o;
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == nullptr ||
checkType(c,o,OBJ_HASH)) return;
addHashFieldToReply(c, o, szFromObj(c->argv[2]));
}
void hmgetCommand(client *c) {
robj *o;
robj_roptr o;
int i;
/* Don't abort when the key cannot be found. Non-existing keys are empty
* hashes, where HMGET should respond with a series of null bulks. */
o = lookupKeyRead(c->db, c->argv[1]);
if (o != NULL && o->type != OBJ_HASH) {
if (o != nullptr && o->type != OBJ_HASH) {
addReply(c, shared.wrongtypeerr);
return;
}
@@ -726,24 +726,24 @@ void hdelCommand(client *c) {
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
c->db->id);
server.dirty += deleted;
g_pserver->dirty += deleted;
}
addReplyLongLong(c,deleted);
}
void hlenCommand(client *c) {
robj *o;
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_HASH)) return;
addReplyLongLong(c,hashTypeLength(o));
}
void hstrlenCommand(client *c) {
robj *o;
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_HASH)) return;
addReplyLongLong(c,hashTypeGetValueLength(o,szFromObj(c->argv[2])));
}
@@ -768,11 +768,11 @@ static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int wh
}
void genericHgetallCommand(client *c, int flags) {
robj *o;
robj_roptr o;
hashTypeIterator *hi;
int length, count = 0;
if ((o = lookupKeyReadOrReply(c,c->argv[1],(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == NULL
if ((o = lookupKeyReadOrReply(c,c->argv[1],(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == nullptr
|| checkType(c,o,OBJ_HASH)) return;
/* We return a map if the user requested keys and values, like in the
@@ -816,19 +816,19 @@ void hgetallCommand(client *c) {
}
void hexistsCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_HASH)) return;
addReply(c, hashTypeExists(o,szFromObj(c->argv[2])) ? shared.cone : shared.czero);
}
void hscanCommand(client *c) {
robj *o;
robj_roptr o;
unsigned long cursor;
if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == nullptr ||
checkType(c,o,OBJ_HASH)) return;
scanGenericCommand(c,o,cursor);
}
+30 -30
View File
@@ -71,16 +71,16 @@ robj *listTypePop(robj *subject, int where) {
return value;
}
unsigned long listTypeLength(const robj *subject) {
unsigned long listTypeLength(robj_roptr subject) {
if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
return quicklistCount((quicklist*)ptrFromObj(subject));
return quicklistCount((const quicklist*)ptrFromObj(subject));
} else {
serverPanic("Unknown list encoding");
}
}
/* Initialize an iterator at the specified index. */
listTypeIterator *listTypeInitIterator(robj *subject, long index,
listTypeIterator *listTypeInitIterator(robj_roptr subject, long index,
unsigned char direction) {
listTypeIterator *li = (listTypeIterator*)zmalloc(sizeof(listTypeIterator), MALLOC_LOCAL);
li->subject = subject;
@@ -181,8 +181,8 @@ void listTypeConvert(robj *subject, int enc) {
serverAssertWithInfo(NULL,subject,subject->encoding==OBJ_ENCODING_ZIPLIST);
if (enc == OBJ_ENCODING_QUICKLIST) {
size_t zlen = server.list_max_ziplist_size;
int depth = server.list_compress_depth;
size_t zlen = g_pserver->list_max_ziplist_size;
int depth = g_pserver->list_compress_depth;
subject->m_ptr = quicklistCreateFromZiplist(zlen, depth, (unsigned char*)ptrFromObj(subject));
subject->encoding = OBJ_ENCODING_QUICKLIST;
} else {
@@ -206,8 +206,8 @@ void pushGenericCommand(client *c, int where) {
for (j = 2; j < c->argc; j++) {
if (!lobj) {
lobj = createQuicklistObject();
quicklistSetOptions((quicklist*)ptrFromObj(lobj), server.list_max_ziplist_size,
server.list_compress_depth);
quicklistSetOptions((quicklist*)ptrFromObj(lobj), g_pserver->list_max_ziplist_size,
g_pserver->list_compress_depth);
dbAdd(c->db,c->argv[1],lobj);
}
listTypePush(lobj,c->argv[j],where);
@@ -220,7 +220,7 @@ void pushGenericCommand(client *c, int where) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
}
server.dirty += pushed;
g_pserver->dirty += pushed;
}
void lpushCommand(client *c) {
@@ -250,7 +250,7 @@ void pushxGenericCommand(client *c, int where) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
}
server.dirty += pushed;
g_pserver->dirty += pushed;
}
void lpushxCommand(client *c) {
@@ -295,7 +295,7 @@ void linsertCommand(client *c) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"linsert",
c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
} else {
/* Notify client of a failed insert */
addReplyLongLong(c,-1);
@@ -306,14 +306,14 @@ void linsertCommand(client *c) {
}
void llenCommand(client *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,OBJ_LIST)) return;
robj_roptr o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == nullptr || checkType(c,o,OBJ_LIST)) return;
addReplyLongLong(c,listTypeLength(o));
}
void lindexCommand(client *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]);
if (o == NULL || checkType(c,o,OBJ_LIST)) return;
robj_roptr o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]);
if (o == nullptr || checkType(c,o,OBJ_LIST)) return;
long index;
robj *value = NULL;
@@ -331,7 +331,7 @@ void lindexCommand(client *c) {
addReplyBulk(c,value);
decrRefCount(value);
} else {
addReplyNull(c);
addReplyNull(c,shared.nullbulk);
}
} else {
serverPanic("Unknown list encoding");
@@ -357,7 +357,7 @@ void lsetCommand(client *c) {
addReply(c,shared.ok);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
} else {
serverPanic("Unknown list encoding");
@@ -383,7 +383,7 @@ void popGenericCommand(client *c, int where) {
dbDelete(c->db,c->argv[1]);
}
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
}
}
@@ -396,13 +396,13 @@ void rpopCommand(client *c) {
}
void lrangeCommand(client *c) {
robj *o;
robj_roptr o;
long start, end, llen, rangelen;
if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
(getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == NULL
if ((o = lookupKeyReadOrReply(c,c->argv[1],(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == nullptr
|| checkType(c,o,OBJ_LIST)) return;
llen = listTypeLength(o);
@@ -414,7 +414,7 @@ void lrangeCommand(client *c) {
/* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) {
addReplyNull(c);
addReplyNull(c,shared.emptymultibulk);
return;
}
if (end >= llen) end = llen-1;
@@ -483,7 +483,7 @@ void ltrimCommand(client *c) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
addReply(c,shared.ok);
}
@@ -511,7 +511,7 @@ void lremCommand(client *c) {
while (listTypeNext(li,&entry)) {
if (listTypeEqual(&entry,obj)) {
listTypeDelete(li, &entry);
server.dirty++;
g_pserver->dirty++;
removed++;
if (toremove && removed == toremove) break;
}
@@ -551,8 +551,8 @@ static void rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *val
/* Create the list if the key does not exist */
if (!dstobj) {
dstobj = createQuicklistObject();
quicklistSetOptions((quicklist*)ptrFromObj(dstobj), server.list_max_ziplist_size,
server.list_compress_depth);
quicklistSetOptions((quicklist*)ptrFromObj(dstobj), g_pserver->list_max_ziplist_size,
g_pserver->list_compress_depth);
dbAdd(c->db,dstkey,dstobj);
}
signalModifiedKey(c->db,dstkey);
@@ -595,7 +595,7 @@ void rpoplpushCommand(client *c) {
}
signalModifiedKey(c->db,touchedkey);
decrRefCount(touchedkey);
server.dirty++;
g_pserver->dirty++;
if (c->cmd->proc == brpoplpushCommand) {
rewriteClientCommandVector(c,3,shared.rpoplpush,c->argv[1],c->argv[2]);
}
@@ -617,7 +617,7 @@ void rpoplpushCommand(client *c) {
* the AOF and replication channel.
*
* The argument 'where' is LIST_TAIL or LIST_HEAD, and indicates if the
* 'value' element was popped fron the head (BLPOP) or tail (BRPOP) so that
* 'value' element was popped from the head (BLPOP) or tail (BRPOP) so that
* we can propagate the command properly.
*
* The function returns C_OK if we are able to serve the client, otherwise
@@ -636,7 +636,7 @@ int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb
shared.rpop;
argv[1] = key;
propagate((where == LIST_HEAD) ?
server.lpopCommand : server.rpopCommand,
cserver.lpopCommand : cserver.rpopCommand,
db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
/* BRPOP/BLPOP */
@@ -659,7 +659,7 @@ int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb
/* Propagate the RPOP operation. */
argv[0] = shared.rpop;
argv[1] = key;
propagate(server.rpopCommand,
propagate(cserver.rpopCommand,
db->id,argv,2,
PROPAGATE_AOF|
PROPAGATE_REPL);
@@ -669,7 +669,7 @@ int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb
argv[0] = shared.lpush;
argv[1] = dstkey;
argv[2] = value;
propagate(server.lpushCommand,
propagate(cserver.lpushCommand,
db->id,argv,3,
PROPAGATE_AOF|
PROPAGATE_REPL);
@@ -721,7 +721,7 @@ void blockingPopGenericCommand(client *c, int where) {
c->argv[j],c->db->id);
}
signalModifiedKey(c->db,c->argv[j]);
server.dirty++;
g_pserver->dirty++;
/* Replicate it as an [LR]POP instead of B[LR]POP. */
rewriteClientCommandVector(c,2,
+48 -43
View File
@@ -39,7 +39,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
/* Factory method to return a set that *can* hold "value". When the object has
* an integer-encodable value, an intset will be returned. Otherwise a regular
* hash table. */
robj *setTypeCreate(sds value) {
robj *setTypeCreate(const char *value) {
if (isSdsRepresentableAsLongLong(value,NULL) == C_OK)
return createIntsetObject();
return createSetObject();
@@ -49,11 +49,11 @@ robj *setTypeCreate(sds value) {
*
* If the value was already member of the set, nothing is done and 0 is
* returned, otherwise the new element is added and 1 is returned. */
int setTypeAdd(robj *subject, sds value) {
int setTypeAdd(robj *subject, const char *value) {
long long llval;
if (subject->encoding == OBJ_ENCODING_HT) {
dict *ht = (dict*)subject->m_ptr;
dictEntry *de = dictAddRaw(ht,value,NULL);
dictEntry *de = dictAddRaw(ht,(char*)value,NULL);
if (de) {
dictSetKey(ht,de,sdsdup(value));
dictSetVal(ht,de,NULL);
@@ -66,7 +66,7 @@ int setTypeAdd(robj *subject, sds value) {
if (success) {
/* Convert to regular set when the intset contains
* too many entries. */
if (intsetLen((intset*)subject->m_ptr) > server.set_max_intset_entries)
if (intsetLen((intset*)subject->m_ptr) > g_pserver->set_max_intset_entries)
setTypeConvert(subject,OBJ_ENCODING_HT);
return 1;
}
@@ -85,7 +85,7 @@ int setTypeAdd(robj *subject, sds value) {
return 0;
}
int setTypeRemove(robj *setobj, sds value) {
int setTypeRemove(robj *setobj, const char *value) {
long long llval;
if (setobj->encoding == OBJ_ENCODING_HT) {
if (dictDelete((dict*)setobj->m_ptr,value) == DICT_OK) {
@@ -104,7 +104,7 @@ int setTypeRemove(robj *setobj, sds value) {
return 0;
}
int setTypeIsMember(robj *subject, sds value) {
int setTypeIsMember(robj_roptr subject, const char *value) {
long long llval;
if (subject->encoding == OBJ_ENCODING_HT) {
return dictFind((dict*)subject->m_ptr,value) != NULL;
@@ -118,7 +118,7 @@ int setTypeIsMember(robj *subject, sds value) {
return 0;
}
setTypeIterator *setTypeInitIterator(robj *subject) {
setTypeIterator *setTypeInitIterator(robj_roptr subject) {
setTypeIterator *si = (setTypeIterator*)zmalloc(sizeof(setTypeIterator), MALLOC_LOCAL);
si->subject = subject;
si->encoding = subject->encoding;
@@ -151,7 +151,7 @@ void setTypeReleaseIterator(setTypeIterator *si) {
* used field with values which are easy to trap if misused.
*
* When there are no longer elements -1 is returned. */
int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele) {
int setTypeNext(setTypeIterator *si, const char **sdsele, int64_t *llele) {
if (si->encoding == OBJ_ENCODING_HT) {
dictEntry *de = dictNext(si->di);
if (de == NULL) return -1;
@@ -176,7 +176,7 @@ int setTypeNext(setTypeIterator *si, sds *sdsele, int64_t *llele) {
* an issue. */
sds setTypeNextObject(setTypeIterator *si) {
int64_t intele;
sds sdsele;
const char *sdsele;
int encoding;
encoding = setTypeNext(si,&sdsele,&intele);
@@ -219,7 +219,12 @@ int setTypeRandomElement(robj *setobj, sds *sdsele, int64_t *llele) {
return setobj->encoding;
}
unsigned long setTypeSize(const robj *subject) {
int setTypeRandomElement(robj_roptr setobj, const char **sdsele, const int64_t *llele)
{
return setTypeRandomElement(setobj.unsafe_robjcast(), (sds*)sdsele, const_cast<int64_t*>(llele));
}
unsigned long setTypeSize(robj_roptr subject) {
if (subject->encoding == OBJ_ENCODING_HT) {
return dictSize((const dict*)subject->m_ptr);
} else if (subject->encoding == OBJ_ENCODING_INTSET) {
@@ -240,7 +245,7 @@ void setTypeConvert(robj *setobj, int enc) {
if (enc == OBJ_ENCODING_HT) {
int64_t intele;
dict *d = dictCreate(&setDictType,NULL);
sds element;
const char *element;
/* Presize the dict to avoid rehashing */
dictExpand(d,intsetLen((intset*)setobj->m_ptr));
@@ -248,8 +253,8 @@ void setTypeConvert(robj *setobj, int enc) {
/* To add the elements we extract integers and create redis objects */
si = setTypeInitIterator(setobj);
while (setTypeNext(si,&element,&intele) != -1) {
element = sdsfromlonglong(intele);
serverAssert(dictAdd(d,element,NULL) == DICT_OK);
sds elementNew = sdsfromlonglong(intele);
serverAssert(dictAdd(d,elementNew,NULL) == DICT_OK);
}
setTypeReleaseIterator(si);
@@ -283,7 +288,7 @@ void saddCommand(client *c) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
server.dirty += added;
g_pserver->dirty += added;
addReplyLongLong(c,added);
}
@@ -310,7 +315,7 @@ void sremCommand(client *c) {
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
c->db->id);
server.dirty += deleted;
g_pserver->dirty += deleted;
}
addReplyLongLong(c,deleted);
}
@@ -360,20 +365,20 @@ void smoveCommand(client *c) {
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
server.dirty++;
g_pserver->dirty++;
/* An extra key has changed when ele was successfully added to dstset */
if (setTypeAdd(dstset,szFromObj(ele))) {
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[2],c->db->id);
}
addReply(c,shared.cone);
}
void sismemberCommand(client *c) {
robj *set;
robj_roptr set;
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,set,OBJ_SET)) return;
if (setTypeIsMember(set,szFromObj(c->argv[2])))
@@ -383,9 +388,9 @@ void sismemberCommand(client *c) {
}
void scardCommand(client *c) {
robj *o;
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_SET)) return;
addReplyLongLong(c,setTypeSize(o));
@@ -429,7 +434,7 @@ void spopWithCountCommand(client *c) {
/* Generate an SPOP keyspace notification */
notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id);
server.dirty += count;
g_pserver->dirty += count;
/* CASE 1:
* The number of requested elements is greater than or equal to
@@ -445,7 +450,7 @@ void spopWithCountCommand(client *c) {
/* Propagate this command as an DEL operation */
rewriteClientCommandVector(c,2,shared.del,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
return;
}
@@ -458,7 +463,7 @@ void spopWithCountCommand(client *c) {
addReplySetLen(c,count);
/* Common iteration vars. */
sds sdsele;
const char *sdsele;
robj *objele;
int encoding;
int64_t llele;
@@ -487,7 +492,7 @@ void spopWithCountCommand(client *c) {
/* Replicate/AOF this command as an SREM operation */
propargv[2] = objele;
alsoPropagate(server.sremCommand,c->db->id,propargv,3,
alsoPropagate(cserver.sremCommand,c->db->id,propargv,3,
PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(objele);
}
@@ -530,7 +535,7 @@ void spopWithCountCommand(client *c) {
/* Replicate/AOF this command as an SREM operation */
propargv[2] = objele;
alsoPropagate(server.sremCommand,c->db->id,propargv,3,
alsoPropagate(cserver.sremCommand,c->db->id,propargv,3,
PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(objele);
}
@@ -547,7 +552,7 @@ void spopWithCountCommand(client *c) {
decrRefCount(propargv[0]);
preventCommandPropagation(c);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
}
void spopCommand(client *c) {
@@ -600,7 +605,7 @@ void spopCommand(client *c) {
/* Set has been modified */
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
g_pserver->dirty++;
}
/* handle the "SRANDMEMBER key <count>" variant. The normal version of the
@@ -615,8 +620,8 @@ void srandmemberWithCountCommand(client *c) {
long l;
unsigned long count, size;
int uniq = 1;
robj *set;
sds ele;
robj_roptr set;
const char *ele;
int64_t llele;
int encoding;
@@ -633,7 +638,7 @@ void srandmemberWithCountCommand(client *c) {
}
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))
== NULL || checkType(c,set,OBJ_SET)) return;
== nullptr || checkType(c,set,OBJ_SET)) return;
size = setTypeSize(set);
/* If count is zero, serve it ASAP to avoid special cases later. */
@@ -747,8 +752,8 @@ void srandmemberWithCountCommand(client *c) {
}
void srandmemberCommand(client *c) {
robj *set;
sds ele;
robj_roptr set;
const char *ele;
int64_t llele;
int encoding;
@@ -761,7 +766,7 @@ void srandmemberCommand(client *c) {
}
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))
== NULL || checkType(c,set,OBJ_SET)) return;
== nullptr || checkType(c,set,OBJ_SET)) return;
encoding = setTypeRandomElement(set,&ele,&llele);
if (encoding == OBJ_ENCODING_INTSET) {
@@ -794,7 +799,7 @@ void sinterGenericCommand(client *c, robj **setkeys,
robj **sets = (robj**)zmalloc(sizeof(robj*)*setnum, MALLOC_SHARED);
setTypeIterator *si;
robj *dstset = NULL;
sds elesds;
const char *elesds;
int64_t intobj;
void *replylen = NULL;
unsigned long j, cardinality = 0;
@@ -803,13 +808,13 @@ void sinterGenericCommand(client *c, robj **setkeys,
for (j = 0; j < setnum; j++) {
robj *setobj = dstkey ?
lookupKeyWrite(c->db,setkeys[j]) :
lookupKeyRead(c->db,setkeys[j]);
lookupKeyRead(c->db,setkeys[j]).unsafe_robjcast();
if (!setobj) {
zfree(sets);
if (dstkey) {
if (dbDelete(c->db,dstkey)) {
signalModifiedKey(c->db,dstkey);
server.dirty++;
g_pserver->dirty++;
}
addReply(c,shared.czero);
} else {
@@ -912,7 +917,7 @@ void sinterGenericCommand(client *c, robj **setkeys,
dstkey,c->db->id);
}
signalModifiedKey(c->db,dstkey);
server.dirty++;
g_pserver->dirty++;
} else {
setDeferredSetLen(c,replylen,cardinality);
}
@@ -943,7 +948,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
for (j = 0; j < setnum; j++) {
robj *setobj = dstkey ?
lookupKeyWrite(c->db,setkeys[j]) :
lookupKeyRead(c->db,setkeys[j]);
lookupKeyRead(c->db,setkeys[j]).unsafe_robjcast();
if (!setobj) {
sets[j] = NULL;
continue;
@@ -1067,7 +1072,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
sdsfree(ele);
}
setTypeReleaseIterator(si);
server.lazyfree_lazy_server_del ? freeObjAsync(dstset) :
g_pserver->lazyfree_lazy_server_del ? freeObjAsync(dstset) :
decrRefCount(dstset);
} else {
/* If we have a target key where to store the resulting set
@@ -1087,7 +1092,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
dstkey,c->db->id);
}
signalModifiedKey(c->db,dstkey);
server.dirty++;
g_pserver->dirty++;
}
zfree(sets);
}
@@ -1109,11 +1114,11 @@ void sdiffstoreCommand(client *c) {
}
void sscanCommand(client *c) {
robj *set;
robj_roptr set;
unsigned long cursor;
if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == nullptr ||
checkType(c,set,OBJ_SET)) return;
scanGenericCommand(c,set,cursor);
}
+36 -36
View File
@@ -241,18 +241,18 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_
* if we need to switch to the next one. 'lp' will be set to NULL if
* the current node is full. */
if (lp != NULL) {
if (server.stream_node_max_bytes &&
lp_bytes > server.stream_node_max_bytes)
if (g_pserver->stream_node_max_bytes &&
lp_bytes > g_pserver->stream_node_max_bytes)
{
lp = NULL;
} else if (server.stream_node_max_entries) {
} else if (g_pserver->stream_node_max_entries) {
int64_t count = lpGetInteger(lpFirst(lp));
if (count > server.stream_node_max_entries) lp = NULL;
if (count > g_pserver->stream_node_max_entries) lp = NULL;
}
}
int flags = STREAM_ITEM_FLAG_NONE;
if (lp == NULL || lp_bytes > server.stream_node_max_bytes) {
if (lp == NULL || lp_bytes > g_pserver->stream_node_max_bytes) {
master_id = id;
streamEncodeID(rax_key,&id);
/* Create the listpack having the master entry ID and fields. */
@@ -492,14 +492,14 @@ void streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamI
streamEncodeID(si->start_key,start);
} else {
si->start_key[0] = 0;
si->start_key[0] = 0;
si->start_key[1] = 0;
}
if (end) {
streamEncodeID(si->end_key,end);
} else {
si->end_key[0] = UINT64_MAX;
si->end_key[0] = UINT64_MAX;
si->end_key[1] = UINT64_MAX;
}
/* Seek the correct node in the radix tree. */
@@ -819,7 +819,7 @@ void streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupnam
argv[11] = createStringObject("JUSTID",6);
argv[12] = createStringObject("LASTID",6);
argv[13] = createObjectFromStreamID(&group->last_id);
propagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);
propagate(cserver.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(argv[0]);
decrRefCount(argv[3]);
decrRefCount(argv[4]);
@@ -846,7 +846,7 @@ void streamPropagateGroupID(client *c, robj *key, streamCG *group, robj *groupna
argv[2] = key;
argv[3] = groupname;
argv[4] = createObjectFromStreamID(&group->last_id);
propagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
propagate(cserver.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(argv[0]);
decrRefCount(argv[1]);
decrRefCount(argv[4]);
@@ -1242,7 +1242,7 @@ void xaddCommand(client *c) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xadd",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
if (maxlen >= 0) {
/* Notify xtrim event if needed. */
@@ -1260,13 +1260,13 @@ void xaddCommand(client *c) {
/* We need to signal to blocked clients that there is new data on this
* stream. */
if (server.blocked_clients_by_type[BLOCKED_STREAM])
if (g_pserver->blocked_clients_by_type[BLOCKED_STREAM])
signalKeyAsReady(c->db, c->argv[1]);
}
/* XRANGE/XREVRANGE actual implementation. */
void xrangeGenericCommand(client *c, int rev) {
robj *o;
robj_roptr o;
stream *s;
streamID startid, endid;
long long count = -1;
@@ -1293,7 +1293,7 @@ void xrangeGenericCommand(client *c, int rev) {
}
/* Return the specified range to the user. */
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == nullptr ||
checkType(c,o,OBJ_STREAM)) return;
s = (stream*)ptrFromObj(o);
@@ -1318,8 +1318,8 @@ void xrevrangeCommand(client *c) {
/* XLEN */
void xlenCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr
|| checkType(c,o,OBJ_STREAM)) return;
stream *s = (stream*)ptrFromObj(o);
addReplyLongLong(c,s->length);
@@ -1419,14 +1419,14 @@ void xreadCommand(client *c) {
* starting from now. */
int id_idx = i - streams_arg - streams_count;
robj *key = c->argv[i-streams_count];
robj *o = lookupKeyRead(c->db,key);
robj_roptr o = lookupKeyRead(c->db,key);
if (o && checkType(c,o,OBJ_STREAM)) goto cleanup;
streamCG *group = NULL;
/* If a group was specified, than we need to be sure that the
* key and group actually exist. */
if (groupname) {
if (o == NULL ||
if (o == nullptr ||
(group = streamLookupCG((stream*)ptrFromObj(o),szFromObj(groupname))) == NULL)
{
addReplyErrorFormat(c, "-NOGROUP No such key '%s' or consumer "
@@ -1475,8 +1475,8 @@ void xreadCommand(client *c) {
/* Try to serve the client synchronously. */
for (int i = 0; i < streams_count; i++) {
robj *o = lookupKeyRead(c->db,c->argv[streams_arg+i]);
if (o == NULL) continue;
robj_roptr o = lookupKeyRead(c->db,c->argv[streams_arg+i]);
if (o == nullptr) continue;
stream *s = (stream*)ptrFromObj(o);
streamID *gt = ids+i; /* ID must be greater than this. */
int serve_synchronously = 0;
@@ -1534,7 +1534,7 @@ void xreadCommand(client *c) {
streamReplyWithRange(c,s,&start,NULL,count,0,
groups ? groups[i] : NULL,
consumer, flags, &spi);
if (groups) server.dirty++;
if (groups) g_pserver->dirty++;
}
}
@@ -1798,7 +1798,7 @@ NULL
streamCG *cg = streamCreateCG(s,grpname,sdslen(grpname),&id);
if (cg) {
addReply(c,shared.ok);
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_STREAM,"xgroup-create",
c->argv[2],c->db->id);
} else {
@@ -1814,14 +1814,14 @@ NULL
}
cg->last_id = id;
addReply(c,shared.ok);
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_STREAM,"xgroup-setid",c->argv[2],c->db->id);
} else if (!strcasecmp(opt,"DESTROY") && c->argc == 4) {
if (cg) {
raxRemove(s->cgroups,(unsigned char*)grpname,sdslen(grpname),NULL);
streamFreeCG(cg);
addReply(c,shared.cone);
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_STREAM,"xgroup-destroy",
c->argv[2],c->db->id);
} else {
@@ -1832,7 +1832,7 @@ NULL
* that were yet associated with such a consumer. */
long long pending = streamDelConsumer(cg,szFromObj(c->argv[4]));
addReplyLongLong(c,pending);
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_STREAM,"xgroup-delconsumer",
c->argv[2],c->db->id);
} else if (!strcasecmp(opt,"HELP")) {
@@ -1872,7 +1872,7 @@ void xsetidCommand(client *c) {
}
s->last_id = id;
addReply(c,shared.ok);
server.dirty++;
g_pserver->dirty++;
notifyKeyspaceEvent(NOTIFY_STREAM,"xsetid",c->argv[1],c->db->id);
}
@@ -1887,14 +1887,14 @@ void xsetidCommand(client *c) {
*/
void xackCommand(client *c) {
streamCG *group = NULL;
robj *o = lookupKeyRead(c->db,c->argv[1]);
robj_roptr o = lookupKeyRead(c->db,c->argv[1]);
if (o) {
if (checkType(c,o,OBJ_STREAM)) return; /* Type error. */
group = streamLookupCG((stream*)ptrFromObj(o),szFromObj(c->argv[2]));
}
/* No key or group? Nothing to ack. */
if (o == NULL || group == NULL) {
if (o == nullptr || group == NULL) {
addReply(c,shared.czero);
return;
}
@@ -1915,7 +1915,7 @@ void xackCommand(client *c) {
raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);
streamFreeNACK(nack);
acknowledged++;
server.dirty++;
g_pserver->dirty++;
}
}
addReplyLongLong(c,acknowledged);
@@ -1958,11 +1958,11 @@ void xpendingCommand(client *c) {
}
/* Lookup the key and the group inside the stream. */
robj *o = lookupKeyRead(c->db,c->argv[1]);
robj_roptr o = lookupKeyRead(c->db,c->argv[1]);
streamCG *group;
if (o && checkType(c,o,OBJ_STREAM)) return;
if (o == NULL ||
if (o == nullptr ||
(group = streamLookupCG((stream*)ptrFromObj(o),szFromObj(groupname))) == NULL)
{
addReplyErrorFormat(c, "-NOGROUP No such key '%s' or consumer "
@@ -2137,7 +2137,7 @@ void xpendingCommand(client *c) {
* what messages it is now in charge of. */
void xclaimCommand(client *c) {
streamCG *group = NULL;
robj *o = lookupKeyRead(c->db,c->argv[1]);
robj_roptr o = lookupKeyRead(c->db,c->argv[1]);
long long minidle; /* Minimum idle time argument. */
long long retrycount = -1; /* -1 means RETRYCOUNT option not given. */
mstime_t deliverytime = -1; /* -1 means IDLE/TIME options not given. */
@@ -2151,7 +2151,7 @@ void xclaimCommand(client *c) {
/* No key or group? Send an error given that the group creation
* is mandatory. */
if (o == NULL || group == NULL) {
if (o == nullptr || group == NULL) {
addReplyErrorFormat(c,"-NOGROUP No such key '%s' or "
"consumer group '%s'", (char*)ptrFromObj(c->argv[1]),
(char*)ptrFromObj(c->argv[2]));
@@ -2309,12 +2309,12 @@ void xclaimCommand(client *c) {
/* Propagate this change. */
streamPropagateXCLAIM(c,c->argv[1],group,c->argv[2],c->argv[j],nack);
propagate_last_id = 0; /* Will be propagated by XCLAIM itself. */
server.dirty++;
g_pserver->dirty++;
}
}
if (propagate_last_id) {
streamPropagateGroupID(c,c->argv[1],group,c->argv[2]);
server.dirty++;
g_pserver->dirty++;
}
setDeferredArrayLen(c,arraylenptr,arraylen);
preventCommandPropagation(c);
@@ -2352,7 +2352,7 @@ void xdelCommand(client *c) {
if (deleted) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xdel",c->argv[1],c->db->id);
server.dirty += deleted;
g_pserver->dirty += deleted;
}
addReplyLongLong(c,deleted);
}
@@ -2429,7 +2429,7 @@ void xtrimCommand(client *c) {
if (deleted) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xtrim",c->argv[1],c->db->id);
server.dirty += deleted;
g_pserver->dirty += deleted;
if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);
}
addReplyLongLong(c,deleted);
+20 -19
View File
@@ -28,7 +28,7 @@
*/
#include "server.h"
#include <math.h> /* isnan(), isinf() */
#include <cmath> /* isnan(), isinf() */
/*-----------------------------------------------------------------------------
* String Commands
@@ -84,7 +84,7 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire,
return;
}
setKey(c->db,key,val);
server.dirty++;
g_pserver->dirty++;
if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,
@@ -155,9 +155,9 @@ void psetexCommand(client *c) {
}
int getGenericCommand(client *c) {
robj *o;
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == nullptr)
return C_OK;
if (o->type != OBJ_STRING) {
@@ -178,7 +178,7 @@ void getsetCommand(client *c) {
c->argv[2] = tryObjectEncoding(c->argv[2]);
setKey(c->db,c->argv[1],c->argv[2]);
notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
void setrangeCommand(client *c) {
@@ -236,29 +236,30 @@ void setrangeCommand(client *c) {
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,
"setrange",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
}
addReplyLongLong(c,sdslen((sds)ptrFromObj(o)));
}
void getrangeCommand(client *c) {
robj *o;
robj_roptr o;
long long start, end;
char *str, llbuf[32];
const char *str;
char llbuf[32];
size_t strlen;
if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)
return;
if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == nullptr ||
checkType(c,o,OBJ_STRING)) return;
if (o->encoding == OBJ_ENCODING_INT) {
str = llbuf;
strlen = ll2string(llbuf,sizeof(llbuf),(long)ptrFromObj(o));
} else {
str = (char*)ptrFromObj(o);
str = szFromObj(o);
strlen = sdslen(str);
}
@@ -287,8 +288,8 @@ void mgetCommand(client *c) {
addReplyArrayLen(c,c->argc-1);
for (j = 1; j < c->argc; j++) {
robj *o = lookupKeyRead(c->db,c->argv[j]);
if (o == NULL) {
robj_roptr o = lookupKeyRead(c->db,c->argv[j]);
if (o == nullptr) {
addReplyNull(c);
} else {
if (o->type != OBJ_STRING) {
@@ -324,7 +325,7 @@ void msetGenericCommand(client *c, int nx) {
setKey(c->db,c->argv[j],c->argv[j+1]);
notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);
}
server.dirty += (c->argc-1)/2;
g_pserver->dirty += (c->argc-1)/2;
addReply(c, nx ? shared.cone : shared.ok);
}
@@ -368,7 +369,7 @@ void incrDecrCommand(client *c, long long incr) {
}
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReply(c,shared.colon);
addReply(c,newObj);
addReply(c,shared.crlf);
@@ -407,7 +408,7 @@ void incrbyfloatCommand(client *c) {
return;
value += incr;
if (isnan(value) || isinf(value)) {
if (std::isnan(value) || std::isinf(value)) {
addReplyError(c,"increment would produce NaN or Infinity");
return;
}
@@ -418,7 +419,7 @@ void incrbyfloatCommand(client *c) {
dbAdd(c->db,c->argv[1],newObj);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReplyBulk(c,newObj);
/* Always replicate INCRBYFLOAT as a SET command with the final value
@@ -459,13 +460,13 @@ void appendCommand(client *c) {
}
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"append",c->argv[1],c->db->id);
server.dirty++;
g_pserver->dirty++;
addReplyLongLong(c,totlen);
}
void strlenCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
robj_roptr o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
checkType(c,o,OBJ_STRING)) return;
addReplyLongLong(c,stringObjectLen(o));
}
+55 -49
View File
@@ -134,7 +134,7 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
unsigned int rank[ZSKIPLIST_MAXLEVEL];
int i, level;
serverAssert(!isnan(score));
serverAssert(!std::isnan(score));
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
/* store rank that is crossed to reach the insert position */
@@ -530,11 +530,11 @@ static int zslParseRange(robj *min, robj *max, zrangespec *spec) {
} else {
if (((char*)ptrFromObj(min))[0] == '(') {
spec->min = strtod((char*)ptrFromObj(min)+1,&eptr);
if (eptr[0] != '\0' || isnan(spec->min)) return C_ERR;
if (eptr[0] != '\0' || std::isnan(spec->min)) return C_ERR;
spec->minex = 1;
} else {
spec->min = strtod((char*)ptrFromObj(min),&eptr);
if (eptr[0] != '\0' || isnan(spec->min)) return C_ERR;
if (eptr[0] != '\0' || std::isnan(spec->min)) return C_ERR;
}
}
if (max->encoding == OBJ_ENCODING_INT) {
@@ -542,11 +542,11 @@ static int zslParseRange(robj *min, robj *max, zrangespec *spec) {
} else {
if (((char*)ptrFromObj(max))[0] == '(') {
spec->max = strtod((char*)ptrFromObj(max)+1,&eptr);
if (eptr[0] != '\0' || isnan(spec->max)) return C_ERR;
if (eptr[0] != '\0' || std::isnan(spec->max)) return C_ERR;
spec->maxex = 1;
} else {
spec->max = strtod((char*)ptrFromObj(max),&eptr);
if (eptr[0] != '\0' || isnan(spec->max)) return C_ERR;
if (eptr[0] != '\0' || std::isnan(spec->max)) return C_ERR;
}
}
@@ -777,7 +777,7 @@ int zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int cl
return cmp;
}
unsigned int zzlLength(unsigned char *zl) {
unsigned int zzlLength(const unsigned char *zl) {
return ziplistLen(zl)/2;
}
@@ -1153,10 +1153,10 @@ unsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsig
* Common sorted set API
*----------------------------------------------------------------------------*/
unsigned long zsetLength(const robj *zobj) {
unsigned long zsetLength(robj_roptr zobj) {
unsigned long length = 0;
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
length = zzlLength((unsigned char*)zobj->m_ptr);
length = zzlLength((const unsigned char*)zobj->m_ptr);
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
length = ((const zset*)zobj->m_ptr)->zsl->length;
} else {
@@ -1243,8 +1243,8 @@ void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;
zset *set = (zset*)zobj->m_ptr;
if (set->zsl->length <= server.zset_max_ziplist_entries &&
maxelelen <= server.zset_max_ziplist_value)
if (set->zsl->length <= g_pserver->zset_max_ziplist_entries &&
maxelelen <= g_pserver->zset_max_ziplist_value)
zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);
}
@@ -1252,7 +1252,7 @@ void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {
* storing it into *score. If the element does not exist C_ERR is returned
* otherwise C_OK is returned and *score is correctly populated.
* If 'zobj' or 'member' is NULL, C_ERR is returned. */
int zsetScore(robj *zobj, sds member, double *score) {
int zsetScore(robj_roptr zobj, sds member, double *score) {
if (!zobj || !member) return C_ERR;
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
@@ -1320,7 +1320,7 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
double curscore;
/* NaN as input is an error regardless of all the other parameters. */
if (isnan(score)) {
if (std::isnan(score)) {
*flags = ZADD_NAN;
return 0;
}
@@ -1339,7 +1339,7 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
/* Prepare the score for the increment if needed. */
if (incr) {
score += curscore;
if (isnan(score)) {
if (std::isnan(score)) {
*flags |= ZADD_NAN;
return 0;
}
@@ -1357,9 +1357,9 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj->m_ptr = zzlInsert((unsigned char*)zobj->m_ptr,ele,score);
if (zzlLength((unsigned char*)zobj->m_ptr) > server.zset_max_ziplist_entries)
if (zzlLength((unsigned char*)zobj->m_ptr) > g_pserver->zset_max_ziplist_entries)
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
if (sdslen(ele) > server.zset_max_ziplist_value)
if (sdslen(ele) > g_pserver->zset_max_ziplist_value)
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
if (newscore) *newscore = score;
*flags |= ZADD_ADDED;
@@ -1385,7 +1385,7 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
/* Prepare the score for the increment if needed. */
if (incr) {
score += curscore;
if (isnan(score)) {
if (std::isnan(score)) {
*flags |= ZADD_NAN;
return 0;
}
@@ -1470,7 +1470,7 @@ int zsetDel(robj *zobj, sds ele) {
* the one with the lowest score. Otherwise if 'reverse' is non-zero
* the rank is computed considering as element with rank 0 the one with
* the highest score. */
long zsetRank(robj *zobj, sds ele, int reverse) {
long zsetRank(robj_roptr zobj, sds ele, int reverse) {
unsigned long llen;
unsigned long rank;
@@ -1600,8 +1600,8 @@ void zaddGenericCommand(client *c, int flags) {
zobj = lookupKeyWrite(c->db,key);
if (zobj == NULL) {
if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */
if (server.zset_max_ziplist_entries == 0 ||
server.zset_max_ziplist_value < sdslen(szFromObj(c->argv[scoreidx+1])))
if (g_pserver->zset_max_ziplist_entries == 0 ||
g_pserver->zset_max_ziplist_value < sdslen(szFromObj(c->argv[scoreidx+1])))
{
zobj = createZsetObject();
} else {
@@ -1631,7 +1631,7 @@ void zaddGenericCommand(client *c, int flags) {
if (!(retflags & ZADD_NOP)) processed++;
score = newscore;
}
server.dirty += (added+updated);
g_pserver->dirty += (added+updated);
reply_to_client:
if (incr) { /* ZINCRBY or INCR option. */
@@ -1682,7 +1682,7 @@ void zremCommand(client *c) {
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
signalModifiedKey(c->db,key);
server.dirty += deleted;
g_pserver->dirty += deleted;
}
addReplyLongLong(c,deleted);
}
@@ -1784,7 +1784,7 @@ void zremrangeGenericCommand(client *c, int rangetype) {
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
}
server.dirty += deleted;
g_pserver->dirty += deleted;
addReplyLongLong(c,deleted);
cleanup:
@@ -2150,7 +2150,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat
/* The result of adding two doubles is NaN when one variable
* is +inf and the other is -inf. When these numbers are added,
* we maintain the convention of the result being 0.0. */
if (isnan(*target)) *target = 0.0;
if (std::isnan(*target)) *target = 0.0;
} else if (aggregate == REDIS_AGGR_MIN) {
*target = val < *target ? val : *target;
} else if (aggregate == REDIS_AGGR_MAX) {
@@ -2283,7 +2283,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
double score, value;
score = src[0].weight * zval.score;
if (isnan(score)) score = 0;
if (std::isnan(score)) score = 0;
for (j = 1; j < setnum; j++) {
/* It is not safe to access the zset we are
@@ -2330,7 +2330,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
while (zuiNext(&src[i],&zval)) {
/* Initialize value */
score = src[i].weight * zval.score;
if (isnan(score)) score = 0;
if (std::isnan(score)) score = 0;
/* Search for this element in the accumulating dictionary. */
de = dictAddRaw(accumulator,zuiSdsFromValue(&zval),&existing);
@@ -2387,14 +2387,14 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
notifyKeyspaceEvent(NOTIFY_ZSET,
(op == SET_OP_UNION) ? "zunionstore" : "zinterstore",
dstkey,c->db->id);
server.dirty++;
g_pserver->dirty++;
} else {
decrRefCount(dstobj);
addReply(c,shared.czero);
if (touched) {
signalModifiedKey(c->db,dstkey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",dstkey,c->db->id);
server.dirty++;
g_pserver->dirty++;
}
}
zfree(src);
@@ -2410,7 +2410,7 @@ void zinterstoreCommand(client *c) {
void zrangeGenericCommand(client *c, int reverse) {
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
int withscores = 0;
long start;
long end;
@@ -2427,7 +2427,7 @@ void zrangeGenericCommand(client *c, int reverse) {
return;
}
if ((zobj = lookupKeyReadOrReply(c,key,(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == NULL
if ((zobj = lookupKeyReadOrReply(c,key,(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == nullptr
|| checkType(c,zobj,OBJ_ZSET)) return;
/* Sanitize indexes. */
@@ -2439,7 +2439,7 @@ void zrangeGenericCommand(client *c, int reverse) {
/* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) {
addReplyNull(c);
addReplyNull(c,shared.emptymultibulk);
return;
}
if (end >= llen) end = llen-1;
@@ -2527,7 +2527,7 @@ void zrevrangeCommand(client *c) {
void genericZrangebyscoreCommand(client *c, int reverse) {
zrangespec range;
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
long offset = 0, limit = -1;
int withscores = 0;
unsigned long rangelen = 0;
@@ -2575,7 +2575,7 @@ void genericZrangebyscoreCommand(client *c, int reverse) {
}
/* Ok, lookup the key and get the range */
if ((zobj = lookupKeyReadOrReply(c,key,(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == NULL ||
if ((zobj = lookupKeyReadOrReply(c,key,(c->resp < 3) ? shared.emptyarray : shared.null[c->resp])) == nullptr ||
checkType(c,zobj,OBJ_ZSET)) return;
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
@@ -2595,7 +2595,10 @@ void genericZrangebyscoreCommand(client *c, int reverse) {
/* No "first" element in the specified interval. */
if (eptr == NULL) {
addReplyNull(c);
if (c->resp < 3)
addReply(c, shared.emptyarray);
else
addReplyNull(c);
return;
}
@@ -2662,7 +2665,10 @@ void genericZrangebyscoreCommand(client *c, int reverse) {
/* No "first" element in the specified interval. */
if (ln == NULL) {
addReplyNull(c);
if (c->resp < 3)
addReply(c, shared.emptyarray);
else
addReplyNull(c);
return;
}
@@ -2719,7 +2725,7 @@ void zrevrangebyscoreCommand(client *c) {
void zcountCommand(client *c) {
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
zrangespec range;
unsigned long count = 0;
@@ -2730,7 +2736,7 @@ void zcountCommand(client *c) {
}
/* Lookup the sorted set */
if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||
if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == nullptr ||
checkType(c, zobj, OBJ_ZSET)) return;
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
@@ -2796,7 +2802,7 @@ void zcountCommand(client *c) {
void zlexcountCommand(client *c) {
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
zlexrangespec range;
unsigned long count = 0;
@@ -2807,7 +2813,7 @@ void zlexcountCommand(client *c) {
}
/* Lookup the sorted set */
if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||
if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == nullptr ||
checkType(c, zobj, OBJ_ZSET))
{
zslFreeLexRange(&range);
@@ -2877,7 +2883,7 @@ void zlexcountCommand(client *c) {
void genericZrangebylexCommand(client *c, int reverse) {
zlexrangespec range;
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
long offset = 0, limit = -1;
unsigned long rangelen = 0;
void *replylen = NULL;
@@ -2920,7 +2926,7 @@ void genericZrangebylexCommand(client *c, int reverse) {
}
/* Ok, lookup the key and get the range */
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == nullptr ||
checkType(c,zobj,OBJ_ZSET))
{
zslFreeLexRange(&range);
@@ -3063,9 +3069,9 @@ void zrevrangebylexCommand(client *c) {
void zcardCommand(client *c) {
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == NULL ||
if ((zobj = lookupKeyReadOrReply(c,key,shared.czero)) == nullptr ||
checkType(c,zobj,OBJ_ZSET)) return;
addReplyLongLong(c,zsetLength(zobj));
@@ -3073,10 +3079,10 @@ void zcardCommand(client *c) {
void zscoreCommand(client *c) {
robj *key = c->argv[1];
robj *zobj;
robj_roptr zobj;
double score;
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == nullptr ||
checkType(c,zobj,OBJ_ZSET)) return;
if (zsetScore(zobj,szFromObj(c->argv[2]),&score) == C_ERR) {
@@ -3089,10 +3095,10 @@ void zscoreCommand(client *c) {
void zrankGenericCommand(client *c, int reverse) {
robj *key = c->argv[1];
robj *ele = c->argv[2];
robj *zobj;
robj_roptr zobj;
long rank;
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == NULL ||
if ((zobj = lookupKeyReadOrReply(c,key,shared.null[c->resp])) == nullptr ||
checkType(c,zobj,OBJ_ZSET)) return;
serverAssertWithInfo(c,ele,sdsEncodedObject(ele));
@@ -3113,11 +3119,11 @@ void zrevrankCommand(client *c) {
}
void zscanCommand(client *c) {
robj *o;
robj_roptr o;
unsigned long cursor;
if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == nullptr ||
checkType(c,o,OBJ_ZSET)) return;
scanGenericCommand(c,o,cursor);
}
@@ -3211,7 +3217,7 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey
}
serverAssertWithInfo(c,zobj,zsetDel(zobj,ele));
server.dirty++;
g_pserver->dirty++;
if (arraylen == 0) { /* Do this only for the first iteration. */
const char *events[2] = {"zpopmin","zpopmax"};
+3 -1
View File
@@ -1 +1,3 @@
#define REDIS_VERSION "0.9.2"
#define KEYDB_REAL_VERSION "5.0.1"
extern const char *KEYDB_SET_VERSION; // Unlike real version, this can be overriden by the config
+4 -4
View File
@@ -468,7 +468,7 @@ int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
}
/* Return the total number of bytes used by the entry pointed to by 'p'. */
unsigned int zipRawEntryLength(unsigned char *p) {
unsigned int zipRawEntryLength(const unsigned char *p) {
unsigned int prevlensize, encoding, lensize, len;
ZIP_DECODE_PREVLENSIZE(p, prevlensize);
ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
@@ -576,7 +576,7 @@ void zipEntry(unsigned char *p, zlentry *e) {
/* Create a new empty ziplist. */
unsigned char *ziplistNew(void) {
unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;
unsigned char *zl = zmalloc(bytes, MALLOC_SHARED);
ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
@@ -1165,12 +1165,12 @@ unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int v
}
/* Return length of ziplist. */
unsigned int ziplistLen(unsigned char *zl) {
unsigned int ziplistLen(const unsigned char *zl) {
unsigned int len = 0;
if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {
len = intrev16ifbe(ZIPLIST_LENGTH(zl));
} else {
unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
const unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
while (*p != ZIP_END) {
p += zipRawEntryLength(p);
len++;
+1 -1
View File
@@ -50,7 +50,7 @@ unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);
unsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num);
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);
unsigned int ziplistLen(unsigned char *zl);
unsigned int ziplistLen(const unsigned char *zl);
size_t ziplistBlobLen(unsigned char *zl);
void ziplistRepr(unsigned char *zl);
+20 -17
View File
@@ -36,7 +36,7 @@
* for instance to free results obtained by backtrace_symbols(). We need
* to define this function before including zmalloc.h that may shadow the
* free implementation if we use jemalloc or another non standard allocator. */
void zlibc_free(void *ptr) {
extern "C" void zlibc_free(void *ptr) {
free(ptr);
}
@@ -49,13 +49,16 @@ void zlibc_free(void *ptr) {
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)
#else
#define PREFIX_SIZE 16
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
#define PREFIX_SIZE (sizeof(long long))
static_assert(PREFIX_SIZE >= (sizeof(long long)), "");
#else
#define PREFIX_SIZE (sizeof(size_t))
static_assert(PREFIX_SIZE >= (sizeof(size_t)), "");
#endif
#endif
static_assert((PREFIX_SIZE % 16) == 0, "Our prefix must be modulo 16-bytes or our pointers will not be aligned");
/* Explicitly override malloc/free etc when using tcmalloc. */
#if defined(USE_MEMKIND)
#define malloc(size, type) salloc(size, type)
@@ -104,9 +107,9 @@ static void zmalloc_default_oom(size_t size) {
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
void *zmalloc(size_t size, enum MALLOC_CLASS class) {
(void)class;
void *ptr = malloc(size+PREFIX_SIZE, class);
void *zmalloc(size_t size, enum MALLOC_CLASS mclass) {
(void)mclass;
void *ptr = malloc(size+PREFIX_SIZE, mclass);
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
@@ -137,9 +140,9 @@ void zfree_no_tcache(void *ptr) {
}
#endif
void *zcalloc(size_t size, enum MALLOC_CLASS class) {
(void)(class);
void *ptr = calloc(1, size+PREFIX_SIZE, class);
void *zcalloc(size_t size, enum MALLOC_CLASS mclass) {
(void)(mclass);
void *ptr = calloc(1, size+PREFIX_SIZE, mclass);
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
@@ -152,7 +155,7 @@ void *zcalloc(size_t size, enum MALLOC_CLASS class) {
#endif
}
void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) {
void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS mclass) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
@@ -163,10 +166,10 @@ void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) {
zfree(ptr);
return NULL;
}
if (ptr == NULL) return zmalloc(size, class);
if (ptr == NULL) return zmalloc(size, mclass);
#ifdef HAVE_MALLOC_SIZE
oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size, class);
newptr = realloc(ptr,size, mclass);
if (!newptr) zmalloc_oom_handler(size);
update_zmalloc_stat_free(oldsize);
@@ -175,7 +178,7 @@ void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS class) {
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+PREFIX_SIZE, class);
newptr = realloc(realptr,size+PREFIX_SIZE, mclass);
if (!newptr) zmalloc_oom_handler(size);
*((size_t*)newptr) = size;
@@ -202,7 +205,7 @@ size_t zmalloc_usable(void *ptr) {
}
#endif
void zfree(void *ptr) {
void zfree(const void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
@@ -210,8 +213,8 @@ void zfree(void *ptr) {
if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr);
update_zmalloc_stat_free(zmalloc_size((void*)ptr));
free((void*)ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
@@ -222,7 +225,7 @@ void zfree(void *ptr) {
char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l, MALLOC_SHARED);
char *p = (char*)zmalloc(l, MALLOC_SHARED);
memcpy(p,s,l);
return p;
+5 -1
View File
@@ -89,7 +89,7 @@ extern "C" {
void *zmalloc(size_t size, enum MALLOC_CLASS mclass);
void *zcalloc(size_t size, enum MALLOC_CLASS mclass);
void *zrealloc(void *ptr, size_t size, enum MALLOC_CLASS mclass);
void zfree(void *ptr);
void zfree(const void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
@@ -120,4 +120,8 @@ int zmalloc_test(int argc, char **argv);
}
#endif
#ifdef __cplusplus
#include "new.h"
#endif
#endif /* __ZMALLOC_H */
+4 -1
View File
@@ -166,12 +166,15 @@ start_server {} {
# Pick a random slave
set slave_id [expr {($master_id+1)%5}]
set sync_count [status $R($master_id) sync_full]
set sync_partial [status $R($master_id) sync_partial_ok]
catch {
$R($slave_id) config rewrite
$R($slave_id) debug restart
}
# note: just waiting for connected_slaves==4 has a race condition since
# we might do the check before the master realized that the slave disconnected
wait_for_condition 50 2000 {
[status $R($master_id) connected_slaves] == 4
[status $R($master_id) sync_partial_ok] == $sync_partial + 1
} else {
fail "Replica not reconnecting"
}
+97
View File
@@ -0,0 +1,97 @@
start_server {tags {"active-repl"} overrides {active-replica yes}} {
set slave [srv 0 client]
set slave_host [srv 0 host]
set slave_port [srv 0 port]
set slave_log [srv 0 stdout]
set slave_pid [s process_id]
start_server [list overrides [list active-replica yes replicaof [list $slave_host $slave_port]]] {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
# Use a short replication timeout on the slave, so that if there
# are no bugs the timeout is triggered in a reasonable amount
# of time.
$slave config set repl-timeout 5
$master config set repl-timeout 5
# Start the replication process...
$slave slaveof $master_host $master_port
#note the master is a replica via the config (see start_server above)
test {Active replicas report the correct role} {
wait_for_condition 50 100 {
[string match *active-replica* [$slave role]]
} else {
fail "Replica0 does not report the correct role"
}
wait_for_condition 50 100 {
[string match *active-replica* [$master role]]
} else {
fail "Replica1 does not report the correct role"
}
}
test {Active replicas propogate} {
$master set testkey foo
wait_for_condition 50 500 {
[string match *foo* [$slave get testkey]]
} else {
fail "replication failed to propogate"
}
$slave set testkey bar
wait_for_condition 50 500 {
[string match bar [$master get testkey]]
} else {
fail "replication failed to propogate in the other direction"
}
}
test {Active replicas propogate binary} {
$master set binkey "\u0000foo"
wait_for_condition 50 500 {
[string match *foo* [$slave get binkey]]
} else {
fail "replication failed to propogate binary data"
}
}
test {Active replicas WAIT} {
# Test that wait succeeds since replicas should be syncronized
$master set testkey foo
$slave set testkey2 test
assert_equal {1} [$master wait 1 1000] { "value should propogate
within 1 second" }
assert_equal {1} [$slave wait 1 1000] { "value should propogate
within 1 second" }
# Now setup a situation where wait should fail
exec kill -SIGSTOP $slave_pid
$master set testkey fee
assert_equal {0} [$master wait 1 1000] { "slave shouldn't be
synchronized since its stopped" }
}
# Resume the replica we paused in the prior test
exec kill -SIGCONT $slave_pid
test {Active replica expire propogates} {
$master set testkey1 foo
wait_for_condition 50 1000 {
[string match *foo* [$slave get testkey1]]
} else {
fail "Replication failed to propogate"
}
$master pexpire testkey1 200
after 1000
assert_equal {0} [$master del testkey1] {"master expired"}
assert_equal {0} [$slave del testkey1] {"slave expired"}
$slave set testkey1 foo px 200
after 1000
assert_equal {0} [$master del testkey1]
assert_equal {0} [$slave del testkey1]
}
}
}
+26
View File
@@ -79,6 +79,32 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond diskless rec
stop_bg_complex_data $load_handle0
stop_bg_complex_data $load_handle1
stop_bg_complex_data $load_handle2
# Wait for the slave to reach the "online"
# state from the POV of the master.
set retry 5000
while {$retry} {
set info [$master info]
if {[string match {*slave0:*state=online*} $info]} {
break
} else {
incr retry -1
after 100
}
}
if {$retry == 0} {
error "assertion:Slave not correctly synchronized"
}
# Wait that slave acknowledge it is online so
# we are sure that DBSIZE and DEBUG DIGEST will not
# fail because of timing issues. (-LOADING error)
wait_for_condition 5000 100 {
[lindex [$slave role] 3] eq {connected}
} else {
fail "Slave still not connected after some time"
}
set retry 10
while {$retry && ([$master debug digest] ne [$slave debug digest])}\
{
+24
View File
@@ -0,0 +1,24 @@
# find the OS
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
# Compile flags for linux / osx
ifeq ($(uname_S),Linux)
SHOBJ_CFLAGS ?= -W -Wall -fno-common -g -ggdb -std=c99 -O2
SHOBJ_LDFLAGS ?= -shared
else
SHOBJ_CFLAGS ?= -W -Wall -dynamic -fno-common -g -ggdb -std=c99 -O2
SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup
endif
.SUFFIXES: .c .so .xo .o
all: commandfilter.so
.c.xo:
$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
commandfilter.xo: ../../src/redismodule.h
commandfilter.so: commandfilter.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
@@ -1,18 +1,18 @@
#define REDISMODULE_EXPERIMENTAL_API
#include "../redismodule.h"
#include "redismodule.h"
#include <string.h>
static RedisModuleString *log_key_name;
static const char log_command_name[] = "hellofilter.log";
static const char ping_command_name[] = "hellofilter.ping";
static const char unregister_command_name[] = "hellofilter.unregister";
static const char log_command_name[] = "commandfilter.log";
static const char ping_command_name[] = "commandfilter.ping";
static const char unregister_command_name[] = "commandfilter.unregister";
static int in_log_command = 0;
static RedisModuleCommandFilter *filter = NULL;
int HelloFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int CommandFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
(void) argc;
(void) argv;
@@ -23,7 +23,7 @@ int HelloFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
return REDISMODULE_OK;
}
int HelloFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int CommandFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
(void) argc;
(void) argv;
@@ -39,7 +39,7 @@ int HelloFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int a
return REDISMODULE_OK;
}
int HelloFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
RedisModuleString *s = RedisModule_CreateString(ctx, "", 0);
@@ -74,9 +74,9 @@ int HelloFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int ar
return REDISMODULE_OK;
}
void HelloFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)
void CommandFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)
{
if (in_log_command) return; /* don't process our own RM_Call() from HelloFilter_LogCommand() */
if (in_log_command) return; /* don't process our own RM_Call() from CommandFilter_LogCommand() */
/* Fun manipulations:
* - Remove @delme
@@ -117,7 +117,7 @@ void HelloFilter_CommandFilter(RedisModuleCommandFilterCtx *filter)
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx,"hellofilter",1,REDISMODULE_APIVER_1)
if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
if (argc != 2) {
@@ -130,18 +130,18 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
RedisModule_StringToLongLong(argv[1], &noself);
if (RedisModule_CreateCommand(ctx,log_command_name,
HelloFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
CommandFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,ping_command_name,
HelloFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR)
CommandFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,unregister_command_name,
HelloFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
CommandFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if ((filter = RedisModule_RegisterCommandFilter(ctx, HelloFilter_CommandFilter,
if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter,
noself ? REDISMODULE_CMDFILTER_NOSELF : 0))
== NULL) return REDISMODULE_ERR;
+2 -2
View File
@@ -41,6 +41,7 @@ set ::all_tests {
integration/replication-3
integration/replication-4
integration/replication-psync
integration/replication-active
integration/aof
integration/rdb
integration/convert-zipmap-hash-on-load
@@ -63,7 +64,6 @@ set ::all_tests {
unit/lazyfree
unit/wait
unit/pendingquerybuf
modules/commandfilter
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0
@@ -504,7 +504,7 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--only}} {
lappend ::only_tests $arg
incr j
} elseif {$opt eq {--skiptill}} {
} elseif {$opt eq {--skip-till}} {
set ::skip_till $arg
incr j
} elseif {$opt eq {--list-tests}} {
+7
View File
@@ -108,4 +108,11 @@ start_server {tags {"acl"}} {
assert_match {*+debug|segfault*} $cmdstr
assert_match {*+acl*} $cmdstr
}
test {ACL #5998 regression: memory leaks adding / removing subcommands} {
r AUTH default ""
r ACL setuser newuser reset -debug +debug|a +debug|b +debug|c
r ACL setuser newuser -debug
# The test framework will detect a leak if any.
}
}
+4 -5
View File
@@ -2,11 +2,11 @@ start_server {tags {"lazyfree"}} {
test "UNLINK can reclaim memory in background" {
set orig_mem [s used_memory]
set args {}
for {set i 0} {$i < 100000} {incr i} {
for {set i 0} {$i < 200000} {incr i} {
lappend args $i
}
r sadd myset {*}$args
assert {[r scard myset] == 100000}
assert {[r scard myset] == 200000}
set peak_mem [s used_memory]
assert {[r unlink myset] == 1}
assert {$peak_mem > $orig_mem+1000000}
@@ -19,14 +19,13 @@ start_server {tags {"lazyfree"}} {
}
test "FLUSHDB ASYNC can reclaim memory in background" {
after 500 # Sometimes Redis is busy with a prior operation
set orig_mem [s used_memory]
set args {}
for {set i 0} {$i < 100000} {incr i} {
for {set i 0} {$i < 200000} {incr i} {
lappend args $i
}
r sadd myset {*}$args
assert {[r scard myset] == 100000}
assert {[r scard myset] == 200000}
set peak_mem [s used_memory]
r flushdb async
assert {$peak_mem > $orig_mem+1000000}
+4 -3
View File
@@ -161,7 +161,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline}
}
# make sure master doesn't disconnect slave because of timeout
$master config set repl-timeout 300 ;# 5 minutes
$master config set repl-timeout 1200 ;# 20 minutes (for valgrind and slow machines)
$master config set maxmemory-policy allkeys-random
$master config set client-output-buffer-limit "replica 100000000 100000000 300"
$master config set repl-backlog-size [expr {10*1024}]
@@ -212,7 +212,8 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline}
assert {[$master dbsize] == 100}
assert {$slave_buf > 2*1024*1024} ;# some of the data may have been pushed to the OS buffers
assert {$delta < 50*1024 && $delta > -50*1024} ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
set delta_max [expr {$cmd_count / 2}] ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
assert {$delta < $delta_max && $delta > -$delta_max}
$master client kill type slave
set killed_used [s -1 used_memory]
@@ -221,7 +222,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline}
set killed_used_no_repl [expr {$killed_used - $killed_mem_not_counted_for_evict}]
set delta_no_repl [expr {$killed_used_no_repl - $used_no_repl}]
assert {$killed_slave_buf == 0}
assert {$delta_no_repl > -50*1024 && $delta_no_repl < 50*1024} ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
assert {$delta_no_repl > -$delta_max && $delta_no_repl < $delta_max}
}
# unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server
@@ -1,4 +1,4 @@
set testmodule [file normalize src/modules/hellofilter.so]
set testmodule [file normalize tests/modules/commandfilter.so]
start_server {tags {"modules"}} {
r module load $testmodule log-key 0
@@ -27,7 +27,7 @@ start_server {tags {"modules"}} {
test {Command Filter applies on RM_Call() commands} {
r del log-key
r hellofilter.ping
r commandfilter.ping
r lrange log-key 0 -1
} "{ping @log}"
@@ -39,13 +39,13 @@ start_server {tags {"modules"}} {
test {Command Filter applies on Lua redis.call() that calls a module} {
r del log-key
r eval "redis.call('hellofilter.ping')" 0
r eval "redis.call('commandfilter.ping')" 0
r lrange log-key 0 -1
} "{ping @log}"
test {Command Filter is unregistered implicitly on module unload} {
r del log-key
r module unload hellofilter
r module unload commandfilter
r set mykey @log
r lrange log-key 0 -1
} {}
@@ -59,14 +59,14 @@ start_server {tags {"modules"}} {
assert_equal "{set mykey @log}" [r lrange log-key 0 -1]
# Unregister
r hellofilter.unregister
r commandfilter.unregister
r del log-key
r set mykey @log
r lrange log-key 0 -1
} {}
r module unload hellofilter
r module unload commandfilter
r module load $testmodule log-key 1
test {Command Filter REDISMODULE_CMDFILTER_NOSELF works as expected} {
@@ -74,10 +74,10 @@ start_server {tags {"modules"}} {
assert_equal "{set mykey @log}" [r lrange log-key 0 -1]
r del log-key
r hellofilter.ping
r commandfilter.ping
assert_equal {} [r lrange log-key 0 -1]
r eval "redis.call('hellofilter.ping')" 0
r eval "redis.call('commandfilter.ping')" 0
assert_equal {} [r lrange log-key 0 -1]
}
+1 -1
View File
@@ -15,7 +15,7 @@ start_server {tags {"obuf-limits"}} {
if {![regexp {omem=([0-9]+)} $c - omem]} break
if {$omem > 200000} break
}
assert {$omem >= 90000 && $omem < 200000}
assert {$omem >= 80000 && $omem < 200000}
$rd1 close
}
+7
View File
@@ -1,3 +1,10 @@
start_server {tags {"other"} overrides {databases 64}} {
test {CONF-DATABASES - ensure the databases config option is respected} {
r select 63
r set testkey {foo}
} {OK}
}
start_server {tags {"other"}} {
if {$::force_failure} {
# This is used just for test suite development purposes.