Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c59c9365be | ||
|
|
c1e23f5e83 | ||
|
|
05971a7a22 | ||
|
|
1c047b6866 | ||
|
|
94a8021d33 | ||
|
|
4a341aecd8 | ||
|
|
053081a222 | ||
|
|
8229c562e3 | ||
|
|
1d2e5a6e74 | ||
|
|
380e7df718 | ||
|
|
7ced02a356 | ||
|
|
1e7ca901c9 | ||
|
|
623f5e655c | ||
|
|
3144475d4a | ||
|
|
cfc8ac1fd9 | ||
|
|
76a46c3cdd | ||
|
|
e6673e44c7 | ||
|
|
8c762d92ba | ||
|
|
6a48dd7340 | ||
|
|
9680d1b462 | ||
|
|
514999c46e | ||
|
|
e29885f13a | ||
|
|
3bf92ad060 | ||
|
|
8f38cf7f0d | ||
|
|
7ce82ac723 | ||
|
|
7f7bbed8e6 | ||
|
|
91b369b0ea | ||
|
|
0008486b75 |
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report about a problem with Redis for Windows
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug/issue**
|
||||
A clear and concise description of what the bug/issue is.
|
||||
|
||||
**To Reproduce**
|
||||
Please provide more information about:
|
||||
- version of Redis for Windows you are using
|
||||
- configuration of your instance if it's not out-of-the-box one (i.e. persistence type)
|
||||
- typical workload handled by your instance
|
||||
- any other details that might be helpful in reproducing the issue.
|
||||
|
||||
**Crash report**
|
||||
If Redis generated a crash report - please paste it here.
|
||||
|
||||
**INFO output**
|
||||
If possible - please provide output of `INFO` command that might be helpful in analyzing your issue.
|
||||
|
||||
**Event Log**
|
||||
Please check your system's Event Log to see if there are additional details logged related to this issue.
|
||||
|
||||
**Additional context**
|
||||
Please provide Windows version you are using, mode in which you are starting Redis (standalone, cluster, Sentinel), etc.
|
||||
+114
@@ -11,6 +11,120 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
SECURITY: There are security fixes in the release.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
================================================================================
|
||||
Redis 5.0.10 Released Mon Oct 26 09:21:49 IST 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency: SECURITY if you use an affected platform (see below).
|
||||
Otherwise the upgrade urgency is MODERATE.
|
||||
|
||||
This release fixes a potential heap overflow when using a heap allocator other
|
||||
than jemalloc or glibc's malloc. See:
|
||||
https://github.com/redis/redis/pull/7963
|
||||
|
||||
Other fixes in this release:
|
||||
|
||||
* Avoid case of Lua scripts being consistently aborted due to OOM
|
||||
* XPENDING will not update consumer's seen-time
|
||||
* A blocked XREADGROUP didn't propagated the XSETID to replicas / AOF
|
||||
* UNLINK support for streams
|
||||
* RESTORE ABSTTL won't store expired keys into the DB
|
||||
* Hide AUTH from MONITOR
|
||||
* Cluster: reduce spurious PFAIL/FAIL states upon delayed PONG receival
|
||||
* Cluster: Fix case of clusters mixing accidentally by gossip
|
||||
* Cluster: Allow blocked XREAD on a cluster replica
|
||||
* Cluster: Optimize memory usage CLUSTER SLOTS command
|
||||
* RedisModule_ValueLength support for stream data type
|
||||
* Minor fixes in redis-check-rdb and redis-cli
|
||||
* Fix redis-check-rdb support for modules aux data
|
||||
* Add fsync in replica when full RDB payload was received
|
||||
|
||||
Full list of commits:
|
||||
|
||||
Yossi Gottlieb in commit ce0d74d8f:
|
||||
Fix wrong zmalloc_size() assumption. (#7963)
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 066699240:
|
||||
Backport Lua 5.2.2 stack overflow fix. (#7733)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
WuYunlong in commit 8a90c7ef3:
|
||||
Add fsync to readSyncBulkPayload(). (#7839)
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
Ariel Shtul in commit f0df2bb3c:
|
||||
Fix redis-check-rdb support for modules aux data (#7826)
|
||||
3 files changed, 21 insertions(+), 1 deletion(-)
|
||||
|
||||
hwware in commit 7add2a412:
|
||||
fix memory leak in sentinel connection sharing
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 315e648f8:
|
||||
Allow blocked XREAD on a cluster replica (#7881)
|
||||
3 files changed, 100 insertions(+), 2 deletions(-)
|
||||
|
||||
guybe7 in commit 4967ee94e:
|
||||
Modules: Invalidate saved_oparray after use (#7688)
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
antirez in commit 065003e8f:
|
||||
Modules: remove spurious call from moduleHandleBlockedClients().
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
Angus Pearson in commit 6cdf62928:
|
||||
Fix broken interval and repeat bahaviour in redis-cli (incluing cluster mode)
|
||||
1 file changed, 11 insertions(+), 6 deletions(-)
|
||||
|
||||
antirez in commit cb6a4971c:
|
||||
Cluster: introduce data_received field.
|
||||
2 files changed, 27 insertions(+), 10 deletions(-)
|
||||
|
||||
Madelyn Olson in commit 83f4de865:
|
||||
Hide AUTH from monitor
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Guy Benoish in commit 3ba08d185:
|
||||
Support streams in general module API functions
|
||||
3 files changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
Itamar Haber in commit 109c0635c:
|
||||
Expands lazyfree's effort estimate to include Streams (#5794)
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
huangzhw in commit 235210d5b:
|
||||
defrag.c activeDefragSdsListAndDict when defrag sdsele, We can't use (#7492)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit fdd3162fe:
|
||||
RESTORE ABSTTL skip expired keys - leak (#7511)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 6139d6d18:
|
||||
RESTORE ABSTTL won't store expired keys into the db (#7472)
|
||||
4 files changed, 45 insertions(+), 15 deletions(-)
|
||||
|
||||
Liu Zhen in commit 0f502c58d:
|
||||
fix clusters mixing accidentally by gossip
|
||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
Guy Benoish in commit 37fd50718:
|
||||
XPENDING should not update consumer's seen-time
|
||||
4 files changed, 30 insertions(+), 18 deletions(-)
|
||||
|
||||
antirez in commit a3ca53e4a:
|
||||
Also use propagate() in streamPropagateGroupID().
|
||||
1 file changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
yanhui13 in commit 7a62eb96e:
|
||||
optimize the output of cluster slots
|
||||
1 file changed, 7 insertions(+), 4 deletions(-)
|
||||
|
||||
srzhao in commit 0efb93d0c:
|
||||
Check OOM at script start to get stable lua OOM state.
|
||||
3 files changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
================================================================================
|
||||
Redis 5.0.9 Released Thu Apr 17 12:41:00 CET 2020
|
||||
================================================================================
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Redis 5.0.9 for Windows
|
||||
# Redis 5.0.14 for Windows
|
||||
|
||||
Branch [win-5.0 branch](https://github.com/tporadowski/redis/tree/win-5.0) at the moment provides version of Redis 5.0.9. It will be released shortly!
|
||||
You can find the release of **Redis 5.0.14 for Windows** on [releases page](https://github.com/tporadowski/redis/releases). Please test it and [report any issues](https://github.com/tporadowski/redis/wiki/Submitting-an-Issue), thanks in advance!
|
||||
|
||||
## Redis 4.0.14 for Windows
|
||||
|
||||
@@ -8,7 +8,7 @@ You can find the release of **Redis 4.0.14 for Windows** on [releases page](http
|
||||
|
||||
**DISCLAIMER**
|
||||
|
||||
At the moment [win-4.0.14 branch](https://github.com/tporadowski/redis/tree/win-4.0.14) provides a **stable port of [Redis 4.0.14](https://github.com/antirez/redis/releases/tag/4.0.14) for Windows x64** merged with archived port of [win-3.2.100 version](https://github.com/MicrosoftArchive/redis/releases/tag/win-3.2.100) from MS Open Tech team. Since the latter is no longer maintained - the sources were merged by hand, projects updated to Visual Studio 2019 (v16.2.5) and any findings (mostly via unit tests) were fixed.
|
||||
At the moment [win-4.0.14 branch](https://github.com/tporadowski/redis/tree/win-4.0.14) provides a **stable port of [Redis 4.0.14](https://github.com/antirez/redis/releases/tag/4.0.14) for Windows x64** and [win-5.0 branch](https://github.com/tporadowski/redis/tree/win-5.0) provides a **stable port of [Redis 5.0.14](https://github.com/redis/redis/releases/tag/5.0.14) for Windows x64**, both merged with archived port of [win-3.2.100 version](https://github.com/MicrosoftArchive/redis/releases/tag/win-3.2.100) from MS Open Tech team. Since the latter is no longer maintained - the sources were merged by hand, projects updated to Visual Studio 2019 (v16.2.5) and any findings (mostly via unit tests) were fixed.
|
||||
|
||||
You can find the original description of what this fork provides, how it evolved, what are its requirements, etc. on Wiki: https://github.com/tporadowski/redis/wiki/Old-MSOpenTech-redis-README.md
|
||||
|
||||
|
||||
@@ -6,6 +6,16 @@ For release notes related to original Redis project - please see 00-RELEASENOTES
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
2020-11-08: Redis 5.0.10 for Windows
|
||||
https://github.com/tporadowski/redis/releases/tag/v5.0.10
|
||||
|
||||
Bugfix/maintenance release of Redis for Windows, updated to be in sync with
|
||||
redis/5.0.10 (https://github.com/redis/redis/releases/tag/5.0.10)
|
||||
|
||||
NOTE: active memory defragmentation feature ("activedefrag" option) is turned OFF.
|
||||
|
||||
--------------------------------------------------------
|
||||
|
||||
2020-05-02: Redis 5.0.9 for Windows
|
||||
https://github.com/tporadowski/redis/releases/tag/v5.0.9
|
||||
|
||||
|
||||
Vendored
+13
@@ -42,6 +42,19 @@ the following additional steps:
|
||||
changed, otherwise you could just copy the old implementation if you are
|
||||
upgrading just to a similar version of Jemalloc.
|
||||
|
||||
Jemalloc for Redis for Windows
|
||||
---
|
||||
|
||||
In order to reconfigure `jemalloc` for Redis for Windows - please follow the steps from `msvc/ReadMe.txt` file. Due to memory allocation tracking in Windows
|
||||
port - options `--with-lg-page` and `--with-lg-hugepage` must be set to the same value (which is now `22` and it defines a `1 << 22` page size = 4MB).
|
||||
|
||||
`jemalloc` has been confiured with:
|
||||
```
|
||||
sh -c "CC=cl ./autogen.sh --with-lg-quantum=3 --with-lg-page=22 --with-lg-hugepage=22 --with-version=5.2.1-0-g6619f70ffc9b53ba5d3b38c8c3d5676905242c87"
|
||||
```
|
||||
|
||||
**NOTE**: active defragmentation is **switched OFF** for now as the Redis-specific modification that supports that feature seems to not work properly (either in jemalooc 5.2.1 or together with memory allocation code specific to Windows port of Redis).
|
||||
|
||||
Geohash
|
||||
---
|
||||
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
@BINDIR@/jemalloc-config <option>
|
||||
Options:
|
||||
--help | -h : Print usage.
|
||||
--version : Print jemalloc version.
|
||||
--revision : Print shared library revision number.
|
||||
--config : Print configure options used to build jemalloc.
|
||||
--prefix : Print installation directory prefix.
|
||||
--bindir : Print binary installation directory.
|
||||
--datadir : Print data installation directory.
|
||||
--includedir : Print include installation directory.
|
||||
--libdir : Print library installation directory.
|
||||
--mandir : Print manual page installation directory.
|
||||
--cc : Print compiler used to build jemalloc.
|
||||
--cflags : Print compiler flags used to build jemalloc.
|
||||
--cppflags : Print preprocessor flags used to build jemalloc.
|
||||
--cxxflags : Print C++ compiler flags used to build jemalloc.
|
||||
--ldflags : Print library flags used to build jemalloc.
|
||||
--libs : Print libraries jemalloc was linked against.
|
||||
EOF
|
||||
}
|
||||
|
||||
prefix="@prefix@"
|
||||
exec_prefix="@exec_prefix@"
|
||||
|
||||
case "$1" in
|
||||
--help | -h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--version)
|
||||
echo "@jemalloc_version@"
|
||||
;;
|
||||
--revision)
|
||||
echo "@rev@"
|
||||
;;
|
||||
--config)
|
||||
echo "@CONFIG@"
|
||||
;;
|
||||
--prefix)
|
||||
echo "@PREFIX@"
|
||||
;;
|
||||
--bindir)
|
||||
echo "@BINDIR@"
|
||||
;;
|
||||
--datadir)
|
||||
echo "@DATADIR@"
|
||||
;;
|
||||
--includedir)
|
||||
echo "@INCLUDEDIR@"
|
||||
;;
|
||||
--libdir)
|
||||
echo "@LIBDIR@"
|
||||
;;
|
||||
--mandir)
|
||||
echo "@MANDIR@"
|
||||
;;
|
||||
--cc)
|
||||
echo "@CC@"
|
||||
;;
|
||||
--cflags)
|
||||
echo "@CFLAGS@"
|
||||
;;
|
||||
--cppflags)
|
||||
echo "@CPPFLAGS@"
|
||||
;;
|
||||
--cxxflags)
|
||||
echo "@CXXFLAGS@"
|
||||
;;
|
||||
--ldflags)
|
||||
echo "@LDFLAGS@ @EXTRA_LDFLAGS@"
|
||||
;;
|
||||
--libs)
|
||||
echo "@LIBS@"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
esac
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
|
||||
@LD_PRELOAD_VAR@=${libdir}/libjemalloc.@SOREV@
|
||||
export @LD_PRELOAD_VAR@
|
||||
exec "$@"
|
||||
Vendored
+5625
File diff suppressed because it is too large
Load Diff
@@ -173,7 +173,7 @@
|
||||
* Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
|
||||
* classes).
|
||||
*/
|
||||
/* #undef LG_QUANTUM */
|
||||
#define LG_QUANTUM 3
|
||||
|
||||
/* One page is 2^LG_PAGE bytes. */
|
||||
#define LG_PAGE 22
|
||||
|
||||
@@ -219,4 +219,32 @@ ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
newsize);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE int
|
||||
iget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {
|
||||
int defrag = 0;
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
szind_t szind;
|
||||
bool is_slab;
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr, true, &szind, &is_slab);
|
||||
if (likely(is_slab)) {
|
||||
/* Small allocation. */
|
||||
extent_t *slab = iealloc(tsdn, ptr);
|
||||
arena_t *arena = extent_arena_get(slab);
|
||||
szind_t binind = extent_szind_get(slab);
|
||||
bin_t *bin = arena->bins[binind].bin_shards;
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
/* don't bother moving allocations from the slab currently used for new allocations */
|
||||
if (slab != bin->slabcur) {
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
size_t availregs = bin_info->nregs * bin->stats.curslabs;
|
||||
*bin_util = ((long long)bin->stats.curregs<<16) / availregs;
|
||||
*run_util = ((long long)(bin_info->nregs - extent_nfree_get(slab))<<16) / bin_info->nregs;
|
||||
defrag = 1;
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
return defrag;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INLINES_C_H */
|
||||
|
||||
@@ -16,7 +16,7 @@ BEGIN {
|
||||
je_malloc_stats_print \
|
||||
je_malloc_usable_size \
|
||||
je_mallocx \
|
||||
je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 \
|
||||
je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 \
|
||||
je_nallocx \
|
||||
je_posix_memalign \
|
||||
je_rallocx \
|
||||
|
||||
@@ -16,7 +16,7 @@ BEGIN {
|
||||
jet_malloc_stats_print \
|
||||
jet_malloc_usable_size \
|
||||
jet_mallocx \
|
||||
jet_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 \
|
||||
jet_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 \
|
||||
jet_nallocx \
|
||||
jet_posix_memalign \
|
||||
jet_rallocx \
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#define je_malloc_stats_print JEMALLOC_N(malloc_stats_print)
|
||||
#define je_malloc_usable_size JEMALLOC_N(malloc_usable_size)
|
||||
#define je_mallocx JEMALLOC_N(mallocx)
|
||||
#define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 JEMALLOC_N(smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756)
|
||||
#define je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 JEMALLOC_N(smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87)
|
||||
#define je_nallocx JEMALLOC_N(nallocx)
|
||||
#define je_posix_memalign JEMALLOC_N(posix_memalign)
|
||||
#define je_rallocx JEMALLOC_N(rallocx)
|
||||
|
||||
@@ -11,7 +11,7 @@ malloc_message:je_malloc_message
|
||||
malloc_stats_print:je_malloc_stats_print
|
||||
malloc_usable_size:je_malloc_usable_size
|
||||
mallocx:je_mallocx
|
||||
smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756:je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87:je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
nallocx:je_nallocx
|
||||
posix_memalign:je_posix_memalign
|
||||
rallocx:je_rallocx
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#undef je_malloc_stats_print
|
||||
#undef je_malloc_usable_size
|
||||
#undef je_mallocx
|
||||
#undef je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
#undef je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
#undef je_nallocx
|
||||
#undef je_posix_memalign
|
||||
#undef je_rallocx
|
||||
|
||||
+11
-6
@@ -72,7 +72,7 @@ extern "C" {
|
||||
# define je_malloc_stats_print je_malloc_stats_print
|
||||
# define je_malloc_usable_size je_malloc_usable_size
|
||||
# define je_mallocx je_mallocx
|
||||
# define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# define je_nallocx je_nallocx
|
||||
# define je_posix_memalign je_posix_memalign
|
||||
# define je_rallocx je_rallocx
|
||||
@@ -88,13 +88,13 @@ extern "C" {
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JEMALLOC_VERSION "5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION "5.2.1-0-g6619f70ffc9b53ba5d3b38c8c3d5676905242c87"
|
||||
#define JEMALLOC_VERSION_MAJOR 5
|
||||
#define JEMALLOC_VERSION_MINOR 2
|
||||
#define JEMALLOC_VERSION_BUGFIX 1
|
||||
#define JEMALLOC_VERSION_NREV 0
|
||||
#define JEMALLOC_VERSION_GID "ea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION_GID_IDENT ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
#define JEMALLOC_VERSION_GID "6619f70ffc9b53ba5d3b38c8c3d5676905242c87"
|
||||
#define JEMALLOC_VERSION_GID_IDENT 6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
|
||||
#define MALLOCX_LG_ALIGN(la) ((int)(la))
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
@@ -212,6 +212,11 @@ extern "C" {
|
||||
# define JEMALLOC_ALLOCATOR
|
||||
#endif
|
||||
|
||||
/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()
|
||||
* function. */
|
||||
//[tporadowski] turned off as it no longer works properly and needs further investigation
|
||||
//#define JEMALLOC_FRAG_HINT
|
||||
|
||||
/*
|
||||
* The je_ prefix on the following public symbol declarations is an artifact
|
||||
* of namespace management, and should be omitted in application code unless
|
||||
@@ -381,7 +386,7 @@ struct extent_hooks_s {
|
||||
# define malloc_stats_print je_malloc_stats_print
|
||||
# define malloc_usable_size je_malloc_usable_size
|
||||
# define mallocx je_mallocx
|
||||
# define smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# define nallocx je_nallocx
|
||||
# define posix_memalign je_posix_memalign
|
||||
# define rallocx je_rallocx
|
||||
@@ -412,7 +417,7 @@ struct extent_hooks_s {
|
||||
# undef je_malloc_stats_print
|
||||
# undef je_malloc_usable_size
|
||||
# undef je_mallocx
|
||||
# undef je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# undef je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# undef je_nallocx
|
||||
# undef je_posix_memalign
|
||||
# undef je_rallocx
|
||||
|
||||
+9
-4
@@ -2,15 +2,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JEMALLOC_VERSION "5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION "5.2.1-0-g6619f70ffc9b53ba5d3b38c8c3d5676905242c87"
|
||||
#define JEMALLOC_VERSION_MAJOR 5
|
||||
#define JEMALLOC_VERSION_MINOR 2
|
||||
#define JEMALLOC_VERSION_BUGFIX 1
|
||||
#define JEMALLOC_VERSION_NREV 0
|
||||
#define JEMALLOC_VERSION_GID "ea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION_GID_IDENT ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
#define JEMALLOC_VERSION_GID "6619f70ffc9b53ba5d3b38c8c3d5676905242c87"
|
||||
#define JEMALLOC_VERSION_GID_IDENT 6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
|
||||
#define MALLOCX_LG_ALIGN(la) ((int)(la))
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
@@ -127,3 +127,8 @@
|
||||
# define JEMALLOC_RESTRICT_RETURN
|
||||
# define JEMALLOC_ALLOCATOR
|
||||
#endif
|
||||
|
||||
/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()
|
||||
* function. */
|
||||
//[tporadowski] turned off as it no longer works properly and needs further investigation
|
||||
//#define JEMALLOC_FRAG_HINT
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JEMALLOC_VERSION "@jemalloc_version@"
|
||||
#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@
|
||||
@@ -127,3 +127,8 @@
|
||||
# define JEMALLOC_RESTRICT_RETURN
|
||||
# define JEMALLOC_ALLOCATOR
|
||||
#endif
|
||||
|
||||
/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()
|
||||
* function. */
|
||||
//[tporadowski] turned off as it no longer works properly and needs further investigation
|
||||
//#define JEMALLOC_FRAG_HINT
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@
|
||||
# define malloc_stats_print je_malloc_stats_print
|
||||
# define malloc_usable_size je_malloc_usable_size
|
||||
# define mallocx je_mallocx
|
||||
# define smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# define nallocx je_nallocx
|
||||
# define posix_memalign je_posix_memalign
|
||||
# define rallocx je_rallocx
|
||||
@@ -53,7 +53,7 @@
|
||||
# undef je_malloc_stats_print
|
||||
# undef je_malloc_usable_size
|
||||
# undef je_mallocx
|
||||
# undef je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# undef je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# undef je_nallocx
|
||||
# undef je_posix_memalign
|
||||
# undef je_rallocx
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# define malloc_stats_print jet_malloc_stats_print
|
||||
# define malloc_usable_size jet_malloc_usable_size
|
||||
# define mallocx jet_mallocx
|
||||
# define smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 jet_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 jet_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# define nallocx jet_nallocx
|
||||
# define posix_memalign jet_posix_memalign
|
||||
# define rallocx jet_rallocx
|
||||
@@ -53,7 +53,7 @@
|
||||
# undef jet_malloc_stats_print
|
||||
# undef jet_malloc_usable_size
|
||||
# undef jet_mallocx
|
||||
# undef jet_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# undef jet_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# undef jet_nallocx
|
||||
# undef jet_posix_memalign
|
||||
# undef jet_rallocx
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
# define je_malloc_stats_print je_malloc_stats_print
|
||||
# define je_malloc_usable_size je_malloc_usable_size
|
||||
# define je_mallocx je_mallocx
|
||||
# define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87 je_smallocx_6619f70ffc9b53ba5d3b38c8c3d5676905242c87
|
||||
# define je_nallocx je_nallocx
|
||||
# define je_posix_memalign je_posix_memalign
|
||||
# define je_rallocx je_rallocx
|
||||
|
||||
Vendored
+12
@@ -3920,3 +3920,15 @@ jemalloc_postfork_child(void) {
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/* Helps the application decide if a pointer is worth re-allocating in order to reduce fragmentation.
|
||||
* returns 0 if the allocation is in the currently active run,
|
||||
* or when it is not causing any frag issue (large or huge bin)
|
||||
* returns the bin utilization and run utilization both in fixed point 16:16.
|
||||
* If the application decides to re-allocate it should use MALLOCX_TCACHE_NONE when doing so. */
|
||||
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
|
||||
je_get_defrag_hint(void* ptr, int *bin_util, int *run_util) {
|
||||
assert(ptr != NULL);
|
||||
return iget_defrag_hint(TSDN_NULL, ptr, bin_util, run_util);
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -274,7 +274,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||
CallInfo *ci;
|
||||
StkId st, base;
|
||||
Proto *p = cl->p;
|
||||
luaD_checkstack(L, p->maxstacksize);
|
||||
luaD_checkstack(L, p->maxstacksize + p->numparams);
|
||||
func = restorestack(L, funcr);
|
||||
if (!p->is_vararg) { /* no varargs? */
|
||||
base = func + 1;
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,0,9
|
||||
PRODUCTVERSION 5,0,9
|
||||
FILEVERSION 5,0,10
|
||||
PRODUCTVERSION 5,0,10
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,10 +68,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Poradowski.com Tomasz Poradowski"
|
||||
VALUE "FileDescription", "Redis for Windows, based on MS OpenTech port."
|
||||
VALUE "FileVersion", "5.0.9"
|
||||
VALUE "FileVersion", "5.0.10"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2006-2018 Salvatore Sanfilippo, Microsoft Open Technologies Inc., Tomasz Poradowski"
|
||||
VALUE "ProductName", "Redis for Windows"
|
||||
VALUE "ProductVersion", "5.0.9"
|
||||
VALUE "ProductVersion", "5.0.10"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -182,7 +182,7 @@ copy /Y $(OutputPath)redis-server.pdb $(OutputPath)redis-check-aof.pdb</Command>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>USE_JEMALLOC;_OFF_T_DEFINED;_WIN32;LACKS_STDLIB_H;NDEBUG;_CONSOLE;__x86_64__;%(PreprocessorDefinitions);_WIN32_WINNT=0x0501</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src;$(SolutionDir)..\deps\geohash-int;$(SolutionDir)..\deps\hiredis;$(SolutionDir)..\deps\jemalloc-5.2.1\include..\deps\jemalloc-5.2.1\include\msvc_compat</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src;$(SolutionDir)..\deps\geohash-int;$(SolutionDir)..\deps\hiredis;$(SolutionDir)..\deps\jemalloc-5.2.1\include;..\deps\jemalloc-5.2.1\include\msvc_compat</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<Product Id="*"
|
||||
Name="Redis on Windows"
|
||||
Language="1033"
|
||||
Version="5.0.9"
|
||||
Version="5.0.10"
|
||||
Manufacturer="Poradowski.com"
|
||||
UpgradeCode="{05410198-7212-4FC4-B7C8-AFEFC3DA0FBC}">
|
||||
<Package InstallerVersion="200"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>redis-64</id>
|
||||
<title>Redis 64-bit</title>
|
||||
<version>5.0.9</version>
|
||||
<version>5.0.10</version>
|
||||
<authors>Alexis Campailla, Enrico Giordani, Jonathan Pickett</authors>
|
||||
<owners>Microsoft Open Technologies, Inc.</owners>
|
||||
<description>A porting of Redis on Windows 64-bit.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>redis-64</id>
|
||||
<title>Redis 64-bit</title>
|
||||
<version>5.0.9</version>
|
||||
<version>5.0.10</version>
|
||||
<authors>Alexis Campailla, Enrico Giordani, Jonathan Pickett</authors>
|
||||
<owners>Microsoft Open Technologies, Inc.</owners>
|
||||
<description>A porting of Redis on Windows 64-bit.
|
||||
|
||||
@@ -289,20 +289,22 @@ public:
|
||||
subCommands = RedisParameterMapper
|
||||
{
|
||||
{ "monitor", &fp4 }, // sentinel monitor [master name] [ip] [port] [quorum]
|
||||
{ "auth-pass", &fp2 }, // sentinel auth-pass [master name] [password]
|
||||
{ "down-after-milliseconds", &fp2 }, // sentinel down-after-milliseconds [master name] [milliseconds]
|
||||
{ "parallel-syncs", &fp2 }, // sentinel parallel-syncs [master name] [number]
|
||||
{ "failover-timeout", &fp2 }, // sentinel failover-timeout [master name] [number]
|
||||
{ "parallel-syncs", &fp2 }, // sentinel parallel-syncs [master name] [number]
|
||||
{ "notification-script", &fp2 }, // sentinel notification-script [master name] [scriptPath]
|
||||
{ "client-reconfig-script", &fp2 }, // sentinel client-reconfig-script [master name] [scriptPath]
|
||||
{ "config-epoch", &fp2 }, // sentinel config-epoch [name] [epoch]
|
||||
{ "auth-pass", &fp2 }, // sentinel auth-pass [master name] [password]
|
||||
{ "current-epoch", &fp1 }, // sentinel current-epoch <epoch>
|
||||
{ "myid", &fp1 }, // sentinel myid <id>
|
||||
{ "config-epoch", &fp2 }, // sentinel config-epoch [name] [epoch]
|
||||
{ "leader-epoch", &fp2 }, // sentinel leader-epoch [name] [epoch]
|
||||
{ "known-slave", &fp3 }, // sentinel known-slave <name> <ip> <port>
|
||||
{ "known-replica", &fp3 }, // sentinel known-slave <name> <ip> <port>
|
||||
{ "known-sentinel", &fp4 }, // sentinel known-sentinel <name> <ip> <port> [runid]
|
||||
{ "rename-command", &fp3 }, // sentinel rename-command <name> <command> <renamed-command>
|
||||
{ "announce-ip", &fp1 }, // sentinel announce-ip <ip>
|
||||
{ "announce-port", &fp1 }, // sentinel announce-port <port>
|
||||
{ "myid", &fp1 }, // sentinel myid <id>
|
||||
{ "deny-scripts-reconfig", &fp1 } // sentinel deny-scripts-reconfig [yes/no]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace Globals
|
||||
/* This function is used to force the VEH on the entire size of the buffer length,
|
||||
* in the event that the buffer crosses the memory page boundaries */
|
||||
void EnsureMemoryIsMapped(const void *buffer, size_t size) {
|
||||
//[tporadowski/#62] when this is called from main process - Globals::pageSize is not initialized,
|
||||
// so prevent EXCEPTION_INT_DIVIDE_BY_ZERO
|
||||
if (Globals::pageSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* pFirstByte = (char*) buffer;
|
||||
char* pLastByte = (char*) buffer + size - 1;
|
||||
char* pFirstPage = pFirstByte - ((size_t) pFirstByte % Globals::pageSize);
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
#include "Win32_EventLog.h"
|
||||
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#include <jemalloc/internal/jemalloc_internal_defs.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
@@ -163,9 +164,9 @@ extern "C"
|
||||
PORT_LONGLONG memtoll(const char *p, int *err); // Forward def from util.h
|
||||
}
|
||||
|
||||
const size_t cAllocationGranularity = 1 << 22; // 4MB per heap block (matches the default allocation threshold of jemalloc)
|
||||
const size_t cAllocationGranularity = 1 << LG_PAGE; // 4MB per heap block (matches the default allocation threshold of jemalloc)
|
||||
#ifdef _WIN64
|
||||
const int cMaxBlocks = 1 << 18; // 4MB * 256K heap blocks = 1TB
|
||||
const int cMaxBlocks = 1 << (40 - LG_PAGE); // 4MB * 256K heap blocks = 1TB
|
||||
#else
|
||||
const int cMaxBlocks = 1 << 8; // 4MB * 256 heap blocks = 1GB
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -452,7 +452,7 @@ void handleClientsBlockedOnKeys(void) {
|
||||
if (group) {
|
||||
consumer = streamLookupConsumer(group,
|
||||
receiver->bpop.xread_consumer->ptr,
|
||||
1);
|
||||
SLC_NONE);
|
||||
noack = receiver->bpop.xread_group_noack;
|
||||
}
|
||||
|
||||
|
||||
+74
-23
@@ -755,6 +755,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
|
||||
node->slaves = NULL;
|
||||
node->slaveof = NULL;
|
||||
node->ping_sent = node->pong_received = 0;
|
||||
node->data_received = 0;
|
||||
node->fail_time = 0;
|
||||
node->link = NULL;
|
||||
memset(node->ip,0,sizeof(node->ip));
|
||||
@@ -1468,7 +1469,10 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
}
|
||||
} else {
|
||||
/* If it's not in NOADDR state and we don't have it, we
|
||||
* start a handshake process against this IP/PORT pairs.
|
||||
* add it to our trusted dict with exact nodeid and flag.
|
||||
* Note that we cannot simply start a handshake against
|
||||
* this IP/PORT pairs, since IP/PORT can be reused already,
|
||||
* otherwise we risk joining another cluster.
|
||||
*
|
||||
* Note that we require that the sender of this gossip message
|
||||
* is a well known node in our cluster, otherwise we risk
|
||||
@@ -1477,7 +1481,12 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
!(flags & CLUSTER_NODE_NOADDR) &&
|
||||
!clusterBlacklistExists(g->nodename))
|
||||
{
|
||||
clusterStartHandshake(g->ip,ntohs(g->port),ntohs(g->cport));
|
||||
clusterNode *node;
|
||||
node = createClusterNode(g->nodename, flags);
|
||||
memcpy(node->ip,g->ip,NET_IP_STR_LEN);
|
||||
node->port = ntohs(g->port);
|
||||
node->cport = ntohs(g->cport);
|
||||
clusterAddNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1684,6 +1693,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
clusterMsg *hdr = (clusterMsg*) link->rcvbuf;
|
||||
uint32_t totlen = ntohl(hdr->totlen);
|
||||
uint16_t type = ntohs(hdr->type);
|
||||
mstime_t now = mstime();
|
||||
|
||||
if (type < CLUSTERMSG_TYPE_COUNT)
|
||||
server.cluster->stats_bus_messages_received[type]++;
|
||||
@@ -1747,6 +1757,13 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
|
||||
/* Check if the sender is a known node. */
|
||||
sender = clusterLookupNode(hdr->sender);
|
||||
|
||||
/* Update the last time we saw any data from this node. We
|
||||
* use this in order to avoid detecting a timeout from a node that
|
||||
* is just sending a lot of data in the cluster bus, for instance
|
||||
* because of Pub/Sub. */
|
||||
if (sender) sender->data_received = now;
|
||||
|
||||
if (sender && !nodeInHandshake(sender)) {
|
||||
/* Update our curretEpoch if we see a newer epoch in the cluster. */
|
||||
senderCurrentEpoch = ntohu64(hdr->currentEpoch);
|
||||
@@ -1761,7 +1778,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
}
|
||||
/* Update the replication offset info for this node. */
|
||||
sender->repl_offset = ntohu64(hdr->offset);
|
||||
sender->repl_offset_time = mstime();
|
||||
sender->repl_offset_time = now;
|
||||
/* If we are a slave performing a manual failover and our master
|
||||
* sent its offset while already paused, populate the MF state. */
|
||||
if (server.cluster->mf_end &&
|
||||
@@ -1875,7 +1892,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
* address. */
|
||||
serverLog(LL_DEBUG,"PONG contains mismatching sender ID. About node %.40s added %d ms ago, having flags %d",
|
||||
link->node->name,
|
||||
(int)(mstime()-(link->node->ctime)),
|
||||
(int)(now-(link->node->ctime)),
|
||||
link->node->flags);
|
||||
link->node->flags |= CLUSTER_NODE_NOADDR;
|
||||
link->node->ip[0] = '\0';
|
||||
@@ -1910,7 +1927,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
|
||||
/* Update our info about the node */
|
||||
if (link->node && type == CLUSTERMSG_TYPE_PONG) {
|
||||
link->node->pong_received = mstime();
|
||||
link->node->pong_received = now;
|
||||
link->node->ping_sent = 0;
|
||||
|
||||
/* The PFAIL condition can be reversed without external
|
||||
@@ -2057,7 +2074,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
"FAIL message received from %.40s about %.40s",
|
||||
hdr->sender, hdr->data.fail.about.nodename);
|
||||
failing->flags |= CLUSTER_NODE_FAIL;
|
||||
failing->fail_time = mstime();
|
||||
failing->fail_time = now;
|
||||
failing->flags &= ~CLUSTER_NODE_PFAIL;
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
|
||||
CLUSTER_TODO_UPDATE_STATE);
|
||||
@@ -2110,9 +2127,9 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
/* Manual failover requested from slaves. Initialize the state
|
||||
* accordingly. */
|
||||
resetManualFailover();
|
||||
server.cluster->mf_end = mstime() + CLUSTER_MF_TIMEOUT;
|
||||
server.cluster->mf_end = now + CLUSTER_MF_TIMEOUT;
|
||||
server.cluster->mf_slave = sender;
|
||||
pauseClients(mstime()+(CLUSTER_MF_TIMEOUT*2));
|
||||
pauseClients(now+(CLUSTER_MF_TIMEOUT*CLUSTER_MF_PAUSE_MULT));
|
||||
serverLog(LL_WARNING,"Manual failover requested by replica %.40s.",
|
||||
sender->name);
|
||||
} else if (type == CLUSTERMSG_TYPE_UPDATE) {
|
||||
@@ -3558,7 +3575,6 @@ void clusterCron(void) {
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
clusterNode *node = dictGetVal(de);
|
||||
now = mstime(); /* Use an updated time at every iteration. */
|
||||
mstime_t delay;
|
||||
|
||||
if (node->flags &
|
||||
(CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE))
|
||||
@@ -3582,7 +3598,7 @@ void clusterCron(void) {
|
||||
this_slaves = okslaves;
|
||||
}
|
||||
|
||||
/* If we are waiting for the PONG more than half the cluster
|
||||
/* If we are not receiving any data for more than half the cluster
|
||||
* timeout, reconnect the link: maybe there is a connection
|
||||
* issue even if the node is alive. */
|
||||
if (node->link && /* is connected */
|
||||
@@ -3591,7 +3607,9 @@ void clusterCron(void) {
|
||||
node->ping_sent && /* we already sent a ping */
|
||||
node->pong_received < node->ping_sent && /* still waiting pong */
|
||||
/* and we are waiting for the pong more than timeout/2 */
|
||||
now - node->ping_sent > server.cluster_node_timeout/2)
|
||||
now - node->ping_sent > server.cluster_node_timeout/2 &&
|
||||
/* and in such interval we are not seeing any traffic at all. */
|
||||
now - node->data_received > server.cluster_node_timeout/2)
|
||||
{
|
||||
/* Disconnect the link, it will be reconnected automatically. */
|
||||
freeClusterLink(node->link);
|
||||
@@ -3626,7 +3644,13 @@ void clusterCron(void) {
|
||||
/* Compute the delay of the PONG. Note that if we already received
|
||||
* the PONG, then node->ping_sent is zero, so can't reach this
|
||||
* code at all. */
|
||||
delay = now - node->ping_sent;
|
||||
mstime_t delay = now - node->ping_sent;
|
||||
|
||||
/* We consider every incoming data as proof of liveness, since
|
||||
* our cluster bus link is also used for data: under heavy data
|
||||
* load pong delays are possible. */
|
||||
mstime_t data_delay = now - node->data_received;
|
||||
if (data_delay < delay) delay = data_delay;
|
||||
|
||||
if (delay > server.cluster_node_timeout) {
|
||||
/* Timeout reached. Set the node as possibly failing if it is
|
||||
@@ -4220,11 +4244,17 @@ void clusterReplyMultiBulkSlots(client *c) {
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
clusterNode *node = dictGetVal(de);
|
||||
int j = 0, start = -1;
|
||||
int i, nested_elements = 0;
|
||||
|
||||
/* Skip slaves (that are iterated when producing the output of their
|
||||
* master) and masters not serving any slot. */
|
||||
if (!nodeIsMaster(node) || node->numslots == 0) continue;
|
||||
|
||||
for(i = 0; i < node->numslaves; i++) {
|
||||
if (nodeFailed(node->slaves[i])) continue;
|
||||
nested_elements++;
|
||||
}
|
||||
|
||||
for (j = 0; j < CLUSTER_SLOTS; j++) {
|
||||
int bit, i;
|
||||
|
||||
@@ -4232,8 +4262,7 @@ void clusterReplyMultiBulkSlots(client *c) {
|
||||
if (start == -1) start = j;
|
||||
}
|
||||
if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) {
|
||||
int nested_elements = 3; /* slots (2) + master addr (1). */
|
||||
void *nested_replylen = addDeferredMultiBulkLength(c);
|
||||
addReplyMultiBulkLen(c, nested_elements + 3); /* slots (2) + master addr (1). */
|
||||
|
||||
if (bit && j == CLUSTER_SLOTS-1) j++;
|
||||
|
||||
@@ -4263,9 +4292,7 @@ void clusterReplyMultiBulkSlots(client *c) {
|
||||
addReplyBulkCString(c, node->slaves[i]->ip);
|
||||
addReplyLongLong(c, node->slaves[i]->port);
|
||||
addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);
|
||||
nested_elements++;
|
||||
}
|
||||
setDeferredMultiBulkLength(c, nested_replylen, nested_elements);
|
||||
num_masters++;
|
||||
}
|
||||
}
|
||||
@@ -4979,7 +5006,8 @@ void restoreCommand(client *c) {
|
||||
}
|
||||
|
||||
/* Make sure this key does not already exist here... */
|
||||
if (!replace && lookupKeyWrite(c->db,c->argv[1]) != NULL) {
|
||||
robj *key = c->argv[1];
|
||||
if (!replace && lookupKeyWrite(c->db,key) != NULL) {
|
||||
addReply(c,shared.busykeyerr);
|
||||
return;
|
||||
}
|
||||
@@ -5001,23 +5029,37 @@ void restoreCommand(client *c) {
|
||||
|
||||
rioInitWithBuffer(&payload,c->argv[3]->ptr);
|
||||
if (((type = rdbLoadObjectType(&payload)) == -1) ||
|
||||
((obj = rdbLoadObject(type,&payload,c->argv[1])) == NULL))
|
||||
((obj = rdbLoadObject(type,&payload,key)) == NULL))
|
||||
{
|
||||
addReplyError(c,"Bad data format");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove the old key if needed. */
|
||||
if (replace) dbDelete(c->db,c->argv[1]);
|
||||
int deleted = 0;
|
||||
if (replace)
|
||||
deleted = dbDelete(c->db,key);
|
||||
|
||||
if (ttl && !absttl) ttl+=mstime();
|
||||
if (ttl && checkAlreadyExpired(ttl)) {
|
||||
if (deleted) {
|
||||
rewriteClientCommandVector(c,2,shared.del,key);
|
||||
signalModifiedKey(c->db,key);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
server.dirty++;
|
||||
}
|
||||
decrRefCount(obj);
|
||||
addReply(c, shared.ok);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the key and set the TTL if any */
|
||||
dbAdd(c->db,c->argv[1],obj);
|
||||
dbAdd(c->db,key,obj);
|
||||
if (ttl) {
|
||||
if (!absttl) ttl+=mstime();
|
||||
setExpire(c,c->db,c->argv[1],ttl);
|
||||
setExpire(c,c->db,key,ttl);
|
||||
}
|
||||
objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,key);
|
||||
addReply(c,shared.ok);
|
||||
server.dirty++;
|
||||
}
|
||||
@@ -5797,6 +5839,15 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
|
||||
int slot = keyHashSlot((char*)key->ptr, (int)sdslen(key->ptr)); WIN_PORT_FIX /* cast (int) */
|
||||
clusterNode *node = server.cluster->slots[slot];
|
||||
|
||||
/* if the client is read-only and attempting to access key that our
|
||||
* replica can handle, allow it. */
|
||||
if ((c->flags & CLIENT_READONLY) &&
|
||||
(c->lastcmd->flags & CMD_READONLY) &&
|
||||
nodeIsSlave(myself) && myself->slaveof == node)
|
||||
{
|
||||
node = myself;
|
||||
}
|
||||
|
||||
/* We send an error and unblock the client if:
|
||||
* 1) The slot is unassigned, emitting a cluster down error.
|
||||
* 2) The slot is not handled by this node, nor being imported. */
|
||||
|
||||
@@ -128,6 +128,7 @@ typedef struct clusterNode {
|
||||
tables. */
|
||||
mstime_t ping_sent; /* Unix time we sent latest ping */
|
||||
mstime_t pong_received; /* Unix time we received the pong */
|
||||
mstime_t data_received; /* Unix time we received any data */
|
||||
mstime_t fail_time; /* Unix time when FAIL flag was set */
|
||||
mstime_t voted_time; /* Last time we voted for a slave of this master */
|
||||
mstime_t repl_offset_time; /* Unix time we received offset for this node */
|
||||
|
||||
@@ -857,14 +857,6 @@ void shutdownCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* When SHUTDOWN is called while the server is loading a dataset in
|
||||
* memory we need to make sure no attempt is performed to save
|
||||
* the dataset on shutdown (otherwise it could overwrite the current DB
|
||||
* with half-read data).
|
||||
*
|
||||
* Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */
|
||||
if (server.loading || server.sentinel_mode)
|
||||
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
|
||||
if (prepareForShutdown(flags) == C_OK) exit(0);
|
||||
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
|
||||
}
|
||||
|
||||
+1
-1
@@ -355,7 +355,7 @@ PORT_LONG activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
|
||||
sdsele = ln->value;
|
||||
if ((newsds = activeDefragSds(sdsele))) {
|
||||
/* When defragging an sds value, we need to update the dict key */
|
||||
uint64_t hash = dictGetHash(d, sdsele);
|
||||
uint64_t hash = dictGetHash(d, newsds);
|
||||
replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);
|
||||
ln->value = newsds;
|
||||
defragged++;
|
||||
|
||||
+11
-7
@@ -391,6 +391,16 @@ void flushSlaveKeysWithExpireList(void) {
|
||||
}
|
||||
}
|
||||
|
||||
int checkAlreadyExpired(PORT_LONGLONG when) {
|
||||
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
|
||||
* should never be executed as a DEL when load the AOF or in the context
|
||||
* of a slave instance.
|
||||
*
|
||||
* Instead we add the already expired key to the database with expire time
|
||||
* (possibly in the past) and wait for an explicit DEL from the master. */
|
||||
return (when <= mstime() && !server.loading && !server.masterhost);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Expires Commands
|
||||
*----------------------------------------------------------------------------*/
|
||||
@@ -418,13 +428,7 @@ void expireGenericCommand(client *c, PORT_LONGLONG basetime, int unit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
|
||||
* should never be executed as a DEL when load the AOF or in the context
|
||||
* of a slave instance.
|
||||
*
|
||||
* Instead we take the other branch of the IF statement setting an expire
|
||||
* (possibly in the past) and wait for an explicit DEL from the master. */
|
||||
if (when <= mstime() && !server.loading && !server.masterhost) {
|
||||
if (checkAlreadyExpired(when)) {
|
||||
robj *aux;
|
||||
|
||||
int deleted = server.lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
|
||||
|
||||
@@ -45,6 +45,30 @@ size_t lazyfreeGetFreeEffort(robj *obj) {
|
||||
} else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {
|
||||
dict *ht = obj->ptr;
|
||||
return dictSize(ht);
|
||||
} else if (obj->type == OBJ_STREAM) {
|
||||
size_t effort = 0;
|
||||
stream *s = obj->ptr;
|
||||
|
||||
/* Make a best effort estimate to maintain constant runtime. Every macro
|
||||
* node in the Stream is one allocation. */
|
||||
effort += s->rax->numnodes;
|
||||
|
||||
/* Every consumer group is an allocation and so are the entries in its
|
||||
* PEL. We use size of the first group's PEL as an estimate for all
|
||||
* others. */
|
||||
if (s->cgroups) {
|
||||
raxIterator ri;
|
||||
streamCG *cg;
|
||||
raxStart(&ri,s->cgroups);
|
||||
raxSeek(&ri,"^",NULL,0);
|
||||
/* There must be at least one group so the following should always
|
||||
* work. */
|
||||
serverAssert(raxNext(&ri));
|
||||
cg = ri.data;
|
||||
effort += raxSize(s->cgroups)*(1+raxSize(cg->pel));
|
||||
raxStop(&ri);
|
||||
}
|
||||
return effort;
|
||||
} else {
|
||||
return 1; /* Everything else is a single allocation. */
|
||||
}
|
||||
|
||||
+13
-4
@@ -485,7 +485,8 @@ int moduleDelKeyIfEmpty(RedisModuleKey *key) {
|
||||
case OBJ_LIST: isempty = listTypeLength(o) == 0; break;
|
||||
case OBJ_SET: isempty = setTypeSize(o) == 0; break;
|
||||
case OBJ_ZSET: isempty = zsetLength(o) == 0; break;
|
||||
case OBJ_HASH : isempty = hashTypeLength(o) == 0; break;
|
||||
case OBJ_HASH: isempty = hashTypeLength(o) == 0; break;
|
||||
case OBJ_STREAM: isempty = streamLength(o) == 0; break;
|
||||
default: isempty = 0;
|
||||
}
|
||||
|
||||
@@ -556,6 +557,8 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {
|
||||
redisOpArrayFree(&server.also_propagate);
|
||||
/* Restore the previous oparray in case of nexted use of the API. */
|
||||
server.also_propagate = ctx->saved_oparray;
|
||||
/* We're done with saved_oparray, let's invalidate it. */
|
||||
redisOpArrayInit(&ctx->saved_oparray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1698,6 +1701,7 @@ int RM_KeyType(RedisModuleKey *key) {
|
||||
case OBJ_ZSET: return REDISMODULE_KEYTYPE_ZSET;
|
||||
case OBJ_HASH: return REDISMODULE_KEYTYPE_HASH;
|
||||
case OBJ_MODULE: return REDISMODULE_KEYTYPE_MODULE;
|
||||
/* case OBJ_STREAM: return REDISMODULE_KEYTYPE_STREAM; - don't wanna add new API to 5.0 */
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -1715,6 +1719,7 @@ size_t RM_ValueLength(RedisModuleKey *key) {
|
||||
case OBJ_SET: return setTypeSize(key->value);
|
||||
case OBJ_ZSET: return zsetLength(key->value);
|
||||
case OBJ_HASH: return hashTypeLength(key->value);
|
||||
case OBJ_STREAM: return streamLength(key->value);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -3907,7 +3912,6 @@ void moduleHandleBlockedClients(void) {
|
||||
ctx.client = bc->client;
|
||||
ctx.blocked_client = bc;
|
||||
bc->reply_callback(&ctx,(void**)c->argv,c->argc);
|
||||
moduleHandlePropagationAfterCommandCallback(&ctx);
|
||||
moduleFreeContext(&ctx);
|
||||
}
|
||||
|
||||
@@ -5355,9 +5359,14 @@ int moduleLoad(const char *path, void **module_argv, int module_argc) {
|
||||
}
|
||||
|
||||
/* Redis module loaded! Register it. */
|
||||
WIN32_ONLY(if (TRUE) {)
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 6011)
|
||||
#endif
|
||||
dictAdd(modules,ctx.module->name,ctx.module);
|
||||
WIN32_ONLY(})
|
||||
#ifdef _WIN32
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
ctx.module->handle = handle;
|
||||
serverLog(LL_NOTICE,"Module '%s' loaded from %s",ctx.module->name,path);
|
||||
moduleFreeContext(&ctx);
|
||||
|
||||
@@ -62,6 +62,7 @@ extern int rdbCheckMode;
|
||||
void rdbCheckError(const char *fmt, ...);
|
||||
void rdbCheckSetError(const char *fmt, ...);
|
||||
|
||||
WIN32_ONLY(__declspec(noreturn))
|
||||
void rdbCheckThenExit(int linenum, char *reason, ...) {
|
||||
va_list ap;
|
||||
char msg[1024];
|
||||
@@ -1000,10 +1001,10 @@ ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {
|
||||
* to call the right module during loading. */
|
||||
int retval = rdbSaveLen(rdb,mt->id);
|
||||
if (retval == -1) return -1;
|
||||
io.bytes += retval;
|
||||
|
||||
/* Then write the module-specific representation + EOF marker. */
|
||||
moduleInitIOContext(io,mt,rdb,key);
|
||||
io.bytes += retval;
|
||||
mt->rdb_save(&io,mv->value);
|
||||
retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);
|
||||
if (retval == -1)
|
||||
@@ -1126,6 +1127,9 @@ ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {
|
||||
/* Save a module-specific aux value. */
|
||||
RedisModuleIO io;
|
||||
int retval = rdbSaveType(rdb, RDB_OPCODE_MODULE_AUX);
|
||||
if (retval == -1) return -1;
|
||||
moduleInitIOContext(io,mt,rdb,NULL); WIN_PORT_FIX /*moved earlier before first usage of "io"*/
|
||||
io.bytes += retval;
|
||||
|
||||
/* Write the "module" identifier as prefix, so that we'll be able
|
||||
* to call the right module during loading. */
|
||||
@@ -1144,7 +1148,6 @@ ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt) {
|
||||
io.bytes += retval;
|
||||
|
||||
/* Then write the module-specific representation + EOF marker. */
|
||||
moduleInitIOContext(io,mt,rdb,NULL);
|
||||
mt->aux_save(&io,when);
|
||||
retval = rdbSaveLen(rdb,RDB_MODULE_OPCODE_EOF);
|
||||
if (retval == -1)
|
||||
@@ -1812,8 +1815,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
|
||||
rdbExitReportCorruptRDB(
|
||||
"Error reading the consumer name from Stream group");
|
||||
}
|
||||
streamConsumer *consumer = streamLookupConsumer(cgroup,cname,
|
||||
1);
|
||||
streamConsumer *consumer =
|
||||
streamLookupConsumer(cgroup,cname,SLC_NONE);
|
||||
sdsfree(cname);
|
||||
consumer->seen_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ robj *rdbLoadObject(int type, rio *rdb, robj *key);
|
||||
void backgroundSaveDoneHandler(int exitcode, int bysignal);
|
||||
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, PORT_LONGLONG expiretime);
|
||||
ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt);
|
||||
robj *rdbLoadCheckModuleValue(rio *rdb, char *modulename);
|
||||
robj *rdbLoadStringObject(rio *rdb);
|
||||
ssize_t rdbSaveStringObject(rio *rdb, robj *obj);
|
||||
ssize_t rdbSaveRawString(rio *rdb, unsigned char *s, size_t len);
|
||||
|
||||
+18
-1
@@ -70,6 +70,7 @@ struct {
|
||||
#define RDB_CHECK_DOING_CHECK_SUM 5
|
||||
#define RDB_CHECK_DOING_READ_LEN 6
|
||||
#define RDB_CHECK_DOING_READ_AUX 7
|
||||
#define RDB_CHECK_DOING_READ_MODULE_AUX 8
|
||||
|
||||
char *rdb_check_doing_string[] = {
|
||||
"start",
|
||||
@@ -79,7 +80,8 @@ char *rdb_check_doing_string[] = {
|
||||
"read-object-value",
|
||||
"check-sum",
|
||||
"read-len",
|
||||
"read-aux"
|
||||
"read-aux",
|
||||
"read-module-aux"
|
||||
};
|
||||
|
||||
char *rdb_type_string[] = {
|
||||
@@ -284,6 +286,21 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
decrRefCount(auxkey);
|
||||
decrRefCount(auxval);
|
||||
continue; /* Read type again. */
|
||||
} else if (type == RDB_OPCODE_MODULE_AUX) {
|
||||
/* AUX: Auxiliary data for modules. */
|
||||
uint64_t moduleid, when_opcode, when;
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_MODULE_AUX;
|
||||
if ((moduleid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
|
||||
if ((when_opcode = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
|
||||
if ((when = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
|
||||
|
||||
char name[10];
|
||||
moduleTypeNameByID(name,moduleid);
|
||||
rdbCheckInfo("MODULE AUX for: %s", name);
|
||||
|
||||
robj *o = rdbLoadCheckModuleValue(&rdb,name);
|
||||
decrRefCount(o);
|
||||
continue; /* Read type again. */
|
||||
} else {
|
||||
if (!rdbIsObjectType(type)) {
|
||||
rdbCheckError("Invalid object type: %d", type);
|
||||
|
||||
+7
-1
@@ -1193,7 +1193,7 @@ static int cliSendCommand(int argc, char **argv, PORT_LONG repeat) {
|
||||
for (j = 0; j < argc; j++)
|
||||
argvlen[j] = sdslen(argv[j]);
|
||||
|
||||
while(repeat-- > 0) {
|
||||
while(repeat < 0 || repeat-- > 0) {
|
||||
redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
|
||||
while (config.monitor_mode) {
|
||||
if (cliReadReply(output_raw) != REDIS_OK) exit(1);
|
||||
@@ -1228,6 +1228,11 @@ static int cliSendCommand(int argc, char **argv, PORT_LONG repeat) {
|
||||
cliSelect();
|
||||
}
|
||||
}
|
||||
if (config.cluster_reissue_command){
|
||||
/* If we need to reissue the command, break to prevent a
|
||||
further 'repeat' number of dud interations */
|
||||
break;
|
||||
}
|
||||
if (config.interval) usleep(config.interval);
|
||||
fflush(stdout); /* Make it grep friendly */
|
||||
}
|
||||
@@ -6080,6 +6085,7 @@ static void getRDB(void) {
|
||||
}
|
||||
close(s); /* Close the file descriptor ASAP as fsync() may take time. */
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
fprintf(stderr,"Transfer finished with success.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1412,6 +1412,17 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
rdbRemoveTempFile(server.rdb_child_pid);
|
||||
}
|
||||
|
||||
/* Make sure the new file (also used for persistence) is fully synced
|
||||
* (not covered by earlier calls to rdb_fsync_range). */
|
||||
if (fsync(server.repl_transfer_fd) == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
"Failed trying to sync the temp DB to disk in "
|
||||
"MASTER <-> REPLICA synchronization: %s",
|
||||
strerror(errno));
|
||||
cancelReplicationHandshake();
|
||||
return;
|
||||
}
|
||||
|
||||
if (rename(server.repl_transfer_tmpfile,server.rdb_filename) == -1) {
|
||||
serverLog(LL_WARNING,"Failed trying to rename the temp DB into dump.rdb in MASTER <-> REPLICA synchronization: %s", IF_WIN32(wsa_strerror(errno),strerror(errno)));
|
||||
cancelReplicationHandshake();
|
||||
|
||||
+3
-4
@@ -524,12 +524,11 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
|
||||
!server.loading && /* Don't care about mem if loading. */
|
||||
!server.masterhost && /* Slave must execute the script. */
|
||||
server.lua_write_dirty == 0 && /* Script had no side effects so far. */
|
||||
server.lua_oom && /* Detected OOM when script start. */
|
||||
(cmd->flags & CMD_DENYOOM))
|
||||
{
|
||||
if (getMaxmemoryState(NULL,NULL,NULL,NULL) != C_OK) {
|
||||
luaPushError(lua, shared.oomerr->ptr);
|
||||
goto cleanup;
|
||||
}
|
||||
luaPushError(lua, shared.oomerr->ptr);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1;
|
||||
|
||||
@@ -1212,6 +1212,7 @@ int sentinelTryConnectionSharing(sentinelRedisInstance *ri) {
|
||||
releaseInstanceLink(ri->link,NULL);
|
||||
ri->link = match->link;
|
||||
match->link->refcount++;
|
||||
dictReleaseIterator(di);
|
||||
return C_OK;
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
+17
-1
@@ -244,7 +244,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"keys",keysCommand,2,"rS",0,NULL,0,0,0,0,0},
|
||||
{"scan",scanCommand,-2,"rR",0,NULL,0,0,0,0,0},
|
||||
{"dbsize",dbsizeCommand,1,"rF",0,NULL,0,0,0,0,0},
|
||||
{"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0},
|
||||
{"auth",authCommand,2,"sltMF",0,NULL,0,0,0,0,0},
|
||||
{"ping",pingCommand,-1,"tF",0,NULL,0,0,0,0,0},
|
||||
{"echo",echoCommand,2,"F",0,NULL,0,0,0,0,0},
|
||||
{"save",saveCommand,1,"as",0,NULL,0,0,0,0,0},
|
||||
@@ -2747,6 +2747,13 @@ int processCommand(client *c) {
|
||||
addReply(c, shared.oomerr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Save out_of_memory result at script start, otherwise if we check OOM
|
||||
* untill first write within script, memory used by lua stack and
|
||||
* arguments might interfere. */
|
||||
if (c->cmd->proc == evalCommand || c->cmd->proc == evalShaCommand) {
|
||||
server.lua_oom = out_of_memory;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't accept write commands if there are problems persisting on disk
|
||||
@@ -2874,6 +2881,15 @@ void closeListeningSockets(int unlink_unix_socket) {
|
||||
}
|
||||
|
||||
int prepareForShutdown(int flags) {
|
||||
/* When SHUTDOWN is called while the server is loading a dataset in
|
||||
* memory we need to make sure no attempt is performed to save
|
||||
* the dataset on shutdown (otherwise it could overwrite the current DB
|
||||
* with half-read data).
|
||||
*
|
||||
* Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */
|
||||
if (server.loading || server.sentinel_mode)
|
||||
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
|
||||
|
||||
int save = flags & SHUTDOWN_SAVE;
|
||||
int nosave = flags & SHUTDOWN_NOSAVE;
|
||||
|
||||
|
||||
@@ -1292,6 +1292,7 @@ struct redisServer {
|
||||
execution. */
|
||||
int lua_kill; /* Kill the script if true. */
|
||||
int lua_always_replicate_commands; /* Default replication type. */
|
||||
int lua_oom; /* OOM detected when script start? */
|
||||
/* Lazy free */
|
||||
int lazyfree_lazy_eviction;
|
||||
int lazyfree_lazy_expire;
|
||||
@@ -1853,6 +1854,7 @@ void propagateExpire(redisDb *db, robj *key, int lazy);
|
||||
int expireIfNeeded(redisDb *db, robj *key);
|
||||
PORT_LONGLONG getExpire(redisDb *db, robj *key);
|
||||
void setExpire(client *c, redisDb *db, robj *key, PORT_LONGLONG when);
|
||||
int checkAlreadyExpired(PORT_LONGLONG when);
|
||||
#ifdef _WIN32
|
||||
time_t getExpireForSave(redisDb *db, robj *key);
|
||||
#endif
|
||||
|
||||
+7
-1
@@ -96,15 +96,21 @@ typedef struct sreamPropInfo {
|
||||
/* Prototypes of exported APIs. */
|
||||
struct client;
|
||||
|
||||
/* Flags for streamLookupConsumer */
|
||||
#define SLC_NONE 0
|
||||
#define SLC_NOCREAT (1<<0) /* Do not create the consumer if it doesn't exist */
|
||||
#define SLC_NOREFRESH (1<<1) /* Do not update consumer's seen-time */
|
||||
|
||||
stream *streamNew(void);
|
||||
void freeStream(stream *s);
|
||||
PORT_ULONG streamLength(const robj *subject);
|
||||
size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end, size_t count, int rev, streamCG *group, streamConsumer *consumer, int flags, streamPropInfo *spi);
|
||||
void streamIteratorStart(streamIterator *si, stream *s, streamID *start, streamID *end, int rev);
|
||||
int streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields);
|
||||
void streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen);
|
||||
void streamIteratorStop(streamIterator *si);
|
||||
streamCG *streamLookupCG(stream *s, sds groupname);
|
||||
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create);
|
||||
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags);
|
||||
streamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id);
|
||||
streamNACK *streamCreateNACK(streamConsumer *consumer);
|
||||
void streamDecodeID(void *buf, streamID *id);
|
||||
|
||||
+37
-14
@@ -82,6 +82,12 @@ void streamIncrID(streamID *id) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the length of a stream. */
|
||||
PORT_ULONG streamLength(const robj *subject) {
|
||||
stream *s = subject->ptr;
|
||||
return s->length;
|
||||
}
|
||||
|
||||
/* Generate the next stream item ID given the previous one. If the current
|
||||
* milliseconds Unix time is greater than the previous one, just use this
|
||||
* as time part and start with sequence part of zero. Otherwise we use the
|
||||
@@ -842,6 +848,11 @@ void streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupnam
|
||||
argv[11] = createStringObject("JUSTID",6);
|
||||
argv[12] = createStringObject("LASTID",6);
|
||||
argv[13] = createObjectFromStreamID(&group->last_id);
|
||||
|
||||
/* We use progagate() because this code path is not always called from
|
||||
* the command execution context. Moreover this will just alter the
|
||||
* consumer group state, and we don't need MULTI/EXEC wrapping because
|
||||
* there is no message state cross-message atomicity required. */
|
||||
propagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[3]);
|
||||
@@ -869,7 +880,12 @@ void streamPropagateGroupID(client *c, robj *key, streamCG *group, robj *groupna
|
||||
argv[2] = key;
|
||||
argv[3] = groupname;
|
||||
argv[4] = createObjectFromStreamID(&group->last_id);
|
||||
alsoPropagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
|
||||
/* We use progagate() because this code path is not always called from
|
||||
* the command execution context. Moreover this will just alter the
|
||||
* consumer group state, and we don't need MULTI/EXEC wrapping because
|
||||
* there is no message state cross-message atomicity required. */
|
||||
propagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[1]);
|
||||
decrRefCount(argv[4]);
|
||||
@@ -1564,7 +1580,8 @@ void xreadCommand(client *c) {
|
||||
addReplyBulk(c,c->argv[streams_arg+i]);
|
||||
streamConsumer *consumer = NULL;
|
||||
if (groups) consumer = streamLookupConsumer(groups[i],
|
||||
consumername->ptr,1);
|
||||
consumername->ptr,
|
||||
SLC_NONE);
|
||||
streamPropInfo spi = {c->argv[i+streams_arg],groupname};
|
||||
int flags = 0;
|
||||
if (noack) flags |= STREAM_RWR_NOACK;
|
||||
@@ -1697,7 +1714,9 @@ streamCG *streamLookupCG(stream *s, sds groupname) {
|
||||
* consumer does not exist it is automatically created as a side effect
|
||||
* of calling this function, otherwise its last seen time is updated and
|
||||
* the existing consumer reference returned. */
|
||||
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create) {
|
||||
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {
|
||||
int create = !(flags & SLC_NOCREAT);
|
||||
int refresh = !(flags & SLC_NOREFRESH);
|
||||
streamConsumer *consumer = raxFind(cg->consumers,(unsigned char*)name,
|
||||
sdslen(name));
|
||||
if (consumer == raxNotFound) {
|
||||
@@ -1708,7 +1727,7 @@ streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create) {
|
||||
raxInsert(cg->consumers,(unsigned char*)name,sdslen(name),
|
||||
consumer,NULL);
|
||||
}
|
||||
consumer->seen_time = mstime();
|
||||
if (refresh) consumer->seen_time = mstime();
|
||||
return consumer;
|
||||
}
|
||||
|
||||
@@ -1716,7 +1735,8 @@ streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create) {
|
||||
* may have pending messages: they are removed from the PEL, and the number
|
||||
* of pending messages "lost" is returned. */
|
||||
uint64_t streamDelConsumer(streamCG *cg, sds name) {
|
||||
streamConsumer *consumer = streamLookupConsumer(cg,name,0);
|
||||
streamConsumer *consumer =
|
||||
streamLookupConsumer(cg,name,SLC_NOCREAT|SLC_NOREFRESH);
|
||||
if (consumer == NULL) return 0;
|
||||
|
||||
uint64_t retval = raxSize(consumer->pel);
|
||||
@@ -2047,15 +2067,18 @@ void xpendingCommand(client *c) {
|
||||
}
|
||||
/* XPENDING <key> <group> <start> <stop> <count> [<consumer>] variant. */
|
||||
else {
|
||||
streamConsumer *consumer = consumername ?
|
||||
streamLookupConsumer(group,consumername->ptr,0):
|
||||
NULL;
|
||||
streamConsumer *consumer = NULL;
|
||||
if (consumername) {
|
||||
consumer = streamLookupConsumer(group,
|
||||
consumername->ptr,
|
||||
SLC_NOCREAT|SLC_NOREFRESH);
|
||||
|
||||
/* If a consumer name was mentioned but it does not exist, we can
|
||||
* just return an empty array. */
|
||||
if (consumername && consumer == NULL) {
|
||||
addReplyMultiBulkLen(c,0);
|
||||
return;
|
||||
/* If a consumer name was mentioned but it does not exist, we can
|
||||
* just return an empty array. */
|
||||
if (consumer == NULL) {
|
||||
addReplyMultiBulkLen(c,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rax *pel = consumer ? consumer->pel : group->pel;
|
||||
@@ -2317,7 +2340,7 @@ void xclaimCommand(client *c) {
|
||||
raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);
|
||||
/* Update the consumer and idle time. */
|
||||
if (consumer == NULL)
|
||||
consumer = streamLookupConsumer(group,c->argv[3]->ptr,1);
|
||||
consumer = streamLookupConsumer(group,c->argv[3]->ptr,SLC_NONE);
|
||||
nack->consumer = consumer;
|
||||
nack->delivery_time = deliverytime;
|
||||
/* Set the delivery attempts counter if given, otherwise
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "5.0.9"
|
||||
#define REDIS_VERSION "5.0.10"
|
||||
|
||||
+1
-3
@@ -33,6 +33,7 @@
|
||||
#include "Win32_Interop/win32_types.h"
|
||||
#include "Win32_Interop/win32fixes.h"
|
||||
#include "Win32_Interop/Win32_QFork.h"
|
||||
#include "Win32_Interop/Win32_PThread.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -188,9 +189,6 @@ void *zrealloc(void *ptr, size_t size) {
|
||||
size_t zmalloc_size(void *ptr) {
|
||||
void *realptr = (char*)ptr-PREFIX_SIZE;
|
||||
size_t size = *((size_t*)realptr);
|
||||
/* Assume at least that all the allocations are padded at sizeof(long) by
|
||||
* the underlying allocator. */
|
||||
if (size&(sizeof(PORT_LONG)-1)) size += sizeof(PORT_LONG)-(size&(sizeof(PORT_LONG)-1));
|
||||
return size+PREFIX_SIZE;
|
||||
}
|
||||
size_t zmalloc_usable(void *ptr) {
|
||||
|
||||
+6
-2
@@ -51,7 +51,7 @@
|
||||
#endif
|
||||
|
||||
#elif defined(USE_JEMALLOC)
|
||||
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
|
||||
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX) "-redis")
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
@@ -64,11 +64,15 @@
|
||||
#include <malloc/malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) malloc_size(p)
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef ZMALLOC_LIB
|
||||
#define ZMALLOC_LIB "libc"
|
||||
#ifdef __GLIBC__
|
||||
#include <malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) malloc_usable_size(p)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* We can enable the Redis defrag capabilities only if we are using Jemalloc
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Check basic transactions on a replica.
|
||||
|
||||
source "../tests/includes/init-tests.tcl"
|
||||
|
||||
test "Create a primary with a replica" {
|
||||
create_cluster 1 1
|
||||
}
|
||||
|
||||
test "Cluster should start ok" {
|
||||
assert_cluster_state ok
|
||||
}
|
||||
|
||||
set primary [Rn 0]
|
||||
set replica [Rn 1]
|
||||
|
||||
test "Cant read from replica without READONLY" {
|
||||
$primary SET a 1
|
||||
catch {$replica GET a} err
|
||||
assert {[string range $err 0 4] eq {MOVED}}
|
||||
}
|
||||
|
||||
test "Can read from replica after READONLY" {
|
||||
$replica READONLY
|
||||
assert {[$replica GET a] eq {1}}
|
||||
}
|
||||
|
||||
test "Can preform HSET primary and HGET from replica" {
|
||||
$primary HSET h a 1
|
||||
$primary HSET h b 2
|
||||
$primary HSET h c 3
|
||||
assert {[$replica HGET h a] eq {1}}
|
||||
assert {[$replica HGET h b] eq {2}}
|
||||
assert {[$replica HGET h c] eq {3}}
|
||||
}
|
||||
|
||||
# didn't cherry pick b120366d4 to 5.0 yet
|
||||
#test "Can MULTI-EXEC transaction of HGET operations from replica" {
|
||||
# $replica MULTI
|
||||
# assert {[$replica HGET h a] eq {QUEUED}}
|
||||
# assert {[$replica HGET h b] eq {QUEUED}}
|
||||
# assert {[$replica HGET h c] eq {QUEUED}}
|
||||
# assert {[$replica EXEC] eq {1 2 3}}
|
||||
#}
|
||||
|
||||
test "MULTI-EXEC with write operations is MOVED" {
|
||||
$replica MULTI
|
||||
catch {$replica HSET h b 4} err
|
||||
assert {[string range $err 0 4] eq {MOVED}}
|
||||
catch {$replica exec} err
|
||||
assert {[string range $err 0 8] eq {EXECABORT}}
|
||||
}
|
||||
|
||||
test "read-only blocking operations from replica" {
|
||||
set rd [redis_deferring_client redis 1]
|
||||
$rd readonly
|
||||
$rd read
|
||||
$rd XREAD BLOCK 0 STREAMS k 0
|
||||
|
||||
wait_for_condition 1000 50 {
|
||||
[RI 1 blocked_clients] eq {1}
|
||||
} else {
|
||||
fail "client wasn't blocked"
|
||||
}
|
||||
|
||||
$primary XADD k * foo bar
|
||||
set res [$rd read]
|
||||
set res [lindex [lindex [lindex [lindex $res 0] 1] 0] 1]
|
||||
assert {$res eq {foo bar}}
|
||||
$rd close
|
||||
}
|
||||
+21
-2
@@ -350,10 +350,16 @@ proc S {n args} {
|
||||
[dict get $s link] {*}$args
|
||||
}
|
||||
|
||||
# Returns a Redis instance by index.
|
||||
# Example:
|
||||
# [Rn 0] info
|
||||
proc Rn {n} {
|
||||
return [dict get [lindex $::redis_instances $n] link]
|
||||
}
|
||||
|
||||
# Like R but to chat with Redis instances.
|
||||
proc R {n args} {
|
||||
set r [lindex $::redis_instances $n]
|
||||
[dict get $r link] {*}$args
|
||||
[Rn $n] {*}$args
|
||||
}
|
||||
|
||||
proc get_info_field {info field} {
|
||||
@@ -532,3 +538,16 @@ proc restart_instance {type id} {
|
||||
}
|
||||
}
|
||||
|
||||
proc redis_deferring_client {type id} {
|
||||
set port [get_instance_attrib $type $id port]
|
||||
set host [get_instance_attrib $type $id host]
|
||||
set client [redis $host $port 1]
|
||||
return $client
|
||||
}
|
||||
|
||||
proc redis_client {type id} {
|
||||
set port [get_instance_attrib $type $id port]
|
||||
set host [get_instance_attrib $type $id host]
|
||||
set client [redis $host $port 0]
|
||||
return $client
|
||||
}
|
||||
|
||||
@@ -180,6 +180,12 @@ start_server {} {
|
||||
}
|
||||
set new_sync_count [status $R($master_id) sync_full]
|
||||
assert {$sync_count == $new_sync_count}
|
||||
|
||||
if {$::uses_cygwin} {
|
||||
# read Windows PID from "INFO" to be cleaned up at the end
|
||||
set pid [status $R($slave_id) process_id]
|
||||
lappend ::winpids $pid
|
||||
}
|
||||
}
|
||||
|
||||
test "PSYNC2: Replica RDB restart with EVALSHA in backlog issue #4483" {
|
||||
@@ -227,6 +233,12 @@ start_server {} {
|
||||
incr retry -1
|
||||
}
|
||||
|
||||
if {$::uses_cygwin} {
|
||||
# read Windows PID from "INFO" to be cleaned up at the end
|
||||
set pid [status $R($slave_id) process_id]
|
||||
lappend ::winpids $pid
|
||||
}
|
||||
|
||||
# The master should be back at 4 slaves eventually
|
||||
wait_for_condition 50 1000 {
|
||||
[status $R($master_id) connected_slaves] == 4
|
||||
@@ -250,3 +262,7 @@ start_server {} {
|
||||
}
|
||||
|
||||
}}}}}
|
||||
|
||||
# clean up any additional processes started by "debug restart" under Windows
|
||||
# when running tests from Cygwin
|
||||
cygwin_clean_up
|
||||
|
||||
+38
-11
@@ -96,30 +96,41 @@ if { $tcl_platform(platform) != "windows" } {
|
||||
}
|
||||
}
|
||||
|
||||
proc windows_is_alive config {
|
||||
set pid [dict get $config pid]
|
||||
set mfilter {PID eq }
|
||||
append mfilter $pid
|
||||
if { [string first $pid [exec tasklist.exe -FI ${mfilter}]] != -1 } {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
proc windows_kill_proc config {
|
||||
set pid [dict get $config pid]
|
||||
catch {exec taskkill.exe /F /T /PID $pid}
|
||||
}
|
||||
|
||||
proc windows_kill_proc2 pid {
|
||||
catch {exec taskkill.exe /F /T /PID $pid}
|
||||
}
|
||||
|
||||
if { $tcl_platform(platform) == "windows" } {
|
||||
proc is_alive config {
|
||||
set pid [dict get $config pid]
|
||||
set mfilter {PID eq }
|
||||
append mfilter $pid
|
||||
if { [string first $pid [exec tasklist.exe -FI ${mfilter}]] != -1 } {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
return [windows_is_alive $config]
|
||||
}
|
||||
}
|
||||
|
||||
if { $tcl_platform(platform) == "windows" } {
|
||||
proc kill_proc config {
|
||||
set pid [dict get $config pid]
|
||||
catch {exec taskkill.exe -F -T -PID $pid}
|
||||
windows_kill_proc $config
|
||||
}
|
||||
}
|
||||
|
||||
if { $tcl_platform(platform) == "windows" } {
|
||||
proc kill_proc2 pid {
|
||||
catch {exec taskkill.exe -F -T -PID $pid}
|
||||
windows_kill_proc2 $pid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,3 +400,19 @@ proc start_server {options {code undefined}} {
|
||||
set _ $srv
|
||||
}
|
||||
}
|
||||
|
||||
proc cygwin_clean_up {} {
|
||||
# clean up any new processes started with "debug restart" using Windows-specific tools
|
||||
foreach newpid $::winpids {
|
||||
set config [dict create "pid" $newpid]
|
||||
|
||||
if {$::verbose} { puts "Checking if WINPID=$newpid is still alive" }
|
||||
while {[windows_is_alive $config]} {
|
||||
if {$::verbose} { puts "Trying to stop server with WINPID=$newpid" }
|
||||
windows_kill_proc $config
|
||||
after 1000
|
||||
}
|
||||
}
|
||||
|
||||
set ::winpids {}
|
||||
}
|
||||
|
||||
@@ -454,6 +454,18 @@ proc print_help_screen {} {
|
||||
} "\n"]
|
||||
}
|
||||
|
||||
# [tporadowski] "debug restart" command in Redis for Windows causes a new process
|
||||
# with new PID to be started, so we need to properly clean this up when running tests
|
||||
# from Cygwin
|
||||
set ::winpids {}
|
||||
set ::uses_cygwin 0
|
||||
if {$::tcl_platform(platform) == "unix"} {
|
||||
set uname [exec uname -s]
|
||||
if {[string first "CYGWIN" $uname] != -1} {
|
||||
set ::uses_cygwin 1
|
||||
}
|
||||
}
|
||||
|
||||
# parse arguments
|
||||
for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
set opt [lindex $argv $j]
|
||||
|
||||
+12
-1
@@ -36,7 +36,18 @@ start_server {tags {"dump"}} {
|
||||
assert {$ttl >= 2900 && $ttl <= 3100}
|
||||
r get foo
|
||||
} {bar}
|
||||
|
||||
|
||||
test {RESTORE with ABSTTL in the past} {
|
||||
r set foo bar
|
||||
set encoded [r dump foo]
|
||||
set now [clock milliseconds]
|
||||
r debug set-active-expire 0
|
||||
r restore foo [expr $now-3000] $encoded absttl REPLACE
|
||||
catch {r debug object foo} e
|
||||
r debug set-active-expire 1
|
||||
set e
|
||||
} {ERR no such key}
|
||||
|
||||
test {RESTORE can set LRU} {
|
||||
r set foo bar
|
||||
set encoded [r dump foo]
|
||||
|
||||
Reference in New Issue
Block a user