Compare commits

...
781 Commits
Author SHA1 Message Date
Oran AgraandGitHub 959d6035e5 Merge 6.2.2 release
Release 6.2.2
2021-04-20 08:03:58 +03:00
Oran Agra aa730ef1ea Redis 6.2.2 2021-04-19 21:39:40 +03:00
Oran Agra f5ca1f9ee9 Merge unstable into 6.2 2021-04-19 21:36:00 +03:00
Viktor SöderqvistandGitHub 61d3fdb474 Fix space included in links in modules API doc (#8818) 2021-04-19 21:35:14 +03:00
Hanna FadidaandGitHub 53a4d6c3b1 Modules: adding a module type for key space notification (#8759)
Adding a new type mask ​for key space notification, REDISMODULE_NOTIFY_MODULE, to enable unique notifications from commands on REDISMODULE_KEYTYPE_MODULE type keys (which is currently unsupported).

Modules can subscribe to a module key keyspace notification by RM_SubscribeToKeyspaceEvents,
and clients by notify-keyspace-events of redis.conf or via the CONFIG SET, with the characters 'd' or 'A' 
(REDISMODULE_NOTIFY_MODULE type mask is part of the '**A**ll' notation for key space notifications).

Refactor: move some pubsub test infra from pubsub.tcl to util.tcl to be re-used by other tests.
2021-04-19 21:33:26 +03:00
guybe7andGitHub f40ca9cb58 Modules: Replicate lazy-expire even if replication is not allowed (#8816)
Before this commit using RM_Call without "!" could cause the master
to lazy-expire a key (delete it) but without replicating to replicas.
This could cause the replica's memory usage to gradually grow and
could also cause consistency issues if the master and replica have
a clock diff.
This bug was introduced in #8617

Added a test which demonstrates that scenario.
2021-04-19 17:16:02 +03:00
7a3d1487e4 ACL channels permission handling for save/load scenario. (#8794)
In the initial release of Redis 6.2 setting a user to only allow pubsub access to
a specific channel, and doing ACL SAVE, resulted in an assertion when
ACL LOAD was used. This was later changed by #8723 (not yet released),
but still not properly resolved (now it errors instead of crash).

The problem is that the server that generates an ACL file, doesn't know what
would be the setting of the acl-pubsub-default config in the server that will load it.
so ACL SAVE needs to always start with resetchannels directive.

This should still be compatible with old acl files (from redis 6.0), and ones from earlier
versions of 6.2 that didn't mess with channels.

Co-authored-by: Harkrishn Patro <harkrisp@amazon.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-04-19 13:27:44 +03:00
sundbandGitHub 3a955d9ad4 Fix ouput buffer limit test (#8803)
The tail size of c->reply is 16kb, but in the test only publish a
few chars each time, due to a change in #8699, the obuf limit
is now checked a new memory allocation is made, so this test
would have sometimes failed to trigger a soft limit disconnection
in time.

The solution is to write bigger payloads to the output buffer, but
still limit their rate (not more than 100k/s).
2021-04-19 10:08:07 +03:00
Wen HuiandGitHub 0413fbc7d0 fix invalid master_link_down_since_seconds in info repication (#8785)
When replica never successfully connect to master, server.repl_down_since
will be initialized to 0, therefore, the info master_link_down_since_seconds
was showing the current unix timestamp, which does not make much sense.

This commit fixes the issue by showing master_link_down_since_seconds to -1.
means the replica never connect to master before.

This commit also resets this variable back to 0 when a replica is turned into
a master, so that it'll behave the same if the master is later turned into a
replica again.

The implication of this change is that if some app is checking if the value is > 60
do something, like conclude the replica is stale, this could case harm (changing
a big positive number with a small one).
2021-04-19 09:34:21 +03:00
Yossi GottliebandGitHub c0f5c678c2 Revert cluster slot migration tests. (#8806)
Disables #8649 and subsequent attempts to stabilize the test.
2021-04-18 20:51:08 +03:00
Oran AgraandGitHub a9897b0084 Fix timing of new replication test (#8807)
In github actions CI with valgrind, i saw that even the fast replica
(one that wasn't paused), didn't get to complete the replication fast
enough, and ended up getting disconnected by timeout.

Additionally, due to a typo in uname, we didn't get to actually run the
CPU efficiency part of the test.
2021-04-18 15:12:34 +03:00
Oran AgraandGitHub f4b5a4d869 Improve testsuite print of log file (#8805)
1. the `dump_logs` option would have printed only logs of servers that were
   spawn before the test proc started, and not ones that the test proc
   started inside it.
2. when a server proc catches an exception it should normally forward the
   exception upwards, specifically when it's an assertion that should be
   caught by a test proc above. however, in `durable` mode, we caught all
   exceptions printed them to stdout and let the code continue,
   this was wrong to do for assertions, which should have still been
   propagated to the test function.
3. don't bother to search for crash log to print if we printed the the
   entire log anyway
4. if no crash log was found, no need to print anything (i.e. the fact it
   wasn't found)
5. rename warnings_from_file to crashlog_from_file
2021-04-18 11:55:54 +03:00
guybe7andGitHub a60016e061 ReplicationCron: Prevent invalid access to freed pointer (#8799)
Fixes #8797
2021-04-16 16:56:38 +03:00
Wen HuiandGitHub 374401d786 Avoid close before logging to preserve errno (#8703) 2021-04-15 21:11:40 +03:00
guybe7andGitHub 7d749d810b Fix error reply in case zset command is not the STORE variant (#8793) 2021-04-15 18:36:51 +03:00
guybe7andGitHub d63d02601f Add a timeout mechanism for replicas stuck in fullsync (#8762)
Starting redis 6.0 (part of the TLS feature), diskless master uses pipe from the fork
child so that the parent is the one sending data to the replicas.
This mechanism has an issue in which a hung replica will cause the master to wait
for it to read the data sent to it forever, thus preventing the fork child from terminating
and preventing the creations of any other forks.

This PR adds a timeout mechanism, much like the ACK-based timeout,
we disconnect replicas that aren't reading the RDB file fast enough.
2021-04-15 17:18:51 +03:00
BonsaiandGitHub 0a2621c673 clean condition and variable store to nwritten that is never read (#8788) 2021-04-14 22:44:08 -07:00
BonsaiandGitHub 07601b7e07 fix typo, stracture to structure (#8784) 2021-04-14 15:46:54 +03:00
YaacovHazanandGitHub 645c664fbb stabilized and improve pendingquerybuf test suit (#8780)
replace the hardcoded after 2000, with waiting for the sync and
wait for condition
2021-04-14 11:49:00 +03:00
Viktor SöderqvistandGitHub d7920ff9b1 Modules API docs: Sections and links (#8442)
* Modules API docs: Link API function names to their definitions

Occurrences of API functions are linked to their definition.

A function index with links to all functions is added on the bottom
of the page.

Comment blocks in module.c starting with a markdown h2 heading are
used as sections. A table of contents is generated from these
headings.

The functions names are changed from h2 to h3, since they are now
rendered as sub-headings within each section.

Existing sections in module.c are used with some minor changes.
Some documentation text is added or sligtly modified.

The markdown renderer will add IDs which may clash with our
generated IDs. By prefixing section IDs with "section-" we make
them different.

Replace double dashes with a unicode long ndash
2021-04-14 00:58:05 +03:00
Huang ZhwandGitHub c6cd1e59b1 Remove extra param in role-change event of sentinelEvent. (#8742) 2021-04-13 20:19:42 +03:00
Viktor SöderqvistandGitHub 4938052f6b Small doc fix for stream module API (#8757)
In a code example, using RedisModule_FreeString instead of
RedisModule_Free makes it behave correctly regardless of whether
automatic memory is used or not.
2021-04-13 20:14:12 +03:00
Oran AgraandGitHub b278e44376 Revert "Fix: server will crash if rdbload or rdbsave method is not provided in module (#8670)" (#8771)
This reverts commit 808f3004f0.
2021-04-13 17:41:46 +03:00
Oran AgraandGitHub c07e16fadd Add more attempts to a timing sensitive test (#8770) 2021-04-13 17:35:10 +03:00
Wen HuiandGitHub 38da8d07d0 Clean up no-conf server warning for sentinel mode (#8769) 2021-04-13 16:28:54 +03:00
Oran AgraandGitHub 733daef127 fix access to uninitialized var in checkClientPauseTimeoutAndReturnIfPaused (#8765)
server.client_pause_end_time is uninitialized, or actually 0, at startup,
which means this method would think the timeout was reached
and go look for paused clients.

This causes no harm since unpauseClients will not find any paused clients.
2021-04-13 08:41:12 +03:00
Oran AgraandGitHub 175a9e3199 Fix busy loop in ae.c when timer event is about to fire (#8764)
The code used to decide on the next time to wake on a timer with
microsecond accuracy, but when deciding to go to sleep it used
milliseconds accuracy (with truncation), this means that it would wake
up too early, see that there's no timer to process, and go to sleep
again for 0ms again and again until the right microsecond arrived.

i.e. a timer for 100ms, would sleep for 99ms, but then do a busy loop
through the kernel in the last millisecond, triggering many calls to
beforeSleep.

The fix is to change all the logic in ae.c to work with microseconds,
which is good since most of the ae backends support micro (or even nano)
seconds. however the epoll backend, doesn't support micro, so to avoid
this problem it needs to round upwards, rather than truncate.

Issue created by the monotonic timer PR #7644 (redis 6.2)
Before that, all the timers in ae.c were in milliseconds (using
mstime), so when it requested the backend to sleep till the next timer
event, it would have worked ok.
2021-04-13 07:35:03 +03:00
Yossi GottliebandGitHub 5e3a15ae1b Fix failing cluster tests. (#8763)
Disable replica migration to avoid a race condition where the
migrated-from node turns into a replica.

Long term, this test should probably be improved to handle multiple
slots and accept such auto migrations but this is a quick fix to
stabilize the CI without completely dropping this test.
2021-04-13 00:00:57 +03:00
Wang YuanandGitHub a0e19e3cf1 Fix wrong check for aof fsync and handle aof fsync errno (#8751)
The bio aof fsync fd may be closed by main thread (AOFRW done handler)
and even possibly reused for another socket, pipe, or file.
This can can an EBADF or EINVAL fsync error, which will lead to -MISCONF errors failing all writes.
We just ignore these errno because aof fsync did not really fail.

We handle errno when fsyncing aof in bio, so we could know the real reason
when users get -MISCONF Errors writing to the AOF file error

Issue created with #8419
2021-04-11 08:14:31 +03:00
yjphandGitHub cd03e293c3 Fix the display of make install (#8667) 2021-04-10 21:25:53 +03:00
Wen HuiandGitHub d96f47cf06 use getPositiveLongFromObjectOrReply for positive check of args (#8750)
just a cleanup
2021-04-07 10:28:53 +03:00
Yang BodongandGitHub 4c14e8668c Fix out of range confusing error messages (XAUTOCLAIM, RPOP count) (#8746)
Fix out of range error messages to be clearer (avoid mentioning 9223372036854775807)
* Fix XAUTOCLAIM COUNT option confusing error msg
* Fix other RPOP and alike error message to mention positive
2021-04-07 10:01:28 +03:00
Oran AgraandGitHub 4de1031d8a update help.h (#8747) 2021-04-06 12:42:18 +03:00
yjphandGitHub 073598ed8f Update the location information in some URLs (#8595) 2021-04-06 12:29:02 +03:00
BonsaiandGitHub 808f3004f0 Fix: server will crash if rdbload or rdbsave method is not provided in module (#8670)
With this fix, module data type registration will fail if the load or save callbacks are not defined, or the optional aux load and save callbacks are not either both defined or both missing.
2021-04-06 12:09:36 +03:00
Yossi GottliebandGitHub 4724dd439e Clean up and stabilize cluster migration tests. (#8745)
This is work in progress, focusing on two main areas:
* Avoiding race conditions with cluster configuration propagation.
* Ignoring limitations with redis-cli --cluster fix which makes it hard
  to distinguish real errors (e.g. failure to fix) from expected
  conditions in this test (e.g. nodes not agreeing on configuration).
2021-04-06 11:57:57 +03:00
Huang ZhwandGitHub 3b74b55084 Fix "default" and overwritten / reset users will not have pubsub channels permissions by default. (#8723)
Background:
Redis 6.2 added ACL control for pubsub channels (#7993), which were supposed
to be permissive by default to retain compatibility with redis 6.0 ACL. 
But due to a bug, only newly created users got this `acl-pubsub-default` applied,
while overwritten (updated) users got reset to `resetchannels` (denied).

Since the "default" user exists before loading the config file,
any ACL change to it, results in an update / overwrite.

So when a "default" user is loaded from config file or include ACL
file with no channels related rules, the user will not have any
permissions to any channels. But other users will have default
permissions to any channels.

When upgraded from 6.0 with config rewrite, this will lead to
"default" user channels permissions lost.
When users are loaded from include file, then call "acl load", users
will also lost channels permissions.

Similarly, the `reset` ACL rule, would have reset the user to be denied
access to any channels, ignoring `acl-pubsub-default` and breaking
compatibility with redis 6.0.

The implication of this fix is that it regains compatibility with redis 6.0,
but breaks compatibility with redis 6.2.0 and 2.0.1. e.g. after the upgrade,
the default user will regain access to pubsub channels.

Other changes:
Additionally this commit rename server.acl_pubusub_default to
server.acl_pubsub_default and fix typo in acl tests.
2021-04-05 23:13:20 +03:00
Huang ZhwandGitHub a3da3e592d redis-cli --bigkeys / memkeys, report detailed error on dbsize failure (#8740)
When DBSIZE failed (e.g. on AUTH error), the printed error didn't reflect the reason.
2021-04-05 08:30:41 +03:00
1cab962098 Add cluster-allow-replica-migration option. (#5285)
Previously (and by default after commit) when master loose its last slot
(due to migration, for example), its replicas will migrate to new last slot
holder.

There are cases where this is not desired:
* Consolidation that results with removed nodes (including the replica, eventually).
* Manually configured cluster topologies, which the admin wishes to preserve.

Needlessly migrating a replica triggers a full synchronization and can have a negative impact, so
we prefer to be able to avoid it where possible.

This commit adds 'cluster-allow-replica-migration' configuration option that is
enabled by default to preserve existed behavior. When disabled, replicas will
not be auto-migrated.

Fixes #4896

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-04-04 09:43:24 +03:00
Wang YuanandGitHub 1eb85249e7 Handle remaining fsync errors (#8419)
In `aof.c`, we call fsync when stop aof, and now print a log to let user know that if fail.
In `cluster.c`, we now return error, the calling function already handles these write errors.
In `redis-cli.c`, users hope to save rdb, we now print a message if fsync failed.
In `rio.c`, we now treat fsync errors like we do for write errors. 
In `server.c`, we try to fsync aof file when shutdown redis, we only can print one log if fail.
In `bio.c`, if failing to fsync aof file, we will set `aof_bio_fsync_status` to error , and reject writing just like last writing aof error,  moreover also set INFO command field `aof_last_write_status` to error.
2021-04-01 12:45:15 +03:00
Valentino GeronandGitHub 44d8b039e8 Fix XAUTOCLAIM response to return the next available id as the cursor (#8725)
This command used to return the last scanned entry id as the cursor,
instead of the next one to be scanned.
so in the next call, the user could / should have sent `(cursor` and not
just `cursor` if he wanted to avoid scanning the same record twice.

Scanning the record twice would look odd if someone is checking what
exactly was scanned, but it also has a side effect of incrementing the
delivery count twice.
2021-04-01 12:13:55 +03:00
Oran AgraandGitHub 370ab4c4db Solve sentinel test issue in TLS due to recent tests change. (#8728)
5629dbe71 added a change that configures the tcp (plaintext) port
alongside the tls port, this causes the INFO command for tcp_port
to return that instead of the tls port when running in tls, and that broke
the sentinel tests that query it.

the fix is to add a method that gets the right port from CONFIG instead
of relying on the tcp_port info field.
2021-04-01 09:44:44 +03:00
guybe7andGitHub 843f769b96 zsetAdd: Fix wrong reply in case of INCR and GT/LT (#8717)
If GT/LT fails the operation we need to reply with
nill (like failure due to NX).

Other changes:
Add the missing $encoding suffix to many zset tests

Note: there's a behavior change just in case of INCR + GT/LT that fails.
The old code was replying with the wrong (rejected) score, and now it'll reply with nil.

Note that that's anyway a corner case so this "behavior change" shouldn't have too much affect.
Using GT/LT with INCR has a predictable result even before we run the command
(INCR GT will only only / always fail if the increment is negative).
2021-04-01 09:33:53 +03:00
Wen HuiandGitHub d5935bb0a4 generalize config file check for sentinel (#8730)
The implications of this change is just that in the past when a config file was missing,
in some cases it was exiting before printing the sever startup prints and sometimes after,
and now it'll always exit before printing them.
2021-04-01 09:01:05 +03:00
wuYinandGitHub e5d50b236c reuse existing range comparators in the zset (#8714)
There are 2 common range comparators for skiplist: zslValueGteMin and
zslValueLteMax, but they're not being reused in zslDeleteRangeByScore

This is a small change to make code cleaner.
2021-04-01 08:50:23 +03:00
Dvir VolkandGitHub 07f39ae47b Added macros for RM_log logging levels (#4246)
Added macros for RM_log logging levels, to avoid typos and the need
to memorize the level strings by heart
2021-04-01 08:44:57 +03:00
sundbandGitHub 569a3f4548 Use chi-square for random distributivity verification in test (#8709)
Problem:
Currently, when performing random distribution verification, we determine
the probability of each element occurring in the sum, but the probability is
only an estimate, these tests had rare sporadic failures, and we cannot verify
what the probability of failure will be.

Solution:
Using the chi-square distribution instead of the original random distribution
validation makes the test more reasonable and easier to find problems.
2021-04-01 08:20:15 +03:00
yoav-steinbergandGitHub 636aa8de76 Always use prev node if there's some room in it setDeferredReply (#8729)
this is a followup PR for #8699
instead of copying the deferred reply data to the previous node only if it has room for the entire thing,
we can now split the new payload, put part of it into the spare space in the prev node,
and the rest may fit into the next node.
2021-04-01 00:07:19 +03:00
guybe7andGitHub 68ba4d43ac Initialize RedisModuleDigest with zeroes (#8731)
In case the module's digest function doesn't modify
'md' it'll contain garbage and result in wrong
DEBUG DIGEST
2021-03-31 23:56:04 +03:00
guybe7andGitHub 2c120af61e XAUTOCLAIM: JUSTID should prevent incrementing delivery_count (#8724)
To align with XCLAIM and the XAUTOCLAIM docs
2021-03-30 23:48:01 +03:00
Jérôme LoyetandGitHub 91f4f41665 Add replica-announced config option (#8653)
The 'sentinel replicas <master>' command will ignore replicas with
`replica-announced` set to no.

The goal of disabling the config setting replica-announced is to allow ghost
replicas. The replica is in the cluster, synchronize with its master, can be
promoted to master and is not exposed to sentinel clients. This way, it is
acting as a live backup or living ghost.

In addition, to prevent the replica to be promoted as master, set
replica-priority to 0.
2021-03-30 23:40:22 +03:00
Yossi GottliebandGitHub 6a052af890 Cluster migration test cleanup. (#8726)
* Dump more output on error (always, cluster tests currently have no
verbose flag).
* Slow down redis-cli check iteration.
2021-03-30 23:33:01 +03:00
Viktor SöderqvistandGitHub 5629dbe715 Add support for plaintext clients in TLS cluster (#8587)
The cluster bus is established over TLS or non-TLS depending on the configuration tls-cluster. The client ports distributed in the cluster and sent to clients are assumed to be TLS or non-TLS also depending on tls-cluster.

The cluster bus is now extended to also contain the non-TLS port of clients in a TLS cluster, when available. The non-TLS port of a cluster node, when available, is sent to clients connected without TLS in responses to CLUSTER SLOTS, CLUSTER NODES, CLUSTER SLAVES and MOVED and ASK redirects, instead of the TLS port.

The user was able to override the client port by defining cluster-announce-port. Now cluster-announce-tls-port is added, so the user can define an alternative announce port for both TLS and non-TLS clients.

Fixes #8134
2021-03-30 23:11:32 +03:00
yoav-steinbergandGitHub 8cbd858d45 Minor client output buffer optimizations (#8699)
* Avoid checking output limits if buffer didn't grow.
* Use previouse node in case it has room in deferred output node.
2021-03-30 23:06:29 +03:00
JunhuaYandGitHub 28375ff63e re-fix config rewrite for empty save directive (#8722)
the bug was also discussed in #8716, and was solved in #8719, but incompletely:
when the server is started, and the save option is default, if you issue the " config set save "" "
to change the save option, and then issue the “config rewrite” command, the " save "" " won't be saved.
2021-03-30 22:49:06 +03:00
Oran AgraandGitHub cd81dcf18b solve race conditions in psync2-pingoff test (#8720)
Another test race condition in the macos tests.
the test was waiting for PINGs to be generated and put on the replication stream,
but waiting for 1 or 2 seconds doesn't really guarantee that.
then the test that expected 6 full syncs, found only 4
2021-03-30 11:41:06 +03:00
Yossi GottliebandGitHub 65311a3360 Fix config rewrite with an empty "save" parameter. (#8719) 2021-03-29 18:53:20 +03:00
315df9ada0 Add cluster slot migration tests (#8649)
Add tests for fixing migrating slot at all stages:

1. when migration is half inited on "migrating" node
2. when migration is half inited on "importing" node
3. migration inited, but not finished
4. migration is half finished on "migrating" node
5. migration is half finished on "importing" node

Also add tests for many simultaneous slot migrations.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
2021-03-29 13:52:02 +03:00
Meir Shpilraien (Spielrein)andGitHub 036963a7da Restore old client 'processCommandAndResetClient' to fix false dead client indicator (#8715)
'processCommandAndResetClient' returns 1 if client is dead. It does it
by checking if serve.current_client is NULL. On script timeout, Redis will re-enter
'processCommandAndResetClient' and when finish we will set server.current_client
to NULL. This will cause later to falsely return 1 and think that the client that
sent the timed-out script is dead (Redis to stop reading from the client buffer).
2021-03-29 13:34:16 +03:00
SteveandGitHub 54fd3d4024 Fix redis-cli handling of rediss:// URL scheme (#8705) 2021-03-29 00:25:30 +03:00
Huang ZhwandGitHub a79bb86856 Remove redundant defrag dictEntry in activeDefragSdsListAndDict. (#8713)
In activeDefragSdsListAndDict when defrag list key sucess, the key
in dict is also replaced. Then the corresponding dictEntry is
defraged. But the dictEntry will be defraged in next step by defrag
the dict values too.
2021-03-28 15:16:30 +03:00
Huang ZhwandGitHub e138698e54 make processCommand check publish channel permissions. (#8534)
Add publish channel permissions check in processCommand.

processCommand didn't check publish channel permissions, so we can
queue a publish command in a transaction. But when exec the transaction,
it will fail with -NOPERM.

We also union keys/commands/channels permissions check togegher in
ACLCheckAllPerm. Remove pubsubCheckACLPermissionsOrReply in 
publishCommand/subscribeCommand/psubscribeCommand. Always 
check permissions in processCommand/execCommand/
luaRedisGenericCommand.
2021-03-26 14:10:01 +03:00
sundbandGitHub db6655deb4 Fix crash when hz config out of range (#8658)
Fix crash when hz config out of range
2021-03-25 18:59:52 -07:00
IgorandGitHub cf0a909e2d Introduce fast path to bypass expensive serveClientsBlockedOnKeyByModule call (#8689)
Introduce fast path to bypass expensive serveClientsBlockedOnKeyByModule call
2021-03-25 07:16:03 -07:00
Wang YuanandGitHub 1976acfe3e Remove deprecated comments about saving expire time (#8692) 2021-03-25 15:09:12 +02:00
Oran AgraandGitHub 497351ad07 Fix SLOWLOG for blocked commands (#8632)
* SLOWLOG didn't record anything for blocked commands because the client
  was reset and argv was already empty. there was a fix for this issue
  specifically for modules, now it works for all blocked clients.
* The original command argv (before being re-written) was also reset
  before adding the slowlog on behalf of the blocked command.
* Latency monitor is now updated regardless of the slowlog flags of the
  command or its execution (their purpose is to hide sensitive info from
  the slowlog, not hide the fact the latency happened).
* Latency monitor now uses real_cmd rather than c->cmd (which may be
  different if the command got re-written, e.g. GEOADD)

Changes:
* Unify shared code between slowlog insertion in call() and
  updateStatsOnUnblock(), hopefully prevent future bugs from happening
  due to the later being overlooked.
* Reset CLIENT_PREVENT_LOGGING in resetClient rather than after command
  processing.
* Add a test for SLOWLOG and BLPOP

Notes:
- real_cmd == c->lastcmd, except inside MULTI and Lua.
- blocked commands never happen in these cases (MULTI / Lua)
- real_cmd == c->cmd, except for when the command is rewritten (e.g.
  GEOADD)
- blocked commands (currently) are never rewritten
- other than the command's CLIENT_PREVENT_LOGGING, and the
  execution flag CLIENT_PREVENT_LOGGING, other cases that we want to
  avoid slowlog are on AOF loading (specifically CMD_CALL_SLOWLOG will
  be off when executed from execCommand that runs from an AOF)
2021-03-25 10:20:27 +02:00
hzzbandGitHub 4a2c4477c9 Fix misleading comment (#8690)
REPLICAOF actually specifies which master to attach.
2021-03-24 19:39:31 -07:00
Madelyn OlsonandGitHub 98d2e0017d Skip unecessary check for pong recieved (#8585) 2021-03-24 19:37:38 -07:00
Qu ChenandGitHub 7de6451818 Properly initialize variable to make valgrind happy in checkChildrenDone(). Removed usage for the obsolete wait3() and wait4() in favor of waitpid(), and properly check for the exit status code. (#8666) 2021-03-24 08:41:05 -07:00
yoav-steinbergandGitHub 3060de88ce Remove cron saving during BGSAVE test. (#8688)
This fixes a race where a bgsave can start during the test after we verified no bgsave is running.
2021-03-24 15:14:47 +02:00
Oran AgraandGitHub f6e1a94e03 Corrupt stream key access to uninitialized memory (#8681)
the corrupt-dump-fuzzer test found a case where an access to a corrupt
stream would have caused accessing to uninitialized memory.
now it'll panic instead.

The issue was that there was a stream that says it has more than 0
records, but looking for the max ID came back empty handed.

p.s. when sanitize-dump-payload is used, this corruption is detected,
and the RESTORE command is gracefully rejected.
2021-03-24 11:33:49 +02:00
Itamar HaberandGitHub dc39b0c190 Fixes a typo in DEBUG HELP (#8638) 2021-03-24 11:11:38 +02:00
chenyangyangandGitHub ccc39f64f8 Add RM_GetAbsExpire/RM_SetAbsExpire for module. (#8564)
Add a check to ensure that the expire parameters in RM_SetExpire and RM_SetAbsExpire must be positive.
2021-03-24 11:10:15 +02:00
Oran AgraandGitHub b45b0d81bb Fix crash report killed by message (#8683)
We sometimes see the crash report saying we were killed by a random
process even in cases where the crash was spontanius in redis.
for instance, crashes found by the corrupt-dump test.

It looks like this si_pid is sometimes left uninitialized, and a good
way to tell if the crash originated in redis or trigged by outside is to
look at si_code, real signal codes are always > 0, and ones generated by
kill are have si_code of 0 or below.
2021-03-24 08:33:24 +02:00
yoav-steinbergandGitHub d026647f4f Avoid evaluating log arguments when log filtered by level. (#8685) 2021-03-24 08:22:12 +02:00
cl1118andGitHub a2af2a306b Update sentinel.c (#8686) 2021-03-23 13:51:53 +02:00
linyi-xqandGitHub 725762c48d clusterState->mf_master_offset init offset change to -1 (#8651)
resolve cluster failover timeout when master repl_offset is 0
2021-03-22 20:00:33 -07:00
Wen HuiandGitHub 6bf81eb7b1 add missing flags in sentinel instance info (#8682) 2021-03-22 20:13:30 +02:00
Huang ZhwandGitHub eef45934fa Cleanup: sentinelGetInstanceTypeString is unused. (#8680)
sentinelGetInstanceTypeString is the same as sentinelRedisInstanceTypeStr,
and not referenced by any functions.
2021-03-22 16:13:24 +02:00
Yossi GottliebandGitHub c4ef1efdb7 Add support for reading encrypted keyfiles. (#8644) 2021-03-22 13:27:46 +02:00
Yossi GottliebandGitHub c3df27d1ea Fix slowdown due to child reporting CoW. (#8645)
Reading CoW from /proc/<pid>/smaps can be slow with large processes on
some platforms.

This measures the time it takes to read CoW info and limits the duty
cycle of future updates to roughly 1/100.

As current_cow_size no longer represnets a current, fixed interval value
there is also a new current_cow_size_age field that provides information
about the age of the size value, in seconds.
2021-03-22 13:25:58 +02:00
Oran AgraandGitHub 2f717c156a fix race in diskless load cluster tests (#8674) 2021-03-22 10:51:13 +02:00
Oran AgraandGitHub a7c02b19bf Fix race in replication test (#8679)
Since redis 6.2, redis immediately tries to connect to the master, not
waiting for replication cron.

in the slow freebsd CI, this test failed and master_link_status was
already "up" when INFO was called.
2021-03-22 10:50:39 +02:00
Huang ZhwandGitHub 0418253a9b Change the order of start startLoadingFile call in redis-check-rdb. (#8676)
redis_check_rdb in some scenarios only stopLoading is called because
startLoadingFile is called too late.
2021-03-22 09:18:56 +02:00
Meir Shpilraien (Spielrein)andGitHub 9ae4f5c73d Fix script kill to work also on scripts that use pcall (#8661)
pcall function runs another LUA function in protected mode, this means
that any error will be caught by this function and will not stop the LUA
execution. The script kill mechanism uses error to stop the running script.
Scripts that uses pcall can catch the error raise by the script kill mechanism,
this will cause a script like this to be unkillable:

local f = function()
        while 1 do
                redis.call('ping')
        end
end
while 1 do
        pcall(f)
end

The fix is, when we want to kill the script, we set the hook function to be invoked 
after each line. This will promise that the execution will get another
error before it is able to enter the pcall function again.
2021-03-17 18:52:11 +02:00
Theo BuehlerandGitHub 169be0426c Fixes for systems with 64-bit time (#8662)
Some operating systems (e.g., NetBSD and OpenBSD) have switched to
using a 64-bit integer for time_t on all platforms. This results in currently
harmless compiler warnings due to potential truncation.
These changes fix these minor portability concerns.

* Fix format string for systems with 64 bit time
* use llabs to avoid truncation with 64 bit time
2021-03-17 15:45:38 +02:00
Wen HuiandGitHub 6097d14d4d fix wrong comment for data_age unit (#8665)
fix typo
2021-03-17 15:41:13 +02:00
Yossi GottliebandGitHub f657315e60 Cleanup: remove dead code from anet. (#8660)
In the long term we may want to move away from anet completely and have
everything implemented natively in connection.c, instead of having an
extra layer.

For now, just get rid of unused code.
2021-03-17 12:52:24 +02:00
Huang ZhwandGitHub a19c4058be When tests exit normally, some processes may still be alive (#8647)
In certain scenario start_server may think it failed to start a redis server
although it started successfully. in these cases, it'll not terminate it, and
it'll remain running when the test is over.

In start_server if config doesn't have bind (the minimal.conf in introspection.tcl),
it will try to bind ipv4 and ipv6. One may success while other fails. It will
output "Could not create server TCP listening socket".
wait_server_started uses this message to check whether instance started
successfully. So it will consider that it failed even though redis started successfully.

Additionally, in some cases it wasn't clear to users why the server exited,
since the warning message printed to the log, could in some cases be harmless,
and in some cases fatal.

This PR adds makes a clear distinction between a warning log message and
a fatal one, and changes the test suite to look for the fatal message.
2021-03-16 17:25:30 +02:00
Saravjeet 'Aman' SinghandGitHub ec34806c1e Update README.md (#3543)
prettify
2021-03-16 16:43:53 +02:00
Garen ChanandGitHub f141329fb7 Fix incorrect presentation in README.md (#5697) 2021-03-16 16:43:03 +02:00
Ikko AshimineandGitHub 6da2bc96e2 Fix typo in t_stream.c (#8592)
arguements -> arguments
2021-03-16 16:41:45 +02:00
Meir Shpilraien (Spielrein)andGitHub 95360c2e0c Fix issue where error replies are not counted on stats (#8659)
lookupKeyReadOrReply and lookupKeyWriteOrReply might decide to reply to the user
with the given robj reply. This reply might be an error reply and if so addReply
function is used instead of addReplyErrorObject which will cause the error reply not
to be counted on stats. The fix checks the first char in the reply and if its '-' (error)
it uses addReplyErrorObject.
2021-03-16 14:49:59 +02:00
Wen HuiandGitHub 18b59f35ef Sentinel: fix potential NULL ptr issue for sentinel instance connection (#8627) 2021-03-16 12:23:19 +02:00
Oran AgraandGitHub 40d555dbb7 set module eviction context flag only in masters (#8631)
REDISMODULE_CTX_FLAGS_EVICT and REDISMODULE_CTX_FLAGS_MAXMEMORY
shouldn't be set when the module is run inside a replica that doesn't do eviction.

one may argue that the database is under eviction (the master does eviction and sends DELs to the replica).
but on the other hand, we don't really know the master's configuration.
all that matters is if the current instance does evictions or not.
2021-03-16 12:17:14 +02:00
Yossi GottliebandGitHub df5f543b65 Server won't start on alpine/libmusl without IPv6. (#8655)
listenToPort attempts to gracefully handle and ignore certain errors but does not store errno prior to logging, which in turn calls several libc functions that may overwrite errno.

This has been discovered due to libmusl strftime() always returning with errno set to EINVAL, which resulted with docker-library/redis#273.
2021-03-16 11:20:03 +02:00
Madelyn OlsonandGitHub e1d98bca5a Redact slowlog entries for config with sensitive data. (#8584)
Redact config set requirepass/masterauth/masteruser from slowlog in addition to showing ACL commands without sensitive values.
2021-03-15 22:00:29 -07:00
guybe7andGitHub dba33a943d Missing EXEC on modules propagation after failed EVAL execution (#8654)
1. moduleReplicateMultiIfNeeded should use server.in_eval like
   moduleHandlePropagationAfterCommandCallback
2. server.in_eval could have been set to 1 and not reset back
   to 0 (a lot of missed early-exits after in_eval is already 1)

Note: The new assertions in processCommand cover (2) and I added
two module tests to cover (1)

Implications:
If an EVAL that failed (and thus left server.in_eval=1) runs before a module
command that replicates, the replication stream will contain MULTI (because
moduleReplicateMultiIfNeeded used to check server.lua_caller which is NULL
at this point) but not EXEC (because server.in_eval==1)
This only affects modules as module.c the only user of server.in_eval.

Affects versions 6.2.0, 6.2.1
2021-03-15 21:19:57 +02:00
Huang ZhwandGitHub 84d056d0f7 Fix typo and outdated comments. (#8640) 2021-03-14 09:41:43 +02:00
Guillem JoverandGitHub 3a5905fa85 Send the readiness notification when we are ready to accept connections (#8409)
On a replica we do accept connections, even though commands accessing
the database will operate in read-only mode. But the server is still
already operational and processing commands.

Not sending the readiness notification means that on a HA setup where
the nodes all start as replicas (with replicaof in the config) with
a replica that cannot connect to the master server and which might not
come back in a predictable amount of time or at all, the service
supervisor will end up timing out the service and terminating it, with
no option to promote it to be the main instance. This seems counter to
what the readiness notification is supposed to be signaling.

Instead send the readiness notification when we start accepting
commands, and then send the various server status changes as that.

Fixes: commit 641c64ada1
Fixes: commit dfb598cf33
2021-03-14 08:46:26 +02:00
KinWaiYuenandGitHub 5b48d90049 Optimize CLUSTER SLOTS reply by reducing unneeded loops (#8541)
This commit more efficiently computes the cluster bulk slots response 
by looping over the entire slot space once, instead of for each node.
2021-03-11 22:40:35 -08:00
Wen HuiandGitHub 3c09ce26fb Sentinel: fix info_refresh time before sentinel get first response (#8567) 2021-03-11 22:28:21 +02:00
guybe7andGitHub a4f03bd7eb Fix some memory leaks in propagagte.c (#8636)
Introduced by 3d0b427c30
2021-03-11 13:50:13 +02:00
Oran AgraandGitHub 4ae1bea323 allow RESET during busy scripts (#8629)
same as DISCARD, this command (RESET) is about client state, not server state
2021-03-11 07:52:27 +02:00
b70d81f60b Process hello command even if the default user has no permissions. (#8633)
Co-authored-by: Harkrishn Patro <harkrisp@amazon.com>
2021-03-10 21:19:35 -08:00
cheese1andGitHub c945e1126a Changes http to https in texts (#8495) 2021-03-10 19:11:16 +02:00
guybe7andGitHub 3d0b427c30 Fix some issues with modules and MULTI/EXEC (#8617)
Bug 1:
When a module ctx is freed moduleHandlePropagationAfterCommandCallback
is called and handles propagation. We want to prevent it from propagating
commands that were not replicated by the same context. Example:
1. module1.foo does: RM_Replicate(cmd1); RM_Call(cmd2); RM_Replicate(cmd3)
2. RM_Replicate(cmd1) propagates MULTI and adds cmd1 to also_propagagte
3. RM_Call(cmd2) create a new ctx, calls call() and destroys the ctx.
4. moduleHandlePropagationAfterCommandCallback is called, calling
   alsoPropagates EXEC (Note: EXEC is still not written to socket),
   setting server.in_trnsaction = 0
5. RM_Replicate(cmd3) is called, propagagting yet another MULTI (now
   we have nested MULTI calls, which is no good) and then cmd3

We must prevent RM_Call(cmd2) from resetting server.in_transaction.
REDISMODULE_CTX_MULTI_EMITTED was revived for that purpose.

Bug 2:
Fix issues with nested RM_Call where some have '!' and some don't.
Example:
1. module1.foo does RM_Call of module2.bar without replication (i.e. no '!')
2. module2.bar internally calls RM_Call of INCR with '!'
3. at the end of module1.foo we call RM_ReplicateVerbatim

We want the replica/AOF to see only module1.foo and not the INCR from module2.bar

Introduced a global replication_allowed flag inside RM_Call to determine
whether we need to replicate or not (even if '!' was specified)

Other changes:
Split beforePropagateMultiOrExec to beforePropagateMulti afterPropagateExec
just for better readability
2021-03-10 18:02:17 +02:00
Itamar HaberandGitHub ceb3a7a8f6 Adds LEN to STRALGO's comment (#8626)
Ref: https://github.com/redis/redis-doc/pull/1529
2021-03-10 16:19:48 +02:00
guybe7andGitHub 61a73de64d Cleanup ZADD_* flags (#8559)
Have a clear separation between in and out flags

Other changes:

delete dead code in RM_ZsetIncrby: if zsetAdd returned error (happens only if
the result of the operation is NAN or if score is NAN) we return immediately so
there is no way that zsetAdd succeeded and returned NAN in the out-flags
2021-03-10 16:09:43 +02:00
Oran AgraandGitHub 7778f1b4b0 strip % sign from current_fork_perc info field (#8628)
`master_sync_perc` and `loading_loaded_perc` don't have that sign,
and i think the info field should be a raw floating point number (the name suggests its units).

we already have `used_memory_peak_perc` and `used_memory_dataset_perc` which do add the `%` sign, but:
1) i think it was a mistake but maybe too late to fix now, and maybe not too late to fix for `current_fork_perc`
2) it is more important to be consistent with the two other "progress" "prec" metrics, and not with the "utilization" metric.
2021-03-10 15:25:16 +02:00
Oran AgraandGitHub 62a197516b key miss stat increment was misplaced (#8630)
The implication is that OBJECT command would was not updating stat_keyspace_misses
2021-03-10 13:20:06 +02:00
sundbandGitHub 95d6297db8 Add run all test support with define REDIS_TEST (#8570)
1. Add `redis-server test all` support to run all tests.
2. Add redis test to daily ci.
3. Add `--accurate` option to run slow tests for more iterations (so that
   by default we run less cycles (shorter time, and less prints).
4. Move dict benchmark to REDIS_TEST.
5. fix some leaks in tests
6. make quicklist tests run on a specific fill set of options rather than huge ranges
7. move some prints in quicklist test outside their loops to reduce prints
8. removing sds.h from dict.c since it is now used in both redis-server and
   redis-cli (uses hiredis sds)
2021-03-10 09:13:11 +02:00
uriyageandGitHub 53774e69fa remove redundant check for evalCommand (#8565) 2021-03-09 11:57:14 -08:00
luhuachaoandGitHub 1ccfd6a1f7 Clean redis-benchmark multi-threaded output (#8615) 2021-03-09 14:50:04 +02:00
25f8d4fb41 Run daily workflows only on redis/redis repo. (#8625)
Co-authored-by: Harkrishn Patro <harkrisp@amazon.com>
2021-03-09 12:35:41 +02:00
Yossi GottliebandGitHub af2175326c Fix memory info on FreeBSD. (#8620)
The obtained process_rss was incorrect (the OS reports pages, not
bytes), resulting with many other fields getting corrupted.

This has been tested on FreeBSD but not other platforms.
2021-03-09 11:33:32 +02:00
Huang ZhwandGitHub 9b4edfdf08 __quicklistCompress may compress more node than required (#8311)
When a quicklist has quicklist->compress * 2 nodes, then call
__quicklistCompress, all nodes will be decompressed and the middle
two nodes will be recompressed again. This violates the fact that
quicklist->compress * 2 nodes are uncompressed. It's harmless
because when visit a node, we always try to uncompress node first.
This only happened when a quicklist has quicklist->compress * 2 + 1
nodes, then delete a node. For other scenarios like insert node and
iterate this will not happen.
2021-03-08 21:43:09 +02:00
Yossi GottliebandGitHub 817894c012 Fix test false positive due to a race condition. (#8616) 2021-03-08 21:22:08 +02:00
Yossi GottliebandGitHub 7d81f39222 Fix flaky unit/maxmemory test on MacOS/BSD. (#8619)
It seems like non-Linux sockets may be less greedy, resulting with more
transient client output buffers.

Haven't proven this but empirically when stressing this test on
non-Linux tends to exhibit increased mem_clients_normal values.
2021-03-08 20:53:53 +02:00
guybe7andGitHub e58118cda6 Fix edge-case when a module client is unblocked (#8618)
Scenario:
1. A module client is blocked on keys with a timeout
2. Shortly before the timeout expires, the key is being populated and signaled
   as ready
3. Redis calls moduleTryServeClientBlockedOnKey (which replies to client) and
   then moduleUnblockClient
4. moduleUnblockClient doesn't really unblock the client, it writes to
   server.module_blocked_pipe and only marks the BC as unblocked.
5. beforeSleep kics in, by this time the client still exists and techincally
   timed-out. beforeSleep replies to the timeout client (double reply) and
   only then moduleHandleBlockedClients is called, reading from module_blocked_pipe
   and calling unblockClient

The solution is similar to what was done in moduleTryServeClientBlockedOnKey: we
should avoid re-processing an already-unblocked client
2021-03-08 19:00:19 +02:00
Pavlo YatsukhnenkoandGitHub 20377a6f3d Wrong usage sdscatprintf in redis-cli. (#8604)
The result of `sdscatprintf` is doubled when using argument twice.
2021-03-08 12:57:27 +02:00
Huang ZhwandGitHub f491eee2a1 Cleanup: dictEncObjHash remove redundant conditional statement (#8488) 2021-03-07 18:09:12 +02:00
Yossi GottliebandGitHub e8e6ca6309 Fix FreeBSD <12.x builds. (#8603) 2021-03-07 14:14:23 +02:00
Huang ZhwandGitHub 367ba4125f Remove some dead code (#8605) 2021-03-05 09:54:34 -08:00
yanchaozhongandGitHub 3d9c6a7ce6 Fix typo in sentinel.c (#8600) 2021-03-05 09:32:11 -08:00
uriyageandGitHub 55c74617d7 remove unused latency variable (#8597)
Remove unused latency variable from redis-cli
2021-03-05 09:29:52 -08:00
Yossi GottliebandGitHub 3c7d6a1853 Improve redis-cli non-binary safe string handling. (#8566)
* The `redis-cli --scan` output should honor output mode (set explicitly or implicitly), and quote key names when not in raw mode.
  * Technically this is a breaking change, but it should be very minor since raw mode is by default on for non-tty output.
  * It should only affect  TTY output (human users) or non-tty output if `--no-raw` is specified.

* Added `--quoted-input` option to treat all arguments as potentially quoted strings.
* Added `--quoted-pattern` option to accept a potentially quoted pattern.

Unquoting is applied to potentially quoted input only if single or double quotes are used. 

Fixes #8561, #8563
2021-03-04 15:03:49 +02:00
sundbandGitHub f07b7393a0 Fix memory overlap in quicklistRotate (#8599)
When the length of the quicklist is 1(only one zipmap), the rotate operation will cause
memory overlap when moving an entity from the tail of the zipmap to the head.
quicklistRotate is a dead code, so it has no impact on the existing code.
2021-03-04 13:12:00 +02:00
YaacovHazanandGitHub ac91822952 Fix RedisModule_IsAOFClient Redis Module API (#8596)
Since the API declared (as #define) in redismodule.h and uses
the CLIENT_ID_AOF that declared in the server.h, when
a module will want to make use of this API, it will get a compilation
error (module doesn't include the server.h).

The API was broken by d6eb3af (failed attempt for a cleanup).
Revert to the original version of RedisModule_IsAOFClient
that uses UINT64_MAX instead of CLIENT_ID_AOF
2021-03-04 13:02:23 +02:00
Yossi GottliebandGitHub ea1b48bd12 Improve SSL cleanup handling. (#8589)
This solves the problem of /dev/random and /dev/urandom open file
descriptors leaking to childs with some versions of OpenSSL.
2021-03-03 10:08:06 +02:00
yoav-steinbergandGitHub fba391ae66 Fix client_recent_max_input/output_buffer in 'INFO CLIENTS' when all clients drop. (#8588)
When no connected clients variables stopped being updated instead of being zeroed over time.

The other changes are cleanup and optimization (avoiding an unnecessary division per client)
2021-03-03 09:36:27 +02:00
Wen HuiandGitHub 257009afff generalize error check for SENTINEL MONITOR rumtime config (#8590) 2021-03-03 08:36:08 +02:00
5d180d2834 Fix potential replication-4 test race condition. (#8583)
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-03-02 18:12:11 +02:00
Yossi GottliebandGitHub 0bad00d049 Fix errors when loading RDB with missing modules. (#8579)
Fixes #8574
2021-03-02 09:39:37 +02:00
Oran AgraandGitHub 92bde124ca Merge Redis 6.2.1
Redis 6.2.1
2021-03-02 08:14:39 +02:00
Oran Agra 7018ad69a2 Redis 6.2.1 2021-03-01 21:02:05 +02:00
Oran Agra 46df4db87b Merge unstable into 6.2 2021-03-01 20:54:02 +02:00
YaacovHazanandGitHub c19530bc71 fix new networking tests to work when the test suite is used in tls mode (#8582)
the tests were unable to connect to the server since the attempted to use normal tcp
2021-03-01 20:53:02 +02:00
Oran AgraandGitHub 349ef3f6a0 fix stream deep sanitization with deleted records (#8568)
When sanitizing the stream listpack, we need to count the deleted records too.
otherwise the last line that checks the next pointer fails.

Add test to cover that state in the stream tests.
2021-03-01 17:23:29 +02:00
YaacovHazanandGitHub a031d268b1 Make port, tls-port and bind configurations modifiable (#8510)
Add ability to modify port, tls-port and bind configurations by CONFIG SET command.

To simplify the code and make it cleaner, a new structure
added, socketFds, which contains the file descriptors array and its counter,
and used for TCP, TLS and Cluster sockets file descriptors.
2021-03-01 16:04:44 +02:00
81a55d026f fix: call CLIENT INFO from redis module will crash the server (#8560)
Because when the RM_Call is invoked. It will create a faker client.
The point is client connection is NULL, so server will crash in connGetInfo

Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2021-03-01 08:18:14 +02:00
David GilmanandGitHub 1bf02e1ca9 Fix compliation on arm64 Mac with jemalloc (#8458)
The arm_thread_state64_get_pc used later in the file is defined
in mach kernel headers. Apparently they get included if you use
the system malloc but not if you use jemalloc.
2021-03-01 08:15:26 +02:00
Qu ChenandGitHub a66814fd7a Make dbid range check for SWAPDB command consistent with SELECT, MOVE, and COPY. (#8555)
DB ID used to be parsed as a long for SWAPDB command, now
make it into an int to be consistent with other commands that parses
the DB ID argument like SELECT, MOVE, COPY. See #8085

The implication is that the error message when the provided db index
is greater than 4M changes slightly.
2021-03-01 07:54:52 +02:00
Pavlo YatsukhnenkoandGitHub 18ff8cd1fb Fix div by 0 in redis-cli cluster creation (#8553)
This could happen on an invalid use, when trying to create a cluster with
a single node and provide it's address 3 time to satisfy redis-cli requirements.
2021-02-28 15:28:09 +02:00
BonsaiandGitHub 17c226b070 Module API for getting user name of a client (#8508)
Adding RM_GetClientUserNameById to get the ACL user name of a client connection.
2021-02-28 14:36:37 +02:00
Viktor SöderqvistandGitHub 6122f1c450 Shared reusable client for RM_Call() (#8516)
A single client pointer is added in the server struct. This is
initialized by the first RM_Call() and reused for every subsequent
RM_Call() except if it's already in use, which means that it's not
used for (recursive) module calls to modules. For these, a new
"fake" client is created each time.

Other changes:
* Avoid allocating a dict iterator in pubsubUnsubscribeAllChannels
  when not needed
2021-02-28 14:11:18 +02:00
Madelyn OlsonandGitHub 4a474843fb Moved requirepass and querybuf length to generic configs (#8557)
Moved additional configs to generic infrastructure.
2021-02-25 21:00:27 -08:00
Wen HuiandGitHub 7d43159a7d redis-cli prompt: fix db number and transaction state inconsistencies after reconnect (#8551)
After reconnect, the prompt was showing the db ID and multi state of the previous connection.
2021-02-25 20:08:24 +02:00
Yossi GottliebandGitHub 3ea4c43add Cleanup usage of malloc_usable_size. (#8554)
* Add better control of malloc_usable_size() usage.
* Use malloc_usable_size on alpine libc daily job.
* Add no-malloc-usable-size daily jobs.
* Fix zmalloc(0) when HAVE_MALLOC_SIZE is undefined.

In order to align with the jemalloc behavior, this should never return
NULL or OOM panic.
2021-02-25 09:24:41 +02:00
Madelyn OlsonandGitHub c6f0ea2c81 Allow stopped redis processes to be killed in tests (#8552) 2021-02-24 14:26:16 -08:00
sundbandGitHub 60d5ef4d82 Use addReplyErrorObject with shared.noscripterr (#8544) 2021-02-24 08:45:13 -08:00
guybe7andGitHub 21316d5709 XTRIM: Parse args before lookupKey (#8550)
This aligns better with other commands, specifically XADD
2021-02-24 16:41:50 +02:00
guybe7andGitHub aba61d9ebd XTRIM should take at least 4 args (#8549)
Fix command arity.
Other than key name it must at least take the trimming strategy and it's limit
2021-02-24 16:41:00 +02:00
965356ae63 Fix quicklistDelRange dead code, delete range wrong (#8257)
This commit fixes a bug in what's currently dead code in redis.

In quicklistDelRange when delete entry from entry.offset to node tail,
extent only need gte node->count - entry.offset, not node->count

Co-authored-by: Yoav Steinberg <yoav@redislabs.com>
2021-02-24 13:18:54 +02:00
Yossi GottliebandGitHub d1b5767a82 Cleanup clang warnings. (#8546) 2021-02-24 10:10:02 +02:00
Yossi GottliebandGitHub ae7d5bf617 Use malloc_usable_size() on FreeBSD. (#8545) 2021-02-24 09:48:04 +02:00
Huang ZwandGitHub e3667cb908 In luaRedisGenericCommand check channel return is ACL_DENIED_CHANNEL (#8535)
not ACL_DENIED_AUTH
2021-02-23 16:55:10 -08:00
guybe7andGitHub f745c0181a Fix race in CONFIG REWRITE sanity (#8536)
server may still be LOADING the RDB when receiving the ping
2021-02-23 20:28:03 +02:00
Yossi GottliebandGitHub dd885780d6 Fix compile errors with no HAVE_MALLOC_SIZE. (#8533)
Also adds a new daily CI test, relying on the fact that we don't use malloc_size() on alpine libmusl.

Fixes #8531
2021-02-23 17:08:49 +02:00
Yossi GottliebandGitHub 95ea74549c Fix failed tests on Linux Alpine and add a CI job. (#8532)
* Remove linux/version.h dependency.

This introduces unnecessary dependencies, and generally not a good idea
as the platform we build on may be different than the platform we run
on.

To determine if sync_file_range exists we can simply rely on header file
hints.

* Fix setproctitle() on libmusl.

The previous ifdef checks were a bit too strict for no apparent
reason.

* Fix tests failure on Linux with no backtrace.

* Add alpine daily CI job.
2021-02-23 12:57:45 +02:00
Oran AgraandGitHub 445aa844b9 Merge Redis 6.2.0 GA
Redis 6.2.0 GA
2021-02-22 23:23:58 +02:00
Oran Agra f098fe319b Redis 6.2.0 GA 2021-02-22 15:56:55 +02:00
Oran Agra 2ab6fef099 Merge origin/unstable into 6.2 2021-02-22 15:48:42 +02:00
Oran AgraandGitHub 8e83bcd2ac update help.h from redis.io commands.json (#8524) 2021-02-22 15:48:17 +02:00
Yossi GottliebandGitHub d32f2e9999 Fix integer overflow (CVE-2021-21309). (#8522)
On 32-bit systems, setting the proto-max-bulk-len config parameter to a high value may result with integer overflow and a subsequent heap overflow when parsing an input bulk (CVE-2021-21309).

This fix has two parts:

Set a reasonable limit to the config parameter.
Add additional checks to prevent the problem in other potential but unknown code paths.
2021-02-22 15:41:32 +02:00
4739131ca6 Remove acl subcommand validation if fully added command exists. (#8483)
This validation was only done for sub-commands and not for commands.

These would have been valid (not produce any error)
ACL SETUSER bob +@all +client
ACL SETUSER bob +client +client

so no reason for this one to fail:
ACL SETUSER bob +client +client|id

One example why this is needed is that pfdebug wasn't part of the @hyperloglog
group and now it is. so something like:
acl setuser user1 +@hyperloglog +pfdebug|test
would have succeeded in early 6.0.x, and fail in 6.2 RC3

Co-authored-by: Harkrishn Patro <harkrisp@amazon.com>
Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-22 15:22:25 +02:00
Huang ZwandGitHub b164c4dec3 In XADD take deleted items into consideration when switch to new node (#8390)
If we set stream-node-max-bytes = 0, then we insert entry then delete,
do this many times, the last stream node will be very big.
2021-02-22 15:08:16 +02:00
f5235b2d76 SRANDMEMBER RESP3 return should be Array, not Set (#8504)
SRANDMEMBER with negative count (non unique) can return the same member
multiple times, and the order of elements in the returned collection matters.
For these reasons returning a RESP3 Set type is not valid for the negative
count, but also not really valid for the positive (unique) variant either (the
command returns an array of random picks, not a set)

This PR also contains a minor optimization for SRANDMEMBER, HRANDFIELD,
and ZRANDMEMBER, to avoid the temporary dict from being rehashed while it grows.

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-22 15:00:59 +02:00
sundbandGitHub 362f2a84bd Improve overflow check of expire time (#8519)
When milliseconds == LLONG_MAX / 1000, *1000 will not overflow.
2021-02-22 08:45:26 +02:00
Yossi GottliebandGitHub d828f90c26 Fix allowed length for REPLCONF ip-address. (#8517)
Originally this was limited to IPv6 address length, but effectively it
has been used for host names and now that Sentinel accepts that as well
we need to be able to store full hostnames.

Fixes #8507
2021-02-21 11:22:36 +02:00
Huang ZwandGitHub f687ac0c32 Client tracking tracking-redir-broken push len is 2 not 3 (#8456)
When redis responds with tracking-redir-broken push message (RESP3),
it was responding with a broken protocol: an array of 3 elements, but only
pushes 2 elements.

Some bugs in the test make this pass. Read the push reply
will consume an extra reply, because the reply length is 3, but there
are only two elements, so the next reply will be treated as third
element. So the test is corrected too.

Other changes:
* checkPrefixCollisionsOrReply success should return 1 instead of -1,
  this bug didn't have any implications.
* improve client tracking tests to validate more of the response it reads.
2021-02-21 09:34:46 +02:00
0772098b1b EXPIRE, EXPIREAT, SETEX, GETEX: Return error when expire time overflows (#8287)
Respond with error if expire time overflows from positive to negative of vice versa.

* `SETEX`, `SET EX`, `GETEX` etc would have already error on negative value,
but now they would also error on overflows (i.e. when the input was positive but
after the manipulation it becomes negative, which would have passed before)
* `EXPIRE` and `EXPIREAT` was ok taking negative values (would implicitly delete
the key), we keep that, but we do error if the user provided a value that changes
sign when manipulated (except the case of changing sign when `basetime` is added)

Signed-off-by: Gnanesh <gnaneshkunal@outlook.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-21 09:09:54 +02:00
Jim BrunnerandGitHub 06966d2a0e dict: pause rehash, minor readability refactor (#8515)
The `dict` field `iterators` is misleading and incorrect. 
This variable is used for 1 purpose - to pause rehashing.

The current `iterators` field doesn't actually count "iterators".
It counts "safe iterators".  But - it doesn't actually count safe iterators
either.  For one, it's only incremented once the safe iterator begins to
iterate, not when it's created.  It's also incremented in `dictScan` to
prevent rehashing (and commented to make it clear why `iterators` is
being incremented during a scan).

This update renames the field as `pauserehash` and creates 2 helper
macros `dictPauseRehashing(d)` and `dictResumeRehashing(d)`
2021-02-20 12:56:30 +02:00
sundbandGitHub 46346e9e3a Fix timing error oom-score-adj test (#8513)
fixes timing issue, fork didn't always get to set the oom score before the test verified it.
2021-02-19 13:01:25 +02:00
Harkrishn PatroandGitHub 303465af35 Remove redundant pubsub list to store the patterns. (#8472)
Remove redundant pubsub list to store the patterns.
2021-02-17 14:13:50 -08:00
Oran AgraandGitHub 71ab81ec69 solve valgrind warning in child_info (#8505)
Valgrind warns about `write` accessing uninitialized memory, which was the struct padding.
2021-02-17 12:30:29 +02:00
aab479f8cf Optimize listpack for stream usage to avoid repeated reallocs (#6281)
Avoid repeated reallocs growing the listpack while entries are being added.
This is done by pre-allocating the listpack to near maximum size, and using
malloc_size to check if it needs realloc or not.
When the listpack reaches the maximum number of entries, we shrink it to fit it's used size.

Co-authored-by: Viktor Söderqvist <viktor@zuiderkwast.se>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-16 16:17:38 +02:00
uriyageandGitHub fd052d2a86 Adds INFO fields to track fork child progress (#8414)
* Adding current_save_keys_total and current_save_keys_processed info fields.
  Present in replication, BGSAVE and AOFRW.
* Changing RM_SendChildCOWInfo() to RM_SendChildHeartbeat(double progress)
* Adding new info field current_fork_perc. Present in Replication, BGSAVE, AOFRW,
  and module forks.
2021-02-16 16:06:51 +02:00
Viktor SöderqvistandOran Agra acb32d472d Add ziplistReplace, in-place optimized for elements of same size
Avoids memmove and reallocs when replacing a ziplist element of the
same encoded size as the new value.

Affects HSET, HINRBY, HINCRBYFLOAT (via hashTypeSet) and LSET (via
quicklistReplaceAtIndex).
2021-02-16 13:01:14 +02:00
Viktor SöderqvistandOran Agra 683e530cf3 Use stack for decoding integer-encoded values in list push
Less heap allocations when commands like LMOVE push integer values.
2021-02-16 13:01:14 +02:00
Viktor SöderqvistandOran Agra f521498b43 Avoid useless copying in LINDEX command for reply 2021-02-16 13:01:14 +02:00
Oran AgraandGitHub fb3457d157 minor test suite cleanup, revive old test (#8497)
There are two tests in other.tcl that were dependant of the sha1 package
import which meant that they didn't usually run.
The reason it was like that was that prior to the creation of DEBUG
DIGEST, the test suite used to have an equivalent function, but that's
no longer the case and this dependency isn't needed.

The other change is to revert config changes done by the test before the
test suite continues. can be useful if using `--host` to run multiple
units against the same server
2021-02-15 17:20:03 +02:00
Yossi GottliebandGitHub b1929bb2b6 Fix incorrect shared ping length. (#8498) 2021-02-15 17:09:33 +02:00
Yossi GottliebandGitHub 141ac8df59 Escape unsafe field name characters in INFO. (#8492)
Fixes #8489
2021-02-15 17:08:53 +02:00
Oran AgraandGitHub 30775bc3e3 solve race in replication-2 test - again (#8491)
this should make it timing independent and also faster in most cases
2021-02-15 12:50:23 +02:00
Viktor SöderqvistandGitHub 0bc8c9c8f9 Modules: In RM_HashSet, add COUNT_ALL flag and set errno (#8446)
The added flag affects the return value of RM_HashSet() to include
the number of inserted fields, in addition to updated and deleted
fields.

errno is set on errors, tests are added and documentation updated.
2021-02-15 11:40:05 +02:00
filipe oliveiraandGitHub efccd6353b redis-benchmark: Fix broken protocol when used with -a or --dbnum (#8486)
Fix the pointers to the slot hash tags in the case of prefixed commands usage
i.e. AUTH / SELECT

It adjusts the pointers to the slot hash tags in the case of prefixed commands
usage as soon as we get the 1st reply (same like we already did for the random
strings within the command )
2021-02-14 14:42:41 +02:00
8c42d1257f Fix errors with sentinel leaked fds test. (#8482)
* Don't run test script on non-Linux.
* Verify that reported fds do indeed exist also in parent, to avoid
  false negatives on some systems (namely CentOS).

Co-authored-by: Andy Pan <panjf2000@gmail.com>
2021-02-11 15:25:01 +02:00
Yossi GottliebandGitHub 94bc26e652 Fix duplicate replicas issue. (#8481)
We need to store replicas referenced by their announced address (IP or
address). Before that, if hostnames were used and the IP address
changed, duplicate entries would have been created.
2021-02-11 11:50:47 +02:00
Yossi GottliebandGitHub 29ac9aea5d Fix Sentinel configuration rewrite. (#8480)
The `resolve-hostnames` and `announce-hostnames` parameters were not
specified correctly according to the new convention introduced by
1aad55b66.
2021-02-10 18:38:10 +02:00
filipe oliveiraandGitHub b5ca1e9e53 Removed time sensitive checks from block on background tests. Fixed uninitialized variable (#8479)
- removes time sensitive checks from block on background tests during leak checks.
- fix uninitialized variable on RedisModuleBlockedClient() when calling
  RM_BlockedClientMeasureTimeEnd() without RM_BlockedClientMeasureTimeStart()
2021-02-10 08:59:07 +02:00
Madelyn OlsonandGitHub 899c85ae67 Moved most static strings into the shared structure (#8411)
Moved most static strings into the shared structure
2021-02-09 11:52:28 -08:00
Itamar HaberandGitHub 4554a49d32 Adds code of conduct (#8471) 2021-02-09 14:38:09 +02:00
WuYunlongandGitHub 203f357c32 Cleanup in redis-cli and tests: release memory on exit, change dup test name (#8475)
1. Rename 18-cluster-nodes-slots.tcl to 19-cluster-nodes-slots.tcl.
  it was conflicting with another test prefixed by 18
2. Release memory on exit in redis-cli.c.
3. Fix freeConvertedSds indentation.
2021-02-09 12:36:09 +02:00
Huang ZwandGitHub 8f9958dc24 Fix typo and some out of date comments (#8449)
Fix typo and some out of date comments
2021-02-08 09:29:32 -08:00
Yossi GottliebandGitHub dbcc0a85d0 Fix and cleanup Sentinel leaked fds test. (#8469)
* For consistency, use tclsh for the script as well
* Ignore leaked fds that originate from grandparent process, since we
  only care about fds redis-sentinel itself is responsible for
* Check every test iteration to catch problems early
* Some cleanups, e.g. parameterization of file name, etc.
2021-02-08 17:02:46 +02:00
filipe oliveiraandGitHub b2351ea0dc [fix] Increasing block on background timeout time to avoid test failure (#8470)
The test failed from time to time on Github actions.
We think it's possible that on the module's blocking timeout
time tracking test, the timeout is happening prior we issue the
RedisModule_BlockedClientMeasureTimeStart(bc) on the
background thread. If that is the case one possible solution
is to increase the timeout.
Increasing to 200ms to 500ms to see if nightly stops failing.
2021-02-08 16:24:00 +02:00
sundbandGitHub 1f12be3072 Fix random probability check in ziplistRandomPairsUnique (#8467)
When (remaining == (total_size - index)), element will definitely be random to.
But when rand() == RAND_MAX, the element will miss, this will trigger assert
in serverAssert(ziplistRandomPairsUnique(zsetobj->ptr, count, keys, vals) == count).
2021-02-08 12:41:16 +02:00
Andy PanandGitHub 88272cf7ac Fix typos in comments (#8466) 2021-02-08 12:09:39 +02:00
62b1f32062 Optimize HRANDFIELD and ZRANDMEMBER case 4 when ziplist encoded (#8444)
It is inefficient to repeatedly pick a single random element from a
ziplist.
For CASE4, which is when the user requested a low number of unique
random picks from the collectoin, we used thta pattern.

Now we use a different algorithm that picks unique elements from a
ziplist, and guarentee no duplicate but doesn't provide random order
(which is only needed in the non-unique random picks case)

Unrelated changes:
* change ziplist count and indexes variables to unsigned
* solve compilation warnings about uninitialized vars in gcc 10.2

Co-authored-by: xinluton <xinluton@qq.com>
2021-02-07 16:55:11 +02:00
Oran AgraandGitHub 02ab14cc2e solve race in replication-2 test (#8461)
use SIGSTOP instead of DEBUG SLEEP, reduces the test
time by some 2 seconds and avoids failures on slow machines
2021-02-07 16:22:30 +02:00
Oran AgraandGitHub 018f7b7378 Update CI on Ubuntu to tcl8.6 (since 20.04 is now used) (#8460)
Github started shifting some repositoreis to use ubuntu 20.04 by default
tcl8.5 is missing in these, but 8.6 exists in both 20.04 and 18.04
2021-02-07 15:41:49 +02:00
Yossi GottliebandGitHub 5b8350aaaa Add --dump-logs tests option. (#8459)
Dump the entire server log if a test failed, to easy troubleshooting
with no access to log files.
2021-02-07 12:37:24 +02:00
Yossi GottliebandGitHub be83bb13a8 Add --insecure option to command line tools. (#8416)
Disable certificate validation, making it possible to connect to servers
without configuring full trust chain.

The use of this option is insecure and makes the connection vulnerable
to man in the middle attacks.
2021-02-07 12:36:56 +02:00
Viktor SöderqvistandGitHub aea6e71ef8 RM_ZsetRem: Delete key if empty (#8453)
Without this fix, RM_ZsetRem can leave empty sorted sets which are
not allowed to exist.

Removing from a sorted set while iterating seems to work (while
inserting causes failed assetions). RM_ZsetRangeEndReached is
modified to return 1 if the key doesn't exist, to terminate
iteration when the last element has been removed.
2021-02-05 19:54:01 +02:00
filipe oliveiraandGitHub b3bdcd2278 Fix compiler warning on implicit declaration of ‘nanosleep’ . Removed unused variable (#8454) 2021-02-05 19:51:31 +02:00
18ac41973b RAND* commands: fix risk of OOM panic in hash and zset, use fair random in hash, and add tests for even distribution to all (#8429)
Changes to HRANDFIELD and ZRANDMEMBER:
* Fix risk of OOM panic when client query a very big negative count (avoid allocating huge temporary buffer).
* Fix uneven random distribution in HRANDFIELD with negative count (wasn't using dictGetFairRandomKey).
* Add tests to check an even random distribution (HRANDFIELD, SRANDMEMBER, ZRANDMEMBER).

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-05 15:56:20 +02:00
Yang BodongandGitHub b7b23a0ff5 Fix GEOSEARCH tcl test error (#8451)
Issue with new test due to longitude wraparound.
2021-02-04 19:39:07 +02:00
ded1655d49 GEOSEARCH bybox bug fixes and new fuzzy tester (#8445)
Fix errors of GEOSEARCH bybox search due to:
1. projection of the box to a trapezoid (when the meter box is converted to long / lat it's no longer a box).
2. width and height mismatch

Changes:
- New GEOSEARCH point in rectangle algorithm
- Fix GEOSEARCH bybox width and height mismatch bug
- Add GEOSEARCH bybox testing to the existing "GEOADD + GEORANGE randomized test"
- Add new fuzzy test to stress test the bybox corners and edges
- Add some tests for edge cases of the bybox algorithm

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-02-04 18:08:35 +02:00
Yossi GottliebandGitHub 52fb306535 Fix 32-bit test modules build. (#8448) 2021-02-04 11:37:28 +02:00
Yossi GottliebandGitHub de6f3ad017 Fix FreeBSD tests and CI Daily issues. (#8438)
* Add bash temporarily to allow sentinel fd leaks test to run.
* Use vmactions-freebsd rdist sync to work around bind permission denied
  and slow execution issues.
* Upgrade to tcl8.6 to be aligned with latest Ubuntu envs.
* Concat all command executions to avoid ignoring failures.
* Skip intensive fuzzer on FreeBSD. For some yet unknown reason, generate_fuzzy_traffic_on_key causes TCL to significantly bloat on FreeBSD resulting with out of memory.
2021-02-03 17:35:28 +02:00
Jonah H. HarrisandGitHub a3718cde06 Optimizing sorted GEORADIUS COUNT with partial sorting. (#8326)
This commit provides an optimization, in terms of time, for all GEORADIUS*
and GEOSEARCH* searches which utilize the default, sorted, COUNT clause.
This is commonly used for nearest-neighbor (top-K points closest to a given lat/lon)
searches. While the previous implementation appends all matching points to the
geoPoint array and performs pruning after-the-fact via a full sort and [0, count)-based
for-loop, this PR sorts only the required number of elements.

This optimization provides a 5-20% improvement in runtime depending on the
density of points of interest (POI) as well as the radius searched.
No performance degradation has been observed.
2021-02-02 10:57:12 +02:00
Huang ZwandGitHub 9760475a39 Cleanup: addReplyAggregateLen and addReplyBulkLen remove redundant check (#8431)
addReplyLongLongWithPrefix, has a check against negative length, and the code
flow removed in this commit bypasses the check.
addReplyAggregateLen has an assertion for negative length, but addReplyBulkLen
does not, so this commit fixes theoretical case of access violation (probably
unreachable though)
2021-02-02 10:54:19 +02:00
zhaozhao.zzandGitHub f2a5fe3678 XINFO should use lookupKeyReadOrReply (#8436)
This bug would have let users observe logically expired keys on replicas and
during CLIENT PAUSE WRITE.
2021-02-02 10:51:19 +02:00
Oran AgraandGitHub 2dba1e391d Merge 6.2 RC3 2021-02-01 20:11:42 +02:00
Oran Agra 95338f9cc4 Redis 6.2 RC3 2021-01-31 19:55:20 +02:00
Oran Agra d0854927fc Merge branch 'unstable' into 6.2 2021-01-31 12:20:34 +02:00
Oran AgraandGitHub b57d0eb418 update help.h with new commands (#8426) 2021-01-31 12:16:58 +02:00
Oran AgraandGitHub 8f27578de2 temporarily disable sentinel test FD leak print (#8425)
These tests are not yet stable. on github actions they show some false leaks.
2021-01-31 12:14:36 +02:00
Oran AgraandGitHub 5a7eb9c881 Fix test issues from introduction of HRANDFIELD (#8424)
* The corrupt dump fuzzer found a division by zero.
* in some cases the random fields from the HRANDFIELD tests produced
  fields with newlines and other special chars (due to \ char), this caused
  the TCL tests to see a bulk response that has a newline in it and add {}
  around it, later it can think this is a nested list. in fact the `alpha` random
  string generator isn't using spaces and newlines, so it should not use `\`
  either.
2021-01-31 12:13:45 +02:00
Wen HuiandGitHub eacccd2acb fix sentinel tests error (#8422)
This commit fixes sentinel announces hostnames test error in certain linux environment
Before this commit, we only check localhost is resolved into 127.0.0.1, however in ubuntu
or some other linux environments "localhost" will be resolved into ::1 ipv6 address first if
the network stack is capable.
2021-01-30 11:18:58 +02:00
filipe oliveiraandGitHub f0c5052aa8 Enabled background and reply time tracking on blocked on keys/blocked on background work clients (#7491)
This commit enables tracking time of the background tasks and on replies,
opening the door for properly tracking commands that rely on blocking / background
 work via the slowlog, latency history, and commandstats. 

Some notes:
- The time spent blocked waiting for key changes, or blocked on synchronous
  replication is not accounted for. 

- **This commit does not affect latency tracking of commands that are non-blocking
  or do not have background work.** ( meaning that it all stays the same with exception to
  `BZPOPMIN`,`BZPOPMAX`,`BRPOP`,`BLPOP`, etc... and module's commands that rely
  on background threads ). 

-  Specifically for latency history command we've added a new event class named
  `command-unblocking` that will enable latency monitoring on commands that spawn
  background threads to do the work.

- For blocking commands we're now considering the total time of a command as the
  time spent on call() + the time spent on replying when unblocked.

- For Modules commands that rely on background threads we're now considering the
  total time of a command as the time spent on call (main thread) + the time spent on
  the background thread ( if marked within `RedisModule_MeasureTimeStart()` and
  `RedisModule_MeasureTimeEnd()` ) + the time spent on replying (main thread)

To test for this feature we've added a `unit/moduleapi/blockonbackground` test that relies on
a module that blocks the client and sleeps on the background for a given time. 
- check blocked command that uses RedisModule_MeasureTimeStart() is tracking background time
- check blocked command that uses RedisModule_MeasureTimeStart() is tracking background time even in timeout
- check blocked command with multiple calls RedisModule_MeasureTimeStart()  is tracking the total background time
- check blocked command without calling RedisModule_MeasureTimeStart() is not reporting background time
2021-01-29 15:38:30 +02:00
b9a0500f16 Add HRANDFIELD and ZRANDMEMBER. improvements to SRANDMEMBER (#8297)
New commands:
`HRANDFIELD [<count> [WITHVALUES]]`
`ZRANDMEMBER [<count> [WITHSCORES]]`
Algorithms are similar to the one in SRANDMEMBER.

Both return a simple bulk response when no arguments are given, and an array otherwise.
In case values/scores are requested, RESP2 returns a long array, and RESP3 a nested array.
note: in all 3 commands, the only option that also provides random order is the one with negative count.

Changes to SRANDMEMBER
* Optimization when count is 1, we can use the more efficient algorithm of non-unique random
* optimization: work with sds strings rather than robj

Other changes:
* zzlGetScore: when zset needs to convert string to double, we use safer memcpy (in
  case the buffer is too small)
* Solve a "bug" in SRANDMEMBER test: it intended to test a positive count (case 3 or
  case 4) and by accident used a negative count

Co-authored-by: xinluton <xinluton@qq.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-29 10:47:28 +02:00
zhaozhao.zzandGitHub 49b3663332 AOF: recover from last write error after turn on appendonly again (#8030)
The key point is how to recover from last AOF write error, for example:

1. start redis with appendonly yes, and append some write commands

2. short write or something else error happen, `server.aof_last_write_status` changed to `C_ERR`, now redis doesn't accept write commands

3. execute `CONFIG SET appendonly no` to avoid the above problem, now redis can accept write commands again

4. disk error resolved, and execute `CONFIG SET appendonly yes` to reopen AOF, but `server.aof_last_write_status` cannot be changed to `C_OK` (if background aof rewrite run less then 1 second, it will free `server.aof_buf` and then serverCron cannot fix `aof_last_write_status`), then redis cannot accept write commands forever.

This PR use a simple way to fix it:

1. just free `server.aof_buf` when stop appendonly to save memory, if error happens in `flushAppendOnlyFile(1)`, the `server.aof_buf` may contains some data which has not be written to aof, I think we can ignore it because we turn off the appendonly.

2. reset fsync status after stop appendonly and call `flushAppendOnlyFile` only when `aof_state` is ON

3. reset `server.last_write_status` when reopen aof to accept write commands
2021-01-29 14:35:10 +08:00
Allen FarrisandGitHub 0d18a1e85f implement FAILOVER command (#8315)
Implement FAILOVER command, which coordinates failover
between the server and one of its replicas.
2021-01-28 13:18:05 -08:00
Yossi GottliebandGitHub 26301897d0 Update CLIENT HELP regarding KILL options. (#8417)
* Indicate address can also be a unix socket path name.
* Document the LADDR option as well.
2021-01-28 20:49:46 +02:00
Yossi GottliebandGitHub 4bb5ccbefb Add proc-title-template option. (#8397)
Make it possible to customize the process title, i.e. include custom
strings, immutable configuration like port, tls-port, unix socket name,
etc.
2021-01-28 18:17:39 +02:00
guybe7andGitHub 01cbf17ba2 Modules: Add event for fork child birth and termination (#8289)
Useful to avoid doing background jobs that can cause excessive COW
2021-01-28 16:38:49 +02:00
Wang YuanandGitHub a16739a3ac Redis exit for fsync error when the AOF fsync policy is 'always' (#8347)
With AOF policy of fsync "always", redis should respect the contract with the user
that on acknowledged write data is already synced on disk.

Redis was already exiting for AOF write error,  but don't care about fsync failure.
So to guarantee data safe, redis should exit for fsync error too (when the AOF fsync
policy is 'always').
2021-01-28 16:25:35 +02:00
Viktor SöderqvistandGitHub 4355145a62 Add modules API for streams (#8288)
APIs added for these stream operations: add, delete, iterate and
trim (by ID or maxlength). The functions are prefixed by RM_Stream.

* RM_StreamAdd
* RM_StreamDelete
* RM_StreamIteratorStart
* RM_StreamIteratorStop
* RM_StreamIteratorNextID
* RM_StreamIteratorNextField
* RM_StreamIteratorDelete
* RM_StreamTrimByLength
* RM_StreamTrimByID

The type RedisModuleStreamID is added and functions for converting
from and to RedisModuleString.

* RM_CreateStringFromStreamID
* RM_StringToStreamID

Whenever the stream functions return REDISMODULE_ERR, errno is set to
provide additional error information.

Refactoring: The zset iterator fields in the RedisModuleKey struct
are wrapped in a union, to allow the same space to be used for type-
specific info for streams and allow future use for other key types.
2021-01-28 16:19:43 +02:00
bb7cd97439 Add hostname support in Sentinel. (#8282)
This is both a bugfix and an enhancement.

Internally, Sentinel relies entirely on IP addresses to identify
instances. When configured with a new master, it also requires users to
specify and IP and not hostname.

However, replicas may use the replica-announce-ip configuration to
announce a hostname. When that happens, Sentinel fails to match the
announced hostname with the expected IP and considers that a different
instance, triggering reconfiguration, etc.

Another use case is where TLS is used and clients are expected to match
the hostname to connect to with the certificate's SAN attribute. To
properly implement this configuration, it is necessary for Sentinel to
redirect clients to a hostname rather than an IP address.

The new 'resolve-hostnames' configuration parameter determines if
Sentinel is willing to accept hostnames. It is set by default to no,
which maintains backwards compatibility and avoids unexpected DNS
resolution delays on systems with DNS configuration issues.

Internally, Sentinel continues to identify instances by their resolved
IP address and will also report the IP by default. The new
'announce-hostnames' parameter determines if Sentinel should prefer to
announce a hostname, when available, rather than an IP address. This
applies to addresses returned to clients, as well as their
representation in the configuration file, REPLICAOF configuration
commands, etc.

This commit also introduces SENTINEL CONFIG GET and SENTINEL CONFIG SET
which can be used to introspect or configure global Sentinel
configuration that was previously was only possible by directly
accessing the configuration file and possibly restarting the instance.

Co-authored-by: myl1024 <myl92916@qq.com>
Co-authored-by: sundb <sundbcn@gmail.com>
2021-01-28 12:09:11 +02:00
17b34c7309 Add 'set-proc-title' config so that this mechanism can be disabled (#3623)
if option `set-proc-title' is no, then do nothing for proc title.

The reason has been explained long ago, see following:

We update redis to 2.8.8, then found there are some side effect when
redis always change the process title.

We run several slave instance on one computer, and all these salves
listen on unix socket only, then ps will show:

  1 S redis 18036 1 0 80 0 - 56130 ep_pol 14:02 ? 00:00:31 /usr/sbin/redis-server *:0
  1 S redis 23949 1 0 80 0 - 11074 ep_pol 15:41 ? 00:00:00 /usr/sbin/redis-server *:0

for redis 2.6 the output of ps is like following:

  1 S redis 18036 1 0 80 0 - 56130 ep_pol 14:02 ? 00:00:31 /usr/sbin/redis-server /etc/redis/a.conf
  1 S redis 23949 1 0 80 0 - 11074 ep_pol 15:41 ? 00:00:00 /usr/sbin/redis-server /etc/redis/b.conf

Later is more informational in our case. The situation
is worse when we manage the config and process running
state by salt. Salt check the process by running "ps |
grep SIG" (for Gentoo System) to check the running
state, where SIG is the string to search for when
looking for the service process with ps. Previously, we
define sig as "/usr/sbin/redis-server
/etc/redis/a.conf". Since the ps output is identical for
our case, so we have no way to check the state of
specified redis instance.

So, for our case, we prefer the old behavior, i.e, do
not change the process title for the main redis process.
Or add an option such as "set-proc-title [yes|no]" to
control this behavior.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-28 11:12:39 +02:00
Yossi GottliebandGitHub 3a5049042a Avoid assertions when testing arm64 cow bug. (#8405)
At least in one case the arm64 cow kernel bug test triggers an assert, which is a problem because it cannot be ignored like cases where the bug is found.

On older systems (Linux <4.5) madvise fails because MADV_FREE is not supported. We treat these failures as an indication the system is not affected.

Fixes #8351, #8406
2021-01-28 10:33:45 +02:00
Raghav MuddurandGitHub 0367a80819 GETEX, GETDEL and SET PXAT/EXAT (#8327)
This commit introduces two new command and two options for an existing command

GETEX <key> [PERSIST][EX seconds][PX milliseconds] [EXAT seconds-timestamp]
[PXAT milliseconds-timestamp]

The getexCommand() function implements extended options and variants of the GET
command. Unlike GET command this command is not read-only. Only one of the options
can be used at a given time.

1. PERSIST removes any TTL associated with the key.
2. EX Set expiry TTL in seconds.
3. PX Set expiry TTL in milliseconds.
4. EXAT Same like EX instead of specifying the number of seconds representing the
    TTL (time to live), it takes an absolute Unix timestamp
5. PXAT Same like PX instead of specifying the number of milliseconds representing the
    TTL (time to live), it takes an absolute Unix timestamp

Command would return either the bulk string, error or nil.

GETDEL <key>
Would delete the key after getting.

SET key value [NX] [XX] [KEEPTTL] [GET] [EX <seconds>] [PX <milliseconds>]
[EXAT <seconds-timestamp>][PXAT <milliseconds-timestamp>]

Two new options added here are EXAT and PXAT

Key implementation notes
- `SET` with `PX/EX/EXAT/PXAT` is always translated to `PXAT` in `AOF`. When relative time is
  specified (`PX/EX`), replication will always use `PX`.
- `setexCommand` and `psetexCommand` would no longer need translation in `feedAppendOnlyFile`
  as they are modified to invoke `setGenericCommand ` with appropriate flags which will take care of
  correct AOF translation.
- `GETEX` without any optional argument behaves like `GET`.
- `GETEX` command is never propagated, It is either propagated as `PEXPIRE[AT], or PERSIST`.
- `GETDEL` command is propagated as `DEL`
- Combined the validation for `SET` and `GETEX` arguments. 
- Test cases to validate AOF/Replication propagation
2021-01-27 19:47:26 +02:00
Huang ZwandGitHub f395119ede Fix bug in activeDefragSdsListAndDict dead code (#8403)
In activeDefragSdsListAndDict when dict_val_type is DEFRAG_SDS_DICT_VAL_VOID_PTR, it should update de->v.val not ln->value.
Because this code path will never be executed, so this bug never happened.
2021-01-27 18:56:43 +02:00
Wang YuanandGitHub ad7d4c6b70 Implement rdb-only replication (#8303)
In some scenarios, such as remote backup, we only want to get remote
redis server db snapshot. Currently, redis-cli acts as a replica and
sends SYNC to redis, but redis still accumulates replication buffer
in the replica client output buffer, that may result in using vast
memory, or failing to transfer RDB because of client-output-buffer-limit.
In this commit, we add 'replconf rdb-only 0|1', redis doesn't send
incremental replication buffer to them if they send 'replconf rdb-only 1',
so we can reduce used memory and improve success of getting RDB.
2021-01-27 17:13:10 +02:00
Oran AgraandGitHub 9e56d3969a Add tests for RESP3 responce of ZINTER and ZRANGE (#8391)
It was confusing as to why these don't return a map type.
the reason is that order matters, so we need to make sure the client
library knows to respect it.
Added comments in the implementation and tests to cover it.
2021-01-26 17:55:32 +02:00
Wen HuiandGitHub 1aad55b66f Sentinel: Fix Config Dependency and Rewrite Sequence (#8271)
This commit fixes a well known and an annoying issue in Sentinel mode.

Cause of this issue:
Currently, Redis rewrite process works well in server mode, however in sentinel mode,
the sentinel config has variant semantics for different configurations, in example configuration
https://github.com/redis/redis/blob/unstable/sentinel.conf, we put comments on these.
However the rewrite process only treat the sentinel config as a single option. During rewrite
process, it will mess up with the lines and comments.

Approaches:
In order to solve this issue, we need to differentiate different subconfig options in sentinel separately,
for example, sentinel monitor <master-name> <ip> <redis-port> <quorum>
we can treat it as sentinel monitor option, instead of the sentinel option.

This commit also fixes the dependency issue when putting configurations in sentinel.conf.
For example before this commit,we must put
`sentinel monitor <master-name> <ip> <redis-port> <quorum>` before
`sentinel auth-pass <master-name> <password>` for a single master,
otherwise the server cannot start and will return error. This commit fixes this issue, as long as
the monitoring master was configured, no matter the sequence is, the sentinel can start and run properly.
2021-01-26 09:31:54 +02:00
Vladimir MaksimovskiandGitHub 70789cf4bb Remove check leading to duplicate branches (#8398)
Remove check leading to duplicate branches and unused withscores parameter
2021-01-25 20:58:03 -08:00
zhaozhao.zzandGitHub 951d25ff66 latency: add expire-del event (#8394) 2021-01-26 09:45:53 +08:00
Oran AgraandGitHub 437e258384 Fix rare test failures due to repl-ping-replica-period (#8393)
some tests use attach_to_replication_stream to watch what's propagated
to replicas, but in some cases the periodic ping may slip in and fail
the test.
we disable that ping by setting the period to once an hour (tests should
not run for that long).

other change is so that the next time this oom-score-adj test fails,
we'll see the value (assert_equals prints it)
2021-01-25 11:05:25 +02:00
Oran AgraandGitHub f225891526 Fix recent test failures (#8386)
1. Valgrind leak in a recent change in a module api test
2. Increase treshold of a RESTORE TTL test
3. Change assertions to use assert_range which prints the values
2021-01-23 21:53:58 +02:00
Viktor SöderqvistandGitHub 9c1483100a Test that module can wake up module blocked on non-empty list key (#8382)
BLPOP and other blocking list commands can only block on empty keys
and LPUSH only wakes up clients when the list is created.

Using the module API, it's possible to block on a non-empty key.
Unblocking a client blocked on a non-empty list (or zset) can only
be done using RedisModule_SignalKeyAsReady(). This commit tests it.
2021-01-22 16:19:37 +02:00
8449a5df87 Sentinel tests, disable FD leak check, and print more details (#8376)
* Print more details about fd leaks
* temporarily prevent the leaks from failing the tests

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-22 12:11:58 +02:00
Huang ZwandGitHub 0232778f35 In dbOverwrite moduleNotifyKeyUnlink should use old val (#8381)
The module notification was using the wrong value.
2021-01-22 09:37:25 +02:00
Yossi GottliebandGitHub f28fccd2db Fix anetCloexec for Sentinel TLS conns. (#8377)
The flag should be set before TLS negotiation begins to avoid a race
condition where a fork+exec before it is completed ends up leaking the
file descriptor.
2021-01-21 19:55:42 +02:00
Yossi GottliebandGitHub 3f3cb9d09b Better error message const correctness. (#8348) 2021-01-21 11:56:08 +02:00
daemyungkangandGitHub 53cdf8a3d1 change log for invalid cluster port (#8343)
Update log message to correctly indicate cluster port range
2021-01-20 22:08:05 -08:00
Simon KotwiczandGitHub 4f9de66f0e Fix typo in redis.conf doc (#8362)
Fix incorrect documentation about default save time
2021-01-20 17:36:59 -08:00
Yossi GottliebandGitHub b548ffabbe CONFIG REWRITE should honor umask settings. (#8371)
Fixes a regression introduced due to a new (safer) way of rewriting configuration files. In the past the file was simply overwritten (same inode), but now Redis creates a new temporary file and later renames it over the old one.

The temp file typically gets created with 0600 permissions so we later fchmod it to fix that. Unlike open with O_CREAT, fchmod doesn't consider umask so we have to do that explicitly.

Fixes #8369
2021-01-20 21:57:24 +02:00
ac5f21d613 Add CI for FreeBSD (#8292)
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-20 14:07:09 +02:00
guybe7andGitHub 5a77d015be Fix misleading module test (#8366)
the test was misleading because the module would actually woke up on a wrong type and
re-blocked, while the test name suggests the module doesn't not wake up at all on a wrong type..

i changed the name of the test + added verification that indeed the module wakes up and gets
re-blocked after it understand it's the wrong type
2021-01-20 14:03:38 +02:00
guybe7andGitHub baf92f3f1c Fix firstkey,lastkey,step in COMMAND command for some commands (#8367)
The output for COMMAND command was wrong for some commands.
clients can use firstkey,lastkey,step to find (some) key name arguments, and the
"movablekeys" flag to know that they can't know all (or any) of the key name arguments.

These commands had the wrong output:
1. GEORADIUS*_RO used to have "movablekeys" (which it doesn't really need)
2. XREAD and XREADGROUP used to have (1,1,1). but that's completely wrong.
3. Z*STORE used to have (0,0,0) but it can at lest give the index of the dstkey (1,1,1)
2021-01-20 13:56:45 +02:00
Viktor SöderqvistandGitHub 16258f21d1 More modules API ref formatting fixes (#8344)
Fix broken formatting in `RM_Call` and `RM_CreateDataType`,
`RM_SubscribeToServerEvent` (nested lists, etc. in list items).

Unhide docs of `RM_LoadDataTypeFromString` and
`RM_SaveDataTypeToString` by removing blank line between docs and
function.

Clarification added to `RM__Assert`: Recommentation to use the
`RedisModule_Assert` macro instead.

All names containing underscores (variable and macro names) are
wrapped in backticks (if not already wrapped in backticks). This
prevents underscore from being interpreted as italics in some
cases.

Names including a wildcard star, e.g. RM_Defrag*(), is wrapped in
backticks (and RM replaced by RedisModule in this case). This
prevents the * from being interpreted as an italics marker.

A list item with a sublist, a paragraph and another sublist is a
combination which seems impossible to achieve with RedCarped
markdown, so the one occurrence of this is rewritten.

Various trivial changes (typos, backticks, etc.).

Ruby script:

* Replace `RM_Xyz` with `RedisModule_Xyz` in docs. (RM is correct
  when refering to the C code but RedisModule is correct in the
  API docs.)
* Automatic backquotes around C functions like `malloc()`.
* Turn URLs into links. The link text is the URL itself.
* Don't add backticks inside bold (**...**)
2021-01-20 11:47:06 +02:00
Andy PanandGitHub 6401920d70 Fix sentinel FD leak test, checking the wrong OS name (#8364) 2021-01-20 10:17:20 +02:00
Andy PanandGitHub 1be29606c5 Fix sentinel FD leak test, not printing the list of leaks (#8363) 2021-01-20 09:58:02 +02:00
Andy PanandGitHub fb66e2e249 Use FD_CLOEXEC in Sentinel, so that FDs don't leak to the scripts it runs (#8242)
Sentinel uses execve to run scripts, so it needs to use FD_CLOEXEC
on all file descriptors, so that they're not accessible by the script it runs.

This commit includes a change to the sentinel tests, which verifies no
FDs are left opened when the script is executed.
2021-01-19 22:57:30 +02:00
Itamar HaberandGitHub aaf71b380e Fix description of the save conf directive (#8337)
The line that said:

Note: you can disable saving completely by commenting out all "save" lines

was incorrect.

Also, note that the save values used in the default redis.conf file are different than the server's defaults, so this PR also addresses this and introduces a potential behavior change for some users.
2021-01-19 20:31:30 +02:00
Oran AgraandGitHub a29aec9abb Add tests to make sure that relative EXPIRE is propagated to replicas (#8357)
This commit adds tests to make sure that relative and absolute expire commands
are propagated as is to replicas and stop any future attempt to change that without
a proper discussion. see #8327 and #5171

Additionally it slightly improve the AOF test that tests the opposite (always
propagating absolute times), by covering more commands, and shaving 2
seconds from the test time.
2021-01-19 18:49:26 +02:00
filipe oliveiraandGitHub d42ea9e88a Removing unnecessary runtime tio_debug checks (#8250)
These statements were dead code.
2021-01-19 17:24:49 +02:00
Viktor SöderqvistandGitHub 4985c11bd6 Bugfix: Make modules blocked on keys unblock on commands like LPUSH (#8356)
This was a regression from #7625 (only in 6.2 RC2).

This makes it possible again to implement blocking list and zset
commands using the modules API.

This commit also includes a test case for the reverse: A module
unblocks a client blocked on BLPOP by inserting elements using
RedisModule_ListPush(). This already works, but it was untested.
2021-01-19 13:15:33 +02:00
Andy PanandGitHub 5198b513d3 Add a precheck before the actual call to fcntl (#8360)
Don't bother to call fcntl if the flags are not gonna be changed.
2021-01-19 11:36:21 +02:00
Raghav MuddurandGitHub 366a16ff05 Update getTimeZone to long (#8346) 2021-01-18 11:37:05 +02:00
Yossi GottliebandGitHub 522d93607a Add io-thread daily CI tests. (#8232)
This adds basic coverage to IO threads by running the cluster and few selected Redis test suite tests with the IO threads enabled.

Also provides some necessary additional improvements to the test suite:

* Add --config to sentinel/cluster tests for arbitrary configuration.
* Fix --tags whitelisting which was broken.
* Add a `network` tag to some tests that are more network intensive. This is work in progress and more tests should be properly tagged in the future.
2021-01-17 15:48:48 +02:00
f5cf1e46a4 [Sentinel] more specific config errors for duplicate known-replica and known-sentinel (#8332)
Previously invalid configuration errors were not very specific and in some cases hard to understand.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
2021-01-17 15:43:21 +02:00
Yang BodongandGitHub 294f93af97 Add lazyfree-lazy-user-flush config to control default behavior of FLUSH[ALL|DB], SCRIPT FLUSH (#8258)
* Adds ASYNC and SYNC arguments to SCRIPT FLUSH
* Adds SYNC argument to FLUSHDB and FLUSHALL
* Adds new config to control the default behavior of FLUSHDB, FLUSHALL and SCRIPT FLUASH.

the new behavior is as follows:
* FLUSH[ALL|DB],SCRIPT FLUSH: Determine sync or async according to the
  value of lazyfree-lazy-user-flush.
* FLUSH[ALL|DB],SCRIPT FLUSH ASYNC: Always flushes the database in an async manner.
* FLUSH[ALL|DB],SCRIPT FLUSH SYNC: Always flushes the database in a sync manner.
2021-01-15 15:32:58 +02:00
Viktor SöderqvistandOran Agra fcb3dfe56d Rename non-API RM-prefixed functions to hide them from API docs
The prefix is changed from `RM_` to `module` on the following
internal functions, to prevent them from appearing in the API docs:

    RM_LogRaw -> moduleLogRaw
    RM_FreeCallReplyRec -> moduleFreeCallReplyRec
    RM_ZsetAddFlagsToCoreFlags -> moduleZsetAddFlagsToCoreFlags
    RM_ZsetAddFlagsFromCoreFlags -> moduleZsetAddFlagsFromCoreFlags
2021-01-15 13:33:56 +02:00
Viktor SöderqvistandOran Agra ebf20b83b2 Modules API reference formatting fixes
Fixes markdown formatting errors and some functions not showing
up in the generated documentation at all.

Ruby script (gendoc.rb) fixes:

* Modified automatic instertion of backquotes:
  * Don't add backquotes around names which are already preceded by a
    backquote. Fixes for example \`RedisModule_Reply\*\` which turning
    into \`\`RedisModule_Reply\`\*\` messes up the formatting.
  * Add backquotes around types such as RedisModuleString (in addition
    to function names `RedisModule_[A-z()]*` and macro names
    `REDISMODULE_[A-z]*`).
  * Require 4 spaces indentation for disabling automatic backquotes, i.e.
    code blocks. Fixes continuations of list items (indented 2 spaces).
* More permissive extraction of doc comments:
  * Allow doc comments starting with `/**`.
  * Make space before `*` on each line optional.
  * Make space after `/*` and `/**` optional (needed when appearing on
    its own line).

Markdown fixes in module.c:

* Fix code blocks not indented enough (4 spaces needed).
* Add black line before code blocks and lists where missing (needed).
* Enclose special markdown characters `_*^<>` in backticks to prevent them
  from messing up formatting.
* Lists with `1)` changed to `1.` for proper markdown lists.
* Remove excessive indentation which causes text to be unintentionally
  rendered as code blocks.
* Other minor formatting fixes.

Other fixes in module.c:

* Remove blank lines between doc comment and function definition. A blank
  line here makes the Ruby script exclude the function in docs.
2021-01-15 13:33:56 +02:00
charsyamandGitHub 108afbc0bb Replace many calling zrealloc to one zmalloc in sentinelResetMasterAndChangeAddress (#8319)
Replace many calling zrealloc to one zmalloc in sentinelResetMasterAndChangeAddress
2021-01-13 16:44:04 -08:00
Wang YuanandGitHub 9cb9f98d2f Optimize performance of clusterGenNodesDescription for large clusters (#8182)
Optimize the performance of clusterGenNodesDescription by only checking slot ownership of each slot once, instead of checking each slot for each node.
2021-01-13 12:36:03 -08:00
houzj.fnstandGitHub f5577fdbd8 remove some unnecessary checks (#7431)
Remove several checks that always evaluate to true.
2021-01-13 12:00:55 -08:00
sundbandGitHub 8a81ed1b5a Fix use of lookupKeyRead and lookupKeyWrite in zrangeGenericCommand, zunionInterDiffGenericCommand (#8316)
* Change zunionInterDiffGenericCommand to use lookupKeyRead if dstkey is null
* Change zrangeGenericCommand to use lookupKey Write if dstkey isn't null

ZRANGESTORE and UNION, ZINTER, ZDIFF are all new commands (6.2 RC1 and RC2).
In redis 6.0 the ZRANGE was using lookupKeyRead, and ZUNIONSTORE / ZINTERSTORE were using lookupKeyWrite.
So there bugs are introduced in 6.2 and will be resolved before it is released.

the implications of this bug are also not big:
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)!
2021-01-13 11:58:12 +02:00
sundbandGitHub 593cde16bc Fix units in log message of copy-on-write (#8320) 2021-01-13 11:38:02 +02:00
Oran AgraandGitHub ec2d180739 Merge 6.2 RC2 2021-01-12 16:21:03 +02:00
Oran Agra 049f2f0805 Redis 6.2 RC2 2021-01-12 16:17:58 +02:00
Oran Agra b34fc03e5e Merge unstable into 6.2 2021-01-12 10:14:04 +02:00
Oran AgraandGitHub 4f8458d8d6 fix race in cluster transactions test (#8312)
we didn't wait for the commands executed on the master to reach the replica.
2021-01-12 10:03:45 +02:00
Madelyn OlsonandGitHub b24b490393 Fix issues in wait test (#8310)
This fixes three issues:
1.  Using debug SLEEP was impacting the subsequent test, and causing it to pass reliably even though it should have failed. There was exactly 5 seconds of artificial pause (after 1000, wait 3000, wait 1000) between the debug sleep 5 and when we needed to unblock the client in the subsequent test. Now the test properly makes sure the client is unblocked, and the subsequent test is fixed.
2. Minor, the client pause types were using & comparisons instead of ==, since it was previously a flag.
3. Test is faster now that some of the hand wavy time is removed.
2021-01-12 09:46:24 +02:00
Oran AgraandGitHub 264953871b 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.
2021-01-12 09:41:57 +02:00
Bob LiandGitHub 542455ce2a 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
2021-01-12 08:22:53 +02:00
sundbandGitHub fe2fdef7b0 Assert that clusterAddNode can't fail (#8296)
Assert that clusterAddNode can't fail
2021-01-09 10:24:58 -08:00
Oran AgraandGitHub 430dadaf83 CLIENT PAUSE - don't drop together with other blocked clients (#8302)
When the server state changes and blocked clients are being dropped, the
paused clients should not be dropped, they're safe to keep since unlike
other blocked types, these commands are not half way though processing,
and the commands they sent may get rejected according to the new server
state.
2021-01-09 10:22:20 -08:00
36f1dea5e1 Fix compile warning when define REDIS_TEST (#8261)
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-09 19:52:42 +02:00
Oran AgraandGitHub 8dd16caec8 Fix last COW INFO report, Skip test on non-linux platforms (#8301)
- the last COW report wasn't always read from the pipe
  (receiveLastChildInfo wasn't used)
- but in fact, there's no reason we won't always try to drain that pipe
  so i'm unifying receiveLastChildInfo with receiveChildInfo
- adjust threshold of the COW test when run in accurate mode
- add some prints in case this test fails again
- fix indentation, page size, and PID! in MacOS proc info

p.s. it seems that pri_pages_dirtied is always 0
2021-01-08 23:35:30 +02:00
Yang BodongandGitHub ea5350c5ec GEOSEARCH - ANY option, for limited search that returns ASAP (#8259)
Support ANY option to return some results that match the criteria ASAP,
without a complete search and implicit sorting.
2021-01-08 18:29:44 +02:00
guybe7andGitHub 814aad65f1 XADD and XTRIM, Trim by MINID, and new LIMIT argument (#8169)
This PR adds another trimming strategy to XADD and XTRIM named MINID
(complements the existing MAXLEN).
It also adds a new LIMIT argument that allows incremental trimming by repeated
calls (rather than all at once).

This provides the ability to trim all records older than a certain ID (which makes it
possible for the user to trim by age too).
Example:
XTRIM mystream MINID ~ 1608540753 will trim entries with id < 1608540753,
but might not trim all (because of the ~ modifier)

The purpose is to ease the use of streams. many users use streams as logs and
the common case is wanting a log
of the last X seconds rather than a log that contains maximum X entries (new
MINID vs existing MAXLEN)

The new LIMIT modifier is only supported when the trim strategy uses ~.
i.e. when the user asked for exact trimming, it all happens in one go (no
possibility for incremental trimming).
However, when ~ is provided, we trim full rax nodes, up to the limit number
of records.
The default limit is 100*stream_node_max_entries (used when LIMIT is not
provided).
I.e. this is a behavior change (even if the existing MAXLEN strategy is used).
An explicit limit of 0 means unlimited (but note that it's not the default).

Other changes:

Refactor arg parsing code for XADD and XTRIM to use common code.
2021-01-08 18:13:25 +02:00
Oran AgraandGitHub 5843a45d01 Skip defrag tests on systems with bigger page sizes (#8294)
The defragger works well on these systems, but the tests and their
thresholds are not adjusted for these big pages, so the defragger isn't
able to get down the fragmentation to the levels the test expects and it
fails on "defrag didn't stop".

Randomly choosing 8k as the threshold for the skipping

Fixes #8265 (which had 65k pages)
2021-01-08 10:03:21 +02:00
Madelyn OlsonandGitHub 999494cef8 Throw error for conflicting bcast tracking prefixes (#8176)
Throw an error if there are conflicting bcast tracking prefixes.
2021-01-08 00:00:35 -08:00
Madelyn OlsonandGitHub 47579bdf5c Add support for client pause WRITE (#8170)
Implementation of client pause WRITE and client unpause
2021-01-07 23:36:54 -08:00
b02780c41d 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>
2021-01-07 17:06:05 +02:00
YaacovHazanandOran Agra ea930a352c Report child copy-on-write info continuously
Add INFO field, rdb_active_cow_size, to report COW of a live fork child while
it's active.
- once in 1024 keys check the time, and if there's more than one second since
  the last report send a report to the parent via the pipe.
- refactor the child_info_data struct, it's an implementation detail that
  shouldn't be in the server struct, and not used to communicate data between
  caller and callee
- remove the magic value from that struct (not sure what it was good for), and
  instead add handling of short reads.
- add another value to the structure, cow_type, to indicate if the report is
  for the new rdb_active_cow_size field, or it's the last report of a
  successful operation
- add new Module API to report the active COW
- add more asserts variants to test.tcl
2021-01-07 16:14:29 +02:00
YaacovHazanandOran Agra f9dacf8aac Refactory fork child related infra, Unify child pid
This is a refactory commit, isn't suppose to have any actual impact.
it does the following:
- keep just one server struct fork child pid variable instead of 3
- have one server struct variable indicating the purpose of the current fork
  child.
- redisFork is now responsible of updating the server struct with the pid,
  which means it can be the one that calls updateDictResizePolicy
- move child info pipe handling into redisFork instead of having them
  repeated outside
- there are two classes of fork purposes, mutually exclusive group (AOF, RDB,
  Module), and one that can create several forks to coexist in parallel (LDB,
  but maybe Modules some day too, Module API allows for that).
- minor fix to killRDBChild:
  unlike killAppendOnlyChild and TerminateModuleForkChild, the killRDBChild
  doesn't clear the pid variable or call wait4, so checkChildrenDone does
  the cleanup for it.
  This commit removes the explicit calls to rdbRemoveTempFile, closeChildInfoPipe,
  updateDictResizePolicy, which didn't do any harm, but where unnecessary.
2021-01-07 16:14:29 +02:00
b5029dfdad Add ZRANGESTORE command, and improve ZSTORE command (#7844)
Add ZRANGESTORE command, and improve ZSTORE command to deprecated Z[REV]RANGE[BYSCORE|BYLEX].

Syntax for the new ZRANGESTORE command:
ZRANGESTORE [BYSCORE | BYLEX] [REV] [LIMIT offset count]

New syntax for ZRANGE:
ZRANGE [BYSCORE | BYLEX] [REV] [WITHSCORES] [LIMIT offset count]

Old syntax for ZRANGE:
ZRANGE [WITHSCORES]

Other ZRANGE commands remain unchanged.

The implementation uses common code for all of these, by utilizing a consumer interface that in one
command response to the client, and in the other command stores a zset key.

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-07 10:58:53 +02:00
Wen HuiandGitHub cfcd0fa6f7 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
2021-01-06 21:20:53 +02:00
714e103ac3 Add XAUTOCLAIM (#7973)
New command: XAUTOCLAIM <key> <group> <consumer> <min-idle-time> <start> [COUNT <count>] [JUSTID]

The purpose is to claim entries from a stale consumer without the usual
XPENDING+XCLAIM combo which takes two round trips.

The syntax for XAUTOCLAIM is similar to scan: A cursor is returned (streamID)
by each call and should be used as start for the next call. 0-0 means the scan is complete.

This PR extends the deferred reply mechanism for any bulk string (not just counts)

This PR carries some unrelated test code changes:
- Renames the term "client" into "consumer" in the stream-cgroups test
- And also changes DEBUG SLEEP into "after"

Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-06 10:34:27 +02:00
huangzhwandGitHub 595ecd5f4b 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.
2021-01-05 18:41:53 +02:00
Oran AgraandGitHub 324070c8f6 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.
2021-01-05 09:15:10 +02:00
Oran AgraandGitHub 2017407b4d 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
2021-01-05 08:29:20 +02:00
Oran AgraandGitHub df9c213bb0 remove unused call to zmalloc_size in defrag.c (#8285) 2021-01-04 23:16:19 +02:00
Itamar HaberandGitHub 9dcdc7e79a HELP subcommand, continued (#5531)
* man-like consistent long formatting
* Uppercases commands, subcommands and options
* Adds 'HELP' to HELP for all
* Lexicographical order
* Uses value notation and other .md likeness
* Moves const char *help to top
* Keeps it under 80 chars
* Misc help typos, consistent conjuctioning (i.e return and not returns)
* Uses addReplySubcommandSyntaxError(c) all over

Signed-off-by: Itamar Haber <itamar@redislabs.com>
2021-01-04 17:02:57 +02:00
10f94b0ab1 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>
2021-01-04 14:48:28 +02:00
Meir Shpilraien (Spielrein)andGitHub ecd5351870 Fix assertion on loading AOF with timed out script. (#8284)
If AOF file contains a long Lua script that timed out, then the `evalCommand` calls
`blockingOperationEnds` which sets `server.blocked_last_cron` to 0. later on,
the AOF `whileBlockedCron` function asserts that this value is not 0.

The fix allows nesting call to `blockingOperationStarts` and `blockingOperationEnds`.

The issue was first introduce in this commit: 9ef8d2f67 (Redis 6.2 RC1)
2021-01-04 13:42:17 +02:00
huangzhwandGitHub 08ad6abd04 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.
2021-01-04 10:28:47 +02:00
33fb617053 GEOADD - add [CH] [NX|XX] options (#8227)
New command flags similar to what SADD already has.

Co-authored-by: huangwei03 <huangwei03@kuaishou.com>
Co-authored-by: Itamar Haber <itamar@redislabs.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2021-01-03 17:13:37 +02:00
Oran AgraandGitHub 91690a2920 Fix uninitialized variable in new errorstats commit (#8280) 2021-01-03 16:14:52 +02:00
Oran AgraandGitHub 7896debe6c Fix rare assertion as a result of: active defrag while loading (#8281)
In #7726 (part of 6.2), we added a mechanism for whileBlockedCron, this
mechanism has an assertion to make sure the timestamp in
whileBlockedCron was always set correctly before the blocking operation
starts.

I now found (thanks to our CI) two bugs in that area:
1) CONFIG RESETSTAT (if it was allowed during loading) would have
   cleared this var
2) the call stopLoading (which calls whileBlockedCron) was made too
   early, while the rio is still in use, in which case the update_cksum
   (rdbLoadProgressCallback) may still be called and whileBlockedCron
   can assert.
2021-01-03 16:09:29 +02:00
Oran AgraandGitHub 41b2ed2bbc 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.
2021-01-03 11:56:26 +02:00
Oran AgraandGitHub 71fbe6e800 Fix leak in new errorstats commit, and a flaky test (#8278) 2021-01-02 08:37:19 +02:00
Oran AgraandGitHub 152b5d46c4 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.
2021-01-01 10:23:30 +02:00
filipe oliveiraandGitHub 90b9f08e5d Add errorstats info section, Add failed_calls and rejected_calls to commandstats (#8217)
This Commit pushes forward the observability on overall error statistics and command statistics within redis-server:

It extends INFO COMMANDSTATS to have
- failed_calls in - so we can keep track of errors that happen from the command itself, broken by command.
- rejected_calls - so we can keep track of errors that were triggered outside the commmand processing per se

Adds a new section to INFO, named ERRORSTATS that enables keeping track of the different errors that
occur within redis ( within processCommand and call ) based on the reply Error Prefix ( The first word
after the "-", up to the first space ).

This commit also fixes RM_ReplyWithError so that it can be correctly identified as an error reply.
2020-12-31 16:53:43 +02:00
sundbandGitHub 1f5a73a530 Merge pushGenericCommand and pushxGenericCommand (#8255)
Merge pushGenericCommand and pushxGenericCommand to remove redundancy and simplify code.
2020-12-29 22:37:37 -08:00
Oran AgraandGitHub 049cf8cdf4 Fix memory leaks in error replies due to recent change (#8249)
Recently efaf09ee4 started using addReplyErrorSds in place of
addReplySds the later takes ownership of the string but the former did
not.
This introduced memory leaks when a script returns an error to redis,
and also in clusterRedirectClient (two new usages of
addReplyErrorSds which was mostly unused till now.

This commit chagnes two thanks.
1. change addReplyErrorSds to take ownership of the error string.
2. scripting.c doesn't actually need to use addReplyErrorSds, it's a
perfect match for addReplyErrorFormat (replaces newlines with spaces)
2020-12-27 21:40:12 +02:00
Oran AgraandGitHub 19d4705ffd Make the protocol-version argument of HELLO optional (#7377) 2020-12-27 16:37:27 +02:00
299f9ebffa Tracking: add CLIENT TRACKINGINFO subcommand (#7309)
Add CLIENT TRACKINGINFO subcommand

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-27 13:14:39 +02:00
Itamar HaberandGitHub f44186e575 Adds count to L/RPOP (#8179)
Adds: `L/RPOP <key> [count]`

Implements no. 2 of the following strategies:

1. Loop on listTypePop - this would result in multiple calls for memory freeing and allocating (see https://github.com/redis/redis/pull/8179/commits/769167a079b0e110d28e4a8099dce1ecd45682b5)
2. Iterate the range to build the reply, then call quickListDelRange - this requires two iterations and **is the current choice**
3. Refactor quicklist to have a pop variant of quickListDelRange - probably optimal but more complex

Also:
* There's a historical check for NULL after calling listTypePop that was converted to an assert.
* This refactors common logic shared between LRANGE and the new form of LPOP/RPOP into addListRangeReply (adds test for b/w compat)
* Consequently, it may have made sense to have `LRANGE l -1 -2` and `LRANGE l 9 0` be legit and return a reverse reply. Due to historical reasons that would be, however, a breaking change.
* Added minimal comments to existing commands to adhere to the style, make core dev life easier and get commit karma, naturally.
2020-12-25 21:49:24 +02:00
Itamar HaberandGitHub e18068d9d8 Use addReplyErrorObject with shared.syntaxerror (#8248) 2020-12-25 18:27:30 +02:00
xhe e6c1aeaf08 fix the format
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-25 10:17:55 +08:00
fae5ceef2a reword
Co-authored-by: Itamar Haber <itamar@redislabs.com>
2020-12-25 01:40:06 +08:00
Oran AgraandGitHub 4617960863 resolve hung test. 2020-12-24 14:33:53 +02:00
xhe 78eaf503fd address comment
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 20:13:57 +08:00
xhe f6711b7da5 reword
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 19:25:30 +08:00
xhe 955e00fbec ask protover for authentication
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 19:23:35 +08:00
4e36925c66 correction
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 19:16:28 +08:00
c4b52fc7c9 cleanup: ziplist prev entry large length use sizeof(uint32_t) instead 4 (#8241)
This is just a cleanup, no bugs in the real world.

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 11:58:43 +02:00
Oran Agra e87c31de66 syncWithMaster: use pipeline for AUTH+REPLCONF*3
The commit deals with the syncWithMaster and the ugly state machine in it.
It attempts to clean it a bit, but more importantly it uses pipeline for
part of the work (rather than 7 round trips, we now have 4).
i.e. the connect and PING are separate, then AUTH + 3 REPLCONF in one pipeline,
and finally the PSYNC (must be separate since the master has to have an empty
output buffer).
2020-12-24 11:55:28 +02:00
Oran Agra 9bd212cf24 syncWithMaster: sendSynchronousCommand split to send, and receive
This is just a refactoring commit.

This function was never actually used as a synchronous (do both send or
receive), it was always used only ine one of the two modes, which meant it
has to take extra arguments that are not relevant for the other.

Besides that, a tool that sends a synchronous command, it not something
we want in our toolbox (synchronous IO in single threaded app is evil).

sendSynchronousCommand was now refactored into separate sending and
receiving APIs, and the sending part has two variants, one taking vaargs,
and the other taking argc+argv (and an optional length array which means
you can use binary sds strings).
2020-12-24 11:55:28 +02:00
xhe ef14c18c8e fix the test
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 17:31:50 +08:00
98f39a37fb simplify
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 17:03:53 +08:00
723b4a15a3 simplify
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 17:03:45 +08:00
c07d3bd8dd simplify
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 17:03:36 +08:00
456c347d45 simplify
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-24 17:03:22 +08:00
xhe 60f13e7a86 try to fix the test
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 16:50:08 +08:00
Brad DunbarandGitHub 35fc7fda7a Typo: timout -> timeout (#8228) 2020-12-24 10:42:52 +02:00
xhe 50d750733e prefer !
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 15:29:17 +08:00
xhe 7a7c60459e add a test
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 15:26:24 +08:00
xhe 2e8f8c9b0c HELLO without protover
Signed-off-by: xhe <xw897002528@gmail.com>
2020-12-24 13:35:41 +08:00
xhe b3dc23c5a8 add a read-only variant for HELLO
As discussed in https://github.com/antirez/redis/issues/7364, it is good
to have a HELLO command variant, which does not switch the current proto
version of a redis server.

While `HELLO` will work, it introduced a certain difficulty on parsing
options of the command. We will need to offset the index of authentication
and setname option by -1.

So 0 is marked a special version meaning non-switching. And we do not
need to change the code much.
2020-12-24 13:03:47 +08:00
Madelyn OlsonandGitHub 59ff42c421 Cleanup key tracking documentation and table management (#8039)
Cleanup key tracking documentation, always cleanup the tracking table, and free the tracking table in an async manner when applicable.
2020-12-23 19:13:12 -08:00
Madelyn OlsonandGitHub efaf09ee4b Flow through the error handling path for most errors (#8226)
Properly throw errors for invalid replication stream and support https://github.com/redis/redis/pull/8217
2020-12-23 19:06:25 -08:00
Wang YuanandGitHub 55abd1c6d0 Add semicolon to calls of test_cond() (#8238) 2020-12-23 09:16:49 -08:00
sundbandGitHub 58e9c26115 Fix redundancy incrRefCount in lmoveGenericCommand (#8218) 2020-12-23 08:37:33 -08:00
Yang BodongandGitHub ee59dc1b5c Tests: fix the problem that Darwin memory leak detection may fail (#8213)
Apparently the "leaks" took reports a different error string about process
that's not found in each version of MacOS.
This cause the test suite to fail on some OS versions, since some tests terminate
the process before looking for leaks.
Instead of looking at the error string, we now look at the (documented) exit code.
2020-12-23 16:28:17 +02:00
266949c7fc Fix random element selection for large hash tables. (#8133)
When a database on a 64 bit build grows past 2^31 keys, the underlying hash table expands to 2^32 buckets. After this point, the algorithms for selecting random elements only return elements from half of the available buckets because they use random() which has a range of 0 to 2^31 - 1. This causes problems for eviction policies which use dictGetSomeKeys or dictGetRandomKey. Over time they cause the hash table to become unbalanced because, while new keys are spread out evenly across all buckets, evictions come from only half of the available buckets. Eventually this half of the table starts to run out of keys and it takes longer and longer to find candidates for eviction. This continues until no more evictions can happen.

This solution addresses this by using a 64 bit PRNG instead of libc random().

Co-authored-by: Greg Femec <gfemec@google.com>
2020-12-23 15:52:07 +02:00
Oran AgraandGitHub 2426aaa099 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
2020-12-23 12:55:05 +02:00
Felix BünemannandGitHub b51f5da314 Fix TLS build on macOS arm64 systems (#8197)
Homebrew for darwin-arm64 uses /opt/homebrew instead of /usr/local as
the prefix, so that it can coexist with darwin-x86_64 using Rosetta 2.
2020-12-23 09:46:23 +02:00
Wen HuiandGitHub 781d7b0d9b Sentinel: add missing calls for sentinelflushconfig when config master at runtime (#8229) 2020-12-22 16:14:15 +02:00
Meir Shpilraien (Spielrein)andGitHub 92a483bca2 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.
2020-12-22 15:17:39 +02:00
Yossi GottliebandGitHub e7047ec2fc 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
2020-12-22 12:24:20 +02:00
Oran AgraandGitHub 411c18bbce Remove read-only flag from non-keyspace cmds, different approach for EXEC to propagate MULTI (#8216)
In the distant history there was only the read flag for commands, and whatever
command that didn't have the read flag was a write one.
Then we added the write flag, but some portions of the code still used !read
Also some commands that don't work on the keyspace at all, still have the read
flag.

Changes in this commit:
1. remove the read-only flag from TIME, ECHO, ROLE and LASTSAVE

2. EXEC command used to decides if it should propagate a MULTI by looking at
   the command flags (!read & !admin).
   When i was about to change it to look at the write flag instead, i realized
   that this would cause it not to propagate a MULTI for PUBLISH, EVAL, and
   SCRIPT, all 3 are not marked as either a read command or a write one (as
   they should), but all 3 are calling forceCommandPropagation.

   So instead of introducing a new flag to denote a command that "writes" but
   not into the keyspace, and still needs propagation, i decided to rely on
   the forceCommandPropagation, and just fix the code to propagate MULTI when
   needed rather than depending on the command flags at all.

   The implication of my change then is that now it won't decide to propagate
   MULTI when it sees one of these: SELECT, PING, INFO, COMMAND, TIME and
   other commands which are neither read nor write.

3. Changing getNodeByQuery and clusterRedirectBlockedClientIfNeeded in
   cluster.c to look at !write rather than read flag.
   This should have no implications, since these code paths are only reachable
   for commands which access keys, and these are always marked as either read
   or write.

This commit improve MULTI propagation tests, for modules and a bunch of
other special cases, all of which used to pass already before that commit.
the only one that test change that uncovered a change of behavior is the
one that DELs a non-existing key, it used to propagate an empty
multi-exec block, and no longer does.
2020-12-22 12:03:49 +02:00
sundbandGitHub 4bc14da2b3 Fix some redundancy use of semicolon in do-while macros (#8221)
* Fix some redundancy use of semicolon in do-while macros
2020-12-21 22:57:45 -08:00
valentinogeronandGitHub 4c13945c37 Fix PFDEBUG commands flag (#8222)
- Mark it as a @hyperloglog command (ACL)
- Should not be allowed in OOM
- Add firstkey, lastkey, step
- Add comment that explains the 'write' flag
2020-12-21 15:40:20 +02:00
sundbandGitHub 51eb0da824 Fix command reset's arity (#8212) 2020-12-18 14:55:39 +02:00
Qu ChenandGitHub 11b3325e99 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.
2020-12-17 21:58:58 +02:00
Qu ChenandGitHub f48afb4710 Handle binary safe string for REQUIREPASS and MASTERAUTH directives (#8200)
* Handle binary safe string for REQUIREPASS and MASTERAUTH directives.
2020-12-17 09:26:33 -08:00
Nick RevinandGitHub 0f3e0cb4ad install redis-check-rdb and redis-check-aof as symlinks to redis-server (#5745) 2020-12-17 14:49:19 +02:00
a56cbd3036 More fixes to printf format specifier. (#7909)
mostly signed / unsigned mismatches.

Signed-off-by: Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-12-17 13:00:48 +02:00
sundbandGitHub 407da77ea4 Fix comment of georadiusGeneric function (#8202) 2020-12-17 11:02:17 +02:00
Wang YuanandGitHub 6413e5f81a [Redis-benchmark] Use IP from CLUSTER NODE reply for first node too (#8154)
If we only has one node in cluster or before 8fdc857, we don't know myself ip, so we should use config.hostip for myself.
However, we should use the IP from the command response to update node->ip if it exists and is different from config.hostip

otherwise, when there's more than one node in cluster, if we use -h with virtual IP or DNS, benchmark doesn't show node real ip and port of myself even though it could get right IP and port by CLUSTER NODES command.
2020-12-17 10:22:13 +02:00
Wen HuiandGitHub 4f67d0b647 fix wrong comment in cluster.h (#8191) 2020-12-16 23:19:12 +02:00
filipe oliveiraandGitHub 1f42bd7057 Included in redis.conf explicit explanation of tls-protocol defaults (#8193) 2020-12-15 22:03:05 +02:00
sundbandGitHub 7993780dda Fix some wrong server.dirty increments (#8140)
Fix wrong server dirty increment in
* spopWithCountCommand
* hsetCommand
* ltrimCommand
* pfaddCommand

Some didn't increment the amount of fields (just one per command).
Others had excessive increments.
2020-12-15 09:30:24 +02:00
Oran AgraandGitHub b8c67ce41b Redis 6.2 RC1. 2020-12-14 20:54:10 +02:00
Itamar HaberandGitHub 9acd40d97b GEOSEARCH: change 'FROMLOC' to 'FROMLONLAT' (#8190)
And formats style a tiniee-winiee bit
2020-12-14 17:15:12 +02:00
Oran AgraandGitHub cfb449cc80 Sanitize dump payload: excessive free on dup zset fields (#8189) 2020-12-14 17:10:31 +02:00
Oran AgraandGitHub 7d9b09adaa Tests: fix new defrag test to be skipped when not supported (#8185)
Additionally the older defrag tests are using an obsolete way to check
if the defragger is suuported (the error no longer contains "DISABLED").
this doesn't usually makes a difference since these tests are completely
skipped if the allocator is not jemalloc, but that would fail if the
allocator is a jemalloc that doesn't support defrag.
2020-12-14 11:13:46 +02:00
1e301ff83e Fix redis-cli crash on nil invalidate messages. (#8183)
This is a backport of redis/hiredis@b9b9f44.

Co-authored-by: michael-grunder <michael.grunder@gmail.com>
2020-12-13 22:11:06 +02:00
filipe oliveiraandGitHub 19d46f8f2f Expose Redis main thread cpu time, and scrape system time via INFO command. (#8132)
Exposes the main thread CPU info via info modules ( linux specific only )
(used_cpu_sys_main_thread and used_cpu_user_main_thread). This is important for:

- distinguish between main thread and io-threads cpu time total cpu time consumed ( check
  what is the first bottleneck on the used config )
- distinguish between main thread and modules threads total cpu time consumed

Apart from it, this commit also exposes the server_time_usec within the Server section so that we can
properly differentiate consecutive collection and calculate for example the CPU% and or / cpu time vs
wall time, etc...
2020-12-13 19:14:46 +02:00
Yossi GottliebandGitHub 86e3395c11 Several (mostly Solaris-related) cleanups (#8171)
* Allow runtest-moduleapi use a different 'make', for systems where GNU Make is 'gmake'.
* Fix issue with builds on Solaris re-building everything from scratch due to CFLAGS/LDFLAGS not stored.
* Fix compile failure on Solaris due to atomicvar and a bunch of warnings.
* Fix garbled log timestamps on Solaris.
2020-12-13 17:09:54 +02:00
Wen HuiandGitHub f74c32cad2 redis-cli prompt: show transaction state, and fix db number on aborted EXEC (#6728) 2020-12-13 14:40:54 +02:00
Oran AgraandGitHub ab60dcf564 Add module event for repl-diskless-load swapdb (#8153)
When a replica uses the diskless-load swapdb approach, it backs up the old database,
then attempts to load a new one, and in case of failure, it restores the backup.

this means that modules with global out of keyspace data, must have an option to
subscribe to events and backup/restore/discard their global data too.
2020-12-13 14:36:06 +02:00
Wen HuiandGitHub 8a23ca0b28 add missing raxStop calls in aof stream rewrite (#8162) 2020-12-13 11:47:24 +02:00
Madelyn OlsonandGitHub 5abdf9a556 Removed usage of bool from tls.c (#8175) 2020-12-13 11:11:29 +02:00
Wen HuiandGitHub bde33501c4 redis-cli -e option to exit with error code immediately when command fails (#8136)
without this option, redis-cli returns 0 even if command fails. kept this for backwards compatibility.
2020-12-13 10:04:45 +02:00
Wang YuanandGitHub e3ff414513 Add total_forks to INFO STATS (#8155) 2020-12-13 10:01:18 +02:00
Yossi GottliebandGitHub 63c1303cfb Modules: add defrag API support. (#8149)
Add a new set of defrag functions that take a defrag context and allow
defragmenting memory blocks and RedisModuleStrings.

Modules can register a defrag callback which will be invoked when the
defrag process handles globals.

Modules with custom data types can also register a datatype-specific
defrag callback which is invoked for keys that require defragmentation.
The callback and associated functions support both one-step and
multi-step options, depending on the complexity of the key as exposed by
the free_effort callback.
2020-12-13 09:56:01 +02:00
gouravandGitHub ddd43b6bc3 Randomize the random number generator's seed used in redis-benchmark (#8174)
The pid of the benchmark process is used to randomize the random number generator's
seed. This ensures that when multiple benchmark processes are started at the same time
to generate load on a server, they use different seeds. This will ensure randomness in
the keys generated by different benchmark processes.
2020-12-12 17:27:35 -08:00
Itamar HaberandGitHub feba7cbf4d Adds 'use-memory' to GEORADIUS[BYMEMBER] (#8107)
Partial resolution for #6860, item 7
2020-12-12 17:31:40 +02:00
杨博东andGitHub 4d06d99bf8 Add GEOSEARCH / GEOSEARCHSTORE commands (#8094)
Add commands to query geospatial data with bounding box.

Two new commands that replace the existing 4 GEORADIUS* commands.

GEOSEARCH key [FROMMEMBER member] [FROMLOC long lat] [BYRADIUS radius
unit] [BYBOX width height unit] [WITHCORD] [WITHDIST] [WITHASH] [COUNT
count] [ASC|DESC]

GEOSEARCHSTORE dest_key src_key [FROMMEMBER member] [FROMLOC long lat]
[BYRADIUS radius unit] [BYBOX width height unit] [WITHCORD] [WITHDIST]
[WITHASH] [COUNT count] [ASC|DESC] [STOREDIST]

- Add two types of CIRCULAR_TYPE and RECTANGLE_TYPE to achieve different searches
- Judge whether the point is within the rectangle, refer to:
geohashGetDistanceIfInRectangle
2020-12-12 02:21:05 +02:00
Yossi GottliebandGitHub 8c291b97b9 TLS: Add different client cert support. (#8076)
This adds a new `tls-client-cert-file` and `tls-client-key-file`
configuration directives which make it possible to use different
certificates for the TLS-server and TLS-client functions of Redis.

This is an optional directive. If it is not specified the `tls-cert-file`
and `tls-key-file` directives are used for TLS client functions as well.

Also, `utils/gen-test-certs.sh` now creates additional server-only and client-only certs and will skip intensive operations if target files already exist.
2020-12-11 18:31:40 +02:00
Yossi GottliebandGitHub 4e064fbab4 Add module data-type support for COPY. (#8112)
This adds a copy callback for module data types, in order to make
modules compatible with the new COPY command.

The callback is optional and COPY will fail for keys with data types
that do not implement it.
2020-12-09 20:22:45 +02:00
Yossi GottliebandGitHub cf3d79d4c1 Fix double close on IPv6 setup error. (#8168)
Fixes #8165.
2020-12-09 20:20:55 +02:00
Oran AgraandGitHub 6a3c4ac50a zset full dump sanitization bug (dup score instead of field) (#8167) 2020-12-09 17:05:05 +02:00
Oran AgraandGitHub 48efc25f74 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.
2020-12-08 16:41:20 +02:00
Oran AgraandGitHub a102b21d17 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)
2020-12-08 16:33:09 +02:00
Wang YuanandGitHub 1acc315cea Minor improvements for list-2 test (#8156)
had some unused variables.
now some are used to assert that they match, others were useless.
2020-12-08 16:26:38 +02:00
Yossi GottliebandGitHub 00db1b5579 Fix failing macOS tests due to wc differences. (#8161) 2020-12-08 16:22:16 +02:00
Itamar HaberandGitHub 37f45d9e56 Adds exclusive range query intervals to XPENDING (#8130) 2020-12-08 11:43:00 +02:00
Yossi GottliebandGitHub ec02c761aa 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.
2020-12-08 11:27:30 +02:00
Wen HuiandGitHub 09b7f6c2a1 use getPositiveLongFromObjectOrReply for positive check in spop (#8146)
This is a cleanup commit that doesn't fix any bug, however it does change the error response text in case the user provided a negative number.
2020-12-08 11:15:39 +02:00
guybe7andGitHub 6bb5503524 More efficient self-XCLAIM (#8098)
when the same consumer re-claim an entry that it already has, there's
no need to remove-and-insert if it's the same rax.
we do need to update the idle time though.
this commit only improves efficiency (doesn't change behavior).
2020-12-07 21:31:35 +02:00
filipe oliveiraandGitHub b5e99bd064 Enable configuring OpenSSL using the standard openssl.cnf (#8143) 2020-12-07 14:30:12 +02:00
bccbc5509a Add CLIENT INFO and CLIENT LIST [id]. (#8113)
* Add CLIENT INFO subcommand.

The output is identical to CLIENT LIST but provides a single line for
the current client only.

* Add CLIENT LIST ID [id...].

Co-authored-by: Itamar Haber <itamar@redislabs.com>
2020-12-07 14:24:05 +02:00
David CARLIERandGitHub ec951cdc15 Solaris based system rss size report. (#8138) 2020-12-06 15:30:29 +02:00
Oran Agra e288430c05 Sanitize dump payload: performance optimizations and tuning
First, if the ziplist header is surely inside the ziplist, do fast path
decoding rather than the careful one.

In that case, streamline the encoding if-else chain to be executed only
once, and the encoding validity tested at the end.

encourage inlining

likely / unlikely hints for speculative execution

Assertion used _exit(1) to tell the compiler that the code after them is
not reachable and get rid of warnings.

But in some cases assertions are placed inside tight loops, and any
piece of code in them can slow down execution (code cache and other
reasons), instead using either abort() or better yet, unreachable
builtin.
2020-12-06 14:54:34 +02:00
Oran Agra 7ca00d694d Sanitize dump payload: fail RESTORE if memory allocation fails
When RDB input attempts to make a huge memory allocation that fails,
RESTORE should fail gracefully rather than die with panic
2020-12-06 14:54:34 +02:00
Oran Agra 3716950cfc Sanitize dump payload: validate no duplicate records in hash/zset/intset
If RESTORE passes successfully with full sanitization, we can't affort
to crash later on assertion due to duplicate records in a hash when
converting it form ziplist to dict.
This means that when doing full sanitization, we must make sure there
are no duplicate records in any of the collections.
2020-12-06 14:54:34 +02:00
Oran Agra 5b44631397 testsuite: fix fd leak, prevent port clashing when using --baseport
when using --baseport to run two tests suite in parallel (different
folders), we need to also make sure the port used by the testsuite to
communicate with it's workers is unique. otherwise the attept to find a
free port connects to the other test suite and messes it.

maybe one day we need to attempt to bind, instead of connect when tring
to find a free port.
2020-12-06 14:54:34 +02:00
Oran Agra c31055db61 Sanitize dump payload: fuzz tester and fixes for segfaults and leaks it exposed
The test creates keys with various encodings, DUMP them, corrupt the payload
and RESTORES it.
It utilizes the recently added use-exit-on-panic config to distinguish between
 asserts and segfaults.
If the restore succeeds, it runs random commands on the key to attempt to
trigger a crash.

It runs in two modes, one with deep sanitation enabled and one without.
In the first one we don't expect any assertions or segfaults, in the second one
we expect assertions, but no segfaults.
We also check for leaks and invalid reads using valgrind, and if we find them
we print the commands that lead to that issue.

Changes in the code (other than the test):
- Replace a few NPD (null pointer deference) flows and division by zero with an
  assertion, so that it doesn't fail the test. (since we set the server to use
  `exit` rather than `abort` on assertion).
- Fix quite a lot of flows in rdb.c that could have lead to memory leaks in
  RESTORE command (since it now responds with an error rather than panic)
- Add a DEBUG flag for SET-SKIP-CHECKSUM-VALIDATION so that the test don't need
  to bother with faking a valid checksum
- Remove a pile of code in serverLogObjectDebugInfo which is actually unsafe to
  run in the crash report (see comments in the code)
- fix a missing boundary check in lzf_decompress

test suite infra improvements:
- be able to run valgrind checks before the process terminates
- rotate log files when restarting servers
2020-12-06 14:54:34 +02:00
Oran Agra 01c13bddea Sanitize dump payload: improve tests of ziplist and stream encodings
- improve stream rdb encoding test to include more types of stream metadata
- add test to cover various ziplist encoding entries (although it does
  look like the stress test above it is able to find some too
- add another test for ziplist encoding for hash with full sanitization
- add similar ziplist encoding tests for list
2020-12-06 14:54:34 +02:00
Oran Agra ca1c182567 Sanitize dump payload: ziplist, listpack, zipmap, intset, stream
When loading an encoded payload we will at least do a shallow validation to
check that the size that's encoded in the payload matches the size of the
allocation.
This let's us later use this encoded size to make sure the various offsets
inside encoded payload don't reach outside the allocation, if they do, we'll
assert/panic, but at least we won't segfault or smear memory.

We can also do 'deep' validation which runs on all the records of the encoded
payload and validates that they don't contain invalid offsets. This lets us
detect corruptions early and reject a RESTORE command rather than accepting
it and asserting (crashing) later when accessing that payload via some command.

configuration:
- adding ACL flag skip-sanitize-payload
- adding config sanitize-dump-payload [yes/no/clients]

For now, we don't have a good way to ensure MIGRATE in cluster resharding isn't
being slowed down by these sanitation, so i'm setting the default value to `no`,
but later on it should be set to `clients` by default.

changes:
- changing rdbReportError not to `exit` in RESTORE command
- adding a new stat to be able to later check if cluster MIGRATE isn't being
  slowed down by sanitation.
2020-12-06 14:54:34 +02:00
Oran AgraandGitHub c4fdf09c05 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.
2020-12-06 14:51:22 +02:00
guybe7andGitHub 1df5bb5687 Make sure we do not propagate nested MULTI/EXEC (#8097)
One way this was happening is when a module issued an RM_Call which would inject MULTI.
If the module command that does that was itself issued by something else that already did
added MULTI (e.g. another module, or a Lua script), it would have caused nested MULTI.

In fact the MULTI state in the client or the MULTI_EMITTED flag in the context isn't
the right indication that we need to propagate MULTI or not, because on a nested calls
(possibly a module action called by a keyspace event of another module action), these
flags aren't retained / reflected.

instead there's now a global propagate_in_transaction flag for that.

in addition to that, we now have a global in_eval and in_exec flags, to serve the flags
of RM_GetContextFlags, since their dependence on the current client is wrong for the same
reasons mentioned above.
2020-12-06 13:14:18 +02:00
Wang YuanandGitHub 75f9dec644 Limit the main db and expires dictionaries to expand (#7954)
As we know, redis may reject user's requests or evict some keys if
used memory is over maxmemory. Dictionaries expanding may make
things worse, some big dictionaries, such as main db and expires dict,
may eat huge memory at once for allocating a new big hash table and be
far more than maxmemory after expanding.
There are related issues: #4213 #4583

More details, when expand dict in redis, we will allocate a new big
ht[1] that generally is double of ht[0], The size of ht[1] will be
very big if ht[0] already is big. For db dict, if we have more than
64 million keys, we need to cost 1GB for ht[1] when dict expands.

If the sum of used memory and new hash table of dict needed exceeds
maxmemory, we shouldn't allow the dict to expand. Because, if we
enable keys eviction, we still couldn't add much more keys after
eviction and rehashing, what's worse, redis will keep less keys when
redis only remains a little memory for storing new hash table instead
of users' data. Moreover users can't write data in redis if disable
keys eviction.

What this commit changed ?

Add a new member function expandAllowed for dict type, it provide a way
for caller to allow expand or not. We expose two parameters for this
function: more memory needed for expanding and dict current load factor,
users can implement a function to make a decision by them.
For main db dict and expires dict type, these dictionaries may be very
big and cost huge memory for expanding, so we implement a judgement
function: we can stop dict to expand provisionally if used memory will
be over maxmemory after dict expands, but to guarantee the performance
of redis, we still allow dict to expand if dict load factor exceeds the
safe load factor.
Add test cases to verify we don't allow main db to expand when left
memory is not enough, so that avoid keys eviction.

Other changes:

For new hash table size when expand. Before this commit, the size is
that double used of dict and later _dictNextPower. Actually we aim to
control a dict load factor between 0.5 and 1.0. Now we replace *2 with
+1, since the first check is that used >= size, the outcome of before
will usually be the same as _dictNextPower(used+1). The only case where
it'll differ is when dict_can_resize is false during fork, so that later
the _dictNextPower(used*2) will cause the dict to jump to *4 (i.e.
_dictNextPower(1025*2) will return 4096).
Fix rehash test cases due to changing algorithm of new hash table size
when expand.
2020-12-06 11:53:04 +02:00
guybe7andGitHub 2f41a38568 Modules: Fix an integer sign bug in moduleTimerHandler (#8131)
bug was introduced in 1a91a2700b
2020-12-03 20:36:48 +02:00
Itamar HaberandGitHub 441c490024 Adds exclusive ranges to X[REV]RANGE (#8072)
Adds the ability to use exclusive (open) start and end query intervals in XRANGE and XREVRANGE queries.

Fixes #6562
2020-12-03 14:36:48 +02:00
Felipe MachadoandGitHub 4cd1fb1f40 Iterate backwards on zdiff/zinter/zunion to optimize for zslInsert (#8105)
In the iterator for these functions, we'll traverse the sorted sets
in a reversed way so that largest elements come first. We prefer
this order because it's optimized for insertion in a skiplist, which
is the destination of the elements being iterated in there functions.
2020-12-03 10:12:07 +02:00
Itamar HaberandGitHub 7459652e3e Fix ACL Pub/Sub test timings (#8122) 2020-12-02 17:24:27 +02:00
b55a827ea2 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>
2020-12-02 13:56:11 +02:00
Yossi GottliebandGitHub 84e0489562 Remove end-of-life CentOS 6 workflows. (#8121) 2020-12-02 13:47:25 +02:00
luhuachaoandGitHub 7885faf18b Modify help msg PING_BULK to PING_MBULK in benchmark (#8109)
As described in redis-benchamrk help message 'The test names are the same as the ones produced as output.', In redis-benchmark output, we can only see PING_BULK, but the cmd `redis-benchmark -t ping_bulk` is not supported. We  have to run it with ping_mbulk which is not user friendly.
2020-12-02 13:17:25 +02:00
69b7113bb5 Getset fix (#8118)
* Fixed SET GET executing on wrong type

Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
2020-12-01 11:46:45 -08:00
sundbandGitHub 3ba2281f96 Improve dbid range check for SELECT, MOVE, COPY (#8085)
SELECT used to read the index into a `long` variable, and then pass it to a function
that takes an `int`, possibly causing an overflow before the range check.

Now all these commands use better and cleaner range check, and that also results in
a slight change of the error response in case of an invalid database index.

SELECT:
in the past it would have returned either `-ERR invalid DB index` (if not a number),
or `-ERR DB index is out of range` (if not between 1..16 or alike).
now it'll return either `-ERR value is out of range` (if not a number), or
`-ERR value is out of range, value must between -2147483648 and 2147483647`
(if not in the range for an int), or `-ERR DB index is out of range`
(if not between 0..16 or alike)


MOVE:
in the past it would only fail with `-ERR index out of range` no matter the reason.
now return the same errors as the new ones for SELECT mentioned above.
(i.e. unlike for SELECT even for a value like 17 we changed the error message)

COPY:
doesn't really matter how it behaved in the past (new command), new behavior is
like the above two.
2020-12-01 21:41:26 +02:00
Itamar HaberandGitHub c1b1e8c329 Adds pub/sub channel patterns to ACL (#7993)
Fixes #7923.

This PR appropriates the special `&` symbol (because `@` and `*` are taken),
followed by a literal value or pattern for describing the Pub/Sub patterns that
an ACL user can interact with. It is similar to the existing key patterns
mechanism in function (additive) and implementation (copy-pasta). It also adds
the allchannels and resetchannels ACL keywords, naturally.

The default user is given allchannels permissions, whereas new users get
whatever is defined by the acl-pubsub-default configuration directive. For
backward compatibility in 6.2, the default of this directive is allchannels but
this is likely to be changed to resetchannels in the next major version for
stronger default security settings.

Unless allchannels is set for the user, channel access permissions are checked
as follows :
* Calls to both PUBLISH and SUBSCRIBE will fail unless a pattern matching the
  argumentative channel name(s) exists for the user.
* Calls to PSUBSCRIBE will fail unless the pattern(s) provided as an argument
  literally exist(s) in the user's list.

Such failures are logged to the ACL log.

Runtime changes to channel permissions for a user with existing subscribing
clients cause said clients to disconnect unless the new permissions permit the
connections to continue. Note, however, that PSUBSCRIBErs' patterns are matched
literally, so given the change bar:* -> b*, pattern subscribers to bar:* will be
disconnected.

Notes/questions:
* UNSUBSCRIBE, PUNSUBSCRIBE and PUBSUB remain unprotected due to lack of reasons
  for touching them.
2020-12-01 14:21:39 +02:00
Wang YuanandGitHub c85bf2352d 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.
2020-11-30 23:15:14 +02:00
sundbandGitHub 04056b767f BITOP speedup when or/and output is 0/255, stop processing further keys (#8110)
when performing the and operation, if the output is 0, we can jump out of the loop.
when performing an or operation, if the output is 0xff, we can jump out of the loop.
2020-11-30 23:03:53 +02:00
Itamar HaberandGitHub d322e7baba Adds support for nested args in cli's help (#8074)
See https://github.com/redis/redis-doc/pull/1443

Also allows nameless commands.
2020-11-30 15:00:16 +02:00
guybe7andGitHub ada2ac9ae2 XPENDING with IDLE (#7972)
Used to filter stream pending entries by their idle-time,
useful for XCLAIMing entries that have not been processed
for some time
2020-11-29 12:08:47 +02:00
Oran AgraandGitHub cb5eadb33b bitops limited to proto_max_bulk_len rather than 512MB (#8096)
we recently did that for SETRANGE and APPEND
2020-11-26 10:58:01 +02:00
Oran AgraandGitHub bbc2c44541 INFO client_recent_max_input_buffer includes argv array (#8065)
this metric already includes the argv bytes, like what clientsCronTrackClientsMemUsage does, but it's missing the array itself.

p.s. For the purpose of tracking expensive clients we don't need to include the size of the client struct and the static reply buffer in it.
2020-11-25 23:39:01 +02:00
kukeyandGitHub cf88779527 Merge two aeDeleteFileEvent refs into one (#7521)
Merge two aeDeleteFileEvent refs into one
2020-11-25 13:37:54 -08:00
Dipankar AchintaandGitHub 79a7c17176 Fix typo in ae.c file (#7895) 2020-11-25 13:36:21 -08:00
David CARLIERandGitHub 0719388cfb raspberry build fix. (#8095)
__ILP32__ is 32 bits ABI and does not imply x86, this patch resolves this.
2020-11-25 12:15:32 -08:00
sundbandGitHub 25f457c7f6 Avoid excessive malloc and free in copyCommand robj creation (#8067)
Avoid multiple conditional judgments
Avoid allocating robj->ptr when we're gonna replace it right after.
2020-11-24 21:40:58 +02:00
Yossi GottliebandGitHub 7e5a6313f0 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.
2020-11-24 17:58:10 +02:00
David CARLIERandGitHub f16b52cb7d redis_set_thread_title support for Haiku. (#8060) 2020-11-23 16:14:33 +02:00
Itamar HaberandGitHub 0963edbc65 Adds tests for XADD/XTRIM's MAXLEN arguments (#8083) 2020-11-23 14:37:58 +02:00
0b1d89d7ae fix comment error about zslDeleteRangeByScore range (#8075)
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-11-22 14:56:45 +02:00
Yossi GottliebandGitHub 08d3e929e5 Clean up building with USE_SYSTEMD. (#8073)
When USE_SYSTEMD=yes is specified, try to use pkg-config to determine
libsystemd linker flags. If not found, silently fall back to simply
using "-lsystemd".

We now use a LIBSYSTEMD_LIBS variable so users can explicitly override
it and specify their own library.

If USE_SYSTEMD is unspecified the old behavior of auto-enabling it if
both pkg-config and libsystemd are available is retained.
2020-11-22 14:40:38 +02:00
Wang YuanandGitHub f207e1682f Fix diskless replication failure when has non-rdb child process (#8070)
If we enable diskless replication, set repl-diskless-sync-delay to 0,
and master has non-rdb child process such as rewrite aof child, master
will try to start to a new BGSAVE but fails immediately (before fork)
when replicas ask for full synchronization, and master always fails
to start a new BGSAVE and disconnects with replicas until non-rdb
child process exists.

this bug was introduced in #6271 (not yet released in 6.0.x)
2020-11-22 14:12:45 +02:00
Oran AgraandGitHub e6fa47380a 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
2020-11-22 14:00:51 +02:00
Oran AgraandGitHub 61954951ed 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.
2020-11-22 13:57:56 +02:00
Rosen PenevandGitHub a221a313ae fix compilation with uClibc-ng (#8054)
backtrace can be compile time disabled.
2020-11-20 18:13:11 +02:00
guybe7andGitHub f8ae991717 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)
2020-11-18 11:16:21 +02:00
d87a0d0286 Unified MULTI, LUA, and RM_Call with respect to blocking commands (#8025)
Blocking command should not be used with MULTI, LUA, and RM_Call. This is because,
the caller, who executes the command in this context, expects a reply.

Today, LUA and MULTI have a special (and different) treatment to blocking commands:

LUA   - Most commands are marked with no-script flag which are checked when executing
and command from LUA, commands that are not marked (like XREAD) verify that their
blocking mode is not used inside LUA (by checking the CLIENT_LUA client flag).
MULTI - Command that is going to block, first verify that the client is not inside
multi (by checking the CLIENT_MULTI client flag). If the client is inside multi, they
return a result which is a match to the empty key with no timeout (for example blpop
inside MULTI will act as lpop)
For modules that perform RM_Call with blocking command, the returned results type is
REDISMODULE_REPLY_UNKNOWN and the caller can not really know what happened.

Disadvantages of the current state are:

No unified approach, LUA, MULTI, and RM_Call, each has a different treatment
Module can not safely execute blocking command (and get reply or error).
Though It is true that modules are not like LUA or MULTI and should be smarter not
to execute blocking commands on RM_Call, sometimes you want to execute a command base
on client input (for example if you create a module that provides a new scripting
language like javascript or python).
While modules (on modules command) can check for REDISMODULE_CTX_FLAGS_LUA or
REDISMODULE_CTX_FLAGS_MULTI to know not to block the client, there is no way to
check if the command came from another module using RM_Call. So there is no way
for a module to know not to block another module RM_Call execution.

This commit adds a way to unify the treatment for blocking clients by introducing
a new CLIENT_DENY_BLOCKING client flag. On LUA, MULTI, and RM_Call the new flag
turned on to signify that the client should not be blocked. A blocking command
verifies that the flag is turned off before blocking. If a blocking command sees
that the CLIENT_DENY_BLOCKING flag is on, it's not blocking and return results
which are matches to empty key with no timeout (as MULTI does today).

The new flag is checked on the following commands:

List blocking commands: BLPOP, BRPOP, BRPOPLPUSH, BLMOVE,
Zset blocking commands: BZPOPMIN, BZPOPMAX
Stream blocking commands: XREAD, XREADGROUP
SUBSCRIBE, PSUBSCRIBE, MONITOR
In addition, the new flag is turned on inside the AOF client, we do not want to
block the AOF client to prevent deadlocks and commands ordering issues (and there
is also an existing assert in the code that verifies it).

To keep backward compatibility on LUA, all the no-script flags on existing commands
were kept untouched. In addition, a LUA special treatment on XREAD and XREADGROUP was kept.

To keep backward compatibility on MULTI (which today allows SUBSCRIBE, and PSUBSCRIBE).
We added a special treatment on those commands to allow executing them on MULTI.

The only backward compatibility issue that this PR introduces is that now MONITOR
is not allowed inside MULTI.

Tests were added to verify blocking commands are not blocking the client on LUA, MULTI,
or RM_Call. Tests were added to verify the module can check for CLIENT_DENY_BLOCKING flag.

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: Itamar Haber <itamar@redislabs.com>
2020-11-17 18:58:55 +02:00
39f716a121 ZREVRANGEBYSCORE Optimization for out of range offset (#5773)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
When the offset is too large, the query is very slow. Especially when the offset is greater than the length of zset it is easy to determine whether the offset is greater than the length of zset at first, and If it exceed the length of zset, then return directly.

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-11-17 18:35:56 +02:00
Yossi GottliebandGitHub d638b05834 Improve and clean up supervised process support. (#8036)
* Configuration file default should now be "auto".
* Expose "process_supervised" as an info field.
* Log messages improvements (clarify required systemd config, report
  auto-detected supervision mode, etc.)
* Set server.supervised properly, so it can take precedence of
  "daemonize" configuration.
* Produce clear warning if systemd is detected/requested but executable
  is compiled without support for it, instead of silently ignoring.
* Handle systemd notification error on startup, and turn off supervised
  mode if it failed.
2020-11-17 12:52:49 +02:00
ea7cf737a1 Add COPY command (#7953)
Syntax:
COPY <key> <new-key> [DB <dest-db>] [REPLACE]

No support for module keys yet.

Co-authored-by: tmgauss
Co-authored-by: Itamar Haber <itamar@redislabs.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-11-17 12:03:05 +02:00
Oran AgraandGitHub 9812e88959 Fix memory leaks in newly added ZDIFF (#8056) 2020-11-16 16:37:15 +02:00
chenyangyangandGitHub c1aaad06d8 Modules callbacks for lazy free effort, and unlink (#7912)
Add two optional callbacks to the RedisModuleTypeMethods structure, which is `free_effort`
and `unlink`. the `free_effort` callback indicates the effort required to free a module memory.
Currently, if the effort exceeds LAZYFREE_THRESHOLD, the module memory may be released
asynchronously. the `unlink` callback indicates the key has been removed from the DB by redis, and
may soon be freed by a background thread.

Add `lazyfreed_objects` info field, which represents the number of objects that have been
lazyfreed since redis was started.

Add `RM_GetTypeMethodVersion` API, which return the current redis-server runtime value of
`REDISMODULE_TYPE_METHOD_VERSION`. You can use that when calling `RM_CreateDataType` to know
which fields of RedisModuleTypeMethods are gonna be supported and which will be ignored.
2020-11-16 10:34:04 +02:00
d8fd48c436 Add new commands ZDIFF and ZDIFFSTORE (#7961)
- Add ZDIFF and ZDIFFSTORE which work similarly to SDIFF and SDIFFSTORE
- Make sure the new WITHSCORES argument that was added for ZUNION isn't considered valid for ZUNIONSTORE

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-11-15 14:14:25 +02:00
sundbandGitHub 204a14b8cf Fix make warning in hellohook,c and testmodule.c (#8044)
* Fix build warning in hellohook,c and testmodule.c

* Change ci->id type to (unsigned long long)
2020-11-13 15:16:40 +02:00
Itamar HaberandGitHub 92ec592520 Adds user parsing to redis-cli URIs (#8048) 2020-11-12 19:19:18 +02:00
Yash LadhaandGitHub c170365dcf cleanup: move list pop logic to single function (#7997)
BLPOP when there are elements in the list works in the same way as LPOP
does. Due to this they also does the same repetitive action and logic
for the same is written at two different places. This is a bad code
practice as the one needs the context to change the BLPOP list pop code
as well when the LPOP code gets changed.

Separated the generic logic from LPOP to a function that is being used
by the BLPOP code as well.
2020-11-12 10:55:51 +02:00
a0576bdeea Initialize original client argv for aof (#8042)
(this is a fixup for #8006)

Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
2020-11-11 23:58:05 +02:00
Yossi GottliebandGitHub 48a9d63af3 Add timer module API tests. (#8041) 2020-11-11 22:57:33 +02:00
tzongwandGitHub d5059ba5e1 Invalidate aeTimer when returning AE_NOMORE (#8022) 2020-11-11 12:10:54 +02:00
Wen HuiandGitHub dd1f20edc5 add tracking bcast flag and client redirection in client list (#7995) 2020-11-11 08:22:17 +02:00
3feff7d78a Rewritten commands are logged as their original command (#8006)
* Rewritten commands are logged as their original command

Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
2020-11-10 13:50:03 -08:00
Itamar HaberandGitHub 5d0c6b0868 Adds help for '--cluster-yes' (#8035) 2020-11-10 22:02:09 +02:00
sundbandMadelyn Olson c09b5941ce optimization src/adlist.c:listJoin() 2020-11-09 21:48:56 -08:00
nitaicaroandGitHub 19c29b6007 Extend client tracking tests (#7998)
Test support for the new map, null and push message types. Map objects are parsed as a list of lists of key value pairs.
for instance: user => john password => 123

will be parsed to the following TCL list:

{{user john} {password 123}}

Also added the following tests:

Redirection still works with RESP3

Able to use a RESP3 client as a redirection client

No duplicate invalidation messages when turning BCAST mode on after normal tracking

Server is able to evacuate enough keys when num of keys surpasses limit by more than defined initial effort

Different clients using different protocols can track the same key

OPTOUT tests

OPTIN tests

Clients can redirect to the same connection

tracking-redir-broken test

HELLO 3 checks

Invalidation messages still work when using RESP3, with and without redirection

Switching to RESP3 doesn't disturb previous tracked keys

Tracking info is correct

Flushall and flushdb produce invalidation messages

These tests achieve 100% line coverage for tracking.c using lcov.
2020-11-09 22:54:47 +02:00
Meir Shpilraien (Spielrein)andGitHub 97d647a139 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.
2020-11-09 10:46:23 +02:00
bugwzandGitHub bcc46a2f25 Fix the init value for repl_id of rdbSaveInfo struct (#8026)
use 40 zeros rather than 30 zeros to match CONFIG_RUN_ID_SIZE.
this doesn't have any real implications.
2020-11-08 09:26:25 +02:00
David CARLIERandGitHub d428de590f DragonFlyBSD resident memory amount (almost) similar as FreeBSD. (#8023) 2020-11-08 09:16:14 +02:00
sundbandGitHub cd1c600548 Typo fix: entires -> entries (#8031) 2020-11-08 08:32:38 +02:00
Wen HuiandGitHub 254367788a Debug Populate: Avoid server crash when passing negative value for key and value size (#8018)
* Debug Populate: Add checks for count and keysize to avoid crash

* provide getRangeLongFromObjectOrReply and getPositiveLongFromObjectOrReply for range check
2020-11-05 19:58:54 +02:00
Yossi GottliebandGitHub 7e4325cbc9 Fix crash log output on ARM. (#8020) 2020-11-05 15:43:53 +02:00
Oran AgraandGitHub 7ace7231c6 Better INFO fields to track diskless and disk-based replication progress (#7981)
Expose new `loading_rdb_used_mem` showing the used memory of the server
that saved the RDB file we're currently using.
This is useful in diskless replication when the total size of the rdb is
unkown, and can be used as a rought estimation of progres.

Use that new field to calculate the "user friendly"
`loading_loaded_perc` and `loading_eta_seconds`.

Expose `master_sync_total_bytes` and `master_sync_total_bytes` to complement
on the existing `master_sync_total_bytes` (which cannot be used on its own
to calculate progress).

Add "user friendly" field for `master_sync_perc`
2020-11-05 11:46:16 +02:00
1fd456f91a Add RESET command. (#7982)
Perform full reset of all client connection states, is if the client was
disconnected and re-connected. This affects:

* MULTI state
* Watched keys
* MONITOR mode
* Pub/Sub subscription
* ACL/Authenticated state
* Client tracking state
* Cluster read-only/asking state
* RESP version (reset to 2)
* Selected database
* CLIENT REPLY state

The response is +RESET to make it easily distinguishable from other
responses.

Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: Itamar Haber <itamar@redislabs.com>
2020-11-05 10:51:26 +02:00
Yossi Gottlieb f6546eff45 Tests: fix filename reported in run_solo tests. 2020-11-04 21:43:55 +02:00
Yossi Gottlieb 2faa0f19eb Fix test failure on slower systems.
Not disabling save, slower systems begun background save that did not
complete in time, resulting with SAVE failing with "ERR Background save
already in progress".
2020-11-04 21:43:55 +02:00
d8fbd3a8ba rdb: corrected RedisModuleIO initialization point (#8014)
- rdbSaveSingleModuleAux() used RedisModuleIO's "bytes" field for
  tracking written bytes before calling moduleInitIOContext() which sets
  "bytes" to zero
- rdbSaveObject() re-initialized RedisModuleIO too late

This return value is not used at the moment since it's only tested
against -1, and the actual byte count isn't used yet.

Co-authored-by: Tomasz Poradowski <tomasz.poradowski@generiscorp.com>
2020-11-04 16:19:18 +02:00
10b5006934 Enable specifying TLS ciphers(suites) in redis-cli/redis-benchmark (#8005)
Enable specifying the preferred ciphers and/or ciphersuites for redis-cli/redis-benchmark.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
2020-11-04 14:49:15 +02:00
Egor SeredinandGitHub f4ca3d8757 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.
2020-11-04 13:38:46 +02:00
Meir Shpilraien (Spielrein)andGitHub 762be79f0b Disable new SIGABRT test on valgrind (#8013)
The crash reports cause false-positive warnings when run with valgrind.
2020-11-04 13:13:55 +02:00
Wen HuiandGitHub 639b73cd2a redis-cli cluster import support source and target that need auth (#7994)
Make it possible for redis-cli cluster import to work with source and
target that require AUTH.

Adding two different flags --cluster-from-user, --cluster-from-pass
and --cluster-askpass for source node authentication.
Also for target authentication, using existing --user and --pass flag.

Example:

./redis-cli --cluster import 127.0.0.1:7000 --cluster-from 127.0.0.1:6379 --pass 1234 --user default --cluster-from-user default --cluster-from-pass 123456

./redis-cli --cluster import 127.0.0.1:7000 --cluster-from 127.0.0.1:6379 --askpass --cluster-from-user default --cluster-from-askpass
2020-11-04 10:00:18 +02:00
Oran AgraandGitHub a698a6391a Add maxclients and cluster_connections to INFO CLIENTS (#7979)
Few config settings are also reflected by the INFO command.
these are mainly ones that are important for either an instant view of
the server status (to compare a metric to it's limit config),
Important configurations that are necessary in the crash log (which
currently doesn't print the config),
And things that are important for monitoring solutions (such as
Prometheus), which rely on INFO to collect their data.

Add cluster_connections to INFO CLUSTER:
This makes it possible to be combined together with connected_clients
and connected_slaves and be matched against maxclients
2020-11-04 09:53:43 +02:00
Wang YuanandGitHub 89c78a9808 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
2020-11-03 17:16:11 +02:00
Meir Shpilraien (Spielrein)andGitHub f210e197f3 Added crash report on SIGABRT (#8004)
The reason that we want to get a full crash report on SIGABRT
is that the jmalloc, when detecting a corruption, calls abort().
This will cause the Redis to exist silently without any report
and without any way to analyze what happened.
2020-11-03 14:59:21 +02:00
Oran AgraandGitHub 9122379abc Propagate GETSET and SET-GET as SET (#7957)
- Generates a more backwards compatible command stream
- Slightly more efficient execution in replica/AOF
- Add a test for coverage
2020-11-03 14:56:57 +02:00
guybe7andGitHub 1a91a2700b 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'
2020-11-02 18:18:42 +02:00
Yossi GottliebandGitHub b5f3996250 Update redis.conf to recommend use of /run. (#8002) 2020-11-01 13:56:44 +02:00
yoav-steinbergandGitHub 84b3c18f71 Add local address to CLIENT LIST, and a CLIENT KILL filter. (#7913)
Useful when you want to know through which bind address the client connected to
the server in case of multiple bind addresses.

- Adding `laddr` field to CLIENT list showing the local (bind) address.
- Adding `LADDR` option to CLIENT KILL to kill all the clients connected
  to a specific local address.
- Refactoring to share code.
2020-10-28 21:13:44 +02:00
Oran AgraandGitHub 441bfa2dfb Optionally (default) fail to start if requested bind address is not available (#7936)
Background:
#3467 (redis 4.0.0), started ignoring ENOPROTOOPT, but did that only for
the default bind (in case bind config wasn't explicitly set).
#5598 (redis 5.0.3), added that for bind addresses explicitly set
(following bug reports in Debian for redis 4.0.9 and 5.0.1), it
also ignored a bunch of other errors like EPROTONOSUPPORT which was
requested in #3894, and also added EADDRNOTAVAIL (wasn't clear why).

This (ignoring EADDRNOTAVAIL) makes redis start successfully, even if a
certain network interface isn't up yet , in which case we rather redis
fail and will be re-tried when the NIC is up, see #7933.

However, it turns out that when IPv6 is disabled (supported but unused),
the error we're getting is EADDRNOTAVAIL. and in many systems the
default config file tries to bind to localhost for both v4 and v6 and
would like to silently ignore the error on v6 if disabled.
This means that we sometimes want to ignore EADDRNOTAVAIL and other times
we wanna fail.

So this commit changes these main things:
1. Ignore all the errors we ignore for both explicitly requested bind
   address and a default implicit one.
2. Add a '-' prefix to allow EADDRNOTAVAIL be ignored (by default that's
   different than the previous behavior).
3. Restructure that function in a more readable and maintainable way see
   below.
4. Make the default behavior of listening to all achievable by setting
  a bind config directive to * (previously only possible by omitting
  it)
5. document everything.

The old structure of this function was that even if there are no bind
addresses requested, the loop that runs though the bind addresses runs
at least once anyway!
In that one iteration of the loop it binds to both v4 and v6 addresses,
handles errors for each of them separately, and then eventually at the
if-else chain, handles the error of the last bind attempt again!
This was very hard to read and very error prone to maintain, instead now
when the bind info is missing we create one with two entries, and run
the simple loop twice.
2020-10-28 21:09:15 +02:00
Madelyn OlsonandMadelyn Olson d310beb417 White space tweaks and skip categories already applied 2020-10-28 10:01:20 -07:00
Madelyn OlsonandMadelyn Olson 411bcf1a41 Further improved ACL algorithm for picking categories 2020-10-28 10:01:20 -07:00
Wen HuiandGitHub efd17316ab add acl related config in sentinel.conf (#7952) 2020-10-28 15:05:00 +02:00
Wen HuiandGitHub 4342703743 refactor aof rewrite code to avoid memory leaks in error handling (#7976) 2020-10-28 12:35:28 +02:00
sundbandGitHub 6987176059 docs: Fix some typos in comments and log messge (#7975) 2020-10-28 08:51:35 +02:00
filipe oliveiraandGitHub 39436b2152 TLS Support for redis-benchmark (#7959) 2020-10-28 08:00:54 +02:00
WuYunlongandMadelyn Olson 66037309c6 Fix waste of CPU time about server log in serverCron.
When all the work is just adding logs, we could pull
the condition out so as to use less CPU time when
loglevel is bigger than LL_VERBOSE.
2020-10-27 11:15:14 -07:00
Oran AgraandGitHub 380f6048e0 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.
2020-10-27 16:36:00 +02:00
zhenwei piandGitHub a9c0602149 Disable THP if enabled (#7381)
In case redis starts and find that THP is enabled ("always"), instead
of printing a log message, which might go unnoticed, redis will try to
disable it (just for the redis process).

Note: it looks like on self-bulit kernels THP is likely be set to "always" by default.

Some discuss about THP side effect on Linux:
according to http://www.antirez.com/news/84, we can see that
redis latency spikes are caused by linux kernel THP feature.
I have tested on E3-2650 v3, and found that 2M huge page costs
about 0.25ms to fix COW page fault.

Add a new config 'disable-thp', the recommended setting is 'yes',
(default) the redis tries to disable THP by prctl syscall. But
users who really want THP can set it to "no"

Thanks to Oran & Yossi for suggestions.

Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
2020-10-27 15:04:18 +02:00
Andrij FedykandGitHub 825fe7bd23 rdb.c: fix typo in a comment (#7970) 2020-10-27 11:27:27 +02:00
Wang YuanandGitHub dc899c4c88 Fix timing dependence in replication tcl tests (#7969)
Remove 'fork child $pid' log in replication.tcl
2020-10-27 09:36:42 +02:00
7fa56dd773 Speedup cluster failover. (#7948)
This commit deals with manual failover as well as non-manual failover.

We did tests with manual failover as follows:
1, Setup redis cluster which holds 16 partions, each having only
   1 corresponding replica.
2, Write a batch of data to redis cluster and make sure the redis is doing
   a active expire in serverCron.
3, Do a manual failover sequentially to each partions with a time interval
   of 3 minutes.
4, Collect logs and do some computaiton work.

The result:
case    avgTime    maxTime    minTime
C1      95.8ms	   227ms      25ms
C2      47.9ms     96ms       12ms
C3      12.6ms     27ms       7ms

Explanation
case C1: All nodes use the version before optimization
case C2: Masters use the elder version while replicas use the optimized version
case C3: All nodes use the optimized version
failover time: The time between when replica got a `manual failover request` and
               when it `won the failover election`.
avgTime: average failover time
maxTime: maximum failover time
minTime: mimimum failover time
ms: millisecond

Co-authored-by: chendq8 <c.d_q@163.com>
2020-10-27 08:13:59 +02:00
Madelyn OlsonandMadelyn Olson dac26729a9 Only supress implitic fallthrough on GCC 7 2020-10-26 21:46:50 -07:00
Madelyn OlsonandMadelyn Olson 4d1120f5fd Update CI so that warnings cause build failures 2020-10-26 21:46:50 -07:00
Yossi GottliebandGitHub 9824fe3e39 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.
2020-10-26 14:49:08 +02:00
Oran AgraandGitHub 4e2e5be201 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.
2020-10-26 11:55:24 +02:00
01acfa71ca redis-benchmark: add tests, --version, a minor bug fixes (#7947)
- add test suite coverage for redis-benchmark
- add --version (similar to what redis-cli has)
- fix bug sending more requests than intended when pipeline > 1.
- when done sending requests, avoid freeing client in the write handler, in theory before
  responses are received (probably dead code since the read handler will call clientDone first)

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-10-26 08:04:59 +02:00
Itamar HaberandGitHub d2af0f25be Adds command introspection to Sentinel (#7940)
Adds the `COMMAND` command to Sentinel.
2020-10-26 00:37:58 +02:00
David CARLIERandGitHub 27f4c212f3 cpu affinity: DragonFlyBSD support (#7956) 2020-10-25 14:14:05 +02:00
Zach FewtrellandGitHub ebfa769925 fix invalid 'failover' identifier in cluster slave selection test (#7942) 2020-10-25 10:05:38 +02:00
WuYunlongandGitHub e05a7df7f9 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.
2020-10-23 15:26:30 +03:00
Wen HuiandGitHub 0f370f9b66 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.
2020-10-22 19:47:32 +03:00
Qu ChenandGitHub 556acefe75 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.
2020-10-22 12:57:45 +03:00
Oran AgraandGitHub c96ece9f5e improve verbose logging on failed test. print log file lines (#7938) 2020-10-22 11:34:54 +03:00
Yossi GottliebandGitHub 843a13e88f Add a --no-latency tests flag. (#7939)
Useful for running tests on systems which may be way slower than usual.
2020-10-22 11:10:53 +03:00
6cf23d6610 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>
2020-10-20 19:52:05 +03:00
Oran AgraandGitHub a425e1d26d fix 32bit build warnings (#7926) 2020-10-20 09:12:24 +03:00
Wen HuiandGitHub 04a0af9085 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)
2020-10-19 15:32:18 +03:00
0047702aab Support ACL for Sentinel Mode (#7888)
This commit implements ACL for Sentinel mode, main work of this PR includes:

- Update Sentinel command table in order to better support ACLs.
- Fix couple of things which currently blocks the support for ACL on sentinel mode.
- Provide "sentinel sentinel-user" and "sentinel sentinel-pass " configuration in order to let sentinel authenticate with a specific user in other sentinels.
- requirepass is kept just for compatibility with old config files

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-10-19 07:33:55 +03:00
Oran AgraandGitHub 457b7073b5 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
2020-10-18 16:56:43 +03:00
Yossi GottliebandGitHub ef92f507dd Fix tests failure on busybox systems. (#7916) 2020-10-18 14:50:29 +03:00
Wen HuiandGitHub f328194d12 support NOMKSTREAM option in xadd command (#7910)
introduces a NOMKSTREAM option for xadd command, this would be useful for some
use cases when we do not want to create new stream by default:

XADD key [MAXLEN [~|=] <count>] [NOMKSTREAM] <ID or *> [field value] [field value]
2020-10-18 10:15:43 +03:00
Pierre JambetandGitHub f6010e106b t_set.c comment update for srandmemberWithCountCommand (#7922)
Reference the correct "case", case 4, in the comment explaining the need
for case 3, when the number of request items is too close to the
cardinality of the set. Case 4 is indeed the "natural approach"
referenced earlier in that sentence.
2020-10-18 08:14:45 +03:00
Tommy Joe LundandGitHub 786d6d55cf Fix typo in server.h (#7921) 2020-10-18 08:11:18 +03:00
Oran AgraandGitHub 19418b6b28 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.
2020-10-14 09:38:47 +03:00
guybe7andGitHub 37fd3d40ae performEvictions: mem_freed may be negative (#7908)
If 'delta' is negative 'mem_freed' may underflow and cause
the while loop to exit prematurely (And not evicting enough
memory)

mem_freed can be negative when:
1. We use lazy free (consuming memory by appending to a list)
2. Thread doing an allocation between the two calls to zmalloc_used_memory.
2020-10-13 19:50:57 +03:00
Hanif AriffinandGitHub f559a57206 Fix printf format specifier for unsigned in ziplistRepr (#7907)
Only used by DEBUG and testing code.

Signed-off-by: Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com>
2020-10-13 12:42:52 +03:00
WuYunlongandGitHub 24092eea06 Delete dbExists() which is redundant. (#7906) 2020-10-13 10:05:05 +03:00
Wang YuanandGitHub aaacb8c955 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.
2020-10-13 09:34:07 +03:00
guybe7andGitHub addf47dcac 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
2020-10-12 17:13:38 +03:00
Andreas LindandGitHub 8b497881f2 Support redis-cli -u rediss://... (#7900) 2020-10-11 18:14:02 +03:00
Yossi GottliebandGitHub 056a43e1a6 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.
2020-10-11 18:10:55 +03:00
adc3183cd2 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>
2020-10-11 17:21:58 +03:00
Yossi GottliebandGitHub 0aec98dce2 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.
2020-10-11 17:11:42 +03:00
Yossi GottliebandGitHub 907da0580b 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.
2020-10-11 16:11:31 +03:00
Yossi Gottlieb 7d117d7591 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).
2020-10-11 16:04:14 +03:00
Yossi Gottlieb 9b7f8ba84b 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.
2020-10-11 16:04:14 +03:00
Uri ShacharandGitHub dab5ec9b8d Support getting configuration from both stdin and file at the same time (#7893)
This allows supplying secret configuration (for example - masterauth) via a secure channel
instead of having it in a plaintext file / command line param, while still allowing for most
of the configuration to reside there.

Also, remove 'special' case handling for --check-rdb which hasn't been relevant
since 4.0.0.
2020-10-11 13:43:23 +03:00
c3f9e01794 Adds new pop-push commands (LMOVE, BLMOVE) (#6929)
Adding [B]LMOVE <src> <dst> RIGHT|LEFT RIGHT|LEFT. deprecating [B]RPOPLPUSH.

Note that when receiving a BRPOPLPUSH we'll still propagate an RPOPLPUSH,
but on BLMOVE RIGHT LEFT we'll propagate an LMOVE

improvement to existing tests
- Replace "after 1000" with "wait_for_condition" when wait for
  clients to block/unblock.
- Add a pre-existing element to target list on basic tests so
  that we can check if the new element was added to the correct
  side of the list.
- check command stats on the replica to make sure the right
  command was replicated

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-10-08 08:33:17 +03:00
Madelyn OlsonandGitHub 2127f7c8eb Fixed excessive categories being displayed from acls (#7889) 2020-10-07 22:09:09 -07:00
Oran AgraandGitHub f659d23619 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
2020-10-07 20:28:57 +03:00
Oran AgraandGitHub 216c110609 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.
2020-10-06 21:43:30 +03:00
Wen HuiandGitHub 6f3b91fe26 ACL : mentioning requirepass and aclfile not compatable in redis conf (#7883) 2020-10-05 18:49:47 +03:00
Wen HuiandGitHub 57b8e00695 support sentinel myid subcommand (#7858) 2020-10-05 13:37:20 +03:00
Oran AgraandGitHub bea40e6a41 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.
2020-10-05 11:15:36 +03:00
Oran AgraandGitHub f87a47dc97 fix new build warning about getGenericCommand (#7882) 2020-10-04 18:10:21 +03:00
6418d767e8 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>
2020-10-04 17:18:17 +03:00
Nykolas Laurentino de LimaandGitHub 66ee45b65c Add GET parameter to SET command (#7852)
Add optional GET parameter to SET command in order to set a new value to
a key and retrieve the old key value. With this change we can deprecate
`GETSET` command and use only the SET command with the GET parameter.
2020-10-02 15:07:19 +03:00
Oran AgraandGitHub 3945a32177 performance and memory reporting improvement - sds take control of it's internal frag (#7875)
This commit has two aspects:
1) improve memory reporting for all the places that use sdsAllocSize to compute
   memory used by a string, in this case it'll include the internal fragmentation.
2) reduce the need for realloc calls by making the sds implicitly take over
   the internal fragmentation of the block it allocated.
2020-10-02 08:19:44 +03:00
Oran AgraandGitHub eb6241a3dd 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
2020-10-01 11:30:22 +03:00
Oran AgraandGitHub dc803d25a6 Fix crash in script timeout during AOF loading (#7870) 2020-10-01 11:27:45 +03:00
Rafi EinsteinandGitHub b8187d39fb Makefile: enable program suffixes via PROG_SUFFIX (#7868) 2020-10-01 10:56:23 +03:00
8fb89a5728 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>
2020-09-30 19:52:01 +03:00
Yossi Gottlieb 67b43f75e2 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.
2020-09-30 17:06:57 +03:00
Yossi Gottlieb cfccfbd6f4 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.
2020-09-30 17:06:57 +03:00
Yossi GottliebandGitHub 1591e3479d 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.
2020-09-29 20:48:21 +03:00
448c435b1b Fix compilation warnings in Lua and jemalloc dependencies (#7785)
- The argument `u` in for `ar` is ignored (and generates warnings since `D` became the default.
  All it does is avoid updating unchanged objects (shouldn't have any impact on our build)
- Enable `LUA_USE_MKSTEMP` to force the use of `mkstemp()` instead of `tmpname()` (which is dead
  code in redis anyway).
- Remove unused variable `c` in `f_parser()`
- Removed misleadingly indented space in `luaL_loadfile()` and ``addfield()`

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-09-29 17:10:54 +03:00
Oran AgraandGitHub ef33252c43 warning: comparison between signed and unsigned integer in 32bit build (#7838) 2020-09-29 17:03:47 +03:00
David CARLIERandGitHub f971a5d8ed Add support for Haiku OS (#7435) 2020-09-29 15:52:13 +03:00
Gavrie PhilipsonandGitHub 49383a4af7 Fix typo in module API docs (#7861) 2020-09-29 13:10:08 +03:00
David CARLIERandGitHub ce8bfc56ad getting rss size implementation for netbsd (#7293) 2020-09-29 08:49:35 +03:00
WuYunlongandGitHub c2e5546071 Normalize sds test mechanism together with some compile warnings. (#7854) 2020-09-28 11:27:26 +03:00
Oran AgraandGitHub 8aa083bd28 Fix new obuf-limits tests to work with TLS (#7848)
Also stabilize new shutdown tests on slow machines (valgrind)
2020-09-27 17:13:33 +03:00
a295770e32 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>
2020-09-27 15:40:07 +03:00
Wang YuanandGitHub c9f00bcce2 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.
2020-09-27 12:35:16 +03:00
Wang YuanandGitHub f1863a1fe7 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.
2020-09-25 16:25:47 +03:00
Uri ShacharandGitHub c30bd02c9d 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.
2020-09-25 12:55:45 +03:00
WuYunlongandGitHub 0d62caab21 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.
2020-09-25 08:08:06 +03:00
Wen HuiandGitHub 323029baa6 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.
2020-09-24 18:17:53 +03:00
57709c4bc6 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>
2020-09-24 16:01:41 +03:00
Guy KorlandandGitHub b464afb9e2 Fix RedisModule_HashGet examples (#6697) 2020-09-24 12:45:30 +03:00
valentinogeronandGitHub 795c454db1 Stream: Inconsistency between master and replica some XREADGROUP case (#7526)
XREADGROUP auto-creates the consumer inside the consumer group the
first time it saw it.
When XREADGROUP is being used with NOACK option, the message will not
be added into the client's PEL and XGROUP SETID would be propagated.
When the replica gets the XGROUP SETID it will only update the last delivered
id of the group, but will not create the consumer.

So, in this commit XGROUP CREATECONSUMER is being added.
Command pattern: XGROUP CREATECONSUMER <key> <group> <consumer>.

When NOACK option is being used, createconsumer command would be
propagated as well.

In case of AOFREWRITE, consumer with an empty PEL would be saved with
XGROUP CREATECONSUMER whereas consumer with pending entries would be
saved with XCLAIM
2020-09-24 12:02:40 +03:00
bodong.ybdandOran Agra b7ce583a5e Refactor multi-key command get keys proc 2020-09-24 08:59:14 +03:00
bodong.ybdandOran Agra e08bf16637 Add ZINTER/ZUNION command
Syntax: ZINTER/ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]
[AGGREGATE SUM|MIN|MAX] [WITHSCORES]

see #7624
2020-09-24 08:59:14 +03:00
66a13ccbdf Add GT and LT options to ZADD for conditional score updates (#7818)
Co-authored-by: Alex Ronke <w.alex.ronke@gmail.com>
2020-09-23 21:56:16 +03:00
Oran AgraandGitHub a735bf5c2a fix recently broken TLS build error, and add coverage for CI (#7833) 2020-09-23 11:30:24 +03:00
David CARLIERandGitHub c3edaa7941 Further NetBSD update and build fixes. (#7831)
mainly backtrace and register dump support.
2020-09-23 10:00:31 +03:00
WuYunlongandGitHub 63cd4d4e20 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.
2020-09-23 09:09:48 +03:00
yixiangandGitHub b96c3595af Fix connGetSocketError usage (#7811) 2020-09-22 12:53:36 +03:00
Oran AgraandGitHub 26ca04a8a2 RM_GetContextFlags - document missing flags (#7821) 2020-09-22 12:11:19 +03:00
Yossi GottliebandGitHub 1980f639b1 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.
2020-09-22 11:38:52 +03:00
63a05dde46 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>
2020-09-22 10:18:07 +03:00
Wang YuanandGitHub 1bb5794a1f Kill disk-based fork child when all replicas drop and 'save' is not enabled (#7819)
When all replicas waiting for a bgsave get disconnected (possibly due to output buffer limit),
It may be good to kill the bgsave child. in diskless replication it already happens, but in
disk-based, the child may still serve some purpose (for persistence).

By killing the child, we prevent it from eating COW memory in vain, and we also allow a new child fork sooner for the next full synchronization or bgsave.
We do that only if rdb persistence wasn't enabled in the configuration.

Btw, now, rdbRemoveTempFile in killRDBChild won't block server, so we can killRDBChild safely.
2020-09-22 09:47:58 +03:00
Wen HuiandGitHub 23b50bcccc 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.
2020-09-22 09:05:47 +03:00
YoongHMandGitHub 9216b96b41 Fix compilation warning in jemalloc's malloc_vsnprintf (#7789)
Change `val` to `unsigned char` before being tested.
The fix is identical to the one that's been made in upstream jemalloc.

warning is:
src/malloc_io.c: In function ‘malloc_vsnprintf’:
src/malloc_io.c:369:2: warning: case label value exceeds maximum value for type
  369 |  case '?' | 0x80:      \
      |  ^~~~
src/malloc_io.c:581:5: note: in expansion of macro ‘GET_ARG_NUMERIC’
  581 |     GET_ARG_NUMERIC(val, 'p');
      |     ^~~~~~~~~~~~~~~
2020-09-21 17:04:34 +03:00
YoongHMandGitHub d3faed875d Fix warning from jemalloc configure script (#7790)
jemalloc configure shows this:
    configure: WARNING: unrecognized options: --enable-cc-silence

The changelog of jemalloc 4.0 has:
  - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious
    warnings by default.
2020-09-21 16:57:15 +03:00
WuYunlongandOran Agra c37acb82aa 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.
2020-09-21 12:10:19 +03:00
WuYunlongandOran Agra 647cac5bb4 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>
2020-09-21 12:10:19 +03:00
Oran AgraandGitHub f11a0c8f30 Fix C11 detection in the makefile (#7822)
445a4b6 introudced a makefile script that detects if the toolchain
supports c11, and it looked that it was passing on MacOS and fails on
Ubuntu, looks like Ubuntu's Dash was spawning a background process,
deleted foo.c before gcc tried to compile it.
2020-09-21 11:17:48 +03:00
Oran AgraandGitHub 2458e54814 RM_GetContextFlags provides indication that we're in a fork child (#7783) 2020-09-20 13:43:28 +03:00
Wen HuiandGitHub dfe9714c86 Add Swapdb Module Event (#7804) 2020-09-20 13:36:20 +03:00
1b3b75208c fix make warnings in debug.c MacOS (#7805)
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-09-20 12:06:17 +03:00
David CARLIERandGitHub eabe3eaec0 debug.c: NetBSD build warning fix. (#7810)
The symbol base address is a const on this system.
2020-09-19 12:24:40 +03:00
Wang YuanandGitHub b002d2b4f1 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.
2020-09-17 18:20:10 +03:00
Wang YuanandGitHub 445a4b669a Implement redisAtomic to replace _Atomic C11 builtin (#7707)
Redis 6.0 introduces I/O threads, it is so cool and efficient, we use C11
_Atomic to establish inter-thread synchronization without mutex. But the
compiler that must supports C11 _Atomic can compile redis code, that brings a
lot of inconvenience since some common platforms can't support by default such
as CentOS7, so we want to implement redis atomic type to make it more portable.

We have implemented our atomic variable for redis that only has 'relaxed'
operations in src/atomicvar.h, so we implement some operations with
'sequentially-consistent', just like the default behavior of C11 _Atomic that
can establish inter-thread synchronization. And we replace all uses of C11
_Atomic with redis atomic variable.

Our implementation of redis atomic variable uses C11 _Atomic, __atomic or
__sync macros if available, it supports most common platforms, and we will
detect automatically which feature we use. In Makefile we use a dummy file to
detect if the compiler supports C11 _Atomic. Now for gcc, we can compile redis
code theoretically if your gcc version is not less than 4.1.2(starts to support
__sync_xxx operations). Otherwise, we remove use mutex fallback to implement
redis atomic variable for performance and test. You will get compiling errors
if your compiler doesn't support all features of above.

For cover redis atomic variable tests, we add other CI jobs that build redis on
CentOS6 and CentOS7 and workflow daily jobs that run the tests on them.
For them, we just install gcc by default in order to cover different compiler
versions, gcc is 4.4.7 by default installation on CentOS6 and 4.8.5 on CentOS7.

We restore the feature that we can test redis with Helgrind to find data race
errors. But you need install Valgrind in the default path configuration firstly
before running your tests, since we use macros in helgrind.h to tell Helgrind
inter-thread happens-before relationship explicitly for avoiding false positives.
Please open an issue on github if you find data race errors relate to this commit.

Unrelated:
- Fix redefinition of typedef 'RedisModuleUserChangedFunc'
  For some old version compilers, they will report errors or warnings, if we
  re-define function type.
2020-09-17 16:01:45 +03:00
Oran AgraandGitHub 092cfca522 Add printf attribute and fix warnings and a minor bug (#7803)
The fix in error handling of rdbGenericLoadStringObject is an actual bugfix
2020-09-16 20:21:04 +03:00
WuYunlongandOran Agra f866023399 bio: doFastMemoryTest should try to kill io threads as well. 2020-09-16 14:15:02 +03:00
WuYunlongandOran Agra 8b70cb0ef8 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.
2020-09-16 14:15:02 +03:00
Jim BrunnerandGitHub 810e28a397 Incremental eviction processing (#7653)
Rather than blindly evicting until maxmemory limit is achieved, this
update adds a time limit to eviction.  While over the maxmemory limit,
eviction will process before each command AND as a timeProc when no
commands are running.

This will reduce the latency impact on many cases, especially pathological
cases like massive used memory increase during dict rehashing.

There is a risk that some other edge cases (like massive pipelined use
of MGET) could cause Redis memory usage to keep growing despite the
eviction attempts, so a new maxmemory-eviction-tenacity config is
introduced to let users mitigate that.
2020-09-16 09:16:01 +03:00
Wen HuiandGitHub 6ff741b5cf 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"
2020-09-15 08:58:21 +03:00
WuYunlongandGitHub 98c8ac0d70 Clarify help text of tcl scripts. (#7798)
Before this commit, following command did not show --tls option:
./runtest-cluster --help
./runtest-sentinel --help
2020-09-15 08:27:42 +03:00
cb2c7c4d34 Recalculate hardcoded variables from $::instances_count in sentinel tests (#7561)
Co-authored-by: MemuraiUser <githubuser@janeasystems.com>
2020-09-13 18:39:59 +03:00
Oran AgraandGitHub 78a6e5eb2b Fix failing valgrind installation in github actions (#7792)
These tests started failing every day on http 404 (not being able to
install valgrind)
2020-09-13 13:51:21 +03:00
Oran AgraandGitHub ed9bfe2262 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
2020-09-13 13:50:23 +03:00
Wang YuanandGitHub 86511bbbef Remove dead global variable 'lru_clock' (#7782) 2020-09-10 17:02:27 +03:00
Oran AgraandGitHub 1c71038540 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
2020-09-10 13:43:38 +03:00
Oran AgraandGitHub 8ea131fc80 Fix leak in new blockedclient module API test (#7780) 2020-09-10 10:22:16 +03:00
Oran AgraandGitHub de8d320230 Fix RESP3 response for HKEYS/HVALS on non-existing key (#7781) 2020-09-10 10:09:13 +03:00
Yossi GottliebandGitHub b2a73c404b 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.
2020-09-09 18:58:06 +03:00
杨博东andGitHub 0666267d27 Tests: Add aclfile load and save tests (#7765)
improves test coverage
2020-09-09 17:13:35 +03:00
042189fd87 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>
2020-09-09 16:01:16 +03:00
Yossi GottliebandGitHub a8b7268911 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.
2020-09-09 15:43:11 +03:00
Oran AgraandGitHub 1461f02deb Change THP warning to use madvise rather than never (#7771)
completes b2419c31c1
2020-09-09 15:39:57 +03:00
天河andGitHub 63730d9dd0 Fix comments of _quicklistSplitNode function. (#4341)
Comments about the behavior of the function where wrong (off by one)
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-09-09 15:28:38 +03:00
Yossi GottliebandGitHub 818a746e32 Fix default/explicit "save" parameter loading. (#7767)
Save parameters should either be default or whatever specified in the
config file. This fixes an issue introduced in #7092 which causes
configuration file settings to be applied on top of the defaults.
2020-09-09 15:12:57 +03:00
ce15620dc1 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>
2020-09-09 15:09:41 +03:00
Jan-Erik RedigerandGitHub b2419c31c1 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.
2020-09-09 15:06:04 +03:00
Yossi GottliebandGitHub 918abd7276 Tests: clean up stale .cli files. (#7768) 2020-09-09 12:30:43 +03:00
b120366d48 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>
2020-09-09 09:35:42 +03:00
Oran AgraandGitHub 340963b2d7 Tests: Some fixes for macOS (#7757)
* 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`.

* 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`

Co-authored-by: Oran Agra <oran@redislabs.com>
2020-09-08 16:28:58 +03:00
Yossi GottliebandGitHub 750acf3a45 Fix CONFIG REWRITE of oom-score-adj-values. (#7761) 2020-09-08 16:00:20 +03:00
Oran AgraandGitHub 0a1e734193 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`
2020-09-08 14:12:03 +03:00
Oran AgraandGitHub 5496b4a7cd Add daily CI for MacOS (#7759) 2020-09-08 10:59:25 +03:00
bodong.ybd f22fa9594d 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`.
2020-09-08 14:27:53 +08:00
Oran AgraandGitHub b491d477c3 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.
2020-09-07 18:06:25 +03:00
Yossi GottliebandGitHub 2df4cb93ac 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.
2020-09-07 17:30:36 +03:00
Oran AgraandGitHub 42ba7a1b75 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.
2020-09-07 16:26:11 +03:00
Oran AgraandGitHub 573246f73c if diskless repl child is killed, make sure to reap the pid (#7742)
Starting redis 6.0 and the changes we made to the diskless master to be
suitable for TLS, I made the master avoid reaping (wait3) the pid of the
child until we know all replicas are done reading their rdb.

I did that in order to avoid a state where the rdb_child_pid is -1 but
we don't yet want to start another fork (still busy serving that data to
replicas).

It turns out that the solution used so far was problematic in case the
fork child was being killed (e.g. by the kernel OOM killer), in that
case there's a chance that we currently disabled the read event on the
rdb pipe, since we're waiting for a replica to become writable again.
and in that scenario the master would have never realized the child
exited, and the replica will remain hung too.
Note that there's no mechanism to detect a hung replica while it's in
rdb transfer state.

The solution here is to add another pipe which is used by the parent to
tell the child it is safe to exit. this mean that when the child exits,
for whatever reason, it is safe to reap it.

Besides that, i'm re-introducing an adjustment to REPLCONF ACK which was
part of #6271 (Accelerate diskless master connections) but was dropped
when that PR was rebased after the TLS fork/pipe changes (5a47794).
Now that RdbPipeCleanup no longer calls checkChildrenDone, and the ACK
has chance to detect that the child exited, it should be the one to call
it so that we don't have to wait for cron (server.hz) to do that.
2020-09-06 16:43:57 +03:00
Oran AgraandGitHub 2b998de460 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.
2020-09-06 11:11:49 +03:00
Oran Agra fe5da2e60d 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.
2020-09-06 09:59:19 +03:00
Oran Agra 1b7ba44e79 test infra - wait_done_loading
reduce code duplication in aof.tcl.
move creation of clients into the test so that it can be skipped
2020-09-06 09:59:19 +03:00
Oran Agra b65e5aca86 test infra - flushall between tests in external mode 2020-09-06 09:59:19 +03:00
Oran Agra 677d14c213 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)
2020-09-06 09:59:19 +03:00
Oran Agra e3e69c25fd test infra - reduce disk space usage
this is important when running a test with --loop
2020-09-06 09:59:19 +03:00
Oran Agra 9d527d076b test infra - write test name to logfile 2020-09-06 09:59:19 +03:00
Yossi GottliebandGitHub 58e5feb3f4 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.
2020-09-03 18:15:48 +03:00
WuYunlongandGitHub 12f798dc18 fix wrong comments in redis.conf, change default always-show-logo (#5695)
1. default value of always-show-logo was not consistent with the default in the code
2. comment about cluster-replica-no-failover is wrong since we can only do manually failover upon replicas
3. improve description about always-show-logo
2020-09-03 10:31:18 +03:00
Oran AgraandGitHub 9ef8d2f671 Run active defrag while blocked / loading (#7726)
During long running scripts or loading RDB/AOF, we may need to do some
defragging. Since processEventsWhileBlocked is called periodically at
unknown intervals, and many cron jobs either depend on run_with_period
(including active defrag), or rely on being called at server.hz rate
(i.e. active defrag knows ho much time to run by looking at server.hz),
the whileBlockedCron may have to run a loop triggering the cron jobs in it
(currently only active defrag) several times.

Other changes:
- Adding a test for defrag during aof loading.
- Changing key-load-delay config to take negative values for fractions
  of a microsecond sleep
2020-09-03 08:47:29 +03:00
Pierre JambetandGitHub d52ce4ea1a Fix error message for the DEBUG ZIPLIST command (#7745)
DEBUG ZIPLIST <key> currently returns the following error string if the
key is not a ziplist: "ERR Not an sds encoded string.". This looks like
an accidental copy/paste error from the error returned in the else if
branch above where this string is returned if the key is not an sds
string. The command was added in
ac61f90625 and looking at the commit,
nothing indicates that it is not an accidental typo.

The error string now returns a correct error: "Not a ziplist encoded
object", which accurately describes the error.
2020-09-02 23:27:48 +03:00
Oran AgraandGitHub 8b0747d657 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
2020-09-02 17:18:09 +03:00
f22f64f0db 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>
2020-09-02 16:23:49 +03:00
Oran AgraandGitHub 747b4004ea fix README about BUILD_WITH_SYSTEMD usage (#7739)
BUILD_WITH_SYSTEMD is an internal variable. Users should use USE_SYSTEMD=yes.
2020-09-01 21:31:37 +03:00
Yossi GottliebandGitHub b35d6e5cff Fix double-make issue with make && make install. (#7734)
All user-supplied variables that affect the build should be explicitly
persisted.

Fixes #7254
2020-09-01 10:02:14 +03:00
Yossi GottliebandGitHub d75ad774a9 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.
2020-08-31 20:42:46 +03:00
Leoš LiterákandGitHub 571571ca19 Update README.md with instructions how to build with systemd support (#7730)
#7728 - update instructions for systemd support
2020-08-31 12:44:09 +03:00
Yossi GottliebandGitHub b61b663895 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.
2020-08-30 12:23:47 +03:00
ee4a15aae0 Optimize __ziplistCascadeUpdate algorithm (#6886)
The previous algorithm is of O(n^2) time complexity.
It would have run through the ziplist entries one by one, each time doing a `realloc` and a
`memmove` (moving the entire tail of the ziplist).

The new algorithm is O(n), it runs over all the records once, computing the size of the `realloc`
needed, then does one `realloc`, and run thought the records again doing many smaller `memmove`s,
each time moving just one record.

So this change reduces many reallocs, and moves each record just once.

Co-authored-by: zhumaohua <zhumaohua@megvii.com>
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-08-28 17:22:35 +03:00
Jim BrunnerandGitHub c01e94a431 Use H/W Monotonic clock and updates to AE (#7644)
Update adds a general source for retrieving a monotonic time.
In addition, AE has been updated to utilize the new monotonic
clock for timer processing.

This performance improvement is **not** enabled in a default build due to various H/W compatibility
concerns, see README.md for details. It does however change the default use of gettimeofday with
clock_gettime and somewhat improves performance.

This update provides the following
1. An interface for retrieving a monotonic clock. getMonotonicUs returns a uint64_t (aka monotime)
   with the number of micro-seconds from an arbitrary point. No more messing with tv_sec/tv_usec.
   Simple routines are provided for measuring elapsed milli-seconds or elapsed micro-seconds (the
   most common use case for a monotonic timer). No worries about time moving backwards.
2. High-speed assembler implementation for x86 and ARM. The standard method for retrieving the
   monotonic clock is POSIX.1b (1993): clock_gettime(CLOCK_MONOTONIC, timespec*). However, most
   modern processors provide a constant speed instruction clock which can be retrieved in a fraction
   of the time that it takes to call clock_gettime. For x86, this is provided by the RDTSC
   instruction. For ARM, this is provided by the CNTVCT_EL0 instruction. As a compile-time option,
   these high-speed timers can be chosen. (Default is POSIX clock_gettime.)
3. Refactor of event loop timers. The timer processing in ae.c has been refactored to use the new
   monotonic clock interface. This results in simpler/cleaner logic and improved performance.
2020-08-28 11:54:10 +03:00
9fcd9e191e 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>
2020-08-27 12:54:01 +03:00
Oran AgraandGitHub 8bdcbbb085 Update memory metrics for INFO during loading (#7690)
During a long AOF or RDB loading, the memory stats were not updated, and
INFO would return stale data, specifically about fragmentation and RSS.
In the past some of these were sampled directly inside the INFO command,
but were moved to cron as an optimization.

This commit introduces a concept of loadingCron which should take
some of the responsibilities of serverCron.
It attempts to limit it's rate to approximately the server Hz, but may
not be very accurate.

In order to avoid too many system call, we use the cached ustime, and
also make sure to update it in both AOF loading and RDB loading inside
processEventsWhileBlocked (it seems AOF loading was missing it).
2020-08-27 11:09:32 +03:00
valentinogeronandGitHub b7289e912c 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
2020-08-27 09:19:24 +03:00
Oran AgraandGitHub daef1f00c2 Add test coverage for CLIENT UNBLOCK (#7712)
plus minor other fixes to list.tcl
2020-08-27 08:09:39 +03:00
filipe oliveiraandGitHub 21784def70 Extended redis-benchmark instant metrics and overall latency report (#7600)
A first step to enable a consistent full percentile analysis on query latency so that we can fully understand the performance and stability characteristics of the redis-server system we are measuring. It also improves the instantaneous reported metrics, and the csv output format.
2020-08-25 21:21:29 +03:00
Itamar HaberandGitHub 5b0a06af48 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.
2020-08-25 15:58:50 +03:00
Wang YuanandGitHub 43af28f5b4 Fix wrong format specifiers of 'sdscatfmt' for the INFO command (#7706)
unlike printf, sdscatfmt doesn't take %d
2020-08-24 22:59:56 +03:00
Wang YuanandGitHub 6b4ae919e8 Fix data race in bugReportStart (#7700)
The previous fix using _Atomic was insufficient, since we check and set it in
different places.

The implications of this bug are just that a portion of the bug report will be shown
twice, in the race case of two concurrent crashes.
2020-08-24 13:54:33 +03:00
Yossi GottliebandGitHub 5449a2a8b5 Add language servers stuff, test/tls to gitignore. (#7698) 2020-08-24 12:54:56 +03:00
Valentino GeronandOran Agra 8b428cf0f7 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.
2020-08-23 16:03:30 +03:00
Valentino GeronandOran Agra 9204a9b2c2 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
```
2020-08-23 16:03:30 +03:00
Yossi GottliebandGitHub f80f3f492a Tests: fix redis-cli with remote hosts. (#7693) 2020-08-23 10:17:43 +03:00
Wen HuiandGitHub e61adc0d89 fix make warnings (#7692) 2020-08-21 23:37:49 +03:00
Wen HuiandGitHub 89f2bfbb58 use dictSlots for getting total slots number in dict (#7691) 2020-08-21 00:14:09 +03:00
huangzhwandGitHub a3d4d7bf68 RedisModuleEvent_LoadingProgress always at 100% progress (#7685)
It was also using the wrong struct, but luckily RedisModuleFlushInfo and RedisModuleLoadingProgress
are identical.
2020-08-20 23:31:06 +03:00
guybe7andGitHub 65c24bd3d4 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.
2020-08-20 19:55:14 +03:00
cbaf3c5bba 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>
2020-08-20 08:59:02 +03:00
Raghav MuddurandGitHub 34c3be365a Update clusterMsgDataPublish to clusterMsgModule (#7682)
Correcting the variable to clusterMsgModule.
2020-08-19 19:13:32 -07:00
Madelyn OlsonandGitHub cbd9af8583 Fixed hset error since it's shared with hmset (#7678) 2020-08-19 19:07:43 -07:00
Wang YuanandGitHub 89d544d6f2 Add comments on 'slave.repldboff' when use diskless replication (#7679) 2020-08-19 10:52:53 +03:00
guybe7andGitHub b87c288016 PERSIST should signalModifiedKey (Like EXPIRE does) (#7671) 2020-08-18 19:07:59 +03:00
Oran AgraandGitHub 0f741a9e2d 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.
2020-08-18 09:53:59 +03:00
Wen HuiandGitHub 88662c243d edit auth failed message (#7648)
Edit auth failed message include user disabled case in hello command
2020-08-18 08:59:24 +03:00
Wen HuiandGitHub 93d87d6d4c [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.
2020-08-18 08:50:03 +03:00
Oran AgraandGitHub cdd925b289 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.
2020-08-18 08:28:43 +03:00
Yossi GottliebandGitHub 64c360c515 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.
2020-08-17 17:46:54 +03:00
Yossi GottliebandGitHub fb2a94af3f 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.
2020-08-17 17:36:50 +03:00
Oran AgraandGitHub 1407a055ee Merge pull request #7661 from michael-grunder/hiredis-unique-sds-symbols
This resolves an issue with sentinel that was created when hiredis was recently updated.
this was due to sds symbol names clashing, since hiredis now includes different implementation
of sdsrange than the one in redis.

The state of things is that redis-benchamrk and redis-cli include only hiredis sds implementation.
redis-cli even operates (calls sdscatlen) on sds that's allocated by hiredis.

Sentinel however has both implementations of the sds library in it (now each with it's own unique
symbols).
2020-08-16 08:54:36 +03:00
michael-grunder 275068c86f Use Hiredis' sdscompat.h to map sds* calls to hi_sds* 2020-08-15 13:13:23 -07:00
michael-grunder 6866158f0d Merge commit 'bffbbeaa9a1a6b8e81384297272cb0631502e8fd' into hiredis-unique-sds-symbols 2020-08-15 12:24:31 -07:00
michael-grunder bffbbeaa9a Squashed 'deps/hiredis/' changes from d5b4c69b7..00272d669
00272d669 Rename sds calls so they don't conflict in Redis.

git-subtree-dir: deps/hiredis
git-subtree-split: 00272d669b11e96b8311d9bfe167c117f8887dd6
2020-08-15 12:24:31 -07:00
11cd983d58 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>
2020-08-14 14:45:34 +03:00
d10b2f3173 wait command optimization (#7333)
Client that issued WAIT last will most likely have the highest replication offset, so imagine a probably common case where all clients are waiting for the same number of replicas. we prefer the loop to start from the last client (one waiting for the highest offset), so that the optimization in the function will call replicationCountAcksByOffset for each client until it found a good one, and stop calling it for the rest of the clients.
the way the loop was implemented would mean that in such case it is likely to call replicationCountAcksByOffset for all clients.

Note: the change from > to >= is not directly related to the above.

Co-authored-by: 曹正斌 <caozb@jiedaibao.com>
2020-08-13 14:30:51 +03:00
RemRainandGitHub 47637bea6d Set the initial seed for random() (#5679) 2020-08-12 13:15:58 -07:00
Yossi GottliebandGitHub 2530dc0ebd 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.
2020-08-12 17:58:56 +03:00
HarveyLiuandGitHub f3df3ec134 fix misleading typo hasActiveChildProcess doc comment (#7588)
and a misspell in rax.c
2020-08-12 10:23:55 +03:00
Madelyn OlsonandGitHub 79c506ebf0 Fixed timer warning (#5953) 2020-08-12 11:16:41 +08:00
Madelyn OlsonandGitHub f2db379fa3 Replace usage of wrongtypeerr with helper (#7633)
* Replace usage of wrongtypeerr with helper
2020-08-11 20:04:54 -07:00
MotaandGitHub ddcbb628a1 Test:Fix invalid cases in hash.tcl and dump.tcl (#4611) 2020-08-12 10:25:24 +08:00
MotaandGitHub fbed632f3a Adds redis-cli and redis-benchmark dependencies for make test target
Obsoletes the need to run `make` before `make test`.
2020-08-11 22:01:15 +03:00
Wagner Francisco MezarobaandGitHub e2a71338eb allow --pattern to be used along with --bigkeys (#3586)
Adds --pattern option to cli's --bigkeys, --hotkeys & --scan modes
2020-08-11 21:57:21 +03:00
Itamar HaberandGitHub 82451a4717 Merge pull request #6777 from pponnuvel/typos_in_readme
Fix typos in README.md
2020-08-11 21:10:58 +03:00
zhaozhao.zzandGitHub ff1e4a7063 redis-benchmark: fix wrong random key for hset (#4895) 2020-08-11 20:51:27 +08:00
Itamar HaberandGitHub 28a8465102 Merge pull request #7618 from ShooterIT/benchmark-zset
[Redis-benchmark] Support zset type
2020-08-11 14:31:11 +03:00
Muhammad ZahalqaandGitHub 5dd499c6d4 Fix unidentical function declaration in bio.c. lazyfree.c: lazyfreeFreeSlotsMapFromBioThread (#7228) 2020-08-11 19:16:10 +08:00
zhaozhao.zzandzhaozhao.zz 589e610ebc CLIENT_MASTER should ignore server.proto_max_bulk_len 2020-08-11 18:59:29 +08:00
zhaozhao.zzandzhaozhao.zz bd4b33d7a2 config: proto-max-bulk-len must be 1mb or greater 2020-08-11 18:59:29 +08:00
zhaozhao.zzandzhaozhao.zz 2e69bfe44d using proto-max-bulk-len in checkStringLength for SETRANGE and APPEND 2020-08-11 18:59:29 +08:00
pingfan108andGitHub 09af8447a1 Correct error message of runtest-cluster and runtest-moduleapi (#7647) 2020-08-11 13:35:20 +03:00
YoongHMandGitHub 8e937ce4cc 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.
2020-08-11 12:30:32 +03:00
6f11acbd67 Implement SMISMEMBER key member [member ...] (#7615)
This is a rebased version of #3078 originally by shaharmor
with the following patches by TysonAndre made after rebasing
to work with the updated C API:

1. Add 2 more unit tests
   (wrong argument count error message, integer over 64 bits)
2. Use addReplyArrayLen instead of addReplyMultiBulkLen.
3. Undo changes to src/help.h - for the ZMSCORE PR,
   I heard those should instead be automatically
   generated from the redis-doc repo if it gets updated

Motivations:

- Example use case: Client code to efficiently check if each element of a set
  of 1000 items is a member of a set of 10 million items.
  (Similar to reasons for working on #7593)
- HMGET and ZMSCORE already exist. This may lead to developers deciding
  to implement functionality that's best suited to a regular set with a
  data type of sorted set or hash map instead, for the multi-get support.

Currently, multi commands or lua scripting to call sismember multiple times
would almost definitely be less efficient than a native smismember
for the following reasons:

- Need to fetch the set from the string every time
  instead of reusing the C pointer.
- Using pipelining or multi-commands would result in more bytes sent
  and received by the client for the repeated SISMEMBER KEY sections.
- Need to specially encode the data and decode it from the client
  for lua-based solutions.
- Proposed solutions using Lua or SADD/SDIFF could trigger writes to
  memory, which is undesirable on a redis replica server
  or when commands get replicated to replicas.

Co-Authored-By: Shahar Mor <shahar@peer5.com>
Co-Authored-By: Tyson Andre <tysonandre775@hotmail.com>
2020-08-11 11:55:06 +03:00
Yossi GottliebandGitHub 944cca98c8 Run daily workflow on main repo only (no forks). (#7646) 2020-08-11 10:59:47 +03:00
Rajat PawarandGitHub 59d437c727 Fix comment about ACLGetCommandPerm() 2020-08-10 23:11:26 -07:00
Jim BrunnerandGitHub f39c9404a3 Prevent dictRehashMilliseconds from rehashing while a safe iterator is present (#5948) 2020-08-10 22:36:34 -07:00
杨博东andGitHub 229327ad8b Avoid redundant calls to signalKeyAsReady (#7625)
signalKeyAsReady has some overhead (namely dictFind) so we should
only call it when there are clients blocked on the relevant type (BLOCKED_*)
2020-08-11 08:18:09 +03:00
Itamar HaberandGitHub efe92ee546 Removes dead code (#7642)
Appears to be handled by server.stream_node_max_bytes in reality.
2020-08-11 08:11:47 +03:00
WuYunlongandGitHub d6220f12a9 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.
2020-08-10 19:18:21 -07:00
Hamed MomeniandGitHub 3941a275f7 fixed a typo in redis.conf (#7636) 2020-08-09 12:02:29 +03:00
Yossi GottliebandGitHub 3f073b1d9c Merge pull request #7609 from michael-grunder/redis-cli-resp3-push
Add redis-cli RESP3 Push support
2020-08-09 11:19:04 +03:00
3f494cc49d 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>
2020-08-09 06:11:47 +03:00
e2d64485b8 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>
2020-08-09 06:08:00 +03:00
Wen HuiandGitHub ca95b71f67 add sentinel command help (#7579) 2020-08-08 12:28:44 -07:00
WuYunlongandGitHub 6dcc6898be Optimize calls to mstime in trackInstantaneousMetric() (#6472) 2020-08-08 22:11:14 +03:00
Wang YuanandGitHub ea7eeb2fd2 Print error info if failed opening config file (#6943) 2020-08-08 22:03:56 +03:00
michael-grunder 81879bc171 Create PUSH handlers in redis-cli
Add logic to redis-cli to display RESP3 PUSH messages when we detect
STDOUT is a tty, with an optional command-line argument to override
the default behavior.

The new argument:  --show-pushes <yn>

Examples:

$ redis-cli -3 --show-pushes no
$ echo "client tracking on\nget k1\nset k1 v1"| redis-cli -3 --show-pushes y
2020-08-08 11:59:17 -07:00
ShooterIT 6a06a5a597 [Redis-benchmark] Remove zrem test, add zpopmin test 2020-08-08 23:08:27 +08:00
Wen HuiandGitHub 3f67b03378 fix memory leak in ACLLoadFromFile error handling (#7623) 2020-08-08 14:42:32 +03:00
Wang YuanandGitHub 1ef014ee6b Fix applying zero offset to null pointer when creating moduleFreeContextReusedClient (#7323)
Before this fix we where attempting to select a db before creating db the DB, see: #7323

This issue doesn't seem to have any implications, since the selected DB index is 0,
the db pointer remains NULL, and will later be correctly set before using this dummy
client for the first time.

As we know, we call 'moduleInitModulesSystem()' before 'initServer()'. We will allocate
memory for server.db in 'initServer', but we call 'createClient()' that will call 'selectDb()'
in 'moduleInitModulesSystem()', before the databases where created. Instead, we should call
'createClient()' for moduleFreeContextReusedClient after 'initServer()'.
2020-08-08 14:36:41 +03:00
xuannianzandGitHub b118502a05 remove superfluous else block (#7620)
The else block would be executed when newlen == 0 and in the case memmove won't be called, so there's no need to set start.
2020-08-08 00:19:18 +03:00
fayadexinqingandGitHub e966264188 fix migration's broadcast PONG message, after the slot modification (#7590) 2020-08-07 13:01:14 -07:00
michael-grunder 7ee5a41aac Squashed 'deps/hiredis/' content from commit 39de5267c
git-subtree-dir: deps/hiredis
git-subtree-split: 39de5267c092859b4cab4bdf79081e9634b70e39
2020-08-06 12:41:58 -07:00
michael-grunder 5f536b5d23 Merge commit '7ee5a41aac7e5abc90d050fa509fa953ca7f1da1' as 'deps/hiredis' 2020-08-06 12:41:58 -07:00
michael-grunder 3ddb6ffa34 Remove hiredis so we can add it as a subtree 2020-08-06 12:41:46 -07:00
Oran AgraandGitHub c17e597d05 Accelerate diskless master connections, and general re-connections (#6271)
Diskless master has some inherent latencies.
1) fork starts with delay from cron rather than immediately
2) replica is put online only after an ACK. but the ACK
   was sent only once a second.
3) but even if it would arrive immediately, it will not
   register in case cron didn't yet detect that the fork is done.

Besides that, when a replica disconnects, it doesn't immediately
attempts to re-connect, it waits for replication cron (one per second).
in case it was already online, it may be important to try to re-connect
as soon as possible, so that the backlog at the master doesn't vanish.

In case it disconnected during rdb transfer, one can argue that it's
not very important to re-connect immediately, but this is needed for the
"diskless loading short read" test to be able to run 100 iterations in 5
seconds, rather than 3 (waiting for replication cron re-connection)

changes in this commit:
1) sync command starts a fork immediately if no sync_delay is configured
2) replica sends REPLCONF ACK when done reading the rdb (rather than on 1s cron)
3) when a replica unexpectedly disconnets, it immediately tries to
   re-connect rather than waiting 1s
4) when when a child exits, if there is another replica waiting, we spawn a new
   one right away, instead of waiting for 1s replicationCron.
5) added a call to connectWithMaster from replicationSetMaster. which is called
   from the REPLICAOF command but also in 3 places in cluster.c, in all of
   these the connection attempt will now be immediate instead of delayed by 1
   second.

side note:
we can add a call to rdbPipeReadHandler in replconfCommand when getting
a REPLCONF ACK from the replica to solve a race where the replica got
the entire rdb and EOF marker before we detected that the pipe was
closed.
in the test i did see this race happens in one about of some 300 runs,
but i concluded that this race is unlikely in real life (where the
replica is on another host and we're more likely to first detect the
pipe was closed.
the test runs 100 iterations in 3 seconds, so in some cases it'll take 4
seconds instead (waiting for another REPLCONF ACK).

Removing unneeded startBgsaveForReplication from updateSlavesWaitingForBgsave
Now that CheckChildrenDone is calling the new replicationStartPendingFork
(extracted from serverCron) there's actually no need to call
startBgsaveForReplication from updateSlavesWaitingForBgsave anymore,
since as soon as updateSlavesWaitingForBgsave returns, CheckChildrenDone is
calling replicationStartPendingFork that handles that anyway.
The code in updateSlavesWaitingForBgsave had a bug in which it ignored
repl-diskless-sync-delay, but removing that code shows that this bug was
hiding another bug, which is that the max_idle should have used >= and
not >, this one second delay has a big impact on my new test.
2020-08-06 16:53:06 +03:00
Oran Agra 81f8524a12 Fix potential race in bugReportStart
this race would only happen when two threads paniced at the same time,
and even then the only consequence is some extra log lines.

race reported in #7391
2020-08-06 16:47:27 +03:00
Oran Agra 90b717e723 Assertion and panic, print crash log without generating SIGSEGV
This makes it possible to add tests that generate assertions, and run
them with valgrind, making sure that there are no memory violations
prior to the assertion.

New config options:
- crash-log-enabled - can be disabled for cleaner core dumps
- crash-memcheck-enabled - useful for faster termination after a crash
- use-exit-on-panic - to be used by the test suite so that valgrind can
  detect leaks and memory corruptions

Other changes:
- Crash log is printed even on system that dont HAVE_BACKTRACE, i.e. in
  both SIGSEGV and assert / panic
- Assertion and panic won't print registers and code around EIP (which
  was useless), but will do fast memory test (which may still indicate
  that the assertion was due to memory corrpution)

I had to reshuffle code in order to re-use it, so i extracted come code
into function without actually doing any changes to the code:
- logServerInfo
- logModulesInfo
- doFastMemoryTest (with the exception of it being conditional)
- dumpCodeAroundEIP

changes to the crash report on segfault:
- logRegisters is called right after the stack trace (before info) done
  just in order to have more re-usable code
- stack trace skips the first two items on the stack (the crash log and
  signal handler functions)
2020-08-06 16:47:27 +03:00
ShooterIT e5a50ed3c4 [Redis-benchmark] Support zset type 2020-08-06 15:36:28 +08:00
Itamar HaberandGitHub 24c539251f Merge pull request #7092 from itamarhaber/fix-5629
Prevents default save configuration being reset...
2020-08-05 21:16:38 +03:00
Oran Agra 1aa31e4da9 redis-cli --cluster-yes - negate force flag for clarity
this internal flag is there so that some commands do not comply to `--cluster-yes`
2020-08-05 18:30:43 +03:00
Frank MeierandOran Agra 51077c8212 reintroduce REDISCLI_CLUSTER_YES env variable in redis-cli
the variable was introduced only in the 5.0 branch in #5879 bc6c1c40db
2020-08-05 18:30:43 +03:00
Frank MeierandGitHub 3244fae7d4 add force option to 'create-cluster create' script call (#7612) 2020-08-05 12:06:33 +03:00
WuYunlongandGitHub 40c7628fc8 Fix tests/cluster/cluster.tcl about wrong usage of lrange. (#6702) 2020-08-04 18:00:58 +03:00
f11f26cc53 Add a ZMSCORE command returning an array of scores. (#7593)
Syntax: `ZMSCORE KEY MEMBER [MEMBER ...]`

This is an extension of #2359
amended by Tyson Andre to work with the changed unstable API,
add more tests, and consistently return an array.

- It seemed as if it would be more likely to get reviewed
  after updating the implementation.

Currently, multi commands or lua scripting to call zscore multiple times
would almost definitely be less efficient than a native ZMSCORE
for the following reasons:

- Need to fetch the set from the string every time instead of reusing the C
  pointer.
- Using pipelining or multi-commands would result in more bytes sent by
  the client for the repeated `ZMSCORE KEY` sections.
- Need to specially encode the data and decode it from the client
  for lua-based solutions.
- The fastest solution I've seen for large sets(thousands or millions)
  involves lua and a variadic ZADD, then a ZINTERSECT, then a ZRANGE 0 -1,
  then UNLINK of a temporary set (or lua). This is still inefficient.

Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
2020-08-04 17:49:33 +03:00
Oran AgraandGitHub 824bd2ac11 fix new rdb test failing on timing issues (#7604)
apparenlty on github actions sometimes 500ms is not enough
2020-08-04 08:53:50 +03:00
hujieandGitHub 2f454e5ae8 remove duplicate semicolon (#7438) 2020-08-02 13:59:51 +03:00
Yossi GottliebandGitHub f2da3efc78 Fix test-centos7-tls daily job. (#7598) 2020-07-31 13:55:57 +03:00
Oran AgraandGitHub f7e7775990 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
2020-07-31 13:14:29 +03:00
Oran AgraandGitHub 50f5181488 Remove dead code from update_zmalloc_stat_alloc (#7589)
this seems like leftover from before 6eb51bf
2020-07-31 13:01:39 +03:00
Yossi GottliebandGitHub 4ac1f9ac55 CI: Add daily CentOS 7.x jobs. (#7582) 2020-07-30 13:25:10 +03:00
WuYunlongandGitHub f3352daf4f Fix running single test 14-consistency-check.tcl (#7587) 2020-07-30 08:56:21 +03:00
fayadexinqingandGitHub 4faad81f50 broadcast a PONG message when slot's migration is over, which may reduce the moved request of clients (#7571) 2020-07-29 18:05:27 -07:00
Kevin McGeheeandGitHub b55c1602b5 Call propagate instead of writing directly to AOF/replicas (#6658)
Use higher-level API to funnel all generic propagation through
single function call.
2020-07-29 17:54:37 -07:00
Yossi GottliebandGitHub 7af05f07ff Clarify RM_BlockClient() error condition. (#6093) 2020-07-29 17:03:38 +03:00
f6cad30bb6 Show threading configuration in INFO output (#7446)
Co-authored-by: Oran Agra <oran@redislabs.com>
2020-07-29 08:46:44 +03:00
63dae52324 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>
2020-07-29 08:25:56 +03:00
Wen HuiandGitHub f33acb3f02 Add SignalModifiedKey hook in XGROUP CREATE with MKSTREAM option (#7562) 2020-07-29 08:22:54 +03:00
Wen HuiandGitHub c69a9b2f61 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
2020-07-28 22:05:48 +03:00
Yossi GottliebandGitHub bedf1b2126 Fix TLS cluster tests. (#7578)
Fix consistency test added in af5167b7f without considering TLS
redis-cli configuration.
2020-07-28 14:04:06 +03:00
784ceeb90d 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>
2020-07-28 11:32:47 +03:00
Oran AgraandGitHub 109b5ccdcd 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
2020-07-28 11:15:29 +03:00
f31260b044 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>
2020-07-28 10:45:21 +03:00
Yossi GottliebandGitHub 5f4a2801cb TLS: support cluster/replication without tls-port. (#7573)
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.
2020-07-27 15:31:53 +03:00
Oran AgraandGitHub 6d92eee69b Daily github action: run cluster and sentinel tests with tls (#7575) 2020-07-27 15:30:36 +03:00
Yossi Gottlieb c75512d89d 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.
2020-07-27 13:26:02 +03:00
grishafandGitHub 4126ca466f Fix prepareForShutdown function declaration (#7566) 2020-07-26 08:27:30 +03:00
zhaozhao.zzandGitHub da840e9851 more strict check in rioConnRead (#7564) 2020-07-24 14:40:19 +08:00
Oran AgraandGitHub 8a57969fd7 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.
2020-07-23 13:06:24 +03:00
8d82639319 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>
2020-07-23 12:38:51 +03:00
Oran AgraandGitHub 40d7fca368 Fix harmless bug in rioConnRead (#7557)
this code is in use only if the master is disk-based, and the replica is
diskless. In this case we use a buffered reader, but we must avoid reading
past the rdb file, into the command stream. which Luckly rdb.c doesn't
really attempt to do (it knows how much it should read).

When rioConnRead detects that the extra buffering attempt reaches beyond
the read limit it should read less, but if the caller actually requested
more, then it should return with an error rather than a short read. the
bug would have resulted in short read.

in order to fix it, the code must consider the real requested size, and
not the extra buffering size.
2020-07-23 12:37:43 +03:00
Brian P O'RourkeandOran Agra 99e6e73235 Add contribution guidelines for vulnerability reports 2020-07-22 16:24:39 +03:00
Brian P O'RourkeandOran Agra 3e35ac9d7f Outdent github issues guidelines 2020-07-22 16:24:39 +03:00
Sungho HwangandGitHub 9fc8a5efe5 Fix typo in deps/README.md (#7553) 2020-07-22 11:33:57 +03:00
Madelyn OlsonandGitHub 818dc3a089 Properly reset errno for rdbLoad (#7542) 2020-07-21 17:00:13 -07:00
Oran AgraandGitHub 36b9494385 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 {
  }
}
2020-07-21 16:56:19 +03:00
Yossi GottliebandGitHub f57e844b2e 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.
2020-07-21 14:17:14 +03:00
Oran AgraandGitHub 343dd9bcce Fixes to release scripts (#7547) 2020-07-21 14:07:06 +03:00
WuYunlongandGitHub f7f77a746a Clarification on the bug that was fixed in PR #7539. (#7541)
Before that PR, processCommand() did not notice that cmd could be a module
command in which case getkeys_proc member has a different meaning.

The outcome was that a module command which doesn't take any key names in its
arguments (similar to SLOWLOG) would be handled as if it might have key name arguments
(similar to MEMORY), would consider cluster redirect but will end up with 0 keys
after an excessive call to getKeysFromCommand, and eventually do the right thing.
2020-07-21 09:41:44 +03:00
Remi ColletandGitHub 3f2fbc4c61 Fix deprecated tail syntax in tests (#7543) 2020-07-21 09:07:54 +03:00
Wen HuiandGitHub 4e8f2d6881 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.
2020-07-21 08:13:05 +03:00
Wen HuiandGitHub 2fbd0271f6 add missing caching command in client help (#7399) 2020-07-20 18:53:03 -07:00
zhaozhao.zzandGitHub 13e50935a8 replication: need handle -NOPERM error after send ping (#7538) 2020-07-20 22:21:55 +08:00
WuYunlongandGitHub e4d7de608c Fix cluster redirect for module command with no firstkey. (#7539)
Before this commit, processCommand() did not notice that cmd could be a module command
which declared `getkeys-api` and handled it for the purpose of cluster redirect it
as if it doesn't use any keys.

This commit fixed it by reusing the codes in addReplyCommand().
2020-07-20 15:33:06 +03:00
Oran AgraandGitHub 19f192c3a5 Run daily CI on PRs to release a branch (#7535) 2020-07-20 13:57:54 +03:00
WuYunlongandGitHub 86fed3fe09 Refactor streamAppendItem() by deleting redundancy condition. (#7487)
It will never happen that "lp != NULL && lp_bytes >= server.stream_node_max_bytes".
Assume that "lp != NULL && lp_bytes >= server.stream_node_max_bytes",
we got the following conditions:
a. lp != NULL
b. lp_bytes >= server.stream_node_max_bytes

If server.stream_node_max_bytes is 0, given condition a, condition b is always satisfied
If server.stream_node_max_bytes is not 0, given condition a and condition b, the codes just a
	few lines above set lp to NULL, a controdiction with condition a

So that condition b is recundant. We could delete it safely.
2020-07-20 13:14:27 +03:00
Scott BrennerandGitHub 2f4e9c3f9f GitHub Actions workflows - use latest version of actions/checkout (#7534) 2020-07-20 09:22:24 +03:00
Itamar HaberandGitHub 416f3f20b4 Merge pull request #7529 from itamarhaber/stable-sha256sum
Adds SHA256SUM to redis-stable tarball upload
2020-07-17 15:20:54 +03:00
Itamar Haber 5df0a64d30 Adds SHA256SUM to redis-stable tarball upload 2020-07-16 21:31:36 +03:00
a25df9dee0 Adds GitHub issue templates (#7468)
Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: yoav-steinberg <yoav@monfort.co.il>
2020-07-16 21:01:27 +03:00
yoav-steinbergandGitHub d484b8a04e 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.
2020-07-16 20:59:38 +03:00
杨博东andGitHub 8596d483bc Stream avoid duplicate parse id (#7450) 2020-07-16 08:57:27 +03:00
Luke PalmerandGitHub 5f716ea467 Send null for invalidate on flush (#7469) 2020-07-15 10:53:41 -07:00
9242ccf238 Notify systemd on sentinel startup (#7168)
Co-authored-by: Daniel Murnane <dmurnane@eitccorp.com>
2020-07-15 13:29:26 +03:00
Developer-Ecosystem-EngineeringandGitHub c2b5f1c15b 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
2020-07-15 12:44:03 +03:00
Wen HuiandGitHub d85af4d6f5 correct error msg for num connections reaching maxclients in cluster mode (#7444) 2020-07-15 12:38:47 +03:00
WuYunlongandGitHub 93bdbf5aa4 Fix command help for unexpected options (#7476) 2020-07-15 12:38:22 +03:00
WuYunlongandGitHub dc690161d5 Refactor RM_KeyType() by using macro. (#7486) 2020-07-15 12:37:44 +03:00
Oran AgraandGitHub a176cb56a3 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.
2020-07-14 20:21:59 +03:00
Oran AgraandGitHub 254c962554 redis-cli tests, fix valgrind timing issue (#7519)
this test when run with valgrind on github actions takes 160 seconds
2020-07-14 18:04:08 +03:00
WuYunlongandGitHub 8128d39737 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
2020-07-14 11:35:04 +03:00
Qu ChenandGitHub 938c35302f Replica always reports master's config epoch in CLUSTER NODES output. (#7235) 2020-07-13 07:16:06 -07:00
Oran AgraandGitHub 6a81450144 RESTORE ABSTTL skip expired keys - leak (#7511) 2020-07-13 16:40:19 +03:00
Oran AgraandGitHub e5227aab89 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.
2020-07-13 16:40:03 +03:00
Oran AgraandGitHub 02ef355f98 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.
2020-07-13 16:09:08 +03:00
Oran AgraandGitHub 7f19a04f0f 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
2020-07-12 13:55:26 +03:00
jimgreen2013andGitHub 67660881ed 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
2020-07-11 14:51:44 -05:00
杨博东andGitHub e9aba28932 STORE variants: SINTER,SUNION,SDIFF,ZUNION use setKey instead of dbDelete+dbAdd (#7489)
one of the differences (other than consistent code with SORT, GEORADIUS), is that the LFU of the old key is retained.
2020-07-11 15:52:41 +03:00
279b4a1464 fix benchmark in cluster mode fails to authenticate (#7488)
Co-authored-by: Oran Agra <oran@redislabs.com> (styling)
2020-07-10 16:37:11 +03:00
Itamar Haber a063c8c188 Prevents default save configuration being reset...
...when using any command line argument
2020-04-13 17:28:11 +03:00
Ponnuvel Palaniyappan abf8150b2e Fix typos in README.md 2020-01-14 07:38:57 +00:00
342 changed files with 38597 additions and 8931 deletions
+24
View File
@@ -0,0 +1,24 @@
---
name: Bug report
about: Help us improve Redis by reporting a bug
title: '[BUG]'
labels: ''
assignees: ''
---
**Describe the bug**
A short description of the bug.
**To reproduce**
Steps to reproduce the behavior and/or a minimal code sample.
**Expected behavior**
A description of what you expected to happen.
**Additional information**
Any additional information that is relevant to the problem.
+20
View File
@@ -0,0 +1,20 @@
---
name: Crash report
about: Submit a crash report
title: '[CRASH]'
labels: ''
assignees: ''
---
**Crash report**
Paste the complete crash log between the quotes below. Please include a few lines from the log preceding the crash report to provide some context.
```
```
**Aditional information**
1. OS distribution and version
2. Steps to reproduce (if any)
+24
View File
@@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest a feature for Redis
title: '[NEW]'
labels: ''
assignees: ''
---
**The problem/use-case that the feature addresses**
A description of the problem that the feature will solve, or the use-case with which the feature will be used.
**Description of the feature**
A description of what you want to happen.
**Alternatives you've considered**
Any alternative solutions or features you've considered, including references to existing open and closed feature requests in this repository.
**Additional information**
Any additional information that is relevant to the feature request.
+8
View File
@@ -0,0 +1,8 @@
---
name: Other
about: Can't find the right issue type? Use this one!
title: ''
labels: ''
assignees: ''
---
+21
View File
@@ -0,0 +1,21 @@
---
name: Question
about: Ask the Redis developers
title: '[QUESTION]'
labels: ''
assignees: ''
---
Please keep in mind that this issue tracker should be used for reporting bugs or proposing improvements to the Redis server.
Generally, questions about using Redis should be directed to the [community](https://redis.io/community):
* [the mailing list](https://groups.google.com/forum/#!forum/redis-db)
* [the `redis` tag at StackOverflow](http://stackoverflow.com/questions/tagged/redis)
* [/r/redis subreddit](http://www.reddit.com/r/redis)
* [the irc channel #redis](http://webchat.freenode.net/?channels=redis) on freenode
It is also possible that your question was already asked here, so please do a quick issues search before submitting. Lastly, if your question is about one of Redis' [clients](https://redis.io/clients), you may to contact your client's developers for help.
That said, please feel free to replace all this with your question :)
+22 -11
View File
@@ -7,12 +7,14 @@ jobs:
test-ubuntu-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make
# Fail build if there are warnings
# build with TLS just for compilation coverage
run: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes
- name: test
run: |
sudo apt-get install tcl8.5
sudo apt-get install tcl8.6
./runtest --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
@@ -20,30 +22,39 @@ jobs:
build-ubuntu-old:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make
run: make REDIS_CFLAGS='-Werror'
build-macos-latest:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make
run: make REDIS_CFLAGS='-Werror'
build-32bit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get update && sudo apt-get install libc6-dev-i386
make 32bit
make REDIS_CFLAGS='-Werror' 32bit
build-libc-malloc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make MALLOC=libc
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
build-centos7-jemalloc:
runs-on: ubuntu-latest
container: centos:7
steps:
- uses: actions/checkout@v2
- name: make
run: |
yum -y install gcc make
make REDIS_CFLAGS='-Werror'
+254 -27
View File
@@ -1,81 +1,308 @@
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: make
run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --accurate --verbose
sudo apt-get install tcl8.6
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
- name: unittest
run: ./src/redis-server test all
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: make MALLOC=libc
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --accurate --verbose
sudo apt-get install tcl8.6
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-32bit:
test-ubuntu-no-malloc-usable-size:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: make MALLOC=libc CFLAGS=-DNO_MALLOC_USABLE_SIZE
- name: test
run: |
sudo apt-get install tcl8.6
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-ubuntu-32bit:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: |
sudo apt-get update && sudo apt-get install libc6-dev-i386
make 32bit
make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --accurate --verbose
sudo apt-get install tcl8.6
./runtest --accurate --verbose --dump-logs
- name: module api test
run: |
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
- name: unittest
run: ./src/redis-server test all
test-tls:
test-ubuntu-tls:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: make
run: |
make BUILD_TLS=yes
- name: test
run: |
sudo apt-get install tcl8.5 tcl-tls
sudo apt-get install tcl8.6 tcl-tls
./utils/gen-test-certs.sh
./runtest --accurate --verbose --tls
./runtest --accurate --verbose --tls --dump-logs
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose --tls
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-ubuntu-io-threads:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: |
make
- name: test
run: |
sudo apt-get install tcl8.6 tcl-tls
./runtest --config io-threads 4 --config io-threads-do-reads yes --accurate --verbose --tags network --dump-logs
- name: cluster tests
run: |
./runtest-cluster --config io-threads 4 --config io-threads-do-reads yes
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: make valgrind
run: make valgrind REDIS_CFLAGS='-Werror -DREDIS_TEST'
- name: test
run: |
sudo apt-get install tcl8.5 valgrind -y
./runtest --valgrind --verbose --clients 1
sudo apt-get update
sudo apt-get install tcl8.6 valgrind -y
./runtest --valgrind --verbose --clients 1 --dump-logs
- name: module api test
run: ./runtest-moduleapi --valgrind --verbose --clients 1
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
- name: unittest
run: |
valgrind --track-origins=yes --suppressions=./src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full --log-file=err.txt ./src/redis-server test all
if grep -q 0x err.txt; then cat err.txt; exit 1; fi
test-valgrind-no-malloc-usable-size:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: make
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE"
- name: test
run: |
sudo apt-get update
sudo apt-get install tcl8.6 valgrind -y
./runtest --valgrind --verbose --clients 1 --dump-logs
- name: module api test
run: ./runtest-moduleapi --valgrind --no-latency --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 gcc make
make
- name: test
run: |
yum -y install which tcl
./runtest --accurate --verbose --dump-logs
- 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 --dump-logs
./runtest --accurate --verbose --dump-logs
- 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 --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-freebsd:
runs-on: macos-latest
if: github.repository == 'redis/redis'
timeout-minutes: 14400
steps:
- uses: actions/checkout@v2
- name: test
uses: vmactions/freebsd-vm@v0.1.2
with:
usesh: true
sync: rsync
prepare: pkg install -y bash gmake lang/tcl86
run: >
gmake &&
./runtest --accurate --verbose --no-latency --dump-logs &&
MAKE=gmake ./runtest-moduleapi --verbose &&
./runtest-sentinel &&
./runtest-cluster
test-alpine-jemalloc:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: alpine:latest
steps:
- uses: actions/checkout@v2
- name: make
run: |
apk add build-base
make REDIS_CFLAGS='-Werror'
- name: test
run: |
apk add tcl procps
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
test-alpine-libc-malloc:
runs-on: ubuntu-latest
if: github.repository == 'redis/redis'
container: alpine:latest
steps:
- uses: actions/checkout@v2
- name: make
run: |
apk add build-base
make REDIS_CFLAGS='-Werror' USE_JEMALLOC=no CFLAGS=-DUSE_MALLOC_USABLE_SIZE
- name: test
run: |
apk add tcl procps
./runtest --accurate --verbose --dump-logs
- name: module api test
run: ./runtest-moduleapi --verbose
- name: sentinel tests
run: ./runtest-sentinel
- name: cluster tests
run: ./runtest-cluster
+5
View File
@@ -27,9 +27,14 @@ 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
redis.code-workspace
+527 -11
View File
@@ -1,16 +1,532 @@
Hello! This file is just a placeholder, since this is the "unstable" branch
of Redis, the place where all the development happens.
Redis 6.2 release notes
=======================
There is no release notes for this branch, it gets forked into another branch
every time there is a partial feature freeze in order to eventually create
a new stable release.
--------------------------------------------------------------------------------
Upgrade urgency levels:
Usually "unstable" is stable enough for you to use it in development environments
however you should never use it in production environments. It is possible
to download the latest stable release here:
LOW: No need to upgrade unless there are new features you want to use.
MODERATE: Program an upgrade of the server, but it's not urgent.
HIGH: There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
SECURITY: There are security fixes in the release.
--------------------------------------------------------------------------------
http://download.redis.io/releases/redis-stable.tar.gz
================================================================================
Redis 6.2.2 Released Mon April 19 19:00:00 IST 2021
================================================================================
More information is available at http://redis.io
Upgrade urgency: HIGH, if you're using ACL and pub/sub, CONFIG REWRITE, or
suffering from performance regression. see below.
Happy hacking!
Bug fixes for regressions in previous releases of Redis 6.2:
* Fix BGSAVE, AOFRW, and replication slowdown due to child reporting CoW (#8645)
* Fix short busy loop when timer event is about to fire (#8764)
* Fix default user, overwritten and reset users losing pubsub channel permissions (#8723)
* Fix config rewrite with an empty `save` config resulsing in default `save` values (#8719)
* Fix not starting on alpine/libmusl without IPv6 (#8655)
* Fix issues with propagation and MULTI/EXEC in modules (#8617)
Several issues around nested calls and thread safe contexts
Bug fixes that are only applicable to previous releases of Redis 6.2:
* ACL Pub/Sub channels permission handling for save/load scenario (#8794)
* Fix early rejection of PUBLISH inside MULTI-EXEC transaction (#8534)
* Fix missing SLOWLOG records for blocked commands (#8632)
* Allow RESET command during busy scripts (#8629)
* Fix some error replies were not counted on stats (#8659)
Bug fixes:
* Add a timeout mechanism for replicas stuck in fullsync (#8762)
* Process HELLO command even if the default user has no permissions (#8633)
* Client issuing a long running script and using a pipeline, got disconnected (#8715)
* Fix script kill to work also on scripts that use `pcall` (#8661)
* Fix list-compress-depth may compress more node than required (#8311)
* Fix redis-cli handling of rediss:// URL scheme (#8705)
* Cluster: Skip unnecessary check which may prevent failure detection (#8585)
* Cluster: Fix hang manual failover when replica just started (#8651)
* Sentinel: Fix info-refresh time field before sentinel get first response (#8567)
* Sentinel: Fix possible crash on failed connection attempt (#8627)
* Systemd: Send the readiness notification when a replica is ready to accept connections (#8409)
Command behavior changes:
* ZADD: fix wrong reply when INCR used with GT/LT which blocked the update (#8717)
It was responding with the incremented value rather than nil
* XAUTOCLAIM: fix response to return the next available id as the cursor (#8725)
Previous behavior was retuning the last one which was already scanned
* XAUTOCLAIM: fix JUSTID to prevent incrementing delivery_count (#8724)
New config options:
* Add cluster-allow-replica-migration config option (#5285)
* Add replica-announced config option (#8653)
* Add support for plaintext clients in TLS cluster (#8587)
* Add support for reading encrypted keyfiles (#8644)
Improvements:
* Fix performance regression in BRPOP on Redis 6.0 (#8689)
* Avoid adding slowlog entries for config with sensitive data (#8584)
* Improve redis-cli non-binary safe string handling (#8566)
* Optimize CLUSTER SLOTS reply (#8541)
* Handle remaining fsync errors (#8419)
Info fields and introspection changes:
* Strip % sign from current_fork_perc info field (#8628)
* Fix RSS memory info on FreeBSD (#8620)
* Fix client_recent_max_input/output_buffer in 'INFO CLIENTS' when all clients drop (#8588)
* Fix invalid master_link_down_since_seconds in info replication (#8785)
Platform and deployment-related changes:
* Fix FreeBSD <12.x builds (#8603)
Modules:
* Add macros for RedisModule_log logging levels (#4246)
* Add RedisModule_GetAbsExpire / RedisModule_SetAbsExpire (#8564)
* Add a module type for key space notification (#8759)
* Set module eviction context flag only in masters (#8631)
* Fix unusable RedisModule_IsAOFClient API (#8596)
* Fix missing EXEC on modules propagation after failed EVAL execution (#8654)
* Fix edge-case when a module client is unblocked (#8618)
================================================================================
Redis 6.2.1 Released Mon Mar 1 17:51:36 IST 2021
================================================================================
Upgrade urgency: LOW.
Here is a comprehensive list of changes in this release compared to 6.2.0,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
Bug fixes:
* Fix sanitize-dump-payload for stream with deleted records (#8568)
* Prevent client-query-buffer-limit config from being set to lower than 1mb (#8557)
Improvements:
* Make port, tls-port and bind config options modifiable at runtime (#8510)
Platform and deployment-related changes:
* Fix compilation error on non-glibc systems if jemalloc is not used (#8533)
* Improved memory consumption and memory usage tracking on FreeBSD (#8545)
* Fix compilation on ARM64 MacOS with jemalloc (#8458)
Modules:
* New Module API for getting user name of a client (#8508)
* Optimize RM_Call by utilizing a shared reusable client (#8516)
* Fix crash running CLIENT INFO via RM_Call (#8560)
================================================================================
Redis 6.2.0 GA Released Tue Feb 22 14:00:00 IST 2021
================================================================================
Upgrade urgency: SECURITY if you use 32bit build of redis (see bellow), MODERATE
if you used earlier versions of Redis 6.2, LOW otherwise.
Integer overflow on 32-bit systems (CVE-2021-21309):
Redis 4.0 or newer uses a configurable limit for the maximum supported bulk
input size. By default, it is 512MB which is a safe value for all platforms.
If the limit is significantly increased, receiving a large request from a client
may trigger several integer overflow scenarios, which would result with buffer
overflow and heap corruption.
Here is a comprehensive list of changes in this release compared to 6.2 RC3,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
Bug fixes:
* Avoid 32-bit overflows when proto-max-bulk-len is set high (#8522)
* Fix broken protocol in client tracking tracking-redir-broken message (#8456)
* Avoid unsafe field name characters in INFO commandstats, errorstats, modules (#8492)
* XINFO able to access expired keys during CLIENT PAUSE WRITE (#8436)
* Fix allowed length for REPLCONF ip-address, needed due to Sentinel's support for hostnames (#8517)
* Fix broken protocol in redis-benchmark when used with -a or --dbnum (#8486)
* XADD counts deleted records too when considering switching to a new listpack (#8390)
Bug fixes that are only applicable to previous releases of Redis 6.2:
* Fixes in GEOSEARCH bybox (accuracy and mismatch between width and height) (#8445)
* Fix risk of OOM panic in HRANDFIELD, ZRANDMEMBER commands with huge negative count (#8429)
* Fix duplicate replicas issue in Sentinel, needed due to hostname support (#8481)
* Fix Sentinel configuration rewrite, an improvement of #8271 (#8480)
Command behavior changes:
* SRANDMEMBER uses RESP3 array type instead of set type (#8504)
* EXPIRE, EXPIREAT, SETEX, GETEX: Return error when provided expire time overflows (#8287)
Other behavior changes:
* Remove ACL subcommand validation if fully added command exists. (#8483)
Improvements:
* Optimize sorting in GEORADIUS / GEOSEARCH with COUNT (#8326)
* Optimize HRANDFIELD and ZRANDMEMBER case 4 when ziplist encoded (#8444)
* Optimize in-place replacement of elements in HSET, HINCRBY, LSET (#8493)
* Remove redundant list to store pubsub patterns (#8472)
* Add --insecure option to command line tools (#8416)
Info fields and introspection changes:
* Add INFO fields to track progress of BGSAVE, AOFRW, replication (#8414)
Modules:
* RM_ZsetRem: Delete key if empty, the bug could leave empty zset keys (#8453)
* RM_HashSet: Add COUNT_ALL flag and set errno (#8446)
================================================================================
Redis 6.2 RC3 Released Tue Feb 1 14:00:00 IST 2021
================================================================================
Upgrade urgency LOW: This is the third Release Candidate of Redis 6.2.
Here is a comprehensive list of changes in this release compared to 6.2 RC2,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
New commands / args:
* Add HRANDFIELD and ZRANDMEMBER commands (#8297)
* Add FAILOVER command (#8315)
* Add GETEX, GETDEL commands (#8327)
* Add PXAT/EXAT arguments to SET command (#8327)
* Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH (#8258)
Sentinel:
* Add hostname support to Sentinel (#8282)
* Prevent file descriptors from leaking into Sentinel scripts (#8242)
* Fix config file line order dependency and config rewrite sequence (#8271)
New configuration options:
* Add set-proc-title config option to disable changes to the process title (#3623)
* Add proc-title-template option to control what's shown in the process title (#8397)
* Add lazyfree-lazy-user-flush config option to control FLUSHALL, FLUSHDB and SCRIPT FLUSH (#8258)
Bug fixes:
* AOF: recover from last write error by turning on/off appendonly config (#8030)
* Exit on fsync error when the AOF fsync policy is 'always' (#8347)
* Avoid assertions (on older kernels) when testing arm64 CoW bug (#8405)
* CONFIG REWRITE should honor umask settings (#8371)
* Fix firstkey,lastkey,step in COMMAND command for some commands (#8367)
Special considerations:
* Fix misleading description of the save configuration directive (#8337)
Improvements:
* A way to get RDB file via replication without excessive replication buffers (#8303)
* Optimize performance of clusterGenNodesDescription for large clusters (#8182)
Info fields and introspection changes:
* SLOWLOG and LATENCY monitor include unblocking time of blocked commands (#7491)
Modules:
* Add modules API for streams (#8288)
* Add event for fork child birth and termination (#8289)
* Add RM_BlockedClientMeasureTime* etc, to track background processing in commandstats (#7491)
* Fix bug in v6.2, wrong value passed to the new unlink callback (#8381)
* Fix bug in v6.2, modules blocked on keys unblock on commands like LPUSH (#8356)
================================================================================
Redis 6.2 RC2 Released Tue Jan 12 16:17:20 IST 2021
================================================================================
Upgrade urgency LOW: This is the second Release Candidate of Redis 6.2.
IMPORTANT: If you're running Redis on ARM64 or a big-endian system, upgrade may
have significant implications. Please be sure to read the notes below.
Here is a comprehensive list of changes in this release compared to 6.2 RC1,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
New commands / args:
* Add the REV, BYLEX and BYSCORE arguments to ZRANGE, and the ZRANGESTORE command (#7844)
* Add the XAUTOCLAIM command (#7973)
* Add the MINID trimming strategy and the LIMIT argument to XADD and XTRIM (#8169)
* Add the ANY argument to GEOSEARCH and GEORADIUS (#8259)
* Add the CH, NX, XX arguments to GEOADD (#8227)
* Add the COUNT argument to LPOP and RPOP (#8179)
* Add the WRITE argument to CLIENT PAUSE for pausing write commands exclusively (#8170)
* Change the proto-ver argument of HELLO to optional (#7377)
* Add the CLIENT TRACKINGINFO subcommand (#7309)
Command behavior changes:
* CLIENT TRACKING yields an error when given overlapping BCAST prefixes (#8176)
* SWAPDB invalidates WATCHed keys (#8239)
* SORT command behaves differently when used on a writable replica (#8283)
Other behavior changes:
* Avoid propagating MULTI/EXEC for read-only transactions (#8216)
* Remove the read-only flag from TIME, ECHO, ROLE, LASTSAVE (#8216)
* Fix the command flags of PFDEBUG (#8222)
* Tracking clients will no longer receive unnecessary key invalidation messages after FLUSHDB (#8039)
* Sentinel: Fix missing updates to the config file after SENTINEL SET command (#8229)
Bug fixes with compatibility implications (bugs introduced in Redis 6.0):
* Fix RDB CRC64 checksum on big-endian systems (#8270)
If you're using big-endian please consider the compatibility implications with
RESTORE, replication and persistence.
* Fix wrong order of key/value in Lua's map response (#8266)
If your scripts use redis.setresp() or return a map (new in Redis 6.0), please
consider the implications.
Bug fixes that are only applicable to previous releases of Redis 6.2:
* Resolve rare assertions in active defragmentation while loading (#8284, #8281)
Bug fixes:
* Fix the selection of a random element from large hash tables (#8133)
* Fix an issue where a forked process deletes the parent's pidfile (#8231)
* Fix crashes when enabling io-threads-do-reads (#8230)
* Fix a crash in redis-cli after executing cluster backup (#8267)
* Fix redis-benchmark to use an IP address for the first cluster node (#8154)
* Fix saving of strings larger than 2GB into RDB files (#8306)
Additional improvements:
* Improve replication handshake time (#8214)
* Release client tracking table memory asynchronously in cases where the DB is also freed asynchronously (#8039)
* Avoid wasteful transient memory allocation in certain cases (#8286, #5954)
* Handle binary string values by the 'requirepass' and 'masterauth' configs (#8200)
Platform and deployment-related changes:
* Install redis-check-rdb and redis-check-aof as symlinks to redis-server (#5745)
* Add a check for an ARM64 Linux kernel bug (#8224)
Due to the potential severity of this issue, Redis will refuse to run on
affected platforms by default.
Info fields and introspection changes:
* Add the errorstats section to the INFO command (#8217)
* Add the failed_calls and rejected_calls fields INFO's commandstats section (#8217)
* Report child copy-on-write metrics continuously (#8264)
Module API changes:
* Add the RedisModule_SendChildCOWInfo API (#8264)
* Add the may-replicate command flag (#8170)
================================================================================
Redis 6.2 RC1 Released Mon Dec 14 11:50:00 IST 2020
================================================================================
Upgrade urgency LOW: This is the first Release Candidate of Redis 6.2.
Introduction to the Redis 6.2 release
=====================================
This release is the first significant Redis release managed by the core team
under the new project governance model.
Redis 6.2 includes many new commands and improvements, but no big features. It
mainly makes Redis more complete and addresses issues that have been requested
by many users frequently or for a long time.
Many of these changes were not eligible for 6.0.x for several reasons:
1. They are not backward compatible, which is always the case with new or
extended commands (that cannot be replicated to an older replica).
2. They require a longer release-candidate test cycle.
Here is a comprehensive list of changes in this release compared to 6.0.9,
each one includes the PR number that added it, so you can get more details
at https://github.com/redis/redis/pull/<number>
New commands / args:
* Add SMISMEMBER command that checks multiple members (#7615)
* Add ZMSCORE command that returns an array of scores (#7593)
* Add LMOVE and BLMOVE commands that pop and push arbitrarily (#6929)
* Add RESET command that resets client connection state (#7982)
* Add COPY command that copies keys (#7953)
* Add ZDIFF and ZDIFFSTORE commands (#7961)
* Add ZINTER and ZUNION commands (#7794)
* Add GEOSEARCH/GEOSEARCHSTORE commands for bounding box spatial queries (#8094)
* Add GET parameter to SET command, for more powerful GETSET (#7852)
* Add exclusive range query to XPENDING (#8130)
* Add exclusive range query to X[REV]RANGE (#8072)
* Add GT and LT options to ZADD for conditional score updates (#7818)
* Add CLIENT INFO and CLIENT LIST for specific ids (#8113)
* Add IDLE argument to XPENDING command (#7972)
* Add local address to CLIENT LIST, and a CLIENT KILL filter. (#7913)
* Add NOMKSTREAM option to XADD command (#7910)
* Add command introspection to Sentinel (#7940)
* Add SENTINEL MYID subcommand (#7858)
New features:
* Dump payload sanitization: prevent corrupt payload causing crashes (#7807)
Has flags to enable full O(N) validation (disabled by default).
* ACL patterns for Pub/Sub channels (#7993)
* Support ACL for Sentinel mode (#7888)
* Support getting configuration from both stdin and file at the same time (#7893)
Lets you avoid storing secrets on the disk.
New features in CLI tools:
* redis-cli RESP3 push support (#7609)
* redis-cli cluster import support source and target that require auth (#7994)
* redis-cli URIs able to provide user name in addition to password (#8048)
* redis-cli/redis-benchmark allow specifying the prefered ciphers/ciphersuites (#8005)
* redis-cli add -e option to exit with code when command execution fails (#8136)
Command behavior changes:
* EXISTS should not alter LRU (#8016)
In Redis 5.0 and 6.0 it would have touched the LRU/LFU of the key.
* OBJECT should not reveal logically expired keys (#8016)
Will now behave the same TYPE or any other non-DEBUG command.
* Improve db id range check for SELECT and MOVE (#8085)
Changes the error message text on a wrong db index.
* Modify AUTH / HELLO error message (#7648)
Changes the error message text when the user isn't found or is disabled.
* BITOPS length limited to proto_max_bulk_len rather than 512MB (#8096)
The limit is now configurable like in SETRANGE, and APPEND.
* GEORADIUS[BYMEMBER] can fail with -OOM if Redis is over the memory limit (#8107)
Other behavior changes:
* Optionally (default) fail to start if requested bind address is not available (#7936)
If you rely on Redis starting successfully even if one of the bind addresses
is not available, you'll need to tune the new config.
* Limit the main db dictionaries expansion to prevent key eviction (#7954)
In the past big dictionary rehashing could result in massive data eviction.
Now this rehashing is delayed (up to a limit), which can result in performance
loss due to hash collisions.
* CONFIG REWRITE is atomic and safer, but requires write access to the config file's folder (#7824, #8051)
This change was already present in 6.0.9, but was missing from the release
notes.
* A new incremental eviction mechanism that reduces latency on eviction spikes (#7653)
In pathological cases this can cause memory to grow uncontrolled and may require
specific tuning.
* Not resetting "save" config when Redis is started with command line arguments. (#7092)
In case you provide command line arguments without "save" and count on it
being disabled, Now the defaults "save" config will kick in.
* Update memory metrics for INFO during loading (#7690)
* When "supervised" config is enabled, it takes precedence over "daemonize". (#8036)
* Assertion and panic, print crash log without generating SIGSEGV (#7585)
* Added crash log report on SIGABRT, instead of silently exiting (#8004)
* Disable THP (Transparent Huge Pages) if enabled (#7381)
If you deliberately enabled it, you'll need to config Redis to keep it.
Bug fixes:
* Handle output buffer limits for module blocked clients (#8141)
Could result in a module sending reply to a blocked client to go beyond the
limit.
* Fix setproctitle related crashes. (#8150, #8088)
Caused various crashes on startup, mainly on Apple M1 chips or under
instrumentation.
* A module doing RM_Call could cause replicas to get nested MULTI (#8097).
* Backup/restore cluster mode keys to slots map for repl-diskless-load=swapdb (#8108)
In cluster mode with repl-diskless-load, when loading failed, slot map
wouldn't have been restored.
* Fix oom-score-adj-values range, and bug when used in config file (#8046)
Enabling setting this in the config file in a line after enabling it, would
have been buggy.
* Reset average ttl when empty databases (#8106)
Just causing misleading metric in INFO
* Disable rehash when Redis has child process (#8007)
This could have caused excessive CoW during BGSAVE, replication or AOFRW.
* Further improved ACL algorithm for picking categories (#7966)
Output of ACL GETUSER is now more similar to the one provided by ACL SETUSER.
* Fix bug with module GIL being released prematurely (#8061)
Could in theory (and rarely) cause multi-threaded modules to corrupt memory.
* Fix cluster redirect for module command with no firstkey. (#7539)
* Reduce effect of client tracking causing feedback loop in key eviction (#8100)
* Kill disk-based fork child when all replicas drop and 'save' is not enabled (#7819)
* Rewritten commands (modified for propagation) are logged as their original command (#8006)
* Fix cluster access to unaligned memory (SIGBUS on old ARM) #7958
* If diskless repl child is killed, make sure to reap the child pid (#7742)
* Broadcast a PONG message when slot's migration is over, may reduce MOVED responses (#7571)
Other improvements:
* TLS Support in redis-benchmark (#7959)
* Accelerate diskless master connections, and general re-connections (#6271)
* Run active defrag while blocked / loading (#7726)
* Performance and memory reporting improvement - sds take control of its internal fragmentation (#7875)
* Speedup cluster failover. (#7948)
Platform / toolchain support related improvements:
* Optionally (not by default) use H/W Monotonic clock for faster time sampling (#7644)
* Remove the requirements for C11 and _Atomic supporting compiler (#7707)
This would allow to more easily build and use Redis on older systems and
compilers again.
* Fix crash log registers output on ARM. (#8020)
* Raspberry build fix. (#8095)
* Setting process title support for Haiku. (#8060)
* DragonFlyBSD RSS memory sampling support. (#8023)
New configuration options:
* Enable configuring OpenSSL using the standard openssl.cnf (#8143)
* oom-score-adj-values config can now take absolute values (besides relative ones) (#8046)
* TLS: Add different client cert support. (#8076)
* Note that a few other changes listed above added their config options.
Info fields and introspection changes:
* Add INFO fields to track diskless and disk-based replication progress (#7981)
* Add INFO field for main thread cpu time, and scrape system time. (#8132)
* Add total_forks to INFO STATS (#8155)
* Add maxclients and cluster_connections to INFO CLIENTS (#7979)
* Add tracking bcast flag and client redirection in client list (#7995)
* Fixed INFO client_recent_max_input_buffer includes argv array (#8065, see #7874)
* Note that a few other changes listed above added their info fields.
Module API changes:
* Add CTX_FLAGS_DENY_BLOCKING as a unified the way to know if blocking is allowed (#8025)
* Add data type callbacks for lazy free effort, and unlink (#7912)
* Add data type callback for COPY command (#8112)
* Add callbacks for defrag support. (#8149)
* Add module event for repl-diskless-load swapdb (#8153)
Module related fixes:
* Moved RMAPI_FUNC_SUPPORTED so that it's usable (#8037)
* Improve timer accuracy (#7987)
* Allow '\0' inside of result of RM_CreateStringPrintf (#6260)
Thanks to all the users and developers who made this release possible.
We'll follow up with more RC releases, until the code looks production ready
and we don't get reports of serious issues for a while.
A special thank you for the amount of work put into this release by:
- Oran Agra
- Yossi Gottlieb
- Viktor Söderqvist
- Yang Bodong
- Filipe Oliveira
- Guy Benoish
- Itamar Haber
- Madelyn Olson
- Wang Yuan
- Felipe Machado
- Wen Hui
- Tatsuya Arisawa
- Jonah H. Harris
- Raghav Muddur
- Jim Brunner
- Yaacov Hazan
- Allen Farris
- Chen Yang
- Nitai Caro
- sundb
- Meir Shpilraien
- maohuazhu
- Valentino Geron
- Zhao Zhao
- Qu Chen
- George Prekas
- Tyson Andre
- Uri Yagelnik
- Michael Grunder
- Huang Zw
- alexronke-channeladvisor
- Andy Pan
- Wu Yunlong
- Wei Kukey
- Yoav Steinberg
- Greg Femec
- Uri Shachar
- Nykolas Laurentino de Lima
- xhe
- zhenwei pi
- David CARLIER
Migrating from 6.0 to 6.2
=========================
Redis 6.2 is mostly a strict superset of 6.0, you should not have any problem
upgrading your application from 6.0 to 6.2. However there are some small changes
of behavior listed above, please make sure you are not badly affected by any of
them.
Specifically these sections:
* Command behavior changes
* Other behavior changes
--------------------------------------------------------------------------------
Cheers,
The Redis team
+96
View File
@@ -0,0 +1,96 @@
Contributor Covenant Code of Conduct
Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
this email address: redis@redis.io.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
1. Correction
Community Impact: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
Consequence: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
2. Warning
Community Impact: A violation through a single incident or series
of actions.
Consequence: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
3. Temporary Ban
Community Impact: A serious violation of community standards, including
sustained inappropriate behavior.
Consequence: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
4. Permanent Ban
Community Impact: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
Consequence: A permanent ban from any sort of public interaction within
the community.
Attribution
This Code of Conduct is adapted from the Contributor Covenant,
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by Mozillas code of conduct
enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+32 -8
View File
@@ -7,18 +7,42 @@ 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:
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
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.
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:
There is also an active community of Redis users at Stack Overflow:
http://stackoverflow.com/questions/tagged/redis
http://stackoverflow.com/questions/tagged/redis
# Reporting Security Bugs
*If you are reporting a security bug*, please contact the core team privately
by emailing redis@redis.io. Your report will be acknowledged by a core team
member and once the report has been reviewed you will receive a more detailed
response including next steps.
If you do not receive a reply you can escalate to the Redis Google Group,
linked above. Because this group is a public space please do not disclose the
issue in detail, only say that you are trying to reach the core team for a
security issue.
Redis follows a responsible disclosure process:
1. Reports are reviewed and analyzed privately
2. Patches are prepared for supported versions of Redis
3. Vendor lists are notified with an embargo date to reduce the public impact
4. We push a fix release and your bug can be posted publicly with credit in
release notes and the version history (and our thanks!)
Issues and pull requests for documentation belong on the redis-doc repo:
https://github.com/redis/redis-doc
# How to provide a patch for a new feature
+75 -54
View File
@@ -3,22 +3,22 @@ This README is just a fast *quick start* document. You can find more detailed do
What is Redis?
--------------
Redis is often referred as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
Redis is often referred to as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
Data structures implemented into Redis have a few special properties:
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that is also non-volatile.
* Implementation of data structures stress on memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modeled using an high level programming language.
* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, cluster, high availability.
* Redis cares to store them on disk, even if they are always served and modified into the server memory. This means that Redis is fast, but that it is also non-volatile.
* The implementation of data structures emphasizes memory efficiency, so data structures inside Redis will likely use less memory compared to the same data structure modelled using a high-level programming language.
* Redis offers a number of features that are natural to find in a database, like replication, tunable levels of durability, clustering, and high availability.
Another good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations to work with complex data types like Lists, Sets, ordered data structures, and so forth.
Another good example is to think of Redis as a more complex version of memcached, where the operations are not just SETs and GETs, but operations that work with complex data types like Lists, Sets, ordered data structures, and so forth.
If you want to know more, this is a list of selected starting points:
* Introduction to Redis data types. http://redis.io/topics/data-types-intro
* Introduction to Redis data types. https://redis.io/topics/data-types-intro
* Try Redis directly inside your browser. http://try.redis.io
* The full list of Redis commands. http://redis.io/commands
* There is much more inside the Redis official documentation. http://redis.io/documentation
* The full list of Redis commands. https://redis.io/commands
* There is much more inside the official Redis documentation. https://redis.io/documentation
Building Redis
--------------
@@ -29,7 +29,7 @@ and 64 bit systems.
It may compile on Solaris derived systems (for instance SmartOS) but our
support for this platform is *best effort* and Redis is not guaranteed to
work as well as in Linux, OSX, and \*BSD there.
work as well as in Linux, OSX, and \*BSD.
It is as simple as:
@@ -40,7 +40,16 @@ libssl-dev on Debian/Ubuntu) and run:
% make BUILD_TLS=yes
You can run a 32 bit Redis binary using:
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 USE_SYSTEMD=yes
To append a suffix to Redis program names, use:
% make PROG_SUFFIX="-alt"
You can build a 32 bit Redis binary using:
% make 32bit
@@ -58,7 +67,7 @@ installed):
Fixing build problems with dependencies or cached build options
---------
Redis has some dependencies which are included into the `deps` directory.
Redis 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.
@@ -85,7 +94,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 Redis, 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"`
@@ -106,18 +115,30 @@ To compile against jemalloc on Mac OS X systems, use:
% make MALLOC=jemalloc
Monotonic clock
---------------
By default, Redis will build using the POSIX clock_gettime function as the
monotonic clock source. On most modern systems, the internal processor clock
can be used to improve performance. Cautions can be found here:
http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/
To build with support for the processor's internal instruction clock, use:
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
Verbose build
-------------
Redis will build with a user friendly colorized output by default.
If you want to see a more verbose output use the following:
Redis will build with a user-friendly colorized output by default.
If you want to see a more verbose output, use the following:
% make V=1
Running Redis
-------------
To run Redis with the default configuration just type:
To run Redis with the default configuration, just type:
% cd src
% ./redis-server
@@ -163,12 +184,12 @@ then in another terminal try the following:
(integer) 2
redis>
You can find the list of all the available commands at http://redis.io/commands.
You can find the list of all the available commands at https://redis.io/commands.
Installing Redis
-----------------
In order to install Redis binaries into /usr/local/bin just use:
In order to install Redis binaries into /usr/local/bin, just use:
% make install
@@ -177,8 +198,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 Redis, 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 Redis, 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
@@ -196,14 +217,14 @@ You'll be able to stop and start Redis using the script named
Code contributions
-----------------
Note: by contributing code to the Redis project in any form, including sending
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][1] file included in the Redis
source distribution.
Please see the [CONTRIBUTING][2] file in this source distribution for more
information.
information, including details on our process for security bugs/vulnerabilities.
[1]: https://github.com/redis/redis/blob/unstable/COPYING
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING
@@ -246,7 +267,7 @@ of complexity incrementally.
Note: lately Redis was refactored quite a bit. Function names and file
names have been changed, so you may find that this documentation reflects the
`unstable` branch more closely. For instance in Redis 3.0 the `server.c`
`unstable` branch more closely. For instance, in Redis 3.0 the `server.c`
and `server.h` files were named `redis.c` and `redis.h`. However the overall
structure is the same. Keep in mind that all the new developments and pull
requests should be performed against the `unstable` branch.
@@ -273,25 +294,25 @@ the structure definition.
Another important Redis data structure is the one defining a client.
In the past it was called `redisClient`, now just `client`. The structure
has many fields, here we'll just show the main ones:
struct client {
int fd;
sds querybuf;
int argc;
robj **argv;
redisDb *db;
int flags;
list *reply;
char buf[PROTO_REPLY_CHUNK_BYTES];
... many other fields ...
}
```c
struct client {
int fd;
sds querybuf;
int argc;
robj **argv;
redisDb *db;
int flags;
list *reply;
char buf[PROTO_REPLY_CHUNK_BYTES];
// ... many other fields ...
}
```
The client structure defines a *connected client*:
* The `fd` field is the client socket file descriptor.
* `argc` and `argv` are populated with the command the client is executing, so that functions implementing a given Redis command can read the arguments.
* `querybuf` accumulates the requests from the client, which are parsed by the Redis server according to the Redis protocol and executed by calling the implementations of the commands the client is executing.
* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writable.
* `reply` and `buf` are dynamic and static buffers that accumulate the replies the server sends to the client. These buffers are incrementally written to the socket as soon as the file descriptor is writeable.
As you can see in the client structure above, arguments in a command
are described as `robj` structures. The following is the full `robj`
@@ -324,20 +345,20 @@ This is the entry point of the Redis server, where the `main()` function
is defined. The following are the most important steps in order to startup
the Redis server.
* `initServerConfig()` setups the default values of the `server` structure.
* `initServerConfig()` sets up the default values of the `server` structure.
* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.
* `aeMain()` starts the event loop which listens for new connections.
There are two special functions called periodically by the event loop:
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timedout clients.
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timed out clients.
2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.
Inside server.c you can find code that handles other vital things of the Redis server:
* `call()` is used in order to call a given command in the context of a given client.
* `activeExpireCycle()` handles eviciton of keys with a time to live set via the `EXPIRE` command.
* `freeMemoryIfNeeded()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
* `activeExpireCycle()` handles eviction of keys with a time to live set via the `EXPIRE` command.
* `performEvictions()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.
networking.c
@@ -347,16 +368,16 @@ This file defines all the I/O functions with clients, masters and replicas
(which in Redis are just special clients):
* `createClient()` allocates and initializes a new client.
* the `addReply*()` family of functions are used by commands implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.
* the `addReply*()` family of functions are used by command implementations in order to append data to the client structure, that will be transmitted to the client as a reply for a given command executed.
* `writeToClient()` transmits the data pending in the output buffers to the client and is called by the *writable event handler* `sendReplyToClient()`.
* `readQueryFromClient()` is the *readable event handler* and accumulates data from read from the client into the query buffer.
* `readQueryFromClient()` is the *readable event handler* and accumulates data read from the client into the query buffer.
* `processInputBuffer()` is the entry point in order to parse the client query buffer according to the Redis protocol. Once commands are ready to be processed, it calls `processCommand()` which is defined inside `server.c` in order to actually execute the command.
* `freeClient()` deallocates, disconnects and removes a client.
aof.c and rdb.c
---
As you can guess from the names these files implement the RDB and AOF
As you can guess from the names, these files implement the RDB and AOF
persistence for Redis. Redis uses a persistence model based on the `fork()`
system call in order to create a thread with the same (shared) memory
content of the main Redis thread. This secondary thread dumps the content
@@ -368,13 +389,13 @@ The implementation inside `aof.c` has additional functions in order to
implement an API that allows commands to append new commands into the AOF
file as clients execute them.
The `call()` function defined inside `server.c` is responsible to call
The `call()` function defined inside `server.c` is responsible for calling
the functions that in turn will write the commands into the AOF.
db.c
---
Certain Redis commands operate on specific data types, others are general.
Certain Redis commands operate on specific data types; others are general.
Examples of generic commands are `DEL` and `EXPIRE`. They operate on keys
and not on their values specifically. All those generic commands are
defined inside `db.c`.
@@ -382,7 +403,7 @@ defined inside `db.c`.
Moreover `db.c` implements an API in order to perform certain operations
on the Redis dataset without directly accessing the internal data structures.
The most important functions inside `db.c` which are used in many commands
The most important functions inside `db.c` which are used in many command
implementations are the following:
* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.
@@ -400,7 +421,7 @@ The `robj` structure defining Redis objects was already described. Inside
a basic level, like functions to allocate new objects, handle the reference
counting and so forth. Notable functions inside this file:
* `incrRefcount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.
* `incrRefCount()` and `decrRefCount()` are used in order to increment or decrement an object reference count. When it drops to 0 the object is finally freed.
* `createObject()` allocates a new object. There are also specialized functions to allocate string objects having a specific content, like `createStringObjectFromLongLong()` and similar functions.
This file also implements the `OBJECT` command.
@@ -424,15 +445,15 @@ replicas, or to continue the replication after a disconnection.
Other C files
---
* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client commands implementations for these data types.
* `t_hash.c`, `t_list.c`, `t_set.c`, `t_string.c`, `t_zset.c` and `t_stream.c` contains the implementation of the Redis data types. They implement both an API to access a given data type, and the client command implementations for these data types.
* `ae.c` implements the Redis event loop, it's a self contained library which is simple to read and understand.
* `sds.c` is the Redis string library, check http://github.com/antirez/sds for more information.
* `anet.c` is a library to use POSIX networking in a simpler way compared to the raw interface exposed by the kernel.
* `dict.c` is an implementation of a non-blocking hash table which rehashes incrementally.
* `scripting.c` implements Lua scripting. It is completely self contained from the rest of the Redis implementation and is simple enough to understand if you are familar with the Lua API.
* `scripting.c` implements Lua scripting. It is completely self-contained and isolated from the rest of the Redis implementation and is simple enough to understand if you are familiar with the Lua API.
* `cluster.c` implements the Redis Cluster. Probably a good read only after being very familiar with the rest of the Redis code base. If you want to read `cluster.c` make sure to read the [Redis Cluster specification][3].
[3]: http://redis.io/topics/cluster-spec
[3]: https://redis.io/topics/cluster-spec
Anatomy of a Redis command
---
@@ -455,12 +476,12 @@ top comment inside `server.c`.
After the command operates in some way, it returns a reply to the client,
usually using `addReply()` or a similar function defined inside `networking.c`.
There are tons of commands implementations inside the Redis source code
that can serve as examples of actual commands implementations. To write
a few toy commands can be a good exercise to familiarize with the code base.
There are tons of command implementations inside the Redis source code
that can serve as examples of actual commands implementations. Writing
a few toy commands can be a good exercise to get familiar with the code base.
There are also many other files not described here, but it is useless to
cover everything. We want to just help you with the first steps.
cover everything. We just want to help you with the first steps.
Eventually you'll find your way inside the Redis code base :-)
Enjoy!
+10 -3
View File
@@ -37,6 +37,7 @@ distclean:
-(cd linenoise && $(MAKE) clean) > /dev/null || true
-(cd lua && $(MAKE) clean) > /dev/null || true
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
-(rm -f .make-*)
.PHONY: distclean
@@ -57,18 +58,24 @@ linenoise: .make-prerequisites
.PHONY: linenoise
hdr_histogram: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
cd hdr_histogram && $(MAKE)
.PHONY: hdr_histogram
ifeq ($(uname_S),SunOS)
# Make isinf() available
LUA_CFLAGS= -D__C99FEATURES__=1
endif
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' $(CFLAGS)
LUA_CFLAGS+= -O2 -Wall -DLUA_ANSI -DENABLE_CJSON_GLOBAL -DREDIS_STATIC='' -DLUA_USE_MKSTEMP $(CFLAGS)
LUA_LDFLAGS+= $(LDFLAGS)
# lua's Makefile defines AR="ar rcu", which is unusual, and makes it more
# challenging to cross-compile lua (and redis). These defines make it easier
# to fit redis into cross-compilation environments, which typically set AR.
AR=ar
ARFLAGS=rcu
ARFLAGS=rc
lua: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
@@ -81,7 +88,7 @@ JEMALLOC_LDFLAGS= $(LDFLAGS)
jemalloc: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
cd jemalloc && ./configure --with-version=5.1.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ --enable-cc-silence CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
cd jemalloc && ./configure --with-version=5.1.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
cd jemalloc && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
.PHONY: jemalloc
+5 -5
View File
@@ -17,11 +17,11 @@ active defragmentation logic. However this feature of Redis is not mandatory
and Redis is able to understand if the Jemalloc version it is compiled
against supports such Redis-specific modifications. So in theory, if you
are not interested in the active defragmentation, you can replace Jemalloc
just following tose steps:
just following these 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
@@ -49,7 +49,7 @@ Hiredis uses the SDS string library, that must be the same version used inside R
1. Check with diff if hiredis API changed and what impact it could have in Redis.
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.
+121
View File
@@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
+41
View File
@@ -0,0 +1,41 @@
The code in this repository code was Written by Gil Tene, Michael Barker,
and Matt Warren, and released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/
For users of this code who wish to consume it under the "BSD" license
rather than under the public domain or CC0 contribution text mentioned
above, the code found under this directory is *also* provided under the
following license (commonly referred to as the BSD 2-Clause License). This
license does not detract from the above stated release of the code into
the public domain, and simply represents an additional license granted by
the Author.
-----------------------------------------------------------------------------
** Beginning of "BSD 2-Clause License" text. **
Copyright (c) 2012, 2013, 2014 Gil Tene
Copyright (c) 2014 Michael Barker
Copyright (c) 2014 Matt Warren
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
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 HOLDER 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.
+20
View File
@@ -0,0 +1,20 @@
STD=
WARN= -Wall
OPT= -Os
R_CFLAGS= $(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS)
R_LDFLAGS= $(LDFLAGS)
DEBUG= -g
R_CC=$(CC) $(R_CFLAGS)
R_LD=$(CC) $(R_LDFLAGS)
hdr_histogram.o: hdr_histogram.h hdr_histogram.c
.c.o:
$(R_CC) -c $<
clean:
rm -f *.o
+10
View File
@@ -0,0 +1,10 @@
HdrHistogram_c v0.11.0
----------------------------------------------
This port contains a subset of the 'C' version of High Dynamic Range (HDR) Histogram available at [github.com/HdrHistogram/HdrHistogram_c](https://github.com/HdrHistogram/HdrHistogram_c).
The code present on `hdr_histogram.c`, `hdr_histogram.h`, and `hdr_atomic.c` was Written by Gil Tene, Michael Barker,
and Matt Warren, and released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/.
+146
View File
@@ -0,0 +1,146 @@
/**
* hdr_atomic.h
* Written by Philip Orwig and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*/
#ifndef HDR_ATOMIC_H__
#define HDR_ATOMIC_H__
#if defined(_MSC_VER)
#include <stdint.h>
#include <intrin.h>
#include <stdbool.h>
static void __inline * hdr_atomic_load_pointer(void** pointer)
{
_ReadBarrier();
return *pointer;
}
static void hdr_atomic_store_pointer(void** pointer, void* value)
{
_WriteBarrier();
*pointer = value;
}
static int64_t __inline hdr_atomic_load_64(int64_t* field)
{
_ReadBarrier();
return *field;
}
static void __inline hdr_atomic_store_64(int64_t* field, int64_t value)
{
_WriteBarrier();
*field = value;
}
static int64_t __inline hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
{
#if defined(_WIN64)
return _InterlockedExchange64(field, value);
#else
int64_t comparand;
int64_t initial_value = *field;
do
{
comparand = initial_value;
initial_value = _InterlockedCompareExchange64(field, value, comparand);
}
while (comparand != initial_value);
return initial_value;
#endif
}
static int64_t __inline hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
{
#if defined(_WIN64)
return _InterlockedExchangeAdd64(field, value) + value;
#else
int64_t comparand;
int64_t initial_value = *field;
do
{
comparand = initial_value;
initial_value = _InterlockedCompareExchange64(field, comparand + value, comparand);
}
while (comparand != initial_value);
return initial_value + value;
#endif
}
static bool __inline hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
{
return *expected == _InterlockedCompareExchange64(field, desired, *expected);
}
#elif defined(__ATOMIC_SEQ_CST)
#define hdr_atomic_load_pointer(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define hdr_atomic_store_pointer(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
#define hdr_atomic_load_64(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define hdr_atomic_store_64(f,v) __atomic_store_n(f,v, __ATOMIC_SEQ_CST)
#define hdr_atomic_exchange_64(f,i) __atomic_exchange_n(f,i, __ATOMIC_SEQ_CST)
#define hdr_atomic_add_fetch_64(field, value) __atomic_add_fetch(field, value, __ATOMIC_SEQ_CST)
#define hdr_atomic_compare_exchange_64(field, expected, desired) __atomic_compare_exchange_n(field, expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#elif defined(__x86_64__)
#include <stdint.h>
#include <stdbool.h>
static inline void* hdr_atomic_load_pointer(void** pointer)
{
void* p = *pointer;
asm volatile ("" ::: "memory");
return p;
}
static inline void hdr_atomic_store_pointer(void** pointer, void* value)
{
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*pointer));
}
static inline int64_t hdr_atomic_load_64(int64_t* field)
{
int64_t i = *field;
asm volatile ("" ::: "memory");
return i;
}
static inline void hdr_atomic_store_64(int64_t* field, int64_t value)
{
asm volatile ("lock; xchgq %0, %1" : "+q" (value), "+m" (*field));
}
static inline int64_t hdr_atomic_exchange_64(volatile int64_t* field, int64_t value)
{
int64_t result = 0;
asm volatile ("lock; xchgq %1, %2" : "=r" (result), "+q" (value), "+m" (*field));
return result;
}
static inline int64_t hdr_atomic_add_fetch_64(volatile int64_t* field, int64_t value)
{
return __sync_add_and_fetch(field, value);
}
static inline bool hdr_atomic_compare_exchange_64(volatile int64_t* field, int64_t* expected, int64_t desired)
{
int64_t original;
asm volatile( "lock; cmpxchgq %2, %1" : "=a"(original), "+m"(*field) : "q"(desired), "0"(*expected));
return original == *expected;
}
#else
#error "Unable to determine atomic operations for your platform"
#endif
#endif /* HDR_ATOMIC_H__ */
+1155
View File
File diff suppressed because it is too large Load Diff
+509
View File
@@ -0,0 +1,509 @@
/**
* hdr_histogram.h
* Written by Michael Barker and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*
* The source for the hdr_histogram utilises a few C99 constructs, specifically
* the use of stdint/stdbool and inline variable declaration.
*/
#ifndef HDR_HISTOGRAM_H
#define HDR_HISTOGRAM_H 1
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
struct hdr_histogram
{
int64_t lowest_trackable_value;
int64_t highest_trackable_value;
int32_t unit_magnitude;
int32_t significant_figures;
int32_t sub_bucket_half_count_magnitude;
int32_t sub_bucket_half_count;
int64_t sub_bucket_mask;
int32_t sub_bucket_count;
int32_t bucket_count;
int64_t min_value;
int64_t max_value;
int32_t normalizing_index_offset;
double conversion_ratio;
int32_t counts_len;
int64_t total_count;
int64_t* counts;
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* Allocate the memory and initialise the hdr_histogram.
*
* Due to the size of the histogram being the result of some reasonably
* involved math on the input parameters this function it is tricky to stack allocate.
* The histogram should be released with hdr_close
*
* @param lowest_trackable_value The smallest possible value to be put into the
* histogram.
* @param highest_trackable_value The largest possible value to be put into the
* histogram.
* @param significant_figures The level of precision for this histogram, i.e. the number
* of figures in a decimal number that will be maintained. E.g. a value of 3 will mean
* the results from the histogram will be accurate up to the first three digits. Must
* be a value between 1 and 5 (inclusive).
* @param result Output parameter to capture allocated histogram.
* @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the
* significant_figure value is outside of the allowed range, ENOMEM if malloc
* failed.
*/
int hdr_init(
int64_t lowest_trackable_value,
int64_t highest_trackable_value,
int significant_figures,
struct hdr_histogram** result);
/**
* Free the memory and close the hdr_histogram.
*
* @param h The histogram you want to close.
*/
void hdr_close(struct hdr_histogram* h);
/**
* Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling
* hdr_init(1, highest_trackable_value, significant_figures, result);
*
* @deprecated use hdr_init.
*/
int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result);
/**
* Reset a histogram to zero - empty out a histogram and re-initialise it
*
* If you want to re-use an existing histogram, but reset everything back to zero, this
* is the routine to use.
*
* @param h The histogram you want to reset to empty.
*
*/
void hdr_reset(struct hdr_histogram* h);
/**
* Get the memory size of the hdr_histogram.
*
* @param h "This" pointer
* @return The amount of memory used by the hdr_histogram in bytes
*/
size_t hdr_get_memory_size(struct hdr_histogram* h);
/**
* Records a value in the histogram, will round this value of to a precision at or better
* than the significant_figure specified at construction time.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_value(struct hdr_histogram* h, int64_t value);
/**
* Records a value in the histogram, will round this value of to a precision at or better
* than the significant_figure specified at construction time.
*
* Will record this value atomically, however the whole structure may appear inconsistent
* when read concurrently with this update. Do NOT mix calls to this method with calls
* to non-atomic updates.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_value_atomic(struct hdr_histogram* h, int64_t value);
/**
* Records count values in the histogram, will round this value of to a
* precision at or better than the significant_figure specified at construction
* time.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @param count Number of 'value's to add to the histogram
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count);
/**
* Records count values in the histogram, will round this value of to a
* precision at or better than the significant_figure specified at construction
* time.
*
* Will record this value atomically, however the whole structure may appear inconsistent
* when read concurrently with this update. Do NOT mix calls to this method with calls
* to non-atomic updates.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @param count Number of 'value's to add to the histogram
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count);
/**
* Record a value in the histogram and backfill based on an expected interval.
*
* Records a value in the histogram, will round this value of to a precision at or better
* than the significant_figure specified at contruction time. This is specifically used
* for recording latency. If the value is larger than the expected_interval then the
* latency recording system has experienced co-ordinated omission. This method fills in the
* values that would have occured had the client providing the load not been blocked.
* @param h "This" pointer
* @param value Value to add to the histogram
* @param expected_interval The delay between recording values.
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
/**
* Record a value in the histogram and backfill based on an expected interval.
*
* Records a value in the histogram, will round this value of to a precision at or better
* than the significant_figure specified at contruction time. This is specifically used
* for recording latency. If the value is larger than the expected_interval then the
* latency recording system has experienced co-ordinated omission. This method fills in the
* values that would have occured had the client providing the load not been blocked.
*
* Will record this value atomically, however the whole structure may appear inconsistent
* when read concurrently with this update. Do NOT mix calls to this method with calls
* to non-atomic updates.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @param expected_interval The delay between recording values.
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_corrected_value_atomic(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
/**
* Record a value in the histogram 'count' times. Applies the same correcting logic
* as 'hdr_record_corrected_value'.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @param count Number of 'value's to add to the histogram
* @param expected_interval The delay between recording values.
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
/**
* Record a value in the histogram 'count' times. Applies the same correcting logic
* as 'hdr_record_corrected_value'.
*
* Will record this value atomically, however the whole structure may appear inconsistent
* when read concurrently with this update. Do NOT mix calls to this method with calls
* to non-atomic updates.
*
* @param h "This" pointer
* @param value Value to add to the histogram
* @param count Number of 'value's to add to the histogram
* @param expected_interval The delay between recording values.
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
* true otherwise.
*/
bool hdr_record_corrected_values_atomic(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
/**
* Adds all of the values from 'from' to 'this' histogram. Will return the
* number of values that are dropped when copying. Values will be dropped
* if they around outside of h.lowest_trackable_value and
* h.highest_trackable_value.
*
* @param h "This" pointer
* @param from Histogram to copy values from.
* @return The number of values dropped when copying.
*/
int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from);
/**
* Adds all of the values from 'from' to 'this' histogram. Will return the
* number of values that are dropped when copying. Values will be dropped
* if they around outside of h.lowest_trackable_value and
* h.highest_trackable_value.
*
* @param h "This" pointer
* @param from Histogram to copy values from.
* @return The number of values dropped when copying.
*/
int64_t hdr_add_while_correcting_for_coordinated_omission(
struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval);
/**
* Get minimum value from the histogram. Will return 2^63-1 if the histogram
* is empty.
*
* @param h "This" pointer
*/
int64_t hdr_min(const struct hdr_histogram* h);
/**
* Get maximum value from the histogram. Will return 0 if the histogram
* is empty.
*
* @param h "This" pointer
*/
int64_t hdr_max(const struct hdr_histogram* h);
/**
* Get the value at a specific percentile.
*
* @param h "This" pointer.
* @param percentile The percentile to get the value for
*/
int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile);
/**
* Gets the standard deviation for the values in the histogram.
*
* @param h "This" pointer
* @return The standard deviation
*/
double hdr_stddev(const struct hdr_histogram* h);
/**
* Gets the mean for the values in the histogram.
*
* @param h "This" pointer
* @return The mean
*/
double hdr_mean(const struct hdr_histogram* h);
/**
* Determine if two values are equivalent with the histogram's resolution.
* Where "equivalent" means that value samples recorded for any two
* equivalent values are counted in a common total count.
*
* @param h "This" pointer
* @param a first value to compare
* @param b second value to compare
* @return 'true' if values are equivalent with the histogram's resolution.
*/
bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b);
/**
* Get the lowest value that is equivalent to the given value within the histogram's resolution.
* Where "equivalent" means that value samples recorded for any two
* equivalent values are counted in a common total count.
*
* @param h "This" pointer
* @param value The given value
* @return The lowest value that is equivalent to the given value within the histogram's resolution.
*/
int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value);
/**
* Get the count of recorded values at a specific value
* (to within the histogram resolution at the value level).
*
* @param h "This" pointer
* @param value The value for which to provide the recorded count
* @return The total count of values recorded in the histogram within the value range that is
* {@literal >=} lowestEquivalentValue(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
*/
int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value);
int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index);
int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index);
struct hdr_iter_percentiles
{
bool seen_last_value;
int32_t ticks_per_half_distance;
double percentile_to_iterate_to;
double percentile;
};
struct hdr_iter_recorded
{
int64_t count_added_in_this_iteration_step;
};
struct hdr_iter_linear
{
int64_t value_units_per_bucket;
int64_t count_added_in_this_iteration_step;
int64_t next_value_reporting_level;
int64_t next_value_reporting_level_lowest_equivalent;
};
struct hdr_iter_log
{
double log_base;
int64_t count_added_in_this_iteration_step;
int64_t next_value_reporting_level;
int64_t next_value_reporting_level_lowest_equivalent;
};
/**
* The basic iterator. This is a generic structure
* that supports all of the types of iteration. Use
* the appropriate initialiser to get the desired
* iteration.
*
* @
*/
struct hdr_iter
{
const struct hdr_histogram* h;
/** raw index into the counts array */
int32_t counts_index;
/** snapshot of the length at the time the iterator is created */
int64_t total_count;
/** value directly from array for the current counts_index */
int64_t count;
/** sum of all of the counts up to and including the count at this index */
int64_t cumulative_count;
/** The current value based on counts_index */
int64_t value;
int64_t highest_equivalent_value;
int64_t lowest_equivalent_value;
int64_t median_equivalent_value;
int64_t value_iterated_from;
int64_t value_iterated_to;
union
{
struct hdr_iter_percentiles percentiles;
struct hdr_iter_recorded recorded;
struct hdr_iter_linear linear;
struct hdr_iter_log log;
} specifics;
bool (* _next_fp)(struct hdr_iter* iter);
};
/**
* Initalises the basic iterator.
*
* @param itr 'This' pointer
* @param h The histogram to iterate over
*/
void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h);
/**
* Initialise the iterator for use with percentiles.
*/
void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance);
/**
* Initialise the iterator for use with recorded values.
*/
void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h);
/**
* Initialise the iterator for use with linear values.
*/
void hdr_iter_linear_init(
struct hdr_iter* iter,
const struct hdr_histogram* h,
int64_t value_units_per_bucket);
/**
* Update the iterator value units per bucket
*/
void hdr_iter_linear_set_value_units_per_bucket(struct hdr_iter* iter, int64_t value_units_per_bucket);
/**
* Initialise the iterator for use with logarithmic values
*/
void hdr_iter_log_init(
struct hdr_iter* iter,
const struct hdr_histogram* h,
int64_t value_units_first_bucket,
double log_base);
/**
* Iterate to the next value for the iterator. If there are no more values
* available return faluse.
*
* @param itr 'This' pointer
* @return 'false' if there are no values remaining for this iterator.
*/
bool hdr_iter_next(struct hdr_iter* iter);
typedef enum
{
CLASSIC,
CSV
} format_type;
/**
* Print out a percentile based histogram to the supplied stream. Note that
* this call will not flush the FILE, this is left up to the user.
*
* @param h 'This' pointer
* @param stream The FILE to write the output to
* @param ticks_per_half_distance The number of iteration steps per half-distance to 100%
* @param value_scale Scale the output values by this amount
* @param format_type Format to use, e.g. CSV.
* @return 0 on success, error code on failure. EIO if an error occurs writing
* the output.
*/
int hdr_percentiles_print(
struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
double value_scale, format_type format);
/**
* Internal allocation methods, used by hdr_dbl_histogram.
*/
struct hdr_histogram_bucket_config
{
int64_t lowest_trackable_value;
int64_t highest_trackable_value;
int64_t unit_magnitude;
int64_t significant_figures;
int32_t sub_bucket_half_count_magnitude;
int32_t sub_bucket_half_count;
int64_t sub_bucket_mask;
int32_t sub_bucket_count;
int32_t bucket_count;
int32_t counts_len;
};
int hdr_calculate_bucket_config(
int64_t lowest_trackable_value,
int64_t highest_trackable_value,
int significant_figures,
struct hdr_histogram_bucket_config* cfg);
void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg);
int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value);
int64_t hdr_next_non_equivalent_value(const struct hdr_histogram* h, int64_t value);
int64_t hdr_median_equivalent_value(const struct hdr_histogram* h, int64_t value);
/**
* Used to reset counters after importing data manuallying into the histogram, used by the logging code
* and other custom serialisation tools.
*/
void hdr_reset_internal_counters(struct hdr_histogram* h);
#ifdef __cplusplus
}
#endif
#endif
+1
View File
@@ -6,3 +6,4 @@
/*.a
/*.pc
*.dSYM
tags
+43 -9
View File
@@ -1,5 +1,4 @@
language: c
sudo: false
compiler:
- gcc
- clang
@@ -8,17 +7,34 @@ os:
- linux
- osx
dist: bionic
branches:
only:
- staging
- trying
- master
- /^release\/.*$/
install:
- if [ "$BITS" == "64" ]; then
wget https://github.com/redis/redis/archive/6.0.6.tar.gz;
tar -xzvf 6.0.6.tar.gz;
pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd;
fi
before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg;
sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /;
export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate;
sudo port -N install openssl redis;
fi;
addons:
apt:
sources:
- sourceline: 'ppa:chris-lea/redis-server'
packages:
- libc6-dbg
- libc6-dev
@@ -27,14 +43,20 @@ addons:
- libc6-dbg:i386
- gcc-multilib
- g++-multilib
- libssl-dev
- libssl-dev:i386
- valgrind
- redis
env:
- BITS="32"
- BITS="64"
script:
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON";
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON";
if [ "$BITS" == "64" ]; then
EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON";
fi;
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
if [ "$BITS" == "32" ]; then
CFLAGS="-m32 -Werror";
@@ -58,12 +80,24 @@ script:
fi;
fi;
export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS
- make && make clean;
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
if [ "$BITS" == "64" ]; then
OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make;
fi;
else
USE_SSL=1 make;
fi;
- mkdir build/ && cd build/
- cmake .. ${EXTRA_CMAKE_OPTS}
- make VERBOSE=1
- ctest -V
- if [ "$BITS" == "64" ]; then
TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V;
else
SKIPS_AS_FAILS=1 ctest -V;
fi;
matrix:
jobs:
include:
# Windows MinGW cross compile on Linux
- os: linux
@@ -89,9 +123,9 @@ matrix:
- eval "${MATRIX_EVAL}"
install:
- choco install ninja
- choco install -y memurai-developer
script:
- mkdir build && cd build
- cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 &&
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&
ninja -v'
- ctest -V
- cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&'
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v
- ./hiredis-test.exe
+164 -18
View File
@@ -1,28 +1,175 @@
### 1.0.0 (unreleased)
## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03)
Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada:
_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart:
[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo),
[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites),
[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron),
[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner),
[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang),
[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally),
[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean),
[kevin1018](https://github.com/kevin1018)
[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0)
**BREAKING CHANGES**:
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
protocol errors. This is consistent with the RESP specification. On 32-bit
platforms, the upper bound is lowered to `SIZE_MAX`.
* `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36))
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well. If it was used to
compare to other values, casting might be necessary or can be removed, if
casting was applied before.
### 0.x.x (unreleased)
**BREAKING CHANGES**:
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well.
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent
with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`.
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
**New features:**
- Support for RESP3
[\#697](https://github.com/redis/hiredis/pull/697),
[\#805](https://github.com/redis/hiredis/pull/805),
[\#819](https://github.com/redis/hiredis/pull/819),
[\#841](https://github.com/redis/hiredis/pull/841)
([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder))
- Support for SSL connections
[\#645](https://github.com/redis/hiredis/pull/645),
[\#699](https://github.com/redis/hiredis/pull/699),
[\#702](https://github.com/redis/hiredis/pull/702),
[\#708](https://github.com/redis/hiredis/pull/708),
[\#711](https://github.com/redis/hiredis/pull/711),
[\#821](https://github.com/redis/hiredis/pull/821),
[more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL)
([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo))
- Run-time allocator injection
[\#800](https://github.com/redis/hiredis/pull/800)
([Michael Grunder](https://github.com/michael-grunder))
- Improved Windows support (including MinGW and Windows CI)
[\#652](https://github.com/redis/hiredis/pull/652),
[\#663](https://github.com/redis/hiredis/pull/663)
([Marcus Geelnard](https://www.bitsnbites.eu/author/m/))
- Adds support for distinct connect and command timeouts
[\#839](https://github.com/redis/hiredis/pull/839),
[\#829](https://github.com/redis/hiredis/pull/829)
([Valentino Geron](https://github.com/valentinogeron))
- Add generic pointer and destructor to `redisContext` that users can use for context.
[\#855](https://github.com/redis/hiredis/pull/855)
([Michael Grunder](https://github.com/michael-grunder))
**Closed issues (that involved code changes):**
- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809)
- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron))
- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827)
- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802)
- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842)
- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825)
- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815)
- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813)
- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794)
- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785),
- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795)
- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777)
- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775)
- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769)
- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767)
- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757)
- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748)
- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646)
- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618)
- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545)
- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518)
- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508)
- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506)
- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502)
- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421)
**Merged pull requests:**
- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder))
- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder))
- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder))
- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44))
- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder))
- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder))
- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder))
- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder))
- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner))
- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder))
- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1))
- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder))
- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder))
- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo))
- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder))
- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally))
- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo))
- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder))
- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder))
- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder))
- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder))
- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder))
- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder))
- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder))
- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder))
- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder))
- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder))
- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo))
- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder))
- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr))
- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder))
- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki))
- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT))
- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder))
- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott))
- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi))
- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros))
- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo))
- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder))
- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo))
- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo))
- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo))
- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo))
- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo))
- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo))
- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer))
- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018))
- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung))
- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean))
- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton))
- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost))
- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites))
- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation))
- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang))
- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder))
- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites))
- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg))
- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz))
- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92))
- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen))
- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo))
- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher))
- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst))
- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee))
- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer))
- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam))
- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg))
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29)
_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_
### 0.14.1 (2020-03-13)
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder)
### 0.14.0 (2018-09-25)
**BREAKING CHANGES**:
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well.
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
@@ -196,4 +343,3 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
### 0.10.0
* See commit log.
+84 -9
View File
@@ -3,6 +3,8 @@ INCLUDE(GNUInstallDirs)
PROJECT(hiredis)
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF)
OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF)
MACRO(getVersionBit name)
SET(VERSION_REGEX "^#define ${name} (.+)$")
@@ -22,7 +24,8 @@ PROJECT(hiredis VERSION "${VERSION}")
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
ADD_LIBRARY(hiredis SHARED
SET(hiredis_sources
alloc.c
async.c
dict.c
hiredis.c
@@ -31,20 +34,32 @@ ADD_LIBRARY(hiredis SHARED
sds.c
sockcompat.c)
SET(hiredis_sources ${hiredis_sources})
IF(WIN32)
ADD_COMPILE_DEFINITIONS(_CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN)
ENDIF()
ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
SET_TARGET_PROPERTIES(hiredis
PROPERTIES
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}")
IF(WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
ENDIF()
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
INSTALL(TARGETS hiredis
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
EXPORT hiredis-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES hiredis.h read.h sds.h async.h
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(DIRECTORY adapters
@@ -53,6 +68,26 @@ INSTALL(DIRECTORY adapters
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
export(EXPORT hiredis-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis-targets.cmake"
NAMESPACE hiredis::)
SET(CMAKE_CONF_INSTALL_DIR share/hiredis)
SET(INCLUDE_INSTALL_DIR include)
include(CMakePackageConfigHelpers)
configure_package_config_file(hiredis-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
PATH_VARS INCLUDE_INSTALL_DIR)
INSTALL(EXPORT hiredis-targets
FILE hiredis-targets.cmake
NAMESPACE hiredis::
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis-config.cmake
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
IF(ENABLE_SSL)
IF (NOT OPENSSL_ROOT_DIR)
IF (APPLE)
@@ -60,26 +95,66 @@ IF(ENABLE_SSL)
ENDIF()
ENDIF()
FIND_PACKAGE(OpenSSL REQUIRED)
ADD_LIBRARY(hiredis_ssl SHARED
SET(hiredis_ssl_sources
ssl.c)
ADD_LIBRARY(hiredis_ssl SHARED
${hiredis_ssl_sources})
IF (APPLE)
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
ENDIF()
SET_TARGET_PROPERTIES(hiredis_ssl
PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}")
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
IF (WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
ENDIF()
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
INSTALL(TARGETS hiredis_ssl
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
EXPORT hiredis_ssl-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES hiredis_ssl.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
export(EXPORT hiredis_ssl-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-targets.cmake"
NAMESPACE hiredis::)
SET(CMAKE_CONF_INSTALL_DIR share/hiredis_ssl)
configure_package_config_file(hiredis_ssl-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
INSTALL_DESTINATION ${CMAKE_CONF_INSTALL_DIR}
PATH_VARS INCLUDE_INSTALL_DIR)
INSTALL(EXPORT hiredis_ssl-targets
FILE hiredis_ssl-targets.cmake
NAMESPACE hiredis::
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl-config.cmake
DESTINATION ${CMAKE_CONF_INSTALL_DIR})
ENDIF()
IF(NOT (WIN32 OR MINGW))
IF(NOT DISABLE_TESTS)
ENABLE_TESTING()
ADD_EXECUTABLE(hiredis-test test.c)
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
IF(ENABLE_SSL_TESTS)
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl)
ELSE()
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
ENDIF()
ADD_TEST(NAME hiredis-test
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
ENDIF()
+53 -18
View File
@@ -3,16 +3,16 @@
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file
OBJ=net.o hiredis.o sds.o async.o read.o sockcompat.o
OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o
SSL_OBJ=ssl.o
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push
ifeq ($(USE_SSL),1)
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
endif
TESTS=hiredis-test
LIBNAME=libhiredis
SSL_LIBNAME=libhiredis_ssl
PKGCONFNAME=hiredis.pc
SSL_LIBNAME=libhiredis_ssl
SSL_PKGCONFNAME=hiredis_ssl.pc
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
@@ -55,12 +55,17 @@ STLIBSUFFIX=a
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=$(AR) rcs
SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME)
# Platform-specific overrides
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
@@ -80,13 +85,22 @@ else
endif
ifeq ($(uname_S),SunOS)
IS_SUN_CC=$(shell sh -c '$(CC) -V 2>&1 |egrep -i -c "sun|studio"')
ifeq ($(IS_SUN_CC),1)
SUN_SHARED_FLAG=-G
else
SUN_SHARED_FLAG=-shared
endif
REAL_LDFLAGS+= -ldl -lnsl -lsocket
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
SSL_DYLIB_MAKE_CMD=$(CC) $(SUN_SHARED_FLAG) -o $(SSL_DYLIBNAME) -h $(SSL_DYLIB_MINOR_NAME) $(LDFLAGS) $(SSL_LDFLAGS)
endif
ifeq ($(uname_S),Darwin)
DYLIBSUFFIX=dylib
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
SSL_DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME) -o $(SSL_DYLIBNAME) $(LDFLAGS) $(SSL_LDFLAGS)
DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup
endif
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
@@ -95,15 +109,16 @@ all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
endif
# Deps (use make dep to generate this)
async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
dict.o: dict.c fmacros.h dict.h
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h
read.o: read.c fmacros.h read.h sds.h
sds.o: sds.c sds.h
alloc.o: alloc.c fmacros.h alloc.h
async.o: async.c fmacros.h alloc.h async.h hiredis.h read.h sds.h net.h dict.c dict.h win32.h async_private.h
dict.o: dict.c fmacros.h alloc.h dict.h
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h alloc.h net.h async.h win32.h
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h
read.o: read.c fmacros.h alloc.h read.h sds.h win32.h
sds.o: sds.c sds.h sdsalloc.h alloc.h
sockcompat.o: sockcompat.c sockcompat.h
ssl.o: ssl.c hiredis.h
test.o: test.c fmacros.h hiredis.h read.h sds.h
ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h
test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h
$(DYLIBNAME): $(OBJ)
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
@@ -112,7 +127,7 @@ $(STLIBNAME): $(OBJ)
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
$(SSL_DYLIBNAME): $(SSL_OBJ)
$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
$(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS)
$(SSL_STLIBNAME): $(SSL_OBJ)
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
@@ -146,6 +161,7 @@ hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@@ -180,13 +196,19 @@ endif
hiredis-example: examples/example.c $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
hiredis-example-push: examples/example-push.c $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
examples: $(EXAMPLES)
TEST_LIBS = $(STLIBNAME)
ifeq ($(USE_SSL),1)
TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread
TEST_LIBS += $(SSL_STLIBNAME)
TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread
endif
hiredis-test: test.o $(TEST_LIBS)
$(CC) -o $@ $(REAL_CFLAGS) -I. $^ $(REAL_LDFLAGS) $(TEST_LDFLAGS)
hiredis-%: %.o $(STLIBNAME)
$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)
@@ -221,7 +243,7 @@ $(PKGCONFNAME): hiredis.h
@echo Libs: -L\$${libdir} -lhiredis >> $@
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
$(SSL_PKGCONFNAME): hiredis.h
$(SSL_PKGCONFNAME): hiredis_ssl.h
@echo "Generating $@ for pkgconfig..."
@echo prefix=$(PREFIX) > $@
@echo exec_prefix=\$${prefix} >> $@
@@ -237,7 +259,7 @@ $(SSL_PKGCONFNAME): hiredis.h
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
@@ -245,6 +267,19 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
mkdir -p $(INSTALL_PKGCONF_PATH)
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
ifeq ($(USE_SSL),1)
install: install-ssl
install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) $(SSL_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(SSL_DYLIB_MINOR_NAME)
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(SSL_DYLIB_MINOR_NAME) $(SSL_DYLIBNAME)
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
mkdir -p $(INSTALL_PKGCONF_PATH)
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
endif
32bit:
@echo ""
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
+259 -13
View File
@@ -1,6 +1,6 @@
[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**
**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).**
# HIREDIS
@@ -24,12 +24,31 @@ The library comes with multiple APIs. There is the
## Upgrading to `1.0.0`
Version 1.0.0 marks a stable release of hiredis.
Version 1.0.0 marks the first stable release of Hiredis.
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
It also bundles the updated `sds` library, to sync up with upstream and Redis.
For most applications a recompile against the new hiredis should be enough.
For code changes see the [Changelog](CHANGELOG.md).
_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._
## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0`
* `redisContext` has two additional members (`free_privdata`, and `privctx`).
* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`.
* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter.
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
protocol errors. This is consistent with the RESP specification. On 32-bit
platforms, the upper bound is lowered to `SIZE_MAX`.
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well. If it was used to
compare to other values, casting might be necessary or can be removed, if
casting was applied before.
## Upgrading from `<0.9.0`
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
@@ -110,6 +129,8 @@ The standard replies that `redisCommand` are of the type `redisReply`. The
`type` field in the `redisReply` should be used to test what kind of reply
was received:
### RESP2
* **`REDIS_REPLY_STATUS`**:
* The command replied with a status reply. The status string can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`.
@@ -134,16 +155,51 @@ was received:
and can be accessed via `reply->element[..index..]`.
Redis may reply with nested arrays but this is fully supported.
### RESP3
Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md)
* **`REDIS_REPLY_DOUBLE`**:
* The command replied with a double-precision floating point number.
The value is stored as a string in the `str` member, and can be converted with `strtod` or similar.
* **`REDIS_REPLY_BOOL`**:
* A boolean true/false reply.
The value is stored in the `integer` member and will be either `0` or `1`.
* **`REDIS_REPLY_MAP`**:
* An array with the added invariant that there will always be an even number of elements.
The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant.
* **`REDIS_REPLY_SET`**:
* An array response where each entry is unique.
Like the MAP type, the data is identical to an array response except there are no duplicate values.
* **`REDIS_REPLY_PUSH`**:
* An array that can be generated spontaneously by Redis.
This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself.
* **`REDIS_REPLY_ATTR`**:
* An array structurally identical to a `MAP` but intended as meta-data about a reply.
_As of Redis 6.0.6 this reply type is not used in Redis_
* **`REDIS_REPLY_BIGNUM`**:
* A string representing an arbitrarily large signed or unsigned integer value.
The number will be encoded as a string in the `str` member of `redisReply`.
* **`REDIS_REPLY_VERB`**:
* A verbatim string, intended to be presented to the user without modification.
The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown).
Replies should be freed using the `freeReplyObject()` function.
Note that this function will take care of freeing sub-reply objects
contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory).
**Important:** the current version of hiredis (0.10.0) frees replies when the
**Important:** the current version of hiredis (1.0.0) frees replies when the
asynchronous API is used. This means you should not call `freeReplyObject` when
you use this API. The reply is cleaned up by hiredis _after_ the callback
returns. This behavior will probably change in future releases, so make sure to
keep an eye on the changelog when upgrading (see issue #39).
returns. We may introduce a flag to make this configurable in future versions of the library.
### Cleaning up
@@ -205,16 +261,16 @@ a single call to `read(2)`):
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
redisGetReply(context,(void *)&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET
redisGetReply(context,(void *)&reply); // reply for GET
freeReplyObject(reply);
```
This API can also be used to implement a blocking subscriber:
```c
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {
// consume message
freeReplyObject(reply);
}
@@ -404,9 +460,199 @@ This should be done only in order to maximize performances when working with
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
as soon as possible in order to prevent allocation of useless memory.
### Reader max array elements
By default the hiredis reply parser sets the maximum number of multi-bulk elements
to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies
with more than this many elements you can set the value higher or to zero, meaning
unlimited with:
```c
context->reader->maxelements = 0;
```
## SSL/TLS Support
### Building
SSL/TLS support is not built by default and requires an explicit flag:
make USE_SSL=1
This requires OpenSSL development package (e.g. including header files to be
available.
When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and
`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries
unaffected so no additional dependencies are introduced.
### Using it
First, you'll need to make sure you include the SSL header file:
```c
#include "hiredis.h"
#include "hiredis_ssl.h"
```
You will also need to link against `libhiredis_ssl`, **in addition** to
`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
Hiredis implements SSL/TLS on top of its normal `redisContext` or
`redisAsyncContext`, so you will need to establish a connection first and then
initiate an SSL/TLS handshake.
#### Hiredis OpenSSL Wrappers
Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
initialize OpenSSL and create a context. You can do that in two ways:
1. Work directly with the OpenSSL API to initialize the library's global context
and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can
call `redisInitiateSSL()`.
2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a
`redisSSLContext` object to hold configuration and use
`redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake.
```c
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
* many contexts.
*/
redisSSLContext *ssl;
/* An error variable to indicate what went wrong, if the context fails to
* initialize.
*/
redisSSLContextError ssl_error;
/* Initialize global OpenSSL state.
*
* You should call this only once when your app initializes, and only if
* you don't explicitly or implicitly initialize OpenSSL it elsewhere.
*/
redisInitOpenSSL();
/* Create SSL context */
ssl = redisCreateSSLContext(
"cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
"/path/to/certs", /* Path of trusted certificates, optional */
"client_cert.pem", /* File name of client certificate file, optional */
"client_key.pem", /* File name of client private key, optional */
"redis.mydomain.com", /* Server name to request (SNI), optional */
&ssl_error
) != REDIS_OK) {
printf("SSL error: %s\n", redisSSLContextGetError(ssl_error);
/* Abort... */
}
/* Create Redis context and establish connection */
c = redisConnect("localhost", 6443);
if (c == NULL || c->err) {
/* Handle error and abort... */
}
/* Negotiate SSL/TLS */
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
/* Handle error, in c->err / c->errstr */
}
```
## RESP3 PUSH replies
Redis 6.0 introduced PUSH replies with the reply-type `>`. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks.
### Default behavior
Hiredis installs handlers on `redisContext` and `redisAsyncContext` by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to `RESP3`.
### Custom PUSH handler prototypes
The callback prototypes differ between `redisContext` and `redisAsyncContext`.
#### redisContext
```c
void my_push_handler(void *privdata, void *reply) {
/* Handle the reply */
/* Note: We need to free the reply in our custom handler for
blocking contexts. This lets us keep the reply if
we want. */
freeReplyObject(reply);
}
```
#### redisAsyncContext
```c
void my_async_push_handler(redisAsyncContext *ac, void *reply) {
/* Handle the reply */
/* Note: Because async hiredis always frees replies, you should
not call freeReplyObject in an async push callback. */
}
```
### Installing a custom handler
There are two ways to set your own PUSH handlers.
1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`.
```c
redisOptions = {0};
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
options->push_cb = my_push_handler;
redisContext *context = redisConnectWithOptions(&options);
```
2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context.
```c
redisContext *context = redisConnect("127.0.0.1", 6379);
redisSetPushCallback(context, my_push_handler);
```
_Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._
### Specifying no handler
If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways.
1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`.
```c
redisOptions = {0};
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
options->options |= REDIS_OPT_NO_PUSH_AUTOFREE;
redisContext *context = redisConnectWithOptions(&options);
```
3. Call `redisSetPushCallback` with `NULL` once connected.
```c
redisContext *context = redisConnect("127.0.0.1", 6379);
redisSetPushCallback(context, NULL);
```
_Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._
## Allocator injection
Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc).
### Overriding
One can override the allocators like so:
```c
hiredisAllocFuncs myfuncs = {
.mallocFn = my_malloc,
.callocFn = my_calloc,
.reallocFn = my_realloc,
.strdupFn = my_strdup,
.freeFn = my_free,
};
// Override allocators (function returns current allocators if needed)
hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs);
```
To reset the allocators to their default libc function simply call:
```c
hiredisResetAllocators();
```
## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
Jan-Erik Rediger (janerik at fnordig dot com)
Salvatore Sanfilippo (antirez at gmail),\
Pieter Noordhuis (pcnoordhuis at gmail)\
Michael Grunder (michael dot grunder at gmail)
_Hiredis is released under the BSD license._
+5 -2
View File
@@ -96,7 +96,7 @@ static void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
hi_free(e);
}
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
@@ -108,7 +108,10 @@ static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisAeEvents*)malloc(sizeof(*e));
e = (redisAeEvents*)hi_malloc(sizeof(*e));
if (e == NULL)
return REDIS_ERR;
e->context = ac;
e->loop = loop;
e->fd = c->fd;
+3
View File
@@ -134,6 +134,9 @@ redis_source_new (redisAsyncContext *ac)
g_return_val_if_fail(ac != NULL, NULL);
source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
if (source == NULL)
return NULL;
source->ac = ac;
source->poll_fd.fd = c->fd;
source->poll_fd.events = 0;
+5 -2
View File
@@ -43,7 +43,7 @@ static void redisIvykisCleanup(void *privdata) {
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
iv_fd_unregister(&e->fd);
free(e);
hi_free(e);
}
static int redisIvykisAttach(redisAsyncContext *ac) {
@@ -55,7 +55,10 @@ static int redisIvykisAttach(redisAsyncContext *ac) {
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisIvykisEvents*)malloc(sizeof(*e));
e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
if (e == NULL)
return REDIS_ERR;
e->context = ac;
/* Register functions to start/stop listening for events */
+35 -3
View File
@@ -41,6 +41,7 @@ typedef struct redisLibevEvents {
struct ev_loop *loop;
int reading, writing;
ev_io rev, wev;
ev_timer timer;
} redisLibevEvents;
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
@@ -103,11 +104,39 @@ static void redisLibevDelWrite(void *privdata) {
}
}
static void redisLibevStopTimer(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
ev_timer_stop(EV_A_ &e->timer);
}
static void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata);
redisLibevDelWrite(privdata);
free(e);
redisLibevStopTimer(privdata);
hi_free(e);
}
static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)timer->data;
redisAsyncHandleTimeout(e->context);
}
static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!ev_is_active(&e->timer)) {
ev_init(&e->timer, redisLibevTimeout);
e->timer.data = e;
}
e->timer.repeat = tv.tv_sec + tv.tv_usec / 1000000.00;
ev_timer_again(EV_A_ &e->timer);
}
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
@@ -119,14 +148,16 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibevEvents*)malloc(sizeof(*e));
e = (redisLibevEvents*)hi_calloc(1, sizeof(*e));
if (e == NULL)
return REDIS_ERR;
e->context = ac;
#if EV_MULTIPLICITY
e->loop = loop;
#else
e->loop = NULL;
#endif
e->reading = e->writing = 0;
e->rev.data = e;
e->wev.data = e;
@@ -136,6 +167,7 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
ac->ev.addWrite = redisLibevAddWrite;
ac->ev.delWrite = redisLibevDelWrite;
ac->ev.cleanup = redisLibevCleanup;
ac->ev.scheduleTimer = redisLibevSetTimeout;
ac->ev.data = e;
/* Initialize read/write events */
+5 -2
View File
@@ -47,7 +47,7 @@ typedef struct redisLibeventEvents {
} redisLibeventEvents;
static void redisLibeventDestroy(redisLibeventEvents *e) {
free(e);
hi_free(e);
}
static void redisLibeventHandler(int fd, short event, void *arg) {
@@ -152,7 +152,10 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibeventEvents*)calloc(1, sizeof(*e));
e = (redisLibeventEvents*)hi_calloc(1, sizeof(*e));
if (e == NULL)
return REDIS_ERR;
e->context = ac;
/* Register functions to start/stop listening for events */
+5 -7
View File
@@ -73,7 +73,7 @@ static void redisLibuvDelWrite(void *privdata) {
static void on_close(uv_handle_t* handle) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
free(p);
hi_free(p);
}
@@ -98,15 +98,13 @@ static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
ac->ev.delWrite = redisLibuvDelWrite;
ac->ev.cleanup = redisLibuvCleanup;
redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
if (!p) {
return REDIS_ERR;
}
redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
if (p == NULL)
return REDIS_ERR;
memset(p, 0, sizeof(*p));
if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
return REDIS_ERR;
}
+4 -3
View File
@@ -27,7 +27,7 @@ static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
CFSocketInvalidate(redisRunLoop->socketRef);
CFRelease(redisRunLoop->socketRef);
}
free(redisRunLoop);
hi_free(redisRunLoop);
}
return REDIS_ERR;
}
@@ -80,8 +80,9 @@ static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLo
/* Nothing should be attached when something is already attached */
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
if( !redisRunLoop ) return REDIS_ERR;
RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
if (redisRunLoop == NULL)
return REDIS_ERR;
/* Setup redis stuff */
redisRunLoop->context = redisAsyncCtx;
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include "alloc.h"
#include <string.h>
#include <stdlib.h>
hiredisAllocFuncs hiredisAllocFns = {
.mallocFn = malloc,
.callocFn = calloc,
.reallocFn = realloc,
.strdupFn = strdup,
.freeFn = free,
};
/* Override hiredis' allocators with ones supplied by the user */
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) {
hiredisAllocFuncs orig = hiredisAllocFns;
hiredisAllocFns = *override;
return orig;
}
/* Reset allocators to use libc defaults */
void hiredisResetAllocators(void) {
hiredisAllocFns = (hiredisAllocFuncs) {
.mallocFn = malloc,
.callocFn = calloc,
.reallocFn = realloc,
.strdupFn = strdup,
.freeFn = free,
};
}
#ifdef _WIN32
void *hi_malloc(size_t size) {
return hiredisAllocFns.mallocFn(size);
}
void *hi_calloc(size_t nmemb, size_t size) {
return hiredisAllocFns.callocFn(nmemb, size);
}
void *hi_realloc(void *ptr, size_t size) {
return hiredisAllocFns.reallocFn(ptr, size);
}
char *hi_strdup(const char *str) {
return hiredisAllocFns.strdupFn(str);
}
void hi_free(void *ptr) {
hiredisAllocFns.freeFn(ptr);
}
#endif
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HIREDIS_ALLOC_H
#define HIREDIS_ALLOC_H
#include <stddef.h> /* for size_t */
#ifdef __cplusplus
extern "C" {
#endif
/* Structure pointing to our actually configured allocators */
typedef struct hiredisAllocFuncs {
void *(*mallocFn)(size_t);
void *(*callocFn)(size_t,size_t);
void *(*reallocFn)(void*,size_t);
char *(*strdupFn)(const char*);
void (*freeFn)(void*);
} hiredisAllocFuncs;
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
void hiredisResetAllocators(void);
#ifndef _WIN32
/* Hiredis' configured allocator function pointer struct */
extern hiredisAllocFuncs hiredisAllocFns;
static inline void *hi_malloc(size_t size) {
return hiredisAllocFns.mallocFn(size);
}
static inline void *hi_calloc(size_t nmemb, size_t size) {
return hiredisAllocFns.callocFn(nmemb, size);
}
static inline void *hi_realloc(void *ptr, size_t size) {
return hiredisAllocFns.reallocFn(ptr, size);
}
static inline char *hi_strdup(const char *str) {
return hiredisAllocFns.strdupFn(str);
}
static inline void hi_free(void *ptr) {
hiredisAllocFns.freeFn(ptr);
}
#else
void *hi_malloc(size_t size);
void *hi_calloc(size_t nmemb, size_t size);
void *hi_realloc(void *ptr, size_t size);
char *hi_strdup(const char *str);
void hi_free(void *ptr);
#endif
#ifdef __cplusplus
}
#endif
#endif /* HIREDIS_ALLOC_H */
+169 -48
View File
@@ -30,6 +30,7 @@
*/
#include "fmacros.h"
#include "alloc.h"
#include <stdlib.h>
#include <string.h>
#ifndef _MSC_VER
@@ -46,18 +47,24 @@
#include "async_private.h"
/* Forward declaration of function in hiredis.c */
/* Forward declarations of hiredis.c functions */
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
void __redisSetError(redisContext *c, int type, const char *str);
/* Functions managing dictionary of callbacks for pub/sub. */
static unsigned int callbackHash(const void *key) {
return dictGenHashFunction((const unsigned char *)key,
sdslen((const sds)key));
hi_sdslen((const hisds)key));
}
static void *callbackValDup(void *privdata, const void *src) {
((void) privdata);
redisCallback *dup = malloc(sizeof(*dup));
redisCallback *dup;
dup = hi_malloc(sizeof(*dup));
if (dup == NULL)
return NULL;
memcpy(dup,src,sizeof(*dup));
return dup;
}
@@ -66,20 +73,20 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
int l1, l2;
((void) privdata);
l1 = sdslen((const sds)key1);
l2 = sdslen((const sds)key2);
l1 = hi_sdslen((const hisds)key1);
l2 = hi_sdslen((const hisds)key2);
if (l1 != l2) return 0;
return memcmp(key1,key2,l1) == 0;
}
static void callbackKeyDestructor(void *privdata, void *key) {
((void) privdata);
sdsfree((sds)key);
hi_sdsfree((hisds)key);
}
static void callbackValDestructor(void *privdata, void *val) {
((void) privdata);
free(val);
hi_free(val);
}
static dictType callbackDict = {
@@ -93,10 +100,19 @@ static dictType callbackDict = {
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
redisAsyncContext *ac;
dict *channels = NULL, *patterns = NULL;
ac = realloc(c,sizeof(redisAsyncContext));
channels = dictCreate(&callbackDict,NULL);
if (channels == NULL)
goto oom;
patterns = dictCreate(&callbackDict,NULL);
if (patterns == NULL)
goto oom;
ac = hi_realloc(c,sizeof(redisAsyncContext));
if (ac == NULL)
return NULL;
goto oom;
c = &(ac->c);
@@ -108,6 +124,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
ac->err = 0;
ac->errstr = NULL;
ac->data = NULL;
ac->dataCleanup = NULL;
ac->ev.data = NULL;
ac->ev.addRead = NULL;
@@ -124,9 +141,14 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
ac->replies.tail = NULL;
ac->sub.invalid.head = NULL;
ac->sub.invalid.tail = NULL;
ac->sub.channels = dictCreate(&callbackDict,NULL);
ac->sub.patterns = dictCreate(&callbackDict,NULL);
ac->sub.channels = channels;
ac->sub.patterns = patterns;
return ac;
oom:
if (channels) dictRelease(channels);
if (patterns) dictRelease(patterns);
return NULL;
}
/* We want the error field to be accessible directly instead of requiring
@@ -145,16 +167,26 @@ redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
redisContext *c;
redisAsyncContext *ac;
/* Clear any erroneously set sync callback and flag that we don't want to
* use freeReplyObject by default. */
myOptions.push_cb = NULL;
myOptions.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
myOptions.options |= REDIS_OPT_NONBLOCK;
c = redisConnectWithOptions(&myOptions);
if (c == NULL) {
return NULL;
}
ac = redisAsyncInitialize(c);
if (ac == NULL) {
redisFree(c);
return NULL;
}
/* Set any configured async push handler */
redisAsyncSetPushCallback(ac, myOptions.async_push_cb);
__redisAsyncCopyError(ac);
return ac;
}
@@ -214,7 +246,7 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
redisCallback *cb;
/* Copy callback from stack to heap */
cb = malloc(sizeof(*cb));
cb = hi_malloc(sizeof(*cb));
if (cb == NULL)
return REDIS_ERR_OOM;
@@ -242,7 +274,7 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
/* Copy callback from heap to stack */
if (target != NULL)
memcpy(target,cb,sizeof(*cb));
free(cb);
hi_free(cb);
return REDIS_OK;
}
return REDIS_ERR;
@@ -257,6 +289,14 @@ static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisRe
}
}
static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
if (ac->push_cb != NULL) {
ac->c.flags |= REDIS_IN_CALLBACK;
ac->push_cb(ac, reply);
ac->c.flags &= ~REDIS_IN_CALLBACK;
}
}
/* Helper function to free the context. */
static void __redisAsyncFree(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
@@ -272,18 +312,28 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
__redisRunCallback(ac,&cb,NULL);
/* Run subscription callbacks callbacks with NULL reply */
it = dictGetIterator(ac->sub.channels);
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
dictRelease(ac->sub.channels);
/* Run subscription callbacks with NULL reply */
if (ac->sub.channels) {
it = dictGetIterator(ac->sub.channels);
if (it != NULL) {
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
}
it = dictGetIterator(ac->sub.patterns);
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
dictRelease(ac->sub.patterns);
dictRelease(ac->sub.channels);
}
if (ac->sub.patterns) {
it = dictGetIterator(ac->sub.patterns);
if (it != NULL) {
while ((de = dictNext(it)) != NULL)
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
}
dictRelease(ac->sub.patterns);
}
/* Signal event lib to clean up */
_EL_CLEANUP(ac);
@@ -298,6 +348,10 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
}
}
if (ac->dataCleanup) {
ac->dataCleanup(ac->data);
}
/* Cleanup self */
redisFree(c);
}
@@ -364,11 +418,11 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
dictEntry *de;
int pvariant;
char *stype;
sds sname;
hisds sname;
/* Custom reply functions are not supported for pub/sub. This will fail
* very hard when they are used... */
if (reply->type == REDIS_REPLY_ARRAY) {
if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) {
assert(reply->elements >= 2);
assert(reply->element[0]->type == REDIS_REPLY_STRING);
stype = reply->element[0]->str;
@@ -381,7 +435,10 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
/* Locate the right callback */
assert(reply->element[1]->type == REDIS_REPLY_STRING);
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len);
if (sname == NULL)
goto oom;
de = dictFind(callbacks,sname);
if (de != NULL) {
cb = dictGetEntryVal(de);
@@ -409,12 +466,39 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
c->flags &= ~REDIS_SUBSCRIBED;
}
}
sdsfree(sname);
hi_sdsfree(sname);
} else {
/* Shift callback for invalid commands. */
__redisShiftCallback(&ac->sub.invalid,dstcb);
}
return REDIS_OK;
oom:
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
#define redisIsSpontaneousPushReply(r) \
(redisIsPushReply(r) && !redisIsSubscribeReply(r))
static int redisIsSubscribeReply(redisReply *reply) {
char *str;
size_t len, off;
/* We will always have at least one string with the subscribe/message type */
if (reply->elements < 1 || reply->element[0]->type != REDIS_REPLY_STRING ||
reply->element[0]->len < sizeof("message") - 1)
{
return 0;
}
/* Get the string/len moving past 'p' if needed */
off = tolower(reply->element[0]->str[0]) == 'p';
str = reply->element[0]->str + off;
len = reply->element[0]->len - off;
return !strncasecmp(str, "subscribe", len) ||
!strncasecmp(str, "message", len);
}
void redisProcessCallbacks(redisAsyncContext *ac) {
@@ -427,7 +511,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
if (reply == NULL) {
/* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
if (c->flags & REDIS_DISCONNECTING && hi_sdslen(c->obuf) == 0
&& ac->replies.head == NULL) {
__redisAsyncDisconnect(ac);
return;
@@ -443,8 +527,18 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
break;
}
/* Even if the context is subscribed, pending regular callbacks will
* get a reply before pub/sub messages arrive. */
/* Send any non-subscribe related PUSH messages to our PUSH handler
* while allowing subscribe related PUSH messages to pass through.
* This allows existing code to be backward compatible and work in
* either RESP2 or RESP3 mode. */
if (redisIsSpontaneousPushReply(reply)) {
__redisRunPushCallback(ac, reply);
c->reader->fn->freeObject(reply);
continue;
}
/* Even if the context is subscribed, pending regular
* callbacks will get a reply before pub/sub messages arrive. */
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
/*
* A spontaneous reply in a not-subscribed context can be the error
@@ -497,20 +591,31 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac);
}
static void __redisAsyncHandleConnectFailure(redisAsyncContext *ac) {
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
__redisAsyncDisconnect(ac);
}
/* Internal helper function to detect socket status the first time a read or
* write event fires. When connecting was not successful, the connect callback
* is called with a REDIS_ERR status and the context is free'd. */
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
int completed = 0;
redisContext *c = &(ac->c);
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
/* Error! */
redisCheckSocketError(c);
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
__redisAsyncDisconnect(ac);
__redisAsyncHandleConnectFailure(ac);
return REDIS_ERR;
} else if (completed == 1) {
/* connected! */
if (c->connection_type == REDIS_CONN_TCP &&
redisSetTcpNoDelay(c) == REDIS_ERR) {
__redisAsyncHandleConnectFailure(ac);
return REDIS_ERR;
}
if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
@@ -582,8 +687,6 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
c->funcs->async_write(ac);
}
void __redisSetError(redisContext *c, int type, const char *str);
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
@@ -641,7 +744,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
const char *cstr, *astr;
size_t clen, alen;
const char *p;
sds sname;
hisds sname;
int ret;
/* Don't accept new commands when the connection is about to be closed. */
@@ -665,7 +768,10 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
/* Add every channel/pattern to the list of subscription callbacks. */
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
sname = sdsnewlen(astr,alen);
sname = hi_sdsnewlen(astr,alen);
if (sname == NULL)
goto oom;
if (pvariant)
cbdict = ac->sub.patterns;
else
@@ -680,7 +786,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
ret = dictReplace(cbdict,sname,&cb);
if (ret == 0) sdsfree(sname);
if (ret == 0) hi_sdsfree(sname);
}
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
/* It is only useful to call (P)UNSUBSCRIBE when the context is
@@ -709,6 +815,9 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
_EL_ADD_WRITE(ac);
return REDIS_OK;
oom:
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
@@ -722,7 +831,7 @@ int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdat
return REDIS_ERR;
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
free(cmd);
hi_free(cmd);
return status;
}
@@ -736,14 +845,14 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata
}
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
sds cmd;
hisds cmd;
int len;
int status;
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
if (len < 0)
return REDIS_ERR;
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
sdsfree(cmd);
hi_sdsfree(cmd);
return status;
}
@@ -752,15 +861,27 @@ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
return status;
}
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
if (!ac->c.timeout) {
ac->c.timeout = calloc(1, sizeof(tv));
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn) {
redisAsyncPushFn *old = ac->push_cb;
ac->push_cb = fn;
return old;
}
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
if (!ac->c.command_timeout) {
ac->c.command_timeout = hi_calloc(1, sizeof(tv));
if (ac->c.command_timeout == NULL) {
__redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory");
__redisAsyncCopyError(ac);
return REDIS_ERR;
}
}
if (tv.tv_sec == ac->c.timeout->tv_sec &&
tv.tv_usec == ac->c.timeout->tv_usec) {
return;
if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
tv.tv_usec != ac->c.command_timeout->tv_usec)
{
*ac->c.command_timeout = tv;
}
*ac->c.timeout = tv;
return REDIS_OK;
}
+6 -1
View File
@@ -70,6 +70,7 @@ typedef struct redisAsyncContext {
/* Not used by hiredis */
void *data;
void (*dataCleanup)(void *privdata);
/* Event library data and hooks */
struct {
@@ -105,6 +106,9 @@ typedef struct redisAsyncContext {
struct dict *channels;
struct dict *patterns;
} sub;
/* Any configured RESP3 PUSH handler */
redisAsyncPushFn *push_cb;
} redisAsyncContext;
/* Functions that proxy to hiredis */
@@ -117,7 +121,8 @@ redisAsyncContext *redisAsyncConnectUnix(const char *path);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPushFn *fn);
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
void redisAsyncDisconnect(redisAsyncContext *ac);
void redisAsyncFree(redisAsyncContext *ac);
+13 -10
View File
@@ -51,18 +51,21 @@
#define _EL_CLEANUP(ctx) do { \
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
ctx->ev.cleanup = NULL; \
} while(0);
} while(0)
static inline void refreshTimeout(redisAsyncContext *ctx) {
if (ctx->c.timeout && ctx->ev.scheduleTimer &&
(ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {
ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);
// } else {
// printf("Not scheduling timer.. (tmo=%p)\n", ctx->c.timeout);
// if (ctx->c.timeout){
// printf("tv_sec: %u. tv_usec: %u\n", ctx->c.timeout->tv_sec,
// ctx->c.timeout->tv_usec);
// }
#define REDIS_TIMER_ISSET(tvp) \
(tvp && ((tvp)->tv_sec || (tvp)->tv_usec))
#define REDIS_EL_TIMER(ac, tvp) \
if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \
(ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \
}
if (ctx->c.flags & REDIS_CONNECTED) {
REDIS_EL_TIMER(ctx, ctx->c.command_timeout);
} else {
REDIS_EL_TIMER(ctx, ctx->c.connect_timeout);
}
}
+25 -11
View File
@@ -34,6 +34,7 @@
*/
#include "fmacros.h"
#include "alloc.h"
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
@@ -71,7 +72,10 @@ static void _dictReset(dict *ht) {
/* Create a new hash table */
static dict *dictCreate(dictType *type, void *privDataPtr) {
dict *ht = malloc(sizeof(*ht));
dict *ht = hi_malloc(sizeof(*ht));
if (ht == NULL)
return NULL;
_dictInit(ht,type,privDataPtr);
return ht;
}
@@ -97,7 +101,9 @@ static int dictExpand(dict *ht, unsigned long size) {
_dictInit(&n, ht->type, ht->privdata);
n.size = realsize;
n.sizemask = realsize-1;
n.table = calloc(realsize,sizeof(dictEntry*));
n.table = hi_calloc(realsize,sizeof(dictEntry*));
if (n.table == NULL)
return DICT_ERR;
/* Copy all the elements from the old to the new table:
* note that if the old hash table is empty ht->size is zero,
@@ -124,7 +130,7 @@ static int dictExpand(dict *ht, unsigned long size) {
}
}
assert(ht->used == 0);
free(ht->table);
hi_free(ht->table);
/* Remap the new hashtable in the old */
*ht = n;
@@ -142,7 +148,10 @@ static int dictAdd(dict *ht, void *key, void *val) {
return DICT_ERR;
/* Allocates the memory and stores key */
entry = malloc(sizeof(*entry));
entry = hi_malloc(sizeof(*entry));
if (entry == NULL)
return DICT_ERR;
entry->next = ht->table[index];
ht->table[index] = entry;
@@ -166,6 +175,9 @@ static int dictReplace(dict *ht, void *key, void *val) {
return 1;
/* It already exists, get the entry */
entry = dictFind(ht, key);
if (entry == NULL)
return 0;
/* Free the old value and set the new one */
/* Set the new value and free the old one. Note that it is important
* to do that in this order, as the value may just be exactly the same
@@ -199,7 +211,7 @@ static int dictDelete(dict *ht, const void *key) {
dictFreeEntryKey(ht,de);
dictFreeEntryVal(ht,de);
free(de);
hi_free(de);
ht->used--;
return DICT_OK;
}
@@ -222,13 +234,13 @@ static int _dictClear(dict *ht) {
nextHe = he->next;
dictFreeEntryKey(ht, he);
dictFreeEntryVal(ht, he);
free(he);
hi_free(he);
ht->used--;
he = nextHe;
}
}
/* Free the table and the allocated cache structure */
free(ht->table);
hi_free(ht->table);
/* Re-initialize the table */
_dictReset(ht);
return DICT_OK; /* never fails */
@@ -237,7 +249,7 @@ static int _dictClear(dict *ht) {
/* Clear & Release the hash table */
static void dictRelease(dict *ht) {
_dictClear(ht);
free(ht);
hi_free(ht);
}
static dictEntry *dictFind(dict *ht, const void *key) {
@@ -256,7 +268,9 @@ static dictEntry *dictFind(dict *ht, const void *key) {
}
static dictIterator *dictGetIterator(dict *ht) {
dictIterator *iter = malloc(sizeof(*iter));
dictIterator *iter = hi_malloc(sizeof(*iter));
if (iter == NULL)
return NULL;
iter->ht = ht;
iter->index = -1;
@@ -286,7 +300,7 @@ static dictEntry *dictNext(dictIterator *iter) {
}
static void dictReleaseIterator(dictIterator *iter) {
free(iter);
hi_free(iter);
}
/* ------------------------- private functions ------------------------------ */
@@ -294,7 +308,7 @@ static void dictReleaseIterator(dictIterator *iter) {
/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *ht) {
/* If the hash table is empty expand it to the initial size,
* if the table is "full" dobule its size. */
* if the table is "full" double its size. */
if (ht->size == 0)
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
if (ht->used == ht->size)
+3
View File
@@ -44,3 +44,6 @@ ENDIF()
ADD_EXECUTABLE(example example.c)
TARGET_LINK_LIBRARIES(example hiredis)
ADD_EXECUTABLE(example-push example-push.c)
TARGET_LINK_LIBRARIES(example-push hiredis)
+2
View File
@@ -33,7 +33,9 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
iv_init();
+2
View File
@@ -33,7 +33,9 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
+18 -1
View File
@@ -34,7 +34,10 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
struct event_base *base = event_base_new();
if (argc < 5) {
fprintf(stderr,
@@ -52,13 +55,25 @@ int main (int argc, char **argv) {
const char *certKey = argv[5];
const char *caCert = argc > 5 ? argv[6] : NULL;
redisSSLContext *ssl;
redisSSLContextError ssl_error;
redisInitOpenSSL();
ssl = redisCreateSSLContext(caCert, NULL,
cert, certKey, NULL, &ssl_error);
if (!ssl) {
printf("Error: %s\n", redisSSLContextGetError(ssl_error));
return 1;
}
redisAsyncContext *c = redisAsyncConnect(hostname, port);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) {
printf("SSL Error!\n");
exit(1);
}
@@ -69,5 +84,7 @@ int main (int argc, char **argv) {
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
event_base_dispatch(base);
redisFreeSSLContext(ssl);
return 0;
}
+4 -1
View File
@@ -38,13 +38,16 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
struct event_base *base = event_base_new();
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
struct timeval tv = {0};
tv.tv_sec = 1;
options.timeout = &tv;
options.connect_timeout = &tv;
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
+3
View File
@@ -33,7 +33,10 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
uv_loop_t* loop = uv_default_loop();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
+160
View File
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
#include <win32.h>
#define KEY_COUNT 5
#define panicAbort(fmt, ...) \
do { \
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, __VA_ARGS__); \
exit(-1); \
} while (0)
static void assertReplyAndFree(redisContext *context, redisReply *reply, int type) {
if (reply == NULL)
panicAbort("NULL reply from server (error: %s)", context->errstr);
if (reply->type != type) {
if (reply->type == REDIS_REPLY_ERROR)
fprintf(stderr, "Redis Error: %s\n", reply->str);
panicAbort("Expected reply type %d but got type %d", type, reply->type);
}
freeReplyObject(reply);
}
/* Switch to the RESP3 protocol and enable client tracking */
static void enableClientTracking(redisContext *c) {
redisReply *reply = redisCommand(c, "HELLO 3");
if (reply == NULL || c->err) {
panicAbort("NULL reply or server error (error: %s)", c->errstr);
}
if (reply->type != REDIS_REPLY_MAP) {
fprintf(stderr, "Error: Can't send HELLO 3 command. Are you sure you're ");
fprintf(stderr, "connected to redis-server >= 6.0.0?\nRedis error: %s\n",
reply->type == REDIS_REPLY_ERROR ? reply->str : "(unknown)");
exit(-1);
}
freeReplyObject(reply);
/* Enable client tracking */
reply = redisCommand(c, "CLIENT TRACKING ON");
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
}
void pushReplyHandler(void *privdata, void *r) {
redisReply *reply = r;
int *invalidations = privdata;
/* Sanity check on the invalidation reply */
if (reply->type != REDIS_REPLY_PUSH || reply->elements != 2 ||
reply->element[1]->type != REDIS_REPLY_ARRAY ||
reply->element[1]->element[0]->type != REDIS_REPLY_STRING)
{
panicAbort("%s", "Can't parse PUSH message!");
}
/* Increment our invalidation count */
*invalidations += 1;
printf("pushReplyHandler(): INVALIDATE '%s' (invalidation count: %d)\n",
reply->element[1]->element[0]->str, *invalidations);
freeReplyObject(reply);
}
/* We aren't actually freeing anything here, but it is included to show that we can
* have hiredis call our data destructor when freeing the context */
void privdata_dtor(void *privdata) {
unsigned int *icount = privdata;
printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount);
}
int main(int argc, char **argv) {
unsigned int j, invalidations = 0;
redisContext *c;
redisReply *reply;
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
int port = (argc > 2) ? atoi(argv[2]) : 6379;
redisOptions o = {0};
REDIS_OPTIONS_SET_TCP(&o, hostname, port);
/* Set our context privdata to the address of our invalidation counter. Each
* time our PUSH handler is called, hiredis will pass the privdata for context.
*
* This could also be done after we create the context like so:
*
* c->privdata = &invalidations;
* c->free_privdata = privdata_dtor;
*/
REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor);
/* Set our custom PUSH message handler */
o.push_cb = pushReplyHandler;
c = redisConnectWithOptions(&o);
if (c == NULL || c->err)
panicAbort("Connection error: %s", c ? c->errstr : "OOM");
/* Enable RESP3 and turn on client tracking */
enableClientTracking(c);
/* Set some keys and then read them back. Once we do that, Redis will deliver
* invalidation push messages whenever the key is modified */
for (j = 0; j < KEY_COUNT; j++) {
reply = redisCommand(c, "SET key:%d initial:%d", j, j);
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
reply = redisCommand(c, "GET key:%d", j);
assertReplyAndFree(c, reply, REDIS_REPLY_STRING);
}
/* Trigger invalidation messages by updating keys we just read */
for (j = 0; j < KEY_COUNT; j++) {
printf(" main(): SET key:%d update:%d\n", j, j);
reply = redisCommand(c, "SET key:%d update:%d", j, j);
assertReplyAndFree(c, reply, REDIS_REPLY_STATUS);
printf(" main(): SET REPLY OK\n");
}
printf("\nTotal detected invalidations: %d, expected: %d\n", invalidations, KEY_COUNT);
/* PING server */
redisFree(c);
}
+15 -2
View File
@@ -4,9 +4,12 @@
#include <hiredis.h>
#include <hiredis_ssl.h>
#include <win32.h>
int main(int argc, char **argv) {
unsigned int j;
redisSSLContext *ssl;
redisSSLContextError ssl_error;
redisContext *c;
redisReply *reply;
if (argc < 4) {
@@ -19,10 +22,18 @@ int main(int argc, char **argv) {
const char *key = argv[4];
const char *ca = argc > 4 ? argv[5] : NULL;
redisInitOpenSSL();
ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
if (!ssl) {
printf("SSL Context error: %s\n",
redisSSLContextGetError(ssl_error));
exit(1);
}
struct timeval tv = { 1, 500000 }; // 1.5 seconds
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
options.timeout = &tv;
options.connect_timeout = &tv;
c = redisConnectWithOptions(&options);
if (c == NULL || c->err) {
@@ -35,7 +46,7 @@ int main(int argc, char **argv) {
exit(1);
}
if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
printf("Couldn't initialize SSL!\n");
printf("Error: %s\n", c->errstr);
redisFree(c);
@@ -93,5 +104,7 @@ int main(int argc, char **argv) {
/* Disconnects and frees the context */
redisFree(c);
redisFreeSSLContext(ssl);
return 0;
}
+1 -1
View File
@@ -1,8 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis.h>
#include <win32.h>
int main(int argc, char **argv) {
unsigned int j, isunix = 0;
+13
View File
@@ -0,0 +1,13 @@
@PACKAGE_INIT@
set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
IF (NOT TARGET hiredis::hiredis)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
ENDIF()
SET(hiredis_LIBRARIES hiredis::hiredis)
SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
check_required_components(hiredis)
+202 -114
View File
@@ -44,8 +44,11 @@
#include "async.h"
#include "win32.h"
extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);
extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
static redisContextFuncs redisContextDefaultFuncs = {
.free_privdata = NULL,
.free_privctx = NULL,
.async_read = redisAsyncRead,
.async_write = redisAsyncWrite,
.read = redisNetRead,
@@ -74,7 +77,7 @@ static redisReplyObjectFunctions defaultFunctions = {
/* Create a reply object */
static redisReply *createReplyObject(int type) {
redisReply *r = calloc(1,sizeof(*r));
redisReply *r = hi_calloc(1,sizeof(*r));
if (r == NULL)
return NULL;
@@ -97,20 +100,22 @@ void freeReplyObject(void *reply) {
case REDIS_REPLY_ARRAY:
case REDIS_REPLY_MAP:
case REDIS_REPLY_SET:
case REDIS_REPLY_PUSH:
if (r->element != NULL) {
for (j = 0; j < r->elements; j++)
freeReplyObject(r->element[j]);
free(r->element);
hi_free(r->element);
}
break;
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
case REDIS_REPLY_DOUBLE:
free(r->str);
case REDIS_REPLY_VERB:
hi_free(r->str);
break;
}
free(r);
hi_free(r);
}
static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
@@ -128,22 +133,18 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
/* Copy string value */
if (task->type == REDIS_REPLY_VERB) {
buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
if (buf == NULL) {
freeReplyObject(r);
return NULL;
}
buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
if (buf == NULL) goto oom;
memcpy(r->vtype,str,3);
r->vtype[3] = '\0';
memcpy(buf,str+4,len-4);
buf[len-4] = '\0';
r->len = len-4;
r->len = len - 4;
} else {
buf = malloc(len+1);
if (buf == NULL) {
freeReplyObject(r);
return NULL;
}
buf = hi_malloc(len+1);
if (buf == NULL) goto oom;
memcpy(buf,str,len);
buf[len] = '\0';
r->len = len;
@@ -154,10 +155,15 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->type == REDIS_REPLY_SET ||
parent->type == REDIS_REPLY_PUSH);
parent->element[task->idx] = r;
}
return r;
oom:
freeReplyObject(r);
return NULL;
}
static void *createArrayObject(const redisReadTask *task, size_t elements) {
@@ -168,7 +174,7 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
return NULL;
if (elements > 0) {
r->element = calloc(elements,sizeof(redisReply*));
r->element = hi_calloc(elements,sizeof(redisReply*));
if (r->element == NULL) {
freeReplyObject(r);
return NULL;
@@ -181,7 +187,8 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) {
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->type == REDIS_REPLY_SET ||
parent->type == REDIS_REPLY_PUSH);
parent->element[task->idx] = r;
}
return r;
@@ -200,7 +207,8 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->type == REDIS_REPLY_SET ||
parent->type == REDIS_REPLY_PUSH);
parent->element[task->idx] = r;
}
return r;
@@ -214,7 +222,7 @@ static void *createDoubleObject(const redisReadTask *task, double value, char *s
return NULL;
r->dval = value;
r->str = malloc(len+1);
r->str = hi_malloc(len+1);
if (r->str == NULL) {
freeReplyObject(r);
return NULL;
@@ -249,7 +257,8 @@ static void *createNilObject(const redisReadTask *task) {
parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET);
parent->type == REDIS_REPLY_SET ||
parent->type == REDIS_REPLY_PUSH);
parent->element[task->idx] = r;
}
return r;
@@ -297,7 +306,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
const char *c = format;
char *cmd = NULL; /* final command */
int pos; /* position in final command */
sds curarg, newarg; /* current argument */
hisds curarg, newarg; /* current argument */
int touched = 0; /* was the current argument touched? */
char **curargv = NULL, **newargv = NULL;
int argc = 0;
@@ -310,7 +319,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
return -1;
/* Build the command string accordingly to protocol */
curarg = sdsempty();
curarg = hi_sdsempty();
if (curarg == NULL)
return -1;
@@ -318,19 +327,19 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
if (*c != '%' || c[1] == '\0') {
if (*c == ' ') {
if (touched) {
newargv = realloc(curargv,sizeof(char*)*(argc+1));
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
if (newargv == NULL) goto memory_err;
curargv = newargv;
curargv[argc++] = curarg;
totlen += bulklen(sdslen(curarg));
totlen += bulklen(hi_sdslen(curarg));
/* curarg is put in argv so it can be overwritten. */
curarg = sdsempty();
curarg = hi_sdsempty();
if (curarg == NULL) goto memory_err;
touched = 0;
}
} else {
newarg = sdscatlen(curarg,c,1);
newarg = hi_sdscatlen(curarg,c,1);
if (newarg == NULL) goto memory_err;
curarg = newarg;
touched = 1;
@@ -347,16 +356,16 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
arg = va_arg(ap,char*);
size = strlen(arg);
if (size > 0)
newarg = sdscatlen(curarg,arg,size);
newarg = hi_sdscatlen(curarg,arg,size);
break;
case 'b':
arg = va_arg(ap,char*);
size = va_arg(ap,size_t);
if (size > 0)
newarg = sdscatlen(curarg,arg,size);
newarg = hi_sdscatlen(curarg,arg,size);
break;
case '%':
newarg = sdscat(curarg,"%");
newarg = hi_sdscat(curarg,"%");
break;
default:
/* Try to detect printf format */
@@ -444,7 +453,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
if (_l < sizeof(_format)-2) {
memcpy(_format,c,_l);
_format[_l] = '\0';
newarg = sdscatvprintf(curarg,_format,_cpy);
newarg = hi_sdscatvprintf(curarg,_format,_cpy);
/* Update current position (note: outer blocks
* increment c twice so compensate here) */
@@ -467,13 +476,13 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
/* Add the last argument if needed */
if (touched) {
newargv = realloc(curargv,sizeof(char*)*(argc+1));
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
if (newargv == NULL) goto memory_err;
curargv = newargv;
curargv[argc++] = curarg;
totlen += bulklen(sdslen(curarg));
totlen += bulklen(hi_sdslen(curarg));
} else {
sdsfree(curarg);
hi_sdsfree(curarg);
}
/* Clear curarg because it was put in curargv or was free'd. */
@@ -483,22 +492,22 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
totlen += 1+countDigits(argc)+2;
/* Build the command at protocol level */
cmd = malloc(totlen+1);
cmd = hi_malloc(totlen+1);
if (cmd == NULL) goto memory_err;
pos = sprintf(cmd,"*%d\r\n",argc);
for (j = 0; j < argc; j++) {
pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
pos += sdslen(curargv[j]);
sdsfree(curargv[j]);
pos += sprintf(cmd+pos,"$%zu\r\n",hi_sdslen(curargv[j]));
memcpy(cmd+pos,curargv[j],hi_sdslen(curargv[j]));
pos += hi_sdslen(curargv[j]);
hi_sdsfree(curargv[j]);
cmd[pos++] = '\r';
cmd[pos++] = '\n';
}
assert(pos == totlen);
cmd[pos] = '\0';
free(curargv);
hi_free(curargv);
*target = cmd;
return totlen;
@@ -513,12 +522,12 @@ memory_err:
cleanup:
if (curargv) {
while(argc--)
sdsfree(curargv[argc]);
free(curargv);
hi_sdsfree(curargv[argc]);
hi_free(curargv);
}
sdsfree(curarg);
free(cmd);
hi_sdsfree(curarg);
hi_free(cmd);
return error_type;
}
@@ -550,16 +559,16 @@ int redisFormatCommand(char **target, const char *format, ...) {
return len;
}
/* Format a command according to the Redis protocol using an sds string and
* sdscatfmt for the processing of arguments. This function takes the
/* Format a command according to the Redis protocol using an hisds string and
* hi_sdscatfmt for the processing of arguments. This function takes the
* number of arguments, an array with arguments and an array with their
* lengths. If the latter is set to NULL, strlen will be used to compute the
* argument lengths.
*/
int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
int redisFormatSdsCommandArgv(hisds *target, int argc, const char **argv,
const size_t *argvlen)
{
sds cmd;
hisds cmd, aux;
unsigned long long totlen;
int j;
size_t len;
@@ -576,32 +585,36 @@ int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
}
/* Use an SDS string for command construction */
cmd = sdsempty();
cmd = hi_sdsempty();
if (cmd == NULL)
return -1;
/* We already know how much storage we need */
cmd = sdsMakeRoomFor(cmd, totlen);
if (cmd == NULL)
aux = hi_sdsMakeRoomFor(cmd, totlen);
if (aux == NULL) {
hi_sdsfree(cmd);
return -1;
/* Construct command */
cmd = sdscatfmt(cmd, "*%i\r\n", argc);
for (j=0; j < argc; j++) {
len = argvlen ? argvlen[j] : strlen(argv[j]);
cmd = sdscatfmt(cmd, "$%u\r\n", len);
cmd = sdscatlen(cmd, argv[j], len);
cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
}
assert(sdslen(cmd)==totlen);
cmd = aux;
/* Construct command */
cmd = hi_sdscatfmt(cmd, "*%i\r\n", argc);
for (j=0; j < argc; j++) {
len = argvlen ? argvlen[j] : strlen(argv[j]);
cmd = hi_sdscatfmt(cmd, "$%u\r\n", len);
cmd = hi_sdscatlen(cmd, argv[j], len);
cmd = hi_sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
}
assert(hi_sdslen(cmd)==totlen);
*target = cmd;
return totlen;
}
void redisFreeSdsCommand(sds cmd) {
sdsfree(cmd);
void redisFreeSdsCommand(hisds cmd) {
hi_sdsfree(cmd);
}
/* Format a command according to the Redis protocol. This function takes the
@@ -627,7 +640,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
}
/* Build the command at protocol level */
cmd = malloc(totlen+1);
cmd = hi_malloc(totlen+1);
if (cmd == NULL)
return -1;
@@ -648,7 +661,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
}
void redisFreeCommand(char *cmd) {
free(cmd);
hi_free(cmd);
}
void __redisSetError(redisContext *c, int type, const char *str) {
@@ -671,15 +684,21 @@ redisReader *redisReaderCreate(void) {
return redisReaderCreateWithFunctions(&defaultFunctions);
}
static redisContext *redisContextInit(const redisOptions *options) {
static void redisPushAutoFree(void *privdata, void *reply) {
(void)privdata;
freeReplyObject(reply);
}
static redisContext *redisContextInit(void) {
redisContext *c;
c = calloc(1, sizeof(*c));
c = hi_calloc(1, sizeof(*c));
if (c == NULL)
return NULL;
c->funcs = &redisContextDefaultFuncs;
c->obuf = sdsempty();
c->obuf = hi_sdsempty();
c->reader = redisReaderCreate();
c->fd = REDIS_INVALID_FD;
@@ -687,7 +706,7 @@ static redisContext *redisContextInit(const redisOptions *options) {
redisFree(c);
return NULL;
}
(void)options; /* options are used in other functions */
return c;
}
@@ -696,18 +715,23 @@ void redisFree(redisContext *c) {
return;
redisNetClose(c);
sdsfree(c->obuf);
hi_sdsfree(c->obuf);
redisReaderFree(c->reader);
free(c->tcp.host);
free(c->tcp.source_addr);
free(c->unix_sock.path);
free(c->timeout);
free(c->saddr);
if (c->funcs->free_privdata) {
c->funcs->free_privdata(c->privdata);
}
hi_free(c->tcp.host);
hi_free(c->tcp.source_addr);
hi_free(c->unix_sock.path);
hi_free(c->connect_timeout);
hi_free(c->command_timeout);
hi_free(c->saddr);
if (c->privdata && c->free_privdata)
c->free_privdata(c->privdata);
if (c->funcs->free_privctx)
c->funcs->free_privctx(c->privctx);
memset(c, 0xff, sizeof(*c));
free(c);
hi_free(c);
}
redisFD redisFreeKeepFd(redisContext *c) {
@@ -721,35 +745,46 @@ int redisReconnect(redisContext *c) {
c->err = 0;
memset(c->errstr, '\0', strlen(c->errstr));
if (c->privdata && c->funcs->free_privdata) {
c->funcs->free_privdata(c->privdata);
c->privdata = NULL;
if (c->privctx && c->funcs->free_privctx) {
c->funcs->free_privctx(c->privctx);
c->privctx = NULL;
}
redisNetClose(c);
sdsfree(c->obuf);
hi_sdsfree(c->obuf);
redisReaderFree(c->reader);
c->obuf = sdsempty();
c->obuf = hi_sdsempty();
c->reader = redisReaderCreate();
if (c->obuf == NULL || c->reader == NULL) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
int ret = REDIS_ERR;
if (c->connection_type == REDIS_CONN_TCP) {
return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
c->timeout, c->tcp.source_addr);
ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
c->connect_timeout, c->tcp.source_addr);
} else if (c->connection_type == REDIS_CONN_UNIX) {
return redisContextConnectUnix(c, c->unix_sock.path, c->timeout);
ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
} else {
/* Something bad happened here and shouldn't have. There isn't
enough information in the context to reconnect. */
__redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect");
ret = REDIS_ERR;
}
return REDIS_ERR;
if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *c->command_timeout);
}
return ret;
}
redisContext *redisConnectWithOptions(const redisOptions *options) {
redisContext *c = redisContextInit(options);
redisContext *c = redisContextInit();
if (c == NULL) {
return NULL;
}
@@ -760,16 +795,32 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
c->flags |= REDIS_REUSEADDR;
}
if (options->options & REDIS_OPT_NOAUTOFREE) {
c->flags |= REDIS_NO_AUTO_FREE;
c->flags |= REDIS_NO_AUTO_FREE;
}
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
* as a default unless specifically flagged that we don't want one. */
if (options->push_cb != NULL)
redisSetPushCallback(c, options->push_cb);
else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
redisSetPushCallback(c, redisPushAutoFree);
c->privdata = options->privdata;
c->free_privdata = options->free_privdata;
if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||
redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return c;
}
if (options->type == REDIS_CONN_TCP) {
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
options->endpoint.tcp.port, options->timeout,
options->endpoint.tcp.port, options->connect_timeout,
options->endpoint.tcp.source_addr);
} else if (options->type == REDIS_CONN_UNIX) {
redisContextConnectUnix(c, options->endpoint.unix_socket,
options->timeout);
options->connect_timeout);
} else if (options->type == REDIS_CONN_USERFD) {
c->fd = options->endpoint.fd;
c->flags |= REDIS_CONNECTED;
@@ -777,9 +828,11 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
// Unknown type - FIXME - FREE
return NULL;
}
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *options->timeout);
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *options->command_timeout);
}
return c;
}
@@ -795,7 +848,7 @@ redisContext *redisConnect(const char *ip, int port) {
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.timeout = &tv;
options.connect_timeout = &tv;
return redisConnectWithOptions(&options);
}
@@ -833,7 +886,7 @@ redisContext *redisConnectUnix(const char *path) {
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path);
options.timeout = &tv;
options.connect_timeout = &tv;
return redisConnectWithOptions(&options);
}
@@ -865,6 +918,13 @@ int redisEnableKeepAlive(redisContext *c) {
return REDIS_OK;
}
/* Set a user provided RESP3 PUSH handler and return any old one set. */
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) {
redisPushFn *old = c->push_cb;
c->push_cb = fn;
return old;
}
/* Use this function to handle a read event on the descriptor. It will try
* and read some bytes from the socket and feed them to the reply parser.
*
@@ -906,21 +966,27 @@ int redisBufferWrite(redisContext *c, int *done) {
if (c->err)
return REDIS_ERR;
if (sdslen(c->obuf) > 0) {
int nwritten = c->funcs->write(c);
if (hi_sdslen(c->obuf) > 0) {
ssize_t nwritten = c->funcs->write(c);
if (nwritten < 0) {
return REDIS_ERR;
} else if (nwritten > 0) {
if (nwritten == (signed)sdslen(c->obuf)) {
sdsfree(c->obuf);
c->obuf = sdsempty();
if (nwritten == (ssize_t)hi_sdslen(c->obuf)) {
hi_sdsfree(c->obuf);
c->obuf = hi_sdsempty();
if (c->obuf == NULL)
goto oom;
} else {
sdsrange(c->obuf,nwritten,-1);
if (hi_sdsrange(c->obuf,nwritten,-1) < 0) goto oom;
}
}
}
if (done != NULL) *done = (sdslen(c->obuf) == 0);
if (done != NULL) *done = (hi_sdslen(c->obuf) == 0);
return REDIS_OK;
oom:
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
/* Internal helper function to try and get a reply from the reader,
@@ -930,9 +996,21 @@ int redisGetReplyFromReader(redisContext *c, void **reply) {
__redisSetError(c,c->reader->err,c->reader->errstr);
return REDIS_ERR;
}
return REDIS_OK;
}
/* Internal helper that returns 1 if the reply was a RESP3 PUSH
* message and we handled it with a user-provided callback. */
static int redisHandledPushReply(redisContext *c, void *reply) {
if (reply && c->push_cb && redisIsPushReply(reply)) {
c->push_cb(c->privdata, reply);
return 1;
}
return 0;
}
int redisGetReply(redisContext *c, void **reply) {
int wdone = 0;
void *aux = NULL;
@@ -953,13 +1031,23 @@ int redisGetReply(redisContext *c, void **reply) {
do {
if (redisBufferRead(c) == REDIS_ERR)
return REDIS_ERR;
if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
return REDIS_ERR;
/* We loop here in case the user has specified a RESP3
* PUSH handler (e.g. for client tracking). */
do {
if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
return REDIS_ERR;
} while (redisHandledPushReply(c, aux));
} while (aux == NULL);
}
/* Set reply object */
if (reply != NULL) *reply = aux;
/* Set reply or free it if we were passed NULL */
if (reply != NULL) {
*reply = aux;
} else {
freeReplyObject(aux);
}
return REDIS_OK;
}
@@ -971,9 +1059,9 @@ int redisGetReply(redisContext *c, void **reply) {
* the reply (or replies in pub/sub).
*/
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
sds newbuf;
hisds newbuf;
newbuf = sdscatlen(c->obuf,cmd,len);
newbuf = hi_sdscatlen(c->obuf,cmd,len);
if (newbuf == NULL) {
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
return REDIS_ERR;
@@ -1006,11 +1094,11 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
}
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
free(cmd);
hi_free(cmd);
return REDIS_ERR;
}
free(cmd);
hi_free(cmd);
return REDIS_OK;
}
@@ -1025,7 +1113,7 @@ int redisAppendCommand(redisContext *c, const char *format, ...) {
}
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
sds cmd;
hisds cmd;
int len;
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
@@ -1035,11 +1123,11 @@ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const s
}
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
sdsfree(cmd);
hi_sdsfree(cmd);
return REDIS_ERR;
}
sdsfree(cmd);
hi_sdsfree(cmd);
return REDIS_OK;
}
+53 -16
View File
@@ -39,14 +39,16 @@
#include <sys/time.h> /* for struct timeval */
#else
struct timeval; /* forward declaration */
typedef long long ssize_t;
#endif
#include <stdint.h> /* uintXX_t, etc */
#include "sds.h" /* for sds */
#include "sds.h" /* for hisds */
#include "alloc.h" /* for allocation wrappers */
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 14
#define HIREDIS_MAJOR 1
#define HIREDIS_MINOR 0
#define HIREDIS_PATCH 0
#define HIREDIS_SONAME 0.14
#define HIREDIS_SONAME 1.0.0
/* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */
@@ -90,6 +92,15 @@ struct timeval; /* forward declaration */
* SO_REUSEADDR is being used. */
#define REDIS_CONNECT_RETRIES 10
/* Forward declarations for structs defined elsewhere */
struct redisAsyncContext;
struct redisContext;
/* RESP3 push helpers and callback prototypes */
#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)
typedef void (redisPushFn)(void *, void *);
typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);
#ifdef __cplusplus
extern "C" {
#endif
@@ -101,7 +112,7 @@ typedef struct redisReply {
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
size_t len; /* Length of string */
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
and REDIS_REPLY_DOUBLE (in additionl to dval). */
REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
terminated 3 character content type, such as "txt". */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
@@ -117,9 +128,9 @@ void freeReplyObject(void *reply);
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
int redisFormatSdsCommandArgv(hisds *target, int argc, const char ** argv, const size_t *argvlen);
void redisFreeCommand(char *cmd);
void redisFreeSdsCommand(sds cmd);
void redisFreeSdsCommand(hisds cmd);
enum redisConnectionType {
REDIS_CONN_TCP,
@@ -138,6 +149,9 @@ struct redisSsl;
*/
#define REDIS_OPT_NOAUTOFREE 0x04
/* Don't automatically intercept and free RESP3 PUSH replies. */
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08
/* In Unix systems a file descriptor is a regular signed int, with -1
* representing an invalid descriptor. In Windows it is a SOCKET
* (32- or 64-bit unsigned integer depending on the architecture), where
@@ -162,8 +176,11 @@ typedef struct {
int type;
/* bit field of REDIS_OPT_xxx */
int options;
/* timeout value. if NULL, no timeout is used */
const struct timeval *timeout;
/* timeout value for connect operation. If NULL, no timeout is used */
const struct timeval *connect_timeout;
/* timeout value for commands. If NULL, no timeout is used. This can be
* updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
const struct timeval *command_timeout;
union {
/** use this field for tcp/ip connections */
struct {
@@ -178,6 +195,14 @@ typedef struct {
* file descriptor */
redisFD fd;
} endpoint;
/* Optional user defined data/destructor */
void *privdata;
void (*free_privdata)(void *);
/* A user defined PUSH message callback */
redisPushFn *push_cb;
redisAsyncPushFn *async_push_cb;
} redisOptions;
/**
@@ -192,15 +217,16 @@ typedef struct {
(opts)->type = REDIS_CONN_UNIX; \
(opts)->endpoint.unix_socket = path;
struct redisAsyncContext;
struct redisContext;
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
(opts)->privdata = data; \
(opts)->free_privdata = dtor; \
typedef struct redisContextFuncs {
void (*free_privdata)(void *);
void (*free_privctx)(void *);
void (*async_read)(struct redisAsyncContext *);
void (*async_write)(struct redisAsyncContext *);
int (*read)(struct redisContext *, char *, size_t);
int (*write)(struct redisContext *);
ssize_t (*read)(struct redisContext *, char *, size_t);
ssize_t (*write)(struct redisContext *);
} redisContextFuncs;
/* Context for a connection to Redis */
@@ -215,7 +241,8 @@ typedef struct redisContext {
redisReader *reader; /* Protocol reader */
enum redisConnectionType connection_type;
struct timeval *timeout;
struct timeval *connect_timeout;
struct timeval *command_timeout;
struct {
char *host;
@@ -231,8 +258,17 @@ typedef struct redisContext {
struct sockadr *saddr;
size_t addrlen;
/* Additional private data for hiredis addons such as SSL */
/* Optional data and corresponding destructor users can use to provide
* context to a given redisContext. Not used by hiredis. */
void *privdata;
void (*free_privdata)(void *);
/* Internal context pointer presently used by hiredis to manage
* SSL connections. */
void *privctx;
/* An optional RESP3 PUSH handler */
redisPushFn *push_cb;
} redisContext;
redisContext *redisConnectWithOptions(const redisOptions *options);
@@ -259,6 +295,7 @@ redisContext *redisConnectFd(redisFD fd);
*/
int redisReconnect(redisContext *c);
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
int redisSetTimeout(redisContext *c, const struct timeval tv);
int redisEnableKeepAlive(redisContext *c);
void redisFree(redisContext *c);
+2 -1
View File
@@ -1,6 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
install_libdir=@CMAKE_INSTALL_LIBDIR@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
libdir=${exec_prefix}/${install_libdir}
includedir=${prefix}/include
pkgincludedir=${includedir}/hiredis
+13
View File
@@ -0,0 +1,13 @@
@PACKAGE_INIT@
set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
IF (NOT TARGET hiredis::hiredis_ssl)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake)
ENDIF()
SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl)
SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR})
check_required_components(hiredis_ssl)
+80 -6
View File
@@ -32,22 +32,96 @@
#ifndef __HIREDIS_SSL_H
#define __HIREDIS_SSL_H
#ifdef __cplusplus
extern "C" {
#endif
/* This is the underlying struct for SSL in ssl.h, which is not included to
* keep build dependencies short here.
*/
struct ssl_st;
/**
* Secure the connection using SSL. This should be done before any command is
* executed on the connection.
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
* calling OpenSSL.
*/
int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
const char *keypath, const char *servername);
typedef struct redisSSLContext redisSSLContext;
/**
* Initiate SSL/TLS negotiation on a provided context.
* Initialization errors that redisCreateSSLContext() may return.
*/
typedef enum {
REDIS_SSL_CTX_NONE = 0, /* No Error */
REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */
REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */
} redisSSLContextError;
/**
* Return the error message corresponding with the specified error code.
*/
const char *redisSSLContextGetError(redisSSLContextError error);
/**
* Helper function to initialize the OpenSSL library.
*
* OpenSSL requires one-time initialization before it can be used. Callers should
* call this function only once, and only if OpenSSL is not directly initialized
* elsewhere.
*/
int redisInitOpenSSL(void);
/**
* Helper function to initialize an OpenSSL context that can be used
* to initiate SSL connections.
*
* cacert_filename is an optional name of a CA certificate/bundle file to load
* and use for validation.
*
* capath is an optional directory path where trusted CA certificate files are
* stored in an OpenSSL-compatible structure.
*
* cert_filename and private_key_filename are optional names of a client side
* certificate and private key files to use for authentication. They need to
* be both specified or omitted.
*
* server_name is an optional and will be used as a server name indication
* (SNI) TLS extension.
*
* If error is non-null, it will be populated in case the context creation fails
* (returning a NULL).
*/
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
const char *cert_filename, const char *private_key_filename,
const char *server_name, redisSSLContextError *error);
/**
* Free a previously created OpenSSL context.
*/
void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx);
/**
* Initiate SSL on an existing redisContext.
*
* This is similar to redisInitiateSSL() but does not require the caller
* to directly interact with OpenSSL, and instead uses a redisSSLContext
* previously created using redisCreateSSLContext().
*/
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx);
/**
* Initiate SSL/TLS negotiation on a provided OpenSSL SSL object.
*/
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
#ifdef __cplusplus
}
#endif
#endif /* __HIREDIS_SSL_H */
+80 -39
View File
@@ -57,8 +57,8 @@ void redisNetClose(redisContext *c) {
}
}
int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
int nread = recv(c->fd, buf, bufcap, 0);
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
ssize_t nread = recv(c->fd, buf, bufcap, 0);
if (nread == -1) {
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
/* Try again later */
@@ -79,8 +79,8 @@ int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
}
}
int redisNetWrite(redisContext *c) {
int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
ssize_t redisNetWrite(redisContext *c) {
ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
if (nwritten < 0) {
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
/* Try again later */
@@ -203,7 +203,7 @@ int redisKeepAlive(redisContext *c, int interval) {
return REDIS_OK;
}
static int redisSetTcpNoDelay(redisContext *c) {
int redisSetTcpNoDelay(redisContext *c) {
int yes = 1;
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
@@ -217,7 +217,7 @@ static int redisSetTcpNoDelay(redisContext *c) {
static int redisContextTimeoutMsec(redisContext *c, long *result)
{
const struct timeval *timeout = c->timeout;
const struct timeval *timeout = c->connect_timeout;
long msec = -1;
/* Only use timeout when not NULL. */
@@ -316,11 +316,7 @@ int redisCheckSocketError(redisContext *c) {
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
const void *to_ptr = &tv;
size_t to_sz = sizeof(tv);
#ifdef _WIN32
DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
to_ptr = &timeout_msec;
to_sz = sizeof(timeout_msec);
#endif
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
return REDIS_ERR;
@@ -332,6 +328,38 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
return REDIS_OK;
}
int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
/* Same timeval struct, short circuit */
if (c->connect_timeout == timeout)
return REDIS_OK;
/* Allocate context timeval if we need to */
if (c->connect_timeout == NULL) {
c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
if (c->connect_timeout == NULL)
return REDIS_ERR;
}
memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
return REDIS_OK;
}
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
/* Same timeval struct, short circuit */
if (c->command_timeout == timeout)
return REDIS_OK;
/* Allocate context timeval if we need to */
if (c->command_timeout == NULL) {
c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
if (c->command_timeout == NULL)
return REDIS_ERR;
}
memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
return REDIS_OK;
}
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
const struct timeval *timeout,
const char *source_addr) {
@@ -356,21 +384,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
* This is a bit ugly, but atleast it works and doesn't leak memory.
**/
if (c->tcp.host != addr) {
free(c->tcp.host);
hi_free(c->tcp.host);
c->tcp.host = strdup(addr);
c->tcp.host = hi_strdup(addr);
if (c->tcp.host == NULL)
goto oom;
}
if (timeout) {
if (c->timeout != timeout) {
if (c->timeout == NULL)
c->timeout = malloc(sizeof(struct timeval));
memcpy(c->timeout, timeout, sizeof(struct timeval));
}
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
goto oom;
} else {
free(c->timeout);
c->timeout = NULL;
hi_free(c->connect_timeout);
c->connect_timeout = NULL;
}
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
@@ -379,11 +405,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
}
if (source_addr == NULL) {
free(c->tcp.source_addr);
hi_free(c->tcp.source_addr);
c->tcp.source_addr = NULL;
} else if (c->tcp.source_addr != source_addr) {
free(c->tcp.source_addr);
c->tcp.source_addr = strdup(source_addr);
hi_free(c->tcp.source_addr);
c->tcp.source_addr = hi_strdup(source_addr);
}
snprintf(_port, 6, "%d", port);
@@ -446,8 +472,11 @@ addrretry:
}
/* For repeat connection */
free(c->saddr);
c->saddr = malloc(p->ai_addrlen);
hi_free(c->saddr);
c->saddr = hi_malloc(p->ai_addrlen);
if (c->saddr == NULL)
goto oom;
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
c->addrlen = p->ai_addrlen;
@@ -474,12 +503,12 @@ addrretry:
wait_for_ready:
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
goto error;
if (redisSetTcpNoDelay(c) != REDIS_OK)
goto error;
}
}
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
goto error;
if (redisSetTcpNoDelay(c) != REDIS_OK)
goto error;
c->flags |= REDIS_CONNECTED;
rv = REDIS_OK;
@@ -492,6 +521,8 @@ addrretry:
goto error;
}
oom:
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
error:
rv = REDIS_ERR;
end:
@@ -525,25 +556,32 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
return REDIS_ERR;
c->connection_type = REDIS_CONN_UNIX;
if (c->unix_sock.path != path)
c->unix_sock.path = strdup(path);
if (c->unix_sock.path != path) {
hi_free(c->unix_sock.path);
c->unix_sock.path = hi_strdup(path);
if (c->unix_sock.path == NULL)
goto oom;
}
if (timeout) {
if (c->timeout != timeout) {
if (c->timeout == NULL)
c->timeout = malloc(sizeof(struct timeval));
memcpy(c->timeout, timeout, sizeof(struct timeval));
}
if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
goto oom;
} else {
free(c->timeout);
c->timeout = NULL;
hi_free(c->connect_timeout);
c->connect_timeout = NULL;
}
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
return REDIS_ERR;
sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));
/* Don't leak sockaddr if we're reconnecting */
if (c->saddr) hi_free(c->saddr);
sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));
if (sa == NULL)
goto oom;
c->addrlen = sizeof(struct sockaddr_un);
sa->sun_family = AF_UNIX;
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
@@ -568,4 +606,7 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
errno = EPROTONOSUPPORT;
return REDIS_ERR;
#endif /* _WIN32 */
oom:
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
+4 -2
View File
@@ -38,8 +38,8 @@
#include "hiredis.h"
void redisNetClose(redisContext *c);
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
int redisNetWrite(redisContext *c);
ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);
ssize_t redisNetWrite(redisContext *c);
int redisCheckSocketError(redisContext *c);
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
@@ -51,4 +51,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
int redisKeepAlive(redisContext *c, int interval);
int redisCheckConnectDone(redisContext *c, int *completed);
int redisSetTcpNoDelay(redisContext *c);
#endif
+108 -50
View File
@@ -42,10 +42,14 @@
#include <limits.h>
#include <math.h>
#include "alloc.h"
#include "read.h"
#include "sds.h"
#include "win32.h"
/* Initial size of our nested reply stack and how much we grow it when needd */
#define REDIS_READER_STACK_SIZE 9
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
size_t len;
@@ -55,7 +59,7 @@ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
}
/* Clear input buffer on errors. */
sdsfree(r->buf);
hi_sdsfree(r->buf);
r->buf = NULL;
r->pos = r->len = 0;
@@ -243,11 +247,12 @@ static void moveToNextTask(redisReader *r) {
return;
}
cur = &(r->rstack[r->ridx]);
prv = &(r->rstack[r->ridx-1]);
cur = r->task[r->ridx];
prv = r->task[r->ridx-1];
assert(prv->type == REDIS_REPLY_ARRAY ||
prv->type == REDIS_REPLY_MAP ||
prv->type == REDIS_REPLY_SET);
prv->type == REDIS_REPLY_SET ||
prv->type == REDIS_REPLY_PUSH);
if (cur->idx == prv->elements-1) {
r->ridx--;
} else {
@@ -262,7 +267,7 @@ static void moveToNextTask(redisReader *r) {
}
static int processLineItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
redisReadTask *cur = r->task[r->ridx];
void *obj;
char *p;
int len;
@@ -297,7 +302,7 @@ static int processLineItem(redisReader *r) {
if (strcasecmp(buf,",inf") == 0) {
d = INFINITY; /* Positive infinite. */
} else if (strcasecmp(buf,",-inf") == 0) {
d = -INFINITY; /* Nevative infinite. */
d = -INFINITY; /* Negative infinite. */
} else {
d = strtod((char*)buf,&eptr);
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
@@ -344,7 +349,7 @@ static int processLineItem(redisReader *r) {
}
static int processBulkItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
redisReadTask *cur = r->task[r->ridx];
void *obj = NULL;
char *p, *s;
long long len;
@@ -415,19 +420,43 @@ static int processBulkItem(redisReader *r) {
return REDIS_ERR;
}
static int redisReaderGrow(redisReader *r) {
redisReadTask **aux;
int newlen;
/* Grow our stack size */
newlen = r->tasks + REDIS_READER_STACK_SIZE;
aux = hi_realloc(r->task, sizeof(*r->task) * newlen);
if (aux == NULL)
goto oom;
r->task = aux;
/* Allocate new tasks */
for (; r->tasks < newlen; r->tasks++) {
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
if (r->task[r->tasks] == NULL)
goto oom;
}
return REDIS_OK;
oom:
__redisReaderSetErrorOOM(r);
return REDIS_ERR;
}
/* Process the array, map and set types. */
static int processAggregateItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
redisReadTask *cur = r->task[r->ridx];
void *obj;
char *p;
long long elements;
int root = 0, len;
/* Set error for nested multi bulks with depth > 7 */
if (r->ridx == 8) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"No support for nested multi bulk replies with depth > 7");
return REDIS_ERR;
if (r->ridx == r->tasks - 1) {
if (redisReaderGrow(r) == REDIS_ERR)
return REDIS_ERR;
}
if ((p = readLine(r,&len)) != NULL) {
@@ -439,7 +468,9 @@ static int processAggregateItem(redisReader *r) {
root = (r->ridx == 0);
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||
(r->maxelements > 0 && elements > r->maxelements))
{
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
"Multi-bulk length out of range");
return REDIS_ERR;
@@ -475,12 +506,12 @@ static int processAggregateItem(redisReader *r) {
cur->elements = elements;
cur->obj = obj;
r->ridx++;
r->rstack[r->ridx].type = -1;
r->rstack[r->ridx].elements = -1;
r->rstack[r->ridx].idx = 0;
r->rstack[r->ridx].obj = NULL;
r->rstack[r->ridx].parent = cur;
r->rstack[r->ridx].privdata = r->privdata;
r->task[r->ridx]->type = -1;
r->task[r->ridx]->elements = -1;
r->task[r->ridx]->idx = 0;
r->task[r->ridx]->obj = NULL;
r->task[r->ridx]->parent = cur;
r->task[r->ridx]->privdata = r->privdata;
} else {
moveToNextTask(r);
}
@@ -495,7 +526,7 @@ static int processAggregateItem(redisReader *r) {
}
static int processItem(redisReader *r) {
redisReadTask *cur = &(r->rstack[r->ridx]);
redisReadTask *cur = r->task[r->ridx];
char *p;
/* check if we need to read type */
@@ -535,6 +566,9 @@ static int processItem(redisReader *r) {
case '=':
cur->type = REDIS_REPLY_VERB;
break;
case '>':
cur->type = REDIS_REPLY_PUSH;
break;
default:
__redisReaderSetErrorProtocolByte(r,*p);
return REDIS_ERR;
@@ -560,6 +594,7 @@ static int processItem(redisReader *r) {
case REDIS_REPLY_ARRAY:
case REDIS_REPLY_MAP:
case REDIS_REPLY_SET:
case REDIS_REPLY_PUSH:
return processAggregateItem(r);
default:
assert(NULL);
@@ -570,33 +605,57 @@ static int processItem(redisReader *r) {
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
redisReader *r;
r = calloc(1,sizeof(redisReader));
r = hi_calloc(1,sizeof(redisReader));
if (r == NULL)
return NULL;
r->fn = fn;
r->buf = sdsempty();
r->maxbuf = REDIS_READER_MAX_BUF;
if (r->buf == NULL) {
free(r);
return NULL;
r->buf = hi_sdsempty();
if (r->buf == NULL)
goto oom;
r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));
if (r->task == NULL)
goto oom;
for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {
r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));
if (r->task[r->tasks] == NULL)
goto oom;
}
r->fn = fn;
r->maxbuf = REDIS_READER_MAX_BUF;
r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;
r->ridx = -1;
return r;
oom:
redisReaderFree(r);
return NULL;
}
void redisReaderFree(redisReader *r) {
if (r == NULL)
return;
if (r->reply != NULL && r->fn && r->fn->freeObject)
r->fn->freeObject(r->reply);
sdsfree(r->buf);
free(r);
if (r->task) {
/* We know r->task[i] is allocated if i < r->tasks */
for (int i = 0; i < r->tasks; i++) {
hi_free(r->task[i]);
}
hi_free(r->task);
}
hi_sdsfree(r->buf);
hi_free(r);
}
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
sds newbuf;
hisds newbuf;
/* Return early when this reader is in an erroneous state. */
if (r->err)
@@ -605,26 +664,25 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
/* Copy the provided buffer. */
if (buf != NULL && len >= 1) {
/* Destroy internal buffer when it is empty and is quite large. */
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
sdsfree(r->buf);
r->buf = sdsempty();
if (r->len == 0 && r->maxbuf != 0 && hi_sdsavail(r->buf) > r->maxbuf) {
hi_sdsfree(r->buf);
r->buf = hi_sdsempty();
if (r->buf == 0) goto oom;
r->pos = 0;
/* r->buf should not be NULL since we just free'd a larger one. */
assert(r->buf != NULL);
}
newbuf = sdscatlen(r->buf,buf,len);
if (newbuf == NULL) {
__redisReaderSetErrorOOM(r);
return REDIS_ERR;
}
newbuf = hi_sdscatlen(r->buf,buf,len);
if (newbuf == NULL) goto oom;
r->buf = newbuf;
r->len = sdslen(r->buf);
r->len = hi_sdslen(r->buf);
}
return REDIS_OK;
oom:
__redisReaderSetErrorOOM(r);
return REDIS_ERR;
}
int redisReaderGetReply(redisReader *r, void **reply) {
@@ -642,12 +700,12 @@ int redisReaderGetReply(redisReader *r, void **reply) {
/* Set first item to process when the stack is empty. */
if (r->ridx == -1) {
r->rstack[0].type = -1;
r->rstack[0].elements = -1;
r->rstack[0].idx = -1;
r->rstack[0].obj = NULL;
r->rstack[0].parent = NULL;
r->rstack[0].privdata = r->privdata;
r->task[0]->type = -1;
r->task[0]->elements = -1;
r->task[0]->idx = -1;
r->task[0]->obj = NULL;
r->task[0]->parent = NULL;
r->task[0]->privdata = r->privdata;
r->ridx = 0;
}
@@ -663,9 +721,9 @@ int redisReaderGetReply(redisReader *r, void **reply) {
/* Discard part of the buffer when we've consumed at least 1k, to avoid
* doing unnecessary calls to memmove() in sds.c. */
if (r->pos >= 1024) {
sdsrange(r->buf,r->pos,-1);
if (hi_sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;
r->pos = 0;
r->len = sdslen(r->buf);
r->len = hi_sdslen(r->buf);
}
/* Emit a reply when there is one. */
+10 -3
View File
@@ -63,7 +63,11 @@
#define REDIS_REPLY_BIGNUM 13
#define REDIS_REPLY_VERB 14
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
/* Default max unused reader buffer. */
#define REDIS_READER_MAX_BUF (1024*16)
/* Default multi-bulk element limit */
#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)
#ifdef __cplusplus
extern "C" {
@@ -71,7 +75,7 @@ extern "C" {
typedef struct redisReadTask {
int type;
int elements; /* number of elements in multibulk container */
long long elements; /* number of elements in multibulk container */
int idx; /* index in parent (array) object */
void *obj; /* holds user-generated value for a read task */
struct redisReadTask *parent; /* parent task */
@@ -96,8 +100,11 @@ typedef struct redisReader {
size_t pos; /* Buffer cursor */
size_t len; /* Buffer length */
size_t maxbuf; /* Max length of unused buffer */
long long maxelements; /* Max multi-bulk elements */
redisReadTask **task;
int tasks;
redisReadTask rstack[9];
int ridx; /* Index of current read task */
void *reply; /* Temporary reply pointer */
+413 -415
View File
File diff suppressed because it is too large Load Diff
+133 -131
View File
@@ -30,247 +30,249 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SDS_H
#define __SDS_H
#ifndef HIREDIS_SDS_H
#define HIREDIS_SDS_H
#define SDS_MAX_PREALLOC (1024*1024)
#define HI_SDS_MAX_PREALLOC (1024*1024)
#ifdef _MSC_VER
#define __attribute__(x)
typedef long long ssize_t;
#define SSIZE_MAX (LLONG_MAX >> 1)
#endif
#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>
typedef char *sds;
typedef char *hisds;
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
struct __attribute__ ((__packed__)) hisdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
struct __attribute__ ((__packed__)) hisdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
struct __attribute__ ((__packed__)) hisdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
struct __attribute__ ((__packed__)) hisdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
struct __attribute__ ((__packed__)) hisdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
#define SDS_TYPE_5 0
#define SDS_TYPE_8 1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
#define HI_SDS_TYPE_5 0
#define HI_SDS_TYPE_8 1
#define HI_SDS_TYPE_16 2
#define HI_SDS_TYPE_32 3
#define HI_SDS_TYPE_64 4
#define HI_SDS_TYPE_MASK 7
#define HI_SDS_TYPE_BITS 3
#define HI_SDS_HDR_VAR(T,s) struct hisdshdr##T *sh = (struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T)));
#define HI_SDS_HDR(T,s) ((struct hisdshdr##T *)((s)-(sizeof(struct hisdshdr##T))))
#define HI_SDS_TYPE_5_LEN(f) ((f)>>HI_SDS_TYPE_BITS)
static inline size_t sdslen(const sds s) {
static inline size_t hi_sdslen(const hisds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->len;
case SDS_TYPE_16:
return SDS_HDR(16,s)->len;
case SDS_TYPE_32:
return SDS_HDR(32,s)->len;
case SDS_TYPE_64:
return SDS_HDR(64,s)->len;
switch(flags & HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5:
return HI_SDS_TYPE_5_LEN(flags);
case HI_SDS_TYPE_8:
return HI_SDS_HDR(8,s)->len;
case HI_SDS_TYPE_16:
return HI_SDS_HDR(16,s)->len;
case HI_SDS_TYPE_32:
return HI_SDS_HDR(32,s)->len;
case HI_SDS_TYPE_64:
return HI_SDS_HDR(64,s)->len;
}
return 0;
}
static inline size_t sdsavail(const sds s) {
static inline size_t hi_sdsavail(const hisds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5: {
switch(flags&HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5: {
return 0;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
case HI_SDS_TYPE_8: {
HI_SDS_HDR_VAR(8,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
case HI_SDS_TYPE_16: {
HI_SDS_HDR_VAR(16,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
case HI_SDS_TYPE_32: {
HI_SDS_HDR_VAR(32,s);
return sh->alloc - sh->len;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
case HI_SDS_TYPE_64: {
HI_SDS_HDR_VAR(64,s);
return sh->alloc - sh->len;
}
}
return 0;
}
static inline void sdssetlen(sds s, size_t newlen) {
static inline void hi_sdssetlen(hisds s, size_t newlen) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
switch(flags&HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
*fp = (unsigned char)(HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS));
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len = (uint8_t)newlen;
case HI_SDS_TYPE_8:
HI_SDS_HDR(8,s)->len = (uint8_t)newlen;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len = (uint16_t)newlen;
case HI_SDS_TYPE_16:
HI_SDS_HDR(16,s)->len = (uint16_t)newlen;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len = (uint32_t)newlen;
case HI_SDS_TYPE_32:
HI_SDS_HDR(32,s)->len = (uint32_t)newlen;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len = (uint64_t)newlen;
case HI_SDS_TYPE_64:
HI_SDS_HDR(64,s)->len = (uint64_t)newlen;
break;
}
}
static inline void sdsinclen(sds s, size_t inc) {
static inline void hi_sdsinclen(hisds s, size_t inc) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
switch(flags&HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5:
{
unsigned char *fp = ((unsigned char*)s)-1;
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
unsigned char newlen = HI_SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
*fp = HI_SDS_TYPE_5 | (newlen << HI_SDS_TYPE_BITS);
}
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->len += (uint8_t)inc;
case HI_SDS_TYPE_8:
HI_SDS_HDR(8,s)->len += (uint8_t)inc;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->len += (uint16_t)inc;
case HI_SDS_TYPE_16:
HI_SDS_HDR(16,s)->len += (uint16_t)inc;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->len += (uint32_t)inc;
case HI_SDS_TYPE_32:
HI_SDS_HDR(32,s)->len += (uint32_t)inc;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->len += (uint64_t)inc;
case HI_SDS_TYPE_64:
HI_SDS_HDR(64,s)->len += (uint64_t)inc;
break;
}
}
/* sdsalloc() = sdsavail() + sdslen() */
static inline size_t sdsalloc(const sds s) {
/* hi_sdsalloc() = hi_sdsavail() + hi_sdslen() */
static inline size_t hi_sdsalloc(const hisds s) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
return SDS_TYPE_5_LEN(flags);
case SDS_TYPE_8:
return SDS_HDR(8,s)->alloc;
case SDS_TYPE_16:
return SDS_HDR(16,s)->alloc;
case SDS_TYPE_32:
return SDS_HDR(32,s)->alloc;
case SDS_TYPE_64:
return SDS_HDR(64,s)->alloc;
switch(flags & HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5:
return HI_SDS_TYPE_5_LEN(flags);
case HI_SDS_TYPE_8:
return HI_SDS_HDR(8,s)->alloc;
case HI_SDS_TYPE_16:
return HI_SDS_HDR(16,s)->alloc;
case HI_SDS_TYPE_32:
return HI_SDS_HDR(32,s)->alloc;
case HI_SDS_TYPE_64:
return HI_SDS_HDR(64,s)->alloc;
}
return 0;
}
static inline void sdssetalloc(sds s, size_t newlen) {
static inline void hi_sdssetalloc(hisds s, size_t newlen) {
unsigned char flags = s[-1];
switch(flags&SDS_TYPE_MASK) {
case SDS_TYPE_5:
switch(flags&HI_SDS_TYPE_MASK) {
case HI_SDS_TYPE_5:
/* Nothing to do, this type has no total allocation info. */
break;
case SDS_TYPE_8:
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
case HI_SDS_TYPE_8:
HI_SDS_HDR(8,s)->alloc = (uint8_t)newlen;
break;
case SDS_TYPE_16:
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
case HI_SDS_TYPE_16:
HI_SDS_HDR(16,s)->alloc = (uint16_t)newlen;
break;
case SDS_TYPE_32:
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
case HI_SDS_TYPE_32:
HI_SDS_HDR(32,s)->alloc = (uint32_t)newlen;
break;
case SDS_TYPE_64:
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
case HI_SDS_TYPE_64:
HI_SDS_HDR(64,s)->alloc = (uint64_t)newlen;
break;
}
}
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty(void);
sds sdsdup(const sds s);
void sdsfree(sds s);
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscatsds(sds s, const sds t);
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);
hisds hi_sdsnewlen(const void *init, size_t initlen);
hisds hi_sdsnew(const char *init);
hisds hi_sdsempty(void);
hisds hi_sdsdup(const hisds s);
void hi_sdsfree(hisds s);
hisds hi_sdsgrowzero(hisds s, size_t len);
hisds hi_sdscatlen(hisds s, const void *t, size_t len);
hisds hi_sdscat(hisds s, const char *t);
hisds hi_sdscatsds(hisds s, const hisds t);
hisds hi_sdscpylen(hisds s, const char *t, size_t len);
hisds hi_sdscpy(hisds s, const char *t);
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
hisds hi_sdscatvprintf(hisds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)
hisds hi_sdscatprintf(hisds s, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
hisds hi_sdscatprintf(hisds s, const char *fmt, ...);
#endif
sds sdscatfmt(sds s, char const *fmt, ...);
sds sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end);
void sdsupdatelen(sds s);
void sdsclear(sds s);
int sdscmp(const sds s1, const sds s2);
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, const char *p, size_t len);
sds *sdssplitargs(const char *line, int *argc);
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
sds sdsjoin(char **argv, int argc, char *sep);
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
hisds hi_sdscatfmt(hisds s, char const *fmt, ...);
hisds hi_sdstrim(hisds s, const char *cset);
int hi_sdsrange(hisds s, ssize_t start, ssize_t end);
void hi_sdsupdatelen(hisds s);
void hi_sdsclear(hisds s);
int hi_sdscmp(const hisds s1, const hisds s2);
hisds *hi_sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
void hi_sdsfreesplitres(hisds *tokens, int count);
void hi_sdstolower(hisds s);
void hi_sdstoupper(hisds s);
hisds hi_sdsfromlonglong(long long value);
hisds hi_sdscatrepr(hisds s, const char *p, size_t len);
hisds *hi_sdssplitargs(const char *line, int *argc);
hisds hi_sdsmapchars(hisds s, const char *from, const char *to, size_t setlen);
hisds hi_sdsjoin(char **argv, int argc, char *sep);
hisds hi_sdsjoinsds(hisds *argv, int argc, const char *sep, size_t seplen);
/* Low level functions exposed to the user API */
sds sdsMakeRoomFor(sds s, size_t addlen);
void sdsIncrLen(sds s, int incr);
sds sdsRemoveFreeSpace(sds s);
size_t sdsAllocSize(sds s);
void *sdsAllocPtr(sds s);
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen);
void hi_sdsIncrLen(hisds s, int incr);
hisds hi_sdsRemoveFreeSpace(hisds s);
size_t hi_sdsAllocSize(hisds s);
void *hi_sdsAllocPtr(hisds s);
/* Export the allocator used by SDS to the program using SDS.
* Sometimes the program SDS is linked to, may use a different set of
* allocators, but may want to allocate or free things that SDS will
* respectively free or allocate. */
void *sds_malloc(size_t size);
void *sds_realloc(void *ptr, size_t size);
void sds_free(void *ptr);
void *hi_sds_malloc(size_t size);
void *hi_sds_realloc(void *ptr, size_t size);
void hi_sds_free(void *ptr);
#ifdef REDIS_TEST
int sdsTest(int argc, char *argv[]);
int hi_sdsTest(int argc, char *argv[]);
#endif
#endif
#endif /* HIREDIS_SDS_H */
+5 -3
View File
@@ -37,6 +37,8 @@
* the include of your alternate allocator if needed (not needed in order
* to use the default libc allocator). */
#define s_malloc malloc
#define s_realloc realloc
#define s_free free
#include "alloc.h"
#define hi_s_malloc hi_malloc
#define hi_s_realloc hi_realloc
#define hi_s_free hi_free
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* SDS compatibility header.
*
* This simple file maps sds types and calls to their unique hiredis symbol names.
* It's useful when we build Hiredis as a dependency of Redis and want to call
* Hiredis' sds symbols rather than the ones built into Redis, as the libraries
* have slightly diverged and could cause hard to track down ABI incompatibility
* bugs.
*
*/
#ifndef HIREDIS_SDS_COMPAT
#define HIREDIS_SDS_COMPAT
#define sds hisds
#define sdslen hi_sdslen
#define sdsavail hi_sdsavail
#define sdssetlen hi_sdssetlen
#define sdsinclen hi_sdsinclen
#define sdsalloc hi_sdsalloc
#define sdssetalloc hi_sdssetalloc
#define sdsAllocPtr hi_sdsAllocPtr
#define sdsAllocSize hi_sdsAllocSize
#define sdscat hi_sdscat
#define sdscatfmt hi_sdscatfmt
#define sdscatlen hi_sdscatlen
#define sdscatprintf hi_sdscatprintf
#define sdscatrepr hi_sdscatrepr
#define sdscatsds hi_sdscatsds
#define sdscatvprintf hi_sdscatvprintf
#define sdsclear hi_sdsclear
#define sdscmp hi_sdscmp
#define sdscpy hi_sdscpy
#define sdscpylen hi_sdscpylen
#define sdsdup hi_sdsdup
#define sdsempty hi_sdsempty
#define sds_free hi_sds_free
#define sdsfree hi_sdsfree
#define sdsfreesplitres hi_sdsfreesplitres
#define sdsfromlonglong hi_sdsfromlonglong
#define sdsgrowzero hi_sdsgrowzero
#define sdsIncrLen hi_sdsIncrLen
#define sdsjoin hi_sdsjoin
#define sdsjoinsds hi_sdsjoinsds
#define sdsll2str hi_sdsll2str
#define sdsMakeRoomFor hi_sdsMakeRoomFor
#define sds_malloc hi_sds_malloc
#define sdsmapchars hi_sdsmapchars
#define sdsnew hi_sdsnew
#define sdsnewlen hi_sdsnewlen
#define sdsrange hi_sdsrange
#define sds_realloc hi_sds_realloc
#define sdsRemoveFreeSpace hi_sdsRemoveFreeSpace
#define sdssplitargs hi_sdssplitargs
#define sdssplitlen hi_sdssplitlen
#define sdstolower hi_sdstolower
#define sdstoupper hi_sdstoupper
#define sdstrim hi_sdstrim
#define sdsull2str hi_sdsull2str
#define sdsupdatelen hi_sdsupdatelen
#endif /* HIREDIS_SDS_COMPAT */
+1 -1
View File
@@ -212,7 +212,7 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
int ret = 0;
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
struct timeval *tv = optval;
const struct timeval *tv = optval;
DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
} else {
+2 -1
View File
@@ -49,9 +49,10 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stddef.h>
#include <errno.h>
#ifdef _MSC_VER
typedef signed long ssize_t;
typedef long long ssize_t;
#endif
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
+198 -120
View File
@@ -34,25 +34,33 @@
#include "async.h"
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "win32.h"
#include "async_private.h"
#include "hiredis_ssl.h"
void __redisSetError(redisContext *c, int type, const char *str);
/* The SSL context is attached to SSL/TLS connections as a privdata. */
typedef struct redisSSLContext {
/**
* OpenSSL SSL_CTX; It is optional and will not be set when using
* user-supplied SSL.
*/
struct redisSSLContext {
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
SSL_CTX *ssl_ctx;
/* Requested SNI, or NULL */
char *server_name;
};
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
typedef struct redisSSL {
/**
* OpenSSL SSL object.
*/
@@ -72,43 +80,11 @@ typedef struct redisSSLContext {
* should resume whenever a read takes place, if possible
*/
int pendingWrite;
} redisSSLContext;
} redisSSL;
/* Forward declaration */
redisContextFuncs redisContextSSLFuncs;
#ifdef HIREDIS_SSL_TRACE
/**
* Callback used for debugging
*/
static void sslLogCallback(const SSL *ssl, int where, int ret) {
const char *retstr = "";
int should_log = 1;
/* Ignore low-level SSL stuff */
if (where & SSL_CB_ALERT) {
should_log = 1;
}
if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {
should_log = 1;
}
if ((where & SSL_CB_EXIT) && ret == 0) {
should_log = 1;
}
if (!should_log) {
return;
}
retstr = SSL_alert_type_string(ret);
printf("ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr);
if (where == SSL_CB_HANDSHAKE_DONE) {
printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));
}
}
#endif
/**
* OpenSSL global initialization and locking handling callbacks.
* Note that this is only required for OpenSSL < 1.1.0.
@@ -119,6 +95,18 @@ static void sslLogCallback(const SSL *ssl, int where, int ret) {
#endif
#ifdef HIREDIS_USE_CRYPTO_LOCKS
#ifdef _WIN32
typedef CRITICAL_SECTION sslLockType;
static void sslLockInit(sslLockType* l) {
InitializeCriticalSection(l);
}
static void sslLockAcquire(sslLockType* l) {
EnterCriticalSection(l);
}
static void sslLockRelease(sslLockType* l) {
LeaveCriticalSection(l);
}
#else
typedef pthread_mutex_t sslLockType;
static void sslLockInit(sslLockType *l) {
pthread_mutex_init(l, NULL);
@@ -129,7 +117,9 @@ static void sslLockAcquire(sslLockType *l) {
static void sslLockRelease(sslLockType *l) {
pthread_mutex_unlock(l);
}
static pthread_mutex_t *ossl_locks;
#endif
static sslLockType* ossl_locks;
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
sslLockType *l = ossl_locks + lkid;
@@ -144,36 +134,151 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
(void)line;
}
static void initOpensslLocks(void) {
static int initOpensslLocks(void) {
unsigned ii, nlocks;
if (CRYPTO_get_locking_callback() != NULL) {
/* Someone already set the callback before us. Don't destroy it! */
return;
return REDIS_OK;
}
nlocks = CRYPTO_num_locks();
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
if (ossl_locks == NULL)
return REDIS_ERR;
for (ii = 0; ii < nlocks; ii++) {
sslLockInit(ossl_locks + ii);
}
CRYPTO_set_locking_callback(opensslDoLock);
return REDIS_OK;
}
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
int redisInitOpenSSL(void)
{
SSL_library_init();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
initOpensslLocks();
#endif
return REDIS_OK;
}
/**
* redisSSLContext helper context destruction.
*/
const char *redisSSLContextGetError(redisSSLContextError error)
{
switch (error) {
case REDIS_SSL_CTX_NONE:
return "No Error";
case REDIS_SSL_CTX_CREATE_FAILED:
return "Failed to create OpenSSL SSL_CTX";
case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
return "Client cert and key must both be specified or skipped";
case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
return "Failed to load CA Certificate or CA Path";
case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
return "Failed to load client certificate";
case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
return "Failed to load private key";
default:
return "Unknown error code";
}
}
void redisFreeSSLContext(redisSSLContext *ctx)
{
if (!ctx)
return;
if (ctx->server_name) {
hi_free(ctx->server_name);
ctx->server_name = NULL;
}
if (ctx->ssl_ctx) {
SSL_CTX_free(ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
}
hi_free(ctx);
}
/**
* redisSSLContext helper context initialization.
*/
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
const char *cert_filename, const char *private_key_filename,
const char *server_name, redisSSLContextError *error)
{
redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
if (ctx == NULL)
goto error;
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ctx->ssl_ctx) {
if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
goto error;
}
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
if ((cert_filename != NULL && private_key_filename == NULL) ||
(private_key_filename != NULL && cert_filename == NULL)) {
if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
goto error;
}
if (capath || cacert_filename) {
if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
goto error;
}
}
if (cert_filename) {
if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
goto error;
}
if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
goto error;
}
}
if (server_name)
ctx->server_name = hi_strdup(server_name);
return ctx;
error:
redisFreeSSLContext(ctx);
return NULL;
}
/**
* SSL Connection initialization.
*/
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
if (c->privdata) {
static int redisSSLConnect(redisContext *c, SSL *ssl) {
if (c->privctx) {
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
return REDIS_ERR;
}
c->privdata = calloc(1, sizeof(redisSSLContext));
redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
if (rssl == NULL) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
c->funcs = &redisContextSSLFuncs;
redisSSLContext *rssl = c->privdata;
rssl->ssl_ctx = ssl_ctx;
rssl->ssl = ssl;
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -183,12 +288,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
ERR_clear_error();
int rv = SSL_connect(rssl->ssl);
if (rv == 1) {
c->privctx = rssl;
return REDIS_OK;
}
rv = SSL_get_error(rssl->ssl, rv);
if (((c->flags & REDIS_BLOCK) == 0) &&
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
c->privctx = rssl;
return REDIS_OK;
}
@@ -203,83 +310,58 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
}
__redisSetError(c, REDIS_ERR_IO, err);
}
hi_free(rssl);
return REDIS_ERR;
}
/**
* A wrapper around redisSSLConnect() for users who manage their own context and
* create their own SSL object.
*/
int redisInitiateSSL(redisContext *c, SSL *ssl) {
return redisSSLConnect(c, NULL, ssl);
return redisSSLConnect(c, ssl);
}
int redisSecureConnection(redisContext *c, const char *capath,
const char *certpath, const char *keypath, const char *servername) {
/**
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
* manage their own SSL objects.
*/
SSL_CTX *ssl_ctx = NULL;
SSL *ssl = NULL;
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
{
if (!c || !redis_ssl_ctx)
return REDIS_ERR;
/* Initialize global OpenSSL stuff */
static int isInit = 0;
if (!isInit) {
isInit = 1;
SSL_library_init();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
initOpensslLocks();
#endif
}
/* We want to verify that redisSSLConnect() won't fail on this, as it will
* not own the SSL object in that case and we'll end up leaking.
*/
if (c->privctx)
return REDIS_ERR;
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) {
__redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
goto error;
}
#ifdef HIREDIS_SSL_TRACE
SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);
#endif
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
__redisSetError(c, REDIS_ERR_OTHER, "certpath and keypath must be specified together");
goto error;
}
if (capath) {
if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {
__redisSetError(c, REDIS_ERR_OTHER, "Invalid CA certificate");
goto error;
}
}
if (certpath) {
if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client certificate");
goto error;
}
if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client key");
goto error;
}
}
ssl = SSL_new(ssl_ctx);
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
if (!ssl) {
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
goto error;
}
if (servername) {
if (!SSL_set_tlsext_host_name(ssl, servername)) {
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't set server name indication");
if (redis_ssl_ctx->server_name) {
if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
__redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
goto error;
}
}
return redisSSLConnect(c, ssl_ctx, ssl);
return redisSSLConnect(c, ssl);
error:
if (ssl) SSL_free(ssl);
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
if (ssl)
SSL_free(ssl);
return REDIS_ERR;
}
static int maybeCheckWant(redisSSLContext *rssl, int rv) {
static int maybeCheckWant(redisSSL *rssl, int rv) {
/**
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* and true is returned. False is returned otherwise
@@ -299,23 +381,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
* Implementation of redisContextFuncs for SSL connections.
*/
static void redisSSLFreeContext(void *privdata){
redisSSLContext *rsc = privdata;
static void redisSSLFree(void *privctx){
redisSSL *rsc = privctx;
if (!rsc) return;
if (rsc->ssl) {
SSL_free(rsc->ssl);
rsc->ssl = NULL;
}
if (rsc->ssl_ctx) {
SSL_CTX_free(rsc->ssl_ctx);
rsc->ssl_ctx = NULL;
}
free(rsc);
hi_free(rsc);
}
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
redisSSLContext *rssl = c->privdata;
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
redisSSL *rssl = c->privctx;
int nread = SSL_read(rssl->ssl, buf, bufcap);
if (nread > 0) {
@@ -356,10 +434,10 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
}
}
static int redisSSLWrite(redisContext *c) {
redisSSLContext *rssl = c->privdata;
static ssize_t redisSSLWrite(redisContext *c) {
redisSSL *rssl = c->privctx;
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
size_t len = rssl->lastLen ? rssl->lastLen : hi_sdslen(c->obuf);
int rv = SSL_write(rssl->ssl, c->obuf, len);
if (rv > 0) {
@@ -380,7 +458,7 @@ static int redisSSLWrite(redisContext *c) {
static void redisSSLAsyncRead(redisAsyncContext *ac) {
int rv;
redisSSLContext *rssl = ac->c.privdata;
redisSSL *rssl = ac->c.privctx;
redisContext *c = &ac->c;
rssl->wantRead = 0;
@@ -410,7 +488,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
int rv, done = 0;
redisSSLContext *rssl = ac->c.privdata;
redisSSL *rssl = ac->c.privctx;
redisContext *c = &ac->c;
rssl->pendingWrite = 0;
@@ -439,7 +517,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
redisContextFuncs redisContextSSLFuncs = {
.free_privdata = redisSSLFreeContext,
.free_privctx = redisSSLFree,
.async_read = redisSSLAsyncRead,
.async_write = redisSSLAsyncWrite,
.read = redisSSLRead,
+490 -91
View File
@@ -1,22 +1,24 @@
#include "fmacros.h"
#include "sockcompat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <strings.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#endif
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include "hiredis.h"
#include "async.h"
#ifdef HIREDIS_TEST_SSL
#include "hiredis_ssl.h"
#endif
#include "net.h"
#include "win32.h"
enum connection_type {
CONN_TCP,
@@ -47,15 +49,35 @@ struct config {
} ssl;
};
struct privdata {
int dtor_counter;
};
struct pushCounters {
int nil;
int str;
};
#ifdef HIREDIS_TEST_SSL
redisSSLContext *_ssl_ctx = NULL;
#endif
/* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0;
static int tests = 0, fails = 0, skips = 0;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
#define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; }
static long long usec(void) {
#ifndef _MSC_VER
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
#else
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10;
#endif
}
/* The assert() calls below have side effects, so we need assert()
@@ -65,6 +87,43 @@ static long long usec(void) {
#define assert(e) (void)(e)
#endif
/* Helper to extract Redis version information. Aborts on any failure. */
#define REDIS_VERSION_FIELD "redis_version:"
void get_redis_version(redisContext *c, int *majorptr, int *minorptr) {
redisReply *reply;
char *eptr, *s, *e;
int major, minor;
reply = redisCommand(c, "INFO");
if (reply == NULL || c->err || reply->type != REDIS_REPLY_STRING)
goto abort;
if ((s = strstr(reply->str, REDIS_VERSION_FIELD)) == NULL)
goto abort;
s += strlen(REDIS_VERSION_FIELD);
/* We need a field terminator and at least 'x.y.z' (5) bytes of data */
if ((e = strstr(s, "\r\n")) == NULL || (e - s) < 5)
goto abort;
/* Extract version info */
major = strtol(s, &eptr, 10);
if (*eptr != '.') goto abort;
minor = strtol(eptr+1, NULL, 10);
/* Push info the caller wants */
if (majorptr) *majorptr = major;
if (minorptr) *minorptr = minor;
freeReplyObject(reply);
return;
abort:
freeReplyObject(reply);
fprintf(stderr, "Error: Cannot determine Redis version, aborting\n");
exit(1);
}
static redisContext *select_database(redisContext *c) {
redisReply *reply;
@@ -87,6 +146,26 @@ static redisContext *select_database(redisContext *c) {
return c;
}
/* Switch protocol */
static void send_hello(redisContext *c, int version) {
redisReply *reply;
int expected;
reply = redisCommand(c, "HELLO %d", version);
expected = version == 3 ? REDIS_REPLY_MAP : REDIS_REPLY_ARRAY;
assert(reply != NULL && reply->type == expected);
freeReplyObject(reply);
}
/* Togggle client tracking */
static void send_client_tracking(redisContext *c, const char *str) {
redisReply *reply;
reply = redisCommand(c, "CLIENT TRACKING %s", str);
assert(reply != NULL && reply->type == REDIS_REPLY_STATUS);
freeReplyObject(reply);
}
static int disconnect(redisContext *c, int keep_fd) {
redisReply *reply;
@@ -105,9 +184,9 @@ static int disconnect(redisContext *c, int keep_fd) {
return -1;
}
static void do_ssl_handshake(redisContext *c, struct config config) {
static void do_ssl_handshake(redisContext *c) {
#ifdef HIREDIS_TEST_SSL
redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);
redisInitiateSSLWithContext(c, _ssl_ctx);
if (c->err) {
printf("SSL error: %s\n", c->errstr);
redisFree(c);
@@ -115,7 +194,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
}
#else
(void) c;
(void) config;
#endif
}
@@ -150,7 +228,7 @@ static redisContext *do_connect(struct config config) {
}
if (config.type == CONN_SSL) {
do_ssl_handshake(c, config);
do_ssl_handshake(c);
}
return select_database(c);
@@ -160,7 +238,7 @@ static void do_reconnect(redisContext *c, struct config config) {
redisReconnect(c);
if (config.type == CONN_SSL) {
do_ssl_handshake(c, config);
do_ssl_handshake(c);
}
}
@@ -172,43 +250,43 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"SET foo bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
hi_free(cmd);
test("Format command with %%s string interpolation: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
hi_free(cmd);
test("Format command with %%s and an empty string: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
hi_free(cmd);
test("Format command with an empty string in between proper interpolations: ");
len = redisFormatCommand(&cmd,"SET %s %s","","foo");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(0+2)+4+(3+2));
free(cmd);
hi_free(cmd);
test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
hi_free(cmd);
test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
hi_free(cmd);
test("Format command with literal %%: ");
len = redisFormatCommand(&cmd,"SET %% %%");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd);
hi_free(cmd);
/* Vararg width depends on the type. These tests make sure that the
* width is correctly determined using the format and subsequent varargs
@@ -219,7 +297,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
hi_free(cmd); \
} while(0)
#define FLOAT_WIDTH_TEST(type) do { \
@@ -228,7 +306,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd); \
hi_free(cmd); \
} while(0)
INTEGER_WIDTH_TEST("d", int);
@@ -259,29 +337,29 @@ static void test_format_commands(void) {
len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
hi_free(cmd);
test("Format command by passing argc/argv with lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,lens);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd);
hi_free(cmd);
sds sds_cmd;
hisds sds_cmd;
sds_cmd = sdsempty();
test("Format command into sds by passing argc/argv without lengths: ");
sds_cmd = NULL;
test("Format command into hisds by passing argc/argv without lengths: ");
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
sdsfree(sds_cmd);
hi_sdsfree(sds_cmd);
sds_cmd = sdsempty();
test("Format command into sds by passing argc/argv with lengths: ");
sds_cmd = NULL;
test("Format command into hisds by passing argc/argv with lengths: ");
len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
sdsfree(sds_cmd);
hi_sdsfree(sds_cmd);
}
static void test_append_formatted_commands(struct config config) {
@@ -300,7 +378,7 @@ static void test_append_formatted_commands(struct config config) {
assert(redisGetReply(c, (void*)&reply) == REDIS_OK);
free(cmd);
hi_free(cmd);
freeReplyObject(reply);
disconnect(c, 0);
@@ -308,7 +386,7 @@ static void test_append_formatted_commands(struct config config) {
static void test_reply_reader(void) {
redisReader *reader;
void *reply;
void *reply, *root;
int ret;
int i;
@@ -332,16 +410,26 @@ static void test_reply_reader(void) {
strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
redisReaderFree(reader);
test("Set error on nested multi bulks with depth > 7: ");
reader = redisReaderCreate();
for (i = 0; i < 9; i++) {
redisReaderFeed(reader,(char*)"*1\r\n",4);
test("Can handle arbitrarily nested multi-bulks: ");
for (i = 0; i < 128; i++) {
redisReaderFeed(reader,(char*)"*1\r\n", 4);
}
redisReaderFeed(reader,(char*)"$6\r\nLOLWUT\r\n",12);
ret = redisReaderGetReply(reader,&reply);
root = reply; /* Keep track of the root reply */
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
((redisReply*)reply)->elements == 1);
ret = redisReaderGetReply(reader,NULL);
test_cond(ret == REDIS_ERR &&
strncasecmp(reader->errstr,"No support for",14) == 0);
test("Can parse arbitrarily nested multi-bulks correctly: ");
while(i--) {
assert(reply != NULL && ((redisReply*)reply)->type == REDIS_REPLY_ARRAY);
reply = ((redisReply*)reply)->element[0];
}
test_cond(((redisReply*)reply)->type == REDIS_REPLY_STRING &&
!memcmp(((redisReply*)reply)->str, "LOLWUT", 6));
freeReplyObject(root);
redisReaderFree(reader);
test("Correctly parses LLONG_MAX: ");
@@ -400,6 +488,16 @@ static void test_reply_reader(void) {
freeReplyObject(reply);
redisReaderFree(reader);
test("Can configure maximum multi-bulk elements: ");
reader = redisReaderCreate();
reader->maxelements = 1024;
redisReaderFeed(reader, "*1025\r\n", 7);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_ERR &&
strcasecmp(reader->errstr, "Multi-bulk length out of range") == 0);
freeReplyObject(reply);
redisReaderFree(reader);
#if LLONG_MAX > SIZE_MAX
test("Set error when array > SIZE_MAX: ");
reader = redisReaderCreate();
@@ -459,6 +557,32 @@ static void test_reply_reader(void) {
((redisReply*)reply)->elements == 0);
freeReplyObject(reply);
redisReaderFree(reader);
/* RESP3 verbatim strings (GitHub issue #802) */
test("Can parse RESP3 verbatim strings: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_VERB &&
!memcmp(((redisReply*)reply)->str,"LOLWUT", 6));
freeReplyObject(reply);
redisReaderFree(reader);
/* RESP3 push messages (Github issue #815) */
test("Can parse RESP3 push messages: ");
reader = redisReaderCreate();
redisReaderFeed(reader,(char*)">2\r\n$6\r\nLOLWUT\r\n:42\r\n",21);
ret = redisReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK &&
((redisReply*)reply)->type == REDIS_REPLY_PUSH &&
((redisReply*)reply)->elements == 2 &&
((redisReply*)reply)->element[0]->type == REDIS_REPLY_STRING &&
!memcmp(((redisReply*)reply)->element[0]->str,"LOLWUT",6) &&
((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER &&
((redisReply*)reply)->element[1]->integer == 42);
freeReplyObject(reply);
redisReaderFree(reader);
}
static void test_free_null(void) {
@@ -474,6 +598,47 @@ static void test_free_null(void) {
test_cond(reply == NULL);
}
static void *hi_malloc_fail(size_t size) {
(void)size;
return NULL;
}
static void *hi_calloc_fail(size_t nmemb, size_t size) {
(void)nmemb;
(void)size;
return NULL;
}
static void *hi_realloc_fail(void *ptr, size_t size) {
(void)ptr;
(void)size;
return NULL;
}
static void test_allocator_injection(void) {
hiredisAllocFuncs ha = {
.mallocFn = hi_malloc_fail,
.callocFn = hi_calloc_fail,
.reallocFn = hi_realloc_fail,
.strdupFn = strdup,
.freeFn = free,
};
// Override hiredis allocators
hiredisSetAllocators(&ha);
test("redisContext uses injected allocators: ");
redisContext *c = redisConnect("localhost", 6379);
test_cond(c == NULL);
test("redisReader uses injected allocators: ");
redisReader *reader = redisReaderCreate();
test_cond(reader == NULL);
// Return allocators to default
hiredisResetAllocators();
}
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
static void test_blocking_connection_errors(void) {
redisContext *c;
@@ -491,19 +656,19 @@ static void test_blocking_connection_errors(void) {
(strcmp(c->errstr, "Name or service not known") == 0 ||
strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 ||
strcmp(c->errstr, "Name does not resolve") == 0 ||
strcmp(c->errstr,
"nodename nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "nodename nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "No address associated with hostname") == 0 ||
strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
strcmp(c->errstr,
"hostname nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "no address associated with name") == 0));
strcmp(c->errstr, "hostname nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "no address associated with name") == 0 ||
strcmp(c->errstr, "No such host is known. ") == 0));
redisFree(c);
} else {
printf("Skipping NXDOMAIN test. Found evil ISP!\n");
freeaddrinfo(ai_tmp);
}
#ifndef _WIN32
test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 1);
test_cond(c->err == REDIS_ERR_IO &&
@@ -514,11 +679,166 @@ static void test_blocking_connection_errors(void) {
c = redisConnectUnix((char*)"/tmp/idontexist.sock");
test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
redisFree(c);
#endif
}
/* Test push handler */
void push_handler(void *privdata, void *r) {
struct pushCounters *pcounts = privdata;
redisReply *reply = r, *payload;
assert(reply && reply->type == REDIS_REPLY_PUSH && reply->elements == 2);
payload = reply->element[1];
if (payload->type == REDIS_REPLY_ARRAY) {
payload = payload->element[0];
}
if (payload->type == REDIS_REPLY_STRING) {
pcounts->str++;
} else if (payload->type == REDIS_REPLY_NIL) {
pcounts->nil++;
}
freeReplyObject(reply);
}
/* Dummy function just to test setting a callback with redisOptions */
void push_handler_async(redisAsyncContext *ac, void *reply) {
(void)ac;
(void)reply;
}
static void test_resp3_push_handler(redisContext *c) {
struct pushCounters pc = {0};
redisPushFn *old = NULL;
redisReply *reply;
void *privdata;
/* Switch to RESP3 and turn on client tracking */
send_hello(c, 3);
send_client_tracking(c, "ON");
privdata = c->privdata;
c->privdata = &pc;
reply = redisCommand(c, "GET key:0");
assert(reply != NULL);
freeReplyObject(reply);
test("RESP3 PUSH messages are handled out of band by default: ");
reply = redisCommand(c, "SET key:0 val:0");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
freeReplyObject(reply);
assert((reply = redisCommand(c, "GET key:0")) != NULL);
freeReplyObject(reply);
old = redisSetPushCallback(c, push_handler);
test("We can set a custom RESP3 PUSH handler: ");
reply = redisCommand(c, "SET key:0 val:0");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.str == 1);
freeReplyObject(reply);
test("We properly handle a NIL invalidation payload: ");
reply = redisCommand(c, "FLUSHDB");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.nil == 1);
freeReplyObject(reply);
/* Unset the push callback and generate an invalidate message making
* sure it is not handled out of band. */
test("With no handler, PUSH replies come in-band: ");
redisSetPushCallback(c, NULL);
assert((reply = redisCommand(c, "GET key:0")) != NULL);
freeReplyObject(reply);
assert((reply = redisCommand(c, "SET key:0 invalid")) != NULL);
test_cond(reply->type == REDIS_REPLY_PUSH);
freeReplyObject(reply);
test("With no PUSH handler, no replies are lost: ");
assert(redisGetReply(c, (void**)&reply) == REDIS_OK);
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS);
freeReplyObject(reply);
/* Return to the originally set PUSH handler */
assert(old != NULL);
redisSetPushCallback(c, old);
/* Switch back to RESP2 and disable tracking */
c->privdata = privdata;
send_client_tracking(c, "OFF");
send_hello(c, 2);
}
redisOptions get_redis_tcp_options(struct config config) {
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, config.tcp.host, config.tcp.port);
return options;
}
static void test_resp3_push_options(struct config config) {
redisAsyncContext *ac;
redisContext *c;
redisOptions options;
test("We set a default RESP3 handler for redisContext: ");
options = get_redis_tcp_options(config);
assert((c = redisConnectWithOptions(&options)) != NULL);
test_cond(c->push_cb != NULL);
redisFree(c);
test("We don't set a default RESP3 push handler for redisAsyncContext: ");
options = get_redis_tcp_options(config);
assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
test_cond(ac->c.push_cb == NULL);
redisAsyncFree(ac);
test("Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: ");
options = get_redis_tcp_options(config);
options.options |= REDIS_OPT_NO_PUSH_AUTOFREE;
assert((c = redisConnectWithOptions(&options)) != NULL);
test_cond(c->push_cb == NULL);
redisFree(c);
test("We can use redisOptions to set a custom PUSH handler for redisContext: ");
options = get_redis_tcp_options(config);
options.push_cb = push_handler;
assert((c = redisConnectWithOptions(&options)) != NULL);
test_cond(c->push_cb == push_handler);
redisFree(c);
test("We can use redisOptions to set a custom PUSH handler for redisAsyncContext: ");
options = get_redis_tcp_options(config);
options.async_push_cb = push_handler_async;
assert((ac = redisAsyncConnectWithOptions(&options)) != NULL);
test_cond(ac->push_cb == push_handler_async);
redisAsyncFree(ac);
}
void free_privdata(void *privdata) {
struct privdata *data = privdata;
data->dtor_counter++;
}
static void test_privdata_hooks(struct config config) {
struct privdata data = {0};
redisOptions options;
redisContext *c;
test("We can use redisOptions to set privdata: ");
options = get_redis_tcp_options(config);
REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata);
assert((c = redisConnectWithOptions(&options)) != NULL);
test_cond(c->privdata == &data);
test("Our privdata destructor fires when we free the context: ");
redisFree(c);
test_cond(data.dtor_counter == 1);
}
static void test_blocking_connection(struct config config) {
redisContext *c;
redisReply *reply;
int major;
c = do_connect(config);
@@ -591,14 +911,42 @@ static void test_blocking_connection(struct config config) {
strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply);
/* Make sure passing NULL to redisGetReply is safe */
test("Can pass NULL to redisGetReply: ");
assert(redisAppendCommand(c, "PING") == REDIS_OK);
test_cond(redisGetReply(c, NULL) == REDIS_OK);
get_redis_version(c, &major, NULL);
if (major >= 6) test_resp3_push_handler(c);
test_resp3_push_options(config);
test_privdata_hooks(config);
disconnect(c, 0);
}
/* Send DEBUG SLEEP 0 to detect if we have this command */
static int detect_debug_sleep(redisContext *c) {
int detected;
redisReply *reply = redisCommand(c, "DEBUG SLEEP 0\r\n");
if (reply == NULL || c->err) {
const char *cause = c->err ? c->errstr : "(none)";
fprintf(stderr, "Error testing for DEBUG SLEEP (Redis error: %s), exiting\n", cause);
exit(-1);
}
detected = reply->type == REDIS_REPLY_STATUS;
freeReplyObject(reply);
return detected;
}
static void test_blocking_connection_timeouts(struct config config) {
redisContext *c;
redisReply *reply;
ssize_t s;
const char *cmd = "DEBUG SLEEP 3\r\n";
const char *sleep_cmd = "DEBUG SLEEP 3\r\n";
struct timeval tv;
c = do_connect(config);
@@ -615,14 +963,24 @@ static void test_blocking_connection_timeouts(struct config config) {
c = do_connect(config);
test("Does not return a reply when the command times out: ");
redisAppendFormattedCommand(c, cmd, strlen(cmd));
s = c->funcs->write(c);
tv.tv_sec = 0;
tv.tv_usec = 10000;
redisSetTimeout(c, tv);
reply = redisCommand(c, "GET foo");
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0);
freeReplyObject(reply);
if (detect_debug_sleep(c)) {
redisAppendFormattedCommand(c, sleep_cmd, strlen(sleep_cmd));
s = c->funcs->write(c);
tv.tv_sec = 0;
tv.tv_usec = 10000;
redisSetTimeout(c, tv);
reply = redisCommand(c, "GET foo");
#ifndef _WIN32
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO &&
strcmp(c->errstr, "Resource temporarily unavailable") == 0);
#else
test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_TIMEOUT &&
strcmp(c->errstr, "recv timeout") == 0);
#endif
freeReplyObject(reply);
} else {
test_skipped();
}
test("Reconnect properly reconnects after a timeout: ");
do_reconnect(c, config);
@@ -649,18 +1007,7 @@ static void test_blocking_io_errors(struct config config) {
/* Connect to target given by config. */
c = do_connect(config);
{
/* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:";
char *p, *eptr;
reply = redisCommand(c,"INFO");
p = strstr(reply->str,field);
major = strtol(p+strlen(field),&eptr,10);
p = eptr+1; /* char next to the first "." */
minor = strtol(p,&eptr,10);
freeReplyObject(reply);
}
get_redis_version(c, &major, &minor);
test("Returns I/O error when the connection is lost: ");
reply = redisCommand(c,"QUIT");
@@ -674,6 +1021,7 @@ static void test_blocking_io_errors(struct config config) {
test_cond(reply == NULL);
}
#ifndef _WIN32
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
@@ -681,14 +1029,19 @@ static void test_blocking_io_errors(struct config config) {
* conditions, the error will be set to EOF. */
assert(c->err == REDIS_ERR_EOF &&
strcmp(c->errstr,"Server closed the connection") == 0);
#endif
redisFree(c);
c = do_connect(config);
test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK);
test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
c->err == REDIS_ERR_IO && errno == EAGAIN);
int respcode = redisGetReply(c,&_reply);
#ifndef _WIN32
test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_IO && errno == EAGAIN);
#else
test_cond(respcode == REDIS_ERR && c->err == REDIS_ERR_TIMEOUT);
#endif
redisFree(c);
}
@@ -716,6 +1069,18 @@ static void test_invalid_timeout_errors(struct config config) {
redisFree(c);
}
/* Wrap malloc to abort on failure so OOM checks don't make the test logic
* harder to follow. */
void *hi_malloc_safe(size_t size) {
void *ptr = hi_malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Error: Out of memory\n");
exit(-1);
}
return ptr;
}
static void test_throughput(struct config config) {
redisContext *c = do_connect(config);
redisReply **replies;
@@ -727,7 +1092,7 @@ static void test_throughput(struct config config) {
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
num = 1000;
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"PING");
@@ -735,10 +1100,10 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
@@ -747,10 +1112,10 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
t1 = usec();
for (i = 0; i < num; i++) {
replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
@@ -758,11 +1123,11 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
num = 10000;
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"PING");
t1 = usec();
@@ -772,10 +1137,10 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"LRANGE mylist 0 499");
t1 = usec();
@@ -786,10 +1151,10 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*num);
replies = hi_malloc_safe(sizeof(redisReply*)*num);
for (i = 0; i < num; i++)
redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
t1 = usec();
@@ -799,7 +1164,7 @@ static void test_throughput(struct config config) {
}
t2 = usec();
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
free(replies);
hi_free(replies);
printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
disconnect(c, 0);
@@ -916,9 +1281,8 @@ int main(int argc, char **argv) {
};
int throughput = 1;
int test_inherit_fd = 1;
/* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN);
int skips_as_fails = 0;
int test_unix_socket;
/* Parse command line options. */
argv++; argc--;
@@ -936,6 +1300,8 @@ int main(int argc, char **argv) {
throughput = 0;
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
test_inherit_fd = 0;
} else if (argc >= 1 && !strcmp(argv[0],"--skips-as-fails")) {
skips_as_fails = 1;
#ifdef HIREDIS_TEST_SSL
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) {
argv++; argc--;
@@ -960,6 +1326,19 @@ int main(int argc, char **argv) {
argv++; argc--;
}
#ifndef _WIN32
/* Ignore broken pipe signal (for I/O error tests). */
signal(SIGPIPE, SIG_IGN);
test_unix_socket = access(cfg.unix_sock.path, F_OK) == 0;
#else
/* Unix sockets don't exist in Windows */
test_unix_socket = 0;
#endif
test_allocator_injection();
test_format_commands();
test_reply_reader();
test_blocking_connection_errors();
@@ -974,15 +1353,25 @@ int main(int argc, char **argv) {
test_append_formatted_commands(cfg);
if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path);
cfg.type = CONN_UNIX;
test_blocking_connection(cfg);
test_blocking_connection_timeouts(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
printf("\nTesting against Unix socket connection (%s): ", cfg.unix_sock.path);
if (test_unix_socket) {
printf("\n");
cfg.type = CONN_UNIX;
test_blocking_connection(cfg);
test_blocking_connection_timeouts(cfg);
test_blocking_io_errors(cfg);
if (throughput) test_throughput(cfg);
} else {
test_skipped();
}
#ifdef HIREDIS_TEST_SSL
if (cfg.ssl.port && cfg.ssl.host) {
redisInitOpenSSL();
_ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL);
assert(_ssl_ctx != NULL);
printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port);
cfg.type = CONN_SSL;
@@ -992,21 +1381,31 @@ int main(int argc, char **argv) {
test_invalid_timeout_errors(cfg);
test_append_formatted_commands(cfg);
if (throughput) test_throughput(cfg);
redisFreeSSLContext(_ssl_ctx);
_ssl_ctx = NULL;
}
#endif
if (test_inherit_fd) {
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
cfg.type = CONN_FD;
test_blocking_connection(cfg);
printf("\nTesting against inherited fd (%s): ", cfg.unix_sock.path);
if (test_unix_socket) {
printf("\n");
cfg.type = CONN_FD;
test_blocking_connection(cfg);
} else {
test_skipped();
}
}
if (fails) {
if (fails || (skips_as_fails && skips)) {
printf("*** %d TESTS FAILED ***\n", fails);
if (skips) {
printf("*** %d TESTS SKIPPED ***\n", skips);
}
return 1;
}
printf("ALL TESTS PASSED\n");
printf("ALL TESTS PASSED (%d skipped)\n", skips);
return 0;
}
+9 -1
View File
@@ -4,7 +4,9 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
REDIS_PORT=${REDIS_PORT:-56379}
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
TEST_SSL=${TEST_SSL:-0}
SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0}
SSL_TEST_ARGS=
SKIPS_ARG=
tmpdir=$(mktemp -d)
PID_FILE=${tmpdir}/hiredis-test-redis.pid
@@ -67,4 +69,10 @@ fi
cat ${tmpdir}/redis.conf
${REDIS_SERVER} ${tmpdir}/redis.conf
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}
# Wait until we detect the unix socket
while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done
# Treat skips as failures if directed
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="--skips-as-fails"
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG}
+1 -1
View File
@@ -512,7 +512,7 @@ CTARGET='-o $@'
LDTARGET='-o $@'
TEST_LD_MODE=
EXTRA_LDFLAGS=
ARFLAGS='crus'
ARFLAGS='crs'
AROUT=' $@'
CC_MM=1
+1 -1
View File
@@ -362,7 +362,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
} \
} while (0)
#define GET_ARG_NUMERIC(val, len) do { \
switch (len) { \
switch ((unsigned char)len) { \
case '?': \
val = va_arg(ap, int); \
break; \
+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
@@ -575,7 +575,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
lf.extraline = 0;
lf.extraline = 0;
}
ungetc(c, lf.f);
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
+2 -2
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;
@@ -493,7 +493,7 @@ static void f_parser (lua_State *L, void *ud) {
Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = (luaY_parser)(L, p->z,
&p->buff, p->name);
+1 -1
View File
@@ -137,7 +137,7 @@ static void addfield (lua_State *L, luaL_Buffer *b, int i) {
if (!lua_isstring(L, -1))
luaL_error(L, "invalid value (%s) at index %d in table for "
LUA_QL("concat"), luaL_typename(L, -1), i);
luaL_addvalue(b);
luaL_addvalue(b);
}
+339 -120
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 Redis Sentinel. Since Redis 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,27 +46,33 @@
################################## NETWORK #####################################
# By default, if no "bind" configuration directive is specified, Redis 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.
# Each address can be prefixed by "-", which means that redis will not fail to
# start if the address is not available. Being not available only refers to
# addresses that does not correspond to any network interfece. Addresses that
# are already in use will always fail, and unsupported protocols will always BE
# silently skipped.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
# bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses
# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6
# bind * -::* # like the default, all available interfaces
#
# ~~~ WARNING ~~~ If the computer running Redis 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 Redis to listen only into
# the IPv4 loopback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
# following bind directive, that will force Redis to listen only on the
# IPv4 and IPv6 (if available) loopback interface addresses (this means Redis
# 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
bind 127.0.0.1 -::1
# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
@@ -93,8 +99,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.
@@ -106,7 +112,7 @@ tcp-backlog 511
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocket /run/redis.sock
# unixsocketperm 700
# Close the connection after a client is idle for N seconds (0 to disable)
@@ -118,8 +124,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.
@@ -144,6 +150,28 @@ tcp-keepalive 300
#
# tls-cert-file redis.crt
# tls-key-file redis.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-key-file-pass secret
# Normally Redis uses the same certificate for both server functions (accepting
# connections) and client functions (replicating from a master, establishing
# cluster bus connections, etc.).
#
# Sometimes certificates are issued with attributes that designate them as
# client-only or server-only certificates. In that case it may be desired to use
# different certificates for incoming (server) and outgoing (client)
# connections. To do that, use the following directives:
#
# tls-client-cert-file client.crt
# tls-client-key-file client.key
#
# If the key file is encrypted using a passphrase, it can be included here
# as well.
#
# tls-client-key-file-pass secret
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
#
@@ -159,9 +187,12 @@ 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
# with its master.
@@ -175,9 +206,12 @@ tcp-keepalive 300
#
# tls-cluster yes
# Explicitly specify TLS versions to support. Allowed values are case insensitive
# and include "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" (OpenSSL >= 1.1.1) or
# any combination. To enable only TLSv1.2 and TLSv1.3, use:
# By default, only TLSv1.2 and TLSv1.3 are enabled and it is highly recommended
# that older formally deprecated versions are kept disabled to reduce the attack surface.
# You can explicitly specify TLS versions to support.
# Allowed values are case insensitive and include "TLSv1", "TLSv1.1", "TLSv1.2",
# "TLSv1.3" (OpenSSL >= 1.1.1) or any combination.
# To enable only TLSv1.2 and TLSv1.3, use:
#
# tls-protocols "TLSv1.2 TLSv1.3"
@@ -219,18 +253,26 @@ tcp-keepalive 300
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
# When Redis is supervised by upstart or systemd, this parameter has no impact.
daemonize no
# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# requires "expect stop" in your upstart job config
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# on startup, and updating Redis status on a regular
# basis.
# 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.
supervised no
# They do not enable continuous pings back to your supervisor.
#
# The default is "no". To run under upstart/systemd, you can simply uncomment
# the line below:
#
# supervised auto
# If a pid file is specified, Redis writes it where specified at startup
# and removes it at exit.
@@ -241,6 +283,9 @@ supervised no
#
# Creating a pid file is best effort: if Redis is not able to create it
# nothing bad happens, the server will start and run normally.
#
# Note that on modern Linux systems "/run/redis.pid" is more conforming
# and should be used instead.
pidfile /var/run/redis_6379.pid
# Specify the server verbosity level.
@@ -266,44 +311,76 @@ logfile ""
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
# To disable the built in crash log, which will possibly produce cleaner core
# dumps when they are needed, uncomment the following:
#
# crash-log-enabled no
# To disable the fast memory check that's run as part of the crash log, which
# will possibly let redis terminate sooner, uncomment the following:
#
# crash-memcheck-enabled no
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16
# By default Redis shows an ASCII art logo only when started to log to the
# standard output and if the standard output is a TTY. Basically this means
# that normally a logo is displayed only in interactive sessions.
# standard output and if the standard output is a TTY and syslog logging is
# disabled. Basically this means that normally a logo is displayed only in
# interactive sessions.
#
# However it is possible to force the pre-4.0 behavior and always show a
# ASCII art logo in startup logs by setting the following option to yes.
always-show-logo yes
always-show-logo no
# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
# provide some runtime information. It is possible to disable this and leave
# the process name as executed by setting the following to no.
set-proc-title yes
# When changing the process title, Redis uses the following template to construct
# the modified title.
#
# Template variables are specified in curly brackets. The following variables are
# supported:
#
# {title} Name of process as executed if parent, or type of child process.
# {listen-addr} Bind address or '*' followed by TCP or TLS port listening on, or
# Unix socket if only that's available.
# {server-mode} Special mode, i.e. "[sentinel]" or "[cluster]".
# {port} TCP port listening on, or 0.
# {tls-port} TLS port listening on, or 0.
# {unixsocket} Unix domain socket listening on, or "".
# {config-file} Name of configuration file used.
#
proc-title-template "{title} {listen-addr} {server-mode}"
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1
save 300 10
save 60 10000
# Save the DB to disk.
#
# save <seconds> <changes>
#
# Redis will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
# * After 3600 seconds (an hour) if at least 1 key changed
# * After 300 seconds (5 minutes) if at least 100 keys changed
# * After 60 seconds if at least 10000 keys changed
#
# You can set these explicitly by uncommenting the three following lines.
#
# save 3600 1
# save 300 100
# save 60 10000
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
@@ -321,7 +398,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
@@ -335,6 +412,21 @@ rdbcompression yes
# tell the loading code to skip the check.
rdbchecksum yes
# Enables or disables full sanitation checks for ziplist and listpack etc when
# loading an RDB or RESTORE payload. This reduces the chances of a assertion or
# crash later on while processing commands.
# Options:
# no - Never perform full sanitation
# yes - Always perform full sanitation
# clients - Perform full sanitation only for user connections.
# Excludes: RDB files, RESTORE commands received from the master
# connection, and client connections which have the
# skip-sanitize-payload ACL flag.
# The default should be 'clients' but since it currently affects cluster
# resharding via MIGRATE, it is temporarily set to 'no' by default.
#
# sanitize-dump-payload no
# The filename where to dump the DB
dbfilename dump.rdb
@@ -409,11 +501,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
@@ -479,12 +571,12 @@ repl-diskless-sync-delay 5
# 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
# stage with the master. Use only if your do what you are doing.
# stage with the master. Use only if you know 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
@@ -514,7 +606,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
@@ -539,21 +632,21 @@ 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.
#
@@ -574,6 +667,18 @@ repl-disable-tcp-nodelay no
# By default the priority is 100.
replica-priority 100
# -----------------------------------------------------------------------------
# By default, Redis Sentinel includes all replicas in its reports. A replica
# can be excluded from Redis Sentinel's announcements. An unannounced replica
# will be ignored by the 'sentinel replicas <master>' command and won't be
# exposed to Redis Sentinel's clients.
#
# This option does not change the behavior of replica-priority. Even with
# replica-announced set to 'no', the replica can be promoted to master. To
# prevent this behavior, set replica-priority to 0.
#
# replica-announced yes
# It is possible for a master to stop accepting writes if there are less than
# N replicas connected, having a lag less or equal than M seconds.
#
@@ -603,8 +708,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.
@@ -614,7 +719,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.
@@ -629,9 +734,9 @@ replica-priority 100
# Redis 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
# a radix key indexed by key name, what clients have which 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
#
@@ -663,7 +768,7 @@ replica-priority 100
################################## SECURITY ###################################
# Warning: since Redis is pretty fast an outside user can try up to
# Warning: since Redis 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
@@ -687,12 +792,14 @@ 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
# with this user, however the already authenticated connections
# will still work.
# skip-sanitize-payload RESTORE dump-payload sanitation is skipped.
# sanitize-payload RESTORE dump-payload is sanitized (default).
# +<command> Allow the execution of that command
# -<command> Disallow the execution of that command
# +@<category> Allow the execution of all the commands in such category
@@ -715,7 +822,12 @@ 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.
# &<pattern> Add a glob-style pattern of Pub/Sub channels that can be
# accessed by the user. It is possible to specify multiple channel
# patterns.
# allchannels Alias for &*
# resetchannels Flush the list of allowed channel patterns.
# ><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.
@@ -769,7 +881,7 @@ acllog-max-len 128
#
# 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
@@ -777,14 +889,38 @@ acllog-max-len 128
#
# aclfile /etc/redis/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>
# if they follow the new protocol: both will work.
#
# The requirepass is not compatable with aclfile option and the ACL LOAD
# command, these will cause requirepass to be ignored.
#
# requirepass foobared
# New users are initialized with restrictive permissions by default, via the
# equivalent of this ACL rule 'off resetkeys -@all'. Starting with Redis 6.2, it
# is possible to manage access to Pub/Sub channels with ACL rules as well. The
# default Pub/Sub channels permission if new users is controlled by the
# acl-pubsub-default configuration directive, which accepts one of these values:
#
# allchannels: grants access to all Pub/Sub channels
# resetchannels: revokes access to all Pub/Sub channels
#
# To ensure backward compatibility while upgrading Redis 6.0, acl-pubsub-default
# defaults to the 'allchannels' permission.
#
# Future compatibility note: it is very likely that in a future version of Redis
# the directive's default of 'allchannels' will be changed to 'resetchannels' in
# order to provide better out-of-the-box Pub/Sub security. Therefore, it is
# recommended that you explicitly define Pub/Sub permissions for all users
# rather then rely on implicit default values. Once you've set explicit
# Pub/Sub for all existing users, you should uncomment the following line.
#
# acl-pubsub-default resetchannels
# Command renaming (DEPRECATED).
#
# ------------------------------------------------------------------------
@@ -873,14 +1009,12 @@ acllog-max-len 128
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
# Note: with any of the above policies, when there are no suitable keys for
# eviction, Redis will return an error on write operations that require
# more memory. These are usually commands that create new keys, add data or
# modify existing keys. A few examples are: SET, INCR, HSET, LPUSH, SUNIONSTORE,
# SORT (due to the STORE argument), and EXEC (if the transaction includes any
# command that requires memory).
#
# The default is:
#
@@ -888,8 +1022,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 Redis 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 Redis 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
@@ -897,6 +1031,14 @@ acllog-max-len 128
#
# maxmemory-samples 5
# Eviction processing is designed to function well with the default setting.
# If there is an unusually large amount of write traffic, this value may need to
# be increased. Decreasing this value may reduce latency at the risk of
# eviction processing effectiveness
# 0 = minimum latency, 10 = default, 100 = process without regard to latency
#
# maxmemory-eviction-tenacity 10
# Starting from Redis 5, by default a replica will ignore its maxmemory setting
# (unless it is promoted to master after a failover or manually). It means
# that the eviction of keys will be just handled by the master, sending the
@@ -929,8 +1071,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
@@ -990,6 +1132,13 @@ replica-lazy-flush no
lazyfree-lazy-user-del no
# FLUSHDB, FLUSHALL, and SCRIPT FLUSH support both asynchronous and synchronous
# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the
# commands. When neither flag is passed, this directive will be used to determine
# if the data should be deleted asynchronously.
lazyfree-lazy-user-flush no
################################ THREADED I/O #################################
# Redis is mostly single threaded, however there are certain threaded
@@ -998,7 +1147,7 @@ lazyfree-lazy-user-del no
#
# 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
# Redis users use pipelining in order to speed up 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.
@@ -1016,7 +1165,7 @@ lazyfree-lazy-user-del no
#
# io-threads 4
#
# Setting io-threads to 1 will just use the main thread as usually.
# Setting io-threads to 1 will just use the main thread as usual.
# 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
@@ -1033,9 +1182,53 @@ lazyfree-lazy-user-del no
#
# 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
# --threads option to match the number of Redis threads, otherwise you'll not
# be able to notice the improvements.
############################ KERNEL OOM CONTROL ##############################
# On Linux, it is possible to hint the kernel OOM killer on what processes
# should be killed first when out of memory.
#
# Enabling this feature makes Redis 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.
#
# Redis supports three options:
#
# 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).
#
# 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
#################### KERNEL transparent hugepage CONTROL ######################
# Usually the kernel Transparent Huge Pages control is set to "madvise" or
# or "never" by default (/sys/kernel/mm/transparent_hugepage/enabled), in which
# case this config has no effect. On systems in which it is set to "always",
# redis will attempt to disable it specifically for the redis process in order
# to avoid latency problems specifically with fork(2) and CoW.
# If for some reason you prefer to keep it enabled, you can set this config to
# "no" and the kernel global to "always".
disable-thp yes
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
@@ -1054,7 +1247,7 @@ lazyfree-lazy-user-del no
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
# Please check https://redis.io/topics/persistence for more information.
appendonly no
@@ -1160,8 +1353,8 @@ aof-load-truncated yes
#
# [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# When loading, Redis 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
@@ -1175,7 +1368,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.
@@ -1201,7 +1394,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
@@ -1228,18 +1421,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
@@ -1263,14 +1456,23 @@ lua-time-limit 5000
# master in your cluster.
#
# Default is 1 (replicas migrate only if their masters remain with at least
# one replica). To disable migration just set it to a very large value.
# one replica). To disable migration just set it to a very large value or
# set cluster-allow-replica-migration to 'no'.
# A value of 0 can be set but is useful only for debugging and dangerous
# in production.
#
# cluster-migration-barrier 1
# Turning off this option allows to use less automatic cluster configuration.
# It both disables migration to orphaned masters and migration from masters
# that became empty.
#
# Default is 'yes' (allow automatic migrations).
#
# cluster-allow-replica-migration yes
# By default Redis 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.
@@ -1283,7 +1485,7 @@ lua-time-limit 5000
# cluster-require-full-coverage yes
# This option, when set to yes, prevents replicas from trying to failover its
# master during master failures. However the master can still perform a
# master during master failures. However the replica can still perform a
# manual failover, if forced to do so.
#
# This is useful in different scenarios, especially in the case of multiple
@@ -1309,7 +1511,7 @@ lua-time-limit 5000
# cluster-allow-reads-when-down no
# In order to setup your cluster make sure to read the documentation
# available at http://redis.io web site.
# available at https://redis.io web site.
########################## CLUSTER DOCKER/NAT support ########################
@@ -1319,16 +1521,21 @@ lua-time-limit 5000
#
# In order to make Redis Cluster working in such environments, a static
# configuration where each node knows its public address is needed. The
# following two options are used for this scope, and are:
# following four options are used for this scope, and are:
#
# * cluster-announce-ip
# * cluster-announce-port
# * cluster-announce-tls-port
# * cluster-announce-bus-port
#
# Each instruct 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.
# Each instructs the node about its address, client ports (for connections
# without and with TLS) 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.
#
# If cluster-tls is set to yes and cluster-announce-tls-port is omitted or set
# to zero, then cluster-announce-port refers to the TLS port. Note also that
# cluster-announce-tls-port has no effect if cluster-tls is set to no.
#
# If the above options are not used, the normal Redis Cluster auto-detection
# will be used instead.
@@ -1336,12 +1543,13 @@ 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:
#
# cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379
# cluster-announce-tls-port 6379
# cluster-announce-port 0
# cluster-announce-bus-port 6380
################################## SLOW LOG ###################################
@@ -1392,7 +1600,7 @@ latency-monitor-threshold 0
############################# EVENT NOTIFICATION ##############################
# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications
# This feature is documented at https://redis.io/topics/notifications
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
@@ -1415,8 +1623,9 @@ latency-monitor-threshold 0
# x Expired events (events generated every time a key expires)
# e Evicted events (events generated when a key is evicted for maxmemory)
# t Stream commands
# d Module key type events
# m Key-miss events (Note: It is not included in the 'A' class)
# A Alias for g$lshzxet, so that the "AKE" string means all the events
# A Alias for g$lshzxetd, so that the "AKE" string means all the events
# (Except key-miss events which are excluded from 'A' due to their
# unique nature).
#
@@ -1465,7 +1674,7 @@ notify-keyspace-events ""
# two kind of inline requests that were anyway illegal: an empty request
# or any request that starts with "/" (there are no Redis 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.
# path of the Gopher protocol implementation and are served as usual as well.
#
# If you open a connection to Redis when Gopher is enabled and send it
# a string like "/foo", if there is a key named "/foo" it is served via the
@@ -1489,8 +1698,11 @@ notify-keyspace-events ""
#
# 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.
# Note that Gopher is not currently supported when 'io-threads-do-reads'
# is enabled.
#
# To enable Gopher support, uncomment the following line and set the option
# from no (the default) to yes.
#
# gopher-enabled no
@@ -1566,7 +1778,7 @@ hll-sparse-max-bytes 3000
# maximum number of items it may contain before switching to a new node when
# appending new stream entries. If any of the following settings are set to
# zero, the limit is ignored, so for instance it is possible to set just a
# max entires limit by setting max-bytes to 0 and max-entries to the desired
# max entries limit by setting max-bytes to 0 and max-entries to the desired
# value.
stream-node-max-bytes 4096
stream-node-max-entries 100
@@ -1637,8 +1849,8 @@ client-output-buffer-limit pubsub 32mb 8mb 60
# client-query-buffer-limit 1gb
# In the Redis 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
@@ -1666,7 +1878,7 @@ hz 10
#
# Since the default HZ value by default is conservatively set to 10, Redis
# 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
@@ -1733,7 +1945,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
@@ -1753,7 +1965,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) Redis will start to create new copies of the
@@ -1830,3 +2042,10 @@ jemalloc-bg-thread yes
#
# Set bgsave child process to cpu affinity 1,10,11
# bgsave_cpulist 1,10-11
# In some cases redis 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
+1 -1
View File
@@ -8,7 +8,7 @@ done
if [ -z $TCLSH ]
then
echo "You need tcl 8.5 or newer in order to run the Redis Sentinel test"
echo "You need tcl 8.5 or newer in order to run the Redis Cluster test"
exit 1
fi
$TCLSH tests/cluster/run.tcl $*
+12 -2
View File
@@ -1,6 +1,7 @@
#!/bin/sh
TCL_VERSIONS="8.5 8.6"
TCLSH=""
[ -z "$MAKE" ] && MAKE=make
for VERSION in $TCL_VERSIONS; do
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
@@ -8,11 +9,11 @@ done
if [ -z $TCLSH ]
then
echo "You need tcl 8.5 or newer in order to run the Redis test"
echo "You need tcl 8.5 or newer in order to run the Redis ModuleApi test"
exit 1
fi
make -C tests/modules && \
$MAKE -C tests/modules && \
$TCLSH tests/test_helper.tcl \
--single unit/moduleapi/commandfilter \
--single unit/moduleapi/fork \
@@ -22,7 +23,16 @@ $TCLSH tests/test_helper.tcl \
--single unit/moduleapi/hooks \
--single unit/moduleapi/misc \
--single unit/moduleapi/blockonkeys \
--single unit/moduleapi/blockonbackground \
--single unit/moduleapi/scan \
--single unit/moduleapi/datatype \
--single unit/moduleapi/auth \
--single unit/moduleapi/keyspace_events \
--single unit/moduleapi/blockedclient \
--single unit/moduleapi/getkeys \
--single unit/moduleapi/test_lazyfree \
--single unit/moduleapi/defrag \
--single unit/moduleapi/hash \
--single unit/moduleapi/zset \
--single unit/moduleapi/stream \
"${@}"
+78 -1
View File
@@ -124,6 +124,42 @@ sentinel monitor mymaster 127.0.0.1 6379 2
# Default is 30 seconds.
sentinel down-after-milliseconds mymaster 30000
# IMPORTANT NOTE: starting with Redis 6.2 ACL capability is supported for
# Sentinel mode, please refer to the Redis website https://redis.io/topics/acl
# for more details.
# Sentinel's ACL users are defined in the following format:
#
# user <username> ... acl rules ...
#
# For example:
#
# user worker +@admin +@connection ~* on >ffa9203c493aa99
#
# For more information about ACL configuration please refer to the Redis
# website at https://redis.io/topics/acl and redis server configuration
# template redis.conf.
# ACL LOG
#
# 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 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 external
# ACL file, the server will refuse to start.
#
# The format of the external ACL user file is exactly the same as the
# format that is used inside redis.conf to describe users.
#
# aclfile /etc/redis/sentinel-users.acl
# requirepass <password>
#
# You can configure Sentinel itself to require a password, however when doing
@@ -131,6 +167,29 @@ sentinel down-after-milliseconds mymaster 30000
# other Sentinels. So you need to configure all your Sentinels in a given
# group with the same "requirepass" password. Check the following documentation
# for more info: https://redis.io/topics/sentinel
#
# IMPORTANT NOTE: starting with Redis 6.2 "requirepass" is a compatibility
# layer on top of the 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>
# if they follow the new protocol: both will work.
#
# New config files are advised to use separate authentication control for
# incoming connections (via ACL), and for outgoing connections (via
# sentinel-user and sentinel-pass)
#
# The requirepass is not compatable with aclfile option and the ACL LOAD
# command, these will cause requirepass to be ignored.
# sentinel sentinel-user <username>
#
# You can configure Sentinel to authenticate with other Sentinels with specific
# user name.
# sentinel sentinel-pass <password>
#
# The password for Sentinel to authenticate with other Sentinels. If sentinel-user
# is not configured, Sentinel will use 'default' user with sentinel-pass to authenticate.
# sentinel parallel-syncs <master-name> <numreplicas>
#
@@ -259,6 +318,24 @@ 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
# HOSTNAMES SUPPORT
#
# Normally Sentinel uses only IP addresses and requires SENTINEL MONITOR
# to specify an IP address. Also, it requires the Redis replica-announce-ip
# keyword to specify only IP addresses.
#
# You may enable hostnames support by enabling resolve-hostnames. Note
# that you must make sure your DNS is configured properly and that DNS
# resolution does not introduce very long delays.
#
SENTINEL resolve-hostnames no
# When resolve-hostnames is enabled, Sentinel still uses IP addresses
# when exposing instances to users, configuration files, etc. If you want
# to retain the hostnames when announced, enable announce-hostnames below.
#
SENTINEL announce-hostnames no
+91 -38
View File
@@ -16,12 +16,17 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
OPTIMIZATION?=-O2
DEPENDENCY_TARGETS=hiredis linenoise lua
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram
NODEPS:=clean distclean
# Default settings
STD=-std=c11 -pedantic -DREDIS_STATIC=''
STD=-pedantic -DREDIS_STATIC=''
# Use -Wno-c11-extensions on clang, either where explicitly used or on
# platforms we can assume it's being used.
ifneq (,$(findstring clang,$(CC)))
STD+=-Wno-c11-extensions
else
ifneq (,$(findstring FreeBSD,$(uname_S)))
STD+=-Wno-c11-extensions
endif
@@ -29,6 +34,16 @@ endif
WARN=-Wall -W -Wno-missing-field-initializers
OPT=$(OPTIMIZATION)
# Detect if the compiler supports C11 _Atomic
C11_ATOMIC := $(shell sh -c 'echo "\#include <stdatomic.h>" > foo.c; \
$(CC) -std=c11 -c foo.c -o foo.o > /dev/null 2>&1; \
if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c')
ifeq ($(C11_ATOMIC),yes)
STD+=-std=c11
else
STD+=-std=c99
endif
PREFIX?=/usr/local
INSTALL_BIN=$(PREFIX)/bin
INSTALL=install
@@ -89,9 +104,11 @@ endif
ifeq ($(uname_S),SunOS)
# SunOS
ifneq ($(@@),32bit)
CFLAGS+= -m64
LDFLAGS+= -m64
ifeq ($(findstring -m32,$(FINAL_CFLAGS)),)
CFLAGS+=-m64
endif
ifeq ($(findstring -m32,$(FINAL_LDFLAGS)),)
LDFLAGS+=-m64
endif
DEBUG=-g
DEBUG_FLAGS=-g
@@ -103,8 +120,18 @@ else
ifeq ($(uname_S),Darwin)
# Darwin
FINAL_LIBS+= -ldl
# Homebrew's OpenSSL is not linked to /usr/local to avoid
# conflicts with the system's LibreSSL installation so it
# must be referenced explicitly during build.
ifeq ($(uname_M),arm64)
# Homebrew arm64 uses /opt/homebrew as HOMEBREW_PREFIX
OPENSSL_CFLAGS=-I/opt/homebrew/opt/openssl/include
OPENSSL_LDFLAGS=-L/opt/homebrew/opt/openssl/lib
else
# Homebrew x86/ppc uses /usr/local as HOMEBREW_PREFIX
OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include
OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib
endif
else
ifeq ($(uname_S),AIX)
# AIX
@@ -121,12 +148,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)
@@ -136,6 +172,12 @@ 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
@@ -148,11 +190,15 @@ endif
endif
endif
endif
endif
endif
# Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
# Determine systemd support and/or build preference (defaulting to auto-detection)
BUILD_WITH_SYSTEMD=no
LIBSYSTEMD_LIBS=-lsystemd
# If 'USE_SYSTEMD' in the environment is neither "no" nor "yes", try to
# auto-detect libsystemd's presence and link accordingly.
ifneq ($(USE_SYSTEMD),no)
@@ -161,17 +207,18 @@ ifneq ($(USE_SYSTEMD),no)
# (unless a later check tells us otherwise)
ifeq ($(LIBSYSTEMD_PKGCONFIG),0)
BUILD_WITH_SYSTEMD=yes
LIBSYSTEMD_LIBS=$(shell $(PKG_CONFIG) --libs libsystemd)
endif
endif
# If 'USE_SYSTEMD' is set to "yes" use pkg-config if available or fall back to
# default -lsystemd.
ifeq ($(USE_SYSTEMD),yes)
ifneq ($(LIBSYSTEMD_PKGCONFIG),0)
$(error USE_SYSTEMD is set to "$(USE_SYSTEMD)", but $(PKG_CONFIG) cannot find libsystemd)
endif
# Force building with libsystemd
BUILD_WITH_SYSTEMD=yes
endif
ifeq ($(BUILD_WITH_SYSTEMD),yes)
FINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)
FINAL_LIBS+=$(LIBSYSTEMD_LIBS)
FINAL_CFLAGS+= -DHAVE_LIBSYSTEMD
endif
@@ -209,6 +256,17 @@ endif
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
endif
ifndef V
define MAKE_INSTALL
@printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$(1)$(ENDCOLOR) 1>&2
@$(INSTALL) $(1) $(2)
endef
else
define MAKE_INSTALL
$(INSTALL) $(1) $(2)
endef
endif
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
@@ -226,15 +284,15 @@ QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(EN
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
endif
REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o 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 lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o
REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o
REDIS_BENCHMARK_NAME=redis-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o
REDIS_CHECK_RDB_NAME=redis-check-rdb
REDIS_CHECK_AOF_NAME=redis-check-aof
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX)
REDIS_SENTINEL_NAME=redis-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 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 lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o
REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX)
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o
REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
REDIS_CHECK_AOF_NAME=redis-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 ""
@@ -255,6 +313,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 LDFLAGS=$(LDFLAGS) >> .make-settings
echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings
@@ -300,10 +360,7 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
# redis-benchmark
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
dict-benchmark: dict.c zmalloc.c sds.c siphash.c
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
-include $(DEP)
@@ -315,7 +372,7 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
$(REDIS_CC) -MMD -o $@ -c $<
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 redis.info lcov-html Makefile.dep
rm -f $(DEP)
.PHONY: clean
@@ -326,10 +383,10 @@ distclean: clean
.PHONY: distclean
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME)
@(cd ..; ./runtest)
test-sentinel: $(REDIS_SENTINEL_NAME)
test-sentinel: $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME)
@(cd ..; ./runtest-sentinel)
check: test
@@ -340,10 +397,6 @@ lcov:
@geninfo -o redis.info .
@genhtml --legend -o lcov-html redis.info
test-sds: sds.c sds.h
$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
/tmp/sds_test
.PHONY: lcov
bench: $(REDIS_BENCHMARK_NAME)
@@ -365,18 +418,18 @@ valgrind:
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
helgrind:
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS"
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc" CFLAGS="-D__ATOMIC_VAR_FORCE_SYNC_MACROS" REDIS_CFLAGS="-I/usr/local/include" REDIS_LDFLAGS="-L/usr/local/lib"
src/help.h:
@../utils/generate-command-help.rb > help.h
install: all
@mkdir -p $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_RDB_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
$(call MAKE_INSTALL,$(REDIS_SERVER_NAME),$(INSTALL_BIN))
$(call MAKE_INSTALL,$(REDIS_BENCHMARK_NAME),$(INSTALL_BIN))
$(call MAKE_INSTALL,$(REDIS_CLI_NAME),$(INSTALL_BIN))
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
uninstall:
+390 -93
View File
@@ -53,6 +53,10 @@ list *UsersToLoad; /* This is a list of users found in the configuration file
list *ACLLog; /* Our security log, the user is able to inspect that
using the ACL LOG command .*/
static rax *commandId = NULL; /* Command name to id mapping */
static unsigned long nextid = 0; /* Next command id that has not been assigned */
struct ACLCategoryItem {
const char *name;
uint64_t flag;
@@ -85,11 +89,15 @@ struct ACLUserFlag {
const char *name;
uint64_t flag;
} ACLUserFlags[] = {
/* Note: the order here dictates the emitted order at ACLDescribeUser */
{"on", USER_FLAG_ENABLED},
{"off", USER_FLAG_DISABLED},
{"allkeys", USER_FLAG_ALLKEYS},
{"allchannels", USER_FLAG_ALLCHANNELS},
{"allcommands", USER_FLAG_ALLCOMMANDS},
{"nopass", USER_FLAG_NOPASS},
{"skip-sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD_SKIP},
{"sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD},
{NULL,0} /* Terminator. */
};
@@ -166,15 +174,15 @@ sds ACLHashPassword(unsigned char *cleartext, size_t len) {
return sdsnewlen(hex,HASH_PASSWORD_LEN);
}
/* Given a hash and the hash length, returns C_OK if it is a valid password
/* Given a hash and the hash length, returns C_OK if it is a valid password
* hash, or C_ERR otherwise. */
int ACLCheckPasswordHash(unsigned char *hash, int hashlen) {
if (hashlen != HASH_PASSWORD_LEN) {
return C_ERR;
return C_ERR;
}
/* Password hashes can only be characters that represent
* hexadecimal values, which are numbers and lowercase
* hexadecimal values, which are numbers and lowercase
* characters 'a' through 'f'. */
for(int i = 0; i < HASH_PASSWORD_LEN; i++) {
char c = hash[i];
@@ -237,16 +245,20 @@ user *ACLCreateUser(const char *name, size_t namelen) {
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
user *u = zmalloc(sizeof(*u));
u->name = sdsnewlen(name,namelen);
u->flags = USER_FLAG_DISABLED;
u->flags = USER_FLAG_DISABLED | server.acl_pubsub_default;
u->allowed_subcommands = NULL;
u->passwords = listCreate();
u->patterns = listCreate();
u->channels = listCreate();
listSetMatchMethod(u->passwords,ACLListMatchSds);
listSetFreeMethod(u->passwords,ACLListFreeSds);
listSetDupMethod(u->passwords,ACLListDupSds);
listSetMatchMethod(u->patterns,ACLListMatchSds);
listSetFreeMethod(u->patterns,ACLListFreeSds);
listSetDupMethod(u->patterns,ACLListDupSds);
listSetMatchMethod(u->channels,ACLListMatchSds);
listSetFreeMethod(u->channels,ACLListFreeSds);
listSetDupMethod(u->channels,ACLListDupSds);
memset(u->allowed_commands,0,sizeof(u->allowed_commands));
raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
return u;
@@ -275,6 +287,7 @@ void ACLFreeUser(user *u) {
sdsfree(u->name);
listRelease(u->passwords);
listRelease(u->patterns);
listRelease(u->channels);
ACLResetSubcommands(u);
zfree(u);
}
@@ -289,7 +302,7 @@ void ACLFreeUserAndKillClients(user *u) {
while ((ln = listNext(&li)) != NULL) {
client *c = listNodeValue(ln);
if (c->user == u) {
/* We'll free the conenction asynchronously, so
/* We'll free the connection asynchronously, so
* in theory to set a different user is not needed.
* However if there are bugs in Redis, soon or later
* this may result in some security hole: it's much
@@ -297,7 +310,13 @@ void ACLFreeUserAndKillClients(user *u) {
* it in non authenticated mode. */
c->user = 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 == server.current_client) {
c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
} else {
freeClientAsync(c);
}
}
}
ACLFreeUser(u);
@@ -309,8 +328,10 @@ void ACLFreeUserAndKillClients(user *u) {
void ACLCopyUser(user *dst, user *src) {
listRelease(dst->passwords);
listRelease(dst->patterns);
listRelease(dst->channels);
dst->passwords = listDup(src->passwords);
dst->patterns = listDup(src->patterns);
dst->channels = listDup(src->channels);
memcpy(dst->allowed_commands,src->allowed_commands,
sizeof(dst->allowed_commands));
dst->flags = src->flags;
@@ -469,21 +490,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. */
@@ -545,9 +613,10 @@ sds ACLDescribeUser(user *u) {
/* Flags. */
for (int j = 0; ACLUserFlags[j].flag; j++) {
/* Skip the allcommands and allkeys flags because they'll be emitted
* later as ~* and +@all. */
/* Skip the allcommands, allkeys and allchannels flags because they'll
* be emitted later as +@all, ~* and &*. */
if (ACLUserFlags[j].flag == USER_FLAG_ALLKEYS ||
ACLUserFlags[j].flag == USER_FLAG_ALLCHANNELS ||
ACLUserFlags[j].flag == USER_FLAG_ALLCOMMANDS) continue;
if (u->flags & ACLUserFlags[j].flag) {
res = sdscat(res,ACLUserFlags[j].name);
@@ -579,6 +648,20 @@ sds ACLDescribeUser(user *u) {
}
}
/* Pub/sub channel patterns. */
if (u->flags & USER_FLAG_ALLCHANNELS) {
res = sdscatlen(res,"&* ",3);
} else {
res = sdscatlen(res,"resetchannels ",14);
listRewind(u->channels,&li);
while((ln = listNext(&li))) {
sds thispat = listNodeValue(ln);
res = sdscatlen(res,"&",1);
res = sdscatsds(res,thispat);
res = sdscatlen(res," ",1);
}
}
/* Command rules. */
sds rules = ACLDescribeUserCommandRules(u);
res = sdscatsds(res,rules);
@@ -624,7 +707,6 @@ void ACLResetSubcommands(user *u) {
u->allowed_subcommands = NULL;
}
/* Add a subcommand to the list of subcommands for the user 'u' and
* the command id specified. */
void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
@@ -685,6 +767,12 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
* It is possible to specify multiple patterns.
* allkeys Alias for ~*
* resetkeys Flush the list of allowed keys patterns.
* &<pattern> Add a pattern of channels that can be mentioned as part of
* Pub/Sub commands. For instance &* allows all the channels. The
* pattern is a glob-style pattern like the one of PSUBSCRIBE.
* It is possible to specify multiple patterns.
* allchannels Alias for &*
* resetchannels Flush the list of allowed keys patterns.
* ><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).
@@ -723,14 +811,14 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
*
* When an error is returned, errno is set to the following values:
*
* EINVAL: The specified opcode is not understood or the key pattern is
* EINVAL: The specified opcode is not understood or the key/channel pattern is
* invalid (contains non allowed characters).
* ENOENT: The command name or command category provided with + or - is not
* known.
* EBUSY: The subcommand you want to add is about a command that is currently
* fully added.
* EEXIST: You are adding a key pattern after "*" was already added. This is
* almost surely an error on the user side.
* EISDIR: You are adding a channel pattern after "*" was already added. This is
* almost surely an error on the user side.
* ENODEV: The password you are trying to remove from the user does not exist.
* EBADMSG: The hash you are trying to add is not a valid hash.
*/
@@ -743,6 +831,12 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
} else if (!strcasecmp(op,"off")) {
u->flags |= USER_FLAG_DISABLED;
u->flags &= ~USER_FLAG_ENABLED;
} else if (!strcasecmp(op,"skip-sanitize-payload")) {
u->flags |= USER_FLAG_SANITIZE_PAYLOAD_SKIP;
u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD;
} else if (!strcasecmp(op,"sanitize-payload")) {
u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD_SKIP;
u->flags |= USER_FLAG_SANITIZE_PAYLOAD;
} else if (!strcasecmp(op,"allkeys") ||
!strcasecmp(op,"~*"))
{
@@ -751,6 +845,14 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
} else if (!strcasecmp(op,"resetkeys")) {
u->flags &= ~USER_FLAG_ALLKEYS;
listEmpty(u->patterns);
} else if (!strcasecmp(op,"allchannels") ||
!strcasecmp(op,"&*"))
{
u->flags |= USER_FLAG_ALLCHANNELS;
listEmpty(u->channels);
} else if (!strcasecmp(op,"resetchannels")) {
u->flags &= ~USER_FLAG_ALLCHANNELS;
listEmpty(u->channels);
} else if (!strcasecmp(op,"allcommands") ||
!strcasecmp(op,"+@all"))
{
@@ -818,12 +920,29 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
}
sds newpat = sdsnewlen(op+1,oplen-1);
listNode *ln = listSearchKey(u->patterns,newpat);
/* Avoid re-adding the same pattern multiple times. */
/* Avoid re-adding the same key pattern multiple times. */
if (ln == NULL)
listAddNodeTail(u->patterns,newpat);
else
sdsfree(newpat);
u->flags &= ~USER_FLAG_ALLKEYS;
} else if (op[0] == '&') {
if (u->flags & USER_FLAG_ALLCHANNELS) {
errno = EISDIR;
return C_ERR;
}
if (ACLStringHasSpaces(op+1,oplen-1)) {
errno = EINVAL;
return C_ERR;
}
sds newpat = sdsnewlen(op+1,oplen-1);
listNode *ln = listSearchKey(u->channels,newpat);
/* Avoid re-adding the same channel pattern multiple times. */
if (ln == NULL)
listAddNodeTail(u->channels,newpat);
else
sdsfree(newpat);
u->flags &= ~USER_FLAG_ALLCHANNELS;
} else if (op[0] == '+' && op[1] != '@') {
if (strchr(op,'|') == NULL) {
if (ACLLookupCommand(op+1) == NULL) {
@@ -856,22 +975,12 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
return C_ERR;
}
/* The command should not be set right now in the command
* bitmap, because adding a subcommand of a fully added
* command is probably an error on the user side. */
unsigned long id = ACLGetCommandID(copy);
if (ACLGetUserCommandBit(u,id) == 1) {
zfree(copy);
errno = EBUSY;
return C_ERR;
/* Add the subcommand to the list of valid ones, if the command is not set. */
if (ACLGetUserCommandBit(u,id) == 0) {
ACLAddAllowedSubcommand(u,id,sub);
}
/* Add the subcommand to the list of valid ones. */
ACLAddAllowedSubcommand(u,id,sub);
/* We have to clear the command bit so that we force the
* subcommand check. */
ACLSetUserCommandBit(u,id,0);
zfree(copy);
}
} else if (op[0] == '-' && op[1] != '@') {
@@ -891,7 +1000,11 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
} else if (!strcasecmp(op,"reset")) {
serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK);
serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK);
serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK);
if (server.acl_pubsub_default & USER_FLAG_ALLCHANNELS)
serverAssert(ACLSetUser(u,"allchannels",-1) == C_OK);
serverAssert(ACLSetUser(u,"off",-1) == C_OK);
serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK);
serverAssert(ACLSetUser(u,"-@all",-1) == C_OK);
} else {
errno = EINVAL;
@@ -902,21 +1015,22 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
/* Return a description of the error that occurred in ACLSetUser() according to
* the errno value set by the function on error. */
char *ACLSetUserStringError(void) {
char *errmsg = "Wrong format";
const char *ACLSetUserStringError(void) {
const char *errmsg = "Wrong format";
if (errno == ENOENT)
errmsg = "Unknown command or category name in ACL";
else if (errno == EINVAL)
errmsg = "Syntax error";
else if (errno == EBUSY)
errmsg = "Adding a subcommand of a command already fully "
"added is not allowed. Remove the command to start. "
"Example: -DEBUG +DEBUG|DIGEST";
else if (errno == EEXIST)
errmsg = "Adding a pattern after the * pattern (or the "
"'allkeys' flag) is not valid and does not have any "
"effect. Try 'resetkeys' to start with an empty "
"list of patterns";
else if (errno == EISDIR)
errmsg = "Adding a pattern after the * pattern (or the "
"'allchannels' flag) is not valid and does not have any "
"effect. Try 'resetchannels' to start with an empty "
"list of channels";
else if (errno == ENODEV)
errmsg = "The password you are trying to remove from the user does "
"not exist";
@@ -932,6 +1046,7 @@ void ACLInitDefaultUser(void) {
DefaultUser = ACLCreateUser("default",7);
ACLSetUser(DefaultUser,"+@all",-1);
ACLSetUser(DefaultUser,"~*",-1);
ACLSetUser(DefaultUser,"&*",-1);
ACLSetUser(DefaultUser,"on",-1);
ACLSetUser(DefaultUser,"nopass",-1);
}
@@ -942,7 +1057,6 @@ void ACLInit(void) {
UsersToLoad = listCreate();
ACLLog = listCreate();
ACLInitDefaultUser();
server.requirepass = NULL; /* Only used for backward compatibility. */
}
/* Check the username and password pair and return C_OK if they are valid,
@@ -1012,18 +1126,16 @@ int ACLAuthenticateUser(client *c, robj *username, robj *password) {
* command name, so that a command retains the same ID in case of modules that
* are unloaded and later reloaded. */
unsigned long ACLGetCommandID(const char *cmdname) {
static rax *map = NULL;
static unsigned long nextid = 0;
sds lowername = sdsnew(cmdname);
sdstolower(lowername);
if (map == NULL) map = raxNew();
void *id = raxFind(map,(unsigned char*)lowername,sdslen(lowername));
if (commandId == NULL) commandId = raxNew();
void *id = raxFind(commandId,(unsigned char*)lowername,sdslen(lowername));
if (id != raxNotFound) {
sdsfree(lowername);
return (unsigned long)id;
}
raxInsert(map,(unsigned char*)lowername,strlen(lowername),
raxInsert(commandId,(unsigned char*)lowername,strlen(lowername),
(void*)nextid,NULL);
sdsfree(lowername);
unsigned long thisid = nextid;
@@ -1041,6 +1153,13 @@ unsigned long ACLGetCommandID(const char *cmdname) {
return thisid;
}
/* Clear command id table and reset nextid to 0. */
void ACLClearCommandID(void) {
if (commandId) raxFree(commandId);
commandId = NULL;
nextid = 0;
}
/* Return an username by its name, or NULL if the user does not exist. */
user *ACLGetUserByName(const char *name, size_t namelen) {
void *myuser = raxFind(Users,(unsigned char*)name,namelen);
@@ -1064,9 +1183,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
/* If there is no associated user, the connection can run anything. */
if (u == NULL) return ACL_OK;
/* Check if the user can execute this command. */
if (!(u->flags & USER_FLAG_ALLCOMMANDS) &&
c->cmd->proc != authCommand)
/* Check if the user can execute this command or if the command
* doesn't need to be authenticated (hello, auth). */
if (!(u->flags & USER_FLAG_ALLCOMMANDS) && !(c->cmd->flags & CMD_NO_AUTH))
{
/* If the bit is not set we have to check further, in case the
* command is allowed just with that specific subcommand. */
@@ -1096,8 +1215,9 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
if (!(c->user->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;
@@ -1118,11 +1238,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
@@ -1130,6 +1250,135 @@ int ACLCheckCommandPerm(client *c, int *keyidxptr) {
return ACL_OK;
}
/* Check if the provided channel is whitelisted by the given allowed channels
* list. Glob-style pattern matching is employed, unless the literal flag is
* set. Returns ACL_OK if access is granted or ACL_DENIED_CHANNEL otherwise. */
int ACLCheckPubsubChannelPerm(sds channel, list *allowed, int literal) {
listIter li;
listNode *ln;
size_t clen = sdslen(channel);
int match = 0;
listRewind(allowed,&li);
while((ln = listNext(&li))) {
sds pattern = listNodeValue(ln);
size_t plen = sdslen(pattern);
if ((literal && !sdscmp(pattern,channel)) ||
(!literal && stringmatchlen(pattern,plen,channel,clen,0)))
{
match = 1;
break;
}
}
if (!match) {
return ACL_DENIED_CHANNEL;
}
return ACL_OK;
}
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
* permissions specified via the upcoming argument, and kill them if so. */
void ACLKillPubsubClientsIfNeeded(user *u, list *upcoming) {
listIter li, lpi;
listNode *ln, *lpn;
robj *o;
int kill = 0;
/* Nothing to kill when the upcoming are a literal super set of the original
* permissions. */
listRewind(u->channels,&li);
while (!kill && ((ln = listNext(&li)) != NULL)) {
sds pattern = listNodeValue(ln);
kill = (ACLCheckPubsubChannelPerm(pattern,upcoming,1) ==
ACL_DENIED_CHANNEL);
}
if (!kill) return;
/* Scan all connected clients to find the user's pub/subs. */
listRewind(server.clients,&li);
while ((ln = listNext(&li)) != NULL) {
client *c = listNodeValue(ln);
kill = 0;
if (c->user == u && getClientType(c) == CLIENT_TYPE_PUBSUB) {
/* Check for pattern violations. */
listRewind(c->pubsub_patterns,&lpi);
while (!kill && ((lpn = listNext(&lpi)) != NULL)) {
o = lpn->value;
kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,1) ==
ACL_DENIED_CHANNEL);
}
/* Check for channel violations. */
if (!kill) {
dictIterator *di = dictGetIterator(c->pubsub_channels);
dictEntry *de;
while (!kill && ((de = dictNext(di)) != NULL)) {
o = dictGetKey(de);
kill = (ACLCheckPubsubChannelPerm(o->ptr,upcoming,0) ==
ACL_DENIED_CHANNEL);
}
dictReleaseIterator(di);
}
/* Kill it. */
if (kill) {
freeClient(c);
}
}
}
}
/* Check if the pub/sub channels of the command, that's ready to be executed in
* the client 'c', can be executed by this client according to the ACLs channels
* associated to the client user c->user.
*
* idx and count are the index and count of channel arguments from the
* command. The literal argument controls whether the user's ACL channels are
* evaluated as literal values or matched as glob-like patterns.
*
* If the user can execute the command ACL_OK is returned, otherwise
* ACL_DENIED_CHANNEL. */
int ACLCheckPubsubPerm(client *c, int idx, int count, int literal, int *idxptr) {
user *u = c->user;
/* If there is no associated user, the connection can run anything. */
if (u == NULL) return ACL_OK;
/* Check if the user can access the channels mentioned in the command's
* arguments. */
if (!(c->user->flags & USER_FLAG_ALLCHANNELS)) {
for (int j = idx; j < idx+count; j++) {
if (ACLCheckPubsubChannelPerm(c->argv[j]->ptr,u->channels,literal)
!= ACL_OK) {
if (idxptr) *idxptr = j;
return ACL_DENIED_CHANNEL;
}
}
}
/* If we survived all the above checks, the user can execute the
* command. */
return ACL_OK;
}
/* Check whether the command is ready to be exceuted by ACLCheckCommandPerm.
* If check passes, then check whether pub/sub channels of the command is
* ready to be executed by ACLCheckPubsubPerm */
int ACLCheckAllPerm(client *c, int *idxptr) {
int acl_retval = ACLCheckCommandPerm(c,idxptr);
if (acl_retval != ACL_OK)
return acl_retval;
if (c->cmd->proc == publishCommand)
acl_retval = ACLCheckPubsubPerm(c,1,1,0,idxptr);
else if (c->cmd->proc == subscribeCommand)
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,0,idxptr);
else if (c->cmd->proc == psubscribeCommand)
acl_retval = ACLCheckPubsubPerm(c,1,c->argc-1,1,idxptr);
return acl_retval;
}
/* =============================================================================
* ACL loading / saving functions
* ==========================================================================*/
@@ -1207,7 +1456,7 @@ int ACLLoadConfiguredUsers(void) {
/* Load every rule defined for this user. */
for (int j = 1; aclrules[j]; j++) {
if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {
char *errmsg = ACLSetUserStringError();
const char *errmsg = ACLSetUserStringError();
serverLog(LL_WARNING,"Error loading ACL rule '%s' for "
"the user named '%s': %s",
aclrules[j],aclrules[0],errmsg);
@@ -1327,6 +1576,7 @@ sds ACLLoadFromFile(const char *filename) {
errors = sdscatprintf(errors,
"'%s:%d: username '%s' contains invalid characters. ",
server.acl_filename, linenum, argv[1]);
sdsfreesplitres(argv,argc);
continue;
}
@@ -1339,7 +1589,7 @@ sds ACLLoadFromFile(const char *filename) {
for (j = 2; j < argc; j++) {
argv[j] = sdstrim(argv[j],"\t\r\n");
if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) != C_OK) {
char *errmsg = ACLSetUserStringError();
const char *errmsg = ACLSetUserStringError();
errors = sdscatprintf(errors,
"%s:%d: %s. ",
server.acl_filename, linenum, errmsg);
@@ -1544,13 +1794,13 @@ void ACLFreeLogEntry(void *leptr) {
* the log entry instead of creating many entries for very similar ACL
* rules issues.
*
* The keypos argument is only used when the reason is ACL_DENIED_KEY, since
* it allows the function to log the key name that caused the problem.
* Similarly the username is only passed when we failed to authenticate the
* user with AUTH or HELLO, for the ACL_DENIED_AUTH reason. Otherwise
* it will just be NULL.
* The argpos argument is used when the reason is ACL_DENIED_KEY or
* ACL_DENIED_CHANNEL, since it allows the function to log the key or channel
* name that caused the problem. Similarly the username is only passed when we
* failed to authenticate the user with AUTH or HELLO, for the ACL_DENIED_AUTH
* reason. Otherwise it will just be NULL.
*/
void addACLLogEntry(client *c, int reason, int keypos, sds username) {
void addACLLogEntry(client *c, int reason, int argpos, sds username) {
/* Create a new entry. */
struct ACLLogEntry *le = zmalloc(sizeof(*le));
le->count = 1;
@@ -1560,8 +1810,9 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username) {
switch(reason) {
case ACL_DENIED_CMD: le->object = sdsnew(c->cmd->name); break;
case ACL_DENIED_KEY: le->object = sdsnew(c->argv[keypos]->ptr); break;
case ACL_DENIED_AUTH: le->object = sdsnew(c->argv[0]->ptr); break;
case ACL_DENIED_KEY: le->object = sdsdup(c->argv[argpos]->ptr); break;
case ACL_DENIED_CHANNEL: le->object = sdsdup(c->argv[argpos]->ptr); break;
case ACL_DENIED_AUTH: le->object = sdsdup(c->argv[0]->ptr); break;
default: le->object = sdsempty();
}
@@ -1608,7 +1859,7 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username) {
le->cinfo = NULL;
ACLFreeLogEntry(le);
} else {
/* Add it to our list of entires. We'll have to trim the list
/* Add it to our list of entries. We'll have to trim the list
* to its maximum size. */
listAddNodeHead(ACLLog, le);
while(listLength(ACLLog) > server.acllog_max_len) {
@@ -1641,6 +1892,10 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username) {
void aclCommand(client *c) {
char *sub = c->argv[1]->ptr;
if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
/* Consider information about passwords or permissions
* to be sensitive, which will be the arguments for this
* subcommand. */
preventCommandLogging(c);
sds username = c->argv[2]->ptr;
/* Check username validity. */
if (ACLStringHasSpaces(username,sdslen(username))) {
@@ -1659,7 +1914,7 @@ void aclCommand(client *c) {
for (int j = 3; j < c->argc; j++) {
if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {
char *errmsg = ACLSetUserStringError();
const char *errmsg = ACLSetUserStringError();
addReplyErrorFormat(c,
"Error in ACL SETUSER modifier '%s': %s",
(char*)c->argv[j]->ptr, errmsg);
@@ -1669,6 +1924,11 @@ void aclCommand(client *c) {
}
}
/* Existing pub/sub clients authenticated with the user may need to be
* disconnected if (some of) their channel permissions were revoked. */
if (u && !(tempu->flags & USER_FLAG_ALLCHANNELS))
ACLKillPubsubClientsIfNeeded(u,tempu->channels);
/* Overwrite the user with the temporary user we modified above. */
if (!u) u = ACLCreateUser(username,sdslen(username));
serverAssert(u != NULL);
@@ -1704,7 +1964,7 @@ void aclCommand(client *c) {
return;
}
addReplyMapLen(c,4);
addReplyMapLen(c,5);
/* Flags */
addReplyBulkCString(c,"flags");
@@ -1749,6 +2009,22 @@ void aclCommand(client *c) {
addReplyBulkCBuffer(c,thispat,sdslen(thispat));
}
}
/* Pub/sub patterns */
addReplyBulkCString(c,"channels");
if (u->flags & USER_FLAG_ALLCHANNELS) {
addReplyArrayLen(c,1);
addReplyBulkCBuffer(c,"*",1);
} else {
addReplyArrayLen(c,listLength(u->channels));
listIter li;
listNode *ln;
listRewind(u->channels,&li);
while((ln = listNext(&li))) {
sds thispat = listNodeValue(ln);
addReplyBulkCBuffer(c,thispat,sdslen(thispat));
}
}
} else if ((!strcasecmp(sub,"list") || !strcasecmp(sub,"users")) &&
c->argc == 2)
{
@@ -1886,6 +2162,7 @@ void aclCommand(client *c) {
switch(le->reason) {
case ACL_DENIED_CMD: reasonstr="command"; break;
case ACL_DENIED_KEY: reasonstr="key"; break;
case ACL_DENIED_CHANNEL: reasonstr="channel"; break;
case ACL_DENIED_AUTH: reasonstr="auth"; break;
default: reasonstr="unknown";
}
@@ -1911,20 +2188,32 @@ 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.",
"LIST -- Show user details in config file format.",
"USERS -- List all the registered usernames.",
"SETUSER <username> [attribs ...] -- Create or modify a user.",
"GETUSER <username> -- Get the user details.",
"DELUSER <username> [...] -- Delete a list of users.",
"CAT -- List available categories.",
"CAT <category> -- List commands inside category.",
"GENPASS [<bits>] -- Generate a secure user password.",
"WHOAMI -- Return the current connection username.",
"LOG [<count> | RESET] -- Show the ACL log entries.",
"CAT [<category>]",
" List all commands that belong to <category>, or all command categories",
" when no category is specified.",
"DELUSER <username> [<username> ...]",
" Delete a list of users.",
"GETUSER <username>",
" Get the user's details.",
"GENPASS [<bits>]",
" Generate a secure 256-bit user password. The optional `bits` argument can",
" be used to specify a different size.",
"LIST",
" Show users details in config file format.",
"LOAD",
" Reload users from the ACL file.",
"LOG [<count> | RESET]",
" Show the ACL log entries.",
"SAVE",
" Save the current config to the ACL file.",
"SETUSER <username> <attribute> [<attribute> ...]",
" Create or modify a user with the specified attributes.",
"USERS",
" List all the registered usernames.",
"WHOAMI",
" Return the current connection username.",
NULL
};
addReplyHelp(c,help);
@@ -1953,7 +2242,7 @@ void addReplyCommandCategories(client *c, struct redisCommand *cmd) {
void authCommand(client *c) {
/* Only two or three argument forms are allowed. */
if (c->argc > 3) {
addReply(c,shared.syntaxerr);
addReplyErrorObject(c,shared.syntaxerr);
return;
}
@@ -1970,7 +2259,7 @@ void authCommand(client *c) {
return;
}
username = createStringObject("default",7);
username = shared.default_username;
password = c->argv[1];
} else {
username = c->argv[1];
@@ -1980,11 +2269,19 @@ void authCommand(client *c) {
if (ACLAuthenticateUser(c,username,password) == C_OK) {
addReply(c,shared.ok);
} else {
addReplyError(c,"-WRONGPASS invalid username-password pair");
addReplyError(c,"-WRONGPASS invalid username-password pair or user is disabled.");
}
/* Free the "default" string object we created for the two
* arguments form. */
if (c->argc == 2) decrRefCount(username);
}
/* Set the password for the "default" ACL user. This implements supports for
* requirepass config, so passing in NULL will set the user to be nopass. */
void ACLUpdateDefaultUserPassword(sds password) {
ACLSetUser(DefaultUser,"resetpass",-1);
if (password) {
sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
sdsfree(aclop);
} else {
ACLSetUser(DefaultUser,"nopass",-1);
}
}
+9 -7
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) {
@@ -359,15 +360,16 @@ void listRotateHeadToTail(list *list) {
/* Add all the elements of the list 'o' at the end of the
* list 'l'. The list 'other' remains empty but otherwise valid. */
void listJoin(list *l, list *o) {
if (o->head)
o->head->prev = l->tail;
if (o->len == 0) return;
o->head->prev = l->tail;
if (l->tail)
l->tail->next = o->head;
else
l->head = o->head;
if (o->tail) l->tail = o->tail;
l->tail = o->tail;
l->len += o->len;
/* Setup other as an empty list. */
+34 -86
View File
@@ -30,6 +30,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "ae.h"
#include "anet.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -40,7 +43,6 @@
#include <time.h>
#include <errno.h>
#include "ae.h"
#include "zmalloc.h"
#include "config.h"
@@ -60,16 +62,18 @@
#endif
#endif
aeEventLoop *aeCreateEventLoop(int setsize) {
aeEventLoop *eventLoop;
int i;
monotonicInit(); /* just in case the calling app didn't initialize */
if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
eventLoop->setsize = setsize;
eventLoop->lastTime = time(NULL);
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 0;
eventLoop->stop = 0;
@@ -199,29 +203,6 @@ int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
return fe->mask;
}
static void aeGetTime(long *seconds, long *milliseconds)
{
struct timeval tv;
gettimeofday(&tv, NULL);
*seconds = tv.tv_sec;
*milliseconds = tv.tv_usec/1000;
}
static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
long cur_sec, cur_ms, when_sec, when_ms;
aeGetTime(&cur_sec, &cur_ms);
when_sec = cur_sec + milliseconds/1000;
when_ms = cur_ms + milliseconds%1000;
if (when_ms >= 1000) {
when_sec ++;
when_ms -= 1000;
}
*sec = when_sec;
*ms = when_ms;
}
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc)
@@ -232,7 +213,7 @@ long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
te = zmalloc(sizeof(*te));
if (te == NULL) return AE_ERR;
te->id = id;
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
te->when = getMonotonicUs() + milliseconds * 1000;
te->timeProc = proc;
te->finalizerProc = finalizerProc;
te->clientData = clientData;
@@ -258,10 +239,8 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
return AE_ERR; /* NO event with the specified ID found */
}
/* Search the first timer to fire.
* This operation is useful to know how many time the select can be
* put in sleep without to delay any event.
* If there are no timers NULL is returned.
/* How many microseconds until the first timer should fire.
* If there are no timers, -1 is returned.
*
* Note that's O(N) since time events are unsorted.
* Possible optimizations (not needed by Redis so far, but...):
@@ -269,19 +248,19 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
* Much better but still insertion or deletion of timers is O(N).
* 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
*/
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
{
static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
aeTimeEvent *te = eventLoop->timeEventHead;
aeTimeEvent *nearest = NULL;
if (te == NULL) return -1;
while(te) {
if (!nearest || te->when_sec < nearest->when_sec ||
(te->when_sec == nearest->when_sec &&
te->when_ms < nearest->when_ms))
nearest = te;
aeTimeEvent *earliest = NULL;
while (te) {
if (!earliest || te->when < earliest->when)
earliest = te;
te = te->next;
}
return nearest;
monotime now = getMonotonicUs();
return (now >= earliest->when) ? 0 : earliest->when - now;
}
/* Process time events */
@@ -289,29 +268,11 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0;
aeTimeEvent *te;
long long maxId;
time_t now = time(NULL);
/* If the system clock is moved to the future, and then set back to the
* right value, time events may be delayed in a random way. Often this
* means that scheduled operations will not be performed soon enough.
*
* Here we try to detect system clock skews, and force all the time
* events to be processed ASAP when this happens: the idea is that
* processing events earlier is less dangerous than delaying them
* indefinitely, and practice suggests it is. */
if (now < eventLoop->lastTime) {
te = eventLoop->timeEventHead;
while(te) {
te->when_sec = 0;
te = te->next;
}
}
eventLoop->lastTime = now;
te = eventLoop->timeEventHead;
maxId = eventLoop->timeEventNextId-1;
monotime now = getMonotonicUs();
while(te) {
long now_sec, now_ms;
long long id;
/* Remove events scheduled for deletion. */
@@ -330,8 +291,10 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
eventLoop->timeEventHead = te->next;
if (te->next)
te->next->prev = te->prev;
if (te->finalizerProc)
if (te->finalizerProc) {
te->finalizerProc(eventLoop, te->clientData);
now = getMonotonicUs();
}
zfree(te);
te = next;
continue;
@@ -346,10 +309,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
te = te->next;
continue;
}
aeGetTime(&now_sec, &now_ms);
if (now_sec > te->when_sec ||
(now_sec == te->when_sec && now_ms >= te->when_ms))
{
if (te->when <= now) {
int retval;
id = te->id;
@@ -357,8 +318,9 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
retval = te->timeProc(eventLoop, id, te->clientData);
te->refcount--;
processed++;
now = getMonotonicUs();
if (retval != AE_NOMORE) {
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
te->when = now + retval * 1000;
} else {
te->id = AE_DELETED_EVENT_ID;
}
@@ -390,37 +352,23 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
/* Nothing to do? return ASAP */
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
/* Note that we want call select() even if there are no
/* Note that we want to call select() even if there are no
* file events to process as long as we want to process time
* events, in order to sleep until the next time event is ready
* to fire. */
if (eventLoop->maxfd != -1 ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp;
int64_t usUntilTimer = -1;
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest) {
long now_sec, now_ms;
usUntilTimer = usUntilEarliestTimer(eventLoop);
aeGetTime(&now_sec, &now_ms);
if (usUntilTimer >= 0) {
tv.tv_sec = usUntilTimer / 1000000;
tv.tv_usec = usUntilTimer % 1000000;
tvp = &tv;
/* How many milliseconds we need to wait for the next
* time event to fire? */
long long ms =
(shortest->when_sec - now_sec)*1000 +
shortest->when_ms - now_ms;
if (ms > 0) {
tvp->tv_sec = ms/1000;
tvp->tv_usec = (ms % 1000)*1000;
} else {
tvp->tv_sec = 0;
tvp->tv_usec = 0;
}
} else {
/* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to set the timeout
@@ -457,7 +405,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
int fired = 0; /* Number of events fired for current fd. */
/* Normally we execute the readable event first, and the writable
* event laster. This is useful as sometimes we may be able
* event later. This is useful as sometimes we may be able
* to serve the reply of a query immediately after processing the
* query.
*
@@ -465,7 +413,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
* asking us to do the reverse: never fire the writable event
* after the readable. In such a case, we invert the calls.
* This is useful when, for instance, we want to do things
* in the beforeSleep() hook, like fsynching a file to disk,
* in the beforeSleep() hook, like fsyncing a file to disk,
* before replying to a client. */
int invert = fe->mask & AE_BARRIER;
+2 -4
View File
@@ -33,7 +33,7 @@
#ifndef __AE_H__
#define __AE_H__
#include <time.h>
#include "monotonic.h"
#define AE_OK 0
#define AE_ERR -1
@@ -79,8 +79,7 @@ typedef struct aeFileEvent {
/* Time event structure */
typedef struct aeTimeEvent {
long long id; /* time event identifier. */
long when_sec; /* seconds */
long when_ms; /* milliseconds */
monotime when;
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
@@ -101,7 +100,6 @@ typedef struct aeEventLoop {
int maxfd; /* highest file descriptor currently registered */
int setsize; /* max number of file descriptors tracked */
long long timeEventNextId;
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; /* Registered events */
aeFiredEvent *fired; /* Fired events */
aeTimeEvent *timeEventHead;
+2 -1
View File
@@ -51,6 +51,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree(state);
return -1;
}
anetCloexec(state->epfd);
eventLoop->apidata = state;
return 0;
}
@@ -110,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
int retval, numevents = 0;
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
tvp ? (tvp->tv_sec*1000 + (tvp->tv_usec + 999)/1000) : -1);
if (retval > 0) {
int j;
+7 -4
View File
@@ -67,7 +67,7 @@ static int evport_debug = 0;
typedef struct aeApiState {
int portfd; /* event port */
int npending; /* # of pending fds */
uint_t npending; /* # of pending fds */
int pending_fds[MAX_EVENT_BATCHSZ]; /* pending fds */
int pending_masks[MAX_EVENT_BATCHSZ]; /* pending fds' masks */
} aeApiState;
@@ -82,6 +82,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree(state);
return -1;
}
anetCloexec(state->portfd);
state->npending = 0;
@@ -95,6 +96,8 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
}
static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
(void) eventLoop;
(void) setsize;
/* Nothing to resize here. */
return 0;
}
@@ -107,7 +110,7 @@ static void aeApiFree(aeEventLoop *eventLoop) {
}
static int aeApiLookupPending(aeApiState *state, int fd) {
int i;
uint_t i;
for (i = 0; i < state->npending; i++) {
if (state->pending_fds[i] == fd)
@@ -232,7 +235,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.
*/
@@ -243,7 +246,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
aeApiState *state = eventLoop->apidata;
struct timespec timeout, *tsp;
int mask, i;
uint_t mask, i;
uint_t nevents;
port_event_t event[MAX_EVENT_BATCHSZ];
+1
View File
@@ -53,6 +53,7 @@ static int aeApiCreate(aeEventLoop *eventLoop) {
zfree(state);
return -1;
}
anetCloexec(state->kqfd);
eventLoop->apidata = state;
return 0;
}
+45 -122
View File
@@ -69,6 +69,11 @@ int anetSetBlock(char *err, int fd, int non_block) {
return ANET_ERR;
}
/* Check if this flag has been set or unset, if so,
* then there is no need to call fcntl to set/unset it again. */
if (!!(flags & O_NONBLOCK) == !!non_block)
return ANET_OK;
if (non_block)
flags |= O_NONBLOCK;
else
@@ -89,6 +94,29 @@ int anetBlock(char *err, int fd) {
return anetSetBlock(err,fd,0);
}
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
* This function should be invoked for fd's on specific places
* where fork + execve system calls are called. */
int anetCloexec(int fd) {
int r;
int flags;
do {
r = fcntl(fd, F_GETFD);
} while (r == -1 && errno == EINTR);
if (r == -1 || (r & FD_CLOEXEC))
return r;
flags = r | FD_CLOEXEC;
do {
r = fcntl(fd, F_SETFD, flags);
} while (r == -1 && errno == EINTR);
return r;
}
/* Set TCP keep alive option to detect dead peers. The interval option
* is only used for Linux as we are using Linux-specific APIs to set
* the probe send time, interval, and count. */
@@ -158,27 +186,6 @@ int anetDisableTcpNoDelay(char *err, int fd)
return anetSetTcpNoDelay(err, fd, 0);
}
int anetSetSendBuffer(char *err, int fd, int buffsize)
{
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
{
anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
int anetTcpKeepAlive(char *err, int fd)
{
int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
return ANET_ERR;
}
return ANET_OK;
}
/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
* number of milliseconds, or disable it if the 'ms' argument is zero. */
int anetSendTimeout(char *err, int fd, long long ms) {
@@ -207,14 +214,13 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
return ANET_OK;
}
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
* do the actual work. It resolves the hostname "host" and set the string
* representation of the IP address into the buffer pointed by "ipbuf".
/* Resolve the hostname "host" and set the string representation of the
* IP address into the buffer pointed by "ipbuf".
*
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
* that are actually already IPv4 or IPv6 addresses. This turns the function
* into a validating / normalizing function. */
int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
int flags)
{
struct addrinfo hints, *info;
@@ -241,14 +247,6 @@ int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
return ANET_OK;
}
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_NONE);
}
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len) {
return anetGenericResolve(err,host,ipbuf,ipbuf_len,ANET_IP_ONLY);
}
static int anetSetReuseAddr(char *err, int fd) {
int yes = 1;
/* Make sure connection-intensive things like the redis benchmark
@@ -359,23 +357,11 @@ end:
}
}
int anetTcpConnect(char *err, const char *addr, int port)
{
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);
}
int anetTcpNonBlockConnect(char *err, const char *addr, int port)
{
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);
}
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port,
const char *source_addr)
{
return anetTcpGenericConnect(err,addr,port,source_addr,
ANET_CONNECT_NONBLOCK);
}
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
const char *source_addr)
{
@@ -411,46 +397,6 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
return s;
}
int anetUnixConnect(char *err, const char *path)
{
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);
}
int anetUnixNonBlockConnect(char *err, const char *path)
{
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK);
}
/* Like read(2) but make sure 'count' is read before to return
* (unless error or EOF condition is encountered) */
int anetRead(int fd, char *buf, int count)
{
ssize_t nread, totlen = 0;
while(totlen != count) {
nread = read(fd,buf,count-totlen);
if (nread == 0) return totlen;
if (nread == -1) return -1;
totlen += nread;
buf += nread;
}
return totlen;
}
/* Like write(2) but make sure 'count' is written before to return
* (unless error is encountered) */
int anetWrite(int fd, char *buf, int count)
{
ssize_t nwritten, totlen = 0;
while(totlen != count) {
nwritten = write(fd,buf,count-totlen);
if (nwritten == 0) return totlen;
if (nwritten == -1) return -1;
totlen += nwritten;
buf += nwritten;
}
return totlen;
}
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
if (bind(s,sa,len) == -1) {
anetSetError(err, "bind: %s", strerror(errno));
@@ -470,7 +416,6 @@ static int anetV6Only(char *err, int s) {
int yes = 1;
if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {
anetSetError(err, "setsockopt: %s", strerror(errno));
close(s);
return ANET_ERR;
}
return ANET_OK;
@@ -487,6 +432,10 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */
if (bindaddr && !strcmp("*", bindaddr))
bindaddr = NULL;
if (af == AF_INET6 && bindaddr && !strcmp("::*", bindaddr))
bindaddr = NULL;
if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) {
anetSetError(err, "%s", gai_strerror(rv));
@@ -588,11 +537,15 @@ int anetUnixAccept(char *err, int s) {
return fd;
}
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type) {
struct sockaddr_storage sa;
socklen_t salen = sizeof(sa);
if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) goto error;
if (fd_to_str_type == FD_TO_PEER_NAME) {
if (getpeername(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
} else {
if (getsockname(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
}
if (ip_len == 0) goto error;
if (sa.ss_family == AF_INET) {
@@ -604,7 +557,7 @@ int anetPeerToString(int fd, char *ip, size_t ip_len, int *port) {
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
if (port) *port = ntohs(s->sin6_port);
} else if (sa.ss_family == AF_UNIX) {
if (ip) strncpy(ip,"/unixsocket",ip_len);
if (ip) snprintf(ip, ip_len, "/unixsocket");
if (port) *port = 0;
} else {
goto error;
@@ -632,41 +585,11 @@ int anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) {
"[%s]:%d" : "%s:%d", ip, port);
}
/* Like anetFormatAddr() but extract ip and port from the socket's peer. */
int anetFormatPeer(int fd, char *buf, size_t buf_len) {
/* Like anetFormatAddr() but extract ip and port from the socket's peer/sockname. */
int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type) {
char ip[INET6_ADDRSTRLEN];
int port;
anetPeerToString(fd,ip,sizeof(ip),&port);
anetFdToString(fd,ip,sizeof(ip),&port,fd_to_str_type);
return anetFormatAddr(buf, buf_len, ip, port);
}
int anetSockName(int fd, char *ip, size_t ip_len, int *port) {
struct sockaddr_storage sa;
socklen_t salen = sizeof(sa);
if (getsockname(fd,(struct sockaddr*)&sa,&salen) == -1) {
if (port) *port = 0;
ip[0] = '?';
ip[1] = '\0';
return -1;
}
if (sa.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
if (port) *port = ntohs(s->sin_port);
} else {
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
if (port) *port = ntohs(s->sin6_port);
}
return 0;
}
int anetFormatSock(int fd, char *fmt, size_t fmt_len) {
char ip[INET6_ADDRSTRLEN];
int port;
anetSockName(fd,ip,sizeof(ip),&port);
return anetFormatAddr(fmt, fmt_len, ip, port);
}
+8 -13
View File
@@ -49,33 +49,28 @@
#undef ip_len
#endif
int anetTcpConnect(char *err, const char *addr, int port);
/* FD to address string conversion types */
#define FD_TO_PEER_NAME 0
#define FD_TO_SOCK_NAME 1
int anetTcpNonBlockConnect(char *err, const char *addr, int port);
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);
int anetUnixConnect(char *err, const char *path);
int anetUnixNonBlockConnect(char *err, const char *path);
int anetRead(int fd, char *buf, int count);
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len);
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags);
int anetTcpServer(char *err, int port, char *bindaddr, int backlog);
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);
int anetUnixServer(char *err, char *path, mode_t perm, int backlog);
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd);
int anetBlock(char *err, int fd);
int anetCloexec(int fd);
int anetEnableTcpNoDelay(char *err, int fd);
int anetDisableTcpNoDelay(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd);
int anetSendTimeout(char *err, int fd, long long ms);
int anetRecvTimeout(char *err, int fd, long long ms);
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type);
int anetKeepAlive(char *err, int fd, int interval);
int anetSockName(int fd, char *ip, size_t ip_len, int *port);
int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port);
int anetFormatPeer(int fd, char *fmt, size_t fmt_len);
int anetFormatSock(int fd, char *fmt, size_t fmt_len);
int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type);
#endif
+305 -129
View File
@@ -206,29 +206,27 @@ int aofFsyncInProgress(void) {
/* Starts a background task that performs fsync() against the specified
* file descriptor (the one of the AOF file) in another thread. */
void aof_background_fsync(int fd) {
bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL);
bioCreateFsyncJob(fd);
}
/* Kills an AOFRW child process if exists */
void killAppendOnlyChild(void) {
int statloc;
/* No AOFRW child? return. */
if (server.aof_child_pid == -1) return;
if (server.child_type != CHILD_TYPE_AOF) return;
/* Kill AOFRW child, wait for child exit. */
serverLog(LL_NOTICE,"Killing running AOF rewrite child: %ld",
(long) server.aof_child_pid);
if (kill(server.aof_child_pid,SIGUSR1) != -1) {
while(wait3(&statloc,0,NULL) != server.aof_child_pid);
(long) server.child_pid);
if (kill(server.child_pid,SIGUSR1) != -1) {
while(waitpid(-1, &statloc, 0) != server.child_pid);
}
/* Reset the buffer accumulating changes while the child saves. */
aofRewriteBufferReset();
aofRemoveTempFile(server.aof_child_pid);
server.aof_child_pid = -1;
aofRemoveTempFile(server.child_pid);
resetChildState();
server.aof_rewrite_time_start = -1;
/* Close pipes used for IPC between the two processes. */
aofClosePipes();
closeChildInfoPipe();
updateDictResizePolicy();
}
/* Called when the user switches from "appendonly yes" to "appendonly no"
@@ -236,7 +234,12 @@ void killAppendOnlyChild(void) {
void stopAppendOnly(void) {
serverAssert(server.aof_state != AOF_OFF);
flushAppendOnlyFile(1);
redis_fsync(server.aof_fd);
if (redis_fsync(server.aof_fd) == -1) {
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
} else {
server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime;
}
close(server.aof_fd);
server.aof_fd = -1;
@@ -244,6 +247,8 @@ void stopAppendOnly(void) {
server.aof_state = AOF_OFF;
server.aof_rewrite_scheduled = 0;
killAppendOnlyChild();
sdsfree(server.aof_buf);
server.aof_buf = sdsempty();
}
/* Called when the user switches from "appendonly no" to "appendonly yes"
@@ -265,14 +270,14 @@ int startAppendOnly(void) {
strerror(errno));
return C_ERR;
}
if (hasActiveChildProcess() && server.aof_child_pid == -1) {
if (hasActiveChildProcess() && server.child_type != CHILD_TYPE_AOF) {
server.aof_rewrite_scheduled = 1;
serverLog(LL_WARNING,"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.");
} else {
/* If there is a pending AOF rewrite, we need to switch it off and
* start a new one: the old one cannot be reused because it is not
* accumulating the AOF buffer. */
if (server.aof_child_pid != -1) {
if (server.child_type == CHILD_TYPE_AOF) {
serverLog(LL_WARNING,"AOF was enabled but there is already an AOF rewriting in background. Stopping background AOF and starting a rewrite now.");
killAppendOnlyChild();
}
@@ -287,6 +292,21 @@ int startAppendOnly(void) {
server.aof_state = AOF_WAIT_REWRITE;
server.aof_last_fsync = server.unixtime;
server.aof_fd = newfd;
/* If AOF fsync error in bio job, we just ignore it and log the event. */
int aof_bio_fsync_status;
atomicGet(server.aof_bio_fsync_status, aof_bio_fsync_status);
if (aof_bio_fsync_status == C_ERR) {
serverLog(LL_WARNING,
"AOF reopen, just ignore the AOF fsync error in bio job");
atomicSet(server.aof_bio_fsync_status,C_OK);
}
/* If AOF was in error state, we just ignore it and log the event. */
if (server.aof_last_write_status == C_ERR) {
serverLog(LL_WARNING,"AOF reopen, just ignore the last error.");
server.aof_last_write_status = C_OK;
}
return C_OK;
}
@@ -453,10 +473,11 @@ void flushAppendOnlyFile(int force) {
/* Handle the AOF write error. */
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
/* We can't recover when the fsync policy is ALWAYS since the
* reply for the client is already in the output buffers, and we
* have the contract with the user that on acknowledged write data
* is synced on disk. */
/* We can't recover when the fsync policy is ALWAYS since the reply
* for the client is already in the output buffers (both writes and
* reads), and the changes to the db can't be rolled back. Since we
* have a contract with the user that on acknowledged or observed
* writes are is synced on disk, we must exit. */
serverLog(LL_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting...");
exit(1);
} else {
@@ -504,7 +525,14 @@ try_fsync:
/* redis_fsync is defined as fdatasync() for Linux in order to avoid
* flushing metadata. */
latencyStartMonitor(latency);
redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */
/* Let's try to get this data on the disk. To guarantee data safe when
* the AOF fsync policy is 'always', we should exit if failed to fsync
* AOF (see comment next to the exit(1) after write error above). */
if (redis_fsync(server.aof_fd) == -1) {
serverLog(LL_WARNING,"Can't persist AOF for fsync error when the "
"AOF fsync policy is 'always': %s. Exiting...", strerror(errno));
exit(1);
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fsync-always",latency);
server.aof_fsync_offset = server.aof_current_size;
@@ -544,7 +572,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.
*
@@ -572,19 +600,16 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r
}
decrRefCount(seconds);
argv[0] = createStringObject("PEXPIREAT",9);
argv[0] = shared.pexpireat;
argv[1] = key;
argv[2] = createStringObjectFromLongLong(when);
buf = catAppendOnlyGenericCommand(buf, 3, argv);
decrRefCount(argv[0]);
decrRefCount(argv[2]);
return buf;
}
void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
sds buf = sdsempty();
robj *tmpargv[3];
/* The DB this command was targeting is not the same as the last command
* we appended. To issue a SELECT command is needed. */
if (dictid != server.aof_selected_db) {
@@ -600,32 +625,31 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
cmd->proc == expireatCommand) {
/* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
} else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {
/* Translate SETEX/PSETEX to SET and PEXPIREAT */
tmpargv[0] = createStringObject("SET",3);
tmpargv[1] = argv[1];
tmpargv[2] = argv[3];
buf = catAppendOnlyGenericCommand(buf,3,tmpargv);
decrRefCount(tmpargv[0]);
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
} else if (cmd->proc == setCommand && argc > 3) {
int i;
robj *exarg = NULL, *pxarg = NULL;
for (i = 3; i < argc; i ++) {
if (!strcasecmp(argv[i]->ptr, "ex")) exarg = argv[i+1];
if (!strcasecmp(argv[i]->ptr, "px")) pxarg = argv[i+1];
robj *pxarg = NULL;
/* When SET is used with EX/PX argument setGenericCommand propagates them with PX millisecond argument.
* So since the command arguments are re-written there, we can rely here on the index of PX being 3. */
if (!strcasecmp(argv[3]->ptr, "px")) {
pxarg = argv[4];
}
serverAssert(!(exarg && pxarg));
/* For AOF we convert SET key value relative time in milliseconds to SET key value absolute time in
* millisecond. Whenever the condition is true it implies that original SET has been transformed
* to SET PX with millisecond time argument so we do not need to worry about unit here.*/
if (pxarg) {
robj *millisecond = getDecodedObject(pxarg);
long long when = strtoll(millisecond->ptr,NULL,10);
when += mstime();
if (exarg || pxarg) {
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
buf = catAppendOnlyGenericCommand(buf,3,argv);
if (exarg)
buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],
exarg);
if (pxarg)
buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],
pxarg);
decrRefCount(millisecond);
robj *newargs[5];
newargs[0] = argv[0];
newargs[1] = argv[1];
newargs[2] = argv[2];
newargs[3] = shared.pxat;
newargs[4] = createStringObjectFromLongLong(when);
buf = catAppendOnlyGenericCommand(buf,5,newargs);
decrRefCount(newargs[4]);
} else {
buf = catAppendOnlyGenericCommand(buf,argc,argv);
}
@@ -646,7 +670,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
* accumulate the differences between the child DB and the current one
* in a buffer, so that when the child process will do its work we
* can append the differences to the new append only file. */
if (server.aof_child_pid != -1)
if (server.child_type == CHILD_TYPE_AOF)
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
sdsfree(buf);
@@ -669,8 +693,22 @@ struct client *createAOFClient(void) {
c->querybuf_peak = 0;
c->argc = 0;
c->argv = NULL;
c->original_argc = 0;
c->original_argv = NULL;
c->argv_len_sum = 0;
c->bufpos = 0;
c->flags = 0;
/*
* The AOF client should never be blocked (unlike master
* replication connection).
* This is because blocking the AOF client might cause
* deadlock (because potentially no one will unblock it).
* Also, if the AOF client will be blocked just for
* background processing there is a chance that the
* command execution order will be violated.
*/
c->flags = CLIENT_DENY_BLOCKING;
c->btype = BLOCKED_NONE;
/* We set the fake client as a slave waiting for the synchronization
* so that Redis will not try to send replies to this client. */
@@ -680,6 +718,7 @@ struct client *createAOFClient(void) {
c->obuf_soft_limit_reached_time = 0;
c->watched_keys = listCreate();
c->peerid = NULL;
c->sockname = NULL;
c->resp = 2;
c->user = NULL;
listSetFreeMethod(c->reply,freeClientReplyValue);
@@ -694,6 +733,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) {
@@ -701,6 +741,7 @@ void freeFakeClient(struct client *c) {
listRelease(c->reply);
listRelease(c->watched_keys);
freeClientMultiState(c);
freeClientOriginalArgv(c);
zfree(c);
}
@@ -858,7 +899,7 @@ int loadAppendOnlyFile(char *filename) {
fakeClient->cmd = NULL;
if (server.aof_load_truncated) valid_up_to = ftello(fp);
if (server.key_load_delay)
usleep(server.key_load_delay);
debugDelay(server.key_load_delay);
}
/* This point can only be reached when EOF is reached without errors.
@@ -959,15 +1000,25 @@ int rewriteListObject(rio *r, robj *key, robj *o) {
if (count == 0) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
if (rioWriteBulkString(r,"RPUSH",5) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
!rioWriteBulkString(r,"RPUSH",5) ||
!rioWriteBulkObject(r,key))
{
quicklistReleaseIterator(li);
return 0;
}
}
if (entry.value) {
if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0;
if (!rioWriteBulkString(r,(char*)entry.value,entry.sz)) {
quicklistReleaseIterator(li);
return 0;
}
} else {
if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0;
if (!rioWriteBulkLongLong(r,entry.longval)) {
quicklistReleaseIterator(li);
return 0;
}
}
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
@@ -993,11 +1044,14 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
if (rioWriteBulkString(r,"SADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
!rioWriteBulkString(r,"SADD",4) ||
!rioWriteBulkObject(r,key))
{
return 0;
}
}
if (rioWriteBulkLongLong(r,llval) == 0) return 0;
if (!rioWriteBulkLongLong(r,llval)) return 0;
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
@@ -1011,11 +1065,18 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0;
if (rioWriteBulkString(r,"SADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items) ||
!rioWriteBulkString(r,"SADD",4) ||
!rioWriteBulkObject(r,key))
{
dictReleaseIterator(di);
return 0;
}
}
if (!rioWriteBulkString(r,ele,sdslen(ele))) {
dictReleaseIterator(di);
return 0;
}
if (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
@@ -1052,15 +1113,18 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
if (rioWriteBulkString(r,"ZADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
!rioWriteBulkString(r,"ZADD",4) ||
!rioWriteBulkObject(r,key))
{
return 0;
}
}
if (rioWriteBulkDouble(r,score) == 0) return 0;
if (!rioWriteBulkDouble(r,score)) return 0;
if (vstr != NULL) {
if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;
if (!rioWriteBulkString(r,(char*)vstr,vlen)) return 0;
} else {
if (rioWriteBulkLongLong(r,vll) == 0) return 0;
if (!rioWriteBulkLongLong(r,vll)) return 0;
}
zzlNext(zl,&eptr,&sptr);
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
@@ -1079,12 +1143,20 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
if (rioWriteBulkString(r,"ZADD",4) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
!rioWriteBulkString(r,"ZADD",4) ||
!rioWriteBulkObject(r,key))
{
dictReleaseIterator(di);
return 0;
}
}
if (!rioWriteBulkDouble(r,*score) ||
!rioWriteBulkString(r,ele,sdslen(ele)))
{
dictReleaseIterator(di);
return 0;
}
if (rioWriteBulkDouble(r,*score) == 0) return 0;
if (rioWriteBulkString(r,ele,sdslen(ele)) == 0) return 0;
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
@@ -1133,13 +1205,21 @@ int rewriteHashObject(rio *r, robj *key, robj *o) {
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
AOF_REWRITE_ITEMS_PER_CMD : items;
if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0;
if (rioWriteBulkString(r,"HMSET",5) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
!rioWriteBulkString(r,"HMSET",5) ||
!rioWriteBulkObject(r,key))
{
hashTypeReleaseIterator(hi);
return 0;
}
}
if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) == 0) return 0;
if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE) == 0) return 0;
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
{
hashTypeReleaseIterator(hi);
return 0;
}
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
items--;
}
@@ -1185,6 +1265,20 @@ int rioWriteStreamPendingEntry(rio *r, robj *key, const char *groupname, size_t
return 1;
}
/* Helper for rewriteStreamObject(): emit the XGROUP CREATECONSUMER is
* needed in order to create consumers that do not have any pending entries.
* All this in the context of the specified key and group. */
int rioWriteStreamEmptyConsumer(rio *r, robj *key, const char *groupname, size_t groupname_len, streamConsumer *consumer) {
/* XGROUP CREATECONSUMER <key> <group> <consumer> */
if (rioWriteBulkCount(r,'*',5) == 0) return 0;
if (rioWriteBulkString(r,"XGROUP",6) == 0) return 0;
if (rioWriteBulkString(r,"CREATECONSUMER",14) == 0) return 0;
if (rioWriteBulkObject(r,key) == 0) return 0;
if (rioWriteBulkString(r,groupname,groupname_len) == 0) return 0;
if (rioWriteBulkString(r,consumer->name,sdslen(consumer->name)) == 0) return 0;
return 1;
}
/* Emit the commands needed to rebuild a stream object.
* The function returns 0 on error, 1 on success. */
int rewriteStreamObject(rio *r, robj *key, robj *o) {
@@ -1201,16 +1295,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 {
@@ -1218,22 +1320,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. */
@@ -1244,21 +1354,38 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
while(raxNext(&ri)) {
streamCG *group = 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
* value so they are discarded. */
* have pending entries. Empty consumers would be generated with
* XGROUP CREATECONSUMER. */
raxIterator ri_cons;
raxStart(&ri_cons,group->consumers);
raxSeek(&ri_cons,"^",NULL,0);
while(raxNext(&ri_cons)) {
streamConsumer *consumer = ri_cons.data;
/* If there are no pending entries, just emit XGROUP CREATECONSUMER */
if (raxSize(consumer->pel) == 0) {
if (rioWriteStreamEmptyConsumer(r,key,(char*)ri.key,
ri.key_len,consumer) == 0)
{
raxStop(&ri_cons);
raxStop(&ri);
streamIteratorStop(&si);
return 0;
}
continue;
}
/* For the current consumer, iterate all the PEL entries
* to emit the XCLAIM protocol. */
raxIterator ri_pel;
@@ -1270,6 +1397,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;
}
}
@@ -1320,6 +1451,8 @@ int rewriteAppendOnlyFileRio(rio *aof) {
dictEntry *de;
size_t processed = 0;
int j;
long key_count = 0;
long long updated_time = 0;
for (j = 0; j < server.dbnum; j++) {
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
@@ -1379,6 +1512,17 @@ int rewriteAppendOnlyFileRio(rio *aof) {
processed = aof->processed_bytes;
aofReadDiffFromParent();
}
/* Update info every 1 second (approximately).
* in order to avoid calling mstime() on each iteration, we will
* check the diff every 1024 keys */
if ((key_count++ & 1023) == 0) {
long long now = mstime();
if (now - updated_time >= 1000) {
sendChildInfo(CHILD_INFO_TYPE_CURRENT_INFO, key_count, "AOF rewrite");
updated_time = now;
}
}
}
dictReleaseIterator(di);
di = NULL;
@@ -1399,7 +1543,7 @@ werr:
* are inserted using a single command. */
int rewriteAppendOnlyFile(char *filename) {
rio aof;
FILE *fp;
FILE *fp = NULL;
char tmpfile[256];
char byte;
@@ -1458,7 +1602,7 @@ int rewriteAppendOnlyFile(char *filename) {
if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr;
if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK)
goto werr;
/* We read the ACK from the server using a 10 seconds timeout. Normally
/* We read the ACK from the server using a 5 seconds timeout. Normally
* it should reply ASAP, but just in case we lose its reply, we are sure
* the child will eventually get terminated. */
if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 ||
@@ -1472,13 +1616,37 @@ int rewriteAppendOnlyFile(char *filename) {
serverLog(LL_NOTICE,
"Concatenating %.2f MB of AOF diff received from parent.",
(double) sdslen(server.aof_child_diff) / (1024*1024));
if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0)
goto werr;
/* Now we write the entire AOF buffer we received from the parent
* via the pipe during the life of this fork child.
* once a second, we'll take a break and send updated COW info to the parent */
size_t bytes_to_write = sdslen(server.aof_child_diff);
const char *buf = server.aof_child_diff;
long long cow_updated_time = mstime();
long long key_count = dbTotalServerKeyCount();
while (bytes_to_write) {
/* We write the AOF buffer in chunk of 8MB so that we can check the time in between them */
size_t chunk_size = bytes_to_write < (8<<20) ? bytes_to_write : (8<<20);
if (rioWrite(&aof,buf,chunk_size) == 0)
goto werr;
bytes_to_write -= chunk_size;
buf += chunk_size;
/* Update COW info */
long long now = mstime();
if (now - cow_updated_time >= 1000) {
sendChildInfo(CHILD_INFO_TYPE_CURRENT_INFO, key_count, "AOF rewrite");
cow_updated_time = now;
}
}
/* 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. */
@@ -1494,7 +1662,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;
@@ -1595,8 +1763,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 */
@@ -1604,7 +1771,7 @@ int rewriteAppendOnlyFileBackground(void) {
redisSetCpuAffinity(server.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_INFO_TYPE_AOF_COW_SIZE, "AOF rewrite");
exitFromChild(0);
} else {
exitFromChild(1);
@@ -1612,7 +1779,6 @@ int rewriteAppendOnlyFileBackground(void) {
} else {
/* Parent */
if (childpid == -1) {
closeChildInfoPipe();
serverLog(LL_WARNING,
"Can't rewrite append only file in background: fork: %s",
strerror(errno));
@@ -1620,10 +1786,10 @@ int rewriteAppendOnlyFileBackground(void) {
return C_ERR;
}
serverLog(LL_NOTICE,
"Background append only file rewriting started by pid %d",childpid);
"Background append only file rewriting started by pid %ld",(long) childpid);
server.aof_rewrite_scheduled = 0;
server.aof_rewrite_time_start = time(NULL);
server.aof_child_pid = childpid;
/* We set appendseldb to -1 in order to force the next call to the
* feedAppendOnlyFile() to issue a SELECT command, so the differences
* accumulated by the parent into server.aof_rewrite_buf will start
@@ -1636,7 +1802,7 @@ int rewriteAppendOnlyFileBackground(void) {
}
void bgrewriteaofCommand(client *c) {
if (server.aof_child_pid != -1) {
if (server.child_type == CHILD_TYPE_AOF) {
addReplyError(c,"Background append only file rewriting already in progress");
} else if (hasActiveChildProcess()) {
server.aof_rewrite_scheduled = 1;
@@ -1653,10 +1819,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 server.aof_current_size field explicitly using stat(2)
@@ -1694,7 +1860,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
* rewritten AOF. */
latencyStartMonitor(latency);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
(int)server.aof_child_pid);
(int)server.child_pid);
newfd = open(tmpfile,O_WRONLY|O_APPEND);
if (newfd == -1) {
serverLog(LL_WARNING,
@@ -1710,6 +1876,20 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-rewrite-diff-write",latency);
if (server.aof_fsync == AOF_FSYNC_EVERYSEC) {
aof_background_fsync(newfd);
} else if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
latencyStartMonitor(latency);
if (redis_fsync(newfd) == -1) {
serverLog(LL_WARNING,
"Error trying to fsync the parent diff to the rewritten AOF: %s", strerror(errno));
close(newfd);
goto cleanup;
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-rewrite-done-fsync",latency);
}
serverLog(LL_NOTICE,
"Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)", (double) aofRewriteBufferSize() / (1024*1024));
@@ -1777,14 +1957,11 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* AOF enabled, replace the old fd with the new one. */
oldfd = server.aof_fd;
server.aof_fd = newfd;
if (server.aof_fsync == AOF_FSYNC_ALWAYS)
redis_fsync(newfd);
else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
aof_background_fsync(newfd);
server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
aofUpdateCurrentSize();
server.aof_rewrite_base_size = server.aof_current_size;
server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime;
/* Clear regular AOF buffer since its contents was just written to
* the new AOF from the background rewrite buffer. */
@@ -1800,7 +1977,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
server.aof_state = AOF_ON;
/* Asynchronously close the overwritten AOF. */
if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);
if (oldfd != -1) bioCreateCloseJob(oldfd);
serverLog(LL_VERBOSE,
"Background AOF rewrite signal handler took %lldus", ustime()-now);
@@ -1811,7 +1988,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)
server.aof_lastbgrewrite_status = C_ERR;
@@ -1822,8 +1999,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
cleanup:
aofClosePipes();
aofRewriteBufferReset();
aofRemoveTempFile(server.aof_child_pid);
server.aof_child_pid = -1;
aofRemoveTempFile(server.child_pid);
server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;
server.aof_rewrite_time_start = -1;
/* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */
+2 -2
View File
@@ -31,13 +31,13 @@ const char *ascii_logo =
" _._ \n"
" _.-``__ ''-._ \n"
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
" ( ' , .-` | `, ) Running in %s mode\n"
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
" | `-._ `._ / _.-' | PID: %ld\n"
" `-._ `-._ `-./ _.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | http://redis.io \n"
" | `-._`-._ _.-'_.-' | https://redis.io \n"
" `-._ `-._`-.__.-'_.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | \n"
+68 -43
View File
@@ -1,5 +1,5 @@
/* This file implements atomic counters using __atomic or __sync macros if
* available, otherwise synchronizing different threads using a mutex.
/* This file implements atomic counters using c11 _Atomic, __atomic or __sync
* macros if available, otherwise we will throw an error when compile.
*
* The exported interface is composed of three macros:
*
@@ -8,20 +8,12 @@
* atomicDecr(var,count) -- Decrement the atomic counter
* atomicGet(var,dstvar) -- Fetch the atomic counter value
* atomicSet(var,value) -- Set the atomic counter value
*
* The variable 'var' should also have a declared mutex with the same
* name and the "_mutex" postfix, for instance:
*
* long myvar;
* pthread_mutex_t myvar_mutex;
* atomicSet(myvar,12345);
*
* If atomic primitives are available (tested in config.h) the mutex
* is not used.
* atomicGetWithSync(var,value) -- 'atomicGet' with inter-thread synchronization
* atomicSetWithSync(var,value) -- 'atomicSet' with inter-thread synchronization
*
* 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);
@@ -58,17 +50,64 @@
*/
#include <pthread.h>
#include "config.h"
#ifndef __ATOMIC_VAR_H
#define __ATOMIC_VAR_H
/* Define redisAtomic for atomic variable. */
#define redisAtomic
/* To test Redis with Helgrind (a Valgrind tool) it is useful to define
* the following macro, so that __sync macros are used: those can be detected
* by Helgrind (even if they are less efficient) so that no false positive
* is reported. */
// #define __ATOMIC_VAR_FORCE_SYNC_MACROS
#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__ATOMIC_RELAXED) && !defined(__sun) && (!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057)
/* There will be many false positives if we test Redis with Helgrind, since
* Helgrind can't understand we have imposed ordering on the program, so
* we use macros in helgrind.h to tell Helgrind inter-thread happens-before
* relationship explicitly for avoiding false positives.
*
* For more details, please see: valgrind/helgrind.h and
* https://www.valgrind.org/docs/manual/hg-manual.html#hg-manual.effective-use
*
* These macros take effect only when 'make helgrind', and you must first
* install Valgrind in the default path configuration. */
#ifdef __ATOMIC_VAR_FORCE_SYNC_MACROS
#include <valgrind/helgrind.h>
#else
#define ANNOTATE_HAPPENS_BEFORE(v) ((void) v)
#define ANNOTATE_HAPPENS_AFTER(v) ((void) v)
#endif
#if !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && defined(__STDC_VERSION__) && \
(__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
/* Use '_Atomic' keyword if the compiler supports. */
#undef redisAtomic
#define redisAtomic _Atomic
/* Implementation using _Atomic in C11. */
#include <stdatomic.h>
#define atomicIncr(var,count) atomic_fetch_add_explicit(&var,(count),memory_order_relaxed)
#define atomicGetIncr(var,oldvalue_var,count) do { \
oldvalue_var = atomic_fetch_add_explicit(&var,(count),memory_order_relaxed); \
} while(0)
#define atomicDecr(var,count) atomic_fetch_sub_explicit(&var,(count),memory_order_relaxed)
#define atomicGet(var,dstvar) do { \
dstvar = atomic_load_explicit(&var,memory_order_relaxed); \
} while(0)
#define atomicSet(var,value) atomic_store_explicit(&var,value,memory_order_relaxed)
#define atomicGetWithSync(var,dstvar) do { \
dstvar = atomic_load_explicit(&var,memory_order_seq_cst); \
} while(0)
#define atomicSetWithSync(var,value) \
atomic_store_explicit(&var,value,memory_order_seq_cst)
#define REDIS_ATOMIC_API "c11-builtin"
#elif !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && \
(!defined(__clang__) || !defined(__APPLE__) || __apple_build_version__ > 4210057) && \
defined(__ATOMIC_RELAXED) && defined(__ATOMIC_SEQ_CST)
/* Implementation using __atomic macros. */
#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
@@ -80,6 +119,11 @@
dstvar = __atomic_load_n(&var,__ATOMIC_RELAXED); \
} while(0)
#define atomicSet(var,value) __atomic_store_n(&var,value,__ATOMIC_RELAXED)
#define atomicGetWithSync(var,dstvar) do { \
dstvar = __atomic_load_n(&var,__ATOMIC_SEQ_CST); \
} while(0)
#define atomicSetWithSync(var,value) \
__atomic_store_n(&var,value,__ATOMIC_SEQ_CST)
#define REDIS_ATOMIC_API "atomic-builtin"
#elif defined(HAVE_ATOMIC)
@@ -96,38 +140,19 @@
#define atomicSet(var,value) do { \
while(!__sync_bool_compare_and_swap(&var,var,value)); \
} while(0)
/* Actually the builtin issues a full memory barrier by default. */
#define atomicGetWithSync(var,dstvar) { \
dstvar = __sync_sub_and_fetch(&var,0,__sync_synchronize); \
ANNOTATE_HAPPENS_AFTER(&var); \
} while(0)
#define atomicSetWithSync(var,value) do { \
ANNOTATE_HAPPENS_BEFORE(&var); \
while(!__sync_bool_compare_and_swap(&var,var,value,__sync_synchronize)); \
} while(0)
#define REDIS_ATOMIC_API "sync-builtin"
#else
/* Implementation using pthread mutex. */
#define atomicIncr(var,count) do { \
pthread_mutex_lock(&var ## _mutex); \
var += (count); \
pthread_mutex_unlock(&var ## _mutex); \
} while(0)
#define atomicGetIncr(var,oldvalue_var,count) do { \
pthread_mutex_lock(&var ## _mutex); \
oldvalue_var = var; \
var += (count); \
pthread_mutex_unlock(&var ## _mutex); \
} while(0)
#define atomicDecr(var,count) do { \
pthread_mutex_lock(&var ## _mutex); \
var -= (count); \
pthread_mutex_unlock(&var ## _mutex); \
} while(0)
#define atomicGet(var,dstvar) do { \
pthread_mutex_lock(&var ## _mutex); \
dstvar = var; \
pthread_mutex_unlock(&var ## _mutex); \
} while(0)
#define atomicSet(var,value) do { \
pthread_mutex_lock(&var ## _mutex); \
var = value; \
pthread_mutex_unlock(&var ## _mutex); \
} while(0)
#define REDIS_ATOMIC_API "pthread-mutex"
#error "Unable to determine atomic operations for your platform"
#endif
#endif /* __ATOMIC_VAR_H */
+56 -29
View File
@@ -78,15 +78,13 @@ static unsigned long long bio_pending[BIO_NUM_OPS];
* file as the API does not expose the internals at all. */
struct bio_job {
time_t time; /* Time at which the job was created. */
/* Job specific arguments pointers. If we need to pass more than three
* arguments we can just pass a pointer to a structure or alike. */
void *arg1, *arg2, *arg3;
/* Job specific arguments.*/
int fd; /* Fd for file based background jobs */
lazy_free_fn *free_fn; /* Function that will free the provided arguments */
void *free_args[]; /* List of arguments to be passed to the free function */
};
void *bioProcessBackgroundJobs(void *arg);
void lazyfreeFreeObjectFromBioThread(robj *o);
void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2);
void lazyfreeFreeSlotsMapFromBioThread(zskiplist *sl);
/* Make sure we have enough stack to perform all the things we do in the
* main thread. */
@@ -128,13 +126,8 @@ void bioInit(void) {
}
}
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
struct bio_job *job = zmalloc(sizeof(*job));
void bioSubmitJob(int type, struct bio_job *job) {
job->time = time(NULL);
job->arg1 = arg1;
job->arg2 = arg2;
job->arg3 = arg3;
pthread_mutex_lock(&bio_mutex[type]);
listAddNodeTail(bio_jobs[type],job);
bio_pending[type]++;
@@ -142,6 +135,35 @@ void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
pthread_mutex_unlock(&bio_mutex[type]);
}
void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...) {
va_list valist;
/* Allocate memory for the job structure and all required
* arguments */
struct bio_job *job = zmalloc(sizeof(*job) + sizeof(void *) * (arg_count));
job->free_fn = free_fn;
va_start(valist, arg_count);
for (int i = 0; i < arg_count; i++) {
job->free_args[i] = va_arg(valist, void *);
}
va_end(valist);
bioSubmitJob(BIO_LAZY_FREE, job);
}
void bioCreateCloseJob(int fd) {
struct bio_job *job = zmalloc(sizeof(*job));
job->fd = fd;
bioSubmitJob(BIO_CLOSE_FILE, job);
}
void bioCreateFsyncJob(int fd) {
struct bio_job *job = zmalloc(sizeof(*job));
job->fd = fd;
bioSubmitJob(BIO_AOF_FSYNC, job);
}
void *bioProcessBackgroundJobs(void *arg) {
struct bio_job *job;
unsigned long type = (unsigned long) arg;
@@ -168,10 +190,7 @@ void *bioProcessBackgroundJobs(void *arg) {
redisSetCpuAffinity(server.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
@@ -199,20 +218,27 @@ void *bioProcessBackgroundJobs(void *arg) {
/* Process the job accordingly to its type. */
if (type == BIO_CLOSE_FILE) {
close((long)job->arg1);
close(job->fd);
} else if (type == BIO_AOF_FSYNC) {
redis_fsync((long)job->arg1);
/* The fd may be closed by main thread and reused for another
* socket, pipe, or file. We just ignore these errno because
* aof fsync did not really fail. */
if (redis_fsync(job->fd) == -1 &&
errno != EBADF && errno != EINVAL)
{
int last_status;
atomicGet(server.aof_bio_fsync_status,last_status);
atomicSet(server.aof_bio_fsync_status,C_ERR);
atomicSet(server.aof_bio_fsync_errno,errno);
if (last_status == C_OK) {
serverLog(LL_WARNING,
"Fail to fsync the AOF file: %s",strerror(errno));
}
} else {
atomicSet(server.aof_bio_fsync_status,C_OK);
}
} else if (type == BIO_LAZY_FREE) {
/* 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. */
if (job->arg1)
lazyfreeFreeObjectFromBioThread(job->arg1);
else if (job->arg2 && job->arg3)
lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);
else if (job->arg3)
lazyfreeFreeSlotsMapFromBioThread(job->arg3);
job->free_fn(job->free_args);
} else {
serverPanic("Wrong job type in bioProcessBackgroundJobs().");
}
@@ -268,10 +294,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,
+5 -1
View File
@@ -30,13 +30,17 @@
#ifndef __BIO_H
#define __BIO_H
typedef void lazy_free_fn(void *args[]);
/* Exported API */
void bioInit(void);
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3);
unsigned long long bioPendingJobsOfType(int type);
unsigned long long bioWaitStepOfType(int type);
time_t bioOlderJobOfType(int type);
void bioKillThreads(void);
void bioCreateCloseJob(int fd);
void bioCreateFsyncJob(int fd);
void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...);
/* Background job opcodes */
#define BIO_CLOSE_FILE 0 /* Deferred close(2) syscall. */
+24 -13
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 or more (server.proto_max_bulk_len) */
size_t redisPopcount(void *s, long count) {
size_t bits = 0;
unsigned char *p = s;
@@ -107,7 +107,7 @@ long redisBitpos(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.
*
@@ -405,7 +405,7 @@ void printBits(unsigned char *p, unsigned long count) {
/* This helper function used by GETBIT / SETBIT parses the bit offset argument
* making sure an error is returned if it is negative or if it overflows
* Redis 512 MB limit for the string value.
* Redis 512 MB limit for the string value or more (server.proto_max_bulk_len).
*
* If the 'hash' argument is true, and 'bits is positive, then the command
* will also parse bit offsets prefixed by "#". In such a case the offset
@@ -428,8 +428,8 @@ int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int b
/* Adjust the offset by 'bits' for #<offset> form. */
if (usehash) loffset *= bits;
/* Limit offset to 512MB in bytes */
if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
/* Limit offset to server.proto_max_bulk_len (512MB in bytes by default) */
if ((loffset < 0) || (loffset >> 3) >= server.proto_max_bulk_len)
{
addReplyError(c,err);
return C_ERR;
@@ -480,12 +480,12 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {
robj *lookupStringForBitCommand(client *c, size_t maxbit) {
size_t byte = maxbit >> 3;
robj *o = lookupKeyWrite(c->db,c->argv[1]);
if (checkType(c,o,OBJ_STRING)) return NULL;
if (o == NULL) {
o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));
dbAdd(c->db,c->argv[1],o);
} else {
if (checkType(c,o,OBJ_STRING)) return NULL;
o = dbUnshareStringValue(c->db,c->argv[1],o);
o->ptr = sdsgrowzero(o->ptr,byte+1);
}
@@ -496,7 +496,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
@@ -611,7 +611,7 @@ void bitopCommand(client *c) {
else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not"))
op = BITOP_NOT;
else {
addReply(c,shared.syntaxerr);
addReplyErrorObject(c,shared.syntaxerr);
return;
}
@@ -735,12 +735,23 @@ void bitopCommand(client *c) {
output = (len[0] <= j) ? 0 : src[0][j];
if (op == BITOP_NOT) output = ~output;
for (i = 1; i < numkeys; i++) {
int skip = 0;
byte = (len[i] <= j) ? 0 : src[i][j];
switch(op) {
case BITOP_AND: output &= byte; break;
case BITOP_OR: output |= byte; break;
case BITOP_AND:
output &= byte;
skip = (output == 0);
break;
case BITOP_OR:
output |= byte;
skip = (output == 0xff);
break;
case BITOP_XOR: output ^= byte; break;
}
if (skip) {
break;
}
}
res[j] = output;
}
@@ -802,7 +813,7 @@ void bitcountCommand(client *c) {
end = strlen-1;
} else {
/* Syntax error. */
addReply(c,shared.syntaxerr);
addReplyErrorObject(c,shared.syntaxerr);
return;
}
@@ -867,7 +878,7 @@ void bitposCommand(client *c) {
end = strlen-1;
} else {
/* Syntax error. */
addReply(c,shared.syntaxerr);
addReplyErrorObject(c,shared.syntaxerr);
return;
}
@@ -959,7 +970,7 @@ void bitfieldGeneric(client *c, int flags) {
}
continue;
} else {
addReply(c,shared.syntaxerr);
addReplyErrorObject(c,shared.syntaxerr);
zfree(ops);
return;
}
+119 -23
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
@@ -61,8 +61,12 @@
*/
#include "server.h"
#include "slowlog.h"
#include "latency.h"
#include "monotonic.h"
int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);
int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int wherefrom, int whereto);
int getListPositionFromObjectOrReply(client *c, robj *arg, int *position);
/* This structure represents the blocked key information that we store
* in the client structure. Each client blocked on keys, has a
@@ -88,6 +92,25 @@ void blockClient(client *c, int btype) {
server.blocked_clients++;
server.blocked_clients_by_type[btype]++;
addClientToTimeoutTable(c);
if (btype == BLOCKED_PAUSE) {
listAddNodeTail(server.paused_clients, c);
c->paused_list_node = listLast(server.paused_clients);
/* Mark this client to execute its command */
c->flags |= CLIENT_PENDING_COMMAND;
}
}
/* This function is called after a client has finished a blocking operation
* in order to update the total command duration, log the command into
* the Slow log if needed, and log the reply duration event if needed. */
void updateStatsOnUnblock(client *c, long blocked_us, long reply_us){
const ustime_t total_cmd_duration = c->duration + blocked_us + reply_us;
c->lastcmd->microseconds += total_cmd_duration;
/* Log the command into the Slow log if needed. */
slowlogPushCurrentCommand(c, c->lastcmd, total_cmd_duration);
/* Log the reply duration event. */
latencyAddSampleIfNeeded("command-unblocking",reply_us/1000);
}
/* This function is called in the beforeSleep() function of the event loop
@@ -109,6 +132,11 @@ void processUnblockedClients(void) {
* client is not blocked before to proceed, but things may change and
* the code is conceptually more correct this way. */
if (!(c->flags & CLIENT_BLOCKED)) {
/* If we have a queued command, execute it now. */
if (processPendingCommandsAndResetClient(c) == C_ERR) {
continue;
}
/* Then process client if it has more data in it's buffer. */
if (c->querybuf && sdslen(c->querybuf) > 0) {
processInputBuffer(c);
}
@@ -118,7 +146,7 @@ void processUnblockedClients(void) {
/* 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:
*
@@ -153,9 +181,22 @@ void unblockClient(client *c) {
} else if (c->btype == BLOCKED_MODULE) {
if (moduleClientIsBlockedOnKeys(c)) unblockClientWaitingData(c);
unblockClientFromModule(c);
} else if (c->btype == BLOCKED_PAUSE) {
listDelNode(server.paused_clients,c->paused_list_node);
c->paused_list_node = NULL;
} else {
serverPanic("Unknown btype in unblockClient().");
}
/* Reset the client for a new query since, for blocking commands
* we do not do it immediately after the command returns (when the
* client got blocked) in order to be still able to access the argument
* vector from module callbacks and updateStatsOnUnblock. */
if (c->btype != BLOCKED_PAUSE) {
freeClientOriginalArgv(c);
resetClient(c);
}
/* Clear the flags, and put the client in the unblocked list so that
* we'll process new commands in its query buffer ASAP. */
server.blocked_clients--;
@@ -199,9 +240,16 @@ void disconnectAllBlockedClients(void) {
client *c = listNodeValue(ln);
if (c->flags & CLIENT_BLOCKED) {
addReplySds(c,sdsnew(
/* PAUSED clients are an exception, when they'll be unblocked, the
* command processing will start from scratch, and the command will
* be either executed or rejected. (unlike LIST blocked clients for
* which the command is already in progress in a way. */
if (c->btype == BLOCKED_PAUSE)
continue;
addReplyError(c,
"-UNBLOCKED force unblock from blocking operation, "
"instance state changed (master -> replica?)\r\n"));
"instance state changed (master -> replica?)");
unblockClient(c);
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
}
@@ -231,26 +279,28 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
}
robj *dstkey = receiver->bpop.target;
int where = (receiver->lastcmd &&
receiver->lastcmd->proc == blpopCommand) ?
LIST_HEAD : LIST_TAIL;
robj *value = listTypePop(o,where);
int wherefrom = receiver->bpop.listpos.wherefrom;
int whereto = receiver->bpop.listpos.whereto;
robj *value = listTypePop(o, wherefrom);
if (value) {
/* Protect receiver->bpop.target, that will be
* freed by the next unblockClient()
* call. */
if (dstkey) incrRefCount(dstkey);
unblockClient(receiver);
monotime replyTimer;
elapsedStart(&replyTimer);
if (serveClientBlockedOnList(receiver,
rl->key,dstkey,rl->db,value,
where) == C_ERR)
wherefrom, whereto) == C_ERR)
{
/* If we failed serving the client we need
* to also undo the POP operation. */
listTypePush(o,value,where);
listTypePush(o,value,wherefrom);
}
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
unblockClient(receiver);
if (dstkey) decrRefCount(dstkey);
decrRefCount(value);
@@ -294,8 +344,11 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
int where = (receiver->lastcmd &&
receiver->lastcmd->proc == bzpopminCommand)
? ZSET_MIN : ZSET_MAX;
unblockClient(receiver);
monotime replyTimer;
elapsedStart(&replyTimer);
genericZpopCommand(receiver,&rl->key,1,where,1,NULL);
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
unblockClient(receiver);
zcard--;
/* Replicate the command. */
@@ -371,13 +424,22 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
int noack = 0;
if (group) {
int created = 0;
consumer =
streamLookupConsumer(group,
receiver->bpop.xread_consumer->ptr,
SLC_NONE);
SLC_NONE,
&created);
noack = receiver->bpop.xread_group_noack;
if (created && noack) {
streamPropagateConsumerCreation(receiver,rl->key,
receiver->bpop.xread_group,
consumer->name);
}
}
monotime replyTimer;
elapsedStart(&replyTimer);
/* Emit the two elements sub-array consisting of
* the name of the stream and the data we
* extracted from it. Wrapped in a single-item
@@ -397,6 +459,7 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
streamReplyWithRange(receiver,s,&start,NULL,
receiver->bpop.xread_count,
0, group, consumer, noack, &pi);
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
/* Note that after we unblock the client, 'gt'
* and other receiver->bpop stuff are no longer
@@ -417,6 +480,10 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
void serveClientsBlockedOnKeyByModule(readyList *rl) {
dictEntry *de;
/* Optimization: If no clients are in type BLOCKED_MODULE,
* we can skip this loop. */
if (!server.blocked_clients_by_type[BLOCKED_MODULE]) return;
/* We serve clients in the same order they blocked for
* this key, from the first blocked to the last. */
de = dictFind(rl->db->blocking_keys,rl->key);
@@ -443,7 +510,10 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
* different modules with different triggers to consider if a key
* is ready or not. This means we can't exit the loop but need
* to continue after the first failure. */
monotime replyTimer;
elapsedStart(&replyTimer);
if (!moduleTryServeClientBlockedOnKey(receiver, rl->key)) continue;
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer));
moduleUnblockClient(receiver);
}
@@ -459,8 +529,8 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
* one new element via some write operation are accumulated into
* the server.ready_keys list. This function will run the list and will
* serve clients accordingly. Note that the function will iterate again and
* again as a result of serving BRPOPLPUSH we can have new blocking clients
* to serve because of the PUSH side of BRPOPLPUSH.
* again as a result of serving BLMOVE we can have new blocking clients
* to serve because of the PUSH side of BLMOVE.
*
* This function is normally "fair", that is, it will server clients
* using a FIFO behavior. However this fairness is violated in certain
@@ -478,7 +548,7 @@ void handleClientsBlockedOnKeys(void) {
/* Point server.ready_keys to a fresh list and save the current one
* locally. This way as we run the old list we are free to call
* signalKeyAsReady() that may push new elements in server.ready_keys
* when handling clients blocked into BRPOPLPUSH. */
* when handling clients blocked into BLMOVE. */
l = server.ready_keys;
server.ready_keys = listCreate();
@@ -493,14 +563,14 @@ void handleClientsBlockedOnKeys(void) {
/* Even if we are not inside call(), increment the call depth
* in order to make sure that keys are expired against a fixed
* reference time, and not against the wallclock time. This
* way we can lookup an object multiple times (BRPOPLPUSH does
* way we can lookup an object multiple times (BLMOVE does
* that) without the risk of it being freed in the second
* lookup, invalidating the first one.
* See https://github.com/antirez/redis/pull/6554. */
* See https://github.com/redis/redis/pull/6554. */
server.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) {
@@ -553,7 +623,7 @@ void handleClientsBlockedOnKeys(void) {
* stream keys, we also provide an array of streamID structures: clients will
* be unblocked only when items with an ID greater or equal to the specified
* one is appended to the stream. */
void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids) {
void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, struct listPos *listpos, streamID *ids) {
dictEntry *de;
list *l;
int j;
@@ -561,6 +631,8 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
c->bpop.timeout = timeout;
c->bpop.target = target;
if (listpos != NULL) c->bpop.listpos = *listpos;
if (target != NULL) incrRefCount(target);
for (j = 0; j < numkeys; j++) {
@@ -634,6 +706,16 @@ void unblockClientWaitingData(client *c) {
}
}
static int getBlockedTypeByType(int type) {
switch (type) {
case OBJ_LIST: return BLOCKED_LIST;
case OBJ_ZSET: return BLOCKED_ZSET;
case OBJ_MODULE: return BLOCKED_MODULE;
case OBJ_STREAM: return BLOCKED_STREAM;
default: return BLOCKED_NONE;
}
}
/* If the specified key has clients blocked waiting for list pushes, this
* function will put the key reference into the server.ready_keys list.
* Note that db->ready_keys is a hash table that allows us to avoid putting
@@ -641,9 +723,24 @@ void unblockClientWaitingData(client *c) {
* made by a script or in the context of MULTI/EXEC.
*
* The list will be finally processed by handleClientsBlockedOnKeys() */
void signalKeyAsReady(redisDb *db, robj *key) {
void signalKeyAsReady(redisDb *db, robj *key, int type) {
readyList *rl;
/* Quick returns. */
int btype = getBlockedTypeByType(type);
if (btype == BLOCKED_NONE) {
/* The type can never block. */
return;
}
if (!server.blocked_clients_by_type[btype] &&
!server.blocked_clients_by_type[BLOCKED_MODULE]) {
/* No clients block on this type. Note: Blocked modules are represented
* by BLOCKED_MODULE, even if the intention is to wake up by normal
* types (list, zset, stream), so we need to check that there are no
* blocked modules before we do a quick return here. */
return;
}
/* No clients blocking for this key? No need to queue it. */
if (dictFind(db->blocking_keys,key) == NULL) return;
@@ -664,4 +761,3 @@ void signalKeyAsReady(redisDb *db, robj *key) {
serverAssert(dictAdd(db->ready_keys,key,NULL) == DICT_OK);
}
+107 -21
View File
@@ -30,18 +30,26 @@
#include "server.h"
#include <unistd.h>
typedef struct {
size_t keys;
size_t cow;
monotime cow_updated;
double progress;
childInfoType information_type; /* Type of information */
} child_info_data;
/* Open a child-parent channel used in order to move information about the
* RDB / AOF saving process from the child to the parent (for instance
* the amount of copy on write memory used) */
void openChildInfoPipe(void) {
if (pipe(server.child_info_pipe) == -1) {
/* On error our two file descriptors should be still set to -1,
* but we call anyway cloesChildInfoPipe() since can't hurt. */
* but we call anyway closeChildInfoPipe() since can't hurt. */
closeChildInfoPipe();
} else if (anetNonBlock(NULL,server.child_info_pipe[0]) != ANET_OK) {
closeChildInfoPipe();
} else {
memset(&server.child_info_data,0,sizeof(server.child_info_data));
server.child_info_nread = 0;
}
}
@@ -54,34 +62,112 @@ void closeChildInfoPipe(void) {
close(server.child_info_pipe[1]);
server.child_info_pipe[0] = -1;
server.child_info_pipe[1] = -1;
server.child_info_nread = 0;
}
}
/* Send COW data to parent. The child should call this function after populating
* the corresponding fields it want to sent (according to the process type). */
void sendChildInfo(int ptype) {
/* Send save data to parent. */
void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress, char *pname) {
if (server.child_info_pipe[1] == -1) return;
server.child_info_data.magic = CHILD_INFO_MAGIC;
server.child_info_data.process_type = ptype;
ssize_t wlen = sizeof(server.child_info_data);
if (write(server.child_info_pipe[1],&server.child_info_data,wlen) != wlen) {
static monotime cow_updated = 0;
static uint64_t cow_update_cost = 0;
static size_t cow = 0;
child_info_data data = {0}; /* zero everything, including padding to satisfy valgrind */
/* When called to report current info, we need to throttle down CoW updates as they
* can be very expensive. To do that, we measure the time it takes to get a reading
* and schedule the next reading to happen not before time*CHILD_COW_COST_FACTOR
* passes. */
monotime now = getMonotonicUs();
if (info_type != CHILD_INFO_TYPE_CURRENT_INFO ||
!cow_updated ||
now - cow_updated > cow_update_cost * CHILD_COW_DUTY_CYCLE)
{
cow = zmalloc_get_private_dirty(-1);
cow_updated = getMonotonicUs();
cow_update_cost = cow_updated - now;
if (cow) {
serverLog((info_type == CHILD_INFO_TYPE_CURRENT_INFO) ? LL_VERBOSE : LL_NOTICE,
"%s: %zu MB of memory used by copy-on-write",
pname, data.cow / (1024 * 1024));
}
}
data.information_type = info_type;
data.keys = keys;
data.cow = cow;
data.cow_updated = cow_updated;
data.progress = progress;
ssize_t wlen = sizeof(data);
if (write(server.child_info_pipe[1], &data, wlen) != wlen) {
/* Nothing to do on error, this will be detected by the other side. */
}
}
/* Receive COW data from parent. */
/* Update Child info. */
void updateChildInfo(childInfoType information_type, size_t cow, monotime cow_updated, size_t keys, double progress) {
if (information_type == CHILD_INFO_TYPE_CURRENT_INFO) {
server.stat_current_cow_bytes = cow;
server.stat_current_cow_updated = cow_updated;
server.stat_current_save_keys_processed = keys;
if (progress != -1) server.stat_module_progress = progress;
} else if (information_type == CHILD_INFO_TYPE_AOF_COW_SIZE) {
server.stat_aof_cow_bytes = cow;
} else if (information_type == CHILD_INFO_TYPE_RDB_COW_SIZE) {
server.stat_rdb_cow_bytes = cow;
} else if (information_type == CHILD_INFO_TYPE_MODULE_COW_SIZE) {
server.stat_module_cow_bytes = cow;
}
}
/* Read child info data from the pipe.
* if complete data read into the buffer,
* data is stored into *buffer, and returns 1.
* otherwise, the partial data is left in the buffer, waiting for the next read, and returns 0. */
int readChildInfo(childInfoType *information_type, size_t *cow, monotime *cow_updated, size_t *keys, double* progress) {
/* We are using here a static buffer in combination with the server.child_info_nread to handle short reads */
static child_info_data buffer;
ssize_t wlen = sizeof(buffer);
/* Do not overlap */
if (server.child_info_nread == wlen) server.child_info_nread = 0;
int nread = read(server.child_info_pipe[0], (char *)&buffer + server.child_info_nread, wlen - server.child_info_nread);
if (nread > 0) {
server.child_info_nread += nread;
}
/* We have complete child info */
if (server.child_info_nread == wlen) {
*information_type = buffer.information_type;
*cow = buffer.cow;
*cow_updated = buffer.cow_updated;
*keys = buffer.keys;
*progress = buffer.progress;
return 1;
} else {
return 0;
}
}
/* Receive info data from child. */
void receiveChildInfo(void) {
if (server.child_info_pipe[0] == -1) return;
ssize_t wlen = sizeof(server.child_info_data);
if (read(server.child_info_pipe[0],&server.child_info_data,wlen) == wlen &&
server.child_info_data.magic == CHILD_INFO_MAGIC)
{
if (server.child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
server.stat_rdb_cow_bytes = server.child_info_data.cow_size;
} else if (server.child_info_data.process_type == CHILD_INFO_TYPE_AOF) {
server.stat_aof_cow_bytes = server.child_info_data.cow_size;
} else if (server.child_info_data.process_type == CHILD_INFO_TYPE_MODULE) {
server.stat_module_cow_bytes = server.child_info_data.cow_size;
}
size_t cow;
monotime cow_updated;
size_t keys;
double progress;
childInfoType information_type;
/* Drain the pipe and update child info so that we get the final message. */
while (readChildInfo(&information_type, &cow, &cow_updated, &keys, &progress)) {
updateChildInfo(information_type, cow, cow_updated, keys, progress);
}
}
+194
View File
@@ -0,0 +1,194 @@
/* CLI (command line interface) common methods
*
* Copyright (c) 2020, Redis Labs
* 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 "cli_common.h"
#include <errno.h>
#include <hiredis.h>
#include <sdscompat.h> /* Use hiredis' sds compat header that maps sds calls to their hi_ variants */
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <hiredis_ssl.h>
#endif
/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
* not building with TLS support.
*/
int cliSecureConnection(redisContext *c, cliSSLconfig config, const char **err) {
#ifdef USE_OPENSSL
static SSL_CTX *ssl_ctx = NULL;
if (!ssl_ctx) {
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) {
*err = "Failed to create SSL_CTX";
goto error;
}
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_verify(ssl_ctx, config.skip_cert_verify ? SSL_VERIFY_NONE : SSL_VERIFY_PEER, NULL);
if (config.cacert || config.cacertdir) {
if (!SSL_CTX_load_verify_locations(ssl_ctx, config.cacert, config.cacertdir)) {
*err = "Invalid CA Certificate File/Directory";
goto error;
}
} else {
if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
*err = "Failed to use default CA paths";
goto error;
}
}
if (config.cert && !SSL_CTX_use_certificate_chain_file(ssl_ctx, config.cert)) {
*err = "Invalid client certificate";
goto error;
}
if (config.key && !SSL_CTX_use_PrivateKey_file(ssl_ctx, config.key, SSL_FILETYPE_PEM)) {
*err = "Invalid private key";
goto error;
}
if (config.ciphers && !SSL_CTX_set_cipher_list(ssl_ctx, config.ciphers)) {
*err = "Error while configuring ciphers";
goto error;
}
#ifdef TLS1_3_VERSION
if (config.ciphersuites && !SSL_CTX_set_ciphersuites(ssl_ctx, config.ciphersuites)) {
*err = "Error while setting cypher suites";
goto error;
}
#endif
}
SSL *ssl = SSL_new(ssl_ctx);
if (!ssl) {
*err = "Failed to create SSL object";
return REDIS_ERR;
}
if (config.sni && !SSL_set_tlsext_host_name(ssl, config.sni)) {
*err = "Failed to configure SNI";
SSL_free(ssl);
return REDIS_ERR;
}
return redisInitiateSSL(c, ssl);
error:
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
return REDIS_ERR;
#else
(void) config;
(void) c;
(void) err;
return REDIS_OK;
#endif
}
/* 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.
*/
ssize_t cliWriteConn(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;
}
/* Wrapper around OpenSSL (libssl and libcrypto) initialisation
*/
int cliSecureInit()
{
#ifdef USE_OPENSSL
ERR_load_crypto_strings();
SSL_load_error_strings();
SSL_library_init();
#endif
return REDIS_OK;
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef __CLICOMMON_H
#define __CLICOMMON_H
#include <hiredis.h>
typedef struct cliSSLconfig {
/* Requested SNI, or NULL */
char *sni;
/* CA Certificate file, or NULL */
char *cacert;
/* Directory where trusted CA certificates are stored, or NULL */
char *cacertdir;
/* Skip server certificate verification. */
int skip_cert_verify;
/* Client certificate to authenticate with, or NULL */
char *cert;
/* Private key file to authenticate with, or NULL */
char *key;
/* Prefered cipher list, or NULL (applies only to <= TLSv1.2) */
char* ciphers;
/* Prefered ciphersuites list, or NULL (applies only to TLSv1.3) */
char* ciphersuites;
} cliSSLconfig;
/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
* not building with TLS support.
*/
int cliSecureConnection(redisContext *c, cliSSLconfig config, const char **err);
/* 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.
*/
ssize_t cliWriteConn(redisContext *c, const char *buf, size_t buf_len);
/* Wrapper around OpenSSL (libssl and libcrypto) initialisation.
*/
int cliSecureInit();
#endif /* __CLICOMMON_H */
+429 -250
View File
File diff suppressed because it is too large Load Diff
+16 -8
View File
@@ -38,7 +38,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; /* Allocated size of rcvbuf */
struct clusterNode *node; /* Node related to this link if any, or NULL */
} clusterLink;
@@ -51,8 +53,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)
@@ -77,6 +79,7 @@ typedef struct clusterLink {
#define CLUSTER_TODO_UPDATE_STATE (1<<1)
#define CLUSTER_TODO_SAVE_CONFIG (1<<2)
#define CLUSTER_TODO_FSYNC_CONFIG (1<<3)
#define CLUSTER_TODO_HANDLE_MANUALFAILOVER (1<<4)
/* Message types.
*
@@ -115,6 +118,7 @@ typedef struct clusterNode {
int flags; /* CLUSTER_NODE_... */
uint64_t configEpoch; /* Last configEpoch observed for this node */
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
sds slots_info; /* Slots info represented by string. */
int numslots; /* Number of slots handled by this node */
int numslaves; /* Number of slave nodes, if this is a master */
struct clusterNode **slaves; /* pointers to slave nodes */
@@ -131,7 +135,9 @@ typedef struct clusterNode {
mstime_t orphaned_time; /* Starting time of orphaned master condition */
long long repl_offset; /* Last known repl offset for this node. */
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
int port; /* Latest known clients port of this node */
int port; /* Latest known clients port (TLS or plain). */
int pport; /* Latest known clients plaintext port. Only used
if the main clients port is for TLS. */
int cport; /* Latest known cluster port of this node. */
clusterLink *link; /* TCP/IP link with this node */
list *fail_reports; /* List of nodes signaling this as failing */
@@ -164,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 -1 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. */
@@ -190,7 +196,8 @@ typedef struct {
uint16_t port; /* base port last time it was seen */
uint16_t cport; /* cluster port last time it was seen */
uint16_t flags; /* node->flags copy */
uint32_t notused1;
uint16_t pport; /* plaintext-port, when base port is TLS */
uint16_t notused1;
} clusterMsgDataGossip;
typedef struct {
@@ -263,7 +270,8 @@ typedef struct {
unsigned char myslots[CLUSTER_SLOTS/8];
char slaveof[CLUSTER_NAMELEN];
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
char notused1[34]; /* 34 bytes reserved for future usage. */
char notused1[32]; /* 32 bytes reserved for future usage. */
uint16_t pport; /* Sender TCP plaintext port, if base port is TLS */
uint16_t cport; /* Sender TCP cluster bus port */
uint16_t flags; /* Sender node flags */
unsigned char state; /* Cluster state from the POV of the sender */

Some files were not shown because too many files have changed in this diff Show More