Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae6a2aa95c | ||
|
|
c8649f8e85 | ||
|
|
b351d5a321 | ||
|
|
fe8de4313f | ||
|
|
2ad2548747 | ||
|
|
f60370ce28 | ||
|
|
464aad9ee7 | ||
|
|
439b8da475 | ||
|
|
1caaf581f1 | ||
|
|
e46ddde28d | ||
|
|
8f70fcc65b | ||
|
|
8d3a1c978a | ||
|
|
c34e64848f | ||
|
|
1be66b98f5 | ||
|
|
423d1909c5 | ||
|
|
1bda797dc0 | ||
|
|
d5bae505ba | ||
|
|
099a2f40ec | ||
|
|
4bd614c52d | ||
|
|
d2c8a4b91e | ||
|
|
85408b7391 | ||
|
|
5a2f4a1e94 | ||
|
|
c4776cafcf | ||
|
|
4cbf903083 | ||
|
|
a91b57eff7 | ||
|
|
8359ce266c | ||
|
|
856c8a47d0 | ||
|
|
5f5f298a4a | ||
|
|
7f4bae8176 | ||
|
|
cee6d309d9 | ||
|
|
cf013b83df | ||
|
|
d424259a12 | ||
|
|
7f6de086fe | ||
|
|
8c6ebf84ae | ||
|
|
ba113c8337 | ||
|
|
348eda81e0 | ||
|
|
3f6bd4fc25 | ||
|
|
d6601de73f | ||
|
|
00023873a8 | ||
|
|
413c8be5ad | ||
|
|
1119ecae6f | ||
|
|
cc244370a2 | ||
|
|
3e7523aceb | ||
|
|
459acdeeb1 | ||
|
|
0b2cbf138e | ||
|
|
9e505e6cd8 | ||
|
|
68305c5b99 | ||
|
|
29622276ec | ||
|
|
3d1d3e23ac | ||
|
|
941cdc2759 | ||
|
|
1a58981868 | ||
|
|
c7b3ce90f1 | ||
|
|
819d291bd7 | ||
|
|
a51eb05b18 | ||
|
|
986dbf716e | ||
|
|
d4439bd41c | ||
|
|
e26a769d96 |
+543
-11
@@ -1,16 +1,548 @@
|
||||
Hello! This file is just a placeholder, since this is the "unstable" branch
|
||||
of Redis, the place where all the development happens.
|
||||
Redis 7.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.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
https://download.redis.io/redis-stable.tar.gz
|
||||
|
||||
More information is available at https://redis.io
|
||||
Redis Community Edition 7.2.6 Released Wed 02 Oct 2024 20:17:04 IDT
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security fixes
|
||||
==============
|
||||
|
||||
* (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.
|
||||
* (CVE-2024-31227) Potential Denial-of-service due to malformed ACL selectors.
|
||||
* (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.
|
||||
|
||||
Bug fixes
|
||||
=========
|
||||
|
||||
* Fixed crashes in cluster mode (#13315)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.5 Released Thu 16 May 2024 12:00:00 IST
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency MODERATE: Program an upgrade of the server, but it's not urgent.
|
||||
|
||||
Bug fixes
|
||||
=========
|
||||
|
||||
* A single shard cluster leaves failed replicas in CLUSTER SLOTS instead of removing them (#12824)
|
||||
* Crash in LSET command when replacing small items and exceeding 4GB (#12955)
|
||||
* Blocking commands timeout is reset due to re-processing command (#13004)
|
||||
* Conversion of numbers in Lua args to redis args can fail. Bug introduced in 7.2.0 (#13115)
|
||||
|
||||
Bug fixes in CLI tools
|
||||
======================
|
||||
|
||||
* redis-cli: --count (for --scan, --bigkeys, etc) was ignored unless --pattern was also used (#13092)
|
||||
* redis-check-aof: incorrectly considering data in manifest format as MP-AOF (#12958)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.4 Released Tue 09 Jan 2024 10:45:52 IST
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security fixes
|
||||
==============
|
||||
* (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory
|
||||
buffers which can result in incorrect accounting of buffer sizes and lead to
|
||||
heap overflow and potential remote code execution.
|
||||
|
||||
Bug fixes
|
||||
=========
|
||||
|
||||
* Fix crashes of cluster commands clusters with mixed versions of 7.0 and 7.2 (#12805, #12832)
|
||||
* Fix slot ownership not being properly handled when deleting a slot from a node (#12564)
|
||||
* Fix atomicity issues with the RedisModuleEvent_Key module API event (#12733)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.3 Released Wed 01 Nov 2023 12:00:00 IST
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: HIGH, Fixes critical bugs affecting most users.
|
||||
|
||||
Bug fixes
|
||||
=========
|
||||
|
||||
* Fix file descriptor leak preventing deleted files from freeing disk space on
|
||||
replicas (#12693)
|
||||
* Fix a possible crash after cluster node removal (#12702)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.2 Released Wed 18 Oct 2023 10:33:40 IDT
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security fixes
|
||||
==============
|
||||
|
||||
* (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a
|
||||
race condition that can be used by another process to bypass desired Unix
|
||||
socket permissions on startup.
|
||||
|
||||
|
||||
Platform / toolchain support related changes
|
||||
=================================================
|
||||
|
||||
* Fix compilation error on MacOS 13 (#12611)
|
||||
|
||||
Bug fixes
|
||||
=========
|
||||
|
||||
* WAITAOF could timeout in the absence of write traffic in case a new AOF is
|
||||
created and an AOF rewrite can't immediately start (#12620)
|
||||
|
||||
Redis cluster
|
||||
=============
|
||||
|
||||
* Fix crash when running rebalance command in a mixed cluster of 7.0 and 7.2
|
||||
nodes (#12604)
|
||||
* Fix the return type of the slot number in cluster shards to integer, which
|
||||
makes it consistent with past behavior (#12561)
|
||||
* Fix CLUSTER commands are called from modules or scripts to return TLS info
|
||||
appropriately (#12569)
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli, fix crash on reconnect when in SUBSCRIBE mode (#12571)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Fix overflow calculation for next timer event (#12474)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.1 Released Wed 06 Sep 2023 15:00:00 IDT
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency SECURITY: See security fixes below.
|
||||
|
||||
Security Fixes
|
||||
==============
|
||||
|
||||
* (CVE-2023-41053) Redis does not correctly identify keys accessed by SORT_RO and,
|
||||
as a result, may grant users executing this command access to keys that are not
|
||||
explicitly authorized by the ACL configuration.
|
||||
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix crashes when joining a node to an existing 7.0 Redis Cluster (#12538)
|
||||
* Correct request_policy and response_policy command tips on for some admin /
|
||||
configuration commands (#12545, #12530)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.2.0 GA Released Tue Aug 15 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the first stable Release for Redis 7.2.
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* redis-cli in cluster mode handles `unknown-endpoint` (#12273)
|
||||
* Update request / response policy hints for a few commands (#12417)
|
||||
* Ensure that the function load timeout is disabled during loading from RDB/AOF and on replicas. (#12451)
|
||||
* Fix false success and a memory leak for ACL selector with bad parenthesis combination (#12452)
|
||||
* Fix the assertion when script timeout occurs after it signaled a blocked client (#12459)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Update MONITOR client's memory correctly for INFO and client-eviction (#12420)
|
||||
* The response of cluster nodes was unnecessarily adding an extra comma when no
|
||||
hostname was present. (#12411)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC3 Released Mon July 10 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the third Release Candidate for Redis 7.2.
|
||||
Upgrade urgency SECURITY: If you're using a previous release candidate of 7.2.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger
|
||||
a heap overflow in the cjson and cmsgpack libraries, and result in heap
|
||||
corruption and potentially remote code execution. The problem exists in all
|
||||
versions of Redis with Lua scripting support, starting from 2.6, and affects
|
||||
only authenticated and authorized users.
|
||||
* (CVE-2023-36824) Extracting key names from a command and a list of arguments
|
||||
may, in some cases, trigger a heap overflow and result in reading random heap
|
||||
memory, heap corruption and potentially remote code execution. Specifically:
|
||||
using COMMAND GETKEYS* and validation of key names in ACL rules.
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
New administrative and introspection commands and command arguments
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* Make SENTINEL CONFIG [SET|GET] variadic. (#10362)
|
||||
|
||||
Potentially Breaking / Behavior Changes
|
||||
=======================================
|
||||
|
||||
* Cluster SHARD IDs are no longer visible in the cluster nodes output,
|
||||
introduced in 7.2-RC1. (#10536, #12166)
|
||||
* When calling PUBLISH with a RESP3 client that's also subscribed to the same channel,
|
||||
the order is changed and the reply is sent before the published message (#12326)
|
||||
|
||||
New configuration options
|
||||
=========================
|
||||
|
||||
* Add a new loglevel "nothing" to disable logging (#12133)
|
||||
* Add cluster-announce-human-nodename - a unique identifier for a node that is
|
||||
be used in logs for debugging (#9564)
|
||||
|
||||
Other General Improvements
|
||||
==========================
|
||||
|
||||
* Allow CLUSTER SLOTS / SHARDS commands during loading (#12269)
|
||||
* Support TLS service when "tls-cluster" is not enabled and persist both plain
|
||||
and TLS port in nodes.conf (#12233)
|
||||
* Update SPOP and RESTORE commands to replicate unlink commands to replicas
|
||||
when the server is configured to use async server deletes (#12320)
|
||||
* Try lazyfree the temporary zset in ZUNION / ZINTER / ZDIFF (#12229)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Optimize PSUBSCRIBE and PUNSUBSCRIBE from O(N*M) to O(N) (#12298)
|
||||
* Optimize SCAN, SSCAN, HSCAN, ZSCAN commands (#12209)
|
||||
* Set Jemalloc --disable-cache-oblivious to reduce memory overhead (#12315)
|
||||
* Optimize ZINTERCARD to avoid create a temporary zset (#12229)
|
||||
* Optimize HRANDFIELD and ZRANDMEMBER listpack encoded (#12205)
|
||||
* Numerous other optimizations (#12155, #12082, #11626, #11944, #12316, #12250,
|
||||
#12177, #12185)
|
||||
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli: Handle RESP3 double responses that contain a NaN (#12254)
|
||||
* redis-cli: Support URIs with IPv6 (#11834)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Align semantics of the new (v7.2 RC2) RM_ReplyWithErrorFormat with RM_ReplyWithError.
|
||||
This is a breaking change that affects the generated error code. (#12321)
|
||||
* Forbid RM_AddPostNotificationJob on loading and on read-only replicas (#12304)
|
||||
* Add ability for module command filter to know which client is being handled (#12219)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix broken protocol when PUBLISH is used inside MULTI when the RESP3
|
||||
publishing client is also subscribed for the channel (#12326)
|
||||
* Fix WAIT to be effective after a blocked module command being unblocked (#12220)
|
||||
* Re-enable downscale rehashing while there is a fork child (#12276)
|
||||
* Fix possible hang in HRANDFIELD, SRANDMEMBER, ZRANDMEMBER when used with `<count>` (#12276)
|
||||
* Improve fairness issue in RANDOMKEY, HRANDFIELD, SRANDMEMBER, ZRANDMEMBER, SPOP, and eviction (#12276)
|
||||
* Cluster: fix a race condition where a slot migration may revert on a subsequent failover or node joining (#12344)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Fix XREADGROUP BLOCK with ">" from hanging (#12301)
|
||||
* Fix assertion when a blocked command is rejected when re-processed. (#12247)
|
||||
* Fix use after free on a blocking RM_Call. (#12342)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC2 Released Mon May 15 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the second Release Candidate for Redis 7.2.
|
||||
|
||||
INFO fields and introspection changes
|
||||
=====================================
|
||||
|
||||
* Add a few low level event loop metrics to help diagnose latency (#11963)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Minor performance improvement to SADD and HSET (#12019)
|
||||
|
||||
Platform / toolchain support related changes
|
||||
=================================================
|
||||
|
||||
* Upgrade to Jemalloc 5.3.0, resolves a rare fork child hang (#12115)
|
||||
* Fix a compiler fortification induced crash when used with link time optimizations (#11982)
|
||||
* Fix local clients detection, 127.*.*.* instead of 127.0.0.1 (#11664)
|
||||
* Report AOF failure status to systemd in shutdown (#12065)
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli: Reimplement and improve help hints based on actual command arg docs (#10515)
|
||||
* redis-cli: Add option --count for tuning SCAN based features (#12042)
|
||||
* redis-benchmark: Add --seed option to seed the random number generator (#11945)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Add RM_RdbLoad and RM_RdbSave APIs (#11852)
|
||||
* Add RM_ReplyWithErrorFormat that can support format string (#11923)
|
||||
* Fix: Delete empty key when RM_ZsetAdd, RM_ZsetIncrby, RM_StreamAdd fail (#12129)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* LPOS with RANK set to LONG_MIN returning wrong result (#12167)
|
||||
* Avoid unnecessary full sync after master restart in a rare case (#12088)
|
||||
* Iterate clients fairly when processing background chores (#12025)
|
||||
* Avoid incorrect shrinking of query buffer when reading large data from clients (#12000)
|
||||
* Sentinel: Fix config rewrite error when old known-slave is used (#11775)
|
||||
* ACL: Disconnect pub-sub subscribers when revoking allchannels permission (#11992)
|
||||
* Add a missing fsync of AOF file in rare cases (#11973)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Fix tracking of command duration metrics for MULTI, EVAL, WAIT and modules (#11970)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC1 Released Wed Mar 22 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the first Release Candidate for Redis 7.2.
|
||||
|
||||
Redis Release Candidate (RC) versions are early versions that are made available
|
||||
for early adopters in the community to test them. We do not consider
|
||||
them suitable for production environments.
|
||||
|
||||
Introduction to the Redis 7.2 release
|
||||
=====================================
|
||||
|
||||
Redis 7.2 includes optimizations, several new commands, some improvements,
|
||||
bug fixes, and several new module APIs.
|
||||
|
||||
In particular, users should be aware of the following changes:
|
||||
|
||||
1. Redis 7.2 uses a new format (version 11) for RDB files, which is incompatible
|
||||
with older versions.
|
||||
2. See section about breaking changes mentioned below.
|
||||
3. If you use modules, see the module API breaking changes section below.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 7.0.10.
|
||||
Each one includes the PR number that added it so that you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
* Introduce WAITAOF command, to block the client until a specified number
|
||||
of Redises have synced all previous write commands to the AOF on disk,
|
||||
see https://redis.io/commands/waitaof/
|
||||
|
||||
New user commands or command arguments
|
||||
--------------------------------------
|
||||
|
||||
* WAITAOF blocks until writes have been synced to disk (#11713)
|
||||
* Add WITHSCORE option to ZRANK and ZREVRANK (#11235)
|
||||
|
||||
New administrative and introspection commands and command arguments
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* CLIENT SETINFO lets client library report name and version Redis (#11758)
|
||||
* CLIENT NO-TOUCH for clients to run commands without affecting LRU/LFU of keys (#11483)
|
||||
* Introduce Shard IDs to logically group nodes in cluster mode based on
|
||||
replication. Shard IDs are automatically assigned and visible via
|
||||
`CLUSTER MYSHARDID`. (#10536)
|
||||
|
||||
Command replies that have been extended
|
||||
---------------------------------------
|
||||
|
||||
* ACL LOG - Add entry id, timestamp created, and timestamp last updated time (#11477)
|
||||
* COMMAND DOCS - Repurpose arg names as the unique ID (#11051)
|
||||
* CLIENT LIST has `T` flag to indicate CLIENT NO-TOUCH (#11483)
|
||||
* CLIENT LIST show lib-name, lib-ver (#11758)
|
||||
|
||||
Potentially Breaking / Behavior Changes
|
||||
=======================================
|
||||
|
||||
* Client side tracking for scripts now tracks the keys that are read by the
|
||||
script instead of the keys that are declared by the caller of EVAL / FCALL (#11770)
|
||||
* Freeze time sampling during command execution and in scripts (#10300)
|
||||
* When a blocked command is being unblocked, checks like ACL, OOM, etc are
|
||||
re-evaluated (#11012)
|
||||
* Unify ACL failure error message text and error codes (#11160)
|
||||
* Blocked stream command that's released when key no longer exists carries a
|
||||
different error code (#11012)
|
||||
* Command stats are updated for blocked commands only when / if the command
|
||||
actually executes (#11012)
|
||||
* The way ACL users are stored internally no longer removes redundant command
|
||||
and category rules, which may alter the way those rules are displayed as part
|
||||
of `ACL SAVE`, `ACL GETUSER` and `ACL LIST` (#11224)
|
||||
* Client connections created for TLS-based replication use SNI if possible (#11458)
|
||||
* Stream consumers: Re-purpose seen-time, add active-time (#11099)
|
||||
* XREADGROUP and X[AUTO]CLAIM create the consumer regardless of whether it was
|
||||
able to perform some reading/claiming (#11099)
|
||||
* ACL default newly created user set sanitize-payload flag in ACL LIST/GETUSER #11279
|
||||
* Fix HELLO command not to affect the client state unless successful (#11659)
|
||||
* Normalize `NAN` in replies to a single nan type, like we do with `inf` (#11597)
|
||||
|
||||
Deprecations
|
||||
============
|
||||
|
||||
* Mark the QUIT command as deprecated (#11439)
|
||||
* Delete RDB loading code for pre-release RDB formats (#11058)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Significant memory optimization of small list type keys (#11303)
|
||||
* Significant memory optimization for small set type keys (#11290)
|
||||
* Significant memory optimization for large sets (#11595)
|
||||
* Significant speed optimization in ZRANGE replies WITHSCORES in case of integer scores (#11779)
|
||||
* Significant speed optimization in double replies, mainly sorted sets commands (#10587)
|
||||
* Optimize the performance of commands with multiple keys in cluster mode (#11044)
|
||||
* Incrementally reclaim OS page cache of RDB file (#11248)
|
||||
* Improve memory management of cluster bus links when there is a large number of pending messages (#11343)
|
||||
* Minor performance improvement for workloads that use commands without pipelining (#11220)
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli accepts commands in subscribed mode (#11873)
|
||||
|
||||
Other General Improvements
|
||||
==========================
|
||||
|
||||
* WAIT now no longer waits for the replication offset after your last command,
|
||||
but rather the replication offset after your last write (#11713)
|
||||
* Automatically propagate node deletion to other nodes in a cluster when
|
||||
`CLUSTER FORGET` is called, allowing nodes to be deleted with a single call
|
||||
in most cases (#10869)
|
||||
* Blocking commands that were disallowed in scripts now behave in scripts the
|
||||
same they did in MULTI (#11568)
|
||||
|
||||
Platform / toolchain support related changes
|
||||
=================================================
|
||||
|
||||
* 32-bit builds compiled without HAVE_MALLOC_SIZE (not jemalloc or glibc)
|
||||
will consume more memory (#11595)
|
||||
* Use jemalloc by default also on ARM (#11407)
|
||||
* Adds stack trace and register dump support in crash report for illumos/solaris (#11335)
|
||||
|
||||
|
||||
New configuration options
|
||||
=========================
|
||||
|
||||
* locale-collate runtime config to control setlocale affecting Lua and SORT (#11059)
|
||||
* Add CONFIG SET and GET loglevel feature in Sentinel (#11214)
|
||||
|
||||
INFO fields and introspection changes
|
||||
=====================================
|
||||
|
||||
* Added 4 new info fields for authentication errors and commands denied access
|
||||
for keys, channels and commands (#11288)
|
||||
* INFO SERVER includes a list of listeners (#9320)
|
||||
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Make it possible for module commands to be part of ACL categories (#11708)
|
||||
* Add K flag to RM_Call to allow running blocking commands and set a callback to get the response (#11568)
|
||||
* Add RM_AddPostNotificationJob to allow writes after keyspace notification hooks (#11199)
|
||||
* RedisModule_Event_Key to notify about keys being unlinked together with reason and value (#9406)
|
||||
* Add RM_BlockClient[Set|Get]PrivateData to associate a module data with the blocked client (#11568)
|
||||
* APIs to allow modules to participate / handle AUTH validation (#11659)
|
||||
* RM_GetContextFlags supports a new flag: REDISMODULE_CTX_FLAGS_SERVER_STARTUP (#9320)
|
||||
* Add REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS and RedisModule_GetModuleOptionsAll (#11199)
|
||||
* RM_BlockClientOnKeysWithFlags allows module to request being unblocked when the key is deleted (#11310)
|
||||
* Introduce aux_save2 makes it possible to skip saving that field in the RDB and
|
||||
enable loading the file in the absence of the module (#11374)
|
||||
* Add a dry run flag to RM_Call to do validations before actual execution (#11158)
|
||||
* Add RM_Microseconds and RM_CachedMicroseconds (#11016)
|
||||
* Add RM_ACLAddLogEntryByUserName API to be used without a user object (#11659)
|
||||
* Make it possible to keep the RM_Call reply for longer than the context lifetime in case
|
||||
auto memory was not used (#11568)
|
||||
|
||||
Potentially Breaking Changes in Module API
|
||||
------------------------------------------
|
||||
|
||||
* RM_Call only enforces OOM on scripts if 'M' flag is set (#11425)
|
||||
* Block some specific characters in module command names (#11434)
|
||||
* Fix replication inconsistency on modules that uses keyspace notifications (#10969)
|
||||
* Prevent command, configs, data types registration after the onload handler (#11708)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Introduce socket shutdown to properly disconnect a client while a fork is active (#11376)
|
||||
* CLIENT RESET clears the CLIENT NO-EVICT flag (#11483)
|
||||
* Reduce memory usage on strings loaded by a module from an RDB file (#11050)
|
||||
* Fix a bug where nodes in a cluster may not replicate or handle internal events for
|
||||
keys deleted when another node in the cluster claimed a slot (#11084)
|
||||
* Fix HINCRBYFLOAT not to create a key if the new value is invalid (#11149)
|
||||
* Make cluster config file saving atomic and fsync acl file saving (#10924)
|
||||
* WAIT command would not block if used in RM_Call (#11713)
|
||||
* Minor fixes to command metadata in COMMAND command (#11201, #10273)
|
||||
|
||||
|
||||
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:
|
||||
|
||||
- Meir Shpilraien
|
||||
- Guy Benoish
|
||||
- Viktor Söderqvist
|
||||
- Zhu Binbin
|
||||
- Oran Agra
|
||||
- sundb
|
||||
- Ran Shidlansik
|
||||
- Zhenwei Pi
|
||||
- Jason Elbaum
|
||||
- Karthik Subbarao
|
||||
- Madelyn Olson
|
||||
- Huang Zhw
|
||||
- Ping Xie
|
||||
- Ozan Tezcan
|
||||
- Chen Tianjie
|
||||
- Deng Ju
|
||||
- Wen Hui
|
||||
- Brennan Cathcart
|
||||
- Itamar Haber
|
||||
- Shaya Potter
|
||||
- Roshan Khatri
|
||||
- Slava Koyfman
|
||||
- Zhu Tian
|
||||
- Moti Cohen
|
||||
- Arad Zilberstein
|
||||
- Basel Naamna
|
||||
- Mingyi Kang
|
||||
- Uri Yagelnik
|
||||
- Filipe Oliveira
|
||||
- Zhao Zhao
|
||||
- Valentino Geron
|
||||
- Yaacov Hazan
|
||||
- Adi Pinsky
|
||||
- David Carlier
|
||||
- Li Changjun
|
||||
|
||||
Happy hacking!
|
||||
|
||||
Vendored
+1
@@ -132,6 +132,7 @@ static int bit_tohex(lua_State *L)
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if (n == INT32_MIN) n = INT32_MIN+1;
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
|
||||
@@ -1051,7 +1051,7 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
|
||||
flags |= ACL_READ_PERMISSION;
|
||||
} else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) {
|
||||
flags |= ACL_WRITE_PERMISSION;
|
||||
} else if (op[offset] == '~') {
|
||||
} else if (op[offset] == '~' && flags) {
|
||||
offset++;
|
||||
break;
|
||||
} else {
|
||||
|
||||
@@ -333,7 +333,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
processed++;
|
||||
now = getMonotonicUs();
|
||||
if (retval != AE_NOMORE) {
|
||||
te->when = now + retval * 1000;
|
||||
te->when = now + (monotime)retval * 1000;
|
||||
} else {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
}
|
||||
|
||||
+6
-5
@@ -417,13 +417,16 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
|
||||
return s;
|
||||
}
|
||||
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) {
|
||||
if (bind(s,sa,len) == -1) {
|
||||
anetSetError(err, "bind: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_LOCAL && perm)
|
||||
chmod(((struct sockaddr_un *) sa)->sun_path, perm);
|
||||
|
||||
if (listen(s, backlog) == -1) {
|
||||
anetSetError(err, "listen: %s", strerror(errno));
|
||||
close(s);
|
||||
@@ -467,7 +470,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
|
||||
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
@@ -508,10 +511,8 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path));
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
if (perm)
|
||||
chmod(sa.sun_path, perm);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,9 @@ aofInfo *aofInfoDup(aofInfo *orig) {
|
||||
return ai;
|
||||
}
|
||||
|
||||
/* Format aofInfo as a string and it will be a line in the manifest. */
|
||||
/* Format aofInfo as a string and it will be a line in the manifest.
|
||||
*
|
||||
* When update this format, make sure to update redis-check-aof as well. */
|
||||
sds aofInfoFormat(sds buf, aofInfo *ai) {
|
||||
sds filename_repr = NULL;
|
||||
|
||||
@@ -976,18 +978,6 @@ void stopAppendOnly(void) {
|
||||
int startAppendOnly(void) {
|
||||
serverAssert(server.aof_state == AOF_OFF);
|
||||
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
|
||||
server.aof_state = AOF_WAIT_REWRITE;
|
||||
if (hasActiveChildProcess() && server.child_type != CHILD_TYPE_AOF) {
|
||||
server.aof_rewrite_scheduled = 1;
|
||||
@@ -2454,7 +2444,23 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
server.aof_lastbgrewrite_status = C_ERR;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if (server.aof_state == AOF_WAIT_REWRITE) {
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
}
|
||||
|
||||
server.stat_aof_rewrites++;
|
||||
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
|
||||
|
||||
+6
-2
@@ -370,7 +370,12 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
list *l;
|
||||
int j;
|
||||
|
||||
c->bstate.timeout = timeout;
|
||||
if (!(c->flags & CLIENT_REPROCESSING_COMMAND)) {
|
||||
/* If the client is re-processing the command, we do not set the timeout
|
||||
* because we need to retain the client's original timeout. */
|
||||
c->bstate.timeout = timeout;
|
||||
}
|
||||
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
/* If the key already exists in the dictionary ignore it. */
|
||||
if (!(client_blocked_entry = dictAddRaw(c->bstate.keys,keys[j],NULL))) {
|
||||
@@ -392,7 +397,6 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
listAddNodeTail(l,c);
|
||||
dictSetVal(c->bstate.keys,client_blocked_entry,listLast(l));
|
||||
|
||||
|
||||
/* We need to add the key to blocking_keys_unblock_on_nokey, if the client
|
||||
* wants to be awakened if key is deleted (like XREADGROUP) */
|
||||
if (unblock_on_nokey) {
|
||||
|
||||
+60
-20
@@ -119,6 +119,20 @@ static inline int defaultClientPort(void) {
|
||||
return server.tls_cluster ? server.tls_port : server.port;
|
||||
}
|
||||
|
||||
/* When a cluster command is called, we need to decide whether to return TLS info or
|
||||
* non-TLS info by the client's connection type. However if the command is called by
|
||||
* a Lua script or RM_call, there is no connection in the fake client, so we use
|
||||
* server.current_client here to get the real client if available. And if it is not
|
||||
* available (modules may call commands without a real client), we return the default
|
||||
* info, which is determined by server.tls_cluster. */
|
||||
static int shouldReturnTlsInfo(void) {
|
||||
if (server.current_client && server.current_client->conn) {
|
||||
return connIsTLS(server.current_client->conn);
|
||||
} else {
|
||||
return server.tls_cluster;
|
||||
}
|
||||
}
|
||||
|
||||
/* Links to the next and previous entries for keys in the same slot are stored
|
||||
* in the dict entry metadata. See Slot to Key API below. */
|
||||
#define dictEntryNextInSlot(de) \
|
||||
@@ -938,13 +952,13 @@ static void updateAnnouncedHumanNodename(clusterNode *node, char *new) {
|
||||
|
||||
|
||||
static void updateShardId(clusterNode *node, const char *shard_id) {
|
||||
if (memcmp(node->shard_id, shard_id, CLUSTER_NAMELEN) != 0) {
|
||||
if (shard_id && memcmp(node->shard_id, shard_id, CLUSTER_NAMELEN) != 0) {
|
||||
clusterRemoveNodeFromShard(node);
|
||||
memcpy(node->shard_id, shard_id, CLUSTER_NAMELEN);
|
||||
clusterAddNodeToShard(shard_id, node);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
|
||||
}
|
||||
if (myself != node && myself->slaveof == node) {
|
||||
if (shard_id && myself != node && myself->slaveof == node) {
|
||||
if (memcmp(myself->shard_id, shard_id, CLUSTER_NAMELEN) != 0) {
|
||||
/* shard-id can diverge right after a rolling upgrade
|
||||
* from pre-7.2 releases */
|
||||
@@ -974,7 +988,7 @@ void clusterInit(void) {
|
||||
server.cluster->myself = NULL;
|
||||
server.cluster->currentEpoch = 0;
|
||||
server.cluster->state = CLUSTER_FAIL;
|
||||
server.cluster->size = 1;
|
||||
server.cluster->size = 0;
|
||||
server.cluster->todo_before_sleep = 0;
|
||||
server.cluster->nodes = dictCreate(&clusterNodesDictType);
|
||||
server.cluster->shards = dictCreate(&clusterSdsToListType);
|
||||
@@ -1120,6 +1134,9 @@ void clusterReset(int hard) {
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Empty the nodes blacklist. */
|
||||
dictEmpty(server.cluster->nodes_black_list, NULL);
|
||||
|
||||
/* Hard reset only: set epochs to 0, change node ID. */
|
||||
if (hard) {
|
||||
sds oldname;
|
||||
@@ -1670,6 +1687,7 @@ void clusterRenameNode(clusterNode *node, char *newname) {
|
||||
serverAssert(retval == DICT_OK);
|
||||
memcpy(node->name, newname, CLUSTER_NAMELEN);
|
||||
clusterAddNode(node);
|
||||
clusterAddNodeToShard(node->shard_id, node);
|
||||
}
|
||||
|
||||
void clusterAddNodeToShard(const char *shard_id, clusterNode *node) {
|
||||
@@ -2217,6 +2235,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
node->tls_port = msg_tls_port;
|
||||
node->cport = ntohs(g->cport);
|
||||
clusterAddNode(node);
|
||||
clusterAddNodeToShard(node->shard_id, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2394,7 +2413,6 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
|
||||
}
|
||||
clusterDelSlot(j);
|
||||
clusterAddSlot(sender,j);
|
||||
bitmapClearBit(server.cluster->owner_not_claiming_slot, j);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
|
||||
CLUSTER_TODO_UPDATE_STATE|
|
||||
CLUSTER_TODO_FSYNC_CONFIG);
|
||||
@@ -2641,8 +2659,7 @@ void clusterProcessPingExtensions(clusterMsg *hdr, clusterLink *link) {
|
||||
clusterNode *n = clusterLookupNode(forgotten_node_ext->name, CLUSTER_NAMELEN);
|
||||
if (n && n != myself && !(nodeIsSlave(myself) && myself->slaveof == n)) {
|
||||
sds id = sdsnewlen(forgotten_node_ext->name, CLUSTER_NAMELEN);
|
||||
dictEntry *de = dictAddRaw(server.cluster->nodes_black_list, id, NULL);
|
||||
serverAssert(de != NULL);
|
||||
dictEntry *de = dictAddOrFind(server.cluster->nodes_black_list, id);
|
||||
uint64_t expire = server.unixtime + ntohu64(forgotten_node_ext->ttl);
|
||||
dictSetUnsignedIntegerVal(de, expire);
|
||||
clusterDelNode(n);
|
||||
@@ -2660,11 +2677,24 @@ void clusterProcessPingExtensions(clusterMsg *hdr, clusterLink *link) {
|
||||
/* We know this will be valid since we validated it ahead of time */
|
||||
ext = getNextPingExt(ext);
|
||||
}
|
||||
|
||||
/* If the node did not send us a hostname extension, assume
|
||||
* they don't have an announced hostname. Otherwise, we'll
|
||||
* set it now. */
|
||||
updateAnnouncedHostname(sender, ext_hostname);
|
||||
updateAnnouncedHumanNodename(sender, ext_humannodename);
|
||||
|
||||
/* If the node did not send us a shard-id extension, it means the sender
|
||||
* does not support it (old version), node->shard_id is randomly generated.
|
||||
* A cluster-wide consensus for the node's shard_id is not necessary.
|
||||
* The key is maintaining consistency of the shard_id on each individual 7.2 node.
|
||||
* As the cluster progressively upgrades to version 7.2, we can expect the shard_ids
|
||||
* across all nodes to naturally converge and align.
|
||||
*
|
||||
* If sender is a replica, set the shard_id to the shard_id of its master.
|
||||
* Otherwise, we'll set it now. */
|
||||
if (ext_shardid == NULL) ext_shardid = clusterNodeGetMaster(sender)->shard_id;
|
||||
|
||||
updateShardId(sender, ext_shardid);
|
||||
}
|
||||
|
||||
@@ -3008,6 +3038,10 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
clusterNodeAddSlave(master,sender);
|
||||
sender->slaveof = master;
|
||||
|
||||
/* Update the shard_id when a replica is connected to its
|
||||
* primary in the very first time. */
|
||||
updateShardId(sender, master->shard_id);
|
||||
|
||||
/* Update config. */
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG);
|
||||
}
|
||||
@@ -4737,10 +4771,13 @@ void clusterCron(void) {
|
||||
/* Timeout reached. Set the node as possibly failing if it is
|
||||
* not already in this state. */
|
||||
if (!(node->flags & (CLUSTER_NODE_PFAIL|CLUSTER_NODE_FAIL))) {
|
||||
serverLog(LL_DEBUG,"*** NODE %.40s possibly failing",
|
||||
node->name);
|
||||
node->flags |= CLUSTER_NODE_PFAIL;
|
||||
update_state = 1;
|
||||
if (myself->flags & CLUSTER_NODE_MASTER && server.cluster->size == 1) {
|
||||
markNodeAsFailingIfNeeded(node);
|
||||
} else {
|
||||
serverLog(LL_DEBUG,"*** NODE %.40s possibly failing", node->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4922,14 +4959,12 @@ int clusterDelSlot(int slot) {
|
||||
if (!n) return C_ERR;
|
||||
|
||||
/* Cleanup the channels in master/replica as part of slot deletion. */
|
||||
list *nodes_for_slot = clusterGetNodesInMyShard(n);
|
||||
serverAssert(nodes_for_slot != NULL);
|
||||
listNode *ln = listSearchKey(nodes_for_slot, myself);
|
||||
if (ln != NULL) {
|
||||
removeChannelsInSlot(slot);
|
||||
}
|
||||
removeChannelsInSlot(slot);
|
||||
/* Clear the slot bit. */
|
||||
serverAssert(clusterNodeClearSlotBit(n,slot) == 1);
|
||||
server.cluster->slots[slot] = NULL;
|
||||
/* Make owner_not_claiming_slot flag consistent with slot ownership information. */
|
||||
bitmapClearBit(server.cluster->owner_not_claiming_slot, slot);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -5570,7 +5605,7 @@ void addNodeToNodeReply(client *c, clusterNode *node) {
|
||||
}
|
||||
|
||||
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
|
||||
addReplyLongLong(c, getNodeClientPort(node, connIsTLS(c->conn)));
|
||||
addReplyLongLong(c, getNodeClientPort(node, shouldReturnTlsInfo()));
|
||||
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
|
||||
|
||||
/* Add the additional endpoint information, this is all the known networking information
|
||||
@@ -5697,13 +5732,13 @@ void addShardReplyForClusterShards(client *c, list *nodes) {
|
||||
addReplyBulkCString(c, "slots");
|
||||
|
||||
/* Use slot_info_pairs from the primary only */
|
||||
while (n->slaveof != NULL) n = n->slaveof;
|
||||
n = clusterNodeGetMaster(n);
|
||||
|
||||
if (n->slot_info_pairs != NULL) {
|
||||
serverAssert((n->slot_info_pairs_count % 2) == 0);
|
||||
addReplyArrayLen(c, n->slot_info_pairs_count);
|
||||
for (int i = 0; i < n->slot_info_pairs_count; i++)
|
||||
addReplyBulkLongLong(c, (unsigned long)n->slot_info_pairs[i]);
|
||||
addReplyLongLong(c, (unsigned long)n->slot_info_pairs[i]);
|
||||
} else {
|
||||
/* If no slot info pair is provided, the node owns no slots */
|
||||
addReplyArrayLen(c, 0);
|
||||
@@ -5946,7 +5981,7 @@ NULL
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"nodes") && c->argc == 2) {
|
||||
/* CLUSTER NODES */
|
||||
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
|
||||
sds nodes = clusterGenNodesDescription(c, 0, connIsTLS(c->conn));
|
||||
sds nodes = clusterGenNodesDescription(c, 0, shouldReturnTlsInfo());
|
||||
addReplyVerbatim(c,nodes,sdslen(nodes),"txt");
|
||||
sdsfree(nodes);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"myid") && c->argc == 2) {
|
||||
@@ -6312,7 +6347,7 @@ NULL
|
||||
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
|
||||
addReplyArrayLen(c,n->numslaves);
|
||||
for (j = 0; j < n->numslaves; j++) {
|
||||
sds ni = clusterGenNodeDescription(c, n->slaves[j], connIsTLS(c->conn));
|
||||
sds ni = clusterGenNodeDescription(c, n->slaves[j], shouldReturnTlsInfo());
|
||||
addReplyBulkCString(c,ni);
|
||||
sdsfree(ni);
|
||||
}
|
||||
@@ -7438,7 +7473,7 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
|
||||
error_code == CLUSTER_REDIR_ASK)
|
||||
{
|
||||
/* Report TLS ports to TLS client, and report non-TLS port to non-TLS client. */
|
||||
int port = getNodeClientPort(n, connIsTLS(c->conn));
|
||||
int port = getNodeClientPort(n, shouldReturnTlsInfo());
|
||||
addReplyErrorSds(c,sdscatprintf(sdsempty(),
|
||||
"-%s %d %s:%d",
|
||||
(error_code == CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
|
||||
@@ -7631,6 +7666,11 @@ unsigned int countKeysInSlot(unsigned int hashslot) {
|
||||
return (*server.db->slots_to_keys).by_slot[hashslot].count;
|
||||
}
|
||||
|
||||
clusterNode *clusterNodeGetMaster(clusterNode *node) {
|
||||
while (node->slaveof != NULL) node = node->slaveof;
|
||||
return node;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Operation(s) on channel rax tree.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
@@ -413,6 +413,7 @@ void clusterInit(void);
|
||||
void clusterInitListeners(void);
|
||||
void clusterCron(void);
|
||||
void clusterBeforeSleep(void);
|
||||
clusterNode *clusterNodeGetMaster(clusterNode *node);
|
||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
|
||||
int verifyClusterNodeId(const char *name, int length);
|
||||
clusterNode *clusterLookupNode(const char *name, int length);
|
||||
|
||||
+76
-21
@@ -1391,7 +1391,10 @@ struct COMMAND_ARG CLIENT_REPLY_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CLIENT SETINFO tips */
|
||||
#define CLIENT_SETINFO_Tips NULL
|
||||
const char *CLIENT_SETINFO_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -1419,7 +1422,10 @@ struct COMMAND_ARG CLIENT_SETINFO_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CLIENT SETNAME tips */
|
||||
#define CLIENT_SETNAME_Tips NULL
|
||||
const char *CLIENT_SETNAME_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -1543,8 +1549,8 @@ struct COMMAND_STRUCT CLIENT_Subcommands[] = {
|
||||
{MAKE_CMD("no-touch","Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,0,CLIENT_NO_TOUCH_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,CLIENT_NO_TOUCH_Keyspecs,0,NULL,1),.args=CLIENT_NO_TOUCH_Args},
|
||||
{MAKE_CMD("pause","Suspends commands processing.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,1,CLIENT_PAUSE_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_PAUSE_Keyspecs,0,NULL,2),.args=CLIENT_PAUSE_Args},
|
||||
{MAKE_CMD("reply","Instructs the server whether to reply to commands.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,0,CLIENT_REPLY_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_REPLY_Keyspecs,0,NULL,1),.args=CLIENT_REPLY_Args},
|
||||
{MAKE_CMD("setinfo","Sets information specific to the client or connection.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,0,CLIENT_SETINFO_Tips,0,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETINFO_Keyspecs,0,NULL,1),.args=CLIENT_SETINFO_Args},
|
||||
{MAKE_CMD("setname","Sets the connection name.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,0,CLIENT_SETNAME_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETNAME_Keyspecs,0,NULL,1),.args=CLIENT_SETNAME_Args},
|
||||
{MAKE_CMD("setinfo","Sets information specific to the client or connection.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,0,CLIENT_SETINFO_Tips,2,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETINFO_Keyspecs,0,NULL,1),.args=CLIENT_SETINFO_Args},
|
||||
{MAKE_CMD("setname","Sets the connection name.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,0,CLIENT_SETNAME_Tips,2,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETNAME_Keyspecs,0,NULL,1),.args=CLIENT_SETNAME_Args},
|
||||
{MAKE_CMD("tracking","Controls server-assisted client-side caching for the connection.","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,0,CLIENT_TRACKING_Tips,0,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKING_Keyspecs,0,NULL,7),.args=CLIENT_TRACKING_Args},
|
||||
{MAKE_CMD("trackinginfo","Returns information about server-assisted client-side caching for the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,0,CLIENT_TRACKINGINFO_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKINGINFO_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("unblock","Unblocks a client blocked by a blocking command from a different connection.","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,0,CLIENT_UNBLOCK_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_UNBLOCK_Keyspecs,0,NULL,2),.args=CLIENT_UNBLOCK_Args},
|
||||
@@ -5886,7 +5892,10 @@ struct COMMAND_ARG ACL_CAT_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL DELUSER tips */
|
||||
#define ACL_DELUSER_Tips NULL
|
||||
const char *ACL_DELUSER_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6060,7 +6069,10 @@ struct COMMAND_ARG ACL_LOG_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL SAVE tips */
|
||||
#define ACL_SAVE_Tips NULL
|
||||
const char *ACL_SAVE_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6080,7 +6092,10 @@ commandHistory ACL_SETUSER_History[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL SETUSER tips */
|
||||
#define ACL_SETUSER_Tips NULL
|
||||
const char *ACL_SETUSER_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6131,7 +6146,7 @@ struct COMMAND_ARG ACL_SETUSER_Args[] = {
|
||||
/* ACL command table */
|
||||
struct COMMAND_STRUCT ACL_Subcommands[] = {
|
||||
{MAKE_CMD("cat","Lists the ACL categories, or the commands inside a category.","O(1) since the categories and commands are a fixed set.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_CAT_History,0,ACL_CAT_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_CAT_Keyspecs,0,NULL,1),.args=ACL_CAT_Args},
|
||||
{MAKE_CMD("deluser","Deletes ACL users, and terminates their connections.","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DELUSER_History,0,ACL_DELUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DELUSER_Keyspecs,0,NULL,1),.args=ACL_DELUSER_Args},
|
||||
{MAKE_CMD("deluser","Deletes ACL users, and terminates their connections.","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DELUSER_History,0,ACL_DELUSER_Tips,2,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DELUSER_Keyspecs,0,NULL,1),.args=ACL_DELUSER_Args},
|
||||
{MAKE_CMD("dryrun","Simulates the execution of a command by a user, without executing the command.","O(1).","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DRYRUN_History,0,ACL_DRYRUN_Tips,0,aclCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DRYRUN_Keyspecs,0,NULL,3),.args=ACL_DRYRUN_Args},
|
||||
{MAKE_CMD("genpass","Generates a pseudorandom, secure password that can be used to identify ACL users.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GENPASS_History,0,ACL_GENPASS_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GENPASS_Keyspecs,0,NULL,1),.args=ACL_GENPASS_Args},
|
||||
{MAKE_CMD("getuser","Lists the ACL rules of a user.","O(N). Where N is the number of password, command and pattern rules that the user has.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GETUSER_History,2,ACL_GETUSER_Tips,0,aclCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GETUSER_Keyspecs,0,NULL,1),.args=ACL_GETUSER_Args},
|
||||
@@ -6139,8 +6154,8 @@ struct COMMAND_STRUCT ACL_Subcommands[] = {
|
||||
{MAKE_CMD("list","Dumps the effective rules in ACL file format.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LIST_History,0,ACL_LIST_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LIST_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("load","Reloads the rules from the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOAD_History,0,ACL_LOAD_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOAD_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("log","Lists recent security events generated due to ACL rules.","O(N) with N being the number of entries shown.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOG_History,1,ACL_LOG_Tips,0,aclCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOG_Keyspecs,0,NULL,1),.args=ACL_LOG_Args},
|
||||
{MAKE_CMD("save","Saves the effective ACL rules in the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SAVE_History,0,ACL_SAVE_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("setuser","Creates and modifies an ACL user and its rules.","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SETUSER_History,2,ACL_SETUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SETUSER_Keyspecs,0,NULL,2),.args=ACL_SETUSER_Args},
|
||||
{MAKE_CMD("save","Saves the effective ACL rules in the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SAVE_History,0,ACL_SAVE_Tips,2,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("setuser","Creates and modifies an ACL user and its rules.","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SETUSER_History,2,ACL_SETUSER_Tips,2,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SETUSER_Keyspecs,0,NULL,2),.args=ACL_SETUSER_Args},
|
||||
{MAKE_CMD("users","Lists all ACL users.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_USERS_History,0,ACL_USERS_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_USERS_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("whoami","Returns the authenticated username of the current connection.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_WHOAMI_History,0,ACL_WHOAMI_Tips,0,aclCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_WHOAMI_Keyspecs,0,NULL,0)},
|
||||
{0}
|
||||
@@ -6446,7 +6461,10 @@ struct COMMAND_ARG CONFIG_GET_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CONFIG RESETSTAT tips */
|
||||
#define CONFIG_RESETSTAT_Tips NULL
|
||||
const char *CONFIG_RESETSTAT_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6463,7 +6481,10 @@ struct COMMAND_ARG CONFIG_GET_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CONFIG REWRITE tips */
|
||||
#define CONFIG_REWRITE_Tips NULL
|
||||
const char *CONFIG_REWRITE_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6508,8 +6529,8 @@ struct COMMAND_ARG CONFIG_SET_Args[] = {
|
||||
struct COMMAND_STRUCT CONFIG_Subcommands[] = {
|
||||
{MAKE_CMD("get","Returns the effective values of configuration parameters.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_GET_History,1,CONFIG_GET_Tips,0,configGetCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_GET_Keyspecs,0,NULL,1),.args=CONFIG_GET_Args},
|
||||
{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_HELP_History,0,CONFIG_HELP_Tips,0,configHelpCommand,2,CMD_LOADING|CMD_STALE,0,CONFIG_HELP_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("resetstat","Resets the server's statistics.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,0,CONFIG_RESETSTAT_Tips,0,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_RESETSTAT_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("rewrite","Persists the effective configuration to file.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,0,CONFIG_REWRITE_Tips,0,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_REWRITE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("resetstat","Resets the server's statistics.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,0,CONFIG_RESETSTAT_Tips,2,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_RESETSTAT_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("rewrite","Persists the effective configuration to file.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,0,CONFIG_REWRITE_Tips,2,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_REWRITE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("set","Sets configuration parameters in-flight.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_SET_History,1,CONFIG_SET_Tips,2,configSetCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_SET_Keyspecs,0,NULL,1),.args=CONFIG_SET_Args},
|
||||
{0}
|
||||
};
|
||||
@@ -6862,7 +6883,7 @@ const char *LATENCY_LATEST_Tips[] = {
|
||||
/* LATENCY RESET tips */
|
||||
const char *LATENCY_RESET_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
"response_policy:agg_sum",
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -7292,12 +7313,29 @@ struct COMMAND_ARG PSYNC_Args[] = {
|
||||
#define REPLICAOF_Keyspecs NULL
|
||||
#endif
|
||||
|
||||
/* REPLICAOF argument table */
|
||||
struct COMMAND_ARG REPLICAOF_Args[] = {
|
||||
/* REPLICAOF args host_port argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_host_port_Subargs[] = {
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* REPLICAOF args no_one argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_no_one_Subargs[] = {
|
||||
{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("one",ARG_TYPE_PURE_TOKEN,-1,"ONE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* REPLICAOF args argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_Subargs[] = {
|
||||
{MAKE_ARG("host-port",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_host_port_Subargs},
|
||||
{MAKE_ARG("no-one",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_no_one_Subargs},
|
||||
};
|
||||
|
||||
/* REPLICAOF argument table */
|
||||
struct COMMAND_ARG REPLICAOF_Args[] = {
|
||||
{MAKE_ARG("args",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_Subargs},
|
||||
};
|
||||
|
||||
/********** RESTORE_ASKING ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
@@ -7416,12 +7454,29 @@ struct COMMAND_ARG SHUTDOWN_Args[] = {
|
||||
#define SLAVEOF_Keyspecs NULL
|
||||
#endif
|
||||
|
||||
/* SLAVEOF argument table */
|
||||
struct COMMAND_ARG SLAVEOF_Args[] = {
|
||||
/* SLAVEOF args host_port argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_host_port_Subargs[] = {
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* SLAVEOF args no_one argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_no_one_Subargs[] = {
|
||||
{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("one",ARG_TYPE_PURE_TOKEN,-1,"ONE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* SLAVEOF args argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_Subargs[] = {
|
||||
{MAKE_ARG("host-port",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_host_port_Subargs},
|
||||
{MAKE_ARG("no-one",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_no_one_Subargs},
|
||||
};
|
||||
|
||||
/* SLAVEOF argument table */
|
||||
struct COMMAND_ARG SLAVEOF_Args[] = {
|
||||
{MAKE_ARG("args",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_Subargs},
|
||||
};
|
||||
|
||||
/********** SLOWLOG GET ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
@@ -10731,12 +10786,12 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("monitor","Listens for all requests received by the server in real-time.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MONITOR_History,0,MONITOR_Tips,0,monitorCommand,1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,MONITOR_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("psync","An internal command used in replication.",NULL,"2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,PSYNC_History,0,PSYNC_Tips,0,syncCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,PSYNC_Keyspecs,0,NULL,2),.args=PSYNC_Args},
|
||||
{MAKE_CMD("replconf","An internal command for configuring the replication stream.","O(1)","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLCONF_History,0,REPLCONF_Tips,0,replconfCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_ALLOW_BUSY,0,REPLCONF_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("replicaof","Configures a server as replica of another, or promotes it to a master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLICAOF_History,0,REPLICAOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,REPLICAOF_Keyspecs,0,NULL,2),.args=REPLICAOF_Args},
|
||||
{MAKE_CMD("replicaof","Configures a server as replica of another, or promotes it to a master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLICAOF_History,0,REPLICAOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,REPLICAOF_Keyspecs,0,NULL,1),.args=REPLICAOF_Args},
|
||||
{MAKE_CMD("restore-asking","An internal command for migrating keys in a cluster.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,RESTORE_ASKING_History,3,RESTORE_ASKING_Tips,0,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_ASKING,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,RESTORE_ASKING_Keyspecs,1,NULL,7),.args=RESTORE_ASKING_Args},
|
||||
{MAKE_CMD("role","Returns the replication role.","O(1)","2.8.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ROLE_History,0,ROLE_Tips,0,roleCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS,ROLE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("save","Synchronously saves the database(s) to disk.","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SAVE_History,0,SAVE_Tips,0,saveCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_NO_MULTI,0,SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("shutdown","Synchronously saves the database(s) to disk and shuts down the Redis server.","O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SHUTDOWN_History,1,SHUTDOWN_Tips,0,shutdownCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_NO_MULTI|CMD_SENTINEL|CMD_ALLOW_BUSY,0,SHUTDOWN_Keyspecs,0,NULL,4),.args=SHUTDOWN_Args},
|
||||
{MAKE_CMD("slaveof","Sets a Redis server as a replica of another, or promotes it to being a master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0","server",COMMAND_GROUP_SERVER,SLAVEOF_History,0,SLAVEOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,SLAVEOF_Keyspecs,0,NULL,2),.args=SLAVEOF_Args},
|
||||
{MAKE_CMD("slaveof","Sets a Redis server as a replica of another, or promotes it to being a master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0","server",COMMAND_GROUP_SERVER,SLAVEOF_History,0,SLAVEOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,SLAVEOF_Keyspecs,0,NULL,1),.args=SLAVEOF_Args},
|
||||
{MAKE_CMD("slowlog","A container for slow log commands.","Depends on subcommand.","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_History,0,SLOWLOG_Tips,0,NULL,-2,0,0,SLOWLOG_Keyspecs,0,NULL,0),.subcommands=SLOWLOG_Subcommands},
|
||||
{MAKE_CMD("swapdb","Swaps two Redis databases.","O(N) where N is the count of clients watching or blocking on keys from both databases.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SWAPDB_History,0,SWAPDB_Tips,0,swapdbCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,SWAPDB_Keyspecs,0,NULL,2),.args=SWAPDB_Args},
|
||||
{MAKE_CMD("sync","An internal command used in replication.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SYNC_History,0,SYNC_Tips,0,syncCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,SYNC_Keyspecs,0,NULL,0)},
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"type": "integer",
|
||||
"description": "The number of users that were deleted"
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
},
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"description": "an even number element array specifying the start and end slot numbers for slot ranges owned by this shard",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
"RESPONSE_POLICY:AGG_SUM"
|
||||
],
|
||||
"reply_schema": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -14,12 +14,40 @@
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
"name": "args",
|
||||
"type": "oneof",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host-port",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no-one",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "no",
|
||||
"type": "pure-token",
|
||||
"token": "NO"
|
||||
},
|
||||
{
|
||||
"name": "one",
|
||||
"type": "pure-token",
|
||||
"token": "ONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
|
||||
@@ -19,12 +19,40 @@
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
"name": "args",
|
||||
"type": "oneof",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host-port",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no-one",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "no",
|
||||
"type": "pure-token",
|
||||
"token": "NO"
|
||||
},
|
||||
{
|
||||
"name": "one",
|
||||
"type": "pure-token",
|
||||
"token": "ONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "GET option is specified, but no object was found ",
|
||||
"description": "GET option is specified, but no object was found",
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -117,7 +117,15 @@
|
||||
"description": "a list of sorted elements",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "GET option is specified, but no object was found",
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -40,8 +40,12 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
||||
#define MAC_OS_10_6_DETECTED
|
||||
#endif
|
||||
|
||||
/* Define redis_fstat to fstat or fstat64() */
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_10_6_DETECTED)
|
||||
#define redis_fstat fstat64
|
||||
#define redis_stat stat64
|
||||
#else
|
||||
@@ -96,7 +100,7 @@
|
||||
#define HAVE_ACCEPT4 1
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
|
||||
#if (defined(__APPLE__) && defined(MAC_OS_10_6_DETECTED)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
|
||||
#define HAVE_KQUEUE 1
|
||||
#endif
|
||||
|
||||
@@ -293,7 +297,7 @@ void setproctitle(const char *fmt, ...);
|
||||
#include <kernel/OS.h>
|
||||
#define redis_set_thread_title(name) rename_thread(find_thread(0), name)
|
||||
#else
|
||||
#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))
|
||||
#if (defined __APPLE__ && defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070)
|
||||
int pthread_setname_np(const char *name);
|
||||
#include <pthread.h>
|
||||
#define redis_set_thread_title(name) pthread_setname_np(name)
|
||||
|
||||
@@ -2294,7 +2294,8 @@ int sortROGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResult
|
||||
keys = getKeysPrepareResult(result, 1);
|
||||
keys[0].pos = 1; /* <sort-key> is always present. */
|
||||
keys[0].flags = CMD_KEY_RO | CMD_KEY_ACCESS;
|
||||
return 1;
|
||||
result->numkeys = 1;
|
||||
return result->numkeys;
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from the SORT command.
|
||||
|
||||
+3
-3
@@ -1190,7 +1190,7 @@ static void* getAndSetMcontextEip(ucontext_t *uc, void *eip) {
|
||||
} \
|
||||
return old_val; \
|
||||
} while(0)
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_10_6_DETECTED)
|
||||
/* OSX < 10.6 */
|
||||
#if defined(__x86_64__)
|
||||
GET_SET_RETURN(uc->uc_mcontext->__ss.__rip, eip);
|
||||
@@ -1199,7 +1199,7 @@ static void* getAndSetMcontextEip(ucontext_t *uc, void *eip) {
|
||||
#else
|
||||
GET_SET_RETURN(uc->uc_mcontext->__ss.__srr0, eip);
|
||||
#endif
|
||||
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
|
||||
#elif defined(__APPLE__) && defined(MAC_OS_10_6_DETECTED)
|
||||
/* OSX >= 10.6 */
|
||||
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
||||
GET_SET_RETURN(uc->uc_mcontext->__ss.__rip, eip);
|
||||
@@ -1290,7 +1290,7 @@ void logRegisters(ucontext_t *uc) {
|
||||
} while(0)
|
||||
|
||||
/* OSX */
|
||||
#if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
|
||||
#if defined(__APPLE__) && defined(MAC_OS_10_6_DETECTED)
|
||||
/* OSX AMD64 */
|
||||
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
||||
serverLog(LL_WARNING,
|
||||
|
||||
@@ -941,6 +941,7 @@ void activeDefragCycle(void) {
|
||||
defrag_later_cursor = 0;
|
||||
current_db = -1;
|
||||
cursor = 0;
|
||||
expires_cursor = 0;
|
||||
db = NULL;
|
||||
goto update_metrics;
|
||||
}
|
||||
|
||||
+5
-10
@@ -1414,30 +1414,25 @@ static int _dictExpandIfNeeded(dict *d)
|
||||
* table (global setting) or we should avoid it but the ratio between
|
||||
* elements/buckets is over the "safe" threshold, we resize doubling
|
||||
* the number of buckets. */
|
||||
if (!dictTypeExpandAllowed(d))
|
||||
return DICT_OK;
|
||||
if ((dict_can_resize == DICT_RESIZE_ENABLE &&
|
||||
d->ht_used[0] >= DICTHT_SIZE(d->ht_size_exp[0])) ||
|
||||
(dict_can_resize != DICT_RESIZE_FORBID &&
|
||||
d->ht_used[0] / DICTHT_SIZE(d->ht_size_exp[0]) > dict_force_resize_ratio))
|
||||
{
|
||||
if (!dictTypeExpandAllowed(d))
|
||||
return DICT_OK;
|
||||
return dictExpand(d, d->ht_used[0] + 1);
|
||||
}
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* TODO: clz optimization */
|
||||
/* Our hash table capability is a power of two */
|
||||
static signed char _dictNextExp(unsigned long size)
|
||||
{
|
||||
unsigned char e = DICT_HT_INITIAL_EXP;
|
||||
|
||||
if (size <= DICT_HT_INITIAL_SIZE) return DICT_HT_INITIAL_EXP;
|
||||
if (size >= LONG_MAX) return (8*sizeof(long)-1);
|
||||
while(1) {
|
||||
if (((unsigned long)1<<e) >= size)
|
||||
return e;
|
||||
e++;
|
||||
}
|
||||
|
||||
return 8*sizeof(long) - __builtin_clzl(size-1);
|
||||
}
|
||||
|
||||
/* Finds and returns the position within the dict where the provided key should
|
||||
|
||||
@@ -667,6 +667,7 @@ int performEvictions(void) {
|
||||
*
|
||||
* AOF and Output buffer memory will be freed eventually so
|
||||
* we only care about memory used by the key space. */
|
||||
enterExecutionUnit(1, 0);
|
||||
delta = (long long) zmalloc_used_memory();
|
||||
latencyStartMonitor(eviction_latency);
|
||||
dbGenericDelete(db,keyobj,server.lazyfree_lazy_eviction,DB_FLAG_KEY_EVICTED);
|
||||
@@ -679,6 +680,7 @@ int performEvictions(void) {
|
||||
notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted",
|
||||
keyobj, db->id);
|
||||
propagateDeletion(db,keyobj,server.lazyfree_lazy_eviction);
|
||||
exitExecutionUnit();
|
||||
postExecutionUnitOperations();
|
||||
decrRefCount(keyobj);
|
||||
keys_freed++;
|
||||
|
||||
@@ -54,10 +54,12 @@
|
||||
int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
|
||||
long long t = dictGetSignedIntegerVal(de);
|
||||
if (now > t) {
|
||||
enterExecutionUnit(1, 0);
|
||||
sds key = dictGetKey(de);
|
||||
robj *keyobj = createStringObject(key,sdslen(key));
|
||||
deleteExpiredKeyAndPropagate(db,keyobj);
|
||||
decrRefCount(keyobj);
|
||||
exitExecutionUnit();
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "bio.h"
|
||||
#include "atomicvar.h"
|
||||
#include "functions.h"
|
||||
#include "cluster.h"
|
||||
|
||||
static redisAtomic size_t lazyfree_objects = 0;
|
||||
static redisAtomic size_t lazyfreed_objects = 0;
|
||||
@@ -177,6 +178,10 @@ void emptyDbAsync(redisDb *db) {
|
||||
dict *oldht1 = db->dict, *oldht2 = db->expires;
|
||||
db->dict = dictCreate(&dbDictType);
|
||||
db->expires = dictCreate(&dbExpiresDictType);
|
||||
if (server.cluster_enabled) {
|
||||
slotToKeyDestroy(db);
|
||||
slotToKeyInit(db);
|
||||
}
|
||||
atomicIncr(lazyfree_objects,dictSize(oldht1));
|
||||
bioCreateLazyFreeJob(lazyfreeFreeDatabase,2,oldht1,oldht2);
|
||||
}
|
||||
|
||||
+20
-22
@@ -7706,15 +7706,15 @@ RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdF
|
||||
bc->background_timer = 0;
|
||||
bc->background_duration = 0;
|
||||
|
||||
c->bstate.timeout = 0;
|
||||
mstime_t timeout = 0;
|
||||
if (timeout_ms) {
|
||||
mstime_t now = mstime();
|
||||
if (timeout_ms > LLONG_MAX - now) {
|
||||
if (timeout_ms > LLONG_MAX - now) {
|
||||
c->bstate.module_blocked_handle = NULL;
|
||||
addReplyError(c, "timeout is out of range"); /* 'timeout_ms+now' would overflow */
|
||||
return bc;
|
||||
}
|
||||
c->bstate.timeout = timeout_ms + now;
|
||||
timeout = timeout_ms + now;
|
||||
}
|
||||
|
||||
if (islua || ismulti) {
|
||||
@@ -7730,8 +7730,9 @@ RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdF
|
||||
addReplyError(c, "Clients undergoing module based authentication can only be blocked on auth");
|
||||
} else {
|
||||
if (keys) {
|
||||
blockForKeys(c,BLOCKED_MODULE,keys,numkeys,c->bstate.timeout,flags&REDISMODULE_BLOCK_UNBLOCK_DELETED);
|
||||
blockForKeys(c,BLOCKED_MODULE,keys,numkeys,timeout,flags&REDISMODULE_BLOCK_UNBLOCK_DELETED);
|
||||
} else {
|
||||
c->bstate.timeout = timeout;
|
||||
blockClient(c,BLOCKED_MODULE);
|
||||
}
|
||||
}
|
||||
@@ -12115,6 +12116,19 @@ int parseLoadexArguments(RedisModuleString ***module_argv, int *module_argc) {
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
/* Unregister module-related things, called when moduleLoad fails or moduleUnload. */
|
||||
void moduleUnregisterCleanup(RedisModule *module) {
|
||||
moduleFreeAuthenticatedClients(module);
|
||||
moduleUnregisterCommands(module);
|
||||
moduleUnsubscribeNotifications(module);
|
||||
moduleUnregisterSharedAPI(module);
|
||||
moduleUnregisterUsedAPI(module);
|
||||
moduleUnregisterFilters(module);
|
||||
moduleUnsubscribeAllServerEvents(module);
|
||||
moduleRemoveConfigs(module);
|
||||
moduleUnregisterAuthCBs(module);
|
||||
}
|
||||
|
||||
/* Load a module and initialize it. On success C_OK is returned, otherwise
|
||||
* C_ERR is returned. */
|
||||
int moduleLoad(const char *path, void **module_argv, int module_argc, int is_loadex) {
|
||||
@@ -12149,11 +12163,7 @@ int moduleLoad(const char *path, void **module_argv, int module_argc, int is_loa
|
||||
serverLog(LL_WARNING,
|
||||
"Module %s initialization failed. Module not loaded",path);
|
||||
if (ctx.module) {
|
||||
moduleUnregisterCommands(ctx.module);
|
||||
moduleUnregisterSharedAPI(ctx.module);
|
||||
moduleUnregisterUsedAPI(ctx.module);
|
||||
moduleRemoveConfigs(ctx.module);
|
||||
moduleUnregisterAuthCBs(ctx.module);
|
||||
moduleUnregisterCleanup(ctx.module);
|
||||
moduleFreeModuleStructure(ctx.module);
|
||||
}
|
||||
moduleFreeContext(&ctx);
|
||||
@@ -12194,8 +12204,6 @@ int moduleLoad(const char *path, void **module_argv, int module_argc, int is_loa
|
||||
}
|
||||
|
||||
if (post_load_err) {
|
||||
/* Unregister module auth callbacks (if any exist) that this Module registered onload. */
|
||||
moduleUnregisterAuthCBs(ctx.module);
|
||||
moduleUnload(ctx.module->name, NULL);
|
||||
moduleFreeContext(&ctx);
|
||||
return C_ERR;
|
||||
@@ -12253,17 +12261,7 @@ int moduleUnload(sds name, const char **errmsg) {
|
||||
}
|
||||
}
|
||||
|
||||
moduleFreeAuthenticatedClients(module);
|
||||
moduleUnregisterCommands(module);
|
||||
moduleUnregisterSharedAPI(module);
|
||||
moduleUnregisterUsedAPI(module);
|
||||
moduleUnregisterFilters(module);
|
||||
moduleUnregisterAuthCBs(module);
|
||||
moduleRemoveConfigs(module);
|
||||
|
||||
/* Remove any notification subscribers this module might have */
|
||||
moduleUnsubscribeNotifications(module);
|
||||
moduleUnsubscribeAllServerEvents(module);
|
||||
moduleUnregisterCleanup(module);
|
||||
|
||||
/* Unload the dynamic library. */
|
||||
if (dlclose(module->handle) == -1) {
|
||||
|
||||
+64
-18
@@ -104,6 +104,9 @@ quicklistBookmark *_quicklistBookmarkFindByName(quicklist *ql, const char *name)
|
||||
quicklistBookmark *_quicklistBookmarkFindByNode(quicklist *ql, quicklistNode *node);
|
||||
void _quicklistBookmarkDelete(quicklist *ql, quicklistBookmark *bm);
|
||||
|
||||
quicklistNode *_quicklistSplitNode(quicklistNode *node, int offset, int after);
|
||||
quicklistNode *_quicklistMergeNodes(quicklist *quicklist, quicklistNode *center);
|
||||
|
||||
/* Simple way to give quicklistEntry structs default values with one call. */
|
||||
#define initEntry(e) \
|
||||
do { \
|
||||
@@ -378,6 +381,15 @@ REDIS_STATIC void __quicklistCompress(const quicklist *quicklist,
|
||||
quicklistCompressNode(reverse);
|
||||
}
|
||||
|
||||
/* This macro is used to compress a node.
|
||||
*
|
||||
* If the 'recompress' flag of the node is true, we compress it directly without
|
||||
* checking whether it is within the range of compress depth.
|
||||
* However, it's important to ensure that the 'recompress' flag of head and tail
|
||||
* is always false, as we always assume that head and tail are not compressed.
|
||||
*
|
||||
* If the 'recompress' flag of the node is false, we check whether the node is
|
||||
* within the range of compress depth before compressing it. */
|
||||
#define quicklistCompress(_ql, _node) \
|
||||
do { \
|
||||
if ((_node)->recompress) \
|
||||
@@ -529,19 +541,25 @@ REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a,
|
||||
(node)->sz = lpBytes((node)->entry); \
|
||||
} while (0)
|
||||
|
||||
static quicklistNode* __quicklistCreatePlainNode(void *value, size_t sz) {
|
||||
static quicklistNode* __quicklistCreateNode(int container, void *value, size_t sz) {
|
||||
quicklistNode *new_node = quicklistCreateNode();
|
||||
new_node->entry = zmalloc(sz);
|
||||
new_node->container = QUICKLIST_NODE_CONTAINER_PLAIN;
|
||||
memcpy(new_node->entry, value, sz);
|
||||
new_node->container = container;
|
||||
if (container == QUICKLIST_NODE_CONTAINER_PLAIN) {
|
||||
new_node->entry = zmalloc(sz);
|
||||
memcpy(new_node->entry, value, sz);
|
||||
} else {
|
||||
new_node->entry = lpPrepend(lpNew(0), value, sz);
|
||||
}
|
||||
new_node->sz = sz;
|
||||
new_node->count++;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
static void __quicklistInsertPlainNode(quicklist *quicklist, quicklistNode *old_node,
|
||||
void *value, size_t sz, int after) {
|
||||
__quicklistInsertNode(quicklist, old_node, __quicklistCreatePlainNode(value, sz), after);
|
||||
void *value, size_t sz, int after)
|
||||
{
|
||||
quicklistNode *new_node = __quicklistCreateNode(QUICKLIST_NODE_CONTAINER_PLAIN, value, sz);
|
||||
__quicklistInsertNode(quicklist, old_node, new_node, after);
|
||||
quicklist->count++;
|
||||
}
|
||||
|
||||
@@ -741,9 +759,13 @@ void quicklistReplaceEntry(quicklistIter *iter, quicklistEntry *entry,
|
||||
void *data, size_t sz)
|
||||
{
|
||||
quicklist* quicklist = iter->quicklist;
|
||||
quicklistNode *node = entry->node;
|
||||
unsigned char *newentry;
|
||||
|
||||
if (likely(!QL_NODE_IS_PLAIN(entry->node) && !isLargeElement(sz))) {
|
||||
entry->node->entry = lpReplace(entry->node->entry, &entry->zi, data, sz);
|
||||
if (likely(!QL_NODE_IS_PLAIN(entry->node) && !isLargeElement(sz) &&
|
||||
(newentry = lpReplace(entry->node->entry, &entry->zi, data, sz)) != NULL))
|
||||
{
|
||||
entry->node->entry = newentry;
|
||||
quicklistNodeUpdateSz(entry->node);
|
||||
/* quicklistNext() and quicklistGetIteratorEntryAtIdx() provide an uncompressed node */
|
||||
quicklistCompress(quicklist, entry->node);
|
||||
@@ -758,17 +780,37 @@ void quicklistReplaceEntry(quicklistIter *iter, quicklistEntry *entry,
|
||||
quicklistInsertAfter(iter, entry, data, sz);
|
||||
__quicklistDelNode(quicklist, entry->node);
|
||||
}
|
||||
} else {
|
||||
entry->node->dont_compress = 1; /* Prevent compression in quicklistInsertAfter() */
|
||||
quicklistInsertAfter(iter, entry, data, sz);
|
||||
} else { /* The node is full or data is a large element */
|
||||
quicklistNode *split_node = NULL, *new_node;
|
||||
node->dont_compress = 1; /* Prevent compression in __quicklistInsertNode() */
|
||||
|
||||
/* If the entry is not at the tail, split the node at the entry's offset. */
|
||||
if (entry->offset != node->count - 1 && entry->offset != -1)
|
||||
split_node = _quicklistSplitNode(node, entry->offset, 1);
|
||||
|
||||
/* Create a new node and insert it after the original node.
|
||||
* If the original node was split, insert the split node after the new node. */
|
||||
new_node = __quicklistCreateNode(isLargeElement(sz) ?
|
||||
QUICKLIST_NODE_CONTAINER_PLAIN : QUICKLIST_NODE_CONTAINER_PACKED, data, sz);
|
||||
__quicklistInsertNode(quicklist, node, new_node, 1);
|
||||
if (split_node) __quicklistInsertNode(quicklist, new_node, split_node, 1);
|
||||
quicklist->count++;
|
||||
|
||||
/* Delete the replaced element. */
|
||||
if (entry->node->count == 1) {
|
||||
__quicklistDelNode(quicklist, entry->node);
|
||||
} else {
|
||||
unsigned char *p = lpSeek(entry->node->entry, -1);
|
||||
quicklistDelIndex(quicklist, entry->node, &p);
|
||||
entry->node->dont_compress = 0; /* Re-enable compression */
|
||||
quicklistCompress(quicklist, entry->node);
|
||||
quicklistCompress(quicklist, entry->node->next);
|
||||
new_node = _quicklistMergeNodes(quicklist, new_node);
|
||||
/* We can't know if the current node and its sibling nodes are correctly compressed,
|
||||
* and we don't know if they are within the range of compress depth, so we need to
|
||||
* use quicklistCompress() for compression, which checks if node is within compress
|
||||
* depth before compressing. */
|
||||
quicklistCompress(quicklist, new_node);
|
||||
quicklistCompress(quicklist, new_node->prev);
|
||||
if (new_node->next) quicklistCompress(quicklist, new_node->next);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,6 +868,8 @@ REDIS_STATIC quicklistNode *_quicklistListpackMerge(quicklist *quicklist,
|
||||
}
|
||||
keep->count = lpLength(keep->entry);
|
||||
quicklistNodeUpdateSz(keep);
|
||||
keep->recompress = 0; /* Prevent 'keep' from being recompressed if
|
||||
* it becomes head or tail after merging. */
|
||||
|
||||
nokeep->count = 0;
|
||||
__quicklistDelNode(quicklist, nokeep);
|
||||
@@ -844,9 +888,10 @@ REDIS_STATIC quicklistNode *_quicklistListpackMerge(quicklist *quicklist,
|
||||
* - (center->next, center->next->next)
|
||||
* - (center->prev, center)
|
||||
* - (center, center->next)
|
||||
*
|
||||
* Returns the new 'center' after merging.
|
||||
*/
|
||||
REDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,
|
||||
quicklistNode *center) {
|
||||
REDIS_STATIC quicklistNode *_quicklistMergeNodes(quicklist *quicklist, quicklistNode *center) {
|
||||
int fill = quicklist->fill;
|
||||
quicklistNode *prev, *prev_prev, *next, *next_next, *target;
|
||||
prev = prev_prev = next = next_next = target = NULL;
|
||||
@@ -886,8 +931,9 @@ REDIS_STATIC void _quicklistMergeNodes(quicklist *quicklist,
|
||||
|
||||
/* Use result of center merge (or original) to merge with next node. */
|
||||
if (_quicklistNodeAllowMerge(target, target->next, fill)) {
|
||||
_quicklistListpackMerge(quicklist, target, target->next);
|
||||
target = _quicklistListpackMerge(quicklist, target, target->next);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/* Split 'node' into two parts, parameterized by 'offset' and 'after'.
|
||||
@@ -1002,7 +1048,7 @@ REDIS_STATIC void _quicklistInsert(quicklistIter *iter, quicklistEntry *entry,
|
||||
} else {
|
||||
quicklistDecompressNodeForUse(node);
|
||||
new_node = _quicklistSplitNode(node, entry->offset, after);
|
||||
quicklistNode *entry_node = __quicklistCreatePlainNode(value, sz);
|
||||
quicklistNode *entry_node = __quicklistCreateNode(QUICKLIST_NODE_CONTAINER_PLAIN, value, sz);
|
||||
__quicklistInsertNode(quicklist, node, entry_node, after);
|
||||
__quicklistInsertNode(quicklist, entry_node, new_node, after);
|
||||
quicklist->count++;
|
||||
@@ -3224,7 +3270,7 @@ int quicklistTest(int argc, char *argv[], int flags) {
|
||||
memcpy(s, "helloworld", 10);
|
||||
memcpy(s + sz - 10, "1234567890", 10);
|
||||
|
||||
quicklistNode *node = __quicklistCreatePlainNode(s, sz);
|
||||
quicklistNode *node = __quicklistCreateNode(QUICKLIST_NODE_CONTAINER_PLAIN, s, sz);
|
||||
|
||||
/* Just to avoid triggering the assertion in __quicklistCompressNode(),
|
||||
* it disables the passing of quicklist head or tail node. */
|
||||
|
||||
@@ -81,9 +81,6 @@
|
||||
#define RDB_TYPE_MODULE_PRE_GA 6 /* Used in 4.0 release candidates */
|
||||
#define RDB_TYPE_MODULE_2 7 /* Module value with annotations for parsing without
|
||||
the generating module being loaded. */
|
||||
/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */
|
||||
|
||||
/* Object types for encoded objects. */
|
||||
#define RDB_TYPE_HASH_ZIPMAP 9
|
||||
#define RDB_TYPE_LIST_ZIPLIST 10
|
||||
#define RDB_TYPE_SET_INTSET 11
|
||||
@@ -97,7 +94,7 @@
|
||||
#define RDB_TYPE_STREAM_LISTPACKS_2 19
|
||||
#define RDB_TYPE_SET_LISTPACK 20
|
||||
#define RDB_TYPE_STREAM_LISTPACKS_3 21
|
||||
/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType() BELOW */
|
||||
/* NOTE: WHEN ADDING NEW RDB TYPE, UPDATE rdbIsObjectType(), and rdb_type_string[] */
|
||||
|
||||
/* Test if a type is an object type. */
|
||||
#define rdbIsObjectType(t) (((t) >= 0 && (t) <= 7) || ((t) >= 9 && (t) <= 21))
|
||||
|
||||
@@ -233,6 +233,7 @@ int checkSingleAof(char *aof_filename, char *aof_filepath, int last_file, int fi
|
||||
struct redis_stat sb;
|
||||
if (redis_fstat(fileno(fp),&sb) == -1) {
|
||||
printf("Cannot stat file: %s, aborting...\n", aof_filename);
|
||||
fclose(fp);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -343,6 +344,7 @@ int fileIsRDB(char *filepath) {
|
||||
struct redis_stat sb;
|
||||
if (redis_fstat(fileno(fp), &sb) == -1) {
|
||||
printf("Cannot stat file: %s\n", filepath);
|
||||
fclose(fp);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -379,6 +381,7 @@ int fileIsManifest(char *filepath) {
|
||||
struct redis_stat sb;
|
||||
if (redis_fstat(fileno(fp), &sb) == -1) {
|
||||
printf("Cannot stat file: %s\n", filepath);
|
||||
fclose(fp);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -395,15 +398,20 @@ int fileIsManifest(char *filepath) {
|
||||
break;
|
||||
} else {
|
||||
printf("Cannot read file: %s\n", filepath);
|
||||
fclose(fp);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip comments lines */
|
||||
/* We will skip comments lines.
|
||||
* At present, the manifest format is fixed, see aofInfoFormat.
|
||||
* We will break directly as long as it encounters other items. */
|
||||
if (buf[0] == '#') {
|
||||
continue;
|
||||
} else if (!memcmp(buf, "file", strlen("file"))) {
|
||||
is_manifest = 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ char *rdb_type_string[] = {
|
||||
"hash-listpack",
|
||||
"zset-listpack",
|
||||
"quicklist-v2",
|
||||
"stream-v2",
|
||||
"set-listpack",
|
||||
"stream-v3",
|
||||
};
|
||||
|
||||
/* Show a few stats collected into 'rdbstate' */
|
||||
|
||||
+3
-1
@@ -1657,6 +1657,7 @@ static int cliConnect(int flags) {
|
||||
redisFree(context);
|
||||
config.dbnum = 0;
|
||||
config.in_multi = 0;
|
||||
config.pubsub_mode = 0;
|
||||
cliRefreshPrompt();
|
||||
}
|
||||
|
||||
@@ -8838,7 +8839,8 @@ static redisReply *sendScan(unsigned long long *it) {
|
||||
reply = redisCommand(context, "SCAN %llu MATCH %b COUNT %d",
|
||||
*it, config.pattern, sdslen(config.pattern), config.count);
|
||||
else
|
||||
reply = redisCommand(context,"SCAN %llu",*it);
|
||||
reply = redisCommand(context, "SCAN %llu COUNT %d",
|
||||
*it, config.count);
|
||||
|
||||
/* Handle any error conditions */
|
||||
if(reply == NULL) {
|
||||
|
||||
@@ -2250,6 +2250,7 @@ void readSyncBulkPayload(connection *conn) {
|
||||
}
|
||||
|
||||
zfree(server.repl_transfer_tmpfile);
|
||||
close(server.repl_transfer_fd);
|
||||
server.repl_transfer_fd = -1;
|
||||
server.repl_transfer_tmpfile = NULL;
|
||||
}
|
||||
|
||||
+11
-2
@@ -818,8 +818,17 @@ static robj **luaArgsToRedisArgv(lua_State *lua, int *argc, int *argv_len) {
|
||||
/* We can't use lua_tolstring() for number -> string conversion
|
||||
* since Lua uses a format specifier that loses precision. */
|
||||
lua_Number num = lua_tonumber(lua,j+1);
|
||||
obj_len = fpconv_dtoa((double)num, dbuf);
|
||||
dbuf[obj_len] = '\0';
|
||||
/* Integer printing function is much faster, check if we can safely use it.
|
||||
* Since lua_Number is not explicitly an integer or a double, we need to make an effort
|
||||
* to convert it as an integer when that's possible, since the string could later be used
|
||||
* in a context that doesn't support scientific notation (e.g. 1e9 instead of 100000000). */
|
||||
long long lvalue;
|
||||
if (double2ll((double)num, &lvalue))
|
||||
obj_len = ll2string(dbuf, sizeof(dbuf), lvalue);
|
||||
else {
|
||||
obj_len = fpconv_dtoa((double)num, dbuf);
|
||||
dbuf[obj_len] = '\0';
|
||||
}
|
||||
obj_s = dbuf;
|
||||
} else {
|
||||
obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);
|
||||
|
||||
@@ -349,20 +349,22 @@ sds sdsResize(sds s, size_t size, int would_regrow) {
|
||||
* type. */
|
||||
int use_realloc = (oldtype==type || (type < oldtype && type > SDS_TYPE_8));
|
||||
size_t newlen = use_realloc ? oldhdrlen+size+1 : hdrlen+size+1;
|
||||
int alloc_already_optimal = 0;
|
||||
#if defined(USE_JEMALLOC)
|
||||
/* je_nallocx returns the expected allocation size for the newlen.
|
||||
* We aim to avoid calling realloc() when using Jemalloc if there is no
|
||||
* change in the allocation size, as it incurs a cost even if the
|
||||
* allocation size stays the same. */
|
||||
alloc_already_optimal = (je_nallocx(newlen, 0) == zmalloc_size(sh));
|
||||
#endif
|
||||
|
||||
if (use_realloc && !alloc_already_optimal) {
|
||||
newsh = s_realloc(sh, newlen);
|
||||
if (newsh == NULL) return NULL;
|
||||
s = (char*)newsh+oldhdrlen;
|
||||
} else if (!alloc_already_optimal) {
|
||||
if (use_realloc) {
|
||||
int alloc_already_optimal = 0;
|
||||
#if defined(USE_JEMALLOC)
|
||||
/* je_nallocx returns the expected allocation size for the newlen.
|
||||
* We aim to avoid calling realloc() when using Jemalloc if there is no
|
||||
* change in the allocation size, as it incurs a cost even if the
|
||||
* allocation size stays the same. */
|
||||
alloc_already_optimal = (je_nallocx(newlen, 0) == zmalloc_size(sh));
|
||||
#endif
|
||||
if (!alloc_already_optimal) {
|
||||
newsh = s_realloc(sh, newlen);
|
||||
if (newsh == NULL) return NULL;
|
||||
s = (char*)newsh+oldhdrlen;
|
||||
}
|
||||
} else {
|
||||
newsh = s_malloc(newlen);
|
||||
if (newsh == NULL) return NULL;
|
||||
memcpy((char*)newsh+hdrlen, s, len);
|
||||
|
||||
+9
-1
@@ -3512,12 +3512,20 @@ void call(client *c, int flags) {
|
||||
* re-processing and unblock the client.*/
|
||||
c->flags |= CLIENT_EXECUTING_COMMAND;
|
||||
|
||||
/* Setting the CLIENT_REPROCESSING_COMMAND flag so that during the actual
|
||||
* processing of the command proc, the client is aware that it is being
|
||||
* re-processed. */
|
||||
if (reprocessing_command) c->flags |= CLIENT_REPROCESSING_COMMAND;
|
||||
|
||||
monotime monotonic_start = 0;
|
||||
if (monotonicGetType() == MONOTONIC_CLOCK_HW)
|
||||
monotonic_start = getMonotonicUs();
|
||||
|
||||
c->cmd->proc(c);
|
||||
|
||||
/* Clear the CLIENT_REPROCESSING_COMMAND flag after the proc is executed. */
|
||||
if (reprocessing_command) c->flags &= ~CLIENT_REPROCESSING_COMMAND;
|
||||
|
||||
exitExecutionUnit();
|
||||
|
||||
/* In case client is blocked after trying to execute the command,
|
||||
@@ -3575,7 +3583,7 @@ void call(client *c, int flags) {
|
||||
|
||||
/* Send the command to clients in MONITOR mode if applicable,
|
||||
* since some administrative commands are considered too dangerous to be shown.
|
||||
* Other exceptions is a client which is unblocked and retring to process the command
|
||||
* Other exceptions is a client which is unblocked and retrying to process the command
|
||||
* or we are currently in the process of loading AOF. */
|
||||
if (update_command_stats && !reprocessing_command &&
|
||||
!(c->cmd->flags & (CMD_SKIP_MONITOR|CMD_ADMIN))) {
|
||||
|
||||
@@ -400,6 +400,7 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
|
||||
auth had been authenticated from the Module. */
|
||||
#define CLIENT_MODULE_PREVENT_AOF_PROP (1ULL<<48) /* Module client do not want to propagate to AOF */
|
||||
#define CLIENT_MODULE_PREVENT_REPL_PROP (1ULL<<49) /* Module client do not want to propagate to replica */
|
||||
#define CLIENT_REPROCESSING_COMMAND (1ULL<<50) /* The client is re-processing the command. */
|
||||
|
||||
/* Client block type (btype field in client structure)
|
||||
* if CLIENT_BLOCKED flag is set. */
|
||||
|
||||
+3
-2
@@ -1172,7 +1172,8 @@ unsigned long zsetLength(const robj *zobj) {
|
||||
* and the value len hint indicates the approximate individual size of the added elements,
|
||||
* they are used to determine the initial representation.
|
||||
*
|
||||
* If the hints are not known, and underestimation or 0 is suitable. */
|
||||
* If the hints are not known, and underestimation or 0 is suitable.
|
||||
* We should never pass a negative value because it will convert to a very large unsigned number. */
|
||||
robj *zsetTypeCreate(size_t size_hint, size_t val_len_hint) {
|
||||
if (size_hint <= server.zset_max_listpack_entries &&
|
||||
val_len_hint <= server.zset_max_listpack_value)
|
||||
@@ -3001,7 +3002,7 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
|
||||
/* Result handler methods for storing the ZRANGESTORE to a zset. */
|
||||
static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
|
||||
{
|
||||
handler->dstobj = zsetTypeCreate(length, 0);
|
||||
handler->dstobj = zsetTypeCreate(length >= 0 ? length : 0, 0);
|
||||
}
|
||||
|
||||
static void zrangeResultEmitCBufferForStore(zrange_result_handler *handler,
|
||||
|
||||
+6
-3
@@ -54,8 +54,11 @@
|
||||
|
||||
/* Glob-style pattern matching. */
|
||||
static int stringmatchlen_impl(const char *pattern, int patternLen,
|
||||
const char *string, int stringLen, int nocase, int *skipLongerMatches)
|
||||
const char *string, int stringLen, int nocase, int *skipLongerMatches, int nesting)
|
||||
{
|
||||
/* Protection against abusive patterns. */
|
||||
if (nesting > 1000) return 0;
|
||||
|
||||
while(patternLen && stringLen) {
|
||||
switch(pattern[0]) {
|
||||
case '*':
|
||||
@@ -67,7 +70,7 @@ static int stringmatchlen_impl(const char *pattern, int patternLen,
|
||||
return 1; /* match */
|
||||
while(stringLen) {
|
||||
if (stringmatchlen_impl(pattern+1, patternLen-1,
|
||||
string, stringLen, nocase, skipLongerMatches))
|
||||
string, stringLen, nocase, skipLongerMatches, nesting+1))
|
||||
return 1; /* match */
|
||||
if (*skipLongerMatches)
|
||||
return 0; /* no match */
|
||||
@@ -189,7 +192,7 @@ static int stringmatchlen_impl(const char *pattern, int patternLen,
|
||||
int stringmatchlen(const char *pattern, int patternLen,
|
||||
const char *string, int stringLen, int nocase) {
|
||||
int skipLongerMatches = 0;
|
||||
return stringmatchlen_impl(pattern,patternLen,string,stringLen,nocase,&skipLongerMatches);
|
||||
return stringmatchlen_impl(pattern,patternLen,string,stringLen,nocase,&skipLongerMatches,0);
|
||||
}
|
||||
|
||||
int stringmatch(const char *pattern, const char *string, int nocase) {
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
#define REDIS_VERSION "255.255.255"
|
||||
#define REDIS_VERSION_NUM 0x00ffffff
|
||||
#define REDIS_VERSION "7.2.6"
|
||||
#define REDIS_VERSION_NUM 0x00070206
|
||||
|
||||
@@ -529,6 +529,18 @@ tags {"aof external:skip"} {
|
||||
assert_match "*Start checking Old-Style AOF*is valid*" $result
|
||||
}
|
||||
|
||||
test {Test redis-check-aof for old style resp AOF - has data in the same format as manifest} {
|
||||
create_aof $aof_dirpath $aof_file {
|
||||
append_to_aof [formatCommand set file file]
|
||||
append_to_aof [formatCommand set "file appendonly.aof.2.base.rdb seq 2 type b" "file appendonly.aof.2.base.rdb seq 2 type b"]
|
||||
}
|
||||
|
||||
catch {
|
||||
exec src/redis-check-aof $aof_file
|
||||
} result
|
||||
assert_match "*Start checking Old-Style AOF*is valid*" $result
|
||||
}
|
||||
|
||||
test {Test redis-check-aof for old style rdb-preamble AOF} {
|
||||
catch {
|
||||
exec src/redis-check-aof tests/assets/rdb-preamble.aof
|
||||
@@ -577,6 +589,19 @@ tags {"aof external:skip"} {
|
||||
assert_match "*Start checking Multi Part AOF*Start to check BASE AOF (RDB format)*DB preamble is OK, proceeding with AOF tail*BASE AOF*is valid*Start to check INCR files*INCR AOF*is valid*All AOF files and manifest are valid*" $result
|
||||
}
|
||||
|
||||
test {Test redis-check-aof for Multi Part AOF contains a format error} {
|
||||
create_aof_manifest $aof_dirpath $aof_manifest_file {
|
||||
append_to_manifest "file appendonly.aof.1.base.aof seq 1 type b\n"
|
||||
append_to_manifest "file appendonly.aof.1.incr.aof seq 1 type i\n"
|
||||
append_to_manifest "!!!\n"
|
||||
}
|
||||
|
||||
catch {
|
||||
exec src/redis-check-aof $aof_manifest_file
|
||||
} result
|
||||
assert_match "*Invalid AOF manifest file format*" $result
|
||||
}
|
||||
|
||||
test {Test redis-check-aof only truncates the last file for Multi Part AOF in fix mode} {
|
||||
create_aof $aof_dirpath $aof_base_file {
|
||||
append_to_aof [formatCommand set foo hello]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "redismodule.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
static RedisModuleString *log_key_name;
|
||||
|
||||
@@ -92,7 +93,7 @@ int CommandFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int CommandFilter_UnfilteredClientdId(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
int CommandFilter_UnfilteredClientId(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
{
|
||||
if (argc < 2)
|
||||
return RedisModule_WrongArity(ctx);
|
||||
@@ -192,7 +193,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1)
|
||||
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
||||
|
||||
if (argc != 2) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
RedisModule_Log(ctx, "warning", "Log key name not specified");
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
@@ -219,7 +220,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
if (RedisModule_CreateCommand(ctx, unfiltered_clientid_name,
|
||||
CommandFilter_UnfilteredClientdId, "admin", 1,1,1) == REDISMODULE_ERR)
|
||||
CommandFilter_UnfilteredClientId, "admin", 1,1,1) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter,
|
||||
@@ -229,6 +230,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
if ((filter1 = RedisModule_RegisterCommandFilter(ctx, CommandFilter_BlmoveSwap, 0)) == NULL)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
if (argc == 3) {
|
||||
const char *ptr = RedisModule_StringPtrLen(argv[2], NULL);
|
||||
if (!strcasecmp(ptr, "noload")) {
|
||||
/* This is a hint that we return ERR at the last moment of OnLoad. */
|
||||
RedisModule_FreeString(ctx, log_key_name);
|
||||
if (retained) RedisModule_FreeString(NULL, retained);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -33,6 +33,7 @@
|
||||
#include "redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* We need to store events to be able to test and see what we got, and we can't
|
||||
@@ -407,9 +408,6 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR; \
|
||||
}
|
||||
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
|
||||
if (RedisModule_Init(ctx,"testhook",1,REDISMODULE_APIVER_1)
|
||||
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
||||
|
||||
@@ -471,6 +469,18 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
if (RedisModule_CreateCommand(ctx,"hooks.pexpireat", cmdKeyExpiry,"",0,0,0) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
if (argc == 1) {
|
||||
const char *ptr = RedisModule_StringPtrLen(argv[0], NULL);
|
||||
if (!strcasecmp(ptr, "noload")) {
|
||||
/* This is a hint that we return ERR at the last moment of OnLoad. */
|
||||
RedisModule_FreeDict(ctx, event_log);
|
||||
RedisModule_FreeDict(ctx, removed_event_log);
|
||||
RedisModule_FreeDict(ctx, removed_subevent_type);
|
||||
RedisModule_FreeDict(ctx, removed_expiry_log);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
ustime_t cached_time = 0;
|
||||
@@ -318,9 +319,6 @@ static int cmdGetDels(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
/* This function must be present on each Redis module. It is used in order to
|
||||
* register the commands into the Redis server. */
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
|
||||
if (RedisModule_Init(ctx,"testkeyspace",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR){
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
@@ -405,6 +403,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
const char *ptr = RedisModule_StringPtrLen(argv[0], NULL);
|
||||
if (!strcasecmp(ptr, "noload")) {
|
||||
/* This is a hint that we return ERR at the last moment of OnLoad. */
|
||||
RedisModule_FreeDict(ctx, loaded_event_log);
|
||||
RedisModule_FreeDict(ctx, module_event_log);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,10 @@ static int KeySpace_NotificationEvicted(RedisModuleCtx *ctx, int type, const cha
|
||||
return REDISMODULE_OK; /* do not count the evicted key */
|
||||
}
|
||||
|
||||
if (strncmp(key_str, "before_evicted", 14) == 0) {
|
||||
return REDISMODULE_OK; /* do not count the before_evicted key */
|
||||
}
|
||||
|
||||
RedisModuleString *new_key = RedisModule_CreateString(NULL, "evicted", 7);
|
||||
RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD);
|
||||
return REDISMODULE_OK;
|
||||
@@ -186,6 +190,55 @@ static int KeySpace_PostNotificationsAsyncSet(RedisModuleCtx *ctx, RedisModuleSt
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
typedef struct KeySpace_EventPostNotificationCtx {
|
||||
RedisModuleString *triggered_on;
|
||||
RedisModuleString *new_key;
|
||||
} KeySpace_EventPostNotificationCtx;
|
||||
|
||||
static void KeySpace_ServerEventPostNotificationFree(void *pd) {
|
||||
KeySpace_EventPostNotificationCtx *pn_ctx = pd;
|
||||
RedisModule_FreeString(NULL, pn_ctx->new_key);
|
||||
RedisModule_FreeString(NULL, pn_ctx->triggered_on);
|
||||
RedisModule_Free(pn_ctx);
|
||||
}
|
||||
|
||||
static void KeySpace_ServerEventPostNotification(RedisModuleCtx *ctx, void *pd) {
|
||||
REDISMODULE_NOT_USED(ctx);
|
||||
KeySpace_EventPostNotificationCtx *pn_ctx = pd;
|
||||
RedisModuleCallReply* rep = RedisModule_Call(ctx, "lpush", "!ss", pn_ctx->new_key, pn_ctx->triggered_on);
|
||||
RedisModule_FreeCallReply(rep);
|
||||
}
|
||||
|
||||
static void KeySpace_ServerEventCallback(RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data) {
|
||||
REDISMODULE_NOT_USED(eid);
|
||||
REDISMODULE_NOT_USED(data);
|
||||
if (subevent > 3) {
|
||||
RedisModule_Log(ctx, "warning", "Got an unexpected subevent '%ld'", subevent);
|
||||
return;
|
||||
}
|
||||
static const char* events[] = {
|
||||
"before_deleted",
|
||||
"before_expired",
|
||||
"before_evicted",
|
||||
"before_overwritten",
|
||||
};
|
||||
|
||||
const RedisModuleString *key_name = RedisModule_GetKeyNameFromModuleKey(((RedisModuleKeyInfo*)data)->key);
|
||||
const char *key_str = RedisModule_StringPtrLen(key_name, NULL);
|
||||
|
||||
for (int i = 0 ; i < 4 ; ++i) {
|
||||
const char *event = events[i];
|
||||
if (strncmp(key_str, event , strlen(event)) == 0) {
|
||||
return; /* don't log any event on our tracking keys */
|
||||
}
|
||||
}
|
||||
|
||||
KeySpace_EventPostNotificationCtx *pn_ctx = RedisModule_Alloc(sizeof(*pn_ctx));
|
||||
pn_ctx->triggered_on = RedisModule_HoldString(NULL, (RedisModuleString*)key_name);
|
||||
pn_ctx->new_key = RedisModule_CreateString(NULL, events[subevent], strlen(events[subevent]));
|
||||
RedisModule_AddPostNotificationJob(ctx, KeySpace_ServerEventPostNotification, pn_ctx, KeySpace_ServerEventPostNotificationFree);
|
||||
}
|
||||
|
||||
/* This function must be present on each Redis module. It is used in order to
|
||||
* register the commands into the Redis server. */
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
@@ -200,6 +253,14 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
int with_key_events = 0;
|
||||
if (argc >= 1) {
|
||||
const char *arg = RedisModule_StringPtrLen(argv[0], 0);
|
||||
if (strcmp(arg, "with_key_events") == 0) {
|
||||
with_key_events = 1;
|
||||
}
|
||||
}
|
||||
|
||||
RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS);
|
||||
|
||||
if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NotificationString) != REDISMODULE_OK){
|
||||
@@ -222,6 +283,12 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
if (with_key_events) {
|
||||
if(RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_Key, KeySpace_ServerEventCallback) != REDISMODULE_OK){
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (RedisModule_CreateCommand(ctx, "postnotification.async_set", KeySpace_PostNotificationsAsyncSet,
|
||||
"write", 0, 0, 0) == REDISMODULE_ERR){
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
@@ -199,3 +199,30 @@ proc are_hostnames_propagated {match_string} {
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
proc wait_node_marked_fail {ref_node_index instance_id_to_check} {
|
||||
wait_for_condition 1000 50 {
|
||||
[check_cluster_node_mark fail $ref_node_index $instance_id_to_check]
|
||||
} else {
|
||||
fail "Replica node never marked as FAIL ('fail')"
|
||||
}
|
||||
}
|
||||
|
||||
proc wait_node_marked_pfail {ref_node_index instance_id_to_check} {
|
||||
wait_for_condition 1000 50 {
|
||||
[check_cluster_node_mark fail\? $ref_node_index $instance_id_to_check]
|
||||
} else {
|
||||
fail "Replica node never marked as PFAIL ('fail?')"
|
||||
}
|
||||
}
|
||||
|
||||
proc check_cluster_node_mark {flag ref_node_index instance_id_to_check} {
|
||||
set nodes [get_cluster_nodes $ref_node_index]
|
||||
|
||||
foreach n $nodes {
|
||||
if {[dict get $n id] eq $instance_id_to_check} {
|
||||
return [cluster_has_flag $n $flag]
|
||||
}
|
||||
}
|
||||
fail "Unable to find instance id in cluster nodes. ID: $instance_id_to_check"
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ set ::all_tests {
|
||||
unit/client-eviction
|
||||
unit/violations
|
||||
unit/replybufsize
|
||||
unit/cluster/announced-endpoints
|
||||
unit/cluster/misc
|
||||
unit/cluster/cli
|
||||
unit/cluster/scripting
|
||||
@@ -103,6 +104,7 @@ set ::all_tests {
|
||||
unit/cluster/slot-ownership
|
||||
unit/cluster/links
|
||||
unit/cluster/cluster-response-tls
|
||||
unit/cluster/failure-marking
|
||||
}
|
||||
# Index to the next test to run in the ::all_tests list.
|
||||
set ::next_test 0
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
start_cluster 2 2 {tags {external:skip cluster}} {
|
||||
|
||||
test "Test change cluster-announce-port and cluster-announce-tls-port at runtime" {
|
||||
set baseport [lindex [R 0 config get port] 1]
|
||||
set count [expr [llength $::servers] +1 ]
|
||||
if {$::tls} {
|
||||
set baseport [lindex [R 0 config get tls-port] 1]
|
||||
} else {
|
||||
set baseport [lindex [R 0 config get port] 1]
|
||||
}
|
||||
set count [expr [llength $::servers] + 1]
|
||||
set used_port [find_available_port $baseport $count]
|
||||
|
||||
R 0 config set cluster-announce-tls-port $used_port
|
||||
@@ -17,12 +21,16 @@ start_cluster 2 2 {tags {external:skip cluster}} {
|
||||
|
||||
R 0 config set cluster-announce-tls-port 0
|
||||
R 0 config set cluster-announce-port 0
|
||||
assert_match "*:$baseport@*" [R 0 CLUSTER NODES]
|
||||
assert_match "*:$baseport@*" [R 0 CLUSTER NODES]
|
||||
}
|
||||
|
||||
test "Test change cluster-announce-bus-port at runtime" {
|
||||
set baseport [lindex [R 0 config get port] 1]
|
||||
set count [expr [llength $::servers] +1 ]
|
||||
if {$::tls} {
|
||||
set baseport [lindex [R 0 config get tls-port] 1]
|
||||
} else {
|
||||
set baseport [lindex [R 0 config get port] 1]
|
||||
}
|
||||
set count [expr [llength $::servers] + 1]
|
||||
set used_port [find_available_port $baseport $count]
|
||||
|
||||
# Verify config set cluster-announce-bus-port
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Test a single primary can mark replica as `fail`
|
||||
start_cluster 1 1 {tags {external:skip cluster}} {
|
||||
|
||||
test "Verify that single primary marks replica as failed" {
|
||||
set primary [srv -0 client]
|
||||
|
||||
set replica1 [srv -1 client]
|
||||
set replica1_pid [srv -1 pid]
|
||||
set replica1_instance_id [dict get [cluster_get_myself 1] id]
|
||||
|
||||
assert {[lindex [$primary role] 0] eq {master}}
|
||||
assert {[lindex [$replica1 role] 0] eq {slave}}
|
||||
|
||||
wait_for_sync $replica1
|
||||
|
||||
pause_process $replica1_pid
|
||||
|
||||
wait_node_marked_fail 0 $replica1_instance_id
|
||||
}
|
||||
}
|
||||
|
||||
# Test multiple primaries wait for a quorum and then mark a replica as `fail`
|
||||
start_cluster 2 1 {tags {external:skip cluster}} {
|
||||
|
||||
test "Verify that multiple primaries mark replica as failed" {
|
||||
set primary1 [srv -0 client]
|
||||
|
||||
set primary2 [srv -1 client]
|
||||
set primary2_pid [srv -1 pid]
|
||||
|
||||
set replica1 [srv -2 client]
|
||||
set replica1_pid [srv -2 pid]
|
||||
set replica1_instance_id [dict get [cluster_get_myself 2] id]
|
||||
|
||||
assert {[lindex [$primary1 role] 0] eq {master}}
|
||||
assert {[lindex [$primary2 role] 0] eq {master}}
|
||||
assert {[lindex [$replica1 role] 0] eq {slave}}
|
||||
|
||||
wait_for_sync $replica1
|
||||
|
||||
pause_process $replica1_pid
|
||||
|
||||
# Pause other primary to allow time for pfail flag to appear
|
||||
pause_process $primary2_pid
|
||||
|
||||
wait_node_marked_pfail 0 $replica1_instance_id
|
||||
|
||||
# Resume other primary and wait for to show replica as failed
|
||||
resume_process $primary2_pid
|
||||
|
||||
wait_node_marked_fail 0 $replica1_instance_id
|
||||
}
|
||||
}
|
||||
@@ -499,4 +499,10 @@ foreach {type large} [array get largevalue] {
|
||||
r SET aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1
|
||||
r KEYS "a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*b"
|
||||
} {}
|
||||
|
||||
test {Regression for pattern matching very long nested loops} {
|
||||
r flushdb
|
||||
r SET [string repeat "a" 50000] 1
|
||||
r KEYS [string repeat "*?" 50000]
|
||||
} {}
|
||||
}
|
||||
|
||||
@@ -577,4 +577,57 @@ start_server {tags {"defrag external:skip"} overrides {appendonly yes auto-aof-r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_cluster 1 0 {tags {"defrag external:skip"} overrides {appendonly yes auto-aof-rewrite-percentage 0 save ""}} {
|
||||
if {[string match {*jemalloc*} [s mem_allocator]] && [r debug mallctl arenas.page] <= 8192} {
|
||||
test "Active defrag stability during async flush or mid stopping in cluster mode" {
|
||||
# Verify that asynchronously empting db doesn't cause defragmentation crashes, see issue #13205.
|
||||
r flushdb async
|
||||
r config set hz 100
|
||||
r config set activedefrag no
|
||||
r config set active-defrag-threshold-lower 1
|
||||
r config set active-defrag-cycle-min 65
|
||||
r config set active-defrag-cycle-max 75
|
||||
r config set active-defrag-ignore-bytes 100k
|
||||
|
||||
# create big keys with 10k items
|
||||
set rd [redis_deferring_client]
|
||||
for {set j 0} {$j < 100000} {incr j} {
|
||||
$rd set $j a
|
||||
$rd expire $j 99999
|
||||
}
|
||||
for {set j 0} {$j < 100000} {incr j} {
|
||||
$rd read ; # Discard replies
|
||||
$rd read ; # Discard replies
|
||||
}
|
||||
|
||||
catch {r config set activedefrag yes} e
|
||||
if {[r config get activedefrag] eq "activedefrag yes"} {
|
||||
# It repeatedly enables and disables active defragmentation,
|
||||
# and checks if it crashes, see issue #13307.
|
||||
for {set i 0} {$i < 10} {incr i} {
|
||||
r config set activedefrag no
|
||||
# Wait for the active defrag to start working (decision once a second).
|
||||
wait_for_condition 50 100 {
|
||||
[s active_defrag_running] eq 0
|
||||
} else {
|
||||
after 120 ;# serverCron only updates the info once in 100ms
|
||||
puts [r info memory]
|
||||
puts [r memory malloc-stats]
|
||||
fail "defrag didn't stop."
|
||||
}
|
||||
|
||||
# Wait for the active defrag to stop working.
|
||||
r config set activedefrag yes
|
||||
wait_for_condition 50 100 {
|
||||
[s active_defrag_running] ne 0
|
||||
} else {
|
||||
fail "defrag not started."
|
||||
}
|
||||
}
|
||||
}
|
||||
r ping
|
||||
}
|
||||
}
|
||||
}
|
||||
} ;# run_solo
|
||||
|
||||
@@ -314,6 +314,14 @@ start_server {tags {"modules"}} {
|
||||
r lpush l a
|
||||
assert_equal [$rd read] {OK}
|
||||
|
||||
# Explanation of the first multi exec block:
|
||||
# {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed
|
||||
# {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed
|
||||
# {incr string_total} - post notification job that was registered when string_changed{string_foo} changed
|
||||
# {incr string_total} - post notification job that was registered when string_changed{string_bar} changed
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{lpush l a}
|
||||
@@ -355,6 +363,25 @@ start_server {tags {"modules"}} {
|
||||
r lpush l a
|
||||
assert_equal [$rd read] {OK}
|
||||
|
||||
# Explanation of the first multi exec block:
|
||||
# {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed
|
||||
# {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed
|
||||
# {incr string_total} - post notification job that was registered when string_changed{string_foo} changed
|
||||
# {incr string_total} - post notification job that was registered when string_changed{string_bar} changed
|
||||
#
|
||||
# Explanation of the second multi exec block:
|
||||
# {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {del string_foo} - lazy expiration of string_foo when 'blpop_and_set_multiple_keys' tries to write to it.
|
||||
# {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys'
|
||||
# {incr expired} - the post notification job, registered after string_foo got expired
|
||||
# {incr string_changed{string_foo}} - post notification job triggered when we set string_foo
|
||||
# {incr string_changed{string_bar}} - post notification job triggered when we set string_bar
|
||||
# {incr string_total} - post notification job triggered when we incr 'string_changed{string_foo}'
|
||||
# {incr string_total} - post notification job triggered when we incr 'string_changed{string_bar}'
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{lpush l a}
|
||||
|
||||
@@ -95,7 +95,7 @@ start_server {tags {"modules"}} {
|
||||
test "Unload the module - commandfilter" {
|
||||
assert_equal {OK} [r module unload commandfilter]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {RM_CommandFilterArgInsert and script argv caching} {
|
||||
# coverage for scripts calling commands that expand the argv array
|
||||
@@ -162,4 +162,14 @@ test {Filtering based on client id} {
|
||||
|
||||
$rr close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_server {} {
|
||||
test {OnLoad failure will handle un-registration} {
|
||||
catch {r module load $testmodule log-key 0 noload}
|
||||
r set mykey @log
|
||||
assert_equal [r lrange log-key 0 -1] {}
|
||||
r rpush mylist elem1 @delme elem2
|
||||
assert_equal [r lrange mylist 0 -1] {elem1 @delme elem2}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,4 +310,12 @@ tags "modules" {
|
||||
assert_equal [string match {*module-event-shutdown*} [exec tail -5 < $replica_stdout]] 1
|
||||
}
|
||||
}
|
||||
|
||||
start_server {} {
|
||||
test {OnLoad failure will handle un-registration} {
|
||||
catch {r module load $testmodule noload}
|
||||
r flushall
|
||||
r ping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,4 +102,17 @@ tags "modules" {
|
||||
assert_equal {OK} [r set x 1 EX 1]
|
||||
}
|
||||
}
|
||||
|
||||
start_server {} {
|
||||
test {OnLoad failure will handle un-registration} {
|
||||
catch {r module load $testmodule noload}
|
||||
r set x 1
|
||||
r hset y f v
|
||||
r lpush z 1 2 3
|
||||
r sadd p 1 2 3
|
||||
r zadd t 1 f1 2 f2
|
||||
r xadd s * f v
|
||||
r ping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
set testmodule [file normalize tests/modules/postnotifications.so]
|
||||
|
||||
tags "modules" {
|
||||
start_server [list overrides [list loadmodule "$testmodule"]] {
|
||||
start_server {} {
|
||||
r module load $testmodule with_key_events
|
||||
|
||||
test {Test write on post notification callback} {
|
||||
set repl [attach_to_replication_stream]
|
||||
@@ -9,11 +10,12 @@ tags "modules" {
|
||||
r set string_x 1
|
||||
assert_equal {1} [r get string_changed{string_x}]
|
||||
assert_equal {1} [r get string_total]
|
||||
|
||||
|
||||
r set string_x 2
|
||||
assert_equal {2} [r get string_changed{string_x}]
|
||||
assert_equal {2} [r get string_total]
|
||||
|
||||
# the {lpush before_overwritten string_x} is a post notification job registered when 'string_x' was overwritten
|
||||
assert_replication_stream $repl {
|
||||
{multi}
|
||||
{select *}
|
||||
@@ -23,6 +25,7 @@ tags "modules" {
|
||||
{exec}
|
||||
{multi}
|
||||
{set string_x 2}
|
||||
{lpush before_overwritten string_x}
|
||||
{incr string_changed{string_x}}
|
||||
{incr string_total}
|
||||
{exec}
|
||||
@@ -37,7 +40,7 @@ tags "modules" {
|
||||
assert_equal {OK} [r postnotification.async_set]
|
||||
assert_equal {1} [r get string_changed{string_x}]
|
||||
assert_equal {1} [r get string_total]
|
||||
|
||||
|
||||
assert_replication_stream $repl {
|
||||
{multi}
|
||||
{select *}
|
||||
@@ -63,12 +66,14 @@ tags "modules" {
|
||||
fail "Failed waiting for x to expired"
|
||||
}
|
||||
|
||||
# the {lpush before_expired x} is a post notification job registered before 'x' got expired
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{set x 1}
|
||||
{pexpireat x *}
|
||||
{multi}
|
||||
{del x}
|
||||
{lpush before_expired x}
|
||||
{incr expired}
|
||||
{exec}
|
||||
}
|
||||
@@ -85,12 +90,14 @@ tags "modules" {
|
||||
after 10
|
||||
assert_equal {} [r get x]
|
||||
|
||||
# the {lpush before_expired x} is a post notification job registered before 'x' got expired
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{set x 1}
|
||||
{pexpireat x *}
|
||||
{multi}
|
||||
{del x}
|
||||
{lpush before_expired x}
|
||||
{incr expired}
|
||||
{exec}
|
||||
}
|
||||
@@ -108,6 +115,7 @@ tags "modules" {
|
||||
after 10
|
||||
assert_equal {OK} [r set read_x 1]
|
||||
|
||||
# the {lpush before_expired x} is a post notification job registered before 'x' got expired
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{set x 1}
|
||||
@@ -115,6 +123,7 @@ tags "modules" {
|
||||
{multi}
|
||||
{set read_x 1}
|
||||
{del x}
|
||||
{lpush before_expired x}
|
||||
{incr expired}
|
||||
{exec}
|
||||
}
|
||||
@@ -143,16 +152,18 @@ tags "modules" {
|
||||
r flushall
|
||||
set repl [attach_to_replication_stream]
|
||||
r set x 1
|
||||
r config set maxmemory-policy allkeys-random
|
||||
r config set maxmemory-policy allkeys-random
|
||||
r config set maxmemory 1
|
||||
|
||||
assert_error {OOM *} {r set y 1}
|
||||
|
||||
# the {lpush before_evicted x} is a post notification job registered before 'x' got evicted
|
||||
assert_replication_stream $repl {
|
||||
{select *}
|
||||
{set x 1}
|
||||
{multi}
|
||||
{del x}
|
||||
{lpush before_evicted x}
|
||||
{incr evicted}
|
||||
{exec}
|
||||
}
|
||||
@@ -164,7 +175,8 @@ tags "modules" {
|
||||
set testmodule2 [file normalize tests/modules/keyspace_events.so]
|
||||
|
||||
tags "modules" {
|
||||
start_server [list overrides [list loadmodule "$testmodule"]] {
|
||||
start_server {} {
|
||||
r module load $testmodule with_key_events
|
||||
r module load $testmodule2
|
||||
test {Test write on post notification callback} {
|
||||
set repl [attach_to_replication_stream]
|
||||
@@ -172,7 +184,7 @@ tags "modules" {
|
||||
r set string_x 1
|
||||
assert_equal {1} [r get string_changed{string_x}]
|
||||
assert_equal {1} [r get string_total]
|
||||
|
||||
|
||||
r set string_x 2
|
||||
assert_equal {2} [r get string_changed{string_x}]
|
||||
assert_equal {2} [r get string_total]
|
||||
@@ -181,6 +193,7 @@ tags "modules" {
|
||||
assert_equal {1} [r get string_changed{string1_x}]
|
||||
assert_equal {3} [r get string_total]
|
||||
|
||||
# the {lpush before_overwritten string_x} is a post notification job registered before 'string_x' got overwritten
|
||||
assert_replication_stream $repl {
|
||||
{multi}
|
||||
{select *}
|
||||
@@ -190,6 +203,7 @@ tags "modules" {
|
||||
{exec}
|
||||
{multi}
|
||||
{set string_x 2}
|
||||
{lpush before_overwritten string_x}
|
||||
{incr string_changed{string_x}}
|
||||
{incr string_total}
|
||||
{exec}
|
||||
@@ -202,4 +216,4 @@ tags "modules" {
|
||||
close_replication_stream $repl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
set system_name [string tolower [exec uname -s]]
|
||||
set user_id [exec id -u]
|
||||
|
||||
if {$system_name eq {linux}} {
|
||||
start_server {tags {"oom-score-adj external:skip"}} {
|
||||
@@ -56,8 +55,15 @@ if {$system_name eq {linux}} {
|
||||
}
|
||||
}
|
||||
|
||||
# Determine whether the current user is unprivileged
|
||||
set original_value [exec cat /proc/self/oom_score_adj]
|
||||
catch {
|
||||
set fd [open "/proc/self/oom_score_adj" "w"]
|
||||
puts $fd -1000
|
||||
close $fd
|
||||
} e
|
||||
# Failed oom-score-adj tests can only run unprivileged
|
||||
if {$user_id != 0} {
|
||||
if {[string match "*permission denied*" $e]} {
|
||||
test {CONFIG SET oom-score-adj handles configuration failures} {
|
||||
# Bad config
|
||||
r config set oom-score-adj no
|
||||
@@ -81,6 +87,11 @@ if {$system_name eq {linux}} {
|
||||
# Make sure previous values remain
|
||||
assert {[r config get oom-score-adj-values] == {oom-score-adj-values {0 100 100}}}
|
||||
}
|
||||
} else {
|
||||
# Restore the original oom_score_adj value
|
||||
set fd [open "/proc/self/oom_score_adj" "w"]
|
||||
puts $fd $original_value
|
||||
close $fd
|
||||
}
|
||||
|
||||
test {CONFIG SET oom-score-adj-values doesn't touch proc when disabled} {
|
||||
|
||||
@@ -146,6 +146,14 @@ start_server {tags {"scripting"}} {
|
||||
} 1 x
|
||||
} {number 1}
|
||||
|
||||
test {EVAL - Lua number -> Redis integer conversion} {
|
||||
r del hash
|
||||
run_script {
|
||||
local foo = redis.pcall('hincrby','hash','field',200000000)
|
||||
return {type(foo),foo}
|
||||
} 0
|
||||
} {number 200000000}
|
||||
|
||||
test {EVAL - Redis bulk -> Lua type conversion} {
|
||||
r set mykey myval
|
||||
run_script {
|
||||
@@ -605,6 +613,12 @@ start_server {tags {"scripting"}} {
|
||||
set e
|
||||
} {ERR *Attempt to modify a readonly table*}
|
||||
|
||||
test {lua bit.tohex bug} {
|
||||
set res [run_script {return bit.tohex(65535, -2147483648)} 0]
|
||||
r ping
|
||||
set res
|
||||
} {0000FFFF}
|
||||
|
||||
test {Test an example script DECR_IF_GT} {
|
||||
set decr_if_gt {
|
||||
local current
|
||||
|
||||
+8
-2
@@ -75,12 +75,14 @@ start_server {
|
||||
assert_equal [lsort -integer $result] [r sort tosort GET #]
|
||||
} {} {cluster:skip}
|
||||
|
||||
test "SORT GET <const>" {
|
||||
foreach command {SORT SORT_RO} {
|
||||
test "$command GET <const>" {
|
||||
r del foo
|
||||
set res [r sort tosort GET foo]
|
||||
set res [r $command tosort GET foo]
|
||||
assert_equal 16 [llength $res]
|
||||
foreach item $res { assert_equal {} $item }
|
||||
} {} {cluster:skip}
|
||||
}
|
||||
|
||||
test "SORT GET (key and hash) with sanity check" {
|
||||
set l1 [r sort tosort GET # GET weight_*]
|
||||
@@ -109,6 +111,10 @@ start_server {
|
||||
test "SORT extracts STORE correctly" {
|
||||
r command getkeys sort abc store def
|
||||
} {abc def}
|
||||
|
||||
test "SORT_RO get keys" {
|
||||
r command getkeys sort_ro abc
|
||||
} {abc}
|
||||
|
||||
test "SORT extracts multiple STORE correctly" {
|
||||
r command getkeys sort abc store invalid store stillbad store def
|
||||
|
||||
@@ -220,6 +220,7 @@ start_server [list overrides [list save ""] ] {
|
||||
|
||||
# checking LSET in case ziplist needs to be split
|
||||
test {Test LSET with packed is split in the middle} {
|
||||
set original_config [config_get_set list-max-listpack-size 4]
|
||||
r flushdb
|
||||
r debug quicklist-packed-threshold 5b
|
||||
r RPUSH lst "aa"
|
||||
@@ -227,6 +228,7 @@ start_server [list overrides [list save ""] ] {
|
||||
r RPUSH lst "cc"
|
||||
r RPUSH lst "dd"
|
||||
r RPUSH lst "ee"
|
||||
assert_encoding quicklist lst
|
||||
r lset lst 2 [string repeat e 10]
|
||||
assert_equal [r lpop lst] "aa"
|
||||
assert_equal [r lpop lst] "bb"
|
||||
@@ -234,6 +236,7 @@ start_server [list overrides [list save ""] ] {
|
||||
assert_equal [r lpop lst] "dd"
|
||||
assert_equal [r lpop lst] "ee"
|
||||
r debug quicklist-packed-threshold 0
|
||||
r config set list-max-listpack-size $original_config
|
||||
} {OK} {needs:debug}
|
||||
|
||||
|
||||
@@ -381,7 +384,63 @@ if {[lindex [r config get proto-max-bulk-len] 1] == 10000000000} {
|
||||
assert_equal [read_big_bulk {r rpop lst}] $str_length
|
||||
} {} {large-memory}
|
||||
|
||||
test {Test LMOVE on plain nodes over 4GB} {
|
||||
test {Test LSET on plain nodes with large elements under packed_threshold over 4GB} {
|
||||
r flushdb
|
||||
r rpush lst a b c d e
|
||||
for {set i 0} {$i < 5} {incr i} {
|
||||
r write "*4\r\n\$4\r\nlset\r\n\$3\r\nlst\r\n\$1\r\n$i\r\n"
|
||||
write_big_bulk 1000000000
|
||||
}
|
||||
r ping
|
||||
} {PONG} {large-memory}
|
||||
|
||||
test {Test LSET splits a quicklist node, and then merge} {
|
||||
# Test when a quicklist node can't be inserted and is split, the split
|
||||
# node merges with the node before it and the `before` node is kept.
|
||||
r flushdb
|
||||
r rpush lst [string repeat "x" 4096]
|
||||
r lpush lst a b c d e f g
|
||||
r lpush lst [string repeat "y" 4096]
|
||||
# now: [y...] [g f e d c b a x...]
|
||||
# (node0) (node1)
|
||||
# Keep inserting elements into node1 until node1 is split into two
|
||||
# nodes([g] [...]), eventually node0 will merge with the [g] node.
|
||||
# Since node0 is larger, after the merge node0 will be kept and
|
||||
# the [g] node will be deleted.
|
||||
for {set i 7} {$i >= 3} {incr i -1} {
|
||||
r write "*4\r\n\$4\r\nlset\r\n\$3\r\nlst\r\n\$1\r\n$i\r\n"
|
||||
write_big_bulk 1000000000
|
||||
}
|
||||
assert_equal "g" [r lindex lst 1]
|
||||
r ping
|
||||
} {PONG} {large-memory}
|
||||
|
||||
test {Test LSET splits a LZF compressed quicklist node, and then merge} {
|
||||
# Test when a LZF compressed quicklist node can't be inserted and is split,
|
||||
# the split node merges with the node before it and the split node is kept.
|
||||
r flushdb
|
||||
r config set list-compress-depth 1
|
||||
r lpush lst [string repeat "x" 2000]
|
||||
r rpush lst [string repeat "y" 7000]
|
||||
r rpush lst a b c d e f g
|
||||
r rpush lst [string repeat "z" 8000]
|
||||
r lset lst 0 h
|
||||
# now: [h] [y... a b c d e f g] [z...]
|
||||
# node0 node1(LZF)
|
||||
# Keep inserting elements into node1 until node1 is split into two
|
||||
# nodes([y...] [...]), eventually node0 will merge with the [y...] node.
|
||||
# Since [y...] node is larger, after the merge node0 will be deleted and
|
||||
# the [y...] node will be kept.
|
||||
for {set i 7} {$i >= 3} {incr i -1} {
|
||||
r write "*4\r\n\$4\r\nlset\r\n\$3\r\nlst\r\n\$1\r\n$i\r\n"
|
||||
write_big_bulk 1000000000
|
||||
}
|
||||
assert_equal "h" [r lindex lst 0]
|
||||
r config set list-compress-depth 0
|
||||
r ping
|
||||
} {PONG} {large-memory}
|
||||
|
||||
test {Test LMOVE on plain nodes over 4GB} {
|
||||
r flushdb
|
||||
r RPUSH lst2{t} "aa"
|
||||
r RPUSH lst2{t} "bb"
|
||||
@@ -1186,6 +1245,34 @@ foreach {pop} {BLPOP BLMPOP_LEFT} {
|
||||
r select 9
|
||||
} {OK} {singledb:skip needs:debug}
|
||||
|
||||
test {BLPOP unblock but the key is expired and then block again - reprocessing command} {
|
||||
r flushall
|
||||
r debug set-active-expire 0
|
||||
set rd [redis_deferring_client]
|
||||
|
||||
set start [clock milliseconds]
|
||||
$rd blpop mylist 1
|
||||
wait_for_blocked_clients_count 1
|
||||
|
||||
# The exec will try to awake the blocked client, but the key is expired,
|
||||
# so the client will be blocked again during the command reprocessing.
|
||||
r multi
|
||||
r rpush mylist a
|
||||
r pexpire mylist 100
|
||||
r debug sleep 0.2
|
||||
r exec
|
||||
|
||||
assert_equal {} [$rd read]
|
||||
set end [clock milliseconds]
|
||||
|
||||
# Before the fix in #13004, this time would have been 1200+ (i.e. more than 1200ms),
|
||||
# now it should be 1000, but in order to avoid timing issues, we increase the range a bit.
|
||||
assert_range [expr $end-$start] 1000 1150
|
||||
|
||||
r debug set-active-expire 1
|
||||
$rd close
|
||||
} {0} {needs:debug}
|
||||
|
||||
foreach {pop} {BLPOP BLMPOP_LEFT} {
|
||||
test "$pop when new key is moved into place" {
|
||||
set rd [redis_deferring_client]
|
||||
|
||||
@@ -475,7 +475,7 @@ start_server {
|
||||
$rd close
|
||||
}
|
||||
|
||||
test {Blocking XREADGROUP for stream key that has clients blocked on list - avoid endless loop} {
|
||||
test {Blocking XREADGROUP for stream key that has clients blocked on stream - avoid endless loop} {
|
||||
r DEL mystream
|
||||
r XGROUP CREATE mystream mygroup $ MKSTREAM
|
||||
|
||||
@@ -498,6 +498,34 @@ start_server {
|
||||
assert_equal [r ping] {PONG}
|
||||
}
|
||||
|
||||
test {Blocking XREADGROUP for stream key that has clients blocked on stream - reprocessing command} {
|
||||
r DEL mystream
|
||||
r XGROUP CREATE mystream mygroup $ MKSTREAM
|
||||
|
||||
set rd1 [redis_deferring_client]
|
||||
set rd2 [redis_deferring_client]
|
||||
|
||||
$rd1 xreadgroup GROUP mygroup myuser BLOCK 0 STREAMS mystream >
|
||||
wait_for_blocked_clients_count 1
|
||||
|
||||
set start [clock milliseconds]
|
||||
$rd2 xreadgroup GROUP mygroup myuser BLOCK 1000 STREAMS mystream >
|
||||
wait_for_blocked_clients_count 2
|
||||
|
||||
# After a while call xadd and let rd2 re-process the command.
|
||||
after 200
|
||||
r xadd mystream * field value
|
||||
assert_equal {} [$rd2 read]
|
||||
set end [clock milliseconds]
|
||||
|
||||
# Before the fix in #13004, this time would have been 1200+ (i.e. more than 1200ms),
|
||||
# now it should be 1000, but in order to avoid timing issues, we increase the range a bit.
|
||||
assert_range [expr $end-$start] 1000 1150
|
||||
|
||||
$rd1 close
|
||||
$rd2 close
|
||||
}
|
||||
|
||||
test {XGROUP DESTROY should unblock XREADGROUP with -NOGROUP} {
|
||||
r config resetstat
|
||||
r del mystream
|
||||
|
||||
@@ -1942,6 +1942,34 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
|
||||
test {BZPOPMIN unblock but the key is expired and then block again - reprocessing command} {
|
||||
r flushall
|
||||
r debug set-active-expire 0
|
||||
set rd [redis_deferring_client]
|
||||
|
||||
set start [clock milliseconds]
|
||||
$rd bzpopmin zset{t} 1
|
||||
wait_for_blocked_clients_count 1
|
||||
|
||||
# The exec will try to awake the blocked client, but the key is expired,
|
||||
# so the client will be blocked again during the command reprocessing.
|
||||
r multi
|
||||
r zadd zset{t} 1 one
|
||||
r pexpire zset{t} 100
|
||||
r debug sleep 0.2
|
||||
r exec
|
||||
|
||||
assert_equal {} [$rd read]
|
||||
set end [clock milliseconds]
|
||||
|
||||
# Before the fix in #13004, this time would have been 1200+ (i.e. more than 1200ms),
|
||||
# now it should be 1000, but in order to avoid timing issues, we increase the range a bit.
|
||||
assert_range [expr $end-$start] 1000 1150
|
||||
|
||||
r debug set-active-expire 1
|
||||
$rd close
|
||||
} {0} {needs:debug}
|
||||
|
||||
test "BZPOPMIN with same key multiple times should work" {
|
||||
set rd [redis_deferring_client]
|
||||
r del z1{t} z2{t}
|
||||
@@ -2211,12 +2239,18 @@ start_server {tags {"zset"}} {
|
||||
} {b 2 c 3}
|
||||
|
||||
test {ZRANGESTORE BYLEX} {
|
||||
set res [r zrangestore z3{t} z1{t} \[b \[c BYLEX]
|
||||
assert_equal $res 2
|
||||
assert_encoding listpack z3{t}
|
||||
set res [r zrangestore z2{t} z1{t} \[b \[c BYLEX]
|
||||
assert_equal $res 2
|
||||
r zrange z2{t} 0 -1 withscores
|
||||
} {b 2 c 3}
|
||||
|
||||
test {ZRANGESTORE BYSCORE} {
|
||||
set res [r zrangestore z4{t} z1{t} 1 2 BYSCORE]
|
||||
assert_equal $res 2
|
||||
assert_encoding listpack z4{t}
|
||||
set res [r zrangestore z2{t} z1{t} 1 2 BYSCORE]
|
||||
assert_equal $res 2
|
||||
r zrange z2{t} 0 -1 withscores
|
||||
|
||||
@@ -140,6 +140,37 @@ tags {"wait aof network external:skip"} {
|
||||
assert_error {ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.} {$master waitaof 1 0 0}
|
||||
}
|
||||
|
||||
test {WAITAOF local if AOFRW was postponed} {
|
||||
r config set appendfsync everysec
|
||||
|
||||
# turn off AOF
|
||||
r config set appendonly no
|
||||
|
||||
# create an RDB child that takes a lot of time to run
|
||||
r set x y
|
||||
r config set rdb-key-save-delay 100000000 ;# 100 seconds
|
||||
r bgsave
|
||||
assert_equal [s rdb_bgsave_in_progress] 1
|
||||
|
||||
# turn on AOF
|
||||
r config set appendonly yes
|
||||
assert_equal [s aof_rewrite_scheduled] 1
|
||||
|
||||
# create a write command (to increment master_repl_offset)
|
||||
r set x y
|
||||
|
||||
# reset save_delay and kill RDB child
|
||||
r config set rdb-key-save-delay 0
|
||||
catch {exec kill -9 [get_child_pid 0]}
|
||||
|
||||
# wait for AOF (will unblock after AOFRW finishes)
|
||||
assert_equal [r waitaof 1 0 10000] {1 0}
|
||||
|
||||
# make sure AOFRW finished
|
||||
assert_equal [s aof_rewrite_in_progress] 0
|
||||
assert_equal [s aof_rewrite_scheduled] 0
|
||||
}
|
||||
|
||||
$master config set appendonly yes
|
||||
waitForBgrewriteaof $master
|
||||
|
||||
|
||||
Reference in New Issue
Block a user