Compare commits

...
403 Commits
Author SHA1 Message Date
John Sully fbe8b859f5 Fix bug in fastlock stack metadata 2021-02-08 00:10:38 +00:00
John Sully 584709ce1d Disable multithreaded rehash when active defrag is enabled. The two are not compatible 2021-02-08 00:08:56 +00:00
John Sully 3249e4683c The server should recalibrate periodically. Also reduce log noise 2021-02-08 00:08:20 +00:00
John Sully 86c6977937 Ensure the C lock implementation behaves the same as the ASM 2021-02-08 00:07:00 +00:00
John Sully eb8d294a95 Ensure rehash completes even when we're in a long running task 2021-02-08 00:07:00 +00:00
John Sully 285afd226b Implement rehash during spinlock 2021-02-08 00:07:00 +00:00
John Sully 7273086bfa Allow multiple threads to rehash simultaneously 2021-02-08 00:04:43 +00:00
John Sully 08303b767b Fix races in free 2021-02-08 00:04:43 +00:00
John Sully 774aa1e03e Initial implementation 2021-02-08 00:04:43 +00:00
John Sully 165333b0fc Fix memory leak in mvccRestore 2021-02-07 23:38:49 +00:00
John Sully cc827e157a Module test reliability 2021-02-01 22:21:49 +00:00
John Sully 7b4e25008a Simplify the handling of CreateTimer 2021-02-01 15:51:22 -05:00
VivekSainiEQ f66e5a9c3d Modified RM_CreateTimer to prevent more that one function being posted at a time 2021-02-01 15:51:22 -05:00
VivekSainiEQ 9954f5b4a4 Changed RM_CreateTimer to only call aePostFunction is existing aePostFunction isn't in flight 2021-02-01 15:51:22 -05:00
VivekSainiEQ 53936661c8 Now post entire timer installation process as one function to make atomic with respect to global locks 2021-02-01 15:51:22 -05:00
VivekSainiEQ e2552ff8a9 Updated header file to remove fSynchronous flag 2021-02-01 15:51:22 -05:00
VivekSainiEQ 6ab08cc3e1 Removed more uses of fSynchronous and the use of condition variable and mutex on the control struct. 2021-02-01 15:51:22 -05:00
VivekSainiEQ 3205373bb3 Added fix for scenario where module thread waiting for s_mutexModule in acquireGIL can deadlock with module thread waiting for s_mutex in releaseGIL 2021-02-01 15:51:22 -05:00
VivekSainiEQ 3b8d1f7076 removed synchronous calls to aePostFunction and changed scope of g_fModuleThread in order to prevent module related deadlocks, issue #214 2021-02-01 15:51:22 -05:00
John Sully 5832c25ad1 ARM build fix 2021-01-31 21:22:23 +00:00
John Sully 569a3d9ff8 Fix test failure caused by allowing pings during load 2021-01-31 07:15:55 +00:00
John Sully 69bc7108bd Reduce intermittent failures by giving tests more time 2021-01-31 06:33:13 +00:00
christianEQ 8a557e9d71 added exception to allow pings during loading 2021-01-31 00:12:49 -05:00
christianEQ fe0f071993 added severity levels for afterErrorReply 2021-01-31 00:12:49 -05:00
John Sully effa53339c Improve multithreaded test reliability 2021-01-31 04:33:27 +00:00
John Sully f6fb0e4620 Fix test failure due to bad merge 2021-01-31 03:40:57 +00:00
John Sully cf46a014c1 Fix CI failure due to missing package 2021-01-31 02:36:30 +00:00
John Sully d72b5fa16d Fix crash on shutdown command 2021-01-31 02:30:28 +00:00
christianEQ 0151507cef Merge pull request #284 from christianEQ/redismerge_2021-01-20
Merge Redis 6.0.10 into KeyDB
2021-01-29 10:59:36 -05:00
christianEQ 0fea781f8f killMainThread -> killServerThreads 2021-01-28 23:57:19 +00:00
christianEQ 3c4cadc020 compare_exchange in connSocketRead, explicitly enforce memory order (even if redundant) 2021-01-28 23:17:48 +00:00
christianEQ e2615ccf88 fixed replicaof no one config, added test case 2021-01-28 19:49:22 +00:00
christianEQ 595deb0e13 updated version num to 255.255.255 2021-01-28 16:54:22 +00:00
christianEQ 5372d1aab2 Merge remote-tracking branch 'john/unstable' into redismerge_2021-01-20 2021-01-27 22:25:14 +00:00
christianEQ 52eca14ca6 arm locker before checking zmalloc_used_memory 2021-01-27 22:24:06 +00:00
Daniel Dai 1155f5ac63 Fix Swapdb crash 2021-01-27 15:24:10 -05:00
christianEQ 7319953ae3 fixed uninitialized variables in ACLSetUserCommandBit 2021-01-26 23:29:15 +00:00
christianEQ b8e106a72e fixed strncpy off-by-one errors 2021-01-26 23:27:28 +00:00
christianEQ f30f8eba2f removed unimplemented killIOThreads function declaration 2021-01-26 22:27:22 +00:00
christianEQ 7ed2296fc5 fixed server.cpp mentions 2021-01-26 22:22:19 +00:00
christianEQ 0691a46669 added note about io-threads to keydb.conf 2021-01-26 22:10:53 +00:00
christianEQ d7f01c1f7d fixed race condition in connSocketWrite 2021-01-26 22:08:07 +00:00
christianEQ 4d71929c60 Removed Redis-specific things from conf file 2021-01-26 21:50:45 +00:00
christianEQ dadce055f8 Merge tag 'tags/6.0.10' into redismerge_2021-01-20 2021-01-26 21:43:09 +00:00
christianEQ d2867580ab changed loading process config types to be consistent across environments 2021-01-14 21:12:36 -05:00
christianEQ 8dc238efae renamed loaded key counter to be more clear 2021-01-14 13:06:57 -05:00
christianEQ 7cd4b5e11c removed 'comment arguments' from config options (doesn't ignore inline comments) 2021-01-14 13:06:57 -05:00
christianEQ 00043f9509 fixed signedness warning 2021-01-14 13:06:57 -05:00
christianEQ 9b81f3f263 added section to keydb.conf for new options, tweaked default 2021-01-14 13:06:57 -05:00
christianEQ 2747d1a1a9 ping replicas during key load 2021-01-14 13:06:57 -05:00
christianEQ 9f7ed0b232 added config option for loading callback interval keys 2021-01-14 13:06:57 -05:00
christianEQ c176ea7366 configurized loading_process_events_interval_bytes 2021-01-14 13:06:57 -05:00
Oran Agra 8d70d498d5 Redis 6.0.10 2021-01-12 16:25:37 +02:00
Oran Agra 5745b469d3 Improve stability of new CSC eviction test (#8160)
c4fdf09c0 added a test that now fails with valgrind
it fails for two resons:
1) the test samples the used memory and then limits the maxmemory to
   that value, but it turns out this is not atomic and on slow machines
   the background cron process that clean out old query buffers reduces
   the memory so that the setting doesn't cause eviction.
2) the dbsize was tested late, after reading some invalidation messages
   by that time more and more keys got evicted, partially draining the
   db. this is not the focus of this fix (still a known limitation)

(cherry picked from commit a102b21d17)
2021-01-12 16:25:37 +02:00
Oran Agra a11c842ecd fix race in cluster transactions test (#8312)
we didn't wait for the commands executed on the master to reach the replica.

(cherry picked from commit 4f8458d8d6)
2021-01-12 16:25:37 +02:00
Oran Agra 615eb0db86 Fix cluster diskless load swapdb test (#8308)
The test was trying to wait for the replica to start loading the rdb
from the master before it kills the master, but it was actually waiting
for ROLE to be in "sync" mode, which corresponds to REPL_STATE_TRANSFER
that starts before the actual loading starts.
now instead it waits for the loading flag to be set.

Besides, the test was dependent on the previous configuration of the
servers, relying on the fact the replica is configured to persist
(either RDB of AOF), now it is set explicitly.

(cherry picked from commit 264953871b)
2021-01-12 16:25:37 +02:00
Bob Li 203feb6ea2 Fix overflow of rdbWriteRaw return value (#8306)
Saving string of more than 2GB to the RDB file, can result in corrupt RDB, or failure in rdbSave.
S

(cherry picked from commit 542455ce2a)
2021-01-12 16:25:37 +02:00
guybe7 08c3f0bca6 Modules: Fix an integer sign bug in moduleTimerHandler (#8131)
bug was introduced in 1a91a2700b

(cherry picked from commit 2f41a38568)
2021-01-12 16:25:37 +02:00
Egor Seredin 3bf02ea7e8 Allow '\0' inside of result of sdscatvprintf, and efficiency improvements (#6260)
This will allow to use: RedisModule_CreateStringPrintf(ctx, "%s %c %s", "string1", 0, "string2");

On large string, the previous code would incrementally retry to double the output buffer.
now it uses the the return value of snprintf and grows to the right size in one step.

and also avoids an excessive strlen in sdscat at the end.

(cherry picked from commit f4ca3d8757)
2021-01-12 16:25:37 +02:00
guybe7 acb4cd2135 Modules: Improve timer accuracy (#7987)
The bug occurs when 'callback' re-registers itself to a point
in the future and the execution time in non-negligible:
'now' refers to time BEFORE callback was executed and is used
to calculate 'next_period'.
We must get the actual current time when calculating 'next_period'

(cherry picked from commit 1a91a2700b)
2021-01-12 16:25:37 +02:00
Meir Shpilraien (Spielrein) fb86ecb802 Moved RMAPI_FUNC_SUPPORTED location such that it will be visible to modules (#8037)
The RMAPI_FUNC_SUPPORTED was defined in the wrong place on redismodule.h
and was not visible to modules.

(cherry picked from commit 97d647a139)
2021-01-12 16:25:37 +02:00
David CARLIER 2c5865cd90 raspberry build fix. (#8095)
__ILP32__ is 32 bits ABI and does not imply x86, this patch resolves this. 

(cherry picked from commit 0719388cfb)
2021-01-12 16:25:37 +02:00
Yossi Gottlieb ebbcd05e03 Fix crash log output on ARM. (#8020)
(cherry picked from commit 7e4325cbc9)
2021-01-12 16:25:37 +02:00
Oran Agra de4f04a272 Fix cluster access to unaligned memory (SIGBUS on old ARM) (#7958)
Turns out this was broken since version 4.0 when we added sds size
classes.
The cluster code uses sds for the receive buffer, and then casts it to a
struct and accesses a 64 bit variable.
This commit replaces the use of sds with a simple reallocated buffer.

(cherry picked from commit 380f6048e0)
2021-01-12 16:25:37 +02:00
Oran Agra f2f57eb44f prevent client tracking from causing feedback loop in performEvictions (#8100)
When client tracking is enabled signalModifiedKey can increase memory usage,
this can cause the loop in performEvictions to keep running since it was measuring
the memory usage impact of signalModifiedKey.

The section that measures the memory impact of the eviction should be just on dbDelete,
excluding keyspace notification, client tracking, and propagation to AOF and replicas.

This resolves part of the problem described in #8069
p.s. fix took 1 minute, test took about 3 hours to write.

(cherry picked from commit c4fdf09c05)
2021-01-12 16:25:37 +02:00
Madelyn Olson 8119259bb6 White space tweaks and skip categories already applied
(cherry picked from commit d310beb417)
2021-01-12 16:25:37 +02:00
Madelyn Olson e664f38170 Further improved ACL algorithm for picking categories
(cherry picked from commit 411bcf1a41)
2021-01-12 16:25:37 +02:00
Yang Bodong f464cf2380 Swapdb should make transaction fail if there is any client watching keys (#8239)
This PR not only fixes the problem that swapdb does not make the
transaction fail, but also optimizes the FLUSHALL and FLUSHDB command to
set the CLIENT_DIRTY_CAS flag to avoid unnecessary traversal of clients.

FLUSHDB was changed to first iterate on all watched keys, and then on the
clients watching each key.
Instead of iterating though all clients, and for each iterate on watched keys.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 10f94b0ab1)
2021-01-12 16:25:37 +02:00
Itamar Haber 02c395a179 Adds 'use-memory' to GEORADIUS[BYMEMBER] (#8107)
Partial resolution for #6860, item 7

(cherry picked from commit feba7cbf4d)
2021-01-12 16:25:37 +02:00
Oran Agra acdccab019 fix valgrind warning created by recent pidfile fix (#8235)
This isn't a leak, just an warning due to unreachable
allocation on the fork child.
Problem created by 92a483b

(cherry picked from commit 2426aaa099)
2021-01-12 16:25:37 +02:00
Meir Shpilraien (Spielrein) e9f674e5fb Fix issue where fork process deletes the parent pidfile (#8231)
Turns out that when the fork child crashes, the crash log was deleting
the pidfile from the disk (although the parent is still running.

Now we set the pidfile of the fork process to NULL so the fork process
will never deletes it.

(cherry picked from commit 92a483bca2)
2021-01-12 16:25:37 +02:00
Wen Hui 0f8d47c9ed Sentinel: add missing calls for sentinelflushconfig when config master at runtime (#8229)
(cherry picked from commit 781d7b0d9b)
2021-01-12 16:25:37 +02:00
Wen Hui f1d56c981d fix memory leak in processInlineBuffer error handling code (#8295)
This code path is normally executed only when v6.0 and above replicates from v2.4

(cherry picked from commit cfcd0fa6f7)
2021-01-12 16:25:37 +02:00
huangzhw ea595784f9 sdscatfmt call sdsMakeRoomFor, asked for more space than intended (#8286)
instead of asking for the extra new space it wanted, it asked to grow the
string by the size it already has too.
i.e. a string of 1000 bytes, needing to grow by 10 bytes, would have been
asking for an **additional** 1010 bytes.

(cherry picked from commit 595ecd5f4b)
2021-01-12 16:25:37 +02:00
huangzhw 549fe1f3af sort Command lookupKeyRead and lookupKeyWrite are used on the opposite (#8283)
This is a recent problem, introduced by 7471743 (redis 6.0)

The implications are:
The sole difference between LookupKeyRead and LookupKeyWrite is for command
executed on a replica, which are not received from its master client. (for the master,
and for the master client on the replica, these two functions behave the same)!

Since SORT is a write command, this bug only implicates a writable-replica.
And these are its implications:

- SORT STORE will behave as it did before the above mentioned commit (like before
  redis 6.0). on a writable-replica an already logically expired the key would have
  appeared missing. (store dest key would be deleted, instead of being populated
  with the data from the already logically expired key)
- SORT (the non store variant, which in theory could have been executed on
  read-only-replica if it weren't for the write flag), will (in redis 6.0) have a new bug
  and return the data from the already logically expired key instead of empty response.

(cherry picked from commit 08ad6abd04)
2021-01-12 16:25:37 +02:00
Oran Agra cf6b0f9cef Fix rdb checksum / crc64 on bigendian (#8270)
Turns out the RDB checksum in Redis 6.0 on bigendian is broken.
It always returned 0, so the RDB files are generated as if checksum is
disabled, and will be loaded ok on littleendian, and on bigendian.
But it'll not be able to load RDB files generated on littleendian or older versions.

Similarly DUMP and RESTORE will work on the same version (0==0),
but will be unable to exchange dump payloads with littleendian or old versions.

(cherry picked from commit 324070c8f6)
2021-01-12 16:25:37 +02:00
Oran Agra 02e930624a fix crash in redis-cli after making cluster backup (#8267)
getRDB is "designed" to work in two modes: one for redis-cli --rdb and
one for redis-cli --cluster backup.
in the later case it uses the hiredis connection from the cluster nodes
and it used to free it without nullifying the context, so a later
attempt to free the context would crash.

I suppose the reason it seems to want to free the hiredis context ASAP
is that it wants to disconnect the replica link, so that replication
buffers will not be accumulated.

(cherry picked from commit 41b2ed2bbc)
2021-01-12 16:25:37 +02:00
Oran Agra ec56906bd6 Fix wrong order of key/value in Lua map response (#8266)
When a Lua script returns a map to redis (a feature which was added in
redis 6 together with RESP3), it would have returned the value first and
the key second.

If the client was using RESP2, it was getting them out of order, and if
the client was in RESP3, it was getting a map of value => key.
This was happening regardless of the Lua script using redis.setresp(3)
or not.

This also affects a case where the script was returning a map which it got
from from redis by doing something like: redis.setresp(3); return redis.call()

This fix is a breaking change for redis 6.0 users who happened to rely
on the wrong order (either ones that used redis.setresp(3), or ones that
returned a map explicitly).

This commit also includes other two changes in the tests:
1. The test suite now handles RESP3 maps as dicts rather than nested
   lists
2. Remove some redundant (duplicate) tests from tracking.tcl

(cherry picked from commit 2017407b4d)
2021-01-12 16:25:37 +02:00
Oran Agra 23d299234c Crash log would crash half way on commands with no arguments (#8260)
The crash log attempts to print the current client info, and when it
does that it attempts to check if the first argument happens to be a key
but it did so for commands with no arguments too, which caused the crash
log to crash half way and not reach its end.

(cherry picked from commit 152b5d46c4)
2021-01-12 16:25:37 +02:00
Yossi Gottlieb f84c181eac Fix crashes with io-threads-do-reads enabled. (#8230)
Normally IO threads should simply read data from the socket into the
buffer and attempt to parse it.

If a protocol error is detected, a reply is generated which may result
with installing a write handler which is not thread safe. This fix
delays that until the client is processed back in the main thread.

Fixes #8220

(cherry picked from commit e7047ec2fc)
2021-01-12 16:25:37 +02:00
George Prekas 8ff0226361 Add check for the MADV_FREE/fork arm64 Linux kernel bug (#8224)
Older arm64 Linux kernels have a bug that could lead to data corruption during
background save under the following scenario:

1) jemalloc uses MADV_FREE on a page,
2) jemalloc reuses and writes the page,
3) Redis forks the background save process, and
4) Linux performs page reclamation.

Under these conditions, Linux will reclaim the page wrongfully and the
background save process will read zeros when it tries to read the page.

The bug has been fixed in Linux with commit:
ff1712f953e27f0b0718762ec17d0adb15c9fd0b ("arm64: pgtable: Ensure dirty bit is
preserved across pte_wrprotect()")

This Commit adds an ignore-warnings config, when not found, redis will
print a warning and exit on startup (default behavior).

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit b02780c41d)

in 6.0 this warning is ignored by default in order to avoid adding
regression, specifically for deployments that don't need persistence or
replication
2021-01-12 16:25:37 +02:00
Qu Chen d756b11fa2 Not over-allocate client query buffer when reading large objects. (#5954)
In response to large client query buffer optimization introduced in 1898e6c. The calculation of the amount of
remaining bytes we need to write to the query buffer was calculated wrong, as a result we are unnecessarily
growing the client query buffer by sdslen(c->querybuf) always. This fix corrects that behavior.

Please note the previous behavior prior to the before-mentioned change was correctly calculating the remaining
additional bytes, and this change makes that calculate to be consistent.

Useful context, the argument of size `ll` starts at qb_pos (which is now the beginning of the sds), but much of it
may have already been read from the socket, so we only need to grow the sds for the remainder of it.

(cherry picked from commit 11b3325e99)
2021-01-12 16:25:37 +02:00
Oran Agra ec74ae7ec1 Handle output buffer limits for Module blocked clients (#8141)
Module blocked clients cache the response in a temporary client,
the reply list in this client would be affected by the recent fix
in #7202, but when the reply is later copied into the real client,
it would have bypassed all the checks for output buffer limit, which
would have resulted in both: responding with a partial response to
the client, and also not disconnecting it at all.

(cherry picked from commit 48efc25f74)
2021-01-12 16:25:37 +02:00
Yossi Gottlieb 785851a736 Fix setproctitle related crashes. (#8150)
Makes spt_init more careful with assumptions about what memory regions
may be overwritten. It will now only consider a contiguous block of argv
and envp elements and mind any gaps.

(cherry picked from commit ec02c761aa)
2021-01-12 16:25:37 +02:00
Yossi Gottlieb 2a2d5a7a1f Fix use-after-free issue in spt_copyenv. (#8088)
Seems to have gone unnoticed for a long time, because at least with
glibc it will only be triggered if setenv() was called before spt_init,
which Redis doesn't.

Fixes #8064.

(cherry picked from commit 7e5a6313f0)
2021-01-12 16:25:37 +02:00
Wang Yuan a60ed4a599 Backup keys to slots map and restore when fail to sync if diskless-load type is swapdb in cluster mode (#8108)
When replica diskless-load type is swapdb in cluster mode, we didn't backup
keys to slots map, so we will lose keys to slots map if fail to sync.
Now we backup keys to slots map at first, and restore it properly when fail.

This commit includes a refactory/cleanup of the backups mechanism (moving it to db.c and re-structuring it a bit).

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit b55a827ea2)
2021-01-12 16:25:37 +02:00
Wang Yuan 5ed6662145 Reset average ttl when empty databases (#8106)
On FLUSHDB or full sync, reset old average TTL stat.
This Stat is incrementally collected by the master over time when it searches for expired keys. 

(cherry picked from commit c85bf2352d)
2021-01-12 16:25:37 +02:00
Oran Agra f885e364ba Fix bug with module GIL being released prematurely (#8061)
This is hopefully usually harmles.
The server.ready_keys will usually be empty so the code after releasing
the GIL will soon be done.
The only case where it'll actually process things is when a module
releases a client (or module) blocked on a key, by triggering this NOT
from within a command (e.g. a timer event).

This bug was introduced in redis 6.0.9, see #7903

(cherry picked from commit e6fa47380a)
2021-01-12 16:25:37 +02:00
Oran Agra 3ac666b3e6 Fix oom-score-adj-values range, abs options, and bug when used in config file (#8046)
Fix: When oom-score-adj-values is provided in the config file after
oom-score-adj yes, it'll take an immediate action, before
readOOMScoreAdj was acquired, resulting in an error (out of range score
due to uninitialized value. delay the reaction the real call is made by
main().

Since the values are clamped to -1000..1000, and they're
applied as an offset from the value at startup (which may be -1000), we
need to allow the offsets to reach to +2000 so that a value of +1000 is
achievable in case the value at startup was -1000.

Adding an option for absolute values rather than relative ones.

(cherry picked from commit 61954951ed)
2021-01-12 16:25:37 +02:00
guybe7 4b37eb13dc EXISTS should not alter LRU, OBJECT should not reveal expired keys on replica (#8016)
The bug was introduced by #5021 which only attempted avoid EXIST on an
already expired key from returning 1 on a replica.

Before that commit, dbExists was used instead of
lookupKeyRead (which had an undesired effect to "touch" the LRU/LFU)

Other than that, this commit fixes OBJECT to also come empty handed on
expired keys in replica.

And DEBUG DIGEST-VALUE to behave like DEBUG OBJECT (get the data from
the key regardless of it's expired state)

(cherry picked from commit f8ae991717)
2021-01-12 16:25:37 +02:00
Wang Yuan 3a13c654e6 Disable rehash when redis has child process (#8007)
In redisFork(), we don't set child pid, so updateDictResizePolicy()
doesn't take effect, that isn't friendly for copy-on-write.

The bug was introduced this in redis 6.0: 56258c6

(cherry picked from commit 89c78a9808)
2021-01-12 16:25:37 +02:00
John Sully d50e4bb819 Fix intermittent module test failure, resizing the repl backlog is not good enough to clear the backlog 2021-01-12 00:29:07 +00:00
benschermel 4485ca74f6 update perf chart, build info, job post link 2021-01-11 11:40:00 -05:00
benschermel 7f72b214e5 created ascii logo image 2021-01-11 11:40:00 -05:00
John Sully bc8cc1adb3 Merge branch 'repl_perf' into unstable 2020-12-24 02:22:57 +00:00
John Sully d03c2de901 Merge branch 'unstable' of https://github.com/JohnSully/KeyDB into unstable 2020-12-11 03:59:50 +00:00
John Sully 7d3c5f1e64 don't try and free a client that can't be freed 2020-12-11 03:59:39 +00:00
John Sully 81573de393 Prevent use after free 2020-12-11 03:59:26 +00:00
John Sully a80dd5d001 Remove AE_ASSERT and use the proper assert infra 2020-12-11 00:24:27 +00:00
John Sully 7e0077f293 Prevent test crash due to unimplemented save for nested hash 2020-12-11 00:23:44 +00:00
John Sully 1eb035e6ad Fix crash freeing nested hash with list 2020-12-11 00:23:14 +00:00
Kajaruban Surendran bdd14ac517 Addressing PR comments 2020-12-04 14:54:06 -05:00
Kajaruban Surendran 63e5d00bdf Addressing PR comments 2020-12-04 14:54:06 -05:00
Kajaruban Surendran c25fb3d74f Handling of aeResizeSetSize's return code for current thread 2020-12-04 14:54:06 -05:00
Kajaruban Surendran 572d41325b check the aeResizeSetSize return code 2020-12-04 14:54:06 -05:00
Kajaruban Surendran e02120cdf7 Adding thread index to the error message 2020-12-04 14:54:06 -05:00
Kajaruban Surendran 7b1221a74d Call aePostFunction to change the setsize of each threads by that thread 2020-12-04 14:54:06 -05:00
Kajaruban Surendran d6b4f994ad Calling initServerThread function cserver.cthreads(server-threads) times than MAX_EVENT_LOOPS(constant) 2020-12-04 14:52:28 -05:00
christianEQ 66a41dafc4 Issue: #204 Allocate 8 MB to thread stack 2020-12-01 14:50:03 -05:00
John Sully f19b9c155e Issue: #260 Don't use C++ static_assert in C files 2020-11-30 23:01:20 +00:00
John Sully 5d016fdb6b code coverage reports should test multithreaded 2020-11-30 22:59:00 +00:00
VivekSainiEQ cae9924fd9 Added module data type load/save tests 2020-11-30 17:47:04 -05:00
VivekSainiEQ 089f28e8ba Removed use of module datatypes, now should work if tests/modules is not built 2020-11-30 17:47:04 -05:00
VivekSainiEQ dcb44d3a09 Added tests for saving various data types to disk and loading them back, and for loading data types from redis to maintain compatibility 2020-11-30 17:47:04 -05:00
VivekSainiEQ f99cba0b92 Added remainder of runtest scripts to make lcov command. Code coverage goes from 57.9% to 72.1% 2020-11-30 17:47:04 -05:00
VivekSainiEQ e10dc0dace Added KeyDB.info to .gitignore file 2020-11-30 17:47:04 -05:00
Kajaruban Surendran 49a89d636b Configurable option for MOTD 2020-11-30 10:42:48 -05:00
VivekSainiEQ b94f91b9b2 Added references to KeyDB.info instead of redis.info 2020-11-25 18:49:20 -05:00
VivekSainiEQ 06a17c8511 Properly escaped $ in lcov command 2020-11-25 18:49:20 -05:00
VivekSainiEQ eebd70aaa6 Updated 'make lcov' command to exclude external dependancies and generate final code coverage percentage 2020-11-25 18:49:20 -05:00
VivekSainiEQ 779c775df9 Added test for issue #257 2020-11-25 17:21:37 -05:00
VivekSainiEQ 1d4d2925c2 Made NULL pointer check explicit, issue #257 2020-11-25 17:21:37 -05:00
VivekSainiEQ b67db7c2d5 Added NULL check to function objFromAllocPtr, now NULL input will map to NULL output even if active replica is enabled 2020-11-25 17:21:37 -05:00
John Sully 18953b2998 Merge branch 'unstable' of https://github.com/JohnSully/KeyDB into unstable 2020-11-23 02:01:54 +00:00
John Sully 527b7eb074 Fix issue where active replication doesn't replicate RDB data 2020-11-23 02:01:40 +00:00
Kajaruban Surendran 34a97c447f fix for the server crash when the maxclients increased via config set 2020-11-20 15:20:46 -05:00
John Sully 1c525e20b1 Blocking clients should not crash if an active replica loads a remote RDB with a key in the blocklist 2020-11-19 23:28:01 +00:00
John Sully 0bcce50285 Ensure multithreaded tests pass 2020-11-13 21:08:10 +00:00
John Sully 96e0b2a2b1 Improve replication performance in multithreaded scenarios 2020-11-13 21:08:10 +00:00
John Sully f3794d2c42 Add ability to compile KeyDB without MOTD and curl 2020-11-13 20:51:30 +00:00
John Sully 97fd736923 Add missing file 2020-11-11 03:29:38 +00:00
John Sully ba950a3d1f Second implementation of nested hashes 2020-11-06 19:24:48 +00:00
John Sully dad5a902d3 Additional test reliability fixes 2020-10-27 07:25:43 +00:00
Oran Agra 25214bd7dc Redis 6.0.9. 2020-10-27 09:12:01 +02:00
Yossi Gottlieb 3cc673bdd8 Fix wrong zmalloc_size() assumption. (#7963)
When using a system with no malloc_usable_size(), zmalloc_size() assumed
that the heap allocator always returns blocks that are long-padded.

This may not always be the case, and will result with zmalloc_size()
returning a size that is bigger than allocated. At least in one case
this leads to out of bound write, process crash and a potential security
vulnerability.

Effectively this does not affect the vast majority of users, who use
jemalloc or glibc.

This problem along with a (different) fix was reported by Drew DeVault.

(cherry picked from commit 9824fe3e39)
2020-10-27 09:12:01 +02:00
Oran Agra de0919cd62 Attempt to fix sporadic test failures due to wait_for_log_messages (#7955)
The tests sometimes fail to find a log message.
Recently i added a print that shows the log files that are searched
and it shows that the message was in deed there.
The only reason i can't think of for this seach to fail, is we we
happened to read an incomplete line, which didn't match our pattern and
then on the next iteration we would continue reading from the line after
it.

The fix is to always re-evaluation the previous line.

(cherry picked from commit 4e2e5be201)
2020-10-27 09:12:01 +02:00
David CARLIER 5aa0fef2ce cpu affinity: DragonFlyBSD support (#7956)
(cherry picked from commit 27f4c212f3)
2020-10-27 09:12:01 +02:00
Zach Fewtrell 3988e32f41 fix invalid 'failover' identifier in cluster slave selection test (#7942)
(cherry picked from commit ebfa769925)
2020-10-27 09:12:01 +02:00
WuYunlong 6f16e52a6d Update rdb_last_bgsave_time_sec in INFO on diskless replication (#7917)
`info Persistence` will include correct (updated) rdb_last_bgsave_time_sec
For diskless bgsave (sockets) too (like a few other persistence info fields).

Refactor code to reduce duplicate code.

(cherry picked from commit e05a7df7f9)
2020-10-27 09:12:01 +02:00
Wen Hui c6c2de9567 do not add save parameter during config rewrite in sentinel mode (#7945)
Previous code would have added default redis save parameters
to the config file on rewrite, which would have been silently ignored
when the config file is loaded.

The new code avoids adding this, and also actively removes these lines
If added by a previous config rewrite. 

(cherry picked from commit 0f370f9b66)
2020-10-27 09:12:01 +02:00
Qu Chen cebc1f2696 WATCH no longer ignores keys which have expired for MULTI/EXEC. (#7920)
This wrong behavior was backed by a test, and also documentation, and dates back to 2010.
But it makes no sense to anyone involved so it was decided to change that.

Note that 20eeddf (invalidate watch on expire on access) was released in 6.0 RC2
and 2d1968f released in in 6.0.0 GA (invalidate watch when key is evicted).
both of which do similar changes.

(cherry picked from commit 556acefe75)
2020-10-27 09:12:01 +02:00
Oran Agra 56d8ad932d improve verbose logging on failed test. print log file lines (#7938)
(cherry picked from commit c96ece9f5e)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 0103dc09d2 Add a --no-latency tests flag. (#7939)
Useful for running tests on systems which may be way slower than usual.

(cherry picked from commit 843a13e88f)
2020-10-27 09:12:01 +02:00
filipe oliveira 71c1427a1b Fixed bug concerning redis-benchmark non clustered benchmark forcing always the same hash tag {tag} (#7931)
Adding the ":{tag}" only if --cluster is used, so that when used against
a proxy it generates traffic to all shards.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 6cf23d6610)
2020-10-27 09:12:01 +02:00
Oran Agra 30bb0dd595 fix 32bit build warnings (#7926)
(cherry picked from commit a425e1d26d)
2020-10-27 09:12:01 +02:00
Wen Hui 99f39d32ab fix double fclose in aofrewrite (#7919)
minor fix for a bug which happen on error handling code
and doesn't look like it could have caused any real harm
(fd number wouldn't have been reused yet)

(cherry picked from commit 04a0af9085)
2020-10-27 09:12:01 +02:00
Oran Agra 851ec530b4 INFO report peak memory before eviction (#7894)
In some cases one command added a very big bulk of memory, and this
would be "resolved" by the eviction before the next command.

Seeing an unexplained mass eviction we would wish to
know the highest momentary usage too.

Tracking it in call() and beforeSleep() adds some hooks in AOF and RDB
loading.

The fix in clientsCronTrackExpansiveClients is related to #7874

(cherry picked from commit 457b7073b5)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 79e6ab31d8 Fix tests failure on busybox systems. (#7916)
(cherry picked from commit ef92f507dd)
2020-10-27 09:12:01 +02:00
Oran Agra b20af834ff Allow requirepass config to clear the password (#7899)
This is a compatibility issue with redis 5.0 that was introduced by ACL.
Before this commit, setting requirepass to an empty string will result
in a server that needs an empty AUTH, unlike redis 5.0 which would
accept connections without an AUTH.

(cherry picked from commit 19418b6b28)
2020-10-27 09:12:01 +02:00
Wang Yuan 1453277294 Remove temporary aof and rdb files in a background thread (#7905)
If we fail or stop to rewrite aof, we need to remove temporary aof.
We also remove temporary rdb when replicas abort to receive rdb.
But currently we delete them in main thread, to avoid blocking,
we should use bg_unlink to remove them in a background thread.

Btw, we have already used this way to removed child process temporary rdb.

(cherry picked from commit aaacb8c955)
2020-10-27 09:12:01 +02:00
guybe7 e4e24460f9 Minor improvements to module blocked on keys (#7903)
- Clarify some documentation comments
- Make sure blocked-on-keys client privdata is accessible
  from withing the timeout callback
- Handle blocked clients in beforeSleep - In case a key
  becomes "ready" outside of processCommand

See #7879 #7880

(cherry picked from commit addf47dcac)
2020-10-27 09:12:01 +02:00
Andreas Lind db9cffe7c2 Support redis-cli -u rediss://... (#7900)
(cherry picked from commit 8b497881f2)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 9c475272da Modules: fix RM_GetCommandKeys API. (#7901)
This cleans up and simplifies the API by passing the command name as the
first argument. Previously the command name was specified explicitly,
but was still included in the argv.

(cherry picked from commit 056a43e1a6)
2020-10-27 09:12:01 +02:00
Meir Shpilraien (Spielrein) 6f2c894d69 Add Module API for version and compatibility checks (#7865)
* Introduce a new API's: RM_GetContextFlagsAll, and
RM_GetKeyspaceNotificationFlagsAll that will return the
full flags mask of each feature. The module writer can
check base on this value if the Flags he needs are
supported or not.

* For each flag, introduce a new value on redismodule.h,
this value represents the LAST value and should be there
as a reminder to update it when a new value is added,
also it will be used in the code to calculate the full
flags mask (assuming flags are incrementally increasing).
In addition, stated that the module writer should not use
the LAST flag directly and he should use the GetFlagAll API's.

* Introduce a new API: RM_IsSubEventSupported, that returns for a given
event and subevent, whether or not the subevent supported.

* Introduce a new macro RMAPI_FUNC_SUPPORTED(func) that returns whether
or not a function API is supported by comparing it to NULL.

* Introduce a new API: int RM_GetServerVersion();, that will return the
current Redis version in the format 0x00MMmmpp; e.g. 0x00060008;

* Changed unstable version from 999.999.999 to 255.255.255

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
(cherry picked from commit adc3183cd2)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 4147a2202e Module API: Add RM_GetClientCertificate(). (#7866)
This API function makes it possible to retrieve the X.509 certificate
used by clients to authenticate TLS connections.

(cherry picked from commit 0aec98dce2)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 28d1fe6718 Modules: Add RM_GetDetachedThreadSafeContext(). (#7886)
The main motivation here is to provide a way for modules to create a
single, global context that can be used for logging.

Currently, it is possible to obtain a thread-safe context that is not
attached to any blocked client by using `RM_GetThreadSafeContext`.
However, the attached context is not linked to the module identity so
log messages produced are not tagged with the module name.

Ideally we'd fix this in `RM_GetThreadSafeContext` itself but as it
doesn't accept the current context as an argument there's no way to do
that in a backwards compatible manner.

(cherry picked from commit 907da0580b)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 116a2042a7 Modules: add RM_GetCommandKeys().
This is essentially the same as calling COMMAND GETKEYS but provides a
more efficient interface that can be used in every context (i.e. not a
Redis command).

(cherry picked from commit 7d117d7591)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 945983ac52 Introduce getKeysResult for getKeysFromCommand.
Avoid using a static buffer for short key index responses, and make it
caller's responsibility to stack-allocate a result type. Responses that
don't fit are still allocated on the heap.

(cherry picked from commit 9b7f8ba84b)
2020-10-27 09:12:01 +02:00
Madelyn Olson e7c8002b56 Fixed excessive categories being displayed from acls (#7889)
(cherry picked from commit 2127f7c8eb)
2020-10-27 09:12:01 +02:00
Oran Agra 86b5e1a72f Add some additional signal info to the crash log (#7891)
- si_code can be very useful info some day.
- a clear indication that redis was killed by an external user

(cherry picked from commit f659d23619)
2020-10-27 09:12:01 +02:00
Oran Agra bab834574c Allow blocked XREAD on a cluster replica (#7881)
I suppose that it was overlooked, since till recently none of the blocked commands were readonly.

other changes:
- add test for the above.
- add better support for additional (and deferring) clients for
  cluster tests
- improve a test which left the client in MULTI state.

(cherry picked from commit 216c110609)
2020-10-27 09:12:01 +02:00
Oran Agra 76f3a63de1 memory reporting of clients argv (#7874)
track and report memory used by clients argv.
this is very usaful in case clients started sending a command and didn't
complete it. in which case the first args of the command are already
trimmed from the query buffer.

in an effort to avoid cache misses and overheads while keeping track of
these, i avoid calling sdsZmallocSize and instead use the sdslen /
bulk-len which can at least give some insight into the problem.

This memory is now added to the total clients memory usage, as well as
the client list.

(cherry picked from commit bea40e6a41)
2020-10-27 09:12:01 +02:00
DvirDukhan 93b4c6b31e redis-cli add control on raw format line delimiter (#7841)
Adding -D option for redis-cli to control newline between command
responses in raw mode.

Also removing cleanup code before calling exit, just in order
to avoid adding more adding more cleanup code (redis doesn't
bother to release allocations before exit anyway)

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 6418d767e8)
2020-10-27 09:12:01 +02:00
Oran Agra 12055ed8c2 Include internal sds fragmentation in MEMORY reporting (#7864)
The MEMORY command is used for debugging memory usage, so it should include internal
fragmentation, same as used_memory

(cherry picked from commit eb6241a3dd)
2020-10-27 09:12:01 +02:00
Oran Agra c80f6219d5 Fix crash in script timeout during AOF loading (#7870)
(cherry picked from commit dc803d25a6)
2020-10-27 09:12:01 +02:00
Rafi Einstein 801c7307fb Makefile: enable program suffixes via PROG_SUFFIX (#7868)
(cherry picked from commit b8187d39fb)
2020-10-27 09:12:01 +02:00
nitaicaro a7b95b7c72 Fixed Tracking test “The other connection is able to get invalidations” (#7871)
PROBLEM:

[$rd1 read] reads invalidation messages one by one, so it's never going to see the second invalidation message produced after INCR b, whether or not it exists. Adding another read will block incase no invalidation message is produced.

FIX:

We switch the order of "INCR a" and "INCR b" - now "INCR b" comes first. We still only read the first invalidation message produces. If an invalidation message is wrongly produces for b - then it will be produced before that of a, since "INCR b" comes before "INCR a".

Co-authored-by: Nitai Caro <caronita@amazon.com>
(cherry picked from commit 8fb89a5728)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 919580a228 Modules: expose real client on conn events.
When REDISMODULE_EVENT_CLIENT_CHANGE events are delivered, modules may
want to mutate the client state (e.g. perform authentication).

This change links the module context with the real client rather than a
fake client for these events.

(cherry picked from commit 67b43f75e2)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb e83d6b0cfa Module API: Fail ineffective auth calls.
The client pointed to by the module context may in some cases be a fake
client. RM_Authenticate*() calls in this case would be ineffective but
appear to succeed, and this change fails them to make it easier to catch
such cases.

(cherry picked from commit cfccfbd6f4)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb cb070accb4 TLS: Do not require CA config if not used. (#7862)
The tls-ca-cert or tls-ca-cert-dir configuration parameters are only
used when Redis needs to authenticate peer certificates, in one of these
scenarios:

1. Incoming clients or replicas, with `tls-auth-clients` enabled.
2. A replica authenticating the master's peer certificate.
3. Cluster nodes authenticating other nodes when establishing the bus
   protocol connection.

(cherry picked from commit 1591e3479d)
2020-10-27 09:12:01 +02:00
Oran Agra 9ce6fc7322 warning: comparison between signed and unsigned integer in 32bit build (#7838)
(cherry picked from commit ef33252c43)
2020-10-27 09:12:01 +02:00
David CARLIER a7b9cfbe98 Add support for Haiku OS (#7435)
(cherry picked from commit f971a5d8ed)
2020-10-27 09:12:01 +02:00
Gavrie Philipson 219bab2d85 Fix typo in module API docs (#7861)
(cherry picked from commit 49383a4af7)
2020-10-27 09:12:01 +02:00
David CARLIER d51bebd4d7 getting rss size implementation for netbsd (#7293)
(cherry picked from commit ce8bfc56ad)
2020-10-27 09:12:01 +02:00
Oran Agra 120c6b43e1 Fix new obuf-limits tests to work with TLS (#7848)
Also stabilize new shutdown tests on slow machines (valgrind)

(cherry picked from commit 8aa083bd28)
2020-10-27 09:12:01 +02:00
caozb 63075b81e6 ignore slaveof no one in redis.conf (#7842)
when slaveof config is "no one", reset any pre-existing config and resume.

also solve a memory leak if slaveof appears twice.
and fail loading if port number is out of range or not an integer.

Co-authored-by: caozhengbin <caozb@yidingyun.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit a295770e32)
2020-10-27 09:12:01 +02:00
Wang Yuan 9418083770 Don't support Gopher if enable io threads to read queries (#7851)
There's currently an issue with IO threads and gopher (issuing lookupKey from within the thread).
simply fix is to just not support it for now.

(cherry picked from commit c9f00bcce2)
2020-10-27 09:12:01 +02:00
Wang Yuan 5d1ac2df64 Set 'loading' and 'shutdown_asap' to volatile sig_atomic_t type (#7845)
We may access and modify these two variables in signal handler function,
to guarantee them async-signal-safe, so we should set them to volatile
sig_atomic_t type.

It doesn't look like this could have caused any real issue, and it seems that
signals are handled in main thread on most platforms. But we want to follow C
and POSIX standard in signal handler function.

(cherry picked from commit f1863a1fe7)
2020-10-27 09:12:01 +02:00
Uri Shachar 90555566ed Fix config rewrite file handling to make it really atomic (#7824)
Make sure we handle short writes correctly, sync to disk after writing  and use
rename to make sure the replacement is actually atomic.
In any case of failure old configuration will remain in place.

Also, add some additional logging to make it easier to diagnose rewrite problems.

(cherry picked from commit c30bd02c9d)
2020-10-27 09:12:01 +02:00
WuYunlong bb7e00b699 Add fsync to readSyncBulkPayload(). (#7839)
We should sync temp DB file before renaming as rdb_fsync_range does not use
flag `SYNC_FILE_RANGE_WAIT_AFTER`.

Refer to `Linux Programmer's Manual`:
SYNC_FILE_RANGE_WAIT_AFTER
    Wait upon write-out of all pages in the range after performing any write.

(cherry picked from commit 0d62caab21)
2020-10-27 09:12:01 +02:00
Wen Hui faa431c98d rdb.c: handle fclose error case differently to avoid double fclose (#7307)
When fclose would fail, the previous implementation would have attempted to do fclose again
this can in theory lead to segfault.

other changes:
check for non-zero return value as failure rather than a specific error code.
this doesn't fix a real bug, just a minor cleanup.

(cherry picked from commit 323029baa6)
2020-10-27 09:12:01 +02:00
Wang Yuan 757ad7cdf2 Don't write replies if close the client ASAP (#7202)
Before this commit, we would have continued to add replies to the reply buffer even if client
output buffer limit is reached, so the used memory would keep increasing over the configured limit.
What's more, we shouldn’t write any reply to the client if it is set 'CLIENT_CLOSE_ASAP' flag
because that doesn't conform to its definition and we will close all clients flagged with
'CLIENT_CLOSE_ASAP' in ‘beforeSleep’.

Because of code execution order, before this, we may firstly write to part of the replies to
the socket before disconnecting it, but in fact, we may can’t send the full replies to clients
since OS socket buffer is limited. But this unexpected behavior makes some commands work well,
for instance ACL DELUSER, if the client deletes the current user, we need to send reply to client
and close the connection, but before, we close the client firstly and write the reply to reply
buffer. secondly, we shouldn't do this despite the fact it works well in most cases.

We add a flag 'CLIENT_CLOSE_AFTER_COMMAND' to mark clients, this flag means we will close the
client after executing commands and send all entire replies, so that we can write replies to
reply buffer during executing commands, send replies to clients, and close them later.

We also fix some implicit problems. If client output buffer limit is enforced in 'multi/exec',
all commands will be executed completely in redis and clients will not read any reply instead of
partial replies. Even more, if the client executes 'ACL deluser' the using user in 'multi/exec',
it will not read the replies after 'ACL deluser' just like before executing 'client kill' itself
in 'multi/exec'.

We added some tests for output buffer limit breach during multi-exec and using a pipeline of
many small commands rather than one with big response.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 57709c4bc6)
2020-10-27 09:12:01 +02:00
Guy Korland 31491202aa Fix RedisModule_HashGet examples (#6697)
(cherry picked from commit b464afb9e2)
2020-10-27 09:12:01 +02:00
Oran Agra 26c1c60187 fix recently broken TLS build error, and add coverage for CI (#7833)
(cherry picked from commit a735bf5c2a)
2020-10-27 09:12:01 +02:00
David CARLIER 2ccbba650c Further NetBSD update and build fixes. (#7831)
mainly backtrace and register dump support.

(cherry picked from commit c3edaa7941)
2020-10-27 09:12:01 +02:00
WuYunlong c73406c2bd Fix redundancy use of semicolon in do-while macros in ziplist.c. (#7832)
this is very dangerous bug, but it looks like it didn't cause any harm.

(cherry picked from commit 63cd4d4e20)
2020-10-27 09:12:01 +02:00
yixiang c1423c016c Fix connGetSocketError usage (#7811)
(cherry picked from commit b96c3595af)
2020-10-27 09:12:01 +02:00
Oran Agra ecf51b0b3d RM_GetContextFlags - document missing flags (#7821)
(cherry picked from commit 26ca04a8a2)
2020-10-27 09:12:01 +02:00
Yossi Gottlieb 9d0388a043 Fix occasional hangs on replication reconnection. (#7830)
This happens only on diskless replicas when attempting to reconnect after 
failing to load an RDB file. It is more likely to occur with larger datasets.

After reconnection is initiated, replicationEmptyDbCallback() may get called 
and try to write to an unconnected socket. This triggered another issue where
the connection is put into an error state and the connect handler never gets
called. The problem is a regression introduced by commit c17e597.

(cherry picked from commit 1980f639b1)
2020-10-27 09:12:01 +02:00
Ariel Shtul 6a4da4958e Fix redis-check-rdb support for modules aux data (#7826)
redis-check-rdb was unable to parse rdb files containing module aux data.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 63a05dde46)
2020-10-27 09:12:01 +02:00
Wen Hui e8f772efff refactor rewriteStreamObject code for adding missing streamIteratorStop call (#7829)
This commit adds streamIteratorStop call in rewriteStreamObject function in some of the return statement. Although currently this will not cause memory leak since stream id is only 16 bytes long.


(cherry picked from commit 23b50bcccc)
2020-10-27 09:12:01 +02:00
WuYunlong 59866fcd30 Make IO threads killable so that they can be canceled at any time.
This commit can be cherry picked to 6.0 only if we also cherry pick f866023.

(cherry picked from commit c37acb82aa)
2020-10-27 09:12:01 +02:00
WuYunlong 4832cf4fd6 Make main thread killable so that it can be canceled at any time.
Refine comment of makeThreadKillable().

This commit can be backported to 5.0, only if we also backport 8b70cb0.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 647cac5bb4)
2020-10-27 09:12:01 +02:00
Oran Agra fd610ae025 RM_GetContextFlags provides indication that we're in a fork child (#7783)
(cherry picked from commit 2458e54814)
2020-10-27 09:12:01 +02:00
Wen Hui 88441019a5 Add Swapdb Module Event (#7804)
(cherry picked from commit dfe9714c86)
2020-10-27 09:12:01 +02:00
Daniel Dai aa1feec746 fix make warnings in debug.c MacOS (#7805)
Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 1b3b75208c)
2020-10-27 09:12:01 +02:00
David CARLIER e5e353c81d debug.c: NetBSD build warning fix. (#7810)
The symbol base address is a const on this system.

(cherry picked from commit eabe3eaec0)
2020-10-27 09:12:01 +02:00
Wang Yuan 0bdddd3c89 Remove tmp rdb file in background thread (#7762)
We're already using bg_unlink in several places to delete the rdb file in the background,
and avoid paying the cost of the deletion from our main thread.
This commit uses bg_unlink to remove the temporary rdb file in the background too.

However, in case we delete that rdb file just before exiting, we don't actually wait for the
background thread or the main thread to delete it, and just let the OS clean up after us.
i.e. we open the file, unlink it and exit with the fd still open.

Furthermore, rdbRemoveTempFile can be called from a thread and was using snprintf which is
not async-signal-safe, we now use ll2string instead.

(cherry picked from commit b002d2b4f1)
2020-10-27 09:12:01 +02:00
Oran Agra 7bc0726016 Add printf attribute and fix warnings and a minor bug (#7803)
The fix in error handling of rdbGenericLoadStringObject is an actual bugfix

(cherry picked from commit 092cfca522)
2020-10-27 09:12:01 +02:00
WuYunlong ea4bf91abc bio: doFastMemoryTest should try to kill io threads as well.
(cherry picked from commit f866023399)
2020-10-27 09:12:01 +02:00
WuYunlong 5d9332266d bio: fix doFastMemoryTest.
If one thread got SIGSEGV, function sigsegvHandler() would be triggered,
it would call bioKillThreads(). But call pthread_cancel() to cancel itself
would make it block. Also note that if SIGSEGV is caught by bio thread, it
should kill the main thread in order to give a positive report.

(cherry picked from commit 8b70cb0ef8)
2020-10-27 09:12:01 +02:00
Wen Hui 7601ed344c correct OBJECT ENCODING response for stream type (#7797)
This commit makes stream object returning "stream" as encoding type in OBJECT ENCODING subcommand and DEBUG OBJECT command.

Till now, it would return "unknown"

(cherry picked from commit 6ff741b5cf)
2020-10-27 09:12:01 +02:00
WuYunlong 405f041e90 Clarify help text of tcl scripts. (#7798)
Before this commit, following command did not show --tls option:
./runtest-cluster --help
./runtest-sentinel --help

(cherry picked from commit 98c8ac0d70)
2020-10-27 09:12:01 +02:00
Mykhailo Pylyp aa6b869c15 Recalculate hardcoded variables from $::instances_count in sentinel tests (#7561)
Co-authored-by: MemuraiUser <githubuser@janeasystems.com>
(cherry picked from commit cb2c7c4d34)
2020-10-27 09:12:01 +02:00
Oran Agra 00a767db38 Fix failing valgrind installation in github actions (#7792)
These tests started failing every day on http 404 (not being able to
install valgrind)

(cherry picked from commit 78a6e5eb2b)
2020-10-27 09:12:01 +02:00
Oran Agra 5106a144f0 fix broken PEXPIREAT test (#7791)
This test was nearly always failing on MacOS github actions.
This is because of bugs in the test that caused it to nearly always run
all 3 attempts and just look at the last one as the pass/fail creteria.

i.e. the test was nearly always running all 3 attempts and still sometimes
succeed. this is because the break condition was different than the test
completion condition.

The reason the test succeeded is because the break condition tested the
results of all 3 tests (PSETEX/PEXPIRE/PEXPIREAT), but the success check
at the end was only testing the result of PSETEX.

The reason the PEXPIREAT test nearly always failed is because it was
getting the current time wrong: getting the current second and loosing
the sub-section time, so the only chance for it to succeed is if it run
right when a certain second started.

Because i now get the time from redis, adding another round trip, i
added another 100ms to the PEXPIRE test to make it less fragile, and
also added many more attempts.

Adding many more attempts before failure to account for slow platforms,
github actions and valgrind

(cherry picked from commit ed9bfe2262)
2020-10-27 09:12:01 +02:00
Wang Yuan 3efdbbc79c Remove dead global variable 'lru_clock' (#7782)
(cherry picked from commit 86511bbbef)
2020-10-27 09:12:01 +02:00
Oran Agra 2ae7f49115 Squash merging 125 typo/grammar/comment/doc PRs (#7773)
List of squashed commits or PRs
===============================

commit 66801ea
Author: hwware <wen.hui.ware@gmail.com>
Date:   Mon Jan 13 00:54:31 2020 -0500

    typo fix in acl.c

commit 46f55db
Author: Itamar Haber <itamar@redislabs.com>
Date:   Sun Sep 6 18:24:11 2020 +0300

    Updates a couple of comments

    Specifically:

    * RM_AutoMemory completed instead of pointing to docs
    * Updated link to custom type doc

commit 61a2aa0
Author: xindoo <xindoo@qq.com>
Date:   Tue Sep 1 19:24:59 2020 +0800

    Correct errors in code comments

commit a5871d1
Author: yz1509 <pro-756@qq.com>
Date:   Tue Sep 1 18:36:06 2020 +0800

    fix typos in module.c

commit 41eede7
Author: bookug <bookug@qq.com>
Date:   Sat Aug 15 01:11:33 2020 +0800

    docs: fix typos in comments

commit c303c84
Author: lazy-snail <ws.niu@outlook.com>
Date:   Fri Aug 7 11:15:44 2020 +0800

    fix spelling in redis.conf

commit 1eb76bf
Author: zhujian <zhujianxyz@gmail.com>
Date:   Thu Aug 6 15:22:10 2020 +0800

    add a missing 'n' in comment

commit 1530ec2
Author: Daniel Dai <764122422@qq.com>
Date:   Mon Jul 27 00:46:35 2020 -0400

    fix spelling in tracking.c

commit e517b31
Author: Hunter-Chen <huntcool001@gmail.com>
Date:   Fri Jul 17 22:33:32 2020 +0800

    Update redis.conf

    Co-authored-by: Itamar Haber <itamar@redislabs.com>

commit c300eff
Author: Hunter-Chen <huntcool001@gmail.com>
Date:   Fri Jul 17 22:33:23 2020 +0800

    Update redis.conf

    Co-authored-by: Itamar Haber <itamar@redislabs.com>

commit 4c058a8
Author: 陈浩鹏 <chenhaopeng@heytea.com>
Date:   Thu Jun 25 19:00:56 2020 +0800

    Grammar fix and clarification

commit 5fcaa81
Author: bodong.ybd <bodong.ybd@alibaba-inc.com>
Date:   Fri Jun 19 10:09:00 2020 +0800

    Fix typos

commit 4caca9a
Author: Pruthvi P <pruthvi@ixigo.com>
Date:   Fri May 22 00:33:22 2020 +0530

    Fix typo eviciton => eviction

commit b2a25f6
Author: Brad Dunbar <dunbarb2@gmail.com>
Date:   Sun May 17 12:39:59 2020 -0400

    Fix a typo.

commit 12842ae
Author: hwware <wen.hui.ware@gmail.com>
Date:   Sun May 3 17:16:59 2020 -0400

    fix spelling in redis conf

commit ddba07c
Author: Chris Lamb <chris@chris-lamb.co.uk>
Date:   Sat May 2 23:25:34 2020 +0100

    Correct a "conflicts" spelling error.

commit 8fc7bf2
Author: Nao YONASHIRO <yonashiro@r.recruit.co.jp>
Date:   Thu Apr 30 10:25:27 2020 +0900

    docs: fix EXPIRE_FAST_CYCLE_DURATION to ACTIVE_EXPIRE_CYCLE_FAST_DURATION

commit 9b2b67a
Author: Brad Dunbar <dunbarb2@gmail.com>
Date:   Fri Apr 24 11:46:22 2020 -0400

    Fix a typo.

commit 0746f10
Author: devilinrust <63737265+devilinrust@users.noreply.github.com>
Date:   Thu Apr 16 00:17:53 2020 +0200

    Fix typos in server.c

commit 92b588d
Author: benjessop12 <56115861+benjessop12@users.noreply.github.com>
Date:   Mon Apr 13 13:43:55 2020 +0100

    Fix spelling mistake in lazyfree.c

commit 1da37aa
Merge: 2d4ba28 af347a8
Author: hwware <wen.hui.ware@gmail.com>
Date:   Thu Mar 5 22:41:31 2020 -0500

    Merge remote-tracking branch 'upstream/unstable' into expiretypofix

commit 2d4ba28
Author: hwware <wen.hui.ware@gmail.com>
Date:   Mon Mar 2 00:09:40 2020 -0500

    fix typo in expire.c

commit 1a746f7
Author: SennoYuki <minakami1yuki@gmail.com>
Date:   Thu Feb 27 16:54:32 2020 +0800

    fix typo

commit 8599b1a
Author: dongheejeong <donghee950403@gmail.com>
Date:   Sun Feb 16 20:31:43 2020 +0000

    Fix typo in server.c

commit f38d4e8
Author: hwware <wen.hui.ware@gmail.com>
Date:   Sun Feb 2 22:58:38 2020 -0500

    fix typo in evict.c

commit fe143fc
Author: Leo Murillo <leonardo.murillo@gmail.com>
Date:   Sun Feb 2 01:57:22 2020 -0600

    Fix a few typos in redis.conf

commit 1ab4d21
Author: viraja1 <anchan.viraj@gmail.com>
Date:   Fri Dec 27 17:15:58 2019 +0530

    Fix typo in Latency API docstring

commit ca1f70e
Author: gosth <danxuedexing@qq.com>
Date:   Wed Dec 18 15:18:02 2019 +0800

    fix typo in sort.c

commit a57c06b
Author: ZYunH <zyunhjob@163.com>
Date:   Mon Dec 16 22:28:46 2019 +0800

    fix-zset-typo

commit b8c92b5
Author: git-hulk <hulk.website@gmail.com>
Date:   Mon Dec 16 15:51:42 2019 +0800

    FIX: typo in cluster.c, onformation->information

commit 9dd981c
Author: wujm2007 <jim.wujm@gmail.com>
Date:   Mon Dec 16 09:37:52 2019 +0800

    Fix typo

commit e132d7a
Author: Sebastien Williams-Wynn <s.williamswynn.mail@gmail.com>
Date:   Fri Nov 15 00:14:07 2019 +0000

    Minor typo change

commit 47f44d5
Author: happynote3966 <01ssrmikururudevice01@gmail.com>
Date:   Mon Nov 11 22:08:48 2019 +0900

    fix comment typo in redis-cli.c

commit b8bdb0d
Author: fulei <fulei@kuaishou.com>
Date:   Wed Oct 16 18:00:17 2019 +0800

    Fix a spelling mistake of comments  in defragDictBucketCallback

commit 0def46a
Author: fulei <fulei@kuaishou.com>
Date:   Wed Oct 16 13:09:27 2019 +0800

    fix some spelling mistakes of comments in defrag.c

commit f3596fd
Author: Phil Rajchgot <tophil@outlook.com>
Date:   Sun Oct 13 02:02:32 2019 -0400

    Typo and grammar fixes

    Redis and its documentation are great -- just wanted to submit a few corrections in the spirit of Hacktoberfest. Thanks for all your work on this project. I use it all the time and it works beautifully.

commit 2b928cd
Author: KangZhiDong <worldkzd@gmail.com>
Date:   Sun Sep 1 07:03:11 2019 +0800

    fix typos

commit 33aea14
Author: Axlgrep <axlgrep@gmail.com>
Date:   Tue Aug 27 11:02:18 2019 +0800

    Fixed eviction spelling issues

commit e282a80
Author: Simen Flatby <simen@oms.no>
Date:   Tue Aug 20 15:25:51 2019 +0200

    Update comments to reflect prop name

    In the comments the prop is referenced as replica-validity-factor,
    but it is really named cluster-replica-validity-factor.

commit 74d1f9a
Author: Jim Green <jimgreen2013@qq.com>
Date:   Tue Aug 20 20:00:31 2019 +0800

    fix comment error, the code is ok

commit eea1407
Author: Liao Tonglang <liaotonglang@gmail.com>
Date:   Fri May 31 10:16:18 2019 +0800

    typo fix

    fix cna't to can't

commit 0da553c
Author: KAWACHI Takashi <tkawachi@gmail.com>
Date:   Wed Jul 17 00:38:16 2019 +0900

    Fix typo

commit 7fc8fb6
Author: Michael Prokop <mika@grml.org>
Date:   Tue May 28 17:58:42 2019 +0200

    Typo fixes

    s/familar/familiar/
    s/compatiblity/compatibility/
    s/ ot / to /
    s/itsef/itself/

commit 5f46c9d
Author: zhumoing <34539422+zhumoing@users.noreply.github.com>
Date:   Tue May 21 21:16:50 2019 +0800

    typo-fixes

    typo-fixes

commit 321dfe1
Author: wxisme <850885154@qq.com>
Date:   Sat Mar 16 15:10:55 2019 +0800

    typo fix

commit b4fb131
Merge: 267e0e6 3df1eb8
Author: Nikitas Bastas <nikitasbst@gmail.com>
Date:   Fri Feb 8 22:55:45 2019 +0200

    Merge branch 'unstable' of antirez/redis into unstable

commit 267e0e6
Author: Nikitas Bastas <nikitasbst@gmail.com>
Date:   Wed Jan 30 21:26:04 2019 +0200

    Minor typo fix

commit 30544e7
Author: inshal96 <39904558+inshal96@users.noreply.github.com>
Date:   Fri Jan 4 16:54:50 2019 +0500

    remove an extra 'a' in the comments

commit 337969d
Author: BrotherGao <yangdongheng11@gmail.com>
Date:   Sat Dec 29 12:37:29 2018 +0800

    fix typo in redis.conf

commit 9f4b121
Merge: 423a030 e504583
Author: BrotherGao <yangdongheng@xiaomi.com>
Date:   Sat Dec 29 11:41:12 2018 +0800

    Merge branch 'unstable' of antirez/redis into unstable

commit 423a030
Merge: 42b02b7 46a51cd
Author: 杨东衡 <yangdongheng@xiaomi.com>
Date:   Tue Dec 4 23:56:11 2018 +0800

    Merge branch 'unstable' of antirez/redis into unstable

commit 42b02b7
Merge: 68c0e6e b8febe6
Author: Dongheng Yang <yangdongheng11@gmail.com>
Date:   Sun Oct 28 15:54:23 2018 +0800

    Merge pull request #1 from antirez/unstable

    update local data

commit 714b589
Author: Christian <crifei93@gmail.com>
Date:   Fri Dec 28 01:17:26 2018 +0100

    fix typo "resulution"

commit e23259d
Author: garenchan <1412950785@qq.com>
Date:   Wed Dec 26 09:58:35 2018 +0800

    fix typo: segfauls -> segfault

commit a9359f8
Author: xjp <jianping_xie@aliyun.com>
Date:   Tue Dec 18 17:31:44 2018 +0800

    Fixed REDISMODULE_H spell bug

commit a12c3e4
Author: jdiaz <jrd.palacios@gmail.com>
Date:   Sat Dec 15 23:39:52 2018 -0600

    Fixes hyperloglog hash function comment block description

commit 770eb11
Author: 林上耀 <1210tom@163.com>
Date:   Sun Nov 25 17:16:10 2018 +0800

    fix typo

commit fd97fbb
Author: Chris Lamb <chris@chris-lamb.co.uk>
Date:   Fri Nov 23 17:14:01 2018 +0100

    Correct "unsupported" typo.

commit a85522d
Author: Jungnam Lee <jungnam.lee@oracle.com>
Date:   Thu Nov 8 23:01:29 2018 +0900

    fix typo in test comments

commit ade8007
Author: Arun Kumar <palerdot@users.noreply.github.com>
Date:   Tue Oct 23 16:56:35 2018 +0530

    Fixed grammatical typo

    Fixed typo for word 'dictionary'

commit 869ee39
Author: Hamid Alaei <hamid.a85@gmail.com>
Date:   Sun Aug 12 16:40:02 2018 +0430

    fix documentations: (ThreadSafeContextStart/Stop -> ThreadSafeContextLock/Unlock), minor typo

commit f89d158
Author: Mayank Jain <mayankjain255@gmail.com>
Date:   Tue Jul 31 23:01:21 2018 +0530

    Updated README.md with some spelling corrections.

    Made correction in spelling of some misspelled words.

commit 892198e
Author: dsomeshwar <someshwar.dhayalan@gmail.com>
Date:   Sat Jul 21 23:23:04 2018 +0530

    typo fix

commit 8a4d780
Author: Itamar Haber <itamar@redislabs.com>
Date:   Mon Apr 30 02:06:52 2018 +0300

    Fixes some typos

commit e3acef6
Author: Noah Rosamilia <ivoahivoah@gmail.com>
Date:   Sat Mar 3 23:41:21 2018 -0500

    Fix typo in /deps/README.md

commit 04442fb
Author: WuYunlong <xzsyeb@126.com>
Date:   Sat Mar 3 10:32:42 2018 +0800

    Fix typo in readSyncBulkPayload() comment.

commit 9f36880
Author: WuYunlong <xzsyeb@126.com>
Date:   Sat Mar 3 10:20:37 2018 +0800

    replication.c comment: run_id -> replid.

commit f866b4a
Author: Francesco 'makevoid' Canessa <makevoid@gmail.com>
Date:   Thu Feb 22 22:01:56 2018 +0000

    fix comment typo in server.c

commit 0ebc69b
Author: 줍 <jubee0124@gmail.com>
Date:   Mon Feb 12 16:38:48 2018 +0900

    Fix typo in redis.conf

    Fix `five behaviors` to `eight behaviors` in [this sentence ](antirez/redis@unstable/redis.conf#L564)

commit b50a620
Author: martinbroadhurst <martinbroadhurst@users.noreply.github.com>
Date:   Thu Dec 28 12:07:30 2017 +0000

    Fix typo in valgrind.sup

commit 7d8f349
Author: Peter Boughton <peter@sorcerersisle.com>
Date:   Mon Nov 27 19:52:19 2017 +0000

    Update CONTRIBUTING; refer doc updates to redis-doc repo.

commit 02dec7e
Author: Klauswk <klauswk1@hotmail.com>
Date:   Tue Oct 24 16:18:38 2017 -0200

    Fix typo in comment

commit e1efbc8
Author: chenshi <baiwfg2@gmail.com>
Date:   Tue Oct 3 18:26:30 2017 +0800

    Correct two spelling errors of comments

commit 93327d8
Author: spacewander <spacewanderlzx@gmail.com>
Date:   Wed Sep 13 16:47:24 2017 +0800

    Update the comment for OBJ_ENCODING_EMBSTR_SIZE_LIMIT's value

    The value of OBJ_ENCODING_EMBSTR_SIZE_LIMIT is 44 now instead of 39.

commit 63d361f
Author: spacewander <spacewanderlzx@gmail.com>
Date:   Tue Sep 12 15:06:42 2017 +0800

    Fix <prevlen> related doc in ziplist.c

    According to the definition of ZIP_BIG_PREVLEN and other related code,
    the guard of single byte <prevlen> should be 254 instead of 255.

commit ebe228d
Author: hanael80 <hanael80@gmail.com>
Date:   Tue Aug 15 09:09:40 2017 +0900

    Fix typo

commit 6b696e6
Author: Matt Robenolt <matt@ydekproductions.com>
Date:   Mon Aug 14 14:50:47 2017 -0700

    Fix typo in LATENCY DOCTOR output

commit a2ec6ae
Author: caosiyang <caosiyang@qiyi.com>
Date:   Tue Aug 15 14:15:16 2017 +0800

    Fix a typo: form => from

commit 3ab7699
Author: caosiyang <caosiyang@qiyi.com>
Date:   Thu Aug 10 18:40:33 2017 +0800

    Fix a typo: replicationFeedSlavesFromMaster() => replicationFeedSlavesFromMasterStream()

commit 72d43ef
Author: caosiyang <caosiyang@qiyi.com>
Date:   Tue Aug 8 15:57:25 2017 +0800

    fix a typo: servewr => server

commit 707c958
Author: Bo Cai <charpty@gmail.com>
Date:   Wed Jul 26 21:49:42 2017 +0800

    redis-cli.c typo: conut -> count.

    Signed-off-by: Bo Cai <charpty@gmail.com>

commit b9385b2
Author: JackDrogon <jack.xsuperman@gmail.com>
Date:   Fri Jun 30 14:22:31 2017 +0800

    Fix some spell problems

commit 20d9230
Author: akosel <aaronjkosel@gmail.com>
Date:   Sun Jun 4 19:35:13 2017 -0500

    Fix typo

commit b167bfc
Author: Krzysiek Witkowicz <krzysiekwitkowicz@gmail.com>
Date:   Mon May 22 21:32:27 2017 +0100

    Fix #4008 small typo in comment

commit 2b78ac8
Author: Jake Clarkson <jacobwclarkson@gmail.com>
Date:   Wed Apr 26 15:49:50 2017 +0100

    Correct typo in tests/unit/hyperloglog.tcl

commit b0f1cdb
Author: Qi Luo <qiluo-msft@users.noreply.github.com>
Date:   Wed Apr 19 14:25:18 2017 -0700

    Fix typo

commit a90b0f9
Author: charsyam <charsyam@naver.com>
Date:   Thu Mar 16 18:19:53 2017 +0900

    fix typos

    fix typos

    fix typos

commit 8430a79
Author: Richard Hart <richardhart92@gmail.com>
Date:   Mon Mar 13 22:17:41 2017 -0400

    Fixed log message typo in listenToPort.

commit 481a1c2
Author: Vinod Kumar <kumar003vinod@gmail.com>
Date:   Sun Jan 15 23:04:51 2017 +0530

    src/db.c: Correct "save" -> "safe" typo

commit 586b4d3
Author: wangshaonan <wshn13@gmail.com>
Date:   Wed Dec 21 20:28:27 2016 +0800

    Fix typo they->the in helloworld.c

commit c1c4b5e
Author: Jenner <hypxm@qq.com>
Date:   Mon Dec 19 16:39:46 2016 +0800

    typo error

commit 1ee1a3f
Author: tielei <43289893@qq.com>
Date:   Mon Jul 18 13:52:25 2016 +0800

    fix some comments

commit 11a41fb
Author: Otto Kekäläinen <otto@seravo.fi>
Date:   Sun Jul 3 10:23:55 2016 +0100

    Fix spelling in documentation and comments

commit 5fb5d82
Author: francischan <f1ancis621@gmail.com>
Date:   Tue Jun 28 00:19:33 2016 +0800

    Fix outdated comments about redis.c file.
    It should now refer to server.c file.

commit 6b254bc
Author: lmatt-bit <lmatt123n@gmail.com>
Date:   Thu Apr 21 21:45:58 2016 +0800

    Refine the comment of dictRehashMilliseconds func

SLAVECONF->REPLCONF in comment - by andyli029

commit ee9869f
Author: clark.kang <charsyam@naver.com>
Date:   Tue Mar 22 11:09:51 2016 +0900

    fix typos

commit f7b3b11
Author: Harisankar H <harisankarh@gmail.com>
Date:   Wed Mar 9 11:49:42 2016 +0530

    Typo correction: "faield" --> "failed"

    Typo correction: "faield" --> "failed"

commit 3fd40fc
Author: Itamar Haber <itamar@redislabs.com>
Date:   Thu Feb 25 10:31:51 2016 +0200

    Fixes a typo in comments

commit 621c160
Author: Prayag Verma <prayag.verma@gmail.com>
Date:   Mon Feb 1 12:36:20 2016 +0530

    Fix typo in Readme.md

    Spelling mistakes -
    `eviciton` > `eviction`
    `familar` > `familiar`

commit d7d07d6
Author: WonCheol Lee <toctoc21c@gmail.com>
Date:   Wed Dec 30 15:11:34 2015 +0900

    Typo fixed

commit a4dade7
Author: Felix Bünemann <buenemann@louis.info>
Date:   Mon Dec 28 11:02:55 2015 +0100

    [ci skip] Improve supervised upstart config docs

    This mentions that "expect stop" is required for supervised upstart
    to work correctly. See http://upstart.ubuntu.com/cookbook/#expect-stop
    for an explanation.

commit d9caba9
Author: daurnimator <quae@daurnimator.com>
Date:   Mon Dec 21 18:30:03 2015 +1100

    README: Remove trailing whitespace

commit 72d42e5
Author: daurnimator <quae@daurnimator.com>
Date:   Mon Dec 21 18:29:32 2015 +1100

    README: Fix typo. th => the

commit dd6e957
Author: daurnimator <quae@daurnimator.com>
Date:   Mon Dec 21 18:29:20 2015 +1100

    README: Fix typo. familar => familiar

commit 3a12b23
Author: daurnimator <quae@daurnimator.com>
Date:   Mon Dec 21 18:28:54 2015 +1100

    README: Fix typo. eviciton => eviction

commit 2d1d03b
Author: daurnimator <quae@daurnimator.com>
Date:   Mon Dec 21 18:21:45 2015 +1100

    README: Fix typo. sever => server

commit 3973b06
Author: Itamar Haber <itamar@garantiadata.com>
Date:   Sat Dec 19 17:01:20 2015 +0200

    Typo fix

commit 4f2e460
Author: Steve Gao <fu@2token.com>
Date:   Fri Dec 4 10:22:05 2015 +0800

    Update README - fix typos

commit b21667c
Author: binyan <binbin.yan@nokia.com>
Date:   Wed Dec 2 22:48:37 2015 +0800

    delete redundancy color judge in sdscatcolor

commit 88894c7
Author: binyan <binbin.yan@nokia.com>
Date:   Wed Dec 2 22:14:42 2015 +0800

    the example output shoule be HelloWorld

commit 2763470
Author: binyan <binbin.yan@nokia.com>
Date:   Wed Dec 2 17:41:39 2015 +0800

    modify error word keyevente

    Signed-off-by: binyan <binbin.yan@nokia.com>

commit 0847b3d
Author: Bruno Martins <bscmartins@gmail.com>
Date:   Wed Nov 4 11:37:01 2015 +0000

    typo

commit bbb9e9e
Author: dawedawe <dawedawe@gmx.de>
Date:   Fri Mar 27 00:46:41 2015 +0100

    typo: zimap -> zipmap

commit 5ed297e
Author: Axel Advento <badwolf.bloodseeker.rev@gmail.com>
Date:   Tue Mar 3 15:58:29 2015 +0800

    Fix 'salve' typos to 'slave'

commit edec9d6
Author: LudwikJaniuk <ludvig.janiuk@gmail.com>
Date:   Wed Jun 12 14:12:47 2019 +0200

    Update README.md

    Co-Authored-By: Qix <Qix-@users.noreply.github.com>

commit 692a7af
Author: LudwikJaniuk <ludvig.janiuk@gmail.com>
Date:   Tue May 28 14:32:04 2019 +0200

    grammar

commit d962b0a
Author: Nick Frost <nickfrostatx@gmail.com>
Date:   Wed Jul 20 15:17:12 2016 -0700

    Minor grammar fix

commit 24fff01aaccaf5956973ada8c50ceb1462e211c6 (typos)
Author: Chad Miller <chadm@squareup.com>
Date:   Tue Sep 8 13:46:11 2020 -0400

    Fix faulty comment about operation of unlink()

commit 3cd5c1f3326c52aa552ada7ec797c6bb16452355
Author: Kevin <kevin.xgr@gmail.com>
Date:   Wed Nov 20 00:13:50 2019 +0800

    Fix typo in server.c.

From a83af59 Mon Sep 17 00:00:00 2001
From: wuwo <wuwo@wacai.com>
Date: Fri, 17 Mar 2017 20:37:45 +0800
Subject: [PATCH] falure to failure

From c961896 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B7=A6=E6=87=B6?= <veficos@gmail.com>
Date: Sat, 27 May 2017 15:33:04 +0800
Subject: [PATCH] fix typo

From e600ef2 Mon Sep 17 00:00:00 2001
From: "rui.zou" <rui.zou@yunify.com>
Date: Sat, 30 Sep 2017 12:38:15 +0800
Subject: [PATCH] fix a typo

From c7d07fa Mon Sep 17 00:00:00 2001
From: Alexandre Perrin <alex@kaworu.ch>
Date: Thu, 16 Aug 2018 10:35:31 +0200
Subject: [PATCH] deps README.md typo

From b25cb67 Mon Sep 17 00:00:00 2001
From: Guy Korland <gkorland@gmail.com>
Date: Wed, 26 Sep 2018 10:55:37 +0300
Subject: [PATCH 1/2] fix typos in header

From ad28ca6 Mon Sep 17 00:00:00 2001
From: Guy Korland <gkorland@gmail.com>
Date: Wed, 26 Sep 2018 11:02:36 +0300
Subject: [PATCH 2/2] fix typos

commit 34924cdedd8552466fc22c1168d49236cb7ee915
Author: Adrian Lynch <adi_ady_ade@hotmail.com>
Date:   Sat Apr 4 21:59:15 2015 +0100

    Typos fixed

commit fd2a1e7
Author: Jan <jsteemann@users.noreply.github.com>
Date:   Sat Oct 27 19:13:01 2018 +0200

    Fix typos

    Fix typos

commit e14e47c1a234b53b0e103c5f6a1c61481cbcbb02
Author: Andy Lester <andy@petdance.com>
Date:   Fri Aug 2 22:30:07 2019 -0500

    Fix multiple misspellings of "following"

commit 79b948ce2dac6b453fe80995abbcaac04c213d5a
Author: Andy Lester <andy@petdance.com>
Date:   Fri Aug 2 22:24:28 2019 -0500

    Fix misspelling of create-cluster

commit 1fffde52666dc99ab35efbd31071a4c008cb5a71
Author: Andy Lester <andy@petdance.com>
Date:   Wed Jul 31 17:57:56 2019 -0500

    Fix typos

commit 204c9ba9651e9e05fd73936b452b9a30be456cfe
Author: Xiaobo Zhu <xiaobo.zhu@shopee.com>
Date:   Tue Aug 13 22:19:25 2019 +0800

    fix typos

Squashed commit of the following:

commit 1d9aaf8
Author: danmedani <danmedani@gmail.com>
Date:   Sun Aug 2 11:40:26 2015 -0700

README typo fix.

Squashed commit of the following:

commit 32bfa7c
Author: Erik Dubbelboer <erik@dubbelboer.com>
Date:   Mon Jul 6 21:15:08 2015 +0200

Fixed grammer

Squashed commit of the following:

commit b24f69c
Author: Sisir Koppaka <sisir.koppaka@gmail.com>
Date:   Mon Mar 2 22:38:45 2015 -0500

utils/hashtable/rehashing.c: Fix typos

Squashed commit of the following:

commit 4e04082
Author: Erik Dubbelboer <erik@dubbelboer.com>
Date:   Mon Mar 23 08:22:21 2015 +0000

Small config file documentation improvements

Squashed commit of the following:

commit acb8773
Author: ctd1500 <ctd1500@gmail.com>
Date:   Fri May 8 01:52:48 2015 -0700

Typo and grammar fixes in readme

commit 2eb75b6
Author: ctd1500 <ctd1500@gmail.com>
Date:   Fri May 8 01:36:18 2015 -0700

fixed redis.conf comment

Squashed commit of the following:

commit a8249a2
Author: Masahiko Sawada <sawada.mshk@gmail.com>
Date:   Fri Dec 11 11:39:52 2015 +0530

Revise correction of typos.

Squashed commit of the following:

commit 3c02028
Author: zhaojun11 <zhaojun11@jd.com>
Date:   Wed Jan 17 19:05:28 2018 +0800

Fix typos include two code typos in cluster.c and latency.c

Squashed commit of the following:

commit 9dba47c
Author: q191201771 <191201771@qq.com>
Date:   Sat Jan 4 11:31:04 2020 +0800

fix function listCreate comment in adlist.c

Update src/server.c

commit 2c7c2cb536e78dd211b1ac6f7bda00f0f54faaeb
Author: charpty <charpty@gmail.com>
Date:   Tue May 1 23:16:59 2018 +0800

    server.c typo: modules system dictionary type comment

    Signed-off-by: charpty <charpty@gmail.com>

commit a8395323fb63cb59cb3591cb0f0c8edb7c29a680
Author: Itamar Haber <itamar@redislabs.com>
Date:   Sun May 6 00:25:18 2018 +0300

    Updates test_helper.tcl's help with undocumented options

    Specifically:

    * Host
    * Port
    * Client

commit bde6f9ced15755cd6407b4af7d601b030f36d60b
Author: wxisme <850885154@qq.com>
Date:   Wed Aug 8 15:19:19 2018 +0800

    fix comments in deps files

commit 3172474ba991532ab799ee1873439f3402412331
Author: wxisme <850885154@qq.com>
Date:   Wed Aug 8 14:33:49 2018 +0800

    fix some comments

commit 01b6f2b6858b5cf2ce4ad5092d2c746e755f53f0
Author: Thor Juhasz <thor@juhasz.pro>
Date:   Sun Nov 18 14:37:41 2018 +0100

    Minor fixes to comments

    Found some parts a little unclear on a first read, which prompted me to have a better look at the file and fix some minor things I noticed.
    Fixing minor typos and grammar. There are no changes to configuration options.
    These changes are only meant to help the user better understand the explanations to the various configuration options

(cherry picked from commit 1c71038540)
2020-10-27 09:12:01 +02:00
John Sully 3e33fbcd35 Disarm not release D'oh 2020-10-27 06:41:33 +00:00
John Sully 444555d3e4 Active replica test reliability enhancements 2020-10-27 06:23:14 +00:00
John Sully 6a74f524e5 Prevent crashes on shutdown due to lock being held 2020-10-27 04:53:19 +00:00
John Sully 9c5b59ac01 Ensure MVCC timestamp is incremented after the real time is updated 2020-10-27 04:52:35 +00:00
John Sully 1840601f8e Fix multithreaded test failure 2020-10-27 01:54:13 +00:00
John Sully 88962af8b2 Avoid unnecessary memory fences 2020-10-26 02:28:38 +00:00
John Sully df22cdf6e9 Remove addReply*Async methods since we already know if its async or not. This is just a source of bugs 2020-10-24 02:18:03 +00:00
John Sully 041446005b Allow the locker to be used even without a client 2020-10-23 20:21:49 +00:00
John Sully 7c39a9b0e1 Fix multithread test failures 2020-10-23 15:57:39 +00:00
John Sully 05cd92f766 Remove leak warning with ASAN in the CLI 2020-10-21 21:16:08 +00:00
John Sully d724e7226c run when necessary 2020-10-16 07:00:03 +00:00
John Sully 738c782f02 Fix replica buffer overflows 2020-10-16 06:47:40 +00:00
John Sully 33b05c859a Avoid locking if we won't run a time event 2020-10-16 06:19:52 +00:00
John Sully ed369d7223 Reduce async write copies 2020-10-15 23:10:17 +00:00
John Sully fb32bb12b1 Fix branding 2020-10-15 20:15:35 +00:00
John Sully 3e996035ea Relax memory order where possible 2020-10-13 06:35:16 +00:00
John Sully ae8a94d615 Significantly improve perf in replication scenarios 2020-10-12 15:27:03 +00:00
John Sully 69a74a567b Avoid excess locking, seeing up to 8% performance improvements 2020-10-12 05:50:58 +00:00
John Sully 1b46d4f09a Perf: remove unnecessary vector operations 2020-10-12 04:28:39 +00:00
John Sully c6f13892e0 Remove dead code 2020-10-09 21:02:09 +00:00
Hanif Bin Ariffin 05e0f32345 Fixed non-empty check in src/Makefile
Per [GNU Make Manual](https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html).
To properly check for non-empty variable, one must strip whitespaces.

Signed-off-by: Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
2020-10-05 10:58:49 -04:00
Hanif Bin Ariffin 8171e6de13 Removed dead code from a macro in zmalloc.cpp
I think the compiler would have removed this no-op anyways but it
definitely wasted me some 30 minutes on this :(

I was hoping I could remove the branch through some bit-hacking but
apparently its dead code :). Oh well, its 30 minutes of refreshing
bit hacking.

Signed-off-by: Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
2020-10-03 20:41:26 -04:00
John Sully b042495f37 Delete CONTRIBUTING 2020-10-01 11:17:56 -04:00
John Sully 420ccdfbae Fix module test failures due to locking 2020-10-01 00:08:54 +00:00
John Sully 41466a4147 Fix TLS test failure due to bad merge 2020-09-30 20:21:40 +00:00
John Sully 9798f2d711 Keep redis-cli.c C89 compatible 2020-09-30 20:17:51 +00:00
John Sully 5f3543921b Mac build break fix 2020-09-30 20:17:02 +00:00
John Sully a41366cc3a Fix stream replication failure with active replication, issue #238 2020-09-30 20:12:54 +00:00
John Sully 4c7e4b91a6 Merge tag '6.0.8' into unstable 2020-09-30 19:47:55 +00:00
John Sully 28183f4b66 Test RDB merge on load with active replication 2020-09-29 03:26:06 +00:00
John Sully a0abc1eddd Drop min-clients-per-thread to a more reasonable number 2020-09-28 19:17:36 +00:00
Oran Agra 03b59cd5f0 6.0.8 2020-09-10 14:09:00 +03:00
Oran Agra 5380716a70 Fix RESP3 response for HKEYS/HVALS on non-existing key 2020-09-10 14:09:00 +03:00
Oran Agra 18b30467f7 Fix leak in new blockedclient module API test
(cherry picked from commit 0e20ad14a6a857cb168b808f94721df19b23dc0c)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb 0052d5196d Tests: fix oom-score-adj false positives. (#7772)
The key save delay is too short and on certain systems the child process
is gone before we have a chance to inspect it.

(cherry picked from commit b2a73c404b)
2020-09-10 14:09:00 +03:00
杨博东 b55b0ea0f2 Tests: Add aclfile load and save tests (#7765)
improves test coverage

(cherry picked from commit 0666267d27)
2020-09-10 14:09:00 +03:00
Roi Lipman 25e211bcf0 RM_ThreadSafeContextTryLock a non-blocking method for acquiring GIL (#7738)
Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 042189fd87)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb 635704d424 Tests: validate CONFIG REWRITE for all params. (#7764)
This is a catch-all test to confirm that that rewrite produces a valid
output for all parameters and that this process does not introduce
undesired configuration changes.

(cherry picked from commit a8b7268911)
2020-09-10 14:09:00 +03:00
Oran Agra 47364bce69 Change THP warning to use madvise rather than never (#7771)
completes b2419c31c1

(cherry picked from commit 1461f02deb)
2020-09-10 14:09:00 +03:00
Itamar Haber 3cd6c26056 Documents RM_Call's fmt (#5448)
Improve RM_Call inline documentation about the fmt argument
so that we don't completely depend on the web docs.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit ce15620dc1)
2020-09-10 14:09:00 +03:00
Jan-Erik Rediger 8b0d797ecd Check that THP is not set to always (madvise is ok) (#4001)
THP can also be set to madvise, in which case it shouldn't cause
problems for Redis since redis (or the allocator) doesn't use madvise
to activate it.

(cherry picked from commit b2419c31c1)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb 3c8b394511 Tests: clean up stale .cli files. (#7768)
(cherry picked from commit 918abd7276)
2020-09-10 14:09:00 +03:00
Eran Liberty 84ba2ec82b Allow exec with read commands on readonly replica in cluster (#7766)
There was a bug. Although cluster replicas would allow read commands,
they would not allow a MULTI-EXEC that's composed solely of read commands.
Adds tests for coverage.

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: Eran Liberty <eranl@amazon.com>
(cherry picked from commit b120366d48)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb cf3375bf7c Fix CONFIG REWRITE of oom-score-adj-values. (#7761)
(cherry picked from commit 750acf3a45)
2020-09-10 14:09:00 +03:00
Oran Agra 643d3e0b79 handle cur_test for nested tests
if there are nested tests and nested servers, we need to restore the
previous value of cur_test when a test exist.

example:
```
test{test 1} {
	start_server {
		test{test 1.1 - master only} {
		}
		start_server {
		    test{test 1.2 - with replication} {
            }
		}
	}
}
```
when `test 1.1 - master only exists`, we're still inside `test 1`

(cherry picked from commit 0a1e734193)
2020-09-10 14:09:00 +03:00
Oran Agra 874c3a9978 Add daily CI for MacOS (#7759)
(cherry picked from commit 5496b4a7cd)
2020-09-10 14:09:00 +03:00
bodong.ybd 781e50d41f Tests: Some fixes for macOS
1) cur_test: when restart_server, "no such variable" error occurs
  ./runtest --single integration/rdb
  test {client freed during loading}
      SET ::cur_test
      restart_server
        kill_server
          test "Check for memory leaks (pid $pid)"
          SET ::cur_test
          UNSET ::cur_test
      UNSET ::cur_test // This global variable has been unset.

2) `ps --ppid` not available on macOS platform, can be replaced with
`pgrep -P pid`.

(cherry picked from commit f22fa9594d)
2020-09-10 14:09:00 +03:00
Oran Agra 57dbd6e90d Fix cluster consistency-check test (#7754)
This test was failing from time to time see discussion at the bottom of #7635
This was probably due to timing, the DEBUG SLEEP executed by redis-cli
didn't sleep for enough time.

This commit changes:
1) use SET-ACTIVE-EXPIRE instead of DEBUG SLEEP
2) reduce many `after` sleeps with retry loops to speed up the test.
3) add many comment explaining the different steps of the test and
   it's purpose.
4) config appendonly before populating the volatile keys, so that they'll
   be part of the AOF command stream rather than the preamble RDB portion.

other complications: recently kill_instance switched from SIGKILL to
SIGTERM, and this would sometimes fail since there was an AOFRW running
in the background. now we wait for it to end before attempting the kill.

(cherry picked from commit b491d477c3)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb e9fef49e12 Tests: fix unmonitored servers. (#7756)
There is an inherent race condition in port allocation for spawned
servers. If a server fails to start because a port is taken, a new port
is allocated. This fixes a problem where the logs are not truncated and
as a result a large number of unmonitored servers are started.

(cherry picked from commit 2df4cb93ac)
2020-09-10 14:09:00 +03:00
Oran Agra a227bc8053 fix broken cluster/sentinel tests by recent commit (#7752)
2b998de46 added a file for stderr to keep valgrind log but i forgot to
add a similar thing when valgrind isn't being used.
the result is that `glob */err.txt` fails.

(cherry picked from commit 42ba7a1b75)
2020-09-10 14:09:00 +03:00
Oran Agra d410dc3162 Improve valgrind support for cluster tests (#7725)
- redirect valgrind reports to a dedicated file rather than console
- try to avoid killing instances with SIGKILL so that we get the memory
  leak report (killing with SIGTERM before resorting to SIGKILL)
- search for valgrind reports when done, print them and fail the tests
- add --dont-clean option to keep the logs on exit
- fix exit error code when crash is found (would have exited with 0)

changes that affect the normal redis test suite:
- refactor check_valgrind_errors into two functions one to search and
  one to report
- move the search half into util.tcl to serve the cluster tests too
- ignore "address range perms" valgrind warnings which seem non relevant.

(cherry picked from commit 2b998de460)
2020-09-10 14:09:00 +03:00
Oran Agra 41c7c7919c test infra - add durable mode to work around test suite crashing
in some cases a command that returns an error possibly due to a timing
issue causes the tcl code to crash and thus prevents the rest of the
tests from running. this adds an option to make the test proceed despite
the crash.
maybe it should be the default mode some day.

(cherry picked from commit fe5da2e60d)
2020-09-10 14:09:00 +03:00
Oran Agra db6c763d8b test infra - wait_done_loading
reduce code duplication in aof.tcl.
move creation of clients into the test so that it can be skipped

(cherry picked from commit 1b7ba44e79)
2020-09-10 14:09:00 +03:00
Oran Agra 72d6f966ac test infra - flushall between tests in external mode
(cherry picked from commit b65e5aca86)
2020-09-10 14:09:00 +03:00
Oran Agra 28e074608c test infra - improve test skipping ability
- skip full units
- skip a single test (not just a list of tests)
- when skipping tag, skip spinning up servers, not just the tests
- skip tags when running against an external server too
- allow using multiple tags (split them)

(cherry picked from commit 677d14c213)
2020-09-10 14:09:00 +03:00
Oran Agra 5b8de5b7f2 test infra - reduce disk space usage
this is important when running a test with --loop

(cherry picked from commit e3e69c25fd)
2020-09-10 14:09:00 +03:00
Oran Agra bce350c666 test infra - write test name to logfile
(cherry picked from commit 9d527d076b)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb 332aa2f919 redis-cli: fix writeConn() buffer handling. (#7749)
Fix issues with writeConn() which resulted with corruption of the stream by leaving an extra byte in the buffer. The trigger for this is partial writes or write errors which were not experienced on Linux but reported on macOS.

(cherry picked from commit 58e5feb3f4)
2020-09-10 14:09:00 +03:00
Oran Agra e4db67ea57 Print server startup messages after daemonization (#7743)
When redis isn't configured to have a log file, having these prints
before damonization puts them in the calling process stdout rather than
/dev/null

(cherry picked from commit 8b0747d657)
2020-09-10 14:09:00 +03:00
Thandayuthapani b983015dd4 Add masters/replicas options to redis-cli --cluster call command (#6491)
* Add master/slave option in --cluster call command

* Update src/redis-cli.c

* Update src/redis-cli.c

Co-authored-by: Itamar Haber <itamar@redislabs.com>
(cherry picked from commit f22f64f0db)
2020-09-10 14:09:00 +03:00
Oran Agra 9b718d41f0 fix README about BUILD_WITH_SYSTEMD usage (#7739)
BUILD_WITH_SYSTEMD is an internal variable. Users should use USE_SYSTEMD=yes.

(cherry picked from commit 747b4004ea)
2020-09-10 14:09:00 +03:00
Yossi Gottlieb da33a18588 Fix double-make issue with make && make install. (#7734)
All user-supplied variables that affect the build should be explicitly
persisted.

Fixes #7254

(cherry picked from commit b35d6e5cff)
2020-09-10 14:09:00 +03:00
Oran Agra dbea5f7a8d Redis 6.0.7 2020-09-01 09:27:58 +03:00
Oran Agra 4bb40a9688 Reduce the probability of failure when start redis in runtest-cluster #7554 (#7635)
When runtest-cluster, at first, we need to create a cluster use spawn_instance,
a port which is not used is choosen, however sometimes we can't run server on
the port. possibley due to a race with another process taking it first.
such as redis/redis/runs/896537490. It may be due to the machine problem or
In order to reduce the probability of failure when start redis in
runtest-cluster, we attemp to use another port when find server do not start up.

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: yanhui13 <yanhui13@meituan.com>
(cherry picked from commit e2d64485b8)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb d2532d1335 Backport Lua 5.2.2 stack overflow fix. (#7733)
This fixes the issue described in CVE-2014-5461. At this time we cannot
confirm that the original issue has a real impact on Redis, but it is
included as an extra safety measure.

(cherry picked from commit d75ad774a9)
2020-09-01 09:27:58 +03:00
Leoš Literák 00d0d870d2 Update README.md with instructions how to build with systemd support (#7730)
#7728 - update instructions for systemd support

(cherry picked from commit 571571ca19)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb f38e2802b6 Fix oom-score-adj on older distros. (#7724)
Don't assume `ps` handles `-h` to display output without headers and
manually trim headers line from output.

(cherry picked from commit b61b663895)
2020-09-01 09:27:58 +03:00
Wang Yuan 1b100a167a Fix wrong format specifiers of 'sdscatfmt' for the INFO command (#7706)
unlike printf, sdscatfmt doesn't take %d

(cherry picked from commit 43af28f5b4)
2020-09-01 09:27:58 +03:00
Wen Hui d4e1c88052 fix make warnings (#7692)
(cherry picked from commit e61adc0d89)
2020-09-01 09:27:58 +03:00
Nathan Scott 2c0c5a809c Annotate module API functions in redismodule.h for use with -fno-common (#6900)
In order to keep the redismodule.h self-contained but still usable with
gcc v10 and later, annotate each API function tentative definition with
the __common__ attribute.  This avoids the 'multiple definition' errors
modules will otherwise see for all API functions at link time.

Further details at gcc.gnu.org/gcc-10/porting_to.html

Turn the existing __attribute__ ((unused)), ((__common__)) and ((print))
annotations into conditional macros for any compilers not accepting this
syntax.  These macros only expand to API annotations under gcc.

Provide a pre- and post- macro for every API function, so that they can
be defined differently by the file that includes redismodule.h.

Removing REDISMODULE_API_FUNC in the interest of keeping the function
declarations readable.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit 11cd983d58)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb f6d04d01b9 Add oom-score-adj configuration option to control Linux OOM killer. (#1690)
Add Linux kernel OOM killer control option.

This adds the ability to control the Linux OOM killer oom_score_adj
parameter for all Redis processes, depending on the process role (i.e.
master, replica, background child).

A oom-score-adj global boolean flag control this feature. In addition,
specific values can be configured using oom-score-adj-values if
additional tuning is required.

(cherry picked from commit 2530dc0ebd)
2020-09-01 09:27:58 +03:00
Meir Shpilraien (Spielrein) 2fc915f509 see #7544, added RedisModule_HoldString api. (#7577)
Added RedisModule_HoldString that either returns a
shallow copy of the given String (by increasing
the String ref count) or a new deep copy of String
in case its not possible to get a shallow copy.

Co-authored-by: Itamar Haber <itamar@redislabs.com>
(cherry picked from commit 3f494cc49d)
2020-09-01 09:27:58 +03:00
ShooterIT e3cfc92180 [Redis-benchmark] Remove zrem test, add zpopmin test
(cherry picked from commit 6a06a5a597)
2020-09-01 09:27:58 +03:00
ShooterIT 7fcf29e615 [Redis-benchmark] Support zset type
(cherry picked from commit e5a50ed3c4)
2020-09-01 09:27:58 +03:00
Arun Ranganathan e81bac32fd Show threading configuration in INFO output (#7446)
Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit f6cad30bb6)
2020-09-01 09:27:58 +03:00
Meir Shpilraien (Spielrein) 2257f38b68 This PR introduces a new loaded keyspace event (#7536)
Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: Itamar Haber <itamar@redislabs.com>
(cherry picked from commit 8d82639319)
2020-09-01 09:27:58 +03:00
Oran Agra 2165a78d10 Fix rejectCommand trims newline in shared error objects, hung clients (#7714)
65a3307bc (released in 6.0.6) has a side effect, when processCommand
rejects a command with pre-made shared object error string, it trims the
newlines from the end of the string. if that string is later used with
addReply, the newline will be missing, breaking the protocol, and
leaving the client hung.

It seems that the only scenario which this happens is when replying with
-LOADING to some command, and later using that reply from the CONFIG
SET command (still during loading). this will result in hung client.

Refactoring the code in order to avoid trimming these newlines from
shared string objects, and do the newline trimming only in other cases
where it's needed.

Co-authored-by: Guy Benoish <guy.benoish@redislabs.com>
(cherry picked from commit 9fcd9e191e)
2020-09-01 09:27:58 +03:00
valentinogeron e6f6731c66 EXEC with only read commands should not be rejected when OOM (#7696)
If the server gets MULTI command followed by only read
commands, and right before it gets the EXEC it reaches OOM,
the client will get OOM response.

So, from now on, it will get OOM response only if there was
at least one command that was tagged with `use-memory` flag

(cherry picked from commit b7289e912c)
2020-09-01 09:27:58 +03:00
Itamar Haber 5a9a653f3e Expands lazyfree's effort estimate to include Streams (#5794)
Otherwise, it is treated as a single allocation and freed synchronously. The following logic is used for estimating the effort in constant-ish time complexity:

1. Check the number of nodes.
1. Add an allocation for each consumer group registered inside the stream.
1. Check the number of PELs in the first CG, and then add this count times the number of CGs.
1. Check the number of consumers in the first CG, and then add this count times the number of CGs.

(cherry picked from commit 5b0a06af48)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb ff0846cbb7 Add language servers stuff, test/tls to gitignore. (#7698)
(cherry picked from commit 5449a2a8b5)
2020-09-01 09:27:58 +03:00
Valentino Geron 3e2ed279fd Assert that setDeferredAggregateLen isn't called with negative value
In case the redis is about to return broken reply we want to crash
with assert so that we are notified about the bug. see #7687.

(cherry picked from commit 8b428cf0f7)
2020-09-01 09:27:58 +03:00
Valentino Geron 19ef1f371d Fix LPOS command when RANK is greater than matches
When calling to LPOS command when RANK is higher than matches,
the return value is non valid response. For example:
```
LPUSH l a
:1
LPOS l b RANK 5 COUNT 10
*-4
```
It may break client-side parser.

Now, we count how many replies were replied in the array.
```
LPUSH l a
:1
LPOS l b RANK 5 COUNT 10
*0
```

(cherry picked from commit 9204a9b2c2)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 8d79702d8a Tests: fix redis-cli with remote hosts. (#7693)
(cherry picked from commit f80f3f492a)
2020-09-01 09:27:58 +03:00
huangzhw 6cfcf76e0b RedisModuleEvent_LoadingProgress always at 100% progress (#7685)
It was also using the wrong struct, but luckily RedisModuleFlushInfo and RedisModuleLoadingProgress
are identical.

(cherry picked from commit a3d4d7bf68)
2020-09-01 09:27:58 +03:00
guybe7 c9478a3b4e Modules: Invalidate saved_oparray after use (#7688)
We wanna avoid a chance of someone using the pointer in it after it'll be freed / realloced.

(cherry picked from commit 65c24bd3d4)
2020-09-01 09:27:58 +03:00
杨博东 113d5ae872 Fix flock cluster config may cause failure to restart after kill -9 (#7674)
After fork, the child process(redis-aof-rewrite) will get the fd opened
by the parent process(redis), when redis killed by kill -9, it will not
graceful exit(call prepareForShutdown()), so redis-aof-rewrite thread may still
alive, the fd(lock) will still be held by redis-aof-rewrite thread, and
redis restart will fail to get lock, means fail to start.

This issue was causing failures in the cluster tests in github actions.

Co-authored-by: Oran Agra <oran@redislabs.com>
(cherry picked from commit cbaf3c5bba)
2020-09-01 09:27:58 +03:00
Raghav Muddur 1bd75aafdb Update clusterMsgDataPublish to clusterMsgModule (#7682)
Correcting the variable to clusterMsgModule.

(cherry picked from commit 34c3be365a)
2020-09-01 09:27:58 +03:00
Madelyn Olson ba48e7e9cd Fixed hset error since it's shared with hmset (#7678)
(cherry picked from commit cbd9af8583)
2020-09-01 09:27:58 +03:00
guybe7 4e19358d65 PERSIST should signalModifiedKey (Like EXPIRE does) (#7671)
(cherry picked from commit b87c288016)
2020-09-01 09:27:58 +03:00
Oran Agra 43bc1ea065 OOM Crash log include size of allocation attempt. (#7670)
Since users often post just the crash log in github issues, the log
print that's above it is missing.
No reason not to include the size in the panic message itself.

(cherry picked from commit 0f741a9e2d)
2020-09-01 09:27:58 +03:00
Wen Hui 3842bfd899 [module] using predefined REDISMODULE_NO_EXPIRE in RM_GetExpire (#7669)
It was already defined in the API header and the documentation, but not used by the implementation.

(cherry picked from commit 93d87d6d4c)
2020-09-01 09:27:58 +03:00
Oran Agra 6dcd641681 Trim trailing spaces in error replies coming from rejectCommand (#7668)
65a3307bc9 added rejectCommand which takes an robj reply and passes it
through addReplyErrorSafe to addReplyErrorLength.
The robj contains newline at it's end, but addReplyErrorSafe converts it
to spaces, and passes it to addReplyErrorLength which adds the protocol
newlines.

The result was that most error replies (like OOM) had extra two trailing
spaces in them.

(cherry picked from commit cdd925b289)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 1180537589 Module API: fix missing RM_CLIENTINFO_FLAG_SSL. (#7666)
The `REDISMODULE_CLIENTINFO_FLAG_SSL` flag was already a part of the `RedisModuleClientInfo` structure but was not implemented.

(cherry picked from commit 64c360c515)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 2e7ad58918 TLS: relax verification on CONFIG SET. (#7665)
Avoid re-configuring (and validating) SSL/TLS configuration on `CONFIG
SET` when TLS is not actively enabled for incoming connections, cluster
bus or replication.

This fixes failures when tests run without `--tls` on binaries that were
built with TLS support.

An additional benefit is that it's now possible to perform a multi-step
configuration process while TLS is disabled. The new configuration will
be verified and applied only when TLS is effectively enabled.

(cherry picked from commit fb2a94af3f)
2020-09-01 09:27:58 +03:00
Madelyn Olson 39ffc3702f Fixed timer warning (#5953)
(cherry picked from commit 79c506ebf0)
2020-09-01 09:27:58 +03:00
Wagner Francisco Mezaroba 31415cb27a allow --pattern to be used along with --bigkeys (#3586)
Adds --pattern option to cli's --bigkeys, --hotkeys & --scan modes

(cherry picked from commit e2a71338eb)
2020-09-01 09:27:58 +03:00
zhaozhao.zz 840a9951b5 redis-benchmark: fix wrong random key for hset (#4895)
(cherry picked from commit ff1e4a7063)
2020-09-01 09:27:58 +03:00
zhaozhao.zz 3c87d572b9 CLIENT_MASTER should ignore server.proto_max_bulk_len
(cherry picked from commit 589e610ebc)
2020-09-01 09:27:58 +03:00
zhaozhao.zz d3831559cf config: proto-max-bulk-len must be 1mb or greater
(cherry picked from commit bd4b33d7a2)
2020-09-01 09:27:58 +03:00
zhaozhao.zz 44ef19743e using proto-max-bulk-len in checkStringLength for SETRANGE and APPEND
(cherry picked from commit 2e69bfe44d)
2020-09-01 09:27:58 +03:00
YoongHM 171f83306e Start redis after network is online (#7639)
The two lines allow systemd to start redis.service after the network is online. Only after the network is online that Redis could bind to IP address other than 127.0.0.1 during initial boot up process.

(cherry picked from commit 8e937ce4cc)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 0314429c8f Run daily workflow on main repo only (no forks). (#7646)
(cherry picked from commit 944cca98c8)
2020-09-01 09:27:58 +03:00
WuYunlong 086707315f see #7250, fix signature of RedisModule_DeauthenticateAndCloseClient (#7645)
In redismodule.h, RedisModule_DeauthenticateAndCloseClient returns void
`void REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);`
But in module.c, RM_DeauthenticateAndCloseClient returns int
`int RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id)`

It it safe to change return value from `void` to `int` from the user's perspective.

(cherry picked from commit d6220f12a9)
2020-09-01 09:27:58 +03:00
Wang Yuan 9fd0c027e7 Print error info if failed opening config file (#6943)
(cherry picked from commit ea7eeb2fd2)
2020-09-01 09:27:58 +03:00
Wen Hui 0e581e0044 fix memory leak in ACLLoadFromFile error handling (#7623)
(cherry picked from commit 3f67b03378)
2020-09-01 09:27:58 +03:00
Oran Agra e8801f7eaa redis-cli --cluster-yes - negate force flag for clarity
this internal flag is there so that some commands do not comply to `--cluster-yes`

(cherry picked from commit 1aa31e4da9)
2020-09-01 09:27:58 +03:00
Frank Meier 3a677b45ac reintroduce REDISCLI_CLUSTER_YES env variable in redis-cli
the variable was introduced only in the 5.0 branch in #5879 bc6c1c40db

(cherry picked from commit 51077c8212)
2020-09-01 09:27:58 +03:00
Frank Meier dc46f6f041 add force option to 'create-cluster create' script call (#7612)
(cherry picked from commit 3244fae7d4)
2020-09-01 09:27:58 +03:00
Oran Agra 916b215fc5 fix new rdb test failing on timing issues (#7604)
apparenlty on github actions sometimes 500ms is not enough

(cherry picked from commit 824bd2ac11)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 9f0e8f7849 Fix test-centos7-tls daily job. (#7598)
(cherry picked from commit f2da3efc78)
2020-09-01 09:27:58 +03:00
Oran Agra a5294c4e52 module hook for master link up missing on successful psync (#7584)
besides, hooks test was time sensitive. when the replica managed to
reconnect quickly after the client kill, the test would fail

(cherry picked from commit f7e7775990)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb e511ab2cc6 CI: Add daily CentOS 7.x jobs. (#7582)
(cherry picked from commit 4ac1f9ac55)
2020-09-01 09:27:58 +03:00
WuYunlong 7200b3aa0f Fix running single test 14-consistency-check.tcl (#7587)
(cherry picked from commit f3352daf4f)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb e8efcdee0a Clarify RM_BlockClient() error condition. (#6093)
(cherry picked from commit 7af05f07ff)
2020-09-01 09:27:58 +03:00
namtsui 2bcc056a41 Avoid an out-of-bounds read in the redis-sentinel (#7443)
The Redis sentinel would crash with a segfault after a few minutes
because it tried to read from a page without read permissions. Check up
front whether the sds is long enough to contain redis:slave or
redis:master before memcmp() as is done everywhere else in
sentinelRefreshInstanceInfo().

Bug report and commit message from Theo Buehler. Fix from Nam Nguyen.

Co-authored-by: Nam Nguyen <namn@berkeley.edu>
(cherry picked from commit 63dae52324)
2020-09-01 09:27:58 +03:00
Wen Hui b2bd3c0653 Add SignalModifiedKey hook in XGROUP CREATE with MKSTREAM option (#7562)
(cherry picked from commit f33acb3f02)
2020-09-01 09:27:58 +03:00
Wen Hui 4238df5436 fix leak in error handling of debug populate command (#7062)
valsize was not modified during the for loop below instead of getting from c->argv[4], therefore there is no need to put inside the for loop.. Moreover, putting the check outside loop will also avoid memory leaking, decrRefCount(key) should be called in the original code if we put the check in for loop

(cherry picked from commit c69a9b2f61)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb c68b3908a6 Fix TLS cluster tests. (#7578)
Fix consistency test added in af5167b7f without considering TLS
redis-cli configuration.

(cherry picked from commit bedf1b2126)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 1c05b87e30 TLS: Propagate and handle SSL_new() failures. (#7576)
The connection API may create an accepted connection object in an error
state, and callers are expected to check it before attempting to use it.

Co-authored-by: mrpre <mrpre@163.com>
(cherry picked from commit 784ceeb90d)
2020-09-01 09:27:58 +03:00
Oran Agra 67750ce3b3 Fix failing tests due to issues with wait_for_log_message (#7572)
- the test now waits for specific set of log messages rather than wait for
  timeout looking for just one message.
- we don't wanna sample the current length of the log after an action, due
  to a race, we need to start the search from the line number of the last
  message we where waiting for.
- when attempting to trigger a full sync, use multi-exec to avoid a race
  where the replica manages to re-connect before we completed the set of
  actions that should force a full sync.
- fix verify_log_message which was broken and unused

(cherry picked from commit 109b5ccdcd)
2020-09-01 09:27:58 +03:00
Jiayuan Chen 096285ab64 Add optional tls verification (#7502)
Adds an `optional` value to the previously boolean `tls-auth-clients` configuration keyword.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
(cherry picked from commit f31260b044)
2020-09-01 09:27:58 +03:00
Oran Agra 0b2f7c6407 Daily github action: run cluster and sentinel tests with tls (#7575)
(cherry picked from commit 6d92eee69b)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb 8a4240b1fa TLS: support cluster/replication without tls-port.
Initialize and configure OpenSSL even when tls-port is not used, because
we may still have tls-cluster or tls-replication.

Also, make sure to reconfigure OpenSSL when these parameters are changed
as TLS could have been enabled for the first time.

(cherry picked from commit c75512d89d)
2020-09-01 09:27:58 +03:00
grishaf 395d339234 Fix prepareForShutdown function declaration (#7566)
(cherry picked from commit 4126ca466f)
2020-09-01 09:27:58 +03:00
Oran Agra 6daa8b9adb Stabilize bgsave test that sometimes fails with valgrind (#7559)
on ci.redis.io the test fails a lot, reporting that bgsave didn't end.
increaseing the timeout we wait for that bgsave to get aborted.
in addition to that, i also verify that it indeed got aborted by
checking that the save counter wasn't reset.

add another test to verify that a successful bgsave indeed resets the
change counter.

(cherry picked from commit 8a57969fd7)
2020-09-01 09:27:58 +03:00
Madelyn Olson e00b6248c9 Properly reset errno for rdbLoad (#7542)
(cherry picked from commit 818dc3a089)
2020-09-01 09:27:58 +03:00
Oran Agra e8aa5583d0 testsuite may leave servers alive on error (#7549)
in cases where you have
test name {
  start_server {
    start_server {
      assert
    }
  }
}

the exception will be thrown to the test proc, and the servers are
supposed to be killed on the way out. but it seems there was always a
bug of not cleaning the server stack, and recently (#7404) we started
relying on that stack in order to kill them, so with that bug sometimes
we would have tried to kill the same server twice, and leave one alive.

luckly, in most cases the pattern is:
start_server {
  test name {
  }
}

(cherry picked from commit 36b9494385)
2020-09-01 09:27:58 +03:00
Yossi Gottlieb f1d5d5d28e Tests: drop TCL 8.6 dependency. (#7548)
This re-implements the redis-cli --pipe test so it no longer depends on a close feature available only in TCL 8.6.

Basically what this test does is run redis-cli --pipe, generates a bunch of commands and pipes them through redis-cli, and inspects the result in both Redis and the redis-cli output.

To do that, we need to close stdin for redis-cli to indicate we're done so it can flush its buffers and exit. TCL has bi-directional channels can only offers a way to "one-way close" a channel with TCL 8.6. To work around that, we now generate the commands into a file and feed that file to redis-cli directly.

As we're writing to an actual file, the number of commands is now reduced.

(cherry picked from commit f57e844b2e)
2020-09-01 09:27:58 +03:00
Oran Agra cc7f4ba67e Fixes to release scripts (#7547)
(cherry picked from commit 343dd9bcce)
2020-09-01 09:27:58 +03:00
Remi Collet af907e4b6d Fix deprecated tail syntax in tests (#7543)
(cherry picked from commit 3f2fbc4c61)
2020-09-01 09:27:58 +03:00
Wen Hui d2f09a054c Add missing calls to raxStop (#7532)
Since the dynamic allocations in raxIterator are only used for deep walks, memory
leak due to missing call to raxStop can only happen for rax with key names longer
than 32 bytes.

Out of all the missing calls, the only ones that may lead to a leak are the rax
for consumer groups and consumers, and these were only in AOFRW and rdbSave, which
normally only happen in fork or at shutdown.

(cherry picked from commit 4e8f2d6881)
2020-09-01 09:27:58 +03:00
Wen Hui cef46edebc add missing caching command in client help (#7399)
(cherry picked from commit 2fbd0271f6)
2020-09-01 09:27:58 +03:00
zhaozhao.zz 9e997cd0d1 replication: need handle -NOPERM error after send ping (#7538)
(cherry picked from commit 13e50935a8)
2020-09-01 09:27:58 +03:00
Scott Brenner 171aa22b0b GitHub Actions workflows - use latest version of actions/checkout (#7534)
(cherry picked from commit 2f4e9c3f9f)
2020-09-01 09:27:58 +03:00
Oran Agra 7bf665f125 Redis 6.0.6. 2020-07-20 21:08:26 +03:00
Oran Agra a5696bdf4f Run daily CI on PRs to release a branch 2020-07-20 21:08:26 +03:00
Itamar Haber e15528bf1d Adds SHA256SUM to redis-stable tarball upload
(cherry picked from commit 5df0a64d30)
2020-07-20 21:08:26 +03:00
yoav-steinberg e28aa99af1 Support passing stack allocated module strings to moduleCreateArgvFromUserFormat (#7528)
Specifically, the key passed to the module aof_rewrite callback is a stack allocated robj. When passing it to RedisModule_EmitAOF (with appropriate "s" fmt string) redis used to panic when trying to inc the ref count of the stack allocated robj. Now support such robjs by coying them to a new heap robj. This doesn't affect performance because using the alternative "c" or "b" format strings also copies the input to a new heap robj.

(cherry picked from commit d484b8a04e)
2020-07-20 21:08:26 +03:00
Luke Palmer 3051430040 Send null for invalidate on flush (#7469)
(cherry picked from commit 5f716ea467)
2020-07-20 21:08:26 +03:00
dmurnane 29b20fd528 Notify systemd on sentinel startup (#7168)
Co-authored-by: Daniel Murnane <dmurnane@eitccorp.com>
(cherry picked from commit 9242ccf238)
2020-07-20 21:08:26 +03:00
Developer-Ecosystem-Engineering 5b36681213 Add registers dump support for Apple silicon (#7453)
Export following environment variables before building on macOS on Apple silicon

export ARCH_FLAGS="-arch arm64"
export SDK_NAME=macosx
export SDK_PATH=$(xcrun --show-sdk-path --sdk $SDK_NAME)
export CFLAGS="$ARCH_FLAGS -isysroot $SDK_PATH -I$SDK_PATH/usr/include"
export CXXFLAGS=$CFLAGS
export LDFLAGS="$ARCH_FLAGS"
export CC="$(xcrun -sdk $SDK_PATH --find clang) $CFLAGS"
export CXX="$(xcrun -sdk $SDK_PATH --find clang++) $CXXFLAGS"
export LD="$(xcrun -sdk $SDK_PATH --find ld) $LDFLAGS"

make
make test
..
All tests passed without errors!

Backtrack logging assumes x86 and required updating

(cherry picked from commit c2b5f1c15b)
2020-07-20 21:08:26 +03:00
Wen Hui 0f75036c07 correct error msg for num connections reaching maxclients in cluster mode (#7444)
(cherry picked from commit d85af4d6f5)
2020-07-20 21:08:26 +03:00
WuYunlong b1a01fda91 Fix command help for unexpected options (#7476)
(cherry picked from commit 93bdbf5aa4)
2020-07-20 21:08:26 +03:00
WuYunlong 83f55f61a6 Refactor RM_KeyType() by using macro. (#7486)
(cherry picked from commit dc690161d5)
2020-07-20 21:08:26 +03:00
Oran Agra f89f50dbd0 diskless master disconnect replicas when rdb child failed (#7518)
in case the rdb child failed, crashed or terminated unexpectedly redis
would have marked the replica clients with repl_put_online_on_ack and
then kill them only after a minute when no ack was received.

it would not stream anything to these connections, so the only effect of
this bug is a delay of 1 minute in the replicas attempt to re-connect.

(cherry picked from commit a176cb56a3)
2020-07-20 21:08:26 +03:00
Oran Agra 05f8975d21 redis-cli tests, fix valgrind timing issue (#7519)
this test when run with valgrind on github actions takes 160 seconds

(cherry picked from commit 254c962554)
2020-07-20 21:08:26 +03:00
WuYunlong 4780cc5efa Fix out of update help info in tcl tests. (#7516)
Before this commit, the output of "./runtest-cluster --help" is incorrect.
After this commit, the format of the following 3 output is consistent:
./runtest --help
./runtest-cluster --help
./runtest-sentinel --help

(cherry picked from commit 8128d39737)
2020-07-20 21:08:26 +03:00
Qu Chen 9bc6af2449 Replica always reports master's config epoch in CLUSTER NODES output. (#7235)
(cherry picked from commit 938c35302f)
2020-07-20 21:08:26 +03:00
Oran Agra c4b428a388 RESTORE ABSTTL skip expired keys - leak (#7511)
(cherry picked from commit 6a81450144)
2020-07-20 21:08:26 +03:00
Oran Agra aea4db2f5a fix recently added time sensitive tests failing with valgrind (#7512)
interestingly the latency monitor test fails because valgrind is slow
enough so that the time inside PEXPIREAT command from the moment of
the first mstime() call to get the basetime until checkAlreadyExpired
calls mstime() again is more than 1ms, and that test was too sensitive.

using this opportunity to speed up the test (unrelated to the failure)
the fix is just the longer time passed to PEXPIRE.

(cherry picked from commit e5227aab89)
2020-07-20 21:08:26 +03:00
Oran Agra b5c5f870a4 runtest --stop pause stops before terminating the redis server (#7513)
in the majority of the cases (on this rarely used feature) we want to
stop and be able to connect to the shard with redis-cli.
since these are two different processes interracting with the tty we
need to stop both, and we'll have to hit enter twice, but it's not that
bad considering it is rarely used.

(cherry picked from commit 02ef355f98)
2020-07-20 21:08:26 +03:00
Oran Agra 23bf3d1cb9 update release scripts for new hosts, and CI to run more tests (#7480)
* update daily CI to include cluster and sentinel tests
* update daily CI to run when creating a new release
* update release scripts to work on the new redis.io hosts

(cherry picked from commit 7f19a04f0f)
2020-07-20 21:08:26 +03:00
jimgreen2013 1be6dbbdf6 fix description about ziplist, the code is ok (#6318)
* fix description about ZIP_BIG_PREVLEN(the code is ok), it's similar to
antirez#4705

* fix description about ziplist entry encoding field (the code is ok),
the max length should be 2^32 - 1 when encoding is 5 bytes

(cherry picked from commit 67660881ed)
2020-07-20 21:08:26 +03:00
马永泽 1acdc84d10 fix benchmark in cluster mode fails to authenticate (#7488)
Co-authored-by: Oran Agra <oran@redislabs.com> (styling)
(cherry picked from commit 279b4a1464)
2020-07-20 21:08:26 +03:00
Abhishek Soni 52929b61f8 fix: typo in CI job name (#7466)
(cherry picked from commit d5648d617e)
2020-07-20 21:08:26 +03:00
Jiayuan Chen b0faf6113e Fix typo in deps README (#7500)
(cherry picked from commit af39d750cd)
2020-07-20 21:08:26 +03:00
WuYunlong f838df92d2 Add missing latency-monitor tcl test to test_helper.tcl. (#6782)
(cherry picked from commit d792db7948)
2020-07-20 21:08:26 +03:00
Yossi Gottlieb 7a536c2912 TLS: Session caching configuration support. (#7420)
* TLS: Session caching configuration support.
* TLS: Remove redundant config initialization.

(cherry picked from commit 3e6f2b1a45)
2020-07-20 21:08:26 +03:00
Yossi Gottlieb 6f1e828454 TLS: Ignore client cert when tls-auth-clients off. (#7457)
(cherry picked from commit 5266293a0f)
2020-07-20 21:08:26 +03:00
James Hilliard 2d3687655b Use pkg-config to properly detect libssl and libcrypto libraries (#7452)
(cherry picked from commit 6a014af79a)
2020-07-20 21:08:26 +03:00
Yossi Gottlieb b057ff81ee TLS: Add missing redis-cli options. (#7456)
* Tests: fix and reintroduce redis-cli tests.

These tests have been broken and disabled for 10 years now!

* TLS: add remaining redis-cli support.

This adds support for the redis-cli --pipe, --rdb and --replica options
previously unsupported in --tls mode.

* Fix writeConn().

(cherry picked from commit d9f970d8d3)
2020-07-20 21:08:26 +03:00
Oran Agra a9d1014fb2 redis-cli --hotkeys fixed to handle non-printable key names
(cherry picked from commit b23e251036)
2020-07-20 21:08:26 +03:00
Oran Agra 183bdc11c0 redis-cli --bigkeys fixed to handle non-printable key names
(cherry picked from commit 6f8a8647de)
2020-07-20 21:08:26 +03:00
Oran Agra 95ba01b538 RESTORE ABSTTL won't store expired keys into the db (#7472)
Similarly to EXPIREAT with TTL in the past, which implicitly deletes the
key and return success, RESTORE should not store key that are already
expired into the db.
When used together with REPLACE it should emit a DEL to keyspace
notification and replication stream.

(cherry picked from commit 5977a94842)
2020-07-20 21:08:26 +03:00
huangzhw 7b21b8c3fb defrag.c activeDefragSdsListAndDict when defrag sdsele, We can't use (#7492)
it to calculate hash, we should use newsds.

(cherry picked from commit d6180c8c86)
2020-07-20 21:08:26 +03:00
Oran Agra 33ca884cc5 skip a test that uses +inf on valgrind (#7440)
On some platforms strtold("+inf") with valgrind returns a non-inf result

[err]: INCRBYFLOAT does not allow NaN or Infinity in tests/unit/type/incr.tcl
Expected 'ERR*would produce*' to equal or match '1189731495357231765085759.....'

(cherry picked from commit 909bc97c52)
2020-07-20 21:08:26 +03:00
Oran Agra 2b5f23197c stabilize tests that look for log lines (#7367)
tests were sensitive to additional log lines appearing in the log
causing the search to come empty handed.

instead of just looking for the n last log lines, capture the log lines
before performing the action, and then search from that offset.

(cherry picked from commit 8e76e13472)
2020-07-20 21:08:26 +03:00
Oran Agra 1104113c07 tests/valgrind: don't use debug restart (#7404)
* tests/valgrind: don't use debug restart

DEBUG REATART causes two issues:
1. it uses execve which replaces the original process and valgrind doesn't
   have a chance to check for errors, so leaks go unreported.
2. valgrind report invalid calls to close() which we're unable to resolve.

So now the tests use restart_server mechanism in the tests, that terminates
the old server and starts a new one, new PID, but same stdout, stderr.

since the stderr can contain two or more valgrind report, it is not enough
to just check for the absence of leaks, we also need to check for some known
errors, we do both, and fail if we either find an error, or can't find a
report saying there are no leaks.

other changes:
- when killing a server that was already terminated we check for leaks too.
- adding DEBUG LEAK which was used to test it.
- adding --trace-children to valgrind, although no longer needed.
- since the stdout contains two or more runs, we need slightly different way
  of checking if the new process is up (explicitly looking for the new PID)
- move the code that handles --wait-server to happen earlier (before
  watching the startup message in the log), and serve the restarted server too.

* squashme - CR fixes

(cherry picked from commit 69ade87325)
2020-07-20 21:08:26 +03:00
Oran Agra 62f8583445 change references to the github repo location (#7479)
(cherry picked from commit 9bbf768d3c)
2020-07-20 21:08:26 +03:00
zhaozhao.zz 3155adb299 BITOP: propagate only when it really SET or DEL targetkey (#5783)
For example:
    BITOP not targetkey sourcekey

If targetkey and sourcekey doesn't exist, BITOP has no effect,
we do not propagate it, thus can save aof and replica flow.

(cherry picked from commit 1978f996d8)
2020-07-20 21:08:26 +03:00
antirez 14a59d4ce7 Update comment to clarify change in #7398.
(cherry picked from commit ad0a9df77a)
2020-07-20 21:08:26 +03:00
antirez 17aaf5ec97 LPOS: option FIRST renamed RANK.
(cherry picked from commit a5a3a7bbc6)
2020-07-20 21:08:26 +03:00
Dave Nielsen 3f5cf24da9 updated copyright year
Changed "2015" to "2020"

(cherry picked from commit 2d6d9f7552)
2020-07-20 21:08:26 +03:00
Oran Agra 05e483cbb3 EXEC always fails with EXECABORT and multi-state is cleared
In order to support the use of multi-exec in pipeline, it is important that
MULTI and EXEC are never rejected and it is easy for the client to know if the
connection is still in multi state.

It was easy to make sure MULTI and DISCARD never fail (done by previous
commits) since these only change the client state and don't do any actual
change in the server, but EXEC is a different story.

Since in the past, it was possible for clients to handle some EXEC errors and
retry the EXEC, we now can't affort to return any error on EXEC other than
EXECABORT, which now carries with it the real reason for the abort too.

Other fixes in this commit:
- Some checks that where performed at the time of queuing need to be re-
  validated when EXEC runs, for instance if the transaction contains writes
  commands, it needs to be aborted. there was one check that was already done
  in execCommand (-READONLY), but other checks where missing: -OOM, -MISCONF,
  -NOREPLICAS, -MASTERDOWN
- When a command is rejected by processCommand it was rejected with addReply,
  which was not recognized as an error in case the bad command came from the
  master. this will enable to count or MONITOR these errors in the future.
- make it easier for tests to create additional (non deferred) clients.
- add tests for the fixes of this commit.

(cherry picked from commit 65a3307bc9)
2020-07-20 21:08:26 +03:00
antirez c8f250f8dd Include cluster.h for getClusterConnectionsCount().
(cherry picked from commit 21f62c3346)
2020-07-20 21:08:26 +03:00
antirez 9412094f25 Fix BITFIELD i64 type handling, see #7417.
(cherry picked from commit 746297314f)
2020-07-20 21:08:26 +03:00
antirez 8312aa27d4 Clarify maxclients and cluster in conf. Remove myself too.
(cherry picked from commit 59fd178014)
2020-07-20 21:08:26 +03:00
hwware d3aa3791fe fix memory leak in sentinel connection sharing
(cherry picked from commit 1bfa2d27a6)
2020-07-20 21:08:26 +03:00
chenhui0212 2b189f098e Fix comments in function raxLowWalk of listpack.c
(cherry picked from commit f800b52172)
2020-07-20 21:08:26 +03:00
Tomasz Poradowski 3328b7a514 ensure SHUTDOWN_NOSAVE in Sentinel mode
- enforcing of SHUTDOWN_NOSAVE flag in one place to make it consitent
  when running in Sentinel mode

(cherry picked from commit 4ee011adb5)
2020-07-20 21:08:26 +03:00
chenhui0212 88e1f80eee fix comments in listpack.c
(cherry picked from commit 71fafd761a)
2020-07-20 21:08:26 +03:00
antirez 0ebbc36059 Use cluster connections too, to limit maxclients.
See #7401.

(cherry picked from commit 4b8d8826af)
2020-07-20 21:08:26 +03:00
antirez 43ed3c3589 Tracking: fix enableBcastTrackingForPrefix() invalid sdslen() call.
Related to #7387.

(cherry picked from commit 784479939d)
2020-07-20 21:08:26 +03:00
root 8095daea4a cluster.c remove if of clusterSendFail in markNodeAsFailingIfNeeded
(cherry picked from commit c92464db69)
2020-07-20 21:08:26 +03:00
meir@redislabs.com 51e178454d Fix RM_ScanKey module api not to return int encoded strings
The scan key module API provides the scan callback with the current
field name and value (if it exists). Those arguments are RedisModuleString*
which means it supposes to point to robj which is encoded as a string.
Using createStringObjectFromLongLong function might return robj that
points to an integer and so break a module that tries for example to
use RedisModule_StringPtrLen on the given field/value.

The PR introduces a fix that uses the createObject function and sdsfromlonglong function.
Using those function promise that the field and value pass to the to the
scan callback will be Strings.

The PR also changes the Scan test module to use RedisModule_StringPtrLen
to catch the issue. without this, the issue is hidden because
RedisModule_ReplyWithString knows to handle integer encoding of the
given robj (RedisModuleString).

The PR also introduces a new test to verify the issue is solved.

(cherry picked from commit a89bf734a9)
2020-07-20 21:08:26 +03:00
antirez 82b2bfd20b Fix LCS object type checking. Related to #7379.
(cherry picked from commit 1055398849)
2020-07-20 21:08:26 +03:00
hwware ec1faeec72 fix memory leak
(cherry picked from commit 7008a0ba66)
2020-07-20 21:08:26 +03:00
hwware c1326d7b10 fix server crash in STRALGO command
(cherry picked from commit 2a05fa0d48)
2020-07-20 21:08:26 +03:00
Benjamin Sergeant 845fb2d1c1 Update redis-cli.c
(cherry picked from commit 93021da221)
2020-07-20 21:08:26 +03:00
Jamie Scott 930fac8058 minor fix
(cherry picked from commit 4f3b15e6ea)
2020-07-20 21:08:26 +03:00
antirez 0cae0900cf help.h updated. 2020-06-12 12:16:24 +02:00
antirez e0022d8cfe LPOS: tests + crash fix. 2020-06-12 12:08:06 +02:00
antirez 495327c020 LPOS: update to latest proposal.
See https://gist.github.com/antirez/3591c5096bc79cad8b5a992e08304f48
2020-06-12 12:08:06 +02:00
antirez afb8b3c030 LPOS: implement the final design. 2020-06-12 12:08:06 +02:00
Paul Spooren 31c9cd8604 LRANK: Add command (the command will be renamed LPOS).
The `LRANK` command returns the index (position) of a given element
within a list. Using the `direction` argument it is possible to specify
going from head to tail (acending, 1) or from tail to head (decending,
-1). Only the first found index is returend. The complexity is O(N).

When using lists as a queue it can be of interest at what position a
given element is, for instance to monitor a job processing through a
work queue. This came up within the Python `rq` project which is based
on Redis[0].

[0]: https://github.com/rq/rq/issues/1197

Signed-off-by: Paul Spooren <mail@aparcar.org>
2020-06-12 12:08:06 +02:00
187 changed files with 10106 additions and 3259 deletions

No files matched your search

+4 -4
View File
@@ -7,7 +7,7 @@ jobs:
test-ubuntu-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get update
@@ -17,7 +17,7 @@ jobs:
run: ./utils/gen-test-certs.sh
- name: test-tls
run: |
sudo apt-get -y install tcl8.5 tcl-tls
sudo apt-get -y install tcl tcl-tls
./runtest --clients 2 --verbose --tls
- name: cluster-test
run: |
@@ -42,14 +42,14 @@ jobs:
build-macos-latest:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make -j2
build-libc-malloc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get update
+101 -10
View File
@@ -1,16 +1,21 @@
name: Daily
on:
pull_request:
branches:
# any PR to a release branch.
- '[0-9].[0-9]'
schedule:
- cron: '0 7 * * *'
- cron: '0 0 * * *'
jobs:
test-jemalloc:
test-ubuntu-jemalloc:
runs-on: ubuntu-latest
timeout-minutes: 1200
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
@@ -21,12 +26,17 @@ jobs:
./runtest --accurate --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-libc-malloc:
test-ubuntu-libc-malloc:
runs-on: ubuntu-latest
timeout-minutes: 1200
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
@@ -37,11 +47,17 @@ jobs:
./runtest --accurate --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
@@ -59,7 +75,7 @@ jobs:
test-ubuntu-arm:
runs-on: [self-hosted, linux, arm]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
@@ -74,16 +90,91 @@ jobs:
test-valgrind:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
make valgrind
- name: test
run: |
sudo apt-get update
sudo apt-get install tcl8.5 valgrind -y
./runtest --valgrind --verbose --clients 1
- name: module api test
run: ./runtest-moduleapi --valgrind --verbose --clients 1
test-centos7-jemalloc:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: centos:7
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: |
yum -y install centos-release-scl
yum -y install devtoolset-7
scl enable devtoolset-7 "make"
- name: test
run: |
yum -y install tcl
./runtest --accurate --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-centos7-tls:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: centos:7
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: |
yum -y install centos-release-scl epel-release
yum -y install devtoolset-7 openssl-devel openssl
scl enable devtoolset-7 "make BUILD_TLS=yes"
- name: test
run: |
yum -y install tcl tcltls
./utils/gen-test-certs.sh
./runtest --accurate --verbose --tls
./runtest --accurate --verbose
- name: module api test
run: |
./runtest-moduleapi --verbose --tls
./runtest-moduleapi --verbose
- name: sentinel tests
run: |
./runtest-sentinel --tls
./runtest-sentinel
- name: cluster tests
run: |
./runtest-cluster --tls
./runtest-cluster
test-macos-latest:
runs-on: macos-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: make
- name: test
run: |
./runtest --accurate --verbose --no-latency
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
+4
View File
@@ -48,9 +48,13 @@ src/nodes.conf
deps/lua/src/lua
deps/lua/src/luac
deps/lua/src/liblua.a
tests/tls/*
.make-*
.prerequisites
*.dSYM
Makefile.dep
.vscode/*
.idea/*
.ccls
.ccls-cache/*
compile_commands.json
+1133
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1 +1 @@
Please check https://github.com/antirez/redis/issues
Please check https://github.com/redis/redis/issues
-50
View File
@@ -1,50 +0,0 @@
Note: by contributing code to the Redis project in any form, including sending
a pull request via Github, a code fragment or patch via private email or
public discussion groups, you agree to release your code under the terms
of the BSD license that you can find in the COPYING file included in the Redis
source distribution. You will include BSD license in the COPYING file within
each source file that you contribute.
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
* Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature
requests. Everything else belongs to the Redis Google Group:
https://groups.google.com/forum/m/#!forum/Redis-db
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
bugs in the Github issues system. We'll be very happy to help you and provide
all the support in the mailing list.
There is also an active community of Redis users at Stack Overflow:
http://stackoverflow.com/questions/tagged/redis
# How to provide a patch for a new feature
1. If it is a major feature or a semantical change, please don't start coding
straight away: if your feature is not a conceptual fit you'll lose a lot of
time writing the code without any reason. Start by posting in the mailing list
and creating an issue at Github with the description of, exactly, what you want
to accomplish and why. Use cases are important for features to be accepted.
Here you'll see if there is consensus about your idea.
2. If in step 1 you get an acknowledgment from the project leaders, use the
following procedure to submit a patch:
a. Fork Redis on github ( http://help.github.com/fork-a-repo/ )
b. Create a topic branch (git checkout -b my_branch)
c. Push to your branch (git push origin my_branch)
d. Initiate a pull request on github ( https://help.github.com/articles/creating-a-pull-request/ )
e. Done :)
3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait
for a *very* long time. However this is not lack of interest, as the project
gets more and more users, we find ourselves in a constant need to prioritize
certain issues/PRs over others. If you think your issue/PR is very important
try to popularize it, have other users commenting and sharing their point of
view and so forth. This helps.
4. For minor fixes just open a pull request on Github.
Thanks!
+2 -2
View File
@@ -1,5 +1,5 @@
Copyright (c) 2006-2015, Salvatore Sanfilippo
Copyright (C) 2019, John Sully
Copyright (c) 2006-2020, Salvatore Sanfilippo
Copyright (C) 2019-2020, John Sully
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+23 -12
View File
@@ -8,6 +8,8 @@
##### Have feedback? Take our quick survey: https://www.surveymonkey.com/r/Y9XNS93
##### KeyDB is Hiring! We are currently building out our dev team. If you are interested please see the posting here: https://keydb.dev/careers.html
What is KeyDB?
--------------
@@ -15,11 +17,13 @@ KeyDB is a high performance fork of Redis with a focus on multithreading, memory
KeyDB maintains full compatibility with the Redis protocol, modules, and scripts. This includes the atomicity guarantees for scripts and transactions. Because KeyDB keeps in sync with Redis development KeyDB is a superset of Redis functionality, making KeyDB a drop in replacement for existing Redis deployments.
On the same hardware KeyDB can perform twice as many queries per second as Redis, with 60% lower latency. Active-Replication simplifies hot-spare failover allowing you to easily distribute writes over replicas and use simple TCP based load balancing/failover. KeyDB's higher performance allows you to do more on less hardware which reduces operation costs and complexity.
On the same hardware KeyDB can achieve significantly higher throughput than Redis. Active-Replication simplifies hot-spare failover allowing you to easily distribute writes over replicas and use simple TCP based load balancing/failover. KeyDB's higher performance allows you to do more on less hardware which reduces operation costs and complexity.
<img src="https://keydb.dev/assets/img/blog/5x_opspersecVSdatasize.PNG"/>
The chart below compares several KeyDB and Redis setups, including the latest Redis6 io-threads option, and TLS benchmarks.
See the full benchmark results and setup information here: https://docs.keydb.dev/blog/2019/10/07/blog-post/
<img src="https://docs.keydb.dev/img/blog/2020-09-15/ops_comparison.png"/>
See the full benchmark results and setup information here: https://docs.keydb.dev/blog/2020/09/29/blog-post/
Why fork Redis?
---------------
@@ -82,6 +86,8 @@ Building KeyDB
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.
More on CentOS/Archlinux/Alpine/Debian/Ubuntu dependencies and builds can be found here: https://docs.keydb.dev/docs/build/
Install dependencies:
% sudo apt install build-essential nasm autotools-dev autoconf libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev
@@ -95,9 +101,14 @@ libssl-dev on Debian/Ubuntu) and run:
% make BUILD_TLS=yes
You can enable flash support with:
To build with systemd support, you'll need systemd development libraries (such
as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
% make MALLOC=memkind
% make USE_SYSTEMD=yes
To append a suffix to KeyDB program names, use:
% make PROG_SUFFIX="-alt"
***Note that the following dependencies may be needed:
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
@@ -112,7 +123,7 @@ installed):
Fixing build problems with dependencies or cached build options
---------
KeyDB has some dependencies which are included into the `deps` directory.
KeyDB has some dependencies which are included in the `deps` directory.
`make` does not automatically rebuild dependencies even if something in
the source code of dependencies changes.
@@ -139,7 +150,7 @@ with a 64 bit target, or the other way around, you need to perform a
In case of build errors when trying to build a 32 bit binary of KeyDB, try
the following steps:
* Install the packages libc6-dev-i386 (also try g++-multilib).
* Install the package libc6-dev-i386 (also try g++-multilib).
* Try using the following command line instead of `make 32bit`:
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
@@ -164,14 +175,14 @@ Verbose build
-------------
KeyDB will build with a user friendly colorized output by default.
If you want to see a more verbose output use the following:
If you want to see a more verbose output, use the following:
% make V=1
Running KeyDB
-------------
To run KeyDB with the default configuration just type:
To run KeyDB with the default configuration, just type:
% cd src
% ./keydb-server
@@ -224,7 +235,7 @@ You can find the list of all the available commands at https://docs.keydb.dev/do
Installing KeyDB
-----------------
In order to install KeyDB binaries into /usr/local/bin just use:
In order to install KeyDB binaries into /usr/local/bin, just use:
% make install
@@ -233,8 +244,8 @@ different destination.
Make install will just install binaries in your system, but will not configure
init scripts and configuration files in the appropriate place. This is not
needed if you want just to play a bit with KeyDB, but if you are installing
it the proper way for a production system, we have a script doing this
needed if you just want to play a bit with KeyDB, but if you are installing
it the proper way for a production system, we have a script that does this
for Ubuntu and Debian systems:
% cd utils
+5 -5
View File
@@ -21,7 +21,7 @@ just following tose steps:
1. Remove the jemalloc directory.
2. Substitute it with the new jemalloc source tree.
3. Edit the Makefile localted in the same directory as the README you are
3. Edit the Makefile located in the same directory as the README you are
reading, and change the --with-version in the Jemalloc configure script
options with the version you are using. This is required because otherwise
Jemalloc configuration script is broken and will not work nested in another
@@ -33,7 +33,7 @@ If you want to upgrade Jemalloc while also providing support for
active defragmentation, in addition to the above steps you need to perform
the following additional steps:
5. In Jemalloc three, file `include/jemalloc/jemalloc_macros.h.in`, make sure
5. In Jemalloc tree, file `include/jemalloc/jemalloc_macros.h.in`, make sure
to add `#define JEMALLOC_FRAG_HINT`.
6. Implement the function `je_get_defrag_hint()` inside `src/jemalloc.c`. You
can see how it is implemented in the current Jemalloc source tree shipped
@@ -47,9 +47,9 @@ Hiredis
Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:
1. Check with diff if hiredis API changed and what impact it could have in Redis.
2. Make sure thet the SDS library inside Hiredis and inside Redis are compatible.
2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
3. After the upgrade, run the Redis Sentinel test.
4. Check manually that redis-cli and redis-benchmark behave as expecteed, since we have no tests for CLI utilities currently.
4. Check manually that redis-cli and redis-benchmark behave as expected, since we have no tests for CLI utilities currently.
Linenoise
---
@@ -77,6 +77,6 @@ and our version:
1. Makefile is modified to allow a different compiler than GCC.
2. We have the implementation source code, and directly link to the following external libraries: `lua_cjson.o`, `lua_struct.o`, `lua_cmsgpack.o` and `lua_bit.o`.
3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order toa void direct bytecode execution.
3. There is a security fix in `ldo.c`, line 498: The check for `LUA_SIGNATURE[0]` is removed in order to avoid direct bytecode execution.
+2 -2
View File
@@ -625,7 +625,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
lndebug("rpos2 %d", rpos2);
/* Go up till we reach the expected positon. */
/* Go up till we reach the expected position. */
if (rows-rpos2 > 0) {
lndebug("go-up %d", rows-rpos2);
snprintf(seq,64,"\x1b[%dA", rows-rpos2);
@@ -767,7 +767,7 @@ void linenoiseEditBackspace(struct linenoiseState *l) {
}
}
/* Delete the previosu word, maintaining the cursor at the start of the
/* Delete the previous word, maintaining the cursor at the start of the
* current word. */
void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
size_t old_pos = l->pos;
+1 -1
View File
@@ -274,7 +274,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
CallInfo *ci;
StkId st, base;
Proto *p = cl->p;
luaD_checkstack(L, p->maxstacksize);
luaD_checkstack(L, p->maxstacksize + p->numparams);
func = restorestack(L, funcr);
if (!p->is_vararg) { /* no varargs? */
base = func + 1;
+130 -172
View File
@@ -24,7 +24,7 @@
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# Note that option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or KeyDB Sentinel. Since KeyDB always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
@@ -46,7 +46,7 @@
################################## NETWORK #####################################
# By default, if no "bind" configuration directive is specified, KeyDB listens
# for connections from all the network interfaces available on the server.
# for connections from all available network interfaces on the host machine.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
#
@@ -58,13 +58,12 @@
# ~~~ WARNING ~~~ If the computer running KeyDB is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force KeyDB to listen only into
# the IPv4 loopback interface address (this means KeyDB will be able to
# accept connections only from clients running into the same computer it
# is running).
# following bind directive, that will force KeyDB to listen only on the
# IPv4 loopback interface address (this means KeyDB will only be able to
# accept client connections from the same host that it is running on).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.
# JUST COMMENT OUT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1
@@ -93,8 +92,8 @@ port 6379
# TCP listen() backlog.
#
# In high requests-per-second environments you need an high backlog in order
# to avoid slow clients connections issues. Note that the Linux kernel
# In high requests-per-second environments you need a high backlog in order
# to avoid slow clients connection issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.
@@ -118,8 +117,8 @@ timeout 0
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Take the connection alive from the point of view of network
# equipment in the middle.
# 2) Force network equipment in the middle to consider the connection to be
# alive.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
@@ -159,11 +158,14 @@ tcp-keepalive 300
# By default, clients (including replica servers) on a TLS port are required
# to authenticate using valid client side certificates.
#
# It is possible to disable authentication using this directive.
# If "no" is specified, client certificates are not required and not accepted.
# If "optional" is specified, client certificates are accepted and must be
# valid if provided, but are not required.
#
# tls-auth-clients no
# tls-auth-clients optional
# By default, a Redis replica does not attempt to establish a TLS connection
# By default, a KeyDB replica does not attempt to establish a TLS connection
# with its master.
#
# Use the following directive to enable TLS on replication links.
@@ -225,11 +227,12 @@ daemonize no
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting KeyDB into SIGSTOP mode
# requires "expect stop" in your upstart job config
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
# They do not enable continuous liveness pings back to your supervisor.
# They do not enable continuous pings back to your supervisor.
supervised no
# If a pid file is specified, KeyDB writes it where specified at startup
@@ -279,6 +282,9 @@ databases 16
# ASCII art logo in startup logs by setting the following option to yes.
always-show-logo yes
# Retrieving "message of today" using CURL requests.
#enable-motd yes
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
@@ -288,7 +294,7 @@ always-show-logo yes
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# In the example below the behavior will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
@@ -321,7 +327,7 @@ save 60 10000
stop-writes-on-bgsave-error yes
# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# By default compression is enabled as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
@@ -409,11 +415,11 @@ dir ./
# still reply to client requests, possibly with out of date data, or the
# data set may just be empty if this is the first synchronization.
#
# 2) if replica-serve-stale-data is set to 'no' the replica will reply with
# an error "SYNC with master in progress" to all the kind of commands
# but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,
# SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,
# COMMAND, POST, HOST: and LATENCY.
# 2) If replica-serve-stale-data is set to 'no' the replica will reply with
# an error "SYNC with master in progress" to all commands except:
# INFO, REPLICAOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE,
# UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST,
# HOST and LATENCY.
#
replica-serve-stale-data yes
@@ -491,14 +497,14 @@ repl-diskless-sync-delay 5
# -----------------------------------------------------------------------------
# WARNING: RDB diskless load is experimental. Since in this setup the replica
# does not immediately store an RDB on disk, it may cause data loss during
# failovers. RDB diskless load + Redis modules not handling I/O reads may also
# cause Redis to abort in case of I/O errors during the initial synchronization
# failovers. RDB diskless load + KeyDB modules not handling I/O reads may also
# cause KeyDB to abort in case of I/O errors during the initial synchronization
# stage with the master. Use only if your do what you are doing.
# -----------------------------------------------------------------------------
#
# Replica can load the RDB it reads from the replication link directly from the
# socket, or store the RDB to a file and read that file after it was completely
# recived from the master.
# received from the master.
#
# In many cases the disk is slower than the network, and storing and loading
# the RDB file may increase replication time (and even increase the master's
@@ -528,7 +534,8 @@ repl-diskless-load disabled
#
# It is important to make sure that this value is greater than the value
# specified for repl-ping-replica-period otherwise a timeout will be detected
# every time there is low traffic between the master and the replica.
# every time there is low traffic between the master and the replica. The default
# value is 60 seconds.
#
# repl-timeout 60
@@ -553,28 +560,28 @@ repl-disable-tcp-nodelay no
# partial resync is enough, just passing the portion of data the replica
# missed while disconnected.
#
# The bigger the replication backlog, the longer the time the replica can be
# disconnected and later be able to perform a partial resynchronization.
# The bigger the replication backlog, the longer the replica can endure the
# disconnect and later be able to perform a partial resynchronization.
#
# The backlog is only allocated once there is at least a replica connected.
# The backlog is only allocated if there is at least one replica connected.
#
# repl-backlog-size 1mb
# After a master has no longer connected replicas for some time, the backlog
# will be freed. The following option configures the amount of seconds that
# need to elapse, starting from the time the last replica disconnected, for
# the backlog buffer to be freed.
# After a master has no connected replicas for some time, the backlog will be
# freed. The following option configures the amount of seconds that need to
# elapse, starting from the time the last replica disconnected, for the backlog
# buffer to be freed.
#
# Note that replicas never free the backlog for timeout, since they may be
# promoted to masters later, and should be able to correctly "partially
# resynchronize" with the replicas: hence they should always accumulate backlog.
# resynchronize" with other replicas: hence they should always accumulate backlog.
#
# A value of 0 means to never release the backlog.
#
# repl-backlog-ttl 3600
# The replica priority is an integer number published by KeyDB in the INFO
# output. It is used by Redis Sentinel in order to select a replica to promote
# output. It is used by KeyDB Sentinel in order to select a replica to promote
# into a master if the master is no longer working correctly.
#
# A replica with a low priority number is considered better for promotion, so
@@ -617,8 +624,8 @@ replica-priority 100
# Another place where this info is available is in the output of the
# "ROLE" command of a master.
#
# The listed IP and address normally reported by a replica is obtained
# in the following way:
# The listed IP address and port normally reported by a replica is
# obtained in the following way:
#
# IP: The address is auto detected by checking the peer address
# of the socket used by the replica to connect with the master.
@@ -628,7 +635,7 @@ replica-priority 100
# listen for connections.
#
# However when port forwarding or Network Address Translation (NAT) is
# used, the replica may be actually reachable via different IP and port
# used, the replica may actually be reachable via different IP and port
# pairs. The following two options can be used by a replica in order to
# report to its master a specific set of IP and port, so that both INFO
# and ROLE will report those values.
@@ -641,31 +648,31 @@ replica-priority 100
############################### KEYS TRACKING #################################
# Redis implements server assisted support for client side caching of values.
# KeyDB implements server assisted support for client side caching of values.
# This is implemented using an invalidation table that remembers, using
# 16 millions of slots, what clients may have certain subsets of keys. In turn
# this is used in order to send invalidation messages to clients. Please
# to understand more about the feature check this page:
# check this page to understand more about the feature:
#
# https://redis.io/topics/client-side-caching
#
# When tracking is enabled for a client, all the read only queries are assumed
# to be cached: this will force Redis to store information in the invalidation
# to be cached: this will force KeyDB to store information in the invalidation
# table. When keys are modified, such information is flushed away, and
# invalidation messages are sent to the clients. However if the workload is
# heavily dominated by reads, Redis could use more and more memory in order
# heavily dominated by reads, KeyDB could use more and more memory in order
# to track the keys fetched by many clients.
#
# For this reason it is possible to configure a maximum fill value for the
# invalidation table. By default it is set to 1M of keys, and once this limit
# is reached, Redis will start to evict keys in the invalidation table
# is reached, KeyDB will start to evict keys in the invalidation table
# even if they were not modified, just to reclaim memory: this will in turn
# force the clients to invalidate the cached values. Basically the table
# maximum size is a trade off between the memory you want to spend server
# side to track information about who cached what, and the ability of clients
# to retain cached objects in memory.
#
# If you set the value to 0, it means there are no limits, and Redis will
# If you set the value to 0, it means there are no limits, and KeyDB will
# retain as many keys as needed in the invalidation table.
# In the "stats" INFO section, you can find information about the number of
# keys in the invalidation table at every given moment.
@@ -677,7 +684,7 @@ replica-priority 100
################################## SECURITY ###################################
# Warning: since KeyDB is pretty fast an outside user can try up to
# Warning: since KeyDB is pretty fast, an outside user can try up to
# 1 million passwords per second against a modern box. This means that you
# should use very strong passwords, otherwise they will be very easy to break.
# Note that because the password is really a shared secret between the client
@@ -701,7 +708,7 @@ replica-priority 100
# AUTH (or the HELLO command AUTH option) in order to be authenticated and
# start to work.
#
# The ACL rules that describe what an user can do are the following:
# The ACL rules that describe what a user can do are the following:
#
# on Enable the user: it is possible to authenticate as this user.
# off Disable the user: it's no longer possible to authenticate
@@ -711,7 +718,7 @@ replica-priority 100
# -<command> Disallow the execution of that command
# +@<category> Allow the execution of all the commands in such category
# with valid categories are like @admin, @set, @sortedset, ...
# and so forth, see the full list in the server.c file where
# and so forth, see the full list in the server.cpp file where
# the KeyDB command table is described and defined.
# The special category @all means all the commands, but currently
# present in the server, and that will be loaded in the future
@@ -729,7 +736,7 @@ replica-priority 100
# It is possible to specify multiple patterns.
# allkeys Alias for ~*
# resetkeys Flush the list of allowed keys patterns.
# ><password> Add this passowrd to the list of valid password for the user.
# ><password> Add this password to the list of valid password for the user.
# For example >mypass will add "mypass" to the list.
# This directive clears the "nopass" flag (see later).
# <<password> Remove this password from the list of valid passwords.
@@ -775,16 +782,15 @@ replica-priority 100
#
# The ACL Log tracks failed commands and authentication events associated
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
# by ACLs. The ACL Log is stored in and consumes memory. There is no limit
# to its length.You can reclaim memory with ACL LOG RESET or set a maximum
# length below.
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
acllog-max-len 128
# Using an external ACL file
#
# Instead of configuring users here in this file, it is possible to use
# a stand-alone file just listing users. The two methods cannot be mixed:
# if you configure users here and at the same time you activate the exteranl
# if you configure users here and at the same time you activate the external
# ACL file, the server will refuse to start.
#
# The format of the external ACL user file is exactly the same as the
@@ -792,7 +798,7 @@ acllog-max-len 128
#
# aclfile /etc/keydb/users.acl
# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatiblity
# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatibility
# layer on top of the new ACL system. The option effect will be just setting
# the password for the default user. Clients will still authenticate using
# AUTH <password> as usually, or more explicitly with AUTH default <password>
@@ -836,6 +842,11 @@ acllog-max-len 128
# Once the limit is reached KeyDB will close all the new connections sending
# an error 'max number of clients reached'.
#
# IMPORTANT: When Redis Cluster is used, the max number of connections is also
# shared with the cluster bus: every node in the cluster will use two
# connections, one incoming and another outgoing. It is important to size the
# limit accordingly in case of very large clusters.
#
# maxclients 10000
############################## MEMORY MANAGEMENT ################################
@@ -898,8 +909,8 @@ acllog-max-len 128
# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default KeyDB will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# accuracy. By default KeyDB will check five keys and pick the one that was
# used least recently, you can change the sample size using the following
# configuration directive.
#
# The default of 5 produces good enough results. 10 Approximates very closely
@@ -927,7 +938,7 @@ acllog-max-len 128
#
# replica-ignore-maxmemory yes
# Redis reclaims expired keys in two ways: upon access when those keys are
# KeyDB reclaims expired keys in two ways: upon access when those keys are
# found to be expired, and also in background, in what is called the
# "active expire key". The key space is slowly and interactively scanned
# looking for expired keys to reclaim, so that it is possible to free memory
@@ -939,8 +950,8 @@ acllog-max-len 128
# it is possible to increase the expire "effort" that is normally set to
# "1", to a greater value, up to the value "10". At its maximum value the
# system will use more CPU, longer cycles (and technically may introduce
# more latency), and will tollerate less already expired keys still present
# in the system. It's a tradeoff betweeen memory, CPU and latecy.
# more latency), and will tolerate less already expired keys still present
# in the system. It's a tradeoff between memory, CPU and latency.
#
# active-expire-effort 1
@@ -1000,51 +1011,36 @@ replica-lazy-flush no
lazyfree-lazy-user-del no
################################ THREADED I/O #################################
############################ KERNEL OOM CONTROL ##############################
# Redis is mostly single threaded, however there are certain threaded
# operations such as UNLINK, slow I/O accesses and other things that are
# performed on side threads.
# On Linux, it is possible to hint the kernel OOM killer on what processes
# should be killed first when out of memory.
#
# Now it is also possible to handle Redis clients socket reads and writes
# in different I/O threads. Since especially writing is so slow, normally
# Redis users use pipelining in order to speedup the Redis performances per
# core, and spawn multiple instances in order to scale more. Using I/O
# threads it is possible to easily speedup two times Redis without resorting
# to pipelining nor sharding of the instance.
# Enabling this feature makes KeyDB actively control the oom_score_adj value
# for all its processes, depending on their role. The default scores will
# attempt to have background child processes killed before all others, and
# replicas killed before masters.
#
# By default threading is disabled, we suggest enabling it only in machines
# that have at least 4 or more cores, leaving at least one spare core.
# Using more than 8 threads is unlikely to help much. We also recommend using
# threaded I/O only if you actually have performance problems, with Redis
# instances being able to use a quite big percentage of CPU time, otherwise
# there is no point in using this feature.
# KeyDB supports three options:
#
# So for instance if you have a four cores boxes, try to use 2 or 3 I/O
# threads, if you have a 8 cores, try to use 6 threads. In order to
# enable I/O threads use the following configuration directive:
# no: Don't make changes to oom-score-adj (default).
# yes: Alias to "relative" see below.
# absolute: Values in oom-score-adj-values are written as is to the kernel.
# relative: Values are used relative to the initial value of oom_score_adj when
# the server starts and are then clamped to a range of -1000 to 1000.
# Because typically the initial value is 0, they will often match the
# absolute values.
oom-score-adj no
# When oom-score-adj is used, this directive controls the specific values used
# for master, replica and background child processes. Values range -2000 to
# 2000 (higher means more likely to be killed).
#
# io-threads 4
#
# Setting io-threads to 1 will just use the main thread as usually.
# When I/O threads are enabled, we only use threads for writes, that is
# to thread the write(2) syscall and transfer the client buffers to the
# socket. However it is also possible to enable threading of reads and
# protocol parsing using the following configuration directive, by setting
# it to yes:
#
# io-threads-do-reads no
#
# Usually threading reads doesn't help much.
#
# NOTE 1: This configuration directive cannot be changed at runtime via
# CONFIG SET. Aso this feature currently does not work when SSL is
# enabled.
#
# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make
# sure you also run the benchmark itself in threaded mode, using the
# --threads option to match the number of Redis theads, otherwise you'll not
# be able to notice the improvements.
# Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities)
# can freely increase their value, but not decrease it below its initial
# settings. This means that setting oom-score-adj to "relative" and setting the
# oom-score-adj-values to positive values will always succeed.
oom-score-adj-values 0 200 800
############################## APPEND ONLY MODE ###############################
@@ -1170,8 +1166,8 @@ aof-load-truncated yes
#
# [RDB file][AOF tail]
#
# When loading KeyDB recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# When loading, KeyDB recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, then continues loading the AOF
# tail.
aof-use-rdb-preamble yes
@@ -1185,7 +1181,7 @@ aof-use-rdb-preamble yes
#
# When a long running script exceeds the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# used to stop a script that did not yet call any write commands. The second
# is the only way to shut down the server in the case a write command was
# already issued by the script but the user doesn't want to wait for the natural
# termination of the script.
@@ -1211,7 +1207,7 @@ lua-time-limit 5000
# Cluster node timeout is the amount of milliseconds a node must be unreachable
# for it to be considered in failure state.
# Most other internal time limits are multiple of the node timeout.
# Most other internal time limits are a multiple of the node timeout.
#
# cluster-node-timeout 15000
@@ -1238,18 +1234,18 @@ lua-time-limit 5000
# the failover if, since the last interaction with the master, the time
# elapsed is greater than:
#
# (node-timeout * replica-validity-factor) + repl-ping-replica-period
# (node-timeout * cluster-replica-validity-factor) + repl-ping-replica-period
#
# So for example if node-timeout is 30 seconds, and the replica-validity-factor
# So for example if node-timeout is 30 seconds, and the cluster-replica-validity-factor
# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the
# replica will not try to failover if it was not able to talk with the master
# for longer than 310 seconds.
#
# A large replica-validity-factor may allow replicas with too old data to failover
# A large cluster-replica-validity-factor may allow replicas with too old data to failover
# a master, while a too small value may prevent the cluster from being able to
# elect a replica at all.
#
# For maximum availability, it is possible to set the replica-validity-factor
# For maximum availability, it is possible to set the cluster-replica-validity-factor
# to a value of 0, which means, that replicas will always try to failover the
# master regardless of the last time they interacted with the master.
# (However they'll always try to apply a delay proportional to their
@@ -1280,7 +1276,7 @@ lua-time-limit 5000
# cluster-migration-barrier 1
# By default KeyDB Cluster nodes stop accepting queries if they detect there
# is at least an hash slot uncovered (no available node is serving it).
# is at least a hash slot uncovered (no available node is serving it).
# This way if the cluster is partially down (for example a range of hash slots
# are no longer covered) all the cluster becomes, eventually, unavailable.
# It automatically returns available as soon as all the slots are covered again.
@@ -1335,7 +1331,7 @@ lua-time-limit 5000
# * cluster-announce-port
# * cluster-announce-bus-port
#
# Each instruct the node about its address, client port, and cluster message
# Each instructs the node about its address, client port, and cluster message
# bus port. The information is then published in the header of the bus packets
# so that other nodes will be able to correctly map the address of the node
# publishing the information.
@@ -1346,7 +1342,7 @@ lua-time-limit 5000
# Note that when remapped, the bus port may not be at the fixed offset of
# clients port + 10000, so you can specify any port and bus-port depending
# on how they get remapped. If the bus-port is not set, a fixed offset of
# 10000 will be used as usually.
# 10000 will be used as usual.
#
# Example:
#
@@ -1449,61 +1445,6 @@ latency-monitor-threshold 0
# specify at least one of K or E, no events will be delivered.
notify-keyspace-events ""
############################### GOPHER SERVER #################################
# KeyDB contains an implementation of the Gopher protocol, as specified in
# the RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt).
#
# The Gopher protocol was very popular in the late '90s. It is an alternative
# to the web, and the implementation both server and client side is so simple
# that the KeyDB server has just 100 lines of code in order to implement this
# support.
#
# What do you do with Gopher nowadays? Well Gopher never *really* died, and
# lately there is a movement in order for the Gopher more hierarchical content
# composed of just plain text documents to be resurrected. Some want a simpler
# internet, others believe that the mainstream internet became too much
# controlled, and it's cool to create an alternative space for people that
# want a bit of fresh air.
#
# Anyway for the 10nth birthday of the KeyDB, we gave it the Gopher protocol
# as a gift.
#
# --- HOW IT WORKS? ---
#
# The KeyDB Gopher support uses the inline protocol of KeyDB, and specifically
# two kind of inline requests that were anyway illegal: an empty request
# or any request that starts with "/" (there are no KeyDB commands starting
# with such a slash). Normal RESP2/RESP3 requests are completely out of the
# path of the Gopher protocol implementation and are served as usually as well.
#
# If you open a connection to KeyDB when Gopher is enabled and send it
# a string like "/foo", if there is a key named "/foo" it is served via the
# Gopher protocol.
#
# In order to create a real Gopher "hole" (the name of a Gopher site in Gopher
# talking), you likely need a script like the following:
#
# https://github.com/antirez/gopher2redis
#
# --- SECURITY WARNING ---
#
# If you plan to put KeyDB on the internet in a publicly accessible address
# to server Gopher pages MAKE SURE TO SET A PASSWORD to the instance.
# Once a password is set:
#
# 1. The Gopher server (when enabled, not by default) will still serve
# content via Gopher.
# 2. However other commands cannot be called before the client will
# authenticate.
#
# So use the 'requirepass' option to protect your instance.
#
# To enable Gopher support uncomment the following line and set
# the option from no (the default) to yes.
#
# gopher-enabled no
############################### ADVANCED CONFIG ###############################
# Hashes are encoded using a memory efficient data structure when they have a
@@ -1647,8 +1588,8 @@ client-output-buffer-limit pubsub 32mb 8mb 60
# client-query-buffer-limit 1gb
# In the KeyDB protocol, bulk requests, that are, elements representing single
# strings, are normally limited ot 512 mb. However you can change this limit
# here.
# strings, are normally limited to 512 mb. However you can change this limit
# here, but must be 1mb or greater
#
# proto-max-bulk-len 512mb
@@ -1676,7 +1617,7 @@ hz 10
#
# Since the default HZ value by default is conservatively set to 10, KeyDB
# offers, and enables by default, the ability to use an adaptive HZ value
# which will temporary raise when there are many connected clients.
# which will temporarily raise when there are many connected clients.
#
# When dynamic HZ is enabled, the actual configured HZ will be used
# as a baseline, but multiples of the configured HZ value will be actually
@@ -1743,7 +1684,7 @@ rdb-save-incremental-fsync yes
# for the key counter to be divided by two (or decremented if it has a value
# less <= 10).
#
# The default value for the lfu-decay-time is 1. A Special value of 0 means to
# The default value for the lfu-decay-time is 1. A special value of 0 means to
# decay the counter every time it happens to be scanned.
#
# lfu-log-factor 10
@@ -1763,7 +1704,7 @@ rdb-save-incremental-fsync yes
# restart is needed in order to lower the fragmentation, or at least to flush
# away all the data and create it again. However thanks to this feature
# implemented by Oran Agra for Redis 4.0 this process can happen at runtime
# in an "hot" way, while the server is running.
# in a "hot" way, while the server is running.
#
# Basically when the fragmentation is over a certain level (see the
# configuration options below) KeyDB will start to create new copies of the
@@ -1816,14 +1757,14 @@ rdb-save-incremental-fsync yes
# Jemalloc background thread for purging will be enabled by default
jemalloc-bg-thread yes
# It is possible to pin different threads and processes of Redis to specific
# It is possible to pin different threads and processes of KeyDB to specific
# CPUs in your system, in order to maximize the performances of the server.
# This is useful both in order to pin different Redis threads in different
# CPUs, but also in order to make sure that multiple Redis instances running
# This is useful both in order to pin different KeyDB threads in different
# CPUs, but also in order to make sure that multiple KeyDB instances running
# in the same host will be pinned to different CPUs.
#
# Normally you can do this using the "taskset" command, however it is also
# possible to this via Redis configuration directly, both in Linux and FreeBSD.
# possible to this via KeyDB configuration directly, both in Linux and FreeBSD.
#
# You can pin the server/IO threads, bio threads, aof rewrite child process, and
# the bgsave child process. The syntax to specify the cpu list is the same as
@@ -1841,10 +1782,25 @@ jemalloc-bg-thread yes
# Set bgsave child process to cpu affinity 1,10,11
# bgsave_cpulist 1,10-11
# In some cases KeyDB will emit warnings and even refuse to start if it detects
# that the system is in bad state, it is possible to suppress these warnings
# by setting the following config which takes a space delimited list of warnings
# to suppress
#
# ignore-warnings ARM64-COW-BUG
# The minimum number of clients on a thread before KeyDB assigns new connections to a different thread
# Tuning this parameter is a tradeoff between locking overhead and distributing the workload over multiple cores
# min-clients-per-thread 50
# How often to run RDB load progress callback?
# The callback runs during key load to ping other servers and prevent timeouts.
# It also updates load time estimates.
# Change these values to run it more or less often. It will run when either condition is true.
# Either when x bytes have been processed, or when x keys have been loaded.
# loading-process-events-interval-bytes 2097152
# loading-process-events-interval-keys 8192
# Avoid forwarding RREPLAY messages to other masters?
# WARNING: This setting is dangerous! You must be certain all masters are connected to each
# other in a true mesh topology or data loss will occur!
@@ -1859,6 +1815,8 @@ jemalloc-bg-thread yes
# Number of worker threads serving requests. This number should be related to the performance
# of your network hardware, not the number of cores on your machine. We don't recommend going
# above 4 at this time. By default this is set 1.
#
# Note: KeyDB does not use io-threads, but io-threads is a config alias for server-threads
server-threads 2
# Should KeyDB pin threads to CPUs? By default this is disabled, and KeyDB will not bind threads.
+4
View File
@@ -25,4 +25,8 @@ $TCLSH tests/test_helper.tcl \
--single unit/moduleapi/scan \
--single unit/moduleapi/datatype \
--single unit/moduleapi/auth \
--single unit/moduleapi/keyspace_events \
--single unit/moduleapi/blockedclient \
--single unit/moduleapi/moduleloadsave \
--single unit/moduleapi/getkeys \
"${@}"
+1 -1
View File
@@ -259,6 +259,6 @@ sentinel deny-scripts-reconfig yes
# SENTINEL SET can also be used in order to perform this configuration at runtime.
#
# In order to set a command back to its original name (undo the renaming), it
# is possible to just rename a command to itsef:
# is possible to just rename a command to itself:
#
# SENTINEL rename-command mymaster CONFIG CONFIG
+1
View File
@@ -2,4 +2,5 @@
*.gcno
*.gcov
redis.info
KeyDB.info
lcov-html
+56 -20
View File
@@ -47,9 +47,9 @@ endif
USEASM?=true
ifneq ($(SANITIZE),)
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
CXXFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
ifneq ($(strip $(SANITIZE)),)
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE -fno-omit-frame-pointer
CXXFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE -fno-omit-frame-pointer
LDFLAGS+= -fsanitize=$(SANITIZE)
MALLOC=libc
USEASM=false
@@ -107,7 +107,7 @@ endif
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
FINAL_CXXFLAGS=$(CXX_STD) $(WARN) $(OPT) $(DEBUG) $(CXXFLAGS) $(REDIS_CFLAGS)
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
FINAL_LIBS+=-lm -lcurl
FINAL_LIBS+=-lm
DEBUG=-g -ggdb
ifneq ($(uname_S),Darwin)
@@ -152,12 +152,21 @@ ifeq ($(uname_S),OpenBSD)
endif
else
ifeq ($(uname_S),NetBSD)
# NetBSD
FINAL_LIBS+= -lpthread
ifeq ($(USE_BACKTRACE),yes)
FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/pkg/include
FINAL_LDFLAGS+= -L/usr/pkg/lib
FINAL_LIBS+= -lexecinfo
endif
else
ifeq ($(uname_S),FreeBSD)
# FreeBSD
FINAL_LIBS+= -lpthread -lexecinfo
else
ifeq ($(uname_S),DragonFly)
# FreeBSD
# DragonFly
FINAL_LIBS+= -lpthread -lexecinfo
else
ifeq ($(uname_S),OpenBSD)
@@ -167,12 +176,23 @@ else
ifeq ($(uname_S),NetBSD)
# NetBSD
FINAL_LIBS+= -lpthread -lexecinfo
else
ifeq ($(uname_S),Haiku)
# Haiku
FINAL_CFLAGS+= -DBSD_SOURCE
FINAL_LDFLAGS+= -lbsd -lnetwork
FINAL_LIBS+= -lpthread
else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
FINAL_LIBS+=-ldl -pthread -lrt -luuid
ifneq ($(NO_MOTD),yes)
FINAL_CFLAGS += -DMOTD
FINAL_CXXFLAGS += -DMOTD
FINAL_LIBS+=-lcurl
endif
endif
endif
endif
endif
endif
@@ -236,10 +256,23 @@ ifeq ($(MALLOC),memkind)
endif
ifeq ($(BUILD_TLS),yes)
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?)
ifeq ($(LIBSSL_PKGCONFIG),0)
LIBSSL_LIBS=$(shell $(PKG_CONFIG) --libs libssl)
else
LIBSSL_LIBS=-lssl
endif
LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?)
ifeq ($(LIBCRYPTO_PKGCONFIG),0)
LIBCRYPTO_LIBS=$(shell $(PKG_CONFIG) --libs libcrypto)
else
LIBCRYPTO_LIBS=-lcrypto
endif
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
endif
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
@@ -261,15 +294,15 @@ QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(EN
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
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 crcspeed.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 tracking.o cron.o connection.o tls.o sha256.o motd.o timeout.o setcpuaffinity.o $(ASM_OBJ)
REDIS_CLI_NAME=keydb-cli
REDIS_SERVER_NAME=keydb-server$(PROG_SUFFIX)
REDIS_SENTINEL_NAME=keydb-sentinel$(PROG_SUFFIX)
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 t_nhash.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 crcspeed.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 tracking.o cron.o connection.o tls.o sha256.o motd.o timeout.o setcpuaffinity.o $(ASM_OBJ)
REDIS_CLI_NAME=keydb-cli$(PROG_SUFFIX)
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o motd.o $(ASM_OBJ)
REDIS_BENCHMARK_NAME=keydb-benchmark
REDIS_BENCHMARK_NAME=keydb-benchmark$(PROG_SUFFIX)
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
REDIS_CHECK_RDB_NAME=keydb-check-rdb$(PROG_SUFFIX)
REDIS_CHECK_AOF_NAME=keydb-check-aof$(PROG_SUFFIX)
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)
@echo ""
@@ -290,6 +323,8 @@ persist-settings: distclean
echo WARN=$(WARN) >> .make-settings
echo OPT=$(OPT) >> .make-settings
echo MALLOC=$(MALLOC) >> .make-settings
echo BUILD_TLS=$(BUILD_TLS) >> .make-settings
echo USE_SYSTEMD=$(USE_SYSTEMD) >> .make-settings
echo CFLAGS=$(CFLAGS) >> .make-settings
echo CXXFLAGS=$(CXXFLAGS) >> .make-settings
echo LDFLAGS=$(LDFLAGS) >> .make-settings
@@ -360,7 +395,7 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
$(KEYDB_AS) $< -o $@
clean:
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov KeyDB.info lcov-html Makefile.dep dict-benchmark
rm -f $(DEP)
.PHONY: clean
@@ -382,9 +417,10 @@ check: test
lcov:
$(MAKE) gcov
@(set -e; cd ..; ./runtest --clients 1)
@geninfo -o redis.info .
@genhtml --legend -o lcov-html redis.info
@(set -e; cd ..; ./runtest --config server-threads 3; ./runtest-sentinel; ./runtest-cluster; ./runtest-moduleapi)
@geninfo -o KeyDB.info --no-external .
@genhtml --legend -o lcov-html KeyDB.info
@genhtml --legend -o lcov-html KeyDB.info | grep lines | awk '{print $$2;}' | sed 's/%//g'
test-sds: sds.c sds.h
$(REDIS_CC) sds.c zmalloc.cpp -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
+78 -23
View File
@@ -300,7 +300,13 @@ void ACLFreeUserAndKillClients(user *u) {
* it in non authenticated mode. */
c->puser = DefaultUser;
c->authenticated = 0;
freeClientAsync(c);
/* We will write replies to this client later, so we can't
* close it directly even if async. */
if (c == serverTL->current_client) {
c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
} else {
freeClientAsync(c);
}
}
}
ACLFreeUser(u);
@@ -377,7 +383,7 @@ int ACLUserCanExecuteFutureCommands(user *u) {
* zero, the user flag ALLCOMMANDS is cleared since it is no longer possible
* to skip the command bit explicit test. */
void ACLSetUserCommandBit(user *u, unsigned long id, int value) {
uint64_t word, bit;
uint64_t word=0, bit=0;
if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;
if (value) {
u->allowed_commands[word] |= bit;
@@ -472,21 +478,68 @@ sds ACLDescribeUserCommandRules(user *u) {
ACLSetUser(fakeuser,"-@all",-1);
}
/* Try to add or subtract each category one after the other. Often a
* single category will not perfectly match the set of commands into
* it, so at the end we do a final pass adding/removing the single commands
* needed to make the bitmap exactly match. */
for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
unsigned long on, off;
ACLCountCategoryBitsForUser(u,&on,&off,ACLCommandCategories[j].name);
if ((additive && on > off) || (!additive && off > on)) {
sds op = sdsnewlen(additive ? "+@" : "-@", 2);
op = sdscat(op,ACLCommandCategories[j].name);
ACLSetUser(fakeuser,op,-1);
rules = sdscatsds(rules,op);
rules = sdscatlen(rules," ",1);
sdsfree(op);
/* Attempt to find a good approximation for categories and commands
* based on the current bits used, by looping over the category list
* and applying the best fit each time. Often a set of categories will not
* perfectly match the set of commands into it, so at the end we do a
* final pass adding/removing the single commands needed to make the bitmap
* exactly match. A temp user is maintained to keep track of categories
* already applied. */
user tu = {0};
user *tempuser = &tu;
/* Keep track of the categories that have been applied, to prevent
* applying them twice. */
char applied[sizeof(ACLCommandCategories)/sizeof(ACLCommandCategories[0])];
memset(applied, 0, sizeof(applied));
memcpy(tempuser->allowed_commands,
u->allowed_commands,
sizeof(u->allowed_commands));
while (1) {
int best = -1;
unsigned long mindiff = INT_MAX, maxsame = 0;
for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
if (applied[j]) continue;
unsigned long on, off, diff, same;
ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name);
/* Check if the current category is the best this loop:
* * It has more commands in common with the user than commands
* that are different.
* AND EITHER
* * It has the fewest number of differences
* than the best match we have found so far.
* * OR it matches the fewest number of differences
* that we've seen but it has more in common. */
diff = additive ? off : on;
same = additive ? on : off;
if (same > diff &&
((diff < mindiff) || (diff == mindiff && same > maxsame)))
{
best = j;
mindiff = diff;
maxsame = same;
}
}
/* We didn't find a match */
if (best == -1) break;
sds op = sdsnewlen(additive ? "+@" : "-@", 2);
op = sdscat(op,ACLCommandCategories[best].name);
ACLSetUser(fakeuser,op,-1);
sds invop = sdsnewlen(additive ? "-@" : "+@", 2);
invop = sdscat(invop,ACLCommandCategories[best].name);
ACLSetUser(tempuser,invop,-1);
rules = sdscatsds(rules,op);
rules = sdscatlen(rules," ",1);
sdsfree(op);
sdsfree(invop);
applied[best] = 1;
}
/* Fix the final ACLs with single commands differences. */
@@ -670,8 +723,8 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
* -<command> Disallow the execution of that command
* +@<category> Allow the execution of all the commands in such category
* with valid categories are like @admin, @set, @sortedset, ...
* and so forth, see the full list in the server.c file where
* the Redis command table is described and defined.
* and so forth, see the full list in the server.cpp file where
* the KeyDB command table is described and defined.
* The special category @all means all the commands, but currently
* present in the server, and that will be loaded in the future
* via modules.
@@ -1099,8 +1152,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
if (!(c->puser->flags & USER_FLAG_ALLKEYS) &&
(c->cmd->getkeys_proc || c->cmd->firstkey))
{
int numkeys;
int *keyidx = getKeysFromCommand(c->cmd,c->argv,c->argc,&numkeys);
getKeysResult result = GETKEYS_RESULT_INIT;
int numkeys = getKeysFromCommand(c->cmd,c->argv,c->argc,&result);
int *keyidx = result.keys;
for (int j = 0; j < numkeys; j++) {
listIter li;
listNode *ln;
@@ -1121,11 +1175,11 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
}
if (!match) {
if (keyidxptr) *keyidxptr = keyidx[j];
getKeysFreeResult(keyidx);
getKeysFreeResult(&result);
return ACL_DENIED_KEY;
}
}
getKeysFreeResult(keyidx);
getKeysFreeResult(&result);
}
/* If we survived all the above checks, the user can execute the
@@ -1330,6 +1384,7 @@ sds ACLLoadFromFile(const char *filename) {
errors = sdscatprintf(errors,
"'%s:%d: username '%s' contains invalid characters. ",
g_pserver->acl_filename, linenum, argv[1]);
sdsfreesplitres(argv,argc);
continue;
}
@@ -1914,7 +1969,7 @@ void aclCommand(client *c) {
addReplyBulkCString(c,"client-info");
addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));
}
} else if (!strcasecmp(sub,"help")) {
} else if (c->argc == 2 && !strcasecmp(sub,"help")) {
const char *help[] = {
"LOAD -- Reload users from the ACL file.",
"SAVE -- Save the current config to the ACL file.",
+5 -4
View File
@@ -34,8 +34,9 @@
#include "zmalloc.h"
/* Create a new list. The created list can be freed with
* AlFreeList(), but private value of every node need to be freed
* by the user before to call AlFreeList().
* listRelease(), but private value of every node need to be freed
* by the user before to call listRelease(), or by setting a free method using
* listSetFreeMethod.
*
* On error, NULL is returned. Otherwise the pointer to the new list. */
list *listCreate(void)
@@ -217,8 +218,8 @@ void listRewindTail(list *list, listIter *li) {
* listDelNode(), but not to remove other elements.
*
* The function returns a pointer to the next element of the list,
* or NULL if there are no more elements, so the classical usage patter
* is:
* or NULL if there are no more elements, so the classical usage
* pattern is:
*
* iter = listGetIterator(list,<direction>);
* while ((node = listNext(iter)) != NULL) {
+45 -87
View File
@@ -48,6 +48,7 @@
#include "fastlock.h"
#include "zmalloc.h"
#include "config.h"
#include "serverassert.h"
#ifdef USE_MUTEX
thread_local int cOwnLock = 0;
@@ -84,8 +85,6 @@ fastlock g_lock("AE (global)");
#endif
thread_local aeEventLoop *g_eventLoopThisThread = NULL;
#define AE_ASSERT(x) if (!(x)) do { fprintf(stderr, "AE_ASSERT FAILURE %s: %d\n", __FILE__, __LINE__); *((volatile int*)1) = 1; } while(0)
/* Include the best multiplexing layer supported by this system.
* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
@@ -110,13 +109,6 @@ enum class AE_ASYNC_OP
CreateFileEvent,
};
struct aeCommandControl
{
std::condition_variable cv;
std::atomic<int> rval;
std::mutex mutexcv;
};
struct aeCommand
{
AE_ASYNC_OP op;
@@ -129,7 +121,6 @@ struct aeCommand
std::function<void()> *pfn;
};
void *clientData;
aeCommandControl *pctl;
};
void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
@@ -140,7 +131,7 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
auto cb = read(fd, &cmd, sizeof(aeCommand));
if (cb != sizeof(cmd))
{
AE_ASSERT(errno == EAGAIN);
serverAssert(errno == EAGAIN);
break;
}
switch (cmd.op)
@@ -150,19 +141,7 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
break;
case AE_ASYNC_OP::CreateFileEvent:
{
if (cmd.pctl != nullptr)
{
cmd.pctl->mutexcv.lock();
std::atomic_store(&cmd.pctl->rval, aeCreateFileEvent(eventLoop, cmd.fd, cmd.mask, cmd.fproc, cmd.clientData));
cmd.pctl->cv.notify_all();
cmd.pctl->mutexcv.unlock();
}
else
{
aeCreateFileEvent(eventLoop, cmd.fd, cmd.mask, cmd.fproc, cmd.clientData);
}
}
aeCreateFileEvent(eventLoop, cmd.fd, cmd.mask, cmd.fproc, cmd.clientData);
break;
case AE_ASYNC_OP::PostFunction:
@@ -176,19 +155,11 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
case AE_ASYNC_OP::PostCppFunction:
{
if (cmd.pctl != nullptr)
cmd.pctl->mutexcv.lock();
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
if (cmd.fLock)
ulock.lock();
(*cmd.pfn)();
if (cmd.pctl != nullptr)
{
cmd.pctl->cv.notify_all();
cmd.pctl->mutexcv.unlock();
}
delete cmd.pfn;
}
break;
@@ -227,7 +198,7 @@ ssize_t safe_write(int fd, const void *pv, size_t cb)
}
int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData, int fSynchronous)
aeFileProc *proc, void *clientData)
{
if (eventLoop == g_eventLoopThisThread)
return aeCreateFileEvent(eventLoop, fd, mask, proc, clientData);
@@ -240,31 +211,15 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
cmd.mask = mask;
cmd.fproc = proc;
cmd.clientData = clientData;
cmd.pctl = nullptr;
cmd.fLock = true;
if (fSynchronous)
{
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
cmd.pctl->mutexcv.lock();
}
auto size = safe_write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
if (size != sizeof(cmd))
{
AE_ASSERT(size == sizeof(cmd) || size <= 0);
AE_ASSERT(errno == EAGAIN);
serverAssert(size == sizeof(cmd) || size <= 0);
serverAssert(errno == EAGAIN);
ret = AE_ERR;
}
if (fSynchronous)
{
{
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::adopt_lock);
cmd.pctl->cv.wait(ulock);
ret = cmd.pctl->rval;
}
delete cmd.pctl;
}
return ret;
}
@@ -287,7 +242,7 @@ int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg)
return AE_OK;
}
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous, bool fLock, bool fForceQueue)
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fLock, bool fForceQueue)
{
if (eventLoop == g_eventLoopThisThread && !fForceQueue)
{
@@ -298,29 +253,18 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynch
aeCommand cmd = {};
cmd.op = AE_ASYNC_OP::PostCppFunction;
cmd.pfn = new (MALLOC_LOCAL) std::function<void()>(fn);
cmd.pctl = nullptr;
cmd.fLock = fLock;
if (fSynchronous)
{
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl;
cmd.pctl->mutexcv.lock();
}
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
if (size != sizeof(cmd))
return AE_ERR;
AE_ASSERT(size == sizeof(cmd));
int ret = AE_OK;
if (fSynchronous)
{
{
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::adopt_lock);
cmd.pctl->cv.wait(ulock);
ret = cmd.pctl->rval;
}
delete cmd.pctl;
if (!(!size || size == sizeof(cmd))) {
printf("Last error: %d\n", errno);
}
return ret;
serverAssert(!size || size == sizeof(cmd));
if (size == 0)
return AE_ERR;
return AE_OK;
}
aeEventLoop *aeCreateEventLoop(int setsize) {
@@ -352,7 +296,7 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
goto err;
eventLoop->fdCmdRead = rgfd[0];
eventLoop->fdCmdWrite = rgfd[1];
fcntl(eventLoop->fdCmdWrite, F_SETFL, O_NONBLOCK);
//fcntl(eventLoop->fdCmdWrite, F_SETFL, O_NONBLOCK);
fcntl(eventLoop->fdCmdRead, F_SETFL, O_NONBLOCK);
eventLoop->cevents = 0;
aeCreateFileEvent(eventLoop, eventLoop->fdCmdRead, AE_READABLE|AE_READ_THREADSAFE, aeProcessCmd, NULL);
@@ -373,6 +317,11 @@ int aeGetSetSize(aeEventLoop *eventLoop) {
return eventLoop->setsize;
}
/* Return the current EventLoop. */
aeEventLoop *aeGetCurrentEventLoop(){
return g_eventLoopThisThread;
}
/* Tells the next iteration/s of the event processing to set timeout of 0. */
void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
if (noWait)
@@ -389,7 +338,7 @@ void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
*
* Otherwise AE_OK is returned and the operation is successful. */
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
int i;
if (setsize == eventLoop->setsize) return AE_OK;
@@ -427,14 +376,14 @@ extern "C" void aeDeleteEventLoop(aeEventLoop *eventLoop) {
}
extern "C" void aeStop(aeEventLoop *eventLoop) {
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
eventLoop->stop = 1;
}
extern "C" int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
@@ -463,12 +412,12 @@ void aeDeleteFileEventAsync(aeEventLoop *eventLoop, int fd, int mask)
cmd.mask = mask;
cmd.fLock = true;
auto cb = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
AE_ASSERT(cb == sizeof(cmd));
serverAssert(cb == sizeof(cmd));
}
extern "C" void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
if (fd >= eventLoop->setsize) return;
aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return;
@@ -526,7 +475,7 @@ extern "C" long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long millise
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
long long id = eventLoop->timeEventNextId++;
aeTimeEvent *te;
@@ -548,7 +497,7 @@ extern "C" long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long millise
extern "C" int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
aeTimeEvent *te = eventLoop->timeEventHead;
while(te) {
if (te->id == id) {
@@ -573,7 +522,7 @@ extern "C" int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
*/
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
aeTimeEvent *te = eventLoop->timeEventHead;
aeTimeEvent *nearest = NULL;
@@ -589,7 +538,7 @@ static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
/* Process time events */
static int processTimeEvents(aeEventLoop *eventLoop) {
std::unique_lock<decltype(g_lock)> ulock(g_lock);
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
int processed = 0;
aeTimeEvent *te;
long long maxId;
@@ -634,8 +583,10 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
eventLoop->timeEventHead = te->next;
if (te->next)
te->next->prev = te->prev;
if (te->finalizerProc)
if (te->finalizerProc) {
if (!ulock.owns_lock()) ulock.lock();
te->finalizerProc(eventLoop, te->clientData);
}
zfree(te);
te = next;
continue;
@@ -654,6 +605,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
if (now_sec > te->when_sec ||
(now_sec == te->when_sec && now_ms >= te->when_ms))
{
if (!ulock.owns_lock()) ulock.lock();
int retval;
id = te->id;
@@ -746,7 +698,7 @@ extern "C" void ProcessEventCore(aeEventLoop *eventLoop, aeFileEvent *fe, int ma
* The function returns the number of events processed. */
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
AE_ASSERT(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
serverAssert(g_eventLoopThisThread == NULL || g_eventLoopThisThread == eventLoop);
int processed = 0, numevents;
/* Nothing to do? return ASAP */
@@ -870,9 +822,9 @@ void aeMain(aeEventLoop *eventLoop) {
ulock.lock();
eventLoop->beforesleep(eventLoop);
}
AE_ASSERT(!aeThreadOwnsLock()); // we should have relinquished it after processing
serverAssert(!aeThreadOwnsLock()); // we should have relinquished it after processing
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
AE_ASSERT(!aeThreadOwnsLock()); // we should have relinquished it after processing
serverAssert(!aeThreadOwnsLock()); // we should have relinquished it after processing
}
}
@@ -890,9 +842,15 @@ void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep,
eventLoop->aftersleepFlags = flags;
}
thread_local spin_worker tl_worker = nullptr;
void setAeLockSetThreadSpinWorker(spin_worker worker)
{
tl_worker = worker;
}
void aeAcquireLock()
{
g_lock.lock();
g_lock.lock(tl_worker);
}
int aeTryAcquireLock(int fWeak)
+4 -2
View File
@@ -135,7 +135,7 @@ aeEventLoop *aeCreateEventLoop(int setsize);
int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg);
#ifdef __cplusplus
} // EXTERN C
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous = false, bool fLock = true, bool fForceQueue = false);
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fLock = true, bool fForceQueue = false);
extern "C" {
#endif
void aeDeleteEventLoop(aeEventLoop *eventLoop);
@@ -144,7 +144,7 @@ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData);
int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData, int fSynchronous);
aeFileProc *proc, void *clientData);
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
void aeDeleteFileEventAsync(aeEventLoop *eventLoop, int fd, int mask);
@@ -160,9 +160,11 @@ const char *aeGetApiName(void);
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep, int flags);
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep, int flags);
int aeGetSetSize(aeEventLoop *eventLoop);
aeEventLoop *aeGetCurrentEventLoop();
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
void aeSetDontWait(aeEventLoop *eventLoop, int noWait);
void setAeLockSetThreadSpinWorker(spin_worker worker);
void aeAcquireLock();
int aeTryAcquireLock(int fWeak);
void aeReleaseLock();
+1 -1
View File
@@ -232,7 +232,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
/*
* ENOMEM is a potentially transient condition, but the kernel won't
* generally return it unless things are really bad. EAGAIN indicates
* we've reached an resource limit, for which it doesn't make sense to
* we've reached a resource limit, for which it doesn't make sense to
* retry (counter-intuitively). All other errors indicate a bug. In any
* of these cases, the best we can do is to abort.
*/
+1 -1
View File
@@ -9,7 +9,7 @@ public:
{
}
void arm(client *c, bool fIfNeeded = false) // if a client is passed, then the client is already locked
void arm(client *c = nullptr, bool fIfNeeded = false) // if a client is passed, then the client is already locked
{
if (m_fArmed)
return;
+67 -38
View File
@@ -566,7 +566,7 @@ sds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {
return dst;
}
/* Create the sds representation of an PEXPIREAT command, using
/* Create the sds representation of a PEXPIREAT command, using
* 'seconds' as time to live and 'cmd' to understand what command
* we are translating into a PEXPIREAT.
*
@@ -642,7 +642,7 @@ sds catAppendOnlyExpireMemberAtCommand(sds buf, struct redisCommand *cmd, robj *
when += mstime();
robj *argvNew[4];
argvNew[0] = createStringObject("PEXPIREMEMBERAT",15);
argvNew[0] = shared.pexpirememberat;
argvNew[1] = argv[1];
argvNew[2] = argv[2];
argvNew[3] = createStringObjectFromLongLong(when);
@@ -752,6 +752,7 @@ struct client *createAOFClient(void) {
c->querybuf_peak = 0;
c->argc = 0;
c->argv = NULL;
c->argv_len_sum = 0;
c->bufpos = 0;
c->flags = 0;
c->fPendingAsyncWrite = FALSE;
@@ -781,6 +782,7 @@ void freeFakeClientArgv(struct client *c) {
for (j = 0; j < c->argc; j++)
decrRefCount(c->argv[j]);
zfree(c->argv);
c->argv_len_sum = 0;
}
void freeFakeClient(struct client *c) {
@@ -1159,7 +1161,7 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
}
} else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = (zset*)ptrFromObj(o);
dictIterator *di = dictGetIterator(zs->pdict);
dictIterator *di = dictGetIterator(zs->dict);
dictEntry *de;
while((de = dictNext(di)) != NULL) {
@@ -1292,16 +1294,24 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
* the ID, the second is an array of field-value pairs. */
/* Emit the XADD <key> <id> ...fields... command. */
if (rioWriteBulkCount(r,'*',3+numfields*2) == 0) return 0;
if (rioWriteBulkString(r,"XADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (rioWriteBulkStreamID(r,&id) == 0) return 0;
if (!rioWriteBulkCount(r,'*',3+numfields*2) ||
!rioWriteBulkString(r,"XADD",4) ||
!rioWriteBulkObject(r,key) ||
!rioWriteBulkStreamID(r,&id))
{
streamIteratorStop(&si);
return 0;
}
while(numfields--) {
unsigned char *field, *value;
int64_t field_len, value_len;
streamIteratorGetField(&si,&field,&value,&field_len,&value_len);
if (rioWriteBulkString(r,(char*)field,field_len) == 0) return 0;
if (rioWriteBulkString(r,(char*)value,value_len) == 0) return 0;
if (!rioWriteBulkString(r,(char*)field,field_len) ||
!rioWriteBulkString(r,(char*)value,value_len))
{
streamIteratorStop(&si);
return 0;
}
}
}
} else {
@@ -1309,22 +1319,30 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
* the key we are serializing is an empty string, which is possible
* for the Stream type. */
id.ms = 0; id.seq = 1;
if (rioWriteBulkCount(r,'*',7) == 0) return 0;
if (rioWriteBulkString(r,"XADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (rioWriteBulkString(r,"MAXLEN",6) == 0) return 0;
if (rioWriteBulkString(r,"0",1) == 0) return 0;
if (rioWriteBulkStreamID(r,&id) == 0) return 0;
if (rioWriteBulkString(r,"x",1) == 0) return 0;
if (rioWriteBulkString(r,"y",1) == 0) return 0;
if (!rioWriteBulkCount(r,'*',7) ||
!rioWriteBulkString(r,"XADD",4) ||
!rioWriteBulkObject(r,key) ||
!rioWriteBulkString(r,"MAXLEN",6) ||
!rioWriteBulkString(r,"0",1) ||
!rioWriteBulkStreamID(r,&id) ||
!rioWriteBulkString(r,"x",1) ||
!rioWriteBulkString(r,"y",1))
{
streamIteratorStop(&si);
return 0;
}
}
/* Append XSETID after XADD, make sure lastid is correct,
* in case of XDEL lastid. */
if (rioWriteBulkCount(r,'*',3) == 0) return 0;
if (rioWriteBulkString(r,"XSETID",6) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (rioWriteBulkStreamID(r,&s->last_id) == 0) return 0;
if (!rioWriteBulkCount(r,'*',3) ||
!rioWriteBulkString(r,"XSETID",6) ||
!rioWriteBulkObject(r,key) ||
!rioWriteBulkStreamID(r,&s->last_id))
{
streamIteratorStop(&si);
return 0;
}
/* Create all the stream consumer groups. */
@@ -1335,12 +1353,17 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
while(raxNext(&ri)) {
streamCG *group = (streamCG*)ri.data;
/* Emit the XGROUP CREATE in order to create the group. */
if (rioWriteBulkCount(r,'*',5) == 0) return 0;
if (rioWriteBulkString(r,"XGROUP",6) == 0) return 0;
if (rioWriteBulkString(r,"CREATE",6) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (rioWriteBulkString(r,(char*)ri.key,ri.key_len) == 0) return 0;
if (rioWriteBulkStreamID(r,&group->last_id) == 0) return 0;
if (!rioWriteBulkCount(r,'*',5) ||
!rioWriteBulkString(r,"XGROUP",6) ||
!rioWriteBulkString(r,"CREATE",6) ||
!rioWriteBulkObject(r,key) ||
!rioWriteBulkString(r,(char*)ri.key,ri.key_len) ||
!rioWriteBulkStreamID(r,&group->last_id))
{
raxStop(&ri);
streamIteratorStop(&si);
return 0;
}
/* Generate XCLAIMs for each consumer that happens to
* have pending entries. Empty consumers have no semantical
@@ -1361,6 +1384,10 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
ri.key_len,consumer,
ri_pel.key,nack) == 0)
{
raxStop(&ri_pel);
raxStop(&ri_cons);
raxStop(&ri);
streamIteratorStop(&si);
return 0;
}
}
@@ -1415,7 +1442,7 @@ int rewriteAppendOnlyFileRio(rio *aof) {
for (j = 0; j < cserver.dbnum; j++) {
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
redisDb *db = g_pserver->db+j;
dict *d = db->pdict;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
@@ -1502,7 +1529,7 @@ werr:
* are inserted using a single command. */
int rewriteAppendOnlyFile(char *filename) {
rio aof;
FILE *fp;
FILE *fp = NULL;
char tmpfile[256];
char byte;
int nodata = 0;
@@ -1580,9 +1607,10 @@ int rewriteAppendOnlyFile(char *filename) {
goto werr;
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
if (fsync(fileno(fp)) == -1) goto werr;
if (fclose(fp) == EOF) goto werr;
if (fflush(fp)) goto werr;
if (fsync(fileno(fp))) goto werr;
if (fclose(fp)) { fp = NULL; goto werr; }
fp = NULL;
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
@@ -1598,7 +1626,7 @@ int rewriteAppendOnlyFile(char *filename) {
werr:
serverLog(LL_WARNING,"Write error writing append only file on disk: %s", strerror(errno));
fclose(fp);
if (fp) fclose(fp);
unlink(tmpfile);
stopSaving(0);
return C_ERR;
@@ -1712,7 +1740,7 @@ int rewriteAppendOnlyFileBackground(void) {
if (hasActiveChildProcess()) return C_ERR;
if (aofCreatePipes() != C_OK) return C_ERR;
openChildInfoPipe();
if ((childpid = redisFork()) == 0) {
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
char tmpfile[256];
/* Child */
@@ -1720,7 +1748,7 @@ int rewriteAppendOnlyFileBackground(void) {
redisSetCpuAffinity(g_pserver->aof_rewrite_cpulist);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_AOF, "AOF rewrite");
sendChildCOWInfo(CHILD_TYPE_AOF, "AOF rewrite");
exitFromChild(0);
} else {
exitFromChild(1);
@@ -1740,6 +1768,7 @@ int rewriteAppendOnlyFileBackground(void) {
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 g_pserver->aof_rewrite_buf will start
@@ -1769,10 +1798,10 @@ void aofRemoveTempFile(pid_t childpid) {
char tmpfile[256];
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid);
unlink(tmpfile);
bg_unlink(tmpfile);
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) childpid);
unlink(tmpfile);
bg_unlink(tmpfile);
}
/* Update the g_pserver->aof_current_size field explicitly using stat(2)
@@ -1927,7 +1956,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
"Background AOF rewrite terminated with error");
} else {
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error condition. */
* triggering an error condition. */
if (bysignal != SIGUSR1)
g_pserver->aof_lastbgrewrite_status = C_ERR;
+17 -10
View File
@@ -28,14 +28,21 @@
*/
const char *ascii_logo =
" \n"
" \n"
" KeyDB %s (%s/%d) %s bit\n"
" \n"
" Running in %s mode\n"
" Port: %d\n"
" PID: %ld\n"
" \n"
" %s\n"
" \n"
" \n"
" _ \n"
" _-(+)-_ \n"
" _-- / \\ --_ \n"
" _-- / \\ --_ KeyDB %s (%s/%d) %s bit \n"
" __-- / \\ --__ \n"
" (+) _ / \\ _ (+) Running in %s mode\n"
" | -- / \\ -- | Port: %d\n"
" | /--_ _ _--\\ | PID: %ld\n"
" | / -(+)- \\ | \n"
" | / | \\ | https://docs.keydb.dev \n"
" | / | \\ | \n"
" | / | \\ | \n"
" (+)_ -- -- -- | -- -- -- _(+) \n"
" --_ | _-- \n"
" --_ | _-- \n"
" -(+)- %s\n"
" \n";
+1 -1
View File
@@ -21,7 +21,7 @@
*
* Never use return value from the macros, instead use the AtomicGetIncr()
* if you need to get the current value and increment it atomically, like
* in the followign example:
* in the following example:
*
* long oldvalue;
* atomicGetIncr(myvar,oldvalue,1);
+4 -6
View File
@@ -168,10 +168,7 @@ void *bioProcessBackgroundJobs(void *arg) {
redisSetCpuAffinity(g_pserver->bio_cpulist);
/* Make the thread killable at any time, so that bioKillThreads()
* can work reliably. */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
makeThreadKillable();
pthread_mutex_lock(&bio_mutex[type]);
/* Block SIGALRM so we are sure that only the main thread will
@@ -206,7 +203,7 @@ void *bioProcessBackgroundJobs(void *arg) {
/* What we free changes depending on what arguments are set:
* arg1 -> free the object at pointer.
* arg2 & arg3 -> free two dictionaries (a Redis DB).
* only arg3 -> free the skiplist. */
* only arg3 -> free the radix tree. */
if (job->arg1)
lazyfreeFreeObjectFromBioThread((robj*)job->arg1);
else if (job->arg2 && job->arg3)
@@ -268,10 +265,11 @@ void bioKillThreads(void) {
int err, j;
for (j = 0; j < BIO_NUM_OPS; j++) {
if (bio_threads[j] == pthread_self()) continue;
if (bio_threads[j] && pthread_cancel(bio_threads[j]) == 0) {
if ((err = pthread_join(bio_threads[j],NULL)) != 0) {
serverLog(LL_WARNING,
"Bio thread for job type #%d can be joined: %s",
"Bio thread for job type #%d can not be joined: %s",
j, strerror(err));
} else {
serverLog(LL_WARNING,
+13 -10
View File
@@ -36,7 +36,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. */
* work with an input string length up to 512 MB. */
size_t redisPopcount(const void *s, long count) {
size_t bits = 0;
unsigned char *p = (unsigned char*)s;
@@ -107,7 +107,7 @@ long redisBitpos(const void *s, unsigned long count, int bit) {
int found;
/* Process whole words first, seeking for first word that is not
* all ones or all zeros respectively if we are lookig for zeros
* all ones or all zeros respectively if we are looking for zeros
* or ones. This is much faster with large strings having contiguous
* blocks of 1 or 0 bits compared to the vanilla bit per bit processing.
*
@@ -257,7 +257,7 @@ int64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {
/* If the top significant bit is 1, propagate it to all the
* higher bits for two's complement representation of signed
* integers. */
if (value & ((uint64_t)1 << (bits-1)))
if (bits < 64 && (value & ((uint64_t)1 << (bits-1))))
value |= ((uint64_t)-1) << bits;
return value;
}
@@ -356,7 +356,6 @@ int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int
handle_wrap:
{
uint64_t mask = ((uint64_t)-1) << bits;
uint64_t msb = (uint64_t)1 << (bits-1);
uint64_t a = value, b = incr, c;
c = a+b; /* Perform addition as unsigned so that's defined. */
@@ -364,10 +363,13 @@ handle_wrap:
/* If the sign bit is set, propagate to all the higher order
* bits, to cap the negative value. If it's clear, mask to
* the positive integer limit. */
if (c & msb) {
c |= mask;
} else {
c &= ~mask;
if (bits < 64) {
uint64_t mask = ((uint64_t)-1) << bits;
if (c & msb) {
c |= mask;
} else {
c &= ~mask;
}
}
*limit = c;
}
@@ -496,7 +498,7 @@ robj *lookupStringForBitCommand(client *c, size_t maxbit) {
* in 'len'. The user is required to pass (likely stack allocated) buffer
* 'llbuf' of at least LONG_STR_SIZE bytes. Such a buffer is used in the case
* the object is integer encoded in order to provide the representation
* without usign heap allocation.
* without using heap allocation.
*
* The function returns the pointer to the object array of bytes representing
* the string it contains, that may be a pointer to 'llbuf' or to the
@@ -831,11 +833,12 @@ void bitopCommand(client *c) {
setKey(c,c->db,targetkey,o);
notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id);
decrRefCount(o);
g_pserver->dirty++;
} else if (dbDelete(c->db,targetkey)) {
signalModifiedKey(c,c->db,targetkey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",targetkey,c->db->id);
g_pserver->dirty++;
}
g_pserver->dirty++;
addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
}
+18 -11
View File
@@ -53,7 +53,7 @@
* to 0, no timeout is processed).
* It usually just needs to send a reply to the client.
*
* When implementing a new type of blocking opeation, the implementation
* When implementing a new type of blocking operation, the implementation
* should modify unblockClient() and replyToBlockedClientTimedOut() in order
* to handle the btype-specific behavior of this two functions.
* If the blocking operation waits for certain keys to change state, the
@@ -128,7 +128,7 @@ void processUnblockedClients(int iel) {
/* This function will schedule the client for reprocessing at a safe time.
*
* This is useful when a client was blocked for some reason (blocking opeation,
* This is useful when a client was blocked for some reason (blocking operation,
* CLIENT PAUSE, or whatever), because it may end with some accumulated query
* buffer that needs to be processed ASAP:
*
@@ -188,9 +188,9 @@ void replyToBlockedClientTimedOut(client *c) {
if (c->btype == BLOCKED_LIST ||
c->btype == BLOCKED_ZSET ||
c->btype == BLOCKED_STREAM) {
addReplyNullArrayAsync(c);
addReplyNullArray(c);
} else if (c->btype == BLOCKED_WAIT) {
addReplyLongLongAsync(c,replicationCountAcksByOffset(c->bpop.reploffset));
addReplyLongLong(c,replicationCountAcksByOffset(c->bpop.reploffset));
} else if (c->btype == BLOCKED_MODULE) {
moduleBlockedClientTimedOut(c);
} else {
@@ -216,7 +216,7 @@ void disconnectAllBlockedClients(void) {
fastlock_lock(&c->lock);
if (c->flags & CLIENT_BLOCKED) {
addReplySdsAsync(c,sdsnew(
addReplySds(c,sdsnew(
"-UNBLOCKED force unblock from blocking operation, "
"instance state changed (master -> replica?)\r\n"));
unblockClient(c);
@@ -373,7 +373,7 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
/* If the group was not found, send an error
* to the consumer. */
if (!group) {
addReplyErrorAsync(receiver,
addReplyError(receiver,
"-NOGROUP the consumer group this client "
"was blocked on no longer exists");
unblockClient(receiver);
@@ -404,12 +404,12 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
* extracted from it. Wrapped in a single-item
* array, since we have just one key. */
if (receiver->resp == 2) {
addReplyArrayLenAsync(receiver,1);
addReplyArrayLenAsync(receiver,2);
addReplyArrayLen(receiver,1);
addReplyArrayLen(receiver,2);
} else {
addReplyMapLenAsync(receiver,1);
addReplyMapLen(receiver,1);
}
addReplyBulkAsync(receiver,rl->key);
addReplyBulk(receiver,rl->key);
streamPropInfo pi = {
rl->key,
@@ -522,7 +522,7 @@ void handleClientsBlockedOnKeys(void) {
serverTL->fixed_time_expire++;
updateCachedTime(0);
/* Serve clients blocked on list key. */
/* Serve clients blocked on the key. */
robj *o = lookupKeyWrite(rl->db,rl->key);
if (o != NULL) {
@@ -672,6 +672,13 @@ void signalKeyAsReady(redisDb *db, robj *key) {
/* Key was already signaled? No need to queue it again. */
if (dictFind(db->ready_keys,key) != NULL) return;
if (key->getrefcount() == OBJ_STATIC_REFCOUNT) {
// Sometimes a key may be stack allocated, we'll need to dupe it
robj *newKey = createStringObject(szFromObj(key), sdslen(szFromObj(key)));
newKey->setrefcount(0); // Start with 0 but don't free
key = newKey;
}
/* Ok, we need to queue this key into g_pserver->ready_keys. */
rl = (readyList*)zmalloc(sizeof(*rl), MALLOC_SHARED);
rl->key = key;
+3 -3
View File
@@ -76,11 +76,11 @@ void receiveChildInfo(void) {
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 (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
if (g_pserver->child_info_data.process_type == CHILD_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) {
} else if (g_pserver->child_info_data.process_type == CHILD_TYPE_AOF) {
g_pserver->stat_aof_cow_bytes = g_pserver->child_info_data.cow_size;
} else if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_MODULE) {
} else if (g_pserver->child_info_data.process_type == CHILD_TYPE_MODULE) {
g_pserver->stat_module_cow_bytes = g_pserver->child_info_data.cow_size;
}
}
+190 -65
View File
@@ -77,6 +77,9 @@ uint64_t clusterGetMaxEpoch(void);
int clusterBumpConfigEpochWithoutConsensus(void);
void moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len);
#define RCVBUF_INIT_LEN 1024
#define RCVBUF_MAX_PREALLOC (1<<20) /* 1MB */
struct redisMaster *getFirstMaster()
{
serverAssert(listLength(g_pserver->masters) <= 1);
@@ -394,7 +397,7 @@ void clusterSaveConfigOrDie(int do_fsync) {
}
}
/* Lock the cluster config using flock(), and leaks the file descritor used to
/* Lock the cluster config using flock(), and leaks the file descriptor used to
* acquire the lock so that the file will be locked forever.
*
* This works because we always update nodes.conf with a new version
@@ -435,7 +438,15 @@ int clusterLockConfig(char *filename) {
return C_ERR;
}
/* Lock acquired: leak the 'fd' by not closing it, so that we'll retain the
* lock to the file as long as the process exists. */
* lock to the file as long as the process exists.
*
* After fork, the child process will get the fd opened by the parent process,
* we need save `fd` to `cluster_config_file_lock_fd`, so that in redisFork(),
* it will be closed in the child process.
* If it is not closed, when the main process is killed -9, but the child process
* (redis-aof-rewrite) is still alive, the fd(lock) will still be held by the
* child process, and the main process will fail to get lock, means fail to start. */
g_pserver->cluster_config_file_lock_fd = fd;
#endif /* __sun */
return C_OK;
@@ -490,6 +501,7 @@ void clusterInit(void) {
/* Lock the cluster config file to make sure every node uses
* its own nodes.conf. */
g_pserver->cluster_config_file_lock_fd = -1;
if (clusterLockConfig(g_pserver->cluster_configfile) == C_ERR)
exit(1);
@@ -557,13 +569,13 @@ void clusterInit(void) {
/* Reset a node performing a soft or hard reset:
*
* 1) All other nodes are forget.
* 1) All other nodes are forgotten.
* 2) All the assigned / open slots are released.
* 3) If the node is a slave, it turns into a master.
* 5) Only for hard reset: a new Node ID is generated.
* 6) Only for hard reset: currentEpoch and configEpoch are set to 0.
* 7) The new configuration is saved and the cluster state updated.
* 8) If the node was a slave, the whole data set is flushed away. */
* 4) Only for hard reset: a new Node ID is generated.
* 5) Only for hard reset: currentEpoch and configEpoch are set to 0.
* 6) The new configuration is saved and the cluster state updated.
* 7) If the node was a slave, the whole data set is flushed away. */
void clusterReset(int hard) {
dictIterator *di;
dictEntry *de;
@@ -630,7 +642,8 @@ clusterLink *createClusterLink(clusterNode *node) {
clusterLink *link = (clusterLink*)zmalloc(sizeof(*link), MALLOC_LOCAL);
link->ctime = mstime();
link->sndbuf = sdsempty();
link->rcvbuf = sdsempty();
link->rcvbuf = (char*)zmalloc(link->rcvbuf_alloc = RCVBUF_INIT_LEN);
link->rcvbuf_len = 0;
link->node = node;
link->conn = NULL;
return link;
@@ -657,7 +670,7 @@ void freeClusterLink(clusterLink *link) {
link->conn = NULL;
}
sdsfree(link->sndbuf);
sdsfree(link->rcvbuf);
zfree(link->rcvbuf);
if (link->node)
link->node->link = NULL;
zfree(link);
@@ -675,7 +688,7 @@ static void clusterConnAcceptHandler(connection *conn) {
/* Create a link object we use to handle the connection.
* It gets passed to the readable handler when data is available.
* Initiallly the link->node pointer is set to NULL as we don't know
* Initially the link->node pointer is set to NULL as we don't know
* which node is, but the right node is references once we know the
* node identity. */
link = createClusterLink(NULL);
@@ -708,7 +721,17 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
return;
}
connection *conn = g_pserver->tls_cluster ? connCreateAcceptedTLS(cfd,1) : connCreateAcceptedSocket(cfd);
connection *conn = g_pserver->tls_cluster ?
connCreateAcceptedTLS(cfd, TLS_CLIENT_AUTH_YES) : connCreateAcceptedSocket(cfd);
/* Make sure connection is not in an error state */
if (connGetState(conn) != CONN_STATE_ACCEPTING) {
serverLog(LL_VERBOSE,
"Error creating an accepting connection for cluster node: %s",
connGetLastError(conn));
connClose(conn);
return;
}
connNonBlock(conn);
connEnableTcpNoDelay(conn);
@@ -729,6 +752,16 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
}
}
/* Return the approximated number of sockets we are using in order to
* take the cluster bus connections. */
unsigned long getClusterConnectionsCount(void) {
/* We decrement the number of nodes by one, since there is the
* "myself" node too in the list. Each node uses two file descriptors,
* one incoming and one outgoing, thus the multiplication by 2. */
return g_pserver->cluster_enabled ?
((dictSize(g_pserver->cluster->nodes)-1)*2) : 0;
}
/* -----------------------------------------------------------------------------
* Key space handling
* -------------------------------------------------------------------------- */
@@ -1069,7 +1102,7 @@ uint64_t clusterGetMaxEpoch(void) {
* 3) Persist the configuration on disk before sending packets with the
* new configuration.
*
* If the new config epoch is generated and assigend, C_OK is returned,
* If the new config epoch is generated and assigned, C_OK is returned,
* otherwise C_ERR is returned (since the node has already the greatest
* configuration around) and no operation is performed.
*
@@ -1142,7 +1175,7 @@ int clusterBumpConfigEpochWithoutConsensus(void) {
*
* In general we want a system that eventually always ends with different
* masters having different configuration epochs whatever happened, since
* nothign is worse than a split-brain condition in a distributed system.
* nothing is worse than a split-brain condition in a distributed system.
*
* BEHAVIOR
*
@@ -1201,7 +1234,7 @@ void clusterHandleConfigEpochCollision(clusterNode *sender) {
* entries from the black list. This is an O(N) operation but it is not a
* problem since add / exists operations are called very infrequently and
* the hash table is supposed to contain very little elements at max.
* However without the cleanup during long uptimes and with some automated
* However without the cleanup during long uptime and with some automated
* node add/removal procedures, entries could accumulate. */
void clusterBlacklistCleanup(void) {
dictIterator *di;
@@ -1292,8 +1325,11 @@ void markNodeAsFailingIfNeeded(clusterNode *node) {
node->fail_time = mstime();
/* Broadcast the failing node name to everybody, forcing all the other
* reachable nodes to flag the node as FAIL. */
if (nodeIsMaster(myself)) clusterSendFail(node->name);
* reachable nodes to flag the node as FAIL.
* We do that even if this node is a replica and not a master: anyway
* the failing state is triggered collecting failure reports from masters,
* so here the replica is only helping propagating this status. */
clusterSendFail(node->name);
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
}
@@ -1352,12 +1388,12 @@ int clusterHandshakeInProgress(char *ip, int port, int cport) {
return de != NULL;
}
/* Start an handshake with the specified address if there is not one
/* Start a handshake with the specified address if there is not one
* already in progress. Returns non-zero if the handshake was actually
* started. On error zero is returned and errno is set to one of the
* following values:
*
* EAGAIN - There is already an handshake in progress for this address.
* EAGAIN - There is already a handshake in progress for this address.
* EINVAL - IP or port are not valid. */
int clusterStartHandshake(char *ip, int port, int cport) {
clusterNode *n;
@@ -1738,7 +1774,7 @@ int clusterProcessPacket(clusterLink *link) {
/* Perform sanity checks */
if (totlen < 16) return 1; /* At least signature, version, totlen, count. */
if (totlen > sdslen(link->rcvbuf)) return 1;
if (totlen > link->rcvbuf_len) return 1;
if (ntohs(hdr->ver) != CLUSTER_PROTO_VER) {
/* Can't handle messages of different versions. */
@@ -1786,7 +1822,7 @@ int clusterProcessPacket(clusterLink *link) {
} else if (type == CLUSTERMSG_TYPE_MODULE) {
uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);
explen += sizeof(clusterMsgDataPublish) -
explen += sizeof(clusterMsgModule) -
3 + ntohl(hdr->data.module.msg.len);
if (totlen != explen) return 1;
}
@@ -1803,7 +1839,7 @@ int clusterProcessPacket(clusterLink *link) {
if (sender) sender->data_received = now;
if (sender && !nodeInHandshake(sender)) {
/* Update our curretEpoch if we see a newer epoch in the cluster. */
/* Update our currentEpoch if we see a newer epoch in the cluster. */
senderCurrentEpoch = ntohu64(hdr->currentEpoch);
senderConfigEpoch = ntohu64(hdr->configEpoch);
if (senderCurrentEpoch > g_pserver->cluster->currentEpoch)
@@ -2295,7 +2331,7 @@ void clusterReadHandler(connection *conn) {
unsigned int readlen, rcvbuflen;
while(1) { /* Read as long as there is data to read. */
rcvbuflen = sdslen(link->rcvbuf);
rcvbuflen = link->rcvbuf_len;
if (rcvbuflen < 8) {
/* First, obtain the first 8 bytes to get the full message
* length. */
@@ -2331,7 +2367,15 @@ void clusterReadHandler(connection *conn) {
return;
} else {
/* Read data and recast the pointer to the new buffer. */
link->rcvbuf = sdscatlen(link->rcvbuf,buf,nread);
size_t unused = link->rcvbuf_alloc - link->rcvbuf_len;
if ((size_t)nread > unused) {
size_t required = link->rcvbuf_len + nread;
/* If less than 1mb, grow to twice the needed size, if larger grow by 1mb. */
link->rcvbuf_alloc = required < RCVBUF_MAX_PREALLOC ? required * 2: required + RCVBUF_MAX_PREALLOC;
link->rcvbuf = (char*)zrealloc(link->rcvbuf, link->rcvbuf_alloc);
}
memcpy(link->rcvbuf + link->rcvbuf_len, buf, nread);
link->rcvbuf_len += nread;
hdr = (clusterMsg*) link->rcvbuf;
rcvbuflen += nread;
}
@@ -2339,8 +2383,11 @@ void clusterReadHandler(connection *conn) {
/* Total length obtained? Process this packet. */
if (rcvbuflen >= 8 && rcvbuflen == ntohl(hdr->totlen)) {
if (clusterProcessPacket(link)) {
sdsfree(link->rcvbuf);
link->rcvbuf = sdsempty();
if (link->rcvbuf_alloc > RCVBUF_INIT_LEN) {
zfree(link->rcvbuf);
link->rcvbuf = (char*)zmalloc(link->rcvbuf_alloc = RCVBUF_INIT_LEN);
}
link->rcvbuf_len = 0;
} else {
return; /* Link no longer valid. */
}
@@ -2421,7 +2468,7 @@ void clusterBuildMessageHdr(clusterMsg *hdr, int type) {
* first byte is zero, they'll do auto discovery. */
memset(hdr->myip,0,NET_IP_STR_LEN);
if (g_pserver->cluster_announce_ip) {
strncpy(hdr->myip,g_pserver->cluster_announce_ip,NET_IP_STR_LEN);
strncpy(hdr->myip,g_pserver->cluster_announce_ip,NET_IP_STR_LEN-1);
hdr->myip[NET_IP_STR_LEN-1] = '\0';
}
@@ -2498,7 +2545,7 @@ void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {
}
/* Send a PING or PONG packet to the specified node, making sure to add enough
* gossip informations. */
* gossip information. */
void clusterSendPing(clusterLink *link, int type) {
unsigned char *buf;
clusterMsg *hdr;
@@ -2518,7 +2565,7 @@ void clusterSendPing(clusterLink *link, int type) {
* node_timeout we exchange with each other node at least 4 packets
* (we ping in the worst case in node_timeout/2 time, and we also
* receive two pings from the host), we have a total of 8 packets
* in the node_timeout*2 falure reports validity time. So we have
* in the node_timeout*2 failure reports validity time. So we have
* that, for a single PFAIL node, we can expect to receive the following
* number of failure reports (in the specified window of time):
*
@@ -2545,7 +2592,7 @@ void clusterSendPing(clusterLink *link, int type) {
* faster to propagate to go from PFAIL to FAIL state. */
int pfail_wanted = g_pserver->cluster->stats_pfail_nodes;
/* Compute the maxium totlen to allocate our buffer. We'll fix the totlen
/* Compute the maximum totlen to allocate our buffer. We'll fix the totlen
* later according to the number of gossip sections we really were able
* to put inside the packet. */
totlen = sizeof(clusterMsg)-sizeof(union clusterMsgData);
@@ -2582,7 +2629,7 @@ void clusterSendPing(clusterLink *link, int type) {
if (thisNode->flags & (CLUSTER_NODE_HANDSHAKE|CLUSTER_NODE_NOADDR) ||
(thisNode->link == NULL && thisNode->numslots == 0))
{
freshnodes--; /* Tecnically not correct, but saves CPU. */
freshnodes--; /* Technically not correct, but saves CPU. */
continue;
}
@@ -3167,7 +3214,7 @@ void clusterHandleSlaveFailover(void) {
}
}
/* If the previous failover attempt timedout and the retry time has
/* If the previous failover attempt timeout and the retry time has
* elapsed, we can setup a new one. */
if (auth_age > auth_retry_time) {
g_pserver->cluster->failover_auth_time = mstime() +
@@ -3273,7 +3320,7 @@ void clusterHandleSlaveFailover(void) {
*
* Slave migration is the process that allows a slave of a master that is
* already covered by at least another slave, to "migrate" to a master that
* is orpaned, that is, left with no working slaves.
* is orphaned, that is, left with no working slaves.
* ------------------------------------------------------------------------- */
/* This function is responsible to decide if this replica should be migrated
@@ -3290,7 +3337,7 @@ void clusterHandleSlaveFailover(void) {
* the nodes anyway, so we spend time into clusterHandleSlaveMigration()
* if definitely needed.
*
* The fuction is called with a pre-computed max_slaves, that is the max
* The function is called with a pre-computed max_slaves, that is the max
* number of working (not in FAIL state) slaves for a single master.
*
* Additional conditions for migration are examined inside the function.
@@ -3409,7 +3456,7 @@ void clusterHandleSlaveMigration(int max_slaves) {
* data loss due to the asynchronous master-slave replication.
* -------------------------------------------------------------------------- */
/* Reset the manual failover state. This works for both masters and slavesa
/* Reset the manual failover state. This works for both masters and slaves
* as all the state about manual failover is cleared.
*
* The function can be used both to initialize the manual failover state at
@@ -3495,7 +3542,7 @@ void clusterCron(void) {
* duplicating the string. This way later we can check if
* the address really changed. */
prev_ip = zstrdup(prev_ip);
strncpy(myself->ip,g_pserver->cluster_announce_ip,NET_IP_STR_LEN);
strncpy(myself->ip,g_pserver->cluster_announce_ip,NET_IP_STR_LEN-1);
myself->ip[NET_IP_STR_LEN-1] = '\0';
} else {
myself->ip[0] = '\0'; /* Force autodetection. */
@@ -3701,7 +3748,7 @@ void clusterCron(void) {
replicationAddMaster(myself->slaveof->ip, myself->slaveof->port);
}
/* Abourt a manual failover if the timeout is reached. */
/* Abort a manual failover if the timeout is reached. */
manualFailoverCheckTimeout();
if (nodeIsSlave(myself)) {
@@ -3806,12 +3853,12 @@ int clusterNodeSetSlotBit(clusterNode *n, int slot) {
* target for replicas migration, if and only if at least one of
* the other masters has slaves right now.
*
* Normally masters are valid targerts of replica migration if:
* Normally masters are valid targets of replica migration if:
* 1. The used to have slaves (but no longer have).
* 2. They are slaves failing over a master that used to have slaves.
*
* However new masters with slots assigned are considered valid
* migration tagets if the rest of the cluster is not a slave-less.
* migration targets if the rest of the cluster is not a slave-less.
*
* See https://github.com/antirez/redis/issues/3043 for more info. */
if (n->numslots == 1 && clusterMastersHaveSlaves())
@@ -3995,7 +4042,7 @@ void clusterUpdateState(void) {
* A) If no other node is in charge according to the current cluster
* configuration, we add these slots to our node.
* B) If according to our config other nodes are already in charge for
* this lots, we set the slots as IMPORTING from our point of view
* this slots, we set the slots as IMPORTING from our point of view
* in order to justify we have those slots, and in order to make
* keydb-trib aware of the issue, so that it can try to fix it.
* 2) If we find data in a DB different than DB0 we return C_ERR to
@@ -4024,7 +4071,7 @@ int verifyClusterConfigWithData(void) {
/* Make sure we only have keys in DB0. */
for (j = 1; j < cserver.dbnum; j++) {
if (dictSize(g_pserver->db[j].pdict)) return C_ERR;
if (dictSize(g_pserver->db[j].dict)) return C_ERR;
}
/* Check that all the slots we see populated memory have a corresponding
@@ -4141,11 +4188,15 @@ sds clusterGenNodeDescription(clusterNode *node) {
else
ci = sdscatlen(ci," - ",3);
unsigned long long nodeEpoch = node->configEpoch;
if (nodeIsSlave(node) && node->slaveof) {
nodeEpoch = node->slaveof->configEpoch;
}
/* Latency from the POV of this node, config epoch, link status */
ci = sdscatprintf(ci,"%lld %lld %llu %s",
(long long) node->ping_sent,
(long long) node->pong_received,
(unsigned long long) node->configEpoch,
nodeEpoch,
(node->link || node->flags & CLUSTER_NODE_MYSELF) ?
"connected" : "disconnected");
@@ -4401,7 +4452,7 @@ NULL
clusterReplyMultiBulkSlots(c);
} else if (!strcasecmp(szFromObj(c->argv[1]),"flushslots") && c->argc == 2) {
/* CLUSTER FLUSHSLOTS */
if (dictSize(g_pserver->db[0].pdict) != 0) {
if (dictSize(g_pserver->db[0].dict) != 0) {
addReplyError(c,"DB must be empty to perform CLUSTER FLUSHSLOTS.");
return;
}
@@ -4521,7 +4572,7 @@ NULL
}
/* If this slot is in migrating status but we have no keys
* for it assigning the slot to another node will clear
* the migratig status. */
* the migrating status. */
if (countKeysInSlot(slot) == 0 &&
g_pserver->cluster->migrating_slots_to[slot])
g_pserver->cluster->migrating_slots_to[slot] = NULL;
@@ -4734,7 +4785,7 @@ NULL
* slots nor keys to accept to replicate some other node.
* Slaves can switch to another master without issues. */
if (nodeIsMaster(myself) &&
(myself->numslots != 0 || dictSize(g_pserver->db[0].pdict) != 0)) {
(myself->numslots != 0 || dictSize(g_pserver->db[0].dict) != 0)) {
addReplyError(c,
"To set a master the node must be empty and "
"without assigned slots.");
@@ -4866,7 +4917,7 @@ NULL
g_pserver->cluster->currentEpoch = epoch;
/* No need to fsync the config here since in the unlucky event
* of a failure to persist the config, the conflict resolution code
* will assign an unique config to this node. */
* will assign a unique config to this node. */
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|
CLUSTER_TODO_SAVE_CONFIG);
addReply(c,shared.ok);
@@ -4891,7 +4942,7 @@ NULL
/* Slaves can be reset while containing data, but not master nodes
* that must be empty. */
if (nodeIsMaster(myself) && dictSize(c->db->pdict) != 0) {
if (nodeIsMaster(myself) && dictSize(c->db->dict) != 0) {
addReplyError(c,"CLUSTER RESET can't be called with "
"master nodes containing keys");
return;
@@ -4914,7 +4965,7 @@ void createDumpPayload(rio *payload, robj_roptr o, robj *key) {
unsigned char buf[2];
uint64_t crc;
/* Serialize the object in a RDB-like format. It consist of an object type
/* Serialize the object in an RDB-like format. It consist of an object type
* byte followed by the serialized object. This is understood by RESTORE. */
rioInitWithBuffer(payload,sdsempty());
serverAssert(rdbSaveObjectType(payload,o));
@@ -4983,6 +5034,51 @@ void dumpCommand(client *c) {
return;
}
/* KEYDB.MVCCRESTORE key mvcc expire serialized-value */
void mvccrestoreCommand(client *c) {
long long mvcc, expire;
robj *key = c->argv[1], *obj = nullptr;
int type;
if (getLongLongFromObjectOrReply(c, c->argv[2], &mvcc, "Invalid MVCC Tstamp") != C_OK)
return;
if (getLongLongFromObjectOrReply(c, c->argv[3], &expire, "Invalid expire") != C_OK)
return;
/* Verify RDB version and data checksum unles the client is already a replica or master */
if (!(c->flags & (CLIENT_SLAVE | CLIENT_MASTER))) {
if (verifyDumpPayload((unsigned char*)ptrFromObj(c->argv[4]),sdslen(szFromObj(c->argv[4]))) == C_ERR)
{
addReplyError(c,"DUMP payload version or checksum are wrong");
return;
}
}
rio payload;
rioInitWithBuffer(&payload,szFromObj(c->argv[4]));
if (((type = rdbLoadObjectType(&payload)) == -1) ||
((obj = rdbLoadObject(type,&payload,szFromObj(key), OBJ_MVCC_INVALID)) == NULL))
{
addReplyError(c,"Bad data format");
return;
}
setMvccTstamp(obj, mvcc);
/* Create the key and set the TTL if any */
if (dbMerge(c->db,key,obj,true)) {
if (expire >= 0) {
setExpire(c,c->db,key,nullptr,expire);
}
signalModifiedKey(c,c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",key,c->db->id);
} else {
decrRefCount(obj);
}
addReply(c,shared.ok);
g_pserver->dirty++;
}
/* RESTORE key ttl serialized-value [REPLACE] */
void restoreCommand(client *c) {
long long ttl, lfu_freq = -1, lru_idle = -1, lru_clock = -1;
@@ -5025,7 +5121,8 @@ void restoreCommand(client *c) {
}
/* Make sure this key does not already exist here... */
if (!replace && lookupKeyWrite(c->db,c->argv[1]) != NULL) {
robj *key = c->argv[1];
if (!replace && lookupKeyWrite(c->db,key) != NULL) {
addReply(c,shared.busykeyerr);
return;
}
@@ -5047,24 +5144,38 @@ void restoreCommand(client *c) {
rioInitWithBuffer(&payload,szFromObj(c->argv[3]));
if (((type = rdbLoadObjectType(&payload)) == -1) ||
((obj = rdbLoadObject(type,&payload,szFromObj(c->argv[1]), OBJ_MVCC_INVALID)) == NULL))
((obj = rdbLoadObject(type,&payload,szFromObj(key), OBJ_MVCC_INVALID)) == NULL))
{
addReplyError(c,"Bad data format");
return;
}
/* Remove the old key if needed. */
if (replace) dbDelete(c->db,c->argv[1]);
int deleted = 0;
if (replace)
deleted = dbDelete(c->db,key);
if (ttl && !absttl) ttl+=mstime();
if (ttl && checkAlreadyExpired(ttl)) {
if (deleted) {
rewriteClientCommandVector(c,2,shared.del,key);
signalModifiedKey(c,c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
g_pserver->dirty++;
}
decrRefCount(obj);
addReply(c, shared.ok);
return;
}
/* Create the key and set the TTL if any */
dbAdd(c->db,c->argv[1],obj);
dbAdd(c->db,key,obj);
if (ttl) {
if (!absttl) ttl+=mstime();
setExpire(c,c->db,c->argv[1],nullptr,ttl);
setExpire(c,c->db,key,nullptr,ttl);
}
objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock,1000);
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",c->argv[1],c->db->id);
signalModifiedKey(c,c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",key,c->db->id);
addReply(c,shared.ok);
g_pserver->dirty++;
}
@@ -5572,7 +5683,7 @@ void readwriteCommand(client *c) {
* resharding in progress).
*
* On success the function returns the node that is able to serve the request.
* If the node is not 'myself' a redirection must be perfomed. The kind of
* If the node is not 'myself' a redirection must be performed. The kind of
* redirection is specified setting the integer passed by reference
* 'error_code', which will be set to CLUSTER_REDIR_ASK or
* CLUSTER_REDIR_MOVED.
@@ -5650,7 +5761,10 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
margc = ms->commands[i].argc;
margv = ms->commands[i].argv;
keyindex = getKeysFromCommand(mcmd,margv,margc,&numkeys);
getKeysResult result = GETKEYS_RESULT_INIT;
numkeys = getKeysFromCommand(mcmd,margv,margc,&result);
keyindex = result.keys;
for (j = 0; j < numkeys; j++) {
robj *thiskey = margv[keyindex[j]];
int thisslot = keyHashSlot((char*)ptrFromObj(thiskey),
@@ -5668,7 +5782,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
* not trapped earlier in processCommand(). Report the same
* error to the client. */
if (n == NULL) {
getKeysFreeResult(keyindex);
getKeysFreeResult(&result);
if (error_code)
*error_code = CLUSTER_REDIR_DOWN_UNBOUND;
return NULL;
@@ -5692,7 +5806,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
if (!equalStringObjects(firstkey,thiskey)) {
if (slot != thisslot) {
/* Error: multiple keys from different slots. */
getKeysFreeResult(keyindex);
getKeysFreeResult(&result);
if (error_code)
*error_code = CLUSTER_REDIR_CROSS_SLOT;
return NULL;
@@ -5704,14 +5818,14 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
}
}
/* Migarting / Improrting slot? Count keys we don't have. */
/* Migrating / Importing slot? Count keys we don't have. */
if ((migrating_slot || importing_slot) &&
lookupKeyRead(&g_pserver->db[0],thiskey) == nullptr)
{
missing_keys++;
}
}
getKeysFreeResult(keyindex);
getKeysFreeResult(&result);
}
/* No key at all in command? then we can serve the request
@@ -5773,10 +5887,12 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
}
/* Handle the read-only client case reading from a slave: if this
* node is a slave and the request is about an hash slot our master
* node is a slave and the request is about a hash slot our master
* is serving, we can reply without redirection. */
int is_readonly_command = (c->cmd->flags & CMD_READONLY) ||
(c->cmd->proc == execCommand && !(c->mstate.cmd_inv_flags & CMD_READONLY));
if (c->flags & CLIENT_READONLY &&
(cmd->flags & CMD_READONLY || cmd->proc == evalCommand ||
(is_readonly_command || cmd->proc == evalCommand ||
cmd->proc == evalShaCommand) &&
nodeIsSlave(myself) &&
myself->slaveof == n)
@@ -5785,7 +5901,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
}
/* Base case: just return the right node. However if this node is not
* myself, set error_code to MOVED since we need to issue a rediretion. */
* myself, set error_code to MOVED since we need to issue a redirection. */
if (n != myself && error_code) *error_code = CLUSTER_REDIR_MOVED;
return n;
}
@@ -5831,7 +5947,7 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
* 3) The client may remain blocked forever (or up to the max timeout time)
* waiting for a key change that will never happen.
*
* If the client is found to be blocked into an hash slot this node no
* If the client is found to be blocked into a hash slot this node no
* longer handles, the client is sent a redirection error, and the function
* returns 1. Otherwise 0 is returned and no operation is performed. */
int clusterRedirectBlockedClientIfNeeded(client *c) {
@@ -5860,6 +5976,15 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
int slot = keyHashSlot((char*)ptrFromObj(key), sdslen(szFromObj(key)));
clusterNode *node = g_pserver->cluster->slots[slot];
/* if the client is read-only and attempting to access key that our
* replica can handle, allow it. */
if ((c->flags & CLIENT_READONLY) &&
(c->lastcmd->flags & CMD_READONLY) &&
nodeIsSlave(myself) && myself->slaveof == node)
{
node = myself;
}
/* We send an error and unblock the client if:
* 1) The slot is unassigned, emitting a cluster down error.
* 2) The slot is not handled by this node, nor being imported. */
+8 -5
View File
@@ -42,7 +42,9 @@ typedef struct clusterLink {
mstime_t ctime; /* Link creation time */
connection *conn; /* Connection to remote node */
sds sndbuf; /* Packet send buffer */
sds rcvbuf; /* Packet reception buffer */
char *rcvbuf; /* Packet reception buffer */
size_t rcvbuf_len; /* Used size of rcvbuf */
size_t rcvbuf_alloc; /* Used size of rcvbuf */
struct clusterNode *node; /* Node related to this link if any, or NULL */
} clusterLink;
@@ -55,8 +57,8 @@ typedef struct clusterLink {
#define CLUSTER_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */
#define CLUSTER_NODE_NOADDR 64 /* We don't know the address of this node */
#define CLUSTER_NODE_MEET 128 /* Send a MEET message to this node */
#define CLUSTER_NODE_MIGRATE_TO 256 /* Master elegible for replica migration. */
#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failver. */
#define CLUSTER_NODE_MIGRATE_TO 256 /* Master eligible for replica migration. */
#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failover. */
#define CLUSTER_NODE_NULL_NAME "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
#define nodeIsMaster(n) ((n)->flags & CLUSTER_NODE_MASTER)
@@ -168,10 +170,10 @@ typedef struct clusterState {
clusterNode *mf_slave; /* Slave performing the manual failover. */
/* Manual failover state of slave. */
long long mf_master_offset; /* Master offset the slave needs to start MF
or zero if stil not received. */
or zero if still not received. */
int mf_can_start; /* If non-zero signal that the manual failover
can start requesting masters vote. */
/* The followign fields are used by masters to take state on elections. */
/* The following fields are used by masters to take state on elections. */
uint64_t lastVoteEpoch; /* Epoch of the last vote granted. */
int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */
/* Messages received and sent by type. */
@@ -287,6 +289,7 @@ typedef struct {
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
int clusterRedirectBlockedClientIfNeeded(client *c);
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
unsigned long getClusterConnectionsCount(void);
#ifdef __cplusplus
}
+291 -68
View File
@@ -100,6 +100,21 @@ configEnum repl_diskless_load_enum[] = {
{NULL, 0}
};
configEnum tls_auth_clients_enum[] = {
{"no", TLS_CLIENT_AUTH_NO},
{"yes", TLS_CLIENT_AUTH_YES},
{"optional", TLS_CLIENT_AUTH_OPTIONAL},
{NULL, 0}
};
configEnum oom_score_adj_enum[] = {
{"no", OOM_SCORE_ADJ_NO},
{"yes", OOM_SCORE_RELATIVE},
{"relative", OOM_SCORE_RELATIVE},
{"absolute", OOM_SCORE_ADJ_ABSOLUTE},
{NULL, 0}
};
/* Output buffer limits presets. */
clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
{0, 0, 0}, /* normal */
@@ -107,6 +122,9 @@ clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
{1024*1024*32, 1024*1024*8, 60} /* pubsub */
};
/* OOM Score defaults */
int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
/* Generic config infrastructure function pointers
* int is_valid_fn(val, err)
* Return 1 when val is valid, and 0 when invalid.
@@ -289,6 +307,63 @@ void queueLoadModule(sds path, sds *argv, int argc) {
listAddNodeTail(g_pserver->loadmodule_queue,loadmod);
}
/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
* g_pserver->oom_score_adj_values if valid.
*/
static int updateOOMScoreAdjValues(sds *args, const char **err, int apply) {
int i;
int values[CONFIG_OOM_COUNT];
for (i = 0; i < CONFIG_OOM_COUNT; i++) {
char *eptr;
long long val = strtoll(args[i], &eptr, 10);
if (*eptr != '\0' || val < -2000 || val > 2000) {
if (err) *err = "Invalid oom-score-adj-values, elements must be between -2000 and 2000.";
return C_ERR;
}
values[i] = val;
}
/* Verify that the values make sense. If they don't omit a warning but
* keep the configuration, which may still be valid for privileged processes.
*/
if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA]) {
serverLog(LOG_WARNING,
"The oom-score-adj-values configuration may not work for non-privileged processes! "
"Please consult the documentation.");
}
/* Store values, retain previous config for rollback in case we fail. */
int old_values[CONFIG_OOM_COUNT];
for (i = 0; i < CONFIG_OOM_COUNT; i++) {
old_values[i] = g_pserver->oom_score_adj_values[i];
g_pserver->oom_score_adj_values[i] = values[i];
}
/* When parsing the config file, we want to apply only when all is done. */
if (!apply)
return C_OK;
/* Update */
if (setOOMScoreAdj(-1) == C_ERR) {
/* Roll back */
for (i = 0; i < CONFIG_OOM_COUNT; i++)
g_pserver->oom_score_adj_values[i] = old_values[i];
if (err)
*err = "Failed to apply oom-score-adj-values configuration, check server logs.";
return C_ERR;
}
return C_OK;
}
void initConfigValues() {
for (standardConfig *config = configs; config->name != NULL; config++) {
config->interface.init(config->data);
@@ -411,7 +486,30 @@ void loadServerConfigFromString(char *config) {
} else if ((!strcasecmp(argv[0],"slaveof") ||
!strcasecmp(argv[0],"replicaof")) && argc == 3) {
slaveof_linenum = linenum;
replicationAddMaster(argv[1], atoi(argv[2]));
if (!strcasecmp(argv[1], "no") && !strcasecmp(argv[2], "one")) {
if (listLength(g_pserver->masters)) {
listIter li;
listNode *ln;
listRewind(g_pserver->masters, &li);
while ((ln = listNext(&li)))
{
struct redisMaster *mi = (struct redisMaster*)listNodeValue(ln);
zfree(mi->masterauth);
zfree(mi->masteruser);
zfree(mi->repl_transfer_tmpfile);
delete mi->staleKeyMap;
zfree(mi);
listDelNode(g_pserver->masters, ln);
}
}
continue;
}
char *ptr;
int port = strtol(argv[2], &ptr, 10);
if (port < 0 || port > 65535 || *ptr != '\0') {
err= "Invalid master port"; goto loaderr;
}
replicationAddMaster(argv[1], port);
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
if (strlen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {
err = "Password is longer than CONFIG_AUTHPASS_MAX_LEN";
@@ -422,11 +520,16 @@ void loadServerConfigFromString(char *config) {
* additionally is to remember the cleartext password in this
* case, for backward compatibility with Redis <= 5. */
ACLSetUser(DefaultUser,"resetpass",-1);
sds aclop = sdscatprintf(sdsempty(),">%s",argv[1]);
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
sdsfree(g_pserver->requirepass);
g_pserver->requirepass = sdsnew(argv[1]);
g_pserver->requirepass = NULL;
if (sdslen(argv[1])) {
sds aclop = sdscatprintf(sdsempty(),">%s",argv[1]);
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
g_pserver->requirepass = sdsnew(argv[1]);
} else {
ACLSetUser(DefaultUser,"nopass",-1);
}
} else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
/* DEAD OPTION */
} else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
@@ -480,6 +583,8 @@ void loadServerConfigFromString(char *config) {
cserver.client_obuf_limits[type].hard_limit_bytes = hard;
cserver.client_obuf_limits[type].soft_limit_bytes = soft;
cserver.client_obuf_limits[type].soft_limit_seconds = soft_seconds;
} else if (!strcasecmp(argv[0],"oom-score-adj-values") && argc == 1 + CONFIG_OOM_COUNT) {
if (updateOOMScoreAdjValues(&argv[1], &err, 0) == C_ERR) goto loaderr;
} else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
int flags = keyspaceEventsStringToFlags(argv[1]);
@@ -541,7 +646,7 @@ void loadServerConfigFromString(char *config) {
}
} else if (!strcasecmp(argv[0], "active-replica") && argc == 2) {
g_pserver->fActiveReplica = yesnotoi(argv[1]);
if (g_pserver->repl_slave_ro) {
if (g_pserver->fActiveReplica && g_pserver->repl_slave_ro) {
g_pserver->repl_slave_ro = FALSE;
serverLog(LL_NOTICE, "Notice: \"active-replica yes\" implies \"replica-read-only no\"");
}
@@ -577,7 +682,7 @@ void loadServerConfigFromString(char *config) {
return;
loaderr:
fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (Redis %s) ***\n",
fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (KeyDB %s) ***\n",
KEYDB_REAL_VERSION);
fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
fprintf(stderr, ">>> '%s'\n", lines[i]);
@@ -605,7 +710,8 @@ void loadServerConfig(char *filename, char *options) {
} else {
if ((fp = fopen(filename,"r")) == NULL) {
serverLog(LL_WARNING,
"Fatal error, can't open config file '%s'", filename);
"Fatal error, can't open config file '%s': %s",
filename, strerror(errno));
exit(1);
}
}
@@ -686,11 +792,16 @@ void configSetCommand(client *c) {
* additionally is to remember the cleartext password in this
* case, for backward compatibility with Redis <= 5. */
ACLSetUser(DefaultUser,"resetpass",-1);
sds aclop = sdscatprintf(sdsempty(),">%s",(char*)ptrFromObj(o));
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
sdsfree(g_pserver->requirepass);
g_pserver->requirepass = sdsnew(szFromObj(o));
g_pserver->requirepass = NULL;
if (sdslen(szFromObj(o))) {
sds aclop = sdscatprintf(sdsempty(),">%s",(char*)ptrFromObj(o));
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
g_pserver->requirepass = sdsnew(szFromObj(o));
} else {
ACLSetUser(DefaultUser,"nopass",-1);
}
} config_set_special_field("save") {
int vlen, j;
sds *v = sdssplitlen(szFromObj(o),sdslen(szFromObj(o))," ",1,&vlen);
@@ -775,6 +886,17 @@ void configSetCommand(client *c) {
cserver.client_obuf_limits[type].soft_limit_seconds = soft_seconds;
}
sdsfreesplitres(v,vlen);
} config_set_special_field("oom-score-adj-values") {
int vlen;
int success = 1;
sds *v = sdssplitlen(szFromObj(o), sdslen(szFromObj(o)), " ", 1, &vlen);
if (vlen != CONFIG_OOM_COUNT || updateOOMScoreAdjValues(v, &errstr, 1) == C_ERR)
success = 0;
sdsfreesplitres(v, vlen);
if (!success)
goto badfmt;
} config_set_special_field("notify-keyspace-events") {
int flags = keyspaceEventsStringToFlags(szFromObj(o));
@@ -987,6 +1109,26 @@ void configGetCommand(client *c) {
}
matches++;
}
if (stringmatch(pattern,"oom-score-adj-values",0)) {
sds buf = sdsempty();
int j;
for (j = 0; j < CONFIG_OOM_COUNT; j++) {
buf = sdscatprintf(buf,"%d", g_pserver->oom_score_adj_values[j]);
if (j != CONFIG_OOM_COUNT-1)
buf = sdscatlen(buf," ",1);
}
addReplyBulkCString(c,"oom-score-adj-values");
addReplyBulkCString(c,buf);
sdsfree(buf);
matches++;
}
if (stringmatch(pattern,"active-replica",1)) {
addReplyBulkCString(c,"active-replica");
addReplyBulkCString(c, g_pserver->fActiveReplica ? "yes" : "no");
matches++;
}
setDeferredMapLen(c,replylen,matches);
}
@@ -1035,6 +1177,8 @@ struct rewriteConfigState {
sds *lines; /* Current lines as an array of sds strings */
int has_tail; /* True if we already added directives that were
not present in the original config file. */
int force_all; /* True if we want all keywords to be force
written. Currently only used for testing. */
};
/* Append the new line to the current configuration state. */
@@ -1082,6 +1226,7 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
state->numlines = 0;
state->lines = NULL;
state->has_tail = 0;
state->force_all = 0;
if (fp == NULL) return state;
/* Read the old file line by line, populate the state. */
@@ -1160,7 +1305,7 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *opti
rewriteConfigMarkAsProcessed(state,option);
if (!l && !force) {
if (!l && !force && !state->force_all) {
/* Option not used previously, and we are not forced to use it. */
sdsfree(line);
sdsfree(o);
@@ -1257,7 +1402,7 @@ void rewriteConfigNumericalOption(struct rewriteConfigState *state, const char *
rewriteConfigRewriteLine(state,option,line,force);
}
/* Rewrite a octal option. */
/* Rewrite an octal option. */
void rewriteConfigOctalOption(struct rewriteConfigState *state, const char *option, int value, int defvalue) {
int force = value != defvalue;
sds line = sdscatprintf(sdsempty(),"%s %o",option,value);
@@ -1282,6 +1427,12 @@ void rewriteConfigSaveOption(struct rewriteConfigState *state) {
int j;
sds line;
/* In Sentinel mode we don't need to rewrite the save parameters */
if (g_pserver->sentinel_mode) {
rewriteConfigMarkAsProcessed(state,"save");
return;
}
/* Note that if there are no save parameters at all, all the current
* config line with "save" will be detected as orphaned and deleted,
* resulting into no RDB persistence as expected. */
@@ -1404,6 +1555,26 @@ void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state
}
}
/* Rewrite the oom-score-adj-values option. */
void rewriteConfigOOMScoreAdjValuesOption(struct rewriteConfigState *state) {
int force = 0;
int j;
const char *option = "oom-score-adj-values";
sds line;
line = sdsnew(option);
line = sdscatlen(line, " ", 1);
for (j = 0; j < CONFIG_OOM_COUNT; j++) {
if (g_pserver->oom_score_adj_values[j] != configOOMScoreAdjValuesDefaults[j])
force = 1;
line = sdscatprintf(line, "%d", g_pserver->oom_score_adj_values[j]);
if (j+1 != CONFIG_OOM_COUNT)
line = sdscatlen(line, " ", 1);
}
rewriteConfigRewriteLine(state,option,line,force);
}
/* Rewrite the bind option. */
void rewriteConfigBindOption(struct rewriteConfigState *state) {
int force = 1;
@@ -1509,60 +1680,62 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
dictReleaseIterator(di);
}
/* This function overwrites the old configuration file with the new content.
*
* 1) The old file length is obtained.
* 2) If the new content is smaller, padding is added.
* 3) A single write(2) call is used to replace the content of the file.
* 4) Later the file is truncated to the length of the new content.
*
* This way we are sure the file is left in a consistent state even if the
* process is stopped between any of the four operations.
/* This function replaces the old configuration file with the new content
* in an atomic manner.
*
* The function returns 0 on success, otherwise -1 is returned and errno
* set accordingly. */
* is set accordingly. */
int rewriteConfigOverwriteFile(char *configfile, sds content) {
int retval = 0;
int fd = open(configfile,O_RDWR|O_CREAT,0644);
int content_size = sdslen(content), padding = 0;
struct stat sb;
sds content_padded;
int fd = -1;
int retval = -1;
char tmp_conffile[PATH_MAX];
const char *tmp_suffix = ".XXXXXX";
size_t offset = 0;
ssize_t written_bytes = 0;
/* 1) Open the old file (or create a new one if it does not
* exist), get the size. */
if (fd == -1) return -1; /* errno set by open(). */
if (fstat(fd,&sb) == -1) {
close(fd);
return -1; /* errno set by fstat(). */
int tmp_path_len = snprintf(tmp_conffile, sizeof(tmp_conffile), "%s%s", configfile, tmp_suffix);
if (tmp_path_len <= 0 || (unsigned int)tmp_path_len >= sizeof(tmp_conffile)) {
serverLog(LL_WARNING, "Config file full path is too long");
errno = ENAMETOOLONG;
return retval;
}
/* 2) Pad the content at least match the old file size. */
content_padded = sdsdup(content);
if (content_size < sb.st_size) {
/* If the old file was bigger, pad the content with
* a newline plus as many "#" chars as required. */
padding = sb.st_size - content_size;
content_padded = sdsgrowzero(content_padded,sb.st_size);
content_padded[content_size] = '\n';
memset(content_padded+content_size+1,'#',padding-1);
#ifdef _GNU_SOURCE
fd = mkostemp(tmp_conffile, O_CLOEXEC);
#else
/* There's a theoretical chance here to leak the FD if a module thread forks & execv in the middle */
fd = mkstemp(tmp_conffile);
#endif
if (fd == -1) {
serverLog(LL_WARNING, "Could not create tmp config file (%s)", strerror(errno));
return retval;
}
/* 3) Write the new content using a single write(2). */
if (write(fd,content_padded,strlen(content_padded)) == -1) {
retval = -1;
goto cleanup;
while (offset < sdslen(content)) {
written_bytes = write(fd, content + offset, sdslen(content) - offset);
if (written_bytes <= 0) {
if (errno == EINTR) continue; /* FD is blocking, no other retryable errors */
serverLog(LL_WARNING, "Failed after writing (%zd) bytes to tmp config file (%s)", offset, strerror(errno));
goto cleanup;
}
offset+=written_bytes;
}
/* 4) Truncate the file to the right length if we used padding. */
if (padding) {
if (ftruncate(fd,content_size) == -1) {
/* Non critical error... */
}
if (fsync(fd))
serverLog(LL_WARNING, "Could not sync tmp config file to disk (%s)", strerror(errno));
else if (fchmod(fd, 0644) == -1)
serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno));
else if (rename(tmp_conffile, configfile) == -1)
serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno));
else {
retval = 0;
serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile);
}
cleanup:
sdsfree(content_padded);
close(fd);
if (retval) unlink(tmp_conffile);
return retval;
}
@@ -1572,15 +1745,18 @@ cleanup:
*
* Configuration parameters that are at their default value, unless already
* explicitly included in the old configuration file, are not rewritten.
* The force_all flag overrides this behavior and forces everything to be
* written. This is currently only used for testing purposes.
*
* On error -1 is returned and errno is set accordingly, otherwise 0. */
int rewriteConfig(char *path) {
int rewriteConfig(char *path, int force_all) {
struct rewriteConfigState *state;
sds newcontent;
int retval;
/* Step 1: read the old config into our rewrite state. */
if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
if (force_all) state->force_all = 1;
/* Step 2: rewrite every single option, replacing or appending it inside
* the rewrite state. */
@@ -1604,6 +1780,7 @@ int rewriteConfig(char *path) {
rewriteConfigClientoutputbufferlimitOption(state);
rewriteConfigYesNoOption(state,"active-replica",g_pserver->fActiveReplica,CONFIG_DEFAULT_ACTIVE_REPLICA);
rewriteConfigStringOption(state, "version-override",KEYDB_SET_VERSION,KEYDB_REAL_VERSION);
rewriteConfigOOMScoreAdjValuesOption(state);
/* Rewrite Sentinel config if in Sentinel mode. */
if (g_pserver->sentinel_mode) rewriteConfigSentinelOption(state);
@@ -1767,7 +1944,7 @@ static int enumConfigSet(typeData data, sds value, int update, const char **err)
}
sdsrange(enumerr,0,-3); /* Remove final ", ". */
strncpy(loadbuf, enumerr, LOADBUF_SIZE);
strncpy(loadbuf, enumerr, LOADBUF_SIZE-1);
loadbuf[LOADBUF_SIZE - 1] = '\0';
sdsfree(enumerr);
@@ -2072,7 +2249,7 @@ static int isValidAOFfilename(char *val, const char **err) {
static int updateHZ(long long val, long long prev, const char **err) {
UNUSED(prev);
UNUSED(err);
/* Hz is more an hint from the user, so we accept values out of range
/* Hz is more a hint from the user, so we accept values out of range
* but cap them to reasonable values. */
g_pserver->config_hz = val;
if (g_pserver->config_hz < CONFIG_MIN_HZ) g_pserver->config_hz = CONFIG_MIN_HZ;
@@ -2090,7 +2267,7 @@ static int updateJemallocBgThread(int val, int prev, const char **err) {
static int updateReplBacklogSize(long long val, long long prev, const char **err) {
/* resizeReplicationBacklog sets g_pserver->repl_backlog_size, and relies on
* being able to tell when the size changes, so restore prev becore calling it. */
* being able to tell when the size changes, so restore prev before calling it. */
UNUSED(err);
g_pserver->repl_backlog_size = prev;
resizeReplicationBacklog(val);
@@ -2150,17 +2327,42 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
}
return 0;
}
for (int iel = 0; iel < MAX_EVENT_LOOPS; ++iel)
/* Change the SetSize for the current thread first. If any error, return the error message to the client,
* otherwise, continue to do the same for other threads */
if ((unsigned int) aeGetSetSize(aeGetCurrentEventLoop()) <
g_pserver->maxclients + CONFIG_FDSET_INCR)
{
if (aeResizeSetSize(aeGetCurrentEventLoop(),
g_pserver->maxclients + CONFIG_FDSET_INCR) == AE_ERR)
{
*err = "The event loop API used by Redis is not able to handle the specified number of clients";
return 0;
}
serverLog(LL_DEBUG,"Successfully changed the setsize for current thread %d", ielFromEventLoop(aeGetCurrentEventLoop()));
}
for (int iel = 0; iel < cserver.cthreads; ++iel)
{
if (g_pserver->rgthreadvar[iel].el == aeGetCurrentEventLoop()){
continue;
}
if ((unsigned int) aeGetSetSize(g_pserver->rgthreadvar[iel].el) <
g_pserver->maxclients + CONFIG_FDSET_INCR)
{
if (aeResizeSetSize(g_pserver->rgthreadvar[iel].el,
g_pserver->maxclients + CONFIG_FDSET_INCR) == AE_ERR)
{
*err = "The event loop API used by Redis is not able to handle the specified number of clients";
int res = aePostFunction(g_pserver->rgthreadvar[iel].el, [iel] {
if (aeResizeSetSize(g_pserver->rgthreadvar[iel].el, g_pserver->maxclients + CONFIG_FDSET_INCR) == AE_ERR) {
serverLog(LL_WARNING,"Failed to change the setsize for Thread %d", iel);
}
});
if (res != AE_OK){
static char msg[128];
sprintf(msg, "Failed to post the request to change setsize for Thread %d", iel);
*err = msg;
return 0;
}
serverLog(LL_DEBUG,"Successfully post the request to change the setsize for thread %d", iel);
}
}
}
@@ -2174,12 +2376,28 @@ static int validateMultiMasterNoForward(int val, const char **) {
return 1;
}
static int updateOOMScoreAdj(int val, int prev, const char **err) {
UNUSED(prev);
if (val) {
if (setOOMScoreAdj(-1) == C_ERR) {
*err = "Failed to set current oom_score_adj. Check server logs.";
return 0;
}
}
return 1;
}
#ifdef USE_OPENSSL
static int updateTlsCfg(char *val, char *prev, const char **err) {
UNUSED(val);
UNUSED(prev);
UNUSED(err);
if (tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
/* If TLS is enabled, try to configure OpenSSL. */
if ((g_pserver->tls_port || g_pserver->tls_replication || g_pserver->tls_cluster)
&& tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
*err = "Unable to update TLS configuration. Check server logs.";
return 0;
}
@@ -2205,6 +2423,7 @@ standardConfig configs[] = {
createBoolConfig("daemonize", NULL, IMMUTABLE_CONFIG, cserver.daemonize, 0, NULL, NULL),
createBoolConfig("lua-replicate-commands", NULL, MODIFIABLE_CONFIG, g_pserver->lua_always_replicate_commands, 1, NULL, NULL),
createBoolConfig("always-show-logo", NULL, IMMUTABLE_CONFIG, g_pserver->always_show_logo, 0, NULL, NULL),
createBoolConfig("enable-motd", NULL, IMMUTABLE_CONFIG, cserver.enable_motd, 1, NULL, NULL),
createBoolConfig("protected-mode", NULL, MODIFIABLE_CONFIG, g_pserver->protected_mode, 1, NULL, NULL),
createBoolConfig("rdbcompression", NULL, MODIFIABLE_CONFIG, g_pserver->rdb_compression, 1, NULL, NULL),
createBoolConfig("rdb-del-sync-files", NULL, MODIFIABLE_CONFIG, g_pserver->rdb_del_sync_files, 0, NULL, NULL),
@@ -2254,6 +2473,7 @@ standardConfig configs[] = {
createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bio_cpulist, NULL, NULL, NULL),
createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->aof_rewrite_cpulist, NULL, NULL, NULL),
createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bgsave_cpulist, NULL, NULL, NULL),
createStringConfig("ignore-warnings", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->ignore_warnings, "ARM64-COW-BUG", NULL, NULL),
/* Enum Configs */
createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, cserver.supervised_mode, SUPERVISED_NONE, NULL, NULL),
@@ -2262,6 +2482,7 @@ standardConfig configs[] = {
createEnumConfig("loglevel", NULL, MODIFIABLE_CONFIG, loglevel_enum, cserver.verbosity, LL_NOTICE, NULL, NULL),
createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, g_pserver->maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, g_pserver->aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, g_pserver->oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj),
/* Integer configs */
createIntConfig("databases", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, cserver.dbnum, 16, INTEGER_CONFIG, NULL, NULL),
@@ -2294,12 +2515,14 @@ standardConfig configs[] = {
createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),
createIntConfig("min-replicas-max-lag", "min-slaves-max-lag", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),
createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 50, INTEGER_CONFIG, NULL, NULL),
createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 20, INTEGER_CONFIG, NULL, NULL),
createIntConfig("replica-quorum", NULL, MODIFIABLE_CONFIG, -1, INT_MAX, g_pserver->repl_quorum, -1, INTEGER_CONFIG, NULL, NULL),
/* Unsigned int configs */
createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, g_pserver->maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),
createUIntConfig("loading-process-events-interval-keys", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->loading_process_events_interval_keys, 8192, MEMORY_CONFIG, NULL, NULL),
/* Unsigned Long configs */
createULongConfig("loading-process-events-interval-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->loading_process_events_interval_bytes, 2*1024*1024, MEMORY_CONFIG, NULL, NULL),
createULongConfig("active-defrag-max-scan-fields", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, cserver.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */
createULongConfig("slowlog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->slowlog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
createULongConfig("acllog-max-len", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->acllog_max_len, 128, INTEGER_CONFIG, NULL, NULL),
@@ -2309,7 +2532,7 @@ standardConfig configs[] = {
createLongLongConfig("cluster-node-timeout", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),
createLongLongConfig("slowlog-log-slower-than", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, g_pserver->slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),
createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),
createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 1024*1024, LLONG_MAX, g_pserver->proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
@@ -2337,7 +2560,7 @@ standardConfig configs[] = {
createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt),
createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, g_pserver->tls_cluster, 0, NULL, NULL),
createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, g_pserver->tls_replication, 0, NULL, NULL),
createBoolConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, g_pserver->tls_auth_clients, 1, NULL, NULL),
createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, g_pserver->tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
@@ -2387,7 +2610,7 @@ NULL
addReplyError(c,"The server is running without a config file");
return;
}
if (rewriteConfig(cserver.configfile) == -1) {
if (rewriteConfig(cserver.configfile, 0) == -1) {
serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
} else {
+9 -4
View File
@@ -54,6 +54,7 @@
#define HAVE_PROC_MAPS 1
#define HAVE_PROC_SMAPS 1
#define HAVE_PROC_SOMAXCONN 1
#define HAVE_PROC_OOM_SCORE_ADJ 1
#endif
/* Test for task_info() */
@@ -63,7 +64,7 @@
/* Test for backtrace() */
#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) || \
defined(__FreeBSD__) || (defined(__OpenBSD__) && defined(USE_BACKTRACE))\
defined(__FreeBSD__) || ((defined(__OpenBSD__) || defined(__NetBSD__)) && defined(USE_BACKTRACE))\
|| defined(__DragonFly__)
#define HAVE_BACKTRACE 1
#endif
@@ -123,6 +124,10 @@
#define USE_SETPROCTITLE
#endif
#if defined(__HAIKU__)
#define ESOCKTNOSUPPORT 0
#endif
#if ((defined __linux && defined(__GLIBC__)) || defined __APPLE__)
#define USE_SETPROCTITLE
#define INIT_SETPROCTITLE_REPLACEMENT
@@ -171,7 +176,7 @@ void setproctitle(const char *fmt, ...);
#endif /* BYTE_ORDER */
/* Sometimes after including an OS-specific header that defines the
* endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what
* endianness we end with __BYTE_ORDER but not with BYTE_ORDER that is what
* the Redis code uses. In this case let's define everything without the
* underscores. */
#ifndef BYTE_ORDER
@@ -241,7 +246,7 @@ void setproctitle(const char *fmt, ...);
#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)
#elif defined __NetBSD__
#include <pthread.h>
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), "%s", name)
#else
#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))
#ifdef __cplusplus
@@ -257,7 +262,7 @@ int pthread_setname_np(const char *name);
#endif
/* Check if we can use setcpuaffinity(). */
#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)
#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__ || defined __DragonFly__)
#define USE_SETCPUAFFINITY
#ifdef __cplusplus
extern "C"
+28 -6
View File
@@ -85,8 +85,12 @@ connection *connCreateSocket() {
/* Create a new socket-type connection that is already associated with
* an accepted connection.
*
* The socket is not read for I/O until connAccept() was called and
* The socket is not ready for I/O until connAccept() was called and
* invoked the connection-level accept handler.
*
* Callers should use connGetState() and verify the created connection
* is not in an error state (which is not possible for a socket connection,
* but could but possible with other protocols).
*/
connection *connCreateAcceptedSocket(int fd) {
connection *conn = connCreateSocket();
@@ -164,7 +168,12 @@ static int connSocketWrite(connection *conn, const void *data, size_t data_len)
int ret = write(conn->fd, data, data_len);
if (ret < 0 && errno != EAGAIN) {
conn->last_errno = errno;
conn->state.store(CONN_STATE_ERROR, std::memory_order_relaxed);
/* Don't overwrite the state of a connection that is not already
* connected, not to mess with handler callbacks.
*/
ConnectionState expected = CONN_STATE_CONNECTED;
conn->state.compare_exchange_strong(expected, CONN_STATE_ERROR, std::memory_order_relaxed);
}
return ret;
@@ -176,7 +185,12 @@ static int connSocketRead(connection *conn, void *buf, size_t buf_len) {
conn->state.store(CONN_STATE_CLOSED, std::memory_order_release);
} else if (ret < 0 && errno != EAGAIN) {
conn->last_errno = errno;
conn->state.store(CONN_STATE_ERROR, std::memory_order_release);
/* Don't overwrite the state of a connection that is not already
* connected, not to mess with handler callbacks.
*/
ConnectionState expected = CONN_STATE_CONNECTED;
conn->state.compare_exchange_strong(expected, CONN_STATE_ERROR, std::memory_order_release);
}
return ret;
@@ -256,8 +270,9 @@ static void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientD
if (conn->state.load(std::memory_order_relaxed) == CONN_STATE_CONNECTING &&
(mask & AE_WRITABLE) && conn->conn_handler) {
if (connGetSocketError(conn)) {
conn->last_errno = errno;
int conn_error = connGetSocketError(conn);
if (conn_error) {
conn->last_errno = conn_error;
conn->state.store(CONN_STATE_ERROR, std::memory_order_release);
} else {
conn->state.store(CONN_STATE_CONNECTED, std::memory_order_release);
@@ -334,6 +349,11 @@ static ssize_t connSocketSyncReadLine(connection *conn, char *ptr, ssize_t size,
return syncReadLine(conn->fd, ptr, size, timeout);
}
static int connSocketGetType(struct connection *conn) {
(void) conn;
return CONN_TYPE_SOCKET;
}
ConnectionType CT_Socket = {
connSocketEventHandler,
@@ -348,7 +368,9 @@ ConnectionType CT_Socket = {
connSocketBlockingConnect,
connSocketSyncWrite,
connSocketSyncRead,
connSocketSyncReadLine
connSocketSyncReadLine,
nullptr,
connSocketGetType
};
+12 -2
View File
@@ -52,6 +52,9 @@ typedef enum {
#define CONN_FLAG_READ_THREADSAFE (1<<2)
#define CONN_FLAG_WRITE_THREADSAFE (1<<3)
#define CONN_TYPE_SOCKET 1
#define CONN_TYPE_TLS 2
typedef void (*ConnectionCallbackFunc)(struct connection *conn);
typedef struct ConnectionType {
@@ -69,6 +72,7 @@ typedef struct ConnectionType {
ssize_t (*sync_read)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
ssize_t (*sync_readline)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
void (*marshal_thread)(struct connection *conn);
int (*get_type)(struct connection *conn);
} ConnectionType;
struct connection {
@@ -107,7 +111,7 @@ static inline int connAccept(connection *conn, ConnectionCallbackFunc accept_han
}
/* Establish a connection. The connect_handler will be called when the connection
* is established, or if an error has occured.
* is established, or if an error has occurred.
*
* The connection handler will be responsible to set up any read/write handlers
* as needed.
@@ -169,7 +173,7 @@ static inline int connSetReadHandler(connection *conn, ConnectionCallbackFunc fu
/* Set a write handler, and possibly enable a write barrier, this flag is
* cleared when write handler is changed or removed.
* With barroer enabled, we never fire the event if the read handler already
* With barrier enabled, we never fire the event if the read handler already
* fired in the same event loop iteration. Useful when you want to persist
* things to disk before sending replies, and want to do that in a group fashion. */
static inline int connSetWriteHandlerWithBarrier(connection *conn, ConnectionCallbackFunc func, int barrier, bool fThreadSafe = false) {
@@ -204,6 +208,11 @@ static inline void connMarshalThread(connection *conn) {
conn->type->marshal_thread(conn);
}
/* Return CONN_TYPE_* for the specified connection */
static inline int connGetType(connection *conn) {
return conn->type->get_type(conn);
}
connection *connCreateSocket();
connection *connCreateAcceptedSocket(int fd);
@@ -232,6 +241,7 @@ int connSockName(connection *conn, char *ip, size_t ip_len, int *port);
const char *connGetInfo(connection *conn, char *buf, size_t buf_len);
/* Helpers for tls special considerations */
sds connTLSGetPeerCert(connection *conn);
int tlsHasPendingData();
int tlsProcessPendingData();
+2 -1
View File
@@ -35,7 +35,8 @@ void crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {
/* generate CRCs for all single byte sequences */
for (int n = 0; n < 256; n++) {
table[0][n] = crcfn(0, &n, 1);
unsigned char v = n;
table[0][n] = crcfn(0, &v, 1);
}
/* generate nested CRC table for future slice-by-8 lookup */
+326 -211
View File
@@ -35,6 +35,13 @@
#include <signal.h>
#include <ctype.h>
/* Database backup. */
struct dbBackup {
redisDb *dbarray;
rax *slots_to_keys;
uint64_t slots_keys_count[CLUSTER_SLOTS];
};
/*-----------------------------------------------------------------------------
* C-level DB API
*----------------------------------------------------------------------------*/
@@ -86,7 +93,7 @@ void updateDbValAccess(dictEntry *de, int flags)
* implementations that should instead rely on lookupKeyRead(),
* lookupKeyWrite() and lookupKeyReadWithFlags(). */
static robj *lookupKey(redisDb *db, robj *key, int flags) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
dictEntry *de = dictFind(db->dict,ptrFromObj(key));
if (de) {
robj *val = (robj*)dictGetVal(de);
@@ -131,11 +138,8 @@ robj_roptr 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(g_pserver->masters) == 0) {
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
}
if (listLength(g_pserver->masters) == 0)
goto keymiss;
/* However if we are in the context of a replica, expireIfNeeded() will
* not really try to expire the key, it only returns information
@@ -145,7 +149,7 @@ robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
* However, if the command caller is not the master, and as additional
* safety measure, the command invoked is a read-only command, we can
* safely return NULL here, and provide a more consistent behavior
* to clients accessign expired values in a read-only fashion, that
* to clients accessing expired values in a read-only fashion, that
* will say the key as non existing.
*
* Notably this covers GETs when slaves are used to scale reads. */
@@ -154,19 +158,21 @@ robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
serverTL->current_client->cmd &&
serverTL->current_client->cmd->flags & CMD_READONLY)
{
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
goto keymiss;
}
}
val = lookupKey(db,key,flags);
if (val == NULL) {
if (val == NULL)
goto keymiss;
g_pserver->stat_keyspace_hits++;
return val;
keymiss:
if (!(flags & LOOKUP_NONOTIFY)) {
g_pserver->stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
}
else
g_pserver->stat_keyspace_hits++;
return val;
return NULL;
}
/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the
@@ -202,13 +208,15 @@ robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply) {
return o;
}
int dbAddCore(redisDb *db, robj *key, robj *val) {
int dbAddCore(redisDb *db, robj *key, robj *val, bool fUpdateMvcc) {
serverAssert(!val->FExpires());
sds copy = sdsdup(szFromObj(key));
int retval = dictAdd(db->pdict, copy, val);
int retval = dictAdd(db->dict, copy, val);
uint64_t mvcc = getMvccTstamp();
setMvccTstamp(key, mvcc);
setMvccTstamp(val, mvcc);
if (fUpdateMvcc) {
setMvccTstamp(key, mvcc);
setMvccTstamp(val, mvcc);
}
if (retval == DICT_OK)
{
@@ -232,7 +240,7 @@ int dbAddCore(redisDb *db, robj *key, robj *val) {
* The program is aborted if the key already exists. */
void dbAdd(redisDb *db, robj *key, robj *val)
{
int retval = dbAddCore(db, key, val);
int retval = dbAddCore(db, key, val, true /* fUpdateMvcc */);
serverAssertWithInfo(NULL,key,retval == DICT_OK);
}
@@ -261,14 +269,14 @@ void dbOverwriteCore(redisDb *db, dictEntry *de, robj *key, robj *val, bool fUpd
setMvccTstamp(val, getMvccTstamp());
}
dictSetVal(db->pdict, de, val);
dictSetVal(db->dict, de, val);
if (g_pserver->lazyfree_lazy_server_del) {
freeObjAsync(old);
dictSetVal(db->pdict, &auxentry, NULL);
dictSetVal(db->dict, &auxentry, NULL);
}
dictFreeVal(db->pdict, &auxentry);
dictFreeVal(db->dict, &auxentry);
}
/* Overwrite an existing key with a new value. Incrementing the reference
@@ -277,7 +285,7 @@ void dbOverwriteCore(redisDb *db, dictEntry *de, robj *key, robj *val, bool fUpd
*
* The program is aborted if the key was not already present. */
void dbOverwrite(redisDb *db, robj *key, robj *val) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
dictEntry *de = dictFind(db->dict,ptrFromObj(key));
serverAssertWithInfo(NULL,key,de != NULL);
dbOverwriteCore(db, de, key, val, !!g_pserver->fActiveReplica, false);
@@ -288,9 +296,9 @@ int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
{
if (fReplace)
{
dictEntry *de = dictFind(db->pdict, ptrFromObj(key));
dictEntry *de = dictFind(db->dict, ptrFromObj(key));
if (de == nullptr)
return (dbAddCore(db, key, val) == DICT_OK);
return (dbAddCore(db, key, val, false /* fUpdateMvcc */) == DICT_OK);
robj *old = (robj*)dictGetVal(de);
if (mvccFromObj(old) <= mvccFromObj(val))
@@ -303,7 +311,7 @@ int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
}
else
{
return (dbAddCore(db, key, val) == DICT_OK);
return (dbAddCore(db, key, val, true /* fUpdateMvcc */) == DICT_OK);
}
}
@@ -319,7 +327,7 @@ int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
* The client 'c' argument may be set to NULL if the operation is performed
* in a context where there is no clear client performing the operation. */
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {
dictEntry *de = dictFind(db->pdict, ptrFromObj(key));
dictEntry *de = dictFind(db->dict, ptrFromObj(key));
if (de == NULL) {
dbAdd(db,key,val);
} else {
@@ -338,7 +346,7 @@ void setKey(client *c, redisDb *db, robj *key, robj *val) {
/* Return true if the specified key exists in the specified database.
* LRU/LFU info is not updated in any way. */
int dbExists(redisDb *db, robj *key) {
return dictFind(db->pdict,ptrFromObj(key)) != NULL;
return dictFind(db->dict,ptrFromObj(key)) != NULL;
}
/* Return a random key, in form of a Redis object.
@@ -348,13 +356,13 @@ int dbExists(redisDb *db, robj *key) {
robj *dbRandomKey(redisDb *db) {
dictEntry *de;
int maxtries = 100;
int allvolatile = dictSize(db->pdict) == db->setexpire->size();
int allvolatile = dictSize(db->dict) == db->setexpire->size();
while(1) {
sds key;
robj *keyobj;
de = dictGetRandomKey(db->pdict);
de = dictGetRandomKey(db->dict);
if (de == NULL) return NULL;
key = (sds)dictGetKey(de);
@@ -392,10 +400,10 @@ int dbSyncDelete(redisDb *db, robj *key) {
/* Deleting an entry from the expires dict will not free the sds of
* the key, because it is shared with the main dictionary. */
dictEntry *de = dictFind(db->pdict, szFromObj(key));
dictEntry *de = dictFind(db->dict, szFromObj(key));
if (de != nullptr && ((robj*)dictGetVal(de))->FExpires())
removeExpireCore(db, key, de);
if (dictDelete(db->pdict,ptrFromObj(key)) == DICT_OK) {
if (dictDelete(db->dict,ptrFromObj(key)) == DICT_OK) {
if (g_pserver->cluster_enabled) slotToKeyDel(szFromObj(key));
return 1;
} else {
@@ -448,48 +456,18 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
return o;
}
/* 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.
/* Remove all keys from the database(s) structure. The dbarray argument
* may not be the server main DBs (could be a backup).
*
* The dbnum can be -1 if all the DBs should be flushed, or the specified
* DB number if we want to flush only a single Redis database number.
*
* Flags are be EMPTYDB_NO_FLAGS if no special flags are specified or
* 1. EMPTYDB_ASYNC if we want the memory to be freed in a different thread.
* 2. EMPTYDB_BACKUP if we want to empty the backup dictionaries created by
* disklessLoadMakeBackups. In that case we only free memory and avoid
* firing module events.
* and the function to return ASAP.
*
* On success the fuction returns the number of keys removed from the
* database(s). Otherwise -1 is returned in the specific case the
* DB number is out of range, and errno is set to EINVAL. */
long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*)) {
int async = (flags & EMPTYDB_ASYNC);
int backup = (flags & EMPTYDB_BACKUP); /* Just free the memory, nothing else */
RedisModuleFlushInfoV1 fi = {REDISMODULE_FLUSHINFO_VERSION,!async,dbnum};
* The dbnum can be -1 if all the DBs should be emptied, or the specified
* DB index if we want to empty only a single database.
* The function returns the number of keys removed from the database(s). */
long long emptyDbStructure(redisDb *dbarray, int dbnum, int async,
void(callback)(void*))
{
long long removed = 0;
if (dbnum < -1 || dbnum >= cserver.dbnum) {
errno = EINVAL;
return -1;
}
/* Pre-flush actions */
if (!backup) {
/* Fire the flushdb modules event. */
moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
REDISMODULE_SUBEVENT_FLUSHDB_START,
&fi);
/* Make sure the WATCHed keys are affected by the FLUSH* commands.
* Note that we need to call the function while the keys are still
* there. */
signalFlushedDb(dbnum);
}
int startdb, enddb;
if (dbnum == -1) {
startdb = 0;
enddb = cserver.dbnum-1;
@@ -498,38 +476,147 @@ long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(
}
for (int j = startdb; j <= enddb; j++) {
removed += dictSize(dbarray[j].pdict);
removed += dictSize(dbarray[j].dict);
if (async) {
emptyDbAsync(&dbarray[j]);
} else {
dictEmpty(dbarray[j].pdict,callback);
dictEmpty(dbarray[j].dict,callback);
dbarray[j].setexpire->clear();
}
}
/* Post-flush actions */
if (!backup) {
if (g_pserver->cluster_enabled) {
if (async) {
slotToKeyFlushAsync();
} else {
slotToKeyFlush();
}
}
if (dbnum == -1) flushSlaveKeysWithExpireList();
/* Also fire the end event. Note that this event will fire almost
* immediately after the start event if the flush is asynchronous. */
moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
REDISMODULE_SUBEVENT_FLUSHDB_END,
&fi);
/* Because all keys of database are removed, reset average ttl. */
dbarray[j].avg_ttl = 0;
dbarray[j].last_expire_set = 0;
}
return removed;
}
/* Remove all keys from all the databases in a Redis server.
* If callback is given the function is called from time to time to
* signal that work is in progress.
*
* The dbnum can be -1 if all the DBs should be flushed, or the specified
* DB number if we want to flush only a single Redis database number.
*
* Flags are be EMPTYDB_NO_FLAGS if no special flags are specified or
* EMPTYDB_ASYNC if we want the memory to be freed in a different thread
* and the function to return ASAP.
*
* On success the function returns the number of keys removed from the
* database(s). Otherwise -1 is returned in the specific case the
* DB number is out of range, and errno is set to EINVAL. */
long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
return emptyDbGeneric(g_pserver->db, dbnum, flags, callback);
int async = (flags & EMPTYDB_ASYNC);
RedisModuleFlushInfoV1 fi = {REDISMODULE_FLUSHINFO_VERSION,!async,dbnum};
long long removed = 0;
if (dbnum < -1 || dbnum >= cserver.dbnum) {
errno = EINVAL;
return -1;
}
/* Fire the flushdb modules event. */
moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
REDISMODULE_SUBEVENT_FLUSHDB_START,
&fi);
/* Make sure the WATCHed keys are affected by the FLUSH* commands.
* Note that we need to call the function while the keys are still
* there. */
signalFlushedDb(dbnum);
/* Empty redis database structure. */
removed = emptyDbStructure(g_pserver->db, dbnum, async, callback);
/* Flush slots to keys map if enable cluster, we can flush entire
* slots to keys map whatever dbnum because only support one DB
* in cluster mode. */
if (g_pserver->cluster_enabled) slotToKeyFlush(async);
if (dbnum == -1) flushSlaveKeysWithExpireList();
/* Also fire the end event. Note that this event will fire almost
* immediately after the start event if the flush is asynchronous. */
moduleFireServerEvent(REDISMODULE_EVENT_FLUSHDB,
REDISMODULE_SUBEVENT_FLUSHDB_END,
&fi);
return removed;
}
/* Store a backup of the database for later use, and put an empty one
* instead of it. */
dbBackup *backupDb(void) {
dbBackup *backup = (dbBackup*)zmalloc(sizeof(dbBackup));
/* Backup main DBs. */
backup->dbarray = (redisDb*)zmalloc(sizeof(redisDb)*cserver.dbnum);
for (int i=0; i<cserver.dbnum; i++) {
backup->dbarray[i] = g_pserver->db[i];
g_pserver->db[i].dict = dictCreate(&dbDictType,NULL);
g_pserver->db[i].setexpire = new(MALLOC_LOCAL) expireset;
g_pserver->db[i].expireitr = g_pserver->db[i].setexpire->end();
}
/* Backup cluster slots to keys map if enable cluster. */
if (g_pserver->cluster_enabled) {
backup->slots_to_keys = g_pserver->cluster->slots_to_keys;
memcpy(backup->slots_keys_count, g_pserver->cluster->slots_keys_count,
sizeof(g_pserver->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));
}
return backup;
}
/* Discard a previously created backup, this can be slow (similar to FLUSHALL)
* Arguments are similar to the ones of emptyDb, see EMPTYDB_ flags. */
void discardDbBackup(dbBackup *buckup, int flags, void(callback)(void*)) {
int async = (flags & EMPTYDB_ASYNC);
/* Release main DBs backup . */
emptyDbStructure(buckup->dbarray, -1, async, callback);
for (int i=0; i<cserver.dbnum; i++) {
dictRelease(buckup->dbarray[i].dict);
delete buckup->dbarray[i].setexpire;
}
/* Release slots to keys map backup if enable cluster. */
if (g_pserver->cluster_enabled) freeSlotsToKeysMap(buckup->slots_to_keys, async);
/* Release buckup. */
zfree(buckup->dbarray);
zfree(buckup);
}
/* Restore the previously created backup (discarding what currently resides
* in the db).
* This function should be called after the current contents of the database
* was emptied with a previous call to emptyDb (possibly using the async mode). */
void restoreDbBackup(dbBackup *buckup) {
/* Restore main DBs. */
for (int i=0; i<cserver.dbnum; i++) {
serverAssert(dictSize(g_pserver->db[i].dict) == 0);
serverAssert(g_pserver->db[i].setexpire->empty());
dictRelease(g_pserver->db[i].dict);
delete g_pserver->db[i].setexpire;
g_pserver->db[i] = buckup->dbarray[i];
}
/* Restore slots to keys map backup if enable cluster. */
if (g_pserver->cluster_enabled) {
serverAssert(g_pserver->cluster->slots_to_keys->numele == 0);
raxFree(g_pserver->cluster->slots_to_keys);
g_pserver->cluster->slots_to_keys = buckup->slots_to_keys;
memcpy(g_pserver->cluster->slots_keys_count, buckup->slots_keys_count,
sizeof(g_pserver->cluster->slots_keys_count));
}
/* Release buckup. */
zfree(buckup->dbarray);
zfree(buckup);
}
int selectDb(client *c, int id) {
@@ -543,7 +630,7 @@ long long dbTotalServerKeyCount() {
long long total = 0;
int j;
for (j = 0; j < cserver.dbnum; j++) {
total += dictSize(g_pserver->db[j].pdict);
total += dictSize(g_pserver->db[j].dict);
}
return total;
}
@@ -565,7 +652,18 @@ void signalModifiedKey(client *c, redisDb *db, robj *key) {
}
void signalFlushedDb(int dbid) {
touchWatchedKeysOnFlush(dbid);
int startdb, enddb;
if (dbid == -1) {
startdb = 0;
enddb = cserver.dbnum-1;
} else {
startdb = enddb = dbid;
}
for (int j = startdb; j <= enddb; j++) {
touchAllWatchedKeysInDb(&g_pserver->db[j], NULL);
}
trackingInvalidateKeysOnFlush(dbid);
}
@@ -680,7 +778,7 @@ void existsCommand(client *c) {
int j;
for (j = 1; j < c->argc; j++) {
if (lookupKeyRead(c->db,c->argv[j])) count++;
if (lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH)) count++;
}
addReplyLongLong(c,count);
}
@@ -730,7 +828,7 @@ void keysCommand(client *c) {
unsigned long numkeys = 0;
void *replylen = addReplyDeferredLen(c);
di = dictGetSafeIterator(c->db->pdict);
di = dictGetSafeIterator(c->db->dict);
allkeys = (pattern[0] == '*' && plen == 1);
while((de = dictNext(di)) != NULL) {
sds key = (sds)dictGetKey(de);
@@ -874,7 +972,7 @@ void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) {
/* Handle the case of a hash table. */
ht = NULL;
if (o == nullptr) {
ht = c->db->pdict;
ht = c->db->dict;
} else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {
ht = (dict*)ptrFromObj(o);
} else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {
@@ -882,7 +980,7 @@ void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) {
count *= 2; /* We return key / value for this type. */
} else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = (zset*)ptrFromObj(o);
ht = zs->pdict;
ht = zs->dict;
count *= 2; /* We return key / value for this type. */
}
@@ -961,7 +1059,7 @@ void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) {
/* Filter element if it is an expired key. */
if (!filter && o == nullptr && expireIfNeeded(c->db, kobj)) filter = 1;
/* Remove the element and its associted value if needed. */
/* Remove the element and its associated value if needed. */
if (filter) {
decrRefCount(kobj);
listDelNode(keys, node);
@@ -1007,7 +1105,7 @@ void scanCommand(client *c) {
}
void dbsizeCommand(client *c) {
addReplyLongLong(c,dictSize(c->db->pdict));
addReplyLongLong(c,dictSize(c->db->dict));
}
void lastsaveCommand(client *c) {
@@ -1057,14 +1155,6 @@ void shutdownCommand(client *c) {
return;
}
}
/* When SHUTDOWN is called while the server is loading a dataset in
* memory we need to make sure no attempt is performed to save
* the dataset on shutdown (otherwise it could overwrite the current DB
* with half-read data).
*
* Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */
if (g_pserver->loading || g_pserver->sentinel_mode)
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
if (prepareForShutdown(flags) == C_OK) throw ShutdownException();
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
}
@@ -1228,23 +1318,16 @@ int dbSwapDatabases(long id1, long id2) {
if (id1 < 0 || id1 >= cserver.dbnum ||
id2 < 0 || id2 >= cserver.dbnum) return C_ERR;
if (id1 == id2) return C_OK;
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
* remain in the same DB they were. */
db1->pdict = db2->pdict;
db1->setexpire = db2->setexpire;
db1->expireitr = db2->expireitr;
db1->avg_ttl = db2->avg_ttl;
db1->last_expire_set = db2->last_expire_set;
db2->pdict = aux.pdict;
db2->setexpire = aux.setexpire;
db2->expireitr = aux.expireitr;
db2->avg_ttl = aux.avg_ttl;
db2->last_expire_set = aux.last_expire_set;
std::swap(db1->dict, db2->dict);
std::swap(db1->setexpire, db2->setexpire);
std::swap(db1->expireitr, db2->expireitr);
std::swap(db1->avg_ttl, db2->avg_ttl);
std::swap(db1->last_expire_set, db2->last_expire_set);
/* Now we need to handle clients blocked on lists: as an effect
* of swapping the two DBs, a client that was waiting for list
@@ -1254,9 +1337,14 @@ int dbSwapDatabases(long id1, long id2) {
* However normally we only do this check for efficiency reasons
* in dbAdd() when a list is created. So here we need to rescan
* the list of clients blocked on lists and signal lists as ready
* if needed. */
* if needed.
*
* Also the swapdb should make transaction fail if there is any
* client watching keys */
scanDatabaseForReadyLists(db1);
touchAllWatchedKeysInDb(db1, db2);
scanDatabaseForReadyLists(db2);
touchAllWatchedKeysInDb(db2, db1);
return C_OK;
}
@@ -1284,6 +1372,8 @@ void swapdbCommand(client *c) {
addReplyError(c,"DB index is out of range");
return;
} else {
RedisModuleSwapDbInfo si = {REDISMODULE_SWAPDBINFO_VERSION,(int32_t)id1,(int32_t)id2};
moduleFireServerEvent(REDISMODULE_EVENT_SWAPDB,0,&si);
g_pserver->dirty++;
addReply(c,shared.ok);
}
@@ -1293,7 +1383,7 @@ void swapdbCommand(client *c) {
* Expires API
*----------------------------------------------------------------------------*/
int removeExpire(redisDb *db, robj *key) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
dictEntry *de = dictFind(db->dict,ptrFromObj(key));
return removeExpireCore(db, key, de);
}
int removeExpireCore(redisDb *db, robj *key, dictEntry *de) {
@@ -1314,7 +1404,7 @@ int removeExpireCore(redisDb *db, robj *key, dictEntry *de) {
}
int removeSubkeyExpire(redisDb *db, robj *key, robj *subkey) {
dictEntry *de = dictFind(db->pdict,ptrFromObj(key));
dictEntry *de = dictFind(db->dict,ptrFromObj(key));
serverAssertWithInfo(NULL,key,de != NULL);
robj *val = (robj*)dictGetVal(de);
@@ -1356,13 +1446,13 @@ void setExpire(client *c, redisDb *db, robj *key, robj *subkey, long long when)
serverAssert(GlobalLocksAcquired());
/* Reuse the sds from the main dict in the expire dict */
kde = dictFind(db->pdict,ptrFromObj(key));
kde = dictFind(db->dict,ptrFromObj(key));
serverAssertWithInfo(NULL,key,kde != NULL);
if (((robj*)dictGetVal(kde))->getrefcount(std::memory_order_relaxed) == OBJ_SHARED_REFCOUNT)
{
// shared objects cannot have the expire bit set, create a real object
dictSetVal(db->pdict, kde, dupStringObject((robj*)dictGetVal(kde)));
dictSetVal(db->dict, kde, dupStringObject((robj*)dictGetVal(kde)));
}
/* Update TTL stats (exponential moving average) */
@@ -1415,13 +1505,13 @@ void setExpire(client *c, redisDb *db, robj *key, expireEntry &&e)
serverAssert(GlobalLocksAcquired());
/* Reuse the sds from the main dict in the expire dict */
kde = dictFind(db->pdict,ptrFromObj(key));
kde = dictFind(db->dict,ptrFromObj(key));
serverAssertWithInfo(NULL,key,kde != NULL);
if (((robj*)dictGetVal(kde))->getrefcount(std::memory_order_relaxed) == OBJ_SHARED_REFCOUNT)
{
// shared objects cannot have the expire bit set, create a real object
dictSetVal(db->pdict, kde, dupStringObject((robj*)dictGetVal(kde)));
dictSetVal(db->dict, kde, dupStringObject((robj*)dictGetVal(kde)));
}
if (((robj*)dictGetVal(kde))->FExpires())
@@ -1446,7 +1536,7 @@ expireEntry *getExpire(redisDb *db, robj_roptr key) {
if (db->setexpire->size() == 0)
return nullptr;
de = dictFind(db->pdict, ptrFromObj(key));
de = dictFind(db->dict, ptrFromObj(key));
if (de == NULL)
return nullptr;
robj *obj = (robj*)dictGetVal(de);
@@ -1623,27 +1713,54 @@ int expireIfNeeded(redisDb *db, robj *key) {
/* -----------------------------------------------------------------------------
* API to get key arguments from commands
* ---------------------------------------------------------------------------*/
#define MAX_KEYS_BUFFER 256
thread_local static int getKeysTempBuffer[MAX_KEYS_BUFFER];
/* Prepare the getKeysResult struct to hold numkeys, either by using the
* pre-allocated keysbuf or by allocating a new array on the heap.
*
* This function must be called at least once before starting to populate
* the result, and can be called repeatedly to enlarge the result array.
*/
int *getKeysPrepareResult(getKeysResult *result, int numkeys) {
/* GETKEYS_RESULT_INIT initializes keys to NULL, point it to the pre-allocated stack
* buffer here. */
if (!result->keys) {
serverAssert(!result->numkeys);
result->keys = result->keysbuf;
}
/* Resize if necessary */
if (numkeys > result->size) {
if (result->keys != result->keysbuf) {
/* We're not using a static buffer, just (re)alloc */
result->keys = (int*)zrealloc(result->keys, numkeys * sizeof(int));
} else {
/* We are using a static buffer, copy its contents */
result->keys = (int*)zmalloc(numkeys * sizeof(int));
if (result->numkeys)
memcpy(result->keys, result->keysbuf, result->numkeys * sizeof(int));
}
result->size = numkeys;
}
return result->keys;
}
/* The base case is to use the keys position as given in the command table
* (firstkey, lastkey, step). */
int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {
int getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result) {
int j, i = 0, last, *keys;
UNUSED(argv);
if (cmd->firstkey == 0) {
*numkeys = 0;
return NULL;
result->numkeys = 0;
return 0;
}
last = cmd->lastkey;
if (last < 0) last = argc+last;
int count = ((last - cmd->firstkey)+1);
keys = getKeysTempBuffer;
if (count > MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*count);
keys = getKeysPrepareResult(result, count);
for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
if (j >= argc) {
@@ -1654,23 +1771,23 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
* return no keys and expect the command implementation to report
* an arity or syntax error. */
if (cmd->flags & CMD_MODULE || cmd->arity < 0) {
getKeysFreeResult(keys);
*numkeys = 0;
return NULL;
getKeysFreeResult(result);
result->numkeys = 0;
return 0;
} else {
serverPanic("Redis built-in command declared keys positions not matching the arity requirements.");
}
}
keys[i++] = j;
}
*numkeys = i;
return keys;
result->numkeys = i;
return i;
}
/* Return all the arguments that are keys in the command passed via argc / argv.
*
* The command returns the positions of all the key arguments inside the array,
* so the actual return value is an heap allocated array of integers. The
* so the actual return value is a heap allocated array of integers. The
* length of the array is returned by reference into *numkeys.
*
* 'cmd' must be point to the corresponding entry into the redisCommand
@@ -1678,26 +1795,26 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
*
* This function uses the command table if a command-specific helper function
* is not required, otherwise it calls the command-specific function. */
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
if (cmd->flags & CMD_MODULE_GETKEYS) {
return moduleGetCommandKeysViaAPI(cmd,argv,argc,numkeys);
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
} else if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) {
return cmd->getkeys_proc(cmd,argv,argc,numkeys);
return cmd->getkeys_proc(cmd,argv,argc,result);
} else {
return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
return getKeysUsingCommandTable(cmd,argv,argc,result);
}
}
/* Free the result of getKeysFromCommand. */
void getKeysFreeResult(int *result) {
if (result != getKeysTempBuffer)
zfree(result);
void getKeysFreeResult(getKeysResult *result) {
if (result && result->keys != result->keysbuf)
zfree(result->keys);
}
/* Helper function to extract keys from following commands:
* ZUNIONSTORE <destkey> <num-keys> <key> <key> ... <key> <options>
* ZINTERSTORE <destkey> <num-keys> <key> <key> ... <key> <options> */
int *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, *keys;
UNUSED(cmd);
@@ -1705,30 +1822,30 @@ int *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *nu
/* Sanity check. Don't return any key if the command is going to
* reply with syntax error. */
if (num < 1 || num > (argc-3)) {
*numkeys = 0;
return NULL;
result->numkeys = 0;
return 0;
}
/* Keys in z{union,inter}store come from two places:
* argv[1] = storage key,
* argv[3...n] = keys to intersect */
keys = getKeysTempBuffer;
if (num+1>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*(num+1));
/* Total keys = {union,inter} keys + storage key */
keys = getKeysPrepareResult(result, num+1);
result->numkeys = num+1;
/* Add all key positions for argv[3...n] to keys[] */
for (i = 0; i < num; i++) keys[i] = 3+i;
/* Finally add the argv[1] key position (the storage key target). */
keys[num] = 1;
*numkeys = num+1; /* Total keys = {union,inter} keys + storage key */
return keys;
return result->numkeys;
}
/* Helper function to extract keys from the following commands:
* EVAL <script> <num-keys> <key> <key> ... <key> [more stuff]
* EVALSHA <script> <num-keys> <key> <key> ... <key> [more stuff] */
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, *keys;
UNUSED(cmd);
@@ -1736,20 +1853,17 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
/* Sanity check. Don't return any key if the command is going to
* reply with syntax error. */
if (num <= 0 || num > (argc-3)) {
*numkeys = 0;
return NULL;
result->numkeys = 0;
return 0;
}
keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*num);
*numkeys = num;
keys = getKeysPrepareResult(result, num);
result->numkeys = num;
/* Add all key positions for argv[3...n] to keys[] */
for (i = 0; i < num; i++) keys[i] = 3+i;
return keys;
return result->numkeys;
}
/* Helper function to extract keys from the SORT command.
@@ -1759,13 +1873,12 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
* The first argument of SORT is always a key, however a list of options
* follow in SQL-alike style. Here we parse just the minimum in order to
* correctly identify keys in the "STORE" option. */
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, j, num, *keys, found_store = 0;
UNUSED(cmd);
num = 0;
keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */
keys = getKeysPrepareResult(result, 2); /* Alloc 2 places for the worst case. */
keys[num++] = 1; /* <sort-key> is always present. */
/* Search for STORE option. By default we consider options to don't
@@ -1797,11 +1910,11 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
}
}
}
*numkeys = num + found_store;
return keys;
result->numkeys = num + found_store;
return result->numkeys;
}
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, first, *keys;
UNUSED(cmd);
@@ -1822,20 +1935,17 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
}
}
keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*num);
keys = getKeysPrepareResult(result, num);
for (i = 0; i < num; i++) keys[i] = first+i;
*numkeys = num;
return keys;
result->numkeys = num;
return num;
}
/* Helper function to extract keys from following commands:
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
* [COUNT count] [STORE key] [STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ... */
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num, *keys;
UNUSED(cmd);
@@ -1858,24 +1968,21 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
* argv[1] = key,
* argv[5...n] = stored key if present
*/
keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int) * num);
keys = getKeysPrepareResult(result, num);
/* Add all key positions to keys[] */
keys[0] = 1;
if(num > 1) {
keys[1] = stored_key;
}
*numkeys = num;
return keys;
result->numkeys = num;
return num;
}
/* LCS ... [KEYS <key1> <key2>] ... */
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
{
int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i;
int *keys = getKeysTempBuffer;
int *keys = getKeysPrepareResult(result, 2);
UNUSED(cmd);
/* We need to parse the options of the command in order to check for the
@@ -1889,33 +1996,32 @@ int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
} else if (!strcasecmp(arg, "keys") && moreargs >= 2) {
keys[0] = i+1;
keys[1] = i+2;
*numkeys = 2;
return keys;
result->numkeys = 2;
return result->numkeys;
}
}
*numkeys = 0;
return keys;
result->numkeys = 0;
return result->numkeys;
}
/* Helper function to extract keys from memory command.
* MEMORY USAGE <key> */
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int *keys;
int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
UNUSED(cmd);
getKeysPrepareResult(result, 1);
if (argc >= 3 && !strcasecmp(szFromObj(argv[1]),"usage")) {
keys = getKeysTempBuffer;
keys[0] = 2;
*numkeys = 1;
return keys;
result->keys[0] = 2;
result->numkeys = 1;
return result->numkeys;
}
*numkeys = 0;
return NULL;
result->numkeys = 0;
return 0;
}
/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]
* STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
int i, num = 0, *keys;
UNUSED(cmd);
@@ -1945,19 +2051,16 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
/* Syntax error. */
if (streams_pos == -1 || num == 0 || num % 2 != 0) {
*numkeys = 0;
return NULL;
result->numkeys = 0;
return 0;
}
num /= 2; /* We have half the keys as there are arguments because
there are also the IDs, one per key. */
keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int) * num);
keys = getKeysPrepareResult(result, num);
for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;
*numkeys = num;
return keys;
result->numkeys = num;
return num;
}
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
@@ -1995,13 +2098,25 @@ void slotToKeyDel(sds key) {
slotToKeyUpdateKey(key,0);
}
void slotToKeyFlush(void) {
serverAssert(GlobalLocksAcquired());
/* Release the radix tree mapping Redis Cluster keys to slots. If 'async'
* is true, we release it asynchronously. */
void freeSlotsToKeysMap(rax *rt, int async) {
if (async) {
freeSlotsToKeysMapAsync(rt);
} else {
raxFree(rt);
}
}
/* Empty the slots-keys map of Redis CLuster by creating a new empty one and
* freeing the old one. */
void slotToKeyFlush(int async) {
rax *old = g_pserver->cluster->slots_to_keys;
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));
freeSlotsToKeysMap(old, async);
}
/* Pupulate the specified array of objects with keys in the specified slot.
+195 -34
View File
@@ -189,7 +189,7 @@ void xorObjectDigest(redisDb *db, robj_roptr keyobj, unsigned char *digest, robj
}
} else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = (zset*)ptrFromObj(o);
dictIterator *di = dictGetIterator(zs->pdict);
dictIterator *di = dictGetIterator(zs->dict);
dictEntry *de;
while((de = dictNext(di)) != NULL) {
@@ -281,8 +281,8 @@ void computeDatasetDigest(unsigned char *final) {
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
if (dictSize(db->pdict) == 0) continue;
di = dictGetSafeIterator(db->pdict);
if (dictSize(db->dict) == 0) continue;
di = dictGetSafeIterator(db->dict);
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
@@ -392,6 +392,7 @@ void debugCommand(client *c) {
"DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false]",
"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
"LOG <message> -- write message to the server log.",
"LEAK <string> -- Create a memory leak of the input string.",
"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
"HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.",
"LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.",
@@ -400,7 +401,7 @@ void debugCommand(client *c) {
"OOM -- Crash the server simulating an out-of-memory error.",
"PANIC -- Crash the server simulating a panic.",
"POPULATE <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.",
"RELOAD [MERGE] [NOFLUSH] [NOSAVE] -- Save the RDB on disk and reload it back in memory. By default it will save the RDB file and load it back. With the NOFLUSH option the current database is not removed before loading the new one, but conficts in keys will kill the server with an exception. When MERGE is used, conflicting keys will be loaded (the key in the loaded RDB file will win). When NOSAVE is used, the server will not save the current dataset in the RDB file before loading. Use DEBUG RELOAD NOSAVE when you want just to load the RDB file you placed in the Redis working directory in order to replace the current dataset in memory. Use DEBUG RELOAD NOSAVE NOFLUSH MERGE when you want to add what is in the current RDB file placed in the Redis current directory, with the current memory content. Use DEBUG RELOAD when you want to verify Redis is able to persist the current dataset in the RDB file, flush the memory content, and load it back.",
"RELOAD [MERGE] [NOFLUSH] [NOSAVE] -- Save the RDB on disk and reload it back in memory. By default it will save the RDB file and load it back. With the NOFLUSH option the current database is not removed before loading the new one, but conflicts in keys will kill the server with an exception. When MERGE is used, conflicting keys will be loaded (the key in the loaded RDB file will win). When NOSAVE is used, the server will not save the current dataset in the RDB file before loading. Use DEBUG RELOAD NOSAVE when you want just to load the RDB file you placed in the Redis working directory in order to replace the current dataset in memory. Use DEBUG RELOAD NOSAVE NOFLUSH MERGE when you want to add what is in the current RDB file placed in the Redis current directory, with the current memory content. Use DEBUG RELOAD when you want to verify Redis is able to persist the current dataset in the RDB file, flush the memory content, and load it back.",
"RESTART -- Graceful restart: save config, db, restart.",
"SDSLEN <key> -- Show low level SDS string info representing key and value.",
"SEGFAULT -- Crash the server with sigsegv.",
@@ -410,6 +411,7 @@ void debugCommand(client *c) {
"STRUCTSIZE -- Return the size of different Redis core C structures.",
"ZIPLIST <key> -- Show low level info about the ziplist encoding.",
"STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.",
"CONFIG-REWRITE-FORCE-ALL -- Like CONFIG REWRITE but writes all configuration options, including keywords not listed in original configuration file or default values.",
#ifdef USE_JEMALLOC
"MALLCTL <key> [<val>] -- Get or set a malloc tunning integer.",
"MALLCTL-STR <key> [<val>] -- Get or set a malloc tunning string.",
@@ -444,6 +446,9 @@ NULL
} else if (!strcasecmp(szFromObj(c->argv[1]),"log") && c->argc == 3) {
serverLog(LL_WARNING, "DEBUG LOG: %s", (char*)ptrFromObj(c->argv[2]));
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"leak") && c->argc == 3) {
sdsdup(szFromObj(c->argv[2]));
addReply(c,shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"reload")) {
int flush = 1, save = 1;
int flags = RDBFLAGS_NONE;
@@ -465,7 +470,7 @@ NULL
}
}
/* The default beahvior is to save the RDB file before loading
/* The default behavior is to save the RDB file before loading
* it back. */
if (save) {
rdbSaveInfo rsi, *rsiptr;
@@ -508,7 +513,7 @@ NULL
robj *val;
const char *strenc;
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
if ((de = dictFind(c->db->dict,ptrFromObj(c->argv[2]))) == NULL) {
addReply(c,shared.nokeyerr);
return;
}
@@ -560,7 +565,7 @@ NULL
robj *val;
sds key;
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
if ((de = dictFind(c->db->dict,ptrFromObj(c->argv[2]))) == NULL) {
addReply(c,shared.nokeyerr);
return;
}
@@ -581,10 +586,10 @@ NULL
(long long) getStringObjectSdsUsedMemory(val));
}
} else if (!strcasecmp(szFromObj(c->argv[1]),"ziplist") && c->argc == 3) {
robj *o;
robj_roptr o;
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
== NULL) return;
== nullptr) return;
if (o->encoding != OBJ_ENCODING_ZIPLIST) {
addReplyError(c,"Not an sds encoded string.");
@@ -600,15 +605,14 @@ NULL
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
return;
dictExpand(c->db->pdict,keys);
dictExpand(c->db->dict,keys);
long valsize = 0;
if ( c->argc == 5 && getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK )
return;
for (j = 0; j < keys; j++) {
long valsize = 0;
snprintf(buf,sizeof(buf),"%s:%lu",
(c->argc == 3) ? "key" : (char*)ptrFromObj(c->argv[3]), j);
key = createStringObject(buf,strlen(buf));
if (c->argc == 5)
if (getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK)
return;
if (lookupKeyWrite(c->db,key) != NULL) {
decrRefCount(key);
continue;
@@ -641,7 +645,11 @@ NULL
for (int j = 2; j < c->argc; j++) {
unsigned char digest[20];
memset(digest,0,20); /* Start with a clean result */
robj_roptr o = lookupKeyReadWithFlags(c->db,c->argv[j],LOOKUP_NOTOUCH);
/* We don't use lookupKey because a debug command should
* work on logically expired keys */
dictEntry *de;
robj* o = (robj*)((de = dictFind(c->db->dict,ptrFromObj(c->argv[j]))) == NULL ? NULL : dictGetVal(de));
if (o) xorObjectDigest(c->db,c->argv[j],digest,o);
sds d = sdsempty();
@@ -759,7 +767,7 @@ NULL
}
stats = sdscatprintf(stats,"[Dictionary HT]\n");
dictGetStats(buf,sizeof(buf),g_pserver->db[dbid].pdict);
dictGetStats(buf,sizeof(buf),g_pserver->db[dbid].dict);
stats = sdscat(stats,buf);
stats = sdscatprintf(stats,"[Expires set]\n");
@@ -769,18 +777,18 @@ NULL
addReplyVerbatim(c,stats,sdslen(stats),"txt");
sdsfree(stats);
} else if (!strcasecmp(szFromObj(c->argv[1]),"htstats-key") && c->argc == 3) {
robj *o;
robj_roptr o;
dict *ht = NULL;
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
== NULL) return;
== nullptr) return;
/* Get the hash table reference from the object, if possible. */
switch (o->encoding) {
case OBJ_ENCODING_SKIPLIST:
{
zset *zs = (zset*)ptrFromObj(o);
ht = zs->pdict;
ht = zs->dict;
}
break;
case OBJ_ENCODING_HT:
@@ -825,6 +833,19 @@ NULL
c->flags &= ~(CLIENT_MASTER | CLIENT_MASTER_FORCE_REPLY);
}
addReply(c, shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"truncate-repl-backlog") && c->argc == 2) {
g_pserver->repl_backlog_idx = 0;
g_pserver->repl_backlog_off = g_pserver->master_repl_offset+1;
g_pserver->repl_backlog_histlen = 0;
if (g_pserver->repl_batch_idxStart >= 0) g_pserver->repl_batch_idxStart = -1;
if (g_pserver->repl_batch_offStart >= 0) g_pserver->repl_batch_offStart = -1;
addReply(c, shared.ok);
} else if (!strcasecmp(szFromObj(c->argv[1]),"config-rewrite-force-all") && c->argc == 2)
{
if (rewriteConfig(cserver.configfile, 1) == -1)
addReplyError(c, "CONFIG-REWRITE-FORCE-ALL failed");
else
addReply(c, shared.ok);
#ifdef USE_JEMALLOC
} else if(!strcasecmp(szFromObj(c->argv[1]),"mallctl") && c->argc >= 3) {
mallctl_int(c, c->argv+2, c->argc-2);
@@ -958,12 +979,15 @@ static void *getMcontextEip(ucontext_t *uc) {
/* OSX >= 10.6 */
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__rip;
#else
#elif defined(__i386__)
return (void*) uc->uc_mcontext->__ss.__eip;
#else
/* OSX ARM64 */
return (void*) arm_thread_state64_get_pc(uc->uc_mcontext->__ss);
#endif
#elif defined(__linux__)
/* Linux */
#if defined(__i386__) || defined(__ILP32__)
#if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__))
return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
#elif defined(__X86_64__) || defined(__x86_64__)
return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
@@ -988,6 +1012,12 @@ static void *getMcontextEip(ucontext_t *uc) {
#elif defined(__x86_64__)
return (void*) uc->sc_rip;
#endif
#elif defined(__NetBSD__)
#if defined(__i386__)
return (void*) uc->uc_mcontext.__gregs[_REG_EIP];
#elif defined(__x86_64__)
return (void*) uc->uc_mcontext.__gregs[_REG_RIP];
#endif
#elif defined(__DragonFly__)
return (void*) uc->uc_mcontext.mc_rip;
#else
@@ -1045,7 +1075,7 @@ void logRegisters(ucontext_t *uc) {
(unsigned long) uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
#else
#elif defined(__i386__)
/* OSX x86 */
serverLog(LL_WARNING,
"\n"
@@ -1071,11 +1101,60 @@ void logRegisters(ucontext_t *uc) {
(unsigned long) uc->uc_mcontext->__ss.__gs
);
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
#else
/* OSX ARM64 */
serverLog(LL_WARNING,
"\n"
"x0:%016lx x1:%016lx x2:%016lx x3:%016lx\n"
"x4:%016lx x5:%016lx x6:%016lx x7:%016lx\n"
"x8:%016lx x9:%016lx x10:%016lx x11:%016lx\n"
"x12:%016lx x13:%016lx x14:%016lx x15:%016lx\n"
"x16:%016lx x17:%016lx x18:%016lx x19:%016lx\n"
"x20:%016lx x21:%016lx x22:%016lx x23:%016lx\n"
"x24:%016lx x25:%016lx x26:%016lx x27:%016lx\n"
"x28:%016lx fp:%016lx lr:%016lx\n"
"sp:%016lx pc:%016lx cpsr:%08lx\n",
(unsigned long) uc->uc_mcontext->__ss.__x[0],
(unsigned long) uc->uc_mcontext->__ss.__x[1],
(unsigned long) uc->uc_mcontext->__ss.__x[2],
(unsigned long) uc->uc_mcontext->__ss.__x[3],
(unsigned long) uc->uc_mcontext->__ss.__x[4],
(unsigned long) uc->uc_mcontext->__ss.__x[5],
(unsigned long) uc->uc_mcontext->__ss.__x[6],
(unsigned long) uc->uc_mcontext->__ss.__x[7],
(unsigned long) uc->uc_mcontext->__ss.__x[8],
(unsigned long) uc->uc_mcontext->__ss.__x[9],
(unsigned long) uc->uc_mcontext->__ss.__x[10],
(unsigned long) uc->uc_mcontext->__ss.__x[11],
(unsigned long) uc->uc_mcontext->__ss.__x[12],
(unsigned long) uc->uc_mcontext->__ss.__x[13],
(unsigned long) uc->uc_mcontext->__ss.__x[14],
(unsigned long) uc->uc_mcontext->__ss.__x[15],
(unsigned long) uc->uc_mcontext->__ss.__x[16],
(unsigned long) uc->uc_mcontext->__ss.__x[17],
(unsigned long) uc->uc_mcontext->__ss.__x[18],
(unsigned long) uc->uc_mcontext->__ss.__x[19],
(unsigned long) uc->uc_mcontext->__ss.__x[20],
(unsigned long) uc->uc_mcontext->__ss.__x[21],
(unsigned long) uc->uc_mcontext->__ss.__x[22],
(unsigned long) uc->uc_mcontext->__ss.__x[23],
(unsigned long) uc->uc_mcontext->__ss.__x[24],
(unsigned long) uc->uc_mcontext->__ss.__x[25],
(unsigned long) uc->uc_mcontext->__ss.__x[26],
(unsigned long) uc->uc_mcontext->__ss.__x[27],
(unsigned long) uc->uc_mcontext->__ss.__x[28],
(unsigned long) arm_thread_state64_get_fp(uc->uc_mcontext->__ss),
(unsigned long) arm_thread_state64_get_lr(uc->uc_mcontext->__ss),
(unsigned long) arm_thread_state64_get_sp(uc->uc_mcontext->__ss),
(unsigned long) arm_thread_state64_get_pc(uc->uc_mcontext->__ss),
(unsigned long) uc->uc_mcontext->__ss.__cpsr
);
logStackContent((void**) arm_thread_state64_get_sp(uc->uc_mcontext->__ss));
#endif
/* Linux */
#elif defined(__linux__)
/* Linux x86 */
#if defined(__i386__) || defined(__ILP32__)
#if defined(__i386__) || ((defined(__X86_64__) || defined(__x86_64__)) && defined(__ILP32__))
serverLog(LL_WARNING,
"\n"
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
@@ -1163,7 +1242,7 @@ void logRegisters(ucontext_t *uc) {
"R10:%016lx R9 :%016lx\nR8 :%016lx R7 :%016lx\n"
"R6 :%016lx R5 :%016lx\nR4 :%016lx R3 :%016lx\n"
"R2 :%016lx R1 :%016lx\nR0 :%016lx EC :%016lx\n"
"fp: %016lx ip:%016lx\n",
"fp: %016lx ip:%016lx\n"
"pc:%016lx sp:%016lx\ncpsr:%016lx fault_address:%016lx\n",
(unsigned long) uc->uc_mcontext.arm_r10,
(unsigned long) uc->uc_mcontext.arm_r9,
@@ -1296,6 +1375,59 @@ void logRegisters(ucontext_t *uc) {
);
logStackContent((void**)uc->sc_esp);
#endif
#elif defined(__NetBSD__)
#if defined(__x86_64__)
serverLog(LL_WARNING,
"\n"
"RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n"
"RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n"
"R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n"
"R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n"
"RIP:%016lx EFL:%016lx\nCSGSFS:%016lx",
(unsigned long) uc->uc_mcontext.__gregs[_REG_RAX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RBX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RCX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RDX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RDI],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RSI],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RBP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RSP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R8],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R9],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R10],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R11],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R12],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R13],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R14],
(unsigned long) uc->uc_mcontext.__gregs[_REG_R15],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RIP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_RFLAGS],
(unsigned long) uc->uc_mcontext.__gregs[_REG_CS]
);
logStackContent((void**)uc->uc_mcontext.__gregs[_REG_RSP]);
#elif defined(__i386__)
serverLog(LL_WARNING,
"\n"
"EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n"
"EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n"
"SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n"
"DS :%08lx ES :%08lx FS :%08lx GS:%08lx",
(unsigned long) uc->uc_mcontext.__gregs[_REG_EAX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EBX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EDX],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EDI],
(unsigned long) uc->uc_mcontext.__gregs[_REG_ESI],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EBP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_ESP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_SS],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EFLAGS],
(unsigned long) uc->uc_mcontext.__gregs[_REG_EIP],
(unsigned long) uc->uc_mcontext.__gregs[_REG_CS],
(unsigned long) uc->uc_mcontext.__gregs[_REG_ES],
(unsigned long) uc->uc_mcontext.__gregs[_REG_FS],
(unsigned long) uc->uc_mcontext.__gregs[_REG_GS]
);
#endif
#elif defined(__DragonFly__)
serverLog(LL_WARNING,
"\n"
@@ -1457,12 +1589,12 @@ void logCurrentClient(void) {
}
/* Check if the first argument, usually a key, is found inside the
* selected DB, and if so print info about the associated object. */
if (cc->argc >= 1) {
if (cc->argc > 1) {
robj *val, *key;
dictEntry *de;
key = getDecodedObject(cc->argv[1]);
de = dictFind(cc->db->pdict, ptrFromObj(key));
de = dictFind(cc->db->dict, ptrFromObj(key));
if (de) {
val = (robj*)dictGetVal(de);
serverLog(LL_WARNING,"key '%s' found in DB containing the following object:", (char*)ptrFromObj(key));
@@ -1476,7 +1608,7 @@ void logCurrentClient(void) {
#define MEMTEST_MAX_REGIONS 128
/* A non destructive memory test executed during segfauls. */
/* A non destructive memory test executed during segfault. */
int memtest_test_linux_anonymous_maps(void) {
FILE *fp;
char line[1024];
@@ -1537,7 +1669,32 @@ int memtest_test_linux_anonymous_maps(void) {
closeDirectLogFiledes(fd);
return errors;
}
#endif
#endif /* HAVE_PROC_MAPS */
static void killServerThreads(void) {
int err;
for (int i = 0; i < cserver.cthreads; i++) {
if (g_pserver->rgthread[i] != pthread_self()) {
pthread_cancel(g_pserver->rgthread[i]);
}
}
if (pthread_self() != cserver.main_thread_id && pthread_cancel(cserver.main_thread_id) == 0) {
if ((err = pthread_join(cserver.main_thread_id,NULL)) != 0) {
serverLog(LL_WARNING, "main thread can not be joined: %s", strerror(err));
} else {
serverLog(LL_WARNING, "main thread terminated");
}
}
}
/* Kill the running threads (other than current) in an unclean way. This function
* should be used only when it's critical to stop the threads for some reason.
* Currently Redis does this only on crash (for instance on SIGSEGV) in order
* to perform a fast memory check without other threads messing with memory. */
void killThreads(void) {
killServerThreads();
bioKillThreads();
}
/* Scans the (assumed) x86 code starting at addr, for a max of `len`
* bytes, searching for E8 (callq) opcodes, and dumping the symbols
@@ -1575,7 +1732,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
bugReportStart();
serverLog(LL_WARNING,
"KeyDB %s crashed by signal: %d", KEYDB_REAL_VERSION, sig);
"KeyDB %s crashed by signal: %d, si_code: %d", KEYDB_REAL_VERSION, sig, info->si_code);
if (eip != NULL) {
serverLog(LL_WARNING,
"Crashed running the instruction at: %p", eip);
@@ -1584,6 +1741,9 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
serverLog(LL_WARNING,
"Accessing address: %p", (void*)info->si_addr);
}
if (info->si_pid != -1) {
serverLog(LL_WARNING, "Killed by PID: %d, UID: %d", info->si_pid, info->si_uid);
}
serverLog(LL_WARNING,
"Failed assertion: %s (%s:%d)", g_pserver->assert_failed,
g_pserver->assert_file, g_pserver->assert_line);
@@ -1617,7 +1777,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
#if defined(HAVE_PROC_MAPS)
/* Test memory */
serverLogRaw(LL_WARNING|LL_RAW, "\n------ FAST MEMORY TEST ------\n");
bioKillThreads();
killThreads();
if (memtest_test_linux_anonymous_maps()) {
serverLogRaw(LL_WARNING|LL_RAW,
"!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!\n");
@@ -1645,13 +1805,14 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
/* Find the address of the next page, which is our "safety"
* limit when dumping. Then try to dump just 128 bytes more
* than EIP if there is room, or stop sooner. */
void *base = (void *)info.dli_saddr;
unsigned long next = ((unsigned long)eip + sz) & ~(sz-1);
unsigned long end = (unsigned long)eip + 128;
if (end > next) end = next;
len = end - (unsigned long)info.dli_saddr;
len = end - (unsigned long)base;
serverLogHexDump(LL_WARNING, "dump of function",
info.dli_saddr ,len);
dumpX86Calls(info.dli_saddr,len);
base ,len);
dumpX86Calls(base,len);
}
}
}
@@ -1664,7 +1825,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (cserver.daemonize && cserver.supervised == 0) unlink(cserver.pidfile);
if (cserver.daemonize && cserver.supervised == 0 && cserver.pidfile) unlink(cserver.pidfile);
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled. */
+28 -27
View File
@@ -47,12 +47,12 @@ extern "C" int je_get_defrag_hint(void* ptr);
/* forward declarations*/
void defragDictBucketCallback(void *privdata, dictEntry **bucketref);
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged);
bool replaceSateliteOSetKeyPtr(expireset &set, sds oldkey, sds newkey);
dictEntry* replaceSatelliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged);
bool replaceSatelliteOSetKeyPtr(expireset &set, sds oldkey, sds newkey);
/* Defrag helper for generic allocations.
*
* returns NULL in case the allocatoin wasn't moved.
* returns NULL in case the allocation wasn't moved.
* when it returns a non-null value, the old pointer was already released
* and should NOT be accessed. */
template<typename TPTR>
@@ -83,7 +83,7 @@ robj* activeDefragAlloc(robj *o) {
/*Defrag helper for sds strings
*
* returns NULL in case the allocatoin wasn't moved.
* returns NULL in case the allocation wasn't moved.
* when it returns a non-null value, the old pointer was already released
* and should NOT be accessed. */
sds activeDefragSds(sds sdsptr) {
@@ -99,7 +99,7 @@ sds activeDefragSds(sds sdsptr) {
/* Defrag helper for robj and/or string objects
*
* returns NULL in case the allocatoin wasn't moved.
* returns NULL in case the allocation wasn't moved.
* when it returns a non-null value, the old pointer was already released
* and should NOT be accessed. */
robj *activeDefragStringOb(robj* ob, long *defragged) {
@@ -137,11 +137,11 @@ robj *activeDefragStringOb(robj* ob, long *defragged) {
}
/* Defrag helper for dictEntries to be used during dict iteration (called on
* each step). Teturns a stat of how many pointers were moved. */
* each step). Returns a stat of how many pointers were moved. */
long dictIterDefragEntry(dictIterator *iter) {
/* This function is a little bit dirty since it messes with the internals
* of the dict and it's iterator, but the benefit is that it is very easy
* to use, and require no other chagnes in the dict. */
* to use, and require no other changes in the dict. */
long defragged = 0;
dictht *ht;
/* Handle the next entry (if there is one), and update the pointer in the
@@ -245,7 +245,7 @@ double *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) {
return NULL;
}
/* Defrag helpler for sorted set.
/* Defrag helper for sorted set.
* Defrag a single dict entry key name, and corresponding skiplist struct */
long activeDefragZsetEntry(zset *zs, dictEntry *de) {
sds newsds;
@@ -256,7 +256,7 @@ long activeDefragZsetEntry(zset *zs, dictEntry *de) {
defragged++, de->key = newsds;
newscore = zslDefrag(zs->zsl, *(double*)dictGetVal(de), sdsele, newsds);
if (newscore) {
dictSetVal(zs->pdict, de, newscore);
dictSetVal(zs->dict, de, newscore);
defragged++;
}
return defragged;
@@ -355,8 +355,8 @@ 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 */
uint64_t hash = dictGetHash(d, sdsele);
replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);
uint64_t hash = dictGetHash(d, newsds);
replaceSatelliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);
ln->value = newsds;
defragged++;
}
@@ -392,7 +392,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, uint64_t hash, long *defragged) {
dictEntry* replaceSatelliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) {
dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);
if (deref) {
dictEntry *de = *deref;
@@ -408,7 +408,7 @@ dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sd
return NULL;
}
bool replaceSateliteOSetKeyPtr(expireset &set, sds oldkey, sds newkey) {
bool replaceSatelliteOSetKeyPtr(expireset &set, sds oldkey, sds newkey) {
auto itr = set.find(oldkey);
if (itr != set.end())
{
@@ -454,7 +454,7 @@ long activeDefragQuickListNodes(quicklist *ql) {
}
/* when the value has lots of elements, we want to handle it later and not as
* oart of the main dictionary scan. this is needed in order to prevent latency
* part of the main dictionary scan. this is needed in order to prevent latency
* spikes when handling large items */
void defragLater(redisDb *db, dictEntry *kde) {
sds key = sdsdup((sds)dictGetKey(kde));
@@ -521,7 +521,7 @@ long scanLaterZset(robj *ob, unsigned long *cursor) {
if (ob->type != OBJ_ZSET || ob->encoding != OBJ_ENCODING_SKIPLIST)
return 0;
zset *zs = (zset*)ptrFromObj(ob);
dict *d = zs->pdict;
dict *d = zs->dict;
scanLaterZsetData data = {zs, 0};
*cursor = dictScan(d, *cursor, scanLaterZsetCallback, defragDictBucketCallback, &data);
return data.defragged;
@@ -596,20 +596,20 @@ 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) > cserver.active_defrag_max_scan_fields)
if (dictSize(zs->dict) > cserver.active_defrag_max_scan_fields)
defragLater(db, kde);
else {
dictIterator *di = dictGetIterator(zs->pdict);
dictIterator *di = dictGetIterator(zs->dict);
while((de = dictNext(di)) != NULL) {
defragged += activeDefragZsetEntry(zs, de);
}
dictReleaseIterator(di);
}
/* handle the dict struct */
if ((newdict = (dict*)activeDefragAlloc(zs->pdict)))
defragged++, zs->pdict = newdict;
if ((newdict = (dict*)activeDefragAlloc(zs->dict)))
defragged++, zs->dict = newdict;
/* defrag the dict tables */
defragged += dictDefragTables(zs->pdict);
defragged += dictDefragTables(zs->dict);
return defragged;
}
@@ -683,6 +683,7 @@ int scanLaterStraemListpacks(robj *ob, unsigned long *cursor, long long endtime,
/* if cursor is non-zero, we seek to the static 'last' */
if (!raxSeek(&ri,">", last, sizeof(last))) {
*cursor = 0;
raxStop(&ri);
return 0;
}
/* assign the iterator node callback after the seek, so that the
@@ -833,7 +834,7 @@ long defragKey(redisDb *db, dictEntry *de) {
{
defragged++, de->key = newsds;
if (!db->setexpire->empty()) {
bool fReplaced = replaceSateliteOSetKeyPtr(*db->setexpire, keysds, newsds);
bool fReplaced = replaceSatelliteOSetKeyPtr(*db->setexpire, keysds, newsds);
serverAssert(fReplaced == ob->FExpires());
} else {
serverAssert(!ob->FExpires());
@@ -908,7 +909,7 @@ void defragScanCallback(void *privdata, const dictEntry *de) {
g_pserver->stat_active_defrag_scanned++;
}
/* Defrag scan callback for each hash table bicket,
/* Defrag scan callback for each hash table bucket,
* used in order to defrag the dictEntry allocations. */
void defragDictBucketCallback(void *privdata, dictEntry **bucketref) {
UNUSED(privdata); /* NOTE: this function is also used by both activeDefragCycle and scanLaterHash, etc. don't use privdata */
@@ -942,7 +943,7 @@ float getAllocatorFragmentation(size_t *out_frag_bytes) {
return frag_pct;
}
/* We may need to defrag other globals, one small allcation can hold a full allocator run.
/* We may need to defrag other globals, one small allocation can hold a full allocator run.
* so although small, it is still important to defrag these */
long defragOtherGlobals() {
long defragged = 0;
@@ -1014,7 +1015,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, defrag_later_current_key);
dictEntry *de = dictFind(db->dict, defrag_later_current_key);
key_defragged = g_pserver->stat_active_defrag_hits;
do {
int quit = 0;
@@ -1113,7 +1114,7 @@ void activeDefragCycle(void) {
if (hasActiveChildProcess())
return; /* Defragging memory while there's a fork will just do damage. */
/* Once a second, check if we the fragmentation justfies starting a scan
/* Once a second, check if the fragmentation justfies starting a scan
* or making it more aggressive. */
run_with_period(1000) {
computeDefragCycles();
@@ -1177,13 +1178,13 @@ void activeDefragCycle(void) {
break; /* this will exit the function and we'll continue on the next cycle */
}
cursor = dictScan(db->pdict, cursor, defragScanCallback, defragDictBucketCallback, db);
cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);
/* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys
* (if we have a lot of pointers in one hash bucket or rehasing),
* check if we reached the time limit.
* 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 */
* the last db we call defragOtherGlobals, which must be done in one cycle */
if (!cursor || (++iterations > 16 ||
g_pserver->stat_active_defrag_hits - prev_defragged > 512 ||
g_pserver->stat_active_defrag_scanned - prev_scanned > 64)) {
+173 -13
View File
@@ -127,6 +127,7 @@ int _dictInit(dict *d, dictType *type,
d->privdata = privDataPtr;
d->rehashidx = -1;
d->iterators = 0;
d->asyncdata = nullptr;
return DICT_OK;
}
@@ -188,13 +189,13 @@ int dictExpand(dict *d, unsigned long size)
int dictRehash(dict *d, int n) {
int empty_visits = n*10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0;
if (d->asyncdata) return 0;
while(n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long)d->rehashidx);
while(d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
@@ -230,6 +231,144 @@ int dictRehash(dict *d, int n) {
return 1;
}
dictAsyncRehashCtl *dictRehashAsyncStart(dict *d, int buckets) {
if (!dictIsRehashing(d)) return 0;
d->asyncdata = new dictAsyncRehashCtl(d, d->asyncdata);
int empty_visits = buckets * 10;
while (d->asyncdata->queue.size() < (size_t)buckets && d->rehashidx < d->ht[0].size) {
dictEntry *de;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
while(d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) goto LDone;
if (d->rehashidx >= d->ht[0].size) goto LDone;
}
de = d->ht[0].table[d->rehashidx];
// We have to queue every node in the bucket, even if we go over our target size
while (de != nullptr) {
d->asyncdata->queue.emplace_back(de);
de = de->next;
}
d->rehashidx++;
}
LDone:
if (d->asyncdata->queue.empty()) {
// We didn't find any work to do (can happen if there is a large gap in the hash table)
auto asyncT = d->asyncdata;
d->asyncdata = d->asyncdata->next;
delete asyncT;
return nullptr;
}
return d->asyncdata;
}
void dictRehashAsync(dictAsyncRehashCtl *ctl) {
for (size_t idx = ctl->hashIdx; idx < ctl->queue.size(); ++idx) {
auto &wi = ctl->queue[idx];
wi.hash = dictHashKey(ctl->dict, dictGetKey(wi.de));
}
ctl->hashIdx = ctl->queue.size();
ctl->done = true;
}
bool dictRehashSomeAsync(dictAsyncRehashCtl *ctl, size_t hashes) {
size_t max = std::min(ctl->hashIdx + hashes, ctl->queue.size());
for (; ctl->hashIdx < max; ++ctl->hashIdx) {
auto &wi = ctl->queue[ctl->hashIdx];
wi.hash = dictHashKey(ctl->dict, dictGetKey(wi.de));
}
if (ctl->hashIdx == ctl->queue.size()) ctl->done = true;
return ctl->hashIdx < ctl->queue.size();
}
void dictCompleteRehashAsync(dictAsyncRehashCtl *ctl, bool fFree) {
dict *d = ctl->dict;
assert(ctl->done);
// Unlink ourselves
bool fUnlinked = false;
dictAsyncRehashCtl **pprev = &d->asyncdata;
while (*pprev != nullptr) {
if (*pprev == ctl) {
*pprev = ctl->next;
fUnlinked = true;
break;
}
pprev = &((*pprev)->next);
}
if (fUnlinked) {
if (ctl->next != nullptr && ctl->deGCList != nullptr) {
// An older work item may depend on our free list, so pass our free list to them
dictEntry **deGC = &ctl->next->deGCList;
while (*deGC != nullptr) deGC = &((*deGC)->next);
*deGC = ctl->deGCList;
ctl->deGCList = nullptr;
}
}
if (fUnlinked && !ctl->release) {
if (d->ht[0].table != nullptr) { // can be null if we're cleared during the rehash
for (auto &wi : ctl->queue) {
// We need to remove it from the source hash table, and store it in the dest.
// Note that it may have been deleted in the meantime and therefore not exist.
// In this case it will be in the garbage collection list
dictEntry **pdePrev = &d->ht[0].table[wi.hash & d->ht[0].sizemask];
while (*pdePrev != nullptr && *pdePrev != wi.de) {
pdePrev = &((*pdePrev)->next);
}
if (*pdePrev != nullptr) { // The element may be NULL if its in the GC list
assert(*pdePrev == wi.de);
*pdePrev = wi.de->next;
// Now link it to the dest hash table
wi.de->next = d->ht[1].table[wi.hash & d->ht[1].sizemask];
d->ht[1].table[wi.hash & d->ht[1].sizemask] = wi.de;
d->ht[0].used--;
d->ht[1].used++;
}
}
}
/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0 && d->asyncdata == nullptr) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
}
}
if (fFree) {
while (ctl->deGCList != nullptr) {
auto next = ctl->deGCList->next;
dictFreeKey(d, ctl->deGCList);
dictFreeVal(d, ctl->deGCList);
zfree(ctl->deGCList);
ctl->deGCList = next;
}
// Was the dictionary free'd while we were in flight?
if (ctl->release) {
if (d->asyncdata != nullptr)
d->asyncdata->release = true;
else
dictRelease(d);
}
delete ctl;
}
}
long long timeInMilliseconds(void) {
struct timeval tv;
@@ -237,7 +376,9 @@ long long timeInMilliseconds(void) {
return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
}
/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
/* Rehash in ms+"delta" milliseconds. The value of "delta" is larger
* than 0, and is smaller than 1 in most cases. The exact upper bound
* depends on the running time of dictRehash(d,100).*/
int dictRehashMilliseconds(dict *d, int ms) {
long long start = timeInMilliseconds();
int rehashes = 0;
@@ -383,9 +524,14 @@ static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {
else
d->ht[table].table[idx] = he->next;
if (!nofree) {
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
if (table == 0 && d->asyncdata != nullptr && idx < d->rehashidx) {
he->next = d->asyncdata->deGCList;
d->asyncdata->deGCList = he->next;
} else {
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
}
}
d->ht[table].used--;
return he;
@@ -433,9 +579,14 @@ dictEntry *dictUnlink(dict *ht, const void *key) {
* to dictUnlink(). It's safe to call this function with 'he' = NULL. */
void dictFreeUnlinkedEntry(dict *d, dictEntry *he) {
if (he == NULL) return;
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
if (d->asyncdata) {
he->next = d->asyncdata->deGCList;
d->asyncdata->deGCList = he;
} else {
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
}
}
/* Destroy an entire dictionary */
@@ -451,9 +602,14 @@ int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
if ((he = ht->table[i]) == NULL) continue;
while(he) {
nextHe = he->next;
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
if (d->asyncdata && i < d->rehashidx) {
he->next = d->asyncdata->deGCList;
d->asyncdata->deGCList = he;
} else {
dictFreeKey(d, he);
dictFreeVal(d, he);
zfree(he);
}
ht->used--;
he = nextHe;
}
@@ -468,6 +624,10 @@ int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
/* Clear & Release the hash table */
void dictRelease(dict *d)
{
if (d->asyncdata) {
d->asyncdata->release = true;
return;
}
_dictClear(d,&d->ht[0],NULL);
_dictClear(d,&d->ht[1],NULL);
zfree(d);
@@ -749,7 +909,7 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
* this function instead what we do is to consider a "linear" range of the table
* that may be constituted of N buckets with chains of different lengths
* appearing one after the other. Then we report a random element in the range.
* In this way we smooth away the problem of different chain lenghts. */
* In this way we smooth away the problem of different chain lengths. */
#define GETFAIR_NUM_ENTRIES 15
dictEntry *dictGetFairRandomKey(dict *d) {
dictEntry *entries[GETFAIR_NUM_ENTRIES];
@@ -1119,7 +1279,7 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {
i, clvector[i], ((float)clvector[i]/ht->size)*100);
}
/* Unlike snprintf(), teturn the number of characters actually written. */
/* Unlike snprintf(), return the number of characters actually written. */
if (bufsize) buf[bufsize-1] = '\0';
return strlen(buf);
}
+36
View File
@@ -36,6 +36,8 @@
#include <stdint.h>
#ifdef __cplusplus
#include <vector>
#include <atomic>
extern "C" {
#endif
@@ -81,12 +83,40 @@ typedef struct dictht {
unsigned long used;
} dictht;
#ifdef __cplusplus
struct dictAsyncRehashCtl {
struct workItem {
dictEntry *de;
uint64_t hash;
workItem(dictEntry *de) {
this->de = de;
}
};
static const int c_targetQueueSize = 512;
dictEntry *deGCList = nullptr;
struct dict *dict = nullptr;
std::vector<workItem> queue;
size_t hashIdx = 0;
bool release = false;
dictAsyncRehashCtl *next = nullptr;
std::atomic<bool> done { false };
dictAsyncRehashCtl(struct dict *d, dictAsyncRehashCtl *next) : dict(d), next(next) {
queue.reserve(c_targetQueueSize);
}
};
#else
struct dictAsyncRehashCtl;
#endif
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
dictAsyncRehashCtl *asyncdata;
} dict;
/* If safe is set to 1 this is a safe iterator, that means, you can call
@@ -190,6 +220,12 @@ unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, dictScanB
uint64_t dictGetHash(dict *d, const void *key);
dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t hash);
/* Async Rehash Functions */
dictAsyncRehashCtl *dictRehashAsyncStart(dict *d, int buckets = dictAsyncRehashCtl::c_targetQueueSize);
void dictRehashAsync(dictAsyncRehashCtl *ctl);
bool dictRehashSomeAsync(dictAsyncRehashCtl *ctl, size_t hashes);
void dictCompleteRehashAsync(dictAsyncRehashCtl *ctl, bool fFree);
/* Hash table types */
extern dictType dictTypeHeapStringCopyKey;
extern dictType dictTypeHeapStrings;
+1 -1
View File
@@ -8,7 +8,7 @@
* to be backward compatible are still in big endian) because most of the
* production environments are little endian, and we have a lot of conversions
* in a few places because ziplists, intsets, zipmaps, need to be endian-neutral
* even in memory, since they are serialied on RDB files directly with a single
* even in memory, since they are serialized on RDB files directly with a single
* write(2) without other additional steps.
*
* ----------------------------------------------------------------------------
+14 -12
View File
@@ -42,7 +42,7 @@
/* To improve the quality of the LRU approximation we take a set of keys
* that are good candidate for eviction across freeMemoryIfNeeded() calls.
*
* Entries inside the eviciton pool are taken ordered by idle time, putting
* Entries inside the eviction pool are taken ordered by idle time, putting
* greater idle times to the right (ascending order).
*
* When an LFU policy is used instead, a reverse frequency indication is used
@@ -88,7 +88,7 @@ unsigned int LRU_CLOCK(void) {
/* Given an object returns the min number of milliseconds the object was never
* requested, using an approximated LRU algorithm. */
unsigned long long estimateObjectIdleTime(robj *o) {
unsigned long long estimateObjectIdleTime(robj_roptr o) {
unsigned long long lruclock = LRU_CLOCK();
if (lruclock >= o->lru) {
return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
@@ -216,7 +216,7 @@ void processEvictionCandidate(int dbid, sds key, robj *o, const expireEntry *e,
/* Try to reuse the cached SDS string allocated in the pool entry,
* because allocating and deallocating this object is costly
* (according to the profiler, not my fantasy. Remember:
* premature optimizbla bla bla bla. */
* premature optimization bla bla bla bla. */
int klen = sdslen(key);
if (klen > EVPOOL_CACHED_SDS_SIZE) {
pool[k].key = sdsdup(key);
@@ -357,7 +357,7 @@ unsigned long LFUDecrAndReturn(robj *o) {
}
/* ----------------------------------------------------------------------------
* The external API for eviction: freeMemroyIfNeeded() is called by the
* The external API for eviction: freeMemoryIfNeeded() is called by the
* server when there is data to add in order to make space if needed.
* --------------------------------------------------------------------------*/
@@ -458,7 +458,7 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
*
* The function returns C_OK if we are under the memory limit or if we
* were over the limit, but the attempt to free memory was successful.
* Otehrwise if we are over the memory limit, but not enough memory
* Otherwise if we are over the memory limit, but not enough memory
* was freed to return back under the limit, the function returns C_ERR. */
int freeMemoryIfNeeded(void) {
serverAssert(GlobalLocksAcquired());
@@ -508,8 +508,8 @@ int freeMemoryIfNeeded(void) {
db = g_pserver->db+i;
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS)
{
if ((keys = dictSize(db->pdict)) != 0) {
evictionPoolPopulate(i, db->pdict, nullptr, pool);
if ((keys = dictSize(db->dict)) != 0) {
evictionPoolPopulate(i, db->dict, nullptr, pool);
total_keys += keys;
}
}
@@ -517,7 +517,7 @@ int freeMemoryIfNeeded(void) {
{
keys = db->setexpire->size();
if (keys != 0)
evictionPoolPopulate(i, db->pdict, db->setexpire, pool);
evictionPoolPopulate(i, db->dict, db->setexpire, pool);
total_keys += keys;
}
}
@@ -529,7 +529,7 @@ int freeMemoryIfNeeded(void) {
bestdbid = pool[k].dbid;
sds key = nullptr;
dictEntry *de = dictFind(g_pserver->db[pool[k].dbid].pdict,pool[k].key);
dictEntry *de = dictFind(g_pserver->db[pool[k].dbid].dict,pool[k].key);
if (de != nullptr && (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS || ((robj*)dictGetVal(de))->FExpires()))
key = (sds)dictGetKey(de);
@@ -563,8 +563,8 @@ int freeMemoryIfNeeded(void) {
db = g_pserver->db+j;
if (g_pserver->maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM)
{
if (dictSize(db->pdict) != 0) {
dictEntry *de = dictGetRandomKey(db->pdict);
if (dictSize(db->dict) != 0) {
dictEntry *de = dictGetRandomKey(db->dict);
bestkey = (sds)dictGetKey(de);
bestdbid = j;
break;
@@ -593,6 +593,8 @@ int freeMemoryIfNeeded(void) {
* we are freeing removing the key, but we can't account for
* that otherwise we would never exit the loop.
*
* Same for CSC invalidation messages generated by signalModifiedKey.
*
* AOF and Output buffer memory will be freed eventually so
* we only care about memory used by the key space. */
delta = (long long) zmalloc_used_memory();
@@ -601,12 +603,12 @@ int freeMemoryIfNeeded(void) {
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
signalModifiedKey(NULL,db,keyobj);
latencyEndMonitor(eviction_latency);
latencyAddSampleIfNeeded("eviction-del",eviction_latency);
delta -= (long long) zmalloc_used_memory();
mem_freed += delta;
g_pserver->stat_evictedkeys++;
signalModifiedKey(NULL,db,keyobj);
notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted",
keyobj, db->id);
decrRefCount(keyobj);
+27 -16
View File
@@ -54,7 +54,7 @@ void activeExpireCycleExpireFullKey(redisDb *db, const char *key) {
dbSyncDelete(db,keyobj);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",keyobj,db->id);
trackingInvalidateKey(NULL, keyobj);
signalModifiedKey(NULL, db, keyobj);
decrRefCount(keyobj);
g_pserver->stat_expiredkeys++;
}
@@ -76,7 +76,7 @@ void activeExpireCycleExpire(redisDb *db, expireEntry &e, long long now) {
}
expireEntryFat *pfat = e.pfatentry();
dictEntry *de = dictFind(db->pdict, e.key());
dictEntry *de = dictFind(db->dict, e.key());
robj *val = (robj*)dictGetVal(de);
int deleted = 0;
@@ -136,7 +136,7 @@ void activeExpireCycleExpire(redisDb *db, expireEntry &e, long long now) {
executeCronJobExpireHook(keyCopy, val);
sdsfree(keyCopy);
decrRefCount(val);
}, false, true /*fLock*/, true /*fForceQueue*/);
}, true /*fLock*/, true /*fForceQueue*/);
}
return;
@@ -297,14 +297,14 @@ void pexpireMemberAtCommand(client *c)
* Expire cycle type:
*
* If type is ACTIVE_EXPIRE_CYCLE_FAST the function will try to run a
* "fast" expire cycle that takes no longer than EXPIRE_FAST_CYCLE_DURATION
* "fast" expire cycle that takes no longer than ACTIVE_EXPIRE_CYCLE_FAST_DURATION
* microseconds, and is not repeated again before the same amount of time.
*
* If type is ACTIVE_EXPIRE_CYCLE_SLOW, that normal expire cycle is
* executed, where the time limit is a percentage of the REDIS_HZ period
* as specified by the ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC define. */
void activeExpireCycle(int type) {
void activeExpireCycleCore(int type) {
/* This function has some global state in order to continue the work
* incrementally across calls. */
static unsigned int current_db = 0; /* Last DB tested. */
@@ -420,6 +420,11 @@ void activeExpireCycle(int type) {
(g_pserver->stat_expired_stale_perc*0.95);
}
void activeExpireCycle(int type)
{
runAndPropogateToReplicas(activeExpireCycleCore, type);
}
/*-----------------------------------------------------------------------------
* Expires of keys created in writable slaves
*
@@ -479,7 +484,7 @@ void expireSlaveKeys(void) {
redisDb *db = g_pserver->db+dbid;
// the expire is hashed based on the key pointer, so we need the point in the main db
dictEntry *deMain = dictFind(db->pdict, keyname);
dictEntry *deMain = dictFind(db->dict, keyname);
auto itr = db->setexpire->end();
if (deMain != nullptr)
itr = db->setexpire->find((sds)dictGetKey(deMain));
@@ -514,7 +519,7 @@ void expireSlaveKeys(void) {
else
dictDelete(slaveKeysWithExpire,keyname);
/* Stop conditions: found 3 keys we cna't expire in a row or
/* Stop conditions: found 3 keys we can't expire in a row or
* time limit was reached. */
cycles++;
if (noexpire > 3) break;
@@ -566,7 +571,7 @@ size_t getSlaveKeyWithExpireCount(void) {
*
* Note: technically we should handle the case of a single DB being flushed
* but it is not worth it since anyway race conditions using the same set
* of key names in a wriatable replica and in its master will lead to
* of key names in a writable replica and in its master will lead to
* inconsistencies. This is just a best-effort thing we do. */
void flushSlaveKeysWithExpireList(void) {
if (slaveKeysWithExpire) {
@@ -575,12 +580,22 @@ void flushSlaveKeysWithExpireList(void) {
}
}
int checkAlreadyExpired(long long when) {
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
* should never be executed as a DEL when load the AOF or in the context
* of a slave instance.
*
* Instead we add the already expired key to the database with expire time
* (possibly in the past) and wait for an explicit DEL from the master. */
return (when <= mstime() && !g_pserver->loading && (!listLength(g_pserver->masters) || g_pserver->fActiveReplica));
}
/*-----------------------------------------------------------------------------
* Expires Commands
*----------------------------------------------------------------------------*/
/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT
* and PEXPIREAT. Because the commad second argument may be relative or absolute
* and PEXPIREAT. Because the command second argument may be relative or absolute
* the "basetime" argument is used to signal what the base time is (either 0
* for *AT variants of the command, or the current time for relative expires).
*
@@ -602,13 +617,7 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
return;
}
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
* should never be executed as a DEL when load the AOF or in the context
* of a replica instance.
*
* 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() && !g_pserver->loading && (!listLength(g_pserver->masters) || g_pserver->fActiveReplica)) {
if (checkAlreadyExpired(when)) {
robj *aux;
int deleted = g_pserver->lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
@@ -715,6 +724,7 @@ void persistCommand(client *c) {
if (lookupKeyWrite(c->db,c->argv[1])) {
if (c->argc == 2) {
if (removeExpire(c->db,c->argv[1])) {
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"persist",c->argv[1],c->db->id);
addReply(c,shared.cone);
g_pserver->dirty++;
@@ -723,6 +733,7 @@ void persistCommand(client *c) {
}
} else if (c->argc == 3) {
if (removeSubkeyExpire(c->db, c->argv[1], c->argv[2])) {
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"persist",c->argv[1],c->db->id);
addReply(c,shared.cone);
g_pserver->dirty++;
+23 -11
View File
@@ -338,7 +338,7 @@ extern "C" void fastlock_init(struct fastlock *lock, const char *name)
}
#ifndef ASM_SPINLOCK
extern "C" void fastlock_lock(struct fastlock *lock)
extern "C" void fastlock_lock(struct fastlock *lock, spin_worker worker)
{
int pidOwner;
__atomic_load(&lock->m_pidOwner, &pidOwner, __ATOMIC_ACQUIRE);
@@ -356,20 +356,32 @@ extern "C" void fastlock_lock(struct fastlock *lock)
__atomic_load(&g_fHighCpuPressure, &fHighPressure, __ATOMIC_RELAXED);
unsigned loopLimit = fHighPressure ? 0x10000 : 0x100000;
for (;;)
{
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if ((ticketT.u & 0xffff) == myticket)
break;
if (worker != nullptr) {
for (;;) {
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if ((ticketT.u & 0xffff) == myticket)
break;
if (!worker())
goto LNormalLoop;
}
} else {
LNormalLoop:
for (;;)
{
__atomic_load(&lock->m_ticket.u, &ticketT.u, __ATOMIC_ACQUIRE);
if ((ticketT.u & 0xffff) == myticket)
break;
#if defined(__i386__) || defined(__amd64__)
__asm__ __volatile__ ("pause");
__asm__ __volatile__ ("pause");
#elif defined(__aarch64__)
__asm__ __volatile__ ("yield");
__asm__ __volatile__ ("yield");
#endif
if ((++cloops % loopLimit) == 0)
{
fastlock_sleep(lock, tid, ticketT.u, myticket);
if ((++cloops % loopLimit) == 0)
{
fastlock_sleep(lock, tid, ticketT.u, myticket);
}
}
}
+11 -3
View File
@@ -6,10 +6,16 @@
extern "C" {
#endif
typedef int (*spin_worker)();
/* Begin C API */
struct fastlock;
void fastlock_init(struct fastlock *lock, const char *name);
void fastlock_lock(struct fastlock *lock);
#ifdef __cplusplus
void fastlock_lock(struct fastlock *lock, spin_worker worker = nullptr);
#else
void fastlock_lock(struct fastlock *lock, spin_worker worker);
#endif
int fastlock_trylock(struct fastlock *lock, int fWeak);
void fastlock_unlock(struct fastlock *lock);
void fastlock_free(struct fastlock *lock);
@@ -56,9 +62,9 @@ struct fastlock
fastlock_init(this, name);
}
inline void lock()
inline void lock(spin_worker worker = nullptr)
{
fastlock_lock(this);
fastlock_lock(this, worker);
}
inline bool try_lock(bool fWeak = false)
@@ -85,4 +91,6 @@ struct fastlock
#endif
};
#ifdef __cplusplus
static_assert(offsetof(struct fastlock, m_ticket) == 64, "ensure padding is correct");
#endif
+48 -4
View File
@@ -22,14 +22,22 @@ fastlock_lock:
# [rdi+64] ...
# uint16_t active
# uint16_t avail
#
# RSI points to a spin function to call, or NULL
# First get our TID and put it in ecx
push rdi # we need our struct pointer (also balance the stack for the call)
.cfi_adjust_cfa_offset 8
sub rsp, 24 # We only use 16 bytes, but we also need the stack aligned
.cfi_adjust_cfa_offset 24
mov [rsp], rdi # we need our struct pointer (also balance the stack for the call)
mov [rsp+8], rsi # backup the spin function
call gettid # get our thread ID (TLS is nasty in ASM so don't bother inlining)
mov esi, eax # back it up in esi
pop rdi # get our pointer back
.cfi_adjust_cfa_offset -8
mov rdi, [rsp] # Restore spin struct
mov r8, [rsp+8] # restore the function (in a different register)
add rsp, 24
.cfi_adjust_cfa_offset -24
cmp [rdi], esi # Is the TID we got back the owner of the lock?
je .LLocked # Don't spin in that case
@@ -47,6 +55,8 @@ fastlock_lock:
# ax now contains the ticket
# OK Start the wait loop
xor ecx, ecx
test r8, r8
jnz .LLoopFunction
.ALIGN 16
.LLoop:
mov edx, [rdi+64]
@@ -83,6 +93,40 @@ fastlock_lock:
mov [rdi], esi # lock->m_pidOwner = gettid()
inc dword ptr [rdi+4] # lock->m_depth++
ret
.LLoopFunction:
sub rsp, 40
.cfi_adjust_cfa_offset 40
xor ecx, ecx
mov [rsp], rcx
mov [rsp+8], r8
mov [rsp+16], rdi
mov [rsp+24], rsi
mov [rsp+32], eax
.LLoopFunctionCore:
mov edx, [rdi+64]
cmp dx, ax
je .LExitLoopFunction
mov r8, [rsp+8]
call r8
test eax, eax
jz .LExitLoopFunctionForNormal
mov eax, [rsp+32] # restore clobbered eax
mov rdi, [rsp+16]
jmp .LLoopFunctionCore
.LExitLoopFunction:
mov rsi, [rsp+24]
add rsp, 40
.cfi_adjust_cfa_offset -40
jmp .LLocked
.LExitLoopFunctionForNormal:
xor ecx, ecx
mov rdi, [rsp+16]
mov rsi, [rsp+24]
mov eax, [rsp+32]
add rsp, 40
.cfi_adjust_cfa_offset -40
jmp .LLoop
.cfi_endproc
.ALIGN 16
+4 -4
View File
@@ -143,8 +143,8 @@ double extractUnitOrReply(client *c, robj *unit) {
}
/* Input Argument Helper.
* Extract the dinstance from the specified two arguments starting at 'argv'
* that shouldbe in the form: <number> <unit> and return the dinstance in the
* Extract the distance from the specified two arguments starting at 'argv'
* that should be in the form: <number> <unit>, and return the distance in the
* specified unit on success. *conversions is populated with the coefficient
* to use in order to convert meters to the unit.
*
@@ -651,7 +651,7 @@ void georadiusGeneric(client *c, int flags) {
if (maxelelen < elelen) maxelelen = elelen;
znode = zslInsert(zs->zsl,score,gp->member);
serverAssert(dictAdd(zs->pdict,gp->member,&znode->score) == DICT_OK);
serverAssert(dictAdd(zs->dict,gp->member,&znode->score) == DICT_OK);
gp->member = NULL;
}
@@ -788,7 +788,7 @@ void geoposCommand(client *c) {
/* GEODIST key ele1 ele2 [unit]
*
* Return the distance, in meters by default, otherwise accordig to "unit",
* Return the distance, in meters by default, otherwise according to "unit",
* between points ele1 and ele2. If one or more elements are missing NULL
* is returned. */
void geodistCommand(client *c) {
+2 -2
View File
@@ -65,7 +65,7 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
}
step -= 2; /* Make sure range is included in most of the base cases. */
/* Wider range torwards the poles... Note: it is possible to do better
/* Wider range towards the poles... Note: it is possible to do better
* than this approximation by computing the distance between meridians
* at this latitude, but this does the trick for now. */
if (lat > 66 || lat < -66) {
@@ -81,7 +81,7 @@ uint8_t geohashEstimateStepsByRadius(double range_meters, double lat) {
/* Return the bounding box of the search area centered at latitude,longitude
* having a radius of radius_meter. bounds[0] - bounds[2] is the minimum
* and maxium longitude, while bounds[1] - bounds[3] is the minimum and
* and maximum longitude, while bounds[1] - bounds[3] is the minimum and
* maximum latitude.
*
* This function does not behave correctly with very large radius values, for
+24 -9
View File
@@ -1,4 +1,4 @@
/* Automatically generated by generate-command-help.rb, do not edit. */
/* Automatically generated by ./utils/generate-command-help.rb, do not edit. */
#ifndef __REDIS_HELP_H
#define __REDIS_HELP_H
@@ -44,6 +44,16 @@ struct commandHelp {
"Generate a pseudorandom secure password to use for ACL users",
9,
"6.0.0" },
{ "ACL GETUSER",
"username",
"Get the rules for a specific ACL user",
9,
"6.0.0" },
{ "ACL HELP",
"-",
"Show helpful text about the different subcommands",
9,
"6.0.0" },
{ "ACL LIST",
"-",
"List the current ACL rules in ACL config file format",
@@ -65,7 +75,7 @@ struct commandHelp {
9,
"6.0.0" },
{ "ACL SETUSER",
"rule [rule ...]",
"username [rule [rule ...]]",
"Modify or create the rules for a specific ACL user",
9,
"6.0.0" },
@@ -85,7 +95,7 @@ struct commandHelp {
1,
"2.0.0" },
{ "AUTH",
"password",
"[username] password",
"Authenticate to the server",
8,
"1.0.0" },
@@ -165,7 +175,7 @@ struct commandHelp {
8,
"5.0.0" },
{ "CLIENT KILL",
"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no]",
"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [USER username] [ADDR ip:port] [SKIPME yes/no]",
"Kill the connection of a client",
8,
"2.4.0" },
@@ -183,14 +193,14 @@ struct commandHelp {
"ON|OFF|SKIP",
"Instruct the server whether to reply to commands",
8,
"3.2" },
"3.2.0" },
{ "CLIENT SETNAME",
"connection-name",
"Set the current connection name",
8,
"2.6.9" },
{ "CLIENT TRACKING",
"ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
"ON|OFF [REDIRECT client-id] [PREFIX prefix [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
"Enable or disable server assisted client side caching support",
8,
"6.0.0" },
@@ -626,7 +636,7 @@ struct commandHelp {
9,
"2.8.13" },
{ "LATENCY RESET",
"[event]",
"[event [event ...]]",
"Reset latency data for one or more events.",
9,
"2.8.13" },
@@ -655,6 +665,11 @@ struct commandHelp {
"Remove and get the first element in a list",
2,
"1.0.0" },
{ "LPOS",
"key element [RANK rank] [COUNT num-matches] [MAXLEN len]",
"Return the index of matching elements on a list",
2,
"6.0.6" },
{ "LPUSH",
"key element [element ...]",
"Prepend one or multiple elements to a list",
@@ -721,7 +736,7 @@ struct commandHelp {
1,
"1.0.0" },
{ "MIGRATE",
"host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [KEYS key]",
"host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [AUTH2 username password] [KEYS key]",
"Atomically transfer a key from a Redis instance to another one.",
0,
"2.6.0" },
@@ -966,7 +981,7 @@ struct commandHelp {
8,
"1.0.0" },
{ "SET",
"key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]",
"key value [EX seconds|PX milliseconds|KEEPTTL] [NX|XX]",
"Set the string value of a key",
1,
"1.0.0" },
+8 -8
View File
@@ -36,9 +36,9 @@
/* The Redis HyperLogLog implementation is based on the following ideas:
*
* * The use of a 64 bit hash function as proposed in [1], in order to don't
* limited to cardinalities up to 10^9, at the cost of just 1 additional
* bit per register.
* * The use of a 64 bit hash function as proposed in [1], in order to estimate
* cardinalities larger than 10^9, at the cost of just 1 additional bit per
* register.
* * The use of 16384 6-bit registers for a great level of accuracy, using
* a total of 12k per key.
* * The use of the Redis string data type. No new type is introduced.
@@ -281,7 +281,7 @@ static const char *invalid_hll_err = "-INVALIDOBJ Corrupted HLL object detected\
* So we right shift of 0 bits (no shift in practice) and
* left shift the next byte of 8 bits, even if we don't use it,
* but this has the effect of clearing the bits so the result
* will not be affacted after the OR.
* will not be affected after the OR.
*
* -------------------------------------------------------------------------
*
@@ -299,7 +299,7 @@ static const char *invalid_hll_err = "-INVALIDOBJ Corrupted HLL object detected\
* |11000000| <- Our byte at b0
* +--------+
*
* To create a AND-mask to clear the bits about this position, we just
* To create an AND-mask to clear the bits about this position, we just
* initialize the mask with the value 63, left shift it of "fs" bits,
* and finally invert the result.
*
@@ -775,7 +775,7 @@ int hllSparseSet(robj *o, long index, uint8_t count) {
* by a ZERO opcode with len > 1, or by an XZERO opcode.
*
* In those cases the original opcode must be split into multiple
* opcodes. The worst case is an XZERO split in the middle resuling into
* opcodes. The worst case is an XZERO split in the middle resulting into
* XZERO - VAL - XZERO, so the resulting sequence max length is
* 5 bytes.
*
@@ -907,7 +907,7 @@ promote: /* Promote to dense representation. */
* the element belongs to is incremented if needed.
*
* This function is actually a wrapper for hllSparseSet(), it only performs
* the hashshing of the elmenet to obtain the index and zeros run length. */
* the hashshing of the element to obtain the index and zeros run length. */
int hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {
long index;
uint8_t count = hllPatLen(ele,elesize,&index);
@@ -1022,7 +1022,7 @@ uint64_t hllCount(struct hllhdr *hdr, int *invalid) {
double m = HLL_REGISTERS;
double E;
int j;
/* Note that reghisto size could be just HLL_Q+2, becuase HLL_Q+1 is
/* Note that reghisto size could be just HLL_Q+2, because HLL_Q+1 is
* the maximum frequency of the "000...1" sequence the hash function is
* able to return. However it is slow to check for sanity of the
* input: instead we history array at a safe size: overflows will
+8 -8
View File
@@ -71,7 +71,7 @@ int THPIsEnabled(void) {
return 0;
}
fclose(fp);
return (strstr(buf,"[never]") == NULL) ? 1 : 0;
return (strstr(buf,"[always]") != NULL) ? 1 : 0;
}
#endif
@@ -85,7 +85,7 @@ int THPGetAnonHugePagesSize(void) {
/* ---------------------------- Latency API --------------------------------- */
/* Latency monitor initialization. We just need to create the dictionary
* of time series, each time serie is created on demand in order to avoid
* of time series, each time series is created on demand in order to avoid
* having a fixed list to maintain. */
void latencyMonitorInit(void) {
g_pserver->latency_events = dictCreate(&latencyTimeSeriesDictType,NULL);
@@ -154,7 +154,7 @@ int latencyResetEvent(char *event_to_reset) {
/* Analyze the samples available for a given event and return a structure
* populate with different metrics, average, MAD, min, max, and so forth.
* Check latency.h definition of struct latenctStat for more info.
* Check latency.h definition of struct latencyStats for more info.
* If the specified event has no elements the structure is populate with
* zero values. */
void analyzeLatencyForEvent(char *event, struct latencyStats *ls) {
@@ -343,7 +343,7 @@ sds createLatencyReport(void) {
}
if (!strcasecmp(event,"aof-fstat") ||
!strcasecmp(event,"rdb-unlik-temp-file")) {
!strcasecmp(event,"rdb-unlink-temp-file")) {
advise_disk_contention = 1;
advise_local_disk = 1;
advices += 2;
@@ -396,7 +396,7 @@ sds createLatencyReport(void) {
/* Better VM. */
report = sdscat(report,"\nI have a few advices for you:\n\n");
if (advise_better_vm) {
report = sdscat(report,"- If you are using a virtual machine, consider upgrading it with a faster one using an hypervisior that provides less latency during fork() calls. Xen is known to have poor fork() performance. Even in the context of the same VM provider, certain kinds of instances can execute fork faster than others.\n");
report = sdscat(report,"- If you are using a virtual machine, consider upgrading it with a faster one using a hypervisior that provides less latency during fork() calls. Xen is known to have poor fork() performance. Even in the context of the same VM provider, certain kinds of instances can execute fork faster than others.\n");
}
/* Slow log. */
@@ -416,7 +416,7 @@ sds createLatencyReport(void) {
if (advise_scheduler) {
report = sdscat(report,"- The system is slow to execute Redis code paths not containing system calls. This usually means the system does not provide Redis CPU time to run for long periods. You should try to:\n"
" 1) Lower the system load.\n"
" 2) Use a computer / VM just for Redis if you are running other softawre in the same system.\n"
" 2) Use a computer / VM just for Redis if you are running other software in the same system.\n"
" 3) Check if you have a \"noisy neighbour\" problem.\n"
" 4) Check with 'keydb-cli --intrinsic-latency 100' what is the intrinsic latency in your system.\n"
" 5) Check if the problem is allocator-related by recompiling Redis with MALLOC=libc, if you are using Jemalloc. However this may create fragmentation problems.\n");
@@ -432,7 +432,7 @@ sds createLatencyReport(void) {
}
if (advise_data_writeback) {
report = sdscat(report,"- Mounting ext3/4 filesystems with data=writeback can provide a performance boost compared to data=ordered, however this mode of operation provides less guarantees, and sometimes it can happen that after a hard crash the AOF file will have an half-written command at the end and will require to be repaired before Redis restarts.\n");
report = sdscat(report,"- Mounting ext3/4 filesystems with data=writeback can provide a performance boost compared to data=ordered, however this mode of operation provides less guarantees, and sometimes it can happen that after a hard crash the AOF file will have a half-written command at the end and will require to be repaired before Redis restarts.\n");
}
if (advise_disk_contention) {
@@ -621,7 +621,7 @@ NULL
resets += latencyResetEvent(szFromObj(c->argv[j]));
addReplyLongLong(c,resets);
}
} else if (!strcasecmp(szFromObj(c->argv[1]),"help") && c->argc >= 2) {
} else if (!strcasecmp(szFromObj(c->argv[1]),"help") && c->argc == 2) {
addReplyHelp(c, help);
} else {
addReplySubcommandSyntaxError(c);
+37 -21
View File
@@ -15,7 +15,7 @@ size_t lazyfreeGetPendingObjectsCount(void) {
/* Return the amount of work needed in order to free an object.
* The return value is not always the actual number of allocations the
* object is compoesd of, but a number proportional to it.
* object is composed of, but a number proportional to it.
*
* For strings the function always returns 1.
*
@@ -41,6 +41,30 @@ size_t lazyfreeGetFreeEffort(robj *obj) {
} else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {
dict *ht = (dict*)ptrFromObj(obj);
return dictSize(ht);
} else if (obj->type == OBJ_STREAM) {
size_t effort = 0;
stream *s = (stream*)ptrFromObj(obj);
/* Make a best effort estimate to maintain constant runtime. Every macro
* node in the Stream is one allocation. */
effort += s->prax->numnodes;
/* Every consumer group is an allocation and so are the entries in its
* PEL. We use size of the first group's PEL as an estimate for all
* others. */
if (s->cgroups) {
raxIterator ri;
streamCG *cg;
raxStart(&ri,s->cgroups);
raxSeek(&ri,"^",NULL,0);
/* There must be at least one group so the following should always
* work. */
serverAssert(raxNext(&ri));
cg = (streamCG*)ri.data;
effort += raxSize(s->cgroups)*(1+raxSize(cg->pel));
raxStop(&ri);
}
return effort;
} else {
return 1; /* Everything else is a single allocation. */
}
@@ -55,7 +79,7 @@ int dbAsyncDelete(redisDb *db, robj *key) {
/* If the value is composed of a few allocations, to free in a lazy way
* is actually just slower... So under a certain limit we just free
* the object synchronously. */
dictEntry *de = dictUnlink(db->pdict,ptrFromObj(key));
dictEntry *de = dictUnlink(db->dict,ptrFromObj(key));
if (de) {
robj *val = (robj*)dictGetVal(de);
if (val->FExpires())
@@ -78,14 +102,14 @@ int dbAsyncDelete(redisDb *db, robj *key) {
if (free_effort > LAZYFREE_THRESHOLD && val->getrefcount(std::memory_order_relaxed) == 1) {
atomicIncr(lazyfree_objects,1);
bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);
dictSetVal(db->pdict,de,NULL);
dictSetVal(db->dict,de,NULL);
}
}
/* Release the key-val pair, or just the key if we set the val
* field to NULL in order to lazy free it later. */
if (de) {
dictFreeUnlinkedEntry(db->pdict,de);
dictFreeUnlinkedEntry(db->dict,de);
if (g_pserver->cluster_enabled) slotToKeyDel(szFromObj(key));
return 1;
} else {
@@ -108,25 +132,19 @@ void freeObjAsync(robj *o) {
* create a new empty set of hash tables and scheduling the old ones for
* lazy freeing. */
void emptyDbAsync(redisDb *db) {
dict *oldht1 = db->pdict;
dict *oldht1 = db->dict;
auto *set = db->setexpire;
db->setexpire = new (MALLOC_LOCAL) expireset();
db->expireitr = db->setexpire->end();
db->pdict = dictCreate(&dbDictType,NULL);
db->dict = dictCreate(&dbDictType,NULL);
atomicIncr(lazyfree_objects,dictSize(oldht1));
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,set);
}
/* 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 = 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));
atomicIncr(lazyfree_objects,old->numele);
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,old);
/* Release the radix tree mapping Redis Cluster keys to slots asynchronously. */
void freeSlotsToKeysMapAsync(rax *rt) {
atomicIncr(lazyfree_objects,rt->numele);
bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,rt);
}
/* Release objects from the lazyfree thread. It's just decrRefCount()
@@ -137,10 +155,8 @@ void lazyfreeFreeObjectFromBioThread(robj *o) {
}
/* Release a database from the lazyfree thread. The 'db' pointer is the
* database which was substitutied with a fresh one in the main thread
* when the database was logically deleted. 'sl' is a skiplist used by
* Redis Cluster in order to take the hash slots -> keys mapping. This
* may be NULL if Redis Cluster is disabled. */
* database which was substituted with a fresh one in the main thread
* when the database was logically deleted. */
void lazyfreeFreeDatabaseFromBioThread(dict *ht1, expireset *set) {
size_t numkeys = dictSize(ht1);
dictRelease(ht1);
@@ -148,7 +164,7 @@ void lazyfreeFreeDatabaseFromBioThread(dict *ht1, expireset *set) {
atomicDecr(lazyfree_objects,numkeys);
}
/* Release the skiplist mapping Redis Cluster keys to slots in the
/* Release the radix tree mapping Redis Cluster keys to slots in the
* lazyfree thread. */
void lazyfreeFreeSlotsMapFromBioThread(rax *rt) {
size_t len = rt->numele;
+5 -5
View File
@@ -405,7 +405,7 @@ unsigned char *lpNext(unsigned char *lp, unsigned char *p) {
}
/* If 'p' points to an element of the listpack, calling lpPrev() will return
* the pointer to the preivous element (the one on the left), or NULL if 'p'
* the pointer to the previous element (the one on the left), or NULL if 'p'
* already pointed to the first element of the listpack. */
unsigned char *lpPrev(unsigned char *lp, unsigned char *p) {
if (p-lp == LP_HDR_SIZE) return NULL;
@@ -768,18 +768,18 @@ unsigned char *lpSeek(unsigned char *lp, long index) {
if (numele != LP_HDR_NUMELE_UNKNOWN) {
if (index < 0) index = (long)numele+index;
if (index < 0) return NULL; /* Index still < 0 means out of range. */
if (index >= numele) return NULL; /* Out of range the other side. */
if (index >= (long)numele) return NULL; /* Out of range the other side. */
/* We want to scan right-to-left if the element we are looking for
* is past the half of the listpack. */
if (index > numele/2) {
if (index > (long)numele/2) {
forward = 0;
/* Left to right scanning always expects a negative index. Convert
/* Right to left scanning always expects a negative index. Convert
* our index to negative form. */
index -= numele;
}
} else {
/* If the listpack length is unspecified, for negative indexes we
* want to always scan left-to-right. */
* want to always scan right-to-left. */
if (index < 0) forward = 0;
}
+2 -2
View File
@@ -85,7 +85,7 @@ void lolwutCommand(client *c) {
}
/* ========================== LOLWUT Canvase ===============================
* Many LOWUT versions will likely print some computer art to the screen.
* Many LOLWUT versions will likely print some computer art to the screen.
* This is the case with LOLWUT 5 and LOLWUT 6, so here there is a generic
* canvas implementation that can be reused. */
@@ -106,7 +106,7 @@ void lwFreeCanvas(lwCanvas *canvas) {
}
/* Set a pixel to the specified color. Color is 0 or 1, where zero means no
* dot will be displyed, and 1 means dot will be displayed.
* dot will be displayed, and 1 means dot will be displayed.
* Coordinates are arranged so that left-top corner is 0,0. You can write
* out of the size of the canvas without issues. */
void lwDrawPixel(lwCanvas *canvas, int x, int y, int color) {
+1 -1
View File
@@ -156,7 +156,7 @@ void lolwut5Command(client *c) {
return;
/* Limits. We want LOLWUT to be always reasonably fast and cheap to execute
* so we have maximum number of columns, rows, and output resulution. */
* so we have maximum number of columns, rows, and output resolution. */
if (cols < 1) cols = 1;
if (cols > 1000) cols = 1000;
if (squares_per_row < 1) squares_per_row = 1;
+1 -1
View File
@@ -127,7 +127,7 @@
/*
* Whether to store pointers or offsets inside the hash table. On
* 64 bit architetcures, pointers take up twice as much space,
* 64 bit architectures, pointers take up twice as much space,
* and might also be slower. Default is to autodetect.
*/
/*#define LZF_USER_OFFSETS autodetect */
+5
View File
@@ -347,10 +347,15 @@ void memtest_alloc_and_test(size_t megabytes, int passes) {
}
void memtest(size_t megabytes, int passes) {
#if !defined(__HAIKU__)
if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
ws.ws_col = 80;
ws.ws_row = 20;
}
#else
ws.ws_col = 80;
ws.ws_row = 20;
#endif
memtest_alloc_and_test(megabytes,passes);
printf("\nYour memory passed this test.\n");
printf("Please if you are still in doubt use the following two tools:\n");
+574 -128
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -125,7 +125,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
cmd_KEYRANGE,"readonly",1,1,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
/* Create our global dictionray. Here we'll set our keys and values. */
/* Create our global dictionary. Here we'll set our keys and values. */
Keyspace = RedisModule_CreateDict(NULL);
return REDISMODULE_OK;
+1 -1
View File
@@ -40,7 +40,7 @@
/* Timer callback. */
void timerHandler(RedisModuleCtx *ctx, void *data) {
REDISMODULE_NOT_USED(ctx);
printf("Fired %s!\n", (const char *) data);
printf("Fired %s!\n", (const char *)data);
RedisModule_Free(data);
}
+2 -2
View File
@@ -91,7 +91,7 @@ int HelloPushCall_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, in
}
/* HELLO.PUSH.CALL2
* This is exaxctly as HELLO.PUSH.CALL, but shows how we can reply to the
* This is exactly as HELLO.PUSH.CALL, but shows how we can reply to the
* client using directly a reply object that Call() returned. */
int HelloPushCall2_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
@@ -345,7 +345,7 @@ int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
/* HELLO.MORE.EXPIRE key milliseconds.
*
* If they key has already an associated TTL, extends it by "milliseconds"
* If the key has already an associated TTL, extends it by "milliseconds"
* milliseconds. Otherwise no operation is performed. */
int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
+7 -3
View File
@@ -71,12 +71,16 @@ static void setMOTDCache(const char *sz)
fclose(pf);
}
extern "C" char *fetchMOTD(int cache)
extern "C" char *fetchMOTD(int cache, int enable_motd)
{
sds str;
CURL *curl;
CURLcode res;
/* Do not try the CURL if the motd is disabled*/
if (!enable_motd) {
return NULL;
}
/* First try and get the string from the cache */
if (cache) {
str = fetchMOTDFromCache();
@@ -124,9 +128,9 @@ extern "C" char *fetchMOTD(int cache)
#else
extern "C" char *fetchMOTD(int /* cache */)
extern "C" char *fetchMOTD(int /* cache */, int /* enable_motd */)
{
return NULL;
}
#endif
#endif
+2 -3
View File
@@ -6,9 +6,8 @@ extern const char *motd_cache_file;
#ifdef __cplusplus
extern "C" {
#endif
char *fetchMOTD(int fCache);
char *fetchMOTD(int fCache, int enable_motd);
#ifdef __cplusplus
}
#endif
#endif
+46 -44
View File
@@ -37,6 +37,7 @@ void initClientMultiState(client *c) {
c->mstate.commands = NULL;
c->mstate.count = 0;
c->mstate.cmd_flags = 0;
c->mstate.cmd_inv_flags = 0;
}
/* Release all the resources associated with MULTI/EXEC state */
@@ -77,6 +78,7 @@ void queueMultiCommand(client *c) {
incrRefCount(mc->argv[j]);
c->mstate.count++;
c->mstate.cmd_flags |= c->cmd->flags;
c->mstate.cmd_inv_flags |= ~c->cmd->flags;
}
void discardTransaction(client *c) {
@@ -87,7 +89,7 @@ void discardTransaction(client *c) {
unwatchAllKeys(c);
}
/* Flag the transacation as DIRTY_EXEC so that EXEC will fail.
/* Flag the transaction 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) {
if (c->flags & CLIENT_MULTI)
@@ -125,6 +127,24 @@ void execCommandPropagateExec(client *c) {
PROPAGATE_AOF|PROPAGATE_REPL);
}
/* Aborts a transaction, with a specific error message.
* The transaction is always aboarted with -EXECABORT so that the client knows
* the server exited the multi state, but the actual reason for the abort is
* included too.
* Note: 'error' may or may not end with \r\n. see addReplyErrorFormat. */
void execCommandAbort(client *c, sds error) {
discardTransaction(c);
if (error[0] == '-') error++;
addReplyErrorFormat(c, "-EXECABORT Transaction discarded because of: %s", error);
/* Send EXEC to clients waiting data from MONITOR. We did send a MULTI
* already, and didn't send any of the queued commands, now we'll just send
* EXEC so it is clear that the transaction is over. */
if (listLength(g_pserver->monitors) && !g_pserver->loading)
replicationFeedMonitors(c,g_pserver->monitors,c->db->id,c->argv,c->argc);
}
void execCommand(client *c) {
int j;
robj **orig_argv;
@@ -138,15 +158,6 @@ void execCommand(client *c) {
return;
}
/* If we are in -BUSY state, flag the transaction and return the
* -BUSY error, like Redis <= 5. This is a temporary fix, may be changed
* ASAP, see issue #7353 on Github. */
if (g_pserver->lua_timedout) {
flagTransaction(c);
addReply(c, shared.slowscripterr);
return;
}
/* Check if we need to abort the EXEC because:
* 1) Some WATCHed key was touched.
* 2) There was a previous error while queueing commands.
@@ -160,21 +171,6 @@ void execCommand(client *c) {
goto handle_monitor;
}
/* If there are write commands inside the transaction, and this is a read
* only replica, we want to send an error. This happens when the transaction
* 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 (!g_pserver->loading && listLength(g_pserver->masters) && g_pserver->repl_slave_ro &&
!(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)
{
addReplyError(c,
"Transaction contains write commands but instance "
"is now a read-only replica. EXEC aborted.");
discardTransaction(c);
goto handle_monitor;
}
/* Exec all the queued commands */
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
orig_argv = c->argv;
@@ -352,32 +348,38 @@ void touchWatchedKey(redisDb *db, robj *key) {
}
}
/* On FLUSHDB or FLUSHALL all the watched keys that are present before the
* flush but will be deleted as effect of the flushing operation should
* be touched. "dbid" is the DB that's getting the flush. -1 if it is
* a FLUSHALL operation (all the DBs flushed). */
void touchWatchedKeysOnFlush(int dbid) {
listIter li1, li2;
/* Set CLIENT_DIRTY_CAS to all clients of DB when DB is dirty.
* It may happen in the following situations:
* FLUSHDB, FLUSHALL, SWAPDB
*
* replaced_with: for SWAPDB, the WATCH should be invalidated if
* the key exists in either of them, and skipped only if it
* doesn't exist in both. */
void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with) {
listIter li;
listNode *ln;
dictEntry *de;
serverAssert(GlobalLocksAcquired());
/* For every client, check all the waited keys */
listRewind(g_pserver->clients,&li1);
while((ln = listNext(&li1))) {
client *c = (client*)listNodeValue(ln);
listRewind(c->watched_keys,&li2);
while((ln = listNext(&li2))) {
watchedKey *wk = (watchedKey*)listNodeValue(ln);
if (dictSize(emptied->watched_keys) == 0) return;
/* For every watched key matching the specified DB, if the
* key exists, mark the client as dirty, as the key will be
* removed. */
if (dbid == -1 || wk->db->id == dbid) {
if (dictFind(wk->db->pdict, ptrFromObj(wk->key)) != NULL)
c->flags |= CLIENT_DIRTY_CAS;
dictIterator *di = dictGetSafeIterator(emptied->watched_keys);
while((de = dictNext(di)) != NULL) {
robj *key = (robj*)dictGetKey(de);
list *clients = (list*)dictGetVal(de);
if (!clients) continue;
listRewind(clients,&li);
while((ln = listNext(&li))) {
client *c = (client*)listNodeValue(ln);
if (dictFind(emptied->dict, ptrFromObj(key))) {
c->flags |= CLIENT_DIRTY_CAS;
} else if (replaced_with && dictFind(replaced_with->dict, ptrFromObj(key))) {
c->flags |= CLIENT_DIRTY_CAS;
}
}
}
dictReleaseIterator(di);
}
void watchCommand(client *c) {
+384 -351
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -62,7 +62,7 @@ int keyspaceEventsStringToFlags(char *classes) {
return flags;
}
/* This function does exactly the revese of the function above: it gets
/* This function does exactly the reverse of the function above: it gets
* as input an integer with the xored flags and returns a string representing
* the selected classes. The string returned is an sds string that needs to
* be released with sdsfree(). */
+35 -33
View File
@@ -30,6 +30,7 @@
#include "server.h"
#include "cron.h"
#include "t_nhash.h"
#include <math.h>
#include <ctype.h>
#include <mutex>
@@ -148,7 +149,7 @@ robj *createStringObject(const char *ptr, size_t len) {
/* Create a string object from a long long value. When possible returns a
* shared integer object, or at least an integer encoded one.
*
* If valueobj is non zero, the function avoids returning a a shared
* If valueobj is non zero, the function avoids returning a shared
* integer, because the object is going to be used as value in the Redis key
* space (for instance when the INCR command is used), so we want LFU/LRU
* values specific for each key. */
@@ -272,7 +273,7 @@ robj *createZsetObject(void) {
zset *zs = (zset*)zmalloc(sizeof(*zs), MALLOC_SHARED);
robj *o;
zs->pdict = dictCreate(&zsetDictType,NULL);
zs->dict = dictCreate(&zsetDictType,NULL);
zs->zsl = zslCreate();
o = createObject(OBJ_ZSET,zs);
o->encoding = OBJ_ENCODING_SKIPLIST;
@@ -309,8 +310,10 @@ void freeStringObject(robj_roptr o) {
void freeListObject(robj_roptr o) {
if (o->encoding == OBJ_ENCODING_QUICKLIST) {
quicklistRelease((quicklist*)ptrFromObj(o));
} else if (o->encoding == OBJ_ENCODING_ZIPLIST) {
zfree(ptrFromObj(o));
} else {
serverPanic("Unknown list encoding type");
serverPanic("Unknown list encoding type: %d", o->encoding);
}
}
@@ -332,7 +335,7 @@ void freeZsetObject(robj_roptr o) {
switch (o->encoding) {
case OBJ_ENCODING_SKIPLIST:
zs = (zset*)ptrFromObj(o);
dictRelease(zs->pdict);
dictRelease(zs->dict);
zslFree(zs->zsl);
zfree(zs);
break;
@@ -395,6 +398,7 @@ void decrRefCount(robj_roptr o) {
case OBJ_MODULE: freeModuleObject(o); break;
case OBJ_STREAM: freeStreamObject(o); break;
case OBJ_CRON: freeCronObject(o); break;
case OBJ_NESTEDHASH: freeNestedHashObject(o); break;
default: serverPanic("Unknown object type"); break;
}
if (g_pserver->fActiveReplica) {
@@ -433,7 +437,7 @@ robj *resetRefCount(robj *obj) {
int checkType(client *c, robj_roptr o, int type) {
if (o->type != type) {
addReplyAsync(c,shared.wrongtypeerr);
addReply(c,shared.wrongtypeerr);
return 1;
}
return 0;
@@ -801,6 +805,7 @@ const char *strEncoding(int encoding) {
case OBJ_ENCODING_INTSET: return "intset";
case OBJ_ENCODING_SKIPLIST: return "skiplist";
case OBJ_ENCODING_EMBSTR: return "embstr";
case OBJ_ENCODING_STREAM: return "stream";
default: return "unknown";
}
}
@@ -847,7 +852,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
if(o->encoding == OBJ_ENCODING_INT) {
asize = sizeof(*o);
} else if(o->encoding == OBJ_ENCODING_RAW) {
asize = sdsAllocSize(szFromObj(o))+sizeof(*o);
asize = sdsZmallocSize(szFromObj(o))+sizeof(*o);
} else if(o->encoding == OBJ_ENCODING_EMBSTR) {
asize = sdslen(szFromObj(o))+2+sizeof(*o);
} else {
@@ -875,7 +880,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));
while((de = dictNext(di)) != NULL && samples < sample_size) {
ele = (sds)dictGetKey(de);
elesize += sizeof(struct dictEntry) + sdsAllocSize(ele);
elesize += sizeof(struct dictEntry) + sdsZmallocSize(ele);
samples++;
}
dictReleaseIterator(di);
@@ -890,14 +895,14 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
asize = sizeof(*o)+(ziplistBlobLen((unsigned char*)ptrFromObj(o)));
} else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
d = ((zset*)ptrFromObj(o))->pdict;
d = ((zset*)ptrFromObj(o))->dict;
zskiplist *zsl = ((zset*)ptrFromObj(o))->zsl;
zskiplistNode *znode = zsl->header->level(0)->forward;
asize = sizeof(*o)+sizeof(zset)+sizeof(zskiplist)+sizeof(dict)+
(sizeof(struct dictEntry*)*dictSlots(d))+
zmalloc_size(zsl->header);
while(znode != NULL && samples < sample_size) {
elesize += sdsAllocSize(znode->ele);
elesize += sdsZmallocSize(znode->ele);
elesize += sizeof(struct dictEntry) + zmalloc_size(znode);
samples++;
znode = znode->level(0)->forward;
@@ -916,7 +921,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
while((de = dictNext(di)) != NULL && samples < sample_size) {
ele = (sds)dictGetKey(de);
ele2 = (sds)dictGetVal(de);
elesize += sdsAllocSize(ele) + sdsAllocSize(ele2);
elesize += sdsZmallocSize(ele) + sdsZmallocSize(ele2);
elesize += sizeof(struct dictEntry);
samples++;
}
@@ -1057,7 +1062,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mem = 0;
if (g_pserver->aof_state != AOF_OFF) {
mem += sdsalloc(g_pserver->aof_buf);
mem += sdsZmallocSize(g_pserver->aof_buf);
mem += aofRewriteBufferSize();
}
mh->aof_buffer = mem;
@@ -1077,16 +1082,16 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
long long keyscount = dictSize(db->pdict);
long long keyscount = dictSize(db->dict);
if (keyscount==0) continue;
mh->total_keys += keyscount;
mh->db = (decltype(mh->db))zrealloc(mh->db,sizeof(mh->db[0])*(mh->num_dbs+1), MALLOC_LOCAL);
mh->db[mh->num_dbs].dbid = j;
mem = dictSize(db->pdict) * sizeof(dictEntry) +
dictSlots(db->pdict) * sizeof(dictEntry*) +
dictSize(db->pdict) * sizeof(robj);
mem = dictSize(db->dict) * sizeof(dictEntry) +
dictSlots(db->dict) * sizeof(dictEntry*) +
dictSize(db->dict) * sizeof(robj);
mh->db[mh->num_dbs].overhead_ht_main = mem;
mem_total+=mem;
@@ -1272,24 +1277,21 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
/* This is a helper function for the OBJECT command. We need to lookup keys
* without any modification of LRU or other parameters. */
robj *objectCommandLookup(client *c, robj *key) {
dictEntry *de;
if ((de = dictFind(c->db->pdict,ptrFromObj(key))) == NULL) return NULL;
return (robj*) dictGetVal(de);
robj_roptr objectCommandLookup(client *c, robj *key) {
return lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH|LOOKUP_NONOTIFY);
}
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
robj *o = objectCommandLookup(c,key);
robj_roptr objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
robj_roptr o = objectCommandLookup(c,key);
if (!o) addReply(c, reply);
return o;
}
/* Object command allows to inspect the internals of an Redis Object.
/* Object command allows to inspect the internals of a Redis Object.
* Usage: OBJECT <refcount|encoding|idletime|freq> <key> */
void objectCommand(client *c) {
robj *o;
robj_roptr o;
if (c->argc == 2 && !strcasecmp(szFromObj(c->argv[1]),"help")) {
const char *help[] = {
@@ -1302,15 +1304,15 @@ NULL
addReplyHelp(c, help);
} else if (!strcasecmp(szFromObj(c->argv[1]),"refcount") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
== nullptr) return;
addReplyLongLong(c,o->getrefcount(std::memory_order_relaxed));
} else if (!strcasecmp(szFromObj(c->argv[1]),"encoding") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
== nullptr) return;
addReplyBulkCString(c,strEncoding(o->encoding));
} else if (!strcasecmp(szFromObj(c->argv[1]),"idletime") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
== nullptr) return;
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;
@@ -1318,7 +1320,7 @@ NULL
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
} else if (!strcasecmp(szFromObj(c->argv[1]),"freq") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
== nullptr) return;
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;
@@ -1327,10 +1329,10 @@ NULL
* in case of the key has not been accessed for a long time,
* because we update the access time only
* when the key is read or overwritten. */
addReplyLongLong(c,LFUDecrAndReturn(o));
addReplyLongLong(c,LFUDecrAndReturn(o.unsafe_robjcast()));
} else if (!strcasecmp(szFromObj(c->argv[1]), "lastmodified") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
== NULL) return;
== nullptr) return;
uint64_t mvcc = mvccFromObj(o);
addReplyLongLong(c, (g_pserver->mstime - (mvcc >> MVCC_MS_SHIFT)) / 1000);
} else {
@@ -1373,12 +1375,12 @@ NULL
return;
}
}
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
if ((de = dictFind(c->db->dict,ptrFromObj(c->argv[2]))) == NULL) {
addReplyNull(c);
return;
}
size_t usage = objectComputeSize((robj*)dictGetVal(de),samples);
usage += sdsAllocSize((sds)dictGetKey(de));
usage += sdsZmallocSize((sds)dictGetKey(de));
usage += sizeof(dictEntry);
addReplyLongLong(c,usage);
} else if (!strcasecmp(szFromObj(c->argv[1]),"stats") && c->argc == 2) {
@@ -1526,7 +1528,7 @@ void *allocPtrFromObj(robj_roptr o) {
}
robj *objFromAllocPtr(void *pv) {
if (g_pserver->fActiveReplica) {
if (pv != nullptr && g_pserver->fActiveReplica) {
return reinterpret_cast<robj*>(reinterpret_cast<redisObjectExtended*>(pv)+1);
}
return reinterpret_cast<robj*>(pv);
+11 -11
View File
@@ -43,12 +43,12 @@ int clientSubscriptionsCount(client *c);
* addReply*() API family. */
void addReplyPubsubMessage(client *c, robj *channel, robj *msg) {
if (c->resp == 2)
addReplyAsync(c,shared.mbulkhdr[3]);
addReply(c,shared.mbulkhdr[3]);
else
addReplyPushLenAsync(c,3);
addReplyAsync(c,shared.messagebulk);
addReplyBulkAsync(c,channel);
if (msg) addReplyBulkAsync(c,msg);
addReplyPushLen(c,3);
addReply(c,shared.messagebulk);
addReplyBulk(c,channel);
if (msg) addReplyBulk(c,msg);
}
/* Send a pubsub message of type "pmessage" to the client. The difference
@@ -56,13 +56,13 @@ void addReplyPubsubMessage(client *c, robj *channel, robj *msg) {
* this message format also includes the pattern that matched the message. */
void addReplyPubsubPatMessage(client *c, robj *pat, robj *channel, robj *msg) {
if (c->resp == 2)
addReplyAsync(c,shared.mbulkhdr[4]);
addReply(c,shared.mbulkhdr[4]);
else
addReplyPushLenAsync(c,4);
addReplyAsync(c,shared.pmessagebulk);
addReplyBulkAsync(c,pat);
addReplyBulkAsync(c,channel);
addReplyBulkAsync(c,msg);
addReplyPushLen(c,4);
addReply(c,shared.pmessagebulk);
addReplyBulk(c,pat);
addReplyBulk(c,channel);
addReplyBulk(c,msg);
}
/* Send the pubsub subscription notification to the client. */
+2 -2
View File
@@ -46,7 +46,7 @@
* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).
* encoding: 2 bits, RAW=1, LZF=2.
* container: 2 bits, NONE=1, ZIPLIST=2.
* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.
* recompress: 1 bit, bool, true if node is temporary decompressed for usage.
* attempted_compress: 1 bit, boolean, used for verifying during testing.
* extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {
@@ -105,7 +105,7 @@ typedef struct quicklistBookmark {
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.
* 'count' is the number of total entries.
* 'len' is the number of quicklist nodes.
* 'compress' is: -1 if compression disabled, otherwise it's the number
* 'compress' is: 0 if compression disabled, otherwise it's the number
* of quicklistNodes to leave uncompressed at ends of quicklist.
* 'fill' is the user-requested (or default) fill factor.
* 'bookmakrs are an optional feature that is used by realloc this struct,
+10 -10
View File
@@ -487,8 +487,8 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode
if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */
memcpy(&h,children+j,sizeof(h));
parentlink = children+j;
j = 0; /* If the new node is compressed and we do not
iterate again (since i == l) set the split
j = 0; /* If the new node is non compressed and we do not
iterate again (since i == len) set the split
position to 0 to signal this node represents
the searched key. */
}
@@ -628,7 +628,7 @@ int raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **
*
* 3b. IF $SPLITPOS != 0:
* Trim the compressed node (reallocating it as well) in order to
* contain $splitpos characters. Change chilid pointer in order to link
* contain $splitpos characters. Change child pointer in order to link
* to the split node. If new compressed node len is just 1, set
* iscompr to 0 (layout is the same). Fix parent's reference.
*
@@ -1082,7 +1082,7 @@ int raxRemove(rax *rax, unsigned char *s, size_t len, void **old) {
}
} else if (h->size == 1) {
/* If the node had just one child, after the removal of the key
* further compression with adjacent nodes is pontentially possible. */
* further compression with adjacent nodes is potentially possible. */
trycompress = 1;
}
@@ -1329,7 +1329,7 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
if (!noup && children) {
debugf("GO DEEPER\n");
/* Seek the lexicographically smaller key in this subtree, which
* is the first one found always going torwards the first child
* is the first one found always going towards the first child
* of every successive node. */
if (!raxStackPush(&it->stack,it->node)) return 0;
raxNode **cp = raxNodeFirstChildPtr(it->node);
@@ -1348,7 +1348,7 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
return 1;
}
} else {
/* If we finished exporing the previous sub-tree, switch to the
/* If we finished exploring the previous sub-tree, switch to the
* new one: go upper until a node is found where there are
* children representing keys lexicographically greater than the
* current key. */
@@ -1510,7 +1510,7 @@ int raxIteratorPrevStep(raxIterator *it, int noup) {
int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {
int eq = 0, lt = 0, gt = 0, first = 0, last = 0;
it->stack.items = 0; /* Just resetting. Intialized by raxStart(). */
it->stack.items = 0; /* Just resetting. Initialized by raxStart(). */
it->flags |= RAX_ITER_JUST_SEEKED;
it->flags &= ~RAX_ITER_EOF;
it->key_len = 0;
@@ -1731,7 +1731,7 @@ int raxPrev(raxIterator *it) {
* tree, expect a disappointing distribution. A random walk produces good
* random elements if the tree is not sparse, however in the case of a radix
* tree certain keys will be reported much more often than others. At least
* this function should be able to expore every possible element eventually. */
* this function should be able to explore every possible element eventually. */
int raxRandomWalk(raxIterator *it, size_t steps) {
if (it->rt->numele == 0) {
it->flags |= RAX_ITER_EOF;
@@ -1825,7 +1825,7 @@ uint64_t raxSize(rax *rax) {
/* ----------------------------- Introspection ------------------------------ */
/* This function is mostly used for debugging and learning purposes.
* It shows an ASCII representation of a tree on standard output, outling
* It shows an ASCII representation of a tree on standard output, outline
* all the nodes and the contained keys.
*
* The representation is as follow:
@@ -1835,7 +1835,7 @@ uint64_t raxSize(rax *rax) {
* [abc]=0x12345678 (node is a key, pointing to value 0x12345678)
* [] (a normal empty node)
*
* Children are represented in new idented lines, each children prefixed by
* Children are represented in new indented lines, each children prefixed by
* the "`-(x)" string, where "x" is the edge byte.
*
* [abc]
+2 -2
View File
@@ -68,7 +68,7 @@ extern "C" {
* successive nodes having a single child are "compressed" into the node
* itself as a string of characters, each representing a next-level child,
* and only the link to the node representing the last character node is
* provided inside the representation. So the above representation is turend
* provided inside the representation. So the above representation is turned
* into:
*
* ["foo"] ""
@@ -133,7 +133,7 @@ typedef struct raxNode {
* nodes).
*
* If the node has an associated key (iskey=1) and is not NULL
* (isnull=0), then after the raxNode pointers poiting to the
* (isnull=0), then after the raxNode pointers pointing to the
* children, an additional value pointer is present (as you can see
* in the representation above as "value-ptr" field).
*/
+136 -57
View File
@@ -36,6 +36,7 @@
#include "cron.h"
#include <math.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
@@ -54,6 +55,9 @@ extern int rdbCheckMode;
void rdbCheckError(const char *fmt, ...);
void rdbCheckSetError(const char *fmt, ...);
#ifdef __GNUC__
void rdbReportError(int corruption_error, int linenum, const char *reason, ...) __attribute__ ((format (printf, 3, 4)));
#endif
void rdbReportError(int corruption_error, int linenum, const char *reason, ...) {
va_list ap;
char msg[1024];
@@ -82,7 +86,7 @@ void rdbReportError(int corruption_error, int linenum, const char *reason, ...)
exit(1);
}
static int rdbWriteRaw(rio *rdb, void *p, size_t len) {
static ssize_t rdbWriteRaw(rio *rdb, void *p, size_t len) {
if (rdb && rioWrite(rdb,p,len) == 0)
return -1;
return len;
@@ -489,7 +493,7 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) {
int plain = flags & RDB_LOAD_PLAIN;
int sds = flags & RDB_LOAD_SDS;
int isencoded;
uint64_t len;
unsigned long long len;
len = rdbLoadLen(rdb,&isencoded);
if (isencoded) {
@@ -501,8 +505,8 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags, size_t *lenptr) {
case RDB_ENC_LZF:
return rdbLoadLzfStringObject(rdb,flags,lenptr);
default:
rdbExitReportCorruptRDB("Unknown RDB string encoding type %d",len);
return nullptr; /* Never reached. */
rdbExitReportCorruptRDB("Unknown RDB string encoding type %llu",len);
return nullptr;
}
}
@@ -706,15 +710,23 @@ ssize_t rdbSaveStreamPEL(rio *rdb, rax *pel, int nacks) {
while(raxNext(&ri)) {
/* We store IDs in raw form as 128 big big endian numbers, like
* they are inside the radix tree key. */
if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) return -1;
if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
if (nacks) {
streamNACK *nack = (streamNACK*)ri.data;
if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1)
if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) return -1;
if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* We don't save the consumer name: we'll save the pending IDs
* for each consumer in the consumer PEL, and resolve the consumer
@@ -743,20 +755,27 @@ size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {
streamConsumer *consumer = (streamConsumer*)ri.data;
/* Consumer name. */
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* Last seen time. */
if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1)
if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* Consumer PEL, without the ACKs (see last parameter of the function
* passed with value of 0), at loading time we'll lookup the ID
* in the consumer group global PEL and will put a reference in the
* consumer local PEL. */
if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1)
if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
}
raxStop(&ri);
@@ -921,9 +940,15 @@ ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
while (raxNext(&ri)) {
unsigned char *lp = (unsigned char*)ri.data;
size_t lp_bytes = lpBytes(lp);
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) return -1;
if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
}
raxStop(&ri);
@@ -955,22 +980,36 @@ ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
streamCG *cg = (streamCG*)ri.data;
/* Save the group name. */
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1)
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* Last ID. */
if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) return -1;
if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) return -1;
if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* Save the global PEL. */
if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) return -1;
if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
/* Save the consumers of this group. */
if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) return -1;
if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) {
raxStop(&ri);
return -1;
}
nwritten += n;
}
raxStop(&ri);
@@ -1170,6 +1209,8 @@ ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {
/* Save a module-specific aux value. */
RedisModuleIO io;
int retval = rdbSaveType(rdb, RDB_OPCODE_MODULE_AUX);
if (retval == -1) return -1;
io.bytes += retval;
/* Write the "module" identifier as prefix, so that we'll be able
* to call the right module during loading. */
@@ -1230,7 +1271,7 @@ int rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {
for (j = 0; j < cserver.dbnum; j++) {
redisDb *db = g_pserver->db+j;
dict *d = db->pdict;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
@@ -1240,7 +1281,7 @@ int rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {
/* Write the RESIZE DB opcode. */
uint64_t db_size, expires_size;
db_size = dictSize(db->pdict);
db_size = dictSize(db->dict);
expires_size = db->setexpire->size();
if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
if (rdbSaveLen(rdb,db_size) == -1) goto werr;
@@ -1357,7 +1398,7 @@ int rdbSave(rdbSaveInfo *rsi)
int rdbSaveFile(char *filename, rdbSaveInfo *rsi) {
char tmpfile[256];
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
FILE *fp;
FILE *fp = NULL;
rio rdb;
int error = 0;
@@ -1386,10 +1427,11 @@ int rdbSaveFile(char *filename, rdbSaveInfo *rsi) {
}
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
if (fsync(fileno(fp)) == -1) goto werr;
if (fclose(fp) == EOF) goto werr;
if (fflush(fp)) goto werr;
if (fsync(fileno(fp))) goto werr;
if (fclose(fp)) { fp = NULL; goto werr; }
fp = NULL;
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
if (rename(tmpfile,filename) == -1) {
@@ -1415,7 +1457,7 @@ int rdbSaveFile(char *filename, rdbSaveInfo *rsi) {
werr:
serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));
fclose(fp);
if (fp) fclose(fp);
unlink(tmpfile);
stopSaving(0);
return C_ERR;
@@ -1430,7 +1472,7 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
g_pserver->lastbgsave_try = time(NULL);
openChildInfoPipe();
if ((childpid = redisFork()) == 0) {
if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) {
int retval;
/* Child */
@@ -1438,7 +1480,7 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
redisSetCpuAffinity(g_pserver->bgsave_cpulist);
retval = rdbSave(rsi);
if (retval == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_RDB, "RDB");
sendChildCOWInfo(CHILD_TYPE_RDB, "RDB");
}
exitFromChild((retval == C_OK) ? 0 : 1);
} else {
@@ -1454,16 +1496,35 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
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;
}
return C_OK; /* unreached */
}
void rdbRemoveTempFile(pid_t childpid) {
/* Note that we may call this function in signal handle 'sigShutdownHandler',
* so we need guarantee all functions we call are async-signal-safe.
* If we call this function from signal handle, we won't call bg_unlik that
* is not async-signal-safe. */
void rdbRemoveTempFile(pid_t childpid, int from_signal) {
char tmpfile[256];
char pid[32];
snprintf(tmpfile,sizeof(tmpfile),"temp-%d.rdb", (int) childpid);
unlink(tmpfile);
/* Generate temp rdb file name using aync-signal safe functions. */
int pid_len = ll2string(pid, sizeof(pid), childpid);
strcpy(tmpfile, "temp-");
strncpy(tmpfile+5, pid, pid_len);
strcpy(tmpfile+5+pid_len, ".rdb");
if (from_signal) {
/* bg_unlink is not async-signal-safe, but in this case we don't really
* need to close the fd, it'll be released when the process exists. */
int fd = open(tmpfile, O_RDONLY|O_NONBLOCK);
UNUSED(fd);
unlink(tmpfile);
} else {
bg_unlink(tmpfile);
}
}
/* This function is called by rdbLoadObject() when the code is in RDB-check
@@ -1590,7 +1651,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, uint64_t mvcc_tstamp) {
zs = (zset*)ptrFromObj(o);
if (zsetlen > DICT_HT_INITIAL_SIZE)
dictExpand(zs->pdict,zsetlen);
dictExpand(zs->dict,zsetlen);
/* Load every single element of the sorted set. */
while(zsetlen--) {
@@ -1621,7 +1682,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, uint64_t mvcc_tstamp) {
if (sdslen(sdsele) > maxelelen) maxelelen = sdslen(sdsele);
znode = zslInsert(zs->zsl,score,sdsele);
dictAdd(zs->pdict,sdsele,&znode->score);
dictAdd(zs->dict,sdsele,&znode->score);
}
/* Convert *after* loading, since sorted sets are not stored ordered. */
@@ -2130,8 +2191,11 @@ void stopSaving(int success) {
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
if (g_pserver->rdb_checksum)
rioGenericUpdateChecksum(r, buf, len);
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)
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) ||
(g_pserver->loading_process_events_interval_keys &&
(r->keys_since_last_callback >= g_pserver->loading_process_events_interval_keys)))
{
/* 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
@@ -2149,6 +2213,14 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
loadingProgress(r->processed_bytes);
processEventsWhileBlocked(serverTL - g_pserver->rgthreadvar);
processModuleLoadingProgressEvent(0);
robj *ping_argv[1];
ping_argv[0] = createStringObject("PING",4);
replicationFeedSlaves(g_pserver->slaves, g_pserver->replicaseldb, ping_argv, 1);
decrRefCount(ping_argv[0]);
r->keys_since_last_callback = 0;
}
}
@@ -2242,12 +2314,12 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
goto eoferr;
if ((expires_size = rdbLoadLen(rdb,NULL)) == RDB_LENERR)
goto eoferr;
dictExpand(db->pdict,db_size);
dictExpand(db->dict,db_size);
continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_AUX) {
/* AUX: generic string-string fields. Use to add state to RDB
* which is backward compatible. Implementations of RDB loading
* are requierd to skip AUX fields they don't understand.
* are required to skip AUX fields they don't understand.
*
* An AUX field is composed of two strings: key and value. */
robj *auxkey, *auxval;
@@ -2275,7 +2347,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
if (luaCreateFunction(NULL,g_pserver->lua,auxval) == NULL) {
rdbExitReportCorruptRDB(
"Can't load Lua script from RDB file! "
"BODY: %s", ptrFromObj(auxval));
"BODY: %s", (char*)ptrFromObj(auxval));
}
} else if (!strcasecmp(szFromObj(auxkey),"redis-ver")) {
serverLog(LL_NOTICE,"Loading RDB produced by version %s",
@@ -2318,7 +2390,9 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
else {
redisObjectStack keyobj;
initStaticStringObject(keyobj,key);
setExpire(NULL, db, &keyobj, subexpireKey, strtoll(szFromObj(auxval), nullptr, 10));
long long expireT = strtoll(szFromObj(auxval), nullptr, 10);
setExpire(NULL, db, &keyobj, subexpireKey, expireT);
replicateSubkeyExpire(db, &keyobj, subexpireKey, expireT);
decrRefCount(subexpireKey);
subexpireKey = nullptr;
}
@@ -2437,6 +2511,11 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
/* Set usage information (for eviction). */
objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock,1000);
/* call key space notification on key loaded for modules only */
moduleNotifyKeyspaceEvent(NOTIFY_LOADED, "loaded", &keyobj, db->id);
replicationNotifyLoadedKey(db, &keyobj, val, expiretime);
}
else
{
@@ -2448,6 +2527,8 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
if (g_pserver->key_load_delay)
usleep(g_pserver->key_load_delay);
rdb->keys_since_last_callback++;
/* Reset the state that is key-specified and is populated by
* opcodes before the key, so that we start from scratch again. */
expiretime = -1;
@@ -2545,7 +2626,7 @@ int rdbLoadFile(const char *filename, rdbSaveInfo *rsi, int rdbflags) {
/* A background saving child (BGSAVE) terminated its work. Handle this.
* This function covers the case of actual BGSAVEs. */
void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
static void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
if (!bysignal && exitcode == 0) {
serverLog(LL_NOTICE,
"Background saving terminated with success");
@@ -2561,27 +2642,20 @@ void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
serverLog(LL_WARNING,
"Background saving terminated by signal %d", bysignal);
latencyStartMonitor(latency);
rdbRemoveTempFile(g_pserver->rdb_child_pid);
rdbRemoveTempFile(g_pserver->rdb_child_pid, 0);
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. */
* triggering an error condition. */
if (bysignal != SIGUSR1)
g_pserver->lastbgsave_status = C_ERR;
}
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);
}
/* A background saving child (BGSAVE) terminated its work. Handle this.
* This function covers the case of RDB -> Slaves socket transfers for
* diskless replication. */
void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
static void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
serverAssert(GlobalLocksAcquired());
if (!bysignal && exitcode == 0) {
@@ -2593,15 +2667,11 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
serverLog(LL_WARNING,
"Background transfer terminated by signal %d", bysignal);
}
g_pserver->rdb_child_pid = -1;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_NONE;
g_pserver->rdb_save_time_start = -1;
updateSlavesWaitingBgsave((!bysignal && exitcode == 0) ? C_OK : C_ERR, RDB_CHILD_TYPE_SOCKET);
}
/* When a background RDB saving/transfer terminates, call the right handler. */
void backgroundSaveDoneHandler(int exitcode, int bysignal) {
int type = g_pserver->rdb_child_type;
switch(g_pserver->rdb_child_type) {
case RDB_CHILD_TYPE_DISK:
backgroundSaveDoneHandlerDisk(exitcode,bysignal);
@@ -2613,6 +2683,14 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
serverPanic("Unknown RDB child type.");
break;
}
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, type);
}
/* Kill the RDB saving child using SIGUSR1 (so that the parent will know
@@ -2620,7 +2698,7 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
* the cleanup needed. */
void killRDBChild(void) {
kill(g_pserver->rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(g_pserver->rdb_child_pid);
rdbRemoveTempFile(g_pserver->rdb_child_pid, 0);
closeChildInfoPipe();
updateDictResizePolicy();
}
@@ -2665,7 +2743,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
/* Create the child process. */
openChildInfoPipe();
if ((childpid = redisFork()) == 0) {
if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) {
/* Child */
int retval;
rio rdb;
@@ -2680,7 +2758,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
retval = C_ERR;
if (retval == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_RDB, "RDB");
sendChildCOWInfo(CHILD_TYPE_RDB, "RDB");
}
rioFreeFd(&rdb);
@@ -2715,6 +2793,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
g_pserver->rdb_save_time_start = time(NULL);
g_pserver->rdb_child_pid = childpid;
g_pserver->rdb_child_type = RDB_CHILD_TYPE_SOCKET;
updateDictResizePolicy();
close(g_pserver->rdb_pipe_write); /* close write in parent so that it can detect the close on the child. */
aePostFunction(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el, []{
if (aeCreateFileEvent(serverTL->el, g_pserver->rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {
+2 -1
View File
@@ -145,7 +145,7 @@ int rdbLoad(rdbSaveInfo *rsi, int rdbflags);
int rdbLoadFile(const char *filename, rdbSaveInfo *rsi, int rdbflags);
int rdbSaveBackground(rdbSaveInfo *rsi);
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
void rdbRemoveTempFile(pid_t childpid);
void rdbRemoveTempFile(pid_t childpid, int from_signal);
int rdbSave(rdbSaveInfo *rsi);
int rdbSaveFile(char *filename, rdbSaveInfo *rsi);
int rdbSaveFp(FILE *pf, rdbSaveInfo *rsi);
@@ -157,6 +157,7 @@ robj *rdbLoadObject(int type, rio *rdb, sds key, uint64_t mvcc_tstamp);
void backgroundSaveDoneHandler(int exitcode, int bysignal);
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);
ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt);
robj *rdbLoadCheckModuleValue(rio *rdb, char *modulename);
robj *rdbLoadStringObject(rio *rdb);
ssize_t rdbSaveStringObject(rio *rdb, robj_roptr obj);
ssize_t rdbSaveRawString(rio *rdb, const unsigned char *s, size_t len);
+109 -68
View File
@@ -1,4 +1,4 @@
/* Redis benchmark utility.
/* Redis benchmark utility.
*
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
@@ -189,6 +189,8 @@ static void *execBenchmarkThread(void *ptr);
static clusterNode *createClusterNode(char *ip, int port);
static redisConfig *getRedisConfig(const char *ip, int port,
const char *hostsocket);
static redisContext *getRedisContext(const char *ip, int port,
const char *hostsocket);
static void freeRedisConfig(redisConfig *cfg);
static int fetchClusterSlotsConfiguration(client c);
static void updateClusterSlotsConfiguration();
@@ -244,46 +246,69 @@ extern "C" void _serverAssert(const char *estr, const char *file, int line) {
*((char*)-1) = 'x';
}
static redisContext *getRedisContext(const char *ip, int port,
const char *hostsocket)
{
redisContext *ctx = NULL;
redisReply *reply = NULL;
if (hostsocket == NULL)
ctx = redisConnect(ip, port);
else
ctx = redisConnectUnix(hostsocket);
if (ctx == NULL || ctx->err) {
fprintf(stderr,"Could not connect to Redis at ");
const char *err = (ctx != NULL ? ctx->errstr : "");
if (hostsocket == NULL)
fprintf(stderr,"%s:%d: %s\n",ip,port,err);
else
fprintf(stderr,"%s: %s\n",hostsocket,err);
goto cleanup;
}
if (config.auth == NULL)
return ctx;
if (config.user == NULL)
reply = (redisReply*)redisCommand(ctx,"AUTH %s", config.auth);
else
reply = (redisReply*)redisCommand(ctx,"AUTH %s %s", config.user, config.auth);
if (reply != NULL) {
if (reply->type == REDIS_REPLY_ERROR) {
if (hostsocket == NULL)
fprintf(stderr, "Node %s:%d replied with error:\n%s\n", ip, port, reply->str);
else
fprintf(stderr, "Node %s replied with error:\n%s\n", hostsocket, reply->str);
goto cleanup;
}
freeReplyObject(reply);
return ctx;
}
fprintf(stderr, "ERROR: failed to fetch reply from ");
if (hostsocket == NULL)
fprintf(stderr, "%s:%d\n", ip, port);
else
fprintf(stderr, "%s\n", hostsocket);
cleanup:
freeReplyObject(reply);
redisFree(ctx);
return NULL;
}
static redisConfig *getRedisConfig(const char *ip, int port,
const char *hostsocket)
{
redisConfig *cfg = (redisConfig*)zcalloc(sizeof(*cfg), MALLOC_LOCAL);
int i = 0;
void *r = NULL;
redisConfig *cfg = (redisConfig*)zcalloc(sizeof(*cfg));
if (!cfg) return NULL;
redisContext *c = NULL;
redisReply *reply = NULL, *sub_reply = NULL;
if (hostsocket == NULL)
c = redisConnect(ip, port);
else
c = redisConnectUnix(hostsocket);
if (c == NULL || c->err) {
fprintf(stderr,"Could not connect to Redis at ");
const char *err = (c != NULL ? c->errstr : "");
if (hostsocket == NULL) fprintf(stderr,"%s:%d: %s\n",ip,port,err);
else fprintf(stderr,"%s: %s\n",hostsocket,err);
goto fail;
c = getRedisContext(ip, port, hostsocket);
if (c == NULL) {
freeRedisConfig(cfg);
return NULL;
}
if(config.auth) {
void *authReply = NULL;
if (config.user == NULL)
redisAppendCommand(c, "AUTH %s", config.auth);
else
redisAppendCommand(c, "AUTH %s %s", config.user, 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");
for (; i < 2; i++) {
void *r;
for (int i=0; i < 2; i++) {
int res = redisGetReply(c, &r);
if (reply) freeReplyObject(reply);
reply = res == REDIS_OK ? ((redisReply *) r) : NULL;
@@ -740,7 +765,7 @@ static client createClient(const char *cmd, size_t len, client from, int thread_
}
c->stagptr[c->staglen++] = p;
c->stagfree--;
p += 5; /* 12 is strlen("{tag}"). */
p += 5; /* 5 is strlen("{tag}"). */
}
}
}
@@ -1001,17 +1026,11 @@ static int fetchClusterConfiguration() {
int success = 1;
redisContext *ctx = NULL;
redisReply *reply = NULL;
char *lines = reply->str, *p, *line;
if (config.hostsocket == NULL)
ctx = redisConnect(config.hostip,config.hostport);
else
ctx = redisConnectUnix(config.hostsocket);
if (ctx->err) {
fprintf(stderr,"Could not connect to Redis at ");
if (config.hostsocket == NULL) {
fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,
ctx->errstr);
} else fprintf(stderr,"%s: %s\n",config.hostsocket,ctx->errstr);
char *lines = NULL;
char *line = NULL;
char *p = NULL;
ctx = getRedisContext(config.hostip, config.hostport, config.hostsocket);
if (ctx == NULL) {
exit(1);
}
clusterNode *firstNode = createClusterNode((char *) config.hostip,
@@ -1207,11 +1226,9 @@ static int fetchClusterSlotsConfiguration(client c) {
assert(node->port);
/* Use first node as entry point to connect to. */
if (ctx == NULL) {
ctx = redisConnect(node->ip, node->port);
if (!ctx || ctx->err) {
ctx = getRedisContext(node->ip, node->port, NULL);
if (!ctx) {
success = 0;
if (ctx && ctx->err)
fprintf(stderr, "REDIS CONNECTION ERROR: %s\n", ctx->errstr);
goto cleanup;
}
}
@@ -1425,7 +1442,8 @@ usage:
" --cluster Enable cluster mode.\n"
" --enable-tracking Send CLIENT TRACKING on before starting benchmark.\n"
" -k <boolean> 1=keep alive 0=reconnect (default 1)\n"
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD\n"
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD,\n"
" random members and scores for ZADD.\n"
" Using this option the benchmark will expand the string __rand_int__\n"
" inside an argument with a 12 digits number in the specified range\n"
" from 0 to keyspacelen-1. The substitution changes every time a command\n"
@@ -1507,6 +1525,7 @@ int test_is_selected(const char *name) {
int main(int argc, const char **argv) {
int i;
char *data, *cmd;
const char *tag;
int len;
client c;
@@ -1558,7 +1577,12 @@ int main(int argc, const char **argv) {
config.latency = (long long*)zmalloc(sizeof(long long)*config.requests, MALLOC_LOCAL);
tag = "";
if (config.cluster_mode) {
// We only include the slot placeholder {tag} if cluster mode is enabled
tag = ":{tag}";
/* Fetch cluster configuration. */
if (!fetchClusterConfiguration() || !config.cluster_nodes) {
if (!config.hostsocket) {
@@ -1672,112 +1696,129 @@ int main(int argc, const char **argv) {
}
if (test_is_selected("set")) {
len = redisFormatCommand(&cmd,"SET key:{tag}:__rand_int__ %s",data);
len = redisFormatCommand(&cmd,"SET key%s:__rand_int__ %s",tag,data);
benchmark("SET",cmd,len);
free(cmd);
}
if (test_is_selected("get")) {
len = redisFormatCommand(&cmd,"GET key:{tag}:__rand_int__");
len = redisFormatCommand(&cmd,"GET key%s:__rand_int__",tag);
benchmark("GET",cmd,len);
free(cmd);
}
if (test_is_selected("incr")) {
len = redisFormatCommand(&cmd,"INCR counter:{tag}:__rand_int__");
len = redisFormatCommand(&cmd,"INCR counter%s:__rand_int__",tag);
benchmark("INCR",cmd,len);
free(cmd);
}
if (test_is_selected("lpush")) {
len = redisFormatCommand(&cmd,"LPUSH mylist:{tag} %s",data);
len = redisFormatCommand(&cmd,"LPUSH mylist%s %s",tag,data);
benchmark("LPUSH",cmd,len);
free(cmd);
}
if (test_is_selected("rpush")) {
len = redisFormatCommand(&cmd,"RPUSH mylist:{tag} %s",data);
len = redisFormatCommand(&cmd,"RPUSH mylist%s %s",tag,data);
benchmark("RPUSH",cmd,len);
free(cmd);
}
if (test_is_selected("lpop")) {
len = redisFormatCommand(&cmd,"LPOP mylist:{tag}");
len = redisFormatCommand(&cmd,"LPOP mylist%s",tag);
benchmark("LPOP",cmd,len);
free(cmd);
}
if (test_is_selected("rpop")) {
len = redisFormatCommand(&cmd,"RPOP mylist:{tag}");
len = redisFormatCommand(&cmd,"RPOP mylist%s",tag);
benchmark("RPOP",cmd,len);
free(cmd);
}
if (test_is_selected("sadd")) {
len = redisFormatCommand(&cmd,
"SADD myset:{tag} element:__rand_int__");
"SADD myset%s element:__rand_int__",tag);
benchmark("SADD",cmd,len);
free(cmd);
}
if (test_is_selected("hset")) {
len = redisFormatCommand(&cmd,
"HSET myhash:{tag}:__rand_int__ element:__rand_int__ %s",data);
"HSET myhash%s element:__rand_int__ %s",tag,data);
benchmark("HSET",cmd,len);
free(cmd);
}
if (test_is_selected("spop")) {
len = redisFormatCommand(&cmd,"SPOP myset:{tag}");
len = redisFormatCommand(&cmd,"SPOP myset%s",tag);
benchmark("SPOP",cmd,len);
free(cmd);
}
if (test_is_selected("zadd")) {
const char *score = "0";
if (config.randomkeys) score = "__rand_int__";
len = redisFormatCommand(&cmd,
"ZADD myzset%s %s element:__rand_int__",tag,score);
benchmark("ZADD",cmd,len);
free(cmd);
}
if (test_is_selected("zpopmin")) {
len = redisFormatCommand(&cmd,"ZPOPMIN myzset%s",tag);
benchmark("ZPOPMIN",cmd,len);
free(cmd);
}
if (test_is_selected("lrange") ||
test_is_selected("lrange_100") ||
test_is_selected("lrange_300") ||
test_is_selected("lrange_500") ||
test_is_selected("lrange_600"))
{
len = redisFormatCommand(&cmd,"LPUSH mylist:{tag} %s",data);
len = redisFormatCommand(&cmd,"LPUSH mylist%s %s",tag,data);
benchmark("LPUSH (needed to benchmark LRANGE)",cmd,len);
free(cmd);
}
if (test_is_selected("lrange") || test_is_selected("lrange_100")) {
len = redisFormatCommand(&cmd,"LRANGE mylist:{tag} 0 99");
len = redisFormatCommand(&cmd,"LRANGE mylist%s 0 99",tag);
benchmark("LRANGE_100 (first 100 elements)",cmd,len);
free(cmd);
}
if (test_is_selected("lrange") || test_is_selected("lrange_300")) {
len = redisFormatCommand(&cmd,"LRANGE mylist:{tag} 0 299");
len = redisFormatCommand(&cmd,"LRANGE mylist%s 0 299",tag);
benchmark("LRANGE_300 (first 300 elements)",cmd,len);
free(cmd);
}
if (test_is_selected("lrange") || test_is_selected("lrange_500")) {
len = redisFormatCommand(&cmd,"LRANGE mylist:{tag} 0 449");
len = redisFormatCommand(&cmd,"LRANGE mylist%s 0 449",tag);
benchmark("LRANGE_500 (first 450 elements)",cmd,len);
free(cmd);
}
if (test_is_selected("lrange") || test_is_selected("lrange_600")) {
len = redisFormatCommand(&cmd,"LRANGE mylist:{tag} 0 599");
len = redisFormatCommand(&cmd,"LRANGE mylist%s 0 599",tag);
benchmark("LRANGE_600 (first 600 elements)",cmd,len);
free(cmd);
}
if (test_is_selected("mset")) {
const char *argv[21];
argv[0] = "MSET";
const char *cmd_argv[21];
cmd_argv[0] = "MSET";
sds key_placeholder = sdscatprintf(sdsnew(""),"key%s:__rand_int__",tag);
for (i = 1; i < 21; i += 2) {
argv[i] = "key:{tag}:__rand_int__";
argv[i+1] = data;
cmd_argv[i] = key_placeholder;
cmd_argv[i+1] = data;
}
len = redisFormatCommandArgv(&cmd,21,argv,NULL);
len = redisFormatCommandArgv(&cmd,21,cmd_argv,NULL);
benchmark("MSET (10 keys)",cmd,len);
free(cmd);
sdsfree(key_placeholder);
}
if (!config.csv) printf("\n");
+20 -2
View File
@@ -58,6 +58,7 @@ struct {
#define RDB_CHECK_DOING_CHECK_SUM 5
#define RDB_CHECK_DOING_READ_LEN 6
#define RDB_CHECK_DOING_READ_AUX 7
#define RDB_CHECK_DOING_READ_MODULE_AUX 8
const char *rdb_check_doing_string[] = {
"start",
@@ -67,7 +68,8 @@ const char *rdb_check_doing_string[] = {
"read-object-value",
"check-sum",
"read-len",
"read-aux"
"read-aux",
"read-module-aux"
};
const char *rdb_type_string[] = {
@@ -272,6 +274,21 @@ int redis_check_rdb(const char *rdbfilename, FILE *fp) {
decrRefCount(auxkey);
decrRefCount(auxval);
continue; /* Read type again. */
} else if (type == RDB_OPCODE_MODULE_AUX) {
/* AUX: Auxiliary data for modules. */
uint64_t moduleid, when_opcode, when;
rdbstate.doing = RDB_CHECK_DOING_READ_MODULE_AUX;
if ((moduleid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
if ((when_opcode = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
if ((when = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
char name[10];
moduleTypeNameByID(name,moduleid);
rdbCheckInfo("MODULE AUX for: %s", name);
robj *o = rdbLoadCheckModuleValue(&rdb,name);
decrRefCount(o);
continue; /* Read type again. */
} else {
if (!rdbIsObjectType(type)) {
rdbCheckError("Invalid object type: %d", type);
@@ -331,7 +348,7 @@ err:
return 1;
}
/* RDB check main: called form redis.c when Redis is executed with the
/* RDB check main: called form server.c when Redis is executed with the
* keydb-check-rdb alias, on during RDB loading errors.
*
* The function works in two ways: can be called with argc/argv as a
@@ -354,6 +371,7 @@ int redis_check_rdb_main(int argc, const char **argv, FILE *fp) {
if (shared.integers[0] == NULL)
createSharedObjects();
g_pserver->loading_process_events_interval_bytes = 0;
g_pserver->loading_process_events_interval_keys = 0;
rdbCheckMode = 1;
rdbCheckInfo("Checking RDB file %s", argv[1]);
rdbCheckSetupSignals();
+14 -12
View File
@@ -109,7 +109,6 @@ extern "C" void freeClusterManager(void) {
}
/* This function returns a random master node, return NULL if none */
static clusterManagerNode *clusterManagerNodeMasterRandom() {
int master_count = 0;
int idx;
@@ -140,7 +139,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
int force_fix = config.cluster_manager_command.flags &
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
/* we want explicit manual confirmation from users for all the fix cases */
int force = 0;
int ignore_force = 1;
dictIterator *iter = nullptr;
@@ -213,7 +212,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
"across the cluster:\n");
clusterManagerPrintSlotsList(none);
if (confirmWithYes("Fix these slots by covering with a random node?",
force)) {
ignore_force)) {
listIter li;
listNode *ln;
listRewind(none, &li);
@@ -240,7 +239,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
printf("The following uncovered slots have keys in just one node:\n");
clusterManagerPrintSlotsList(single);
if (confirmWithYes("Fix these slots by covering with those nodes?",
force)) {
ignore_force)) {
listIter li;
listNode *ln;
listRewind(single, &li);
@@ -272,7 +271,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
printf("The following uncovered slots have keys in multiple nodes:\n");
clusterManagerPrintSlotsList(multi);
if (confirmWithYes("Fix these slots by moving keys "
"into a single node?", force)) {
"into a single node?", ignore_force)) {
listIter li;
listNode *ln;
listRewind(multi, &li);
@@ -336,6 +335,7 @@ cleanup:
return fixed;
}
/* Return the anti-affinity score, which is a measure of the amount of
* violations of anti-affinity in the current cluster layout, that is, how
* badly the masters and slaves are distributed in the different IP
@@ -759,7 +759,9 @@ static void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {
/* Pipeline TYPE commands */
for(i=0;i<keys->elements;i++) {
redisAppendCommand(context, "TYPE %s", keys->element[i]->str);
const char* argv[] = {"TYPE", keys->element[i]->str};
size_t lens[] = {4, keys->element[i]->len};
redisAppendCommandArgv(context, 2, argv, lens);
}
/* Retrieve types */
@@ -856,20 +858,20 @@ void findBigKeys(int memkeys, unsigned memkeys_samples) {
sampled++;
if(type->biggest<sizes[i]) {
printf(
"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
pct, type->name, keys->element[i]->str, sizes[i],
!memkeys? type->sizeunit: "bytes");
/* Keep track of biggest key name for this type */
if (type->biggest_key)
sdsfree(type->biggest_key);
type->biggest_key = sdsnew(keys->element[i]->str);
type->biggest_key = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
if(!type->biggest_key) {
fprintf(stderr, "Failed to allocate memory for key!\n");
exit(1);
}
printf(
"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
pct, type->name, type->biggest_key, sizes[i],
!memkeys? type->sizeunit: "bytes");
/* Keep track of the biggest size for this type */
type->biggest = sizes[i];
}
+215 -82
View File
@@ -146,7 +146,7 @@ static void cliRefreshPrompt(void) {
/* Return the name of the dotfile for the specified 'dotfilename'.
* Normally it just concatenates user $HOME to the file specified
* in 'dotfilename'. However if the environment varialbe 'envoverride'
* in 'dotfilename'. However if the environment variable 'envoverride'
* is set, its value is taken as the path.
*
* The function returns NULL (if the file is /dev/null or cannot be
@@ -220,12 +220,20 @@ static sds percentDecode(const char *pe, size_t len) {
static void parseRedisUri(const char *uri) {
const char *scheme = "redis://";
const char *tlsscheme = "rediss://";
const char *curr = uri;
const char *end = uri + strlen(uri);
const char *userinfo, *username, *port, *host, *path;
/* URI must start with a valid scheme. */
if (strncasecmp(scheme, curr, strlen(scheme))) {
if (!strncasecmp(tlsscheme, curr, strlen(tlsscheme))) {
#ifdef USE_OPENSSL
config.tls = 1;
#else
fprintf(stderr,"rediss:// is only supported when redis-cli is compiled with OpenSSL\n");
exit(1);
#endif
} else if (strncasecmp(scheme, curr, strlen(scheme))) {
fprintf(stderr,"Invalid URI scheme\n");
exit(1);
}
@@ -1060,7 +1068,7 @@ static int cliReadReply(int output_raw_strings) {
} else {
if (config.output == OUTPUT_RAW) {
out = cliFormatReplyRaw(reply);
out = sdscat(out,"\n");
out = sdscatsds(out, config.cmd_delim);
} else if (config.output == OUTPUT_STANDARD) {
out = cliFormatReplyTTY(reply,"");
} else if (config.output == OUTPUT_CSV) {
@@ -1342,6 +1350,9 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"-d") && !lastarg) {
sdsfree(config.mb_delim);
config.mb_delim = sdsnew(argv[++i]);
} else if (!strcmp(argv[i],"-D") && !lastarg) {
sdsfree(config.cmd_delim);
config.cmd_delim = sdsnew(argv[++i]);
} else if (!strcmp(argv[i],"--verbose")) {
config.verbose = 1;
} else if (!strcmp(argv[i],"--cluster") && !lastarg) {
@@ -1354,6 +1365,12 @@ static int parseOptions(int argc, char **argv) {
i = j;
} else if (!strcmp(argv[i],"--cluster") && lastarg) {
usage();
} else if ((!strcmp(argv[i],"--cluster-only-masters"))) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY;
} else if ((!strcmp(argv[i],"--cluster-only-replicas"))) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY;
} else if (!strcmp(argv[i],"--cluster-replicas") && !lastarg) {
config.cluster_manager_command.replicas = atoi(argv[++i]);
} else if (!strcmp(argv[i],"--cluster-master-id") && !lastarg) {
@@ -1435,6 +1452,8 @@ static int parseOptions(int argc, char **argv) {
printf("keydb-cli %s\n", version);
sdsfree(version);
exit(0);
} else if (!strcmp(argv[i],"--no-motd")) {
config.disable_motd = 1;
} else if (!strcmp(argv[i],"-3")) {
config.resp3 = 1;
} else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-') {
@@ -1480,6 +1499,11 @@ static void parseEnv() {
if (auth != NULL && config.auth == NULL) {
config.auth = auth;
}
char *cluster_yes = getenv(REDIS_CLI_CLUSTER_YES_ENV);
if (cluster_yes != NULL && !strcmp(cluster_yes, "1")) {
config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_YES;
}
}
static sds readArgFromStdin(void) {
@@ -1511,7 +1535,7 @@ static void usage(void) {
" -a <password> Password to use when connecting to the server.\n"
" You can also use the " REDIS_CLI_AUTH_ENV " environment\n"
" variable to pass this password more safely\n"
" (if both are used, this argument takes predecence).\n"
" (if both are used, this argument takes precedence).\n"
" --user <username> Used to send ACL style 'AUTH username pass'. Needs -a.\n"
" --pass <password> Alias of -a for consistency with the new --user option.\n"
" --askpass Force user to input password with mask from STDIN.\n"
@@ -1524,7 +1548,8 @@ static void usage(void) {
" -n <db> Database number.\n"
" -3 Start session in RESP3 protocol mode.\n"
" -x Read last argument from STDIN.\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n"
" -d <delimiter> Delimiter between response bulks for raw formatting (default: \\n).\n"
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
#ifdef USE_OPENSSL
" --tls Establish a secure TLS connection.\n"
@@ -1570,7 +1595,8 @@ static void usage(void) {
" --hotkeys Sample Redis keys looking for hot keys.\n"
" only works when maxmemory-policy is *lfu.\n"
" --scan List all keys using the SCAN command.\n"
" --pattern <pat> Useful with --scan to specify a SCAN pattern.\n"
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys\n"
" options (default: *).\n"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
" The test will run for the specified amount of seconds.\n"
" --eval <file> Send an EVAL command using the Lua script at <file>.\n"
@@ -1609,10 +1635,10 @@ static void usage(void) {
exit(1);
}
int confirmWithYes(const char *msg, int force) {
int confirmWithYes(const char *msg, int ignore_force) {
/* if force is true and --cluster-yes option is on,
* do not prompt for an answer */
if (force &&
if (!ignore_force &&
(config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_YES)) {
return 1;
}
@@ -1801,6 +1827,7 @@ static void repl(void) {
if (argv == NULL) {
printf("Invalid argument(s)\n");
fflush(stdout);
linenoiseFree(line);
continue;
} else if (argc > 0) {
@@ -1863,14 +1890,15 @@ static void repl(void) {
exit(0);
}
static int noninteractive(int argc, char **argv) {
static int noninteractive(int argc, char ***argv) {
int retval = 0;
if (config.stdinarg) {
argv = zrealloc(argv, (argc+1)*sizeof(char*), MALLOC_LOCAL);
argv[argc] = readArgFromStdin();
retval = issueCommand(argc+1, argv);
*argv = zrealloc(*argv, (argc+1)*sizeof(char*), MALLOC_LOCAL);
(*argv)[argc] = readArgFromStdin();
retval = issueCommand(argc+1, *argv);
sdsfree((*argv)[argc]);
} else {
retval = issueCommand(argc, argv);
retval = issueCommand(argc, *argv);
}
return retval;
}
@@ -1938,7 +1966,7 @@ static int evalMode(int argc, char **argv) {
argv2[2] = sdscatprintf(sdsempty(),"%d",keys);
/* Call it */
int eval_ldb = config.eval_ldb; /* Save it, may be reverteed. */
int eval_ldb = config.eval_ldb; /* Save it, may be reverted. */
retval = issueCommand(argc+3-got_comma, argv2);
if (eval_ldb) {
if (!config.eval_ldb) {
@@ -2045,7 +2073,7 @@ clusterManagerCommandDef clusterManagerCommands[] = {
"new_host:new_port existing_host:existing_port", "slave,master-id <arg>"},
{"del-node", clusterManagerCommandDeleteNode, 2, "host:port node_id",NULL},
{"call", clusterManagerCommandCall, -2,
"host:port command arg arg .. arg", NULL},
"host:port command arg arg .. arg", "only-masters,only-replicas"},
{"set-timeout", clusterManagerCommandSetTimeout, 2,
"host:port milliseconds", NULL},
{"import", clusterManagerCommandImport, 1, "host:port",
@@ -3037,6 +3065,7 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
size_t *argv_len = NULL;
int c = (replace ? 8 : 7);
if (config.auth) c += 2;
if (config.user) c += 1;
size_t argc = c + reply->elements;
size_t i, offset = 6; // Keys Offset
argv = zcalloc(argc * sizeof(char *), MALLOC_LOCAL);
@@ -3063,12 +3092,24 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
offset++;
}
if (config.auth) {
argv[offset] = "AUTH";
argv_len[offset] = 4;
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
if (config.user) {
argv[offset] = "AUTH2";
argv_len[offset] = 5;
offset++;
argv[offset] = config.user;
argv_len[offset] = strlen(config.user);
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
} else {
argv[offset] = "AUTH";
argv_len[offset] = 4;
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
}
}
argv[offset] = "KEYS";
argv_len[offset] = 4;
@@ -4416,8 +4457,6 @@ static void clusterManagerMode(clusterManagerCommandProc *proc) {
exit(0);
cluster_manager_err:
freeClusterManager();
sdsfree(config.hostip);
sdsfree(config.mb_delim);
exit(1);
}
@@ -4595,8 +4634,8 @@ assign_replicas:
}
clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);
clusterManagerShowNodes();
int force = 1;
if (confirmWithYes("Can I set the above configuration?", force)) {
int ignore_force = 0;
if (confirmWithYes("Can I set the above configuration?", ignore_force)) {
listRewind(cluster_manager.nodes, &li);
while ((ln = listNext(&li)) != NULL) {
clusterManagerNode *node = ln->value;
@@ -5487,6 +5526,10 @@ static int clusterManagerCommandCall(int argc, char **argv) {
listRewind(cluster_manager.nodes, &li);
while ((ln = listNext(&li)) != NULL) {
clusterManagerNode *n = ln->value;
if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY)
&& (n->replicate != NULL)) continue; // continue if node is slave
if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY)
&& (n->replicate == NULL)) continue; // continue if node is master
if (!n->context && !clusterManagerNodeConnect(n)) continue;
redisReply *reply = NULL;
redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);
@@ -5710,13 +5753,13 @@ struct distsamples {
* samples greater than the previous one, and is also the stop sentinel.
*
* "tot' is the total number of samples in the different buckets, so it
* is the SUM(samples[i].conut) for i to 0 up to the max sample.
* is the SUM(samples[i].count) for i to 0 up to the max sample.
*
* As a side effect the function sets all the buckets count to 0. */
void showLatencyDistSamples(struct distsamples *samples, long long tot) {
int j;
/* We convert samples into a index inside the palette
/* We convert samples into an index inside the palette
* proportional to the percentage a given bucket represents.
* This way intensity of the different parts of the spectrum
* don't change relative to the number of requests, which avoids to
@@ -5856,10 +5899,84 @@ void sendCapa() {
sendReplconf("capa", "eof");
}
/* Wrapper around hiredis to allow arbitrary reads and writes.
*
* We piggybacks on top of hiredis to achieve transparent TLS support,
* and use its internal buffers so it can co-exist with commands
* previously/later issued on the connection.
*
* Interface is close to enough to read()/write() so things should mostly
* work transparently.
*/
/* Write a raw buffer through a redisContext. If we already have something
* in the buffer (leftovers from hiredis operations) it will be written
* as well.
*/
static ssize_t writeConn(redisContext *c, const char *buf, size_t buf_len)
{
int done = 0;
/* Append data to buffer which is *usually* expected to be empty
* but we don't assume that, and write.
*/
c->obuf = sdscatlen(c->obuf, buf, buf_len);
if (redisBufferWrite(c, &done) == REDIS_ERR) {
if (!(c->flags & REDIS_BLOCK))
errno = EAGAIN;
/* On error, we assume nothing was written and we roll back the
* buffer to its original state.
*/
if (sdslen(c->obuf) > buf_len)
sdsrange(c->obuf, 0, -(buf_len+1));
else
sdsclear(c->obuf);
return -1;
}
/* If we're done, free up everything. We may have written more than
* buf_len (if c->obuf was not initially empty) but we don't have to
* tell.
*/
if (done) {
sdsclear(c->obuf);
return buf_len;
}
/* Write was successful but we have some leftovers which we should
* remove from the buffer.
*
* Do we still have data that was there prior to our buf? If so,
* restore buffer to it's original state and report no new data was
* writen.
*/
if (sdslen(c->obuf) > buf_len) {
sdsrange(c->obuf, 0, -(buf_len+1));
return 0;
}
/* At this point we're sure no prior data is left. We flush the buffer
* and report how much we've written.
*/
size_t left = sdslen(c->obuf);
sdsclear(c->obuf);
return buf_len - left;
}
/* Read raw bytes through a redisContext. The read operation is not greedy
* and may not fill the buffer entirely.
*/
static ssize_t readConn(redisContext *c, char *buf, size_t len)
{
return c->funcs->read(c, buf, len);
}
/* Sends SYNC and reads the number of bytes in the payload. Used both by
* slaveMode() and getRDB().
* returns 0 in case an EOF marker is used. */
unsigned long long sendSync(int fd, char *out_eof) {
unsigned long long sendSync(redisContext *c, char *out_eof) {
/* To start we need to send the SYNC command and return the payload.
* The hiredis client lib does not understand this part of the protocol
* and we don't want to mess with its buffers, so everything is performed
@@ -5868,7 +5985,7 @@ unsigned long long sendSync(int fd, char *out_eof) {
ssize_t nread;
/* Send the SYNC command. */
if (write(fd,"SYNC\r\n",6) != 6) {
if (writeConn(c, "SYNC\r\n", 6) != 6) {
fprintf(stderr,"Error writing to master\n");
exit(1);
}
@@ -5876,7 +5993,7 @@ unsigned long long sendSync(int fd, char *out_eof) {
/* Read $<payload>\r\n, making sure to read just up to "\n" */
p = buf;
while(1) {
nread = read(fd,p,1);
nread = readConn(c,p,1);
if (nread <= 0) {
fprintf(stderr,"Error reading bulk length while SYNCing\n");
exit(1);
@@ -5897,11 +6014,10 @@ unsigned long long sendSync(int fd, char *out_eof) {
}
static void slaveMode(void) {
int fd = context->fd;
static char eofmark[RDB_EOF_MARK_SIZE];
static char lastbytes[RDB_EOF_MARK_SIZE];
static int usemark = 0;
unsigned long long payload = sendSync(fd, eofmark);
unsigned long long payload = sendSync(context,eofmark);
char buf[1024];
int original_output = config.output;
@@ -5921,7 +6037,7 @@ static void slaveMode(void) {
while(payload) {
ssize_t nread;
nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
nread = readConn(context,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"Error reading RDB payload while SYNCing\n");
exit(1);
@@ -5964,14 +6080,15 @@ static void slaveMode(void) {
/* This function implements --rdb, so it uses the replication protocol in order
* to fetch the RDB file from a remote server. */
static void getRDB(clusterManagerNode *node) {
int s, fd;
int fd;
redisContext *s;
char *filename;
if (node != NULL) {
assert(node->context);
s = node->context->fd;
s = node->context;
filename = clusterManagerGetNodeRDBFilename(node);
} else {
s = context->fd;
s = context;
filename = config.rdb_filename;
}
static char eofmark[RDB_EOF_MARK_SIZE];
@@ -6006,7 +6123,7 @@ static void getRDB(clusterManagerNode *node) {
while(payload) {
ssize_t nread, nwritten;
nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
nread = readConn(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
if (nread <= 0) {
fprintf(stderr,"I/O Error reading RDB payload from socket\n");
exit(1);
@@ -6040,7 +6157,9 @@ static void getRDB(clusterManagerNode *node) {
} else {
fprintf(stderr,"Transfer finished with success.\n");
}
close(s); /* Close the file descriptor ASAP as fsync() may take time. */
redisFree(s); /* Close the connection ASAP as fsync() may take time. */
if (node)
node->context = NULL;
fsync(fd);
close(fd);
fprintf(stderr,"Transfer finished with success.\n");
@@ -6057,11 +6176,9 @@ static void getRDB(clusterManagerNode *node) {
#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
static void pipeMode(void) {
int fd = context->fd;
long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
char ibuf[1024*16], obuf[1024*16]; /* Input and output buffers */
char obuf[1024*16]; /* Output buffer */
char aneterr[ANET_ERR_LEN];
redisReader *reader = redisReaderCreate();
redisReply *reply;
int eof = 0; /* True once we consumed all the standard input. */
int done = 0;
@@ -6071,47 +6188,38 @@ static void pipeMode(void) {
srand(time(NULL));
/* Use non blocking I/O. */
if (anetNonBlock(aneterr,fd) == ANET_ERR) {
if (anetNonBlock(aneterr,context->fd) == ANET_ERR) {
fprintf(stderr, "Can't set the socket in non blocking mode: %s\n",
aneterr);
exit(1);
}
context->flags &= ~REDIS_BLOCK;
/* Transfer raw protocol and read replies from the server at the same
* time. */
while(!done) {
int mask = AE_READABLE;
if (!eof || obuf_len != 0) mask |= AE_WRITABLE;
mask = aeWait(fd,mask,1000);
mask = aeWait(context->fd,mask,1000);
/* Handle the readable state: we can read replies from the server. */
if (mask & AE_READABLE) {
ssize_t nread;
int read_error = 0;
/* Read from socket and feed the hiredis reader. */
do {
nread = read(fd,ibuf,sizeof(ibuf));
if (nread == -1 && errno != EAGAIN && errno != EINTR) {
fprintf(stderr, "Error reading from the server: %s\n",
strerror(errno));
if (!read_error && redisBufferRead(context) == REDIS_ERR) {
read_error = 1;
break;
}
if (nread > 0) {
redisReaderFeed(reader,ibuf,nread);
last_read_time = time(NULL);
}
} while(nread > 0);
/* Consume replies. */
do {
if (redisReaderGetReply(reader,(void**)&reply) == REDIS_ERR) {
reply = NULL;
if (redisGetReply(context, (void **) &reply) == REDIS_ERR) {
fprintf(stderr, "Error reading replies from server\n");
exit(1);
}
if (reply) {
last_read_time = time(NULL);
if (reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr,"%s\n", reply->str);
errors++;
@@ -6144,7 +6252,7 @@ static void pipeMode(void) {
while(1) {
/* Transfer current buffer to server. */
if (obuf_len != 0) {
ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);
ssize_t nwritten = writeConn(context,obuf+obuf_pos,obuf_len);
if (nwritten == -1) {
if (errno != EAGAIN && errno != EINTR) {
@@ -6160,6 +6268,10 @@ static void pipeMode(void) {
loop_nwritten += nwritten;
if (obuf_len != 0) break; /* Can't accept more data. */
}
if (context->err) {
fprintf(stderr, "Server I/O Error: %s\n", context->errstr);
exit(1);
}
/* If buffer is empty, load from stdin. */
if (obuf_len == 0 && !eof) {
ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));
@@ -6210,7 +6322,6 @@ static void pipeMode(void) {
break;
}
}
redisReaderFree(reader);
printf("errors: %lld, replies: %lld\n", errors, replies);
if (errors)
exit(1);
@@ -6223,7 +6334,13 @@ static void pipeMode(void) {
*--------------------------------------------------------------------------- */
redisReply *sendScan(unsigned long long *it) {
redisReply *reply = redisCommand(context, "SCAN %llu", *it);
redisReply *reply;
if (config.pattern)
reply = redisCommand(context,"SCAN %llu MATCH %s",
*it,config.pattern);
else
reply = redisCommand(context,"SCAN %llu",*it);
/* Handle any error conditions */
if(reply == NULL) {
@@ -6298,15 +6415,21 @@ void getKeySizes(redisReply *keys, typeinfo **types,
if(!types[i] || (!types[i]->sizecmd && !memkeys))
continue;
if (!memkeys)
redisAppendCommand(context, "%s %s",
types[i]->sizecmd, keys->element[i]->str);
else if (memkeys_samples==0)
redisAppendCommand(context, "%s %s %s",
"MEMORY", "USAGE", keys->element[i]->str);
else
redisAppendCommand(context, "%s %s %s SAMPLES %u",
"MEMORY", "USAGE", keys->element[i]->str, memkeys_samples);
if (!memkeys) {
const char* argv[] = {types[i]->sizecmd, keys->element[i]->str};
size_t lens[] = {strlen(types[i]->sizecmd), keys->element[i]->len};
redisAppendCommandArgv(context, 2, argv, lens);
} else if (memkeys_samples==0) {
const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str};
size_t lens[] = {6, 5, keys->element[i]->len};
redisAppendCommandArgv(context, 3, argv, lens);
} else {
sds samplesstr = sdsfromlonglong(memkeys_samples);
const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str, "SAMPLES", samplesstr};
size_t lens[] = {6, 5, keys->element[i]->len, 7, sdslen(samplesstr)};
redisAppendCommandArgv(context, 5, argv, lens);
sdsfree(samplesstr);
}
}
/* Retrieve sizes */
@@ -6344,21 +6467,27 @@ static void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {
/* Pipeline OBJECT freq commands */
for(i=0;i<keys->elements;i++) {
redisAppendCommand(context, "OBJECT freq %s", keys->element[i]->str);
const char* argv[] = {"OBJECT", "FREQ", keys->element[i]->str};
size_t lens[] = {6, 4, keys->element[i]->len};
redisAppendCommandArgv(context, 3, argv, lens);
}
/* Retrieve freqs */
for(i=0;i<keys->elements;i++) {
if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
sds keyname = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
fprintf(stderr, "Error getting freq for key '%s' (%d: %s)\n",
keys->element[i]->str, context->err, context->errstr);
keyname, context->err, context->errstr);
sdsfree(keyname);
exit(1);
} else if(reply->type != REDIS_REPLY_INTEGER) {
if(reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr, "Error: %s\n", reply->str);
exit(1);
} else {
fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keys->element[i]->str);
sds keyname = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keyname);
sdsfree(keyname);
freqs[i] = 0;
}
} else {
@@ -6429,10 +6558,10 @@ static void findHotKeys(void) {
memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);
}
counters[k] = freqs[i];
hotkeys[k] = sdsnew(keys->element[i]->str);
hotkeys[k] = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
printf(
"[%05.2f%%] Hot key '%s' found so far with counter %llu\n",
pct, keys->element[i]->str, freqs[i]);
pct, hotkeys[k], freqs[i]);
}
/* Sleep if we've been directed to do so */
@@ -6736,7 +6865,7 @@ static void LRUTestMode(void) {
* Intrisic latency mode.
*
* Measure max latency of a running process that does not result from
* syscalls. Basically this software should provide an hint about how much
* syscalls. Basically this software should provide a hint about how much
* time the kernel leaves the process without a chance to run.
*--------------------------------------------------------------------------- */
@@ -6884,6 +7013,7 @@ int main(int argc, char **argv) {
else
config.output = OUTPUT_STANDARD;
config.mb_delim = sdsnew("\n");
config.cmd_delim = sdsnew("\n");
firstarg = parseOptions(argc,argv);
argc -= firstarg;
@@ -6907,8 +7037,6 @@ int main(int argc, char **argv) {
if (CLUSTER_MANAGER_MODE()) {
clusterManagerCommandProc *proc = validateClusterManagerCommand();
if (!proc) {
sdsfree(config.hostip);
sdsfree(config.mb_delim);
exit(1);
}
clusterManagerMode(proc);
@@ -6995,8 +7123,9 @@ 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(1 /* cache */);
if (config.output == OUTPUT_STANDARD && !config.disable_motd) {
/*enable_motd=1 will retrieve the message of today using CURL*/
char *szMotd = fetchMOTD(1 /* cache */, 1 /* enable_motd */);
if (szMotd != NULL) {
printf("Message of the day:\n %s\n", szMotd);
sdsfree(szMotd);
@@ -7018,7 +7147,11 @@ int main(int argc, char **argv) {
if (config.eval) {
return evalMode(argc,argv);
} else {
return noninteractive(argc,convertToSds(argc,argv));
sds *sdsArgs = convertToSds(argc,argv);
int rval = noninteractive(argc,&sdsArgs);
for (int i = 0; i < argc; ++i)
sdsfree(sdsArgs[i]);
zfree(sdsArgs);
return rval;
}
}
+5
View File
@@ -16,6 +16,7 @@ extern "C" {
#define REDIS_CLI_RCFILE_ENV "REDISCLI_RCFILE"
#define REDIS_CLI_RCFILE_DEFAULT ".redisclirc"
#define REDIS_CLI_AUTH_ENV "REDISCLI_AUTH"
#define REDIS_CLI_CLUSTER_YES_ENV "REDISCLI_CLUSTER_YES"
#define CLUSTER_MANAGER_SLOTS 16384
#define CLUSTER_MANAGER_MIGRATE_TIMEOUT 60000
@@ -67,6 +68,8 @@ extern "C" {
#define CLUSTER_MANAGER_CMD_FLAG_COLOR 1 << 8
#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS 1 << 9
#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS 1 << 10
#define CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY 1 << 11
#define CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY 1 << 12
#define CLUSTER_MANAGER_OPT_GETFRIENDS 1 << 0
#define CLUSTER_MANAGER_OPT_COLD 1 << 1
@@ -168,6 +171,7 @@ extern struct config {
char *user;
int output; /* output mode, see OUTPUT_* defines */
sds mb_delim;
sds cmd_delim;
char prompt[128];
char *eval;
int eval_ldb;
@@ -179,6 +183,7 @@ extern struct config {
clusterManagerCommand cluster_manager_command;
int no_auth_warning;
int resp3;
int disable_motd;
} config;
struct clusterManager {
+335 -243
View File
@@ -116,6 +116,13 @@ extern "C" {
#define REDISMODULE_CTX_FLAGS_ACTIVE_CHILD (1<<18)
/* The next EXEC will fail due to dirty CAS (touched keys). */
#define REDISMODULE_CTX_FLAGS_MULTI_DIRTY (1<<19)
/* Redis is currently running inside background child process. */
#define REDISMODULE_CTX_FLAGS_IS_CHILD (1<<20)
/* Next context flag, must be updated when adding new flags above!
This flag should not be used directly by the module.
* Use RedisModule_GetContextFlagsAll instead. */
#define _REDISMODULE_CTX_FLAGS_NEXT (1<<21)
/* Keyspace changes notification classes. Every class is associated with a
* character for configuration purposes.
@@ -132,8 +139,14 @@ extern "C" {
#define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */
#define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */
#define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */
#define REDISMODULE_NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
/* Next notification flag, must be updated when adding new flags above!
This flag should not be used directly by the module.
* Use RedisModule_GetKeyspaceNotificationFlagsAll instead. */
#define _REDISMODULE_NOTIFY_NEXT (1<<13)
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */
/* A special pointer that we can use between the core and the module to signal
* field deletion, and that is impossible to be a valid pointer. */
@@ -182,7 +195,9 @@ typedef uint64_t RedisModuleTimerID;
* are modified from the user's sperspective, to invalidate WATCH. */
#define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)
/* Server events definitions. */
/* Server events definitions.
* Those flags should not be used directly by the module, instead
* the module should use RedisModuleEvent_* variables */
#define REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED 0
#define REDISMODULE_EVENT_PERSISTENCE 1
#define REDISMODULE_EVENT_FLUSHDB 2
@@ -194,6 +209,10 @@ typedef uint64_t RedisModuleTimerID;
#define REDISMODULE_EVENT_CRON_LOOP 8
#define REDISMODULE_EVENT_MODULE_CHANGE 9
#define REDISMODULE_EVENT_LOADING_PROGRESS 10
#define REDISMODULE_EVENT_SWAPDB 11
/* Next event flag, should be updated if a new event added. */
#define _REDISMODULE_EVENT_NEXT 12
typedef struct RedisModuleEvent {
uint64_t id; /* REDISMODULE_EVENT_... defines. */
@@ -247,6 +266,10 @@ static const RedisModuleEvent
RedisModuleEvent_LoadingProgress = {
REDISMODULE_EVENT_LOADING_PROGRESS,
1
},
RedisModuleEvent_SwapDB = {
REDISMODULE_EVENT_SWAPDB,
1
};
/* Those are values that are used for the 'subevent' callback argument. */
@@ -255,33 +278,47 @@ static const RedisModuleEvent
#define REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START 2
#define REDISMODULE_SUBEVENT_PERSISTENCE_ENDED 3
#define REDISMODULE_SUBEVENT_PERSISTENCE_FAILED 4
#define _REDISMODULE_SUBEVENT_PERSISTENCE_NEXT 5
#define REDISMODULE_SUBEVENT_LOADING_RDB_START 0
#define REDISMODULE_SUBEVENT_LOADING_AOF_START 1
#define REDISMODULE_SUBEVENT_LOADING_REPL_START 2
#define REDISMODULE_SUBEVENT_LOADING_ENDED 3
#define REDISMODULE_SUBEVENT_LOADING_FAILED 4
#define _REDISMODULE_SUBEVENT_LOADING_NEXT 5
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1
#define _REDISMODULE_SUBEVENT_CLIENT_CHANGE_NEXT 2
#define REDISMODULE_SUBEVENT_MASTER_LINK_UP 0
#define REDISMODULE_SUBEVENT_MASTER_LINK_DOWN 1
#define _REDISMODULE_SUBEVENT_MASTER_NEXT 2
#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE 0
#define REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE 1
#define _REDISMODULE_SUBEVENT_REPLICA_CHANGE_NEXT 2
#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER 0
#define REDISMODULE_EVENT_REPLROLECHANGED_NOW_REPLICA 1
#define _REDISMODULE_EVENT_REPLROLECHANGED_NEXT 2
#define REDISMODULE_SUBEVENT_FLUSHDB_START 0
#define REDISMODULE_SUBEVENT_FLUSHDB_END 1
#define _REDISMODULE_SUBEVENT_FLUSHDB_NEXT 2
#define REDISMODULE_SUBEVENT_MODULE_LOADED 0
#define REDISMODULE_SUBEVENT_MODULE_UNLOADED 1
#define _REDISMODULE_SUBEVENT_MODULE_NEXT 2
#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB 0
#define REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF 1
#define _REDISMODULE_SUBEVENT_LOADING_PROGRESS_NEXT 2
#define _REDISMODULE_SUBEVENT_SHUTDOWN_NEXT 0
#define _REDISMODULE_SUBEVENT_CRON_LOOP_NEXT 0
#define _REDISMODULE_SUBEVENT_SWAPDB_NEXT 0
/* RedisModuleClientInfo flags. */
#define REDISMODULE_CLIENTINFO_FLAG_SSL (1<<0)
@@ -378,12 +415,48 @@ typedef struct RedisModuleLoadingProgressInfo {
#define RedisModuleLoadingProgress RedisModuleLoadingProgressV1
#define REDISMODULE_SWAPDBINFO_VERSION 1
typedef struct RedisModuleSwapDbInfo {
uint64_t version; /* Not used since this structure is never passed
from the module to the core right now. Here
for future compatibility. */
int32_t dbnum_first; /* Swap Db first dbnum */
int32_t dbnum_second; /* Swap Db second dbnum */
} RedisModuleSwapDbInfoV1;
#define RedisModuleSwapDbInfo RedisModuleSwapDbInfoV1
/* ------------------------- End of common defines ------------------------ */
#ifndef REDISMODULE_CORE
typedef long long mstime_t;
/* Macro definitions specific to individual compilers */
#ifndef REDISMODULE_ATTR_UNUSED
# ifdef __GNUC__
# define REDISMODULE_ATTR_UNUSED __attribute__((unused))
# else
# define REDISMODULE_ATTR_UNUSED
# endif
#endif
#ifndef REDISMODULE_ATTR_PRINTF
# ifdef __GNUC__
# define REDISMODULE_ATTR_PRINTF(idx,cnt) __attribute__((format(printf,idx,cnt)))
# else
# define REDISMODULE_ATTR_PRINTF(idx,cnt)
# endif
#endif
#ifndef REDISMODULE_ATTR_COMMON
# if defined(__GNUC__) && !defined(__clang__)
# define REDISMODULE_ATTR_COMMON __attribute__((__common__))
# else
# define REDISMODULE_ATTR_COMMON
# endif
#endif
/* Incomplete structures for compiler checks but opaque access. */
typedef struct RedisModuleCtx RedisModuleCtx;
typedef struct RedisModuleKey RedisModuleKey;
@@ -440,256 +513,264 @@ typedef struct RedisModuleTypeMethods {
#define REDISMODULE_GET_API(name) \
RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name))
#define REDISMODULE_API_FUNC(x) (*x)
void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes);
void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes);
void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr);
void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size);
char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str);
int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *);
int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);
void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver);
int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name);
int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll);
int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid);
void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);
void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp);
int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp);
size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp);
int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where);
RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len);
void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply);
int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply);
long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply);
size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply);
RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);
#ifdef __GNUC__
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#else
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);
/* Default API declaration prefix (not 'extern' for backwards compatibility) */
#ifndef REDISMODULE_API
#define REDISMODULE_API
#endif
void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);
const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply);
int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll);
int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d);
int REDISMODULE_API_FUNC(RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d);
void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx);
const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply);
int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str);
char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode);
int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen);
mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire);
void REDISMODULE_API_FUNC(RedisModule_ResetDataset)(int restart_aof, int async);
unsigned long long REDISMODULE_API_FUNC(RedisModule_DbSize)(RedisModuleCtx *ctx);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_RandomKey)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr);
int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore);
int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score);
int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted);
void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score);
int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...);
int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...);
int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos);
unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_GetClientInfoById)(void *ci, uint64_t id);
int REDISMODULE_API_FUNC(RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message);
int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_AvoidReplicaTraffic)();
void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes);
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods);
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value);
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);
void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
int REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options);
int REDISMODULE_API_FUNC(RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname);
void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);
void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s);
void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io);
char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr);
void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value);
double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value);
long double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);
void *REDISMODULE_API_FUNC(RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt);
#ifdef __GNUC__
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
#else
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);
/* Default API declaration suffix (compiler attributes) */
#ifndef REDISMODULE_ATTR
#define REDISMODULE_ATTR REDISMODULE_ATTR_COMMON
#endif
void REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);
void REDISMODULE_API_FUNC(RedisModule_LatencyAddSample)(const char *event, mstime_t latency);
int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);
void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str);
int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);
RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io);
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key);
long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);
void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);
void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);
void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md);
RedisModuleDict *REDISMODULE_API_FUNC(RedisModule_CreateDict)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d);
uint64_t REDISMODULE_API_FUNC(RedisModule_DictSize)(RedisModuleDict *d);
int REDISMODULE_API_FUNC(RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);
int REDISMODULE_API_FUNC(RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);
int REDISMODULE_API_FUNC(RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr);
int REDISMODULE_API_FUNC(RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr);
void *REDISMODULE_API_FUNC(RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey);
void *REDISMODULE_API_FUNC(RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey);
int REDISMODULE_API_FUNC(RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval);
int REDISMODULE_API_FUNC(RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval);
RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen);
RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key);
void REDISMODULE_API_FUNC(RedisModule_DictIteratorStop)(RedisModuleDictIter *di);
int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);
int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);
void *REDISMODULE_API_FUNC(RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);
void *REDISMODULE_API_FUNC(RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);
int REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);
int REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);
int REDISMODULE_API_FUNC(RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);
int REDISMODULE_API_FUNC(RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name);
int REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name);
int REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value);
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value);
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value);
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value);
RedisModuleServerInfoData *REDISMODULE_API_FUNC(RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section);
void REDISMODULE_API_FUNC(RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field);
const char *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field);
long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);
unsigned long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);
double REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err);
int REDISMODULE_API_FUNC(RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback);
int REDISMODULE_API_FUNC(RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle);
int REDISMODULE_API_FUNC(RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle);
int REDISMODULE_API_FUNC(RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq);
int REDISMODULE_API_FUNC(RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq);
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata);
void REDISMODULE_API_FUNC(RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx);
RedisModuleScanCursor *REDISMODULE_API_FUNC(RedisModule_ScanCursorCreate)();
void REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor);
void REDISMODULE_API_FUNC(RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor);
int REDISMODULE_API_FUNC(RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata);
int REDISMODULE_API_FUNC(RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata);
REDISMODULE_API void * (*RedisModule_Alloc)(size_t bytes) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_Realloc)(void *ptr, size_t bytes) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_Free)(void *ptr) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_Calloc)(size_t nmemb, size_t size) REDISMODULE_ATTR;
REDISMODULE_API char * (*RedisModule_Strdup)(const char *str) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetApi)(const char *, void *) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsModuleNameBusy)(const char *name) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_WrongArity)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_ListPop)(RedisModuleKey *key, int where) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCallReply * (*RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CallReplyType)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API long long (*RedisModule_CallReplyInteger)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR_PRINTF(2,3) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithNull)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_AutoMemory)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DeleteKey)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_UnlinkKey)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API char * (*RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen) REDISMODULE_ATTR;
REDISMODULE_API mstime_t (*RedisModule_GetExpire)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ResetDataset)(int restart_aof, int async) REDISMODULE_ATTR;
REDISMODULE_API unsigned long long (*RedisModule_DbSize)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_RandomKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ZsetRangeStop)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetRangeNext)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetRangePrev)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ZsetRangeEndReached)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_HashSet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_HashGet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR;
REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)() REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleType * (*RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleType * (*RedisModule_ModuleTypeGetType)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_ModuleTypeGetValue)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsIOError)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value) REDISMODULE_ATTR;
REDISMODULE_API uint64_t (*RedisModule_LoadUnsigned)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value) REDISMODULE_ATTR;
REDISMODULE_API int64_t (*RedisModule_LoadSigned)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_LoadString)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API char * (*RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveDouble)(RedisModuleIO *io, double value) REDISMODULE_ATTR;
REDISMODULE_API double (*RedisModule_LoadDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveFloat)(RedisModuleIO *io, float value) REDISMODULE_ATTR;
REDISMODULE_API float (*RedisModule_LoadFloat)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value) REDISMODULE_ATTR;
REDISMODULE_API long double (*RedisModule_LoadLongDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
REDISMODULE_API void (*RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
REDISMODULE_API void (*RedisModule__Assert)(const char *estr, const char *file, int line) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_LatencyAddSample)(const char *event, mstime_t latency) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_HoldString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetContextFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key) REDISMODULE_ATTR;
REDISMODULE_API long long (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_DigestEndSequence)(RedisModuleDigest *md) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleDict * (*RedisModule_CreateDict)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d) REDISMODULE_ATTR;
REDISMODULE_API uint64_t (*RedisModule_DictSize)(RedisModuleDict *d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_DictIteratorStop)(RedisModuleDictIter *di) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleServerInfoData * (*RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
REDISMODULE_API long long (*RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
REDISMODULE_API unsigned long long (*RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
REDISMODULE_API double (*RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)() REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetContextFlagsAll)() REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetKeyspaceNotificationFlagsAll)() REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsSubEventSupported)(RedisModuleEvent event, uint64_t subevent) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetServerVersion)() REDISMODULE_ATTR;
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
#define REDISMODULE_EXPERIMENTAL_API_VERSION 3
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms);
int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata);
int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx);
void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx);
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc);
RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc);
void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);
int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);
int REDISMODULE_API_FUNC(RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
int REDISMODULE_API_FUNC(RedisModule_GetNotifyKeyspaceEvents)();
int REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx);
void REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback);
int REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len);
int REDISMODULE_API_FUNC(RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags);
char **REDISMODULE_API_FUNC(RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes);
void REDISMODULE_API_FUNC(RedisModule_FreeClusterNodesList)(char **ids);
RedisModuleTimerID REDISMODULE_API_FUNC(RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data);
int REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data);
int REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);
const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);
size_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void);
void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len);
void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);
void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);
void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);
RedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags);
int REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx);
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos);
int REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data);
int REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);
int REDISMODULE_API_FUNC(RedisModule_KillForkChild)(int child_pid);
float REDISMODULE_API_FUNC(RedisModule_GetUsedMemoryRatio)();
size_t REDISMODULE_API_FUNC(RedisModule_MallocSize)(void* ptr);
RedisModuleUser *REDISMODULE_API_FUNC(RedisModule_CreateModuleUser)(const char *name);
void REDISMODULE_API_FUNC(RedisModule_FreeModuleUser)(RedisModuleUser *user);
int REDISMODULE_API_FUNC(RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl);
int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);
int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);
void REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetDetachedThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)() REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) REDISMODULE_ATTR;
REDISMODULE_API char ** (*RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeClusterNodesList)(char **ids) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleTimerID (*RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_GetMyClusterID)(void) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_GetClusterSize)(void) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_GetRandomBytes)(unsigned char *dst, size_t len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_GetRandomHexChars)(char *dst, size_t len) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleCommandFilter * (*RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx) REDISMODULE_ATTR;
REDISMODULE_API const RedisModuleString * (*RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KillForkChild)(int child_pid) REDISMODULE_ATTR;
REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)() REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_MallocSize)(void* ptr) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleUser * (*RedisModule_CreateModuleUser)(const char *name) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_FreeModuleUser)(RedisModuleUser *user) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_GetClientCertificate)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR;
#endif
#define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)
/* This is included inline inside each Redis module. */
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR_UNUSED;
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {
void *getapifuncptr = ((void**)ctx)[0];
RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;
@@ -811,6 +892,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(LatencyAddSample);
REDISMODULE_GET_API(StringAppendBuffer);
REDISMODULE_GET_API(RetainString);
REDISMODULE_GET_API(HoldString);
REDISMODULE_GET_API(StringCompare);
REDISMODULE_GET_API(GetContextFromIO);
REDISMODULE_GET_API(GetKeyNameFromIO);
@@ -872,11 +954,17 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(ScanCursorDestroy);
REDISMODULE_GET_API(Scan);
REDISMODULE_GET_API(ScanKey);
REDISMODULE_GET_API(GetContextFlagsAll);
REDISMODULE_GET_API(GetKeyspaceNotificationFlagsAll);
REDISMODULE_GET_API(IsSubEventSupported);
REDISMODULE_GET_API(GetServerVersion);
#ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API(GetThreadSafeContext);
REDISMODULE_GET_API(GetDetachedThreadSafeContext);
REDISMODULE_GET_API(FreeThreadSafeContext);
REDISMODULE_GET_API(ThreadSafeContextLock);
REDISMODULE_GET_API(ThreadSafeContextTryLock);
REDISMODULE_GET_API(ThreadSafeContextUnlock);
REDISMODULE_GET_API(BlockClient);
REDISMODULE_GET_API(UnblockClient);
@@ -923,6 +1011,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(DeauthenticateAndCloseClient);
REDISMODULE_GET_API(AuthenticateClientWithACLUser);
REDISMODULE_GET_API(AuthenticateClientWithUser);
REDISMODULE_GET_API(GetClientCertificate);
REDISMODULE_GET_API(GetCommandKeys);
#endif
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
@@ -932,6 +1022,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
#define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))
#define RMAPI_FUNC_SUPPORTED(func) (func != NULL)
#else
/* Things only defined for the modules core, not exported to modules
@@ -944,4 +1036,4 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
}
#endif
#endif /* REDISMOUDLE_H */
#endif /* REDISMODULE_H */
+304 -223
View File
@@ -160,16 +160,16 @@ client *replicaFromMaster(redisMaster *mi)
* the file deletion to the filesystem. This call removes the file in a
* background thread instead. We actually just do close() in the thread,
* by using the fact that if there is another instance of the same file open,
* the foreground unlink() will not really do anything, and deleting the
* file will only happen once the last reference is lost. */
* the foreground unlink() will only remove the fs name, and deleting the
* file's storage space will only happen once the last reference is lost. */
int bg_unlink(const char *filename) {
int fd = open(filename,O_RDONLY|O_NONBLOCK);
if (fd == -1) {
/* Can't open the file? Fall back to unlinking in the main thread. */
return unlink(filename);
} else {
/* The following unlink() will not do anything since file
* is still open. */
/* The following unlink() removes the name but doesn't free the
* file contents because a process still has it open. */
int retval = unlink(filename);
if (retval == -1) {
/* If we got an unlink error, we just return it, closing the
@@ -196,6 +196,10 @@ void createReplicationBacklog(void) {
* byte we have is the next byte that will be generated for the
* replication stream. */
g_pserver->repl_backlog_off = g_pserver->master_repl_offset+1;
/* Allow transmission to clients */
g_pserver->repl_batch_idxStart = 0;
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
}
/* This function is called when the user modifies the replication backlog
@@ -209,20 +213,44 @@ void resizeReplicationBacklog(long long newsize) {
newsize = CONFIG_REPL_BACKLOG_MIN_SIZE;
if (g_pserver->repl_backlog_size == newsize) return;
g_pserver->repl_backlog_size = newsize;
if (g_pserver->repl_backlog != NULL) {
/* What we actually do is to flush the old buffer and realloc a new
* empty one. It will refill with new data incrementally.
* The reason is that copying a few gigabytes adds latency and even
* worse often we need to alloc additional space before freeing the
* old buffer. */
zfree(g_pserver->repl_backlog);
g_pserver->repl_backlog = (char*)zmalloc(g_pserver->repl_backlog_size, MALLOC_LOCAL);
g_pserver->repl_backlog_histlen = 0;
g_pserver->repl_backlog_idx = 0;
/* Next byte we have is... the next since the buffer is empty. */
g_pserver->repl_backlog_off = g_pserver->master_repl_offset+1;
if (g_pserver->repl_batch_idxStart >= 0) {
// We need to keep critical data so we can't shrink less than the hot data in the buffer
newsize = std::max(newsize, g_pserver->master_repl_offset - g_pserver->repl_batch_offStart);
char *backlog = (char*)zmalloc(newsize);
g_pserver->repl_backlog_histlen = g_pserver->master_repl_offset - g_pserver->repl_batch_offStart;
if (g_pserver->repl_backlog_idx >= g_pserver->repl_batch_idxStart) {
auto cbActiveBacklog = g_pserver->repl_backlog_idx - g_pserver->repl_batch_idxStart;
memcpy(backlog, g_pserver->repl_backlog + g_pserver->repl_batch_idxStart, cbActiveBacklog);
serverAssert(g_pserver->repl_backlog_histlen == cbActiveBacklog);
} else {
auto cbPhase1 = g_pserver->repl_backlog_size - g_pserver->repl_batch_idxStart;
memcpy(backlog, g_pserver->repl_backlog + g_pserver->repl_batch_idxStart, cbPhase1);
memcpy(backlog + cbPhase1, g_pserver->repl_backlog, g_pserver->repl_backlog_idx);
auto cbActiveBacklog = cbPhase1 + g_pserver->repl_backlog_idx;
serverAssert(g_pserver->repl_backlog_histlen == cbActiveBacklog);
}
zfree(g_pserver->repl_backlog);
g_pserver->repl_backlog = backlog;
g_pserver->repl_backlog_idx = g_pserver->repl_backlog_histlen;
g_pserver->repl_batch_idxStart = 0;
} else {
zfree(g_pserver->repl_backlog);
g_pserver->repl_backlog = (char*)zmalloc(newsize);
g_pserver->repl_backlog_histlen = 0;
g_pserver->repl_backlog_idx = 0;
/* Next byte we have is... the next since the buffer is empty. */
g_pserver->repl_backlog_off = g_pserver->master_repl_offset+1;
}
}
g_pserver->repl_backlog_size = newsize;
}
void freeReplicationBacklog(void) {
@@ -247,6 +275,21 @@ void feedReplicationBacklog(const void *ptr, size_t len) {
serverAssert(GlobalLocksAcquired());
const unsigned char *p = (const unsigned char*)ptr;
if (g_pserver->repl_batch_idxStart >= 0) {
long long minimumsize = g_pserver->master_repl_offset + len - g_pserver->repl_batch_offStart+1;
if (minimumsize > g_pserver->repl_backlog_size) {
flushReplBacklogToClients();
minimumsize = g_pserver->master_repl_offset + len - g_pserver->repl_batch_offStart+1;
if (minimumsize > g_pserver->repl_backlog_size) {
// This is an emergency overflow, we better resize to fit
long long newsize = std::max(g_pserver->repl_backlog_size*2, minimumsize);
serverLog(LL_WARNING, "Replication backlog is too small, resizing to: %lld", newsize);
resizeReplicationBacklog(newsize);
}
}
}
g_pserver->master_repl_offset += len;
/* This is a circular buffer, so write as much data we can at every
@@ -315,7 +358,7 @@ void replicationFeedSlave(client *replica, int dictid, robj **argv, int argc, bo
if (g_pserver->repl_backlog && fSendRaw) feedReplicationBacklogWithObject(selectcmd);
/* Send it to slaves */
addReplyAsync(replica,selectcmd);
addReply(replica,selectcmd);
if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)
decrRefCount(selectcmd);
@@ -329,18 +372,18 @@ void replicationFeedSlave(client *replica, int dictid, robj **argv, int argc, bo
if (fSendRaw)
{
/* Add the multi bulk length. */
addReplyArrayLenAsync(replica,argc);
addReplyArrayLen(replica,argc);
/* Finally any additional argument that was not stored inside the
* static buffer if any (from j to argc). */
for (int j = 0; j < argc; j++)
addReplyBulkAsync(replica,argv[j]);
addReplyBulk(replica,argv[j]);
}
else
{
struct redisCommand *cmd = lookupCommand(szFromObj(argv[0]));
sds buf = catCommandForAofAndActiveReplication(sdsempty(), cmd, argv, argc);
addReplyProtoAsync(replica, buf, sdslen(buf));
addReplyProto(replica, buf, sdslen(buf));
sdsfree(buf);
}
}
@@ -369,13 +412,11 @@ static int writeProtoNum(char *dst, const size_t cchdst, long long num)
* as well. This function is used if the instance is a master: we use
* the commands received by our clients in order to create the replication
* stream. Instead if the instance is a replica and has sub-slaves attached,
* we use replicationFeedSlavesFromMaster() */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
listNode *ln, *lnReply;
listIter li, liReply;
int j, len;
* we use replicationFeedSlavesFromMasterStream() */
void replicationFeedSlavesCore(list *slaves, int dictid, robj **argv, int argc) {
int j;
serverAssert(GlobalLocksAcquired());
static client *fake = nullptr;
serverAssert(g_pserver->repl_batch_offStart >= 0);
if (dictid < 0)
dictid = 0; // this can happen if we send a PING before any real operation
@@ -394,58 +435,34 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
/* We can't have slaves attached and no backlog. */
serverAssert(!(listLength(slaves) != 0 && g_pserver->repl_backlog == NULL));
if (fake == nullptr)
{
fake = createClient(nullptr, serverTL - g_pserver->rgthreadvar);
fake->flags |= CLIENT_FORCE_REPLY;
}
bool fSendRaw = !g_pserver->fActiveReplica;
replicationFeedSlave(fake, dictid, argv, argc, fSendRaw); // Note: updates the repl log, keep above the repl update code below
/* Send SELECT command to every replica if needed. */
if (g_pserver->replicaseldb != dictid) {
char llstr[LONG_STR_SIZE];
robj *selectcmd;
long long cchbuf = fake->bufpos;
listRewind(fake->reply, &liReply);
while ((lnReply = listNext(&liReply)))
{
clientReplyBlock* reply = (clientReplyBlock*)listNodeValue(lnReply);
cchbuf += reply->used;
/* For a few DBs we have pre-computed SELECT command. */
if (dictid >= 0 && dictid < PROTO_SHARED_SELECT_CMDS) {
selectcmd = shared.select[dictid];
} else {
int dictid_len;
dictid_len = ll2string(llstr,sizeof(llstr),dictid);
selectcmd = createObject(OBJ_STRING,
sdscatprintf(sdsempty(),
"*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n",
dictid_len, llstr));
}
/* Add the SELECT command into the backlog. */
/* We don't do this for advanced replication because this will be done later when it adds the whole RREPLAY command */
if (g_pserver->repl_backlog && fSendRaw) feedReplicationBacklogWithObject(selectcmd);
if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)
decrRefCount(selectcmd);
}
serverAssert(argc > 0);
serverAssert(cchbuf > 0);
// The code below used to be: snprintf(proto, sizeof(proto), "*5\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf);
// but that was much too slow
static const char *protoRREPLAY = "*5\r\n$7\r\nRREPLAY\r\n$36\r\n00000000-0000-0000-0000-000000000000\r\n$";
char proto[1024];
int cchProto = 0;
if (!fSendRaw)
{
char uuid[37];
uuid_unparse(cserver.uuid, uuid);
cchProto = strlen(protoRREPLAY);
memcpy(proto, protoRREPLAY, strlen(protoRREPLAY));
memcpy(proto + 22, uuid, 36); // Note UUID_STR_LEN includes the \0 trailing byte which we don't want
cchProto += ll2string(proto + cchProto, sizeof(proto)-cchProto, cchbuf);
memcpy(proto + cchProto, "\r\n", 3);
cchProto += 2;
}
long long master_repl_offset_start = g_pserver->master_repl_offset;
char szDbNum[128];
int cchDbNum = 0;
if (!fSendRaw)
cchDbNum = writeProtoNum(szDbNum, sizeof(szDbNum), dictid);
char szMvcc[128];
int cchMvcc = 0;
incrementMvccTstamp(); // Always increment MVCC tstamp so we're consistent with active and normal replication
if (!fSendRaw)
cchMvcc = writeProtoNum(szMvcc, sizeof(szMvcc), getMvccTstamp());
g_pserver->replicaseldb = dictid;
/* Write the command to the replication backlog if any. */
if (g_pserver->repl_backlog)
@@ -456,10 +473,11 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
/* Add the multi bulk reply length. */
aux[0] = '*';
len = ll2string(aux+1,sizeof(aux)-1,argc);
aux[len+1] = '\r';
aux[len+2] = '\n';
feedReplicationBacklog(aux,len+3);
int multilen = ll2string(aux+1,sizeof(aux)-1,argc);
aux[multilen+1] = '\r';
aux[multilen+2] = '\n';
feedReplicationBacklog(aux,multilen+3);
for (j = 0; j < argc; j++) {
long objlen = stringObjectLen(argv[j]);
@@ -468,7 +486,7 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
* not just as a plain string, so create the $..CRLF payload len
* and add the final CRLF */
aux[0] = '$';
len = ll2string(aux+1,sizeof(aux)-1,objlen);
int len = ll2string(aux+1,sizeof(aux)-1,objlen);
aux[len+1] = '\r';
aux[len+2] = '\n';
feedReplicationBacklog(aux,len+3);
@@ -478,67 +496,57 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
}
else
{
feedReplicationBacklog(proto, cchProto);
feedReplicationBacklog(fake->buf, fake->bufpos);
listRewind(fake->reply, &liReply);
while ((lnReply = listNext(&liReply)))
char szDbNum[128];
int cchDbNum = 0;
if (!fSendRaw)
cchDbNum = writeProtoNum(szDbNum, sizeof(szDbNum), dictid);
char szMvcc[128];
int cchMvcc = 0;
incrementMvccTstamp(); // Always increment MVCC tstamp so we're consistent with active and normal replication
if (!fSendRaw)
cchMvcc = writeProtoNum(szMvcc, sizeof(szMvcc), getMvccTstamp());
//size_t cchlen = multilen+3;
struct redisCommand *cmd = lookupCommand(szFromObj(argv[0]));
sds buf = catCommandForAofAndActiveReplication(sdsempty(), cmd, argv, argc);
size_t cchlen = sdslen(buf);
// The code below used to be: snprintf(proto, sizeof(proto), "*5\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf);
// but that was much too slow
static const char *protoRREPLAY = "*5\r\n$7\r\nRREPLAY\r\n$36\r\n00000000-0000-0000-0000-000000000000\r\n$";
char proto[1024];
int cchProto = 0;
if (!fSendRaw)
{
clientReplyBlock* reply = (clientReplyBlock*)listNodeValue(lnReply);
feedReplicationBacklog(reply->buf(), reply->used);
char uuid[37];
uuid_unparse(cserver.uuid, uuid);
cchProto = strlen(protoRREPLAY);
memcpy(proto, protoRREPLAY, strlen(protoRREPLAY));
memcpy(proto + 22, uuid, 36); // Note UUID_STR_LEN includes the \0 trailing byte which we don't want
cchProto += ll2string(proto + cchProto, sizeof(proto)-cchProto, cchlen);
memcpy(proto + cchProto, "\r\n", 3);
cchProto += 2;
}
feedReplicationBacklog(proto, cchProto);
feedReplicationBacklog(buf, sdslen(buf));
const char *crlf = "\r\n";
feedReplicationBacklog(crlf, 2);
feedReplicationBacklog(szDbNum, cchDbNum);
feedReplicationBacklog(szMvcc, cchMvcc);
sdsfree(buf);
}
}
}
/* Write the command to every replica. */
listRewind(slaves,&li);
while((ln = listNext(&li))) {
client *replica = (client*)ln->value;
/* Don't feed slaves that are still waiting for BGSAVE to start */
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
if (replica->flags & CLIENT_CLOSE_ASAP) continue;
std::unique_lock<decltype(replica->lock)> lock(replica->lock, std::defer_lock);
// When writing to clients on other threads the global lock is sufficient provided we only use AddReply*Async()
if (FCorrectThread(replica))
lock.lock();
if (serverTL->current_client && FSameHost(serverTL->current_client, replica))
{
replica->reploff_skipped += g_pserver->master_repl_offset - master_repl_offset_start;
continue;
}
/* Feed slaves that are waiting for the initial SYNC (so these commands
* are queued in the output buffer until the initial SYNC completes),
* or are already in sync with the master. */
if (!fSendRaw)
addReplyProtoAsync(replica, proto, cchProto);
addReplyProtoAsync(replica,fake->buf,fake->bufpos);
listRewind(fake->reply, &liReply);
while ((lnReply = listNext(&liReply)))
{
clientReplyBlock* reply = (clientReplyBlock*)listNodeValue(lnReply);
addReplyProtoAsync(replica, reply->buf(), reply->used);
}
if (!fSendRaw)
{
addReplyAsync(replica,shared.crlf);
addReplyProtoAsync(replica, szDbNum, cchDbNum);
addReplyProtoAsync(replica, szMvcc, cchMvcc);
}
}
// Cleanup cached fake client output buffers
fake->bufpos = 0;
fake->sentlen = 0;
fake->reply_bytes = 0;
listEmpty(fake->reply);
void replicationFeedSlaves(list *replicas, int dictid, robj **argv, int argc) {
runAndPropogateToReplicas(replicationFeedSlavesCore, replicas, dictid, argv, argc);
}
/* This is a debugging function that gets called when we detect something
@@ -578,10 +586,7 @@ void showLatestBacklog(void) {
/* This function is used in order to proxy what we receive from our master
* to our sub-slaves. */
#include <ctype.h>
void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen) {
listNode *ln;
listIter li;
void replicationFeedSlavesFromMasterStream(char *buf, size_t buflen) {
/* Debugging: this is handy to see the stream sent from master
* to slaves. Disabled with if(0). */
if (0) {
@@ -593,23 +598,6 @@ void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t bufle
}
if (g_pserver->repl_backlog) feedReplicationBacklog(buf,buflen);
listRewind(slaves,&li);
while((ln = listNext(&li))) {
client *replica = (client*)ln->value;
std::unique_lock<decltype(replica->lock)> ulock(replica->lock, std::defer_lock);
if (FCorrectThread(replica))
ulock.lock();
if (FMasterHost(replica))
continue; // Active Active case, don't feed back
/* Don't feed slaves that are still waiting for BGSAVE to start */
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
addReplyProtoAsync(replica,buf,buflen);
}
if (listLength(slaves))
ProcessPendingAsyncWrites(); // flush them to their respective threads
}
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc) {
@@ -651,7 +639,7 @@ void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv,
// When writing to clients on other threads the global lock is sufficient provided we only use AddReply*Async()
if (FCorrectThread(c))
lock.lock();
addReplyAsync(monitor,cmdobj);
addReply(monitor,cmdobj);
}
decrRefCount(cmdobj);
}
@@ -784,7 +772,7 @@ int masterTryPartialResynchronization(client *c) {
(strcasecmp(master_replid, g_pserver->replid2) ||
psync_offset > g_pserver->second_replid_offset))
{
/* Run id "?" is used by slaves that want to force a full resync. */
/* Replid "?" is used by slaves that want to force a full resync. */
if (master_replid[0] != '?') {
if (strcasecmp(master_replid, g_pserver->replid) &&
strcasecmp(master_replid, g_pserver->replid2))
@@ -963,7 +951,7 @@ int startBgsaveForReplication(int mincapa) {
return retval;
}
/* SYNC and PSYNC command implemenation. */
/* SYNC and PSYNC command implementation. */
void syncCommand(client *c) {
/* ignore SYNC if already replica or in monitor mode */
if (c->flags & CLIENT_SLAVE) return;
@@ -1609,6 +1597,16 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type)
} else if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
struct redis_stat buf;
if (bgsaveerr != C_OK) {
ul.unlock();
if (FCorrectThread(replica))
freeClient(replica);
else
freeClientAsync(replica);
serverLog(LL_WARNING,"SYNC failed. BGSAVE child returned an error");
continue;
}
/* If this was an RDB on disk save, we have to prepare to send
* the RDB from disk to the replica socket. Otherwise if this was
* already an RDB -> Slaves socket transfer, used in the case of
@@ -1647,15 +1645,6 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type)
replica->repl_put_online_on_ack = 1;
replica->repl_ack_time = g_pserver->unixtime; /* Timeout otherwise. */
} else {
if (bgsaveerr != C_OK) {
ul.unlock();
if (FCorrectThread(replica))
freeClient(replica);
else
freeClientAsync(replica);
serverLog(LL_WARNING,"SYNC failed. BGSAVE child returned an error");
continue;
}
if ((replica->repldbfd = open(g_pserver->rdb_filename,O_RDONLY)) == -1 ||
redis_fstat(replica->repldbfd,&buf) == -1) {
ul.unlock();
@@ -1792,7 +1781,7 @@ void replicationEmptyDbCallback(void *privdata) {
}
}
/* Once we have a link with the master and the synchroniziation was
/* Once we have a link with the master and the synchronization was
* performed, this function materializes the master client we store
* at g_pserver->master, starting from the specified file descriptor. */
void replicationCreateMasterClient(redisMaster *mi, connection *conn, int dbid) {
@@ -1857,16 +1846,10 @@ static int useDisklessLoad() {
}
/* Helper function for readSyncBulkPayload() to make backups of the current
* DBs before socket-loading the new ones. The backups may be restored later
* or freed by disklessLoadRestoreBackups(). */
redisDb *disklessLoadMakeBackups(void) {
redisDb *backups = (redisDb*)zmalloc(sizeof(redisDb)*cserver.dbnum);
for (int i=0; i<cserver.dbnum; i++) {
backups[i] = g_pserver->db[i];
g_pserver->db[i].pdict = dictCreate(&dbDictType,NULL);
g_pserver->db[i].setexpire = new (MALLOC_LOCAL) expireset();
}
return backups;
* databases before socket-loading the new ones. The backups may be restored
* by disklessLoadRestoreBackup or freed by disklessLoadDiscardBackup later. */
dbBackup *disklessLoadMakeBackup(void) {
return backupDb();
}
/* Helper function for readSyncBulkPayload(): when replica-side diskless
@@ -1874,30 +1857,15 @@ redisDb *disklessLoadMakeBackups(void) {
* before loading the new ones from the socket.
*
* If the socket loading went wrong, we want to restore the old backups
* into the server databases. This function does just that in the case
* the 'restore' argument (the number of DBs to replace) is non-zero.
*
* When instead the loading succeeded we want just to free our old backups,
* in that case the funciton will do just that when 'restore' is 0. */
void disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags)
{
if (restore) {
/* Restore. */
emptyDbGeneric(g_pserver->db,-1,empty_db_flags,replicationEmptyDbCallback);
for (int i=0; i<cserver.dbnum; i++) {
dictRelease(g_pserver->db[i].pdict);
delete g_pserver->db[i].setexpire;
g_pserver->db[i] = backup[i];
}
} else {
/* Delete (Pass EMPTYDB_BACKUP in order to avoid firing module events) . */
emptyDbGeneric(backup,-1,empty_db_flags|EMPTYDB_BACKUP,replicationEmptyDbCallback);
for (int i=0; i<cserver.dbnum; i++) {
dictRelease(backup[i].pdict);
delete backup[i].setexpire;
}
}
zfree(backup);
* into the server databases. */
void disklessLoadRestoreBackup(dbBackup *buckup) {
restoreDbBackup(buckup);
}
/* Helper function for readSyncBulkPayload() to discard our old backups
* when the loading succeeded. */
void disklessLoadDiscardBackup(dbBackup *buckup, int flag) {
discardDbBackup(buckup, flag, replicationEmptyDbCallback);
}
/* Asynchronously read the SYNC payload we receive from a master */
@@ -1906,7 +1874,7 @@ void readSyncBulkPayload(connection *conn) {
char buf[PROTO_IOBUF_LEN];
ssize_t nread, readlen, nwritten;
int use_diskless_load = useDisklessLoad();
redisDb *diskless_load_backup = NULL;
dbBackup *diskless_load_backup = NULL;
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
int empty_db_flags = g_pserver->repl_slave_lazy_flush ? EMPTYDB_ASYNC :
EMPTYDB_NO_FLAGS;
@@ -1918,7 +1886,7 @@ void readSyncBulkPayload(connection *conn) {
serverAssert(GlobalLocksAcquired());
/* Static vars used to hold the EOF mark, and the last bytes received
* form the server: when they match, we reached the end of the transfer. */
* from the server: when they match, we reached the end of the transfer. */
static char eofmark[CONFIG_RUN_ID_SIZE];
static char lastbytes[CONFIG_RUN_ID_SIZE];
static int usemark = 0;
@@ -2092,11 +2060,11 @@ void readSyncBulkPayload(connection *conn) {
g_pserver->repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB)
{
/* Create a backup of server.db[] and initialize to empty
* dictionaries */
diskless_load_backup = disklessLoadMakeBackups();
* dictionaries. */
diskless_load_backup = disklessLoadMakeBackup();
}
/* We call to emptyDb even in case of REPL_DISKLESS_LOAD_SWAPDB
* (Where disklessLoadMakeBackups left server.db empty) because we
* (Where disklessLoadMakeBackup left server.db empty) because we
* want to execute all the auxiliary logic of emptyDb (Namely,
* fire module events) */
if (!fUpdate) {
@@ -2129,14 +2097,14 @@ void readSyncBulkPayload(connection *conn) {
"from socket");
cancelReplicationHandshake(mi);
rioFreeConn(&rdb, NULL);
/* Remove the half-loaded data in case we started with
* an empty replica. */
emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
if (g_pserver->repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB) {
/* Restore the backed up databases. */
disklessLoadRestoreBackups(diskless_load_backup,1,
empty_db_flags);
} else {
/* Remove the half-loaded data in case we started with
* an empty replica. */
emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
disklessLoadRestoreBackup(diskless_load_backup);
}
/* Note that there's no point in restarting the AOF on SYNC
@@ -2151,7 +2119,7 @@ void readSyncBulkPayload(connection *conn) {
/* Delete the backup databases we created before starting to load
* the new RDB. Now the RDB was loaded with success so the old
* data is useless. */
disklessLoadRestoreBackups(diskless_load_backup,0,empty_db_flags);
disklessLoadDiscardBackup(diskless_load_backup, empty_db_flags);
}
/* Verify the end mark is correct. */
@@ -2185,6 +2153,17 @@ void readSyncBulkPayload(connection *conn) {
const char *rdb_filename = mi->repl_transfer_tmpfile;
/* Make sure the new file (also used for persistence) is fully synced
* (not covered by earlier calls to rdb_fsync_range). */
if (fsync(mi->repl_transfer_fd) == -1) {
serverLog(LL_WARNING,
"Failed trying to sync the temp DB to disk in "
"MASTER <-> REPLICA synchronization: %s",
strerror(errno));
cancelReplicationHandshake(mi);
return;
}
/* Rename rdb like renaming rewrite aof asynchronously. */
if (!fUpdate) {
int old_rdb_fd = open(g_pserver->rdb_filename,O_RDONLY|O_NONBLOCK);
@@ -2254,7 +2233,7 @@ void readSyncBulkPayload(connection *conn) {
REDISMODULE_SUBEVENT_MASTER_LINK_UP,
NULL);
/* After a full resynchroniziation we use the replication ID and
/* After a full resynchronization we use the replication ID and
* offset of the master. The secondary ID / offset are cleared since
* we are starting a new history. */
if (fUpdate)
@@ -2268,6 +2247,8 @@ void readSyncBulkPayload(connection *conn) {
* we are starting a new history. */
memcpy(g_pserver->replid,mi->master->replid,sizeof(g_pserver->replid));
g_pserver->master_repl_offset = mi->master->reploff;
if (g_pserver->repl_batch_offStart >= 0)
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
}
clearReplicationId2();
@@ -2275,7 +2256,7 @@ void readSyncBulkPayload(connection *conn) {
* accumulate the backlog regardless of the fact they have sub-slaves
* or not, in order to behave correctly if they are promoted to
* masters after a failover. */
if (g_pserver->repl_backlog == NULL) createReplicationBacklog();
if (g_pserver->repl_backlog == NULL) runAndPropogateToReplicas(createReplicationBacklog);
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Finished with success");
if (cserver.supervised_mode == SUPERVISED_SYSTEMD) {
@@ -2360,7 +2341,7 @@ char *sendSynchronousCommand(redisMaster *mi, int flags, connection *conn, ...)
/* Try a partial resynchronization with the master if we are about to reconnect.
* If there is no cached master structure, at least try to issue a
* "PSYNC ? -1" command in order to trigger a full resync using the PSYNC
* command in order to obtain the master run id and the master replication
* command in order to obtain the master replid and the master replication
* global offset.
*
* This function is designed to be called from syncWithMaster(), so the
@@ -2388,7 +2369,7 @@ char *sendSynchronousCommand(redisMaster *mi, int flags, connection *conn, ...)
*
* PSYNC_CONTINUE: If the PSYNC command succeeded and we can continue.
* PSYNC_FULLRESYNC: If PSYNC is supported but a full resync is needed.
* In this case the master run_id and global replication
* In this case the master replid and global replication
* offset is saved.
* PSYNC_NOT_SUPPORTED: If the server does not understand PSYNC at all and
* the caller should fall back to SYNC.
@@ -2419,7 +2400,7 @@ int slaveTryPartialResynchronization(redisMaster *mi, connection *conn, int read
/* Writing half */
if (!read_reply) {
/* Initially set master_initial_offset to -1 to mark the current
* master run_id and offset as not valid. Later if we'll be able to do
* master replid and offset as not valid. Later if we'll be able to do
* a FULL resync using the PSYNC command we'll set the offset at the
* right value, so that this information will be propagated to the
* client structure representing the master into g_pserver->master. */
@@ -2460,7 +2441,7 @@ int slaveTryPartialResynchronization(redisMaster *mi, connection *conn, int read
if (!strncmp(reply,"+FULLRESYNC",11)) {
char *replid = NULL, *offset = NULL;
/* FULL RESYNC, parse the reply in order to extract the run id
/* FULL RESYNC, parse the reply in order to extract the replid
* and the replication offset. */
replid = strchr(reply,' ');
if (replid) {
@@ -2535,7 +2516,7 @@ int slaveTryPartialResynchronization(redisMaster *mi, connection *conn, int read
/* If this instance was restarted and we read the metadata to
* PSYNC from the persistence file, our replication backlog could
* be still not initialized. Create it. */
if (g_pserver->repl_backlog == NULL) createReplicationBacklog();
if (g_pserver->repl_backlog == NULL) runAndPropogateToReplicas(createReplicationBacklog);
return PSYNC_CONTINUE;
}
@@ -2645,6 +2626,7 @@ void syncWithMaster(connection *conn) {
* both. */
if (err[0] != '+' &&
strncmp(err,"-NOAUTH",7) != 0 &&
strncmp(err,"-NOPERM",7) != 0 &&
strncmp(err,"-ERR operation not permitted",28) != 0)
{
serverLog(LL_WARNING,"Error reply to PING from master: '%s'",err);
@@ -2812,7 +2794,7 @@ void syncWithMaster(connection *conn) {
/* Try a partial resynchonization. If we don't have a cached master
* slaveTryPartialResynchronization() will at least try to use PSYNC
* to start a full resynchronization so that we get the master run id
* to start a full resynchronization so that we get the master replid
* and the global offset, to try a partial resync at the next
* reconnection attempt. */
if (mi->repl_state == REPL_STATE_SEND_PSYNC) {
@@ -2981,7 +2963,7 @@ void replicationAbortSyncTransfer(redisMaster *mi) {
undoConnectWithMaster(mi);
if (mi->repl_transfer_fd!=-1) {
close(mi->repl_transfer_fd);
unlink(mi->repl_transfer_tmpfile);
bg_unlink(mi->repl_transfer_tmpfile);
zfree(mi->repl_transfer_tmpfile);
mi->repl_transfer_tmpfile = NULL;
mi->repl_transfer_fd = -1;
@@ -2995,7 +2977,7 @@ void replicationAbortSyncTransfer(redisMaster *mi) {
* If there was a replication handshake in progress 1 is returned and
* the replication state (g_pserver->repl_state) set to REPL_STATE_CONNECT.
*
* Otherwise zero is returned and no operation is perforemd at all. */
* Otherwise zero is returned and no operation is performed at all. */
int cancelReplicationHandshake(redisMaster *mi) {
if (mi->repl_state == REPL_STATE_TRANSFER) {
replicationAbortSyncTransfer(mi);
@@ -3047,7 +3029,11 @@ struct redisMaster *replicationAddMaster(char *ip, int port) {
else
freeClientAsync(mi->master);
}
disconnectAllBlockedClients(); /* Clients blocked in master, now replica. */
if (!g_pserver->fActiveReplica)
disconnectAllBlockedClients(); /* Clients blocked in master, now replica. */
/* Update oom_score_adj */
setOOMScoreAdj(-1);
/* Force our slaves to resync with us as well. They may hopefully be able
* to partially resync with us, but we can notify the replid change. */
@@ -3145,6 +3131,9 @@ void replicationUnsetMaster(redisMaster *mi) {
listDelNode(g_pserver->masters, ln);
freeMasterInfo(mi);
/* Update oom_score_adj */
setOOMScoreAdj(-1);
/* Fire the role change modules event. */
moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,
@@ -3259,7 +3248,7 @@ void replicaofCommand(client *c) {
miNew->masterhost, miNew->masterport, client);
sdsfree(client);
}
addReplyAsync(c,shared.ok);
addReply(c,shared.ok);
}
/* ROLE command: provide information about the role of the instance
@@ -3487,6 +3476,11 @@ void replicationResurrectCachedMaster(redisMaster *mi, connection *conn) {
but this client was unlinked so its OK here */
mi->master->iel = serverTL - g_pserver->rgthreadvar; // martial to this thread
/* Fire the master link modules event. */
moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,
REDISMODULE_SUBEVENT_MASTER_LINK_UP,
NULL);
/* Re-add to the list of clients. */
linkClient(mi->master);
serverAssert(connGetPrivateData(mi->master->conn) == mi->master);
@@ -3540,7 +3534,7 @@ void refreshGoodSlavesCount(void) {
*
* We don't care about taking a different cache for every different replica
* since to fill the cache again is not very costly, the goal of this code
* is to avoid that the same big script is trasmitted a big number of times
* is to avoid that the same big script is transmitted a big number of times
* per second wasting bandwidth and processor speed, but it is not a problem
* if we need to rebuild the cache from scratch from time to time, every used
* script will need to be transmitted a single time to reappear in the cache.
@@ -3550,7 +3544,7 @@ void refreshGoodSlavesCount(void) {
* 1) Every time a new replica connects, we flush the whole script cache.
* 2) We only send as EVALSHA what was sent to the master as EVALSHA, without
* trying to convert EVAL into EVALSHA specifically for slaves.
* 3) Every time we trasmit a script as EVAL to the slaves, we also add the
* 3) Every time we transmit a script as EVAL to the slaves, we also add the
* corresponding SHA1 of the script into the cache as we are sure every
* replica knows about the script starting from now.
* 4) On SCRIPT FLUSH command, we replicate the command to all the slaves
@@ -3641,7 +3635,7 @@ int replicationScriptCacheExists(sds sha1) {
/* This just set a flag so that we broadcast a REPLCONF GETACK command
* to all the slaves in the beforeSleep() function. Note that this way
* we "group" all the clients that want to wait for synchronouns replication
* we "group" all the clients that want to wait for synchronous replication
* in a given event loop iteration, and send a single GETACK for them all. */
void replicationRequestAckFromSlaves(void) {
g_pserver->get_ack_from_slaves = 1;
@@ -3734,7 +3728,7 @@ void processClientsWaitingReplicas(void) {
last_numreplicas > c->bpop.numreplicas)
{
unblockClient(c);
addReplyLongLongAsync(c,last_numreplicas);
addReplyLongLong(c,last_numreplicas);
} else {
int numreplicas = replicationCountAcksByOffset(c->bpop.reploffset);
@@ -3742,7 +3736,7 @@ void processClientsWaitingReplicas(void) {
last_offset = c->bpop.reploffset;
last_numreplicas = numreplicas;
unblockClient(c);
addReplyLongLongAsync(c,numreplicas);
addReplyLongLong(c,numreplicas);
}
}
fastlock_unlock(&c->lock);
@@ -4353,3 +4347,90 @@ static void propagateMasterStaleKeys()
decrRefCount(rgobj[0]);
}
void replicationNotifyLoadedKey(redisDb *db, robj_roptr key, robj_roptr val, long long expire) {
if (!g_pserver->fActiveReplica || listLength(g_pserver->slaves) == 0)
return;
// Send a digest over to the replicas
rio r;
createDumpPayload(&r, val, key.unsafe_robjcast());
redisObjectStack objPayload;
initStaticStringObject(objPayload, r.io.buffer.ptr);
redisObjectStack objTtl;
initStaticStringObject(objTtl, sdscatprintf(sdsempty(), "%lld", expire));
redisObjectStack objMvcc;
initStaticStringObject(objMvcc, sdscatprintf(sdsempty(), "%lu", mvccFromObj(val)));
redisObject *argv[5] = {shared.mvccrestore, key.unsafe_robjcast(), &objMvcc, &objTtl, &objPayload};
replicationFeedSlaves(g_pserver->slaves, db - g_pserver->db, argv, 5);
sdsfree(szFromObj(&objTtl));
sdsfree(szFromObj(&objMvcc));
sdsfree(r.io.buffer.ptr);
}
void replicateSubkeyExpire(redisDb *db, robj_roptr key, robj_roptr subkey, long long expire) {
if (!g_pserver->fActiveReplica || listLength(g_pserver->slaves) == 0)
return;
redisObjectStack objTtl;
initStaticStringObject(objTtl, sdscatprintf(sdsempty(), "%lld", expire));
redisObject *argv[4] = {shared.pexpirememberat, key.unsafe_robjcast(), subkey.unsafe_robjcast(), &objTtl};
replicationFeedSlaves(g_pserver->slaves, db - g_pserver->db, argv, 4);
sdsfree(szFromObj(&objTtl));
}
void flushReplBacklogToClients()
{
serverAssert(GlobalLocksAcquired());
if (g_pserver->repl_batch_offStart < 0)
return;
if (g_pserver->repl_batch_offStart != g_pserver->master_repl_offset) {
bool fAsyncWrite = false;
// Ensure no overflow
serverAssert(g_pserver->repl_batch_offStart < g_pserver->master_repl_offset);
serverAssert(g_pserver->master_repl_offset - g_pserver->repl_batch_offStart <= g_pserver->repl_backlog_size);
serverAssert(g_pserver->repl_batch_idxStart != g_pserver->repl_backlog_idx);
listIter li;
listNode *ln;
listRewind(g_pserver->slaves, &li);
while ((ln = listNext(&li))) {
client *replica = (client*)listNodeValue(ln);
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
if (replica->flags & CLIENT_CLOSE_ASAP) continue;
std::unique_lock<fastlock> ul(replica->lock, std::defer_lock);
if (FCorrectThread(replica))
ul.lock();
else
fAsyncWrite = true;
if (g_pserver->repl_backlog_idx >= g_pserver->repl_batch_idxStart) {
long long cbCopy = g_pserver->repl_backlog_idx - g_pserver->repl_batch_idxStart;
serverAssert((g_pserver->master_repl_offset - g_pserver->repl_batch_offStart) == cbCopy);
serverAssert((g_pserver->repl_backlog_size - g_pserver->repl_batch_idxStart) >= (cbCopy));
serverAssert((g_pserver->repl_batch_idxStart + cbCopy) <= g_pserver->repl_backlog_size);
addReplyProto(replica, g_pserver->repl_backlog + g_pserver->repl_batch_idxStart, cbCopy);
} else {
auto cbPhase1 = g_pserver->repl_backlog_size - g_pserver->repl_batch_idxStart;
addReplyProto(replica, g_pserver->repl_backlog + g_pserver->repl_batch_idxStart, cbPhase1);
addReplyProto(replica, g_pserver->repl_backlog, g_pserver->repl_backlog_idx);
serverAssert((cbPhase1 + g_pserver->repl_backlog_idx) == (g_pserver->master_repl_offset - g_pserver->repl_batch_offStart));
}
}
if (fAsyncWrite)
ProcessPendingAsyncWrites();
// This may be called multiple times per "frame" so update with our progress flushing to clients
g_pserver->repl_batch_idxStart = g_pserver->repl_backlog_idx;
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
}
}
+4
View File
@@ -94,6 +94,7 @@ static const rio rioBufferIO = {
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* keys since last callback */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
@@ -148,6 +149,7 @@ static const rio rioFileIO = {
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* keys since last callback */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
@@ -243,6 +245,7 @@ static const rio rioConnIO = {
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* keys since last callback */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
@@ -361,6 +364,7 @@ static const rio rioFdIO = {
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* keys since last callback */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
+3
View File
@@ -62,6 +62,9 @@ struct _rio {
/* The current checksum and flags (see RIO_FLAG_*) */
uint64_t cksum, flags;
/* number of keys loaded since last rdbLoadProgressCallback */
long int keys_since_last_callback;
/* number of bytes read or written */
size_t processed_bytes;
+16 -16
View File
@@ -72,7 +72,7 @@ struct ldbState {
list *children; /* All forked debugging sessions pids. */
int bp[LDB_BREAKPOINTS_MAX]; /* An array of breakpoints line numbers. */
int bpcount; /* Number of valid entries inside bp. */
int step; /* Stop at next line ragardless of breakpoints. */
int step; /* Stop at next line regardless of breakpoints. */
int luabp; /* Stop at next line because redis.breakpoint() was called. */
sds *src; /* Lua script source code split by line. */
int lines; /* Number of lines in 'src'. */
@@ -413,9 +413,9 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
lua_pushnil(lua); /* Use nil to start iteration. */
while (lua_next(lua,-2)) {
/* Stack now: table, key, value */
luaReplyToRedisReply(c, lua); /* Return value. */
lua_pushvalue(lua,-1); /* Dup key before consuming. */
lua_pushvalue(lua,-2); /* Dup key before consuming. */
luaReplyToRedisReply(c, lua); /* Return key. */
luaReplyToRedisReply(c, lua); /* Return value. */
/* Stack now: table, key. */
maplen++;
}
@@ -899,7 +899,7 @@ int luaRedisReplicateCommandsCommand(lua_State *lua) {
/* redis.breakpoint()
*
* Allows to stop execution during a debuggign session from within
* Allows to stop execution during a debugging session from within
* the Lua code implementation, like if a breakpoint was set in the code
* immediately after the function. */
int luaRedisBreakpointCommand(lua_State *lua) {
@@ -1509,7 +1509,7 @@ void evalGenericCommand(client *c, int evalsha) {
/* Hash the code if this is an EVAL call */
sha1hex(funcname+2,(char*)ptrFromObj(c->argv[1]),sdslen((sds)ptrFromObj(c->argv[1])));
} else {
/* We already have the SHA if it is a EVALSHA */
/* We already have the SHA if it is an EVALSHA */
int j;
char *sha = (char*)ptrFromObj(c->argv[1]);
@@ -1645,7 +1645,7 @@ void evalGenericCommand(client *c, int evalsha) {
* To do so we use a cache of SHA1s of scripts that we already propagated
* as full EVAL, that's called the Replication Script Cache.
*
* For repliation, everytime a new replica attaches to the master, we need to
* For replication, everytime a new replica 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 && !g_pserver->lua_replicate_commands) {
@@ -1818,7 +1818,7 @@ void ldbLog(sds entry) {
}
/* A version of ldbLog() which prevents producing logs greater than
* ldb.maxlen. The first time the limit is reached an hint is generated
* ldb.maxlen. The first time the limit is reached a hint is generated
* to inform the user that reply trimming can be disabled using the
* debugger "maxlen" command. */
void ldbLogWithMaxLen(sds entry) {
@@ -1859,7 +1859,7 @@ void ldbSendLogs(void) {
}
/* Start a debugging session before calling EVAL implementation.
* The techique we use is to capture the client socket file descriptor,
* The technique we use is to capture the client socket file descriptor,
* in order to perform direct I/O with it from within Lua hooks. This
* way we don't have to re-enter Redis in order to handle I/O.
*
@@ -1873,7 +1873,7 @@ void ldbSendLogs(void) {
int ldbStartSession(client *c) {
ldb.forked = (c->flags & CLIENT_LUA_DEBUG_SYNC) == 0;
if (ldb.forked) {
pid_t cp = redisFork();
pid_t cp = redisFork(CHILD_TYPE_LDB);
if (cp == -1) {
addReplyError(c,"Fork() failed: can't run EVAL in debugging mode.");
return 0;
@@ -1942,7 +1942,7 @@ void ldbEndSession(client *c) {
connNonBlock(ldb.conn);
connSendTimeout(ldb.conn,0);
/* Close the client connectin after sending the final EVAL reply
/* Close the client connection after sending the final EVAL reply
* in order to signal the end of the debugging session. */
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
@@ -2112,7 +2112,7 @@ void ldbLogSourceLine(int lnum) {
/* Implement the "list" command of the Lua debugger. If around is 0
* the whole file is listed, otherwise only a small portion of the file
* around the specified line is shown. When a line number is specified
* the amonut of context (lines before/after) is specified via the
* the amount of context (lines before/after) is specified via the
* 'context' argument. */
void ldbList(int around, int context) {
int j;
@@ -2123,7 +2123,7 @@ void ldbList(int around, int context) {
}
}
/* Append an human readable representation of the Lua value at position 'idx'
/* Append a human readable representation of the Lua value at position 'idx'
* on the stack of the 'lua' state, to the SDS string passed as argument.
* The new SDS string with the represented value attached is returned.
* Used in order to implement ldbLogStackValue().
@@ -2367,7 +2367,7 @@ char *ldbRedisProtocolToHuman_Double(sds *o, char *reply) {
return p+2;
}
/* Log a Redis reply as debugger output, in an human readable format.
/* Log a Redis reply as debugger output, in a human readable format.
* If the resulting string is longer than 'len' plus a few more chars
* used as prefix, it gets truncated. */
void ldbLogRedisReply(char *reply) {
@@ -2551,7 +2551,7 @@ void ldbTrace(lua_State *lua) {
}
}
/* Impleemnts the debugger "maxlen" command. It just queries or sets the
/* Implements the debugger "maxlen" command. It just queries or sets the
* ldb.maxlen variable. */
void ldbMaxlen(sds *argv, int argc) {
if (argc == 2) {
@@ -2624,8 +2624,8 @@ ldbLog(sdsnew(" mode dataset changes will be retained."));
ldbLog(sdsnew(""));
ldbLog(sdsnew("Debugger functions you can call from Lua scripts:"));
ldbLog(sdsnew("redis.debug() Produce logs in the debugger console."));
ldbLog(sdsnew("redis.breakpoint() Stop execution like if there was a breakpoing."));
ldbLog(sdsnew(" in the next line of code."));
ldbLog(sdsnew("redis.breakpoint() Stop execution like if there was a breakpoint in the"));
ldbLog(sdsnew(" next line of code."));
ldbSendLogs();
} else if (!strcasecmp(argv[0],"s") || !strcasecmp(argv[0],"step") ||
!strcasecmp(argv[0],"n") || !strcasecmp(argv[0],"next")) {
+30 -10
View File
@@ -444,7 +444,7 @@ sds sdscatlen(sds s, const void *t, size_t len) {
return s;
}
/* Append the specified null termianted C string to the sds string 's'.
/* Append the specified null terminated C string to the sds string 's'.
*
* After the call, the passed sds string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
@@ -492,7 +492,7 @@ int sdsll2str(char *s, long long value) {
size_t l;
/* Generate the string representation, this method produces
* an reversed string. */
* a reversed string. */
v = (value < 0) ? -value : value;
p = s;
do {
@@ -523,7 +523,7 @@ int sdsull2str(char *s, unsigned long long v) {
size_t l;
/* Generate the string representation, this method produces
* an reversed string. */
* a reversed string. */
p = s;
do {
*p++ = '0'+(v%10);
@@ -562,6 +562,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
char staticbuf[1024], *buf = staticbuf, *t;
size_t buflen = strlen(fmt)*2;
int bufstrlen;
/* We try to start using a static buffer for speed.
* If not possible we revert to heap allocation. */
@@ -572,16 +573,19 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
buflen = sizeof(staticbuf);
}
/* Try with buffers two times bigger every time we fail to
/* Alloc enough space for buffer and \0 after failing to
* fit the string in the current buffer size. */
while(1) {
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
bufstrlen = vsnprintf(buf, buflen, fmt, cpy);
va_end(cpy);
if (buf[buflen-2] != '\0') {
if (bufstrlen < 0) {
if (buf != staticbuf) s_free(buf);
buflen *= 2;
return NULL;
}
if (((size_t)bufstrlen) >= buflen) {
if (buf != staticbuf) s_free(buf);
buflen = ((size_t)bufstrlen) + 1;
buf = s_malloc(buflen, MALLOC_SHARED);
if (buf == NULL) return NULL;
continue;
@@ -590,7 +594,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
}
/* Finally concat the obtained string to the SDS string and return it. */
t = sdscat(s, buf);
t = sdscatlen(s, buf, bufstrlen);
if (buf != staticbuf) s_free(buf);
return t;
}
@@ -645,7 +649,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* To avoid continuous reallocations, let's start with a buffer that
* can hold at least two times the format string itself. It's not the
* best heuristic but seems to work in practice. */
s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);
s = sdsMakeRoomFor(s, strlen(fmt)*2);
va_start(ap,fmt);
f = fmt; /* Next format specifier byte to process. */
i = initlen; /* Position of the next byte to write to dest str. */
@@ -1198,6 +1202,22 @@ int sdsTest(void) {
test_cond("sdscatprintf() seems working in the base case",
sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
sdsfree(x);
x = sdscatprintf(sdsempty(),"a%cb",0);
test_cond("sdscatprintf() seems working with \\0 inside of result",
sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0)
{
sdsfree(x);
char etalon[1024*1024];
for (size_t i = 0; i < sizeof(etalon); i++) {
etalon[i] = '0';
}
x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0);
test_cond("sdscatprintf() can print 1MB",
sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0)
}
sdsfree(x);
x = sdsnew("--");
x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
+10
View File
@@ -390,6 +390,10 @@ public:
: sdsview(sdsdup(other.m_str))
{}
sdsstring(const char *rgch, size_t cch)
: sdsview(sdsnewlen(rgch, cch))
{}
sdsstring(sdsstring &&other)
: sdsview(other.m_str)
{
@@ -410,6 +414,12 @@ public:
return *this;
}
sds release() {
sds sdsT = m_str;
m_str = nullptr;
return sdsT;
}
~sdsstring()
{
sdsfree(m_str);
+25 -25
View File
@@ -133,13 +133,13 @@ typedef struct sentinelAddr {
/* The link to a sentinelRedisInstance. When we have the same set of Sentinels
* monitoring many masters, we have different instances representing the
* same Sentinels, one per master, and we need to share the hiredis connections
* among them. Oherwise if 5 Sentinels are monitoring 100 masters we create
* among them. Otherwise if 5 Sentinels are monitoring 100 masters we create
* 500 outgoing connections instead of 5.
*
* So this structure represents a reference counted link in terms of the two
* hiredis connections for commands and Pub/Sub, and the fields needed for
* failure detection, since the ping/pong time are now local to the link: if
* the link is available, the instance is avaialbe. This way we don't just
* the link is available, the instance is available. This way we don't just
* have 5 connections instead of 500, we also send 5 pings instead of 500.
*
* Links are shared only for Sentinels: master and slave instances have
@@ -988,7 +988,7 @@ instanceLink *createInstanceLink(void) {
return link;
}
/* Disconnect an hiredis connection in the context of an instance link. */
/* Disconnect a hiredis connection in the context of an instance link. */
void instanceLinkCloseConnection(instanceLink *link, redisAsyncContext *c) {
if (c == NULL) return;
@@ -1078,6 +1078,7 @@ int sentinelTryConnectionSharing(sentinelRedisInstance *ri) {
releaseInstanceLink(ri->link,NULL);
ri->link = match->link;
match->link->refcount++;
dictReleaseIterator(di);
return C_OK;
}
dictReleaseIterator(di);
@@ -1126,7 +1127,7 @@ int sentinelUpdateSentinelAddressInAllMasters(sentinelRedisInstance *ri) {
return reconfigured;
}
/* This function is called when an hiredis connection reported an error.
/* This function is called when a hiredis connection reported an error.
* We set it to NULL and mark the link as disconnected so that it will be
* reconnected again.
*
@@ -1955,7 +1956,7 @@ void sentinelFlushConfig(void) {
int rewrite_status;
g_pserver->hz = CONFIG_DEFAULT_HZ;
rewrite_status = rewriteConfig(cserver.configfile);
rewrite_status = rewriteConfig(cserver.configfile, 0);
g_pserver->hz = saved_hz;
if (rewrite_status == -1) goto werr;
@@ -2016,7 +2017,7 @@ void sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) {
* The connection type is "cmd" or "pubsub" as specified by 'type'.
*
* This makes it possible to list all the sentinel instances connected
* to a Redis servewr with CLIENT LIST, grepping for a specific name format. */
* to a Redis server with CLIENT LIST, grepping for a specific name format. */
void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, const char *type) {
char name[64];
@@ -2219,8 +2220,8 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
}
/* role:<role> */
if (!memcmp(l,"role:master",11)) role = SRI_MASTER;
else if (!memcmp(l,"role:slave",10)) role = SRI_SLAVE;
if (sdslen(l) >= 11 && !memcmp(l,"role:master",11)) role = SRI_MASTER;
else if (sdslen(l) >= 10 && !memcmp(l,"role:slave",10)) role = SRI_SLAVE;
if (role == SRI_SLAVE) {
/* master_host:<host> */
@@ -2471,7 +2472,7 @@ void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privd
ri->last_pub_time = mstime();
}
/* Process an hello message received via Pub/Sub in master or slave instance,
/* Process a hello message received via Pub/Sub in master or slave instance,
* or sent directly to this sentinel via the (fake) PUBLISH command of Sentinel.
*
* If the master name specified in the message is not known, the message is
@@ -2608,7 +2609,7 @@ void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privd
sentinelProcessHelloMessage(r->element[2]->str, r->element[2]->len);
}
/* Send an "Hello" message via Pub/Sub to the specified 'ri' Redis
/* Send a "Hello" message via Pub/Sub to the specified 'ri' Redis
* instance in order to broadcast the current configuration for this
* master, and to advertise the existence of this Sentinel at the same time.
*
@@ -2662,7 +2663,7 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
}
/* Reset last_pub_time in all the instances in the specified dictionary
* in order to force the delivery of an Hello update ASAP. */
* in order to force the delivery of a Hello update ASAP. */
void sentinelForceHelloUpdateDictOfRedisInstances(dict *instances) {
dictIterator *di;
dictEntry *de;
@@ -2676,13 +2677,13 @@ void sentinelForceHelloUpdateDictOfRedisInstances(dict *instances) {
dictReleaseIterator(di);
}
/* This function forces the delivery of an "Hello" message (see
/* This function forces the delivery of a "Hello" message (see
* sentinelSendHello() top comment for further information) to all the Redis
* and Sentinel instances related to the specified 'master'.
*
* It is technically not needed since we send an update to every instance
* with a period of SENTINEL_PUBLISH_PERIOD milliseconds, however when a
* Sentinel upgrades a configuration it is a good idea to deliever an update
* Sentinel upgrades a configuration it is a good idea to deliver an update
* to the other Sentinels ASAP. */
int sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master) {
if (!(master->flags & SRI_MASTER)) return C_ERR;
@@ -3083,7 +3084,7 @@ void sentinelCommand(client *c) {
* ip and port are the ip and port of the master we want to be
* checked by Sentinel. Note that the command will not check by
* name but just by master, in theory different Sentinels may monitor
* differnet masters with the same name.
* different masters with the same name.
*
* current-epoch is needed in order to understand if we are allowed
* to vote for a failover leader or not. Each Sentinel can vote just
@@ -3510,14 +3511,13 @@ void sentinelSetCommand(client *c) {
"Reconfiguration of scripts path is denied for "
"security reasons. Check the deny-scripts-reconfig "
"configuration directive in your Sentinel configuration");
return;
goto seterr;
}
if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c,
"Notification script seems non existing or non executable");
if (changes) sentinelFlushConfig();
return;
goto seterr;
}
sdsfree(ri->notification_script);
ri->notification_script = strlen(value) ? sdsnew(value) : NULL;
@@ -3530,15 +3530,14 @@ void sentinelSetCommand(client *c) {
"Reconfiguration of scripts path is denied for "
"security reasons. Check the deny-scripts-reconfig "
"configuration directive in your Sentinel configuration");
return;
goto seterr;
}
if (strlen(value) && access(value,X_OK) == -1) {
addReplyError(c,
"Client reconfiguration script seems non existing or "
"non executable");
if (changes) sentinelFlushConfig();
return;
goto seterr;
}
sdsfree(ri->client_reconfig_script);
ri->client_reconfig_script = strlen(value) ? sdsnew(value) : NULL;
@@ -3588,8 +3587,7 @@ void sentinelSetCommand(client *c) {
} else {
addReplyErrorFormat(c,"Unknown option or number of arguments for "
"SENTINEL SET '%s'", option);
if (changes) sentinelFlushConfig();
return;
goto seterr;
}
/* Log the event. */
@@ -3615,9 +3613,11 @@ void sentinelSetCommand(client *c) {
return;
badfmt: /* Bad format errors */
if (changes) sentinelFlushConfig();
addReplyErrorFormat(c,"Invalid argument '%s' for SENTINEL SET '%s'",
(char*)ptrFromObj(c->argv[badarg]),option);
seterr:
if (changes) sentinelFlushConfig();
return;
}
/* Our fake PUBLISH command: it is actually useful only to receive hello messages
@@ -3996,7 +3996,7 @@ int sentinelSendSlaveOf(sentinelRedisInstance *ri, const char *host, int port) {
* the following tasks:
* 1) Reconfigure the instance according to the specified host/port params.
* 2) Rewrite the configuration.
* 3) Disconnect all clients (but this one sending the commnad) in order
* 3) Disconnect all clients (but this one sending the command) in order
* to trigger the ask-master-on-reconnection protocol for connected
* clients.
*
@@ -4551,7 +4551,7 @@ void sentinelHandleDictOfRedisInstances(dict *instances) {
* difference bigger than SENTINEL_TILT_TRIGGER milliseconds if one of the
* following conditions happen:
*
* 1) The Sentiel process for some time is blocked, for every kind of
* 1) The Sentinel process for some time is blocked, for every kind of
* random reason: the load is huge, the computer was frozen for some time
* in I/O or alike, the process was stopped by a signal. Everything.
* 2) The system clock was altered significantly.
+581 -179
View File
File diff suppressed because it is too large. Load diff
+185 -93
View File
@@ -327,6 +327,7 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
#define PROTO_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
#define PROTO_IOBUF_LEN (1024*16) /* Generic I/O buffer size */
#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */
#define PROTO_ASYNC_REPLY_CHUNK_BYTES (1024)
#define PROTO_INLINE_MAX_SIZE (1024*64) /* Max size of inline reads */
#define PROTO_MBULK_BIG_ARG (1024*32)
#define LONG_STR_SIZE 21 /* Bytes needed for long -> str + '\0' */
@@ -340,10 +341,18 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
* in order to make sure of not over provisioning more than 128 fds. */
#define CONFIG_FDSET_INCR (CONFIG_MIN_RESERVED_FDS+96)
/* OOM Score Adjustment classes. */
#define CONFIG_OOM_MASTER 0
#define CONFIG_OOM_REPLICA 1
#define CONFIG_OOM_BGCHILD 2
#define CONFIG_OOM_COUNT 3
extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
/* Hash table parameters */
#define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */
/* Command flags. Please check the command table defined in the redis.c file
/* Command flags. Please check the command table defined in the server.cpp file
* for more information about the meaning of every flag. */
#define CMD_WRITE (1ULL<<0) /* "write" flag */
#define CMD_READONLY (1ULL<<1) /* "read-only" flag */
@@ -448,7 +457,9 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
about writes performed by myself.*/
#define CLIENT_IN_TO_TABLE (1ULL<<38) /* This client is in the timeout table. */
#define CLIENT_PROTOCOL_ERROR (1ULL<<39) /* Protocol error chatting with it. */
#define CLIENT_FORCE_REPLY (1ULL<<40) /* Should addReply be forced to write the text? */
#define CLIENT_CLOSE_AFTER_COMMAND (1ULL<<40) /* Close after executing commands
* and writing entire reply. */
#define CLIENT_FORCE_REPLY (1ULL<<41) /* Should addReply be forced to write the text? */
/* Client block type (btype field in client structure)
* if CLIENT_BLOCKED flag is set. */
@@ -532,6 +543,12 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
#define LL_WARNING 3
#define LL_RAW (1<<10) /* Modifier to log without timestamp */
/* Error severity levels */
#define ERR_CRITICAL 0
#define ERR_ERROR 1
#define ERR_WARNING 2
#define ERR_NOTICE 3
/* Supervision options */
#define SUPERVISED_NONE 0
#define SUPERVISED_AUTODETECT 1
@@ -554,11 +571,21 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
#define REPL_DISKLESS_LOAD_WHEN_DB_EMPTY 1
#define REPL_DISKLESS_LOAD_SWAPDB 2
/* TLS Client Authentication */
#define TLS_CLIENT_AUTH_NO 0
#define TLS_CLIENT_AUTH_YES 1
#define TLS_CLIENT_AUTH_OPTIONAL 2
/* Sets operations codes */
#define SET_OP_UNION 0
#define SET_OP_DIFF 1
#define SET_OP_INTER 2
/* oom-score-adj defines */
#define OOM_SCORE_ADJ_NO 0
#define OOM_SCORE_RELATIVE 1
#define OOM_SCORE_ADJ_ABSOLUTE 2
/* Redis maxmemory strategies. Instead of using just incremental number
* for this defines, we use a set of flags so that testing for certain
* properties common to multiple policies is faster. */
@@ -622,6 +649,7 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
#define NOTIFY_EVICTED (1<<9) /* e */
#define NOTIFY_STREAM (1<<10) /* t */
#define NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
#define NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM) /* A flag */
/* Get the first bind addr or NULL */
@@ -639,11 +667,11 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
/* A redis object, that is a type able to hold a string / list / set */
/* The actual Redis Object */
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
#define OBJ_HASH 4 /* Hash object. */
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
#define OBJ_HASH 4 /* Hash object. */
/* The "module" object type is a special one that signals that the object
* is one directly managed by a Redis module. In this case the value points
@@ -656,10 +684,10 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
* by a 64 bit module type ID, which has a 54 bits module-specific signature
* in order to dispatch the loading to the right module, plus a 10 bits
* encoding version. */
#define OBJ_MODULE 5 /* Module object. */
#define OBJ_STREAM 6 /* Stream object. */
#define OBJ_CRON 7 /* CRON job */
#define OBJ_MODULE 5 /* Module object. */
#define OBJ_STREAM 6 /* Stream object. */
#define OBJ_CRON 7 /* CRON job */
#define OBJ_NESTEDHASH 8 /* Nested Hash Object */
/* Extract encver / signature from a module type ID. */
#define REDISMODULE_TYPE_ENCVER_BITS 10
@@ -805,7 +833,7 @@ struct redisObjectExtended {
uint64_t mvcc_tstamp;
};
typedef class redisObject {
typedef struct redisObject {
protected:
redisObject() {}
@@ -907,19 +935,24 @@ struct redisDb {
~redisDb();
dict *pdict; /* The keyspace for this DB */
::dict *dict; /* The keyspace for this DB */
expireset *setexpire;
expireset::setiter expireitr;
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
::dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
::dict *ready_keys; /* Blocked keys that received a PUSH */
::dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long last_expire_set; /* when the last expire was set */
double avg_ttl; /* Average TTL, just for stats */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
};
/* Declare database backup that include redis main DBs and slots to keys map.
* Definition is in db.c. We can't define it here since we define CLUSTER_SLOTS
* in cluster.h. */
typedef struct dbBackup dbBackup;
/* Client MULTI/EXEC state */
typedef struct multiCmd {
robj **argv;
@@ -933,6 +966,9 @@ typedef struct multiState {
int cmd_flags; /* The accumulated command flags OR-ed together.
So if at least a command has a given flag, it
will be set in this field. */
int cmd_inv_flags; /* Same as cmd_flags, OR-ing the ~flags. so that it
is possible to know if all the commands have a
certain flag. */
int minreplicas; /* MINREPLICAS for synchronous replication */
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
} multiState;
@@ -945,7 +981,7 @@ typedef struct blockingState {
* is > timeout then the operation timed out. */
/* BLOCKED_LIST, BLOCKED_ZSET and BLOCKED_STREAM */
dict *keys; /* The keys we are waiting to terminate a blocking
::dict *keys; /* The keys we are waiting to terminate a blocking
* operation such as BLPOP or XREAD. Or NULL. */
robj *target; /* The key that should receive the element,
* for BRPOPLPUSH. */
@@ -1047,6 +1083,7 @@ typedef struct client {
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */
int argc; /* Num of arguments of current command. */
robj **argv; /* Arguments of current command. */
size_t argv_len_sum; /* Sum of lengths of objects in argv list. */
struct redisCommand *cmd, *lastcmd; /* Last command executed. */
user *puser; /* User associated with this connection. If the
user is set to NULL the connection can do
@@ -1065,7 +1102,7 @@ typedef struct client {
std::atomic<uint64_t> 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 fPendingAsyncWriteHandler;
std::atomic<bool> fPendingAsyncWriteHandler;
int authenticated; /* Needed when the default user requires auth. */
int replstate; /* Replication state if this is a replica. */
int repl_put_online_on_ack; /* Install replica write handler on ACK. */
@@ -1082,7 +1119,7 @@ typedef struct client {
copying this replica output buffer
should use. */
char replid[CONFIG_RUN_ID_SIZE+1]; /* Master replication ID (if master). */
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
int slave_listening_port; /* As configured with: REPLCONF listening-port */
char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
multiState mstate; /* MULTI/EXEC state */
@@ -1090,7 +1127,7 @@ typedef struct client {
blockingState bpop; /* blocking state */
long long woff; /* Last write global replication offset. */
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
::dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
sds peerid; /* Cached peer ID. */
listNode *client_list_node; /* list node in client list */
@@ -1128,16 +1165,14 @@ typedef struct client {
char buf[PROTO_REPLY_CHUNK_BYTES];
/* Async Response Buffer - other threads write here */
int bufposAsync;
int buflenAsync;
char *bufAsync;
clientReplyBlock *replyAsync;
int iel; /* the event loop index we're registered with */
struct fastlock lock;
int master_error;
// post a function from a non-client thread to run on its client thread
bool postFunction(std::function<void(client *)> fn);
bool postFunction(std::function<void(client *)> fn, bool fLock = true);
} client;
struct saveparam {
@@ -1160,7 +1195,7 @@ struct sharedObjectsStruct {
*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink,
*rpop, *lpop, *lpush, *rpoplpush, *zpopmin, *zpopmax, *emptyscan,
*multi, *exec, *srem, *hdel, *zrem,
*multi, *exec, *srem, *hdel, *zrem, *mvccrestore, *pexpirememberat,
*select[PROTO_SHARED_SELECT_CMDS],
*integers[OBJ_SHARED_INTEGERS],
*mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */
@@ -1194,7 +1229,7 @@ typedef struct zskiplist {
} zskiplist;
typedef struct zset {
dict *pdict;
::dict *dict;
zskiplist *zsl;
} zset;
@@ -1219,7 +1254,7 @@ typedef struct redisOp {
} redisOp;
/* Defines an array of Redis operations. There is an API to add to this
* structure in a easy way.
* structure in an easy way.
*
* redisOpArrayInit();
* redisOpArrayAppend();
@@ -1326,9 +1361,11 @@ struct clusterState;
#endif
#define CHILD_INFO_MAGIC 0xC17DDA7A12345678LL
#define CHILD_INFO_TYPE_RDB 0
#define CHILD_INFO_TYPE_AOF 1
#define CHILD_INFO_TYPE_MODULE 3
#define CHILD_TYPE_NONE 0
#define CHILD_TYPE_RDB 1
#define CHILD_TYPE_AOF 2
#define CHILD_TYPE_LDB 3
#define CHILD_TYPE_MODULE 4
#define MAX_EVENT_LOOPS 16
#define IDX_EVENT_LOOP_MAIN 0
@@ -1356,6 +1393,7 @@ struct redisServerThreadVars {
long unsigned commandsExecuted = 0;
bool fRetrySetAofEvent = false;
std::vector<client*> vecclientsProcess;
dictAsyncRehashCtl *rehashCtl = nullptr;
};
struct redisMaster {
@@ -1393,6 +1431,7 @@ struct redisMaster {
struct redisServerConst {
pid_t pid; /* Main process pid. */
time_t stat_starttime; /* Server start time */
pthread_t main_thread_id; /* Main thread id */
char *configfile; /* Absolute config file path, or NULL */
char *executable; /* Absolute executable file path. */
char **exec_argv; /* Executable argv vector (copy). */
@@ -1440,6 +1479,7 @@ struct redisServerConst {
bool fUsePro = false;
int thread_min_client_threshold = 50;
int multimaster_no_forward;
int enable_motd; /* Flag to retrieve the Message of today using CURL request*/
};
struct redisServer {
@@ -1448,12 +1488,14 @@ struct redisServer {
int config_hz; /* Configured HZ value. May be different than
the actual 'hz' field value if dynamic-hz
is enabled. */
std::atomic<int> hz; /* serverCron() calls frequency in hertz */
std::atomic<int> hz; /* serverCron() calls frequency in hertz */
int in_fork_child; /* indication that this is a fork child */
redisDb *db;
dict *commands; /* Command table */
dict *orig_commands; /* Command table before command renaming. */
::dict *commands; /* Command table */
::dict *orig_commands; /* Command table before command renaming. */
struct redisServerThreadVars rgthreadvar[MAX_EVENT_LOOPS];
pthread_t rgthread[MAX_EVENT_LOOPS];
std::atomic<unsigned int> lruclock; /* Clock for LRU eviction */
std::atomic<int> shutdown_asap; /* SHUTDOWN needed ASAP */
@@ -1464,9 +1506,10 @@ struct redisServer {
int sentinel_mode; /* True if this instance is a Sentinel. */
size_t initial_memory_usage; /* Bytes used after initialization. */
int always_show_logo; /* Show logo even for non-stdout logging. */
char *ignore_warnings; /* Config: warnings that should be ignored. */
/* Modules */
dict *moduleapi; /* Exported core APIs dictionary for modules. */
dict *sharedapi; /* Like moduleapi but containing the APIs that
::dict *moduleapi; /* Exported core APIs dictionary for modules. */
::dict *sharedapi; /* Like moduleapi but containing the APIs that
modules share with each other. */
list *loadmodule_queue; /* List of modules to load at startup. */
pid_t module_child_pid; /* PID of module child */
@@ -1487,7 +1530,7 @@ struct redisServer {
rax *clients_timeout_table; /* Radix tree for blocked clients timeouts. */
rax *clients_index; /* Active clients dictionary by client ID. */
mstime_t clients_pause_end_time; /* Time when we undo clients_paused */
dict *migrate_cached_sockets;/* MIGRATE cached sockets */
::dict *migrate_cached_sockets;/* MIGRATE cached sockets */
std::atomic<uint64_t> next_client_id; /* Next client unique ID. Incremental. */
int protected_mode; /* Don't accept external connections. */
long long events_processed_while_blocked; /* processEventsWhileBlocked() */
@@ -1497,7 +1540,8 @@ struct redisServer {
off_t loading_total_bytes;
off_t loading_loaded_bytes;
time_t loading_start_time;
off_t loading_process_events_interval_bytes;
unsigned long loading_process_events_interval_bytes;
unsigned int loading_process_events_interval_keys;
int active_expire_enabled; /* Can be disabled for testing purposes. */
@@ -1535,6 +1579,10 @@ struct redisServer {
size_t stat_module_cow_bytes; /* Copy on write bytes during module fork. */
uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */
long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */
long long stat_io_reads_processed; /* Number of read events processed by IO / Main threads */
long long stat_io_writes_processed; /* Number of write events processed by IO / Main threads */
std::atomic<long long> stat_total_reads_processed; /* Total number of read events processed */
std::atomic<long long> stat_total_writes_processed; /* Total number of write events processed */
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */
struct {
@@ -1670,7 +1718,7 @@ struct redisServer {
char *slave_announce_ip; /* Give the master this ip address. */
int repl_slave_lazy_flush; /* Lazy FLUSHALL before loading DB? */
/* Replication script cache. */
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
::dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
list *repl_scriptcache_fifo; /* First in, first out LRU eviction. */
unsigned int repl_scriptcache_size; /* Max number of elements. */
/* Synchronous replication. */
@@ -1680,10 +1728,13 @@ struct redisServer {
unsigned int maxclients; /* Max number of simultaneous clients */
unsigned long long maxmemory; /* Max number of memory bytes to use */
int maxmemory_policy; /* Policy for key eviction */
int maxmemory_samples; /* Pricision of random sampling */
int maxmemory_samples; /* Precision of random sampling */
int lfu_log_factor; /* LFU logarithmic counter factor. */
int lfu_decay_time; /* LFU counter decay factor. */
long long proto_max_bulk_len; /* Protocol bulk length maximum size. */
int oom_score_adj_base; /* Base oom_score_adj value, as observed on startup */
int oom_score_adj_values[CONFIG_OOM_COUNT]; /* Linux oom_score_adj configuration */
int oom_score_adj; /* If true, oom_score_adj is managed */
/* Blocked clients */
unsigned int blocked_clients; /* # of clients executing a blocking cmd.*/
unsigned int blocked_clients_by_type[BLOCKED_NUM];
@@ -1716,9 +1767,9 @@ struct redisServer {
mstime_t mstime; /* 'unixtime' in milliseconds. */
ustime_t ustime; /* 'unixtime' in microseconds. */
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
::dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
dict *pubsub_patterns_dict; /* A dict of pubsub_patterns */
::dict *pubsub_patterns_dict; /* A dict of pubsub_patterns */
int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
xor of NOTIFY_... flags. */
/* Cluster */
@@ -1741,11 +1792,12 @@ struct redisServer {
REDISMODULE_CLUSTER_FLAG_*. */
int cluster_allow_reads_when_down; /* Are reads allowed when the cluster
is down? */
int cluster_config_file_lock_fd; /* cluster config fd, will be flock */
/* Scripting */
lua_State *lua; /* The Lua interpreter. We use just one for all clients */
client *lua_caller = nullptr; /* The client running EVAL right now, or NULL */
char* lua_cur_script = nullptr; /* SHA1 of the script currently running, or NULL */
dict *lua_scripts; /* A dictionary of SHA1 -> Lua scripts */
::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 */
mstime_t lua_time_start; /* Start time of script, milliseconds time */
@@ -1754,7 +1806,7 @@ struct redisServer {
int lua_random_dirty; /* True if a random command was called during the
execution of the current script. */
int lua_replicate_commands; /* True if we are doing single commands repl. */
int lua_multi_emitted;/* True if we already proagated MULTI. */
int lua_multi_emitted;/* True if we already propagated MULTI. */
int lua_repl; /* Script replication flags for redis.set_repl(). */
int lua_timedout; /* True if we reached the time limit for script
execution. */
@@ -1768,7 +1820,7 @@ struct redisServer {
int lazyfree_lazy_user_del;
/* Latency monitor */
long long latency_monitor_threshold;
dict *latency_events;
::dict *latency_events;
/* ACLs */
char *acl_filename; /* ACL Users file. NULL if not configured. */
unsigned long acllog_max_len; /* Maximum length of the ACL LOG list. */
@@ -1802,6 +1854,10 @@ struct redisServer {
char *bio_cpulist; /* cpu affinity list of bio thread. */
char *aof_rewrite_cpulist; /* cpu affinity list of aof rewrite process. */
char *bgsave_cpulist; /* cpu affinity list of bgsave process. */
long long repl_batch_offStart = -1;
long long repl_batch_idxStart = -1;
};
typedef struct pubsubPattern {
@@ -1809,8 +1865,21 @@ typedef struct pubsubPattern {
robj *pattern;
} pubsubPattern;
#define MAX_KEYS_BUFFER 256
/* A result structure for the various getkeys function calls. It lists the
* keys as indices to the provided argv.
*/
typedef struct {
int keysbuf[MAX_KEYS_BUFFER]; /* Pre-allocated buffer, to save heap allocations */
int *keys; /* Key indices array, points to keysbuf or heap */
int numkeys; /* Number of key indices return */
int size; /* Available array size */
} getKeysResult;
#define GETKEYS_RESULT_INIT { {0}, NULL, 0, MAX_KEYS_BUFFER }
typedef void redisCommandProc(client *c);
typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
typedef int redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
struct redisCommand {
const char *name;
redisCommandProc *proc;
@@ -1922,7 +1991,7 @@ extern dictType modulesDictType;
void moduleInitModulesSystem(void);
int moduleLoad(const char *path, void **argv, int argc);
void moduleLoadFromQueue(void);
int *moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
moduleType *moduleTypeLookupModuleByID(uint64_t id);
void moduleTypeNameByID(char *name, uint64_t moduleid);
void moduleFreeContext(struct RedisModuleCtx *ctx);
@@ -1932,6 +2001,7 @@ void moduleBlockedClientTimedOut(client *c);
void moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);
size_t moduleCount(void);
void moduleAcquireGIL(int fServerThread);
int moduleTryAcquireGIL(bool fServerThread);
void moduleReleaseGIL(int fServerThread);
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
void moduleCallCommandFilters(client *c);
@@ -1979,30 +2049,29 @@ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void readQueryFromClient(connection *conn);
void addReplyNull(client *c, robj_roptr objOldProtocol = nullptr);
void addReplyNull(client *c);
void addReplyNullArray(client *c);
void addReplyNullArrayAsync(client *c);
void addReplyBool(client *c, int b);
void addReplyVerbatim(client *c, const char *s, size_t len, const char *ext);
void addReplyVerbatimAsync(client *c, const char *s, size_t len, const char *ext);
void addReplyProto(client *c, const char *s, size_t len);
void addReplyProtoCString(client *c, const char *s);
void addReplyBulk(client *c, robj_roptr obj);
void AddReplyFromClient(client *c, client *src);
void addReplyBulkCString(client *c, const char *s);
void addReplyBulkCStringAsync(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_roptr obj);
void addReplySds(client *c, sds s);
void addReplyBulkSds(client *c, sds s);
void addReplyErrorObject(client *c, robj *err, int severity);
void addReplyErrorSds(client *c, sds err);
void addReplyError(client *c, const char *err);
void addReplyStatus(client *c, const char *status);
void addReplyDouble(client *c, double d);
void addReplyHumanLongDouble(client *c, long double d);
void addReplyHumanLongDoubleAsync(client *c, long double d);
void addReplyLongLong(client *c, long long ll);
#ifdef __cplusplus
void addReplyLongLongWithPrefixCore(client *c, long long ll, char prefix, bool fAsync);
void addReplyLongLongWithPrefixCore(client *c, long long ll, char prefix);
#endif
void addReplyArrayLen(client *c, long length);
void addReplyMapLen(client *c, long length);
@@ -2047,23 +2116,6 @@ void linkClient(client *c);
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_roptr obj);
void addReplyArrayLenAsync(client *c, long length);
void addReplyProtoAsync(client *c, const char *s, size_t len);
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);
void addReplyNullAsync(client *c);
void addReplyDoubleAsync(client *c, double d);
void *addReplyDeferredLenAsync(client *c);
void setDeferredArrayLenAsync(client *c, void *node, long length);
void addReplySdsAsync(client *c, sds s);
void addReplyBulkSdsAsync(client *c, sds s);
void addReplyPushLenAsync(client *c, long length);
void addReplyLongLongAsync(client *c, long long ll);
void ProcessPendingAsyncWrites(void);
client *lookupClientByID(uint64_t id);
@@ -2111,9 +2163,10 @@ void initClientMultiState(client *c);
void freeClientMultiState(client *c);
void queueMultiCommand(client *c);
void touchWatchedKey(redisDb *db, robj *key);
void touchWatchedKeysOnFlush(int dbid);
void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with);
void discardTransaction(client *c);
void flagTransaction(client *c);
void execCommandAbort(client *c, sds error);
void execCommandPropagateMulti(client *c);
void execCommandPropagateExec(client *c);
@@ -2165,7 +2218,7 @@ const char *strEncoding(int encoding);
int compareStringObjects(robj *a, robj *b);
int collateStringObjects(robj *a, robj *b);
int equalStringObjects(robj *a, robj *b);
unsigned long long estimateObjectIdleTime(robj *o);
unsigned long long estimateObjectIdleTime(robj_roptr o);
void trimStringObjectIfNeeded(robj *o);
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
@@ -2177,7 +2230,7 @@ ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout);
/* Replication */
void initMasterInfo(struct redisMaster *master);
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen);
void replicationFeedSlavesFromMasterStream(char *buf, size_t buflen);
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc);
void updateSlavesWaitingBgsave(int bgsaveerr, int type);
void replicationCron(void);
@@ -2209,6 +2262,8 @@ void updateMasterAuth();
void showLatestBacklog();
void rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
void rdbPipeWriteHandlerConnRemoved(struct connection *conn);
void replicationNotifyLoadedKey(redisDb *db, robj_roptr key, robj_roptr val, long long expire);
void replicateSubkeyExpire(redisDb *db, robj_roptr key, robj_roptr subkey, long long expire);
/* Generic persistence functions */
void startLoadingFile(FILE* fp, const char * filename, int rdbflags);
@@ -2227,6 +2282,7 @@ int writeCommandsDeniedByDiskError(void);
/* RDB persistence */
#include "rdb.h"
void killRDBChild(void);
int bg_unlink(const char *filename);
/* AOF persistence */
void flushAppendOnlyFile(int force);
@@ -2250,7 +2306,7 @@ void sendChildInfo(int process_type);
void receiveChildInfo(void);
/* Fork helpers */
int redisFork();
int redisFork(int type);
int hasActiveChildProcess();
void sendChildCOWInfo(int ptype, const char *pname);
@@ -2298,7 +2354,7 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username);
/* Flags only used by the ZADD command but not by zsetAdd() API: */
#define ZADD_CH (1<<16) /* Return num of elements added or updated. */
/* Struct to hold a inclusive/exclusive range spec by score comparison. */
/* Struct to hold an inclusive/exclusive range spec by score comparison. */
typedef struct {
double min, max;
int minex, maxex; /* are min or max exclusive? */
@@ -2389,6 +2445,7 @@ const char *evictPolicyToString(void);
struct redisMemOverhead *getMemoryOverheadData(void);
void freeMemoryOverheadData(struct redisMemOverhead *mh);
void checkChildrenDone(void);
int setOOMScoreAdj(int process_class);
#define RESTART_SERVER_NONE 0
#define RESTART_SERVER_GRACEFULLY (1<<0) /* Do proper shutdown. */
@@ -2452,7 +2509,7 @@ void appendServerSaveParams(time_t seconds, int changes);
void resetServerSaveParams(void);
struct rewriteConfigState; /* Forward declaration to export API. */
void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);
int rewriteConfig(char *path);
int rewriteConfig(char *path, int force_all);
void initConfigValues();
/* db.c -- Keyspace access API */
@@ -2466,18 +2523,20 @@ expireEntry *getExpire(redisDb *db, robj_roptr key);
void setExpire(client *c, redisDb *db, robj *key, robj *subkey, long long when);
void setExpire(client *c, redisDb *db, robj *key, expireEntry &&entry);
robj_roptr lookupKeyRead(redisDb *db, robj *key);
int checkAlreadyExpired(long long when);
robj *lookupKeyWrite(redisDb *db, robj *key);
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
robj_roptr lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
robj *lookupKeyWriteWithFlags(redisDb *db, robj *key, int flags);
robj *objectCommandLookup(client *c, robj *key);
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);
robj_roptr objectCommandLookup(client *c, robj *key);
robj_roptr objectCommandLookupOrReply(client *c, robj *key, robj *reply);
int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
long long lru_clock, int lru_multiplier);
#define LOOKUP_NONE 0
#define LOOKUP_NOTOUCH (1<<0)
#define LOOKUP_UPDATEMVCC (1<<1)
#define LOOKUP_NONOTIFY (1<<1)
#define LOOKUP_UPDATEMVCC (1<<2)
void dbAdd(redisDb *db, robj *key, robj *val);
int dbAddRDBLoad(redisDb *db, sds key, robj *val);
void dbOverwrite(redisDb *db, robj *key, robj *val);
@@ -2492,11 +2551,14 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);
#define EMPTYDB_NO_FLAGS 0 /* No flags. */
#define EMPTYDB_ASYNC (1<<0) /* Reclaim memory in another thread. */
#define EMPTYDB_BACKUP (1<<2) /* DB array is a backup for REPL_DISKLESS_LOAD_SWAPDB. */
long long emptyDb(int dbnum, int flags, void(callback)(void*));
long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(void*));
long long emptyDbStructure(redisDb *dbarray, int dbnum, int async, void(callback)(void*));
void flushAllDataAndResetRDB(int flags);
long long dbTotalServerKeyCount();
dbBackup *backupDb(void);
void restoreDbBackup(dbBackup *buckup);
void discardDbBackup(dbBackup *buckup, int flags, void(callback)(void*));
int selectDb(client *c, int id);
void signalModifiedKey(client *c, redisDb *db, robj *key);
@@ -2509,24 +2571,27 @@ void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor);
int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);
void slotToKeyAdd(sds key);
void slotToKeyDel(sds key);
void slotToKeyFlush(void);
int dbAsyncDelete(redisDb *db, robj *key);
void emptyDbAsync(redisDb *db);
void slotToKeyFlushAsync(void);
void slotToKeyFlush(int async);
size_t lazyfreeGetPendingObjectsCount(void);
void freeObjAsync(robj *o);
void freeObjAsync(robj *obj);
void freeSlotsToKeysMapAsync(rax *rt);
void freeSlotsToKeysMap(rax *rt, int async);
/* API to get key arguments from commands */
int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
void getKeysFreeResult(int *result);
int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys);
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *getKeysPrepareResult(getKeysResult *result, int numkeys);
int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
void getKeysFreeResult(getKeysResult *result);
int zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, getKeysResult *result);
int evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
int lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result);
/* Cluster */
void clusterInit(void);
@@ -2537,6 +2602,7 @@ void clusterPropagatePublish(robj *channel, robj *message);
void migrateCloseTimedoutSockets(void);
void clusterBeforeSleep(void);
int clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, unsigned char *payload, uint32_t len);
void createDumpPayload(rio *payload, robj_roptr o, robj *key);
/* Sentinel */
void initSentinelConfig(void);
@@ -2673,6 +2739,7 @@ void flushdbCommand(client *c);
void flushallCommand(client *c);
void sortCommand(client *c);
void lremCommand(client *c);
void lposCommand(client *c);
void rpoplpushCommand(client *c);
void infoCommand(client *c);
void mgetCommand(client *c);
@@ -2753,6 +2820,7 @@ void watchCommand(client *c);
void unwatchCommand(client *c);
void clusterCommand(client *c);
void restoreCommand(client *c);
void mvccrestoreCommand(client *c);
void migrateCommand(client *c);
void askingCommand(client *c);
void readonlyCommand(client *c);
@@ -2868,6 +2936,30 @@ inline int FCorrectThread(client *c)
}
#define AssertCorrectThread(c) serverAssert(FCorrectThread(c))
void flushReplBacklogToClients();
template<typename FN_PTR, class ...TARGS>
void runAndPropogateToReplicas(FN_PTR *pfn, TARGS... args) {
// Store the replication backlog starting params, we use this to know how much data was written.
// these are TLS in case we need to expand the buffer and therefore need to update them
bool fNestedProcess = (g_pserver->repl_batch_idxStart >= 0);
if (!fNestedProcess) {
g_pserver->repl_batch_offStart = g_pserver->master_repl_offset;
g_pserver->repl_batch_idxStart = g_pserver->repl_backlog_idx;
}
pfn(args...);
if (!fNestedProcess) {
flushReplBacklogToClients();
g_pserver->repl_batch_offStart = -1;
g_pserver->repl_batch_idxStart = -1;
}
}
void killThreads(void);
void makeThreadKillable(void);
/* TLS stuff */
void tlsInit(void);
void tlsInitThread();
+8 -1
View File
@@ -36,6 +36,10 @@
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#ifdef __DragonFly__
#include <pthread.h>
#include <pthread_np.h>
#endif
#ifdef __NetBSD__
#include <pthread.h>
#include <sched.h>
@@ -72,7 +76,7 @@ void setcpuaffinity(const char *cpulist) {
#ifdef __linux__
cpu_set_t cpuset;
#endif
#ifdef __FreeBSD__
#if defined (__FreeBSD__) || defined(__DragonFly__)
cpuset_t cpuset;
#endif
#ifdef __NetBSD__
@@ -139,6 +143,9 @@ void setcpuaffinity(const char *cpulist) {
#ifdef __FreeBSD__
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);
#endif
#ifdef __DragonFly__
pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
#endif
#ifdef __NetBSD__
pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);
cpuset_destroy(cpuset);
+89 -23
View File
@@ -50,6 +50,10 @@
#if !HAVE_SETPROCTITLE
#if (defined __linux || defined __APPLE__)
#ifdef __GLIBC__
#define HAVE_CLEARENV
#endif
extern char **environ;
static struct {
@@ -80,11 +84,9 @@ static inline size_t spt_min(size_t a, size_t b) {
* For discussion on the portability of the various methods, see
* http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
*/
static int spt_clearenv(void) {
#if __GLIBC__
clearenv();
return 0;
int spt_clearenv(void) {
#ifdef HAVE_CLEARENV
return clearenv();
#else
extern char **environ;
static char **tmp;
@@ -100,34 +102,62 @@ static int spt_clearenv(void) {
} /* spt_clearenv() */
static int spt_copyenv(char *oldenv[]) {
static int spt_copyenv(int envc, char *oldenv[]) {
extern char **environ;
char **envcopy = NULL;
char *eq;
int i, error;
int envsize;
if (environ != oldenv)
return 0;
if ((error = spt_clearenv()))
goto error;
/* Copy environ into envcopy before clearing it. Shallow copy is
* enough as clearenv() only clears the environ array.
*/
envsize = (envc + 1) * sizeof(char *);
envcopy = malloc(envsize);
if (!envcopy)
return ENOMEM;
memcpy(envcopy, oldenv, envsize);
for (i = 0; oldenv[i]; i++) {
if (!(eq = strchr(oldenv[i], '=')))
/* Note that the state after clearenv() failure is undefined, but we'll
* just assume an error means it was left unchanged.
*/
if ((error = spt_clearenv())) {
environ = oldenv;
free(envcopy);
return error;
}
/* Set environ from envcopy */
for (i = 0; envcopy[i]; i++) {
if (!(eq = strchr(envcopy[i], '=')))
continue;
*eq = '\0';
error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;
error = (0 != setenv(envcopy[i], eq + 1, 1))? errno : 0;
*eq = '=';
if (error)
goto error;
/* On error, do our best to restore state */
if (error) {
#ifdef HAVE_CLEARENV
/* We don't assume it is safe to free environ, so we
* may leak it. As clearenv() was shallow using envcopy
* here is safe.
*/
environ = envcopy;
#else
free(envcopy);
free(environ); /* Safe to free, we have just alloc'd it */
environ = oldenv;
#endif
return error;
}
}
free(envcopy);
return 0;
error:
environ = oldenv;
return error;
} /* spt_copyenv() */
@@ -148,32 +178,57 @@ static int spt_copyargs(int argc, char *argv[]) {
return 0;
} /* spt_copyargs() */
/* Initialize and populate SPT to allow a future setproctitle()
* call.
*
* As setproctitle() basically needs to overwrite argv[0], we're
* trying to determine what is the largest contiguous block
* starting at argv[0] we can use for this purpose.
*
* As this range will overwrite some or all of the argv and environ
* strings, a deep copy of these two arrays is performed.
*/
void spt_init(int argc, char *argv[]) {
char **envp = environ;
char *base, *end, *nul, *tmp;
int i, error;
int i, error, envc;
if (!(base = argv[0]))
return;
/* We start with end pointing at the end of argv[0] */
nul = &base[strlen(base)];
end = nul + 1;
/* Attempt to extend end as far as we can, while making sure
* that the range between base and end is only allocated to
* argv, or anything that immediately follows argv (presumably
* envp).
*/
for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
if (!argv[i] || argv[i] < end)
continue;
end = argv[i] + strlen(argv[i]) + 1;
if (end >= argv[i] && end <= argv[i] + strlen(argv[i]))
end = argv[i] + strlen(argv[i]) + 1;
}
/* In case the envp array was not an immediate extension to argv,
* scan it explicitly.
*/
for (i = 0; envp[i]; i++) {
if (envp[i] < end)
continue;
end = envp[i] + strlen(envp[i]) + 1;
if (end >= envp[i] && end <= envp[i] + strlen(envp[i]))
end = envp[i] + strlen(envp[i]) + 1;
}
envc = i;
/* We're going to deep copy argv[], but argv[0] will still point to
* the old memory for the purpose of updating the title so we need
* to keep the original value elsewhere.
*/
if (!(SPT.arg0 = strdup(argv[0])))
goto syerr;
@@ -194,8 +249,8 @@ void spt_init(int argc, char *argv[]) {
setprogname(tmp);
#endif
if ((error = spt_copyenv(envp)))
/* Now make a full deep copy of the environment and argv[] */
if ((error = spt_copyenv(envc, envp)))
goto error;
if ((error = spt_copyargs(argc, argv)))
@@ -263,3 +318,14 @@ error:
#endif /* __linux || __APPLE__ */
#endif /* !HAVE_SETPROCTITLE */
#ifdef SETPROCTITLE_TEST_MAIN
int main(int argc, char *argv[]) {
spt_init(argc, argv);
printf("SPT.arg0: [%p] '%s'\n", SPT.arg0, SPT.arg0);
printf("SPT.base: [%p] '%s'\n", SPT.base, SPT.base);
printf("SPT.end: [%p] (%d bytes after base)'\n", SPT.end, (int) (SPT.end - SPT.base));
return 0;
}
#endif
+3 -3
View File
@@ -22,7 +22,7 @@
1. We use SipHash 1-2. This is not believed to be as strong as the
suggested 2-4 variant, but AFAIK there are not trivial attacks
against this reduced-rounds version, and it runs at the same speed
as Murmurhash2 that we used previously, why the 2-4 variant slowed
as Murmurhash2 that we used previously, while the 2-4 variant slowed
down Redis by a 4% figure more or less.
2. Hard-code rounds in the hope the compiler can optimize it more
in this raw from. Anyway we always want the standard 2-4 variant.
@@ -36,7 +36,7 @@
perform a text transformation in some temporary buffer, which is costly.
5. Remove debugging code.
6. Modified the original test.c file to be a stand-alone function testing
the function in the new form (returing an uint64_t) using just the
the function in the new form (returning an uint64_t) using just the
relevant test vector.
*/
#include <assert.h>
@@ -46,7 +46,7 @@
#include <ctype.h>
/* Fast tolower() alike function that does not care about locale
* but just returns a-z insetad of A-Z. */
* but just returns a-z instead of A-Z. */
int siptlw(int c) {
if (c >= 'A' && c <= 'Z') {
return c+('a'-'A');
+1 -1
View File
@@ -75,7 +75,7 @@ slowlogEntry *slowlogCreateEntry(client *c, robj **argv, int argc, long long dur
} else if (argv[j]->getrefcount(std::memory_order_relaxed) == OBJ_SHARED_REFCOUNT) {
se->argv[j] = argv[j];
} else {
/* Here we need to dupliacate the string objects composing the
/* Here we need to duplicate the string objects composing the
* argument vector of the command, because those may otherwise
* end shared with string objects stored into keys. Having
* shared objects between any part of Redis, and the data
+5 -5
View File
@@ -116,7 +116,7 @@ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst, int writeflag)
if (fieldobj) {
if (o->type != OBJ_HASH) goto noobj;
/* Retrieve value from hash by the field name. The returend object
/* Retrieve value from hash by the field name. The returned object
* is a new object with refcount already incremented. */
o = hashTypeGetValueObject(o, szFromObj(fieldobj));
} else {
@@ -271,7 +271,7 @@ void sortCommand(client *c) {
}
/* Lookup the key to sort. It must be of the right types */
if (storekey)
if (!storekey)
sortval = lookupKeyRead(c->db,c->argv[1]).unsafe_robjcast();
else
sortval = lookupKeyWrite(c->db,c->argv[1]);
@@ -317,7 +317,7 @@ void sortCommand(client *c) {
switch(sortval->type) {
case OBJ_LIST: vectorlen = listTypeLength(sortval); break;
case OBJ_SET: vectorlen = setTypeSize(sortval); break;
case OBJ_ZSET: vectorlen = dictSize(((zset*)ptrFromObj(sortval))->pdict); break;
case OBJ_ZSET: vectorlen = dictSize(((zset*)ptrFromObj(sortval))->dict); break;
default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
}
@@ -412,7 +412,7 @@ void sortCommand(client *c) {
/* Check if starting point is trivial, before doing log(N) lookup. */
if (desc) {
long zsetlen = dictSize(((zset*)ptrFromObj(sortval))->pdict);
long zsetlen = dictSize(((zset*)ptrFromObj(sortval))->dict);
ln = zsl->tail;
if (start > 0)
@@ -436,7 +436,7 @@ void sortCommand(client *c) {
end -= start;
start = 0;
} else if (sortval->type == OBJ_ZSET) {
dict *set = ((zset*)ptrFromObj(sortval))->pdict;
dict *set = ((zset*)ptrFromObj(sortval))->dict;
dictIterator *di;
dictEntry *setele;
sds sdsele;
+1 -1
View File
@@ -92,7 +92,7 @@ void freeSparklineSequence(struct sequence *seq) {
* ------------------------------------------------------------------------- */
/* Render part of a sequence, so that render_sequence() call call this function
* with differnent parts in order to create the full output without overflowing
* with different parts in order to create the full output without overflowing
* the current terminal columns. */
sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) {
int j;
+1 -1
View File
@@ -74,7 +74,7 @@ typedef struct streamConsumer {
consumer not yet acknowledged. Keys are
big endian message IDs, while values are
the same streamNACK structure referenced
in the "pel" of the conumser group structure
in the "pel" of the consumer group structure
itself, so the value is shared. */
} streamConsumer;
+5 -3
View File
@@ -532,7 +532,7 @@ void hsetCommand(client *c) {
robj *o;
if ((c->argc % 2) == 1) {
addReplyError(c,"wrong number of arguments for HMSET");
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",c->cmd->name);
return;
}
@@ -630,7 +630,7 @@ void hincrbyfloatCommand(client *c) {
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
* in order to make sure that differences in float precision or formatting
* will not create differences in replicas or after an AOF restart. */
robj *aux, *newobj;
aux = createStringObject("HSET",4);
@@ -772,7 +772,9 @@ void genericHgetallCommand(client *c, int flags) {
hashTypeIterator *hi;
int length, count = 0;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymap[c->resp]))
robj *emptyResp = (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) ?
shared.emptymap[c->resp] : shared.emptyarray;
if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyResp))
== nullptr || checkType(c,o,OBJ_HASH)) return;
/* We return a map if the user requested keys and values, like in the
+125 -7
View File
@@ -487,6 +487,124 @@ void ltrimCommand(client *c) {
addReply(c,shared.ok);
}
/* LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]
*
* The "rank" is the position of the match, so if it is 1, the first match
* is returned, if it is 2 the second match is returned and so forth.
* It is 1 by default. If negative has the same meaning but the search is
* performed starting from the end of the list.
*
* If COUNT is given, instead of returning the single element, a list of
* all the matching elements up to "num-matches" are returned. COUNT can
* be combiled with RANK in order to returning only the element starting
* from the Nth. If COUNT is zero, all the matching elements are returned.
*
* MAXLEN tells the command to scan a max of len elements. If zero (the
* default), all the elements in the list are scanned if needed.
*
* The returned elements indexes are always referring to what LINDEX
* would return. So first element from head is 0, and so forth. */
void lposCommand(client *c) {
robj *ele;
robj_roptr o;
ele = c->argv[2];
int direction = LIST_TAIL;
long rank = 1, count = -1, maxlen = 0; /* Count -1: option not given. */
/* Parse the optional arguments. */
for (int j = 3; j < c->argc; j++) {
char *opt = szFromObj(c->argv[j]);
int moreargs = (c->argc-1)-j;
if (!strcasecmp(opt,"RANK") && moreargs) {
j++;
if (getLongFromObjectOrReply(c, c->argv[j], &rank, NULL) != C_OK)
return;
if (rank == 0) {
addReplyError(c,"RANK can't be zero: use 1 to start from "
"the first match, 2 from the second, ...");
return;
}
} else if (!strcasecmp(opt,"COUNT") && moreargs) {
j++;
if (getLongFromObjectOrReply(c, c->argv[j], &count, NULL) != C_OK)
return;
if (count < 0) {
addReplyError(c,"COUNT can't be negative");
return;
}
} else if (!strcasecmp(opt,"MAXLEN") && moreargs) {
j++;
if (getLongFromObjectOrReply(c, c->argv[j], &maxlen, NULL) != C_OK)
return;
if (maxlen < 0) {
addReplyError(c,"MAXLEN can't be negative");
return;
}
} else {
addReply(c,shared.syntaxerr);
return;
}
}
/* A negative rank means start from the tail. */
if (rank < 0) {
rank = -rank;
direction = LIST_HEAD;
}
/* We return NULL or an empty array if there is no such key (or
* if we find no matches, depending on the presence of the COUNT option. */
if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) {
if (count != -1)
addReply(c,shared.emptyarray);
else
addReply(c,shared.null[c->resp]);
return;
}
if (checkType(c,o,OBJ_LIST)) return;
/* If we got the COUNT option, prepare to emit an array. */
void *arraylenptr = NULL;
if (count != -1) arraylenptr = addReplyDeferredLen(c);
/* Seek the element. */
listTypeIterator *li;
li = listTypeInitIterator(o,direction == LIST_HEAD ? -1 : 0,direction);
listTypeEntry entry;
long llen = listTypeLength(o);
long index = 0, matches = 0, matchindex = -1, arraylen = 0;
while (listTypeNext(li,&entry) && (maxlen == 0 || index < maxlen)) {
if (listTypeEqual(&entry,ele)) {
matches++;
matchindex = (direction == LIST_TAIL) ? index : llen - index - 1;
if (matches >= rank) {
if (arraylenptr) {
arraylen++;
addReplyLongLong(c,matchindex);
if (count && matches-rank+1 >= count) break;
} else {
break;
}
}
}
index++;
matchindex = -1; /* Remember if we exit the loop without a match. */
}
listTypeReleaseIterator(li);
/* Reply to the client. Note that arraylenptr is not NULL only if
* the COUNT option was selected. */
if (arraylenptr != NULL) {
setDeferredArrayLen(c,arraylenptr,arraylen);
} else {
if (matchindex != -1)
addReplyLongLong(c,matchindex);
else
addReply(c,shared.null[c->resp]);
}
}
void lremCommand(client *c) {
robj *subject, *obj;
obj = c->argv[3];
@@ -559,7 +677,7 @@ static void rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *val
listTypePush(dstobj,value,LIST_HEAD);
notifyKeyspaceEvent(NOTIFY_LIST,"lpush",dstkey,c->db->id);
/* Always send the pushed value to the client. */
addReplyBulkAsync(c,value);
addReplyBulk(c,value);
}
void rpoplpushCommand(client *c) {
@@ -606,7 +724,7 @@ void rpoplpushCommand(client *c) {
* Blocking POP operations
*----------------------------------------------------------------------------*/
/* This is a helper function for handleClientsBlockedOnKeys(). It's work
/* This is a helper function for handleClientsBlockedOnKeys(). Its work
* is to serve a specific client (receiver) that is blocked on 'key'
* in the context of the specified 'db', doing the following:
*
@@ -640,9 +758,9 @@ int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb
db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
/* BRPOP/BLPOP */
addReplyArrayLenAsync(receiver,2);
addReplyBulkAsync(receiver,key);
addReplyBulkAsync(receiver,value);
addReplyArrayLen(receiver,2);
addReplyBulk(receiver,key);
addReplyBulk(receiver,value);
/* Notify event. */
const char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
@@ -697,7 +815,7 @@ void blockingPopGenericCommand(client *c, int where) {
return;
} else {
if (listTypeLength(o) != 0) {
/* Non empty list, this is like a non normal [LR]POP. */
/* Non empty list, this is like a normal [LR]POP. */
const char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
robj *value = listTypePop(o,where);
serverAssert(value != NULL);
@@ -733,7 +851,7 @@ void blockingPopGenericCommand(client *c, int where) {
return;
}
/* If the list is empty or the key does not exists we must block */
/* If the keys do not exist we must block */
blockForKeys(c,BLOCKED_LIST,c->argv + 1,c->argc - 2,timeout,NULL,NULL);
}
+353
View File
@@ -0,0 +1,353 @@
/*
* Copyright (c) 2020, EQ Alpha Technology Ltd. <john at eqalpha dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "server.h"
#include <math.h>
void dictObjectDestructor(void *privdata, void *val);
dictType nestedHashDictType {
dictSdsHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCompare, /* key compare */
dictSdsDestructor, /* key destructor */
dictObjectDestructor, /* val destructor */
};
robj *createNestHashBucket() {
dict *d = dictCreate(&nestedHashDictType, nullptr);
return createObject(OBJ_NESTEDHASH, d);
}
void freeNestedHashObject(robj_roptr o) {
dictRelease((dict*)ptrFromObj(o));
}
robj *fetchFromKey(redisDb *db, robj_roptr key) {
const char *pchCur = szFromObj(key);
const char *pchStart = pchCur;
const char *pchMax = pchCur + sdslen(pchCur);
robj *o = nullptr;
while (pchCur <= pchMax) {
if (pchCur == pchMax || *pchCur == '.') {
// WARNING: Don't deref pchCur as it may be pchMax
// New word
if ((pchCur - pchStart) < 1) {
throw shared.syntaxerr; // malformed
}
dict *d = nullptr;
if (o == nullptr)
d = db->dict;
else
d = (dict*)ptrFromObj(o);
sdsstring str(pchStart, pchCur - pchStart);
dictEntry *de = dictFind(d, str.get());
o = (de != nullptr) ? (robj*)dictGetVal(de) : nullptr;
if (o == nullptr) throw shared.nokeyerr; // Not Found
serverAssert(o->type == OBJ_NESTEDHASH || o->type == OBJ_STRING || o->type == OBJ_LIST);
if (o->type == OBJ_STRING && pchCur != pchMax)
throw shared.nokeyerr; // Past the end
pchStart = pchCur + 1;
}
++pchCur;
}
return o;
}
// Returns one if we overwrote a value
bool setWithKey(redisDb *db, robj_roptr key, robj *val, bool fCreateBuckets) {
const char *pchCur = szFromObj(key);
const char *pchStart = pchCur;
const char *pchMax = pchCur + sdslen(pchCur);
robj *o = nullptr;
while (pchCur <= pchMax) {
if (pchCur == pchMax || *pchCur == '.') {
// WARNING: Don't deref pchCur as it may be pchMax
// New word
if ((pchCur - pchStart) < 1) {
throw shared.syntaxerr; // malformed
}
dict *d = nullptr;
if (o == nullptr)
d = db->dict;
else
d = (dict*)ptrFromObj(o);
sdsstring str(pchStart, pchCur - pchStart);
dictEntry *de = dictFind(d, str.get());
if (pchCur == pchMax) {
val->addref();
if (de != nullptr) {
decrRefCount((robj*)dictGetVal(de));
dictSetVal(d, de, val);
return true;
} else {
dictAdd(d, str.release(), val);
return false;
}
} else {
o = (de != nullptr) ? (robj*)dictGetVal(de) : nullptr;
if (o == nullptr) {
if (!fCreateBuckets)
throw shared.nokeyerr; // Not Found
o = createNestHashBucket();
serverAssert(dictAdd(d, str.release(), o) == DICT_OK);
} else if (o->type != OBJ_NESTEDHASH) {
decrRefCount(o);
o = createNestHashBucket();
de->v.val = o;
}
}
pchStart = pchCur + 1;
}
++pchCur;
}
throw "Internal Error";
}
void writeNestedHashToClient(client *c, robj_roptr o) {
if (o == nullptr) {
addReply(c, shared.null[c->resp]);
} else if (o->type == OBJ_STRING) {
addReplyBulk(c, o);
} else if (o->type == OBJ_LIST) {
unsigned char *zl = (unsigned char*)ptrFromObj(o);
addReplyArrayLen(c, ziplistLen(zl));
unsigned char *p = ziplistIndex(zl, ZIPLIST_HEAD);
while (p != nullptr) {
unsigned char *str;
unsigned int len;
long long lval;
if (ziplistGet(p, &str, &len, &lval)) {
char rgT[128];
if (str == nullptr) {
len = ll2string(rgT, 128, lval);
str = (unsigned char*)rgT;
}
addReplyBulkCBuffer(c, (const char*)str, len);
}
p = ziplistNext(zl, p);
}
} else {
serverAssert(o->type == OBJ_NESTEDHASH );
dict *d = (dict*)ptrFromObj(o);
if (dictSize(d) > 1)
addReplyArrayLen(c, dictSize(d));
dictIterator *di = dictGetIterator(d);
dictEntry *de;
while ((de = dictNext(di))) {
robj_roptr oT = (robj*)dictGetVal(de);
addReplyArrayLen(c, 2);
addReplyBulkCBuffer(c, (sds)dictGetKey(de), sdslen((sds)dictGetKey(de)));
if (oT->type == OBJ_STRING) {
addReplyBulk(c, oT);
} else {
writeNestedHashToClient(c, oT);
}
}
dictReleaseIterator(di);
}
}
inline bool FSimpleJsonEscapeCh(char ch) {
return (ch == '"' || ch == '\\');
}
inline bool FExtendedJsonEscapeCh(char ch) {
return ch <= 0x1F;
}
sds writeJsonValue(sds output, const char *valIn, size_t cchIn) {
const char *val = valIn;
size_t cch = cchIn;
int cchEscapeExtra = 0;
// First scan for escaped chars
for (size_t ich = 0; ich < cchIn; ++ich) {
if (FSimpleJsonEscapeCh(valIn[ich])) {
++cchEscapeExtra;
} else if (FExtendedJsonEscapeCh(valIn[ich])) {
cchEscapeExtra += 5;
}
}
if (cchEscapeExtra > 0) {
size_t ichDst = 0;
sds dst = sdsnewlen(SDS_NOINIT, cchIn+cchEscapeExtra);
for (size_t ich = 0; ich < cchIn; ++ich) {
switch (valIn[ich]) {
case '"':
dst[ichDst++] = '\\'; dst[ichDst++] = '"';
break;
case '\\':
dst[ichDst++] = '\\'; dst[ichDst++] = '\\';
break;
default:
serverAssert(!FSimpleJsonEscapeCh(valIn[ich]));
if (FExtendedJsonEscapeCh(valIn[ich])) {
dst[ichDst++] = '\\'; dst[ichDst++] = 'u';
sprintf(dst + ichDst, "%4x", valIn[ich]);
ichDst += 4;
} else {
dst[ichDst++] = valIn[ich];
}
break;
}
}
val = (const char*)dst;
serverAssert(ichDst == (cchIn+cchEscapeExtra));
cch = ichDst;
}
output = sdscat(output, "\"");
output = sdscatlen(output, val, cch);
output = sdscat(output, "\"");
if (val != valIn)
sdsfree(val);
return output;
}
sds writeJsonValue(sds output, sds val) {
return writeJsonValue(output, (const char*)val, sdslen(val));
}
sds writeNestedHashAsJson(sds output, robj_roptr o) {
if (o->type == OBJ_STRING) {
output = writeJsonValue(output, (sds)szFromObj(o));
} else if (o->type == OBJ_LIST) {
unsigned char *zl = (unsigned char*)ptrFromObj(o);
output = sdscat(output, "[");
unsigned char *p = ziplistIndex(zl, ZIPLIST_HEAD);
bool fFirst = true;
while (p != nullptr) {
unsigned char *str;
unsigned int len;
long long lval;
if (ziplistGet(p, &str, &len, &lval)) {
char rgT[128];
if (str == nullptr) {
len = ll2string(rgT, 128, lval);
str = (unsigned char*)rgT;
}
if (!fFirst)
output = sdscat(output, ",");
fFirst = false;
output = writeJsonValue(output, (const char*)str, len);
}
p = ziplistNext(zl, p);
}
output = sdscat(output, "]");
} else {
output = sdscat(output, "{");
dictIterator *di = dictGetIterator((dict*)ptrFromObj(o));
dictEntry *de;
bool fFirst = true;
while ((de = dictNext(di))) {
robj_roptr oT = (robj*)dictGetVal(de);
if (!fFirst)
output = sdscat(output, ",");
fFirst = false;
output = writeJsonValue(output, (sds)dictGetKey(de));
output = sdscat(output, " : ");
output = writeNestedHashAsJson(output, oT);
}
dictReleaseIterator(di);
output = sdscat(output, "}");
}
return output;
}
void nhsetCommand(client *c) {
if (c->argc < 3)
throw shared.syntaxerr;
robj *val = c->argv[2];
if (c->argc > 3) {
// Its a list, we'll store as a ziplist
val = createZiplistObject();
for (int iarg = 2; iarg < c->argc; ++iarg) {
sds arg = (sds)szFromObj(c->argv[iarg]);
val->m_ptr = ziplistPush((unsigned char*)ptrFromObj(val), (unsigned char*)arg, sdslen(arg), ZIPLIST_TAIL);
}
}
try {
if (setWithKey(c->db, c->argv[1], val, true)) {
addReplyLongLong(c, 1); // we replaced a value
} else {
addReplyLongLong(c, 0); // we added a new value
}
} catch (...) {
if (val != c->argv[2])
decrRefCount(val);
throw;
}
if (val != c->argv[2])
decrRefCount(val);
}
void nhgetCommand(client *c) {
if (c->argc != 2 && c->argc != 3)
throw shared.syntaxerr;
bool fJson = false;
int argOffset = 0;
if (c->argc == 3) {
argOffset++;
if (strcasecmp(szFromObj(c->argv[1]), "json") == 0) {
fJson = true;
} else if (strcasecmp(szFromObj(c->argv[1]), "resp") != 0) {
throw shared.syntaxerr;
}
}
robj *o = fetchFromKey(c->db, c->argv[argOffset + 1]);
if (fJson) {
sds val = writeNestedHashAsJson(sdsnew(nullptr), o);
addReplyBulkSds(c, val);
} else {
writeNestedHashToClient(c, o);
}
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
void freeNestedHashObject(robj_roptr o);
void nhsetCommand(client *c);
void nhgetCommand(client *c);
Loaded 100 of 187 files, more files were not shown because too many files have changed in this diff. Show more