Compare commits

...
67 Commits
Author SHA1 Message Date
Ran ShidlansikandGitHub 08e388cab8 Valkey release 8.1.0-ga (#1903)
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2025-03-31 17:56:45 +03:00
VanessaTangandRan Shidlansik 6348cb52f5 Redact protocol error log when hide-user-data-from-log enabled (#1889)
In this code logic:
https://github.com/valkey-io/valkey/blob/unstable/src/networking.c#L2767-L2773,
`c->querybuf + c->qb_pos` may also include user data.
Update the log message when config `hide-user-data-from-log` is enabled.

---------

Signed-off-by: VanessaTang <yuetan@amazon.com>
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Co-authored-by: Ran Shidlansik <ranshid@amazon.com>
2025-03-31 14:05:51 +03:00
fb011c8d5c Remove TLSCONN_DEBUG dead code in tls.c (#1891)
This code is a dead code, there is also a undefined fd error in it.
Obviously this will not compile if this kind of debugging is enabled.

This seems to be broken ever since
2d5ba02f8f
which was done in 2019. It's dead code that we never test and never use.

Fixes #1887

Signed-off-by: WelongZuo <zuowl@qq.com>
Co-authored-by: z00636558 <zuowenlong6@huawei.com>
2025-03-31 14:05:51 +03:00
BinbinandRan Shidlansik 4d3f4d75ef Fix TCL tmp dir leak in the ACL load test (#1895)
Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-31 14:05:51 +03:00
Jim BrunnerandRan Shidlansik e261ae1260 Fix merge error introduced in #1186 (#1894)
Merge error introduced in #1186 .

Prior to the merge, the time value passed to modules was a `monotime`.
The merge reverts it to a wall clock time (on the sending side only).

Signed-off-by: Jim Brunner <brunnerj@amazon.com>
2025-03-31 14:05:51 +03:00
ef4e7d6555 Fix crash in VM_GetCurrentUserName when no valid user (#1885)
VM_GetCurrentUserName make the binary crash when it gets not fully
structured ctx, when it doesn't have a valid ctx, client, user behind
it. Rather than making the binary crash, safely return NULL.

---------

Signed-off-by: jeon1226 <jeon1226@gmail.com>
Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-31 14:05:51 +03:00
157a85b9e9 Update COPY and MOVE command description clearer in JSON file (#1843)
As I do the code review for PR
https://github.com/valkey-io/valkey/pull/1671 (Add multi-database
support to cluster mode), I find
the return value message of the command COPY and MOVE is not very clear
to user. Thus I update them in this PR and valkey-doc repo as PR
https://github.com/valkey-io/valkey-doc/pull/249

---------

Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: Wen Hui <wen.hui.ware@gmail.com>
Co-authored-by: Binbin <binloveplay1314@qq.com>
2025-03-31 14:05:51 +03:00
Harkrishn PatroandRan Shidlansik 1ae5d2d8f1 [cluster] Add node id to log statement for closing link on first message as lightweight (#1869)
---------

Signed-off-by: Harkrishn Patro <harkrisp@amazon.com>
2025-03-31 14:05:51 +03:00
Wen HuiandRan Shidlansik 7e6a497ed1 Minor cleanup remove unnecessary cast since slot is int (#1865)
Signed-off-by: hwware <wen.hui.ware@gmail.com>
2025-03-31 14:05:51 +03:00
e2743ca467 Valkey release 8.1.0-rc2 (#1862)
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-20 18:46:54 +02:00
9951b16d1b Fix RANDOMKEY infinite loop during CLIENT PAUSE (#1850)
When the `client pause write` is set and all the keys in the server
are expired keys, executing the `randomkey` command will lead to an
infinite loop.

The reason is that expired keys are not deleted in this case. Limit
the number of tries and return an expired key after the max number
tries in this case.

Closes #1848.

---------

Signed-off-by: li-benson <1260437731@qq.com>
Signed-off-by: youngmore <youngmore1024@outlook.com>
Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
Co-authored-by: youngmore <youngmore1024@outlook.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
Meinhard ZhouandViktor Söderqvist f5c368e9e1 Support IPv6 address in RDMA test (#1817)
Signed-off-by: Meinhard Zhou <zhouenhua@bytedance.com>
2025-03-18 11:51:02 +01:00
Nikhil MangloreandViktor Söderqvist dc72f47588 Update correct repository name for automation trigger workflow (#1855)
Update automation repository name to `valkey-release-automation`

Signed-off-by: Nikhil Manglore <nmanglor@amazon.com>
2025-03-18 11:51:02 +01:00
0befdee478 Trigger post-release tasks in Valkey for a new release (#1830)
This trigger-build-release.yml file will be used to automate the release
process as described in #1397. It will trigger the post release task after a new release

---------

Signed-off-by: Nikhil Manglore <nmanglor@amazon.com>
Signed-off-by: Nikhil Manglore <nikhilmanglore9@gmail.com>
Co-authored-by: Roshan Khatri <117414976+roshkhatri@users.noreply.github.com>
2025-03-18 11:51:02 +01:00
fa564cdc71 Update valkey-benchmark parseURI function name and comment (#1845)
In Valkey, when user runs the valkey-benchmark tools, the command
**valkey-benchmark -u** could accept 4 schemes of server URI format:  
"valkey://", "valkeys://", "redis://", "rediss://"   

Thus, I add them in the function comment, and update the function name
to parseRedisOrValkeyUri.

Finally, I fix one mistake for valkey-benchmark tool name for this
command.

---------

Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: Wen Hui <wen.hui.ware@gmail.com>
Co-authored-by: Harkrishn Patro <bunty.hari@gmail.com>
2025-03-18 11:51:02 +01:00
Rain ValentineandViktor Söderqvist 4b7ccb3ed3 Fix defrag when type/encoding changes during scan (#1801)
I was hunting for defrag bugs with Jim and found a couple improvements
to make. Jim pointed out that in several of the callbacks, if the
encoding were to change it simply returns without doing anything to
`cursor` to make it reach 0, meaning that it would continue no-op
working on that item without making any progress. Type and encoding can
change while the defrag scan is in progress if the value is mutated or
replaced by something else with the same key.

---------

Signed-off-by: Rain Valentine <rsg000@gmail.com>
2025-03-18 11:51:02 +01:00
31cd2309b8 Fix ACL LOAD crash on replica since the primary client don't has a user (#1842)
Check for NULL before accessing users of a client during the `ACL LOAD`
command.
This prevents the command causing a segmentation fault in the replica.

Fixes #1832.

---------

Signed-off-by: Bogdan Petre <bogdan.petre@aiven.io>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Co-authored-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist 4bbcb0173a Add missing close_replication_stream on multi test (#1841)
Minor cleanup.

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist f363394877 Save config file and brocast the PONG when configEpoch changed (#1813)
This is somehow related with #974 and #1777. When the epoch changes,
we should save the configuration file and broadcast a PONG as much
as possible.

For example, if a primary down after bumping the epoch, its replicas
may initiate a failover, but the other primaries may refuse to vote
because the epoch of the replica has not been updated.

Or for example, for some reasons we bump the epoch, if the epoch
is not updated in time in the cluster, it may affect the judgment
of message staleness.

These broadcasts are expensive in large clusters, but none of these
seem high frequency so it should be fine.

---------

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
c70d660b31 Update CloseKey module API documentation to avoid use-after-free behavior (#1834)
### Issue: https://github.com/valkey-io/valkey/issues/1775

Without a clear comment, users might mistakenly attempt to access or
close the same key object multiple times, leading to use-after-free
errors, undefined behavior, or crashes due to double-free operations.

---------

Signed-off-by: Seungmin Lee <sungming@amazon.com>
Signed-off-by: Seungmin Lee <155032684+sungming2@users.noreply.github.com>
Co-authored-by: Seungmin Lee <sungming@amazon.com>
Co-authored-by: Harkrishn Patro <bunty.hari@gmail.com>
2025-03-18 11:51:02 +01:00
eifrah-awsandViktor Söderqvist 18d6e34ce0 [CMake] Check both arm64 and aarch64 for ARM based system architecture (#1829)
While macOS will report `arm64` for `uname -m` command, others might
report `aarch64`. This PR fixes this

Signed-off-by: Eran Ifrah <eifrah@amazon.com>
2025-03-18 11:51:02 +01:00
Madelyn OlsonandViktor Söderqvist a14d587ff0 Fix bug where invalidation messages were getting sent to closing clients (#1823)
So I think we were seeing [these
timeouts](https://github.com/valkey-io/valkey/actions/runs/13688155322/job/38276139543#step:6:839)
because QUIT behaves differently between IO threading and non-IO
Threading. In both cases, `QUIT` is a close after reply command. Once
the client has written out the results, it gets added to the queue to
that gets cleaned up at the end of the event loop. Normally this is
fine, as before we circle around to the next event loop this client is
definitely killed.

For IO threads, we need to process the pending IO commands to add the
client to the kill queue. This may not happen immediately, which means
we might go down and process that `SET` command *before* we free the
client that is supposedly already quit. This is very sensitive to
timing, so it's not very likely, but still possible. Once the `SET` has
been executed, the invariants in the tests are off since it will get a
correct invalidation.

The fix is to also mark a client as broken if it's being closed. 

The test was also hanging because of a test issue, because the
conditional `lsearch` check was returning 1 or 0 strings, which are both
valid exit criteria for the wait_for.

```
./runtest --io-threads --accurate --verbose --tags network --dump-logs --single unit/tracking --loops 500 --clients 25
```

Fixes #1647 (I believe this now!)

---------

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2025-03-18 11:51:02 +01:00
Ricardo DiasandViktor Söderqvist 41834b157c Adds a memory leak check after running a unit test (#1798)
This commit adds check after each test function execution of a unit
test, that checks if there is still memory allocated.

This check prevents situations where tests, which do these kind of
checks, fail due to memory leaks caused by previous tests.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
2025-03-18 11:51:02 +01:00
ranshidandViktor Söderqvist 154a633c33 Enable large-memory tests solo runs in daily workflow (#1816)
This change re-introduce large-memory tests to be run as solo tests on
daily runs.

the change includes:
1. separate the large-memory tests into separate test block (mainly
helpful in workflow manual dispatch cases)
2. place all current large memory tests in run solo blocks in order to
prevent potential runner OOM failures.

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist 1bccc8e3ba Fix timing issue in the module propagate test (#1815)
There is a timing issue in the test:
```
*** [err]: module RM_Call of expired key propagation in tests/unit/moduleapi/propagate.tcl
Expected '1' to be equal to '2' (context: type eval line 27 cmd {assert_equal [$replica propagate-test.obeyed] 2} proc ::test)
```

We should wait for sync, otherwise the replica may not be fully
synchronized before checking. The test was introduced in #1582.

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
zhenwei piandViktor Söderqvist 71b230c62a Use the wrapper from cli_common instead of hiredis (#1802)
There are mixed usage around `redisConnect*`, we should use
`redisConnectWrapper*` wrapper from cli_common instead of
`redisConnect` from hiredis. This is a cleanup.

Signed-off-by: zhenwei pi <zhenwei.pi@linux.dev>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
2025-03-18 11:51:02 +01:00
70080e8a97 Fix incorrect assertion in client list operations (#1800)
The current assertion introduced in #11220:
```c
serverAssert(&c->clients_pending_write_node.next != NULL || &c->clients_pending_write_node.prev != NULL);
```

is incorrect for two reasons:
1. Using &pointer.next would always be non-NULL since it's the address
of the field.
2. The check is incorrect even without the & because in a single-node
list, both pointers can be NULL.

Fix:
1. Remove the always-true assertion
2. Add proper assertions in listUnlinkNode to ensure the node membership
in the list to cover all list cases.

Signed-off-by: Uri Yagelnik <uriy@amazon.com>
Signed-off-by: uriyage <78144248+uriyage@users.noreply.github.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
Co-authored-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
secwallandViktor Söderqvist 1f5d891080 Drop lua object files on clean (#1812)
Let `make clean` delete the files `src/lua/*.o` and `src/lua/*.d`.

---------

Signed-off-by: secwall <secwall@yandex-team.ru>
2025-03-18 11:51:02 +01:00
Madelyn OlsonandViktor Söderqvist b091afc47a Stop running large memory test for address santizer (#1810)
After this change, https://github.com/valkey-io/valkey/pull/1767/files,
the address sanitizer test seems to be causing the test process to just
die, possibly from OOM:
https://github.com/valkey-io/valkey/actions/runs/13620692597/job/38069694391#step:6:4755.

If we switch the large memory tests to be sequential on ASAN, it might
resolve it, but I wanted to open this to propose reverting it to get the
tests passing again.

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2025-03-18 11:51:02 +01:00
zhaozhao.zzandViktor Söderqvist 06d7a561ae make net_input_bytes_curr_cmd more readable (#1756)
The metric `net_input_bytes_curr_cmd` is now computed by aggregating its components separately.

Signed-off-by: zhaozhao.zz <zhaozhao.zz@alibaba-inc.com>
2025-03-18 11:51:02 +01:00
eifrah-awsandViktor Söderqvist 0d4ef40768 Fixed build error with CMake (#1806)
Explicitly cast `long long` -> `long double` to avoid build warnings.
This issue manifests when using `clang 19`. Using the `Makefile` build,
we only get a warning. This small PR fixes this.

Fixes issue: https://github.com/valkey-io/valkey/issues/1805

Signed-off-by: Eran Ifrah <eifrah@amazon.com>
2025-03-18 11:51:02 +01:00
Jim BrunnerandViktor Söderqvist 732bb009bb Update and stabilize defrag tests (#1762)
A number of tests related to defrag have had stability problems.

One reason for stability issues is that the tests are run sequentially
on the same server, and it takes jemalloc some time to release freed
pages. This is especially noticed immediately after a flushall. Over a
period of 10 seconds, it is observable that the "fragmentation bytes"
can decrease by several MB. Another reason is that there's no
standardization between tests. For each test, people have been
independently hacking/tweaking the success criteria, without addressing
underlying issues.

This update revamps all of the defrag tests:
* A fresh server is started for each test. Running each test in
isolation improves stability.
* A uniform function `log_frag` is now used for debug logging
* A uniform function `perform_defrag_test` ensures that each test is
written and executed in a uniform fashion. Limits are imposed to ensure
that the defrag results are consistent/reproducible. The intent is to
eliminate failures do to various tweaks to values in individual tests.
* Latency is tested much more strictly for most tests, reflecting the
recent improvements to defrag latency.
* The test `defrag edge case` has been removed. This test attempted to
create N pages with EXACTLY equal fragmentation in an attempt to confuse
the defrag logic. It's unlikely that this test was performing correctly,
and had questionable value.
* Tests for hash/list/set/zset/stream have been separated and
standardized. It was unlikely that the old test was performing properly
as none of the actual data structures were fragmented!

It's noted that pubsub doesn't appear to be defragging correctly. The
old test was based on deletion of strings (only) which doesn't actually
reflect what happens when a pubsub channel is removed. The test has been
reduced to only check that pubsub is not damaged during defrag - but
doesn't test for defrag efficacy. This isn't likely a significant issue
as it would be unlikely to create many thousands of pubsub channels and
then have associated fragmentation issues.
https://github.com/valkey-io/valkey/issues/1774

Resolves: https://github.com/valkey-io/valkey/issues/1746

---------

Signed-off-by: Jim Brunner <brunnerj@amazon.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
Viktor Söderqvist 8cf8e3fb28 Fix hashTypeEntryDefrag returning bad pointer (#1799)
Fixes #1795.

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist bc8a114957 Add cluster-manual-failover-timeout to configure the timeout for manual failover (#1690)
Allows cluster admins to configure the cluster manual failover timeout
as
needed, admins can configure how long a primary would be paused in the
worst case scenario such as a failover timed out due to the insufficient
votes.

The configuration name is cluster-manual-failover-timeout, the unit is
milliseconds, and the range is [1, INT_MAX]ms.

---------

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
Ricardo DiasandViktor Söderqvist b2ffab404d Fix memory leak in test_quicklist.c unit test (#1797)
In last daily run we had the following failure:

```
test — compress and decomress quicklist plain node large than UINT32_MAX
[test_quicklistCompressAndDecomressQuicklistPlainNodeLargeThanUINT32MAX - unit/test_quicklist.c:2288] Compress and decompress: 4096 MB in 39.27 seconds.

[ok] - test_quicklist.c:test_quicklistCompressAndDecomressQuicklistPlainNodeLargeThanUINT32MAX
[END] - test_quicklist.c: 58 tests, 58 passed, 0 failed
[START] - test_rax.c
[test_raxRandomWalk - unit/test_rax.c:548] Failed assertion: raxAllocSize(t) == zmalloc_used_memory()
[fail] - test_rax.c:test_raxRandomWalk
```

Job Link:
https://github.com/valkey-io/valkey/actions/runs/13555915665/job/37890070586

Although the assert that failed was in a `test_rax.c` test function, the
problem was in the last test function from the `test_quicklist.c` unit
test that ran just before the test that failed.

The

`test_quicklistCompressAndDecomressQuicklistPlainNodeLargeThanUINT32MAX`
test function was not freeing the string allocated in the beginning of
the test.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
2025-03-18 11:51:02 +01:00
Madelyn OlsonandViktor Söderqvist 1eebcd5f81 Consistent look and feel of licenses (#1788)
Use a consistent set of licenses for Valkey files. I took a look and
applied sort of a "did we make a material change in this file?" and
tried to be conservative in adding the trademark. We could also be
liberal as well.

Resolves: https://github.com/valkey-io/valkey/issues/1692.

Included documentation about the licensing here:
https://github.com/valkey-io/valkey/pull/1787.

Licenses are now also always explicitly first, even about documentation
files.

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2025-03-18 11:51:02 +01:00
a1473af68f Fix clang build error in bitops.c (#1794)
In a recent PR https://github.com/valkey-io/valkey/pull/1741 the new
header `<immintrin.h>` was added, which transitively includes
`<mm_malloc.h>` header, where a function called `_mm_malloc(...)` makes
a call to the `malloc` function.

The Valkey server code explicitly sets the malloc function as a
deprecated function in `server.h`:
```c
void *malloc(size_t size) __attribute__((deprecated));
```

The Valkey server code is then compiled with
`-Werror=deprecated-declarations` option to detect the uses of
deprecated functions like `malloc`, and due to this, when the `bitops.c`
file is compiled with Clang, fails with the following error:

```
In file included from bitops.c:33:
In file included from /usr/lib/llvm-18/lib/clang/18/include/immintrin.h:26:
In file included from /usr/lib/llvm-18/lib/clang/18/include/xmmintrin.h:31:
/usr/lib/llvm-18/lib/clang/18/include/mm_malloc.h:35:12: error: 'malloc' is deprecated [-Werror,-Wdeprecated-declarations]
   35 |     return malloc(__size);
      |            ^
./server.h:3874:42: note: 'malloc' has been explicitly marked deprecated here
 3874 | void *malloc(size_t size) __attribute__((deprecated));
```

There is a difference in behavior though, between GCC and Clang. The
`bitops.c` file compiles successfully with GCC.

I don't know exactly why GCC does not issue a warning in this case. My
best guess is that GCC does not issue warnings from code of the standard
library.

To fix the build error in clang, we explicitly use `pragma` macro to
tell clang to ignore deprecated declarations warnings in `bitops.c`.

---------

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <rjd15372@gmail.com>
Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
2025-03-18 11:51:02 +01:00
Roshan KhatriandViktor Söderqvist fcd9f0ffac Migrate binaries build to ARM github runners (#1790)
This PR migrates the workflows to run on github hosted arm runners for each ubuntu version that is currently supported. It is much faster than the emulated version that used to take around 21 mins. Currently, it takes around 4-5 mins.

Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
2025-03-18 11:51:02 +01:00
Wen HuiandViktor Söderqvist c0a9643312 Add Cluster Bus Port out of range error message for Cluster Meet command (#1686)
Now for the cluster meet command, we first check the port and cport
number, and then the function clusterStartHandshake() is called.
In the function clusterStartHandshake(), ip address is checked first and
then port and cport range are checked.
Even port or cport range is out of bound, only error message "Invalid
node address specified" is reported.
This is not correct.

In this PR, I just make the port and cport check together before
clusterStartHandshake() function. Thus the port number and range can be
checked together.

One example:

Current behavior:

127.0.0.1:7000> cluster meet 10.21.96.98 65000
(error) ERR Invalid node address specified: 10.21.96.98:65000
127.0.0.1:7000> cluster meet 1928.2292.2983.3884.26622 6379
(error) ERR Invalid node address specified:
1928.2292.2983.3884.26622:6379

Whatever the wrong ip address or incorrect bus port number, user get the
same error message.

New behavior:

127.0.0.1:7000> cluster meet 10.21.96.98 65000
(error) ERR Cluster bus port number is out of range
127.0.0.1:7000> cluster meet 1928.2292.2983.3884.26622 6379
(error) ERR Invalid node address specified:
1928.2292.2983.3884.26622:6379

User can get much clearer error message.

Related note pr: https://github.com/valkey-io/valkey-doc/pull/240

---------

Signed-off-by: hwware <wen.hui.ware@gmail.com>
Signed-off-by: Wen Hui <wen.hui.ware@gmail.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
zhaozhao.zzandViktor Söderqvist 908f863870 cmd's out bytes need count deferred reply (#1760)
the special deferred reply is ignored in current command's
`net_output_bytes_curr_cmd` counting

Signed-off-by: zhaozhao.zz <zhaozhao.zz@alibaba-inc.com>
2025-03-18 11:51:02 +01:00
xbaselandViktor Söderqvist a681073365 valkey-cli: ensure output ends with a newline if missing when printing reply (#1782) 2025-03-18 11:51:02 +01:00
Ricardo DiasandViktor Söderqvist 2bdb021d93 Remove unicode optimization in Lua cjson library (#1785)
The Lua cjson library implements an optimization that pre-allocates a
string buffer by estimating the maximum memory used if all characters in
a string require to be represented as unicode escapes, which may take 6
bytes each. Therefore, if a string has `len` bytes, the pre-allocated
buffer will have `len * 6` bytes.

This optimization can easily cause OOM errors, because if someone uses a
string with a few gigabytes of size, the pre-allocator will require 6
times the size of that string, and when running the Lua script in a
small instance with low memory, it will make the valkey-server process
to abort.

I ran the following Lua script to check if there is a significant
performance regression:

```
local s = string.rep("a", 1024 * 1024 * 1024)
return #cjson.encode(s..s..s)
```

The execution duration, in seconds, for 3 runs before and after this
commit is the following:

Before: 46.309; 42.443; 42.242

After: 46.729; 42.969; 42.774

---------

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <rjd15372@gmail.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
Viktor Söderqvist 10c00687d8 Fix undefined behaviour in bitops unit test (#1786)
The unit test was added in #1741. It fails when compiled with UBSAN.
Using a local array like `char buf[size]` is undefined behaviour when
`size == 0`. This fix just makes it size 1 in this case.

The failure I got locally:

unit/test_bitops.c:28:13: runtime error: variable length array bound
evaluates to non-positive value 0

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
chzhooandViktor Söderqvist 6fc7ecb575 Optimize bitcount command by SIMD (#1741)
**Background**

- Currently, we implement bitcount using a lookup table method
- By SIMD, parallel table lookups can be achieved, which boosts
performance
- Most x86 servers support the AVX2 instruction set

**BenchMark**
| Value Size | QPS (After optimization) | QPS (Before optimization) |
change |
| ---- | ---- | ---- | ---- |
|16 B | 114925| 115924 |  -0.8%|
|256 B| 112619 | 112201|  +0.3%|
|4 KB| 105523|96251| +9.6%|
|64 KB|79723|36796| +116%|
|1MB|21306|3466|+514%|

CPU: AMD EPYC 9754 128-Core Processor * 8
OS:   Ubuntu Server 22.04 LTS 64bit
Memory: 16GB
VM: Tencent cloud SA5.2XLARGE16

**Test Plan**
Pending. Will add test if it looks okay

**Other**
This PR is based on
https://github.com/WojciechMula/sse-popcount/blob/master/popcnt-avx2-lookup.cpp

---------

Signed-off-by: chzhoo <czawyx@163.com>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist 5dd14d8f4d Fix temp file leak druing replication error handling (#1721)
Before actually entering REPL_STATE_TRANSFER, we usually have
some other things to do, such as registering the ae handler, etc.
If an error occurs at this time, we may leak the previously opened
temp file.

This commit adds a new cleanupTransferResources function to do the
cleanup, avoiding code duplication.

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
Jim BrunnerandViktor Söderqvist a9656d44cc defrag: remove assert on defrag_later (#1779)
If a flushall is performed in the middle of defrag, the current stage
will be aborted when the change in kvs is detected. However the defrag
cycle will continue and defrag will complete normally. In a normal
termination, we expect the `defrag_later` list to be empty - and it
might not be in this condition.

This update removes the incorrect assertion, allowing the list to be
freed just like for an abnormal termination.

Signed-off-by: Jim Brunner <brunnerj@amazon.com>
2025-03-18 11:51:02 +01:00
a8622d8f75 Enable TCP_NODELAY by default in incoming and outgoing connections (#1763)
Fixes https://github.com/valkey-io/valkey/issues/1758
This is a follow up issue from
https://github.com/valkey-io/valkey/pull/1706#issuecomment-2669567875


### Problem: 
The absence of TCP_NODELAY can cause unnecessary latency due to [Nagle’s
algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) (buffers
small TCP packets), which could be disabled for traffic.
We need to consider enabling TCP_NODELAY by default in both incoming and
outgoing connections
---------

Signed-off-by: Seungmin Lee <sungming@amazon.com>
Signed-off-by: Harkrishn Patro <harkrisp@amazon.com>
Co-authored-by: Seungmin Lee <sungming@amazon.com>
Co-authored-by: Harkrishn Patro <harkrisp@amazon.com>
2025-03-18 11:51:02 +01:00
Viktor Söderqvist 6bfd2abe00 Embed hash value in hash type entry (#1579)
For a hash type key represented as a hash table, embed the field and
value in one allocation when they fit together in an allocation of up to
128 bytes. For larger fields and values, another layout is used where
the value is a separately allocated sds string.

**Implementation**

The hashTypeEntry pointer is changed to be the field sds, i.e. a pointer
to the embedded field content, which is located after the sds header in
memory. We encode the entry layout in the some unused bits in the sds
header.

Entry with embedded field and value, used when they're both small. The
value is stored as SDS_TYPE_8. The field can use any SDS type.

    +--------------+---------------+
    | field        | value         |
    | hdr "foo" \0 | hdr8 "bar" \0 |
    +------^-------+---------------+
           |
           |
         entry pointer = field sds

Entry with value-pointer and embedded field, used for larger field-value
pairs. The field is SDS type 8 or higher.

    +-------+--------------+
    | value | field        |
    | ptr   | hdr "foo" \0 |
    +-------+------^-------+
                   |
                   |
                entry pointer = field sds

An embedded sds5 field implies the first entry layout.

Fixes #1551.

---------

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
Anastasia AlexandrovaandViktor Söderqvist c2c9d1a36d Fixed active-expire-effort description in conf file (#1773)
Expired keys search happens without user actions. Therefore, the
"interactively" word in the description of the active-expire-key
parameter is confusing and is changed to "incrementally."
 
modified:   valkey.conf

---------

Signed-off-by: Anastasia Alexadrova <anastasia.alexandrova@percona.com>
2025-03-18 11:51:02 +01:00
ranshidandViktor Söderqvist 17420f6540 Add large memory flag for asan tests (#1767)
Currently we do not use --large-memory flavor in our daily tests.
There is some value enabling this flavor in order to identify different issues related to buffer overrun and bad memory access.

---------

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2025-03-18 11:51:02 +01:00
Nikhil MangloreandViktor Söderqvist 67d4826522 Fixed issue with CONFIG RESETSTAT in cluster module message callback test (#1768)
**Problem**
The cluster module test "Cluster module message DING/DONG
acknowledgment" is failing despite successful message exchanges between
nodes. While these messages are being properly exchanged between the
nodes, running multiple tests on the same cluster instance is causing
issues with message statistics tracking. `CONFIG RESETSTAT` doesn't seem
to reset the stats under certain cases which causes the error as the
stats are adding onto each other from previous tests.

**Solution**
Remove some overlap with the previous test case so we don't need `CONFIG
RESETSTAT` between the test cases.

Resolves #1764

---------

Signed-off-by: Nikhil Manglore <nmanglor@amazon.com>
2025-03-18 11:51:02 +01:00
Viktor Söderqvist 0915747ff4 Disable Fedora Fawhide in Daily runs (#1769)
Because of a packaging problem of TCL in Fedora Rawhide (Fedora's
unstable branch), temporarily disable tests until the packaging is fixed
in the distro.

---------

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
secwallandViktor Söderqvist 55a5d5c53d Fix murmur32 on large strings (#1748)
Current murmur32 implementation fails on large strings.
Way to reproduce crash:
```
eval 'local s = string.rep("a", 1024 * 1024 * 1024) return #cjson.encode(s..s..s)' 0
```
(Basically this is a copy-paste from large test in
`tests/unit/scripting.tcl`).

Shouldn't we run tests with `--large-memory` on daily CI so we could
find this earlier?

I think we need to backport this to 8.1 branch.

Signed-off-by: secwall <secwall@yandex-team.ru>
2025-03-18 11:51:02 +01:00
cf89dd54e5 Fix error "SSL routines::bad length" when connTLSWrite is called second time with smaller buffer (#1737)
Issue described in #1135 

When call to `connTLSWrite` fails on next attempt `connTLSWritev` should
provide buffer with at least same amount of bytes,
but if first call was with buffer smaller than
`NET_MAX_WRITES_PER_EVENT` then buffer gets larger and exceed
`NET_MAX_WRITES_PER_EVENT`, `connTLSWritev` will not combine `iov` into
one buffer resulting into chance that first element of `iov` is smaller
than last failed call to `connTLSWrite` and causing `SSL routines::bad
length`.

This change force combining `iov`s into one buffer if first element of
`iov` is smaller than last failed write by `connTLSWrite`

---------

Signed-off-by: Marek Zoremba <marek@janeasystems.com>
Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
Signed-off-by: ranshid <ranshid@amazon.com>
Co-authored-by: Madelyn Olson <madelyneolson@gmail.com>
Co-authored-by: ranshid <ranshid@amazon.com>
2025-03-18 11:51:02 +01:00
743db67670 Move TCP/TLS specific options from generic client to connection type (#1706)
Fixes https://github.com/valkey-io/valkey/issues/1702

Signed-off-by: Seungmin Lee <sungming@amazon.com>
Co-authored-by: Seungmin Lee <sungming@amazon.com>
2025-03-18 11:51:02 +01:00
5b2ad0fe2c Add new module API flag to bypass command validation (#1357)
### Issue https://github.com/valkey-io/valkey/issues/1175
### Problem
Performance degradation occurs due to the sanity
check([lookupCommandByCString](https://github.com/valkey-io/valkey/blob/unstable/src/module.c#L3539))
in the VM_Replicate function, which converts const char* into sds and
free them for command dictionary lookup. This check is mainly used for
debugging purpose, but it is unnecessary for trusted modules. The user
seeks a way to bypass this check for performance gains.

### Solution
Introduce a new SKIP_COMMAND_VALIDATION option which allows individual
modules to opt out of command validation

### Test
ADded a unit test

---------

Signed-off-by: Seungmin Lee <sungming@amazon.com>
Co-authored-by: Seungmin Lee <sungming@amazon.com>
2025-03-18 11:51:02 +01:00
Ricardo DiasandViktor Söderqvist 475153931b Allow to specify the random number generator seed in unit tests (#1751)
In this commit, we add a new parameter to the unit tests binary, called
`--seed`, that allows to specify the seed used by the several random
numbers generators used in the Valkey server code.

We also now print the seed information right before starting the unit
tests execution, so that someone that needs to reproduce an error, can
use the same seed to re-run the failed tests.

---------

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
2025-03-18 11:51:02 +01:00
Nikhil MangloreandViktor Söderqvist 5a1141d344 Pass null-terminated node ID for VM_RegisterClusterMessageReceiver and add test coverage (#1708)
* Pass `sender_id` as `NULL` terminated string as part of
`ValkeyModuleClusterMessageReceiver` for ease of usage by the module(s).

* Implement test coverage described in #1656 and ensures that nodes in
the cluster module properly acknowledge a "DING" message by sending a
"DONG" response.

Closes #1656.

---------

Signed-off-by: Nikhil Manglore <nmanglor@amazon.com>
2025-03-18 11:51:02 +01:00
skyfireleeandViktor Söderqvist e449cc9752 Fix hashtable memory leak and kvstore overhead_hashtable_lut assert (#1750)
When releasing a kvstore and its hashtables, if it is still rehashed and
table 0 has no data, it may still have allocated buckets that were not
released. This lead to allocated hashtable buckets being leaked. This
looked like missed statistics but it was actually a memory leak.

fix: https://github.com/valkey-io/valkey/issues/1657

---------

Signed-off-by: artikell <739609084@qq.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
Viktor Söderqvist 65d31e744d Comment out assert in kvstore for overhead lut (#1745)
Until we found out why the assert is flaky, let's comment it out. We can
live with this overhead lut being slightly wrong, but we don't want the
assert to abort the program.

Fixes #1657.

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-03-18 11:51:02 +01:00
bbd96ab81f Fix raxRemove crash at memcpy() due to key size exceeds max Rax size (#1722)
Fix raxRemove crash at memcpy() (line 1181) due to key size exceeds
`RAX_NODE_MAX_SIZE`. Note that this could happen when key size was more
than 512MB if we allow it by increasing the default
`proto-max-bulk-len`. The crash could happen when we recompress the rax
after removing a key due to expiry or DEL while memcpy() merge the key
that exceed 512MB limit. While the counting phase has the size check,
the actual compress logic is missing it which lead to this crash.

---------

Signed-off-by: Ram Prasad Voleti <ramvolet@amazon.com>
Co-authored-by: Ram Prasad Voleti <ramvolet@amazon.com>
2025-03-18 11:51:02 +01:00
ranshidandViktor Söderqvist 0ccc15fd0b Explicitly use github arm runners for ARM release (#1742)
In the last 8.1.0-rc1 release attempt we identified that the cross
compilation on x86 for ARM is broken
(example:
https://github.com/valkey-io/valkey/actions/runs/13324096504/job/37225952044)

This PR sets use of arm runners during release binaries.

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2025-03-18 11:51:02 +01:00
Madelyn OlsonandViktor Söderqvist 699d8c64d3 Add a daily test running for ARM (#1738)
Use official arm github runners to verify the ARM build as part of daily run. Also, updated the daily notify job failure list to a natural yaml list.
---------

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2025-03-18 11:51:02 +01:00
Ray CaoandViktor Söderqvist 1691a000ef Modify parameter of clientMatchesFilter function. (#1733)
small change to [#1401](https://github.com/valkey-io/valkey/pull/1401/).
pass `clientFilter *` to clientMatchesFilter as suggest in
[comment](https://github.com/valkey-io/valkey/pull/1401/commits/6bc64ca5373a9c53a65529ac3999a87e327f7d03#r1875844273).

Signed-off-by: Ray Cao <zisong.cw@alibaba-inc.com>
2025-03-18 11:51:02 +01:00
BinbinandViktor Söderqvist c8687e7326 Simplify isNodeAvailable function and add comments (#994)
Use getNodeReplicationOffset to replace the original logic
and add the corresponding annotations.

Signed-off-by: Binbin <binloveplay1314@qq.com>
2025-03-18 11:51:02 +01:00
04e7c62ae6 Apply better sorting to the release notes thank-you section (#1735)
Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
Signed-off-by: ranshid <ranshid@amazon.com>
Co-authored-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2025-02-16 08:50:51 +02:00
ranshidandGitHub 0010dc7d3c Valkey release 8.1.0-rc1 (#1713)
This is the PR for 8.1.0 release. 
It includes: 
1. Release note changes
2. version changes

---------

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
2025-02-14 08:49:17 +02:00
92 changed files with 2116 additions and 1092 deletions
@@ -21,15 +21,15 @@
},
{
"arch": "arm64",
"target": "ubuntu20.04",
"target": "ubuntu-22.04-arm",
"type": "deb",
"platform": "focal"
"platform": "jammy"
},
{
"arch": "arm64",
"target": "ubuntu22.04",
"target": "ubuntu-24.04-arm",
"type": "deb",
"platform": "jammy"
"platform": "noble"
}
]
}
@@ -35,7 +35,7 @@ jobs:
build-valkey:
# Capture source tarball and generate checksum for it
name: Build package ${{ matrix.distro.target }} ${{ matrix.distro.arch }}
runs-on: "ubuntu-latest"
runs-on: ${{matrix.distro.target}}
strategy:
fail-fast: false
matrix: ${{ fromJSON(inputs.build_matrix) }}
@@ -51,13 +51,11 @@ jobs:
aws-region: ${{ inputs.region }}
role-to-assume: ${{ secrets.role_to_assume }}
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y build-essential libssl-dev libsystemd-dev
- name: Make Valkey
uses: uraimo/run-on-arch-action@v2
with:
arch: aarch64
distro: ${{matrix.distro.target}}
install: apt-get update && apt-get install -y build-essential libssl-dev libsystemd-dev
run: make -C src all BUILD_TLS=yes USE_SYSTEMD=yes
run: make -C src all BUILD_TLS=yes USE_SYSTEMD=yes
- name: Create Tarball and SHA256sums
run: |
+94 -11
View File
@@ -12,10 +12,10 @@ on:
inputs:
skipjobs:
description: "jobs to skip (delete the ones you wanna keep, do not leave empty)"
default: "valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,rpm-distros,malloc,specific,fortify,reply-schema"
default: "valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,rpm-distros,malloc,specific,fortify,reply-schema,arm"
skiptests:
description: "tests to skip (delete the ones you wanna keep, do not leave empty)"
default: "valkey,modules,sentinel,cluster,unittest"
default: "valkey,modules,sentinel,cluster,unittest,large-memory"
test_args:
description: "extra test arguments"
default: ""
@@ -79,6 +79,48 @@ jobs:
if: true && !contains(github.event.inputs.skiptests, 'unittest')
run: ./src/valkey-unit-tests --accurate
test-ubuntu-arm:
runs-on: ubuntu-24.04-arm
if: |
(github.event_name == 'workflow_dispatch' ||
(github.event_name == 'schedule' && github.repository == 'valkey-io/valkey') ||
(github.event_name == 'pull_request' && (contains(github.event.pull_request.labels.*.name, 'run-extra-tests') || github.event.pull_request.base.ref != 'unstable'))) &&
(!contains(github.event.inputs.skipjobs, 'ubuntu') || !contains(github.event.inputs.skipjobs, 'arm'))
timeout-minutes: 14400
steps:
- name: prep
if: github.event_name == 'workflow_dispatch'
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
echo "skiptests: ${{github.event.inputs.skiptests}}"
echo "test_args: ${{github.event.inputs.test_args}}"
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: make
run: make all-with-unit-tests SERVER_CFLAGS='-Werror'
- name: testprep
run: sudo apt-get install tcl8.6 tclx
- name: test
if: true && !contains(github.event.inputs.skiptests, 'valkey')
run: ./runtest ${{ github.event_name != 'pull_request' && '--accurate' || '' }} --verbose --dump-logs ${{github.event.inputs.test_args}}
- name: module api test
if: true && !contains(github.event.inputs.skiptests, 'modules')
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
- name: sentinel tests
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
- name: cluster tests
if: true && !contains(github.event.inputs.skiptests, 'cluster')
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
- name: unittest
if: true && !contains(github.event.inputs.skiptests, 'unittest')
run: ./src/valkey-unit-tests --accurate
test-ubuntu-jemalloc-fortify:
runs-on: ubuntu-latest
if: |
@@ -675,7 +717,13 @@ jobs:
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
- name: unittest
if: true && !contains(github.event.inputs.skiptests, 'unittest')
run: ./src/valkey-unit-tests
run: ./src/valkey-unit-tests --large-memory
- name: large memory tests
if: true && !contains(github.event.inputs.skiptests, 'valkey') && !contains(github.event.inputs.skiptests, 'large-memory')
run: ./runtest --accurate --verbose --dump-logs --large-memory --tags large-memory ${{github.event.inputs.test_args}}
- name: large memory module api tests
if: true && !contains(github.event.inputs.skiptests, 'modules') && !contains(github.event.inputs.skiptests, 'large-memory')
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --large-memory --tags large-memory ${{github.event.inputs.test_args}}
test-sanitizer-undefined:
runs-on: ubuntu-latest
@@ -725,7 +773,13 @@ jobs:
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
- name: unittest
if: true && !contains(github.event.inputs.skiptests, 'unittest')
run: ./src/valkey-unit-tests --accurate
run: ./src/valkey-unit-tests --accurate --large-memory
- name: large memory tests
if: true && !contains(github.event.inputs.skiptests, 'valkey') && !contains(github.event.inputs.skiptests, 'large-memory')
run: ./runtest --accurate --verbose --dump-logs --large-memory --tags large-memory ${{github.event.inputs.test_args}}
- name: large memory module api tests
if: true && !contains(github.event.inputs.skiptests, 'modules') && !contains(github.event.inputs.skiptests, 'large-memory')
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --large-memory --tags large-memory ${{github.event.inputs.test_args}}
test-sanitizer-force-defrag:
runs-on: ubuntu-latest
@@ -794,8 +848,8 @@ jobs:
install_epel: true
- name: test-fedoralatest-jemalloc
container: fedora:latest
- name: test-fedorarawhide-jemalloc
container: fedora:rawhide
# - name: test-fedorarawhide-jemalloc
# container: fedora:rawhide
name: ${{ matrix.name }}
runs-on: ubuntu-latest
@@ -860,8 +914,8 @@ jobs:
install_epel: true
- name: test-fedoralatest-tls-module
container: fedora:latest
- name: test-fedorarawhide-tls-module
container: fedora:rawhide
# - name: test-fedorarawhide-tls-module
# container: fedora:rawhide
name: ${{ matrix.name }}
runs-on: ubuntu-latest
@@ -932,8 +986,8 @@ jobs:
install_epel: true
- name: test-fedoralatest-tls-module-no-tls
container: fedora:latest
- name: test-fedorarawhide-tls-module-no-tls
container: fedora:rawhide
# - name: test-fedorarawhide-tls-module-no-tls
# container: fedora:rawhide
name: ${{ matrix.name }}
runs-on: ubuntu-latest
@@ -1262,7 +1316,36 @@ jobs:
notify-about-job-results:
runs-on: ubuntu-latest
if: always() && github.event_name == 'schedule' && github.repository == 'valkey-io/valkey'
needs: [test-ubuntu-jemalloc, test-ubuntu-jemalloc-fortify, test-ubuntu-libc-malloc, test-ubuntu-no-malloc-usable-size, test-ubuntu-32bit, test-ubuntu-tls, test-ubuntu-tls-no-tls, test-ubuntu-io-threads, test-ubuntu-tls-io-threads, test-ubuntu-reclaim-cache, test-valgrind-test, test-valgrind-misc, test-valgrind-no-malloc-usable-size-test, test-valgrind-no-malloc-usable-size-misc, test-sanitizer-address, test-sanitizer-undefined, test-sanitizer-force-defrag, test-rpm-distros-jemalloc, test-rpm-distros-tls-module, test-rpm-distros-tls-module-no-tls, test-macos-latest, test-macos-latest-sentinel, test-macos-latest-cluster, build-macos, test-freebsd, test-alpine-jemalloc, test-alpine-libc-malloc, reply-schemas-validator]
needs:
- test-ubuntu-jemalloc
- test-ubuntu-arm
- test-ubuntu-jemalloc-fortify
- test-ubuntu-libc-malloc
- test-ubuntu-no-malloc-usable-size
- test-ubuntu-32bit
- test-ubuntu-tls
- test-ubuntu-tls-no-tls
- test-ubuntu-io-threads
- test-ubuntu-tls-io-threads
- test-ubuntu-reclaim-cache
- test-valgrind-test
- test-valgrind-misc
- test-valgrind-no-malloc-usable-size-test
- test-valgrind-no-malloc-usable-size-misc
- test-sanitizer-address
- test-sanitizer-undefined
- test-sanitizer-force-defrag
- test-rpm-distros-jemalloc
- test-rpm-distros-tls-module
- test-rpm-distros-tls-module-no-tls
- test-macos-latest
- test-macos-latest-sentinel
- test-macos-latest-cluster
- build-macos
- test-freebsd
- test-alpine-jemalloc
- test-alpine-libc-malloc
- reply-schemas-validator
steps:
- name: Collect job status
run: |
@@ -0,0 +1,50 @@
name: Trigger Build Release
on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: Version of Valkey to build
required: true
environment:
description: Environment to build
required: true
type: choice
options:
- dev
- prod
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- name: Determine version and environment
id: determine-vars
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo "Triggered by a release event."
VERSION=${{ github.event.release.tag_name }}
ENVIRONMENT="prod"
else
echo "Triggered manually (workflow_dispatch)."
VERSION=${{ inputs.version }}
ENVIRONMENT=${{ inputs.environment }}
fi
# Set the outputs for version and environment
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT
- name: Trigger build
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # Version 3
with:
token: ${{ secrets.AUTOMATION_PAT }}
repository: ${{ github.repository_owner }}/valkey-release-automation
event-type: build-release
client-payload: >
{
"version": "${{ steps.determine-vars.outputs.version }}",
"environment": "${{ steps.determine-vars.outputs.environment }}"
}
+194 -11
View File
@@ -1,16 +1,199 @@
Hello! This file is just a placeholder, since this is the "unstable" branch
of Valkey, the place where all the development happens.
Valkey 8.1 release notes
========================
--------------------------------------------------------------------------------
Upgrade urgency levels:
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.
--------------------------------------------------------------------------------
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.
================================================================================
Valkey 8.1.0 GA - Released Mon 31 March 2025
================================================================================
Upgrade urgency LOW: This is the first release of Valkey 8.1,
a minor version update designed to further enhance performance, reliability, observability and usability
over Valkey 8.0 for all Valkey installations. This release is fully compatible with all previous Valkey releases
as well as Redis OSS 7.2.4.
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:
Behavior Changes
================
* Hide input buffer data from being logged on protocol error when hide-user-data-from-log is enabled (#1889)
https://valkey.io/download/
Bug fixes
=========
* Fix a bug in VM_GetCurrentUserName which leads to engine crash when no valid username provided (#1885)
More information is available at https://valkey.io
================================================================================
Valkey 8.1.0 RC2 - Released Thu 20 March 2025
================================================================================
Upgrade urgency LOW: This is the second release candidate of Valkey 8.1, with several bug fixes,
control on manual-failover timeout and expended module API to reduce module executed commands overhead.
Happy hacking!
Performance/Efficiency Improvements - Core
==========================================
* Optimize bitcount command by using x86 SIMD instructions (#1741)
* Embed hash value in hash data type entries to reduce memory footprint (#1579)
Cluster modifications
====================
* Add cluster-manual-failover-timeout configuration to control the timeout for manual failover (#1690)
* Improve error message reporting when invalid port is provided for cluster meet command. (#1686)
* broadcast epoch ASAP when configEpoch changed (#1813)
Module Improvements
===================
* Add new module API flag to bypass command validation in order to reduce processing overhead (#1357)
Behavior Changes
================
* Enable TCP_NODELAY for engine initiated cluster and replication connections (#1763)
Bug Fixes
=========
* Fix `ACL LOAD` crash on a connected replica node (#1842)
* Fix bug where no tracking-redir-broken is issued when the redirect client is in the process of getting closed. (#1823)
* Fix replica sometimes disconnecting when replication is using TLS. (#1737)
* Fix file descriptor leak when aborting dual channel replication due to error (#1721)
* Fix rax crash when using keys larger than 512MB (#1722)
* Fix RANDOMKEY command leading to infinite loop during when all CLIENT are PAUSED and all keys are with expiry (#1850)
* Removing unicode optimization in Lua cjson library to avoid OOM when very large strings are used. (#1785)
* Fix update large-reply in COMMANDLOG when reply is deferred (#1760)
* Avoid setting TCP/TLS specific options for UNIX Domain Socket connections (#1706)
* Fix a bug in the valkey-cli which would incorrectly render commands with text output in multi/exec (#1782)
Build and Packaging changes
=================================
* Check both arm64 and aarch64 for ARM based system architecture during CMake builds (#1829)
* Cleanup lua object files on make distclean (#1812)
* Fixed build error with CMake when using clang v19 (#1806)
================================================================================
Valkey 8.1.0 RC1 - Released Thu 11 Feb 2025
================================================================================
Upgrade urgency LOW: This is the first release candidate of Valkey 8.1, with
performance improvements, extended observability and cluster improvements and different bug fixes.
It includes a new implementation of the Valkey dictionary which is more memory and cache efficient,
better performance for encryption in transit, reduced replication overhead by offloading work to I/O threads,
faster failover support in cluster mode, major improvements to the active defrag process to reduce the impact on command processing,
different API changes for improved usability and ability to track large requests and replies.
Valkey now supports new new check-and-set feature for native STRINGs.
API and Interface changes
=========================
* Introduce cancel argument to bgsave command (#757)
* Add conditional update support to the `SET` command using `IFEQ` argument (#1324)
* Add more filters to `CLIENT LIST` (#1401)
* Add `availability_zone` to the HELLO response (#1487)
Observability and Monitoring changes
====================================
* Extend `LATENCY LATEST` to add sum / cnt stats (#1570)
* Add `paused_actions` and `paused_timeout_milliseconds` for `INFO CLIENTS` (#1519)
* Add paused_reason to `INFO CLIENTS` (#1564)
* Added `COMMANDLOG` to record slow executions and large requests/replies (#1294)
* Fix cluster info sent stats for message with light header (#1563)
* Add latency stats around cluster config file operations (#1534)
* Add new flag in `CLIENT LIST` for import-source client (#1398)
* Show client capabilities in `CLIENT LIST` / `CLIENT INFO` (#1698)
Performance/Efficiency Improvements - Core
==========================================
* Introduce a new memory efficient hash table to store keys (#1186)
* Accelerate hash table iterator with prefetching (#1501)
* Accelerate hash table iterator with value prefetching (#1568)
* Replace dict with new hashtable: hash datatype (#1502)
* Replace dict with new hashtable for sets datatype (#1176)
* Replace dict with new hashtable: sorted set datatype (#1427)
* Free strings during BGSAVE/BGAOFRW to reduce copy-on-write (#905)
* Create an empty lua table with specified initial capacity as much as possible (#1092)
* Move prepareClientToWrite out of loop for HGETALL command (#1119)
* Improved hashing algorithm for Lua tables (#1168)
* Replace dict with new hashtable for sets datatype (#1176)
* Do security attack check only when command not found to reduce the critical path. (#1212)
* Trim free space from inline command argument strings to avoid excess memory usage (#1213)
* Increase the max number of io threads to 256. (#1220)
* Refactor of ActiveDefrag to reduce latencies (#1242)
* Integrate fast_float to optionally replace strtod (#1260)
* Improvements for TLS with I/O threads (#1271)
* Optimize PFCOUNT, PFMERGE command by SIMD acceleration (#1293)
* Optimize sdscatrepr by batch processing printable characters (#1342)
* Optimize ZRANK to avoid path comparisons (#1389)
* Move clientCron onto a separate timer (#1387)
* Client struct: lazy init components and optimize struct layout (#1405)
* Offload reading the replication stream to IO threads (#1449)
* Skip CRC checksumming during diskless full sync with TLS enabled. (#1479)
New/Modified configurations
===========================
* Deprecate `io-threads-do-reads`, which has no effect since io threads will now always do reads. (#1138)
* Introduce `import-mode` config to avoid expiration and eviction during data syncing (#1185)
* Introduce new `rdb-version-check` config which allows for relaxed RDB version verification (#1604)
* Deprecate `dynamic-hz`, since server cron jobs are handled dynamically by default (#1387)
* Introduce `log-format` and `log-timestamp-format` to control the log format (#1022)
* Introducing `active-defrag-cycle-us` for more fine-grinned control of memory defragmentation run time (#1242)
* Introduce new configurations to control the new `COMMANDLOG` reporting thresholds (#1294)
Build and Packaging changes
=================================
* Introduce CMake build system for valkey (#1196)
* RDMA builtin support (#1209)
* Fix Valkey binary build workflow, version support changes. (#1429)
* Remove Valkey specific changes in jemalloc source code (#1266)
Module Improvements
===================
* Add API UpdateRuntimeArgs for updating the module arguments during runtime (#1041)
* Add support for MustObeyClient Module API (#1582)
* Adds support for scripting engines as Valkey modules (#1277, #1497)
Cluster improvements
====================
* Do election in order based on failed primary rank to avoid voting conflicts (#1018)
* Make replica `CLUSTER RESET` flush async based on `lazyfree-lazy-user-flush` (#1190)
* Trigger the election as soon as possible when doing a forced manual failover (#1067)
* Make manual failover reset the on-going election to promote failover (#1274)
* Broadcast a PONG to all node in cluster when role changed (#1295)
* Manual failover vote is not limited by two times the node timeout (#1305)
* Automatic failover vote is not limited by two times the node timeout (#1356)
Behavior Changes
================
* Streams use an additional 8 bytes to track their internal size (#688)
* Take hz into account in activerehashing to avoid CPU spikes (#977)
* Incr `expired_keys` if the expiration time is already expired (#1517)
* Fix replica not able to initiate election in time when epoch fails (#1009)
* Make `FUNCTION RESTORE FLUSH` flush async based on `lazyfree-lazy-user-flush` (#1254)
* Allow `MEMORY MALLOC-STATS` and `MEMORY PURGE` during loading phase (#1317)
* Use `DEEPBIND` flag when loading external modules in order to avoid symbol conflicts (#1703)
Logging and Tooling Improvements
================================
* Remove the restriction that cli --cluster create requires at least 3 primary nodes (#1075)
* Add short client info log to CLUSTER MEET / FORGET / RESET commands (#1249)
* Support for reading from replicas in valkey-benchmark (#1392)
* valkey-cli will now re-select previously selected database after reconnect (#1694)
* valkey-cli will now auto-exit from subscribed mode when there are no more active subscriptions (#1432)
Bug Fixes
=========
* Mark the node as FAIL when the node is marked as NOADDR and broadcast the FAIL (#1191)
* [Bug Fix] Optimize RDB Load Performance and Fix Cluster Mode Resizing (#1199)
* Log as primary role (M) instead of child process (C) during startup (#1282)
* Fix empty primary may have dirty slots data due to bad migration (#1285)
* RDMA: Fix dead loop when transfer large data (20KB) (#1386)
We appreciate the efforts of all who contributed code to this release!
Alan Scherger (flyinprogrammer), Amit Nagler (naglera), Anastasia Alexandrova (nastena1606), Basel Naamna (xbasel), Ben Totten (bentotten), Benson-li (li-benson),
Binbin (enjoy-binbin), Bogdan Petre (bogdanp05), chzhoo, Caiyi Wu (Codebells), Danish Mehmood (danish-mehmood), Eran Ifrah (eifrah-aws), Guillaume Koenig (knggk),
Harkrishn Patro (hpatro), Jacob Murphy (murphyjacob4), jeon1226, Jim Brunner (JimB123), Josef Šimánek (simi), Jungwoo Song (bluayer), Karthick Ariyaratnam (karthyuom),
Karthik Subbarao (KarthikSubbarao), kronwerk, Lipeng Zhu (lipzhu), Madelyn Olson (madolson), Marek Zoremba (zori-janea), Masahiro Ide (imasahiro), Meinhard Zhou (MeinhardZhou), Melroy van den Berg (melroy89), Mikhail Koviazin (mkmkme), Nadav Gigi (NadavGigi), Nadav Levanoni (nadav-levanoni), Nikhil Manglore (Nikhil-Manglore), Parth Patel (parthpatel), Pierre (pieturin), Ping Xie (PingXie), Qu Chen (QuChen88), Rain Valentine (SoftlyRaining), Ray Cao (RayaCoo), Ran Shidlansik (ranshid), Ray Cao (RayaCoo), Ricardo Dias (rjd15372), Romain Geissler (Romain-Geissler-1A), Roman Gershman (romange), Roshan Khatri (roshkhatri), Rueian (rueian), Sarthak Aggarwal (sarthakaggarwal97), Seungmin Lee (sungming2),
Shai Zarka (zarkash-aws), Shivshankar (Shivshankar-Reddy), Simon Baatz (gmbnomis), Sinkevich Artem (ArtSin), Stav Ben-Tov (stav-bentov), Stefan Mueller (muelstefamzn), secwall,
Tal Shachar (talxsha), Thalia Archibald (thaliaarchi), Uri Yagelnik (uriyage), Vadym Khoptynets (poiuj), Vanessa Tang (YueTang-Vanessa), Viktor Söderqvist (zuiderkwast), Viktor Szépe (szepeviktor), VoletiRam,
Vu Diep (vudiep411), Wen Hui (hwware), WelongZuo, Yanqi Lv (lyq2333), Yury Fridlyand (Yury-Fridlyand), Zvi Schneider (zvi-code), bodong.ybd (yangbodong22011), chx9 (chx9), otheng (otheng03), skyfirelee (artikell), Shawn Wang (xingbowang), Xuyang WANG (Nugine), zhaozhao.zz (soloestoy), zhenwei pi (pizhenwei), zixuan zhao (azuredream), 烈香 (hengyoush),
风去幽墨 (fengquyoumo)
+2
View File
@@ -1,5 +1,7 @@
# License 1
SPDX-License-Identifier: BSD-3-Clause
BSD 3-Clause License
Copyright (c) 2024-present, Valkey contributors
+1 -1
View File
@@ -240,7 +240,7 @@ endif ()
set(BUILDING_ARM64 0)
set(BUILDING_ARM32 0)
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64")
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
set(BUILDING_ARM64 1)
endif ()
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "../fast_float/fast_float.h"
+3 -3
View File
@@ -81,9 +81,9 @@ uint32_t murmur32(const uint8_t* key, size_t len, uint32_t seed) {
static const uint32_t n = 0xe6546b64;
uint32_t hash = seed;
const int nblocks = len / 4;
const size_t nblocks = len / 4;
const uint32_t* blocks = (const uint32_t*) key;
for (int i = 0; i < nblocks; i++) {
for (size_t i = 0; i < nblocks; i++) {
uint32_t k = blocks[i];
k *= c1;
k = (k << r1) | (k >> (32 - r1));
@@ -116,7 +116,7 @@ uint32_t murmur32(const uint8_t* key, size_t len, uint32_t seed) {
hash ^= (hash >> 16);
return hash;
}
}
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
GCObject *o;
+4 -8
View File
@@ -469,13 +469,9 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
str = lua_tolstring(l, lindex, &len);
/* Worst case is len * 6 (all unicode escapes).
* This buffer is reused constantly for small strings
* If there are any excess pages, they won't be hit anyway.
* This gains ~5% speedup. */
if (len > SIZE_MAX / 6 - 3)
if (len > SIZE_MAX - 3)
abort(); /* Overflow check */
strbuf_ensure_empty_length(json, len * 6 + 2);
strbuf_ensure_empty_length(json, len + 2);
strbuf_append_char_unsafe(json, '\"');
for (i = 0; i < len; i++) {
@@ -483,9 +479,9 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
if (escstr)
strbuf_append_string(json, escstr);
else
strbuf_append_char_unsafe(json, str[i]);
strbuf_append_char(json, str[i]);
}
strbuf_append_char_unsafe(json, '\"');
strbuf_append_char(json, '\"');
}
/* Find the size of the array on the top of the Lua stack
+1 -1
View File
@@ -563,7 +563,7 @@ endif
commands.c: $(COMMANDS_DEF_FILENAME).def
clean:
rm -rf $(SERVER_NAME) $(ENGINE_SENTINEL_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME) $(ENGINE_CHECK_RDB_NAME) $(ENGINE_CHECK_AOF_NAME) $(ENGINE_UNIT_TESTS) $(ENGINE_LIB_NAME) unit/*.o unit/*.d *.o *.gcda *.gcno *.gcov valkey.info lcov-html Makefile.dep *.so
rm -rf $(SERVER_NAME) $(ENGINE_SENTINEL_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME) $(ENGINE_CHECK_RDB_NAME) $(ENGINE_CHECK_AOF_NAME) $(ENGINE_UNIT_TESTS) $(ENGINE_LIB_NAME) unit/*.o unit/*.d lua/*.o lua/*.d *.o *.gcda *.gcno *.gcov valkey.info lcov-html Makefile.dep *.so
rm -f $(DEP)
.PHONY: clean
+3
View File
@@ -2377,6 +2377,9 @@ static sds ACLLoadFromFile(const char *filename) {
listRewind(server.clients, &li);
while ((ln = listNext(&li)) != NULL) {
client *c = listNodeValue(ln);
/* Some clients, e.g. the one from the primary to replica, don't have a user
* associated with them. */
if (!c->user) continue;
user *original = c->user;
list *channels = NULL;
user *new_user = ACLGetUserByName(c->user->name, sdslen(c->user->name));
+15 -4
View File
@@ -31,6 +31,8 @@
#include <stdlib.h>
#include "adlist.h"
#include "serverassert.h"
#include "zmalloc.h"
/* Create a new list. The created list can be freed with
@@ -187,14 +189,23 @@ void listDelNode(list *list, listNode *node) {
* Remove the specified node from the list without freeing it.
*/
void listUnlinkNode(list *list, listNode *node) {
if (node->prev)
assert(list->len > 0);
if (node->prev) {
assert(node->prev->next == node);
node->prev->next = node->next;
else
} else {
assert(list->head == node);
list->head = node->next;
if (node->next)
}
if (node->next) {
assert(node->next->prev == node);
node->next->prev = node->prev;
else
} else {
assert(list->tail == node);
list->tail = node->prev;
}
node->next = NULL;
node->prev = NULL;
+2 -2
View File
@@ -1,8 +1,8 @@
/* Copyright 2024- Valkey contributors
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* This file implements allocator-specific defragmentation logic used
* within the Valkey engine. Below is the relationship between various
+7
View File
@@ -496,6 +496,9 @@ static int anetTcpGenericConnect(char *err, const char *addr, int port, const ch
continue;
}
/* Enable TCP_NODELAY by default */
anetEnableTcpNoDelay(NULL, s);
/* If we ended an iteration of the for loop without errors, we
* have a connected socket. Let's return to the caller. */
goto end;
@@ -684,6 +687,10 @@ int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port)
if (ip) inet_ntop(AF_INET6, (void *)&(s->sin6_addr), ip, ip_len);
if (port) *port = ntohs(s->sin6_port);
}
/* Enable TCP_NODELAY by default */
anetEnableTcpNoDelay(NULL, fd);
return fd;
}
+5 -2
View File
@@ -1,6 +1,5 @@
/*
* Copyright (c) 2009-2012, Redis Ltd.
* Copyright (c) 2024, Valkey contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,7 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
const char *ascii_logo =
" .+^+. \n"
" .+#########+. \n"
+117 -13
View File
@@ -29,26 +29,116 @@
*/
#include "server.h"
#ifdef HAVE_AVX2
/* Define __MM_MALLOC_H to prevent importing the memory aligned
* allocation functions, which we don't use. */
#define __MM_MALLOC_H
#include <immintrin.h>
#endif
/* -----------------------------------------------------------------------------
* Helpers and low level bit functions.
* -------------------------------------------------------------------------- */
/* Count number of bits set in the binary array pointed by 's' and long
* 'count' bytes. The implementation of this function is required to
* work with an input string length up to 512 MB or more (server.proto_max_bulk_len) */
long long serverPopcount(void *s, long count) {
static const unsigned char bitsinbyte[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2,
3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4,
3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4,
5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6,
6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
#ifdef HAVE_AVX2
/* The SIMD version of popcount enhances performance through parallel lookup tables which is based on the following article:
* https://arxiv.org/pdf/1611.07612 */
ATTRIBUTE_TARGET_AVX2
long long popcountAVX2(void *s, long count) {
long i = 0;
unsigned char *p = (unsigned char *)s;
long long bits = 0;
/* clang-format off */
const __m256i lookup = _mm256_setr_epi8(
/* First Lane [0:127] */
/* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2,
/* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3,
/* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3,
/* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4,
/* Second Lane [128:255] identical to first lane due to lane isolation in _mm256_shuffle_epi8.
* For more information, see following URL
* https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_shuffle_epi8 */
/* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2,
/* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3,
/* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3,
/* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4);
/* clang-format on */
const __m256i low_mask = _mm256_set1_epi8(0x0f);
__m256i acc = _mm256_setzero_si256();
/* Count 32 bytes per iteration. */
#define ITER_32_BYTES \
{ \
const __m256i vec = _mm256_loadu_si256((const __m256i *)(p + i)); \
const __m256i lo = _mm256_and_si256(vec, low_mask); \
const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(vec, 4), low_mask); \
const __m256i popcnt1 = _mm256_shuffle_epi8(lookup, lo); \
const __m256i popcnt2 = _mm256_shuffle_epi8(lookup, hi); \
local = _mm256_add_epi8(local, popcnt1); \
local = _mm256_add_epi8(local, popcnt2); \
i += 32; \
}
/* We divide the array into the following three parts
* Part A Part B Part C
* +-----------------+--------------+---------+
* | 8 * 32bytes * X | 32bytes * Y | Z bytes |
* +-----------------+--------------+---------+
*/
/* Part A: loop unrolling, processing 8 * 32 bytes per iteration. */
while (i + 8 * 32 <= count) {
__m256i local = _mm256_setzero_si256();
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
ITER_32_BYTES
acc = _mm256_add_epi64(acc, _mm256_sad_epu8(local, _mm256_setzero_si256()));
}
/* Part B: when the remaining data length is less than 8 * 32 bytes,
* process 32 bytes per iteration. */
__m256i local = _mm256_setzero_si256();
while (i + 32 <= count) {
ITER_32_BYTES;
}
acc = _mm256_add_epi64(acc, _mm256_sad_epu8(local, _mm256_setzero_si256()));
#undef ITER_32_BYTES
bits += _mm256_extract_epi64(acc, 0);
bits += _mm256_extract_epi64(acc, 1);
bits += _mm256_extract_epi64(acc, 2);
bits += _mm256_extract_epi64(acc, 3);
/* Part C: count the remaining bytes. */
for (; i < count; i++) {
bits += bitsinbyte[p[i]];
}
return bits;
}
#endif
/* The scalar version of popcount based on lookup tables. */
long long popcountScalar(void *s, long count) {
long long bits = 0;
unsigned char *p = s;
uint32_t *p4;
static const unsigned char bitsinbyte[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2,
3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4,
3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4,
5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6,
6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
/* Count initial bytes not aligned to 32 bit. */
while ((unsigned long)p & 3 && count) {
@@ -97,6 +187,20 @@ long long serverPopcount(void *s, long count) {
return bits;
}
/* Count number of bits set in the binary array pointed by 's' and long
* 'count' bytes. The implementation of this function is required to
* work with an input string length up to 512 MB or more (server.proto_max_bulk_len) */
long long serverPopcount(void *s, long count) {
#ifdef HAVE_AVX2
/* If length of s >= 256 bits and the CPU supports AVX2,
* we prefer to use the SIMD version */
if (count >= 32) {
return popcountAVX2(s, count);
}
#endif
return popcountScalar(s, count);
}
/* Return the position of the first bit set to one (if 'bit' is 1) or
* zero (if 'bit' is 0) in the bitmap starting at 's' and long 'count' bytes.
*
+7 -3
View File
@@ -26,9 +26,13 @@
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ---------------------------------------------------------------------------
*
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* API:
*
* blockClient() set the CLIENT_BLOCKED flag in the client, and set the
+26 -12
View File
@@ -303,12 +303,12 @@ static sds percentDecode(const char *pe, size_t len) {
/* Parse a URI and extract the server connection information.
* URI scheme is based on the provisional specification[1] excluding support
* for query parameters. Valid URIs are:
* scheme: "valkey://"
* scheme: "valkey://" or "valkeys://" or "redis://" or "rediss://"
* authority: [[<username> ":"] <password> "@"] [<hostname> [":" <port>]]
* path: ["/" [<db>]]
*
* [1]: https://www.iana.org/assignments/uri-schemes/prov/redis */
void parseRedisUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int *tls_flag) {
void parseUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int *tls_flag) {
#ifdef USE_OPENSSL
UNUSED(tool_name);
#else
@@ -428,19 +428,33 @@ sds cliVersion(void) {
}
/* This is a wrapper to call redisConnect or redisConnectWithTimeout. */
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv) {
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
return redisConnect(ip, port);
} else {
return redisConnectWithTimeout(ip, port, tv);
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv, int nonblock) {
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port);
if (tv.tv_sec || tv.tv_usec) {
options.connect_timeout = &tv;
}
if (nonblock) {
options.options |= REDIS_OPT_NONBLOCK;
}
return redisConnectWithOptions(&options);
}
/* This is a wrapper to call redisConnectUnix or redisConnectUnixWithTimeout. */
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv) {
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
return redisConnectUnix(path);
} else {
return redisConnectUnixWithTimeout(path, tv);
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv, int nonblock) {
redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path);
if (tv.tv_sec || tv.tv_usec) {
options.connect_timeout = &tv;
}
if (nonblock) {
options.options |= REDIS_OPT_NONBLOCK;
}
return redisConnectWithOptions(&options);
}
+3 -3
View File
@@ -45,7 +45,7 @@ sds *getSdsArrayFromArgv(int argc, char **argv, int quoted);
sds unquoteCString(char *str);
void parseRedisUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int *tls_flag);
void parseUri(const char *uri, const char *tool_name, cliConnInfo *connInfo, int *tls_flag);
void freeCliConnInfo(cliConnInfo connInfo);
@@ -53,7 +53,7 @@ sds escapeJsonString(sds s, const char *p, size_t len);
sds cliVersion(void);
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv);
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv, int nonblock);
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv, int nonblock);
#endif /* __CLICOMMON_H */
+11 -9
View File
@@ -26,7 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* cluster.c contains the common parts of a clustering
* implementation, the parts that are shared between
@@ -357,7 +361,6 @@ migrateCachedSocket *migrateGetSocket(client *c, robj *host, robj *port, long ti
sdsfree(name);
return NULL;
}
connEnableTcpNoDelay(conn);
/* Add to the cache and return it to the caller. */
cs = zmalloc(sizeof(*cs));
@@ -1341,16 +1344,15 @@ void addNodeToNodeReply(client *c, clusterNode *node) {
* not finished their initial sync, in failed state, or are
* otherwise considered not available to serve read commands. */
int isNodeAvailable(clusterNode *node) {
/* We don't consider PFAIL here because it's not a reliable indicator
* for node available and we don't want clients to use it. */
if (clusterNodeIsFailing(node)) {
return 0;
}
long long repl_offset = clusterNodeReplOffset(node);
if (clusterNodeIsMyself(node)) {
/* Nodes do not update their own information
* in the cluster node list. */
repl_offset = getNodeReplicationOffset(node);
}
return (repl_offset != 0);
/* Hide empty replicas in here, from a data-path POV, an empty replica
* is not available. */
return getNodeReplicationOffset(node) != 0;
}
void addNodeReplyForClusterSlot(client *c, clusterNode *node, int start_slot, int end_slot) {
+1 -2
View File
@@ -77,7 +77,7 @@ const char **clusterCommandExtendedHelp(void);
int clusterAllowFailoverCmd(client *c);
void clusterPromoteSelfToPrimary(void);
int clusterManualFailoverTimeLimit(void);
mstime_t clusterManualFailoverTimeLimit(void);
void clusterCommandSlots(client *c);
void clusterCommandMyId(client *c);
@@ -109,7 +109,6 @@ clusterNode *getNodeBySlot(int slot);
int clusterNodeClientPort(clusterNode *n, int use_tls);
char *clusterNodeHostname(clusterNode *node);
const char *clusterNodePreferredEndpoint(clusterNode *n, client *c);
long long clusterNodeReplOffset(clusterNode *node);
clusterNode *clusterLookupNode(const char *name, int length);
int detectAndUpdateCachedNodeHealth(void);
client *createCachedResponseClient(int resp);
+38 -26
View File
@@ -26,7 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* cluster_legacy.c contains the implementation of the cluster API that is
* specific to the standard, cluster-bus based clustering mechanism.
@@ -1493,7 +1497,7 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
connClose(conn);
return;
}
connEnableTcpNoDelay(conn);
connKeepAlive(conn, server.cluster_node_timeout / 1000 * 2);
/* Use non-blocking I/O for cluster messages. */
@@ -1934,7 +1938,7 @@ int clusterBumpConfigEpochWithoutConsensus(void) {
if (myself->configEpoch == 0 || myself->configEpoch != maxEpoch) {
server.cluster->currentEpoch++;
myself->configEpoch = server.cluster->currentEpoch;
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_FSYNC_CONFIG);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_FSYNC_CONFIG | CLUSTER_TODO_BROADCAST_ALL);
serverLog(LL_NOTICE, "New configEpoch set to %llu", (unsigned long long)myself->configEpoch);
return C_OK;
} else {
@@ -1997,7 +2001,7 @@ void clusterHandleConfigEpochCollision(clusterNode *sender) {
/* Get the next ID available at the best of this node knowledge. */
server.cluster->currentEpoch++;
myself->configEpoch = server.cluster->currentEpoch;
clusterSaveConfigOrDie(1);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_FSYNC_CONFIG | CLUSTER_TODO_BROADCAST_ALL);
serverLog(LL_NOTICE, "configEpoch collision with node %.40s (%s). configEpoch set to %llu", sender->name,
sender->human_nodename, (unsigned long long)myself->configEpoch);
}
@@ -2197,12 +2201,6 @@ static int clusterStartHandshake(char *ip, int port, int cport) {
return 0;
}
/* Port sanity check */
if (port <= 0 || port > 65535 || cport <= 0 || cport > 65535) {
errno = EINVAL;
return 0;
}
/* Set norm_ip as the normalized string representation of the node
* IP address. */
memset(norm_ip, 0, NET_IP_STR_LEN);
@@ -3045,7 +3043,10 @@ static void clusterProcessModulePacket(clusterMsgModule *module_data, clusterNod
uint32_t len = ntohl(module_data->len);
uint8_t type = module_data->type;
unsigned char *payload = module_data->bulk_data;
moduleCallClusterReceivers(sender->name, module_id, type, payload, len);
sds sender_name = sdsnewlen(sender->name, CLUSTER_NAMELEN);
moduleCallClusterReceivers(sender_name, module_id, type, payload, len);
sdsfree(sender_name);
}
static void clusterProcessLightPacket(clusterNode *sender, clusterLink *link, uint16_t type) {
@@ -3212,7 +3213,8 @@ int clusterProcessPacket(clusterLink *link) {
freeClusterLink(link);
serverLog(
LL_NOTICE,
"Closing link for node that sent a lightweight message of type %s as its first message on the link",
"Closing link for node %.40s that sent a lightweight message of type %s as its first message on the link",
hdr->sender,
clusterGetMessageTypeString(type));
return 0;
}
@@ -3716,9 +3718,9 @@ int clusterProcessPacket(clusterLink *link) {
/* Manual failover requested from replicas. Initialize the state
* accordingly. */
resetManualFailover();
server.cluster->mf_end = now + CLUSTER_MF_TIMEOUT;
server.cluster->mf_end = now + server.cluster_mf_timeout;
server.cluster->mf_replica = sender;
pauseActions(PAUSE_DURING_FAILOVER, now + (CLUSTER_MF_TIMEOUT * CLUSTER_MF_PAUSE_MULT),
pauseActions(PAUSE_DURING_FAILOVER, now + (server.cluster_mf_timeout * CLUSTER_MF_PAUSE_MULT),
PAUSE_ACTIONS_CLIENT_WRITE_SET);
serverLog(LL_NOTICE, "Manual failover requested by replica %.40s (%s).", sender->name, sender->human_nodename);
/* We need to send a ping message to the replica, as it would carry
@@ -4773,13 +4775,13 @@ void clusterFailoverReplaceYourPrimary(void) {
}
}
/* 3) Update state and save config. */
/* 3) Update state, note that we do not use CLUSTER_TODO_UPDATE_STATE since we want the node
* to update the cluster state ASAP after failover. */
clusterUpdateState();
clusterSaveConfigOrDie(1);
/* 4) Pong all the other nodes so that they can update the state
/* 4) Save and fsync the config, and pong all the other nodes so that they can update the state
* accordingly and detect that we switched to primary role. */
clusterDoBeforeSleep(CLUSTER_TODO_BROADCAST_ALL);
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_FSYNC_CONFIG | CLUSTER_TODO_BROADCAST_ALL);
/* 5) If there was a manual failover in progress, clear the state. */
resetManualFailover();
@@ -4962,6 +4964,7 @@ void clusterHandleReplicaFailover(void) {
/* Update my configEpoch to the epoch of the election. */
if (myself->configEpoch < server.cluster->failover_auth_epoch) {
myself->configEpoch = server.cluster->failover_auth_epoch;
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_FSYNC_CONFIG | CLUSTER_TODO_BROADCAST_ALL);
serverLog(LL_NOTICE, "configEpoch set to %llu after successful failover",
(unsigned long long)myself->configEpoch);
}
@@ -5089,7 +5092,7 @@ void clusterHandleReplicaMigration(int max_replicas) {
* setting mf_end to the millisecond unix time at which we'll abort the
* attempt.
* 2) Replica sends a MFSTART message to the primary requesting to pause clients
* for two times the manual failover timeout CLUSTER_MF_TIMEOUT.
* for CLUSTER_MF_PAUSE_MULT times the server.cluster_mf_timeout.
* When primary is paused for manual failover, it also starts to flag
* packets with CLUSTERMSG_FLAG0_PAUSED.
* 3) Replica waits for primary to send its replication offset flagged as PAUSED.
@@ -6219,7 +6222,7 @@ int checkSlotAssignmentsOrReply(client *c, unsigned char *slots, int del, int st
return C_ERR;
}
if (slots[slot]++ == 1) {
addReplyErrorFormat(c, "Slot %d specified multiple times", (int)slot);
addReplyErrorFormat(c, "Slot %d specified multiple times", slot);
return C_ERR;
}
}
@@ -6242,6 +6245,10 @@ void clusterUpdateSlots(client *c, unsigned char *slots, int del) {
}
}
/* Get the replication offset of a node.
*
* Nodes do not update their own information in the cluster state,
* so for self node, we cannot use `node->repl_offset` directly. */
long long getNodeReplicationOffset(clusterNode *node) {
if (node->flags & CLUSTER_NODE_MYSELF) {
return nodeIsReplica(node) ? replicationGetReplicaOffset() : server.primary_repl_offset;
@@ -6471,7 +6478,7 @@ clusterNode *getMyClusterNode(void) {
return server.cluster->myself;
}
int clusterManualFailoverTimeLimit(void) {
mstime_t clusterManualFailoverTimeLimit(void) {
return server.cluster->mf_end;
}
@@ -6894,6 +6901,10 @@ int clusterCommandSpecial(client *c) {
addReplyErrorFormat(c, "Invalid base port specified: %s", (char *)c->argv[3]->ptr);
return 1;
}
if (port <= 0 || port > 65535) {
addReplyErrorFormat(c, "Port number is out of range");
return 1;
}
if (c->argc == 5) {
if (getLongLongFromObject(c->argv[4], &cport) != C_OK) {
@@ -6904,6 +6915,11 @@ int clusterCommandSpecial(client *c) {
cport = port + CLUSTER_PORT_INCR;
}
if (cport <= 0 || cport > 65535) {
addReplyErrorFormat(c, "Cluster bus port number is out of range");
return 1;
}
if (clusterStartHandshake(c->argv[2]->ptr, port, cport) == 0 && errno == EINVAL) {
addReplyErrorFormat(c, "Invalid node address specified: %s:%s", (char *)c->argv[2]->ptr,
(char *)c->argv[3]->ptr);
@@ -7116,7 +7132,7 @@ int clusterCommandSpecial(client *c) {
return 1;
}
resetManualFailover();
server.cluster->mf_end = mstime() + CLUSTER_MF_TIMEOUT;
server.cluster->mf_end = mstime() + server.cluster_mf_timeout;
sds client = catClientInfoShortString(sdsempty(), c, server.hide_user_data_from_log);
if (takeover) {
@@ -7279,10 +7295,6 @@ char *clusterNodeHostname(clusterNode *node) {
return node->hostname;
}
long long clusterNodeReplOffset(clusterNode *node) {
return node->repl_offset;
}
const char *clusterNodePreferredEndpoint(clusterNode *n, client *c) {
char *hostname = clusterNodeHostname(n);
switch (server.cluster_preferred_endpoint_type) {
-1
View File
@@ -7,7 +7,6 @@
* multiplicators of the node timeout value (when ending with MULT). */
#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */
#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if primary is back. */
#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */
#define CLUSTER_MF_PAUSE_MULT 2 /* Primary pause manual failover mult. */
#define CLUSTER_REPLICA_MIGRATION_DELAY 5000 /* Delay for replica migration. */
+2 -3
View File
@@ -1,9 +1,8 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "cluster_slot_stats.h"
#define UNASSIGNED_SLOT 0
+6 -4
View File
@@ -1,3 +1,9 @@
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/* Commandlog implements a system that is able to remember the latest N
* queries that took more than M microseconds to execute, or consumed
* too much network bandwidth and memory for input/output buffers.
@@ -14,10 +20,6 @@
* but is accessible thanks to the COMMANDLOG command.
*
* ----------------------------------------------------------------------------
*
* Copyright Valkey Contributors.
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
*/
#include "commandlog.h"
+1 -1
View File
@@ -82,7 +82,7 @@
"const": 1
},
{
"description": "Source was not copied.",
"description": "Source was not copied when the destination key already exists",
"const": 0
}
]
+1 -1
View File
@@ -52,7 +52,7 @@
"const": 1
},
{
"description": "Key wasn't moved.",
"description": "Key wasn't moved. When key already exists in the destination database, or it does not exist in the source database",
"const": 0
}
]
+1
View File
@@ -3325,6 +3325,7 @@ standardConfig static_configs[] = {
createLongLongConfig("proto-max-bulk-len", NULL, DEBUG_CONFIG | MODIFIABLE_CONFIG, 1024 * 1024, LONG_MAX, server.proto_max_bulk_len, 512ll * 1024 * 1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, server.repl_backlog_size, 10 * 1024 * 1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 10mb */
createLongLongConfig("cluster-manual-failover-timeout", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, server.cluster_mf_timeout, 5000, INTEGER_CONFIG, NULL, NULL),
/* Unsigned Long Long configs */
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
-2
View File
@@ -380,8 +380,6 @@ static inline const char *connGetInfo(connection *conn, char *buf, size_t buf_le
/* anet-style wrappers to conns */
int connBlock(connection *conn);
int connNonBlock(connection *conn);
int connEnableTcpNoDelay(connection *conn);
int connDisableTcpNoDelay(connection *conn);
int connKeepAlive(connection *conn, int interval);
int connSendTimeout(connection *conn, long long ms);
int connRecvTimeout(connection *conn, long long ms);
+1 -1
View File
@@ -445,7 +445,7 @@ robj *dbRandomKey(serverDb *db) {
sds key = objectGetKey(valkey);
robj *keyobj = createStringObject(key, sdslen(key));
if (objectIsExpired(valkey)) {
if (allvolatile && (server.primary_host || server.import_mode) && --maxtries == 0) {
if (allvolatile && (server.primary_host || server.import_mode || isPausedActions(PAUSE_ACTION_EXPIRE)) && --maxtries == 0) {
/* If the DB is composed only of keys with an expire set,
* it could happen that all the keys are already logically
* expired in the replica, so the function cannot stop because
+21 -18
View File
@@ -32,12 +32,18 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "hashtable.h"
#include "eval.h"
#include "script.h"
#include "module.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef HAVE_DEFRAG
@@ -346,7 +352,7 @@ static void activeDefragSdsDict(dict *d, int val_type) {
} while (cursor != 0);
}
void activeDefragSdsHashtableCallback(void *privdata, void *entry_ref) {
static void activeDefragSdsHashtableCallback(void *privdata, void *entry_ref) {
UNUSED(privdata);
sds *sds_ref = (sds *)entry_ref;
sds new_sds = activeDefragSds(*sds_ref);
@@ -398,7 +404,7 @@ static long scanLaterList(robj *ob, unsigned long *cursor, monotime endtime) {
quicklistNode *node;
long iterations = 0;
int bookmark_failed = 0;
if (ob->type != OBJ_LIST || ob->encoding != OBJ_ENCODING_QUICKLIST) return 0;
serverAssert(ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST);
if (*cursor == 0) {
/* if cursor is 0, we start new iteration */
@@ -441,7 +447,7 @@ static void scanLaterZsetCallback(void *privdata, void *element_ref) {
}
static void scanLaterZset(robj *ob, unsigned long *cursor) {
if (ob->type != OBJ_ZSET || ob->encoding != OBJ_ENCODING_SKIPLIST) return;
serverAssert(ob->type == OBJ_ZSET && ob->encoding == OBJ_ENCODING_SKIPLIST);
zset *zs = (zset *)ob->ptr;
*cursor = hashtableScanDefrag(zs->ht, *cursor, scanLaterZsetCallback, zs->zsl, activeDefragAlloc, HASHTABLE_SCAN_EMIT_REF);
}
@@ -455,7 +461,7 @@ static void scanHashtableCallbackCountScanned(void *privdata, void *elemref) {
}
static void scanLaterSet(robj *ob, unsigned long *cursor) {
if (ob->type != OBJ_SET || ob->encoding != OBJ_ENCODING_HASHTABLE) return;
serverAssert(ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HASHTABLE);
hashtable *ht = ob->ptr;
*cursor = hashtableScanDefrag(ht, *cursor, activeDefragSdsHashtableCallback, NULL, activeDefragAlloc, HASHTABLE_SCAN_EMIT_REF);
}
@@ -470,7 +476,7 @@ static void activeDefragHashTypeEntry(void *privdata, void *element_ref) {
}
static void scanLaterHash(robj *ob, unsigned long *cursor) {
if (ob->type != OBJ_HASH || ob->encoding != OBJ_ENCODING_HASHTABLE) return;
serverAssert(ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HASHTABLE);
hashtable *ht = ob->ptr;
*cursor = hashtableScanDefrag(ht, *cursor, activeDefragHashTypeEntry, NULL, activeDefragAlloc, HASHTABLE_SCAN_EMIT_REF);
}
@@ -557,10 +563,7 @@ static int scanLaterStreamListpacks(robj *ob, unsigned long *cursor, monotime en
static unsigned char last[sizeof(streamID)];
raxIterator ri;
long iterations = 0;
if (ob->type != OBJ_STREAM || ob->encoding != OBJ_ENCODING_STREAM) {
*cursor = 0;
return 0;
}
serverAssert(ob->type == OBJ_STREAM && ob->encoding == OBJ_ENCODING_STREAM);
stream *s = ob->ptr;
raxStart(&ri, s->rax);
@@ -822,15 +825,15 @@ static void defragPubsubScanCallback(void *privdata, void *elemref) {
* and 1 if time is up and more work is needed. */
static int defragLaterItem(robj *ob, unsigned long *cursor, monotime endtime, int dbid) {
if (ob) {
if (ob->type == OBJ_LIST) {
if (ob->type == OBJ_LIST && ob->encoding == OBJ_ENCODING_QUICKLIST) {
return scanLaterList(ob, cursor, endtime);
} else if (ob->type == OBJ_SET) {
} else if (ob->type == OBJ_SET && ob->encoding == OBJ_ENCODING_HASHTABLE) {
scanLaterSet(ob, cursor);
} else if (ob->type == OBJ_ZSET) {
} else if (ob->type == OBJ_ZSET && ob->encoding == OBJ_ENCODING_SKIPLIST) {
scanLaterZset(ob, cursor);
} else if (ob->type == OBJ_HASH) {
} else if (ob->type == OBJ_HASH && ob->encoding == OBJ_ENCODING_HASHTABLE) {
scanLaterHash(ob, cursor);
} else if (ob->type == OBJ_STREAM) {
} else if (ob->type == OBJ_STREAM && ob->encoding == OBJ_ENCODING_STREAM) {
return scanLaterStreamListpacks(ob, cursor, endtime);
} else if (ob->type == OBJ_MODULE) {
/* Fun fact (and a bug since forever): The key is passed to
@@ -839,10 +842,9 @@ static int defragLaterItem(robj *ob, unsigned long *cursor, monotime endtime, in
* callbacks. Nobody can ever have used this, i.e. accessed the key
* name in the defrag module type callback. */
void *sds_key_passed_as_robj = objectGetKey(ob);
long long endtimeWallClock = ustime() + (endtime - getMonotonicUs());
return moduleLateDefrag(sds_key_passed_as_robj, ob, cursor, endtimeWallClock, dbid);
return moduleLateDefrag(sds_key_passed_as_robj, ob, cursor, endtime, dbid);
} else {
*cursor = 0; /* object type may have changed since we schedule it for later */
*cursor = 0; /* object type/encoding may have changed since we schedule it for later */
}
} else {
*cursor = 0; /* object may have been deleted already */
@@ -1042,7 +1044,6 @@ static void endDefragCycle(bool normal_termination) {
// For normal termination, we expect...
serverAssert(!defrag.current_stage);
serverAssert(listLength(defrag.remaining_stages) == 0);
serverAssert(!defrag_later || listLength(defrag_later) == 0);
} else {
// Defrag is being terminated abnormally
aeDeleteTimeEvent(server.el, defrag.timeproc_id);
@@ -1058,6 +1059,8 @@ static void endDefragCycle(bool normal_termination) {
listRelease(defrag.remaining_stages);
defrag.remaining_stages = NULL;
/* For a normal termination, this list is usually empty, but it might contain elements
* if a stage was aborted due to a flushall or some other DB swap. */
if (defrag_later) {
listRelease(defrag_later);
defrag_later = NULL;
+5 -1
View File
@@ -32,7 +32,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fmacros.h"
#include <stddef.h>
+5
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* This file initializes the global LUA object and registers functions to call Valkey API from within the LUA language.
+5
View File
@@ -29,6 +29,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
+5
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "functions.h"
#include "sds.h"
+2 -1
View File
@@ -1032,7 +1032,7 @@ void hashtableEmpty(hashtable *ht, void(callback)(hashtable *)) {
if (ht->bucket_exp[table_index] < 0) {
continue;
}
if (ht->used[table_index] > 0) {
if (ht->used[table_index] > 0 || ht->child_buckets[table_index] > 0) {
for (size_t idx = 0; idx < numBuckets(ht->bucket_exp[table_index]); idx++) {
if (callback && (idx & 65535) == 0) callback(ht);
bucket *b = &ht->tables[table_index][idx];
@@ -1058,6 +1058,7 @@ void hashtableEmpty(hashtable *ht, void(callback)(hashtable *)) {
} while (b != NULL);
}
}
zfree(ht->tables[table_index]);
if (ht->type->trackMemUsage) {
ht->type->trackMemUsage(ht, -sizeof(bucket) * numBuckets(ht->bucket_exp[table_index]));
+5
View File
@@ -28,6 +28,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "intrinsics.h"
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "io_threads.h"
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
+4 -3
View File
@@ -1,8 +1,9 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* This file utilizes prefetching keys and data for multiple commands in a batch,
* to improve performance by amortizing memory access costs across multiple operations.
*/
+46 -15
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/* --------------------------------------------------------------------------
* Modules API documentation information
@@ -2566,7 +2571,16 @@ void VM_Yield(ValkeyModuleCtx *ctx, int flags, const char *busy_reply) {
* By default, the server will not fire key-space notifications that happened inside
* a key-space notification callback. This flag allows to change this behavior
* and fire nested key-space notifications. Notice: if enabled, the module
* should protected itself from infinite recursion. */
* should protected itself from infinite recursion.
*
* VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION:
* When set, this option allows the module to skip command validation.
* This is useful in scenarios where the module needs to bypass
* command validation for specific operations
* to reduce overhead or handle trusted custom command logic.
* ValkeyModule_Replicate and ValkeyModule_EmitAOF
* are affected by this option, allowing them to operate without
* command validation check. */
void VM_SetModuleOptions(ValkeyModuleCtx *ctx, int options) {
ctx->module->options = options;
}
@@ -3630,8 +3644,10 @@ int VM_Replicate(ValkeyModuleCtx *ctx, const char *cmdname, const char *fmt, ...
int argc = 0, flags = 0, j;
va_list ap;
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) return VALKEYMODULE_ERR;
if (!ctx->module || !(ctx->module->options & VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION)) {
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) return VALKEYMODULE_ERR;
}
/* Create the client and dispatch the command. */
va_start(ap, fmt);
@@ -4183,7 +4199,7 @@ static void moduleCloseKey(ValkeyModuleKey *key) {
decrRefCount(key->key);
}
/* Close a key handle. */
/* Close a key handle. The key handle is freed and should not be accessed anymore. */
void VM_CloseKey(ValkeyModuleKey *key) {
if (key == NULL) return;
moduleCloseKey(key);
@@ -7535,15 +7551,17 @@ void VM_EmitAOF(ValkeyModuleIO *io, const char *cmdname, const char *fmt, ...) {
int argc = 0, flags = 0, j;
va_list ap;
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) {
serverLog(LL_WARNING,
"Fatal: AOF method for module data type '%s' tried to "
"emit unknown command '%s'",
io->type->name, cmdname);
io->error = 1;
errno = EINVAL;
return;
if (!io->ctx || !io->ctx->module || !(io->ctx->module->options & VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION)) {
cmd = lookupCommandByCString((char *)cmdname);
if (!cmd) {
serverLog(LL_WARNING,
"Fatal: AOF method for module data type '%s' tried to "
"emit unknown command '%s'",
io->type->name, cmdname);
io->error = 1;
errno = EINVAL;
return;
}
}
/* Emit the arguments into the AOF in RESP format. */
@@ -8950,7 +8968,13 @@ void moduleCallClusterReceivers(const char *sender_id,
* was already a registered callback, this will replace the callback function
* with the one provided, otherwise if the callback is set to NULL and there
* is already a callback for this function, the callback is unregistered
* (so this API call is also used in order to delete the receiver). */
* (so this API call is also used in order to delete the receiver).
*
* When a message of this type is received, the registered callback function
* will be invoked with details, including the 40-byte node ID of the sender.
*
* In Valkey 8.1 and later, the node ID is null-terminated. Prior to 8.1, it was
* not null-terminated */
void VM_RegisterClusterMessageReceiver(ValkeyModuleCtx *ctx,
uint8_t type,
ValkeyModuleClusterMessageReceiver callback) {
@@ -9737,8 +9761,15 @@ ValkeyModuleString *VM_GetModuleUserACLString(ValkeyModuleUser *user) {
* See more information in VM_GetModuleUserFromUserName.
*
* The returned string must be released with ValkeyModule_FreeString() or by
* enabling automatic memory management. */
* enabling automatic memory management.
*
* If the context is not associated with a client connection, NULL is returned
* and errno is set to EINVAL. */
ValkeyModuleString *VM_GetCurrentUserName(ValkeyModuleCtx *ctx) {
if (ctx == NULL || ctx->client == NULL || ctx->client->user == NULL || ctx->client->user->name == NULL) {
errno = EINVAL;
return NULL;
}
return VM_CreateString(ctx, ctx->client->user->name, sdslen(ctx->client->user->name));
}
+46 -45
View File
@@ -71,7 +71,7 @@ static void pauseClientsByClient(mstime_t end, int isPauseClientAll);
int postponeClientRead(client *c);
char *getClientSockname(client *c);
static int parseClientFiltersOrReply(client *c, int index, clientFilter *filter);
static int clientMatchesFilter(client *client, clientFilter client_filter);
static int clientMatchesFilter(client *client, clientFilter *client_filter);
static sds getAllFilteredClientsInfoString(clientFilter *client_filter, int hide_user_data);
int ProcessingEventsWhileBlocked = 0; /* See processEventsWhileBlocked(). */
@@ -157,8 +157,6 @@ client *createClient(connection *conn) {
* in the context of a client. When commands are executed in other
* contexts (for instance a Lua script) we need a non connected client. */
if (conn) {
connEnableTcpNoDelay(conn);
if (server.tcpkeepalive) connKeepAlive(conn, server.tcpkeepalive);
connSetReadHandler(conn, readQueryFromClient);
connSetPrivateData(conn, c);
conn->flags |= CONN_FLAG_ALLOW_ACCEPT_OFFLOAD;
@@ -842,6 +840,7 @@ void setDeferredReply(client *c, void *node, const char *s, size_t length) {
size_t len_to_copy = prev->size - prev->used;
if (len_to_copy > length) len_to_copy = length;
memcpy(prev->buf + prev->used, s, len_to_copy);
c->net_output_bytes_curr_cmd += len_to_copy;
prev->used += len_to_copy;
length -= len_to_copy;
if (length == 0) {
@@ -855,6 +854,7 @@ void setDeferredReply(client *c, void *node, const char *s, size_t length) {
next->used < PROTO_REPLY_CHUNK_BYTES * 4 && c->io_write_state != CLIENT_PENDING_IO) {
memmove(next->buf + length, next->buf, next->used);
memcpy(next->buf, s, length);
c->net_output_bytes_curr_cmd += length;
next->used += length;
listDelNode(c->reply, ln);
} else {
@@ -865,6 +865,7 @@ void setDeferredReply(client *c, void *node, const char *s, size_t length) {
buf->size = usable_size - sizeof(clientReplyBlock);
buf->used = length;
memcpy(buf->buf, s, length);
c->net_output_bytes_curr_cmd += length;
listNodeValue(ln) = buf;
c->reply_bytes += buf->size;
@@ -1582,7 +1583,6 @@ void unlinkClient(client *c) {
/* Remove from the list of pending writes if needed. */
if (c->flag.pending_write) {
serverAssert(&c->clients_pending_write_node.next != NULL || &c->clients_pending_write_node.prev != NULL);
if (c->io_write_state == CLIENT_IDLE) {
listUnlinkNode(server.clients_pending_write, &c->clients_pending_write_node);
} else {
@@ -2764,24 +2764,27 @@ static void setProtocolError(const char *errstr, client *c) {
/* Sample some protocol to given an idea about what was inside. */
char buf[256];
buf[0] = '\0';
if (c->querybuf && sdslen(c->querybuf) - c->qb_pos < PROTO_DUMP_LEN) {
snprintf(buf, sizeof(buf), "Query buffer during protocol error: '%s'", c->querybuf + c->qb_pos);
} else if (c->querybuf) {
snprintf(buf, sizeof(buf), "Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s'",
PROTO_DUMP_LEN / 2, c->querybuf + c->qb_pos, sdslen(c->querybuf) - c->qb_pos - PROTO_DUMP_LEN,
PROTO_DUMP_LEN / 2, c->querybuf + sdslen(c->querybuf) - PROTO_DUMP_LEN / 2);
}
if (server.hide_user_data_from_log) {
snprintf(buf, sizeof(buf), "*redacted*");
} else {
if (c->querybuf && sdslen(c->querybuf) - c->qb_pos < PROTO_DUMP_LEN) {
snprintf(buf, sizeof(buf), "'%s'", c->querybuf + c->qb_pos);
} else if (c->querybuf) {
snprintf(buf, sizeof(buf), "'%.*s' (... more %zu bytes ...) '%.*s'",
PROTO_DUMP_LEN / 2, c->querybuf + c->qb_pos, sdslen(c->querybuf) - c->qb_pos - PROTO_DUMP_LEN,
PROTO_DUMP_LEN / 2, c->querybuf + sdslen(c->querybuf) - PROTO_DUMP_LEN / 2);
}
/* Remove non printable chars. */
char *p = buf;
while (*p != '\0') {
if (!isprint(*p)) *p = '.';
p++;
/* Remove non printable chars. */
char *p = buf;
while (*p != '\0') {
if (!isprint(*p)) *p = '.';
p++;
}
}
/* Log all the client and protocol info. */
int loglevel = (c->flag.primary) ? LL_WARNING : LL_VERBOSE;
serverLog(loglevel, "Protocol error (%s) from client: %s. %s", errstr, client, buf);
serverLog(loglevel, "Protocol error (%s) from client: %s. Query buffer: %s", errstr, client, buf);
sdsfree(client);
}
c->flag.close_after_reply = 1;
@@ -2854,30 +2857,30 @@ void processMultibulkBuffer(client *c) {
*
* Calculation: For multi bulk buffer, we accumulate four factors, namely;
*
* 1) multibulklen_slen + 1
* 1) multibulklen_slen + 3
* Cumulative string length (and not the value of) of multibulklen,
* including +1 from RESP first byte.
* 2) bulklen_slen + c->argc
* including the first "*" byte and last "\r\n" 2 bytes from RESP.
* 2) bulklen_slen + 3
* Cumulative string length (and not the value of) of bulklen,
* including +1 from RESP first byte per argument count.
* including +3 from RESP first "$" byte and last "\r\n" 2 bytes per argument count.
* 3) c->argv_len_sum
* Cumulative string length of all argument vectors.
* 4) c->argc * 4 + 2
* Cumulative string length of all white-spaces, for which there exists a total of
* 4 bytes per argument, plus 2 bytes from the leading '\r\n' from multibulklen.
* 4) c->argc * 2
* Cumulative string length of the arguments' white-spaces, for which there exists a total of
* "\r\n" 2 bytes per argument.
*
* For example;
* Command) SET key value
* RESP) *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
*
* 1) String length of "*3" is 2, obtained from (multibulklen_slen + 1).
* 2) String length of "$3" "$3" "$5" is 6, obtained from (bulklen_slen + c->argc).
* 1) String length of "*3\r\n" is 4, obtained from (multibulklen_slen + 3).
* 2) String length of "$3\r\n" "$3\r\n" "$5\r\n" is 12, obtained from (bulklen_slen + 3).
* 3) String length of "SET" "key" "value" is 11, obtained from (c->argv_len_sum).
* 4) String length of all white-spaces "\r\n" is 14, obtained from (c->argc * 4 + 2).
* 4) String length of the 3 arguments' white-spaces "\r\n" is 6, obtained from (c->argc * 2).
*
* The 1st component is calculated within the below line.
* */
c->net_input_bytes_curr_cmd += (multibulklen_slen + 1);
c->net_input_bytes_curr_cmd += (multibulklen_slen + 3);
}
serverAssertWithInfo(c, NULL, c->multibulklen > 0);
@@ -2942,9 +2945,8 @@ void processMultibulkBuffer(client *c) {
}
}
c->bulklen = ll;
/* Per-slot network bytes-in calculation, 2nd component.
* c->argc portion is deferred, as it may not have been fully populated at this point. */
c->net_input_bytes_curr_cmd += bulklen_slen;
/* Per-slot network bytes-in calculation, 2nd component. */
c->net_input_bytes_curr_cmd += (bulklen_slen + 3);
}
/* Read bulk argument */
@@ -2982,9 +2984,8 @@ void processMultibulkBuffer(client *c) {
/* We're done when c->multibulk == 0 */
if (c->multibulklen == 0) {
/* Per-slot network bytes-in calculation, 3rd and 4th components.
* Here, the deferred c->argc from 2nd component is added, resulting in c->argc * 5 instead of * 4. */
c->net_input_bytes_curr_cmd += (c->argv_len_sum + (c->argc * 5 + 2));
/* Per-slot network bytes-in calculation, 3rd and 4th components. */
c->net_input_bytes_curr_cmd += (c->argv_len_sum + (c->argc * 2));
c->read_flags |= READ_FLAGS_PARSING_COMPLETED;
}
}
@@ -3484,7 +3485,7 @@ static sds getAllFilteredClientsInfoString(clientFilter *client_filter, int hide
listRewind(server.clients, &li);
while ((ln = listNext(&li)) != NULL) {
client = listNodeValue(ln);
if (!clientMatchesFilter(client, *client_filter)) continue;
if (!clientMatchesFilter(client, client_filter)) continue;
o = catClientInfoString(o, client, hide_user_data);
o = sdscatlen(o, "\n", 1);
}
@@ -3687,15 +3688,15 @@ static int parseClientFiltersOrReply(client *c, int index, clientFilter *filter)
return C_OK;
}
static int clientMatchesFilter(client *client, clientFilter client_filter) {
static int clientMatchesFilter(client *client, clientFilter *client_filter) {
/* Check each filter condition and return false if the client does not match. */
if (client_filter.addr && strcmp(getClientPeerId(client), client_filter.addr) != 0) return 0;
if (client_filter.laddr && strcmp(getClientSockname(client), client_filter.laddr) != 0) return 0;
if (client_filter.type != -1 && getClientType(client) != client_filter.type) return 0;
if (client_filter.ids && !intsetFind(client_filter.ids, client->id)) return 0;
if (client_filter.user && client->user != client_filter.user) return 0;
if (client_filter.skipme && client == server.current_client) return 0;
if (client_filter.max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < client_filter.max_age) return 0;
if (client_filter->addr && strcmp(getClientPeerId(client), client_filter->addr) != 0) return 0;
if (client_filter->laddr && strcmp(getClientSockname(client), client_filter->laddr) != 0) return 0;
if (client_filter->type != -1 && getClientType(client) != client_filter->type) return 0;
if (client_filter->ids && !intsetFind(client_filter->ids, client->id)) return 0;
if (client_filter->user && client->user != client_filter->user) return 0;
if (client_filter->skipme && client == server.current_client) return 0;
if (client_filter->max_age != 0 && (long long)(commandTimeSnapshot() / 1000 - client->ctime) < client_filter->max_age) return 0;
/* If all conditions are satisfied, the client matches the filter. */
return 1;
@@ -3888,7 +3889,7 @@ void clientKillCommand(client *c) {
listRewind(server.clients, &li);
while ((ln = listNext(&li)) != NULL) {
client *client = listNodeValue(ln);
if (!clientMatchesFilter(client, client_filter)) continue;
if (!clientMatchesFilter(client, &client_filter)) continue;
/* Kill it. */
if (c == client) {
+1 -1
View File
@@ -1194,7 +1194,7 @@ size_t objectComputeSize(robj *key, robj *o, size_t sample_size, int dbid) {
asize = sizeof(*o) + hashtableMemUsage(ht);
while (hashtableNext(&iter, &next) && samples < sample_size) {
elesize += hashTypeEntryAllocSize(next);
elesize += hashTypeEntryMemUsage(next);
samples++;
}
hashtableResetIterator(&iter);
+1
View File
@@ -1187,6 +1187,7 @@ int raxRemove(rax *rax, unsigned char *s, size_t len, void **old) {
rax_free(tofree);
rax->numnodes--;
if (h->iskey || (!h->iscompr && h->size != 1)) break;
if (comprsize + h->size > RAX_NODE_MAX_SIZE) break;
}
debugnode("New node", new);
+5
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "lzf.h" /* LZF compression library */
+5
View File
@@ -7,6 +7,11 @@
* the top-level directory.
* ==========================================================================
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#define VALKEYMODULE_CORE_MODULE
#include "server.h"
+36 -28
View File
@@ -27,7 +27,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "cluster.h"
@@ -46,6 +50,8 @@
#include <sys/stat.h>
#include <ctype.h>
void cleanupTransferResources(void);
void replicationAbortSyncTransfer(void);
void replicationDiscardCachedPrimary(void);
void replicationResurrectCachedPrimary(connection *conn);
void replicationResurrectProvisionalPrimary(void);
@@ -1152,7 +1158,7 @@ void syncCommand(client *c) {
/* Setup the replica as one waiting for BGSAVE to start. The following code
* paths will change the state if we handle the replica differently. */
c->repl_data->repl_state = REPLICA_STATE_WAIT_BGSAVE_START;
if (server.repl_disable_tcp_nodelay) connDisableTcpNoDelay(c->conn); /* Non critical if it fails. */
if (server.repl_disable_tcp_nodelay) anetDisableTcpNoDelay(NULL, c->conn->fd); /* Non critical if it fails. */
c->repl_data->repldbfd = -1;
c->flag.replica = 1;
listAddNodeTail(server.replicas, c);
@@ -2653,12 +2659,7 @@ void replicationAbortDualChannelSyncTransfer(void) {
connClose(server.repl_rdb_transfer_s);
server.repl_rdb_transfer_s = NULL;
}
zfree(server.repl_transfer_tmpfile);
server.repl_transfer_tmpfile = NULL;
if (server.repl_transfer_fd != -1) {
close(server.repl_transfer_fd);
server.repl_transfer_fd = -1;
}
cleanupTransferResources();
server.repl_rdb_channel_state = REPL_DUAL_CHANNEL_STATE_NONE;
server.repl_provisional_primary.read_reploff = 0;
server.repl_provisional_primary.reploff = 0;
@@ -2867,14 +2868,8 @@ error:
connClose(server.repl_transfer_s);
server.repl_transfer_s = NULL;
}
if (server.repl_rdb_transfer_s) {
connClose(server.repl_rdb_transfer_s);
server.repl_rdb_transfer_s = NULL;
}
if (server.repl_transfer_fd != -1) close(server.repl_transfer_fd);
server.repl_transfer_fd = -1;
server.repl_state = REPL_STATE_CONNECT;
replicationAbortDualChannelSyncTransfer();
server.repl_state = REPL_STATE_CONNECT;
}
/* Replication: Replica side.
@@ -3816,7 +3811,8 @@ void syncWithPrimary(connection *conn) {
server.repl_rdb_transfer_s = connCreate(connTypeOfReplication());
if (connConnect(server.repl_rdb_transfer_s, server.primary_host, server.primary_port, server.bind_source_addr,
dualChannelFullSyncWithPrimary) == C_ERR) {
serverLog(LL_WARNING, "Unable to connect to Primary: %s", connGetLastError(server.repl_transfer_s));
dualChannelServerLog(LL_WARNING, "Unable to connect to Primary: %s",
connGetLastError(server.repl_transfer_s));
connClose(server.repl_rdb_transfer_s);
server.repl_rdb_transfer_s = NULL;
goto error;
@@ -3856,10 +3852,7 @@ error:
connClose(server.repl_rdb_transfer_s);
server.repl_rdb_transfer_s = NULL;
}
if (server.repl_transfer_fd != -1) close(server.repl_transfer_fd);
if (server.repl_transfer_tmpfile) zfree(server.repl_transfer_tmpfile);
server.repl_transfer_tmpfile = NULL;
server.repl_transfer_fd = -1;
replicationAbortSyncTransfer();
server.repl_state = REPL_STATE_CONNECT;
return;
@@ -3886,11 +3879,33 @@ int connectWithPrimary(void) {
return C_OK;
}
/* In disk-based replication, replica will open a temp db file to store the RDB file.
* Before entering the REPL_STATE_TRANSFER or after entering the REPL_STATE_TRANSFER,
* if an error occurs, we need to clean up related resources, such as closing the tmp
* file fd and deleting the temp file.
*
* Noted that repl_transfer_fd and repl_transfer_tmpfile should be set/unset together. */
void cleanupTransferResources(void) {
if (server.repl_transfer_fd == -1) {
serverAssert(server.repl_transfer_tmpfile == NULL);
return;
}
serverAssert(server.repl_transfer_tmpfile != NULL);
close(server.repl_transfer_fd);
bg_unlink(server.repl_transfer_tmpfile);
zfree(server.repl_transfer_tmpfile);
server.repl_transfer_tmpfile = NULL;
server.repl_transfer_fd = -1;
}
/* This function can be called when a non blocking connection is currently
* in progress to undo it.
* Never call this function directly, use cancelReplicationHandshake() instead.
*/
void undoConnectWithPrimary(void) {
if (server.repl_transfer_s == NULL) return;
connClose(server.repl_transfer_s);
server.repl_transfer_s = NULL;
}
@@ -3899,15 +3914,8 @@ void undoConnectWithPrimary(void) {
* Never call this function directly, use cancelReplicationHandshake() instead.
*/
void replicationAbortSyncTransfer(void) {
serverAssert(server.repl_state == REPL_STATE_TRANSFER);
undoConnectWithPrimary();
if (server.repl_transfer_fd != -1) {
close(server.repl_transfer_fd);
bg_unlink(server.repl_transfer_tmpfile);
zfree(server.repl_transfer_tmpfile);
server.repl_transfer_tmpfile = NULL;
server.repl_transfer_fd = -1;
}
cleanupTransferResources();
}
/* This function aborts a non blocking replication attempt if there is one
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
+22
View File
@@ -95,6 +95,28 @@ static inline unsigned char sdsType(const_sds s) {
return flags & SDS_TYPE_MASK;
}
/* Returns a user data bit stored in the SDS header by sdsSetAuxBit. The bit
* index is 0-4. Returns 0 or 1. Always returns 0 for SDS_TYPE_5. */
static inline int sdsGetAuxBit(const_sds s, int bit) {
unsigned char flags = s[-1];
return sdsType(s) == SDS_TYPE_5 ? 0 : flags >> (SDS_TYPE_BITS + bit);
}
/* Stores a bit in an unused area in the SDS header, except for SDS_TYPE_5. The
* bit index is 0-4. The value is 0 or 1. The aux bits are lost if the SDS is
* auto-resized. This is only for special uses like immutable SDS embedded in
* other structures. */
static inline void sdsSetAuxBit(sds s, int bit, int value) {
if (sdsType(s) == SDS_TYPE_5) return;
unsigned char flags = s[-1];
if (value) {
flags |= 1 << (SDS_TYPE_BITS + bit);
} else {
flags &= ~(1 << (SDS_TYPE_BITS + bit));
}
s[-1] = (char)flags;
}
static inline size_t sdslen(const_sds s) {
switch (sdsType(s)) {
case SDS_TYPE_5: return SDS_TYPE_5_LEN(s[-1]);
+5 -1
View File
@@ -26,7 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "monotonic.h"
#include "cluster.h"
+3 -4
View File
@@ -2084,6 +2084,7 @@ struct valkeyServer {
unsigned long cluster_blacklist_ttl; /* Duration in seconds that a node is denied re-entry into
* the cluster after it is forgotten with CLUSTER FORGET. */
int cluster_slot_stats_enabled; /* Cluster slot usage statistics tracking enabled. */
mstime_t cluster_mf_timeout; /* Milliseconds to do a manual failover. */
/* Debug config that goes along with cluster_drop_packet_filter. When set, the link is closed on packet drop. */
uint32_t debug_cluster_close_link_on_packet_drop : 1;
/* Debug config to control the random ping. When set, we will disable the random ping in clusterCron. */
@@ -2584,8 +2585,6 @@ void populateCommandLegacyRangeSpec(struct serverCommand *c);
long long ustime(void);
mstime_t mstime(void);
mstime_t commandTimeSnapshot(void);
void getRandomHexChars(char *p, size_t len);
void getRandomBytes(unsigned char *p, size_t len);
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
void exitFromChild(int retcode);
long long serverPopcount(void *s, long count);
@@ -3250,11 +3249,11 @@ robj *setTypeDup(robj *o);
#define HASH_SET_TAKE_VALUE (1 << 1)
#define HASH_SET_COPY 0
typedef struct hashTypeEntry hashTypeEntry;
typedef void hashTypeEntry;
hashTypeEntry *hashTypeCreateEntry(sds field, sds value);
sds hashTypeEntryGetField(const hashTypeEntry *entry);
sds hashTypeEntryGetValue(const hashTypeEntry *entry);
size_t hashTypeEntryAllocSize(hashTypeEntry *entry);
size_t hashTypeEntryMemUsage(hashTypeEntry *entry);
hashTypeEntry *hashTypeEntryDefrag(hashTypeEntry *entry, void *(*defragfn)(void *), sds (*sdsdefragfn)(sds));
void dismissHashTypeEntry(hashTypeEntry *entry);
void freeHashTypeEntry(hashTypeEntry *entry);
+2 -10
View File
@@ -326,6 +326,8 @@ static void connSocketAcceptHandler(aeEventLoop *el, int fd, void *privdata, int
return;
}
serverLog(LL_VERBOSE, "Accepted %s:%d", cip, cport);
if (server.tcpkeepalive) anetKeepAlive(NULL, cfd, server.tcpkeepalive);
acceptCommonHandler(connCreateAcceptedSocket(cfd, NULL), flags, cip);
}
}
@@ -462,16 +464,6 @@ int connNonBlock(connection *conn) {
return anetNonBlock(NULL, conn->fd);
}
int connEnableTcpNoDelay(connection *conn) {
if (conn->fd == -1) return C_ERR;
return anetEnableTcpNoDelay(NULL, conn->fd);
}
int connDisableTcpNoDelay(connection *conn) {
if (conn->fd == -1) return C_ERR;
return anetDisableTcpNoDelay(NULL, conn->fd);
}
int connKeepAlive(connection *conn, int interval) {
if (conn->fd == -1) return C_ERR;
return anetKeepAlive(NULL, conn->fd, interval);
+197 -37
View File
@@ -26,55 +26,200 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include <math.h>
#include <stdbool.h>
/*-----------------------------------------------------------------------------
* Hash Entry API
*----------------------------------------------------------------------------*/
struct hashTypeEntry {
sds value;
char field_offset;
char field_data[];
};
/* The hashTypeEntry pointer is the field sds. We encode the entry layout type
* in the field SDS header. Field type SDS_TYPE_5 doesn't have any spare bits to
* encode this so we use it only for the first layout type.
*
* Entry with embedded value, used for small sizes. The value is stored as
* SDS_TYPE_8. The field can use any SDS type.
*
* +--------------+---------------+
* | field | value |
* | hdr "foo" \0 | hdr8 "bar" \0 |
* +------^-------+---------------+
* |
* |
* entry pointer = field sds
*
* Entry with value pointer, used for larger fields and values. The field is SDS
* type 8 or higher.
*
* +-------+--------------+
* | value | field |
* | ptr | hdr "foo" \0 |
* +-------+------^-------+
* |
* |
* entry pointer = field sds
*/
/* The maximum allocation size we want to use for entries with embedded
* values. */
#define EMBED_VALUE_MAX_ALLOC_SIZE 128
/* SDS aux flag. If set, it indicates that the entry has an embedded value
* pointer located in memory before the embedded field. If unset, the entry
* instead has an embedded value located after the embedded field. */
#define FIELD_SDS_AUX_BIT_ENTRY_HAS_VALUE_PTR 0
static inline bool entryHasValuePtr(const hashTypeEntry *entry) {
return sdsGetAuxBit(entry, FIELD_SDS_AUX_BIT_ENTRY_HAS_VALUE_PTR);
}
/* Returns the location of a pointer to a separately allocated value. Only for
* an entry without an embedded value. */
static sds *hashTypeEntryGetValueRef(const hashTypeEntry *entry) {
serverAssert(entryHasValuePtr(entry));
char *field_data = sdsAllocPtr(entry);
field_data -= sizeof(sds *);
return (sds *)field_data;
}
/* takes ownership of value, does not take ownership of field */
hashTypeEntry *hashTypeCreateEntry(sds field, sds value) {
size_t field_len = sdslen(field);
char field_sds_type = sdsReqType(field_len);
int field_sds_type = sdsReqType(field_len);
size_t field_size = sdsReqSize(field_len, field_sds_type);
size_t total_size = sizeof(hashTypeEntry) + field_size;
hashTypeEntry *entry = zmalloc(total_size);
entry->value = value;
entry->field_offset = sdsHdrSize(field_sds_type);
sdswrite(entry->field_data, field_size, field_sds_type, field, field_len);
return entry;
size_t value_len = sdslen(value);
size_t value_size = sdsReqSize(value_len, SDS_TYPE_8);
sds embedded_field_sds;
if (field_size + value_size <= EMBED_VALUE_MAX_ALLOC_SIZE) {
/* Embed field and value. Value is fixed to SDS_TYPE_8. Unused
* allocation space is recorded in the embedded value's SDS header.
*
* +--------------+---------------+
* | field | value |
* | hdr "foo" \0 | hdr8 "bar" \0 |
* +--------------+---------------+
*/
size_t min_size = field_size + value_size;
size_t buf_size;
char *buf = zmalloc_usable(min_size, &buf_size);
embedded_field_sds = sdswrite(buf, field_size, field_sds_type, field, field_len);
sdswrite(buf + field_size, buf_size - field_size, SDS_TYPE_8, value, value_len);
/* Field sds aux bits are zero, which we use for this entry encoding. */
sdsSetAuxBit(embedded_field_sds, FIELD_SDS_AUX_BIT_ENTRY_HAS_VALUE_PTR, 0);
serverAssert(!entryHasValuePtr(embedded_field_sds));
sdsfree(value);
} else {
/* Embed field, but not value. Field must be >= SDS_TYPE_8 to encode to
* indicate this type of entry.
*
* +-------+---------------+
* | value | field |
* | ptr | hdr8 "foo" \0 |
* +-------+---------------+
*/
char field_sds_type = sdsReqType(field_len);
if (field_sds_type == SDS_TYPE_5) field_sds_type = SDS_TYPE_8;
field_size = sdsReqSize(field_len, field_sds_type);
size_t alloc_size = sizeof(sds *) + field_size;
char *buf = zmalloc(alloc_size);
*(sds *)buf = value;
embedded_field_sds = sdswrite(buf + sizeof(sds *), field_size, field_sds_type, field, field_len);
/* Store the entry encoding type in sds aux bits. */
sdsSetAuxBit(embedded_field_sds, FIELD_SDS_AUX_BIT_ENTRY_HAS_VALUE_PTR, 1);
serverAssert(entryHasValuePtr(embedded_field_sds));
}
return (void *)embedded_field_sds;
}
/* The entry pointer is the field sds, but that's an implementation detail. */
sds hashTypeEntryGetField(const hashTypeEntry *entry) {
const char *field = entry->field_data + entry->field_offset;
return (sds)field;
return (sds)entry;
}
sds hashTypeEntryGetValue(const hashTypeEntry *entry) {
return entry->value;
if (entryHasValuePtr(entry)) {
return *hashTypeEntryGetValueRef(entry);
} else {
/* Skip field content, field null terminator and value sds8 hdr. */
size_t offset = sdslen(entry) + 1 + sdsHdrSize(SDS_TYPE_8);
return (char *)entry + offset;
}
}
/* frees previous value, takes ownership of new value */
static void hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) {
sdsfree(entry->value);
entry->value = value;
/* Returns the address of the entry allocation. */
static void *hashTypeEntryAllocPtr(hashTypeEntry *entry) {
char *buf = sdsAllocPtr(entry);
if (entryHasValuePtr(entry)) {
buf -= sizeof(sds *);
}
return buf;
}
/* Returns allocation size of hashTypeEntry and data owned by hashTypeEntry,
* even if not embedded in the same allocation. */
size_t hashTypeEntryAllocSize(hashTypeEntry *entry) {
size_t size = zmalloc_usable_size(entry);
size += sdsAllocSize(entry->value);
return size;
/* Frees previous value, takes ownership of new value, returns entry (may be
* reallocated). */
static hashTypeEntry *hashTypeEntryReplaceValue(hashTypeEntry *entry, sds value) {
sds field = (sds)entry;
size_t field_size = sdsHdrSize(sdsType(field)) + sdsalloc(field) + 1;
size_t value_len = sdslen(value);
size_t value_size = sdsReqSize(value_len, SDS_TYPE_8);
if (!entryHasValuePtr(entry)) {
/* Reuse the allocation if the new value fits and leaves no more than
* 25% unused space after replacing the value. */
char *alloc_ptr = sdsAllocPtr(entry);
size_t required_size = field_size + value_size;
size_t alloc_size;
if (required_size <= EMBED_VALUE_MAX_ALLOC_SIZE &&
required_size <= (alloc_size = hashTypeEntryMemUsage(entry)) &&
required_size >= alloc_size * 3 / 4) {
/* It fits in the allocation and leaves max 25% unused space. */
sdswrite(alloc_ptr + field_size, alloc_size - field_size, SDS_TYPE_8, value, value_len);
sdsfree(value);
return entry;
}
hashTypeEntry *new_entry = hashTypeCreateEntry(hashTypeEntryGetField(entry), value);
freeHashTypeEntry(entry);
return new_entry;
} else {
/* The value pointer is located before the embedded field. */
if (field_size + value_size <= EMBED_VALUE_MAX_ALLOC_SIZE) {
/* Convert to entry with embedded value. */
hashTypeEntry *new_entry = hashTypeCreateEntry(field, value);
freeHashTypeEntry(entry);
return new_entry;
} else {
/* Not embedded value. */
sds *value_ref = hashTypeEntryGetValueRef(entry);
sdsfree(*value_ref);
*value_ref = value;
return entry;
}
}
}
/* Returns memory usage of a hashTypeEntry, including all allocations owned by
* the hashTypeEntry. */
size_t hashTypeEntryMemUsage(hashTypeEntry *entry) {
size_t mem = 0;
if (entryHasValuePtr(entry)) {
/* Alloc size is not stored in the embedded field. */
mem = zmalloc_usable_size(hashTypeEntryAllocPtr(entry));
mem += sdsAllocSize(*hashTypeEntryGetValueRef(entry));
} else {
/* Remaining alloc size is encoded in the embedded value SDS header. */
sds field = entry;
sds value = (char *)entry + sdslen(field) + 1 + sdsHdrSize(SDS_TYPE_8);
size_t field_size = sdsHdrSize(sdsType(field)) + sdslen(field) + 1;
size_t value_size = sdsHdrSize(SDS_TYPE_8) + sdsalloc(value) + 1;
mem = field_size + value_size;
}
return mem;
}
/* Defragments a hashtable entry (field-value pair) if needed, using the
@@ -85,25 +230,35 @@ size_t hashTypeEntryAllocSize(hashTypeEntry *entry) {
* If the location of the hashTypeEntry changed we return the new location,
* otherwise we return NULL. */
hashTypeEntry *hashTypeEntryDefrag(hashTypeEntry *entry, void *(*defragfn)(void *), sds (*sdsdefragfn)(sds)) {
hashTypeEntry *new_entry = defragfn(entry);
if (new_entry) entry = new_entry;
sds new_value = sdsdefragfn(entry->value);
if (new_value) entry->value = new_value;
return new_entry;
if (entryHasValuePtr(entry)) {
sds *value_ref = hashTypeEntryGetValueRef(entry);
sds new_value = sdsdefragfn(*value_ref);
if (new_value) *value_ref = new_value;
}
char *allocation = hashTypeEntryAllocPtr(entry);
char *new_allocation = defragfn(allocation);
if (new_allocation != NULL) {
/* Return the same offset into the new allocation as the entry's offset
* in the old allocation. */
return new_allocation + ((char *)entry - allocation);
}
return NULL;
}
/* Used for releasing memory to OS to avoid unnecessary CoW. Called when we've
* forked and memory won't be used again. See zmadvise_dontneed() */
void dismissHashTypeEntry(hashTypeEntry *entry) {
/* Only dismiss values memory since the field size usually is small. */
dismissSds(entry->value);
if (entryHasValuePtr(entry)) {
dismissSds(*hashTypeEntryGetValueRef(entry));
}
}
void freeHashTypeEntry(hashTypeEntry *entry) {
sdsfree(entry->value);
zfree(entry);
if (entryHasValuePtr(entry)) {
sdsfree(*hashTypeEntryGetValueRef(entry));
}
zfree(hashTypeEntryAllocPtr(entry));
}
/*-----------------------------------------------------------------------------
@@ -321,7 +476,12 @@ int hashTypeSet(robj *o, sds field, sds value, int flags) {
hashtableInsertAtPosition(ht, entry, &position);
} else {
/* exists: replace value */
hashTypeEntryReplaceValue(existing, v);
void *new_entry = hashTypeEntryReplaceValue(existing, v);
if (new_entry != existing) {
/* It has been reallocated. */
int replaced = hashtableReplaceReallocatedEntry(ht, existing, new_entry);
serverAssert(replaced);
}
update = 1;
}
} else {
+5
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "hashtable.h"
+5
View File
@@ -26,6 +26,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include <math.h> /* isnan(), isinf() */
+5 -1
View File
@@ -27,7 +27,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
/*-----------------------------------------------------------------------------
* Sorted set API
*----------------------------------------------------------------------------*/
+1 -1
View File
@@ -166,7 +166,7 @@ int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int
return C_ERR;
ftval *= 1000.0; /* seconds => millisec */
if (ftval > LLONG_MAX) {
if (ftval > (long double)LLONG_MAX) {
addReplyError(c, "timeout is out of range");
return C_ERR;
}
+33 -15
View File
@@ -415,12 +415,6 @@ error:
return C_ERR;
}
#ifdef TLS_DEBUGGING
#define TLSCONN_DEBUG(fmt, ...) serverLog(LL_DEBUG, "TLSCONN: " fmt, __VA_ARGS__)
#else
#define TLSCONN_DEBUG(fmt, ...)
#endif
static ConnectionType CT_TLS;
/* Normal socket connections have a simple events/handler correlation.
@@ -452,6 +446,10 @@ typedef struct tls_connection {
SSL *ssl;
char *ssl_error;
listNode *pending_list_node;
/* Per https://docs.openssl.org/master/man3/SSL_write, after a write call with partially written data,
* we must make subsequent write calls with the same length. We use this field to keep track of
* the previous write length. */
size_t last_failed_write_data_len;
} tls_connection;
static connection *createTLSConnection(int client_side) {
@@ -693,9 +691,6 @@ static void TLSAccept(void *_conn) {
static void tlsHandleEvent(tls_connection *conn, int mask) {
int ret, conn_error;
TLSCONN_DEBUG("tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d", fd, conn->c.state, mask,
conn->c.read_handler != NULL, conn->c.write_handler != NULL, conn->flags);
switch (conn->c.state) {
case CONN_STATE_CONNECTING:
conn_error = anetGetError(conn->c.fd);
@@ -798,6 +793,8 @@ static void tlsAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask)
return;
}
serverLog(LL_VERBOSE, "Accepted %s:%d", cip, cport);
if (server.tcpkeepalive) anetKeepAlive(NULL, cfd, server.tcpkeepalive);
acceptCommonHandler(connCreateAcceptedTLS(cfd, &server.tls_auth_clients), flags, cip);
}
}
@@ -910,11 +907,23 @@ static int connTLSWrite(connection *conn_, const void *data, size_t data_len) {
if (conn->c.state != CONN_STATE_CONNECTED) return -1;
ERR_clear_error();
/* In case when last write failed due to some internal reason, retry has to provide
* at least the same amount of bytes (https://docs.openssl.org/master/man3/SSL_write).
* If that condition is not met, OpenSSL will return "SSL routines::bad length".
* Currently we only suspect this can happen during primary cron sending '\n'
* indication to the replica, so we silently return from this function without
* impacting the connection state. */
if (data_len < conn->last_failed_write_data_len) {
// TODO: place debugAssert for this case once the known issue described is resolved
return -1;
}
ret = SSL_write(conn->ssl, data, data_len);
conn->last_failed_write_data_len = ret <= 0 ? data_len : 0;
return updateStateAfterSSLIO(conn, ret, 1);
}
static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt) {
tls_connection *conn = (tls_connection *)conn_;
if (iovcnt == 1) return connTLSWrite(conn_, iov[0].iov_base, iov[0].iov_len);
/* Accumulate the amount of bytes of each buffer and check if it exceeds NET_MAX_WRITES_PER_EVENT. */
@@ -924,10 +933,15 @@ static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt)
if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT) break;
}
/* The amount of all buffers is greater than NET_MAX_WRITES_PER_EVENT,
* which is not worth doing so much memory copying to reduce system calls,
* therefore, invoke connTLSWrite() multiple times to avoid memory copies. */
if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT) {
/* In case the amount of all buffers is greater than NET_MAX_WRITES_PER_EVENT,
* it might not worth doing so much memory copying to reduce system calls,
* therefore, invoke connTLSWrite() multiple times to avoid memory copies.
* However, in case when last write failed we still have to repeat sending last_failed_write_data_len
* bytes. Because of openssl implementation we cannot repeat sending writes with length smaller than
* the last failed write (https://docs.openssl.org/master/man3/SSL_write) so in case the first io buffer
* does not provide at least the same amount of bytes as previous failed write, we will have to fallback to
* memory copy to a static buffer before calling SSL_write. */
if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT && iovcnt > 0 && iov[0].iov_len >= conn->last_failed_write_data_len) {
ssize_t tot_sent = 0;
for (int i = 0; i < iovcnt; i++) {
ssize_t sent = connTLSWrite(conn_, iov[i].iov_base, iov[i].iov_len);
@@ -941,10 +955,14 @@ static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt)
/* The amount of all buffers is less than NET_MAX_WRITES_PER_EVENT,
* which is worth doing more memory copies in exchange for fewer system calls,
* so concatenate these scattered buffers into a contiguous piece of memory
* and send it away by one call to connTLSWrite(). */
* and send it away by one call to connTLSWrite().
* However, code can fallback here in case when last write failed and first
* element of io is buffer not big enough to provide required amount of bytes
* to retry, so iov_bytes_len may exceed NET_MAX_WRITES_PER_EVENT by the amount
* of remaining bytes from last taken io. */
char buf[iov_bytes_len];
size_t offset = 0;
for (int i = 0; i < iovcnt; i++) {
for (int i = 0; i < iovcnt && offset < iov_bytes_len; i++) {
memcpy(buf + offset, iov[i].iov_base, iov[i].iov_len);
offset += iov[i].iov_len;
}
+1 -1
View File
@@ -280,7 +280,7 @@ void sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {
int using_redirection = 0;
if (c->pubsub_data->client_tracking_redirection) {
client *redir = lookupClientByID(c->pubsub_data->client_tracking_redirection);
if (!redir) {
if (!redir || redir->flag.close_after_reply || redir->flag.close_asap) {
c->flag.tracking_broken_redir = 1;
/* We need to signal to the original connection that we
* are unable to send invalidation messages to the redirected
+75
View File
@@ -0,0 +1,75 @@
#include <time.h>
#include <stdint.h>
#include "test_help.h"
#include "../config.h"
#include "../zmalloc.h"
extern long long popcountScalar(void *s, long count);
#ifdef HAVE_AVX2
extern long long popcountAVX2(void *s, long count);
#endif
static long long bitcount(void *s, long count) {
long long bits = 0;
uint8_t *p = (uint8_t *)s;
for (int x = 0; x < count; x += 1) {
uint8_t val = *(x + p);
while (val) {
bits += val & 1;
val >>= 1;
}
}
return bits;
}
static int test_case(const char *msg, int size) {
size_t bufsize = size > 0 ? size : 1;
uint8_t buf[bufsize];
int fuzzing = 1000;
for (int y = 0; y < fuzzing; y += 1) {
for (int z = 0; z < size; z += 1) {
buf[z] = random() % 256;
}
long long expect = bitcount(buf, size);
long long ret_scalar = popcountScalar(buf, size);
TEST_ASSERT_MESSAGE(msg, expect == ret_scalar);
#ifdef HAVE_AVX2
long long ret_avx2 = popcountAVX2(buf, size);
TEST_ASSERT_MESSAGE(msg, expect == ret_avx2);
#endif
}
return 0;
}
int test_popcount(int argc, char **argv, int flags) {
UNUSED(argc);
UNUSED(argv);
UNUSED(flags);
#define TEST_CASE(MSG, SIZE) \
if (test_case("Test failed: " MSG, SIZE)) { \
return 1; \
}
/* The AVX2 version divides the array into the following 3 parts."
* Part A Part B Part C
* +-----------------+--------------+---------+
* | 8 * 32bytes * X | 32bytes * Y | Z bytes |
* +-----------------+--------------+---------+
*/
/* So we test the following cases */
TEST_CASE("Popcount(Part A)", 8 * 32 * 2);
TEST_CASE("Popcount(Part B)", 32 * 2);
TEST_CASE("Popcount(Part C)", 2);
TEST_CASE("Popcount(Part A + Part B)", 8 * 32 * 7 + 32 * 2);
TEST_CASE("Popcount(Part A + Part C)", 8 * 32 * 11 + 7);
TEST_CASE("Popcount(Part A + Part B + Part C)", 8 * 32 * 3 + 3 * 32 + 5);
TEST_CASE("Popcount(Corner case)", 0);
#undef TEST_CASE
return 0;
}
File diff suppressed because one or more lines are too long
+48 -21
View File
@@ -5,6 +5,11 @@ uint64_t hashTestCallback(const void *key) {
return hashtableGenHashFunction((char *)key, strlen((char *)key));
}
uint64_t hashConflictTestCallback(const void *key) {
UNUSED(key);
return 0;
}
int cmpTestCallback(const void *k1, const void *k2) {
return strcmp(k1, k2);
}
@@ -23,6 +28,16 @@ hashtableType KvstoreHashtableTestType = {
.getMetadataSize = kvstoreHashtableMetadataSize,
};
hashtableType KvstoreConflictHashtableTestType = {
.hashFunction = hashConflictTestCallback,
.keyCompare = cmpTestCallback,
.entryDestructor = freeTestCallback,
.rehashingStarted = kvstoreHashtableRehashingStarted,
.rehashingCompleted = kvstoreHashtableRehashingCompleted,
.trackMemUsage = kvstoreHashtableTrackMemUsage,
.getMetadataSize = kvstoreHashtableMetadataSize,
};
char *stringFromInt(int value) {
char buf[32];
int len;
@@ -70,31 +85,43 @@ int test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyHashtable(int argc, char **arg
UNUSED(argv);
UNUSED(flags);
int i;
void *key;
kvstoreIterator *kvs_it;
hashtableType *type[] = {
&KvstoreHashtableTestType,
&KvstoreConflictHashtableTestType,
NULL,
};
int didx = 0;
int curr_slot = 0;
kvstore *kvs1 = kvstoreCreate(&KvstoreHashtableTestType, 0, KVSTORE_ALLOCATE_HASHTABLES_ON_DEMAND);
for (int t = 0; type[t] != NULL; t++) {
hashtableType *testType = type[t];
TEST_PRINT_INFO("Testing %d hashtableType\n", t);
for (i = 0; i < 16; i++) {
TEST_ASSERT(kvstoreHashtableAdd(kvs1, didx, stringFromInt(i)));
int i;
void *key;
kvstoreIterator *kvs_it;
int didx = 0;
int curr_slot = 0;
kvstore *kvs1 = kvstoreCreate(testType, 0, KVSTORE_ALLOCATE_HASHTABLES_ON_DEMAND);
for (i = 0; i < 16; i++) {
TEST_ASSERT(kvstoreHashtableAdd(kvs1, didx, stringFromInt(i)));
}
kvs_it = kvstoreIteratorInit(kvs1, HASHTABLE_ITER_SAFE);
while (kvstoreIteratorNext(kvs_it, &key)) {
curr_slot = kvstoreIteratorGetCurrentHashtableIndex(kvs_it);
TEST_ASSERT(kvstoreHashtableDelete(kvs1, curr_slot, key));
}
kvstoreIteratorRelease(kvs_it);
hashtable *ht = kvstoreGetHashtable(kvs1, didx);
TEST_ASSERT(ht != NULL);
TEST_ASSERT(kvstoreHashtableSize(kvs1, didx) == 0);
TEST_ASSERT(kvstoreSize(kvs1) == 0);
kvstoreRelease(kvs1);
}
kvs_it = kvstoreIteratorInit(kvs1, HASHTABLE_ITER_SAFE);
while (kvstoreIteratorNext(kvs_it, &key)) {
curr_slot = kvstoreIteratorGetCurrentHashtableIndex(kvs_it);
TEST_ASSERT(kvstoreHashtableDelete(kvs1, curr_slot, key));
}
kvstoreIteratorRelease(kvs_it);
hashtable *ht = kvstoreGetHashtable(kvs1, didx);
TEST_ASSERT(ht != NULL);
TEST_ASSERT(kvstoreHashtableSize(kvs1, didx) == 0);
TEST_ASSERT(kvstoreSize(kvs1) == 0);
kvstoreRelease(kvs1);
return 0;
}
+41 -2
View File
@@ -1,13 +1,18 @@
/*
* Copyright Valkey contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include "test_files.h"
#include "test_help.h"
#include "../util.h"
#include "../mt19937-64.h"
#include "../hashtable.h"
#include "../zmalloc.h"
/* We override the default assertion mechanism, so that it prints out info and then dies. */
void _serverAssert(const char *estr, const char *file, int line) {
@@ -32,6 +37,12 @@ int runTestSuite(struct unitTestSuite *test, int argc, char **argv, int flags) {
}
}
/* Check if the test suite has cleaned up all the memory used. */
if (zmalloc_used_memory() > 0) {
printf("[" KRED "%s" KRESET "] Memory leak detected of %zu bytes\n", test->filename, zmalloc_used_memory());
failed_tests++;
}
printf("[" KBLUE "END" KRESET "] - %s: ", test->filename);
printf("%d tests, %d passed, %d failed\n", test_num, test_num - failed_tests, failed_tests);
return !failed_tests;
@@ -40,6 +51,7 @@ int runTestSuite(struct unitTestSuite *test, int argc, char **argv, int flags) {
int main(int argc, char **argv) {
int flags = 0;
char *file = NULL;
char *seed = NULL;
for (int j = 1; j < argc; j++) {
char *arg = argv[j];
if (!strcasecmp(arg, "--accurate"))
@@ -51,13 +63,40 @@ int main(int argc, char **argv) {
file = argv[j + 1];
} else if (!strcasecmp(arg, "--valgrind")) {
flags |= UNIT_TEST_VALGRIND;
} else if (!strcasecmp(arg, "--seed")) {
seed = argv[j + 1];
}
}
if (seed) {
setRandomSeedCString(seed, strlen(seed));
}
char seed_cstr[129];
getRandomSeedCString(seed_cstr, 129);
printf("Tests will run with seed=%s\n", seed_cstr);
unsigned long long genrandseed;
getRandomBytes((void *)&genrandseed, sizeof(genrandseed));
uint8_t hashseed[16];
getRandomBytes(hashseed, sizeof(hashseed));
int numtests = sizeof(unitTestSuite) / sizeof(struct unitTestSuite);
int failed_num = 0, suites_executed = 0;
for (int j = 0; j < numtests; j++) {
if (file && strcasecmp(file, unitTestSuite[j].filename)) continue;
/* We need to explicitly set the seed in the several random numbers
* generator that valkey server uses so that the unit tests reproduce
* the random values in a deterministic way. */
setRandomSeedCString(seed_cstr, strlen(seed_cstr));
init_genrand64(genrandseed);
srandom((unsigned)genrandseed);
hashtableSetHashFunctionSeed(hashseed);
if (!runTestSuite(&unitTestSuite[j], argc, argv, flags)) {
failed_num++;
}
+1
View File
@@ -2294,6 +2294,7 @@ int test_quicklistCompressAndDecomressQuicklistPlainNodeLargeThanUINT32MAX(int a
zfree(node->next);
zfree(node->entry);
zfree(node);
zfree(s);
#endif
return 0;
+52
View File
@@ -1023,3 +1023,55 @@ int test_raxFuzz(int argc, char **argv, int flags) {
}
return !!errors;
}
/* This test verifies that raxRemove correctly handles compression when two keys
* share a common prefix. Upon deletion of one key, rax attempts to recompress
* the structure back to its original form for other key. Historically, there was
* a crash when deleting one key because rax would attempt to recompress the
* structure without checking the 512MB size limit.
*
* This test is disabled by default because it uses a lot of memory. */
int test_raxRecompressHugeKey(int argc, char **argv, int flags) {
UNUSED(argc);
UNUSED(argv);
if (!(flags & UNIT_TEST_LARGE_MEMORY)) return 0;
rax *rt = raxNew();
/* Insert small keys */
char small_key[32];
const char *small_prefix = ",({5oM}";
int i;
for (i = 1; i <= 20; i++) {
snprintf(small_key, sizeof(small_key), "%s%d", small_prefix, i);
size_t keylen = strlen(small_key);
raxInsert(rt, (unsigned char *)small_key, keylen,
(void *)(long)i, NULL);
}
/* Insert large key exceeding compressed node size limit */
size_t max_keylen = ((1 << 29) - 1) + 100; // Compressed node limit + overflow
const char *large_prefix = ",({ABC}";
unsigned char *large_key = zmalloc(max_keylen + strlen(large_prefix));
if (!large_key) {
fprintf(stderr, "Failed to allocate memory for large key\n");
raxFree(rt);
return 1;
}
memcpy(large_key, large_prefix, strlen(large_prefix));
memset(large_key + strlen(large_prefix), '1', max_keylen);
raxInsert(rt, large_key, max_keylen + strlen(large_prefix), NULL, NULL);
/* Remove small keys to trigger recompression crash in raxRemove() */
for (i = 20; i >= 1; i--) {
snprintf(small_key, sizeof(small_key), "%s%d", small_prefix, i);
size_t keylen = strlen(small_key);
raxRemove(rt, (unsigned char *)small_key, keylen, NULL);
}
zfree(large_key);
raxFree(rt);
return 0;
}
+2 -3
View File
@@ -1,10 +1,9 @@
/*
* Copyright Valkey Contributors.
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "../valkey_strtod.h"
#include "errno.h"
#include "math.h"
+54 -22
View File
@@ -51,6 +51,7 @@
#include "sha256.h"
#include "config.h"
#include "zmalloc.h"
#include "serverassert.h"
#include "valkey_strtod.h"
@@ -905,36 +906,67 @@ int version2num(const char *version) {
return v;
}
/* Global state. */
static int seed_initialized = 0;
static unsigned char seed[64]; /* 512 bit internal block size. */
static void initializeRandomSeed(void) {
assert(seed_initialized == 0);
/* Initialize a seed and use SHA1 in counter mode, where we hash
* the same seed with a progressive counter. For the goals of this
* function we just need non-colliding strings, there are no
* cryptographic security needs. */
FILE *fp = fopen("/dev/urandom", "r");
if (fp == NULL || fread(seed, sizeof(seed), 1, fp) != 1) {
/* Revert to a weaker seed, and in this case reseed again
* at every call.*/
for (unsigned int j = 0; j < sizeof(seed); j++) {
struct timeval tv;
gettimeofday(&tv, NULL);
pid_t pid = getpid();
seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;
}
} else {
seed_initialized = 1;
}
if (fp) fclose(fp);
}
/* This function receives a string with 64 bytes encoded as 128 hexadecimal
* digits, and sets the 64 byte random seed. */
void setRandomSeedCString(char *seed_str, size_t len) {
assert(len == sizeof(seed) * 2);
for (size_t i = 0; i < sizeof(seed); i++) {
sscanf(seed_str + (i * 2), "%02hhX", &seed[i]);
}
seed_initialized = 1;
}
/* This function populates a char buffer with 129 bytes with the 64 bytes
* random seed encoded as 128 hexadecimal digits and a null terminator. */
void getRandomSeedCString(char *buff, size_t len) {
assert(len == (sizeof(seed) * 2 + 1));
if (!seed_initialized) {
initializeRandomSeed();
}
for (size_t i = 0; i < sizeof(seed); i++) {
snprintf(buff + (i * 2), 3, "%02hhX", seed[i]);
}
buff[sizeof(seed) * 2] = '\0';
}
/* Get random bytes, attempts to get an initial seed from /dev/urandom and
* the uses a one way hash function in counter mode to generate a random
* stream. However if /dev/urandom is not available, a weaker seed is used.
*
* This function is not thread safe, since the state is global. */
void getRandomBytes(unsigned char *p, size_t len) {
/* Global state. */
static int seed_initialized = 0;
static unsigned char seed[64]; /* 512 bit internal block size. */
static uint64_t counter = 0; /* The counter we hash with the seed. */
static uint64_t counter = 0; /* The counter we hash with the seed. */
if (!seed_initialized) {
/* Initialize a seed and use SHA1 in counter mode, where we hash
* the same seed with a progressive counter. For the goals of this
* function we just need non-colliding strings, there are no
* cryptographic security needs. */
FILE *fp = fopen("/dev/urandom", "r");
if (fp == NULL || fread(seed, sizeof(seed), 1, fp) != 1) {
/* Revert to a weaker seed, and in this case reseed again
* at every call.*/
for (unsigned int j = 0; j < sizeof(seed); j++) {
struct timeval tv;
gettimeofday(&tv, NULL);
pid_t pid = getpid();
seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;
}
} else {
seed_initialized = 1;
}
if (fp) fclose(fp);
initializeRandomSeed();
}
while (len) {
+4
View File
@@ -99,5 +99,9 @@ int snprintf_async_signal_safe(char *to, size_t n, const char *fmt, ...);
#endif
size_t valkey_strlcpy(char *dst, const char *src, size_t dsize);
size_t valkey_strlcat(char *dst, const char *src, size_t dsize);
void getRandomSeedCString(char *buff, size_t len);
void setRandomSeedCString(char *seed_str, size_t len);
void getRandomHexChars(char *p, size_t len);
void getRandomBytes(unsigned char *p, size_t len);
#endif
+7 -5
View File
@@ -244,10 +244,11 @@ static dictType dtype = {
static redisContext *getRedisContext(const char *ip, int port, const char *hostsocket) {
redisContext *ctx = NULL;
redisReply *reply = NULL;
struct timeval tv = {0};
if (hostsocket == NULL)
ctx = redisConnect(ip, port);
ctx = redisConnectWrapper(ip, port, tv, 0);
else
ctx = redisConnectUnix(hostsocket);
ctx = redisConnectUnixWrapper(hostsocket, tv, 0);
if (ctx == NULL || ctx->err) {
fprintf(stderr, "Could not connect to server at ");
char *err = (ctx != NULL ? ctx->errstr : "");
@@ -637,6 +638,7 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
const char *ip = NULL;
int port = 0;
struct timeval tv = {0};
c->cluster_node = NULL;
if (config.hostsocket == NULL || is_cluster_client) {
if (!is_cluster_client) {
@@ -654,9 +656,9 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
port = node->port;
c->cluster_node = node;
}
c->context = redisConnectNonBlock(ip, port);
c->context = redisConnectWrapper(ip, port, tv, 1);
} else {
c->context = redisConnectUnixNonBlock(config.hostsocket);
c->context = redisConnectUnixWrapper(config.hostsocket, tv, 1);
}
if (c->context->err) {
fprintf(stderr, "Could not connect to server at ");
@@ -1340,7 +1342,7 @@ int parseOptions(int argc, char **argv) {
if (lastarg) goto invalid;
config.conn_info.user = sdsnew(argv[++i]);
} else if (!strcmp(argv[i], "-u") && !lastarg) {
parseRedisUri(argv[++i], "redis-benchmark", &config.conn_info, &config.tls);
parseUri(argv[++i], "valkey-benchmark", &config.conn_info, &config.tls);
if (config.conn_info.hostport < 0 || config.conn_info.hostport > 65535) {
fprintf(stderr, "Invalid server port.\n");
exit(1);
+14 -7
View File
@@ -27,7 +27,11 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fmacros.h"
#include <stdio.h>
@@ -1631,9 +1635,9 @@ static int cliConnect(int flags) {
/* Do not use hostsocket when we got redirected in cluster mode */
if (config.hostsocket == NULL || (config.cluster_mode && config.cluster_reissue_command)) {
context = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, config.connect_timeout);
context = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, config.connect_timeout, 0);
} else {
context = redisConnectUnixWrapper(config.hostsocket, config.connect_timeout);
context = redisConnectUnixWrapper(config.hostsocket, config.connect_timeout, 0);
}
if (!context->err && config.tls) {
@@ -2356,6 +2360,9 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
output_raw = 1;
}
/* In a multi block, commands will return status strings instead of verbatim strings. */
if (config.in_multi) output_raw = 0;
if (!strcasecmp(command, "shutdown")) config.shutdown = 1;
if (!strcasecmp(command, "monitor")) config.monitor_mode = 1;
int is_subscribe =
@@ -2518,7 +2525,7 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
fflush(stdout);
redisFree(c);
c = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, config.connect_timeout);
c = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, config.connect_timeout, 0);
if (!c->err && config.tls) {
const char *err = NULL;
if (cliSecureConnection(c, config.sslconfig, &err) == REDIS_ERR && err) {
@@ -2601,7 +2608,7 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i], "--user") && !lastarg) {
config.conn_info.user = sdsnew(argv[++i]);
} else if (!strcmp(argv[i], "-u") && !lastarg) {
parseRedisUri(argv[++i], "valkey-cli", &config.conn_info, &config.tls);
parseUri(argv[++i], "valkey-cli", &config.conn_info, &config.tls);
if (config.conn_info.hostport < 0 || config.conn_info.hostport > 65535) {
fprintf(stderr, "Invalid server port.\n");
exit(1);
@@ -3949,7 +3956,7 @@ cleanup:
static int clusterManagerNodeConnect(clusterManagerNode *node) {
if (node->context) redisFree(node->context);
node->context = redisConnectWrapper(node->ip, node->port, config.connect_timeout);
node->context = redisConnectWrapper(node->ip, node->port, config.connect_timeout, 0);
if (!node->context->err && config.tls) {
const char *err = NULL;
if (cliSecureConnection(node->context, config.sslconfig, &err) == REDIS_ERR && err) {
@@ -7666,7 +7673,7 @@ static int clusterManagerCommandImport(int argc, char **argv) {
char *reply_err = NULL;
redisReply *src_reply = NULL;
// Connect to the source node.
redisContext *src_ctx = redisConnectWrapper(src_ip, src_port, config.connect_timeout);
redisContext *src_ctx = redisConnectWrapper(src_ip, src_port, config.connect_timeout, 0);
if (src_ctx->err) {
success = 0;
fprintf(stderr, "Could not connect to Valkey at %s:%d: %s.\n", src_ip, src_port, src_ctx->errstr);
+6 -1
View File
@@ -325,10 +325,15 @@ typedef uint64_t ValkeyModuleTimerID;
* If enabled, the module is responsible to break endless loop. */
#define VALKEYMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS (1 << 3)
/* Skipping command validation can improve performance by reducing the overhead associated
* with command checking, especially in high-throughput scenarios where commands
* are already pre-validated or trusted. */
#define VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION (1 << 4)
/* Next option flag, must be updated when adding new module flags above!
* This flag should not be used directly by the module.
* Use ValkeyModule_GetModuleOptionsAll instead. */
#define _VALKEYMODULE_OPTIONS_FLAGS_NEXT (1 << 4)
#define _VALKEYMODULE_OPTIONS_FLAGS_NEXT (1 << 5)
/* Definitions for ValkeyModule_SetCommandInfo. */
+3 -3
View File
@@ -4,13 +4,13 @@
* similar. */
#define SERVER_NAME "valkey"
#define SERVER_TITLE "Valkey"
#define VALKEY_VERSION "255.255.255"
#define VALKEY_VERSION_NUM 0x00ffffff
#define VALKEY_VERSION "8.1.0"
#define VALKEY_VERSION_NUM 0x00080100
/* The release stage is used in order to provide release status information.
* In unstable branch the status is always "dev".
* During release process the status will be set to rc1,rc2...rcN.
* When the version is released the status will be "ga". */
#define VALKEY_RELEASE_STAGE "dev"
#define VALKEY_RELEASE_STAGE "ga"
/* Redis OSS compatibility version, should never
* exceed 7.2.x. */
+21
View File
@@ -109,6 +109,27 @@ if {!$::valgrind} {
assert_equal "PONG" [r ping]
}
}
# test hide-user-data-from-log
set server_path [tmpdir server5.log]
start_server [list overrides [list dir $server_path crash-memcheck-enabled no hide-user-data-from-log no]] {
test "Config hide-user-data-from-log is off" {
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$blabla\r\n"
r flush
catch {r debug segfault}
wait_for_log_messages 0 {"*Query buffer: *\$blabla*"} 0 10 1000
}
}
set server_path [tmpdir server6.log]
start_server [list overrides [list dir $server_path crash-memcheck-enabled no hide-user-data-from-log yes]] {
test "Config hide-user-data-from-log is on" {
r write "*3\r\n\$3\r\nSET\r\n\$1\r\nx\r\n\$blabla\r\n"
r flush
catch {r debug segfault}
wait_for_log_messages 0 {"*Query buffer: *redacted*"} 0 10 1000
}
}
}
# test DEBUG ASSERT
+12
View File
@@ -46,6 +46,15 @@ int PingallCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
return ValkeyModule_ReplyWithSimpleString(ctx, "OK");
}
void DingReceiver(ValkeyModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {
ValkeyModule_Log(ctx, "notice", "DING (type %d) RECEIVED from %.*s: '%.*s'", type, VALKEYMODULE_NODE_ID_LEN, sender_id, (int)len, payload);
ValkeyModule_SendClusterMessage(ctx, sender_id, MSGTYPE_DONG, "Message Received!", 17);
}
void DongReceiver(ValkeyModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len) {
ValkeyModule_Log(ctx, "notice", "DONG (type %d) RECEIVED from %.*s: '%.*s'", type, VALKEYMODULE_NODE_ID_LEN, sender_id, (int)len, payload);
}
int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) {
VALKEYMODULE_NOT_USED(argv);
VALKEYMODULE_NOT_USED(argc);
@@ -63,5 +72,8 @@ int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int arg
if (ValkeyModule_CreateCommand(ctx, "test.cluster_shards", test_cluster_shards, "", 0, 0, 0) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
/* Register our handlers for different message types. */
ValkeyModule_RegisterClusterMessageReceiver(ctx, MSGTYPE_DING, DingReceiver);
ValkeyModule_RegisterClusterMessageReceiver(ctx, MSGTYPE_DONG, DongReceiver);
return VALKEYMODULE_OK;
}
+3
View File
@@ -703,6 +703,9 @@ int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int arg
return VALKEYMODULE_ERR;
}
/* This option tests skip command validation for ValkeyModule_EmitAOF */
ValkeyModule_SetModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);
if (ValkeyModule_CreateCommand(ctx, "mem.alloc", MemAlloc_RedisCommand, "write deny-oom", 1, 1, 1) == VALKEYMODULE_ERR) {
return VALKEYMODULE_ERR;
}
+7 -4
View File
@@ -249,8 +249,8 @@ int propagateTestSimpleCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv,
VALKEYMODULE_NOT_USED(argc);
/* Replicate two commands to test MULTI/EXEC wrapping. */
ValkeyModule_Replicate(ctx,"INCR","c","counter-1");
ValkeyModule_Replicate(ctx,"INCR","c","counter-2");
ValkeyModule_Replicate(ctx, "INCR", "c", "counter-1");
ValkeyModule_Replicate(ctx, "INCR", "c", "counter-2");
ValkeyModule_ReplyWithSimpleString(ctx,"OK");
return VALKEYMODULE_OK;
}
@@ -265,8 +265,8 @@ int propagateTestMixedCommand(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, i
reply = ValkeyModule_Call(ctx, "INCR", "c!", "using-call");
ValkeyModule_FreeCallReply(reply);
ValkeyModule_Replicate(ctx,"INCR","c","counter-1");
ValkeyModule_Replicate(ctx,"INCR","c","counter-2");
ValkeyModule_Replicate(ctx, "INCR", "c", "counter-1");
ValkeyModule_Replicate(ctx, "INCR", "c", "counter-2");
reply = ValkeyModule_Call(ctx, "INCR", "c!", "after-call");
ValkeyModule_FreeCallReply(reply);
@@ -347,6 +347,9 @@ int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int arg
detached_ctx = ValkeyModule_GetDetachedThreadSafeContext(ctx);
/* This option tests skip command validation for ValkeyModule_Replicate */
ValkeyModule_SetModuleOptions(ctx, VALKEYMODULE_OPTIONS_SKIP_COMMAND_VALIDATION);
if (ValkeyModule_SubscribeToKeyspaceEvents(ctx, VALKEYMODULE_NOTIFY_ALL, KeySpace_NotificationGeneric) == VALKEYMODULE_ERR)
return VALKEYMODULE_ERR;
+6 -3
View File
@@ -27,7 +27,7 @@ def build_program():
return 0
# iterate /sys/class/infiniband, find any usable RDMA device, and return IPv4 address
# iterate /sys/class/infiniband, find any usable RDMA device, and return IPv4/IPV6 address
def find_rdma_dev():
# Ex, /sys/class/infiniband/mlx5_0
# Ex, /sys/class/infiniband/rxe_eth0
@@ -39,9 +39,12 @@ def find_rdma_dev():
netdev = ibclass + dev + "/ports/1/gid_attrs/ndevs/0"
with open(netdev) as fp:
addrs = netifaces.ifaddresses(fp.readline().strip("\n"))
if netifaces.AF_INET not in addrs:
if netifaces.AF_INET in addrs:
ipaddr = addrs[netifaces.AF_INET][0]["addr"]
elif netifaces.AF_INET6 in addrs:
ipaddr = addrs[netifaces.AF_INET6][0]["addr"]
else:
continue
ipaddr = addrs[netifaces.AF_INET][0]["addr"]
print("Valkey Over RDMA test prepare " + dev + " <" + ipaddr + "> [OK]")
return ipaddr
except os.error:
+6 -2
View File
@@ -124,8 +124,9 @@ proc assert_refcount_morethan {key ref} {
# Wait for the specified condition to be true, with the specified number of
# max retries and delay between retries. Otherwise the 'elsescript' is
# executed.
proc wait_for_condition {maxtries delay e _else_ elsescript} {
# executed. If 'debugscript' is provided, it is executed after failure of
# the confition (before the retry delay).
proc wait_for_condition {maxtries delay e _else_ elsescript {_debug_ ""} {debugscript ""}} {
while {[incr maxtries -1] >= 0} {
set errcode [catch {uplevel 1 [list expr $e]} result]
if {$errcode == 0} {
@@ -133,6 +134,9 @@ proc wait_for_condition {maxtries delay e _else_ elsescript} {
} else {
return -code $errcode $result
}
if {$_debug_ == "debug"} {
uplevel 1 $debugscript
}
after $delay
}
if {$maxtries == -1} {
+38
View File
@@ -1306,3 +1306,41 @@ tags {acl external:skip} {
}
}
}
set server_path [tmpdir "server.acl"]
exec cp -f tests/assets/user.acl $server_path
start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"] tags {"repl external:skip"}] {
set primary [srv 0 client]
set primary_host [srv 0 host]
set primary_port [srv 0 port]
test "Test ACL LOAD works on primary" {
exec cp -f tests/assets/user.acl $server_path
$primary ACL setuser harry on nopass resetchannels &test +@all ~*
$primary ACL save
$primary ACL load
$primary AUTH harry anything
}
start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"]] {
set replica [srv 0 client]
$replica replicaof $primary_host $primary_port
test "Test ACL LOAD works on replica" {
# wait for replication to be in sync
wait_for_condition 50 100 {
[lindex [$replica role] 0] eq {slave} &&
[string match {*master_link_status:up*} [$replica info replication]]
} else {
fail "Can't turn the instance into a replica"
}
# Check ACL LOAD works as expected
exec cp -f tests/assets/user.acl $server_path
$replica ACL setuser joe on nopass resetchannels &test +@all ~*
$replica ACL save
$replica ACL load
$replica AUTH joe anything
}
}
}
+1 -1
View File
@@ -631,7 +631,7 @@ start_server {tags {"bitops"}} {
}
run_solo {bitops-large-memory} {
start_server {tags {"bitops"}} {
start_server {tags {"bitops large-memory"}} {
test "BIT pos larger than UINT_MAX" {
set bytes [expr (1 << 29) + 1]
set bitpos [expr (1 << 32)]
File diff suppressed because it is too large Load Diff
+11
View File
@@ -21,6 +21,17 @@ start_cluster 3 0 [list config_lines $modules] {
} else {
fail "node 2 or node 3 didn't receive cluster module message"
}
verify_log_message -1 "*DING (type 1) RECEIVED*Hey*" 0
verify_log_message -2 "*DING (type 1) RECEIVED*Hey*" 0
}
test "Cluster module receive message API - VM_RegisterClusterMessageReceiver" {
wait_for_condition 50 100 {
[CI 0 cluster_stats_messages_module_received] eq 2
} else {
fail "node 1 didn't receive DONG messages"
}
assert_equal 2 [count_log_message 0 "* <cluster> DONG (type 2) RECEIVED*"]
}
}
+2
View File
@@ -597,11 +597,13 @@ tags "modules" {
assert_equal [$master get k1] 1
assert_equal [$master ttl k1] -1
wait_for_ofs_sync $master $replica
assert_equal [$replica get k1] 1
assert_equal [$replica ttl k1] -1
# Validate that replicated commands were "obeyed" from primary.
assert_equal [$replica propagate-test.obeyed] 1
$master propagate-test.incr k1
wait_for_ofs_sync $master $replica
assert_equal [$replica propagate-test.obeyed] 2
}
+27 -17
View File
@@ -61,23 +61,6 @@ start_server {tags {"modules"}} {
assert_equal $result $n
}
test {Module stream XADD big fields doesn't create empty key} {
set original_proto [config_get_set proto-max-bulk-len 2147483647] ;#2gb
set original_query [config_get_set client-query-buffer-limit 2147483647] ;#2gb
r del mystream
r write "*4\r\n\$10\r\nstream.add\r\n\$8\r\nmystream\r\n\$5\r\nfield\r\n"
catch {
write_big_bulk 1073741824 ;#1gb
} err
assert {$err eq "ERR StreamAdd failed"}
assert_equal 0 [r exists mystream]
# restore defaults
r config set proto-max-bulk-len $original_proto
r config set client-query-buffer-limit $original_query
} {OK} {large-memory}
test {Module stream iterator} {
r del mystream
set streamid1 [r xadd mystream * item 1 value a]
@@ -174,3 +157,30 @@ start_server {tags {"modules"}} {
assert_equal {OK} [r module unload stream]
}
}
run_solo {stream-large-memory} {
set testmodule [file normalize tests/modules/stream.so]
start_server {tags {"modules large-memory"}} {
r module load $testmodule
test {Module stream XADD big fields doesn't create empty key} {
set original_proto [config_get_set proto-max-bulk-len 2147483647] ;#2gb
set original_query [config_get_set client-query-buffer-limit 2147483647] ;#2gb
r del mystream
r write "*4\r\n\$10\r\nstream.add\r\n\$8\r\nmystream\r\n\$5\r\nfield\r\n"
catch {
write_big_bulk 1073741824 ;#1gb
} err
assert {$err eq "ERR StreamAdd failed"}
assert_equal 0 [r exists mystream]
# restore defaults
r config set proto-max-bulk-len $original_proto
r config set client-query-buffer-limit $original_query
} {OK} {large-memory}
assert_equal {OK} [r module unload stream]
}
}
+1
View File
@@ -515,6 +515,7 @@ start_server {tags {"multi"}} {
{select *}
{set foo bar}
}
close_replication_stream $repl
r replicaof no one
} {OK} {needs:repl cluster:skip}
+22
View File
@@ -412,6 +412,28 @@ start_server {tags {"pause network"}} {
} {bar2}
}
test "Test the randomkey command will not cause the server to get into an infinite loop during the client pause write" {
# first, clear the database to avoid interference from existing keys on the test results
r flushall
# then set a key with expire time
r set key value px 3
# set pause-write model and wait key expired
r client pause 10000 write
after 5
wait_for_condition 50 100 {
[r randomkey] == "key"
} else {
fail "execute randomkey failed, caused by the infinite loop"
}
r client unpause
assert_equal [r randomkey] {}
}
# Make sure we unpause at the end
r client unpause
}
+4 -2
View File
@@ -241,6 +241,7 @@ start_server {tags {"tracking network logreqres:skip"}} {
# make sure r is working resp 3
r HELLO 3
r CLIENT TRACKING on REDIRECT $redir_id
set redir_error "tracking-redir-broken $redir_id"
$rd_sg SET key1 1
r GET key1
$rd_redirection QUIT
@@ -257,11 +258,12 @@ start_server {tags {"tracking network logreqres:skip"}} {
# Wait to read the tracking-redir-broken
wait_for_condition 1000 50 {
[lsearch -exact [r PING] "tracking-redir-broken"]
[set response [r PING]] != "PONG"
} else {
fail "Failed to get redirect broken indication"
}
# Consume PING reply
assert_equal $redir_error $response
# Consume PING reply
assert_equal PONG [r read]
}
+1 -1
View File
@@ -282,7 +282,7 @@ foreach type {listpack quicklist} {
}
run_solo {list-large-memory} {
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
# test if the server supports such large configs (avoid 32 bit builds)
catch {
+1 -1
View File
@@ -1169,7 +1169,7 @@ foreach type {single multiple single_multiple} {
}
run_solo {set-large-memory} {
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
# test if the server supports such large configs (avoid 32 bit builds)
catch {
+6 -6
View File
@@ -1,7 +1,7 @@
# One XADD with one huge 5GB field
# Expected to fail resulting in an empty stream
run_solo {violations} {
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
test {XADD one huge field} {
r config set proto-max-bulk-len 10000000000 ;#10gb
r config set client-query-buffer-limit 10000000000 ;#10gb
@@ -18,7 +18,7 @@ start_server [list overrides [list save ""] ] {
# One XADD with one huge (exactly nearly) 4GB field
# This uncovers the overflow in lpEncodeGetType
# Expected to fail resulting in an empty stream
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
test {XADD one huge field - 1} {
r config set proto-max-bulk-len 10000000000 ;#10gb
r config set client-query-buffer-limit 10000000000 ;#10gb
@@ -33,7 +33,7 @@ start_server [list overrides [list save ""] ] {
}
# Gradually add big stream fields using repeated XADD calls
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
test {several XADD big fields} {
r config set stream-node-max-bytes 0
for {set j 0} {$j<10} {incr j} {
@@ -46,7 +46,7 @@ start_server [list overrides [list save ""] ] {
# Add over 4GB to a single stream listpack (one XADD command)
# Expected to fail resulting in an empty stream
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
test {single XADD big fields} {
r write "*23\r\n\$4\r\nXADD\r\n\$1\r\nS\r\n\$1\r\n*\r\n"
for {set j 0} {$j<10} {incr j} {
@@ -63,7 +63,7 @@ start_server [list overrides [list save ""] ] {
# Gradually add big hash fields using repeated HSET calls
# This reproduces the overflow in the call to ziplistResize
# Object will be converted to hashtable encoding
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
r config set hash-max-ziplist-value 1000000000 ;#1gb
test {hash with many big fields} {
for {set j 0} {$j<10} {incr j} {
@@ -75,7 +75,7 @@ start_server [list overrides [list save ""] ] {
# Add over 4GB to a single hash field (one HSET command)
# Object will be converted to hashtable encoding
start_server [list overrides [list save ""] ] {
start_server [list overrides [list save ""] tags {"large-memory"}] {
test {hash with one huge field} {
catch {r config set hash-max-ziplist-value 10000000000} ;#10gb
r config set proto-max-bulk-len 10000000000 ;#10gb
+20 -2
View File
@@ -1320,8 +1320,8 @@ acllog-max-len 128
# replica-ignore-maxmemory yes
# The server reclaims expired keys in two ways: upon access when those keys are
# found to be expired, and also in background, in what is called the
# "active expire key". The key space is slowly and interactively scanned
# found to be expired, and also in the background, in what is called the
# "active expire key". The key space is slowly and incrementally scanned
# looking for expired keys to reclaim, so that it is possible to free memory
# of keys that are expired and will never be accessed again in a short time.
#
@@ -1841,6 +1841,24 @@ aof-timestamp-enabled no
#
# cluster-replica-no-failover no
# The timeout in milliseconds for cluster manual failover. If a manual failover
# does not complete within the specified time, both the replica and the primary
# will abort it.
#
# A manual failover is a special kind of failover that is usually executed when
# there are no actual failures, and we wish to swap the current primary with one
# of its replicas in a safe way, without any window for data loss.
#
# To avoid data loss, the primary and the replica need to wait for each other for
# a period of time, the primary need to pause the clients writes to stop processing
# traffic. The default failover timeout is 5000ms, it is possible to configure the
# timeout and decide how long the primary will pause in the worst case scenario,
# i.e. the manual failover timed out due to the insufficient votes.
#
# Check https://valkey.io/commands/cluster-failover/ for more information.
#
# cluster-manual-failover-timeout 5000
# This option, when set to yes, allows nodes to serve read traffic while the
# cluster is in a down state, as long as it believes it owns the slots.
#