Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6bd962476 | ||
|
|
b080e580d1 | ||
|
|
091a877d50 | ||
|
|
5d3fac562e | ||
|
|
b28a0989dd | ||
|
|
0832b29446 | ||
|
|
d49db62cfa | ||
|
|
fed247f532 | ||
|
|
4db1b1e6d7 | ||
|
|
f211ac55ac | ||
|
|
9072630159 | ||
|
|
0ff2a21801 | ||
|
|
06f2b090a9 | ||
|
|
966b43659c | ||
|
|
6f5203c4db | ||
|
|
847ddf62a5 | ||
|
|
acbc047f7a |
No files matched your search
@@ -7,10 +7,9 @@ jobs:
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make BUILD_TLS=yes -j2
|
||||
- name: gen-cert
|
||||
@@ -35,24 +34,22 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make -j2
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j2
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: make -j2
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make MALLOC=libc -j2
|
||||
|
||||
+10
-100
@@ -1,21 +1,16 @@
|
||||
name: Daily
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
# any PR to a release branch.
|
||||
- '[0-9].[0-9]'
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '0 7 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-ubuntu-jemalloc:
|
||||
test-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
timeout-minutes: 1200
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
@@ -26,17 +21,12 @@ jobs:
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-libc-malloc:
|
||||
test-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
timeout-minutes: 1200
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
@@ -47,17 +37,11 @@ jobs:
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
@@ -75,7 +59,7 @@ jobs:
|
||||
test-ubuntu-arm:
|
||||
runs-on: [self-hosted, linux, arm]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
@@ -90,10 +74,9 @@ jobs:
|
||||
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
@@ -104,76 +87,3 @@ jobs:
|
||||
./runtest --valgrind --verbose --clients 1
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --verbose --clients 1
|
||||
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl
|
||||
yum -y install devtoolset-7
|
||||
scl enable devtoolset-7 "make"
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install tcl
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-centos7-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=yes"
|
||||
- name: test
|
||||
run: |
|
||||
yum -y install tcl tcltls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls
|
||||
./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: |
|
||||
./runtest-sentinel --tls
|
||||
./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster
|
||||
|
||||
test-macos-latest:
|
||||
runs-on: macos-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
- name: test
|
||||
run: |
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
@@ -17,6 +17,6 @@ jobs:
|
||||
make -j8
|
||||
- name: test-multithread (5X)
|
||||
run: |
|
||||
sudo apt-get install -y tcl tcl-tls
|
||||
sudo apt-get install -y tcl8.5
|
||||
./runtest --loopn 5 --config server-threads 3 --clients 5 --endurance
|
||||
|
||||
@@ -48,13 +48,9 @@ src/nodes.conf
|
||||
deps/lua/src/lua
|
||||
deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
*.dSYM
|
||||
Makefile.dep
|
||||
.vscode/*
|
||||
.idea/*
|
||||
.ccls
|
||||
.ccls-cache/*
|
||||
compile_commands.json
|
||||
-787
@@ -11,793 +11,6 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
SECURITY: There are security fixes in the release.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
================================================================================
|
||||
Redis 6.0.8 Released Wed Sep 09 23:34:17 IDT 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency HIGH: Anyone who's using Redis 6.0.7 with Sentinel or
|
||||
CONFIG REWRITE command is affected and should upgrade ASAP, see #7760.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* CONFIG REWRITE after setting oom-score-adj-values either via CONFIG SET or
|
||||
loading it from a config file, will generate a corrupt config file that will
|
||||
cause Redis to fail to start
|
||||
* Fix issue with redis-cli --pipe on MacOS
|
||||
* Fix RESP3 response for HKEYS/HVALS on non-existing key
|
||||
* Various small bug fixes
|
||||
|
||||
New features / Changes:
|
||||
|
||||
* Remove THP warning when set to madvise
|
||||
* Allow EXEC with read commands on readonly replica in cluster
|
||||
* Add masters/replicas options to redis-cli --cluster call command
|
||||
|
||||
Module API:
|
||||
|
||||
* Add RedisModule_ThreadSafeContextTryLock
|
||||
|
||||
Full list of commits:
|
||||
|
||||
Oran Agra in commit cdabf696a:
|
||||
Fix RESP3 response for HKEYS/HVALS on non-existing key
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit ec633c716:
|
||||
Fix leak in new blockedclient module API test
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit 6bac07c5c:
|
||||
Tests: fix oom-score-adj false positives. (#7772)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
杨博东 in commit 6043dc614:
|
||||
Tests: Add aclfile load and save tests (#7765)
|
||||
2 files changed, 41 insertions(+)
|
||||
|
||||
Roi Lipman in commit c0b5f9bf0:
|
||||
RM_ThreadSafeContextTryLock a non-blocking method for acquiring GIL (#7738)
|
||||
7 files changed, 122 insertions(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit 5780a1599:
|
||||
Tests: validate CONFIG REWRITE for all params. (#7764)
|
||||
6 files changed, 43 insertions(+), 6 deletions(-)
|
||||
|
||||
Oran Agra in commit e3c14b25d:
|
||||
Change THP warning to use madvise rather than never (#7771)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Itamar Haber in commit 28929917b:
|
||||
Documents RM_Call's fmt (#5448)
|
||||
1 file changed, 25 insertions(+)
|
||||
|
||||
Jan-Erik Rediger in commit 9146402c2:
|
||||
Check that THP is not set to always (madvise is ok) (#4001)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit d05089429:
|
||||
Tests: clean up stale .cli files. (#7768)
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
Eran Liberty in commit 8861c1bae:
|
||||
Allow exec with read commands on readonly replica in cluster (#7766)
|
||||
3 files changed, 59 insertions(+), 3 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 2cf2ff2f6:
|
||||
Fix CONFIG REWRITE of oom-score-adj-values. (#7761)
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit 1386c80f7:
|
||||
handle cur_test for nested tests
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
Oran Agra in commit c7d4945f0:
|
||||
Add daily CI for MacOS (#7759)
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
bodong.ybd in commit 32548264c:
|
||||
Tests: Some fixes for macOS
|
||||
3 files changed, 26 insertions(+), 11 deletions(-)
|
||||
|
||||
Oran Agra in commit 1e17f9812:
|
||||
Fix cluster consistency-check test (#7754)
|
||||
1 file changed, 55 insertions(+), 29 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit f4ecdf86a:
|
||||
Tests: fix unmonitored servers. (#7756)
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
Oran Agra in commit 9f020050d:
|
||||
fix broken cluster/sentinel tests by recent commit (#7752)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit fdbabb496:
|
||||
Improve valgrind support for cluster tests (#7725)
|
||||
3 files changed, 83 insertions(+), 23 deletions(-)
|
||||
|
||||
Oran Agra in commit 35a6a0bbc:
|
||||
test infra - add durable mode to work around test suite crashing
|
||||
3 files changed, 35 insertions(+), 3 deletions(-)
|
||||
|
||||
Oran Agra in commit e3136b13f:
|
||||
test infra - wait_done_loading
|
||||
2 files changed, 16 insertions(+), 36 deletions(-)
|
||||
|
||||
Oran Agra in commit 83c75dbd9:
|
||||
test infra - flushall between tests in external mode
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 265f5d3cf:
|
||||
test infra - improve test skipping ability
|
||||
3 files changed, 91 insertions(+), 36 deletions(-)
|
||||
|
||||
Oran Agra in commit fcd3a9908:
|
||||
test infra - reduce disk space usage
|
||||
3 files changed, 33 insertions(+), 11 deletions(-)
|
||||
|
||||
Oran Agra in commit b6ea4699f:
|
||||
test infra - write test name to logfile
|
||||
3 files changed, 35 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit 4a4b07fc6:
|
||||
redis-cli: fix writeConn() buffer handling. (#7749)
|
||||
1 file changed, 37 insertions(+), 6 deletions(-)
|
||||
|
||||
Oran Agra in commit f2d08de2e:
|
||||
Print server startup messages after daemonization (#7743)
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
Thandayuthapani in commit 77541d555:
|
||||
Add masters/replicas options to redis-cli --cluster call command (#6491)
|
||||
1 file changed, 13 insertions(+), 2 deletions(-)
|
||||
|
||||
Oran Agra in commit 91d13a854:
|
||||
fix README about BUILD_WITH_SYSTEMD usage (#7739)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit 88d03d965:
|
||||
Fix double-make issue with make && make install. (#7734)
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
================================================================================
|
||||
Redis 6.0.7 Released Fri Aug 28 11:05:09 IDT 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency MODERATE: several bugs with moderate impact are fixed,
|
||||
Specifically the first two listed below which cause protocol errors for clients.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* CONFIG SET could hung the client when arrives during RDB/ROF loading (When
|
||||
processed after another command that was also rejected with -LOADING error)
|
||||
* LPOS command when RANK is greater than matches responded wiht broken protocol
|
||||
(negative multi-bulk count)
|
||||
* UNLINK / Lazyfree for stream type key would have never do async freeing
|
||||
* PERSIST should invalidate WATCH (Like EXPIRE does)
|
||||
* EXEC with only read commands could have be rejected when OOM
|
||||
* TLS: relax verification on CONFIG SET (Don't error if some configs are set
|
||||
and tls isn't enabled)
|
||||
* TLS: support cluster/replication without tls-port
|
||||
* Systemd startup after network is online
|
||||
* Redis-benchmark improvements
|
||||
* Various small bug fixes
|
||||
|
||||
New features:
|
||||
|
||||
* Add oom-score-adj configuration option to control Linux OOM killer
|
||||
* Show IO threads statistics and status in INFO output
|
||||
* Add optional tls verification mode (see tls-auth-clients)
|
||||
|
||||
Module API:
|
||||
|
||||
* Add RedisModule_HoldString
|
||||
* Add loaded keyspace event
|
||||
* Fix RedisModuleEvent_LoadingProgress
|
||||
* Fix RedisModuleEvent_MasterLinkChange hook missing on successful psync
|
||||
* Fix missing RM_CLIENTINFO_FLAG_SSL
|
||||
* Refactor redismodule.h for use with -fno-common / extern
|
||||
|
||||
Full list of commits:
|
||||
|
||||
Oran Agra in commit c26394e4f:
|
||||
Reduce the probability of failure when start redis in runtest-cluster #7554 (#7635)
|
||||
1 file changed, 23 insertions(+), 5 deletions(-)
|
||||
|
||||
Leoš Literák in commit 745d5e802:
|
||||
Update README.md with instructions how to build with systemd support (#7730)
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit 03f1d208a:
|
||||
Fix oom-score-adj on older distros. (#7724)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 941174d9c:
|
||||
Backport Lua 5.2.2 stack overflow fix. (#7733)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Wang Yuan in commit c897dba14:
|
||||
Fix wrong format specifiers of 'sdscatfmt' for the INFO command (#7706)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Wen Hui in commit 5e3fab5e7:
|
||||
fix make warnings (#7692)
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
Nathan Scott in commit a2b09c13f:
|
||||
Annotate module API functions in redismodule.h for use with -fno-common (#6900)
|
||||
1 file changed, 265 insertions(+), 241 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit bf244273f:
|
||||
Add oom-score-adj configuration option to control Linux OOM killer. (#1690)
|
||||
8 files changed, 306 insertions(+), 1 deletion(-)
|
||||
|
||||
Meir Shpilraien (Spielrein) in commit b5a6ab98f:
|
||||
see #7544, added RedisModule_HoldString api. (#7577)
|
||||
4 files changed, 83 insertions(+), 8 deletions(-)
|
||||
|
||||
ShooterIT in commit ff04cf62b:
|
||||
[Redis-benchmark] Remove zrem test, add zpopmin test
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
ShooterIT in commit 0f3260f31:
|
||||
[Redis-benchmark] Support zset type
|
||||
1 file changed, 16 insertions(+)
|
||||
|
||||
Arun Ranganathan in commit 45d0b94fc:
|
||||
Show threading configuration in INFO output (#7446)
|
||||
3 files changed, 46 insertions(+), 14 deletions(-)
|
||||
|
||||
Meir Shpilraien (Spielrein) in commit a22f61e12:
|
||||
This PR introduces a new loaded keyspace event (#7536)
|
||||
8 files changed, 135 insertions(+), 4 deletions(-)
|
||||
|
||||
Oran Agra in commit 1c9ca1030:
|
||||
Fix rejectCommand trims newline in shared error objects, hung clients (#7714)
|
||||
4 files changed, 42 insertions(+), 23 deletions(-)
|
||||
|
||||
valentinogeron in commit 217471795:
|
||||
EXEC with only read commands should not be rejected when OOM (#7696)
|
||||
2 files changed, 51 insertions(+), 8 deletions(-)
|
||||
|
||||
Itamar Haber in commit 6e6c47d16:
|
||||
Expands lazyfree's effort estimate to include Streams (#5794)
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit da6813623:
|
||||
Add language servers stuff, test/tls to gitignore. (#7698)
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
Valentino Geron in commit de7fb126e:
|
||||
Assert that setDeferredAggregateLen isn't called with negative value
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Valentino Geron in commit 6cf27f25f:
|
||||
Fix LPOS command when RANK is greater than matches
|
||||
2 files changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 9bba54ace:
|
||||
Tests: fix redis-cli with remote hosts. (#7693)
|
||||
3 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
huangzhw in commit 0fec2cb81:
|
||||
RedisModuleEvent_LoadingProgress always at 100% progress (#7685)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
guybe7 in commit 931e19aa6:
|
||||
Modules: Invalidate saved_oparray after use (#7688)
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
杨博东 in commit 6f2065570:
|
||||
Fix flock cluster config may cause failure to restart after kill -9 (#7674)
|
||||
4 files changed, 31 insertions(+), 7 deletions(-)
|
||||
|
||||
Raghav Muddur in commit 200149a2a:
|
||||
Update clusterMsgDataPublish to clusterMsgModule (#7682)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Madelyn Olson in commit 72daa1b4e:
|
||||
Fixed hset error since it's shared with hmset (#7678)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
guybe7 in commit 3bf9ac994:
|
||||
PERSIST should signalModifiedKey (Like EXPIRE does) (#7671)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit b37501684:
|
||||
OOM Crash log include size of allocation attempt. (#7670)
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
Wen Hui in commit 2136cb68f:
|
||||
[module] using predefined REDISMODULE_NO_EXPIRE in RM_GetExpire (#7669)
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit f56aee4bc:
|
||||
Trim trailing spaces in error replies coming from rejectCommand (#7668)
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit 012d7506a:
|
||||
Module API: fix missing RM_CLIENTINFO_FLAG_SSL. (#7666)
|
||||
6 files changed, 82 insertions(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit a0adbc857:
|
||||
TLS: relax verification on CONFIG SET. (#7665)
|
||||
2 files changed, 24 insertions(+), 7 deletions(-)
|
||||
|
||||
Madelyn Olson in commit 2ef29715b:
|
||||
Fixed timer warning (#5953)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Wagner Francisco Mezaroba in commit b76f171f5:
|
||||
allow --pattern to be used along with --bigkeys (#3586)
|
||||
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
zhaozhao.zz in commit cc7b57765:
|
||||
redis-benchmark: fix wrong random key for hset (#4895)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
zhaozhao.zz in commit 479c1ba77:
|
||||
CLIENT_MASTER should ignore server.proto_max_bulk_len
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
zhaozhao.zz in commit f61ce8a52:
|
||||
config: proto-max-bulk-len must be 1mb or greater
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
zhaozhao.zz in commit 0350f597a:
|
||||
using proto-max-bulk-len in checkStringLength for SETRANGE and APPEND
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
YoongHM in commit eea63548d:
|
||||
Start redis after network is online (#7639)
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit aef6d74fb:
|
||||
Run daily workflow on main repo only (no forks). (#7646)
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
WuYunlong in commit 917b4d241:
|
||||
see #7250, fix signature of RedisModule_DeauthenticateAndCloseClient (#7645)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Wang Yuan in commit efab7fd54:
|
||||
Print error info if failed opening config file (#6943)
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
Wen Hui in commit 8c4468bcf:
|
||||
fix memory leak in ACLLoadFromFile error handling (#7623)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 89724e1d2:
|
||||
redis-cli --cluster-yes - negate force flag for clarity
|
||||
1 file changed, 9 insertions(+), 9 deletions(-)
|
||||
|
||||
Frank Meier in commit c813739af:
|
||||
reintroduce REDISCLI_CLUSTER_YES env variable in redis-cli
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
Frank Meier in commit 7e3b86c18:
|
||||
add force option to 'create-cluster create' script call (#7612)
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
Oran Agra in commit 3f7fa4312:
|
||||
fix new rdb test failing on timing issues (#7604)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 417976d7a:
|
||||
Fix test-centos7-tls daily job. (#7598)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Oran Agra in commit c41818c51:
|
||||
module hook for master link up missing on successful psync (#7584)
|
||||
2 files changed, 22 insertions(+), 2 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 6ef3fc185:
|
||||
CI: Add daily CentOS 7.x jobs. (#7582)
|
||||
1 file changed, 50 insertions(+), 4 deletions(-)
|
||||
|
||||
WuYunlong in commit 002c37482:
|
||||
Fix running single test 14-consistency-check.tcl (#7587)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Yossi Gottlieb in commit 66cbbb6ad:
|
||||
Clarify RM_BlockClient() error condition. (#6093)
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
namtsui in commit 22aba2207:
|
||||
Avoid an out-of-bounds read in the redis-sentinel (#7443)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Wen Hui in commit af08887dc:
|
||||
Add SignalModifiedKey hook in XGROUP CREATE with MKSTREAM option (#7562)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Wen Hui in commit a5e0a64b0:
|
||||
fix leak in error handling of debug populate command (#7062)
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit cbfdfa231:
|
||||
Fix TLS cluster tests. (#7578)
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
Yossi Gottlieb in commit 6d5376d30:
|
||||
TLS: Propagate and handle SSL_new() failures. (#7576)
|
||||
4 files changed, 48 insertions(+), 6 deletions(-)
|
||||
|
||||
Oran Agra in commit a662cd577:
|
||||
Fix failing tests due to issues with wait_for_log_message (#7572)
|
||||
3 files changed, 38 insertions(+), 34 deletions(-)
|
||||
|
||||
Jiayuan Chen in commit 2786a4b5e:
|
||||
Add optional tls verification (#7502)
|
||||
6 files changed, 40 insertions(+), 5 deletions(-)
|
||||
|
||||
Oran Agra in commit 3ef3d3612:
|
||||
Daily github action: run cluster and sentinel tests with tls (#7575)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit f20f63322:
|
||||
TLS: support cluster/replication without tls-port.
|
||||
2 files changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
grishaf in commit 3c9ae059d:
|
||||
Fix prepareForShutdown function declaration (#7566)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit 3f4803af9:
|
||||
Stabilize bgsave test that sometimes fails with valgrind (#7559)
|
||||
1 file changed, 20 insertions(+), 2 deletions(-)
|
||||
|
||||
Madelyn Olson in commit 1a3c51a1f:
|
||||
Properly reset errno for rdbLoad (#7542)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 92d80b13a:
|
||||
testsuite may leave servers alive on error (#7549)
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
Yossi Gottlieb in commit 245582ba7:
|
||||
Tests: drop TCL 8.6 dependency. (#7548)
|
||||
1 file changed, 27 insertions(+), 22 deletions(-)
|
||||
|
||||
Oran Agra in commit f20e1ba2d:
|
||||
Fixes to release scripts (#7547)
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Remi Collet in commit 60ff56993:
|
||||
Fix deprecated tail syntax in tests (#7543)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Wen Hui in commit 34e8541b9:
|
||||
Add missing calls to raxStop (#7532)
|
||||
4 files changed, 63 insertions(+), 19 deletions(-)
|
||||
|
||||
Wen Hui in commit 2f7bc5435:
|
||||
add missing caching command in client help (#7399)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
zhaozhao.zz in commit c15be9ffe:
|
||||
replication: need handle -NOPERM error after send ping (#7538)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Scott Brenner in commit 1b29152c3:
|
||||
GitHub Actions workflows - use latest version of actions/checkout (#7534)
|
||||
2 files changed, 10 insertions(+), 10 deletions(-)
|
||||
|
||||
================================================================================
|
||||
Redis 6.0.6 Released Mon Jul 20 09:31:30 IDT 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency MODERATE: several bugs with moderate impact are fixed here.
|
||||
|
||||
The most important issues are listed here:
|
||||
|
||||
* Fix crash when enabling CLIENT TRACKING with prefix
|
||||
* EXEC always fails with EXECABORT and multi-state is cleared
|
||||
* RESTORE ABSTTL won't store expired keys into the db
|
||||
* redis-cli better handling of non-pritable key names
|
||||
* TLS: Ignore client cert when tls-auth-clients off
|
||||
* Tracking: fix invalidation message on flush
|
||||
* Notify systemd on Sentinel startup
|
||||
* Fix crash on a misuse of STRALGO
|
||||
* Few fixes in module API
|
||||
* Fix a few rare leaks (STRALGO error misuse, Sentinel)
|
||||
* Fix a possible invalid access in defrag of scripts (unlikely to cause real harm)
|
||||
|
||||
New features:
|
||||
|
||||
* LPOS command to search in a list
|
||||
* Use user+pass for MIGRATE in redis-cli and redis-benchmark in cluster mode
|
||||
* redis-cli support TLS for --pipe, --rdb and --replica options
|
||||
* TLS: Session caching configuration support
|
||||
|
||||
And this is the full list of commits:
|
||||
|
||||
Itamar Haber in commit 50548cafc:
|
||||
Adds SHA256SUM to redis-stable tarball upload
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
yoav-steinberg in commit 3a4c6684f:
|
||||
Support passing stack allocated module strings to moduleCreateArgvFromUserFormat (#7528)
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
Luke Palmer in commit 2fd0b2bd6:
|
||||
Send null for invalidate on flush (#7469)
|
||||
1 file changed, 14 insertions(+), 10 deletions(-)
|
||||
|
||||
dmurnane in commit c3c81e1a8:
|
||||
Notify systemd on sentinel startup (#7168)
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
Developer-Ecosystem-Engineering in commit e2770f29b:
|
||||
Add registers dump support for Apple silicon (#7453)
|
||||
1 file changed, 54 insertions(+), 2 deletions(-)
|
||||
|
||||
Wen Hui in commit b068eae97:
|
||||
correct error msg for num connections reaching maxclients in cluster mode (#7444)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
WuYunlong in commit e6169ae5c:
|
||||
Fix command help for unexpected options (#7476)
|
||||
6 files changed, 20 insertions(+), 3 deletions(-)
|
||||
|
||||
WuYunlong in commit abf08fc02:
|
||||
Refactor RM_KeyType() by using macro. (#7486)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit 11b83076a:
|
||||
diskless master disconnect replicas when rdb child failed (#7518)
|
||||
1 file changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
Oran Agra in commit 8f27f2f7d:
|
||||
redis-cli tests, fix valgrind timing issue (#7519)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
WuYunlong in commit 180b588e8:
|
||||
Fix out of update help info in tcl tests. (#7516)
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
Qu Chen in commit 417c60bdc:
|
||||
Replica always reports master's config epoch in CLUSTER NODES output. (#7235)
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit 72a242419:
|
||||
RESTORE ABSTTL skip expired keys - leak (#7511)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Oran Agra in commit 2ca45239f:
|
||||
fix recently added time sensitive tests failing with valgrind (#7512)
|
||||
2 files changed, 12 insertions(+), 6 deletions(-)
|
||||
|
||||
Oran Agra in commit 123dc8b21:
|
||||
runtest --stop pause stops before terminating the redis server (#7513)
|
||||
2 files changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
Oran Agra in commit a6added45:
|
||||
update release scripts for new hosts, and CI to run more tests (#7480)
|
||||
5 files changed, 68 insertions(+), 26 deletions(-)
|
||||
|
||||
jimgreen2013 in commit cf4869f9e:
|
||||
fix description about ziplist, the code is ok (#6318)
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
马永泽 in commit d548f219b:
|
||||
fix benchmark in cluster mode fails to authenticate (#7488)
|
||||
1 file changed, 56 insertions(+), 40 deletions(-)
|
||||
|
||||
Abhishek Soni in commit e58eb7b89:
|
||||
fix: typo in CI job name (#7466)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Jiayuan Chen in commit 6def10a2b:
|
||||
Fix typo in deps README (#7500)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
WuYunlong in commit 8af61afef:
|
||||
Add missing latency-monitor tcl test to test_helper.tcl. (#6782)
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Yossi Gottlieb in commit a419f400e:
|
||||
TLS: Session caching configuration support. (#7420)
|
||||
6 files changed, 56 insertions(+), 16 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit 2e4bb2667:
|
||||
TLS: Ignore client cert when tls-auth-clients off. (#7457)
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
James Hilliard in commit f0b1aee9e:
|
||||
Use pkg-config to properly detect libssl and libcrypto libraries (#7452)
|
||||
1 file changed, 15 insertions(+), 3 deletions(-)
|
||||
|
||||
Yossi Gottlieb in commit e92b99564:
|
||||
TLS: Add missing redis-cli options. (#7456)
|
||||
3 files changed, 166 insertions(+), 52 deletions(-)
|
||||
|
||||
Oran Agra in commit 1f3db5bf5:
|
||||
redis-cli --hotkeys fixed to handle non-printable key names
|
||||
1 file changed, 11 insertions(+), 5 deletions(-)
|
||||
|
||||
Oran Agra in commit c3044f369:
|
||||
redis-cli --bigkeys fixed to handle non-printable key names
|
||||
1 file changed, 24 insertions(+), 16 deletions(-)
|
||||
|
||||
Oran Agra in commit b3f75527b:
|
||||
RESTORE ABSTTL won't store expired keys into the db (#7472)
|
||||
4 files changed, 46 insertions(+), 16 deletions(-)
|
||||
|
||||
huangzhw in commit 6f87fc92f:
|
||||
defrag.c activeDefragSdsListAndDict when defrag sdsele, We can't use (#7492)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit d8e6a3e5b:
|
||||
skip a test that uses +inf on valgrind (#7440)
|
||||
1 file changed, 12 insertions(+), 9 deletions(-)
|
||||
|
||||
Oran Agra in commit 28fd1a110:
|
||||
stabilize tests that look for log lines (#7367)
|
||||
3 files changed, 33 insertions(+), 11 deletions(-)
|
||||
|
||||
Oran Agra in commit a513b4ed9:
|
||||
tests/valgrind: don't use debug restart (#7404)
|
||||
4 files changed, 114 insertions(+), 57 deletions(-)
|
||||
|
||||
Oran Agra in commit 70e72fc1b:
|
||||
change references to the github repo location (#7479)
|
||||
5 files changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
zhaozhao.zz in commit c63e533cc:
|
||||
BITOP: propagate only when it really SET or DEL targetkey (#5783)
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
antirez in commit 31040ff54:
|
||||
Update comment to clarify change in #7398.
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
antirez in commit b605fe827:
|
||||
LPOS: option FIRST renamed RANK.
|
||||
2 files changed, 19 insertions(+), 19 deletions(-)
|
||||
|
||||
Dave Nielsen in commit 8deb24954:
|
||||
updated copyright year
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit a61c2930c:
|
||||
EXEC always fails with EXECABORT and multi-state is cleared
|
||||
6 files changed, 204 insertions(+), 91 deletions(-)
|
||||
|
||||
antirez in commit 3c8041637:
|
||||
Include cluster.h for getClusterConnectionsCount().
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
antirez in commit 5be673ee8:
|
||||
Fix BITFIELD i64 type handling, see #7417.
|
||||
1 file changed, 8 insertions(+), 6 deletions(-)
|
||||
|
||||
antirez in commit 5f289df9b:
|
||||
Clarify maxclients and cluster in conf. Remove myself too.
|
||||
2 files changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
hwware in commit 000f928d6:
|
||||
fix memory leak in sentinel connection sharing
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
chenhui0212 in commit d9a3c0171:
|
||||
Fix comments in function raxLowWalk of listpack.c
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Tomasz Poradowski in commit 7526e4506:
|
||||
ensure SHUTDOWN_NOSAVE in Sentinel mode
|
||||
2 files changed, 9 insertions(+), 8 deletions(-)
|
||||
|
||||
chenhui0212 in commit 6487cbc33:
|
||||
fix comments in listpack.c
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
antirez in commit 69b66bfca:
|
||||
Use cluster connections too, to limit maxclients.
|
||||
3 files changed, 23 insertions(+), 8 deletions(-)
|
||||
|
||||
antirez in commit 5a960a033:
|
||||
Tracking: fix enableBcastTrackingForPrefix() invalid sdslen() call.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
root in commit 1c2e50de3:
|
||||
cluster.c remove if of clusterSendFail in markNodeAsFailingIfNeeded
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
meir@redislabs.com in commit 040efb697:
|
||||
Fix RM_ScanKey module api not to return int encoded strings
|
||||
3 files changed, 24 insertions(+), 7 deletions(-)
|
||||
|
||||
antirez in commit 1b8b7941d:
|
||||
Fix LCS object type checking. Related to #7379.
|
||||
1 file changed, 17 insertions(+), 10 deletions(-)
|
||||
|
||||
hwware in commit 6b571b45a:
|
||||
fix memory leak
|
||||
1 file changed, 11 insertions(+), 12 deletions(-)
|
||||
|
||||
hwware in commit 674759062:
|
||||
fix server crash in STRALGO command
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
Benjamin Sergeant in commit a05ffefdc:
|
||||
Update redis-cli.c
|
||||
1 file changed, 19 insertions(+), 6 deletions(-)
|
||||
|
||||
Jamie Scott in commit 870b63733:
|
||||
minor fix
|
||||
1 file changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
================================================================================
|
||||
Redis 6.0.5 Released Tue Jun 09 11:56:08 CEST 2020
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency MODERATE: several bugs with moderate impact are fixed here.
|
||||
|
||||
The most important issues are listed here:
|
||||
|
||||
* Fix handling of speical chars in ACL LOAD.
|
||||
* Make Redis Cluster more robust about operation errors that may lead
|
||||
to two clusters to mix together.
|
||||
* Revert the sendfile() implementation of RDB transfer. It causes some delay.
|
||||
* Fix TLS certificate loading for chained certificates.
|
||||
* Fix AOF rewirting of KEEPTTL SET option.
|
||||
* Fix MULTI/EXEC behavior during -BUSY script errors.
|
||||
|
||||
And this is the full list of commits:
|
||||
|
||||
antirez in commit ee8dd01bb:
|
||||
Temporary fix for #7353 issue about EVAL during -BUSY.
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
xhe in commit a4a856d53:
|
||||
return the correct proto version HELLO should return the current proto version, while the code hardcoded 3
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Oran Agra in commit e2046b300:
|
||||
Don't queue commands in an already aborted MULTI state
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
Oran Agra in commit b35fdf1de:
|
||||
Avoid rejecting WATCH / UNWATCH, like MULTI/EXEC/DISCARD
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
zhaozhao.zz in commit 1d7bf208c:
|
||||
AOF: append origin SET if no expire option
|
||||
2 files changed, 23 insertions(+), 8 deletions(-)
|
||||
|
||||
Oran Agra in commit 676445ad9:
|
||||
fix disconnectSlaves, to try to free each slave.
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
zhaozhao.zz in commit 4846c0c8a:
|
||||
donot free protected client in freeClientsInAsyncFreeQueue
|
||||
1 file changed, 9 insertions(+), 3 deletions(-)
|
||||
|
||||
Oran Agra in commit f33de403e:
|
||||
fix pingoff test race
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Kevin Fwu in commit 49af4d07e:
|
||||
Fix TLS certificate loading for chained certificates.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
antirez in commit 329fddbda:
|
||||
Revert "Implements sendfile for redis."
|
||||
2 files changed, 2 insertions(+), 55 deletions(-)
|
||||
|
||||
antirez in commit 925a2cd5a:
|
||||
Revert "avoid using sendfile if tls-replication is enabled"
|
||||
1 file changed, 27 insertions(+), 34 deletions(-)
|
||||
|
||||
Liu Zhen in commit 84a7a9058:
|
||||
fix clusters mixing accidentally by gossip
|
||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
antirez in commit cd63359a1:
|
||||
Fix handling of special chars in ACL LOAD.
|
||||
1 file changed, 8 insertions(+), 4 deletions(-)
|
||||
|
||||
================================================================================
|
||||
Redis 6.0.4 Released Thu May 28 11:36:45 CEST 2020
|
||||
================================================================================
|
||||
|
||||
@@ -1 +1 @@
|
||||
Please check https://github.com/redis/redis/issues
|
||||
Please check https://github.com/antirez/redis/issues
|
||||
@@ -1,5 +1,5 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
Copyright (C) 2019-2020, John Sully
|
||||
Copyright (c) 2006-2015, Salvatore Sanfilippo
|
||||
Copyright (C) 2019, John Sully
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||

|
||||

|
||||
[](https://gitter.im/KeyDB/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://stackshare.io/eq-alpha-technology-inc/eq-alpha-technology-inc)
|
||||
|
||||
##### New! Want to extend KeyDB with Javascript? Try [ModJS](https://github.com/JohnSully/ModJS)
|
||||
@@ -95,11 +96,9 @@ libssl-dev on Debian/Ubuntu) and run:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
|
||||
To build with systemd support, you'll need systemd development libraries (such
|
||||
as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
|
||||
|
||||
% make USE_SYSTEMD=yes
|
||||
You can enable flash support with:
|
||||
|
||||
% make MALLOC=memkind
|
||||
|
||||
***Note that the following dependencies may be needed:
|
||||
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
|
||||
|
||||
@@ -56,6 +56,8 @@ Note that unlike Redis, KeyDB fully supports multithreading of TLS connections.
|
||||
To-Do List
|
||||
----------
|
||||
|
||||
- [ ] Add session caching support. Check if/how it's handled by clients to
|
||||
assess how useful/important it is.
|
||||
- [ ] redis-benchmark support. The current implementation is a mix of using
|
||||
hiredis for parsing and basic networking (establishing connections), but
|
||||
directly manipulating sockets for most actions. This will need to be cleaned
|
||||
|
||||
Vendored
+1
-1
@@ -47,7 +47,7 @@ Hiredis
|
||||
Hiredis uses the SDS string library, that must be the same version used inside Redis itself. Hiredis is also very critical for Sentinel. Historically Redis often used forked versions of hiredis in a way or the other. In order to upgrade it is advised to take a lot of care:
|
||||
|
||||
1. Check with diff if hiredis API changed and what impact it could have in Redis.
|
||||
2. Make sure that the SDS library inside Hiredis and inside Redis are compatible.
|
||||
2. Make sure thet the SDS library inside Hiredis and inside Redis are compatible.
|
||||
3. After the upgrade, run the Redis Sentinel test.
|
||||
4. Check manually that redis-cli and redis-benchmark behave as expecteed, since we have no tests for CLI utilities currently.
|
||||
|
||||
|
||||
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 + p->numparams);
|
||||
luaD_checkstack(L, p->maxstacksize);
|
||||
func = restorestack(L, funcr);
|
||||
if (!p->is_vararg) { /* no varargs? */
|
||||
base = func + 1;
|
||||
|
||||
+5
-77
@@ -159,12 +159,9 @@ tcp-keepalive 300
|
||||
# By default, clients (including replica servers) on a TLS port are required
|
||||
# to authenticate using valid client side certificates.
|
||||
#
|
||||
# If "no" is specified, client certificates are not required and not accepted.
|
||||
# If "optional" is specified, client certificates are accepted and must be
|
||||
# valid if provided, but are not required.
|
||||
# It is possible to disable authentication using this directive.
|
||||
#
|
||||
# tls-auth-clients no
|
||||
# tls-auth-clients optional
|
||||
|
||||
# By default, a Redis replica does not attempt to establish a TLS connection
|
||||
# with its master.
|
||||
@@ -202,22 +199,6 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-prefer-server-ciphers yes
|
||||
|
||||
# By default, TLS session caching is enabled to allow faster and less expensive
|
||||
# reconnections by clients that support it. Use the following directive to disable
|
||||
# caching.
|
||||
#
|
||||
# tls-session-caching no
|
||||
|
||||
# Change the default number of TLS sessions cached. A zero value sets the cache
|
||||
# to unlimited size. The default size is 20480.
|
||||
#
|
||||
# tls-session-cache-size 5000
|
||||
|
||||
# Change the default timeout of cached TLS sessions. The default timeout is 300
|
||||
# seconds.
|
||||
#
|
||||
# tls-session-cache-timeout 60
|
||||
|
||||
################################# GENERAL #####################################
|
||||
|
||||
# By default KeyDB does not run as a daemon. Use 'yes' if you need it.
|
||||
@@ -420,20 +401,6 @@ dir ./
|
||||
#
|
||||
replica-serve-stale-data yes
|
||||
|
||||
# Active Replicas will allow read only data access while loading remote RDBs
|
||||
# provided they are permitted to serve stale data. As an option you may also
|
||||
# permit them to accept write commands. This is an EXPERIMENTAL feature and
|
||||
# may result in commands not being fully synchronized
|
||||
#
|
||||
# allow-write-during-load no
|
||||
|
||||
# You can modify the number of masters necessary to form a replica quorum when
|
||||
# multi-master is enabled and replica-serve-stale-data is "no". By default
|
||||
# this is set to -1 which implies the number of known masters (e.g. those
|
||||
# you added with replicaof)
|
||||
#
|
||||
# replica-quorum -1
|
||||
|
||||
# You can configure a replica instance to accept writes or not. Writing against
|
||||
# a replica instance may be useful to store some ephemeral data (because data
|
||||
# written on a replica will be easily deleted after resync with the master) but
|
||||
@@ -778,8 +745,9 @@ replica-priority 100
|
||||
#
|
||||
# The ACL Log tracks failed commands and authentication events associated
|
||||
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
|
||||
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
|
||||
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
|
||||
# by ACLs. The ACL Log is stored in and consumes memory. There is no limit
|
||||
# to its length.You can reclaim memory with ACL LOG RESET or set a maximum
|
||||
# length below.
|
||||
acllog-max-len 128
|
||||
|
||||
# Using an external ACL file
|
||||
@@ -838,11 +806,6 @@ acllog-max-len 128
|
||||
# Once the limit is reached KeyDB will close all the new connections sending
|
||||
# an error 'max number of clients reached'.
|
||||
#
|
||||
# IMPORTANT: When Redis Cluster is used, the max number of connections is also
|
||||
# shared with the cluster bus: every node in the cluster will use two
|
||||
# connections, one incoming and another outgoing. It is important to size the
|
||||
# limit accordingly in case of very large clusters.
|
||||
#
|
||||
# maxclients 10000
|
||||
|
||||
############################## MEMORY MANAGEMENT ################################
|
||||
@@ -1053,32 +1016,6 @@ lazyfree-lazy-user-del no
|
||||
# --threads option to match the number of Redis theads, otherwise you'll not
|
||||
# be able to notice the improvements.
|
||||
|
||||
############################ KERNEL OOM CONTROL ##############################
|
||||
|
||||
# On Linux, it is possible to hint the kernel OOM killer on what processes
|
||||
# should be killed first when out of memory.
|
||||
#
|
||||
# Enabling this feature makes Redis actively control the oom_score_adj value
|
||||
# for all its processes, depending on their role. The default scores will
|
||||
# attempt to have background child processes killed before all others, and
|
||||
# replicas killed before masters.
|
||||
|
||||
oom-score-adj no
|
||||
|
||||
# When oom-score-adj is used, this directive controls the specific values used
|
||||
# for master, replica and background child processes. Values range -1000 to
|
||||
# 1000 (higher means more likely to be killed).
|
||||
#
|
||||
# Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities)
|
||||
# can freely increase their value, but not decrease it below its initial
|
||||
# settings.
|
||||
#
|
||||
# Values are used relative to the initial value of oom_score_adj when the server
|
||||
# starts. Because typically the initial value is 0, they will often match the
|
||||
# absolute values.
|
||||
|
||||
oom-score-adj-values 0 200 800
|
||||
|
||||
############################## APPEND ONLY MODE ###############################
|
||||
|
||||
# By default KeyDB asynchronously dumps the dataset on disk. This mode is
|
||||
@@ -1681,7 +1618,7 @@ client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
|
||||
# In the KeyDB protocol, bulk requests, that are, elements representing single
|
||||
# strings, are normally limited ot 512 mb. However you can change this limit
|
||||
# here, but must be 1mb or greater
|
||||
# here.
|
||||
#
|
||||
# proto-max-bulk-len 512mb
|
||||
|
||||
@@ -1874,15 +1811,6 @@ jemalloc-bg-thread yes
|
||||
# Set bgsave child process to cpu affinity 1,10,11
|
||||
# bgsave_cpulist 1,10-11
|
||||
|
||||
# The minimum number of clients on a thread before KeyDB assigns new connections to a different thread
|
||||
# Tuning this parameter is a tradeoff between locking overhead and distributing the workload over multiple cores
|
||||
# min-clients-per-thread 50
|
||||
|
||||
# Avoid forwarding RREPLAY messages to other masters?
|
||||
# WARNING: This setting is dangerous! You must be certain all masters are connected to each
|
||||
# other in a true mesh topology or data loss will occur!
|
||||
# This command can be used to reduce multimaster bus traffic
|
||||
# multi-master-no-forward no
|
||||
|
||||
# Path to directory for file backed scratchpad. The file backed scratchpad
|
||||
# reduces memory requirements by storing rarely accessed data on disk
|
||||
|
||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
@@ -25,6 +25,4 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/scan \
|
||||
--single unit/moduleapi/datatype \
|
||||
--single unit/moduleapi/auth \
|
||||
--single unit/moduleapi/keyspace_events \
|
||||
--single unit/moduleapi/blockedclient \
|
||||
"${@}"
|
||||
+5
-20
@@ -236,23 +236,10 @@ ifeq ($(MALLOC),memkind)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
|
||||
LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?)
|
||||
ifeq ($(LIBSSL_PKGCONFIG),0)
|
||||
LIBSSL_LIBS=$(shell $(PKG_CONFIG) --libs libssl)
|
||||
else
|
||||
LIBSSL_LIBS=-lssl
|
||||
endif
|
||||
LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?)
|
||||
ifeq ($(LIBCRYPTO_PKGCONFIG),0)
|
||||
LIBCRYPTO_LIBS=$(shell $(PKG_CONFIG) --libs libcrypto)
|
||||
else
|
||||
LIBCRYPTO_LIBS=-lcrypto
|
||||
endif
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
|
||||
endif
|
||||
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
|
||||
@@ -303,8 +290,6 @@ persist-settings: distclean
|
||||
echo WARN=$(WARN) >> .make-settings
|
||||
echo OPT=$(OPT) >> .make-settings
|
||||
echo MALLOC=$(MALLOC) >> .make-settings
|
||||
echo BUILD_TLS=$(BUILD_TLS) >> .make-settings
|
||||
echo USE_SYSTEMD=$(USE_SYSTEMD) >> .make-settings
|
||||
echo CFLAGS=$(CFLAGS) >> .make-settings
|
||||
echo CXXFLAGS=$(CXXFLAGS) >> .make-settings
|
||||
echo LDFLAGS=$(LDFLAGS) >> .make-settings
|
||||
@@ -417,7 +402,7 @@ bench: $(REDIS_BENCHMARK_NAME)
|
||||
$(MAKE) CXXFLAGS="-m32" CFLAGS="-m32" LDFLAGS="-m32"
|
||||
|
||||
gcov:
|
||||
$(MAKE) REDIS_CXXFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
|
||||
$(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
|
||||
|
||||
noopt:
|
||||
$(MAKE) OPTIMIZATION="-O0"
|
||||
|
||||
+5
-10
@@ -735,11 +735,10 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
|
||||
* EEXIST: You are adding a key pattern after "*" was already added. This is
|
||||
* almost surely an error on the user side.
|
||||
* ENODEV: The password you are trying to remove from the user does not exist.
|
||||
* EBADMSG: The hash you are trying to add is not a valid hash.
|
||||
* EBADMSG: The hash you are trying to add is not a valid hash.
|
||||
*/
|
||||
int ACLSetUser(user *u, const char *op, ssize_t oplen) {
|
||||
if (oplen == -1) oplen = strlen(op);
|
||||
if (oplen == 0) return C_OK; /* Empty string is a no-operation. */
|
||||
if (!strcasecmp(op,"on")) {
|
||||
u->flags |= USER_FLAG_ENABLED;
|
||||
u->flags &= ~USER_FLAG_DISABLED;
|
||||
@@ -1301,7 +1300,7 @@ sds ACLLoadFromFile(const char *filename) {
|
||||
if (lines[i][0] == '\0') continue;
|
||||
|
||||
/* Split into arguments */
|
||||
argv = sdssplitlen(lines[i],sdslen(lines[i])," ",1,&argc);
|
||||
argv = sdssplitargs(lines[i],&argc);
|
||||
if (argv == NULL) {
|
||||
errors = sdscatprintf(errors,
|
||||
"%s:%d: unbalanced quotes in acl line. ",
|
||||
@@ -1330,18 +1329,14 @@ sds ACLLoadFromFile(const char *filename) {
|
||||
errors = sdscatprintf(errors,
|
||||
"'%s:%d: username '%s' contains invalid characters. ",
|
||||
g_pserver->acl_filename, linenum, argv[1]);
|
||||
sdsfreesplitres(argv,argc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Try to process the line using the fake user to validate if
|
||||
* the rules are able to apply cleanly. At this stage we also
|
||||
* trim trailing spaces, so that we don't have to handle that
|
||||
* in ACLSetUser(). */
|
||||
/* Try to process the line using the fake user to validate iif
|
||||
* the rules are able to apply cleanly. */
|
||||
ACLSetUser(fakeuser,"reset",-1);
|
||||
int j;
|
||||
for (j = 2; j < argc; j++) {
|
||||
argv[j] = sdstrim(argv[j],"\t\r\n");
|
||||
if (ACLSetUser(fakeuser,argv[j],sdslen(argv[j])) != C_OK) {
|
||||
const char *errmsg = ACLSetUserStringError();
|
||||
errors = sdscatprintf(errors,
|
||||
@@ -1915,7 +1910,7 @@ void aclCommand(client *c) {
|
||||
addReplyBulkCString(c,"client-info");
|
||||
addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));
|
||||
}
|
||||
} else if (c->argc == 2 && !strcasecmp(sub,"help")) {
|
||||
} else if (!strcasecmp(sub,"help")) {
|
||||
const char *help[] = {
|
||||
"LOAD -- Reload users from the ACL file.",
|
||||
"SAVE -- Save the current config to the ACL file.",
|
||||
|
||||
+6
-9
@@ -234,7 +234,7 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
|
||||
int ret = AE_OK;
|
||||
|
||||
aeCommand cmd;
|
||||
aeCommand cmd = {};
|
||||
cmd.op = AE_ASYNC_OP::CreateFileEvent;
|
||||
cmd.fd = fd;
|
||||
cmd.mask = mask;
|
||||
@@ -258,11 +258,9 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
|
||||
if (fSynchronous)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::adopt_lock);
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
cmd.pctl->cv.wait(ulock);
|
||||
ret = cmd.pctl->rval;
|
||||
}
|
||||
delete cmd.pctl;
|
||||
}
|
||||
|
||||
@@ -296,13 +294,14 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynch
|
||||
}
|
||||
|
||||
aeCommand cmd = {};
|
||||
memset(&cmd, 0, sizeof(aeCommand));
|
||||
cmd.op = AE_ASYNC_OP::PostCppFunction;
|
||||
cmd.pfn = new (MALLOC_LOCAL) std::function<void()>(fn);
|
||||
cmd.pctl = nullptr;
|
||||
cmd.fLock = fLock;
|
||||
if (fSynchronous)
|
||||
{
|
||||
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl;
|
||||
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
|
||||
cmd.pctl->mutexcv.lock();
|
||||
}
|
||||
|
||||
@@ -313,11 +312,9 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynch
|
||||
int ret = AE_OK;
|
||||
if (fSynchronous)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::adopt_lock);
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
cmd.pctl->cv.wait(ulock);
|
||||
ret = cmd.pctl->rval;
|
||||
}
|
||||
delete cmd.pctl;
|
||||
}
|
||||
return ret;
|
||||
@@ -457,7 +454,7 @@ void aeDeleteFileEventAsync(aeEventLoop *eventLoop, int fd, int mask)
|
||||
{
|
||||
if (eventLoop == g_eventLoopThisThread)
|
||||
return aeDeleteFileEvent(eventLoop, fd, mask);
|
||||
aeCommand cmd = {};
|
||||
aeCommand cmd;
|
||||
cmd.op = AE_ASYNC_OP::DeleteFileEvent;
|
||||
cmd.fd = fd;
|
||||
cmd.mask = mask;
|
||||
|
||||
+1
-4
@@ -9,14 +9,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void arm(client *c, bool fIfNeeded = false) // if a client is passed, then the client is already locked
|
||||
void arm(client *c) // if a client is passed, then the client is already locked
|
||||
{
|
||||
if (m_fArmed)
|
||||
return;
|
||||
|
||||
if (fIfNeeded && aeThreadOwnsLock())
|
||||
return;
|
||||
|
||||
serverAssertDebug(!GlobalLocksAcquired());
|
||||
|
||||
if (c != nullptr)
|
||||
|
||||
+15
-27
@@ -671,23 +671,19 @@ sds catCommandForAofAndActiveReplication(sds buf, struct redisCommand *cmd, robj
|
||||
} else if (cmd->proc == setCommand && argc > 3) {
|
||||
int i;
|
||||
robj *exarg = NULL, *pxarg = NULL;
|
||||
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
|
||||
buf = catAppendOnlyGenericCommand(buf,3,argv);
|
||||
for (i = 3; i < argc; i ++) {
|
||||
if (!strcasecmp(szFromObj(argv[i]), "ex")) exarg = argv[i+1];
|
||||
if (!strcasecmp(szFromObj(argv[i]), "px")) pxarg = argv[i+1];
|
||||
}
|
||||
serverAssert(!(exarg && pxarg));
|
||||
if (exarg || pxarg) {
|
||||
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
|
||||
buf = catAppendOnlyGenericCommand(buf,3,argv);
|
||||
if (exarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cserver.expireCommand,argv[1],
|
||||
exarg);
|
||||
if (pxarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cserver.pexpireCommand,argv[1],
|
||||
pxarg);
|
||||
} else {
|
||||
buf = catAppendOnlyGenericCommand(buf,argc,argv);
|
||||
}
|
||||
if (exarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cserver.expireCommand,argv[1],
|
||||
exarg);
|
||||
if (pxarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cserver.pexpireCommand,argv[1],
|
||||
pxarg);
|
||||
} else if (cmd->proc == expireMemberCommand || cmd->proc == expireMemberAtCommand ||
|
||||
cmd->proc == pexpireMemberAtCommand) {
|
||||
/* Translate subkey expire commands to PEXPIREMEMBERAT */
|
||||
@@ -1335,16 +1331,12 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
|
||||
while(raxNext(&ri)) {
|
||||
streamCG *group = (streamCG*)ri.data;
|
||||
/* Emit the XGROUP CREATE in order to create the group. */
|
||||
if (!rioWriteBulkCount(r,'*',5) ||
|
||||
!rioWriteBulkString(r,"XGROUP",6) ||
|
||||
!rioWriteBulkString(r,"CREATE",6) ||
|
||||
!rioWriteBulkObject(r,key) ||
|
||||
!rioWriteBulkString(r,(char*)ri.key,ri.key_len) ||
|
||||
!rioWriteBulkStreamID(r,&group->last_id))
|
||||
{
|
||||
raxStop(&ri);
|
||||
return 0;
|
||||
}
|
||||
if (rioWriteBulkCount(r,'*',5) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"XGROUP",6) == 0) return 0;
|
||||
if (rioWriteBulkString(r,"CREATE",6) == 0) return 0;
|
||||
if (rioWriteBulkObject(r,key) == 0) return 0;
|
||||
if (rioWriteBulkString(r,(char*)ri.key,ri.key_len) == 0) return 0;
|
||||
if (rioWriteBulkStreamID(r,&group->last_id) == 0) return 0;
|
||||
|
||||
/* Generate XCLAIMs for each consumer that happens to
|
||||
* have pending entries. Empty consumers have no semantical
|
||||
@@ -1365,9 +1357,6 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
|
||||
ri.key_len,consumer,
|
||||
ri_pel.key,nack) == 0)
|
||||
{
|
||||
raxStop(&ri_pel);
|
||||
raxStop(&ri_cons);
|
||||
raxStop(&ri);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1433,8 +1422,7 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
/* Iterate this DB writing every entry */
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
sds keystr;
|
||||
redisObjectStack key;
|
||||
robj *o = nullptr;
|
||||
robj key, *o;
|
||||
|
||||
keystr = (sds)dictGetKey(de);
|
||||
o = (robj*)dictGetVal(de);
|
||||
|
||||
+7
-10
@@ -257,7 +257,7 @@ int64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {
|
||||
/* If the top significant bit is 1, propagate it to all the
|
||||
* higher bits for two's complement representation of signed
|
||||
* integers. */
|
||||
if (bits < 64 && (value & ((uint64_t)1 << (bits-1))))
|
||||
if (value & ((uint64_t)1 << (bits-1)))
|
||||
value |= ((uint64_t)-1) << bits;
|
||||
return value;
|
||||
}
|
||||
@@ -356,6 +356,7 @@ int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int
|
||||
|
||||
handle_wrap:
|
||||
{
|
||||
uint64_t mask = ((uint64_t)-1) << bits;
|
||||
uint64_t msb = (uint64_t)1 << (bits-1);
|
||||
uint64_t a = value, b = incr, c;
|
||||
c = a+b; /* Perform addition as unsigned so that's defined. */
|
||||
@@ -363,13 +364,10 @@ handle_wrap:
|
||||
/* If the sign bit is set, propagate to all the higher order
|
||||
* bits, to cap the negative value. If it's clear, mask to
|
||||
* the positive integer limit. */
|
||||
if (bits < 64) {
|
||||
uint64_t mask = ((uint64_t)-1) << bits;
|
||||
if (c & msb) {
|
||||
c |= mask;
|
||||
} else {
|
||||
c &= ~mask;
|
||||
}
|
||||
if (c & msb) {
|
||||
c |= mask;
|
||||
} else {
|
||||
c &= ~mask;
|
||||
}
|
||||
*limit = c;
|
||||
}
|
||||
@@ -833,12 +831,11 @@ void bitopCommand(client *c) {
|
||||
setKey(c,c->db,targetkey,o);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id);
|
||||
decrRefCount(o);
|
||||
g_pserver->dirty++;
|
||||
} else if (dbDelete(c->db,targetkey)) {
|
||||
signalModifiedKey(c,c->db,targetkey);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",targetkey,c->db->id);
|
||||
g_pserver->dirty++;
|
||||
}
|
||||
g_pserver->dirty++;
|
||||
addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
|
||||
}
|
||||
|
||||
|
||||
+17
-78
@@ -435,15 +435,7 @@ int clusterLockConfig(char *filename) {
|
||||
return C_ERR;
|
||||
}
|
||||
/* Lock acquired: leak the 'fd' by not closing it, so that we'll retain the
|
||||
* lock to the file as long as the process exists.
|
||||
*
|
||||
* After fork, the child process will get the fd opened by the parent process,
|
||||
* we need save `fd` to `cluster_config_file_lock_fd`, so that in redisFork(),
|
||||
* it will be closed in the child process.
|
||||
* If it is not closed, when the main process is killed -9, but the child process
|
||||
* (redis-aof-rewrite) is still alive, the fd(lock) will still be held by the
|
||||
* child process, and the main process will fail to get lock, means fail to start. */
|
||||
g_pserver->cluster_config_file_lock_fd = fd;
|
||||
* lock to the file as long as the process exists. */
|
||||
#endif /* __sun */
|
||||
|
||||
return C_OK;
|
||||
@@ -498,7 +490,6 @@ void clusterInit(void) {
|
||||
|
||||
/* Lock the cluster config file to make sure every node uses
|
||||
* its own nodes.conf. */
|
||||
g_pserver->cluster_config_file_lock_fd = -1;
|
||||
if (clusterLockConfig(g_pserver->cluster_configfile) == C_ERR)
|
||||
exit(1);
|
||||
|
||||
@@ -717,17 +708,7 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection *conn = g_pserver->tls_cluster ?
|
||||
connCreateAcceptedTLS(cfd, TLS_CLIENT_AUTH_YES) : connCreateAcceptedSocket(cfd);
|
||||
|
||||
/* Make sure connection is not in an error state */
|
||||
if (connGetState(conn) != CONN_STATE_ACCEPTING) {
|
||||
serverLog(LL_VERBOSE,
|
||||
"Error creating an accepting connection for cluster node: %s",
|
||||
connGetLastError(conn));
|
||||
connClose(conn);
|
||||
return;
|
||||
}
|
||||
connection *conn = g_pserver->tls_cluster ? connCreateAcceptedTLS(cfd,1) : connCreateAcceptedSocket(cfd);
|
||||
connNonBlock(conn);
|
||||
connEnableTcpNoDelay(conn);
|
||||
|
||||
@@ -748,16 +729,6 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the approximated number of sockets we are using in order to
|
||||
* take the cluster bus connections. */
|
||||
unsigned long getClusterConnectionsCount(void) {
|
||||
/* We decrement the number of nodes by one, since there is the
|
||||
* "myself" node too in the list. Each node uses two file descriptors,
|
||||
* one incoming and one outgoing, thus the multiplication by 2. */
|
||||
return g_pserver->cluster_enabled ?
|
||||
((dictSize(g_pserver->cluster->nodes)-1)*2) : 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Key space handling
|
||||
* -------------------------------------------------------------------------- */
|
||||
@@ -1321,11 +1292,8 @@ void markNodeAsFailingIfNeeded(clusterNode *node) {
|
||||
node->fail_time = mstime();
|
||||
|
||||
/* Broadcast the failing node name to everybody, forcing all the other
|
||||
* reachable nodes to flag the node as FAIL.
|
||||
* We do that even if this node is a replica and not a master: anyway
|
||||
* the failing state is triggered collecting failure reports from masters,
|
||||
* so here the replica is only helping propagating this status. */
|
||||
clusterSendFail(node->name);
|
||||
* reachable nodes to flag the node as FAIL. */
|
||||
if (nodeIsMaster(myself)) clusterSendFail(node->name);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
|
||||
}
|
||||
|
||||
@@ -1533,10 +1501,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
}
|
||||
} else {
|
||||
/* If it's not in NOADDR state and we don't have it, we
|
||||
* 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.
|
||||
* start a handshake process against this IP/PORT pairs.
|
||||
*
|
||||
* Note that we require that the sender of this gossip message
|
||||
* is a well known node in our cluster, otherwise we risk
|
||||
@@ -1545,12 +1510,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
!(flags & CLUSTER_NODE_NOADDR) &&
|
||||
!clusterBlacklistExists(g->nodename))
|
||||
{
|
||||
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);
|
||||
clusterStartHandshake(g->ip,ntohs(g->port),ntohs(g->cport));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1818,7 +1778,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
} else if (type == CLUSTERMSG_TYPE_MODULE) {
|
||||
uint32_t explen = sizeof(clusterMsg)-sizeof(union clusterMsgData);
|
||||
|
||||
explen += sizeof(clusterMsgModule) -
|
||||
explen += sizeof(clusterMsgDataPublish) -
|
||||
3 + ntohl(hdr->data.module.msg.len);
|
||||
if (totlen != explen) return 1;
|
||||
}
|
||||
@@ -4173,15 +4133,11 @@ sds clusterGenNodeDescription(clusterNode *node) {
|
||||
else
|
||||
ci = sdscatlen(ci," - ",3);
|
||||
|
||||
unsigned long long nodeEpoch = node->configEpoch;
|
||||
if (nodeIsSlave(node) && node->slaveof) {
|
||||
nodeEpoch = node->slaveof->configEpoch;
|
||||
}
|
||||
/* Latency from the POV of this node, config epoch, link status */
|
||||
ci = sdscatprintf(ci,"%lld %lld %llu %s",
|
||||
(long long) node->ping_sent,
|
||||
(long long) node->pong_received,
|
||||
nodeEpoch,
|
||||
(unsigned long long) node->configEpoch,
|
||||
(node->link || node->flags & CLUSTER_NODE_MYSELF) ?
|
||||
"connected" : "disconnected");
|
||||
|
||||
@@ -5061,8 +5017,7 @@ void restoreCommand(client *c) {
|
||||
}
|
||||
|
||||
/* Make sure this key does not already exist here... */
|
||||
robj *key = c->argv[1];
|
||||
if (!replace && lookupKeyWrite(c->db,key) != NULL) {
|
||||
if (!replace && lookupKeyWrite(c->db,c->argv[1]) != NULL) {
|
||||
addReply(c,shared.busykeyerr);
|
||||
return;
|
||||
}
|
||||
@@ -5084,38 +5039,24 @@ void restoreCommand(client *c) {
|
||||
|
||||
rioInitWithBuffer(&payload,szFromObj(c->argv[3]));
|
||||
if (((type = rdbLoadObjectType(&payload)) == -1) ||
|
||||
((obj = rdbLoadObject(type,&payload,szFromObj(key), OBJ_MVCC_INVALID)) == NULL))
|
||||
((obj = rdbLoadObject(type,&payload,szFromObj(c->argv[1]), OBJ_MVCC_INVALID)) == NULL))
|
||||
{
|
||||
addReplyError(c,"Bad data format");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove the old key if needed. */
|
||||
int deleted = 0;
|
||||
if (replace)
|
||||
deleted = dbDelete(c->db,key);
|
||||
|
||||
if (ttl && !absttl) ttl+=mstime();
|
||||
if (ttl && checkAlreadyExpired(ttl)) {
|
||||
if (deleted) {
|
||||
rewriteClientCommandVector(c,2,shared.del,key);
|
||||
signalModifiedKey(c,c->db,key);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
|
||||
g_pserver->dirty++;
|
||||
}
|
||||
decrRefCount(obj);
|
||||
addReply(c, shared.ok);
|
||||
return;
|
||||
}
|
||||
if (replace) dbDelete(c->db,c->argv[1]);
|
||||
|
||||
/* Create the key and set the TTL if any */
|
||||
dbAdd(c->db,key,obj);
|
||||
dbAdd(c->db,c->argv[1],obj);
|
||||
if (ttl) {
|
||||
setExpire(c,c->db,key,nullptr,ttl);
|
||||
if (!absttl) ttl+=mstime();
|
||||
setExpire(c,c->db,c->argv[1],nullptr,ttl);
|
||||
}
|
||||
objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock,1000);
|
||||
signalModifiedKey(c,c->db,key);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",key,c->db->id);
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",c->argv[1],c->db->id);
|
||||
addReply(c,shared.ok);
|
||||
g_pserver->dirty++;
|
||||
}
|
||||
@@ -5826,10 +5767,8 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
/* Handle the read-only client case reading from a slave: if this
|
||||
* node is a slave and the request is about an hash slot our master
|
||||
* is serving, we can reply without redirection. */
|
||||
int is_readonly_command = (c->cmd->flags & CMD_READONLY) ||
|
||||
(c->cmd->proc == execCommand && !(c->mstate.cmd_inv_flags & CMD_READONLY));
|
||||
if (c->flags & CLIENT_READONLY &&
|
||||
(is_readonly_command || cmd->proc == evalCommand ||
|
||||
(cmd->flags & CMD_READONLY || cmd->proc == evalCommand ||
|
||||
cmd->proc == evalShaCommand) &&
|
||||
nodeIsSlave(myself) &&
|
||||
myself->slaveof == n)
|
||||
|
||||
@@ -287,7 +287,6 @@ typedef struct {
|
||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
|
||||
int clusterRedirectBlockedClientIfNeeded(client *c);
|
||||
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
|
||||
unsigned long getClusterConnectionsCount(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+11
-248
@@ -35,7 +35,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
const char *KEYDB_SET_VERSION = KEYDB_REAL_VERSION;
|
||||
char *replicateDbDisableString = nullptr; // The raw string the user provided so we can rewrite the same way
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Config file name-value maps.
|
||||
@@ -101,12 +100,6 @@ configEnum repl_diskless_load_enum[] = {
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
configEnum tls_auth_clients_enum[] = {
|
||||
{"no", TLS_CLIENT_AUTH_NO},
|
||||
{"yes", TLS_CLIENT_AUTH_YES},
|
||||
{"optional", TLS_CLIENT_AUTH_OPTIONAL},
|
||||
{NULL, 0}
|
||||
};
|
||||
/* Output buffer limits presets. */
|
||||
clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
|
||||
{0, 0, 0}, /* normal */
|
||||
@@ -114,9 +107,6 @@ clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
|
||||
{1024*1024*32, 1024*1024*8, 60} /* pubsub */
|
||||
};
|
||||
|
||||
/* OOM Score defaults */
|
||||
int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT] = { 0, 200, 800 };
|
||||
|
||||
/* Generic config infrastructure function pointers
|
||||
* int is_valid_fn(val, err)
|
||||
* Return 1 when val is valid, and 0 when invalid.
|
||||
@@ -299,59 +289,6 @@ void queueLoadModule(sds path, sds *argv, int argc) {
|
||||
listAddNodeTail(g_pserver->loadmodule_queue,loadmod);
|
||||
}
|
||||
|
||||
/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
|
||||
* g_pserver->oom_score_adj_values if valid.
|
||||
*/
|
||||
|
||||
static int updateOOMScoreAdjValues(sds *args, const char **err) {
|
||||
int i;
|
||||
int values[CONFIG_OOM_COUNT];
|
||||
|
||||
for (i = 0; i < CONFIG_OOM_COUNT; i++) {
|
||||
char *eptr;
|
||||
long long val = strtoll(args[i], &eptr, 10);
|
||||
|
||||
if (*eptr != '\0' || val < -1000 || val > 1000) {
|
||||
if (err) *err = "Invalid oom-score-adj-values, elements must be between -1000 and 1000.";
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
values[i] = val;
|
||||
}
|
||||
|
||||
/* Verify that the values make sense. If they don't omit a warning but
|
||||
* keep the configuration, which may still be valid for privileged processes.
|
||||
*/
|
||||
|
||||
if (values[CONFIG_OOM_REPLICA] < values[CONFIG_OOM_MASTER] ||
|
||||
values[CONFIG_OOM_BGCHILD] < values[CONFIG_OOM_REPLICA]) {
|
||||
serverLog(LOG_WARNING,
|
||||
"The oom-score-adj-values configuration may not work for non-privileged processes! "
|
||||
"Please consult the documentation.");
|
||||
}
|
||||
|
||||
/* Store values, retain previous config for rollback in case we fail. */
|
||||
int old_values[CONFIG_OOM_COUNT];
|
||||
for (i = 0; i < CONFIG_OOM_COUNT; i++) {
|
||||
old_values[i] = g_pserver->oom_score_adj_values[i];
|
||||
g_pserver->oom_score_adj_values[i] = values[i];
|
||||
}
|
||||
|
||||
/* Update */
|
||||
if (setOOMScoreAdj(-1) == C_ERR) {
|
||||
/* Roll back */
|
||||
for (i = 0; i < CONFIG_OOM_COUNT; i++)
|
||||
g_pserver->oom_score_adj_values[i] = old_values[i];
|
||||
|
||||
if (err)
|
||||
*err = "Failed to apply oom-score-adj-values configuration, check server logs.";
|
||||
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
void initConfigValues() {
|
||||
for (standardConfig *config = configs; config->name != NULL; config++) {
|
||||
config->interface.init(config->data);
|
||||
@@ -543,8 +480,6 @@ void loadServerConfigFromString(char *config) {
|
||||
cserver.client_obuf_limits[type].hard_limit_bytes = hard;
|
||||
cserver.client_obuf_limits[type].soft_limit_bytes = soft;
|
||||
cserver.client_obuf_limits[type].soft_limit_seconds = soft_seconds;
|
||||
} else if (!strcasecmp(argv[0],"oom-score-adj-values") && argc == 1 + CONFIG_OOM_COUNT) {
|
||||
if (updateOOMScoreAdjValues(&argv[1], &err) == C_ERR) goto loaderr;
|
||||
} else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
|
||||
int flags = keyspaceEventsStringToFlags(argv[1]);
|
||||
|
||||
@@ -583,7 +518,7 @@ void loadServerConfigFromString(char *config) {
|
||||
err = "KeyDB not compliled with scratch-file support.";
|
||||
goto loaderr;
|
||||
#endif
|
||||
} else if ((!strcasecmp(argv[0],"server-threads") || !strcasecmp(argv[0],"io-threads")) && argc == 2) {
|
||||
} else if (!strcasecmp(argv[0],"server-threads") && argc == 2) {
|
||||
cserver.cthreads = atoi(argv[1]);
|
||||
if (cserver.cthreads <= 0 || cserver.cthreads > MAX_EVENT_LOOPS) {
|
||||
err = "Invalid number of threads specified";
|
||||
@@ -606,7 +541,7 @@ void loadServerConfigFromString(char *config) {
|
||||
}
|
||||
} else if (!strcasecmp(argv[0], "active-replica") && argc == 2) {
|
||||
g_pserver->fActiveReplica = yesnotoi(argv[1]);
|
||||
if (g_pserver->fActiveReplica && g_pserver->repl_slave_ro) {
|
||||
if (g_pserver->repl_slave_ro) {
|
||||
g_pserver->repl_slave_ro = FALSE;
|
||||
serverLog(LL_NOTICE, "Notice: \"active-replica yes\" implies \"replica-read-only no\"");
|
||||
}
|
||||
@@ -670,8 +605,7 @@ void loadServerConfig(char *filename, char *options) {
|
||||
} else {
|
||||
if ((fp = fopen(filename,"r")) == NULL) {
|
||||
serverLog(LL_WARNING,
|
||||
"Fatal error, can't open config file '%s': %s",
|
||||
filename, strerror(errno));
|
||||
"Fatal error, can't open config file '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -841,17 +775,6 @@ void configSetCommand(client *c) {
|
||||
cserver.client_obuf_limits[type].soft_limit_seconds = soft_seconds;
|
||||
}
|
||||
sdsfreesplitres(v,vlen);
|
||||
} config_set_special_field("oom-score-adj-values") {
|
||||
int vlen;
|
||||
int success = 1;
|
||||
|
||||
sds *v = sdssplitlen(szFromObj(o), sdslen(szFromObj(o)), " ", 1, &vlen);
|
||||
if (vlen != CONFIG_OOM_COUNT || updateOOMScoreAdjValues(v, &errstr) == C_ERR)
|
||||
success = 0;
|
||||
|
||||
sdsfreesplitres(v, vlen);
|
||||
if (!success)
|
||||
goto badfmt;
|
||||
} config_set_special_field("notify-keyspace-events") {
|
||||
int flags = keyspaceEventsStringToFlags(szFromObj(o));
|
||||
|
||||
@@ -1064,26 +987,6 @@ void configGetCommand(client *c) {
|
||||
}
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"oom-score-adj-values",0)) {
|
||||
sds buf = sdsempty();
|
||||
int j;
|
||||
|
||||
for (j = 0; j < CONFIG_OOM_COUNT; j++) {
|
||||
buf = sdscatprintf(buf,"%d", g_pserver->oom_score_adj_values[j]);
|
||||
if (j != CONFIG_OOM_COUNT-1)
|
||||
buf = sdscatlen(buf," ",1);
|
||||
}
|
||||
|
||||
addReplyBulkCString(c,"oom-score-adj-values");
|
||||
addReplyBulkCString(c,buf);
|
||||
sdsfree(buf);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"active-replica",1)) {
|
||||
addReplyBulkCString(c,"active-replica");
|
||||
addReplyBulkCString(c, g_pserver->fActiveReplica ? "yes" : "no");
|
||||
matches++;
|
||||
}
|
||||
|
||||
setDeferredMapLen(c,replylen,matches);
|
||||
}
|
||||
@@ -1132,8 +1035,6 @@ struct rewriteConfigState {
|
||||
sds *lines; /* Current lines as an array of sds strings */
|
||||
int has_tail; /* True if we already added directives that were
|
||||
not present in the original config file. */
|
||||
int force_all; /* True if we want all keywords to be force
|
||||
written. Currently only used for testing. */
|
||||
};
|
||||
|
||||
/* Append the new line to the current configuration state. */
|
||||
@@ -1181,7 +1082,6 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
|
||||
state->numlines = 0;
|
||||
state->lines = NULL;
|
||||
state->has_tail = 0;
|
||||
state->force_all = 0;
|
||||
if (fp == NULL) return state;
|
||||
|
||||
/* Read the old file line by line, populate the state. */
|
||||
@@ -1260,7 +1160,7 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *opti
|
||||
|
||||
rewriteConfigMarkAsProcessed(state,option);
|
||||
|
||||
if (!l && !force && !state->force_all) {
|
||||
if (!l && !force) {
|
||||
/* Option not used previously, and we are not forced to use it. */
|
||||
sdsfree(line);
|
||||
sdsfree(o);
|
||||
@@ -1504,26 +1404,6 @@ void rewriteConfigClientoutputbufferlimitOption(struct rewriteConfigState *state
|
||||
}
|
||||
}
|
||||
|
||||
/* Rewrite the oom-score-adj-values option. */
|
||||
void rewriteConfigOOMScoreAdjValuesOption(struct rewriteConfigState *state) {
|
||||
int force = 0;
|
||||
int j;
|
||||
const char *option = "oom-score-adj-values";
|
||||
sds line;
|
||||
|
||||
line = sdsnew(option);
|
||||
line = sdscatlen(line, " ", 1);
|
||||
for (j = 0; j < CONFIG_OOM_COUNT; j++) {
|
||||
if (g_pserver->oom_score_adj_values[j] != configOOMScoreAdjValuesDefaults[j])
|
||||
force = 1;
|
||||
|
||||
line = sdscatprintf(line, "%d", g_pserver->oom_score_adj_values[j]);
|
||||
if (j+1 != CONFIG_OOM_COUNT)
|
||||
line = sdscatlen(line, " ", 1);
|
||||
}
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the bind option. */
|
||||
void rewriteConfigBindOption(struct rewriteConfigState *state) {
|
||||
int force = 1;
|
||||
@@ -1692,18 +1572,15 @@ cleanup:
|
||||
*
|
||||
* Configuration parameters that are at their default value, unless already
|
||||
* explicitly included in the old configuration file, are not rewritten.
|
||||
* The force_all flag overrides this behavior and forces everything to be
|
||||
* written. This is currently only used for testing purposes.
|
||||
*
|
||||
* On error -1 is returned and errno is set accordingly, otherwise 0. */
|
||||
int rewriteConfig(char *path, int force_all) {
|
||||
int rewriteConfig(char *path) {
|
||||
struct rewriteConfigState *state;
|
||||
sds newcontent;
|
||||
int retval;
|
||||
|
||||
/* Step 1: read the old config into our rewrite state. */
|
||||
if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1;
|
||||
if (force_all) state->force_all = 1;
|
||||
|
||||
/* Step 2: rewrite every single option, replacing or appending it inside
|
||||
* the rewrite state. */
|
||||
@@ -1727,7 +1604,6 @@ int rewriteConfig(char *path, int force_all) {
|
||||
rewriteConfigClientoutputbufferlimitOption(state);
|
||||
rewriteConfigYesNoOption(state,"active-replica",g_pserver->fActiveReplica,CONFIG_DEFAULT_ACTIVE_REPLICA);
|
||||
rewriteConfigStringOption(state, "version-override",KEYDB_SET_VERSION,KEYDB_REAL_VERSION);
|
||||
rewriteConfigOOMScoreAdjValuesOption(state);
|
||||
|
||||
/* Rewrite Sentinel config if in Sentinel mode. */
|
||||
if (g_pserver->sentinel_mode) rewriteConfigSentinelOption(state);
|
||||
@@ -2193,79 +2069,6 @@ static int isValidAOFfilename(char *val, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateDbDisable(char *val, const char **err) {
|
||||
cserver.setDbReplicateDisable.clear();
|
||||
serverLog(LL_WARNING, "UPDATE");
|
||||
if (val == nullptr)
|
||||
return 1;
|
||||
|
||||
char *pchCur = val;
|
||||
char *pchStart = val;
|
||||
int dbStart = -1;
|
||||
|
||||
for (;;) {
|
||||
char *endptr;
|
||||
switch (*pchCur)
|
||||
{
|
||||
case '-':
|
||||
if (pchStart < pchCur && dbStart < 0) {
|
||||
errno = 0;
|
||||
dbStart = strtol(pchStart, &endptr, 10);
|
||||
if (errno || endptr != pchCur || dbStart < 0) {
|
||||
*err = "Invalid database in list";
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
*err = "Invalid database range in list";
|
||||
return 0;
|
||||
}
|
||||
pchStart = pchCur + 1;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
case '\0':
|
||||
if (pchStart < pchCur) {
|
||||
errno = 0;
|
||||
int db = strtol(pchStart, &endptr, 10);
|
||||
if (errno || endptr != pchCur || db < 0) {
|
||||
*err = "Invalid database in list";
|
||||
return 0;
|
||||
}
|
||||
if (dbStart < 0)
|
||||
dbStart = db;
|
||||
for (;dbStart <= db; ++dbStart) {
|
||||
if (cserver.setDbReplicateDisable.find(dbStart) == cserver.setDbReplicateDisable.end()) {
|
||||
cserver.setDbReplicateDisable.insert(dbStart);
|
||||
}
|
||||
}
|
||||
dbStart = -1;
|
||||
}
|
||||
pchStart = pchCur + 1;
|
||||
break;
|
||||
}
|
||||
if (*pchCur == '\0')
|
||||
break;
|
||||
++pchCur;
|
||||
}
|
||||
|
||||
if (dbStart >= 0) {
|
||||
*err = "Incomplete database range";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Display the disabled databases in the log (sorted ascending)
|
||||
std::vector<int> vecdb;
|
||||
for (int db : cserver.setDbReplicateDisable) {
|
||||
vecdb.push_back(db);
|
||||
}
|
||||
std::sort(vecdb.begin(), vecdb.end());
|
||||
for (int db : vecdb) {
|
||||
serverLog(LL_WARNING, "Replicate disabled for DB: %d", db);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateHZ(long long val, long long prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
UNUSED(err);
|
||||
@@ -2364,36 +2167,13 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int validateMultiMasterNoForward(int val, const char **) {
|
||||
if (val) {
|
||||
serverLog(LL_WARNING, "WARNING: multi-master-no-forward is set, you *must* use a mesh topology or dataloss will occur");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateOOMScoreAdj(int val, int prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
|
||||
if (val) {
|
||||
if (setOOMScoreAdj(-1) == C_ERR) {
|
||||
*err = "Failed to set current oom_score_adj. Check server logs.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
static int updateTlsCfg(char *val, char *prev, const char **err) {
|
||||
UNUSED(val);
|
||||
UNUSED(prev);
|
||||
UNUSED(err);
|
||||
|
||||
/* If TLS is enabled, try to configure OpenSSL. */
|
||||
if ((g_pserver->tls_port || g_pserver->tls_replication || g_pserver->tls_cluster)
|
||||
&& tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
|
||||
*err = "Unable to update TLS configuration. Check server logs.";
|
||||
if (tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
|
||||
*err = "Unable to configure tls-cert-file. Check server logs.";
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -2403,15 +2183,8 @@ static int updateTlsCfgBool(int val, int prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
return updateTlsCfg(NULL, NULL, err);
|
||||
}
|
||||
|
||||
static int updateTlsCfgInt(long long val, long long prev, const char **err) {
|
||||
UNUSED(val);
|
||||
UNUSED(prev);
|
||||
return updateTlsCfg(NULL, NULL, err);
|
||||
}
|
||||
#endif /* USE_OPENSSL */
|
||||
|
||||
int fDummy = false;
|
||||
standardConfig configs[] = {
|
||||
/* Bool configs */
|
||||
createBoolConfig("rdbchecksum", NULL, IMMUTABLE_CONFIG, g_pserver->rdb_checksum, 1, NULL, NULL),
|
||||
@@ -2427,7 +2200,6 @@ standardConfig configs[] = {
|
||||
createBoolConfig("lazyfree-lazy-eviction", NULL, MODIFIABLE_CONFIG, g_pserver->lazyfree_lazy_eviction, 0, NULL, NULL),
|
||||
createBoolConfig("lazyfree-lazy-expire", NULL, MODIFIABLE_CONFIG, g_pserver->lazyfree_lazy_expire, 0, NULL, NULL),
|
||||
createBoolConfig("lazyfree-lazy-server-del", NULL, MODIFIABLE_CONFIG, g_pserver->lazyfree_lazy_server_del, 0, NULL, NULL),
|
||||
createBoolConfig("lazyfree-lazy-user-del", NULL, MODIFIABLE_CONFIG, g_pserver->lazyfree_lazy_user_del , 0, NULL, NULL),
|
||||
createBoolConfig("repl-disable-tcp-nodelay", NULL, MODIFIABLE_CONFIG, g_pserver->repl_disable_tcp_nodelay, 0, NULL, NULL),
|
||||
createBoolConfig("repl-diskless-sync", NULL, MODIFIABLE_CONFIG, g_pserver->repl_diskless_sync, 0, NULL, NULL),
|
||||
createBoolConfig("aof-rewrite-incremental-fsync", NULL, MODIFIABLE_CONFIG, g_pserver->aof_rewrite_incremental_fsync, 1, NULL, NULL),
|
||||
@@ -2448,10 +2220,6 @@ standardConfig configs[] = {
|
||||
createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, g_pserver->cluster_enabled, 0, NULL, NULL),
|
||||
createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG, g_pserver->aof_enabled, 0, NULL, updateAppendonly),
|
||||
createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, g_pserver->cluster_allow_reads_when_down, 0, NULL, NULL),
|
||||
createBoolConfig("multi-master-no-forward", NULL, MODIFIABLE_CONFIG, cserver.multimaster_no_forward, 0, validateMultiMasterNoForward, NULL),
|
||||
createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL),
|
||||
createBoolConfig("io-threads-do-reads", NULL, IMMUTABLE_CONFIG, fDummy, 0, NULL, NULL),
|
||||
createBoolConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, g_pserver->oom_score_adj, 0, NULL, updateOOMScoreAdj),
|
||||
|
||||
/* String Configs */
|
||||
createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->acl_filename, "", NULL, NULL),
|
||||
@@ -2468,7 +2236,6 @@ standardConfig configs[] = {
|
||||
createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bio_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->aof_rewrite_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bgsave_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("replicate-db-disable", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, replicateDbDisableString, nullptr, updateDbDisable, nullptr),
|
||||
|
||||
/* Enum Configs */
|
||||
createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, cserver.supervised_mode, SUPERVISED_NONE, NULL, NULL),
|
||||
@@ -2509,8 +2276,7 @@ standardConfig configs[] = {
|
||||
createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
|
||||
createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),
|
||||
createIntConfig("min-replicas-max-lag", "min-slaves-max-lag", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),
|
||||
createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 20, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("replica-quorum", NULL, MODIFIABLE_CONFIG, -1, INT_MAX, g_pserver->repl_quorum, -1, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 50, INTEGER_CONFIG, NULL, NULL),
|
||||
/* Unsigned int configs */
|
||||
createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, g_pserver->maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),
|
||||
|
||||
@@ -2524,7 +2290,7 @@ standardConfig configs[] = {
|
||||
createLongLongConfig("cluster-node-timeout", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->cluster_node_timeout, 15000, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("slowlog-log-slower-than", NULL, MODIFIABLE_CONFIG, -1, LLONG_MAX, g_pserver->slowlog_log_slower_than, 10000, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 1024*1024, LLONG_MAX, g_pserver->proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
|
||||
createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
|
||||
createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
|
||||
|
||||
@@ -2548,13 +2314,10 @@ standardConfig configs[] = {
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
createIntConfig("tls-port", NULL, IMMUTABLE_CONFIG, 0, 65535, g_pserver->tls_port, 0, INTEGER_CONFIG, NULL, NULL), /* TCP port. */
|
||||
createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt),
|
||||
createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt),
|
||||
createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, g_pserver->tls_cluster, 0, NULL, NULL),
|
||||
createBoolConfig("tls-replication", NULL, MODIFIABLE_CONFIG, g_pserver->tls_replication, 0, NULL, NULL),
|
||||
createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, g_pserver->tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
|
||||
createBoolConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, g_pserver->tls_auth_clients, 1, NULL, NULL),
|
||||
createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),
|
||||
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
|
||||
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
|
||||
createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
|
||||
createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.dh_params_file, NULL, NULL, updateTlsCfg),
|
||||
@@ -2602,7 +2365,7 @@ NULL
|
||||
addReplyError(c,"The server is running without a config file");
|
||||
return;
|
||||
}
|
||||
if (rewriteConfig(cserver.configfile, 0) == -1) {
|
||||
if (rewriteConfig(cserver.configfile) == -1) {
|
||||
serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
|
||||
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
|
||||
} else {
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
#define HAVE_PROC_MAPS 1
|
||||
#define HAVE_PROC_SMAPS 1
|
||||
#define HAVE_PROC_SOMAXCONN 1
|
||||
#define HAVE_PROC_OOM_SCORE_ADJ 1
|
||||
#endif
|
||||
|
||||
/* Test for task_info() */
|
||||
|
||||
+9
-20
@@ -85,12 +85,8 @@ connection *connCreateSocket() {
|
||||
/* Create a new socket-type connection that is already associated with
|
||||
* an accepted connection.
|
||||
*
|
||||
* The socket is not ready for I/O until connAccept() was called and
|
||||
* The socket is not read for I/O until connAccept() was called and
|
||||
* invoked the connection-level accept handler.
|
||||
*
|
||||
* Callers should use connGetState() and verify the created connection
|
||||
* is not in an error state (which is not possible for a socket connection,
|
||||
* but could but possible with other protocols).
|
||||
*/
|
||||
connection *connCreateAcceptedSocket(int fd) {
|
||||
connection *conn = connCreateSocket();
|
||||
@@ -168,7 +164,7 @@ static int connSocketWrite(connection *conn, const void *data, size_t data_len)
|
||||
int ret = write(conn->fd, data, data_len);
|
||||
if (ret < 0 && errno != EAGAIN) {
|
||||
conn->last_errno = errno;
|
||||
conn->state.store(CONN_STATE_ERROR, std::memory_order_relaxed);
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -177,10 +173,10 @@ static int connSocketWrite(connection *conn, const void *data, size_t data_len)
|
||||
static int connSocketRead(connection *conn, void *buf, size_t buf_len) {
|
||||
int ret = read(conn->fd, buf, buf_len);
|
||||
if (!ret) {
|
||||
conn->state.store(CONN_STATE_CLOSED, std::memory_order_release);
|
||||
conn->state = CONN_STATE_CLOSED;
|
||||
} else if (ret < 0 && errno != EAGAIN) {
|
||||
conn->last_errno = errno;
|
||||
conn->state.store(CONN_STATE_ERROR, std::memory_order_release);
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -257,14 +253,14 @@ static void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientD
|
||||
UNUSED(fd);
|
||||
connection *conn = (connection*)clientData;
|
||||
|
||||
if (conn->state.load(std::memory_order_relaxed) == CONN_STATE_CONNECTING &&
|
||||
if (conn->state == CONN_STATE_CONNECTING &&
|
||||
(mask & AE_WRITABLE) && conn->conn_handler) {
|
||||
|
||||
if (connGetSocketError(conn)) {
|
||||
conn->last_errno = errno;
|
||||
conn->state.store(CONN_STATE_ERROR, std::memory_order_release);
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
} else {
|
||||
conn->state.store(CONN_STATE_CONNECTED, std::memory_order_release);
|
||||
conn->state = CONN_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
if (!conn->write_handler) aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
@@ -338,11 +334,6 @@ static ssize_t connSocketSyncReadLine(connection *conn, char *ptr, ssize_t size,
|
||||
return syncReadLine(conn->fd, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static int connSocketGetType(struct connection *conn) {
|
||||
(void) conn;
|
||||
|
||||
return CONN_TYPE_SOCKET;
|
||||
}
|
||||
|
||||
ConnectionType CT_Socket = {
|
||||
connSocketEventHandler,
|
||||
@@ -357,9 +348,7 @@ ConnectionType CT_Socket = {
|
||||
connSocketBlockingConnect,
|
||||
connSocketSyncWrite,
|
||||
connSocketSyncRead,
|
||||
connSocketSyncReadLine,
|
||||
nullptr,
|
||||
connSocketGetType
|
||||
connSocketSyncReadLine
|
||||
};
|
||||
|
||||
|
||||
@@ -418,7 +407,7 @@ int connRecvTimeout(connection *conn, long long ms) {
|
||||
}
|
||||
|
||||
int connGetState(connection *conn) {
|
||||
return conn->state.load(std::memory_order_relaxed);
|
||||
return conn->state;
|
||||
}
|
||||
|
||||
void connSetThreadAffinity(connection *conn, int cpu) {
|
||||
|
||||
@@ -52,9 +52,6 @@ typedef enum {
|
||||
#define CONN_FLAG_READ_THREADSAFE (1<<2)
|
||||
#define CONN_FLAG_WRITE_THREADSAFE (1<<3)
|
||||
|
||||
#define CONN_TYPE_SOCKET 1
|
||||
#define CONN_TYPE_TLS 2
|
||||
|
||||
typedef void (*ConnectionCallbackFunc)(struct connection *conn);
|
||||
|
||||
typedef struct ConnectionType {
|
||||
@@ -72,7 +69,6 @@ typedef struct ConnectionType {
|
||||
ssize_t (*sync_read)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
|
||||
ssize_t (*sync_readline)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
|
||||
void (*marshal_thread)(struct connection *conn);
|
||||
int (*get_type)(struct connection *conn);
|
||||
} ConnectionType;
|
||||
|
||||
struct connection {
|
||||
@@ -208,11 +204,6 @@ static inline void connMarshalThread(connection *conn) {
|
||||
conn->type->marshal_thread(conn);
|
||||
}
|
||||
|
||||
/* Return CONN_TYPE_* for the specified connection */
|
||||
static inline int connGetType(connection *conn) {
|
||||
return conn->type->get_type(conn);
|
||||
}
|
||||
|
||||
connection *connCreateSocket();
|
||||
connection *connCreateAcceptedSocket(int fd);
|
||||
|
||||
|
||||
+18
-17
@@ -93,7 +93,7 @@ static robj *lookupKey(redisDb *db, robj *key, int flags) {
|
||||
updateDbValAccess(de, flags);
|
||||
|
||||
if (flags & LOOKUP_UPDATEMVCC) {
|
||||
setMvccTstamp(val, getMvccTstamp());
|
||||
val->mvcc_tstamp = getMvccTstamp();
|
||||
}
|
||||
return val;
|
||||
} else {
|
||||
@@ -206,9 +206,7 @@ int dbAddCore(redisDb *db, robj *key, robj *val) {
|
||||
serverAssert(!val->FExpires());
|
||||
sds copy = sdsdup(szFromObj(key));
|
||||
int retval = dictAdd(db->pdict, copy, val);
|
||||
uint64_t mvcc = getMvccTstamp();
|
||||
setMvccTstamp(key, mvcc);
|
||||
setMvccTstamp(val, mvcc);
|
||||
val->mvcc_tstamp = key->mvcc_tstamp = getMvccTstamp();
|
||||
|
||||
if (retval == DICT_OK)
|
||||
{
|
||||
@@ -258,7 +256,7 @@ void dbOverwriteCore(redisDb *db, dictEntry *de, robj *key, robj *val, bool fUpd
|
||||
if (fUpdateMvcc) {
|
||||
if (val->getrefcount(std::memory_order_relaxed) == OBJ_SHARED_REFCOUNT)
|
||||
val = dupStringObject(val);
|
||||
setMvccTstamp(val, getMvccTstamp());
|
||||
val->mvcc_tstamp = getMvccTstamp();
|
||||
}
|
||||
|
||||
dictSetVal(db->pdict, de, val);
|
||||
@@ -293,12 +291,12 @@ int dbMerge(redisDb *db, robj *key, robj *val, int fReplace)
|
||||
return (dbAddCore(db, key, val) == DICT_OK);
|
||||
|
||||
robj *old = (robj*)dictGetVal(de);
|
||||
if (mvccFromObj(old) <= mvccFromObj(val))
|
||||
if (old->mvcc_tstamp <= val->mvcc_tstamp)
|
||||
{
|
||||
dbOverwriteCore(db, de, key, val, false, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -685,13 +683,6 @@ void existsCommand(client *c) {
|
||||
addReplyLongLong(c,count);
|
||||
}
|
||||
|
||||
void mexistsCommand(client *c) {
|
||||
addReplyArrayLen(c, c->argc - 1);
|
||||
for (int j = 1; j < c->argc; ++j) {
|
||||
addReplyBool(c, lookupKeyRead(c->db, c->argv[j]));
|
||||
}
|
||||
}
|
||||
|
||||
void selectCommand(client *c) {
|
||||
long id;
|
||||
|
||||
@@ -1057,6 +1048,14 @@ void shutdownCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* When SHUTDOWN is called while the server is loading a dataset in
|
||||
* memory we need to make sure no attempt is performed to save
|
||||
* the dataset on shutdown (otherwise it could overwrite the current DB
|
||||
* with half-read data).
|
||||
*
|
||||
* Also when in Sentinel mode clear the SAVE flag and force NOSAVE. */
|
||||
if (g_pserver->loading || g_pserver->sentinel_mode)
|
||||
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
|
||||
if (prepareForShutdown(flags) == C_OK) throw ShutdownException();
|
||||
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
|
||||
}
|
||||
@@ -1220,7 +1219,8 @@ int dbSwapDatabases(long id1, long id2) {
|
||||
if (id1 < 0 || id1 >= cserver.dbnum ||
|
||||
id2 < 0 || id2 >= cserver.dbnum) return C_ERR;
|
||||
if (id1 == id2) return C_OK;
|
||||
redisDb aux(g_pserver->db[id1]);
|
||||
redisDb aux;
|
||||
memcpy(&aux, &g_pserver->db[id1], sizeof(redisDb));
|
||||
redisDb *db1 = &g_pserver->db[id1], *db2 = &g_pserver->db[id2];
|
||||
|
||||
/* Swap hash tables. Note that we don't swap blocking_keys,
|
||||
@@ -1387,7 +1387,7 @@ void setExpire(client *c, redisDb *db, robj *key, robj *subkey, long long when)
|
||||
db->setexpire->insert(e);
|
||||
}
|
||||
|
||||
int writable_slave = listLength(g_pserver->masters) && g_pserver->repl_slave_ro == 0 && !g_pserver->fActiveReplica;
|
||||
int writable_slave = listLength(g_pserver->masters) && g_pserver->repl_slave_ro == 0;
|
||||
if (c && writable_slave && !(c->flags & CLIENT_MASTER))
|
||||
rememberSlaveKeyWithExpire(db,key);
|
||||
}
|
||||
@@ -1424,7 +1424,7 @@ void setExpire(client *c, redisDb *db, robj *key, expireEntry &&e)
|
||||
((robj*)dictGetVal(kde))->SetFExpires(true);
|
||||
|
||||
|
||||
int writable_slave = listLength(g_pserver->masters) && g_pserver->repl_slave_ro == 0 && !g_pserver->fActiveReplica;
|
||||
int writable_slave = listLength(g_pserver->masters) && g_pserver->repl_slave_ro == 0;
|
||||
if (c && writable_slave && !(c->flags & CLIENT_MASTER))
|
||||
rememberSlaveKeyWithExpire(db,key);
|
||||
}
|
||||
@@ -1480,6 +1480,7 @@ void propagateExpire(redisDb *db, robj *key, int lazy) {
|
||||
void propagateSubkeyExpire(redisDb *db, int type, robj *key, robj *subkey)
|
||||
{
|
||||
robj *argv[3];
|
||||
robj objT;
|
||||
redisCommand *cmd = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
|
||||
+6
-68
@@ -392,7 +392,6 @@ void debugCommand(client *c) {
|
||||
"DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false]",
|
||||
"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
|
||||
"LOG <message> -- write message to the server log.",
|
||||
"LEAK <string> -- Create a memory leak of the input string.",
|
||||
"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
|
||||
"HTSTATS-KEY <key> -- Like htstats but for the hash table stored as key's value.",
|
||||
"LOADAOF -- Flush the AOF buffers on disk and reload the AOF in memory.",
|
||||
@@ -411,7 +410,6 @@ void debugCommand(client *c) {
|
||||
"STRUCTSIZE -- Return the size of different Redis core C structures.",
|
||||
"ZIPLIST <key> -- Show low level info about the ziplist encoding.",
|
||||
"STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.",
|
||||
"CONFIG-REWRITE-FORCE-ALL -- Like CONFIG REWRITE but writes all configuration options, including keywords not listed in original configuration file or default values.",
|
||||
#ifdef USE_JEMALLOC
|
||||
"MALLCTL <key> [<val>] -- Get or set a malloc tunning integer.",
|
||||
"MALLCTL-STR <key> [<val>] -- Get or set a malloc tunning string.",
|
||||
@@ -446,9 +444,6 @@ NULL
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"log") && c->argc == 3) {
|
||||
serverLog(LL_WARNING, "DEBUG LOG: %s", (char*)ptrFromObj(c->argv[2]));
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"leak") && c->argc == 3) {
|
||||
sdsdup(szFromObj(c->argv[2]));
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"reload")) {
|
||||
int flush = 1, save = 1;
|
||||
int flags = RDBFLAGS_NONE;
|
||||
@@ -606,13 +601,14 @@ NULL
|
||||
if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != C_OK)
|
||||
return;
|
||||
dictExpand(c->db->pdict,keys);
|
||||
long valsize = 0;
|
||||
if ( c->argc == 5 && getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK )
|
||||
return;
|
||||
for (j = 0; j < keys; j++) {
|
||||
long valsize = 0;
|
||||
snprintf(buf,sizeof(buf),"%s:%lu",
|
||||
(c->argc == 3) ? "key" : (char*)ptrFromObj(c->argv[3]), j);
|
||||
key = createStringObject(buf,strlen(buf));
|
||||
if (c->argc == 5)
|
||||
if (getLongFromObjectOrReply(c, c->argv[4], &valsize, NULL) != C_OK)
|
||||
return;
|
||||
if (lookupKeyWrite(c->db,key) != NULL) {
|
||||
decrRefCount(key);
|
||||
continue;
|
||||
@@ -829,12 +825,6 @@ NULL
|
||||
c->flags &= ~(CLIENT_MASTER | CLIENT_MASTER_FORCE_REPLY);
|
||||
}
|
||||
addReply(c, shared.ok);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"config-rewrite-force-all") && c->argc == 2)
|
||||
{
|
||||
if (rewriteConfig(cserver.configfile, 1) == -1)
|
||||
addReplyError(c, "CONFIG-REWRITE-FORCE-ALL failed");
|
||||
else
|
||||
addReply(c, shared.ok);
|
||||
#ifdef USE_JEMALLOC
|
||||
} else if(!strcasecmp(szFromObj(c->argv[1]),"mallctl") && c->argc >= 3) {
|
||||
mallctl_int(c, c->argv+2, c->argc-2);
|
||||
@@ -968,11 +958,8 @@ static void *getMcontextEip(ucontext_t *uc) {
|
||||
/* OSX >= 10.6 */
|
||||
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
||||
return (void*) uc->uc_mcontext->__ss.__rip;
|
||||
#elif defined(__i386__)
|
||||
return (void*) uc->uc_mcontext->__ss.__eip;
|
||||
#else
|
||||
/* OSX ARM64 */
|
||||
return (void*) arm_thread_state64_get_pc(uc->uc_mcontext->__ss);
|
||||
return (void*) uc->uc_mcontext->__ss.__eip;
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
/* Linux */
|
||||
@@ -1058,7 +1045,7 @@ void logRegisters(ucontext_t *uc) {
|
||||
(unsigned long) uc->uc_mcontext->__ss.__gs
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext->__ss.__rsp);
|
||||
#elif defined(__i386__)
|
||||
#else
|
||||
/* OSX x86 */
|
||||
serverLog(LL_WARNING,
|
||||
"\n"
|
||||
@@ -1084,55 +1071,6 @@ void logRegisters(ucontext_t *uc) {
|
||||
(unsigned long) uc->uc_mcontext->__ss.__gs
|
||||
);
|
||||
logStackContent((void**)uc->uc_mcontext->__ss.__esp);
|
||||
#else
|
||||
/* OSX ARM64 */
|
||||
serverLog(LL_WARNING,
|
||||
"\n"
|
||||
"x0:%016lx x1:%016lx x2:%016lx x3:%016lx\n"
|
||||
"x4:%016lx x5:%016lx x6:%016lx x7:%016lx\n"
|
||||
"x8:%016lx x9:%016lx x10:%016lx x11:%016lx\n"
|
||||
"x12:%016lx x13:%016lx x14:%016lx x15:%016lx\n"
|
||||
"x16:%016lx x17:%016lx x18:%016lx x19:%016lx\n"
|
||||
"x20:%016lx x21:%016lx x22:%016lx x23:%016lx\n"
|
||||
"x24:%016lx x25:%016lx x26:%016lx x27:%016lx\n"
|
||||
"x28:%016lx fp:%016lx lr:%016lx\n"
|
||||
"sp:%016lx pc:%016lx cpsr:%08lx\n",
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[0],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[1],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[2],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[3],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[4],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[5],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[6],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[7],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[8],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[9],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[10],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[11],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[12],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[13],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[14],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[15],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[16],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[17],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[18],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[19],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[20],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[21],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[22],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[23],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[24],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[25],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[26],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[27],
|
||||
(unsigned long) uc->uc_mcontext->__ss.__x[28],
|
||||
(unsigned long) arm_thread_state64_get_fp(uc->uc_mcontext->__ss),
|
||||
(unsigned long) arm_thread_state64_get_lr(uc->uc_mcontext->__ss),
|
||||
(unsigned long) arm_thread_state64_get_sp(uc->uc_mcontext->__ss),
|
||||
(unsigned long) arm_thread_state64_get_pc(uc->uc_mcontext->__ss),
|
||||
(unsigned long) uc->uc_mcontext->__ss.__cpsr
|
||||
);
|
||||
logStackContent((void**) arm_thread_state64_get_sp(uc->uc_mcontext->__ss));
|
||||
#endif
|
||||
/* Linux */
|
||||
#elif defined(__linux__)
|
||||
|
||||
+3
-12
@@ -55,8 +55,7 @@ bool replaceSateliteOSetKeyPtr(expireset &set, sds oldkey, sds newkey);
|
||||
* returns NULL in case the allocatoin wasn't moved.
|
||||
* when it returns a non-null value, the old pointer was already released
|
||||
* and should NOT be accessed. */
|
||||
template<typename TPTR>
|
||||
TPTR* activeDefragAlloc(TPTR *ptr) {
|
||||
void* activeDefragAlloc(void *ptr) {
|
||||
size_t size;
|
||||
void *newptr;
|
||||
if(!je_get_defrag_hint(ptr)) {
|
||||
@@ -71,14 +70,7 @@ TPTR* activeDefragAlloc(TPTR *ptr) {
|
||||
newptr = zmalloc_no_tcache(size);
|
||||
memcpy(newptr, ptr, size);
|
||||
zfree_no_tcache(ptr);
|
||||
return (TPTR*)newptr;
|
||||
}
|
||||
|
||||
template<>
|
||||
robj* activeDefragAlloc(robj *o) {
|
||||
void *pvSrc = allocPtrFromObj(o);
|
||||
void *pvDst = activeDefragAlloc(pvSrc);
|
||||
return objFromAllocPtr(pvDst);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
/*Defrag helper for sds strings
|
||||
@@ -355,7 +347,7 @@ long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
|
||||
sdsele = (sds)ln->value;
|
||||
if ((newsds = activeDefragSds(sdsele))) {
|
||||
/* When defragging an sds value, we need to update the dict key */
|
||||
uint64_t hash = dictGetHash(d, newsds);
|
||||
uint64_t hash = dictGetHash(d, sdsele);
|
||||
replaceSateliteDictKeyPtrAndOrDefragDictEntry(d, sdsele, newsds, hash, &defragged);
|
||||
ln->value = newsds;
|
||||
defragged++;
|
||||
@@ -683,7 +675,6 @@ int scanLaterStraemListpacks(robj *ob, unsigned long *cursor, long long endtime,
|
||||
/* if cursor is non-zero, we seek to the static 'last' */
|
||||
if (!raxSeek(&ri,">", last, sizeof(last))) {
|
||||
*cursor = 0;
|
||||
raxStop(&ri);
|
||||
return 0;
|
||||
}
|
||||
/* assign the iterator node callback after the seek, so that the
|
||||
|
||||
+9
-102
@@ -80,7 +80,7 @@ void activeExpireCycleExpire(redisDb *db, expireEntry &e, long long now) {
|
||||
robj *val = (robj*)dictGetVal(de);
|
||||
int deleted = 0;
|
||||
|
||||
redisObjectStack objKey;
|
||||
robj objKey;
|
||||
initStaticStringObject(objKey, (char*)e.key());
|
||||
bool fTtlChanged = false;
|
||||
|
||||
@@ -145,7 +145,7 @@ void activeExpireCycleExpire(redisDb *db, expireEntry &e, long long now) {
|
||||
serverAssert(false);
|
||||
}
|
||||
|
||||
redisObjectStack objSubkey;
|
||||
robj objSubkey;
|
||||
initStaticStringObject(objSubkey, (char*)pfat->nextExpireEntry().spsubkey.get());
|
||||
propagateSubkeyExpire(db, val->type, &objKey, &objSubkey);
|
||||
|
||||
@@ -575,16 +575,6 @@ void flushSlaveKeysWithExpireList(void) {
|
||||
}
|
||||
}
|
||||
|
||||
int checkAlreadyExpired(long long when) {
|
||||
/* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
|
||||
* should never be executed as a DEL when load the AOF or in the context
|
||||
* of a slave instance.
|
||||
*
|
||||
* Instead we add the already expired key to the database with expire time
|
||||
* (possibly in the past) and wait for an explicit DEL from the master. */
|
||||
return (when <= mstime() && !g_pserver->loading && (!listLength(g_pserver->masters) || g_pserver->fActiveReplica));
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Expires Commands
|
||||
*----------------------------------------------------------------------------*/
|
||||
@@ -612,7 +602,13 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkAlreadyExpired(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 replica instance.
|
||||
*
|
||||
* Instead we take the other branch of the IF statement setting an expire
|
||||
* (possibly in the past) and wait for an explicit DEL from the master. */
|
||||
if (when <= mstime() && !g_pserver->loading && (!listLength(g_pserver->masters) || g_pserver->fActiveReplica)) {
|
||||
robj *aux;
|
||||
|
||||
int deleted = g_pserver->lazyfree_lazy_expire ? dbAsyncDelete(c->db,key) :
|
||||
@@ -719,7 +715,6 @@ void persistCommand(client *c) {
|
||||
if (lookupKeyWrite(c->db,c->argv[1])) {
|
||||
if (c->argc == 2) {
|
||||
if (removeExpire(c->db,c->argv[1])) {
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"persist",c->argv[1],c->db->id);
|
||||
addReply(c,shared.cone);
|
||||
g_pserver->dirty++;
|
||||
@@ -728,7 +723,6 @@ void persistCommand(client *c) {
|
||||
}
|
||||
} else if (c->argc == 3) {
|
||||
if (removeSubkeyExpire(c->db, c->argv[1], c->argv[2])) {
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"persist",c->argv[1],c->db->id);
|
||||
addReply(c,shared.cone);
|
||||
g_pserver->dirty++;
|
||||
@@ -751,90 +745,3 @@ void touchCommand(client *c) {
|
||||
addReplyLongLong(c,touched);
|
||||
}
|
||||
|
||||
expireEntryFat::~expireEntryFat()
|
||||
{
|
||||
if (m_dictIndex != nullptr)
|
||||
dictRelease(m_dictIndex);
|
||||
}
|
||||
|
||||
void expireEntryFat::createIndex()
|
||||
{
|
||||
serverAssert(m_dictIndex == nullptr);
|
||||
m_dictIndex = dictCreate(&keyptrDictType, nullptr);
|
||||
|
||||
for (auto &entry : m_vecexpireEntries)
|
||||
{
|
||||
if (entry.spsubkey != nullptr)
|
||||
{
|
||||
dictEntry *de = dictAddRaw(m_dictIndex, (void*)entry.spsubkey.get(), nullptr);
|
||||
de->v.s64 = entry.when;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void expireEntryFat::expireSubKey(const char *szSubkey, long long when)
|
||||
{
|
||||
if (m_vecexpireEntries.size() >= INDEX_THRESHOLD && m_dictIndex == nullptr)
|
||||
createIndex();
|
||||
|
||||
// First check if the subkey already has an expiration
|
||||
if (m_dictIndex != nullptr && szSubkey != nullptr)
|
||||
{
|
||||
dictEntry *de = dictFind(m_dictIndex, szSubkey);
|
||||
if (de != nullptr)
|
||||
{
|
||||
auto itr = std::lower_bound(m_vecexpireEntries.begin(), m_vecexpireEntries.end(), de->v.u64);
|
||||
while (itr != m_vecexpireEntries.end() && itr->when == de->v.s64)
|
||||
{
|
||||
bool fFound = false;
|
||||
if (szSubkey == nullptr && itr->spsubkey == nullptr) {
|
||||
fFound = true;
|
||||
} else if (szSubkey != nullptr && itr->spsubkey != nullptr && sdscmp((sds)itr->spsubkey.get(), (sds)szSubkey) == 0) {
|
||||
fFound = true;
|
||||
}
|
||||
if (fFound) {
|
||||
m_vecexpireEntries.erase(itr);
|
||||
dictDelete(m_dictIndex, szSubkey);
|
||||
break;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &entry : m_vecexpireEntries)
|
||||
{
|
||||
if (szSubkey != nullptr)
|
||||
{
|
||||
// if this is a subkey expiry then its not a match if the expireEntry is either for the
|
||||
// primary key or a different subkey
|
||||
if (entry.spsubkey == nullptr || sdscmp((sds)entry.spsubkey.get(), (sds)szSubkey) != 0)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entry.spsubkey != nullptr)
|
||||
continue;
|
||||
}
|
||||
m_vecexpireEntries.erase(m_vecexpireEntries.begin() + (&entry - m_vecexpireEntries.data()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto itrInsert = std::lower_bound(m_vecexpireEntries.begin(), m_vecexpireEntries.end(), when);
|
||||
const char *subkey = (szSubkey) ? sdsdup(szSubkey) : nullptr;
|
||||
auto itr = m_vecexpireEntries.emplace(itrInsert, when, subkey);
|
||||
if (m_dictIndex && subkey) {
|
||||
dictEntry *de = dictAddRaw(m_dictIndex, (void*)itr->spsubkey.get(), nullptr);
|
||||
de->v.s64 = when;
|
||||
}
|
||||
}
|
||||
|
||||
void expireEntryFat::popfrontExpireEntry()
|
||||
{
|
||||
if (m_dictIndex != nullptr && m_vecexpireEntries.begin()->spsubkey) {
|
||||
int res = dictDelete(m_dictIndex, (void*)m_vecexpireEntries.begin()->spsubkey.get());
|
||||
serverAssert(res == DICT_OK);
|
||||
}
|
||||
m_vecexpireEntries.erase(m_vecexpireEntries.begin());
|
||||
}
|
||||
-220
@@ -1,220 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class expireEntryFat
|
||||
{
|
||||
friend class expireEntry;
|
||||
static const int INDEX_THRESHOLD = 16;
|
||||
public:
|
||||
struct subexpireEntry
|
||||
{
|
||||
long long when;
|
||||
std::unique_ptr<const char, void(*)(const char*)> spsubkey;
|
||||
|
||||
subexpireEntry(long long when, const char *subkey)
|
||||
: when(when), spsubkey(subkey, sdsfree)
|
||||
{}
|
||||
|
||||
bool operator<(long long when) const noexcept { return this->when < when; }
|
||||
bool operator<(const subexpireEntry &se) { return this->when < se.when; }
|
||||
};
|
||||
|
||||
private:
|
||||
sds m_keyPrimary;
|
||||
std::vector<subexpireEntry> m_vecexpireEntries; // Note a NULL for the sds portion means the expire is for the primary key
|
||||
dict *m_dictIndex = nullptr;
|
||||
|
||||
void createIndex();
|
||||
public:
|
||||
expireEntryFat(sds keyPrimary)
|
||||
: m_keyPrimary(keyPrimary)
|
||||
{}
|
||||
~expireEntryFat();
|
||||
|
||||
long long when() const noexcept { return m_vecexpireEntries.front().when; }
|
||||
const char *key() const noexcept { return m_keyPrimary; }
|
||||
|
||||
bool operator<(long long when) const noexcept { return this->when() < when; }
|
||||
|
||||
void expireSubKey(const char *szSubkey, long long when);
|
||||
|
||||
bool FEmpty() const noexcept { return m_vecexpireEntries.empty(); }
|
||||
const subexpireEntry &nextExpireEntry() const noexcept { return m_vecexpireEntries.front(); }
|
||||
void popfrontExpireEntry();
|
||||
const subexpireEntry &operator[](size_t idx) { return m_vecexpireEntries[idx]; }
|
||||
size_t size() const noexcept { return m_vecexpireEntries.size(); }
|
||||
};
|
||||
|
||||
class expireEntry {
|
||||
union
|
||||
{
|
||||
sds m_key;
|
||||
expireEntryFat *m_pfatentry;
|
||||
} u;
|
||||
long long m_when; // LLONG_MIN means this is a fat entry and we should use the pointer
|
||||
|
||||
public:
|
||||
class iter
|
||||
{
|
||||
friend class expireEntry;
|
||||
expireEntry *m_pentry = nullptr;
|
||||
size_t m_idx = 0;
|
||||
|
||||
public:
|
||||
iter(expireEntry *pentry, size_t idx)
|
||||
: m_pentry(pentry), m_idx(idx)
|
||||
{}
|
||||
|
||||
iter &operator++() { ++m_idx; return *this; }
|
||||
|
||||
const char *subkey() const
|
||||
{
|
||||
if (m_pentry->FFat())
|
||||
return (*m_pentry->pfatentry())[m_idx].spsubkey.get();
|
||||
return nullptr;
|
||||
}
|
||||
long long when() const
|
||||
{
|
||||
if (m_pentry->FFat())
|
||||
return (*m_pentry->pfatentry())[m_idx].when;
|
||||
return m_pentry->when();
|
||||
}
|
||||
|
||||
bool operator!=(const iter &other)
|
||||
{
|
||||
return m_idx != other.m_idx;
|
||||
}
|
||||
|
||||
const iter &operator*() const { return *this; }
|
||||
};
|
||||
|
||||
expireEntry(sds key, const char *subkey, long long when)
|
||||
{
|
||||
if (subkey != nullptr)
|
||||
{
|
||||
m_when = LLONG_MIN;
|
||||
u.m_pfatentry = new (MALLOC_LOCAL) expireEntryFat(key);
|
||||
u.m_pfatentry->expireSubKey(subkey, when);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.m_key = key;
|
||||
m_when = when;
|
||||
}
|
||||
}
|
||||
|
||||
expireEntry(expireEntryFat *pfatentry)
|
||||
{
|
||||
u.m_pfatentry = pfatentry;
|
||||
m_when = LLONG_MIN;
|
||||
}
|
||||
|
||||
expireEntry(expireEntry &&e)
|
||||
{
|
||||
u.m_key = e.u.m_key;
|
||||
m_when = e.m_when;
|
||||
e.u.m_key = (char*)key(); // we do this so it can still be found in the set
|
||||
e.m_when = 0;
|
||||
}
|
||||
|
||||
~expireEntry()
|
||||
{
|
||||
if (FFat())
|
||||
delete u.m_pfatentry;
|
||||
}
|
||||
|
||||
void setKeyUnsafe(sds key)
|
||||
{
|
||||
if (FFat())
|
||||
u.m_pfatentry->m_keyPrimary = key;
|
||||
else
|
||||
u.m_key = key;
|
||||
}
|
||||
|
||||
inline bool FFat() const noexcept { return m_when == LLONG_MIN; }
|
||||
expireEntryFat *pfatentry() { assert(FFat()); return u.m_pfatentry; }
|
||||
|
||||
|
||||
bool operator==(const char *key) const noexcept
|
||||
{
|
||||
return this->key() == key;
|
||||
}
|
||||
|
||||
bool operator<(const expireEntry &e) const noexcept
|
||||
{
|
||||
return when() < e.when();
|
||||
}
|
||||
bool operator<(long long when) const noexcept
|
||||
{
|
||||
return this->when() < when;
|
||||
}
|
||||
|
||||
const char *key() const noexcept
|
||||
{
|
||||
if (FFat())
|
||||
return u.m_pfatentry->key();
|
||||
return u.m_key;
|
||||
}
|
||||
long long when() const noexcept
|
||||
{
|
||||
if (FFat())
|
||||
return u.m_pfatentry->when();
|
||||
return m_when;
|
||||
}
|
||||
|
||||
void update(const char *subkey, long long when)
|
||||
{
|
||||
if (!FFat())
|
||||
{
|
||||
if (subkey == nullptr)
|
||||
{
|
||||
m_when = when;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have to upgrade to a fat entry
|
||||
long long whenT = m_when;
|
||||
sds keyPrimary = u.m_key;
|
||||
m_when = LLONG_MIN;
|
||||
u.m_pfatentry = new (MALLOC_LOCAL) expireEntryFat(keyPrimary);
|
||||
u.m_pfatentry->expireSubKey(nullptr, whenT);
|
||||
// at this point we're fat so fall through
|
||||
}
|
||||
}
|
||||
u.m_pfatentry->expireSubKey(subkey, when);
|
||||
}
|
||||
|
||||
iter begin() { return iter(this, 0); }
|
||||
iter end()
|
||||
{
|
||||
if (FFat())
|
||||
return iter(this, u.m_pfatentry->size());
|
||||
return iter(this, 1);
|
||||
}
|
||||
|
||||
void erase(iter &itr)
|
||||
{
|
||||
if (!FFat())
|
||||
throw -1; // assert
|
||||
pfatentry()->m_vecexpireEntries.erase(
|
||||
pfatentry()->m_vecexpireEntries.begin() + itr.m_idx);
|
||||
}
|
||||
|
||||
bool FGetPrimaryExpire(long long *pwhen)
|
||||
{
|
||||
*pwhen = -1;
|
||||
for (auto itr : *this)
|
||||
{
|
||||
if (itr.subkey() == nullptr)
|
||||
{
|
||||
*pwhen = itr.when();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
explicit operator const char*() const noexcept { return key(); }
|
||||
explicit operator long long() const noexcept { return when(); }
|
||||
};
|
||||
typedef semiorderedset<expireEntry, const char *, true /*expireEntry can be memmoved*/> expireset;
|
||||
+6
-20
@@ -35,11 +35,7 @@
|
||||
#include <sched.h>
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
#ifdef __FreeBSD__
|
||||
#include <pthread_np.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <map>
|
||||
#ifdef __linux__
|
||||
@@ -171,12 +167,7 @@ extern "C" pid_t gettid()
|
||||
#else
|
||||
if (pidCache == -1) {
|
||||
uint64_t tidT;
|
||||
#ifdef __FreeBSD__
|
||||
// Check https://github.com/ClickHouse/ClickHouse/commit/8d51824ddcb604b6f179a0216f0d32ba5612bd2e
|
||||
tidT = pthread_getthreadid_np();
|
||||
#else
|
||||
pthread_threadid_np(nullptr, &tidT);
|
||||
#endif
|
||||
serverAssert(tidT < UINT_MAX);
|
||||
pidCache = (int)tidT;
|
||||
}
|
||||
@@ -352,9 +343,7 @@ extern "C" void fastlock_lock(struct fastlock *lock)
|
||||
unsigned myticket = __atomic_fetch_add(&lock->m_ticket.m_avail, 1, __ATOMIC_RELEASE);
|
||||
unsigned cloops = 0;
|
||||
ticket ticketT;
|
||||
int fHighPressure;
|
||||
__atomic_load(&g_fHighCpuPressure, &fHighPressure, __ATOMIC_RELAXED);
|
||||
unsigned loopLimit = fHighPressure ? 0x10000 : 0x100000;
|
||||
unsigned loopLimit = g_fHighCpuPressure ? 0x10000 : 0x100000;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -489,22 +478,19 @@ void fastlock_auto_adjust_waits()
|
||||
{
|
||||
#ifdef __linux__
|
||||
struct sysinfo sysinf;
|
||||
int fHighPressurePrev, fHighPressureNew;
|
||||
__atomic_load(&g_fHighCpuPressure, &fHighPressurePrev, __ATOMIC_RELAXED);
|
||||
fHighPressureNew = fHighPressurePrev;
|
||||
auto fHighPressurePrev = g_fHighCpuPressure;
|
||||
memset(&sysinf, 0, sizeof sysinf);
|
||||
if (!sysinfo(&sysinf)) {
|
||||
auto avgCoreLoad = sysinf.loads[0] / get_nprocs();
|
||||
int fHighPressureNew = (avgCoreLoad > ((1 << SI_LOAD_SHIFT) * 0.9));
|
||||
__atomic_store(&g_fHighCpuPressure, &fHighPressureNew, __ATOMIC_RELEASE);
|
||||
if (fHighPressureNew)
|
||||
g_fHighCpuPressure = (avgCoreLoad > ((1 << SI_LOAD_SHIFT) * 0.9));
|
||||
if (g_fHighCpuPressure)
|
||||
serverLog(!fHighPressurePrev ? 3 /*LL_WARNING*/ : 1 /* LL_VERBOSE */, "NOTICE: Detuning locks due to high load per core: %.2f%%", avgCoreLoad / (double)(1 << SI_LOAD_SHIFT)*100.0);
|
||||
}
|
||||
|
||||
if (!fHighPressureNew && fHighPressurePrev) {
|
||||
if (!g_fHighCpuPressure && fHighPressurePrev) {
|
||||
serverLog(3 /*LL_WARNING*/, "NOTICE: CPU pressure reduced");
|
||||
}
|
||||
#else
|
||||
g_fHighCpuPressure = g_fTestMode;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+8
-28
@@ -1,4 +1,4 @@
|
||||
/* Automatically generated by ./utils/generate-command-help.rb, do not edit. */
|
||||
/* Automatically generated by generate-command-help.rb, do not edit. */
|
||||
|
||||
#ifndef __REDIS_HELP_H
|
||||
#define __REDIS_HELP_H
|
||||
@@ -44,16 +44,6 @@ struct commandHelp {
|
||||
"Generate a pseudorandom secure password to use for ACL users",
|
||||
9,
|
||||
"6.0.0" },
|
||||
{ "ACL GETUSER",
|
||||
"username",
|
||||
"Get the rules for a specific ACL user",
|
||||
9,
|
||||
"6.0.0" },
|
||||
{ "ACL HELP",
|
||||
"-",
|
||||
"Show helpful text about the different subcommands",
|
||||
9,
|
||||
"6.0.0" },
|
||||
{ "ACL LIST",
|
||||
"-",
|
||||
"List the current ACL rules in ACL config file format",
|
||||
@@ -75,7 +65,7 @@ struct commandHelp {
|
||||
9,
|
||||
"6.0.0" },
|
||||
{ "ACL SETUSER",
|
||||
"username [rule [rule ...]]",
|
||||
"rule [rule ...]",
|
||||
"Modify or create the rules for a specific ACL user",
|
||||
9,
|
||||
"6.0.0" },
|
||||
@@ -175,7 +165,7 @@ struct commandHelp {
|
||||
8,
|
||||
"5.0.0" },
|
||||
{ "CLIENT KILL",
|
||||
"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [USER username] [ADDR ip:port] [SKIPME yes/no]",
|
||||
"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no]",
|
||||
"Kill the connection of a client",
|
||||
8,
|
||||
"2.4.0" },
|
||||
@@ -193,14 +183,14 @@ struct commandHelp {
|
||||
"ON|OFF|SKIP",
|
||||
"Instruct the server whether to reply to commands",
|
||||
8,
|
||||
"3.2.0" },
|
||||
"3.2" },
|
||||
{ "CLIENT SETNAME",
|
||||
"connection-name",
|
||||
"Set the current connection name",
|
||||
8,
|
||||
"2.6.9" },
|
||||
{ "CLIENT TRACKING",
|
||||
"ON|OFF [REDIRECT client-id] [PREFIX prefix [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
|
||||
"ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
|
||||
"Enable or disable server assisted client side caching support",
|
||||
8,
|
||||
"6.0.0" },
|
||||
@@ -636,7 +626,7 @@ struct commandHelp {
|
||||
9,
|
||||
"2.8.13" },
|
||||
{ "LATENCY RESET",
|
||||
"[event [event ...]]",
|
||||
"[event]",
|
||||
"Reset latency data for one or more events.",
|
||||
9,
|
||||
"2.8.13" },
|
||||
@@ -665,11 +655,6 @@ struct commandHelp {
|
||||
"Remove and get the first element in a list",
|
||||
2,
|
||||
"1.0.0" },
|
||||
{ "LPOS",
|
||||
"key element [RANK rank] [COUNT num-matches] [MAXLEN len]",
|
||||
"Return the index of matching elements on a list",
|
||||
2,
|
||||
"6.0.6" },
|
||||
{ "LPUSH",
|
||||
"key element [element ...]",
|
||||
"Prepend one or multiple elements to a list",
|
||||
@@ -981,7 +966,7 @@ struct commandHelp {
|
||||
8,
|
||||
"1.0.0" },
|
||||
{ "SET",
|
||||
"key value [EX seconds|PX milliseconds|KEEPTTL] [NX|XX]",
|
||||
"key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]",
|
||||
"Set the string value of a key",
|
||||
1,
|
||||
"1.0.0" },
|
||||
@@ -1340,12 +1325,7 @@ struct commandHelp {
|
||||
"Rename a hash key, copying the value.",
|
||||
4,
|
||||
"6.5.3"
|
||||
},
|
||||
{ "KEYDB.MEXISTS",
|
||||
"key [key ...]",
|
||||
"Determine if a key exists",
|
||||
0,
|
||||
"6.5.12" },
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -71,7 +71,7 @@ int THPIsEnabled(void) {
|
||||
return 0;
|
||||
}
|
||||
fclose(fp);
|
||||
return (strstr(buf,"[always]") != NULL) ? 1 : 0;
|
||||
return (strstr(buf,"[never]") == NULL) ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -621,7 +621,7 @@ NULL
|
||||
resets += latencyResetEvent(szFromObj(c->argv[j]));
|
||||
addReplyLongLong(c,resets);
|
||||
}
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"help") && c->argc == 2) {
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"help") && c->argc >= 2) {
|
||||
addReplyHelp(c, help);
|
||||
} else {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
|
||||
@@ -41,30 +41,6 @@ size_t lazyfreeGetFreeEffort(robj *obj) {
|
||||
} else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {
|
||||
dict *ht = (dict*)ptrFromObj(obj);
|
||||
return dictSize(ht);
|
||||
} else if (obj->type == OBJ_STREAM) {
|
||||
size_t effort = 0;
|
||||
stream *s = (stream*)ptrFromObj(obj);
|
||||
|
||||
/* Make a best effort estimate to maintain constant runtime. Every macro
|
||||
* node in the Stream is one allocation. */
|
||||
effort += s->prax->numnodes;
|
||||
|
||||
/* Every consumer group is an allocation and so are the entries in its
|
||||
* PEL. We use size of the first group's PEL as an estimate for all
|
||||
* others. */
|
||||
if (s->cgroups) {
|
||||
raxIterator ri;
|
||||
streamCG *cg;
|
||||
raxStart(&ri,s->cgroups);
|
||||
raxSeek(&ri,"^",NULL,0);
|
||||
/* There must be at least one group so the following should always
|
||||
* work. */
|
||||
serverAssert(raxNext(&ri));
|
||||
cg = (streamCG*)ri.data;
|
||||
effort += raxSize(s->cgroups)*(1+raxSize(cg->pel));
|
||||
raxStop(&ri);
|
||||
}
|
||||
return effort;
|
||||
} else {
|
||||
return 1; /* Everything else is a single allocation. */
|
||||
}
|
||||
|
||||
+2
-2
@@ -773,13 +773,13 @@ unsigned char *lpSeek(unsigned char *lp, long index) {
|
||||
* is past the half of the listpack. */
|
||||
if (index > numele/2) {
|
||||
forward = 0;
|
||||
/* Right to left scanning always expects a negative index. Convert
|
||||
/* Left to right scanning always expects a negative index. Convert
|
||||
* our index to negative form. */
|
||||
index -= numele;
|
||||
}
|
||||
} else {
|
||||
/* If the listpack length is unspecified, for negative indexes we
|
||||
* want to always scan right-to-left. */
|
||||
* want to always scan left-to-right. */
|
||||
if (index < 0) forward = 0;
|
||||
}
|
||||
|
||||
|
||||
+9
-181
@@ -329,7 +329,6 @@ static int s_cAcquisitionsServer = 0;
|
||||
static int s_cAcquisitionsModule = 0;
|
||||
static std::mutex s_mutex;
|
||||
static std::condition_variable s_cv;
|
||||
static std::recursive_mutex s_mutexModule;
|
||||
|
||||
typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
|
||||
|
||||
@@ -624,8 +623,6 @@ void moduleHandlePropagationAfterCommandCallback(RedisModuleCtx *ctx) {
|
||||
redisOpArrayFree(&g_pserver->also_propagate);
|
||||
/* Restore the previous oparray in case of nexted use of the API. */
|
||||
g_pserver->also_propagate = ctx->saved_oparray;
|
||||
/* We're done with saved_oparray, let's invalidate it. */
|
||||
redisOpArrayInit(&ctx->saved_oparray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1155,65 +1152,6 @@ void RM_RetainString(RedisModuleCtx *ctx, RedisModuleString *str) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be used instead of RedisModule_RetainString().
|
||||
* The main difference between the two is that this function will always
|
||||
* succeed, whereas RedisModule_RetainString() may fail because of an
|
||||
* assertion.
|
||||
*
|
||||
* The function returns a pointer to RedisModuleString, which is owned
|
||||
* by the caller. It requires a call to RedisModule_FreeString() to free
|
||||
* the string when automatic memory management is disabled for the context.
|
||||
* When automatic memory management is enabled, you can either call
|
||||
* RedisModule_FreeString() or let the automation free it.
|
||||
*
|
||||
* This function is more efficient than RedisModule_CreateStringFromString()
|
||||
* because whenever possible, it avoids copying the underlying
|
||||
* RedisModuleString. The disadvantage of using this function is that it
|
||||
* might not be possible to use RedisModule_StringAppendBuffer() on the
|
||||
* returned RedisModuleString.
|
||||
*
|
||||
* It is possible to call this function with a NULL context.
|
||||
*/
|
||||
RedisModuleString* RM_HoldString(RedisModuleCtx *ctx, RedisModuleString *str) {
|
||||
if (str->getrefcount(std::memory_order_relaxed) == OBJ_STATIC_REFCOUNT) {
|
||||
return RM_CreateStringFromString(ctx, str);
|
||||
}
|
||||
|
||||
incrRefCount(str);
|
||||
if (ctx != NULL) {
|
||||
/*
|
||||
* Put the str in the auto memory management of the ctx.
|
||||
* It might already be there, in this case, the ref count will
|
||||
* be 2 and we will decrease the ref count twice and free the
|
||||
* object in the auto memory free function.
|
||||
*
|
||||
* Why we can not do the same trick of just remove the object
|
||||
* from the auto memory (like in RM_RetainString)?
|
||||
* This code shows the issue:
|
||||
*
|
||||
* RM_AutoMemory(ctx);
|
||||
* str1 = RM_CreateString(ctx, "test", 4);
|
||||
* str2 = RM_HoldString(ctx, str1);
|
||||
* RM_FreeString(str1);
|
||||
* RM_FreeString(str2);
|
||||
*
|
||||
* If after the RM_HoldString we would just remove the string from
|
||||
* the auto memory, this example will cause access to a freed memory
|
||||
* on 'RM_FreeString(str2);' because the String will be free
|
||||
* on 'RM_FreeString(str1);'.
|
||||
*
|
||||
* So it's safer to just increase the ref count
|
||||
* and add the String to auto memory again.
|
||||
*
|
||||
* The limitation is that it is not possible to use RedisModule_StringAppendBuffer
|
||||
* on the String.
|
||||
*/
|
||||
autoMemoryAdd(ctx,REDISMODULE_AM_STRING,str);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Given a string module object, this function returns the string pointer
|
||||
* and length of the string. The returned pointer and length should only
|
||||
* be used for read only accesses and never modified. */
|
||||
@@ -1816,8 +1754,6 @@ int modulePopulateClientInfoStructure(void *ci, client *client, int structver) {
|
||||
ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_TRACKING;
|
||||
if (client->flags & CLIENT_BLOCKED)
|
||||
ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_BLOCKED;
|
||||
if (connGetType(client->conn) == CONN_TYPE_TLS)
|
||||
ci1->flags |= REDISMODULE_CLIENTINFO_FLAG_SSL;
|
||||
|
||||
int port;
|
||||
connPeerToString(client->conn,ci1->addr,sizeof(ci1->addr),&port);
|
||||
@@ -2178,7 +2114,7 @@ int RM_KeyType(RedisModuleKey *key) {
|
||||
case OBJ_HASH: return REDISMODULE_KEYTYPE_HASH;
|
||||
case OBJ_MODULE: return REDISMODULE_KEYTYPE_MODULE;
|
||||
case OBJ_STREAM: return REDISMODULE_KEYTYPE_STREAM;
|
||||
default: return REDISMODULE_KEYTYPE_EMPTY;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2236,8 +2172,7 @@ mstime_t RM_GetExpire(RedisModuleKey *key) {
|
||||
if (pexpire != nullptr)
|
||||
pexpire->FGetPrimaryExpire(&expire);
|
||||
|
||||
if (expire == -1 || key->value == NULL)
|
||||
return REDISMODULE_NO_EXPIRE;
|
||||
if (expire == -1 || key->value == NULL) return -1;
|
||||
expire -= mstime();
|
||||
return expire >= 0 ? expire : 0;
|
||||
}
|
||||
@@ -3328,11 +3263,8 @@ robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int
|
||||
argv[argc++] = createStringObject(cstr,strlen(cstr));
|
||||
} else if (*p == 's') {
|
||||
robj *obj = (robj*)va_arg(ap,void*);
|
||||
if (obj->getrefcount(std::memory_order_relaxed) == OBJ_STATIC_REFCOUNT)
|
||||
obj = createStringObject(szFromObj(obj),sdslen(szFromObj(obj)));
|
||||
else
|
||||
incrRefCount(obj);
|
||||
argv[argc++] = obj;
|
||||
incrRefCount(obj);
|
||||
} else if (*p == 'b') {
|
||||
char *buf = va_arg(ap,char*);
|
||||
size_t len = va_arg(ap,size_t);
|
||||
@@ -3378,23 +3310,6 @@ fmterr:
|
||||
}
|
||||
|
||||
/* Exported API to call any Redis command from modules.
|
||||
*
|
||||
* * **cmdname**: The Redis command to call.
|
||||
* * **fmt**: A format specifier string for the command's arguments. Each
|
||||
* of the arguments should be specified by a valid type specification:
|
||||
* b The argument is a buffer and is immediately followed by another
|
||||
* argument that is the buffer's length.
|
||||
* c The argument is a pointer to a plain C string (null-terminated).
|
||||
* l The argument is long long integer.
|
||||
* s The argument is a RedisModuleString.
|
||||
* v The argument(s) is a vector of RedisModuleString.
|
||||
*
|
||||
* The format specifier can also include modifiers:
|
||||
* ! Sends the Redis command and its arguments to replicas and AOF.
|
||||
* A Suppress AOF propagation, send only to replicas (requires `!`).
|
||||
* R Suppress replicas propagation, send only to AOF (requires `!`).
|
||||
* * **...**: The actual arguments to the Redis command.
|
||||
*
|
||||
* On success a RedisModuleCallReply object is returned, otherwise
|
||||
* NULL is returned and errno is set to the following values:
|
||||
*
|
||||
@@ -3406,14 +3321,6 @@ fmterr:
|
||||
* in a readonly state.
|
||||
* ENETDOWN: operation in Cluster instance when cluster is down.
|
||||
*
|
||||
* Example code fragment:
|
||||
*
|
||||
* reply = RedisModule_Call(ctx,"INCRBY","sc",argv[1],"10");
|
||||
* if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {
|
||||
* long long myval = RedisModule_CallReplyInteger(reply);
|
||||
* // Do something with myval.
|
||||
* }
|
||||
*
|
||||
* This API is documented here: https://redis.io/topics/modules-intro
|
||||
*/
|
||||
RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) {
|
||||
@@ -4517,7 +4424,6 @@ void unblockClientFromModule(client *c) {
|
||||
* Even when blocking on keys, RM_UnblockClient() can be called however, but
|
||||
* in that case the privdata argument is disregarded, because we pass the
|
||||
* reply callback the privdata that is set here while blocking.
|
||||
*
|
||||
*/
|
||||
RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) {
|
||||
client *c = ctx->client;
|
||||
@@ -4610,14 +4516,6 @@ int moduleTryServeClientBlockedOnKey(client *c, robj *key) {
|
||||
* Note: RedisModule_UnblockClient should be called for every blocked client,
|
||||
* even if client was killed, timed-out or disconnected. Failing to do so
|
||||
* will result in memory leaks.
|
||||
*
|
||||
* There are some cases where RedisModule_BlockClient() cannot be used:
|
||||
*
|
||||
* 1. If the client is a Lua script.
|
||||
* 2. If the client is executing a MULTI block.
|
||||
*
|
||||
* In these cases, a call to RedisModule_BlockClient() will **not** block the
|
||||
* client, but instead produce a specific error reply.
|
||||
*/
|
||||
RedisModuleBlockedClient *RM_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) {
|
||||
return moduleBlockClient(ctx,reply_callback,timeout_callback,free_privdata,timeout_ms, NULL,0,NULL);
|
||||
@@ -4997,34 +4895,14 @@ void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
|
||||
zfree(ctx);
|
||||
}
|
||||
|
||||
static bool g_fModuleThread = false;
|
||||
/* Acquire the server lock before executing a thread safe API call.
|
||||
* This is not needed for `RedisModule_Reply*` calls when there is
|
||||
* a blocked client connected to the thread safe context. */
|
||||
void RM_ThreadSafeContextLock(RedisModuleCtx *ctx) {
|
||||
UNUSED(ctx);
|
||||
if (serverTL == nullptr) {
|
||||
serverTL = &g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN]; // arbitrary module threads get the main thread context
|
||||
g_fModuleThread = true;
|
||||
}
|
||||
moduleAcquireGIL(FALSE /*fServerThread*/);
|
||||
}
|
||||
|
||||
/* Similar to RM_ThreadSafeContextLock but this function
|
||||
* would not block if the server lock is already acquired.
|
||||
*
|
||||
* If successful (lock acquired) REDISMODULE_OK is returned,
|
||||
* otherwise REDISMODULE_ERR is returned and errno is set
|
||||
* accordingly. */
|
||||
int RM_ThreadSafeContextTryLock(RedisModuleCtx *ctx) {
|
||||
UNUSED(ctx);
|
||||
|
||||
int res = moduleTryAcquireGIL(false /*fServerThread*/);
|
||||
if(res != 0) {
|
||||
errno = res;
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
return REDISMODULE_OK;
|
||||
if (serverTL == nullptr)
|
||||
serverTL = &g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN]; // arbitrary module threads get the main thread context
|
||||
}
|
||||
|
||||
/* Release the server lock after a thread safe API call was executed. */
|
||||
@@ -5033,20 +4911,10 @@ void RM_ThreadSafeContextUnlock(RedisModuleCtx *ctx) {
|
||||
moduleReleaseGIL(FALSE /*fServerThread*/);
|
||||
}
|
||||
|
||||
// A module may be triggered synchronously in a non-module context. In this scenario we don't lock again
|
||||
// as the server thread acquisition is sufficient. If we did try to lock we would deadlock
|
||||
static bool FModuleCallBackLock(bool fServerThread)
|
||||
{
|
||||
return !fServerThread && aeThreadOwnsLock() && !g_fModuleThread && s_cAcquisitionsServer > 0;
|
||||
}
|
||||
void moduleAcquireGIL(int fServerThread) {
|
||||
std::unique_lock<std::mutex> lock(s_mutex);
|
||||
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
|
||||
|
||||
if (FModuleCallBackLock(fServerThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (*pcheck > 0)
|
||||
s_cv.wait(lock);
|
||||
|
||||
@@ -5056,53 +4924,20 @@ void moduleAcquireGIL(int fServerThread) {
|
||||
}
|
||||
else
|
||||
{
|
||||
s_mutexModule.lock();
|
||||
++s_cAcquisitionsModule;
|
||||
fModuleGILWlocked++;
|
||||
}
|
||||
}
|
||||
|
||||
int moduleTryAcquireGIL(bool fServerThread) {
|
||||
std::unique_lock<std::mutex> lock(s_mutex, std::defer_lock);
|
||||
if (!lock.try_lock())
|
||||
return 1;
|
||||
int *pcheck = fServerThread ? &s_cAcquisitionsModule : &s_cAcquisitionsServer;
|
||||
|
||||
if (FModuleCallBackLock(fServerThread)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*pcheck > 0)
|
||||
return 1;
|
||||
|
||||
if (fServerThread)
|
||||
{
|
||||
++s_cAcquisitionsServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!s_mutexModule.try_lock())
|
||||
return 1;
|
||||
++s_cAcquisitionsModule;
|
||||
fModuleGILWlocked++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void moduleReleaseGIL(int fServerThread) {
|
||||
std::unique_lock<std::mutex> lock(s_mutex);
|
||||
|
||||
if (FModuleCallBackLock(fServerThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fServerThread)
|
||||
{
|
||||
--s_cAcquisitionsServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_mutexModule.unlock();
|
||||
--s_cAcquisitionsModule;
|
||||
fModuleGILWlocked--;
|
||||
}
|
||||
@@ -5141,11 +4976,6 @@ int moduleGILAcquiredByModule(void) {
|
||||
* - REDISMODULE_NOTIFY_STREAM: Stream events
|
||||
* - REDISMODULE_NOTIFY_KEYMISS: Key-miss events
|
||||
* - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS)
|
||||
* - REDISMODULE_NOTIFY_LOADED: A special notification available only for modules,
|
||||
* indicates that the key was loaded from persistence.
|
||||
* Notice, when this event fires, the given key
|
||||
* can not be retained, use RM_CreateStringFromString
|
||||
* instead.
|
||||
*
|
||||
* We do not distinguish between key events and keyspace events, and it is up
|
||||
* to the module to filter the actions taken based on the key.
|
||||
@@ -7015,7 +6845,7 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
|
||||
int pos = 0;
|
||||
int64_t ll;
|
||||
while(intsetGet((intset*)ptrFromObj(o),pos++,&ll)) {
|
||||
robj *field = createObject(OBJ_STRING,sdsfromlonglong(ll));
|
||||
robj *field = createStringObjectFromLongLong(ll);
|
||||
fn(key, field, NULL, privdata);
|
||||
decrRefCount(field);
|
||||
}
|
||||
@@ -7031,12 +6861,12 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
|
||||
ziplistGet(p,&vstr,&vlen,&vll);
|
||||
robj *field = (vstr != NULL) ?
|
||||
createStringObject((char*)vstr,vlen) :
|
||||
createObject(OBJ_STRING,sdsfromlonglong(vll));
|
||||
createStringObjectFromLongLong(vll);
|
||||
p = ziplistNext((unsigned char*)ptrFromObj(o),p);
|
||||
ziplistGet(p,&vstr,&vlen,&vll);
|
||||
robj *value = (vstr != NULL) ?
|
||||
createStringObject((char*)vstr,vlen) :
|
||||
createObject(OBJ_STRING,sdsfromlonglong(vll));
|
||||
createStringObjectFromLongLong(vll);
|
||||
fn(key, field, value, privdata);
|
||||
p = ziplistNext((unsigned char*)ptrFromObj(o),p);
|
||||
decrRefCount(field);
|
||||
@@ -7491,7 +7321,7 @@ void processModuleLoadingProgressEvent(int is_aof) {
|
||||
int progress = -1;
|
||||
if (g_pserver->loading_total_bytes)
|
||||
progress = (g_pserver->loading_total_bytes<<10) / g_pserver->loading_total_bytes;
|
||||
RedisModuleLoadingProgressV1 fi = {REDISMODULE_LOADING_PROGRESS_VERSION,
|
||||
RedisModuleFlushInfoV1 fi = {REDISMODULE_LOADING_PROGRESS_VERSION,
|
||||
g_pserver->hz,
|
||||
progress};
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_LOADING_PROGRESS,
|
||||
@@ -8107,7 +7937,6 @@ void moduleRegisterCoreAPI(void) {
|
||||
REGISTER_API(LatencyAddSample);
|
||||
REGISTER_API(StringAppendBuffer);
|
||||
REGISTER_API(RetainString);
|
||||
REGISTER_API(HoldString);
|
||||
REGISTER_API(StringCompare);
|
||||
REGISTER_API(GetContextFromIO);
|
||||
REGISTER_API(GetKeyNameFromIO);
|
||||
@@ -8122,7 +7951,6 @@ void moduleRegisterCoreAPI(void) {
|
||||
REGISTER_API(GetThreadSafeContext);
|
||||
REGISTER_API(FreeThreadSafeContext);
|
||||
REGISTER_API(ThreadSafeContextLock);
|
||||
REGISTER_API(ThreadSafeContextTryLock);
|
||||
REGISTER_API(ThreadSafeContextUnlock);
|
||||
REGISTER_API(DigestAddStringBuffer);
|
||||
REGISTER_API(DigestAddLongLong);
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
/* Timer callback. */
|
||||
void timerHandler(RedisModuleCtx *ctx, void *data) {
|
||||
REDISMODULE_NOT_USED(ctx);
|
||||
printf("Fired %s!\n", (const char *)data);
|
||||
printf("Fired %s!\n", (const char *) data);
|
||||
RedisModule_Free(data);
|
||||
}
|
||||
|
||||
|
||||
+15
-27
@@ -37,7 +37,6 @@ void initClientMultiState(client *c) {
|
||||
c->mstate.commands = NULL;
|
||||
c->mstate.count = 0;
|
||||
c->mstate.cmd_flags = 0;
|
||||
c->mstate.cmd_inv_flags = 0;
|
||||
}
|
||||
|
||||
/* Release all the resources associated with MULTI/EXEC state */
|
||||
@@ -60,13 +59,6 @@ void queueMultiCommand(client *c) {
|
||||
multiCmd *mc;
|
||||
int j;
|
||||
|
||||
/* No sense to waste memory if the transaction is already aborted.
|
||||
* this is useful in case client sends these in a pipeline, or doesn't
|
||||
* bother to read previous responses and didn't notice the multi was already
|
||||
* aborted. */
|
||||
if (c->flags & CLIENT_DIRTY_EXEC)
|
||||
return;
|
||||
|
||||
c->mstate.commands = (multiCmd*)zrealloc(c->mstate.commands,
|
||||
sizeof(multiCmd)*(c->mstate.count+1), MALLOC_LOCAL);
|
||||
mc = c->mstate.commands+c->mstate.count;
|
||||
@@ -78,7 +70,6 @@ void queueMultiCommand(client *c) {
|
||||
incrRefCount(mc->argv[j]);
|
||||
c->mstate.count++;
|
||||
c->mstate.cmd_flags |= c->cmd->flags;
|
||||
c->mstate.cmd_inv_flags |= ~c->cmd->flags;
|
||||
}
|
||||
|
||||
void discardTransaction(client *c) {
|
||||
@@ -127,24 +118,6 @@ void execCommandPropagateExec(client *c) {
|
||||
PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
}
|
||||
|
||||
/* Aborts a transaction, with a specific error message.
|
||||
* The transaction is always aboarted with -EXECABORT so that the client knows
|
||||
* the server exited the multi state, but the actual reason for the abort is
|
||||
* included too.
|
||||
* Note: 'error' may or may not end with \r\n. see addReplyErrorFormat. */
|
||||
void execCommandAbort(client *c, sds error) {
|
||||
discardTransaction(c);
|
||||
|
||||
if (error[0] == '-') error++;
|
||||
addReplyErrorFormat(c, "-EXECABORT Transaction discarded because of: %s", error);
|
||||
|
||||
/* Send EXEC to clients waiting data from MONITOR. We did send a MULTI
|
||||
* already, and didn't send any of the queued commands, now we'll just send
|
||||
* EXEC so it is clear that the transaction is over. */
|
||||
if (listLength(g_pserver->monitors) && !g_pserver->loading)
|
||||
replicationFeedMonitors(c,g_pserver->monitors,c->db->id,c->argv,c->argc);
|
||||
}
|
||||
|
||||
void execCommand(client *c) {
|
||||
int j;
|
||||
robj **orig_argv;
|
||||
@@ -171,6 +144,21 @@ void execCommand(client *c) {
|
||||
goto handle_monitor;
|
||||
}
|
||||
|
||||
/* If there are write commands inside the transaction, and this is a read
|
||||
* only replica, we want to send an error. This happens when the transaction
|
||||
* was initiated when the instance was a master or a writable replica and
|
||||
* then the configuration changed (for example instance was turned into
|
||||
* a replica). */
|
||||
if (!g_pserver->loading && listLength(g_pserver->masters) && g_pserver->repl_slave_ro &&
|
||||
!(c->flags & CLIENT_MASTER) && c->mstate.cmd_flags & CMD_WRITE)
|
||||
{
|
||||
addReplyError(c,
|
||||
"Transaction contains write commands but instance "
|
||||
"is now a read-only replica. EXEC aborted.");
|
||||
discardTransaction(c);
|
||||
goto handle_monitor;
|
||||
}
|
||||
|
||||
/* Exec all the queued commands */
|
||||
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
|
||||
orig_argv = c->argv;
|
||||
|
||||
+42
-106
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "server.h"
|
||||
#include "atomicvar.h"
|
||||
#include "cluster.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <math.h>
|
||||
@@ -57,7 +56,7 @@ size_t getStringObjectSdsUsedMemory(robj *o) {
|
||||
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
|
||||
switch(o->encoding) {
|
||||
case OBJ_ENCODING_RAW: return sdsZmallocSize((sds)ptrFromObj(o));
|
||||
case OBJ_ENCODING_EMBSTR: return zmalloc_size(allocPtrFromObj(o))-sizeof(robj);
|
||||
case OBJ_ENCODING_EMBSTR: return zmalloc_size(o)-sizeof(robj);
|
||||
default: return 0; /* Just integer encoding for now. */
|
||||
}
|
||||
}
|
||||
@@ -474,18 +473,14 @@ std::string escapeString(sds str)
|
||||
*
|
||||
* If the error code is already passed in the string 's', the error
|
||||
* code provided is used, otherwise the string "-ERR " for the generic
|
||||
* error code is automatically added.
|
||||
* Note that 's' must NOT end with \r\n. */
|
||||
* error code is automatically added. */
|
||||
void addReplyErrorLengthCore(client *c, const char *s, size_t len, bool fAsync) {
|
||||
/* If the string already starts with "-..." then the error code
|
||||
* is provided by the caller. Otherwise we use "-ERR". */
|
||||
if (!len || s[0] != '-') addReplyProtoCore(c,"-ERR ",5,fAsync);
|
||||
addReplyProtoCore(c,s,len,fAsync);
|
||||
addReplyProtoCore(c,"\r\n",2,fAsync);
|
||||
}
|
||||
|
||||
/* Do some actions after an error reply was sent (Log if needed, updates stats, etc.) */
|
||||
void afterErrorReply(client *c, const char *s, size_t len) {
|
||||
/* Sometimes it could be normal that a replica replies to a master with
|
||||
* an error and this function gets called. Actually the error will never
|
||||
* be sent because addReply*() against master clients has no effect...
|
||||
@@ -511,11 +506,10 @@ void afterErrorReply(client *c, const char *s, size_t len) {
|
||||
from = "master";
|
||||
}
|
||||
|
||||
if (len > 4096) len = 4096;
|
||||
const char *cmdname = c->lastcmd ? c->lastcmd->name : "<unknown>";
|
||||
serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error "
|
||||
"to its %s: '%.*s' after processing the command "
|
||||
"'%s'", from, to, (int)len, s, cmdname);
|
||||
"to its %s: '%s' after processing the command "
|
||||
"'%s'", from, to, s, cmdname);
|
||||
if (ctype == CLIENT_TYPE_MASTER && g_pserver->repl_backlog &&
|
||||
g_pserver->repl_backlog_histlen > 0)
|
||||
{
|
||||
@@ -530,43 +524,27 @@ void addReplyErrorLength(client *c, const char *s, size_t len)
|
||||
addReplyErrorLengthCore(c, s, len, false);
|
||||
}
|
||||
|
||||
/* The 'err' object is expected to start with -ERRORCODE and end with \r\n.
|
||||
* Unlike addReplyErrorSds and others alike which rely on addReplyErrorLength. */
|
||||
void addReplyErrorObject(client *c, robj *err) {
|
||||
addReply(c, err);
|
||||
afterErrorReply(c, szFromObj(err), sdslen(szFromObj(err))-2); /* Ignore trailing \r\n */
|
||||
}
|
||||
|
||||
/* See addReplyErrorLength for expectations from the input string. */
|
||||
void addReplyError(client *c, const char *err) {
|
||||
addReplyErrorLengthCore(c,err,strlen(err), false);
|
||||
}
|
||||
|
||||
void addReplyErrorAsync(client *c, const char *err) {
|
||||
addReplyErrorLengthCore(c, err, strlen(err), true);
|
||||
afterErrorReply(c,err,strlen(err));
|
||||
}
|
||||
|
||||
/* See addReplyErrorLength for expectations from the input string. */
|
||||
void addReplyErrorSds(client *c, sds err) {
|
||||
addReplyErrorLength(c,err,sdslen(err));
|
||||
afterErrorReply(c,err,sdslen(err));
|
||||
}
|
||||
|
||||
/* See addReplyErrorLength for expectations from the formatted string.
|
||||
* The formatted string is safe to contain \r and \n anywhere. */
|
||||
void addReplyErrorFormat(client *c, const char *fmt, ...) {
|
||||
size_t l, j;
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
sds s = sdscatvprintf(sdsempty(),fmt,ap);
|
||||
va_end(ap);
|
||||
/* Trim any newlines at the end (ones will be added by addReplyErrorLength) */
|
||||
s = sdstrim(s, "\r\n");
|
||||
/* Make sure there are no newlines in the middle of the string, otherwise
|
||||
* invalid protocol is emitted. */
|
||||
s = sdsmapchars(s, "\r\n", " ", 2);
|
||||
/* Make sure there are no newlines in the string, otherwise invalid protocol
|
||||
* is emitted. */
|
||||
l = sdslen(s);
|
||||
for (j = 0; j < l; j++) {
|
||||
if (s[j] == '\r' || s[j] == '\n') s[j] = ' ';
|
||||
}
|
||||
addReplyErrorLength(c,s,sdslen(s));
|
||||
afterErrorReply(c,s,sdslen(s));
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
@@ -638,7 +616,6 @@ void *addReplyDeferredLenAsync(client *c) {
|
||||
|
||||
/* Populate the length object and try gluing it to the next chunk. */
|
||||
void setDeferredAggregateLen(client *c, void *node, long length, char prefix) {
|
||||
serverAssert(length >= 0);
|
||||
listNode *ln = (listNode*)node;
|
||||
clientReplyBlock *next;
|
||||
char lenstr[128];
|
||||
@@ -1228,38 +1205,21 @@ void clientAcceptHandler(connection *conn) {
|
||||
#define MAX_ACCEPTS_PER_CALL 1000
|
||||
static void acceptCommonHandler(connection *conn, int flags, char *ip, int iel) {
|
||||
client *c;
|
||||
char conninfo[100];
|
||||
UNUSED(ip);
|
||||
AeLocker locker;
|
||||
locker.arm(nullptr);
|
||||
|
||||
if (connGetState(conn) != CONN_STATE_ACCEPTING) {
|
||||
serverLog(LL_VERBOSE,
|
||||
"Accepted client connection in error state: %s (conn: %s)",
|
||||
connGetLastError(conn),
|
||||
connGetInfo(conn, conninfo, sizeof(conninfo)));
|
||||
connClose(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Limit the number of connections we take at the same time.
|
||||
*
|
||||
* Admission control will happen before a client is created and connAccept()
|
||||
/* Admission control will happen before a client is created and connAccept()
|
||||
* called, because we don't want to even start transport-level negotiation
|
||||
* if rejected. */
|
||||
if (listLength(g_pserver->clients) + getClusterConnectionsCount()
|
||||
>= g_pserver->maxclients)
|
||||
{
|
||||
const char *err;
|
||||
if (g_pserver->cluster_enabled)
|
||||
err = "-ERR max number of clients + cluster "
|
||||
"connections reached\r\n";
|
||||
else
|
||||
err = "-ERR max number of clients reached\r\n";
|
||||
* if rejected.
|
||||
*/
|
||||
if (listLength(g_pserver->clients) >= g_pserver->maxclients) {
|
||||
const char *err = "-ERR max number of clients reached\r\n";
|
||||
|
||||
/* That's a best effort error message, don't check write errors.
|
||||
* Note that for TLS connections, no handshake was done yet so nothing
|
||||
* is written and the connection will just drop. */
|
||||
* Note that for TLS connections, no handshake was done yet so nothing is written
|
||||
* and the connection will just drop.
|
||||
*/
|
||||
if (connWrite(conn,err,strlen(err)) == -1) {
|
||||
/* Nothing to do, Just to avoid the warning... */
|
||||
}
|
||||
@@ -1270,6 +1230,7 @@ static void acceptCommonHandler(connection *conn, int flags, char *ip, int iel)
|
||||
|
||||
/* Create connection and client */
|
||||
if ((c = createClient(conn, iel)) == NULL) {
|
||||
char conninfo[100];
|
||||
serverLog(LL_WARNING,
|
||||
"Error registering fd event for the new client: %s (conn: %s)",
|
||||
connGetLastError(conn),
|
||||
@@ -1303,10 +1264,9 @@ static void acceptCommonHandler(connection *conn, int flags, char *ip, int iel)
|
||||
void acceptOnThread(connection *conn, int flags, char *cip)
|
||||
{
|
||||
int ielCur = ielFromEventLoop(serverTL->el);
|
||||
bool fBootLoad = (g_pserver->loading == LOADING_BOOT);
|
||||
|
||||
int ielTarget = 0;
|
||||
if (fBootLoad)
|
||||
if (g_pserver->loading)
|
||||
{
|
||||
ielTarget = IDX_EVENT_LOOP_MAIN; // During load only the main thread is active
|
||||
}
|
||||
@@ -1330,10 +1290,10 @@ void acceptOnThread(connection *conn, int flags, char *cip)
|
||||
szT = (char*)zmalloc(NET_IP_STR_LEN, MALLOC_LOCAL);
|
||||
memcpy(szT, cip, NET_IP_STR_LEN);
|
||||
}
|
||||
int res = aePostFunction(g_pserver->rgthreadvar[ielTarget].el, [conn, flags, ielTarget, szT, fBootLoad] {
|
||||
int res = aePostFunction(g_pserver->rgthreadvar[ielTarget].el, [conn, flags, ielTarget, szT] {
|
||||
connMarshalThread(conn);
|
||||
acceptCommonHandler(conn,flags,szT,ielTarget);
|
||||
if (!g_fTestMode && !fBootLoad)
|
||||
if (!g_fTestMode && !g_pserver->loading)
|
||||
rgacceptsInFlight[ielTarget].fetch_sub(1, std::memory_order_relaxed);
|
||||
zfree(szT);
|
||||
});
|
||||
@@ -1342,7 +1302,7 @@ void acceptOnThread(connection *conn, int flags, char *cip)
|
||||
return;
|
||||
// If res != AE_OK we can still try to accept on the local thread
|
||||
}
|
||||
if (!g_fTestMode && !fBootLoad)
|
||||
if (!g_fTestMode && !g_pserver->loading)
|
||||
rgacceptsInFlight[ielTarget].fetch_sub(1, std::memory_order_relaxed);
|
||||
|
||||
aeAcquireLock();
|
||||
@@ -1685,7 +1645,7 @@ int freeClientsInAsyncFreeQueue(int iel) {
|
||||
while((ln = listNext(&li)))
|
||||
{
|
||||
client *c = (client*)listNodeValue(ln);
|
||||
if (c->iel == iel && !(c->flags & CLIENT_PROTECTED))
|
||||
if (c->iel == iel)
|
||||
{
|
||||
vecclientsFree.push_back(c);
|
||||
listDelNode(g_pserver->clients_to_close, ln);
|
||||
@@ -1719,9 +1679,6 @@ client *lookupClientByID(uint64_t id) {
|
||||
* set to 0. So when handler_installed is set to 0 the function must be
|
||||
* thread safe. */
|
||||
int writeToClient(client *c, int handler_installed) {
|
||||
/* Update total number of writes on server */
|
||||
g_pserver->stat_total_writes_processed.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
ssize_t nwritten = 0, totwritten = 0;
|
||||
clientReplyBlock *o;
|
||||
AssertCorrectThread(c);
|
||||
@@ -1971,12 +1928,9 @@ int handleClientsWithPendingWrites(int iel, int aof_state) {
|
||||
}
|
||||
}
|
||||
|
||||
if (listLength(serverTL->clients_pending_asyncwrite))
|
||||
{
|
||||
AeLocker locker;
|
||||
locker.arm(nullptr);
|
||||
ProcessPendingAsyncWrites();
|
||||
}
|
||||
AeLocker locker;
|
||||
locker.arm(nullptr);
|
||||
ProcessPendingAsyncWrites();
|
||||
|
||||
return processed;
|
||||
}
|
||||
@@ -2234,8 +2188,7 @@ int processMultibulkBuffer(client *c) {
|
||||
}
|
||||
|
||||
ok = string2ll(c->querybuf+c->qb_pos+1,newline-(c->querybuf+c->qb_pos+1),&ll);
|
||||
if (!ok || ll < 0 ||
|
||||
(!(c->flags & CLIENT_MASTER) && ll > g_pserver->proto_max_bulk_len)) {
|
||||
if (!ok || ll < 0 || ll > g_pserver->proto_max_bulk_len) {
|
||||
addReplyError(c,"Protocol error: invalid bulk length");
|
||||
setProtocolError("invalid bulk length",c);
|
||||
return C_ERR;
|
||||
@@ -2303,7 +2256,7 @@ int processMultibulkBuffer(client *c) {
|
||||
* 1. The client is reset unless there are reasons to avoid doing it.
|
||||
* 2. In the case of master clients, the replication offset is updated.
|
||||
* 3. Propagate commands we got from our master to replicas down the line. */
|
||||
void commandProcessed(client *c, int flags) {
|
||||
void commandProcessed(client *c) {
|
||||
long long prev_offset = c->reploff;
|
||||
if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
|
||||
/* Update the applied replication offset of our master. */
|
||||
@@ -2331,7 +2284,7 @@ void commandProcessed(client *c, int flags) {
|
||||
ae.arm(c);
|
||||
long long applied = c->reploff - prev_offset;
|
||||
if (applied) {
|
||||
if (!g_pserver->fActiveReplica && (flags & CMD_CALL_PROPAGATE))
|
||||
if (!g_pserver->fActiveReplica)
|
||||
{
|
||||
replicationFeedSlavesFromMasterStream(g_pserver->slaves,
|
||||
c->pending_querybuf, applied);
|
||||
@@ -2352,10 +2305,9 @@ void commandProcessed(client *c, int flags) {
|
||||
int processCommandAndResetClient(client *c, int flags) {
|
||||
int deadclient = 0;
|
||||
serverTL->current_client = c;
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
if (processCommand(c, flags) == C_OK) {
|
||||
commandProcessed(c, flags);
|
||||
AeLocker locker;
|
||||
if (processCommand(c, flags, locker) == C_OK) {
|
||||
commandProcessed(c);
|
||||
}
|
||||
if (serverTL->current_client == NULL) deadclient = 1;
|
||||
serverTL->current_client = NULL;
|
||||
@@ -2445,9 +2397,6 @@ void readQueryFromClient(connection *conn) {
|
||||
if (!lock.try_lock())
|
||||
return; // Process something else while we wait
|
||||
|
||||
/* Update total number of reads on server */
|
||||
g_pserver->stat_total_reads_processed.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
readlen = PROTO_IOBUF_LEN;
|
||||
/* If this is a multi bulk request, and we are processing a bulk reply
|
||||
* that is large enough, try to maximize the probability that the query
|
||||
@@ -2496,7 +2445,7 @@ void readQueryFromClient(connection *conn) {
|
||||
sdsIncrLen(c->querybuf,nread);
|
||||
c->lastinteraction = g_pserver->unixtime;
|
||||
if (c->flags & CLIENT_MASTER) c->read_reploff += nread;
|
||||
g_pserver->stat_net_input_bytes.fetch_add(nread, std::memory_order_relaxed);
|
||||
g_pserver->stat_net_input_bytes += nread;
|
||||
if (sdslen(c->querybuf) > cserver.client_max_querybuf_len) {
|
||||
sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();
|
||||
|
||||
@@ -2508,26 +2457,14 @@ void readQueryFromClient(connection *conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
serverTL->vecclientsProcess.push_back(c);
|
||||
}
|
||||
|
||||
void processClients()
|
||||
{
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
for (client *c : serverTL->vecclientsProcess) {
|
||||
/* There is more data in the client input buffer, continue parsing it
|
||||
* in case to check if there is a full command to execute. */
|
||||
std::unique_lock<fastlock> ul(c->lock);
|
||||
processInputBuffer(c, CMD_CALL_FULL);
|
||||
}
|
||||
|
||||
/* There is more data in the client input buffer, continue parsing it
|
||||
* in case to check if there is a full command to execute. */
|
||||
processInputBuffer(c, CMD_CALL_FULL);
|
||||
if (listLength(serverTL->clients_pending_asyncwrite))
|
||||
{
|
||||
aelock.arm(c);
|
||||
ProcessPendingAsyncWrites();
|
||||
}
|
||||
|
||||
serverTL->vecclientsProcess.clear();
|
||||
}
|
||||
|
||||
void getClientsMaxBuffers(unsigned long *longest_output_list,
|
||||
@@ -2716,7 +2653,6 @@ void clientCommand(client *c) {
|
||||
"SETNAME <name> -- Assign the name <name> to the current connection.",
|
||||
"UNBLOCK <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client.",
|
||||
"TRACKING (on|off) [REDIRECT <id>] [BCAST] [PREFIX first] [PREFIX second] [OPTIN] [OPTOUT]... -- Enable client keys tracking for client side caching.",
|
||||
"CACHING (yes|no) -- Enable/Disable tracking of the keys for next command in OPTIN/OPTOUT mode.",
|
||||
"GETREDIR -- Return the client ID we are redirecting to when tracking is enabled.",
|
||||
NULL
|
||||
};
|
||||
@@ -2904,7 +2840,7 @@ NULL
|
||||
if (c->name)
|
||||
addReplyBulk(c,c->name);
|
||||
else
|
||||
addReplyNull(c);
|
||||
addReplyNull(c, shared.nullbulk);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"pause") && c->argc == 3) {
|
||||
/* CLIENT PAUSE */
|
||||
long long duration;
|
||||
@@ -3121,7 +3057,7 @@ void helloCommand(client *c) {
|
||||
addReplyBulkCString(c,KEYDB_SET_VERSION);
|
||||
|
||||
addReplyBulkCString(c,"proto");
|
||||
addReplyLongLong(c,ver);
|
||||
addReplyLongLong(c,3);
|
||||
|
||||
addReplyBulkCString(c,"id");
|
||||
addReplyLongLong(c,c->id);
|
||||
@@ -3486,7 +3422,7 @@ void processEventsWhileBlocked(int iel) {
|
||||
|
||||
|
||||
aeReleaseLock();
|
||||
serverAssert(!GlobalLocksAcquired());
|
||||
serverAssertDebug(!GlobalLocksAcquired());
|
||||
try
|
||||
{
|
||||
while (iterations--) {
|
||||
|
||||
+9
-57
@@ -41,15 +41,12 @@
|
||||
/* ===================== Creation and parsing of objects ==================== */
|
||||
|
||||
robj *createObject(int type, void *ptr) {
|
||||
size_t mvccExtraBytes = g_pserver->fActiveReplica ? sizeof(redisObjectExtended) : 0;
|
||||
char *oB = (char*)zcalloc(sizeof(robj)+mvccExtraBytes, MALLOC_SHARED);
|
||||
robj *o = reinterpret_cast<robj*>(oB + mvccExtraBytes);
|
||||
|
||||
robj *o = (robj*)zcalloc(sizeof(*o), MALLOC_SHARED);
|
||||
o->type = type;
|
||||
o->encoding = OBJ_ENCODING_RAW;
|
||||
o->m_ptr = ptr;
|
||||
o->setrefcount(1);
|
||||
setMvccTstamp(o, OBJ_MVCC_INVALID);
|
||||
o->mvcc_tstamp = OBJ_MVCC_INVALID;
|
||||
|
||||
/* Set the LRU to the current lruclock (minutes resolution), or
|
||||
* alternatively the LFU counter. */
|
||||
@@ -98,16 +95,13 @@ robj *createEmbeddedStringObject(const char *ptr, size_t len) {
|
||||
size_t allocsize = sizeof(struct sdshdr8)+len+1;
|
||||
if (allocsize < sizeof(void*))
|
||||
allocsize = sizeof(void*);
|
||||
|
||||
size_t mvccExtraBytes = g_pserver->fActiveReplica ? sizeof(redisObjectExtended) : 0;
|
||||
char *oB = (char*)zcalloc(sizeof(robj)+allocsize-sizeof(redisObject::m_ptr)+mvccExtraBytes, MALLOC_SHARED);
|
||||
robj *o = reinterpret_cast<robj*>(oB + mvccExtraBytes);
|
||||
robj *o = (robj*)zcalloc(sizeof(robj)+allocsize-sizeof(o->m_ptr), MALLOC_SHARED);
|
||||
struct sdshdr8 *sh = (sdshdr8*)(&o->m_ptr);
|
||||
|
||||
o->type = OBJ_STRING;
|
||||
o->encoding = OBJ_ENCODING_EMBSTR;
|
||||
o->setrefcount(1);
|
||||
setMvccTstamp(o, OBJ_MVCC_INVALID);
|
||||
o->mvcc_tstamp = OBJ_MVCC_INVALID;
|
||||
|
||||
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
||||
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
|
||||
@@ -135,9 +129,8 @@ robj *createEmbeddedStringObject(const char *ptr, size_t len) {
|
||||
*
|
||||
* The current limit of 52 is chosen so that the biggest string object
|
||||
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
|
||||
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 52
|
||||
|
||||
//static_assert((sizeof(redisObject)+OBJ_ENCODING_EMBSTR_SIZE_LIMIT-8) == 64, "Max EMBSTR obj should be 64 bytes total");
|
||||
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 48
|
||||
static_assert((sizeof(redisObject)+OBJ_ENCODING_EMBSTR_SIZE_LIMIT-8) == 64, "Max EMBSTR obj should be 64 bytes total");
|
||||
robj *createStringObject(const char *ptr, size_t len) {
|
||||
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
|
||||
return createEmbeddedStringObject(ptr,len);
|
||||
@@ -397,11 +390,7 @@ void decrRefCount(robj_roptr o) {
|
||||
case OBJ_CRON: freeCronObject(o); break;
|
||||
default: serverPanic("Unknown object type"); break;
|
||||
}
|
||||
if (g_pserver->fActiveReplica) {
|
||||
zfree(reinterpret_cast<redisObjectExtended*>(o.unsafe_robjcast())-1);
|
||||
} else {
|
||||
zfree(o.unsafe_robjcast());
|
||||
}
|
||||
zfree(o.unsafe_robjcast());
|
||||
} else {
|
||||
if (prev <= 0) serverPanic("decrRefCount against refcount <= 0");
|
||||
}
|
||||
@@ -1331,8 +1320,7 @@ NULL
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]), "lastmodified") && c->argc == 3) {
|
||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
||||
== NULL) return;
|
||||
uint64_t mvcc = mvccFromObj(o);
|
||||
addReplyLongLong(c, (g_pserver->mstime - (mvcc >> MVCC_MS_SHIFT)) / 1000);
|
||||
addReplyLongLong(c, (g_pserver->mstime - (o->mvcc_tstamp >> MVCC_MS_SHIFT)) / 1000);
|
||||
} else {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
}
|
||||
@@ -1374,7 +1362,7 @@ NULL
|
||||
}
|
||||
}
|
||||
if ((de = dictFind(c->db->pdict,ptrFromObj(c->argv[2]))) == NULL) {
|
||||
addReplyNull(c);
|
||||
addReplyNull(c, shared.nullbulk);
|
||||
return;
|
||||
}
|
||||
size_t usage = objectComputeSize((robj*)dictGetVal(de),samples);
|
||||
@@ -1512,39 +1500,3 @@ void redisObject::setrefcount(unsigned ref)
|
||||
serverAssert(!FExpires());
|
||||
refcount.store(ref, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
redisObjectStack::redisObjectStack()
|
||||
{
|
||||
// We need to ensure the Extended Object is first in the class layout
|
||||
serverAssert(reinterpret_cast<ptrdiff_t>(static_cast<redisObject*>(this)) != reinterpret_cast<ptrdiff_t>(this));
|
||||
}
|
||||
|
||||
void *allocPtrFromObj(robj_roptr o) {
|
||||
if (g_pserver->fActiveReplica)
|
||||
return reinterpret_cast<redisObjectExtended*>(o.unsafe_robjcast()) - 1;
|
||||
return o.unsafe_robjcast();
|
||||
}
|
||||
|
||||
robj *objFromAllocPtr(void *pv) {
|
||||
if (g_pserver->fActiveReplica) {
|
||||
return reinterpret_cast<robj*>(reinterpret_cast<redisObjectExtended*>(pv)+1);
|
||||
}
|
||||
return reinterpret_cast<robj*>(pv);
|
||||
}
|
||||
|
||||
uint64_t mvccFromObj(robj_roptr o)
|
||||
{
|
||||
if (g_pserver->fActiveReplica) {
|
||||
redisObjectExtended *oe = reinterpret_cast<redisObjectExtended*>(o.unsafe_robjcast()) - 1;
|
||||
return oe->mvcc_tstamp;
|
||||
}
|
||||
return OBJ_MVCC_INVALID;
|
||||
}
|
||||
|
||||
void setMvccTstamp(robj *o, uint64_t mvcc)
|
||||
{
|
||||
if (!g_pserver->fActiveReplica)
|
||||
return;
|
||||
redisObjectExtended *oe = reinterpret_cast<redisObjectExtended*>(o) - 1;
|
||||
oe->mvcc_tstamp = mvcc;
|
||||
}
|
||||
+2
-2
@@ -89,7 +89,7 @@ void addReplyPubsubUnsubscribed(client *c, robj *channel) {
|
||||
if (channel)
|
||||
addReplyBulk(c,channel);
|
||||
else
|
||||
addReplyNull(c);
|
||||
addReplyNull(c, shared.nullbulk);
|
||||
addReplyLongLong(c,clientSubscriptionsCount(c));
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {
|
||||
if (pattern)
|
||||
addReplyBulk(c,pattern);
|
||||
else
|
||||
addReplyNull(c);
|
||||
addReplyNull(c, shared.nullbulk);
|
||||
addReplyLongLong(c,clientSubscriptionsCount(c));
|
||||
}
|
||||
|
||||
|
||||
@@ -487,8 +487,8 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode
|
||||
if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */
|
||||
memcpy(&h,children+j,sizeof(h));
|
||||
parentlink = children+j;
|
||||
j = 0; /* If the new node is non compressed and we do not
|
||||
iterate again (since i == len) set the split
|
||||
j = 0; /* If the new node is compressed and we do not
|
||||
iterate again (since i == l) set the split
|
||||
position to 0 to signal this node represents
|
||||
the searched key. */
|
||||
}
|
||||
|
||||
+25
-64
@@ -706,23 +706,15 @@ ssize_t rdbSaveStreamPEL(rio *rdb, rax *pel, int nacks) {
|
||||
while(raxNext(&ri)) {
|
||||
/* We store IDs in raw form as 128 big big endian numbers, like
|
||||
* they are inside the radix tree key. */
|
||||
if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbWriteRaw(rdb,ri.key,sizeof(streamID))) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
if (nacks) {
|
||||
streamNACK *nack = (streamNACK*)ri.data;
|
||||
if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1) {
|
||||
raxStop(&ri);
|
||||
if ((n = rdbSaveMillisecondTime(rdb,nack->delivery_time)) == -1)
|
||||
return -1;
|
||||
}
|
||||
nwritten += n;
|
||||
if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveLen(rdb,nack->delivery_count)) == -1) return -1;
|
||||
nwritten += n;
|
||||
/* We don't save the consumer name: we'll save the pending IDs
|
||||
* for each consumer in the consumer PEL, and resolve the consumer
|
||||
@@ -751,27 +743,20 @@ size_t rdbSaveStreamConsumers(rio *rdb, streamCG *cg) {
|
||||
streamConsumer *consumer = (streamConsumer*)ri.data;
|
||||
|
||||
/* Consumer name. */
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
/* Last seen time. */
|
||||
if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1) {
|
||||
raxStop(&ri);
|
||||
if ((n = rdbSaveMillisecondTime(rdb,consumer->seen_time)) == -1)
|
||||
return -1;
|
||||
}
|
||||
nwritten += n;
|
||||
|
||||
/* Consumer PEL, without the ACKs (see last parameter of the function
|
||||
* passed with value of 0), at loading time we'll lookup the ID
|
||||
* in the consumer group global PEL and will put a reference in the
|
||||
* consumer local PEL. */
|
||||
if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1) {
|
||||
raxStop(&ri);
|
||||
if ((n = rdbSaveStreamPEL(rdb,consumer->pel,0)) == -1)
|
||||
return -1;
|
||||
}
|
||||
nwritten += n;
|
||||
}
|
||||
raxStop(&ri);
|
||||
@@ -936,15 +921,9 @@ ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
|
||||
while (raxNext(&ri)) {
|
||||
unsigned char *lp = (unsigned char*)ri.data;
|
||||
size_t lp_bytes = lpBytes(lp);
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) return -1;
|
||||
nwritten += n;
|
||||
if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveRawString(rdb,lp,lp_bytes)) == -1) return -1;
|
||||
nwritten += n;
|
||||
}
|
||||
raxStop(&ri);
|
||||
@@ -976,36 +955,22 @@ ssize_t rdbSaveObject(rio *rdb, robj_roptr o, robj *key) {
|
||||
streamCG *cg = (streamCG*)ri.data;
|
||||
|
||||
/* Save the group name. */
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1) {
|
||||
raxStop(&ri);
|
||||
if ((n = rdbSaveRawString(rdb,ri.key,ri.key_len)) == -1)
|
||||
return -1;
|
||||
}
|
||||
nwritten += n;
|
||||
|
||||
/* Last ID. */
|
||||
if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveLen(rdb,cg->last_id.ms)) == -1) return -1;
|
||||
nwritten += n;
|
||||
if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveLen(rdb,cg->last_id.seq)) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
/* Save the global PEL. */
|
||||
if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveStreamPEL(rdb,cg->pel,1)) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
/* Save the consumers of this group. */
|
||||
if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) {
|
||||
raxStop(&ri);
|
||||
return -1;
|
||||
}
|
||||
if ((n = rdbSaveStreamConsumers(rdb,cg)) == -1) return -1;
|
||||
nwritten += n;
|
||||
}
|
||||
raxStop(&ri);
|
||||
@@ -1124,10 +1089,8 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, expireEntry *pexpire) {
|
||||
}
|
||||
|
||||
char szT[32];
|
||||
if (g_pserver->fActiveReplica) {
|
||||
snprintf(szT, 32, "%" PRIu64, mvccFromObj(val));
|
||||
if (rdbSaveAuxFieldStrStr(rdb,"mvcc-tstamp", szT) == -1) return -1;
|
||||
}
|
||||
snprintf(szT, 32, "%" PRIu64, val->mvcc_tstamp);
|
||||
if (rdbSaveAuxFieldStrStr(rdb,"mvcc-tstamp", szT) == -1) return -1;
|
||||
|
||||
/* Save type, key, value */
|
||||
if (rdbSaveObjectType(rdb,val) == -1) return -1;
|
||||
@@ -1181,7 +1144,7 @@ int rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
|
||||
|
||||
int saveKey(rio *rdb, redisDb *db, int flags, size_t *processed, const char *keystr, robj *o)
|
||||
{
|
||||
redisObjectStack key;
|
||||
robj key;
|
||||
|
||||
initStaticStringObject(key,(char*)keystr);
|
||||
expireEntry *pexpire = getExpire(db, &key);
|
||||
@@ -2034,7 +1997,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, uint64_t mvcc_tstamp) {
|
||||
exit(1);
|
||||
}
|
||||
RedisModuleIO io;
|
||||
redisObjectStack keyobj;
|
||||
robj keyobj;
|
||||
initStaticStringObject(keyobj,key);
|
||||
moduleInitIOContext(io,mt,rdb,&keyobj);
|
||||
io.ver = (rdbtype == RDB_TYPE_MODULE) ? 1 : 2;
|
||||
@@ -2083,7 +2046,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, uint64_t mvcc_tstamp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
setMvccTstamp(o, mvcc_tstamp);
|
||||
o->mvcc_tstamp = mvcc_tstamp;
|
||||
serverAssert(!o->FExpires());
|
||||
return o;
|
||||
}
|
||||
@@ -2092,7 +2055,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key, uint64_t mvcc_tstamp) {
|
||||
* needed to provide loading stats. */
|
||||
void startLoading(size_t size, int rdbflags) {
|
||||
/* Load the DB */
|
||||
g_pserver->loading = (rdbflags & RDBFLAGS_REPLICATION) ? LOADING_REPLICATION : LOADING_BOOT;
|
||||
g_pserver->loading = 1;
|
||||
g_pserver->loading_start_time = time(NULL);
|
||||
g_pserver->loading_loaded_bytes = 0;
|
||||
g_pserver->loading_total_bytes = size;
|
||||
@@ -2351,7 +2314,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
redisObjectStack keyobj;
|
||||
redisObject keyobj;
|
||||
initStaticStringObject(keyobj,key);
|
||||
setExpire(NULL, db, &keyobj, subexpireKey, strtoll(szFromObj(auxval), nullptr, 10));
|
||||
decrRefCount(subexpireKey);
|
||||
@@ -2435,22 +2398,23 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
|
||||
key = nullptr;
|
||||
goto eoferr;
|
||||
}
|
||||
bool fStaleMvccKey = (rsi) ? mvccFromObj(val) < rsi->mvccMinThreshold : false;
|
||||
bool fStaleMvccKey = (rsi) ? val->mvcc_tstamp < rsi->mvccMinThreshold : false;
|
||||
|
||||
/* Check if the key already expired. This function is used when loading
|
||||
* an RDB file from disk, either at startup, or when an RDB was
|
||||
* received from the master. In the latter case, the master is
|
||||
* responsible for key expiry. If we would expire keys here, the
|
||||
* snapshot taken by the master may not be reflected on the replica. */
|
||||
redisObjectStack keyobj;
|
||||
robj keyobj;
|
||||
initStaticStringObject(keyobj,key);
|
||||
bool fExpiredKey = iAmMaster() && !(rdbflags&RDBFLAGS_AOF_PREAMBLE) && expiretime != -1 && expiretime < now;
|
||||
if (fStaleMvccKey || fExpiredKey) {
|
||||
if (fStaleMvccKey && !fExpiredKey && rsi != nullptr && rsi->mi != nullptr && rsi->mi->staleKeyMap != nullptr && lookupKeyRead(db, &keyobj) == nullptr) {
|
||||
// We have a key that we've already deleted and is not back in our database.
|
||||
// We'll need to inform the sending master of the delete if it is also a replica of us
|
||||
robj_sharedptr objKeyDup(createStringObject(key, sdslen(key)));
|
||||
rsi->mi->staleKeyMap->operator[](db - g_pserver->db).push_back(objKeyDup);
|
||||
robj *objKeyDup = createStringObject(key, sdslen(key));
|
||||
rsi->mi->staleKeyMap->operator[](db - g_pserver->db).push_back(objKeyDup);
|
||||
decrRefCount(objKeyDup);
|
||||
}
|
||||
fLastKeyExpired = true;
|
||||
sdsfree(key);
|
||||
@@ -2472,9 +2436,6 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
|
||||
|
||||
/* Set usage information (for eviction). */
|
||||
objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock,1000);
|
||||
|
||||
/* call key space notification on key loaded for modules only */
|
||||
moduleNotifyKeyspaceEvent(NOTIFY_LOADED, "loaded", &keyobj, db->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+48
-81
@@ -1,4 +1,4 @@
|
||||
/* Redis benchmark utility.
|
||||
/* Redis benchmark utility.
|
||||
*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
@@ -189,8 +189,6 @@ static void *execBenchmarkThread(void *ptr);
|
||||
static clusterNode *createClusterNode(char *ip, int port);
|
||||
static redisConfig *getRedisConfig(const char *ip, int port,
|
||||
const char *hostsocket);
|
||||
static redisContext *getRedisContext(const char *ip, int port,
|
||||
const char *hostsocket);
|
||||
static void freeRedisConfig(redisConfig *cfg);
|
||||
static int fetchClusterSlotsConfiguration(client c);
|
||||
static void updateClusterSlotsConfiguration();
|
||||
@@ -246,69 +244,46 @@ extern "C" void _serverAssert(const char *estr, const char *file, int line) {
|
||||
*((char*)-1) = 'x';
|
||||
}
|
||||
|
||||
static redisContext *getRedisContext(const char *ip, int port,
|
||||
const char *hostsocket)
|
||||
{
|
||||
redisContext *ctx = NULL;
|
||||
redisReply *reply = NULL;
|
||||
if (hostsocket == NULL)
|
||||
ctx = redisConnect(ip, port);
|
||||
else
|
||||
ctx = redisConnectUnix(hostsocket);
|
||||
if (ctx == NULL || ctx->err) {
|
||||
fprintf(stderr,"Could not connect to Redis at ");
|
||||
const char *err = (ctx != NULL ? ctx->errstr : "");
|
||||
if (hostsocket == NULL)
|
||||
fprintf(stderr,"%s:%d: %s\n",ip,port,err);
|
||||
else
|
||||
fprintf(stderr,"%s: %s\n",hostsocket,err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (config.auth == NULL)
|
||||
return ctx;
|
||||
if (config.user == NULL)
|
||||
reply = (redisReply*)redisCommand(ctx,"AUTH %s", config.auth);
|
||||
else
|
||||
reply = (redisReply*)redisCommand(ctx,"AUTH %s %s", config.user, config.auth);
|
||||
if (reply != NULL) {
|
||||
if (reply->type == REDIS_REPLY_ERROR) {
|
||||
if (hostsocket == NULL)
|
||||
fprintf(stderr, "Node %s:%d replied with error:\n%s\n", ip, port, reply->str);
|
||||
else
|
||||
fprintf(stderr, "Node %s replied with error:\n%s\n", hostsocket, reply->str);
|
||||
goto cleanup;
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
return ctx;
|
||||
}
|
||||
fprintf(stderr, "ERROR: failed to fetch reply from ");
|
||||
if (hostsocket == NULL)
|
||||
fprintf(stderr, "%s:%d\n", ip, port);
|
||||
else
|
||||
fprintf(stderr, "%s\n", hostsocket);
|
||||
cleanup:
|
||||
freeReplyObject(reply);
|
||||
redisFree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static redisConfig *getRedisConfig(const char *ip, int port,
|
||||
const char *hostsocket)
|
||||
{
|
||||
redisConfig *cfg = (redisConfig*)zcalloc(sizeof(*cfg));
|
||||
redisConfig *cfg = (redisConfig*)zcalloc(sizeof(*cfg), MALLOC_LOCAL);
|
||||
int i = 0;
|
||||
void *r = NULL;
|
||||
if (!cfg) return NULL;
|
||||
redisContext *c = NULL;
|
||||
redisReply *reply = NULL, *sub_reply = NULL;
|
||||
c = getRedisContext(ip, port, hostsocket);
|
||||
if (c == NULL) {
|
||||
freeRedisConfig(cfg);
|
||||
return NULL;
|
||||
if (hostsocket == NULL)
|
||||
c = redisConnect(ip, port);
|
||||
else
|
||||
c = redisConnectUnix(hostsocket);
|
||||
if (c == NULL || c->err) {
|
||||
fprintf(stderr,"Could not connect to Redis at ");
|
||||
const char *err = (c != NULL ? c->errstr : "");
|
||||
if (hostsocket == NULL) fprintf(stderr,"%s:%d: %s\n",ip,port,err);
|
||||
else fprintf(stderr,"%s: %s\n",hostsocket,err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(config.auth) {
|
||||
void *authReply = NULL;
|
||||
if (config.user == NULL)
|
||||
redisAppendCommand(c, "AUTH %s", config.auth);
|
||||
else
|
||||
redisAppendCommand(c, "AUTH %s %s", config.user, config.auth);
|
||||
if (REDIS_OK != redisGetReply(c, &authReply)) goto fail;
|
||||
if (reply) freeReplyObject(reply);
|
||||
reply = ((redisReply *) authReply);
|
||||
if (reply->type == REDIS_REPLY_ERROR) {
|
||||
fprintf(stderr, "ERROR: %s\n", reply->str);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
redisAppendCommand(c, "CONFIG GET %s", "save");
|
||||
redisAppendCommand(c, "CONFIG GET %s", "appendonly");
|
||||
|
||||
void *r;
|
||||
for (int i=0; i < 2; i++) {
|
||||
for (; i < 2; i++) {
|
||||
int res = redisGetReply(c, &r);
|
||||
if (reply) freeReplyObject(reply);
|
||||
reply = res == REDIS_OK ? ((redisReply *) r) : NULL;
|
||||
@@ -1026,11 +1001,17 @@ static int fetchClusterConfiguration() {
|
||||
int success = 1;
|
||||
redisContext *ctx = NULL;
|
||||
redisReply *reply = NULL;
|
||||
char *lines = NULL;
|
||||
char *line = NULL;
|
||||
char *p = NULL;
|
||||
ctx = getRedisContext(config.hostip, config.hostport, config.hostsocket);
|
||||
if (ctx == NULL) {
|
||||
char *lines = reply->str, *p, *line;
|
||||
if (config.hostsocket == NULL)
|
||||
ctx = redisConnect(config.hostip,config.hostport);
|
||||
else
|
||||
ctx = redisConnectUnix(config.hostsocket);
|
||||
if (ctx->err) {
|
||||
fprintf(stderr,"Could not connect to Redis at ");
|
||||
if (config.hostsocket == NULL) {
|
||||
fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,
|
||||
ctx->errstr);
|
||||
} else fprintf(stderr,"%s: %s\n",config.hostsocket,ctx->errstr);
|
||||
exit(1);
|
||||
}
|
||||
clusterNode *firstNode = createClusterNode((char *) config.hostip,
|
||||
@@ -1226,9 +1207,11 @@ static int fetchClusterSlotsConfiguration(client c) {
|
||||
assert(node->port);
|
||||
/* Use first node as entry point to connect to. */
|
||||
if (ctx == NULL) {
|
||||
ctx = getRedisContext(node->ip, node->port, NULL);
|
||||
if (!ctx) {
|
||||
ctx = redisConnect(node->ip, node->port);
|
||||
if (!ctx || ctx->err) {
|
||||
success = 0;
|
||||
if (ctx && ctx->err)
|
||||
fprintf(stderr, "REDIS CONNECTION ERROR: %s\n", ctx->errstr);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
@@ -1442,8 +1425,7 @@ usage:
|
||||
" --cluster Enable cluster mode.\n"
|
||||
" --enable-tracking Send CLIENT TRACKING on before starting benchmark.\n"
|
||||
" -k <boolean> 1=keep alive 0=reconnect (default 1)\n"
|
||||
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD,\n"
|
||||
" random members and scores for ZADD.\n"
|
||||
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD\n"
|
||||
" Using this option the benchmark will expand the string __rand_int__\n"
|
||||
" inside an argument with a 12 digits number in the specified range\n"
|
||||
" from 0 to keyspacelen-1. The substitution changes every time a command\n"
|
||||
@@ -1740,7 +1722,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
if (test_is_selected("hset")) {
|
||||
len = redisFormatCommand(&cmd,
|
||||
"HSET myhash:{tag} element:__rand_int__ %s",data);
|
||||
"HSET myhash:{tag}:__rand_int__ element:__rand_int__ %s",data);
|
||||
benchmark("HSET",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
@@ -1751,21 +1733,6 @@ int main(int argc, const char **argv) {
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("zadd")) {
|
||||
const char *score = "0";
|
||||
if (config.randomkeys) score = "__rand_int__";
|
||||
len = redisFormatCommand(&cmd,
|
||||
"ZADD myzset:{tag} %s element:__rand_int__",score);
|
||||
benchmark("ZADD",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("zpopmin")) {
|
||||
len = redisFormatCommand(&cmd,"ZPOPMIN myzset:{tag}");
|
||||
benchmark("ZPOPMIN",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("lrange") ||
|
||||
test_is_selected("lrange_100") ||
|
||||
test_is_selected("lrange_300") ||
|
||||
|
||||
+12
-14
@@ -109,6 +109,7 @@ extern "C" void freeClusterManager(void) {
|
||||
}
|
||||
|
||||
/* This function returns a random master node, return NULL if none */
|
||||
|
||||
static clusterManagerNode *clusterManagerNodeMasterRandom() {
|
||||
int master_count = 0;
|
||||
int idx;
|
||||
@@ -139,7 +140,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
|
||||
int force_fix = config.cluster_manager_command.flags &
|
||||
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
|
||||
/* we want explicit manual confirmation from users for all the fix cases */
|
||||
int ignore_force = 1;
|
||||
int force = 0;
|
||||
|
||||
dictIterator *iter = nullptr;
|
||||
|
||||
@@ -212,7 +213,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
|
||||
"across the cluster:\n");
|
||||
clusterManagerPrintSlotsList(none);
|
||||
if (confirmWithYes("Fix these slots by covering with a random node?",
|
||||
ignore_force)) {
|
||||
force)) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(none, &li);
|
||||
@@ -239,7 +240,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
|
||||
printf("The following uncovered slots have keys in just one node:\n");
|
||||
clusterManagerPrintSlotsList(single);
|
||||
if (confirmWithYes("Fix these slots by covering with those nodes?",
|
||||
ignore_force)) {
|
||||
force)) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(single, &li);
|
||||
@@ -271,7 +272,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
|
||||
printf("The following uncovered slots have keys in multiple nodes:\n");
|
||||
clusterManagerPrintSlotsList(multi);
|
||||
if (confirmWithYes("Fix these slots by moving keys "
|
||||
"into a single node?", ignore_force)) {
|
||||
"into a single node?", force)) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(multi, &li);
|
||||
@@ -335,7 +336,6 @@ cleanup:
|
||||
return fixed;
|
||||
}
|
||||
|
||||
|
||||
/* Return the anti-affinity score, which is a measure of the amount of
|
||||
* violations of anti-affinity in the current cluster layout, that is, how
|
||||
* badly the masters and slaves are distributed in the different IP
|
||||
@@ -759,9 +759,7 @@ static void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {
|
||||
|
||||
/* Pipeline TYPE commands */
|
||||
for(i=0;i<keys->elements;i++) {
|
||||
const char* argv[] = {"TYPE", keys->element[i]->str};
|
||||
size_t lens[] = {4, keys->element[i]->len};
|
||||
redisAppendCommandArgv(context, 2, argv, lens);
|
||||
redisAppendCommand(context, "TYPE %s", keys->element[i]->str);
|
||||
}
|
||||
|
||||
/* Retrieve types */
|
||||
@@ -858,20 +856,20 @@ void findBigKeys(int memkeys, unsigned memkeys_samples) {
|
||||
sampled++;
|
||||
|
||||
if(type->biggest<sizes[i]) {
|
||||
printf(
|
||||
"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
|
||||
pct, type->name, keys->element[i]->str, sizes[i],
|
||||
!memkeys? type->sizeunit: "bytes");
|
||||
|
||||
/* Keep track of biggest key name for this type */
|
||||
if (type->biggest_key)
|
||||
sdsfree(type->biggest_key);
|
||||
type->biggest_key = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
|
||||
type->biggest_key = sdsnew(keys->element[i]->str);
|
||||
if(!type->biggest_key) {
|
||||
fprintf(stderr, "Failed to allocate memory for key!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf(
|
||||
"[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
|
||||
pct, type->name, type->biggest_key, sizes[i],
|
||||
!memkeys? type->sizeunit: "bytes");
|
||||
|
||||
/* Keep track of the biggest size for this type */
|
||||
type->biggest = sizes[i];
|
||||
}
|
||||
|
||||
+62
-177
@@ -1354,12 +1354,6 @@ static int parseOptions(int argc, char **argv) {
|
||||
i = j;
|
||||
} else if (!strcmp(argv[i],"--cluster") && lastarg) {
|
||||
usage();
|
||||
} else if ((!strcmp(argv[i],"--cluster-only-masters"))) {
|
||||
config.cluster_manager_command.flags |=
|
||||
CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY;
|
||||
} else if ((!strcmp(argv[i],"--cluster-only-replicas"))) {
|
||||
config.cluster_manager_command.flags |=
|
||||
CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY;
|
||||
} else if (!strcmp(argv[i],"--cluster-replicas") && !lastarg) {
|
||||
config.cluster_manager_command.replicas = atoi(argv[++i]);
|
||||
} else if (!strcmp(argv[i],"--cluster-master-id") && !lastarg) {
|
||||
@@ -1441,8 +1435,6 @@ static int parseOptions(int argc, char **argv) {
|
||||
printf("keydb-cli %s\n", version);
|
||||
sdsfree(version);
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[i],"--no-motd")) {
|
||||
config.disable_motd = 1;
|
||||
} else if (!strcmp(argv[i],"-3")) {
|
||||
config.resp3 = 1;
|
||||
} else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-') {
|
||||
@@ -1488,11 +1480,6 @@ static void parseEnv() {
|
||||
if (auth != NULL && config.auth == NULL) {
|
||||
config.auth = auth;
|
||||
}
|
||||
|
||||
char *cluster_yes = getenv(REDIS_CLI_CLUSTER_YES_ENV);
|
||||
if (cluster_yes != NULL && !strcmp(cluster_yes, "1")) {
|
||||
config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_YES;
|
||||
}
|
||||
}
|
||||
|
||||
static sds readArgFromStdin(void) {
|
||||
@@ -1583,8 +1570,7 @@ static void usage(void) {
|
||||
" --hotkeys Sample Redis keys looking for hot keys.\n"
|
||||
" only works when maxmemory-policy is *lfu.\n"
|
||||
" --scan List all keys using the SCAN command.\n"
|
||||
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys\n"
|
||||
" options (default: *).\n"
|
||||
" --pattern <pat> Useful with --scan to specify a SCAN pattern.\n"
|
||||
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
|
||||
" The test will run for the specified amount of seconds.\n"
|
||||
" --eval <file> Send an EVAL command using the Lua script at <file>.\n"
|
||||
@@ -1623,10 +1609,10 @@ static void usage(void) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int confirmWithYes(const char *msg, int ignore_force) {
|
||||
int confirmWithYes(const char *msg, int force) {
|
||||
/* if force is true and --cluster-yes option is on,
|
||||
* do not prompt for an answer */
|
||||
if (!ignore_force &&
|
||||
if (force &&
|
||||
(config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_YES)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -1815,7 +1801,6 @@ static void repl(void) {
|
||||
|
||||
if (argv == NULL) {
|
||||
printf("Invalid argument(s)\n");
|
||||
fflush(stdout);
|
||||
linenoiseFree(line);
|
||||
continue;
|
||||
} else if (argc > 0) {
|
||||
@@ -2060,7 +2045,7 @@ clusterManagerCommandDef clusterManagerCommands[] = {
|
||||
"new_host:new_port existing_host:existing_port", "slave,master-id <arg>"},
|
||||
{"del-node", clusterManagerCommandDeleteNode, 2, "host:port node_id",NULL},
|
||||
{"call", clusterManagerCommandCall, -2,
|
||||
"host:port command arg arg .. arg", "only-masters,only-replicas"},
|
||||
"host:port command arg arg .. arg", NULL},
|
||||
{"set-timeout", clusterManagerCommandSetTimeout, 2,
|
||||
"host:port milliseconds", NULL},
|
||||
{"import", clusterManagerCommandImport, 1, "host:port",
|
||||
@@ -3052,7 +3037,6 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
|
||||
size_t *argv_len = NULL;
|
||||
int c = (replace ? 8 : 7);
|
||||
if (config.auth) c += 2;
|
||||
if (config.user) c += 1;
|
||||
size_t argc = c + reply->elements;
|
||||
size_t i, offset = 6; // Keys Offset
|
||||
argv = zcalloc(argc * sizeof(char *), MALLOC_LOCAL);
|
||||
@@ -3079,24 +3063,12 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
|
||||
offset++;
|
||||
}
|
||||
if (config.auth) {
|
||||
if (config.user) {
|
||||
argv[offset] = "AUTH2";
|
||||
argv_len[offset] = 5;
|
||||
offset++;
|
||||
argv[offset] = config.user;
|
||||
argv_len[offset] = strlen(config.user);
|
||||
offset++;
|
||||
argv[offset] = config.auth;
|
||||
argv_len[offset] = strlen(config.auth);
|
||||
offset++;
|
||||
} else {
|
||||
argv[offset] = "AUTH";
|
||||
argv_len[offset] = 4;
|
||||
offset++;
|
||||
argv[offset] = config.auth;
|
||||
argv_len[offset] = strlen(config.auth);
|
||||
offset++;
|
||||
}
|
||||
argv[offset] = "AUTH";
|
||||
argv_len[offset] = 4;
|
||||
offset++;
|
||||
argv[offset] = config.auth;
|
||||
argv_len[offset] = strlen(config.auth);
|
||||
offset++;
|
||||
}
|
||||
argv[offset] = "KEYS";
|
||||
argv_len[offset] = 4;
|
||||
@@ -4623,8 +4595,8 @@ assign_replicas:
|
||||
}
|
||||
clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);
|
||||
clusterManagerShowNodes();
|
||||
int ignore_force = 0;
|
||||
if (confirmWithYes("Can I set the above configuration?", ignore_force)) {
|
||||
int force = 1;
|
||||
if (confirmWithYes("Can I set the above configuration?", force)) {
|
||||
listRewind(cluster_manager.nodes, &li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
clusterManagerNode *node = ln->value;
|
||||
@@ -5515,10 +5487,6 @@ static int clusterManagerCommandCall(int argc, char **argv) {
|
||||
listRewind(cluster_manager.nodes, &li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
clusterManagerNode *n = ln->value;
|
||||
if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY)
|
||||
&& (n->replicate != NULL)) continue; // continue if node is slave
|
||||
if ((config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY)
|
||||
&& (n->replicate == NULL)) continue; // continue if node is master
|
||||
if (!n->context && !clusterManagerNodeConnect(n)) continue;
|
||||
redisReply *reply = NULL;
|
||||
redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);
|
||||
@@ -5888,84 +5856,10 @@ void sendCapa() {
|
||||
sendReplconf("capa", "eof");
|
||||
}
|
||||
|
||||
/* Wrapper around hiredis to allow arbitrary reads and writes.
|
||||
*
|
||||
* We piggybacks on top of hiredis to achieve transparent TLS support,
|
||||
* and use its internal buffers so it can co-exist with commands
|
||||
* previously/later issued on the connection.
|
||||
*
|
||||
* Interface is close to enough to read()/write() so things should mostly
|
||||
* work transparently.
|
||||
*/
|
||||
|
||||
/* Write a raw buffer through a redisContext. If we already have something
|
||||
* in the buffer (leftovers from hiredis operations) it will be written
|
||||
* as well.
|
||||
*/
|
||||
static ssize_t writeConn(redisContext *c, const char *buf, size_t buf_len)
|
||||
{
|
||||
int done = 0;
|
||||
|
||||
/* Append data to buffer which is *usually* expected to be empty
|
||||
* but we don't assume that, and write.
|
||||
*/
|
||||
c->obuf = sdscatlen(c->obuf, buf, buf_len);
|
||||
if (redisBufferWrite(c, &done) == REDIS_ERR) {
|
||||
if (!(c->flags & REDIS_BLOCK))
|
||||
errno = EAGAIN;
|
||||
|
||||
/* On error, we assume nothing was written and we roll back the
|
||||
* buffer to its original state.
|
||||
*/
|
||||
if (sdslen(c->obuf) > buf_len)
|
||||
sdsrange(c->obuf, 0, -(buf_len+1));
|
||||
else
|
||||
sdsclear(c->obuf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If we're done, free up everything. We may have written more than
|
||||
* buf_len (if c->obuf was not initially empty) but we don't have to
|
||||
* tell.
|
||||
*/
|
||||
if (done) {
|
||||
sdsclear(c->obuf);
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
/* Write was successful but we have some leftovers which we should
|
||||
* remove from the buffer.
|
||||
*
|
||||
* Do we still have data that was there prior to our buf? If so,
|
||||
* restore buffer to it's original state and report no new data was
|
||||
* writen.
|
||||
*/
|
||||
if (sdslen(c->obuf) > buf_len) {
|
||||
sdsrange(c->obuf, 0, -(buf_len+1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At this point we're sure no prior data is left. We flush the buffer
|
||||
* and report how much we've written.
|
||||
*/
|
||||
size_t left = sdslen(c->obuf);
|
||||
sdsclear(c->obuf);
|
||||
return buf_len - left;
|
||||
}
|
||||
|
||||
/* Read raw bytes through a redisContext. The read operation is not greedy
|
||||
* and may not fill the buffer entirely.
|
||||
*/
|
||||
static ssize_t readConn(redisContext *c, char *buf, size_t len)
|
||||
{
|
||||
return c->funcs->read(c, buf, len);
|
||||
}
|
||||
|
||||
/* Sends SYNC and reads the number of bytes in the payload. Used both by
|
||||
* slaveMode() and getRDB().
|
||||
* returns 0 in case an EOF marker is used. */
|
||||
unsigned long long sendSync(redisContext *c, char *out_eof) {
|
||||
unsigned long long sendSync(int fd, char *out_eof) {
|
||||
/* To start we need to send the SYNC command and return the payload.
|
||||
* The hiredis client lib does not understand this part of the protocol
|
||||
* and we don't want to mess with its buffers, so everything is performed
|
||||
@@ -5974,7 +5868,7 @@ unsigned long long sendSync(redisContext *c, char *out_eof) {
|
||||
ssize_t nread;
|
||||
|
||||
/* Send the SYNC command. */
|
||||
if (writeConn(c, "SYNC\r\n", 6) != 6) {
|
||||
if (write(fd,"SYNC\r\n",6) != 6) {
|
||||
fprintf(stderr,"Error writing to master\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -5982,7 +5876,7 @@ unsigned long long sendSync(redisContext *c, char *out_eof) {
|
||||
/* Read $<payload>\r\n, making sure to read just up to "\n" */
|
||||
p = buf;
|
||||
while(1) {
|
||||
nread = readConn(c,p,1);
|
||||
nread = read(fd,p,1);
|
||||
if (nread <= 0) {
|
||||
fprintf(stderr,"Error reading bulk length while SYNCing\n");
|
||||
exit(1);
|
||||
@@ -6003,10 +5897,11 @@ unsigned long long sendSync(redisContext *c, char *out_eof) {
|
||||
}
|
||||
|
||||
static void slaveMode(void) {
|
||||
int fd = context->fd;
|
||||
static char eofmark[RDB_EOF_MARK_SIZE];
|
||||
static char lastbytes[RDB_EOF_MARK_SIZE];
|
||||
static int usemark = 0;
|
||||
unsigned long long payload = sendSync(context,eofmark);
|
||||
unsigned long long payload = sendSync(fd, eofmark);
|
||||
char buf[1024];
|
||||
int original_output = config.output;
|
||||
|
||||
@@ -6026,7 +5921,7 @@ static void slaveMode(void) {
|
||||
while(payload) {
|
||||
ssize_t nread;
|
||||
|
||||
nread = readConn(context,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
if (nread <= 0) {
|
||||
fprintf(stderr,"Error reading RDB payload while SYNCing\n");
|
||||
exit(1);
|
||||
@@ -6069,15 +5964,14 @@ static void slaveMode(void) {
|
||||
/* This function implements --rdb, so it uses the replication protocol in order
|
||||
* to fetch the RDB file from a remote server. */
|
||||
static void getRDB(clusterManagerNode *node) {
|
||||
int fd;
|
||||
redisContext *s;
|
||||
int s, fd;
|
||||
char *filename;
|
||||
if (node != NULL) {
|
||||
assert(node->context);
|
||||
s = node->context;
|
||||
s = node->context->fd;
|
||||
filename = clusterManagerGetNodeRDBFilename(node);
|
||||
} else {
|
||||
s = context;
|
||||
s = context->fd;
|
||||
filename = config.rdb_filename;
|
||||
}
|
||||
static char eofmark[RDB_EOF_MARK_SIZE];
|
||||
@@ -6112,7 +6006,7 @@ static void getRDB(clusterManagerNode *node) {
|
||||
while(payload) {
|
||||
ssize_t nread, nwritten;
|
||||
|
||||
nread = readConn(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
if (nread <= 0) {
|
||||
fprintf(stderr,"I/O Error reading RDB payload from socket\n");
|
||||
exit(1);
|
||||
@@ -6146,7 +6040,7 @@ static void getRDB(clusterManagerNode *node) {
|
||||
} else {
|
||||
fprintf(stderr,"Transfer finished with success.\n");
|
||||
}
|
||||
redisFree(s); /* Close the file descriptor ASAP as fsync() may take time. */
|
||||
close(s); /* Close the file descriptor ASAP as fsync() may take time. */
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
fprintf(stderr,"Transfer finished with success.\n");
|
||||
@@ -6163,9 +6057,11 @@ static void getRDB(clusterManagerNode *node) {
|
||||
|
||||
#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
|
||||
static void pipeMode(void) {
|
||||
int fd = context->fd;
|
||||
long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
|
||||
char obuf[1024*16]; /* Output buffer */
|
||||
char ibuf[1024*16], obuf[1024*16]; /* Input and output buffers */
|
||||
char aneterr[ANET_ERR_LEN];
|
||||
redisReader *reader = redisReaderCreate();
|
||||
redisReply *reply;
|
||||
int eof = 0; /* True once we consumed all the standard input. */
|
||||
int done = 0;
|
||||
@@ -6175,38 +6071,47 @@ static void pipeMode(void) {
|
||||
srand(time(NULL));
|
||||
|
||||
/* Use non blocking I/O. */
|
||||
if (anetNonBlock(aneterr,context->fd) == ANET_ERR) {
|
||||
if (anetNonBlock(aneterr,fd) == ANET_ERR) {
|
||||
fprintf(stderr, "Can't set the socket in non blocking mode: %s\n",
|
||||
aneterr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
context->flags &= ~REDIS_BLOCK;
|
||||
|
||||
/* Transfer raw protocol and read replies from the server at the same
|
||||
* time. */
|
||||
while(!done) {
|
||||
int mask = AE_READABLE;
|
||||
|
||||
if (!eof || obuf_len != 0) mask |= AE_WRITABLE;
|
||||
mask = aeWait(context->fd,mask,1000);
|
||||
mask = aeWait(fd,mask,1000);
|
||||
|
||||
/* Handle the readable state: we can read replies from the server. */
|
||||
if (mask & AE_READABLE) {
|
||||
ssize_t nread;
|
||||
int read_error = 0;
|
||||
|
||||
/* Read from socket and feed the hiredis reader. */
|
||||
do {
|
||||
if (!read_error && redisBufferRead(context) == REDIS_ERR) {
|
||||
nread = read(fd,ibuf,sizeof(ibuf));
|
||||
if (nread == -1 && errno != EAGAIN && errno != EINTR) {
|
||||
fprintf(stderr, "Error reading from the server: %s\n",
|
||||
strerror(errno));
|
||||
read_error = 1;
|
||||
break;
|
||||
}
|
||||
if (nread > 0) {
|
||||
redisReaderFeed(reader,ibuf,nread);
|
||||
last_read_time = time(NULL);
|
||||
}
|
||||
} while(nread > 0);
|
||||
|
||||
reply = NULL;
|
||||
if (redisGetReply(context, (void **) &reply) == REDIS_ERR) {
|
||||
/* Consume replies. */
|
||||
do {
|
||||
if (redisReaderGetReply(reader,(void**)&reply) == REDIS_ERR) {
|
||||
fprintf(stderr, "Error reading replies from server\n");
|
||||
exit(1);
|
||||
}
|
||||
if (reply) {
|
||||
last_read_time = time(NULL);
|
||||
if (reply->type == REDIS_REPLY_ERROR) {
|
||||
fprintf(stderr,"%s\n", reply->str);
|
||||
errors++;
|
||||
@@ -6239,7 +6144,7 @@ static void pipeMode(void) {
|
||||
while(1) {
|
||||
/* Transfer current buffer to server. */
|
||||
if (obuf_len != 0) {
|
||||
ssize_t nwritten = writeConn(context,obuf+obuf_pos,obuf_len);
|
||||
ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);
|
||||
|
||||
if (nwritten == -1) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
@@ -6255,10 +6160,6 @@ static void pipeMode(void) {
|
||||
loop_nwritten += nwritten;
|
||||
if (obuf_len != 0) break; /* Can't accept more data. */
|
||||
}
|
||||
if (context->err) {
|
||||
fprintf(stderr, "Server I/O Error: %s\n", context->errstr);
|
||||
exit(1);
|
||||
}
|
||||
/* If buffer is empty, load from stdin. */
|
||||
if (obuf_len == 0 && !eof) {
|
||||
ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));
|
||||
@@ -6309,6 +6210,7 @@ static void pipeMode(void) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
redisReaderFree(reader);
|
||||
printf("errors: %lld, replies: %lld\n", errors, replies);
|
||||
if (errors)
|
||||
exit(1);
|
||||
@@ -6321,13 +6223,7 @@ static void pipeMode(void) {
|
||||
*--------------------------------------------------------------------------- */
|
||||
|
||||
redisReply *sendScan(unsigned long long *it) {
|
||||
redisReply *reply;
|
||||
|
||||
if (config.pattern)
|
||||
reply = redisCommand(context,"SCAN %llu MATCH %s",
|
||||
*it,config.pattern);
|
||||
else
|
||||
reply = redisCommand(context,"SCAN %llu",*it);
|
||||
redisReply *reply = redisCommand(context, "SCAN %llu", *it);
|
||||
|
||||
/* Handle any error conditions */
|
||||
if(reply == NULL) {
|
||||
@@ -6402,21 +6298,15 @@ void getKeySizes(redisReply *keys, typeinfo **types,
|
||||
if(!types[i] || (!types[i]->sizecmd && !memkeys))
|
||||
continue;
|
||||
|
||||
if (!memkeys) {
|
||||
const char* argv[] = {types[i]->sizecmd, keys->element[i]->str};
|
||||
size_t lens[] = {strlen(types[i]->sizecmd), keys->element[i]->len};
|
||||
redisAppendCommandArgv(context, 2, argv, lens);
|
||||
} else if (memkeys_samples==0) {
|
||||
const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str};
|
||||
size_t lens[] = {6, 5, keys->element[i]->len};
|
||||
redisAppendCommandArgv(context, 3, argv, lens);
|
||||
} else {
|
||||
sds samplesstr = sdsfromlonglong(memkeys_samples);
|
||||
const char* argv[] = {"MEMORY", "USAGE", keys->element[i]->str, "SAMPLES", samplesstr};
|
||||
size_t lens[] = {6, 5, keys->element[i]->len, 7, sdslen(samplesstr)};
|
||||
redisAppendCommandArgv(context, 5, argv, lens);
|
||||
sdsfree(samplesstr);
|
||||
}
|
||||
if (!memkeys)
|
||||
redisAppendCommand(context, "%s %s",
|
||||
types[i]->sizecmd, keys->element[i]->str);
|
||||
else if (memkeys_samples==0)
|
||||
redisAppendCommand(context, "%s %s %s",
|
||||
"MEMORY", "USAGE", keys->element[i]->str);
|
||||
else
|
||||
redisAppendCommand(context, "%s %s %s SAMPLES %u",
|
||||
"MEMORY", "USAGE", keys->element[i]->str, memkeys_samples);
|
||||
}
|
||||
|
||||
/* Retrieve sizes */
|
||||
@@ -6454,27 +6344,21 @@ static void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {
|
||||
|
||||
/* Pipeline OBJECT freq commands */
|
||||
for(i=0;i<keys->elements;i++) {
|
||||
const char* argv[] = {"OBJECT", "FREQ", keys->element[i]->str};
|
||||
size_t lens[] = {6, 4, keys->element[i]->len};
|
||||
redisAppendCommandArgv(context, 3, argv, lens);
|
||||
redisAppendCommand(context, "OBJECT freq %s", keys->element[i]->str);
|
||||
}
|
||||
|
||||
/* Retrieve freqs */
|
||||
for(i=0;i<keys->elements;i++) {
|
||||
if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
|
||||
sds keyname = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
|
||||
fprintf(stderr, "Error getting freq for key '%s' (%d: %s)\n",
|
||||
keyname, context->err, context->errstr);
|
||||
sdsfree(keyname);
|
||||
keys->element[i]->str, context->err, context->errstr);
|
||||
exit(1);
|
||||
} else if(reply->type != REDIS_REPLY_INTEGER) {
|
||||
if(reply->type == REDIS_REPLY_ERROR) {
|
||||
fprintf(stderr, "Error: %s\n", reply->str);
|
||||
exit(1);
|
||||
} else {
|
||||
sds keyname = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
|
||||
fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keyname);
|
||||
sdsfree(keyname);
|
||||
fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keys->element[i]->str);
|
||||
freqs[i] = 0;
|
||||
}
|
||||
} else {
|
||||
@@ -6545,10 +6429,10 @@ static void findHotKeys(void) {
|
||||
memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);
|
||||
}
|
||||
counters[k] = freqs[i];
|
||||
hotkeys[k] = sdscatrepr(sdsempty(), keys->element[i]->str, keys->element[i]->len);
|
||||
hotkeys[k] = sdsnew(keys->element[i]->str);
|
||||
printf(
|
||||
"[%05.2f%%] Hot key '%s' found so far with counter %llu\n",
|
||||
pct, hotkeys[k], freqs[i]);
|
||||
pct, keys->element[i]->str, freqs[i]);
|
||||
}
|
||||
|
||||
/* Sleep if we've been directed to do so */
|
||||
@@ -7111,7 +6995,7 @@ int main(int argc, char **argv) {
|
||||
/* Start interactive mode when no command is provided */
|
||||
if (argc == 0 && !config.eval) {
|
||||
/* Show the message of the day if we are interactive */
|
||||
if (config.output == OUTPUT_STANDARD && !config.disable_motd) {
|
||||
if (config.output == OUTPUT_STANDARD) {
|
||||
char *szMotd = fetchMOTD(1 /* cache */);
|
||||
if (szMotd != NULL) {
|
||||
printf("Message of the day:\n %s\n", szMotd);
|
||||
@@ -7137,3 +7021,4 @@ int main(int argc, char **argv) {
|
||||
return noninteractive(argc,convertToSds(argc,argv));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ extern "C" {
|
||||
#define REDIS_CLI_RCFILE_ENV "REDISCLI_RCFILE"
|
||||
#define REDIS_CLI_RCFILE_DEFAULT ".redisclirc"
|
||||
#define REDIS_CLI_AUTH_ENV "REDISCLI_AUTH"
|
||||
#define REDIS_CLI_CLUSTER_YES_ENV "REDISCLI_CLUSTER_YES"
|
||||
|
||||
#define CLUSTER_MANAGER_SLOTS 16384
|
||||
#define CLUSTER_MANAGER_MIGRATE_TIMEOUT 60000
|
||||
@@ -68,8 +67,6 @@ extern "C" {
|
||||
#define CLUSTER_MANAGER_CMD_FLAG_COLOR 1 << 8
|
||||
#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS 1 << 9
|
||||
#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS 1 << 10
|
||||
#define CLUSTER_MANAGER_CMD_FLAG_MASTERS_ONLY 1 << 11
|
||||
#define CLUSTER_MANAGER_CMD_FLAG_SLAVES_ONLY 1 << 12
|
||||
|
||||
#define CLUSTER_MANAGER_OPT_GETFRIENDS 1 << 0
|
||||
#define CLUSTER_MANAGER_OPT_COLD 1 << 1
|
||||
@@ -182,7 +179,6 @@ extern struct config {
|
||||
clusterManagerCommand cluster_manager_command;
|
||||
int no_auth_warning;
|
||||
int resp3;
|
||||
int disable_motd;
|
||||
} config;
|
||||
|
||||
struct clusterManager {
|
||||
|
||||
+241
-269
@@ -132,9 +132,9 @@ extern "C" {
|
||||
#define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */
|
||||
#define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */
|
||||
#define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */
|
||||
#define REDISMODULE_NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
|
||||
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */
|
||||
|
||||
|
||||
/* A special pointer that we can use between the core and the module to signal
|
||||
* field deletion, and that is impossible to be a valid pointer. */
|
||||
#define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)
|
||||
@@ -384,31 +384,6 @@ typedef struct RedisModuleLoadingProgressInfo {
|
||||
|
||||
typedef long long mstime_t;
|
||||
|
||||
/* Macro definitions specific to individual compilers */
|
||||
#ifndef REDISMODULE_ATTR_UNUSED
|
||||
# ifdef __GNUC__
|
||||
# define REDISMODULE_ATTR_UNUSED __attribute__((unused))
|
||||
# else
|
||||
# define REDISMODULE_ATTR_UNUSED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef REDISMODULE_ATTR_PRINTF
|
||||
# ifdef __GNUC__
|
||||
# define REDISMODULE_ATTR_PRINTF(idx,cnt) __attribute__((format(printf,idx,cnt)))
|
||||
# else
|
||||
# define REDISMODULE_ATTR_PRINTF(idx,cnt)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef REDISMODULE_ATTR_COMMON
|
||||
# if defined(__GNUC__) && !defined(__clang__)
|
||||
# define REDISMODULE_ATTR_COMMON __attribute__((__common__))
|
||||
# else
|
||||
# define REDISMODULE_ATTR_COMMON
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Incomplete structures for compiler checks but opaque access. */
|
||||
typedef struct RedisModuleCtx RedisModuleCtx;
|
||||
typedef struct RedisModuleKey RedisModuleKey;
|
||||
@@ -465,257 +440,256 @@ typedef struct RedisModuleTypeMethods {
|
||||
#define REDISMODULE_GET_API(name) \
|
||||
RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name))
|
||||
|
||||
/* Default API declaration prefix (not 'extern' for backwards compatibility) */
|
||||
#ifndef REDISMODULE_API
|
||||
#define REDISMODULE_API
|
||||
#define REDISMODULE_API_FUNC(x) (*x)
|
||||
|
||||
|
||||
void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes);
|
||||
void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size);
|
||||
char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver);
|
||||
int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name);
|
||||
int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);
|
||||
void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp);
|
||||
int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp);
|
||||
size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where);
|
||||
RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
|
||||
const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply);
|
||||
long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply);
|
||||
size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply);
|
||||
RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);
|
||||
#ifdef __GNUC__
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
#else
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
/* Default API declaration suffix (compiler attributes) */
|
||||
#ifndef REDISMODULE_ATTR
|
||||
#define REDISMODULE_ATTR REDISMODULE_ATTR_COMMON
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);
|
||||
const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d);
|
||||
void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx);
|
||||
const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str);
|
||||
char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen);
|
||||
mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ResetDataset)(int restart_aof, int async);
|
||||
unsigned long long REDISMODULE_API_FUNC(RedisModule_DbSize)(RedisModuleCtx *ctx);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_RandomKey)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...);
|
||||
int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...);
|
||||
int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos);
|
||||
unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetClientInfoById)(void *ci, uint64_t id);
|
||||
int REDISMODULE_API_FUNC(RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_AvoidReplicaTraffic)();
|
||||
void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes);
|
||||
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value);
|
||||
RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_IsIOError)(RedisModuleIO *io);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
|
||||
uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
|
||||
int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io);
|
||||
void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io);
|
||||
char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value);
|
||||
double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
|
||||
float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value);
|
||||
long double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt);
|
||||
#ifdef __GNUC__
|
||||
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
#else
|
||||
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
|
||||
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
REDISMODULE_API void * (*RedisModule_Alloc)(size_t bytes) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_Realloc)(void *ptr, size_t bytes) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_Free)(void *ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_Calloc)(size_t nmemb, size_t size) REDISMODULE_ATTR;
|
||||
REDISMODULE_API char * (*RedisModule_Strdup)(const char *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetApi)(const char *, void *) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_IsModuleNameBusy)(const char *name) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_WrongArity)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR;
|
||||
REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_ListPop)(RedisModuleKey *key, int where) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleCallReply * (*RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const char * (*RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CallReplyType)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API long long (*RedisModule_CallReplyInteger)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR_PRINTF(2,3) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithNullArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithEmptyArray)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithCString)(RedisModuleCtx *ctx, const char *buf) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithEmptyString)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithVerbatimString)(RedisModuleCtx *ctx, const char *buf, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithNull)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithLongDouble)(RedisModuleCtx *ctx, long double d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_AutoMemory)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const char * (*RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DeleteKey)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_UnlinkKey)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API char * (*RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen) REDISMODULE_ATTR;
|
||||
REDISMODULE_API mstime_t (*RedisModule_GetExpire)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ResetDataset)(int restart_aof, int async) REDISMODULE_ATTR;
|
||||
REDISMODULE_API unsigned long long (*RedisModule_DbSize)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_RandomKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ZsetRangeStop)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetRangeNext)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetRangePrev)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ZsetRangeEndReached)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_HashSet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_HashGet)(RedisModuleKey *key, int flags, ...) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR;
|
||||
REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)() REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleType * (*RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ModuleTypeReplaceValue)(RedisModuleKey *key, RedisModuleType *mt, void *new_value, void **old_value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleType * (*RedisModule_ModuleTypeGetType)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_ModuleTypeGetValue)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_IsIOError)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SetModuleOptions)(RedisModuleCtx *ctx, int options) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SignalModifiedKey)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API uint64_t (*RedisModule_LoadUnsigned)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int64_t (*RedisModule_LoadSigned)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_LoadString)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API char * (*RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveDouble)(RedisModuleIO *io, double value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API double (*RedisModule_LoadDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveFloat)(RedisModuleIO *io, float value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API float (*RedisModule_LoadFloat)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API long double (*RedisModule_LoadLongDouble)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
|
||||
REDISMODULE_API void (*RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4);
|
||||
REDISMODULE_API void (*RedisModule__Assert)(const char *estr, const char *file, int line) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_LatencyAddSample)(const char *event, mstime_t latency) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_HoldString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetContextFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromIO)(RedisModuleIO *io) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API long long (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_DigestEndSequence)(RedisModuleDigest *md) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleDict * (*RedisModule_CreateDict)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API uint64_t (*RedisModule_DictSize)(RedisModuleDict *d) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleDictIter * (*RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_DictIteratorStop)(RedisModuleDictIter *di) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleServerInfoData * (*RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const char * (*RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field) REDISMODULE_ATTR;
|
||||
REDISMODULE_API long long (*RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
|
||||
REDISMODULE_API unsigned long long (*RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
|
||||
REDISMODULE_API double (*RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)() REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) REDISMODULE_ATTR;
|
||||
|
||||
void REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);
|
||||
void REDISMODULE_API_FUNC(RedisModule_LatencyAddSample)(const char *event, mstime_t latency);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);
|
||||
void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);
|
||||
RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);
|
||||
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromIO)(RedisModuleIO *io);
|
||||
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetKeyNameFromModuleKey)(RedisModuleKey *key);
|
||||
long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);
|
||||
void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);
|
||||
void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);
|
||||
void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md);
|
||||
RedisModuleDict *REDISMODULE_API_FUNC(RedisModule_CreateDict)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeDict)(RedisModuleCtx *ctx, RedisModuleDict *d);
|
||||
uint64_t REDISMODULE_API_FUNC(RedisModule_DictSize)(RedisModuleDict *d);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictSetC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictReplaceC)(RedisModuleDict *d, void *key, size_t keylen, void *ptr);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictSet)(RedisModuleDict *d, RedisModuleString *key, void *ptr);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictReplace)(RedisModuleDict *d, RedisModuleString *key, void *ptr);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_DictGetC)(RedisModuleDict *d, void *key, size_t keylen, int *nokey);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_DictGet)(RedisModuleDict *d, RedisModuleString *key, int *nokey);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictDelC)(RedisModuleDict *d, void *key, size_t keylen, void *oldval);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictDel)(RedisModuleDict *d, RedisModuleString *key, void *oldval);
|
||||
RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStartC)(RedisModuleDict *d, const char *op, void *key, size_t keylen);
|
||||
RedisModuleDictIter *REDISMODULE_API_FUNC(RedisModule_DictIteratorStart)(RedisModuleDict *d, const char *op, RedisModuleString *key);
|
||||
void REDISMODULE_API_FUNC(RedisModule_DictIteratorStop)(RedisModuleDictIter *di);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseekC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictIteratorReseek)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_DictNextC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_DictPrevC)(RedisModuleDictIter *di, size_t *keylen, void **dataptr);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ctx, RedisModuleDictIter *di, void **dataptr);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen);
|
||||
int REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value);
|
||||
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value);
|
||||
RedisModuleServerInfoData *REDISMODULE_API_FUNC(RedisModule_GetServerInfo)(RedisModuleCtx *ctx, const char *section);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeServerInfo)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetField)(RedisModuleCtx *ctx, RedisModuleServerInfoData *data, const char* field);
|
||||
const char *REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldC)(RedisModuleServerInfoData *data, const char* field);
|
||||
long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldSigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);
|
||||
unsigned long long REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldUnsigned)(RedisModuleServerInfoData *data, const char* field, int *out_err);
|
||||
double REDISMODULE_API_FUNC(RedisModule_ServerInfoGetFieldDouble)(RedisModuleServerInfoData *data, const char* field, int *out_err);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SubscribeToServerEvent)(RedisModuleCtx *ctx, RedisModuleEvent event, RedisModuleEventCallback callback);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SetLRU)(RedisModuleKey *key, mstime_t lru_idle);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetLRU)(RedisModuleKey *key, mstime_t *lru_idle);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SetLFU)(RedisModuleKey *key, long long lfu_freq);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetLFU)(RedisModuleKey *key, long long *lfu_freq);
|
||||
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClientOnKeys)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key);
|
||||
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx);
|
||||
RedisModuleScanCursor *REDISMODULE_API_FUNC(RedisModule_ScanCursorCreate)();
|
||||
void REDISMODULE_API_FUNC(RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor);
|
||||
int REDISMODULE_API_FUNC(RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata);
|
||||
/* Experimental APIs */
|
||||
#ifdef REDISMODULE_EXPERIMENTAL_API
|
||||
#define REDISMODULE_EXPERIMENTAL_API_VERSION 3
|
||||
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)() REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) REDISMODULE_ATTR;
|
||||
REDISMODULE_API char ** (*RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeClusterNodesList)(char **ids) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleTimerID (*RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const char * (*RedisModule_GetMyClusterID)(void) REDISMODULE_ATTR;
|
||||
REDISMODULE_API size_t (*RedisModule_GetClusterSize)(void) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_GetRandomBytes)(unsigned char *dst, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_GetRandomHexChars)(char *dst, size_t len) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void * (*RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleCommandFilter * (*RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API const RedisModuleString * (*RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_KillForkChild)(int child_pid) REDISMODULE_ATTR;
|
||||
REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)() REDISMODULE_ATTR;
|
||||
REDISMODULE_API size_t (*RedisModule_MallocSize)(void* ptr) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleUser * (*RedisModule_CreateModuleUser)(const char *name) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_FreeModuleUser)(RedisModuleUser *user) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR;
|
||||
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms);
|
||||
int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata);
|
||||
int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx);
|
||||
RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc);
|
||||
RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);
|
||||
int REDISMODULE_API_FUNC(RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetNotifyKeyspaceEvents)();
|
||||
int REDISMODULE_API_FUNC(RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx);
|
||||
void REDISMODULE_API_FUNC(RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, char *target_id, uint8_t type, unsigned char *msg, uint32_t len);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetClusterNodeInfo)(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags);
|
||||
char **REDISMODULE_API_FUNC(RedisModule_GetClusterNodesList)(RedisModuleCtx *ctx, size_t *numnodes);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeClusterNodesList)(char **ids);
|
||||
RedisModuleTimerID REDISMODULE_API_FUNC(RedisModule_CreateTimer)(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data);
|
||||
int REDISMODULE_API_FUNC(RedisModule_StopTimer)(RedisModuleCtx *ctx, RedisModuleTimerID id, void **data);
|
||||
int REDISMODULE_API_FUNC(RedisModule_GetTimerInfo)(RedisModuleCtx *ctx, RedisModuleTimerID id, uint64_t *remaining, void **data);
|
||||
const char *REDISMODULE_API_FUNC(RedisModule_GetMyClusterID)(void);
|
||||
size_t REDISMODULE_API_FUNC(RedisModule_GetClusterSize)(void);
|
||||
void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t len);
|
||||
void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);
|
||||
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);
|
||||
void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);
|
||||
RedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb, int flags);
|
||||
int REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx);
|
||||
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
|
||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos);
|
||||
int REDISMODULE_API_FUNC(RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *user_data);
|
||||
int REDISMODULE_API_FUNC(RedisModule_ExitFromChild)(int retcode);
|
||||
int REDISMODULE_API_FUNC(RedisModule_KillForkChild)(int child_pid);
|
||||
float REDISMODULE_API_FUNC(RedisModule_GetUsedMemoryRatio)();
|
||||
size_t REDISMODULE_API_FUNC(RedisModule_MallocSize)(void* ptr);
|
||||
RedisModuleUser *REDISMODULE_API_FUNC(RedisModule_CreateModuleUser)(const char *name);
|
||||
void REDISMODULE_API_FUNC(RedisModule_FreeModuleUser)(RedisModuleUser *user);
|
||||
int REDISMODULE_API_FUNC(RedisModule_SetModuleUserACL)(RedisModuleUser *user, const char* acl);
|
||||
int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);
|
||||
int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id);
|
||||
void REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);
|
||||
#endif
|
||||
|
||||
#define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)
|
||||
|
||||
/* This is included inline inside each Redis module. */
|
||||
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) REDISMODULE_ATTR_UNUSED;
|
||||
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));
|
||||
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {
|
||||
void *getapifuncptr = ((void**)ctx)[0];
|
||||
RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;
|
||||
@@ -837,7 +811,6 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
||||
REDISMODULE_GET_API(LatencyAddSample);
|
||||
REDISMODULE_GET_API(StringAppendBuffer);
|
||||
REDISMODULE_GET_API(RetainString);
|
||||
REDISMODULE_GET_API(HoldString);
|
||||
REDISMODULE_GET_API(StringCompare);
|
||||
REDISMODULE_GET_API(GetContextFromIO);
|
||||
REDISMODULE_GET_API(GetKeyNameFromIO);
|
||||
@@ -904,7 +877,6 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
||||
REDISMODULE_GET_API(GetThreadSafeContext);
|
||||
REDISMODULE_GET_API(FreeThreadSafeContext);
|
||||
REDISMODULE_GET_API(ThreadSafeContextLock);
|
||||
REDISMODULE_GET_API(ThreadSafeContextTryLock);
|
||||
REDISMODULE_GET_API(ThreadSafeContextUnlock);
|
||||
REDISMODULE_GET_API(BlockClient);
|
||||
REDISMODULE_GET_API(UnblockClient);
|
||||
|
||||
+47
-135
@@ -377,11 +377,6 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
static client *fake = nullptr;
|
||||
|
||||
if (cserver.setDbReplicateDisable.find(dictid) != cserver.setDbReplicateDisable.end()) {
|
||||
if (strcasecmp(szFromObj(argv[0]), "ping") != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (dictid < 0)
|
||||
dictid = 0; // this can happen if we send a PING before any real operation
|
||||
|
||||
@@ -1171,7 +1166,6 @@ LError:
|
||||
* full resync. */
|
||||
void replconfCommand(client *c) {
|
||||
int j;
|
||||
bool fCapaCommand = false;
|
||||
|
||||
if ((c->argc % 2) == 0) {
|
||||
/* Number of arguments must be odd to make sure that every
|
||||
@@ -1182,7 +1176,6 @@ void replconfCommand(client *c) {
|
||||
|
||||
/* Process every option-value pair. */
|
||||
for (j = 1; j < c->argc; j+=2) {
|
||||
fCapaCommand = false;
|
||||
if (!strcasecmp((const char*)ptrFromObj(c->argv[j]),"listening-port")) {
|
||||
long port;
|
||||
|
||||
@@ -1207,8 +1200,6 @@ void replconfCommand(client *c) {
|
||||
c->slave_capa |= SLAVE_CAPA_PSYNC2;
|
||||
else if (!strcasecmp((const char*)ptrFromObj(c->argv[j+1]), "activeExpire"))
|
||||
c->slave_capa |= SLAVE_CAPA_ACTIVE_EXPIRE;
|
||||
|
||||
fCapaCommand = true;
|
||||
} else if (!strcasecmp((const char*)ptrFromObj(c->argv[j]),"ack")) {
|
||||
/* REPLCONF ACK is used by replica to inform the master the amount
|
||||
* of replication stream that it processed so far. It is an
|
||||
@@ -1251,16 +1242,7 @@ void replconfCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fCapaCommand) {
|
||||
sds reply = sdsnew("+OK");
|
||||
if (g_pserver->fActiveReplica)
|
||||
reply = sdscat(reply, " active-replica");
|
||||
reply = sdscat(reply, "\r\n");
|
||||
addReplySds(c, reply);
|
||||
} else {
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
|
||||
/* This function puts a replica in the online state, and should be called just
|
||||
@@ -1388,28 +1370,30 @@ void sendBulkToSlave(connection *conn) {
|
||||
* try to use sendfile system call if supported, unless tls is enabled.
|
||||
* fallback to normal read+write otherwise. */
|
||||
nwritten = 0;
|
||||
ssize_t buflen;
|
||||
char buf[PROTO_IOBUF_LEN];
|
||||
if (!nwritten) {
|
||||
ssize_t buflen;
|
||||
char buf[PROTO_IOBUF_LEN];
|
||||
|
||||
lseek(replica->repldbfd,replica->repldboff,SEEK_SET);
|
||||
buflen = read(replica->repldbfd,buf,PROTO_IOBUF_LEN);
|
||||
if (buflen <= 0) {
|
||||
serverLog(LL_WARNING,"Read error sending DB to replica: %s",
|
||||
(buflen == 0) ? "premature EOF" : strerror(errno));
|
||||
ul.unlock();
|
||||
aeLock.arm(nullptr);
|
||||
freeClient(replica);
|
||||
return;
|
||||
}
|
||||
if ((nwritten = connWrite(conn,buf,buflen)) == -1) {
|
||||
if (connGetState(conn) != CONN_STATE_CONNECTED) {
|
||||
serverLog(LL_WARNING,"Write error sending DB to replica: %s",
|
||||
connGetLastError(conn));
|
||||
lseek(replica->repldbfd,replica->repldboff,SEEK_SET);
|
||||
buflen = read(replica->repldbfd,buf,PROTO_IOBUF_LEN);
|
||||
if (buflen <= 0) {
|
||||
serverLog(LL_WARNING,"Read error sending DB to replica: %s",
|
||||
(buflen == 0) ? "premature EOF" : strerror(errno));
|
||||
ul.unlock();
|
||||
aeLock.arm(nullptr);
|
||||
freeClient(replica);
|
||||
return;
|
||||
}
|
||||
if ((nwritten = connWrite(conn,buf,buflen)) == -1) {
|
||||
if (connGetState(conn) != CONN_STATE_CONNECTED) {
|
||||
serverLog(LL_WARNING,"Write error sending DB to replica: %s",
|
||||
connGetLastError(conn));
|
||||
ul.unlock();
|
||||
aeLock.arm(nullptr);
|
||||
freeClient(replica);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
replica->repldboff += nwritten;
|
||||
@@ -1614,16 +1598,6 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type)
|
||||
} else if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
|
||||
struct redis_stat buf;
|
||||
|
||||
if (bgsaveerr != C_OK) {
|
||||
ul.unlock();
|
||||
if (FCorrectThread(replica))
|
||||
freeClient(replica);
|
||||
else
|
||||
freeClientAsync(replica);
|
||||
serverLog(LL_WARNING,"SYNC failed. BGSAVE child returned an error");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this was an RDB on disk save, we have to prepare to send
|
||||
* the RDB from disk to the replica socket. Otherwise if this was
|
||||
* already an RDB -> Slaves socket transfer, used in the case of
|
||||
@@ -1662,6 +1636,15 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type)
|
||||
replica->repl_put_online_on_ack = 1;
|
||||
replica->repl_ack_time = g_pserver->unixtime; /* Timeout otherwise. */
|
||||
} else {
|
||||
if (bgsaveerr != C_OK) {
|
||||
ul.unlock();
|
||||
if (FCorrectThread(replica))
|
||||
freeClient(replica);
|
||||
else
|
||||
freeClientAsync(replica);
|
||||
serverLog(LL_WARNING,"SYNC failed. BGSAVE child returned an error");
|
||||
continue;
|
||||
}
|
||||
if ((replica->repldbfd = open(g_pserver->rdb_filename,O_RDONLY)) == -1 ||
|
||||
redis_fstat(replica->repldbfd,&buf) == -1) {
|
||||
ul.unlock();
|
||||
@@ -2086,6 +2069,7 @@ void readSyncBulkPayload(connection *conn) {
|
||||
*
|
||||
* 2. Or when we are done reading from the socket to the RDB file, in
|
||||
* such case we want just to read the RDB file in memory. */
|
||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Flushing old data");
|
||||
|
||||
/* We need to stop any AOF rewriting child before flusing and parsing
|
||||
* the RDB, otherwise we'll create a copy-on-write disaster. */
|
||||
@@ -2105,10 +2089,7 @@ void readSyncBulkPayload(connection *conn) {
|
||||
* (Where disklessLoadMakeBackups left server.db empty) because we
|
||||
* want to execute all the auxiliary logic of emptyDb (Namely,
|
||||
* fire module events) */
|
||||
if (!fUpdate) {
|
||||
serverLog(LL_NOTICE, "MASTER <-> REPLICA sync: Flushing old data");
|
||||
emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
|
||||
}
|
||||
emptyDb(-1,empty_db_flags,replicationEmptyDbCallback);
|
||||
|
||||
/* Before loading the DB into memory we need to delete the readable
|
||||
* handler, otherwise it will get called recursively since
|
||||
@@ -2576,30 +2557,6 @@ int slaveTryPartialResynchronization(redisMaster *mi, connection *conn, int read
|
||||
return PSYNC_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void parseMasterCapa(redisMaster *mi, sds strcapa)
|
||||
{
|
||||
if (sdslen(strcapa) < 1 || strcapa[0] != '+')
|
||||
return;
|
||||
|
||||
char *szStart = strcapa + 1; // skip the +
|
||||
char *pchEnd = szStart;
|
||||
|
||||
mi->isActive = false;
|
||||
for (;;)
|
||||
{
|
||||
if (*pchEnd == ' ' || *pchEnd == '\0') {
|
||||
// Parse the word
|
||||
if (strncmp(szStart, "active-replica", pchEnd - szStart) == 0) {
|
||||
mi->isActive = true;
|
||||
}
|
||||
szStart = pchEnd + 1;
|
||||
}
|
||||
if (*pchEnd == '\0')
|
||||
break;
|
||||
++pchEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/* This handler fires when the non blocking connect was able to
|
||||
* establish a connection with the master. */
|
||||
void syncWithMaster(connection *conn) {
|
||||
@@ -2651,7 +2608,6 @@ void syncWithMaster(connection *conn) {
|
||||
* both. */
|
||||
if (err[0] != '+' &&
|
||||
strncmp(err,"-NOAUTH",7) != 0 &&
|
||||
strncmp(err,"-NOPERM",7) != 0 &&
|
||||
strncmp(err,"-ERR operation not permitted",28) != 0)
|
||||
{
|
||||
serverLog(LL_WARNING,"Error reply to PING from master: '%s'",err);
|
||||
@@ -2794,8 +2750,16 @@ void syncWithMaster(connection *conn) {
|
||||
*
|
||||
* The master will ignore capabilities it does not understand. */
|
||||
if (mi->repl_state == REPL_STATE_SEND_CAPA) {
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,conn,"REPLCONF",
|
||||
"capa","eof","capa","psync2","capa","activeExpire",NULL);
|
||||
if (g_pserver->fActiveReplica)
|
||||
{
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,conn,"REPLCONF",
|
||||
"capa","eof","capa","psync2","capa","activeExpire",NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = sendSynchronousCommand(mi, SYNC_CMD_WRITE,conn,"REPLCONF",
|
||||
"capa","eof","capa","psync2",NULL);
|
||||
}
|
||||
if (err) goto write_error;
|
||||
sdsfree(err);
|
||||
mi->repl_state = REPL_STATE_RECEIVE_CAPA;
|
||||
@@ -2810,8 +2774,6 @@ void syncWithMaster(connection *conn) {
|
||||
if (err[0] == '-') {
|
||||
serverLog(LL_NOTICE,"(Non critical) Master does not understand "
|
||||
"REPLCONF capa: %s", err);
|
||||
} else {
|
||||
parseMasterCapa(mi, err);
|
||||
}
|
||||
sdsfree(err);
|
||||
mi->repl_state = REPL_STATE_SEND_PSYNC;
|
||||
@@ -3056,9 +3018,6 @@ struct redisMaster *replicationAddMaster(char *ip, int port) {
|
||||
}
|
||||
disconnectAllBlockedClients(); /* Clients blocked in master, now replica. */
|
||||
|
||||
/* Update oom_score_adj */
|
||||
setOOMScoreAdj(-1);
|
||||
|
||||
/* Force our slaves to resync with us as well. They may hopefully be able
|
||||
* to partially resync with us, but we can notify the replid change. */
|
||||
if (!g_pserver->fActiveReplica)
|
||||
@@ -3155,9 +3114,6 @@ void replicationUnsetMaster(redisMaster *mi) {
|
||||
listDelNode(g_pserver->masters, ln);
|
||||
freeMasterInfo(mi);
|
||||
|
||||
/* Update oom_score_adj */
|
||||
setOOMScoreAdj(-1);
|
||||
|
||||
/* Fire the role change modules event. */
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
|
||||
REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,
|
||||
@@ -3195,46 +3151,15 @@ void replicaofCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->argc > 3) {
|
||||
if (c->argc != 4) {
|
||||
addReplyError(c, "Invalid arguments");
|
||||
return;
|
||||
}
|
||||
if (!strcasecmp((const char*)ptrFromObj(c->argv[1]),"remove")) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
bool fRemoved = false;
|
||||
long port;
|
||||
string2l(szFromObj(c->argv[3]), sdslen(szFromObj(c->argv[3])), &port);
|
||||
LRestart:
|
||||
listRewind(g_pserver->masters, &li);
|
||||
while ((ln = listNext(&li))) {
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(ln);
|
||||
if (mi->masterport != port)
|
||||
continue;
|
||||
if (sdscmp(szFromObj(c->argv[2]), mi->masterhost) == 0) {
|
||||
replicationUnsetMaster(mi);
|
||||
fRemoved = true;
|
||||
goto LRestart;
|
||||
}
|
||||
}
|
||||
if (!fRemoved) {
|
||||
addReplyError(c, "Master not found");
|
||||
return;
|
||||
} else if (listLength(g_pserver->masters) == 0) {
|
||||
goto LLogNoMaster;
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp((const char*)ptrFromObj(c->argv[1]),"no") &&
|
||||
/* The special host/port combination "NO" "ONE" turns the instance
|
||||
* into a master. Otherwise the new master address is set. */
|
||||
if (!strcasecmp((const char*)ptrFromObj(c->argv[1]),"no") &&
|
||||
!strcasecmp((const char*)ptrFromObj(c->argv[2]),"one")) {
|
||||
/* The special host/port combination "NO" "ONE" turns the instance
|
||||
* into a master. Otherwise the new master address is set. */
|
||||
if (listLength(g_pserver->masters)) {
|
||||
while (listLength(g_pserver->masters))
|
||||
{
|
||||
replicationUnsetMaster((redisMaster*)listNodeValue(listFirst(g_pserver->masters)));
|
||||
}
|
||||
LLogNoMaster:
|
||||
sds client = catClientInfoString(sdsempty(),c);
|
||||
serverLog(LL_NOTICE,"MASTER MODE enabled (user request from '%s')",
|
||||
client);
|
||||
@@ -3500,11 +3425,6 @@ void replicationResurrectCachedMaster(redisMaster *mi, connection *conn) {
|
||||
but this client was unlinked so its OK here */
|
||||
mi->master->iel = serverTL - g_pserver->rgthreadvar; // martial to this thread
|
||||
|
||||
/* Fire the master link modules event. */
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_MASTER_LINK_CHANGE,
|
||||
REDISMODULE_SUBEVENT_MASTER_LINK_UP,
|
||||
NULL);
|
||||
|
||||
/* Re-add to the list of clients. */
|
||||
linkClient(mi->master);
|
||||
serverAssert(connGetPrivateData(mi->master->conn) == mi->master);
|
||||
@@ -4056,20 +3976,12 @@ int FBrokenLinkToMaster()
|
||||
listNode *ln;
|
||||
listRewind(g_pserver->masters, &li);
|
||||
|
||||
int connected = 0;
|
||||
while ((ln = listNext(&li)))
|
||||
{
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(ln);
|
||||
if (mi->repl_state == REPL_STATE_CONNECTED)
|
||||
++connected;
|
||||
if (mi->repl_state != REPL_STATE_CONNECTED)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g_pserver->repl_quorum < 0) {
|
||||
return connected < (int)listLength(g_pserver->masters);
|
||||
} else {
|
||||
return connected < g_pserver->repl_quorum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4306,7 +4218,7 @@ void replicaReplayCommand(client *c)
|
||||
serverTL->current_client = current_clientSave;
|
||||
|
||||
// call() will not propogate this for us, so we do so here
|
||||
if (!s_pstate->FCancelled() && s_pstate->FFirst() && !cserver.multimaster_no_forward)
|
||||
if (!s_pstate->FCancelled() && s_pstate->FFirst())
|
||||
alsoPropagate(cserver.rreplayCommand,c->db->id,c->argv,c->argc,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
|
||||
s_pstate->Pop();
|
||||
|
||||
+3
-4
@@ -1078,7 +1078,6 @@ int sentinelTryConnectionSharing(sentinelRedisInstance *ri) {
|
||||
releaseInstanceLink(ri->link,NULL);
|
||||
ri->link = match->link;
|
||||
match->link->refcount++;
|
||||
dictReleaseIterator(di);
|
||||
return C_OK;
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
@@ -1956,7 +1955,7 @@ void sentinelFlushConfig(void) {
|
||||
int rewrite_status;
|
||||
|
||||
g_pserver->hz = CONFIG_DEFAULT_HZ;
|
||||
rewrite_status = rewriteConfig(cserver.configfile, 0);
|
||||
rewrite_status = rewriteConfig(cserver.configfile);
|
||||
g_pserver->hz = saved_hz;
|
||||
|
||||
if (rewrite_status == -1) goto werr;
|
||||
@@ -2220,8 +2219,8 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
|
||||
}
|
||||
|
||||
/* role:<role> */
|
||||
if (sdslen(l) >= 11 && !memcmp(l,"role:master",11)) role = SRI_MASTER;
|
||||
else if (sdslen(l) >= 10 && !memcmp(l,"role:slave",10)) role = SRI_SLAVE;
|
||||
if (!memcmp(l,"role:master",11)) role = SRI_MASTER;
|
||||
else if (!memcmp(l,"role:slave",10)) role = SRI_SLAVE;
|
||||
|
||||
if (role == SRI_SLAVE) {
|
||||
/* master_host:<host> */
|
||||
|
||||
+100
-245
@@ -63,9 +63,6 @@
|
||||
#include <mutex>
|
||||
#include "aelocker.h"
|
||||
#include "motd.h"
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
int g_fTestMode = false;
|
||||
const char *motd_url = "http://api.keydb.dev/motd/motd_server.txt";
|
||||
@@ -250,10 +247,6 @@ struct redisCommand redisCommandTable[] = {
|
||||
"read-only fast @keyspace",
|
||||
0,NULL,1,-1,1,0,0,0},
|
||||
|
||||
{"keydb.mexists",mexistsCommand,-2,
|
||||
"read-only fast @keyspace",
|
||||
0,NULL,1,-1,1,0,0,0},
|
||||
|
||||
{"setbit",setbitCommand,4,
|
||||
"write use-memory @bitmap",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
@@ -354,10 +347,6 @@ struct redisCommand redisCommandTable[] = {
|
||||
"write @list",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"lpos",lposCommand,-3,
|
||||
"read-only @list",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"lrem",lremCommand,4,
|
||||
"write @list",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
@@ -779,7 +768,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
"admin no-script ok-stale",
|
||||
0,NULL,0,0,0,0,0,0},
|
||||
|
||||
{"replicaof",replicaofCommand,-3,
|
||||
{"replicaof",replicaofCommand,3,
|
||||
"admin no-script ok-stale",
|
||||
0,NULL,0,0,0,0,0,0},
|
||||
|
||||
@@ -820,11 +809,11 @@ struct redisCommand redisCommandTable[] = {
|
||||
0,NULL,0,0,0,0,0,0},
|
||||
|
||||
{"watch",watchCommand,-2,
|
||||
"no-script fast ok-loading ok-stale @transaction",
|
||||
"no-script fast @transaction",
|
||||
0,NULL,1,-1,1,0,0,0},
|
||||
|
||||
{"unwatch",unwatchCommand,1,
|
||||
"no-script fast ok-loading ok-stale @transaction",
|
||||
"no-script fast @transaction",
|
||||
0,NULL,0,0,0,0,0,0},
|
||||
|
||||
{"cluster",clusterCommand,-2,
|
||||
@@ -1742,18 +1731,6 @@ void clientsCron(int iel) {
|
||||
freeClientsInAsyncFreeQueue(iel);
|
||||
}
|
||||
|
||||
bool expireOwnKeys()
|
||||
{
|
||||
if (iAmMaster()) {
|
||||
return true;
|
||||
} else if (!g_pserver->fActiveReplica && (listLength(g_pserver->masters) == 1)) {
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(listFirst(g_pserver->masters));
|
||||
if (mi->isActive)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This function handles 'background' operations we are required to do
|
||||
* incrementally in Redis databases, such as active key expiring, resizing,
|
||||
* rehashing. */
|
||||
@@ -1761,7 +1738,7 @@ void databasesCron(void) {
|
||||
/* Expire keys by random sampling. Not required for slaves
|
||||
* as master will synthesize DELs for us. */
|
||||
if (g_pserver->active_expire_enabled) {
|
||||
if (expireOwnKeys()) {
|
||||
if (iAmMaster()) {
|
||||
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
|
||||
} else {
|
||||
expireSlaveKeys();
|
||||
@@ -2204,7 +2181,6 @@ int serverCronLite(struct aeEventLoop *eventLoop, long long id, void *clientData
|
||||
}
|
||||
|
||||
extern int ProcessingEventsWhileBlocked;
|
||||
void processClients();
|
||||
|
||||
/* This function gets called every time Redis is entering the
|
||||
* main loop of the event driven library, that is, before to sleep
|
||||
@@ -2223,7 +2199,6 @@ void processClients();
|
||||
void beforeSleep(struct aeEventLoop *eventLoop) {
|
||||
UNUSED(eventLoop);
|
||||
int iel = ielFromEventLoop(eventLoop);
|
||||
processClients();
|
||||
|
||||
/* Handle precise timeouts of blocked clients. */
|
||||
handleBlockedClientsTimeout();
|
||||
@@ -2308,7 +2283,6 @@ void beforeSleepLite(struct aeEventLoop *eventLoop)
|
||||
|
||||
/* Try to process pending commands for clients that were just unblocked. */
|
||||
aeAcquireLock();
|
||||
processClients();
|
||||
if (listLength(g_pserver->rgthreadvar[iel].unblocked_clients)) {
|
||||
processUnblockedClients(iel);
|
||||
}
|
||||
@@ -2480,7 +2454,6 @@ void initMasterInfo(redisMaster *master)
|
||||
master->cached_master = NULL;
|
||||
master->master_initial_offset = -1;
|
||||
|
||||
master->isActive = false;
|
||||
|
||||
master->repl_state = REPL_STATE_NONE;
|
||||
master->repl_down_since = 0; /* Never connected, repl is down since EVER. */
|
||||
@@ -2571,10 +2544,6 @@ void initServerConfig(void) {
|
||||
for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++)
|
||||
cserver.client_obuf_limits[j] = clientBufferLimitsDefaults[j];
|
||||
|
||||
/* Linux OOM Score config */
|
||||
for (j = 0; j < CONFIG_OOM_COUNT; j++)
|
||||
g_pserver->oom_score_adj_values[j] = configOOMScoreAdjValuesDefaults[j];
|
||||
|
||||
/* Double constants initialization */
|
||||
R_Zero = 0.0;
|
||||
R_PosInf = 1.0/R_Zero;
|
||||
@@ -2656,7 +2625,7 @@ int restartServer(int flags, mstime_t delay) {
|
||||
/* Config rewriting. */
|
||||
if (flags & RESTART_SERVER_CONFIG_REWRITE &&
|
||||
cserver.configfile &&
|
||||
rewriteConfig(cserver.configfile, 0) == -1)
|
||||
rewriteConfig(cserver.configfile) == -1)
|
||||
{
|
||||
serverLog(LL_WARNING,"Can't restart: configuration rewrite process "
|
||||
"failed");
|
||||
@@ -2701,59 +2670,6 @@ int restartServer(int flags, mstime_t delay) {
|
||||
return C_ERR; /* Never reached. */
|
||||
}
|
||||
|
||||
static void readOOMScoreAdj(void) {
|
||||
#ifdef HAVE_PROC_OOM_SCORE_ADJ
|
||||
char buf[64];
|
||||
int fd = open("/proc/self/oom_score_adj", O_RDONLY);
|
||||
|
||||
if (fd < 0) return;
|
||||
if (read(fd, buf, sizeof(buf)) > 0)
|
||||
g_pserver->oom_score_adj_base = atoi(buf);
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function will configure the current process's oom_score_adj according
|
||||
* to user specified configuration. This is currently implemented on Linux
|
||||
* only.
|
||||
*
|
||||
* A process_class value of -1 implies OOM_CONFIG_MASTER or OOM_CONFIG_REPLICA,
|
||||
* depending on current role.
|
||||
*/
|
||||
int setOOMScoreAdj(int process_class) {
|
||||
|
||||
if (!g_pserver->oom_score_adj) return C_OK;
|
||||
if (process_class == -1)
|
||||
process_class = (listLength(g_pserver->masters) ? CONFIG_OOM_REPLICA : CONFIG_OOM_MASTER);
|
||||
|
||||
serverAssert(process_class >= 0 && process_class < CONFIG_OOM_COUNT);
|
||||
|
||||
#ifdef HAVE_PROC_OOM_SCORE_ADJ
|
||||
int fd;
|
||||
int val;
|
||||
char buf[64];
|
||||
|
||||
val = g_pserver->oom_score_adj_base + g_pserver->oom_score_adj_values[process_class];
|
||||
if (val > 1000) val = 1000;
|
||||
if (val < -1000) val = -1000;
|
||||
|
||||
snprintf(buf, sizeof(buf) - 1, "%d\n", val);
|
||||
|
||||
fd = open("/proc/self/oom_score_adj", O_WRONLY);
|
||||
if (fd < 0 || write(fd, buf, strlen(buf)) < 0) {
|
||||
serverLog(LOG_WARNING, "Unable to write oom_score_adj: %s", strerror(errno));
|
||||
if (fd != -1) close(fd);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return C_OK;
|
||||
#else
|
||||
/* Unsupported */
|
||||
return C_ERR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function will try to raise the max number of open files accordingly to
|
||||
* the configured max number of clients. It also reserves a number of file
|
||||
* descriptors (CONFIG_MIN_RESERVED_FDS) for extra operations of
|
||||
@@ -3144,8 +3060,7 @@ void initServer(void) {
|
||||
g_pserver->get_ack_from_slaves = 0;
|
||||
cserver.system_memory_size = zmalloc_get_memory_size();
|
||||
|
||||
if ((g_pserver->tls_port || g_pserver->tls_replication || g_pserver->tls_cluster)
|
||||
&& tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
|
||||
if (g_pserver->tls_port && tlsConfigure(&g_pserver->tls_ctx_config) == C_ERR) {
|
||||
serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
|
||||
exit(1);
|
||||
}
|
||||
@@ -3629,7 +3544,7 @@ void call(client *c, int flags) {
|
||||
!(flags & CMD_CALL_PROPAGATE_AOF))
|
||||
propagate_flags &= ~PROPAGATE_AOF;
|
||||
|
||||
if ((c->cmd->flags & CMD_SKIP_PROPOGATE) && g_pserver->fActiveReplica)
|
||||
if (c->cmd->flags & CMD_SKIP_PROPOGATE)
|
||||
propagate_flags &= ~PROPAGATE_REPL;
|
||||
|
||||
/* Call propagate() only if at least one of AOF / replication
|
||||
@@ -3706,38 +3621,6 @@ void call(client *c, int flags) {
|
||||
serverTL->fixed_time_expire--;
|
||||
}
|
||||
|
||||
/* Used when a command that is ready for execution needs to be rejected, due to
|
||||
* varios pre-execution checks. it returns the appropriate error to the client.
|
||||
* If there's a transaction is flags it as dirty, and if the command is EXEC,
|
||||
* it aborts the transaction.
|
||||
* Note: 'reply' is expected to end with \r\n */
|
||||
void rejectCommand(client *c, robj *reply) {
|
||||
flagTransaction(c);
|
||||
if (c->cmd && c->cmd->proc == execCommand) {
|
||||
execCommandAbort(c, szFromObj(reply));
|
||||
} else {
|
||||
/* using addReplyError* rather than addReply so that the error can be logged. */
|
||||
addReplyErrorObject(c, reply);
|
||||
}
|
||||
}
|
||||
|
||||
void rejectCommandFormat(client *c, const char *fmt, ...) {
|
||||
flagTransaction(c);
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
sds s = sdscatvprintf(sdsempty(),fmt,ap);
|
||||
va_end(ap);
|
||||
/* Make sure there are no newlines in the string, otherwise invalid protocol
|
||||
* is emitted (The args come from the user, they may contain any character). */
|
||||
sdsmapchars(s, "\r\n", " ", 2);
|
||||
if (c->cmd && c->cmd->proc == execCommand) {
|
||||
execCommandAbort(c, s);
|
||||
} else {
|
||||
addReplyErrorSds(c, s);
|
||||
}
|
||||
sdsfree(s);
|
||||
}
|
||||
|
||||
/* If this function gets called we already read a whole
|
||||
* command, arguments are in the client argv/argc fields.
|
||||
* processCommand() execute the command or prepare the
|
||||
@@ -3746,12 +3629,12 @@ void rejectCommandFormat(client *c, const char *fmt, ...) {
|
||||
* If C_OK is returned the client is still alive and valid and
|
||||
* other operations can be performed by the caller. Otherwise
|
||||
* if C_ERR is returned the client was destroyed (i.e. after QUIT). */
|
||||
int processCommand(client *c, int callFlags) {
|
||||
int processCommand(client *c, int callFlags, AeLocker &locker) {
|
||||
AssertCorrectThread(c);
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
if (moduleHasCommandFilters())
|
||||
{
|
||||
locker.arm(c);
|
||||
moduleCallCommandFilters(c);
|
||||
}
|
||||
|
||||
@@ -3769,30 +3652,23 @@ int processCommand(client *c, int callFlags) {
|
||||
* such as wrong arity, bad command name and so forth. */
|
||||
c->cmd = c->lastcmd = lookupCommand((sds)ptrFromObj(c->argv[0]));
|
||||
if (!c->cmd) {
|
||||
flagTransaction(c);
|
||||
sds args = sdsempty();
|
||||
int i;
|
||||
for (i=1; i < c->argc && sdslen(args) < 128; i++)
|
||||
args = sdscatprintf(args, "`%.*s`, ", 128-(int)sdslen(args), (char*)ptrFromObj(c->argv[i]));
|
||||
rejectCommandFormat(c,"unknown command `%s`, with args beginning with: %s",
|
||||
addReplyErrorFormat(c,"unknown command `%s`, with args beginning with: %s",
|
||||
(char*)ptrFromObj(c->argv[0]), args);
|
||||
sdsfree(args);
|
||||
return C_OK;
|
||||
} else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
|
||||
(c->argc < -c->cmd->arity)) {
|
||||
rejectCommandFormat(c,"wrong number of arguments for '%s' command",
|
||||
flagTransaction(c);
|
||||
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
|
||||
c->cmd->name);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
int is_write_command = (c->cmd->flags & CMD_WRITE) ||
|
||||
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_WRITE));
|
||||
int is_denyoom_command = (c->cmd->flags & CMD_DENYOOM) ||
|
||||
(c->cmd->proc == execCommand && (c->mstate.cmd_flags & CMD_DENYOOM));
|
||||
int is_denystale_command = !(c->cmd->flags & CMD_STALE) ||
|
||||
(c->cmd->proc == execCommand && (c->mstate.cmd_inv_flags & CMD_STALE));
|
||||
int is_denyloading_command = !(c->cmd->flags & CMD_LOADING) ||
|
||||
(c->cmd->proc == execCommand && (c->mstate.cmd_inv_flags & CMD_LOADING));
|
||||
|
||||
/* Check if the user is authenticated. This check is skipped in case
|
||||
* the default user is flagged as "nopass" and is active. */
|
||||
int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) ||
|
||||
@@ -3802,23 +3678,28 @@ int processCommand(client *c, int callFlags) {
|
||||
/* AUTH and HELLO and no auth modules are valid even in
|
||||
* non-authenticated state. */
|
||||
if (!(c->cmd->flags & CMD_NO_AUTH)) {
|
||||
rejectCommand(c,shared.noautherr);
|
||||
flagTransaction(c);
|
||||
addReply(c,shared.noautherr);
|
||||
return C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the user can run this command according to the current
|
||||
* ACLs. */
|
||||
if (c->puser && !(c->puser->flags & USER_FLAG_ALLCOMMANDS))
|
||||
locker.arm(c); // ACLs require the lock
|
||||
|
||||
int acl_keypos;
|
||||
int acl_retval = ACLCheckCommandPerm(c,&acl_keypos);
|
||||
if (acl_retval != ACL_OK) {
|
||||
addACLLogEntry(c,acl_retval,acl_keypos,NULL);
|
||||
flagTransaction(c);
|
||||
if (acl_retval == ACL_DENIED_CMD)
|
||||
rejectCommandFormat(c,
|
||||
addReplyErrorFormat(c,
|
||||
"-NOPERM this user has no permissions to run "
|
||||
"the '%s' command or its subcommand", c->cmd->name);
|
||||
else
|
||||
rejectCommandFormat(c,
|
||||
addReplyErrorFormat(c,
|
||||
"-NOPERM this user has no permissions to access "
|
||||
"one of the keys used as arguments");
|
||||
return C_OK;
|
||||
@@ -3835,6 +3716,7 @@ int processCommand(client *c, int callFlags) {
|
||||
!(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0 &&
|
||||
c->cmd->proc != execCommand))
|
||||
{
|
||||
locker.arm(c);
|
||||
int hashslot;
|
||||
int error_code;
|
||||
clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,
|
||||
@@ -3849,6 +3731,9 @@ int processCommand(client *c, int callFlags) {
|
||||
return C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!locker.isArmed())
|
||||
locker.arm(c);
|
||||
|
||||
incrementMvccTstamp();
|
||||
|
||||
@@ -3864,20 +3749,17 @@ int processCommand(client *c, int callFlags) {
|
||||
* into a replica, that may be the active client, to be freed. */
|
||||
if (serverTL->current_client == NULL) return C_ERR;
|
||||
|
||||
int reject_cmd_on_oom = is_denyoom_command;
|
||||
/* If client is in MULTI/EXEC context, queuing may consume an unlimited
|
||||
* amount of memory, so we want to stop that.
|
||||
* However, we never want to reject DISCARD, or even EXEC (unless it
|
||||
* contains denied commands, in which case is_denyoom_command is already
|
||||
* set. */
|
||||
if (c->flags & CLIENT_MULTI &&
|
||||
c->cmd->proc != execCommand &&
|
||||
c->cmd->proc != discardCommand) {
|
||||
reject_cmd_on_oom = 1;
|
||||
}
|
||||
|
||||
if (out_of_memory && reject_cmd_on_oom) {
|
||||
rejectCommand(c, shared.oomerr);
|
||||
/* It was impossible to free enough memory, and the command the client
|
||||
* is trying to execute is denied during OOM conditions or the client
|
||||
* is in MULTI/EXEC context? Error. */
|
||||
if (out_of_memory &&
|
||||
(c->cmd->flags & CMD_DENYOOM ||
|
||||
(c->flags & CLIENT_MULTI &&
|
||||
c->cmd->proc != execCommand &&
|
||||
c->cmd->proc != discardCommand)))
|
||||
{
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.oomerr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -3898,14 +3780,17 @@ int processCommand(client *c, int callFlags) {
|
||||
int deny_write_type = writeCommandsDeniedByDiskError();
|
||||
if (deny_write_type != DISK_ERROR_TYPE_NONE &&
|
||||
listLength(g_pserver->masters) == 0 &&
|
||||
(is_write_command ||c->cmd->proc == pingCommand))
|
||||
(c->cmd->flags & CMD_WRITE ||
|
||||
c->cmd->proc == pingCommand))
|
||||
{
|
||||
flagTransaction(c);
|
||||
if (deny_write_type == DISK_ERROR_TYPE_RDB)
|
||||
rejectCommand(c, shared.bgsaveerr);
|
||||
addReply(c, shared.bgsaveerr);
|
||||
else
|
||||
rejectCommandFormat(c,
|
||||
"-MISCONF Errors writing to the AOF file: %s",
|
||||
strerror(g_pserver->aof_last_write_errno));
|
||||
addReplySds(c,
|
||||
sdscatprintf(sdsempty(),
|
||||
"-MISCONF Errors writing to the AOF file: %s\r\n",
|
||||
strerror(g_pserver->aof_last_write_errno)));
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -3914,10 +3799,11 @@ int processCommand(client *c, int callFlags) {
|
||||
if (listLength(g_pserver->masters) == 0 &&
|
||||
g_pserver->repl_min_slaves_to_write &&
|
||||
g_pserver->repl_min_slaves_max_lag &&
|
||||
is_write_command &&
|
||||
c->cmd->flags & CMD_WRITE &&
|
||||
g_pserver->repl_good_slaves_count < g_pserver->repl_min_slaves_to_write)
|
||||
{
|
||||
rejectCommand(c, shared.noreplicaserr);
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.noreplicaserr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -3925,9 +3811,10 @@ int processCommand(client *c, int callFlags) {
|
||||
* accept write commands if this is our master. */
|
||||
if (listLength(g_pserver->masters) && g_pserver->repl_slave_ro &&
|
||||
!(c->flags & CLIENT_MASTER) &&
|
||||
is_write_command)
|
||||
c->cmd->flags & CMD_WRITE)
|
||||
{
|
||||
rejectCommand(c, shared.roslaveerr);
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.roslaveerr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -3939,7 +3826,7 @@ int processCommand(client *c, int callFlags) {
|
||||
c->cmd->proc != unsubscribeCommand &&
|
||||
c->cmd->proc != psubscribeCommand &&
|
||||
c->cmd->proc != punsubscribeCommand) {
|
||||
rejectCommandFormat(c,
|
||||
addReplyErrorFormat(c,
|
||||
"Can't execute '%s': only (P)SUBSCRIBE / "
|
||||
"(P)UNSUBSCRIBE / PING / QUIT are allowed in this context",
|
||||
c->cmd->name);
|
||||
@@ -3951,22 +3838,19 @@ int processCommand(client *c, int callFlags) {
|
||||
* link with master. */
|
||||
if (FBrokenLinkToMaster() &&
|
||||
g_pserver->repl_serve_stale_data == 0 &&
|
||||
is_denystale_command &&
|
||||
!(g_pserver->fActiveReplica && c->cmd->proc == syncCommand))
|
||||
!(c->cmd->flags & CMD_STALE)
|
||||
&& !(g_pserver->fActiveReplica && c->cmd->proc == syncCommand))
|
||||
{
|
||||
rejectCommand(c, shared.masterdownerr);
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.masterdownerr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Loading DB? Return an error if the command has not the
|
||||
* CMD_LOADING flag. */
|
||||
if (g_pserver->loading && is_denyloading_command) {
|
||||
/* Active Replicas can execute read only commands, and optionally write commands */
|
||||
if (!(g_pserver->loading == LOADING_REPLICATION && g_pserver->fActiveReplica && ((c->cmd->flags & CMD_READONLY) || g_pserver->fWriteDuringActiveLoad)))
|
||||
{
|
||||
rejectCommand(c, shared.loadingerr);
|
||||
return C_OK;
|
||||
}
|
||||
if (g_pserver->loading && !(c->cmd->flags & CMD_LOADING)) {
|
||||
addReply(c, shared.loadingerr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Lua script too slow? Only allow a limited number of commands.
|
||||
@@ -3980,9 +3864,8 @@ int processCommand(client *c, int callFlags) {
|
||||
c->cmd->proc != helloCommand &&
|
||||
c->cmd->proc != replconfCommand &&
|
||||
c->cmd->proc != multiCommand &&
|
||||
c->cmd->proc != execCommand &&
|
||||
c->cmd->proc != discardCommand &&
|
||||
c->cmd->proc != watchCommand &&
|
||||
c->cmd->proc != unwatchCommand &&
|
||||
!(c->cmd->proc == shutdownCommand &&
|
||||
c->argc == 2 &&
|
||||
tolower(((char*)ptrFromObj(c->argv[1]))[0]) == 'n') &&
|
||||
@@ -3990,7 +3873,8 @@ int processCommand(client *c, int callFlags) {
|
||||
c->argc == 2 &&
|
||||
tolower(((char*)ptrFromObj(c->argv[1]))[0]) == 'k'))
|
||||
{
|
||||
rejectCommand(c, shared.slowscripterr);
|
||||
flagTransaction(c);
|
||||
addReply(c, shared.slowscripterr);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -4043,15 +3927,6 @@ 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 (g_pserver->loading || g_pserver->sentinel_mode)
|
||||
flags = (flags & ~SHUTDOWN_SAVE) | SHUTDOWN_NOSAVE;
|
||||
|
||||
int save = flags & SHUTDOWN_SAVE;
|
||||
int nosave = flags & SHUTDOWN_NOSAVE;
|
||||
|
||||
@@ -4613,7 +4488,7 @@ sds genRedisInfoString(const char *section) {
|
||||
"aof_last_cow_size:%zu\r\n"
|
||||
"module_fork_in_progress:%d\r\n"
|
||||
"module_fork_last_cow_size:%zu\r\n",
|
||||
!!g_pserver->loading.load(std::memory_order_relaxed), /* Note: libraries expect 1 or 0 here so coerce our enum */
|
||||
g_pserver->loading.load(std::memory_order_relaxed),
|
||||
g_pserver->dirty,
|
||||
g_pserver->rdb_child_pid != -1,
|
||||
(intmax_t)g_pserver->lastsave,
|
||||
@@ -4719,9 +4594,7 @@ sds genRedisInfoString(const char *section) {
|
||||
"tracking_total_keys:%lld\r\n"
|
||||
"tracking_total_items:%llu\r\n"
|
||||
"tracking_total_prefixes:%lld\r\n"
|
||||
"unexpected_error_replies:%lld\r\n"
|
||||
"total_reads_processed:%lld\r\n"
|
||||
"total_writes_processed:%lld\r\n",
|
||||
"unexpected_error_replies:%lld\r\n",
|
||||
g_pserver->stat_numconnections,
|
||||
g_pserver->stat_numcommands,
|
||||
getInstantaneousMetric(STATS_METRIC_COMMAND),
|
||||
@@ -4752,9 +4625,7 @@ sds genRedisInfoString(const char *section) {
|
||||
(unsigned long long) trackingGetTotalKeys(),
|
||||
(unsigned long long) trackingGetTotalItems(),
|
||||
(unsigned long long) trackingGetTotalPrefixes(),
|
||||
g_pserver->stat_unexpected_error_replies,
|
||||
g_pserver->stat_total_reads_processed.load(std::memory_order_relaxed),
|
||||
g_pserver->stat_total_writes_processed.load(std::memory_order_relaxed));
|
||||
g_pserver->stat_unexpected_error_replies);
|
||||
}
|
||||
|
||||
/* Replication */
|
||||
@@ -4766,61 +4637,64 @@ sds genRedisInfoString(const char *section) {
|
||||
listLength(g_pserver->masters) == 0 ? "master"
|
||||
: g_pserver->fActiveReplica ? "active-replica" : "slave");
|
||||
if (listLength(g_pserver->masters)) {
|
||||
info = sdscatprintf(info, "master_global_link_status:%s\r\n",
|
||||
FBrokenLinkToMaster() ? "down" : "up");
|
||||
|
||||
int cmasters = 0;
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(g_pserver->masters, &li);
|
||||
bool fAllUp = true;
|
||||
while ((ln = listNext(&li))) {
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(ln);
|
||||
fAllUp = fAllUp && mi->repl_state == REPL_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
info = sdscatprintf(info, "master_global_link_status:%s\r\n",
|
||||
fAllUp ? "up" : "down");
|
||||
|
||||
int cmasters = 0;
|
||||
listRewind(g_pserver->masters, &li);
|
||||
while ((ln = listNext(&li)))
|
||||
{
|
||||
long long slave_repl_offset = 1;
|
||||
redisMaster *mi = (redisMaster*)listNodeValue(ln);
|
||||
info = sdscatprintf(info, "Master %d: \r\n", cmasters);
|
||||
++cmasters;
|
||||
|
||||
if (mi->master)
|
||||
slave_repl_offset = mi->master->reploff;
|
||||
else if (mi->cached_master)
|
||||
slave_repl_offset = mi->cached_master->reploff;
|
||||
|
||||
char master_prefix[128] = "";
|
||||
if (cmasters != 0) {
|
||||
snprintf(master_prefix, sizeof(master_prefix), "_%d", cmasters);
|
||||
}
|
||||
|
||||
info = sdscatprintf(info,
|
||||
"master%s_host:%s\r\n"
|
||||
"master%s_port:%d\r\n"
|
||||
"master%s_link_status:%s\r\n"
|
||||
"master%s_last_io_seconds_ago:%d\r\n"
|
||||
"master%s_sync_in_progress:%d\r\n"
|
||||
"master_host:%s\r\n"
|
||||
"master_port:%d\r\n"
|
||||
"master_link_status:%s\r\n"
|
||||
"master_last_io_seconds_ago:%d\r\n"
|
||||
"master_sync_in_progress:%d\r\n"
|
||||
"slave_repl_offset:%lld\r\n"
|
||||
,master_prefix, mi->masterhost,
|
||||
master_prefix, mi->masterport,
|
||||
master_prefix, (mi->repl_state == REPL_STATE_CONNECTED) ?
|
||||
,mi->masterhost,
|
||||
mi->masterport,
|
||||
(mi->repl_state == REPL_STATE_CONNECTED) ?
|
||||
"up" : "down",
|
||||
master_prefix, mi->master ?
|
||||
mi->master ?
|
||||
((int)(g_pserver->unixtime-mi->master->lastinteraction)) : -1,
|
||||
master_prefix, mi->repl_state == REPL_STATE_TRANSFER,
|
||||
mi->repl_state == REPL_STATE_TRANSFER,
|
||||
slave_repl_offset
|
||||
);
|
||||
|
||||
if (mi->repl_state == REPL_STATE_TRANSFER) {
|
||||
info = sdscatprintf(info,
|
||||
"master%s_sync_left_bytes:%lld\r\n"
|
||||
"master%s_sync_last_io_seconds_ago:%d\r\n"
|
||||
, master_prefix, (long long)
|
||||
"master_sync_left_bytes:%lld\r\n"
|
||||
"master_sync_last_io_seconds_ago:%d\r\n"
|
||||
, (long long)
|
||||
(mi->repl_transfer_size - mi->repl_transfer_read),
|
||||
master_prefix, (int)(g_pserver->unixtime-mi->repl_transfer_lastio)
|
||||
(int)(g_pserver->unixtime-mi->repl_transfer_lastio)
|
||||
);
|
||||
}
|
||||
|
||||
if (mi->repl_state != REPL_STATE_CONNECTED) {
|
||||
info = sdscatprintf(info,
|
||||
"master%s_link_down_since_seconds:%jd\r\n",
|
||||
master_prefix, (intmax_t)g_pserver->unixtime-mi->repl_down_since);
|
||||
"master_link_down_since_seconds:%jd\r\n",
|
||||
(intmax_t)g_pserver->unixtime-mi->repl_down_since);
|
||||
}
|
||||
++cmasters;
|
||||
}
|
||||
info = sdscatprintf(info,
|
||||
"slave_priority:%d\r\n"
|
||||
@@ -5039,7 +4913,7 @@ void linuxMemoryWarnings(void) {
|
||||
serverLog(LL_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
|
||||
}
|
||||
if (THPIsEnabled()) {
|
||||
serverLog(LL_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with KeyDB. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. KeyDB must be restarted after THP is disabled (set to 'madvise' or 'never').");
|
||||
serverLog(LL_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with KeyDB. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. KeyDB must be restarted after THP is disabled.");
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
@@ -5218,24 +5092,13 @@ void setupChildSignalHandlers(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* After fork, the child process will inherit the resources
|
||||
* of the parent process, e.g. fd(socket or flock) etc.
|
||||
* should close the resources not used by the child process, so that if the
|
||||
* parent restarts it can bind/lock despite the child possibly still running. */
|
||||
void closeClildUnusedResourceAfterFork() {
|
||||
closeListeningSockets(0);
|
||||
if (g_pserver->cluster_enabled && g_pserver->cluster_config_file_lock_fd != -1)
|
||||
close(g_pserver->cluster_config_file_lock_fd); /* don't care if this fails */
|
||||
}
|
||||
|
||||
int redisFork() {
|
||||
int childpid;
|
||||
long long start = ustime();
|
||||
if ((childpid = fork()) == 0) {
|
||||
/* Child */
|
||||
setOOMScoreAdj(CONFIG_OOM_BGCHILD);
|
||||
closeListeningSockets(0);
|
||||
setupChildSignalHandlers();
|
||||
closeClildUnusedResourceAfterFork();
|
||||
} else {
|
||||
/* Parent */
|
||||
g_pserver->stat_fork_time = ustime()-start;
|
||||
@@ -5283,7 +5146,6 @@ void loadDataFromDisk(void) {
|
||||
serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
|
||||
} else if (g_pserver->rdb_filename != NULL || g_pserver->rdb_s3bucketpath != NULL) {
|
||||
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
|
||||
errno = 0; /* Prevent a stale value from affecting error checking */
|
||||
if (rdbLoad(&rsi,RDBFLAGS_NONE) == C_OK) {
|
||||
serverLog(LL_NOTICE,"DB loaded from disk: %.3f seconds",
|
||||
(float)(ustime()-start)/1000000);
|
||||
@@ -5325,8 +5187,7 @@ void loadDataFromDisk(void) {
|
||||
void redisOutOfMemoryHandler(size_t allocation_size) {
|
||||
serverLog(LL_WARNING,"Out Of Memory allocating %zu bytes!",
|
||||
allocation_size);
|
||||
serverPanic("Redis aborting for OUT OF MEMORY. Allocating %zu bytes!",
|
||||
allocation_size);
|
||||
serverPanic("Redis aborting for OUT OF MEMORY");
|
||||
}
|
||||
|
||||
void fuzzOutOfMemoryHandler(size_t allocation_size) {
|
||||
@@ -5663,10 +5524,6 @@ int main(int argc, char **argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cserver.supervised = redisIsSupervised(cserver.supervised_mode);
|
||||
int background = cserver.daemonize && !cserver.supervised;
|
||||
if (background) daemonize();
|
||||
|
||||
serverLog(LL_WARNING, "oO0OoO0OoO0Oo KeyDB is starting oO0OoO0OoO0Oo");
|
||||
serverLog(LL_WARNING,
|
||||
"KeyDB version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started",
|
||||
@@ -5684,11 +5541,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
validateConfiguration();
|
||||
|
||||
cserver.supervised = redisIsSupervised(cserver.supervised_mode);
|
||||
int background = cserver.daemonize && !cserver.supervised;
|
||||
if (background) daemonize();
|
||||
|
||||
for (int iel = 0; iel < MAX_EVENT_LOOPS; ++iel)
|
||||
{
|
||||
initServerThread(g_pserver->rgthreadvar+iel, iel == IDX_EVENT_LOOP_MAIN);
|
||||
}
|
||||
readOOMScoreAdj();
|
||||
initServer();
|
||||
initNetworking(cserver.cthreads > 1 /* fReusePort */);
|
||||
|
||||
@@ -5747,10 +5607,6 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
InitServerLast();
|
||||
sentinelIsRunning();
|
||||
if (cserver.supervised_mode == SUPERVISED_SYSTEMD) {
|
||||
redisCommunicateSystemd("STATUS=Ready to accept connections\n");
|
||||
redisCommunicateSystemd("READY=1\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pserver->rdb_filename == nullptr)
|
||||
@@ -5774,7 +5630,6 @@ int main(int argc, char **argv) {
|
||||
aeReleaseLock(); //Finally we can dump the lock
|
||||
moduleReleaseGIL(true);
|
||||
|
||||
setOOMScoreAdj(-1);
|
||||
serverAssert(cserver.cthreads > 0 && cserver.cthreads <= MAX_EVENT_LOOPS);
|
||||
pthread_t rgthread[MAX_EVENT_LOOPS];
|
||||
for (int iel = 0; iel < cserver.cthreads; ++iel)
|
||||
|
||||
+280
-121
@@ -57,7 +57,6 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
@@ -95,7 +94,6 @@ typedef long long ustime_t; /* microsecond time type. */
|
||||
#include "semiorderedset.h"
|
||||
#include "connection.h" /* Connection abstraction */
|
||||
#include "serverassert.h"
|
||||
#include "expire.h"
|
||||
|
||||
#define REDISMODULE_CORE 1
|
||||
#include "redismodule.h" /* Redis modules API defines. */
|
||||
@@ -106,9 +104,6 @@ typedef long long ustime_t; /* microsecond time type. */
|
||||
#include "endianconv.h"
|
||||
#include "crc64.h"
|
||||
|
||||
#define LOADING_BOOT 1
|
||||
#define LOADING_REPLICATION 2
|
||||
|
||||
extern int g_fTestMode;
|
||||
|
||||
struct redisObject;
|
||||
@@ -171,24 +166,22 @@ class robj_sharedptr
|
||||
|
||||
public:
|
||||
robj_sharedptr()
|
||||
: m_ptr(nullptr)
|
||||
{}
|
||||
explicit robj_sharedptr(redisObject *ptr)
|
||||
: m_ptr(ptr)
|
||||
{
|
||||
if(m_ptr)
|
||||
: m_ptr(nullptr)
|
||||
{}
|
||||
robj_sharedptr(redisObject *ptr)
|
||||
: m_ptr(ptr)
|
||||
{
|
||||
incrRefCount(ptr);
|
||||
}
|
||||
}
|
||||
~robj_sharedptr()
|
||||
{
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
}
|
||||
robj_sharedptr(const robj_sharedptr& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
{
|
||||
if(m_ptr)
|
||||
incrRefCount(m_ptr);
|
||||
{
|
||||
m_ptr = other.m_ptr;
|
||||
incrRefCount(m_ptr);
|
||||
}
|
||||
|
||||
robj_sharedptr(robj_sharedptr&& other)
|
||||
@@ -199,19 +192,41 @@ public:
|
||||
|
||||
robj_sharedptr &operator=(const robj_sharedptr& other)
|
||||
{
|
||||
robj_sharedptr tmp(other);
|
||||
using std::swap;
|
||||
swap(m_ptr, tmp.m_ptr);
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
m_ptr = other.m_ptr;
|
||||
incrRefCount(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
robj_sharedptr &operator=(redisObject *ptr)
|
||||
{
|
||||
robj_sharedptr tmp(ptr);
|
||||
using std::swap;
|
||||
swap(m_ptr, tmp.m_ptr);
|
||||
if (m_ptr)
|
||||
decrRefCount(m_ptr);
|
||||
m_ptr = ptr;
|
||||
incrRefCount(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool operator==(const robj_sharedptr &other) const
|
||||
{
|
||||
return m_ptr == other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator==(const void *p) const
|
||||
{
|
||||
return m_ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(const robj_sharedptr &other) const
|
||||
{
|
||||
return m_ptr != other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator!=(const void *p) const
|
||||
{
|
||||
return m_ptr != p;
|
||||
}
|
||||
|
||||
redisObject* operator->() const
|
||||
{
|
||||
return m_ptr;
|
||||
@@ -222,7 +237,7 @@ public:
|
||||
return !m_ptr;
|
||||
}
|
||||
|
||||
explicit operator bool() const{
|
||||
operator bool() const{
|
||||
return !!m_ptr;
|
||||
}
|
||||
|
||||
@@ -232,39 +247,8 @@ public:
|
||||
}
|
||||
|
||||
redisObject *get() { return m_ptr; }
|
||||
const redisObject *get() const { return m_ptr; }
|
||||
};
|
||||
|
||||
inline bool operator==(const robj_sharedptr &lhs, const robj_sharedptr &rhs)
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
inline bool operator!=(const robj_sharedptr &lhs, const robj_sharedptr &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator==(const robj_sharedptr &lhs, const void *p)
|
||||
{
|
||||
return lhs.get() == p;
|
||||
}
|
||||
|
||||
inline bool operator==(const void *p, const robj_sharedptr &rhs)
|
||||
{
|
||||
return rhs == p;
|
||||
}
|
||||
|
||||
inline bool operator!=(const robj_sharedptr &lhs, const void *p)
|
||||
{
|
||||
return !(lhs == p);
|
||||
}
|
||||
|
||||
inline bool operator!=(const void *p, const robj_sharedptr &rhs)
|
||||
{
|
||||
return !(rhs == p);
|
||||
}
|
||||
|
||||
/* Error codes */
|
||||
#define C_OK 0
|
||||
#define C_ERR -1
|
||||
@@ -341,14 +325,6 @@ inline bool operator!=(const void *p, const robj_sharedptr &rhs)
|
||||
* in order to make sure of not over provisioning more than 128 fds. */
|
||||
#define CONFIG_FDSET_INCR (CONFIG_MIN_RESERVED_FDS+96)
|
||||
|
||||
/* OOM Score Adjustment classes. */
|
||||
#define CONFIG_OOM_MASTER 0
|
||||
#define CONFIG_OOM_REPLICA 1
|
||||
#define CONFIG_OOM_BGCHILD 2
|
||||
#define CONFIG_OOM_COUNT 3
|
||||
|
||||
extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
|
||||
|
||||
/* Hash table parameters */
|
||||
#define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */
|
||||
|
||||
@@ -563,11 +539,6 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
|
||||
#define REPL_DISKLESS_LOAD_WHEN_DB_EMPTY 1
|
||||
#define REPL_DISKLESS_LOAD_SWAPDB 2
|
||||
|
||||
/* TLS Client Authentication */
|
||||
#define TLS_CLIENT_AUTH_NO 0
|
||||
#define TLS_CLIENT_AUTH_YES 1
|
||||
#define TLS_CLIENT_AUTH_OPTIONAL 2
|
||||
|
||||
/* Sets operations codes */
|
||||
#define SET_OP_UNION 0
|
||||
#define SET_OP_DIFF 1
|
||||
@@ -636,7 +607,6 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
|
||||
#define NOTIFY_EVICTED (1<<9) /* e */
|
||||
#define NOTIFY_STREAM (1<<10) /* t */
|
||||
#define NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
|
||||
#define NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
|
||||
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM) /* A flag */
|
||||
|
||||
/* Get the first bind addr or NULL */
|
||||
@@ -815,16 +785,7 @@ typedef struct RedisModuleDigest {
|
||||
|
||||
#define MVCC_MS_SHIFT 20
|
||||
|
||||
// This struct will be allocated ahead of the ROBJ when needed
|
||||
struct redisObjectExtended {
|
||||
uint64_t mvcc_tstamp;
|
||||
};
|
||||
|
||||
typedef struct redisObject {
|
||||
protected:
|
||||
redisObject() {}
|
||||
|
||||
public:
|
||||
unsigned type:4;
|
||||
unsigned encoding:4;
|
||||
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
|
||||
@@ -833,6 +794,7 @@ public:
|
||||
private:
|
||||
mutable std::atomic<unsigned> refcount {0};
|
||||
public:
|
||||
uint64_t mvcc_tstamp;
|
||||
void *m_ptr;
|
||||
|
||||
inline bool FExpires() const { return refcount.load(std::memory_order_relaxed) >> 31; }
|
||||
@@ -843,18 +805,7 @@ public:
|
||||
void addref() const { refcount.fetch_add(1, std::memory_order_relaxed); }
|
||||
unsigned release() const { return refcount.fetch_sub(1, std::memory_order_seq_cst) & ~(1U << 31); }
|
||||
} robj;
|
||||
static_assert(sizeof(redisObject) == 16, "object size is critical, don't increase");
|
||||
|
||||
class redisObjectStack : public redisObjectExtended, public redisObject
|
||||
{
|
||||
public:
|
||||
redisObjectStack();
|
||||
};
|
||||
|
||||
uint64_t mvccFromObj(robj_roptr o);
|
||||
void setMvccTstamp(redisObject *o, uint64_t mvcc);
|
||||
void *allocPtrFromObj(robj_roptr o);
|
||||
robj *objFromAllocPtr(void *pv);
|
||||
static_assert(sizeof(redisObject) == 24, "object size is critical, don't increase");
|
||||
|
||||
__attribute__((always_inline)) inline const void *ptrFromObj(robj_roptr &o)
|
||||
{
|
||||
@@ -880,6 +831,243 @@ __attribute__((always_inline)) inline char *szFromObj(const robj *o)
|
||||
return (char*)ptrFromObj(o);
|
||||
}
|
||||
|
||||
class expireEntryFat
|
||||
{
|
||||
friend class expireEntry;
|
||||
public:
|
||||
struct subexpireEntry
|
||||
{
|
||||
long long when;
|
||||
std::unique_ptr<const char, void(*)(const char*)> spsubkey;
|
||||
|
||||
subexpireEntry(long long when, const char *subkey)
|
||||
: when(when), spsubkey(subkey, sdsfree)
|
||||
{}
|
||||
|
||||
bool operator<(long long when) const noexcept { return this->when < when; }
|
||||
bool operator<(const subexpireEntry &se) { return this->when < se.when; }
|
||||
};
|
||||
|
||||
private:
|
||||
sds m_keyPrimary;
|
||||
std::vector<subexpireEntry> m_vecexpireEntries; // Note a NULL for the sds portion means the expire is for the primary key
|
||||
|
||||
public:
|
||||
expireEntryFat(sds keyPrimary)
|
||||
: m_keyPrimary(keyPrimary)
|
||||
{}
|
||||
long long when() const noexcept { return m_vecexpireEntries.front().when; }
|
||||
const char *key() const noexcept { return m_keyPrimary; }
|
||||
|
||||
bool operator<(long long when) const noexcept { return this->when() < when; }
|
||||
|
||||
void expireSubKey(const char *szSubkey, long long when)
|
||||
{
|
||||
// First check if the subkey already has an expiration
|
||||
for (auto &entry : m_vecexpireEntries)
|
||||
{
|
||||
if (szSubkey != nullptr)
|
||||
{
|
||||
// if this is a subkey expiry then its not a match if the expireEntry is either for the
|
||||
// primary key or a different subkey
|
||||
if (entry.spsubkey == nullptr || sdscmp((sds)entry.spsubkey.get(), (sds)szSubkey) != 0)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entry.spsubkey != nullptr)
|
||||
continue;
|
||||
}
|
||||
m_vecexpireEntries.erase(m_vecexpireEntries.begin() + (&entry - m_vecexpireEntries.data()));
|
||||
break;
|
||||
}
|
||||
auto itrInsert = std::lower_bound(m_vecexpireEntries.begin(), m_vecexpireEntries.end(), when);
|
||||
const char *subkey = (szSubkey) ? sdsdup(szSubkey) : nullptr;
|
||||
m_vecexpireEntries.emplace(itrInsert, when, subkey);
|
||||
}
|
||||
|
||||
bool FEmpty() const noexcept { return m_vecexpireEntries.empty(); }
|
||||
const subexpireEntry &nextExpireEntry() const noexcept { return m_vecexpireEntries.front(); }
|
||||
void popfrontExpireEntry() { m_vecexpireEntries.erase(m_vecexpireEntries.begin()); }
|
||||
const subexpireEntry &operator[](size_t idx) { return m_vecexpireEntries[idx]; }
|
||||
size_t size() const noexcept { return m_vecexpireEntries.size(); }
|
||||
};
|
||||
|
||||
class expireEntry {
|
||||
union
|
||||
{
|
||||
sds m_key;
|
||||
expireEntryFat *m_pfatentry;
|
||||
} u;
|
||||
long long m_when; // LLONG_MIN means this is a fat entry and we should use the pointer
|
||||
|
||||
public:
|
||||
class iter
|
||||
{
|
||||
friend class expireEntry;
|
||||
expireEntry *m_pentry = nullptr;
|
||||
size_t m_idx = 0;
|
||||
|
||||
public:
|
||||
iter(expireEntry *pentry, size_t idx)
|
||||
: m_pentry(pentry), m_idx(idx)
|
||||
{}
|
||||
|
||||
iter &operator++() { ++m_idx; return *this; }
|
||||
|
||||
const char *subkey() const
|
||||
{
|
||||
if (m_pentry->FFat())
|
||||
return (*m_pentry->pfatentry())[m_idx].spsubkey.get();
|
||||
return nullptr;
|
||||
}
|
||||
long long when() const
|
||||
{
|
||||
if (m_pentry->FFat())
|
||||
return (*m_pentry->pfatentry())[m_idx].when;
|
||||
return m_pentry->when();
|
||||
}
|
||||
|
||||
bool operator!=(const iter &other)
|
||||
{
|
||||
return m_idx != other.m_idx;
|
||||
}
|
||||
|
||||
const iter &operator*() const { return *this; }
|
||||
};
|
||||
|
||||
expireEntry(sds key, const char *subkey, long long when)
|
||||
{
|
||||
if (subkey != nullptr)
|
||||
{
|
||||
m_when = LLONG_MIN;
|
||||
u.m_pfatentry = new (MALLOC_LOCAL) expireEntryFat(key);
|
||||
u.m_pfatentry->expireSubKey(subkey, when);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.m_key = key;
|
||||
m_when = when;
|
||||
}
|
||||
}
|
||||
|
||||
expireEntry(expireEntryFat *pfatentry)
|
||||
{
|
||||
u.m_pfatentry = pfatentry;
|
||||
m_when = LLONG_MIN;
|
||||
}
|
||||
|
||||
expireEntry(expireEntry &&e)
|
||||
{
|
||||
u.m_key = e.u.m_key;
|
||||
m_when = e.m_when;
|
||||
e.u.m_key = (char*)key(); // we do this so it can still be found in the set
|
||||
e.m_when = 0;
|
||||
}
|
||||
|
||||
~expireEntry()
|
||||
{
|
||||
if (FFat())
|
||||
delete u.m_pfatentry;
|
||||
}
|
||||
|
||||
void setKeyUnsafe(sds key)
|
||||
{
|
||||
if (FFat())
|
||||
u.m_pfatentry->m_keyPrimary = key;
|
||||
else
|
||||
u.m_key = key;
|
||||
}
|
||||
|
||||
inline bool FFat() const noexcept { return m_when == LLONG_MIN; }
|
||||
expireEntryFat *pfatentry() { assert(FFat()); return u.m_pfatentry; }
|
||||
|
||||
|
||||
bool operator==(const char *key) const noexcept
|
||||
{
|
||||
return this->key() == key;
|
||||
}
|
||||
|
||||
bool operator<(const expireEntry &e) const noexcept
|
||||
{
|
||||
return when() < e.when();
|
||||
}
|
||||
bool operator<(long long when) const noexcept
|
||||
{
|
||||
return this->when() < when;
|
||||
}
|
||||
|
||||
const char *key() const noexcept
|
||||
{
|
||||
if (FFat())
|
||||
return u.m_pfatentry->key();
|
||||
return u.m_key;
|
||||
}
|
||||
long long when() const noexcept
|
||||
{
|
||||
if (FFat())
|
||||
return u.m_pfatentry->when();
|
||||
return m_when;
|
||||
}
|
||||
|
||||
void update(const char *subkey, long long when)
|
||||
{
|
||||
if (!FFat())
|
||||
{
|
||||
if (subkey == nullptr)
|
||||
{
|
||||
m_when = when;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have to upgrade to a fat entry
|
||||
long long whenT = m_when;
|
||||
sds keyPrimary = u.m_key;
|
||||
m_when = LLONG_MIN;
|
||||
u.m_pfatentry = new (MALLOC_LOCAL) expireEntryFat(keyPrimary);
|
||||
u.m_pfatentry->expireSubKey(nullptr, whenT);
|
||||
// at this point we're fat so fall through
|
||||
}
|
||||
}
|
||||
u.m_pfatentry->expireSubKey(subkey, when);
|
||||
}
|
||||
|
||||
iter begin() { return iter(this, 0); }
|
||||
iter end()
|
||||
{
|
||||
if (FFat())
|
||||
return iter(this, u.m_pfatentry->size());
|
||||
return iter(this, 1);
|
||||
}
|
||||
|
||||
void erase(iter &itr)
|
||||
{
|
||||
if (!FFat())
|
||||
throw -1; // assert
|
||||
pfatentry()->m_vecexpireEntries.erase(
|
||||
pfatentry()->m_vecexpireEntries.begin() + itr.m_idx);
|
||||
}
|
||||
|
||||
bool FGetPrimaryExpire(long long *pwhen)
|
||||
{
|
||||
*pwhen = -1;
|
||||
for (auto itr : *this)
|
||||
{
|
||||
if (itr.subkey() == nullptr)
|
||||
{
|
||||
*pwhen = itr.when();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
explicit operator const char*() const noexcept { return key(); }
|
||||
explicit operator long long() const noexcept { return when(); }
|
||||
};
|
||||
typedef semiorderedset<expireEntry, const char *, true /*expireEntry can be memmoved*/> expireset;
|
||||
|
||||
/* The a string name for an object's type as listed above
|
||||
* Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines,
|
||||
* and Module types have their registered name returned. */
|
||||
@@ -948,9 +1136,6 @@ typedef struct multiState {
|
||||
int cmd_flags; /* The accumulated command flags OR-ed together.
|
||||
So if at least a command has a given flag, it
|
||||
will be set in this field. */
|
||||
int cmd_inv_flags; /* Same as cmd_flags, OR-ing the ~flags. so that it
|
||||
is possible to know if all the commands have a
|
||||
certain flag. */
|
||||
int minreplicas; /* MINREPLICAS for synchronous replication */
|
||||
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
|
||||
} multiState;
|
||||
@@ -1326,9 +1511,6 @@ typedef struct redisTLSContextConfig {
|
||||
char *ciphers;
|
||||
char *ciphersuites;
|
||||
int prefer_server_ciphers;
|
||||
int session_caching;
|
||||
int session_cache_size;
|
||||
int session_cache_timeout;
|
||||
} redisTLSContextConfig;
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
@@ -1373,7 +1555,6 @@ struct redisServerThreadVars {
|
||||
char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */
|
||||
long unsigned commandsExecuted = 0;
|
||||
bool fRetrySetAofEvent = false;
|
||||
std::vector<client*> vecclientsProcess;
|
||||
};
|
||||
|
||||
struct redisMaster {
|
||||
@@ -1389,7 +1570,6 @@ struct redisMaster {
|
||||
char master_replid[CONFIG_RUN_ID_SIZE+1]; /* Master PSYNC runid. */
|
||||
long long master_initial_offset; /* Master PSYNC offset. */
|
||||
|
||||
bool isActive = false;
|
||||
int repl_state; /* Replication status if the instance is a replica */
|
||||
off_t repl_transfer_size; /* Size of RDB to read from master during sync. */
|
||||
off_t repl_transfer_read; /* Amount of RDB read from master during sync. */
|
||||
@@ -1457,9 +1637,6 @@ struct redisServerConst {
|
||||
unsigned char uuid[UUID_BINARY_LEN]; /* This server's UUID - populated on boot */
|
||||
bool fUsePro = false;
|
||||
int thread_min_client_threshold = 50;
|
||||
int multimaster_no_forward;
|
||||
|
||||
std::unordered_set<int> setDbReplicateDisable;
|
||||
};
|
||||
|
||||
struct redisServer {
|
||||
@@ -1555,10 +1732,6 @@ struct redisServer {
|
||||
size_t stat_module_cow_bytes; /* Copy on write bytes during module fork. */
|
||||
uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */
|
||||
long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */
|
||||
long long stat_io_reads_processed; /* Number of read events processed by IO / Main threads */
|
||||
long long stat_io_writes_processed; /* Number of write events processed by IO / Main threads */
|
||||
std::atomic<long long> stat_total_reads_processed; /* Total number of read events processed */
|
||||
std::atomic<long long> stat_total_writes_processed; /* Total number of write events processed */
|
||||
/* The following two are used to track instantaneous metrics, like
|
||||
* number of operations per second, network traffic. */
|
||||
struct {
|
||||
@@ -1686,7 +1859,6 @@ struct redisServer {
|
||||
int repl_syncio_timeout; /* Timeout for synchronous I/O calls */
|
||||
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
|
||||
int repl_serve_stale_data; /* Serve stale data when link is down? */
|
||||
int repl_quorum; /* For multimaster what do we consider a quorum? -1 means all master must be online */
|
||||
int repl_slave_ro; /* Slave is read only? */
|
||||
int repl_slave_ignore_maxmemory; /* If true slaves do not evict. */
|
||||
int slave_priority; /* Reported in INFO and used by Sentinel. */
|
||||
@@ -1708,9 +1880,6 @@ struct redisServer {
|
||||
int lfu_log_factor; /* LFU logarithmic counter factor. */
|
||||
int lfu_decay_time; /* LFU counter decay factor. */
|
||||
long long proto_max_bulk_len; /* Protocol bulk length maximum size. */
|
||||
int oom_score_adj_base; /* Base oom_score_adj value, as observed on startup */
|
||||
int oom_score_adj_values[CONFIG_OOM_COUNT]; /* Linux oom_score_adj configuration */
|
||||
int oom_score_adj; /* If true, oom_score_adj is managed */
|
||||
/* Blocked clients */
|
||||
unsigned int blocked_clients; /* # of clients executing a blocking cmd.*/
|
||||
unsigned int blocked_clients_by_type[BLOCKED_NUM];
|
||||
@@ -1768,7 +1937,6 @@ struct redisServer {
|
||||
REDISMODULE_CLUSTER_FLAG_*. */
|
||||
int cluster_allow_reads_when_down; /* Are reads allowed when the cluster
|
||||
is down? */
|
||||
int cluster_config_file_lock_fd; /* cluster config fd, will be flock */
|
||||
/* Scripting */
|
||||
lua_State *lua; /* The Lua interpreter. We use just one for all clients */
|
||||
client *lua_caller = nullptr; /* The client running EVAL right now, or NULL */
|
||||
@@ -1811,7 +1979,6 @@ struct redisServer {
|
||||
int watchdog_period; /* Software watchdog period in ms. 0 = off */
|
||||
|
||||
int fActiveReplica; /* Can this replica also be a master? */
|
||||
int fWriteDuringActiveLoad; /* Can this active-replica write during an RDB load? */
|
||||
|
||||
// Format:
|
||||
// Lower 20 bits: a counter incrementing for each command executed in the same millisecond
|
||||
@@ -1960,7 +2127,6 @@ void moduleBlockedClientTimedOut(client *c);
|
||||
void moduleBlockedClientPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||
size_t moduleCount(void);
|
||||
void moduleAcquireGIL(int fServerThread);
|
||||
int moduleTryAcquireGIL(bool fServerThread);
|
||||
void moduleReleaseGIL(int fServerThread);
|
||||
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
|
||||
void moduleCallCommandFilters(client *c);
|
||||
@@ -2024,8 +2190,6 @@ void addReplyBulkLongLong(client *c, long long ll);
|
||||
void addReply(client *c, robj_roptr obj);
|
||||
void addReplySds(client *c, sds s);
|
||||
void addReplyBulkSds(client *c, sds s);
|
||||
void addReplyErrorObject(client *c, robj *err);
|
||||
void addReplyErrorSds(client *c, sds err);
|
||||
void addReplyError(client *c, const char *err);
|
||||
void addReplyStatus(client *c, const char *status);
|
||||
void addReplyDouble(client *c, double d);
|
||||
@@ -2145,7 +2309,6 @@ void touchWatchedKey(redisDb *db, robj *key);
|
||||
void touchWatchedKeysOnFlush(int dbid);
|
||||
void discardTransaction(client *c);
|
||||
void flagTransaction(client *c);
|
||||
void execCommandAbort(client *c, sds error);
|
||||
void execCommandPropagateMulti(client *c);
|
||||
void execCommandPropagateExec(client *c);
|
||||
|
||||
@@ -2382,7 +2545,7 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
|
||||
size_t freeMemoryGetNotCountedMemory();
|
||||
int freeMemoryIfNeeded(void);
|
||||
int freeMemoryIfNeededAndSafe(void);
|
||||
int processCommand(client *c, int callFlags);
|
||||
int processCommand(client *c, int callFlags, class AeLocker &locker);
|
||||
void setupSignalHandlers(void);
|
||||
struct redisCommand *lookupCommand(sds name);
|
||||
struct redisCommand *lookupCommandByCString(const char *s);
|
||||
@@ -2421,7 +2584,6 @@ const char *evictPolicyToString(void);
|
||||
struct redisMemOverhead *getMemoryOverheadData(void);
|
||||
void freeMemoryOverheadData(struct redisMemOverhead *mh);
|
||||
void checkChildrenDone(void);
|
||||
int setOOMScoreAdj(int process_class);
|
||||
|
||||
#define RESTART_SERVER_NONE 0
|
||||
#define RESTART_SERVER_GRACEFULLY (1<<0) /* Do proper shutdown. */
|
||||
@@ -2485,7 +2647,7 @@ void appendServerSaveParams(time_t seconds, int changes);
|
||||
void resetServerSaveParams(void);
|
||||
struct rewriteConfigState; /* Forward declaration to export API. */
|
||||
void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);
|
||||
int rewriteConfig(char *path, int force_all);
|
||||
int rewriteConfig(char *path);
|
||||
void initConfigValues();
|
||||
|
||||
/* db.c -- Keyspace access API */
|
||||
@@ -2499,7 +2661,6 @@ expireEntry *getExpire(redisDb *db, robj_roptr key);
|
||||
void setExpire(client *c, redisDb *db, robj *key, robj *subkey, long long when);
|
||||
void setExpire(client *c, redisDb *db, robj *key, expireEntry &&entry);
|
||||
robj_roptr lookupKeyRead(redisDb *db, robj *key);
|
||||
int checkAlreadyExpired(long long when);
|
||||
robj *lookupKeyWrite(redisDb *db, robj *key);
|
||||
robj_roptr lookupKeyReadOrReply(client *c, robj *key, robj *reply);
|
||||
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
|
||||
@@ -2649,7 +2810,6 @@ void getCommand(client *c);
|
||||
void delCommand(client *c);
|
||||
void unlinkCommand(client *c);
|
||||
void existsCommand(client *c);
|
||||
void mexistsCommand(client *c);
|
||||
void setbitCommand(client *c);
|
||||
void getbitCommand(client *c);
|
||||
void bitfieldCommand(client *c);
|
||||
@@ -2707,7 +2867,6 @@ void flushdbCommand(client *c);
|
||||
void flushallCommand(client *c);
|
||||
void sortCommand(client *c);
|
||||
void lremCommand(client *c);
|
||||
void lposCommand(client *c);
|
||||
void rpoplpushCommand(client *c);
|
||||
void infoCommand(client *c);
|
||||
void mgetCommand(client *c);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __STORAGE_H__
|
||||
#define __STORAGE_H__
|
||||
|
||||
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 48 // Note: also defined in object.c - should always match
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
+2
-4
@@ -532,7 +532,7 @@ void hsetCommand(client *c) {
|
||||
robj *o;
|
||||
|
||||
if ((c->argc % 2) == 1) {
|
||||
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",c->cmd->name);
|
||||
addReplyError(c,"wrong number of arguments for HMSET");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -772,9 +772,7 @@ void genericHgetallCommand(client *c, int flags) {
|
||||
hashTypeIterator *hi;
|
||||
int length, count = 0;
|
||||
|
||||
robj *emptyResp = (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) ?
|
||||
shared.emptymap[c->resp] : shared.emptyarray;
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyResp))
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymap[c->resp]))
|
||||
== nullptr || checkType(c,o,OBJ_HASH)) return;
|
||||
|
||||
/* We return a map if the user requested keys and values, like in the
|
||||
|
||||
+1
-119
@@ -331,7 +331,7 @@ void lindexCommand(client *c) {
|
||||
addReplyBulk(c,value);
|
||||
decrRefCount(value);
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
addReplyNull(c,shared.nullbulk);
|
||||
}
|
||||
} else {
|
||||
serverPanic("Unknown list encoding");
|
||||
@@ -487,124 +487,6 @@ void ltrimCommand(client *c) {
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
|
||||
/* LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]
|
||||
*
|
||||
* The "rank" is the position of the match, so if it is 1, the first match
|
||||
* is returned, if it is 2 the second match is returned and so forth.
|
||||
* It is 1 by default. If negative has the same meaning but the search is
|
||||
* performed starting from the end of the list.
|
||||
*
|
||||
* If COUNT is given, instead of returning the single element, a list of
|
||||
* all the matching elements up to "num-matches" are returned. COUNT can
|
||||
* be combiled with RANK in order to returning only the element starting
|
||||
* from the Nth. If COUNT is zero, all the matching elements are returned.
|
||||
*
|
||||
* MAXLEN tells the command to scan a max of len elements. If zero (the
|
||||
* default), all the elements in the list are scanned if needed.
|
||||
*
|
||||
* The returned elements indexes are always referring to what LINDEX
|
||||
* would return. So first element from head is 0, and so forth. */
|
||||
void lposCommand(client *c) {
|
||||
robj *ele;
|
||||
robj_roptr o;
|
||||
ele = c->argv[2];
|
||||
int direction = LIST_TAIL;
|
||||
long rank = 1, count = -1, maxlen = 0; /* Count -1: option not given. */
|
||||
|
||||
/* Parse the optional arguments. */
|
||||
for (int j = 3; j < c->argc; j++) {
|
||||
char *opt = szFromObj(c->argv[j]);
|
||||
int moreargs = (c->argc-1)-j;
|
||||
|
||||
if (!strcasecmp(opt,"RANK") && moreargs) {
|
||||
j++;
|
||||
if (getLongFromObjectOrReply(c, c->argv[j], &rank, NULL) != C_OK)
|
||||
return;
|
||||
if (rank == 0) {
|
||||
addReplyError(c,"RANK can't be zero: use 1 to start from "
|
||||
"the first match, 2 from the second, ...");
|
||||
return;
|
||||
}
|
||||
} else if (!strcasecmp(opt,"COUNT") && moreargs) {
|
||||
j++;
|
||||
if (getLongFromObjectOrReply(c, c->argv[j], &count, NULL) != C_OK)
|
||||
return;
|
||||
if (count < 0) {
|
||||
addReplyError(c,"COUNT can't be negative");
|
||||
return;
|
||||
}
|
||||
} else if (!strcasecmp(opt,"MAXLEN") && moreargs) {
|
||||
j++;
|
||||
if (getLongFromObjectOrReply(c, c->argv[j], &maxlen, NULL) != C_OK)
|
||||
return;
|
||||
if (maxlen < 0) {
|
||||
addReplyError(c,"MAXLEN can't be negative");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
addReply(c,shared.syntaxerr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* A negative rank means start from the tail. */
|
||||
if (rank < 0) {
|
||||
rank = -rank;
|
||||
direction = LIST_HEAD;
|
||||
}
|
||||
|
||||
/* We return NULL or an empty array if there is no such key (or
|
||||
* if we find no matches, depending on the presence of the COUNT option. */
|
||||
if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) {
|
||||
if (count != -1)
|
||||
addReply(c,shared.emptyarray);
|
||||
else
|
||||
addReply(c,shared.null[c->resp]);
|
||||
return;
|
||||
}
|
||||
if (checkType(c,o,OBJ_LIST)) return;
|
||||
|
||||
/* If we got the COUNT option, prepare to emit an array. */
|
||||
void *arraylenptr = NULL;
|
||||
if (count != -1) arraylenptr = addReplyDeferredLen(c);
|
||||
|
||||
/* Seek the element. */
|
||||
listTypeIterator *li;
|
||||
li = listTypeInitIterator(o,direction == LIST_HEAD ? -1 : 0,direction);
|
||||
listTypeEntry entry;
|
||||
long llen = listTypeLength(o);
|
||||
long index = 0, matches = 0, matchindex = -1, arraylen = 0;
|
||||
while (listTypeNext(li,&entry) && (maxlen == 0 || index < maxlen)) {
|
||||
if (listTypeEqual(&entry,ele)) {
|
||||
matches++;
|
||||
matchindex = (direction == LIST_TAIL) ? index : llen - index - 1;
|
||||
if (matches >= rank) {
|
||||
if (arraylenptr) {
|
||||
arraylen++;
|
||||
addReplyLongLong(c,matchindex);
|
||||
if (count && matches-rank+1 >= count) break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
matchindex = -1; /* Remember if we exit the loop without a match. */
|
||||
}
|
||||
listTypeReleaseIterator(li);
|
||||
|
||||
/* Reply to the client. Note that arraylenptr is not NULL only if
|
||||
* the COUNT option was selected. */
|
||||
if (arraylenptr != NULL) {
|
||||
setDeferredArrayLen(c,arraylenptr,arraylen);
|
||||
} else {
|
||||
if (matchindex != -1)
|
||||
addReplyLongLong(c,matchindex);
|
||||
else
|
||||
addReply(c,shared.null[c->resp]);
|
||||
}
|
||||
}
|
||||
|
||||
void lremCommand(client *c) {
|
||||
robj *subject, *obj;
|
||||
obj = c->argv[3];
|
||||
|
||||
+1
-6
@@ -43,7 +43,6 @@
|
||||
void streamFreeCG(streamCG *cg);
|
||||
void streamFreeNACK(streamNACK *na);
|
||||
size_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer);
|
||||
bool FInReplicaReplay();
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* Low level stream encoding: a radix tree of listpacks.
|
||||
@@ -839,9 +838,6 @@ void streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupnam
|
||||
*
|
||||
* Note that JUSTID is useful in order to avoid that XCLAIM will do
|
||||
* useless work in the replica side, trying to fetch the stream item. */
|
||||
if (FInReplicaReplay())
|
||||
return;
|
||||
|
||||
robj *argv[14];
|
||||
argv[0] = createStringObject("XCLAIM",6);
|
||||
argv[1] = key;
|
||||
@@ -1851,7 +1847,6 @@ NULL
|
||||
o = createStreamObject();
|
||||
dbAdd(c->db,c->argv[2],o);
|
||||
s = (stream*)ptrFromObj(o);
|
||||
signalModifiedKey(c,c->db,c->argv[2]);
|
||||
}
|
||||
|
||||
streamCG *cg = streamCreateCG(s,grpname,sdslen(grpname),&id);
|
||||
@@ -1896,7 +1891,7 @@ NULL
|
||||
g_pserver->dirty++;
|
||||
notifyKeyspaceEvent(NOTIFY_STREAM,"xgroup-delconsumer",
|
||||
c->argv[2],c->db->id);
|
||||
} else if (c->argc == 2 && !strcasecmp(opt,"HELP")) {
|
||||
} else if (!strcasecmp(opt,"HELP")) {
|
||||
addReplyHelp(c, help);
|
||||
} else {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
|
||||
+22
-39
@@ -35,8 +35,8 @@
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int checkStringLength(client *c, long long size) {
|
||||
if (!(c->flags & CLIENT_MASTER) && size > g_pserver->proto_max_bulk_len) {
|
||||
addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
|
||||
if (size > 512*1024*1024) {
|
||||
addReplyError(c,"string exceeds maximum allowed size (512MB)");
|
||||
return C_ERR;
|
||||
}
|
||||
return C_OK;
|
||||
@@ -505,12 +505,6 @@ void stralgoLCS(client *c) {
|
||||
const char *b = NULL;
|
||||
int getlen = 0, getidx = 0, withmatchlen = 0;
|
||||
robj_roptr obja, objb;
|
||||
uint32_t arraylen = 0; /* Number of ranges emitted in the array. */
|
||||
uint32_t alen, blen, *lcs, idx;
|
||||
int computelcs;
|
||||
sds result = NULL; /* Resulting LCS string. */
|
||||
void *arraylenptr = NULL; /* Deffered length of the array for IDX. */
|
||||
uint32_t arange_start, arange_end, brange_start = 0, brange_end = 0;
|
||||
|
||||
for (j = 2; j < (uint32_t)c->argc; j++) {
|
||||
char *opt = szFromObj(c->argv[j]);
|
||||
@@ -524,13 +518,13 @@ void stralgoLCS(client *c) {
|
||||
withmatchlen = 1;
|
||||
} else if (!strcasecmp(opt,"MINMATCHLEN") && moreargs) {
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[j+1],&minmatchlen,NULL)
|
||||
!= C_OK) goto cleanup;
|
||||
!= C_OK) return;
|
||||
if (minmatchlen < 0) minmatchlen = 0;
|
||||
j++;
|
||||
} else if (!strcasecmp(opt,"STRINGS") && moreargs > 1) {
|
||||
if (a != NULL) {
|
||||
addReplyError(c,"Either use STRINGS or KEYS");
|
||||
goto cleanup;
|
||||
return;
|
||||
}
|
||||
a = szFromObj(c->argv[j+1]);
|
||||
b = szFromObj(c->argv[j+2]);
|
||||
@@ -538,21 +532,10 @@ void stralgoLCS(client *c) {
|
||||
} else if (!strcasecmp(opt,"KEYS") && moreargs > 1) {
|
||||
if (a != NULL) {
|
||||
addReplyError(c,"Either use STRINGS or KEYS");
|
||||
goto cleanup;
|
||||
return;
|
||||
}
|
||||
obja = lookupKeyRead(c->db,c->argv[j+1]);
|
||||
objb = lookupKeyRead(c->db,c->argv[j+2]);
|
||||
if ((obja && obja->type != OBJ_STRING) ||
|
||||
(objb && objb->type != OBJ_STRING))
|
||||
{
|
||||
addReplyError(c,
|
||||
"The specified keys must contain string values");
|
||||
/* Don't cleanup the objects, we need to do that
|
||||
* only after callign getDecodedObject(). */
|
||||
obja = NULL;
|
||||
objb = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
obja = obja ? getDecodedObject(obja) : createStringObject("",0);
|
||||
objb = objb ? getDecodedObject(objb) : createStringObject("",0);
|
||||
a = szFromObj(obja);
|
||||
@@ -560,7 +543,7 @@ void stralgoLCS(client *c) {
|
||||
j += 2;
|
||||
} else {
|
||||
addReply(c,shared.syntaxerr);
|
||||
goto cleanup;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,23 +551,23 @@ void stralgoLCS(client *c) {
|
||||
if (a == NULL) {
|
||||
addReplyError(c,"Please specify two strings: "
|
||||
"STRINGS or KEYS options are mandatory");
|
||||
goto cleanup;
|
||||
return;
|
||||
} else if (getlen && getidx) {
|
||||
addReplyError(c,
|
||||
"If you want both the length and indexes, please "
|
||||
"just use IDX.");
|
||||
goto cleanup;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute the LCS using the vanilla dynamic programming technique of
|
||||
* building a table of LCS(x,y) substrings. */
|
||||
alen = sdslen(a);
|
||||
blen = sdslen(b);
|
||||
uint32_t alen = sdslen(a);
|
||||
uint32_t blen = sdslen(b);
|
||||
|
||||
/* Setup an uint32_t array to store at LCS[i,j] the length of the
|
||||
* LCS A0..i-1, B0..j-1. Note that we have a linear array here, so
|
||||
* we index it as LCS[j+(blen+1)*j] */
|
||||
lcs = (uint32_t*)zmalloc((alen+1)*(blen+1)*sizeof(uint32_t));
|
||||
uint32_t *lcs = (uint32_t*)zmalloc((alen+1)*(blen+1)*sizeof(uint32_t));
|
||||
#define LCS(A,B) lcs[(B)+((A)*(blen+1))]
|
||||
|
||||
/* Start building the LCS table. */
|
||||
@@ -613,18 +596,20 @@ void stralgoLCS(client *c) {
|
||||
|
||||
/* Store the actual LCS string in "result" if needed. We create
|
||||
* it backward, but the length is already known, we store it into idx. */
|
||||
idx = LCS(alen,blen);
|
||||
arange_start = alen; /* alen signals that values are not set. */
|
||||
arange_end = 0;
|
||||
brange_start = 0;
|
||||
brange_end = 0;
|
||||
uint32_t idx = LCS(alen,blen);
|
||||
sds result = NULL; /* Resulting LCS string. */
|
||||
void *arraylenptr = NULL; /* Deffered length of the array for IDX. */
|
||||
uint32_t arange_start = alen, /* alen signals that values are not set. */
|
||||
arange_end = 0,
|
||||
brange_start = 0,
|
||||
brange_end = 0;
|
||||
|
||||
/* Do we need to compute the actual LCS string? Allocate it in that case. */
|
||||
computelcs = getidx || !getlen;
|
||||
int computelcs = getidx || !getlen;
|
||||
if (computelcs) result = sdsnewlen(SDS_NOINIT,idx);
|
||||
|
||||
/* Start with a deferred array if we have to emit the ranges. */
|
||||
arraylen = 0; /* Number of ranges emitted in the array. */
|
||||
uint32_t arraylen = 0; /* Number of ranges emitted in the array. */
|
||||
if (getidx) {
|
||||
addReplyMapLen(c,2);
|
||||
addReplyBulkCString(c,"matches");
|
||||
@@ -706,12 +691,10 @@ void stralgoLCS(client *c) {
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
sdsfree(result);
|
||||
zfree(lcs);
|
||||
|
||||
cleanup:
|
||||
if (obja) decrRefCount(obja);
|
||||
if (objb) decrRefCount(objb);
|
||||
sdsfree(result);
|
||||
zfree(lcs);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,6 @@ void handleBlockedClientsTimeout(void) {
|
||||
raxRemove(g_pserver->clients_timeout_table,ri.key,ri.key_len,NULL);
|
||||
raxSeek(&ri,"^",NULL,0);
|
||||
}
|
||||
raxStop(&ri);
|
||||
}
|
||||
|
||||
/* Get a timeout value from an object and store it into 'timeout'.
|
||||
|
||||
+11
-51
@@ -150,6 +150,8 @@ void tlsInit(void) {
|
||||
serverLog(LL_WARNING, "OpenSSL: Failed to seed random number generator.");
|
||||
}
|
||||
|
||||
/* Server configuration */
|
||||
g_pserver->tls_auth_clients = 1; /* Secure by default */
|
||||
tlsInitThread();
|
||||
}
|
||||
|
||||
@@ -191,15 +193,6 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
|
||||
SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
|
||||
#endif
|
||||
|
||||
if (ctx_config->session_caching) {
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
|
||||
SSL_CTX_sess_set_cache_size(ctx, ctx_config->session_cache_size);
|
||||
SSL_CTX_set_timeout(ctx, ctx_config->session_cache_timeout);
|
||||
SSL_CTX_set_session_id_context(ctx, (const unsigned char*) "KeyDB", 5);
|
||||
} else {
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
}
|
||||
|
||||
protocols = parseProtocolsConfig(ctx_config->protocols);
|
||||
if (protocols == -1) goto error;
|
||||
|
||||
@@ -233,7 +226,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
#endif
|
||||
|
||||
if (SSL_CTX_use_certificate_chain_file(ctx, ctx_config->cert_file) <= 0) {
|
||||
if (SSL_CTX_use_certificate_file(ctx, ctx_config->cert_file, SSL_FILETYPE_PEM) <= 0) {
|
||||
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
|
||||
serverLog(LL_WARNING, "Failed to load certificate: %s: %s", ctx_config->cert_file, errbuf);
|
||||
goto error;
|
||||
@@ -360,44 +353,15 @@ void connTLSMarshalThread(connection *c) {
|
||||
conn->el = serverTL->el;
|
||||
}
|
||||
|
||||
/* Fetch the latest OpenSSL error and store it in the connection */
|
||||
static void updateTLSError(tls_connection *conn) {
|
||||
conn->c.last_errno = 0;
|
||||
if (conn->ssl_error) zfree(conn->ssl_error);
|
||||
conn->ssl_error = (char*)zmalloc(512);
|
||||
ERR_error_string_n(ERR_get_error(), conn->ssl_error, 512);
|
||||
}
|
||||
|
||||
/* Create a new TLS connection that is already associated with
|
||||
* an accepted underlying file descriptor.
|
||||
*
|
||||
* The socket is not ready for I/O until connAccept() was called and
|
||||
* invoked the connection-level accept handler.
|
||||
*
|
||||
* Callers should use connGetState() and verify the created connection
|
||||
* is not in an error state.
|
||||
*/
|
||||
connection *connCreateAcceptedTLS(int fd, int require_auth) {
|
||||
tls_connection *conn = (tls_connection *) connCreateTLS();
|
||||
conn->c.fd = fd;
|
||||
conn->c.state = CONN_STATE_ACCEPTING;
|
||||
|
||||
if (!conn->ssl) {
|
||||
updateTLSError(conn);
|
||||
conn->c.state = CONN_STATE_ERROR;
|
||||
return (connection *) conn;
|
||||
}
|
||||
|
||||
switch (require_auth) {
|
||||
case TLS_CLIENT_AUTH_NO:
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
||||
break;
|
||||
case TLS_CLIENT_AUTH_OPTIONAL:
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, NULL);
|
||||
break;
|
||||
default: /* TLS_CLIENT_AUTH_YES, also fall-secure */
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
||||
break;
|
||||
if (!require_auth) {
|
||||
/* We still verify certificates if provided, but don't require them.
|
||||
*/
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, NULL);
|
||||
}
|
||||
|
||||
SSL_set_fd(conn->ssl, conn->c.fd);
|
||||
@@ -430,7 +394,10 @@ static int handleSSLReturnCode(tls_connection *conn, int ret_value, WantIOType *
|
||||
break;
|
||||
default:
|
||||
/* Error! */
|
||||
updateTLSError(conn);
|
||||
conn->c.last_errno = 0;
|
||||
if (conn->ssl_error) zfree(conn->ssl_error);
|
||||
conn->ssl_error = (char*)zmalloc(512);
|
||||
ERR_error_string_n(ERR_get_error(), conn->ssl_error, 512);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -917,12 +884,6 @@ exit:
|
||||
return nread;
|
||||
}
|
||||
|
||||
static int connTLSGetType(connection *conn_) {
|
||||
(void) conn_;
|
||||
|
||||
return CONN_TYPE_TLS;
|
||||
}
|
||||
|
||||
ConnectionType CT_TLS = {
|
||||
tlsEventHandler,
|
||||
connTLSConnect,
|
||||
@@ -938,7 +899,6 @@ ConnectionType CT_TLS = {
|
||||
connTLSSyncRead,
|
||||
connTLSSyncReadLine,
|
||||
connTLSMarshalThread,
|
||||
connTLSGetType
|
||||
};
|
||||
|
||||
int tlsHasPendingData() {
|
||||
|
||||
+13
-16
@@ -102,7 +102,7 @@ void disableTracking(client *c) {
|
||||
/* Set the client 'c' to track the prefix 'prefix'. If the client 'c' is
|
||||
* already registered for the specified prefix, no operation is performed. */
|
||||
void enableBcastTrackingForPrefix(client *c, const char *prefix, size_t plen) {
|
||||
bcastState *bs = (bcastState*)raxFind(PrefixTable,(unsigned char*)prefix,plen);
|
||||
bcastState *bs = (bcastState*)raxFind(PrefixTable,(unsigned char*)prefix,sdslen(prefix));
|
||||
/* If this is the first client subscribing to such prefix, create
|
||||
* the prefix in the table. */
|
||||
if (bs == raxNotFound) {
|
||||
@@ -198,13 +198,12 @@ void trackingRememberKeys(client *c) {
|
||||
*
|
||||
* In case the 'proto' argument is non zero, the function will assume that
|
||||
* 'keyname' points to a buffer of 'keylen' bytes already expressed in the
|
||||
* form of Redis RESP protocol. This is used for:
|
||||
* - In BCAST mode, to send an array of invalidated keys to all
|
||||
* applicable clients
|
||||
* - Following a flush command, to send a single RESP NULL to indicate
|
||||
* that all keys are now invalid. */
|
||||
void sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {
|
||||
* form of Redis RESP protocol, representing an array of keys to send
|
||||
* to the client as value of the invalidation. This is used in BCAST mode
|
||||
* in order to optimized the implementation to use less CPU time. */
|
||||
void sendTrackingMessage(client *c, const char *keyname, size_t keylen, int proto) {
|
||||
std::unique_lock<fastlock> ul(c->lock);
|
||||
serverAssert(c->lock.fOwnLock());
|
||||
|
||||
int using_redirection = 0;
|
||||
if (c->client_tracking_redirection) {
|
||||
@@ -348,19 +347,17 @@ void trackingInvalidateKey(client *c, robj *keyobj) {
|
||||
trackingInvalidateKeyRaw(c,szFromObj(keyobj),sdslen(szFromObj(keyobj)),1);
|
||||
}
|
||||
|
||||
/* This function is called when one or all the Redis databases are
|
||||
* flushed (dbid == -1 in case of FLUSHALL). Caching keys are not
|
||||
* specific for each DB but are global: currently what we do is send a
|
||||
* special notification to clients with tracking enabled, sending a
|
||||
* RESP NULL, which means, "all the keys", in order to avoid flooding
|
||||
* clients with many invalidation messages for all the keys they may
|
||||
* hold.
|
||||
/* This function is called when one or all the Redis databases are flushed
|
||||
* (dbid == -1 in case of FLUSHALL). Caching keys are not specific for
|
||||
* each DB but are global: currently what we do is send a special
|
||||
* notification to clients with tracking enabled, invalidating the caching
|
||||
* key "", which means, "all the keys", in order to avoid flooding clients
|
||||
* with many invalidation messages for all the keys they may hold.
|
||||
*/
|
||||
void freeTrackingRadixTree(void *rt) {
|
||||
raxFree((rax*)rt);
|
||||
}
|
||||
|
||||
/* A RESP NULL is sent to indicate that all keys are invalid */
|
||||
void trackingInvalidateKeysOnFlush(int dbid) {
|
||||
if (g_pserver->tracking_clients) {
|
||||
listNode *ln;
|
||||
@@ -369,7 +366,7 @@ void trackingInvalidateKeysOnFlush(int dbid) {
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
client *c = (client*)listNodeValue(ln);
|
||||
if (c->flags & CLIENT_TRACKING) {
|
||||
sendTrackingMessage(c,szFromObj(shared.null[c->resp]),sdslen(szFromObj(shared.null[c->resp])),1);
|
||||
sendTrackingMessage(c,"",1,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@
|
||||
* |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes
|
||||
* String value with length greater than or equal to 16384 bytes.
|
||||
* Only the 4 bytes following the first byte represents the length
|
||||
* up to 2^32-1. The 6 lower bits of the first byte are not used and
|
||||
* up to 32^2-1. The 6 lower bits of the first byte are not used and
|
||||
* are set to zero.
|
||||
* IMPORTANT: The 32 bit number is stored in big endian.
|
||||
* |11000000| - 3 bytes
|
||||
@@ -194,7 +194,7 @@
|
||||
#define ZIP_BIG_PREVLEN 254 /* Max number of bytes of the previous entry, for
|
||||
the "prevlen" field prefixing each entry, to be
|
||||
represented with just a single byte. Otherwise
|
||||
it is represented as FE AA BB CC DD, where
|
||||
it is represented as FF AA BB CC DD, where
|
||||
AA BB CC DD are a 4 bytes unsigned integer
|
||||
representing the previous entry len. */
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
user alice on allcommands allkeys >alice
|
||||
user bob on -@all +@set +acl ~set* >bob
|
||||
@@ -1,5 +1,4 @@
|
||||
source "../tests/includes/init-tests.tcl"
|
||||
source "../../../tests/support/cli.tcl"
|
||||
|
||||
test "Create a 5 nodes cluster" {
|
||||
create_cluster 5 5
|
||||
@@ -39,79 +38,50 @@ proc cluster_write_keys_with_expire {id ttl} {
|
||||
$cluster close
|
||||
}
|
||||
|
||||
# make sure that replica who restarts from persistence will load keys
|
||||
# that have already expired, critical for correct execution of commands
|
||||
# that arrive from the master
|
||||
proc test_slave_load_expired_keys {aof} {
|
||||
test "Slave expired keys is loaded when restarted: appendonly=$aof" {
|
||||
set master_id [find_non_empty_master]
|
||||
set replica_id [get_one_of_my_replica $master_id]
|
||||
|
||||
set master_dbsize_0 [R $master_id dbsize]
|
||||
set replica_dbsize_0 [R $replica_id dbsize]
|
||||
assert_equal $master_dbsize_0 $replica_dbsize_0
|
||||
set master_dbsize [R $master_id dbsize]
|
||||
set slave_dbsize [R $replica_id dbsize]
|
||||
assert_equal $master_dbsize $slave_dbsize
|
||||
|
||||
set data_ttl 5
|
||||
cluster_write_keys_with_expire $master_id $data_ttl
|
||||
after 100
|
||||
set replica_dbsize_1 [R $replica_id dbsize]
|
||||
assert {$replica_dbsize_1 > $slave_dbsize}
|
||||
|
||||
# config the replica persistency and rewrite the config file to survive restart
|
||||
# note that this needs to be done before populating the volatile keys since
|
||||
# that triggers and AOFRW, and we rather the AOF file to have SETEX commands
|
||||
# rather than an RDB with volatile keys
|
||||
R $replica_id config set appendonly $aof
|
||||
R $replica_id config rewrite
|
||||
|
||||
# fill with 100 keys with 3 second TTL
|
||||
set data_ttl 3
|
||||
cluster_write_keys_with_expire $master_id $data_ttl
|
||||
|
||||
# wait for replica to be in sync with master
|
||||
wait_for_condition 500 10 {
|
||||
[R $replica_id dbsize] eq [R $master_id dbsize]
|
||||
} else {
|
||||
fail "replica didn't sync"
|
||||
}
|
||||
|
||||
set replica_dbsize_1 [R $replica_id dbsize]
|
||||
assert {$replica_dbsize_1 > $replica_dbsize_0}
|
||||
|
||||
# make replica create persistence file
|
||||
if {$aof == "yes"} {
|
||||
# we need to wait for the initial AOFRW to be done, otherwise
|
||||
# kill_instance (which now uses SIGTERM will fail ("Writing initial AOF, can't exit")
|
||||
wait_for_condition 100 10 {
|
||||
[RI $replica_id aof_rewrite_in_progress] eq 0
|
||||
} else {
|
||||
fail "keys didn't expire"
|
||||
}
|
||||
} else {
|
||||
R $replica_id save
|
||||
}
|
||||
|
||||
# kill the replica (would stay down until re-started)
|
||||
set start_time [clock seconds]
|
||||
set end_time [expr $start_time+$data_ttl+2]
|
||||
R $replica_id save
|
||||
set replica_dbsize_2 [R $replica_id dbsize]
|
||||
assert {$replica_dbsize_2 > $slave_dbsize}
|
||||
kill_instance redis $replica_id
|
||||
|
||||
# Make sure the master doesn't do active expire (sending DELs to the replica)
|
||||
R $master_id DEBUG SET-ACTIVE-EXPIRE 0
|
||||
set master_port [get_instance_attrib redis $master_id port]
|
||||
exec ../../../src/keydb-cli -h 127.0.0.1 -p $master_port debug sleep [expr $data_ttl+3] > /dev/null &
|
||||
|
||||
# wait for all the keys to get logically expired
|
||||
after [expr $data_ttl*1000]
|
||||
|
||||
# start the replica again (loading an RDB or AOF file)
|
||||
while {[clock seconds] <= $end_time} {
|
||||
#wait for $data_ttl seconds
|
||||
}
|
||||
restart_instance redis $replica_id
|
||||
|
||||
# make sure the keys are still there
|
||||
set replica_dbsize_3 [R $replica_id dbsize]
|
||||
assert {$replica_dbsize_3 > $replica_dbsize_0}
|
||||
|
||||
# restore settings
|
||||
R $master_id DEBUG SET-ACTIVE-EXPIRE 1
|
||||
|
||||
# wait for the master to expire all keys and replica to get the DELs
|
||||
wait_for_condition 500 10 {
|
||||
[R $replica_id dbsize] eq $master_dbsize_0
|
||||
wait_for_condition 200 50 {
|
||||
[R $replica_id ping] eq {PONG}
|
||||
} else {
|
||||
fail "keys didn't expire"
|
||||
fail "replica #$replica_id not started"
|
||||
}
|
||||
|
||||
set replica_dbsize_3 [R $replica_id dbsize]
|
||||
assert {$replica_dbsize_3 > $slave_dbsize}
|
||||
}
|
||||
}
|
||||
|
||||
test_slave_load_expired_keys no
|
||||
after 5000
|
||||
test_slave_load_expired_keys yes
|
||||
@@ -1,48 +0,0 @@
|
||||
# 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}}
|
||||
}
|
||||
|
||||
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}}
|
||||
}
|
||||
+25
-97
@@ -19,7 +19,6 @@ set ::verbose 0
|
||||
set ::valgrind 0
|
||||
set ::tls 0
|
||||
set ::pause_on_error 0
|
||||
set ::dont_clean 0
|
||||
set ::simulate_error 0
|
||||
set ::failed 0
|
||||
set ::sentinel_instances {}
|
||||
@@ -39,7 +38,7 @@ if {[catch {cd tmp}]} {
|
||||
|
||||
# Execute the specified instance of the server specified by 'type', using
|
||||
# the provided configuration file. Returns the PID of the process.
|
||||
proc exec_instance {type dirname cfgfile} {
|
||||
proc exec_instance {type cfgfile dir} {
|
||||
if {$type eq "redis"} {
|
||||
set prgname keydb-server
|
||||
} elseif {$type eq "sentinel"} {
|
||||
@@ -48,11 +47,13 @@ proc exec_instance {type dirname cfgfile} {
|
||||
error "Unknown instance type."
|
||||
}
|
||||
|
||||
set errfile [file join $dirname err.txt]
|
||||
set stdout [format "%s/%s" $dir "stdout"]
|
||||
set stderr [format "%s/%s" $dir "stderr"]
|
||||
|
||||
if {$::valgrind} {
|
||||
set pid [exec valgrind --track-origins=yes --suppressions=../../../src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full ../../../src/${prgname} $cfgfile 2>> $errfile &]
|
||||
set pid [exec valgrind --track-origins=yes --suppressions=../../../src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full ../../../src/${prgname} $cfgfile &]
|
||||
} else {
|
||||
set pid [exec ../../../src/${prgname} $cfgfile 2>> $errfile &]
|
||||
set pid [exec ../../../src/${prgname} $cfgfile > $stdout 2> $stderr &]
|
||||
}
|
||||
return $pid
|
||||
}
|
||||
@@ -61,6 +62,8 @@ proc exec_instance {type dirname cfgfile} {
|
||||
proc spawn_instance {type base_port count {conf {}}} {
|
||||
for {set j 0} {$j < $count} {incr j} {
|
||||
set port [find_available_port $base_port $::redis_port_count]
|
||||
incr base_port
|
||||
puts "Starting $type #$j at port $port"
|
||||
|
||||
# Create a directory for this instance.
|
||||
set dirname "${type}_${j}"
|
||||
@@ -93,34 +96,12 @@ proc spawn_instance {type base_port count {conf {}}} {
|
||||
close $cfg
|
||||
|
||||
# Finally exec it and remember the pid for later cleanup.
|
||||
set retry 100
|
||||
while {$retry} {
|
||||
set pid [exec_instance $type $dirname $cfgfile]
|
||||
set pid [exec_instance $type $cfgfile $dirname]
|
||||
lappend ::pids $pid
|
||||
|
||||
# Check availability
|
||||
if {[server_is_up 127.0.0.1 $port 100] == 0} {
|
||||
puts "Starting $type #$j at port $port failed, try another"
|
||||
incr retry -1
|
||||
set port [find_available_port $base_port $::redis_port_count]
|
||||
set cfg [open $cfgfile a+]
|
||||
if {$::tls} {
|
||||
puts $cfg "tls-port $port"
|
||||
} else {
|
||||
puts $cfg "port $port"
|
||||
}
|
||||
close $cfg
|
||||
} else {
|
||||
puts "Starting $type #$j at port $port"
|
||||
lappend ::pids $pid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Check availability finally
|
||||
# Check availability
|
||||
if {[server_is_up 127.0.0.1 $port 100] == 0} {
|
||||
set logfile [file join $dirname log.txt]
|
||||
puts [exec tail $logfile]
|
||||
abort_sentinel_test "Problems starting $type #$j: ping timeout, maybe server start failed, check $logfile"
|
||||
abort_sentinel_test "Problems starting $type #$j: ping timeout"
|
||||
}
|
||||
|
||||
# Push the instance into the right list
|
||||
@@ -146,60 +127,16 @@ proc log_crashes {} {
|
||||
puts "\n*** Crash report found in $log ***"
|
||||
set found 1
|
||||
}
|
||||
if {$found} {
|
||||
puts $line
|
||||
incr ::failed
|
||||
}
|
||||
if {$found} {puts $line}
|
||||
}
|
||||
}
|
||||
|
||||
set logs [glob */err.txt]
|
||||
foreach log $logs {
|
||||
set res [find_valgrind_errors $log]
|
||||
if {$res != ""} {
|
||||
puts $res
|
||||
incr ::failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc is_alive pid {
|
||||
if {[catch {exec ps -p $pid} err]} {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
proc stop_instance pid {
|
||||
catch {exec kill $pid}
|
||||
if {$::valgrind} {
|
||||
set max_wait 60000
|
||||
} else {
|
||||
set max_wait 10000
|
||||
}
|
||||
while {[is_alive $pid]} {
|
||||
incr wait 10
|
||||
|
||||
if {$wait >= $max_wait} {
|
||||
puts "Forcing process $pid to exit..."
|
||||
catch {exec kill -KILL $pid}
|
||||
} elseif {$wait % 1000 == 0} {
|
||||
puts "Waiting for process $pid to exit..."
|
||||
}
|
||||
after 10
|
||||
}
|
||||
}
|
||||
|
||||
proc cleanup {} {
|
||||
puts "Cleaning up..."
|
||||
foreach pid $::pids {
|
||||
puts "killing stale instance $pid"
|
||||
stop_instance $pid
|
||||
}
|
||||
log_crashes
|
||||
if {$::dont_clean} {
|
||||
return
|
||||
foreach pid $::pids {
|
||||
catch {exec kill -9 $pid}
|
||||
}
|
||||
foreach dir $::dirs {
|
||||
catch {exec rm -rf $dir}
|
||||
@@ -224,8 +161,6 @@ proc parse_options {} {
|
||||
set ::run_matching "*${val}*"
|
||||
} elseif {$opt eq "--pause-on-error"} {
|
||||
set ::pause_on_error 1
|
||||
} elseif {$opt eq {--dont-clean}} {
|
||||
set ::dont_clean 1
|
||||
} elseif {$opt eq "--fail"} {
|
||||
set ::simulate_error 1
|
||||
} elseif {$opt eq {--valgrind}} {
|
||||
@@ -238,8 +173,9 @@ proc parse_options {} {
|
||||
-keyfile "$::tlsdir/redis.key"
|
||||
set ::tls 1
|
||||
} elseif {$opt eq "--help"} {
|
||||
puts "Hello, I'm sentinel.tcl and I run Sentinel unit tests."
|
||||
puts "\nOptions:"
|
||||
puts "--single <pattern> Only runs tests specified by pattern."
|
||||
puts "--dont-clean Keep log files on exit."
|
||||
puts "--pause-on-error Pause for manual inspection on error."
|
||||
puts "--fail Simulate a test failure."
|
||||
puts "--valgrind Run with valgrind."
|
||||
@@ -422,16 +358,10 @@ 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} {
|
||||
[Rn $n] {*}$args
|
||||
set r [lindex $::redis_instances $n]
|
||||
[dict get $r link] {*}$args
|
||||
}
|
||||
|
||||
proc get_info_field {info field} {
|
||||
@@ -541,7 +471,7 @@ proc kill_instance {type id} {
|
||||
error "You tried to kill $type $id twice."
|
||||
}
|
||||
|
||||
stop_instance $pid
|
||||
exec kill -9 $pid
|
||||
set_instance_attrib $type $id pid -1
|
||||
set_instance_attrib $type $id link you_tried_to_talk_with_killed_instance
|
||||
|
||||
@@ -550,12 +480,12 @@ proc kill_instance {type id} {
|
||||
|
||||
# Wait for the port it was using to be available again, so that's not
|
||||
# an issue to start a new server ASAP with the same port.
|
||||
set retry 100
|
||||
set retry 10
|
||||
while {[incr retry -1]} {
|
||||
set port_is_free [catch {set s [socket 127.0.0.1 $port]}]
|
||||
set port_is_free [catch {set s [socket 127.0.01 $port]}]
|
||||
if {$port_is_free} break
|
||||
catch {close $s}
|
||||
after 100
|
||||
after 1000
|
||||
}
|
||||
if {$retry == 0} {
|
||||
error "Port $port does not return available after killing instance."
|
||||
@@ -576,15 +506,13 @@ proc restart_instance {type id} {
|
||||
|
||||
# Execute the instance with its old setup and append the new pid
|
||||
# file for cleanup.
|
||||
set pid [exec_instance $type $dirname $cfgfile]
|
||||
set pid [exec_instance $type $cfgfile $dirname]
|
||||
set_instance_attrib $type $id pid $pid
|
||||
lappend ::pids $pid
|
||||
|
||||
# Check that the instance is running
|
||||
if {[server_is_up 127.0.0.1 $port 100] == 0} {
|
||||
set logfile [file join $dirname log.txt]
|
||||
puts [exec tail $logfile]
|
||||
abort_sentinel_test "Problems starting $type #$id: ping timeout, maybe server start failed, check $logfile"
|
||||
abort_sentinel_test "Problems starting $type #$id: ping timeout"
|
||||
}
|
||||
|
||||
# Connect with it with a fresh link
|
||||
|
||||
@@ -52,9 +52,15 @@ tags {"aof"} {
|
||||
assert_equal 1 [is_alive $srv]
|
||||
}
|
||||
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
|
||||
test "Truncated AOF loaded: we expect foo to be equal to 5" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
assert {[$client get foo] eq "5"}
|
||||
}
|
||||
|
||||
@@ -69,9 +75,15 @@ tags {"aof"} {
|
||||
assert_equal 1 [is_alive $srv]
|
||||
}
|
||||
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
|
||||
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
assert {[$client get foo] eq "6"}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +183,11 @@ tags {"aof"} {
|
||||
|
||||
test "Fixed AOF: Keyspace should contain values that were parseable" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
assert_equal "hello" [$client get foo]
|
||||
assert_equal "" [$client get bar]
|
||||
}
|
||||
@@ -191,7 +207,11 @@ tags {"aof"} {
|
||||
|
||||
test "AOF+SPOP: Set should have 1 member" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
assert_equal 1 [$client scard set]
|
||||
}
|
||||
}
|
||||
@@ -211,7 +231,11 @@ tags {"aof"} {
|
||||
|
||||
test "AOF+SPOP: Set should have 1 member" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
assert_equal 1 [$client scard set]
|
||||
}
|
||||
}
|
||||
@@ -230,7 +254,11 @@ tags {"aof"} {
|
||||
|
||||
test "AOF+EXPIRE: List should be empty" {
|
||||
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
|
||||
wait_done_loading $client
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$client ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
assert_equal 0 [$client llen list]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ start_server {} {
|
||||
# make sure replication is still alive and kicking
|
||||
$R(1) incr x
|
||||
wait_for_condition 50 1000 {
|
||||
[status $R(0) loading] == 0 &&
|
||||
[$R(0) get x] == 1
|
||||
} else {
|
||||
fail "replica didn't get incr"
|
||||
|
||||
@@ -284,8 +284,7 @@ start_server {} {
|
||||
set sync_partial_err [status $R($master_id) sync_partial_err]
|
||||
catch {
|
||||
$R($slave_id) config rewrite
|
||||
restart_server [expr {0-$slave_id}] true
|
||||
set R($slave_id) [srv [expr {0-$slave_id}] client]
|
||||
$R($slave_id) debug restart
|
||||
}
|
||||
# note: just waiting for connected_slaves==4 has a race condition since
|
||||
# we might do the check before the master realized that the slave disconnected
|
||||
@@ -333,8 +332,7 @@ start_server {} {
|
||||
|
||||
catch {
|
||||
$R($slave_id) config rewrite
|
||||
restart_server [expr {0-$slave_id}] true
|
||||
set R($slave_id) [srv [expr {0-$slave_id}] client]
|
||||
$R($slave_id) debug restart
|
||||
}
|
||||
|
||||
# Reconfigure the slave correctly again, when it's back online.
|
||||
|
||||
+18
-26
@@ -25,7 +25,7 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "encodings.rd
|
||||
|
||||
set server_path [tmpdir "server.rdb-startup-test"]
|
||||
|
||||
start_server [list overrides [list "dir" $server_path] keep_persistence true] {
|
||||
start_server [list overrides [list "dir" $server_path]] {
|
||||
test {Server started empty with non-existing RDB file} {
|
||||
r debug digest
|
||||
} {0000000000000000000000000000000000000000}
|
||||
@@ -33,13 +33,13 @@ start_server [list overrides [list "dir" $server_path] keep_persistence true] {
|
||||
r save
|
||||
}
|
||||
|
||||
start_server [list overrides [list "dir" $server_path] keep_persistence true] {
|
||||
start_server [list overrides [list "dir" $server_path]] {
|
||||
test {Server started empty with empty RDB file} {
|
||||
r debug digest
|
||||
} {0000000000000000000000000000000000000000}
|
||||
}
|
||||
|
||||
start_server [list overrides [list "dir" $server_path] keep_persistence true] {
|
||||
start_server [list overrides [list "dir" $server_path]] {
|
||||
test {Test RDB stream encoding} {
|
||||
for {set j 0} {$j < 1000} {incr j} {
|
||||
if {rand() < 0.9} {
|
||||
@@ -64,7 +64,7 @@ set defaults {}
|
||||
proc start_server_and_kill_it {overrides code} {
|
||||
upvar defaults defaults srv srv server_path server_path
|
||||
set config [concat $defaults $overrides]
|
||||
set srv [start_server [list overrides $config keep_persistence true]]
|
||||
set srv [start_server [list overrides $config]]
|
||||
uplevel 1 $code
|
||||
kill_server $srv
|
||||
}
|
||||
@@ -118,34 +118,16 @@ start_server_and_kill_it [list "dir" $server_path] {
|
||||
|
||||
start_server {} {
|
||||
test {Test FLUSHALL aborts bgsave} {
|
||||
# 1000 keys with 1ms sleep per key shuld take 1 second
|
||||
r config set rdb-key-save-delay 1000
|
||||
r debug populate 1000
|
||||
r bgsave
|
||||
assert_equal [s rdb_bgsave_in_progress] 1
|
||||
r flushall
|
||||
# wait half a second max
|
||||
wait_for_condition 5 100 {
|
||||
[s rdb_bgsave_in_progress] == 0
|
||||
} else {
|
||||
fail "bgsave not aborted"
|
||||
}
|
||||
# veirfy that bgsave failed, by checking that the change counter is still high
|
||||
assert_lessthan 999 [s rdb_changes_since_last_save]
|
||||
after 200
|
||||
assert_equal [s rdb_bgsave_in_progress] 0
|
||||
# make sure the server is still writable
|
||||
r set x xx
|
||||
}
|
||||
|
||||
test {bgsave resets the change counter} {
|
||||
r config set rdb-key-save-delay 0
|
||||
r bgsave
|
||||
wait_for_condition 50 100 {
|
||||
[s rdb_bgsave_in_progress] == 0
|
||||
} else {
|
||||
fail "bgsave not done"
|
||||
}
|
||||
assert_equal [s rdb_changes_since_last_save] 0
|
||||
}
|
||||
}
|
||||
|
||||
test {client freed during loading} {
|
||||
@@ -155,8 +137,18 @@ test {client freed during loading} {
|
||||
# 100mb of rdb, 100k keys will load in more than 1 second
|
||||
r debug populate 100000 key 1000
|
||||
|
||||
restart_server 0 false
|
||||
catch {
|
||||
r debug restart
|
||||
}
|
||||
|
||||
set stdout [srv 0 stdout]
|
||||
while 1 {
|
||||
# check that the new server actually started and is ready for connections
|
||||
if {[exec grep -i "Server initialized" | wc -l < $stdout] > 1} {
|
||||
break
|
||||
}
|
||||
after 10
|
||||
}
|
||||
# make sure it's still loading
|
||||
assert_equal [s loading] 1
|
||||
|
||||
@@ -188,4 +180,4 @@ test {client freed during loading} {
|
||||
# no need to keep waiting for loading to complete
|
||||
exec kill [srv 0 pid]
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-105
@@ -1,19 +1,14 @@
|
||||
source tests/support/cli.tcl
|
||||
|
||||
start_server {tags {"cli"}} {
|
||||
proc open_cli {{opts "-n 9"} {infile ""}} {
|
||||
proc open_cli {} {
|
||||
set ::env(TERM) dumb
|
||||
set cmdline [rediscli [srv host] [srv port] $opts]
|
||||
if {$infile ne ""} {
|
||||
set cmdline "$cmdline < $infile"
|
||||
set mode "r"
|
||||
} else {
|
||||
set mode "r+"
|
||||
}
|
||||
set fd [open "|$cmdline" $mode]
|
||||
set cmdline [rediscli [srv port] "-n 9"]
|
||||
set fd [open "|$cmdline" "r+"]
|
||||
fconfigure $fd -buffering none
|
||||
fconfigure $fd -blocking false
|
||||
fconfigure $fd -translation binary
|
||||
assert_equal "redis> " [read_cli $fd]
|
||||
set _ $fd
|
||||
}
|
||||
|
||||
@@ -37,14 +32,11 @@ start_server {tags {"cli"}} {
|
||||
}
|
||||
|
||||
# Helpers to run tests in interactive mode
|
||||
|
||||
proc format_output {output} {
|
||||
set _ [string trimright [regsub -all "\r" $output ""] "\n"]
|
||||
}
|
||||
|
||||
proc run_command {fd cmd} {
|
||||
write_cli $fd $cmd
|
||||
set _ [format_output [read_cli $fd]]
|
||||
set lines [split [read_cli $fd] "\n"]
|
||||
assert_equal "redis> " [lindex $lines end]
|
||||
join [lrange $lines 0 end-1] "\n"
|
||||
}
|
||||
|
||||
proc test_interactive_cli {name code} {
|
||||
@@ -65,8 +57,8 @@ start_server {tags {"cli"}} {
|
||||
}
|
||||
|
||||
proc _run_cli {opts args} {
|
||||
set cmd [rediscli [srv host] [srv port] [list -n 9 {*}$args]]
|
||||
foreach {key value} $opts {
|
||||
set cmd [rediscli [srv port] [list -n 9 {*}$args]]
|
||||
foreach {key value} $args {
|
||||
if {$key eq "pipe"} {
|
||||
set cmd "sh -c \"$value | $cmd\""
|
||||
}
|
||||
@@ -80,7 +72,7 @@ start_server {tags {"cli"}} {
|
||||
fconfigure $fd -translation binary
|
||||
set resp [read $fd 1048576]
|
||||
close $fd
|
||||
set _ [format_output $resp]
|
||||
set _ $resp
|
||||
}
|
||||
|
||||
proc run_cli {args} {
|
||||
@@ -88,11 +80,11 @@ start_server {tags {"cli"}} {
|
||||
}
|
||||
|
||||
proc run_cli_with_input_pipe {cmd args} {
|
||||
_run_cli [list pipe $cmd] -x {*}$args
|
||||
_run_cli [list pipe $cmd] {*}$args
|
||||
}
|
||||
|
||||
proc run_cli_with_input_file {path args} {
|
||||
_run_cli [list path $path] -x {*}$args
|
||||
_run_cli [list path $path] {*}$args
|
||||
}
|
||||
|
||||
proc test_nontty_cli {name code} {
|
||||
@@ -109,7 +101,7 @@ start_server {tags {"cli"}} {
|
||||
test_interactive_cli "INFO response should be printed raw" {
|
||||
set lines [split [run_command $fd info] "\n"]
|
||||
foreach line $lines {
|
||||
assert [regexp {^$|^#|^[a-z0-9_]+:.+} $line]
|
||||
assert [regexp {^[a-z0-9_]+:[a-z0-9_]+} $line]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +121,7 @@ start_server {tags {"cli"}} {
|
||||
test_interactive_cli "Multi-bulk reply" {
|
||||
r rpush list foo
|
||||
r rpush list bar
|
||||
assert_equal "1) \"foo\"\n2) \"bar\"" [run_command $fd "lrange list 0 -1"]
|
||||
assert_equal "1. \"foo\"\n2. \"bar\"" [run_command $fd "lrange list 0 -1"]
|
||||
}
|
||||
|
||||
test_interactive_cli "Parsing quotes" {
|
||||
@@ -152,37 +144,36 @@ start_server {tags {"cli"}} {
|
||||
}
|
||||
|
||||
test_tty_cli "Status reply" {
|
||||
assert_equal "OK" [run_cli set key bar]
|
||||
assert_equal "OK\n" [run_cli set key bar]
|
||||
assert_equal "bar" [r get key]
|
||||
}
|
||||
|
||||
test_tty_cli "Integer reply" {
|
||||
r del counter
|
||||
assert_equal "(integer) 1" [run_cli incr counter]
|
||||
assert_equal "(integer) 1\n" [run_cli incr counter]
|
||||
}
|
||||
|
||||
test_tty_cli "Bulk reply" {
|
||||
r set key "tab\tnewline\n"
|
||||
assert_equal "\"tab\\tnewline\\n\"" [run_cli get key]
|
||||
assert_equal "\"tab\\tnewline\\n\"\n" [run_cli get key]
|
||||
}
|
||||
|
||||
test_tty_cli "Multi-bulk reply" {
|
||||
r del list
|
||||
r rpush list foo
|
||||
r rpush list bar
|
||||
assert_equal "1) \"foo\"\n2) \"bar\"" [run_cli lrange list 0 -1]
|
||||
assert_equal "1. \"foo\"\n2. \"bar\"\n" [run_cli lrange list 0 -1]
|
||||
}
|
||||
|
||||
test_tty_cli "Read last argument from pipe" {
|
||||
assert_equal "OK" [run_cli_with_input_pipe "echo foo" set key]
|
||||
assert_equal "OK\n" [run_cli_with_input_pipe "echo foo" set key]
|
||||
assert_equal "foo\n" [r get key]
|
||||
}
|
||||
|
||||
test_tty_cli "Read last argument from file" {
|
||||
set tmpfile [write_tmpfile "from file"]
|
||||
assert_equal "OK" [run_cli_with_input_file $tmpfile set key]
|
||||
assert_equal "OK\n" [run_cli_with_input_file $tmpfile set key]
|
||||
assert_equal "from file" [r get key]
|
||||
file delete $tmpfile
|
||||
}
|
||||
|
||||
test_nontty_cli "Status reply" {
|
||||
@@ -197,7 +188,7 @@ start_server {tags {"cli"}} {
|
||||
|
||||
test_nontty_cli "Bulk reply" {
|
||||
r set key "tab\tnewline\n"
|
||||
assert_equal "tab\tnewline" [run_cli get key]
|
||||
assert_equal "tab\tnewline\n" [run_cli get key]
|
||||
}
|
||||
|
||||
test_nontty_cli "Multi-bulk reply" {
|
||||
@@ -216,80 +207,5 @@ start_server {tags {"cli"}} {
|
||||
set tmpfile [write_tmpfile "from file"]
|
||||
assert_equal "OK" [run_cli_with_input_file $tmpfile set key]
|
||||
assert_equal "from file" [r get key]
|
||||
file delete $tmpfile
|
||||
}
|
||||
|
||||
proc test_redis_cli_rdb_dump {} {
|
||||
r flushdb
|
||||
|
||||
set dir [lindex [r config get dir] 1]
|
||||
|
||||
assert_equal "OK" [r debug populate 100000 key 1000]
|
||||
catch {run_cli --rdb "$dir/cli.rdb"} output
|
||||
assert_match {*Transfer finished with success*} $output
|
||||
|
||||
file delete "$dir/dump.rdb"
|
||||
file rename "$dir/cli.rdb" "$dir/dump.rdb"
|
||||
|
||||
assert_equal "OK" [r set should-not-exist 1]
|
||||
assert_equal "OK" [r debug reload nosave]
|
||||
assert_equal {} [r get should-not-exist]
|
||||
}
|
||||
|
||||
test "Dumping an RDB" {
|
||||
# Disk-based master
|
||||
assert_match "OK" [r config set repl-diskless-sync no]
|
||||
test_redis_cli_rdb_dump
|
||||
|
||||
# Disk-less master
|
||||
assert_match "OK" [r config set repl-diskless-sync yes]
|
||||
assert_match "OK" [r config set repl-diskless-sync-delay 0]
|
||||
test_redis_cli_rdb_dump
|
||||
}
|
||||
|
||||
test "Connecting as a replica" {
|
||||
set fd [open_cli "--replica"]
|
||||
wait_for_condition 500 500 {
|
||||
[string match {*slave0:*state=online*} [r info]]
|
||||
} else {
|
||||
fail "redis-cli --replica did not connect"
|
||||
}
|
||||
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
r set test-key test-value-$i
|
||||
}
|
||||
r client kill type slave
|
||||
catch {
|
||||
assert_match {*SET*key-a*} [read_cli $fd]
|
||||
}
|
||||
|
||||
close_cli $fd
|
||||
}
|
||||
|
||||
test "Piping raw protocol" {
|
||||
set cmds [tmpfile "cli_cmds"]
|
||||
set cmds_fd [open $cmds "w"]
|
||||
|
||||
puts $cmds_fd [formatCommand select 9]
|
||||
puts $cmds_fd [formatCommand del test-counter]
|
||||
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
puts $cmds_fd [formatCommand incr test-counter]
|
||||
puts $cmds_fd [formatCommand set large-key [string repeat "x" 20000]]
|
||||
}
|
||||
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
puts $cmds_fd [formatCommand set very-large-key [string repeat "x" 512000]]
|
||||
}
|
||||
close $cmds_fd
|
||||
|
||||
set cli_fd [open_cli "--pipe" $cmds]
|
||||
fconfigure $cli_fd -blocking true
|
||||
set output [read_cli $cli_fd]
|
||||
|
||||
assert_equal {1000} [r get test-counter]
|
||||
assert_match {*All data transferred*errors: 0*replies: 2102*} $output
|
||||
|
||||
file delete $cmds
|
||||
}
|
||||
}
|
||||
@@ -215,38 +215,14 @@ start_server {tags {"active-repl"} overrides {active-replica yes}} {
|
||||
assert_equal {1} [$slave wait 1 500] { "value should propogate
|
||||
within 0.5 seconds" }
|
||||
exec kill -SIGSTOP $slave_pid
|
||||
after 3000
|
||||
after 3000
|
||||
# Ensure testkey1 is gone. Note, we can't do this directly as the normal commands lie to us
|
||||
# about what is actually in the dict. The only way to know is with a count from info
|
||||
assert_equal {1} [expr [string first {keys=1} [$master info keyspace]] >= 0] {"slave expired"}
|
||||
assert_equal {1} [expr [string first {keys=1} [$master info keyspace]] >= 0] {"slave expired"}
|
||||
}
|
||||
|
||||
|
||||
exec kill -SIGCONT $slave_pid
|
||||
|
||||
test {Active replica merge works when reconnecting} {
|
||||
$slave flushall
|
||||
$slave set testkey foo
|
||||
wait_for_condition 50 1000 {
|
||||
[string match *foo* [$master get testkey]]
|
||||
} else {
|
||||
fail "Replication failed to propogate"
|
||||
}
|
||||
$slave replicaof no one
|
||||
$master replicaof no one
|
||||
after 100
|
||||
$master set testkey baz
|
||||
after 100
|
||||
$slave set testkey bar
|
||||
after 100
|
||||
$slave replicaof $master_host $master_port
|
||||
after 1000
|
||||
$master replicaof $slave_host $slave_port
|
||||
after 1000
|
||||
|
||||
assert_equal {bar} [$slave get testkey]
|
||||
assert_equal {bar} [$master get testkey]
|
||||
}
|
||||
|
||||
test {Active replica different databases} {
|
||||
$master select 3
|
||||
$master set testkey abcd
|
||||
@@ -261,18 +237,3 @@ start_server {tags {"active-repl"} overrides {active-replica yes}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_server {tags {"active-repl"} overrides {active-replica yes}} {
|
||||
set slave [srv 0 client]
|
||||
set slave_host [srv 0 host]
|
||||
set slave_port [srv 0 port]
|
||||
start_server {tags {"active-repl"} overrides { active-replica yes}} {
|
||||
r set testkeyB bar
|
||||
test {Active Replica Merges Database On Sync} {
|
||||
$slave set testkeyA foo
|
||||
r replicaof $slave_host $slave_port
|
||||
after 1000
|
||||
assert_equal 2 [r dbsize]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
foreach topology {mesh ring} {
|
||||
|
||||
foreach noforward [expr {[string equal $topology "mesh"] ? {no yes} : {no}}] {
|
||||
start_server {tags {"multi-master"} overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
@@ -10,12 +8,8 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
set R($j) [srv [expr 0-$j] client]
|
||||
set R_host($j) [srv [expr 0-$j] host]
|
||||
set R_port($j) [srv [expr 0-$j] port]
|
||||
|
||||
$R($j) config set multi-master-no-forward $noforward
|
||||
}
|
||||
|
||||
set topology_name "$topology[expr {[string equal $noforward "yes"] ? " no-forward" : ""}]"
|
||||
|
||||
# Initialize as mesh
|
||||
if [string equal $topology "mesh"] {
|
||||
for {set j 0} {$j < 4} {incr j} {
|
||||
@@ -37,7 +31,7 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
$R(3) replicaof $R_host(2) $R_port(2)
|
||||
}
|
||||
|
||||
test "$topology_name all nodes up" {
|
||||
test "$topology all nodes up" {
|
||||
for {set j 0} {$j < 4} {incr j} {
|
||||
wait_for_condition 50 100 {
|
||||
[string match {*master_global_link_status:up*} [$R($j) info replication]]
|
||||
@@ -47,7 +41,7 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
}
|
||||
}
|
||||
|
||||
test "$topology_name replicates to all nodes" {
|
||||
test "$topology replicates to all nodes" {
|
||||
$R(0) set testkey foo
|
||||
after 500
|
||||
for {set n 0} {$n < 4} {incr n} {
|
||||
@@ -59,7 +53,7 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
}
|
||||
}
|
||||
|
||||
test "$topology_name replicates only once" {
|
||||
test "$topology replicates only once" {
|
||||
$R(0) set testkey 1
|
||||
after 500
|
||||
#wait_for_condition 50 100 {
|
||||
@@ -80,7 +74,7 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
}
|
||||
}
|
||||
|
||||
test "$topology_name transaction replicates only once" {
|
||||
test "$topology transaction replicates only once" {
|
||||
for {set j 0} {$j < 1000} {incr j} {
|
||||
$R(0) set testkey 1
|
||||
$R(0) multi
|
||||
@@ -96,27 +90,6 @@ start_server {overrides {hz 500 active-replica yes multi-master yes}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Keep this test last since it muchs with the config
|
||||
if [string equal $topology "mesh"] {
|
||||
test "$topology_name quorum respected" {
|
||||
$R(0) config set replica-serve-stale-data no
|
||||
|
||||
# No issues when all nodes are connected with default settings
|
||||
$R(0) get testkey
|
||||
|
||||
# No issues when quorum is equal to the number of nodes
|
||||
$R(0) config set replica-quorum 3
|
||||
$R(0) get testkey
|
||||
|
||||
$R(0) config set replica-quorum 4
|
||||
catch {
|
||||
$R(0) get testkey
|
||||
} e
|
||||
assert_match {*MASTER is down*} $e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +423,6 @@ test {diskless loading short read} {
|
||||
}
|
||||
|
||||
# Start the replication process...
|
||||
set loglines [count_log_lines -1]
|
||||
$master config set repl-diskless-sync-delay 0
|
||||
$replica replicaof $master_host $master_port
|
||||
|
||||
@@ -433,30 +432,29 @@ test {diskless loading short read} {
|
||||
for {set i 0} {$i < $attempts} {incr i} {
|
||||
# wait for the replica to start reading the rdb
|
||||
# using the log file since the replica only responds to INFO once in 2mb
|
||||
set res [wait_for_log_messages -1 {"*Loading DB in memory*"} $loglines 2000 1]
|
||||
set loglines [lindex $res 1]
|
||||
wait_for_log_message -1 "*Loading DB in memory*" 5 2000 1
|
||||
|
||||
# add some additional random sleep so that we kill the master on a different place each time
|
||||
after [expr {int(rand()*50)}]
|
||||
after [expr {int(rand()*100)}]
|
||||
|
||||
# kill the replica connection on the master
|
||||
set killed [$master client kill type replica]
|
||||
|
||||
set res [wait_for_log_messages -1 {"*Internal error in RDB*" "*Finished with success*" "*Successful partial resynchronization*"} $loglines 1000 1]
|
||||
if {$::verbose} { puts $res }
|
||||
set log_text [lindex $res 0]
|
||||
set loglines [lindex $res 1]
|
||||
if {![string match "*Internal error in RDB*" $log_text]} {
|
||||
if {[catch {
|
||||
set res [wait_for_log_message -1 "*Internal error in RDB*" 5 100 10]
|
||||
if {$::verbose} {
|
||||
puts $res
|
||||
}
|
||||
}]} {
|
||||
puts "failed triggering short read"
|
||||
# force the replica to try another full sync
|
||||
$master multi
|
||||
$master client kill type replica
|
||||
$master set asdf asdf
|
||||
# the side effect of resizing the backlog is that it is flushed (16k is the min size)
|
||||
$master config set repl-backlog-size [expr {16384 + $i}]
|
||||
$master exec
|
||||
}
|
||||
# wait for loading to stop (fail)
|
||||
wait_for_condition 1000 1 {
|
||||
wait_for_condition 100 10 {
|
||||
[s -1 loading] eq 0
|
||||
} else {
|
||||
fail "Replica didn't disconnect"
|
||||
@@ -530,7 +528,6 @@ start_server {tags {"repl"}} {
|
||||
# start replication
|
||||
# it's enough for just one replica to be slow, and have it's write handler enabled
|
||||
# so that the whole rdb generation process is bound to that
|
||||
set loglines [count_log_lines -1]
|
||||
[lindex $replicas 0] config set repl-diskless-load swapdb
|
||||
[lindex $replicas 0] config set key-load-delay 100
|
||||
[lindex $replicas 0] replicaof $master_host $master_port
|
||||
@@ -538,7 +535,7 @@ start_server {tags {"repl"}} {
|
||||
|
||||
# wait for the replicas to start reading the rdb
|
||||
# using the log file since the replica only responds to INFO once in 2mb
|
||||
wait_for_log_messages -1 {"*Loading DB in memory*"} $loglines 800 10
|
||||
wait_for_log_message -1 "*Loading DB in memory*" 8 800 10
|
||||
|
||||
if {$measure_time} {
|
||||
set master_statfile "/proc/$master_pid/stat"
|
||||
@@ -554,7 +551,6 @@ start_server {tags {"repl"}} {
|
||||
$master incr $all_drop
|
||||
|
||||
# disconnect replicas depending on the current test
|
||||
set loglines [count_log_lines -2]
|
||||
if {$all_drop == "all" || $all_drop == "fast"} {
|
||||
exec kill [srv 0 pid]
|
||||
set replicas_alive [lreplace $replicas_alive 1 1]
|
||||
@@ -573,13 +569,13 @@ start_server {tags {"repl"}} {
|
||||
|
||||
# make sure we got what we were aiming for, by looking for the message in the log file
|
||||
if {$all_drop == "all"} {
|
||||
wait_for_log_messages -2 {"*Diskless rdb transfer, last replica dropped, killing fork child*"} $loglines 1 1
|
||||
wait_for_log_message -2 "*Diskless rdb transfer, last replica dropped, killing fork child*" 12 1 1
|
||||
}
|
||||
if {$all_drop == "no"} {
|
||||
wait_for_log_messages -2 {"*Diskless rdb transfer, done reading from pipe, 2 replicas still up*"} $loglines 1 1
|
||||
wait_for_log_message -2 "*Diskless rdb transfer, done reading from pipe, 2 replicas still up*" 12 1 1
|
||||
}
|
||||
if {$all_drop == "slow" || $all_drop == "fast"} {
|
||||
wait_for_log_messages -2 {"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*"} $loglines 1 1
|
||||
wait_for_log_message -2 "*Diskless rdb transfer, done reading from pipe, 1 replicas still up*" 12 1 1
|
||||
}
|
||||
|
||||
# make sure we don't have a busy loop going thought epoll_wait
|
||||
|
||||
@@ -22,10 +22,7 @@ TEST_MODULES = \
|
||||
blockonkeys.so \
|
||||
scan.so \
|
||||
datatype.so \
|
||||
auth.so \
|
||||
keyspace_events.so \
|
||||
blockedclient.so
|
||||
|
||||
auth.so
|
||||
|
||||
.PHONY: all
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "redismodule.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define UNUSED(V) ((void) V)
|
||||
|
||||
void *sub_worker(void *arg) {
|
||||
// Get Redis module context
|
||||
RedisModuleCtx *ctx = (RedisModuleCtx *)arg;
|
||||
|
||||
// Try acquiring GIL
|
||||
int res = RedisModule_ThreadSafeContextTryLock(ctx);
|
||||
|
||||
// GIL is already taken by the calling thread expecting to fail.
|
||||
assert(res != REDISMODULE_OK);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *worker(void *arg) {
|
||||
// Retrieve blocked client
|
||||
RedisModuleBlockedClient *bc = (RedisModuleBlockedClient *)arg;
|
||||
|
||||
// Get Redis module context
|
||||
RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bc);
|
||||
|
||||
// Acquire GIL
|
||||
RedisModule_ThreadSafeContextLock(ctx);
|
||||
|
||||
// Create another thread which will try to acquire the GIL
|
||||
pthread_t tid;
|
||||
int res = pthread_create(&tid, NULL, sub_worker, ctx);
|
||||
assert(res == 0);
|
||||
|
||||
// Wait for thread
|
||||
pthread_join(tid, NULL);
|
||||
|
||||
// Release GIL
|
||||
RedisModule_ThreadSafeContextUnlock(ctx);
|
||||
|
||||
// Reply to client
|
||||
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
||||
|
||||
// Unblock client
|
||||
RedisModule_UnblockClient(bc, NULL);
|
||||
|
||||
// Free the Redis module context
|
||||
RedisModule_FreeThreadSafeContext(ctx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int acquire_gil(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
{
|
||||
UNUSED(argv);
|
||||
UNUSED(argc);
|
||||
|
||||
/* This command handler tries to acquire the GIL twice
|
||||
* once in the worker thread using "RedisModule_ThreadSafeContextLock"
|
||||
* second in the sub-worker thread
|
||||
* using "RedisModule_ThreadSafeContextTryLock"
|
||||
* as the GIL is already locked. */
|
||||
RedisModuleBlockedClient *bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0);
|
||||
|
||||
pthread_t tid;
|
||||
int res = pthread_create(&tid, NULL, worker, bc);
|
||||
assert(res == 0);
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
|
||||
if (RedisModule_Init(ctx, "blockedclient", 1, REDISMODULE_APIVER_1)== REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
if (RedisModule_CreateCommand(ctx, "acquire_gil", acquire_gil, "", 0, 0, 0) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
@@ -30,7 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API 1
|
||||
#include "redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -144,12 +143,10 @@ void flushdbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void
|
||||
{
|
||||
REDISMODULE_NOT_USED(e);
|
||||
|
||||
RedisModule_ThreadSafeContextLock(ctx);
|
||||
RedisModuleFlushInfo *fi = data;
|
||||
char *keyname = (sub == REDISMODULE_SUBEVENT_FLUSHDB_START) ?
|
||||
"flush-start" : "flush-end";
|
||||
LogNumericEvent(ctx, keyname, fi->dbnum);
|
||||
RedisModule_ThreadSafeContextUnlock(ctx);
|
||||
}
|
||||
|
||||
void roleChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data)
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/* This module is used to test the server keyspace events API.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2020, Meir Shpilraien <meir at redislabs dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
|
||||
#include "redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/** strores all the keys on which we got 'loaded' keyspace notification **/
|
||||
RedisModuleDict *loaded_event_log = NULL;
|
||||
|
||||
static int KeySpace_Notification(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key){
|
||||
REDISMODULE_NOT_USED(ctx);
|
||||
REDISMODULE_NOT_USED(type);
|
||||
|
||||
if(strcmp(event, "loaded") == 0){
|
||||
const char* keyName = RedisModule_StringPtrLen(key, NULL);
|
||||
int nokey;
|
||||
RedisModule_DictGetC(loaded_event_log, (void*)keyName, strlen(keyName), &nokey);
|
||||
if(nokey){
|
||||
RedisModule_DictSetC(loaded_event_log, (void*)keyName, strlen(keyName), RedisModule_HoldString(ctx, key));
|
||||
}
|
||||
}
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
static int cmdIsKeyLoaded(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
|
||||
if(argc != 2){
|
||||
return RedisModule_WrongArity(ctx);
|
||||
}
|
||||
|
||||
const char* key = RedisModule_StringPtrLen(argv[1], NULL);
|
||||
|
||||
int nokey;
|
||||
RedisModuleString* keyStr = RedisModule_DictGetC(loaded_event_log, (void*)key, strlen(key), &nokey);
|
||||
|
||||
RedisModule_ReplyWithArray(ctx, 2);
|
||||
RedisModule_ReplyWithLongLong(ctx, !nokey);
|
||||
if(nokey){
|
||||
RedisModule_ReplyWithNull(ctx);
|
||||
}else{
|
||||
RedisModule_ReplyWithString(ctx, keyStr);
|
||||
}
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
/* This function must be present on each Redis module. It is used in order to
|
||||
* register the commands into the Redis server. */
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
|
||||
if (RedisModule_Init(ctx,"testkeyspace",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR){
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
loaded_event_log = RedisModule_CreateDict(ctx);
|
||||
|
||||
if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_LOADED, KeySpace_Notification) != REDISMODULE_OK){
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
if (RedisModule_CreateCommand(ctx,"keyspace.is_key_loaded", cmdIsKeyLoaded,"",0,0,0) == REDISMODULE_ERR){
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int RedisModule_OnUnload(RedisModuleCtx *ctx) {
|
||||
RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(loaded_event_log, "^", NULL, 0);
|
||||
char* key;
|
||||
size_t keyLen;
|
||||
RedisModuleString* val;
|
||||
while((key = RedisModule_DictNextC(iter, &keyLen, (void**)&val))){
|
||||
RedisModule_FreeString(ctx, val);
|
||||
}
|
||||
RedisModule_FreeDict(ctx, loaded_event_log);
|
||||
loaded_event_log = NULL;
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
@@ -195,42 +195,6 @@ int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
{
|
||||
(void) argv;
|
||||
(void) argc;
|
||||
|
||||
RedisModuleClientInfo ci = { .version = REDISMODULE_CLIENTINFO_VERSION };
|
||||
|
||||
if (RedisModule_GetClientInfoById(&ci, RedisModule_GetClientId(ctx)) == REDISMODULE_ERR) {
|
||||
RedisModule_ReplyWithError(ctx, "failed to get client info");
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
RedisModule_ReplyWithArray(ctx, 10);
|
||||
char flags[512];
|
||||
snprintf(flags, sizeof(flags) - 1, "%s:%s:%s:%s:%s:%s",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_SSL ? "ssl" : "",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_PUBSUB ? "pubsub" : "",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_BLOCKED ? "blocked" : "",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_TRACKING ? "tracking" : "",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET ? "unixsocket" : "",
|
||||
ci.flags & REDISMODULE_CLIENTINFO_FLAG_MULTI ? "multi" : "");
|
||||
|
||||
RedisModule_ReplyWithCString(ctx, "flags");
|
||||
RedisModule_ReplyWithCString(ctx, flags);
|
||||
RedisModule_ReplyWithCString(ctx, "id");
|
||||
RedisModule_ReplyWithLongLong(ctx, ci.id);
|
||||
RedisModule_ReplyWithCString(ctx, "addr");
|
||||
RedisModule_ReplyWithCString(ctx, ci.addr);
|
||||
RedisModule_ReplyWithCString(ctx, "port");
|
||||
RedisModule_ReplyWithLongLong(ctx, ci.port);
|
||||
RedisModule_ReplyWithCString(ctx, "db");
|
||||
RedisModule_ReplyWithLongLong(ctx, ci.db);
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
@@ -257,8 +221,6 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||
return REDISMODULE_ERR;
|
||||
if (RedisModule_CreateCommand(ctx,"test.getlfu", test_getlfu,"",0,0,0) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
+4
-16
@@ -55,23 +55,11 @@ void scan_key_callback(RedisModuleKey *key, RedisModuleString* field, RedisModul
|
||||
REDISMODULE_NOT_USED(key);
|
||||
scan_key_pd* pd = privdata;
|
||||
RedisModule_ReplyWithArray(pd->ctx, 2);
|
||||
size_t fieldCStrLen;
|
||||
|
||||
// The implementation of RedisModuleString is robj with lots of encodings.
|
||||
// We want to make sure the robj that passes to this callback in
|
||||
// String encoded, this is why we use RedisModule_StringPtrLen and
|
||||
// RedisModule_ReplyWithStringBuffer instead of directly use
|
||||
// RedisModule_ReplyWithString.
|
||||
const char* fieldCStr = RedisModule_StringPtrLen(field, &fieldCStrLen);
|
||||
RedisModule_ReplyWithStringBuffer(pd->ctx, fieldCStr, fieldCStrLen);
|
||||
if(value){
|
||||
size_t valueCStrLen;
|
||||
const char* valueCStr = RedisModule_StringPtrLen(value, &valueCStrLen);
|
||||
RedisModule_ReplyWithStringBuffer(pd->ctx, valueCStr, valueCStrLen);
|
||||
} else {
|
||||
RedisModule_ReplyWithString(pd->ctx, field);
|
||||
if (value)
|
||||
RedisModule_ReplyWithString(pd->ctx, value);
|
||||
else
|
||||
RedisModule_ReplyWithNull(pd->ctx);
|
||||
}
|
||||
|
||||
pd->nreplies++;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ proc rediscli_tls_config {testsdir} {
|
||||
}
|
||||
}
|
||||
|
||||
proc rediscli {host port {opts {}}} {
|
||||
set cmd [list src/keydb-cli --no-motd -h $host -p $port]
|
||||
proc rediscli {port {opts {}}} {
|
||||
set cmd [list src/keydb-cli -p $port]
|
||||
lappend cmd {*}[rediscli_tls_config "tests"]
|
||||
lappend cmd {*}$opts
|
||||
return $cmd
|
||||
|
||||
+73
-220
@@ -13,20 +13,15 @@ proc start_server_error {config_file error} {
|
||||
}
|
||||
|
||||
proc check_valgrind_errors stderr {
|
||||
set res [find_valgrind_errors $stderr]
|
||||
if {$res != ""} {
|
||||
send_data_packet $::test_server_fd err "Valgrind error: $res\n"
|
||||
}
|
||||
}
|
||||
set fd [open $stderr]
|
||||
set buf [read $fd]
|
||||
close $fd
|
||||
|
||||
proc clean_persistence config {
|
||||
# we may wanna keep the logs for later, but let's clean the persistence
|
||||
# files right away, since they can accumulate and take up a lot of space
|
||||
set config [dict get $config "config"]
|
||||
set rdb [format "%s/%s" [dict get $config "dir"] "dump.rdb"]
|
||||
set aof [format "%s/%s" [dict get $config "dir"] "appendonly.aof"]
|
||||
catch {exec rm -rf $rdb}
|
||||
catch {exec rm -rf $aof}
|
||||
if {[regexp -- { at 0x} $buf] ||
|
||||
(![regexp -- {definitely lost: 0 bytes} $buf] &&
|
||||
![regexp -- {no leaks are possible} $buf])} {
|
||||
send_data_packet $::test_server_fd err "Valgrind error: $buf\n"
|
||||
}
|
||||
}
|
||||
|
||||
proc kill_server config {
|
||||
@@ -34,13 +29,7 @@ proc kill_server config {
|
||||
if {$::external} return
|
||||
|
||||
# nevermind if its already dead
|
||||
if {![is_alive $config]} {
|
||||
# Check valgrind errors if needed
|
||||
if {$::valgrind} {
|
||||
check_valgrind_errors [dict get $config stderr]
|
||||
}
|
||||
return
|
||||
}
|
||||
if {![is_alive $config]} { return }
|
||||
set pid [dict get $config pid]
|
||||
|
||||
# check for leaks
|
||||
@@ -148,19 +137,7 @@ proc server_is_up {host port retrynum} {
|
||||
|
||||
# doesn't really belong here, but highly coupled to code in start_server
|
||||
proc tags {tags code} {
|
||||
# If we 'tags' contain multiple tags, quoted and seperated by spaces,
|
||||
# we want to get rid of the quotes in order to have a proper list
|
||||
set tags [string map { \" "" } $tags]
|
||||
set ::tags [concat $::tags $tags]
|
||||
# We skip unwanted tags
|
||||
foreach tag $::denytags {
|
||||
if {[lsearch $::tags $tag] >= 0} {
|
||||
incr ::num_aborted
|
||||
send_data_packet $::test_server_fd ignore "Tag: $tag"
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
return
|
||||
}
|
||||
}
|
||||
uplevel 1 $code
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
}
|
||||
@@ -176,96 +153,7 @@ proc create_server_config_file {filename config} {
|
||||
close $fp
|
||||
}
|
||||
|
||||
proc spawn_server {config_file stdout stderr} {
|
||||
if {$::valgrind} {
|
||||
set pid [exec valgrind --track-origins=yes --trace-children=yes --suppressions=[pwd]/src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/keydb-server $config_file >> $stdout 2>> $stderr &]
|
||||
} elseif ($::stack_logging) {
|
||||
set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/keydb-server $config_file >> $stdout 2>> $stderr &]
|
||||
} else {
|
||||
set pid [exec src/keydb-server $config_file >> $stdout 2>> $stderr &]
|
||||
}
|
||||
|
||||
if {$::wait_server} {
|
||||
set msg "server started PID: $pid. press any key to continue..."
|
||||
puts $msg
|
||||
read stdin 1
|
||||
}
|
||||
|
||||
# Tell the test server about this new instance.
|
||||
send_data_packet $::test_server_fd server-spawned $pid
|
||||
return $pid
|
||||
}
|
||||
|
||||
# Wait for actual startup, return 1 if port is busy, 0 otherwise
|
||||
proc wait_server_started {config_file stdout pid} {
|
||||
set checkperiod 100; # Milliseconds
|
||||
set maxiter [expr {120*1000/$checkperiod}] ; # Wait up to 2 minutes.
|
||||
set port_busy 0
|
||||
while 1 {
|
||||
if {[regexp -- " PID: $pid" [exec cat $stdout]]} {
|
||||
break
|
||||
}
|
||||
after $checkperiod
|
||||
incr maxiter -1
|
||||
if {$maxiter == 0} {
|
||||
start_server_error $config_file "No PID detected in log $stdout"
|
||||
puts "--- LOG CONTENT ---"
|
||||
puts [exec cat $stdout]
|
||||
puts "-------------------"
|
||||
break
|
||||
}
|
||||
|
||||
# Check if the port is actually busy and the server failed
|
||||
# for this reason.
|
||||
if {[regexp {Could not create server TCP} [exec cat $stdout]]} {
|
||||
set port_busy 1
|
||||
break
|
||||
}
|
||||
}
|
||||
return $port_busy
|
||||
}
|
||||
|
||||
proc start_server {options {code undefined}} {
|
||||
# setup defaults
|
||||
set baseconfig "default.conf"
|
||||
set overrides {}
|
||||
set tags {}
|
||||
set keep_persistence false
|
||||
|
||||
# parse options
|
||||
foreach {option value} $options {
|
||||
switch $option {
|
||||
"config" {
|
||||
set baseconfig $value
|
||||
}
|
||||
"overrides" {
|
||||
set overrides $value
|
||||
}
|
||||
"tags" {
|
||||
# If we 'tags' contain multiple tags, quoted and seperated by spaces,
|
||||
# we want to get rid of the quotes in order to have a proper list
|
||||
set tags [string map { \" "" } $value]
|
||||
set ::tags [concat $::tags $tags]
|
||||
}
|
||||
"keep_persistence" {
|
||||
set keep_persistence $value
|
||||
}
|
||||
default {
|
||||
error "Unknown option $option"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We skip unwanted tags
|
||||
foreach tag $::denytags {
|
||||
if {[lsearch $::tags $tag] >= 0} {
|
||||
incr ::num_aborted
|
||||
send_data_packet $::test_server_fd ignore "Tag: $tag"
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# If we are running against an external server, we just push the
|
||||
# host/port pair in the stack the first time
|
||||
if {$::external} {
|
||||
@@ -277,32 +165,33 @@ proc start_server {options {code undefined}} {
|
||||
dict set srv "client" $client
|
||||
$client select 9
|
||||
|
||||
set config {}
|
||||
dict set config "port" $::port
|
||||
dict set srv "config" $config
|
||||
|
||||
# append the server to the stack
|
||||
lappend ::servers $srv
|
||||
}
|
||||
r flushall
|
||||
if {[catch {set retval [uplevel 1 $code]} error]} {
|
||||
if {$::durable} {
|
||||
set msg [string range $error 10 end]
|
||||
lappend details $msg
|
||||
lappend details $::errorInfo
|
||||
lappend ::tests_failed $details
|
||||
|
||||
incr ::num_failed
|
||||
send_data_packet $::test_server_fd err [join $details "\n"]
|
||||
} else {
|
||||
# Re-raise, let handler up the stack take care of this.
|
||||
error $error $::errorInfo
|
||||
}
|
||||
}
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
uplevel 1 $code
|
||||
return
|
||||
}
|
||||
|
||||
# setup defaults
|
||||
set baseconfig "default.conf"
|
||||
set overrides {}
|
||||
set tags {}
|
||||
|
||||
# parse options
|
||||
foreach {option value} $options {
|
||||
switch $option {
|
||||
"config" {
|
||||
set baseconfig $value }
|
||||
"overrides" {
|
||||
set overrides $value }
|
||||
"tags" {
|
||||
set tags $value
|
||||
set ::tags [concat $::tags $value] }
|
||||
default {
|
||||
error "Unknown option $option" }
|
||||
}
|
||||
}
|
||||
|
||||
set data [split [exec cat "tests/assets/$baseconfig"] "\n"]
|
||||
set config {}
|
||||
if {$::tls} {
|
||||
@@ -353,13 +242,6 @@ proc start_server {options {code undefined}} {
|
||||
set stdout [format "%s/%s" [dict get $config "dir"] "stdout"]
|
||||
set stderr [format "%s/%s" [dict get $config "dir"] "stderr"]
|
||||
|
||||
# if we're inside a test, write the test name to the server log file
|
||||
if {[info exists ::cur_test]} {
|
||||
set fd [open $stdout "a+"]
|
||||
puts $fd "### Starting server for test $::cur_test"
|
||||
close $fd
|
||||
}
|
||||
|
||||
# We need a loop here to retry with different ports.
|
||||
set server_started 0
|
||||
while {$server_started == 0} {
|
||||
@@ -369,10 +251,44 @@ proc start_server {options {code undefined}} {
|
||||
|
||||
send_data_packet $::test_server_fd "server-spawning" "port $port"
|
||||
|
||||
set pid [spawn_server $config_file $stdout $stderr]
|
||||
if {$::valgrind} {
|
||||
set pid [exec valgrind --track-origins=yes --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/keydb-server $config_file > $stdout 2> $stderr &]
|
||||
} elseif ($::stack_logging) {
|
||||
set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/keydb-server $config_file > $stdout 2> $stderr &]
|
||||
} else {
|
||||
set pid [exec src/keydb-server $config_file > $stdout 2> $stderr &]
|
||||
}
|
||||
|
||||
# Tell the test server about this new instance.
|
||||
send_data_packet $::test_server_fd server-spawned $pid
|
||||
|
||||
# check that the server actually started
|
||||
set port_busy [wait_server_started $config_file $stdout $pid]
|
||||
# ugly but tries to be as fast as possible...
|
||||
if {$::valgrind} {set retrynum 1000} else {set retrynum 100}
|
||||
|
||||
# Wait for actual startup
|
||||
set checkperiod 100; # Milliseconds
|
||||
set maxiter [expr {120*1000/100}] ; # Wait up to 2 minutes.
|
||||
set port_busy 0
|
||||
while {![info exists _pid]} {
|
||||
regexp {PID:\s(\d+)} [exec cat $stdout] _ _pid
|
||||
after $checkperiod
|
||||
incr maxiter -1
|
||||
if {$maxiter == 0} {
|
||||
start_server_error $config_file "No PID detected in log $stdout"
|
||||
puts "--- LOG CONTENT ---"
|
||||
puts [exec cat $stdout]
|
||||
puts "-------------------"
|
||||
break
|
||||
}
|
||||
|
||||
# Check if the port is actually busy and the server failed
|
||||
# for this reason.
|
||||
if {[regexp {Could not create server TCP} [exec cat $stdout]]} {
|
||||
set port_busy 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Sometimes we have to try a different port, even if we checked
|
||||
# for availability. Other test clients may grab the port before we
|
||||
@@ -386,15 +302,9 @@ proc start_server {options {code undefined}} {
|
||||
dict set config port $port
|
||||
}
|
||||
create_server_config_file $config_file $config
|
||||
|
||||
# Truncate log so wait_server_started will not be looking at
|
||||
# output of the failed server.
|
||||
close [open $stdout "w"]
|
||||
|
||||
continue; # Try again
|
||||
}
|
||||
|
||||
if {$::valgrind} {set retrynum 1000} else {set retrynum 100}
|
||||
if {$code ne "undefined"} {
|
||||
set serverisup [server_is_up $::host $port $retrynum]
|
||||
} else {
|
||||
@@ -438,6 +348,12 @@ proc start_server {options {code undefined}} {
|
||||
error_and_quit $config_file $line
|
||||
}
|
||||
|
||||
if {$::wait_server} {
|
||||
set msg "server started PID: [dict get $srv "pid"]. press any key to continue..."
|
||||
puts $msg
|
||||
read stdin 1
|
||||
}
|
||||
|
||||
while 1 {
|
||||
# check that the server actually started and is ready for connections
|
||||
if {[exec grep -i "Ready to accept" | wc -l < $stdout] > 0} {
|
||||
@@ -457,12 +373,6 @@ proc start_server {options {code undefined}} {
|
||||
if {[catch { uplevel 1 $code } error]} {
|
||||
set backtrace $::errorInfo
|
||||
|
||||
# fetch srv back from the server list, in case it was restarted by restart_server (new PID)
|
||||
set srv [lindex $::servers end]
|
||||
|
||||
# pop the server object
|
||||
set ::servers [lrange $::servers 0 end-1]
|
||||
|
||||
# Kill the server without checking for leaks
|
||||
dict set srv "skipleaks" 1
|
||||
kill_server $srv
|
||||
@@ -477,23 +387,9 @@ proc start_server {options {code undefined}} {
|
||||
}
|
||||
puts ""
|
||||
|
||||
if {$::durable} {
|
||||
set msg [string range $error 10 end]
|
||||
lappend details $msg
|
||||
lappend details $backtrace
|
||||
lappend ::tests_failed $details
|
||||
|
||||
incr ::num_failed
|
||||
send_data_packet $::test_server_fd err [join $details "\n"]
|
||||
} else {
|
||||
# Re-raise, let handler up the stack take care of this.
|
||||
error $error $backtrace
|
||||
}
|
||||
error $error $backtrace
|
||||
}
|
||||
|
||||
# fetch srv back from the server list, in case it was restarted by restart_server (new PID)
|
||||
set srv [lindex $::servers end]
|
||||
|
||||
# Don't do the leak check when no tests were run
|
||||
if {$num_tests == $::num_tests} {
|
||||
dict set srv "skipleaks" 1
|
||||
@@ -504,51 +400,8 @@ proc start_server {options {code undefined}} {
|
||||
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
kill_server $srv
|
||||
if {!$keep_persistence} {
|
||||
clean_persistence $srv
|
||||
}
|
||||
set _ ""
|
||||
} else {
|
||||
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
||||
set _ $srv
|
||||
}
|
||||
}
|
||||
|
||||
proc restart_server {level wait_ready} {
|
||||
set srv [lindex $::servers end+$level]
|
||||
kill_server $srv
|
||||
|
||||
set stdout [dict get $srv "stdout"]
|
||||
set stderr [dict get $srv "stderr"]
|
||||
set config_file [dict get $srv "config_file"]
|
||||
|
||||
# if we're inside a test, write the test name to the server log file
|
||||
if {[info exists ::cur_test]} {
|
||||
set fd [open $stdout "a+"]
|
||||
puts $fd "### Restarting server for test $::cur_test"
|
||||
close $fd
|
||||
}
|
||||
|
||||
set prev_ready_count [exec grep -i "Ready to accept" | wc -l < $stdout]
|
||||
|
||||
set pid [spawn_server $config_file $stdout $stderr]
|
||||
|
||||
# check that the server actually started
|
||||
wait_server_started $config_file $stdout $pid
|
||||
|
||||
# update the pid in the servers list
|
||||
dict set srv "pid" $pid
|
||||
# re-set $srv in the servers list
|
||||
lset ::servers end+$level $srv
|
||||
|
||||
if {$wait_ready} {
|
||||
while 1 {
|
||||
# check that the server actually started and is ready for connections
|
||||
if {[exec grep -i "Ready to accept" | wc -l < $stdout] > $prev_ready_count} {
|
||||
break
|
||||
}
|
||||
after 10
|
||||
}
|
||||
}
|
||||
reconnect $level
|
||||
}
|
||||
+11
-27
@@ -4,7 +4,6 @@ set ::num_failed 0
|
||||
set ::num_skipped 0
|
||||
set ::num_aborted 0
|
||||
set ::tests_failed {}
|
||||
set ::cur_test ""
|
||||
|
||||
proc fail {msg} {
|
||||
error "assertion:$msg"
|
||||
@@ -100,7 +99,16 @@ proc wait_for_condition {maxtries delay e _else_ elsescript} {
|
||||
}
|
||||
}
|
||||
|
||||
proc test {name code {okpattern undefined} {options undefined}} {
|
||||
proc test {name code {okpattern undefined}} {
|
||||
# abort if tagged with a tag to deny
|
||||
foreach tag $::denytags {
|
||||
if {[lsearch $::tags $tag] >= 0} {
|
||||
incr ::num_aborted
|
||||
send_data_packet $::test_server_fd ignore $name
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# abort if test name in skiptests
|
||||
if {[lsearch $::skiptests $name] >= 0} {
|
||||
incr ::num_skipped
|
||||
@@ -135,39 +143,16 @@ proc test {name code {okpattern undefined} {options undefined}} {
|
||||
set details {}
|
||||
lappend details "$name in $::curfile"
|
||||
|
||||
# set a cur_test global to be logged into new servers that are spown
|
||||
# and log the test name in all existing servers
|
||||
set prev_test $::cur_test
|
||||
set ::cur_test "$name in $::curfile"
|
||||
if {!$::external} {
|
||||
foreach srv $::servers {
|
||||
set stdout [dict get $srv stdout]
|
||||
set fd [open $stdout "a+"]
|
||||
puts $fd "### Starting test $::cur_test"
|
||||
close $fd
|
||||
}
|
||||
}
|
||||
|
||||
send_data_packet $::test_server_fd testing $name
|
||||
|
||||
if {[catch {set retval [uplevel 1 $code]} error]} {
|
||||
set assertion [string match "assertion:*" $error]
|
||||
if {$assertion || $::durable} {
|
||||
if {[string match "assertion:*" $error]} {
|
||||
set msg [string range $error 10 end]
|
||||
lappend details $msg
|
||||
if {!$assertion} {
|
||||
lappend details $::errorInfo
|
||||
}
|
||||
lappend ::tests_failed $details
|
||||
|
||||
incr ::num_failed
|
||||
send_data_packet $::test_server_fd err [join $details "\n"]
|
||||
|
||||
if {$::stop_on_failure} {
|
||||
puts "Test error (last server port:[srv port], log:[srv stdout]), press enter to teardown the test."
|
||||
flush stdout
|
||||
gets stdin
|
||||
}
|
||||
} else {
|
||||
# Re-raise, let handler up the stack take care of this.
|
||||
error $error $::errorInfo
|
||||
@@ -192,5 +177,4 @@ proc test {name code {okpattern undefined} {options undefined}} {
|
||||
send_data_packet $::test_server_fd err "Detected a memory leak in test '$name': $output"
|
||||
}
|
||||
}
|
||||
set ::cur_test $prev_test
|
||||
}
|
||||
+5
-89
@@ -106,61 +106,25 @@ proc wait_for_ofs_sync {r1 r2} {
|
||||
}
|
||||
}
|
||||
|
||||
proc wait_done_loading r {
|
||||
wait_for_condition 50 100 {
|
||||
[catch {$r ping} e] == 0
|
||||
} else {
|
||||
fail "Loading DB is taking too much time."
|
||||
}
|
||||
}
|
||||
|
||||
# count current log lines in server's stdout
|
||||
proc count_log_lines {srv_idx} {
|
||||
set _ [exec wc -l < [srv $srv_idx stdout]]
|
||||
}
|
||||
|
||||
# verify pattern exists in server's sdtout after a certain line number
|
||||
proc verify_log_message {srv_idx pattern from_line} {
|
||||
incr from_line
|
||||
set result [exec tail -n +$from_line < [srv $srv_idx stdout]]
|
||||
if {![string match $pattern $result]} {
|
||||
error "assertion:expected message not found in log file: $pattern"
|
||||
}
|
||||
}
|
||||
|
||||
# wait for pattern to be found in server's stdout after certain line number
|
||||
# return value is a list containing the line that matched the pattern and the line number
|
||||
proc wait_for_log_messages {srv_idx patterns from_line maxtries delay} {
|
||||
proc wait_for_log_message {srv_idx pattern last_lines maxtries delay} {
|
||||
set retry $maxtries
|
||||
set next_line [expr $from_line + 1] ;# searching form the line after
|
||||
set stdout [srv $srv_idx stdout]
|
||||
while {$retry} {
|
||||
set result [exec tail -n +$next_line < $stdout]
|
||||
set result [exec tail -$last_lines < $stdout]
|
||||
set result [split $result "\n"]
|
||||
foreach line $result {
|
||||
foreach pattern $patterns {
|
||||
if {[string match $pattern $line]} {
|
||||
return [list $line $next_line]
|
||||
}
|
||||
if {[string match $pattern $line]} {
|
||||
return $line
|
||||
}
|
||||
incr next_line
|
||||
}
|
||||
incr retry -1
|
||||
after $delay
|
||||
}
|
||||
if {$retry == 0} {
|
||||
fail "log message of '$patterns' not found in $stdout after line: $from_line till line: [expr $next_line -1]"
|
||||
fail "log message of '$pattern' not found"
|
||||
}
|
||||
}
|
||||
|
||||
# write line to server log file
|
||||
proc write_log_line {srv_idx msg} {
|
||||
set logfile [srv $srv_idx stdout]
|
||||
set fd [open $logfile "a+"]
|
||||
puts $fd "### $msg"
|
||||
close $fd
|
||||
}
|
||||
|
||||
# Random integer between 0 and max (excluded).
|
||||
proc randomInt {max} {
|
||||
expr {int(rand()*$max)}
|
||||
@@ -439,29 +403,6 @@ proc colorstr {color str} {
|
||||
}
|
||||
}
|
||||
|
||||
proc find_valgrind_errors {stderr} {
|
||||
set fd [open $stderr]
|
||||
set buf [read $fd]
|
||||
close $fd
|
||||
|
||||
# Look for stack trace (" at 0x") and other errors (Invalid, Mismatched, etc).
|
||||
# Look for "Warnings", but not the "set address range perms". These don't indicate any real concern.
|
||||
# Look for the absense of a leak free summary (happens when redis isn't terminated properly).
|
||||
if {[regexp -- { at 0x} $buf] ||
|
||||
[regexp -- {^(?=.*Warning)(?:(?!set address range perms).)*$} $buf] ||
|
||||
[regexp -- {Invalid} $buf] ||
|
||||
[regexp -- {Mismatched} $buf] ||
|
||||
[regexp -- {uninitialized} $buf] ||
|
||||
[regexp -- {has a fishy} $buf] ||
|
||||
[regexp -- {overlap} $buf] ||
|
||||
(![regexp -- {definitely lost: 0 bytes} $buf] &&
|
||||
![regexp -- {no leaks are possible} $buf])} {
|
||||
return $buf
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
# Execute a background process writing random data for the specified number
|
||||
# of seconds to the specified Redis instance.
|
||||
proc start_write_load {host port seconds} {
|
||||
@@ -501,28 +442,3 @@ proc start_bg_complex_data {host port db ops} {
|
||||
proc stop_bg_complex_data {handle} {
|
||||
catch {exec /bin/kill -9 $handle}
|
||||
}
|
||||
|
||||
proc populate {num prefix size} {
|
||||
set rd [redis_deferring_client]
|
||||
for {set j 0} {$j < $num} {incr j} {
|
||||
$rd set $prefix$j [string repeat A $size]
|
||||
}
|
||||
for {set j 0} {$j < $num} {incr j} {
|
||||
$rd read
|
||||
}
|
||||
$rd close
|
||||
}
|
||||
|
||||
proc get_child_pid {idx} {
|
||||
set pid [srv $idx pid]
|
||||
if {[string match {*Darwin*} [exec uname -a]]} {
|
||||
set fd [open "|pgrep -P $pid" "r"]
|
||||
set child_pid [string trim [lindex [split [read $fd] \n] 0]]
|
||||
} else {
|
||||
set fd [open "|ps --ppid $pid -o pid" "r"]
|
||||
set child_pid [string trim [lindex [split [read $fd] \n] 1]]
|
||||
}
|
||||
close $fd
|
||||
|
||||
return $child_pid
|
||||
}
|
||||
+8
-65
@@ -38,7 +38,6 @@ set ::all_tests {
|
||||
unit/rreplay
|
||||
unit/cron
|
||||
unit/replication
|
||||
unit/latency-monitor
|
||||
integration/block-repl
|
||||
integration/replication
|
||||
integration/replication-2
|
||||
@@ -54,7 +53,6 @@ set ::all_tests {
|
||||
integration/psync2
|
||||
integration/psync2-reg
|
||||
integration/psync2-pingoff
|
||||
integration/redis-cli
|
||||
unit/pubsub
|
||||
unit/slowlog
|
||||
unit/scripting
|
||||
@@ -73,7 +71,6 @@ set ::all_tests {
|
||||
unit/pendingquerybuf
|
||||
unit/tls
|
||||
unit/tracking
|
||||
unit/oom-score-adj
|
||||
}
|
||||
# Index to the next test to run in the ::all_tests list.
|
||||
set ::next_test 0
|
||||
@@ -84,14 +81,12 @@ set ::baseport 21111; # initial port for spawned redis servers
|
||||
set ::portcount 8000; # we don't wanna use more than 10000 to avoid collision with cluster bus ports
|
||||
set ::traceleaks 0
|
||||
set ::valgrind 0
|
||||
set ::durable 0
|
||||
set ::tls 0
|
||||
set ::stack_logging 0
|
||||
set ::verbose 0
|
||||
set ::quiet 0
|
||||
set ::denytags {}
|
||||
set ::skiptests {}
|
||||
set ::skipunits {}
|
||||
set ::allowtags {}
|
||||
set ::only_tests {}
|
||||
set ::single_tests {}
|
||||
@@ -207,21 +202,6 @@ proc redis_deferring_client {args} {
|
||||
return $client
|
||||
}
|
||||
|
||||
proc redis_client {args} {
|
||||
set level 0
|
||||
if {[llength $args] > 0 && [string is integer [lindex $args 0]]} {
|
||||
set level [lindex $args 0]
|
||||
set args [lrange $args 1 end]
|
||||
}
|
||||
|
||||
# create client that defers reading reply
|
||||
set client [redis [srv $level "host"] [srv $level "port"] 0 $::tls]
|
||||
|
||||
# select the right db and read the response (OK)
|
||||
$client select 9
|
||||
return $client
|
||||
}
|
||||
|
||||
# Provide easy access to INFO properties. Same semantic as "proc r".
|
||||
proc s {args} {
|
||||
set level 0
|
||||
@@ -372,8 +352,8 @@ proc read_from_test_client fd {
|
||||
puts $err
|
||||
lappend ::failed_tests $err
|
||||
set ::active_clients_task($fd) "(ERR) $data"
|
||||
if {$::stop_on_failure} {
|
||||
puts -nonewline "(Test stopped, press enter to resume the tests)"
|
||||
if {$::stop_on_failure} {
|
||||
puts -nonewline "(Test stopped, press enter to continue)"
|
||||
flush stdout
|
||||
gets stdin
|
||||
}
|
||||
@@ -435,12 +415,6 @@ proc lpop {listVar {count 1}} {
|
||||
set ele
|
||||
}
|
||||
|
||||
proc lremove {listVar value} {
|
||||
upvar 1 $listVar var
|
||||
set idx [lsearch -exact $var $value]
|
||||
set var [lreplace $var $idx $idx]
|
||||
}
|
||||
|
||||
# A new client is idle. Remove it from the list of active clients and
|
||||
# if there are still test units to run, launch them.
|
||||
proc signal_idle_client fd {
|
||||
@@ -533,7 +507,6 @@ proc send_data_packet {fd status data} {
|
||||
proc print_help_screen {} {
|
||||
puts [join {
|
||||
"--valgrind Run the test over valgrind."
|
||||
"--durable suppress test crashes and keep running"
|
||||
"--stack-logging Enable OSX leaks/malloc stack logging."
|
||||
"--accurate Run slow randomized tests for more iterations."
|
||||
"--quiet Don't show individual tests."
|
||||
@@ -541,13 +514,11 @@ proc print_help_screen {} {
|
||||
"--list-tests List all the available test units."
|
||||
"--only <test> Just execute the specified test by test name. this option can be repeated."
|
||||
"--skip-till <unit> Skip all units until (and including) the specified one."
|
||||
"--skipunit <unit> Skip one unit."
|
||||
"--clients <num> Number of test clients (default 16)."
|
||||
"--timeout <sec> Test timeout in seconds (default 10 min)."
|
||||
"--force-failure Force the execution of a test that always fails."
|
||||
"--config <k> <v> Extra config file argument."
|
||||
"--skipfile <file> Name of a file containing test names that should be skipped (one per line)."
|
||||
"--skiptest <name> Name of a file containing test names that should be skipped (one per line)."
|
||||
"--dont-clean Don't delete redis log files after the run."
|
||||
"--stop Blocks once the first test fails."
|
||||
"--loop Execute the specified set of tests forever."
|
||||
@@ -585,9 +556,6 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
set file_data [read $fp]
|
||||
close $fp
|
||||
set ::skiptests [split $file_data "\n"]
|
||||
} elseif {$opt eq {--skiptest}} {
|
||||
lappend ::skiptests $arg
|
||||
incr j
|
||||
} elseif {$opt eq {--valgrind}} {
|
||||
set ::valgrind 1
|
||||
} elseif {$opt eq {--stack-logging}} {
|
||||
@@ -626,9 +594,6 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
} elseif {$opt eq {--only}} {
|
||||
lappend ::only_tests $arg
|
||||
incr j
|
||||
} elseif {$opt eq {--skipunit}} {
|
||||
lappend ::skipunits $arg
|
||||
incr j
|
||||
} elseif {$opt eq {--skip-till}} {
|
||||
set ::skip_till $arg
|
||||
incr j
|
||||
@@ -646,8 +611,6 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
} elseif {$opt eq {--clients}} {
|
||||
set ::numclients $arg
|
||||
incr j
|
||||
} elseif {$opt eq {--durable}} {
|
||||
set ::durable 1
|
||||
} elseif {$opt eq {--dont-clean}} {
|
||||
set ::dont_clean 1
|
||||
} elseif {$opt eq {--wait-server}} {
|
||||
@@ -673,23 +636,13 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
}
|
||||
}
|
||||
|
||||
set filtered_tests {}
|
||||
|
||||
# Set the filtered tests to be the short list (single_tests) if exists.
|
||||
# Otherwise, we start filtering all_tests
|
||||
if {[llength $::single_tests] > 0} {
|
||||
set filtered_tests $::single_tests
|
||||
} else {
|
||||
set filtered_tests $::all_tests
|
||||
}
|
||||
|
||||
# If --skip-till option was given, we populate the list of single tests
|
||||
# If --skil-till option was given, we populate the list of single tests
|
||||
# to run with everything *after* the specified unit.
|
||||
if {$::skip_till != ""} {
|
||||
set skipping 1
|
||||
foreach t $::all_tests {
|
||||
if {$skipping == 1} {
|
||||
lremove filtered_tests $t
|
||||
if {$skipping == 0} {
|
||||
lappend ::single_tests $t
|
||||
}
|
||||
if {$t == $::skip_till} {
|
||||
set skipping 0
|
||||
@@ -701,20 +654,10 @@ if {$::skip_till != ""} {
|
||||
}
|
||||
}
|
||||
|
||||
# If --skipunits option was given, we populate the list of single tests
|
||||
# to run with everything *not* in the skipunits list.
|
||||
if {[llength $::skipunits] > 0} {
|
||||
foreach t $::all_tests {
|
||||
if {[lsearch $::skipunits $t] != -1} {
|
||||
lremove filtered_tests $t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Override the list of tests with the specific tests we want to run
|
||||
# in case there was some filter, that is --single, -skipunit or --skip-till options.
|
||||
if {[llength $filtered_tests] < [llength $::all_tests]} {
|
||||
set ::all_tests $filtered_tests
|
||||
# in case there was some filter, that is --single or --skip-till options.
|
||||
if {[llength $::single_tests] > 0} {
|
||||
set ::all_tests $::single_tests
|
||||
}
|
||||
|
||||
proc attach_to_replication_stream {} {
|
||||
|
||||
@@ -255,48 +255,4 @@ start_server {tags {"acl"}} {
|
||||
r ACL setuser default on
|
||||
set e
|
||||
} {*NOAUTH*}
|
||||
|
||||
test {ACL HELP should not have unexpected options} {
|
||||
catch {r ACL help xxx} e
|
||||
assert_match "*Unknown subcommand or wrong number of arguments*" $e
|
||||
}
|
||||
}
|
||||
|
||||
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"]] {
|
||||
# user alice on allcommands allkeys >alice
|
||||
# user bob on -@all +@set +acl ~set* >bob
|
||||
|
||||
test "Alice: can excute all command" {
|
||||
r AUTH alice alice
|
||||
assert_equal "alice" [r acl whoami]
|
||||
r SET key value
|
||||
}
|
||||
|
||||
test "Bob: just excute @set and acl command" {
|
||||
r AUTH bob bob
|
||||
assert_equal "bob" [r acl whoami]
|
||||
assert_equal "3" [r sadd set 1 2 3]
|
||||
catch {r SET key value} e
|
||||
set e
|
||||
} {*NOPERM*}
|
||||
|
||||
test "ACL load and save" {
|
||||
r ACL setuser eve +get allkeys >eve on
|
||||
r ACL save
|
||||
|
||||
# ACL load will free user and kill clients
|
||||
r ACL load
|
||||
catch {r ACL LIST} e
|
||||
assert_match {*I/O error*} $e
|
||||
|
||||
reconnect
|
||||
r AUTH alice alice
|
||||
r SET key value
|
||||
r AUTH eve eve
|
||||
r GET key
|
||||
catch {r SET key value} e
|
||||
set e
|
||||
} {*NOPERM*}
|
||||
}
|
||||
+1
-12
@@ -36,18 +36,7 @@ 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]
|
||||
|
||||
@@ -313,14 +313,4 @@ start_server {tags {"expire"}} {
|
||||
after 3000
|
||||
assert_equal [r dbsize] 0
|
||||
}
|
||||
|
||||
test {SET - use KEEPTTL option, TTL should not be removed after loadaof} {
|
||||
r config set appendonly yes
|
||||
r set foo bar EX 100
|
||||
r set foo bar2 KEEPTTL
|
||||
after 2000
|
||||
r debug loadaof
|
||||
set ttl [r ttl foo]
|
||||
assert {$ttl <= 98 && $ttl > 90}
|
||||
}
|
||||
}
|
||||
@@ -78,8 +78,17 @@ start_server {tags {"introspection"}} {
|
||||
syslog-facility
|
||||
databases
|
||||
port
|
||||
tls-port
|
||||
io-threads
|
||||
tls-port
|
||||
tls-prefer-server-ciphers
|
||||
tls-cert-file
|
||||
tls-key-file
|
||||
tls-dh-params-file
|
||||
tls-ca-cert-file
|
||||
tls-ca-cert-dir
|
||||
tls-protocols
|
||||
tls-ciphers
|
||||
tls-ciphersuites
|
||||
logfile
|
||||
unixsocketperm
|
||||
slaveof
|
||||
@@ -90,25 +99,6 @@ start_server {tags {"introspection"}} {
|
||||
bio_cpulist
|
||||
aof_rewrite_cpulist
|
||||
bgsave_cpulist
|
||||
active-replica
|
||||
replicate-db-disable
|
||||
}
|
||||
|
||||
if {!$::tls} {
|
||||
append skip_configs {
|
||||
tls-prefer-server-ciphers
|
||||
tls-session-cache-timeout
|
||||
tls-session-cache-size
|
||||
tls-session-caching
|
||||
tls-cert-file
|
||||
tls-key-file
|
||||
tls-dh-params-file
|
||||
tls-ca-cert-file
|
||||
tls-ca-cert-dir
|
||||
tls-protocols
|
||||
tls-ciphers
|
||||
tls-ciphersuites
|
||||
}
|
||||
}
|
||||
|
||||
set configs {}
|
||||
@@ -137,28 +127,4 @@ start_server {tags {"introspection"}} {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
# Do a force-all config rewrite and make sure we're able to parse
|
||||
# it.
|
||||
test {CONFIG REWRITE sanity} {
|
||||
# Capture state of config before
|
||||
set configs {}
|
||||
foreach {k v} [r config get *] {
|
||||
dict set configs $k $v
|
||||
}
|
||||
|
||||
# Rewrite entire configuration, restart and confirm the
|
||||
# server is able to parse it and start.
|
||||
assert_equal [r debug config-rewrite-force-all] "OK"
|
||||
restart_server 0 0
|
||||
assert_equal [r ping] "PONG"
|
||||
|
||||
# Verify no changes were introduced
|
||||
dict for {k v} $configs {
|
||||
assert_equal $v [lindex [r config get $k] 1] $k
|
||||
}
|
||||
}
|
||||
|
||||
# Config file at this point is at a wierd state, and includes all
|
||||
# known keywords. Might be a good idea to avoid adding tests here.
|
||||
}
|
||||
@@ -50,26 +50,15 @@ start_server {tags {"latency-monitor"}} {
|
||||
|
||||
test {LATENCY of expire events are correctly collected} {
|
||||
r config set latency-monitor-threshold 20
|
||||
r flushdb
|
||||
if {$::valgrind} {set count 100000} else {set count 1000000}
|
||||
r eval {
|
||||
local i = 0
|
||||
while (i < tonumber(ARGV[1])) do
|
||||
redis.call('sadd',KEYS[1],i)
|
||||
while (i < 1000000) do
|
||||
redis.call('sadd','mybigkey',i)
|
||||
i = i+1
|
||||
end
|
||||
} 1 mybigkey $count
|
||||
r pexpire mybigkey 50
|
||||
wait_for_condition 5 100 {
|
||||
[r dbsize] == 0
|
||||
} else {
|
||||
fail "key wasn't expired"
|
||||
}
|
||||
} 0
|
||||
r pexpire mybigkey 1
|
||||
after 500
|
||||
assert_match {*expire-cycle*} [r latency latest]
|
||||
}
|
||||
|
||||
test {LATENCY HELP should not have unexpected options} {
|
||||
catch {r LATENCY help xxx} e
|
||||
assert_match "*Unknown subcommand or wrong number of arguments*" $e
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
# source tests/support/util.tcl
|
||||
|
||||
set testmodule [file normalize tests/modules/blockedclient.so]
|
||||
|
||||
start_server {tags {"modules"}} {
|
||||
r module load $testmodule
|
||||
|
||||
test {Locked GIL acquisition} {
|
||||
assert_match "OK" [r acquire_gil]
|
||||
}
|
||||
}
|
||||
@@ -114,21 +114,6 @@ tags "modules" {
|
||||
test {Test master link down hook} {
|
||||
r client kill type master
|
||||
assert_equal [r hooks.event_count masterlink-down] 1
|
||||
|
||||
wait_for_condition 50 100 {
|
||||
[string match {*master_link_status:up*} [r info replication]]
|
||||
} else {
|
||||
fail "Replica didn't reconnect"
|
||||
}
|
||||
|
||||
assert_equal [r hooks.event_count masterlink-down] 1
|
||||
assert_equal [r hooks.event_count masterlink-up] 2
|
||||
}
|
||||
|
||||
wait_for_condition 50 10 {
|
||||
[string match {*master_link_status:up*} [r info replication]]
|
||||
} else {
|
||||
fail "Can't turn the instance into a replica"
|
||||
}
|
||||
|
||||
$replica replicaof no one
|
||||
@@ -140,8 +125,8 @@ tags "modules" {
|
||||
}
|
||||
|
||||
test {Test replica-offline hook} {
|
||||
assert_equal [r -1 hooks.event_count replica-online] 2
|
||||
assert_equal [r -1 hooks.event_count replica-offline] 2
|
||||
assert_equal [r -1 hooks.event_count replica-online] 1
|
||||
assert_equal [r -1 hooks.event_count replica-offline] 1
|
||||
}
|
||||
# get the replica stdout, to be used by the next test
|
||||
set replica_stdout [srv 0 stdout]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
set testmodule [file normalize tests/modules/keyspace_events.so]
|
||||
|
||||
tags "modules" {
|
||||
start_server [list overrides [list loadmodule "$testmodule"]] {
|
||||
|
||||
test {Test loaded key space event} {
|
||||
r set x 1
|
||||
r hset y f v
|
||||
r lpush z 1 2 3
|
||||
r sadd p 1 2 3
|
||||
r zadd t 1 f1 2 f2
|
||||
r xadd s * f v
|
||||
r debug reload
|
||||
assert_equal {1 x} [r keyspace.is_key_loaded x]
|
||||
assert_equal {1 y} [r keyspace.is_key_loaded y]
|
||||
assert_equal {1 z} [r keyspace.is_key_loaded z]
|
||||
assert_equal {1 p} [r keyspace.is_key_loaded p]
|
||||
assert_equal {1 t} [r keyspace.is_key_loaded t]
|
||||
assert_equal {1 s} [r keyspace.is_key_loaded s]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,23 +67,4 @@ start_server {tags {"modules"}} {
|
||||
assert { $was_set == 0 }
|
||||
}
|
||||
|
||||
test {test module clientinfo api} {
|
||||
# Test basic sanity and SSL flag
|
||||
set info [r test.clientinfo]
|
||||
set ssl_flag [expr $::tls ? {"ssl:"} : {":"}]
|
||||
|
||||
assert { [dict get $info db] == 9 }
|
||||
assert { [dict get $info flags] == "${ssl_flag}::::" }
|
||||
|
||||
# Test MULTI flag
|
||||
r multi
|
||||
r test.clientinfo
|
||||
set info [lindex [r exec] 0]
|
||||
assert { [dict get $info flags] == "${ssl_flag}::::multi" }
|
||||
|
||||
# Test TRACKING flag
|
||||
r client tracking on
|
||||
set info [r test.clientinfo]
|
||||
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
|
||||
}
|
||||
}
|
||||
Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user