Merge pull request #10532 from oranagra/7.0-rc3
CI / test-ubuntu-latest (push) Has been cancelled
CI / test-sanitizer-address (push) Has been cancelled
CI / build-debian-old (push) Has been cancelled
CI / build-macos-latest (push) Has been cancelled
CI / build-32bit (push) Has been cancelled
CI / build-libc-malloc (push) Has been cancelled
CI / build-centos7-jemalloc (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
External Server Tests / test-external-standalone (push) Has been cancelled
External Server Tests / test-external-cluster (push) Has been cancelled
External Server Tests / test-external-nodebug (push) Has been cancelled
Spellcheck / Spellcheck (push) Has been cancelled
CI / test-ubuntu-latest (push) Has been cancelled
CI / test-sanitizer-address (push) Has been cancelled
CI / build-debian-old (push) Has been cancelled
CI / build-macos-latest (push) Has been cancelled
CI / build-32bit (push) Has been cancelled
CI / build-libc-malloc (push) Has been cancelled
CI / build-centos7-jemalloc (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
External Server Tests / test-external-standalone (push) Has been cancelled
External Server Tests / test-external-cluster (push) Has been cancelled
External Server Tests / test-external-nodebug (push) Has been cancelled
Spellcheck / Spellcheck (push) Has been cancelled
Release 7.0 rc3
This commit is contained in:
@@ -7,7 +7,7 @@ jobs:
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
test-sanitizer-address:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:oldoldstable
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential
|
||||
@@ -45,14 +45,14 @@ jobs:
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:7
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
yum -y install gcc make
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
# run weekly new vulnerability was added to the the database
|
||||
# run weekly new vulnerability was added to the database
|
||||
- cron: '0 0 * * 0'
|
||||
|
||||
jobs:
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
+291
-75
@@ -11,7 +11,7 @@ on:
|
||||
inputs:
|
||||
skipjobs:
|
||||
description: 'jobs to skip (delete the ones you wanna keep, do not leave empty)'
|
||||
default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,ubuntu,centos,malloc'
|
||||
default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc'
|
||||
skiptests:
|
||||
description: 'tests to skip (delete the ones you wanna keep, do not leave empty)'
|
||||
default: 'redis,modules,sentinel,cluster,unittest'
|
||||
@@ -34,8 +34,8 @@ jobs:
|
||||
test-ubuntu-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis') && !contains(github.event.inputs.skipjobs, 'ubuntu')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'ubuntu')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipping: ${{github.event.inputs.skipjobs}} and ${{github.event.inputs.skiptests}}"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -71,8 +71,8 @@ jobs:
|
||||
test-ubuntu-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis') && !contains(github.event.inputs.skipjobs, 'malloc')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'malloc')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -104,8 +104,8 @@ jobs:
|
||||
test-ubuntu-no-malloc-usable-size:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis') && !contains(github.event.inputs.skipjobs, 'malloc')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'malloc')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -137,8 +137,8 @@ jobs:
|
||||
test-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, '32bit')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, '32bit')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -177,8 +177,8 @@ jobs:
|
||||
test-ubuntu-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'tls')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -200,29 +200,25 @@ jobs:
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest --accurate --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-ubuntu-io-threads:
|
||||
test-ubuntu-tls-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'iothreads')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -230,7 +226,47 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
make BUILD_TLS=yes REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tclx tcl-tls
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-ubuntu-io-threads:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'iothreads')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -246,11 +282,11 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster --config io-threads 4 --config io-threads-do-reads yes ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-valgrind:
|
||||
test-valgrind-test:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'valgrind')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'valgrind') && !contains(github.event.inputs.skiptests, 'redis')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -258,7 +294,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -271,6 +307,29 @@ jobs:
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
|
||||
test-valgrind-misc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'valgrind') && !(contains(github.event.inputs.skiptests, 'modules') && contains(github.event.inputs.skiptests, 'unittest'))
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make valgrind REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
@@ -280,11 +339,11 @@ jobs:
|
||||
valgrind --track-origins=yes --suppressions=./src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full --log-file=err.txt ./src/redis-server test all
|
||||
if grep -q 0x err.txt; then cat err.txt; exit 1; fi
|
||||
|
||||
test-valgrind-no-malloc-usable-size:
|
||||
test-valgrind-no-malloc-usable-size-test:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'valgrind')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'valgrind') && !contains(github.event.inputs.skiptests, 'redis')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -292,12 +351,12 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE" REDIS_CFLAGS='-Werror'
|
||||
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE -DREDIS_TEST" REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -305,15 +364,43 @@ jobs:
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
|
||||
test-valgrind-no-malloc-usable-size-misc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'valgrind') && !(contains(github.event.inputs.skiptests, 'modules') && contains(github.event.inputs.skiptests, 'unittest'))
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE -DREDIS_TEST" REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: unittest
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: |
|
||||
valgrind --track-origins=yes --suppressions=./src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full --log-file=err.txt ./src/redis-server test all
|
||||
if grep -q 0x err.txt; then cat err.txt; exit 1; fi
|
||||
|
||||
test-sanitizer-address:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'sanitizer')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'sanitizer')
|
||||
timeout-minutes: 14400
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -326,7 +413,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -355,8 +442,8 @@ jobs:
|
||||
test-sanitizer-undefined:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'sanitizer')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'sanitizer')
|
||||
timeout-minutes: 14400
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -369,7 +456,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -398,8 +485,8 @@ jobs:
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis') && !contains(github.event.inputs.skipjobs, 'centos')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'centos')
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
@@ -408,7 +495,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -434,8 +521,8 @@ jobs:
|
||||
test-centos7-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'tls')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
@@ -444,7 +531,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -460,29 +547,26 @@ jobs:
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest --accurate --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-macos-latest:
|
||||
runs-on: macos-latest
|
||||
test-centos7-tls-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'macos')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -490,7 +574,49 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- 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 REDIS_CFLAGS='-Werror'"
|
||||
- name: testprep
|
||||
run: |
|
||||
yum -y install tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-macos-latest:
|
||||
runs-on: macos-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'macos') && !(contains(github.event.inputs.skiptests, 'redis') && contains(github.event.inputs.skiptests, 'modules'))
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -502,18 +628,12 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: ./runtest-moduleapi --verbose --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-10.15
|
||||
test-macos-latest-sentinel:
|
||||
runs-on: macos-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'freebsd')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'macos') && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -521,7 +641,51 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-macos-latest-cluster:
|
||||
runs-on: macos-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'macos') && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-freebsd:
|
||||
runs-on: macos-10.15
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !(contains(github.event.inputs.skiptests, 'redis') && contains(github.event.inputs.skiptests, 'modules'))
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -536,14 +700,66 @@ jobs:
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq redis ; then ./runtest --verbose --timeout 2400 --no-latency --dump-logs ${{github.event.inputs.test_args}} || exit 1 ; fi ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq modules ; then MAKE=gmake ./runtest-moduleapi --verbose --timeout 2400 --no-latency --dump-logs ${{github.event.inputs.test_args}} || exit 1 ; fi ;
|
||||
|
||||
test-freebsd-sentinel:
|
||||
runs-on: macos-10.15
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.1.6
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86 lang/tclx
|
||||
run: >
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq sentinel ; then ./runtest-sentinel ${{github.event.inputs.cluster_test_args}} || exit 1 ; fi ;
|
||||
|
||||
test-freebsd-cluster:
|
||||
runs-on: macos-10.15
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'freebsd') && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.1.6
|
||||
with:
|
||||
usesh: true
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: pkg install -y bash gmake lang/tcl86 lang/tclx
|
||||
run: >
|
||||
gmake || exit 1 ;
|
||||
if echo "${{github.event.inputs.skiptests}}" | grep -vq cluster ; then ./runtest-cluster ${{github.event.inputs.cluster_test_args}} || exit 1 ; fi ;
|
||||
|
||||
test-alpine-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'alpine')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'alpine')
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -551,7 +767,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -577,8 +793,8 @@ jobs:
|
||||
test-alpine-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'schedule' && github.repository == 'redis/redis')) && !contains(github.event.inputs.skipjobs, 'alpine')
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'alpine')
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -586,7 +802,7 @@ jobs:
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: pip cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
|
||||
+86
-1
@@ -1,5 +1,87 @@
|
||||
================================================================================
|
||||
Redis 7.0 RC1 Released Mon Feb 28 12:00:00 IST 2022
|
||||
Redis 7.0 RC3 Released Tue Apr 5 12:00:00 IST 2022
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is another Release Candidate of Redis 7.0.
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
New administrative and introspection commands and command arguments
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* CLUSTER SHARDS command deprecates CLUSTER SLOTS (#10293)
|
||||
|
||||
|
||||
Potentially Breaking Changes
|
||||
============================
|
||||
|
||||
* CONFIG GET response returned in a non-deterministic order.
|
||||
It's possible that a client was relying on configs order (#10323)
|
||||
* SORT / SORT_RO commands reject keys access patterns in GET and BY if ACL
|
||||
doesn't grant the command full keyspace access (#10340)
|
||||
* FUNCTION LOAD command introduced in 7.0-RC1 was stripped of the ENGINE, and
|
||||
NAME arguments which are now part of the script itself. The DESCRIPTION
|
||||
argument was completely removed (#10500)
|
||||
* Set disable-thp config to be immutable (#10409)
|
||||
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Optimize performance and memory usage on replicas (#10413)
|
||||
* A faster and more robust code of zslRandomLevel using RAND_MAX (#5539)
|
||||
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli: Use exit code 1 on error (#10468)
|
||||
* redis-cli: Do DNS lookup before sending CLUSTER MEET (#10436)
|
||||
* redis-benchmark: Fix --cluster with IPv6. (#10393)
|
||||
* redis-cli: Better --json Unicode support and --quoted-json (#10286)
|
||||
|
||||
|
||||
INFO fields and introspection changes
|
||||
=====================================
|
||||
|
||||
* MEMORY STATS: Show cluster.links memory usage (#10302)
|
||||
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* APIs for exposing module configs to config file and CONFIG command (#10285)
|
||||
* Add an event notifying about configuration changes (#10311)
|
||||
* Add API for redacting command arguments from SLOWLOG and MONITOR (#10425)
|
||||
* RM_Call: new flags for script mode compatibility, no writes, and error replies (#10372)
|
||||
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Sentinel: Fix no reconnect after auth-pass is changed (#10400)
|
||||
* Cluster: Fix race condition: Turn into replica on SETSLOT (#10489, #10381)
|
||||
* XREADGROUP: Unblock client when the stream key is deleted (#10306)
|
||||
|
||||
|
||||
Fixes for issue in previous release candidates of Redis 7.0
|
||||
-----------------------------------------------------------
|
||||
|
||||
* ACL DRYRUN does not validate the verified command args. (#10405)
|
||||
* ACL DRYRUN returns the tested common permission error (#10359)
|
||||
* Incorrect parsing of hostname information from nodes.conf (#10435)
|
||||
* BITSET and BITFIELD SET should propagate even if just length changed (#10459)
|
||||
* SHUTDOWN, Fix a possible crash when the shutdown was aborted (#10440)
|
||||
* Script should not allow may-replicate commands when client pause write (#10364)
|
||||
* Optimization tracking memory usage from i/o threads. (#10401)
|
||||
* Initialize help when using redis-cli help or redis-cli ? (#10382)
|
||||
* Dismiss COW of client output buffer now that it's dynamic (#10371)
|
||||
* Fix memory corruption when EVAL fails before being processed (#10519)
|
||||
|
||||
|
||||
================================================================================
|
||||
Redis 7.0 RC2 Released Mon Feb 28 12:00:00 IST 2022
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is another Release Candidate of Redis 7.0.
|
||||
@@ -439,6 +521,7 @@ A special thank you for the amount of work put into this release by:
|
||||
- Viktor Söderqvist
|
||||
- Wang Yuan
|
||||
- Harkrishn Patro
|
||||
- Nick Chun
|
||||
- Ozan Tezcan
|
||||
- Wen Hui
|
||||
- Huang Zhw
|
||||
@@ -449,8 +532,10 @@ A special thank you for the amount of work put into this release by:
|
||||
- Itamar Haber
|
||||
- Zhao Zhao
|
||||
- Itay Perry
|
||||
- Moti Cohen
|
||||
- Ning Sun
|
||||
- zhugezy
|
||||
- Ran Shidlansik
|
||||
- menwen
|
||||
- Andy Pan
|
||||
|
||||
|
||||
+7
-7
@@ -126,7 +126,7 @@ protected-mode yes
|
||||
#
|
||||
# no - Block for any connection (remain immutable)
|
||||
# yes - Allow for any connection (no protection)
|
||||
# local - Allow only for local local connections. Ones originating from the
|
||||
# local - Allow only for local connections. Ones originating from the
|
||||
# IPv4 address (127.0.0.1), IPv6 address (::1) or Unix domain sockets.
|
||||
#
|
||||
# enable-protected-configs no
|
||||
@@ -627,7 +627,7 @@ repl-diskless-sync-max-replicas 0
|
||||
#
|
||||
# In many cases the disk is slower than the network, and storing and loading
|
||||
# the RDB file may increase replication time (and even increase the master's
|
||||
# Copy on Write memory and salve buffers).
|
||||
# Copy on Write memory and replica buffers).
|
||||
# However, parsing the RDB file directly from the socket may mean that we have
|
||||
# to flush the contents of the current database before the full rdb was
|
||||
# received. For this reason we have the following options:
|
||||
@@ -1224,7 +1224,7 @@ replica-lazy-flush no
|
||||
|
||||
lazyfree-lazy-user-del no
|
||||
|
||||
# FLUSHDB, FLUSHALL, and SCRIPT FLUSH support both asynchronous and synchronous
|
||||
# FLUSHDB, FLUSHALL, SCRIPT FLUSH and FUNCTION FLUSH support both asynchronous and synchronous
|
||||
# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the
|
||||
# commands. When neither flag is passed, this directive will be used to determine
|
||||
# if the data should be deleted asynchronously.
|
||||
@@ -1287,7 +1287,7 @@ lazyfree-lazy-user-flush no
|
||||
# attempt to have background child processes killed before all others, and
|
||||
# replicas killed before masters.
|
||||
#
|
||||
# Redis supports three options:
|
||||
# Redis supports these options:
|
||||
#
|
||||
# no: Don't make changes to oom-score-adj (default).
|
||||
# yes: Alias to "relative" see below.
|
||||
@@ -1640,7 +1640,7 @@ aof-timestamp-enabled no
|
||||
# cluster-replica-no-failover no
|
||||
|
||||
# This option, when set to yes, allows nodes to serve read traffic while the
|
||||
# the cluster is in a down state, as long as it believes it owns the slots.
|
||||
# cluster is in a down state, as long as it believes it owns the slots.
|
||||
#
|
||||
# This is useful for two cases. The first case is for when an application
|
||||
# doesn't require consistency of data during node failures or network partitions.
|
||||
@@ -1958,7 +1958,7 @@ activerehashing yes
|
||||
# The limit can be set differently for the three different classes of clients:
|
||||
#
|
||||
# normal -> normal clients including MONITOR clients
|
||||
# replica -> replica clients
|
||||
# replica -> replica clients
|
||||
# pubsub -> clients subscribed to at least one pubsub channel or pattern
|
||||
#
|
||||
# The syntax of every client-output-buffer-limit directive is the following:
|
||||
@@ -2164,7 +2164,7 @@ rdb-save-incremental-fsync yes
|
||||
# defragmentation process. If you are not sure about what they mean it is
|
||||
# a good idea to leave the defaults untouched.
|
||||
|
||||
# Enabled active defragmentation
|
||||
# Active defragmentation is disabled by default
|
||||
# activedefrag no
|
||||
|
||||
# Minimum amount of fragmentation waste to start active defrag
|
||||
|
||||
@@ -20,6 +20,7 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/fork \
|
||||
--single unit/moduleapi/testrdb \
|
||||
--single unit/moduleapi/infotest \
|
||||
--single unit/moduleapi/moduleconfigs \
|
||||
--single unit/moduleapi/infra \
|
||||
--single unit/moduleapi/propagate \
|
||||
--single unit/moduleapi/hooks \
|
||||
|
||||
@@ -120,10 +120,7 @@ typedef struct {
|
||||
* understand if the command can be executed. */
|
||||
uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64];
|
||||
/* allowed_firstargs is used by ACL rules to block access to a command unless a
|
||||
* specific argv[1] is given (or argv[2] in case it is applied on a sub-command).
|
||||
* For example, a user can use the rule "-select +select|0" to block all
|
||||
* SELECT commands, except "SELECT 0".
|
||||
* And for a sub-command: "+config -config|set +config|set|loglevel"
|
||||
* specific argv[1] is given.
|
||||
*
|
||||
* For each command ID (corresponding to the command bit set in allowed_commands),
|
||||
* This array points to an array of SDS strings, terminated by a NULL pointer,
|
||||
@@ -1531,6 +1528,37 @@ static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keyle
|
||||
return ACL_DENIED_KEY;
|
||||
}
|
||||
|
||||
/* Checks if the provided selector selector has access specified in flags
|
||||
* to all keys in the keyspace. For example, CMD_KEY_READ access requires either
|
||||
* '%R~*', '~*', or allkeys to be granted to the selector. Returns 1 if all
|
||||
* the access flags are satisfied with this selector or 0 otherwise.
|
||||
*/
|
||||
static int ACLSelectorHasUnrestrictedKeyAccess(aclSelector *selector, int flags) {
|
||||
/* The selector can access any key */
|
||||
if (selector->flags & SELECTOR_FLAG_ALLKEYS) return 1;
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(selector->patterns,&li);
|
||||
|
||||
int access_flags = 0;
|
||||
if (flags & CMD_KEY_ACCESS) access_flags |= ACL_READ_PERMISSION;
|
||||
if (flags & CMD_KEY_INSERT) access_flags |= ACL_WRITE_PERMISSION;
|
||||
if (flags & CMD_KEY_DELETE) access_flags |= ACL_WRITE_PERMISSION;
|
||||
if (flags & CMD_KEY_UPDATE) access_flags |= ACL_WRITE_PERMISSION;
|
||||
|
||||
/* Test this key against every pattern. */
|
||||
while((ln = listNext(&li))) {
|
||||
keyPattern *pattern = listNodeValue(ln);
|
||||
if ((pattern->flags & access_flags) != access_flags)
|
||||
continue;
|
||||
if (!strcmp(pattern->pattern,"*")) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks a channel against a provided list of channels. The is_pattern
|
||||
* argument should only be used when subscribing (not when publishing)
|
||||
* and controls whether the input channel is evaluated as a channel pattern
|
||||
@@ -1675,6 +1703,39 @@ int ACLUserCheckKeyPerm(user *u, const char *key, int keylen, int flags) {
|
||||
return ACL_DENIED_KEY;
|
||||
}
|
||||
|
||||
/* Checks if the user can execute the given command with the added restriction
|
||||
* it must also have the access specified in flags to any key in the key space.
|
||||
* For example, CMD_KEY_READ access requires either '%R~*', '~*', or allkeys to be
|
||||
* granted in addition to the access required by the command. Returns 1
|
||||
* if the user has access or 0 otherwise.
|
||||
*/
|
||||
int ACLUserCheckCmdWithUnrestrictedKeyAccess(user *u, struct redisCommand *cmd, robj **argv, int argc, int flags) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
int local_idxptr;
|
||||
|
||||
/* If there is no associated user, the connection can run anything. */
|
||||
if (u == NULL) return 1;
|
||||
|
||||
/* For multiple selectors, we cache the key result in between selector
|
||||
* calls to prevent duplicate lookups. */
|
||||
aclKeyResultCache cache;
|
||||
initACLKeyResultCache(&cache);
|
||||
|
||||
/* Check each selector sequentially */
|
||||
listRewind(u->selectors,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
aclSelector *s = (aclSelector *) listNodeValue(ln);
|
||||
int acl_retval = ACLSelectorCheckCmd(s, cmd, argv, argc, &local_idxptr, &cache);
|
||||
if (acl_retval == ACL_OK && ACLSelectorHasUnrestrictedKeyAccess(s, flags)) {
|
||||
cleanupACLKeyResultCache(&cache);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
cleanupACLKeyResultCache(&cache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the channel can be accessed by the client according to
|
||||
* the ACLs associated with the specified user.
|
||||
*
|
||||
@@ -2411,6 +2472,22 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
|
||||
}
|
||||
}
|
||||
|
||||
const char* getAclErrorMessage(int acl_res) {
|
||||
/* Notice that a variant of this code also exists on aclCommand so
|
||||
* it also need to be updated on changed. */
|
||||
switch (acl_res) {
|
||||
case ACL_DENIED_CMD:
|
||||
return "can't run this command or subcommand";
|
||||
case ACL_DENIED_KEY:
|
||||
return "can't access at least one of the keys mentioned in the command arguments";
|
||||
case ACL_DENIED_CHANNEL:
|
||||
return "can't publish to the channel mentioned in the command";
|
||||
default:
|
||||
return "lacking the permissions for the command";
|
||||
}
|
||||
serverPanic("Reached deadcode on getAclErrorMessage");
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* ACL related commands
|
||||
* ==========================================================================*/
|
||||
@@ -2793,13 +2870,22 @@ setuser_cleanup:
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cmd->arity > 0 && cmd->arity != c->argc-3) ||
|
||||
(c->argc-3 < -cmd->arity))
|
||||
{
|
||||
addReplyErrorFormat(c,"wrong number of arguments for '%s' command", cmd->fullname);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx;
|
||||
int result = ACLCheckAllUserCommandPerm(u, cmd, c->argv + 3, c->argc - 3, &idx);
|
||||
/* Notice that a variant of this code also exists on getAclErrorMessage so
|
||||
* it also need to be updated on changed. */
|
||||
if (result != ACL_OK) {
|
||||
sds err = sdsempty();
|
||||
if (result == ACL_DENIED_CMD) {
|
||||
err = sdscatfmt(err, "This user has no permissions to run "
|
||||
"the '%s' command", c->cmd->fullname);
|
||||
"the '%s' command", cmd->fullname);
|
||||
} else if (result == ACL_DENIED_KEY) {
|
||||
err = sdscatfmt(err, "This user has no permissions to access "
|
||||
"the '%s' key", c->argv[idx + 3]->ptr);
|
||||
|
||||
@@ -813,10 +813,10 @@ int openNewIncrAofForAppend(void) {
|
||||
* AOFs has not reached the limit threshold.
|
||||
* */
|
||||
#define AOF_REWRITE_LIMITE_THRESHOLD 3
|
||||
#define AOF_REWRITE_LIMITE_NAX_MINUTES 60 /* 1 hour */
|
||||
#define AOF_REWRITE_LIMITE_MAX_MINUTES 60 /* 1 hour */
|
||||
int aofRewriteLimited(void) {
|
||||
int limit = 0;
|
||||
static int limit_deley_minutes = 0;
|
||||
static int limit_delay_minutes = 0;
|
||||
static time_t next_rewrite_time = 0;
|
||||
|
||||
unsigned long incr_aof_num = listLength(server.aof_manifest->incr_aof_list);
|
||||
@@ -824,25 +824,25 @@ int aofRewriteLimited(void) {
|
||||
if (server.unixtime < next_rewrite_time) {
|
||||
limit = 1;
|
||||
} else {
|
||||
if (limit_deley_minutes == 0) {
|
||||
if (limit_delay_minutes == 0) {
|
||||
limit = 1;
|
||||
limit_deley_minutes = 1;
|
||||
limit_delay_minutes = 1;
|
||||
} else {
|
||||
limit_deley_minutes *= 2;
|
||||
limit_delay_minutes *= 2;
|
||||
}
|
||||
|
||||
if (limit_deley_minutes > AOF_REWRITE_LIMITE_NAX_MINUTES) {
|
||||
limit_deley_minutes = AOF_REWRITE_LIMITE_NAX_MINUTES;
|
||||
if (limit_delay_minutes > AOF_REWRITE_LIMITE_MAX_MINUTES) {
|
||||
limit_delay_minutes = AOF_REWRITE_LIMITE_MAX_MINUTES;
|
||||
}
|
||||
|
||||
next_rewrite_time = server.unixtime + limit_deley_minutes * 60;
|
||||
next_rewrite_time = server.unixtime + limit_delay_minutes * 60;
|
||||
|
||||
serverLog(LL_WARNING,
|
||||
"Background AOF rewrite has repeatedly failed %ld times and triggered the limit, will retry in %d minutes",
|
||||
incr_aof_num, limit_deley_minutes);
|
||||
incr_aof_num, limit_delay_minutes);
|
||||
}
|
||||
} else {
|
||||
limit_deley_minutes = 0;
|
||||
limit_delay_minutes = 0;
|
||||
next_rewrite_time = 0;
|
||||
}
|
||||
|
||||
@@ -2142,19 +2142,9 @@ static int rewriteFunctions(rio *aof) {
|
||||
dictEntry *entry = NULL;
|
||||
while ((entry = dictNext(iter))) {
|
||||
functionLibInfo *li = dictGetVal(entry);
|
||||
if (li->desc) {
|
||||
if (rioWrite(aof, "*7\r\n", 4) == 0) goto werr;
|
||||
} else {
|
||||
if (rioWrite(aof, "*5\r\n", 4) == 0) goto werr;
|
||||
}
|
||||
if (rioWrite(aof, "*3\r\n", 4) == 0) goto werr;
|
||||
char function_load[] = "$8\r\nFUNCTION\r\n$4\r\nLOAD\r\n";
|
||||
if (rioWrite(aof, function_load, sizeof(function_load) - 1) == 0) goto werr;
|
||||
if (rioWriteBulkString(aof, li->ei->name, sdslen(li->ei->name)) == 0) goto werr;
|
||||
if (rioWriteBulkString(aof, li->name, sdslen(li->name)) == 0) goto werr;
|
||||
if (li->desc) {
|
||||
if (rioWriteBulkString(aof, "description", 11) == 0) goto werr;
|
||||
if (rioWriteBulkString(aof, li->desc, sdslen(li->desc)) == 0) goto werr;
|
||||
}
|
||||
if (rioWriteBulkString(aof, li->code, sdslen(li->code)) == 0) goto werr;
|
||||
}
|
||||
dictReleaseIterator(iter);
|
||||
|
||||
+13
-11
@@ -478,19 +478,21 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {
|
||||
* so that the 'maxbit' bit can be addressed. The object is finally
|
||||
* returned. Otherwise if the key holds a wrong type NULL is returned and
|
||||
* an error is sent to the client. */
|
||||
robj *lookupStringForBitCommand(client *c, uint64_t maxbit, int *created) {
|
||||
robj *lookupStringForBitCommand(client *c, uint64_t maxbit, int *dirty) {
|
||||
size_t byte = maxbit >> 3;
|
||||
robj *o = lookupKeyWrite(c->db,c->argv[1]);
|
||||
if (checkType(c,o,OBJ_STRING)) return NULL;
|
||||
if (dirty) *dirty = 0;
|
||||
|
||||
if (o == NULL) {
|
||||
if (created) *created = 1;
|
||||
o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));
|
||||
dbAdd(c->db,c->argv[1],o);
|
||||
if (dirty) *dirty = 1;
|
||||
} else {
|
||||
if (created) *created = 0;
|
||||
o = dbUnshareStringValue(c->db,c->argv[1],o);
|
||||
size_t oldlen = sdslen(o->ptr);
|
||||
o->ptr = sdsgrowzero(o->ptr,byte+1);
|
||||
if (dirty && oldlen != sdslen(o->ptr)) *dirty = 1;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
@@ -547,8 +549,8 @@ void setbitCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
int created;
|
||||
if ((o = lookupStringForBitCommand(c,bitoffset,&created)) == NULL) return;
|
||||
int dirty;
|
||||
if ((o = lookupStringForBitCommand(c,bitoffset,&dirty)) == NULL) return;
|
||||
|
||||
/* Get current values */
|
||||
byte = bitoffset >> 3;
|
||||
@@ -556,10 +558,10 @@ void setbitCommand(client *c) {
|
||||
bit = 7 - (bitoffset & 0x7);
|
||||
bitval = byteval & (1 << bit);
|
||||
|
||||
/* Either it is newly created, or the bit changes before and after.
|
||||
/* Either it is newly created, changed length, or the bit changes before and after.
|
||||
* Note that the bitval here is actually a decimal number.
|
||||
* So we need to use `!!` to convert it to 0 or 1 for comparison. */
|
||||
if (created || (!!bitval != on)) {
|
||||
if (dirty || (!!bitval != on)) {
|
||||
/* Update byte with new bit value. */
|
||||
byteval &= ~(1 << bit);
|
||||
byteval |= ((on & 0x1) << bit);
|
||||
@@ -1028,7 +1030,7 @@ struct bitfieldOp {
|
||||
void bitfieldGeneric(client *c, int flags) {
|
||||
robj *o;
|
||||
uint64_t bitoffset;
|
||||
int j, numops = 0, changes = 0, created = 0;
|
||||
int j, numops = 0, changes = 0, dirty = 0;
|
||||
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
|
||||
int owtype = BFOVERFLOW_WRAP; /* Overflow type. */
|
||||
int readonly = 1;
|
||||
@@ -1122,7 +1124,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
/* Lookup by making room up to the farthest bit reached by
|
||||
* this operation. */
|
||||
if ((o = lookupStringForBitCommand(c,
|
||||
highest_write_offset,&created)) == NULL) {
|
||||
highest_write_offset,&dirty)) == NULL) {
|
||||
zfree(ops);
|
||||
return;
|
||||
}
|
||||
@@ -1172,7 +1174,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
setSignedBitfield(o->ptr,thisop->offset,
|
||||
thisop->bits,newval);
|
||||
|
||||
if (created || (oldval != newval))
|
||||
if (dirty || (oldval != newval))
|
||||
changes++;
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
@@ -1204,7 +1206,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
setUnsignedBitfield(o->ptr,thisop->offset,
|
||||
thisop->bits,newval);
|
||||
|
||||
if (created || (oldval != newval))
|
||||
if (dirty || (oldval != newval))
|
||||
changes++;
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
|
||||
+90
-50
@@ -141,12 +141,7 @@ void processUnblockedClients(void) {
|
||||
* the code is conceptually more correct this way. */
|
||||
if (!(c->flags & CLIENT_BLOCKED)) {
|
||||
/* If we have a queued command, execute it now. */
|
||||
if (processPendingCommandsAndResetClient(c) == C_OK) {
|
||||
/* Now process client if it has more data in it's buffer. */
|
||||
if (c->querybuf && sdslen(c->querybuf) > 0) {
|
||||
if (processInputBuffer(c) == C_ERR) c = NULL;
|
||||
}
|
||||
} else {
|
||||
if (processPendingCommandAndInputBuffer(c) == C_ERR) {
|
||||
c = NULL;
|
||||
}
|
||||
}
|
||||
@@ -204,7 +199,7 @@ void unblockClient(client *c) {
|
||||
* we do not do it immediately after the command returns (when the
|
||||
* client got blocked) in order to be still able to access the argument
|
||||
* vector from module callbacks and updateStatsOnUnblock. */
|
||||
if (c->btype != BLOCKED_POSTPONE) {
|
||||
if (c->btype != BLOCKED_POSTPONE && c->btype != BLOCKED_SHUTDOWN) {
|
||||
freeClientOriginalArgv(c);
|
||||
resetClient(c);
|
||||
}
|
||||
@@ -288,25 +283,24 @@ void disconnectAllBlockedClients(void) {
|
||||
* when there may be clients blocked on a list key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
|
||||
/* Optimization: If no clients are in type BLOCKED_LIST,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_LIST]) return;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
int deleted = 0;
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while(numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = clientnode->value;
|
||||
|
||||
if (receiver->btype != BLOCKED_LIST) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listRotateHeadToTail(clients);
|
||||
continue;
|
||||
}
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_LIST) continue;
|
||||
|
||||
int deleted = 0;
|
||||
robj *dstkey = receiver->bpop.target;
|
||||
int wherefrom = receiver->bpop.blockpos.wherefrom;
|
||||
int whereto = receiver->bpop.blockpos.whereto;
|
||||
@@ -342,25 +336,24 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
|
||||
* when there may be clients blocked on a sorted set key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
|
||||
/* Optimization: If no clients are in type BLOCKED_ZSET,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_ZSET]) return;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
int deleted = 0;
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while (numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = clientnode->value;
|
||||
|
||||
if (receiver->btype != BLOCKED_ZSET) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listRotateHeadToTail(clients);
|
||||
continue;
|
||||
}
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_ZSET) continue;
|
||||
|
||||
int deleted = 0;
|
||||
long llen = zsetLength(o);
|
||||
long count = receiver->bpop.count;
|
||||
int where = receiver->bpop.blockpos.wherefrom;
|
||||
@@ -407,6 +400,10 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
|
||||
* when there may be clients blocked on a stream key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
|
||||
/* Optimization: If no clients are in type BLOCKED_STREAM,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_STREAM]) return;
|
||||
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
stream *s = o->ptr;
|
||||
|
||||
@@ -520,30 +517,21 @@ unblock_receiver:
|
||||
* see if the key is really able to serve the client, and in that case,
|
||||
* unblock it. */
|
||||
void serveClientsBlockedOnKeyByModule(readyList *rl) {
|
||||
dictEntry *de;
|
||||
|
||||
/* Optimization: If no clients are in type BLOCKED_MODULE,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_MODULE]) return;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
|
||||
while(numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = clientnode->value;
|
||||
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again: clients here may not be
|
||||
* ready to be served, so they'll remain in the list
|
||||
* sometimes. We want also be able to skip clients that are
|
||||
* not blocked for the MODULE type safely. */
|
||||
listRotateHeadToTail(clients);
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_MODULE) continue;
|
||||
|
||||
/* Note that if *this* client cannot be served by this key,
|
||||
@@ -566,6 +554,49 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for handleClientsBlockedOnKeys(). This function is called
|
||||
* when there may be clients blocked, via XREADGROUP, on an existing stream which
|
||||
* was deleted. We need to unblock the clients in that case.
|
||||
* The idea is that a client that is blocked via XREADGROUP is different from
|
||||
* any other blocking type in the sense that it depends on the existence of both
|
||||
* the key and the group. Even if the key is deleted and then revived with XADD
|
||||
* it won't help any clients blocked on XREADGROUP because the group no longer
|
||||
* exist, so they would fail with -NOGROUP anyway.
|
||||
* The conclusion is that it's better to unblock these client (with error) upon
|
||||
* the deletion of the key, rather than waiting for the first XADD. */
|
||||
void unblockDeletedStreamReadgroupClients(readyList *rl) {
|
||||
/* Optimization: If no clients are in type BLOCKED_STREAM,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_STREAM]) return;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_STREAM || !receiver->bpop.xread_group)
|
||||
continue;
|
||||
|
||||
long long prev_error_replies = server.stat_total_error_replies;
|
||||
client *old_client = server.current_client;
|
||||
server.current_client = receiver;
|
||||
monotime replyTimer;
|
||||
elapsedStart(&replyTimer);
|
||||
addReplyError(receiver, "-UNBLOCKED the stream key no longer exists");
|
||||
updateStatsOnUnblock(receiver, 0, elapsedUs(replyTimer), server.stat_total_error_replies != prev_error_replies);
|
||||
unblockClient(receiver);
|
||||
afterCommand(receiver);
|
||||
server.current_client = old_client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function should be called by Redis every time a single command,
|
||||
* a MULTI/EXEC block, or a Lua script, terminated its execution after
|
||||
* being called by a client. It handles serving clients blocked in
|
||||
@@ -624,17 +655,27 @@ void handleClientsBlockedOnKeys(void) {
|
||||
/* Serve clients blocked on the key. */
|
||||
robj *o = lookupKeyReadWithFlags(rl->db, rl->key, LOOKUP_NONOTIFY | LOOKUP_NOSTATS);
|
||||
if (o != NULL) {
|
||||
if (o->type == OBJ_LIST)
|
||||
int objtype = o->type;
|
||||
if (objtype == OBJ_LIST)
|
||||
serveClientsBlockedOnListKey(o,rl);
|
||||
else if (o->type == OBJ_ZSET)
|
||||
else if (objtype == OBJ_ZSET)
|
||||
serveClientsBlockedOnSortedSetKey(o,rl);
|
||||
else if (o->type == OBJ_STREAM)
|
||||
else if (objtype == OBJ_STREAM)
|
||||
serveClientsBlockedOnStreamKey(o,rl);
|
||||
/* We want to serve clients blocked on module keys
|
||||
* regardless of the object type: we don't know what the
|
||||
* module is trying to accomplish right now. */
|
||||
serveClientsBlockedOnKeyByModule(rl);
|
||||
/* If we have XREADGROUP clients blocked on this key, and
|
||||
* the key is not a stream, it must mean that the key was
|
||||
* overwritten by either SET or something like
|
||||
* (MULTI, DEL key, SADD key e, EXEC).
|
||||
* In this case we need to unblock all these clients. */
|
||||
if (objtype != OBJ_STREAM)
|
||||
unblockDeletedStreamReadgroupClients(rl);
|
||||
} else {
|
||||
/* Unblock all XREADGROUP clients of this deleted key */
|
||||
unblockDeletedStreamReadgroupClients(rl);
|
||||
/* Edge case: If lookupKeyReadWithFlags decides to expire the key we have to
|
||||
* take care of the propagation here, because afterCommand wasn't called */
|
||||
if (server.also_propagate.numops > 0)
|
||||
@@ -823,4 +864,3 @@ void signalKeyAsReady(redisDb *db, robj *key, int type) {
|
||||
incrRefCount(key);
|
||||
serverAssert(dictAdd(db->ready_keys,key,NULL) == DICT_OK);
|
||||
}
|
||||
|
||||
|
||||
@@ -525,3 +525,18 @@ CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_d
|
||||
res->deferred_error_list = deferred_error_list;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Create a new CallReply struct from the reply blob representing an error message.
|
||||
* Automatically creating deferred_error_list and set a copy of the reply in it.
|
||||
* Refer to callReplyCreate for detailed explanation. */
|
||||
CallReply *callReplyCreateError(sds reply, void *private_data) {
|
||||
sds err_buff = reply;
|
||||
if (err_buff[0] != '-') {
|
||||
err_buff = sdscatfmt(sdsempty(), "-ERR %S\r\n", reply);
|
||||
sdsfree(reply);
|
||||
}
|
||||
list *deferred_error_list = listCreate();
|
||||
listSetFreeMethod(deferred_error_list, (void (*)(void*))sdsfree);
|
||||
listAddNodeTail(deferred_error_list, sdsnew(err_buff));
|
||||
return callReplyCreate(err_buff, deferred_error_list, private_data);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
typedef struct CallReply CallReply;
|
||||
|
||||
CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_data);
|
||||
CallReply *callReplyCreateError(sds reply, void *private_data);
|
||||
int callReplyType(CallReply *rep);
|
||||
const char *callReplyGetString(CallReply *rep, size_t *len);
|
||||
long long callReplyGetLongLong(CallReply *rep);
|
||||
|
||||
+26
-1
@@ -299,7 +299,7 @@ static sds percentDecode(const char *pe, size_t len) {
|
||||
}
|
||||
|
||||
/* Parse a URI and extract the server connection information.
|
||||
* URI scheme is based on the the provisional specification[1] excluding support
|
||||
* URI scheme is based on the provisional specification[1] excluding support
|
||||
* for query parameters. Valid URIs are:
|
||||
* scheme: "redis://"
|
||||
* authority: [[<username> ":"] <password> "@"] [<hostname> [":" <port>]]
|
||||
@@ -371,3 +371,28 @@ void freeCliConnInfo(cliConnInfo connInfo){
|
||||
if (connInfo.auth) sdsfree(connInfo.auth);
|
||||
if (connInfo.user) sdsfree(connInfo.user);
|
||||
}
|
||||
|
||||
/*
|
||||
* Escape a Unicode string for JSON output (--json), following RFC 7159:
|
||||
* https://datatracker.ietf.org/doc/html/rfc7159#section-7
|
||||
*/
|
||||
sds escapeJsonString(sds s, const char *p, size_t len) {
|
||||
s = sdscatlen(s,"\"",1);
|
||||
while(len--) {
|
||||
switch(*p) {
|
||||
case '\\':
|
||||
case '"':
|
||||
s = sdscatprintf(s,"\\%c",*p);
|
||||
break;
|
||||
case '\n': s = sdscatlen(s,"\\n",2); break;
|
||||
case '\f': s = sdscatlen(s,"\\f",2); break;
|
||||
case '\r': s = sdscatlen(s,"\\r",2); break;
|
||||
case '\t': s = sdscatlen(s,"\\t",2); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",2); break;
|
||||
default:
|
||||
s = sdscatprintf(s,(*p >= 0 && *p <= 0x1f) ? "\\u%04x" : "%c",*p);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return sdscatlen(s,"\"",1);
|
||||
}
|
||||
|
||||
@@ -48,4 +48,7 @@ sds unquoteCString(char *str);
|
||||
void parseRedisUri(const char *uri, const char* tool_name, cliConnInfo *connInfo, int *tls_flag);
|
||||
|
||||
void freeCliConnInfo(cliConnInfo connInfo);
|
||||
|
||||
sds escapeJsonString(sds s, const char *p, size_t len);
|
||||
|
||||
#endif /* __CLICOMMON_H */
|
||||
|
||||
+266
-52
@@ -56,7 +56,6 @@ void clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request);
|
||||
void clusterUpdateState(void);
|
||||
int clusterNodeGetSlotBit(clusterNode *n, int slot);
|
||||
sds clusterGenNodesDescription(int filter, int use_pport);
|
||||
clusterNode *clusterLookupNode(const char *name);
|
||||
list *clusterGetNodesServingMySlots(clusterNode *node);
|
||||
int clusterNodeAddSlave(clusterNode *master, clusterNode *slave);
|
||||
int clusterAddSlot(clusterNode *n, int slot);
|
||||
@@ -74,6 +73,8 @@ void clusterCloseAllSlots(void);
|
||||
void clusterSetNodeAsMaster(clusterNode *n);
|
||||
void clusterDelNode(clusterNode *delnode);
|
||||
sds representClusterNodeFlags(sds ci, uint16_t flags);
|
||||
sds representSlotInfo(sds ci, uint16_t *slot_info_pairs, int slot_info_pairs_count);
|
||||
void clusterFreeNodesSlotsInfo(clusterNode *n);
|
||||
uint64_t clusterGetMaxEpoch(void);
|
||||
int clusterBumpConfigEpochWithoutConsensus(void);
|
||||
void moduleCallClusterReceivers(const char *sender_id, uint64_t module_id, uint8_t type, const unsigned char *payload, uint32_t len);
|
||||
@@ -210,7 +211,11 @@ int clusterLoadConfig(char *filename) {
|
||||
}
|
||||
|
||||
/* Create this node if it does not exist */
|
||||
n = clusterLookupNode(argv[0]);
|
||||
if (verifyClusterNodeId(argv[0], sdslen(argv[0])) == C_ERR) {
|
||||
sdsfreesplitres(argv, argc);
|
||||
goto fmterr;
|
||||
}
|
||||
n = clusterLookupNode(argv[0], sdslen(argv[0]));
|
||||
if (!n) {
|
||||
n = createClusterNode(argv[0],0);
|
||||
clusterAddNode(n);
|
||||
@@ -218,6 +223,17 @@ int clusterLoadConfig(char *filename) {
|
||||
/* Format for the node address information:
|
||||
* ip:port[@cport][,hostname] */
|
||||
|
||||
/* Hostname is an optional argument that defines the endpoint
|
||||
* that can be reported to clients instead of IP. */
|
||||
char *hostname = strchr(argv[1], ',');
|
||||
if (hostname) {
|
||||
*hostname = '\0';
|
||||
hostname++;
|
||||
n->hostname = sdscpy(n->hostname, hostname);
|
||||
} else if (sdslen(n->hostname) != 0) {
|
||||
sdsclear(n->hostname);
|
||||
}
|
||||
|
||||
/* Address and port */
|
||||
if ((p = strrchr(argv[1],':')) == NULL) {
|
||||
sdsfreesplitres(argv,argc);
|
||||
@@ -237,17 +253,6 @@ int clusterLoadConfig(char *filename) {
|
||||
* base port. */
|
||||
n->cport = busp ? atoi(busp) : n->port + CLUSTER_PORT_INCR;
|
||||
|
||||
/* Hostname is an optional argument that defines the endpoint
|
||||
* that can be reported to clients instead of IP. */
|
||||
char *hostname = strchr(p, ',');
|
||||
if (hostname) {
|
||||
*hostname = '\0';
|
||||
hostname++;
|
||||
n->hostname = sdscpy(n->hostname, hostname);
|
||||
} else if (sdslen(n->hostname) != 0) {
|
||||
sdsclear(n->hostname);
|
||||
}
|
||||
|
||||
/* The plaintext port for client in a TLS cluster (n->pport) is not
|
||||
* stored in nodes.conf. It is received later over the bus protocol. */
|
||||
|
||||
@@ -286,7 +291,11 @@ int clusterLoadConfig(char *filename) {
|
||||
/* Get master if any. Set the master and populate master's
|
||||
* slave list. */
|
||||
if (argv[3][0] != '-') {
|
||||
master = clusterLookupNode(argv[3]);
|
||||
if (verifyClusterNodeId(argv[3], sdslen(argv[3])) == C_ERR) {
|
||||
sdsfreesplitres(argv, argc);
|
||||
goto fmterr;
|
||||
}
|
||||
master = clusterLookupNode(argv[3], sdslen(argv[3]));
|
||||
if (!master) {
|
||||
master = createClusterNode(argv[3],0);
|
||||
clusterAddNode(master);
|
||||
@@ -322,7 +331,14 @@ int clusterLoadConfig(char *filename) {
|
||||
goto fmterr;
|
||||
}
|
||||
p += 3;
|
||||
cn = clusterLookupNode(p);
|
||||
|
||||
char *pr = strchr(p, ']');
|
||||
size_t node_len = pr - p;
|
||||
if (pr == NULL || verifyClusterNodeId(p, node_len) == C_ERR) {
|
||||
sdsfreesplitres(argv, argc);
|
||||
goto fmterr;
|
||||
}
|
||||
cn = clusterLookupNode(p, CLUSTER_NAMELEN);
|
||||
if (!cn) {
|
||||
cn = createClusterNode(p,0);
|
||||
clusterAddNode(cn);
|
||||
@@ -796,7 +812,7 @@ void setClusterNodeToInboundClusterLink(clusterNode *node, clusterLink *link) {
|
||||
* we would always process the disconnection of the existing inbound link before
|
||||
* accepting a new existing inbound link. Therefore, it's possible to have more than
|
||||
* one inbound link from the same node at the same time. */
|
||||
serverLog(LL_DEBUG, "Replacing inbound link fd %d from node %s with fd %d",
|
||||
serverLog(LL_DEBUG, "Replacing inbound link fd %d from node %.40s with fd %d",
|
||||
node->inbound_link->conn->fd, node->name, link->conn->fd);
|
||||
}
|
||||
node->inbound_link = link;
|
||||
@@ -942,7 +958,9 @@ clusterNode *createClusterNode(char *nodename, int flags) {
|
||||
node->configEpoch = 0;
|
||||
node->flags = flags;
|
||||
memset(node->slots,0,sizeof(node->slots));
|
||||
node->slots_info = NULL;
|
||||
node->slot_info_pairs = NULL;
|
||||
node->slot_info_pairs_count = 0;
|
||||
node->slot_info_pairs_alloc = 0;
|
||||
node->numslots = 0;
|
||||
node->numslaves = 0;
|
||||
node->slaves = NULL;
|
||||
@@ -1178,12 +1196,23 @@ void clusterDelNode(clusterNode *delnode) {
|
||||
freeClusterNode(delnode);
|
||||
}
|
||||
|
||||
/* Node lookup by name */
|
||||
clusterNode *clusterLookupNode(const char *name) {
|
||||
sds s = sdsnewlen(name, CLUSTER_NAMELEN);
|
||||
dictEntry *de;
|
||||
/* Cluster node sanity check. Returns C_OK if the node id
|
||||
* is valid an C_ERR otherwise. */
|
||||
int verifyClusterNodeId(const char *name, int length) {
|
||||
if (length != CLUSTER_NAMELEN) return C_ERR;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
return C_ERR;
|
||||
}
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
de = dictFind(server.cluster->nodes,s);
|
||||
/* Node lookup by name */
|
||||
clusterNode *clusterLookupNode(const char *name, int length) {
|
||||
if (verifyClusterNodeId(name, length) != C_OK) return NULL;
|
||||
sds s = sdsnewlen(name, length);
|
||||
dictEntry *de = dictFind(server.cluster->nodes, s);
|
||||
sdsfree(s);
|
||||
if (de == NULL) return NULL;
|
||||
return dictGetVal(de);
|
||||
@@ -1599,7 +1628,7 @@ int clusterStartHandshake(char *ip, int port, int cport) {
|
||||
void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
uint16_t count = ntohs(hdr->count);
|
||||
clusterMsgDataGossip *g = (clusterMsgDataGossip*) hdr->data.ping.gossip;
|
||||
clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);
|
||||
clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender, CLUSTER_NAMELEN);
|
||||
|
||||
while(count--) {
|
||||
uint16_t flags = ntohs(g->flags);
|
||||
@@ -1618,7 +1647,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
}
|
||||
|
||||
/* Update our state accordingly to the gossip sections */
|
||||
node = clusterLookupNode(g->nodename);
|
||||
node = clusterLookupNode(g->nodename, CLUSTER_NAMELEN);
|
||||
if (node) {
|
||||
/* We already know this node.
|
||||
Handle failure reports, only when the sender is a master. */
|
||||
@@ -1895,6 +1924,17 @@ void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t senderConfigEpoc
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
|
||||
CLUSTER_TODO_UPDATE_STATE|
|
||||
CLUSTER_TODO_FSYNC_CONFIG);
|
||||
} else if (myself->slaveof && myself->slaveof->slaveof) {
|
||||
/* Safeguard against sub-replicas. A replica's master can turn itself
|
||||
* into a replica if its last slot is removed. If no other node takes
|
||||
* over the slot, there is nothing else to trigger replica migration. */
|
||||
serverLog(LL_WARNING,
|
||||
"I'm a sub-replica! Reconfiguring myself as a replica of grandmaster %.40s",
|
||||
myself->slaveof->slaveof->name);
|
||||
clusterSetMaster(myself->slaveof->slaveof);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
|
||||
CLUSTER_TODO_UPDATE_STATE|
|
||||
CLUSTER_TODO_FSYNC_CONFIG);
|
||||
} else if (dirty_slots_count) {
|
||||
/* If we are here, we received an update message which removed
|
||||
* ownership for certain slots we still have keys about, but still
|
||||
@@ -1970,7 +2010,7 @@ int writeHostnamePingExt(clusterMsgPingExt **cursor) {
|
||||
/* We previously validated the extensions, so this function just needs to
|
||||
* handle the extensions. */
|
||||
void clusterProcessPingExtensions(clusterMsg *hdr, clusterLink *link) {
|
||||
clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender);
|
||||
clusterNode *sender = link->node ? link->node : clusterLookupNode(hdr->sender, CLUSTER_NAMELEN);
|
||||
char *ext_hostname = NULL;
|
||||
uint16_t extensions = ntohs(hdr->extensions);
|
||||
/* Loop through all the extensions and process them */
|
||||
@@ -2003,7 +2043,7 @@ static clusterNode *getNodeFromLinkAndMsg(clusterLink *link, clusterMsg *hdr) {
|
||||
sender = link->node;
|
||||
} else {
|
||||
/* Otherwise, fetch sender based on the message */
|
||||
sender = clusterLookupNode(hdr->sender);
|
||||
sender = clusterLookupNode(hdr->sender, CLUSTER_NAMELEN);
|
||||
/* We know the sender node but haven't associate it with the link. This must
|
||||
* be an inbound link because only for inbound links we didn't know which node
|
||||
* to associate when they were created. */
|
||||
@@ -2213,7 +2253,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
if (type == CLUSTERMSG_TYPE_PING || type == CLUSTERMSG_TYPE_PONG ||
|
||||
type == CLUSTERMSG_TYPE_MEET)
|
||||
{
|
||||
serverLog(LL_DEBUG,"%s packet received: %s",
|
||||
serverLog(LL_DEBUG,"%s packet received: %.40s",
|
||||
clusterGetMessageTypeString(type),
|
||||
link->node ? link->node->name : "NULL");
|
||||
if (!link->inbound) {
|
||||
@@ -2314,7 +2354,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
clusterSetNodeAsMaster(sender);
|
||||
} else {
|
||||
/* Node is a slave. */
|
||||
clusterNode *master = clusterLookupNode(hdr->slaveof);
|
||||
clusterNode *master = clusterLookupNode(hdr->slaveof, CLUSTER_NAMELEN);
|
||||
|
||||
if (nodeIsMaster(sender)) {
|
||||
/* Master turned into a slave! Reconfigure the node. */
|
||||
@@ -2429,7 +2469,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
clusterNode *failing;
|
||||
|
||||
if (sender) {
|
||||
failing = clusterLookupNode(hdr->data.fail.about.nodename);
|
||||
failing = clusterLookupNode(hdr->data.fail.about.nodename, CLUSTER_NAMELEN);
|
||||
if (failing &&
|
||||
!(failing->flags & (CLUSTER_NODE_FAIL|CLUSTER_NODE_MYSELF)))
|
||||
{
|
||||
@@ -2517,7 +2557,7 @@ int clusterProcessPacket(clusterLink *link) {
|
||||
ntohu64(hdr->data.update.nodecfg.configEpoch);
|
||||
|
||||
if (!sender) return 1; /* We don't know the sender. */
|
||||
n = clusterLookupNode(hdr->data.update.nodecfg.nodename);
|
||||
n = clusterLookupNode(hdr->data.update.nodecfg.nodename, CLUSTER_NAMELEN);
|
||||
if (!n) return 1; /* We don't know the reported node. */
|
||||
if (n->configEpoch >= reportedConfigEpoch) return 1; /* Nothing new. */
|
||||
|
||||
@@ -3148,7 +3188,7 @@ int clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uin
|
||||
clusterNode *node = NULL;
|
||||
|
||||
if (target != NULL) {
|
||||
node = clusterLookupNode(target);
|
||||
node = clusterLookupNode(target, strlen(target));
|
||||
if (node == NULL || node->link == NULL) return C_ERR;
|
||||
}
|
||||
|
||||
@@ -4561,6 +4601,22 @@ sds representClusterNodeFlags(sds ci, uint16_t flags) {
|
||||
return ci;
|
||||
}
|
||||
|
||||
/* Concatenate the slot ownership information to the given SDS string 'ci'.
|
||||
* If the slot ownership is in a contiguous block, it's represented as start-end pair,
|
||||
* else each slot is added separately. */
|
||||
sds representSlotInfo(sds ci, uint16_t *slot_info_pairs, int slot_info_pairs_count) {
|
||||
for (int i = 0; i< slot_info_pairs_count; i+=2) {
|
||||
unsigned long start = slot_info_pairs[i];
|
||||
unsigned long end = slot_info_pairs[i+1];
|
||||
if (start == end) {
|
||||
ci = sdscatfmt(ci, " %i", start);
|
||||
} else {
|
||||
ci = sdscatfmt(ci, " %i-%i", start, end);
|
||||
}
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
/* Generate a csv-alike representation of the specified cluster node.
|
||||
* See clusterGenNodesDescription() top comment for more information.
|
||||
*
|
||||
@@ -4609,8 +4665,8 @@ sds clusterGenNodeDescription(clusterNode *node, int use_pport) {
|
||||
|
||||
/* Slots served by this instance. If we already have slots info,
|
||||
* append it directly, otherwise, generate slots only if it has. */
|
||||
if (node->slots_info) {
|
||||
ci = sdscatsds(ci, node->slots_info);
|
||||
if (node->slot_info_pairs) {
|
||||
ci = representSlotInfo(ci, node->slot_info_pairs, node->slot_info_pairs_count);
|
||||
} else if (node->numslots > 0) {
|
||||
start = -1;
|
||||
for (j = 0; j < CLUSTER_SLOTS; j++) {
|
||||
@@ -4670,12 +4726,15 @@ void clusterGenNodesSlotsInfo(int filter) {
|
||||
* or end of slot. */
|
||||
if (i == CLUSTER_SLOTS || n != server.cluster->slots[i]) {
|
||||
if (!(n->flags & filter)) {
|
||||
if (n->slots_info == NULL) n->slots_info = sdsempty();
|
||||
if (start == i-1) {
|
||||
n->slots_info = sdscatfmt(n->slots_info," %i",start);
|
||||
} else {
|
||||
n->slots_info = sdscatfmt(n->slots_info," %i-%i",start,i-1);
|
||||
if (n->slot_info_pairs_count+2 > n->slot_info_pairs_alloc) {
|
||||
if (n->slot_info_pairs_alloc == 0)
|
||||
n->slot_info_pairs_alloc = 8;
|
||||
else
|
||||
n->slot_info_pairs_alloc *= 2;
|
||||
n->slot_info_pairs = zrealloc(n->slot_info_pairs, n->slot_info_pairs_alloc * sizeof(uint16_t));
|
||||
}
|
||||
n->slot_info_pairs[n->slot_info_pairs_count++] = start;
|
||||
n->slot_info_pairs[n->slot_info_pairs_count++] = i-1;
|
||||
}
|
||||
if (i == CLUSTER_SLOTS) break;
|
||||
n = server.cluster->slots[i];
|
||||
@@ -4684,6 +4743,13 @@ void clusterGenNodesSlotsInfo(int filter) {
|
||||
}
|
||||
}
|
||||
|
||||
void clusterFreeNodesSlotsInfo(clusterNode *n) {
|
||||
zfree(n->slot_info_pairs);
|
||||
n->slot_info_pairs = NULL;
|
||||
n->slot_info_pairs_count = 0;
|
||||
n->slot_info_pairs_alloc = 0;
|
||||
}
|
||||
|
||||
/* Generate a csv-alike representation of the nodes we are aware of,
|
||||
* including the "myself" node, and return an SDS string containing the
|
||||
* representation (it is up to the caller to free it).
|
||||
@@ -4718,10 +4784,7 @@ sds clusterGenNodesDescription(int filter, int use_pport) {
|
||||
ci = sdscatlen(ci,"\n",1);
|
||||
|
||||
/* Release slots info. */
|
||||
if (node->slots_info) {
|
||||
sdsfree(node->slots_info);
|
||||
node->slots_info = NULL;
|
||||
}
|
||||
clusterFreeNodesSlotsInfo(node);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
return ci;
|
||||
@@ -4942,6 +5005,136 @@ void addNodeReplyForClusterSlot(client *c, clusterNode *node, int start_slot, in
|
||||
setDeferredArrayLen(c, nested_replylen, nested_elements);
|
||||
}
|
||||
|
||||
/* Add detailed information of a node to the output buffer of the given client. */
|
||||
void addNodeDetailsToShardReply(client *c, clusterNode *node) {
|
||||
int reply_count = 0;
|
||||
void *node_replylen = addReplyDeferredLen(c);
|
||||
addReplyBulkCString(c, "id");
|
||||
addReplyBulkCBuffer(c, node->name, CLUSTER_NAMELEN);
|
||||
reply_count++;
|
||||
|
||||
/* We use server.tls_cluster as a proxy for whether or not
|
||||
* the remote port is the tls port or not */
|
||||
int plaintext_port = server.tls_cluster ? node->pport : node->port;
|
||||
int tls_port = server.tls_cluster ? node->port : 0;
|
||||
if (plaintext_port) {
|
||||
addReplyBulkCString(c, "port");
|
||||
addReplyLongLong(c, plaintext_port);
|
||||
reply_count++;
|
||||
}
|
||||
|
||||
if (tls_port) {
|
||||
addReplyBulkCString(c, "tls-port");
|
||||
addReplyLongLong(c, tls_port);
|
||||
reply_count++;
|
||||
}
|
||||
|
||||
addReplyBulkCString(c, "ip");
|
||||
addReplyBulkCString(c, node->ip);
|
||||
reply_count++;
|
||||
|
||||
addReplyBulkCString(c, "endpoint");
|
||||
addReplyBulkCString(c, getPreferredEndpoint(node));
|
||||
reply_count++;
|
||||
|
||||
if (node->hostname) {
|
||||
addReplyBulkCString(c, "hostname");
|
||||
addReplyBulkCString(c, node->hostname);
|
||||
reply_count++;
|
||||
}
|
||||
|
||||
long long node_offset;
|
||||
if (node->flags & CLUSTER_NODE_MYSELF) {
|
||||
node_offset = nodeIsSlave(node) ? replicationGetSlaveOffset() : server.master_repl_offset;
|
||||
} else {
|
||||
node_offset = node->repl_offset;
|
||||
}
|
||||
|
||||
addReplyBulkCString(c, "role");
|
||||
addReplyBulkCString(c, nodeIsSlave(node) ? "replica" : "master");
|
||||
reply_count++;
|
||||
|
||||
addReplyBulkCString(c, "replication-offset");
|
||||
addReplyLongLong(c, node_offset);
|
||||
reply_count++;
|
||||
|
||||
addReplyBulkCString(c, "health");
|
||||
const char *health_msg = NULL;
|
||||
if (nodeFailed(node)) {
|
||||
health_msg = "fail";
|
||||
} else if (nodeIsSlave(node) && node_offset == 0) {
|
||||
health_msg = "loading";
|
||||
} else {
|
||||
health_msg = "online";
|
||||
}
|
||||
addReplyBulkCString(c, health_msg);
|
||||
reply_count++;
|
||||
|
||||
setDeferredMapLen(c, node_replylen, reply_count);
|
||||
}
|
||||
|
||||
/* Add the shard reply of a single shard based off the given primary node. */
|
||||
void addShardReplyForClusterShards(client *c, clusterNode *node, uint16_t *slot_info_pairs, int slot_pairs_count) {
|
||||
addReplyMapLen(c, 2);
|
||||
addReplyBulkCString(c, "slots");
|
||||
if (slot_info_pairs) {
|
||||
serverAssert((slot_pairs_count % 2) == 0);
|
||||
addReplyArrayLen(c, slot_pairs_count);
|
||||
for (int i = 0; i < slot_pairs_count; i++)
|
||||
addReplyBulkLongLong(c, (unsigned long)slot_info_pairs[i]);
|
||||
} else {
|
||||
/* If no slot info pair is provided, the node owns no slots */
|
||||
addReplyArrayLen(c, 0);
|
||||
}
|
||||
|
||||
addReplyBulkCString(c, "nodes");
|
||||
list *nodes_for_slot = clusterGetNodesServingMySlots(node);
|
||||
/* At least the provided node should be serving its slots */
|
||||
serverAssert(nodes_for_slot);
|
||||
addReplyArrayLen(c, listLength(nodes_for_slot));
|
||||
if (listLength(nodes_for_slot) != 0) {
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(nodes_for_slot, &li);
|
||||
while ((ln = listNext(&li))) {
|
||||
clusterNode *node = listNodeValue(ln);
|
||||
addNodeDetailsToShardReply(c, node);
|
||||
}
|
||||
listRelease(nodes_for_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add to the output buffer of the given client, an array of slot (start, end)
|
||||
* pair owned by the shard, also the primary and set of replica(s) along with
|
||||
* information about each node. */
|
||||
void clusterReplyShards(client *c) {
|
||||
void *shard_replylen = addReplyDeferredLen(c);
|
||||
int shard_count = 0;
|
||||
/* This call will add slot_info_pairs to all nodes */
|
||||
clusterGenNodesSlotsInfo(0);
|
||||
dictIterator *di = dictGetSafeIterator(server.cluster->nodes);
|
||||
dictEntry *de;
|
||||
/* Iterate over all the available nodes in the cluster, for each primary
|
||||
* node return generate the cluster shards response. if the primary node
|
||||
* doesn't own any slot, cluster shard response contains the node related
|
||||
* information and an empty slots array. */
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
clusterNode *n = dictGetVal(de);
|
||||
if (nodeIsSlave(n)) {
|
||||
/* You can force a replica to own slots, even though it'll get reverted,
|
||||
* so freeing the slot pair here just in case. */
|
||||
clusterFreeNodesSlotsInfo(n);
|
||||
continue;
|
||||
}
|
||||
shard_count++;
|
||||
/* n->slot_info_pairs is set to NULL when the the node owns no slots. */
|
||||
addShardReplyForClusterShards(c, n, n->slot_info_pairs, n->slot_info_pairs_count);
|
||||
clusterFreeNodesSlotsInfo(n);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
setDeferredArrayLen(c, shard_replylen, shard_count);
|
||||
}
|
||||
|
||||
void clusterReplyMultiBulkSlots(client * c) {
|
||||
/* Format: 1) 1) start slot
|
||||
* 2) end slot
|
||||
@@ -5035,6 +5228,8 @@ void clusterCommand(client *c) {
|
||||
"SLOTS",
|
||||
" Return information about slots range mappings. Each range is made of:",
|
||||
" start, end, master and replicas IP addresses, ports and ids",
|
||||
"SHARDS",
|
||||
" Return information about slot range mappings and the nodes associated with them.",
|
||||
"LINKS",
|
||||
" Return information about all network links between this node and its peers.",
|
||||
" Output format is an array where each array element is a map containing attributes of a link",
|
||||
@@ -5084,6 +5279,9 @@ NULL
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"slots") && c->argc == 2) {
|
||||
/* CLUSTER SLOTS */
|
||||
clusterReplyMultiBulkSlots(c);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"shards") && c->argc == 2) {
|
||||
/* CLUSTER SHARDS */
|
||||
clusterReplyShards(c);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"flushslots") && c->argc == 2) {
|
||||
/* CLUSTER FLUSHSLOTS */
|
||||
if (dictSize(server.db[0].dict) != 0) {
|
||||
@@ -5181,7 +5379,8 @@ NULL
|
||||
addReplyErrorFormat(c,"I'm not the owner of hash slot %u",slot);
|
||||
return;
|
||||
}
|
||||
if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {
|
||||
n = clusterLookupNode(c->argv[4]->ptr, sdslen(c->argv[4]->ptr));
|
||||
if (n == NULL) {
|
||||
addReplyErrorFormat(c,"I don't know about node %s",
|
||||
(char*)c->argv[4]->ptr);
|
||||
return;
|
||||
@@ -5197,7 +5396,8 @@ NULL
|
||||
"I'm already the owner of hash slot %u",slot);
|
||||
return;
|
||||
}
|
||||
if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {
|
||||
n = clusterLookupNode(c->argv[4]->ptr, sdslen(c->argv[4]->ptr));
|
||||
if (n == NULL) {
|
||||
addReplyErrorFormat(c,"I don't know about node %s",
|
||||
(char*)c->argv[4]->ptr);
|
||||
return;
|
||||
@@ -5213,8 +5413,7 @@ NULL
|
||||
server.cluster->migrating_slots_to[slot] = NULL;
|
||||
} else if (!strcasecmp(c->argv[3]->ptr,"node") && c->argc == 5) {
|
||||
/* CLUSTER SETSLOT <SLOT> NODE <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[4]->ptr);
|
||||
|
||||
n = clusterLookupNode(c->argv[4]->ptr, sdslen(c->argv[4]->ptr));
|
||||
if (!n) {
|
||||
addReplyErrorFormat(c,"Unknown node %s",
|
||||
(char*)c->argv[4]->ptr);
|
||||
@@ -5241,9 +5440,26 @@ NULL
|
||||
server.cluster->migrating_slots_to[slot])
|
||||
server.cluster->migrating_slots_to[slot] = NULL;
|
||||
|
||||
int slot_was_mine = server.cluster->slots[slot] == myself;
|
||||
clusterDelSlot(slot);
|
||||
clusterAddSlot(n,slot);
|
||||
|
||||
/* If we are a master left without slots, we should turn into a
|
||||
* replica of the new master. */
|
||||
if (slot_was_mine &&
|
||||
n != myself &&
|
||||
myself->numslots == 0 &&
|
||||
server.cluster_allow_replica_migration)
|
||||
{
|
||||
serverLog(LL_WARNING,
|
||||
"Configuration change detected. Reconfiguring myself "
|
||||
"as a replica of %.40s", n->name);
|
||||
clusterSetMaster(n);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG |
|
||||
CLUSTER_TODO_UPDATE_STATE |
|
||||
CLUSTER_TODO_FSYNC_CONFIG);
|
||||
}
|
||||
|
||||
/* If this node was importing this slot, assigning the slot to
|
||||
* itself also clears the importing status. */
|
||||
if (n == myself &&
|
||||
@@ -5409,8 +5625,7 @@ NULL
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"forget") && c->argc == 3) {
|
||||
/* CLUSTER FORGET <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr);
|
||||
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
|
||||
if (!n) {
|
||||
addReplyErrorFormat(c,"Unknown node %s", (char*)c->argv[2]->ptr);
|
||||
return;
|
||||
@@ -5428,9 +5643,8 @@ NULL
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"replicate") && c->argc == 3) {
|
||||
/* CLUSTER REPLICATE <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr);
|
||||
|
||||
/* Lookup the specified node in our table. */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
|
||||
if (!n) {
|
||||
addReplyErrorFormat(c,"Unknown node %s", (char*)c->argv[2]->ptr);
|
||||
return;
|
||||
@@ -5466,7 +5680,7 @@ NULL
|
||||
} else if ((!strcasecmp(c->argv[1]->ptr,"slaves") ||
|
||||
!strcasecmp(c->argv[1]->ptr,"replicas")) && c->argc == 3) {
|
||||
/* CLUSTER SLAVES <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr);
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
|
||||
int j;
|
||||
|
||||
/* Lookup the specified node in our table. */
|
||||
@@ -5493,7 +5707,7 @@ NULL
|
||||
c->argc == 3)
|
||||
{
|
||||
/* CLUSTER COUNT-FAILURE-REPORTS <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr);
|
||||
clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
|
||||
|
||||
if (!n) {
|
||||
addReplyErrorFormat(c,"Unknown node %s", (char*)c->argv[2]->ptr);
|
||||
|
||||
+5
-2
@@ -118,7 +118,9 @@ typedef struct clusterNode {
|
||||
int flags; /* CLUSTER_NODE_... */
|
||||
uint64_t configEpoch; /* Last configEpoch observed for this node */
|
||||
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
|
||||
sds slots_info; /* Slots info represented by string. */
|
||||
uint16_t *slot_info_pairs; /* Slots info represented as (start/end) pair (consecutive index). */
|
||||
int slot_info_pairs_count; /* Used number of slots in slot_info_pairs */
|
||||
int slot_info_pairs_alloc; /* Allocated number of slots in slot_info_pairs */
|
||||
int numslots; /* Number of slots handled by this node */
|
||||
int numslaves; /* Number of slave nodes, if this is a master */
|
||||
struct clusterNode **slaves; /* pointers to slave nodes */
|
||||
@@ -375,7 +377,8 @@ void clusterInit(void);
|
||||
void clusterCron(void);
|
||||
void clusterBeforeSleep(void);
|
||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
|
||||
clusterNode *clusterLookupNode(const char *name);
|
||||
int verifyClusterNodeId(const char *name, int length);
|
||||
clusterNode *clusterLookupNode(const char *name, int length);
|
||||
int clusterRedirectBlockedClientIfNeeded(client *c);
|
||||
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
|
||||
void migrateCloseTimedoutSockets(void);
|
||||
|
||||
+288
-66
@@ -602,6 +602,17 @@ struct redisCommandArg CLUSTER_SETSLOT_Args[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** CLUSTER SHARDS ********************/
|
||||
|
||||
/* CLUSTER SHARDS history */
|
||||
#define CLUSTER_SHARDS_History NULL
|
||||
|
||||
/* CLUSTER SHARDS tips */
|
||||
const char *CLUSTER_SHARDS_tips[] = {
|
||||
"nondeterministic_output",
|
||||
NULL
|
||||
};
|
||||
|
||||
/********** CLUSTER SLAVES ********************/
|
||||
|
||||
/* CLUSTER SLAVES history */
|
||||
@@ -624,7 +635,7 @@ struct redisCommandArg CLUSTER_SLAVES_Args[] = {
|
||||
/* CLUSTER SLOTS history */
|
||||
commandHistory CLUSTER_SLOTS_History[] = {
|
||||
{"4.0.0","Added node IDs."},
|
||||
{"7.0.0","Added additional networking metadata and added support for hostnames and unknown endpoints."},
|
||||
{"7.0.0","Added additional networking metadata field."},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -660,8 +671,9 @@ struct redisCommand CLUSTER_Subcommands[] = {
|
||||
{"saveconfig","Forces the node to save cluster state on disk","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,CLUSTER_SAVECONFIG_tips,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0},
|
||||
{"set-config-epoch","Set the configuration epoch in a new node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,CLUSTER_SET_CONFIG_EPOCH_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SET_CONFIG_EPOCH_Args},
|
||||
{"setslot","Bind a hash slot to a specific node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,CLUSTER_SETSLOT_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SETSLOT_Args},
|
||||
{"slaves","List replica nodes of the specified master node","O(1)","3.0.0",CMD_DOC_NONE,"`CLUSTER REPLICAS`","5.0.0",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,CLUSTER_SLAVES_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SLAVES_Args},
|
||||
{"slots","Get array of Cluster slot to node mappings","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,CLUSTER_SLOTS_tips,clusterCommand,2,CMD_STALE,0},
|
||||
{"shards","Get array of cluster slots to node mappings","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,CLUSTER_SHARDS_tips,clusterCommand,2,CMD_STALE,0},
|
||||
{"slaves","List replica nodes of the specified master node","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,CLUSTER_SLAVES_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SLAVES_Args},
|
||||
{"slots","Get array of Cluster slot to node mappings","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,CLUSTER_SLOTS_tips,clusterCommand,2,CMD_STALE,0},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -871,7 +883,6 @@ struct redisCommandArg CLIENT_NO_EVICT_Args[] = {
|
||||
|
||||
/* CLIENT PAUSE history */
|
||||
commandHistory CLIENT_PAUSE_History[] = {
|
||||
{"3.2.10","Client pause prevents client pause and key eviction as well."},
|
||||
{"6.2.0","`CLIENT PAUSE WRITE` mode added along with the `mode` option."},
|
||||
{0}
|
||||
};
|
||||
@@ -1312,7 +1323,7 @@ struct redisCommandArg MIGRATE_username_password_Subargs[] = {
|
||||
/* MIGRATE argument table */
|
||||
struct redisCommandArg MIGRATE_Args[] = {
|
||||
{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"key_or_empty_string",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=MIGRATE_key_or_empty_string_Subargs},
|
||||
{"destination-db",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
@@ -1404,7 +1415,10 @@ struct redisCommandArg OBJECT_IDLETIME_Args[] = {
|
||||
#define OBJECT_REFCOUNT_History NULL
|
||||
|
||||
/* OBJECT REFCOUNT tips */
|
||||
#define OBJECT_REFCOUNT_tips NULL
|
||||
const char *OBJECT_REFCOUNT_tips[] = {
|
||||
"nondeterministic_output",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* OBJECT REFCOUNT argument table */
|
||||
struct redisCommandArg OBJECT_REFCOUNT_Args[] = {
|
||||
@@ -1549,10 +1563,7 @@ NULL
|
||||
/********** RENAME ********************/
|
||||
|
||||
/* RENAME history */
|
||||
commandHistory RENAME_History[] = {
|
||||
{"3.2.0","The command no longer returns an error when source and destination names are the same."},
|
||||
{0}
|
||||
};
|
||||
#define RENAME_History NULL
|
||||
|
||||
/* RENAME tips */
|
||||
#define RENAME_tips NULL
|
||||
@@ -1658,7 +1669,7 @@ struct redisCommandArg SORT_Args[] = {
|
||||
{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"offset_count",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_offset_count_Subargs},
|
||||
{"pattern",ARG_TYPE_STRING,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN},
|
||||
{"pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN},
|
||||
{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_order_Subargs},
|
||||
{"sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"destination",ARG_TYPE_KEY,2,"STORE",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
@@ -1692,7 +1703,7 @@ struct redisCommandArg SORT_RO_Args[] = {
|
||||
{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"offset_count",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_RO_offset_count_Subargs},
|
||||
{"pattern",ARG_TYPE_STRING,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN},
|
||||
{"pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN},
|
||||
{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_RO_order_Subargs},
|
||||
{"sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{0}
|
||||
@@ -3036,7 +3047,7 @@ struct redisCommandArg PUBLISH_Args[] = {
|
||||
|
||||
/* PUBSUB CHANNELS argument table */
|
||||
struct redisCommandArg PUBSUB_CHANNELS_Args[] = {
|
||||
{"pattern",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -3080,7 +3091,7 @@ struct redisCommandArg PUBSUB_NUMSUB_Args[] = {
|
||||
|
||||
/* PUBSUB SHARDCHANNELS argument table */
|
||||
struct redisCommandArg PUBSUB_SHARDCHANNELS_Args[] = {
|
||||
{"pattern",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -3163,10 +3174,7 @@ struct redisCommandArg SSUBSCRIBE_Args[] = {
|
||||
/********** SUBSCRIBE ********************/
|
||||
|
||||
/* SUBSCRIBE history */
|
||||
commandHistory SUBSCRIBE_History[] = {
|
||||
{"6.2.0","`RESET` can be called to exit subscribed state."},
|
||||
{0}
|
||||
};
|
||||
#define SUBSCRIBE_History NULL
|
||||
|
||||
/* SUBSCRIBE tips */
|
||||
#define SUBSCRIBE_tips NULL
|
||||
@@ -3384,7 +3392,10 @@ NULL
|
||||
#define FUNCTION_LIST_History NULL
|
||||
|
||||
/* FUNCTION LIST tips */
|
||||
#define FUNCTION_LIST_tips NULL
|
||||
const char *FUNCTION_LIST_tips[] = {
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* FUNCTION LIST argument table */
|
||||
struct redisCommandArg FUNCTION_LIST_Args[] = {
|
||||
@@ -3407,10 +3418,7 @@ NULL
|
||||
|
||||
/* FUNCTION LOAD argument table */
|
||||
struct redisCommandArg FUNCTION_LOAD_Args[] = {
|
||||
{"engine-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"library-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"library-description",ARG_TYPE_STRING,-1,"DESCRIPTION",NULL,NULL,CMD_ARG_OPTIONAL},
|
||||
{"function-code",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
@@ -3449,8 +3457,9 @@ struct redisCommandArg FUNCTION_RESTORE_Args[] = {
|
||||
|
||||
/* FUNCTION STATS tips */
|
||||
const char *FUNCTION_STATS_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_shards",
|
||||
"response_policy:one_succeeded",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -3462,7 +3471,7 @@ struct redisCommand FUNCTION_Subcommands[] = {
|
||||
{"help","Show helpful text about the different subcommands","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_HELP_History,FUNCTION_HELP_tips,functionHelpCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_SCRIPTING},
|
||||
{"kill","Kill the function currently in execution.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_KILL_History,FUNCTION_KILL_tips,functionKillCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING},
|
||||
{"list","List information about all the functions","O(N) where N is the number of functions","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_LIST_History,FUNCTION_LIST_tips,functionListCommand,-2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_LIST_Args},
|
||||
{"load","Create a function with the given arguments (name, code, description)","O(1) (considering compilation time is redundant)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_LOAD_History,FUNCTION_LOAD_tips,functionLoadCommand,-5,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_LOAD_Args},
|
||||
{"load","Create a function with the given arguments (name, code, description)","O(1) (considering compilation time is redundant)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_LOAD_History,FUNCTION_LOAD_tips,functionLoadCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_LOAD_Args},
|
||||
{"restore","Restore all the functions on the given payload","O(N) where N is the number of functions on the payload","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_RESTORE_History,FUNCTION_RESTORE_tips,functionRestoreCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_RESTORE_Args},
|
||||
{"stats","Return information about the function currently running (name, description, duration)","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_STATS_History,FUNCTION_STATS_tips,functionStatsCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING},
|
||||
{0}
|
||||
@@ -3520,7 +3529,7 @@ struct redisCommandArg SCRIPT_EXISTS_Args[] = {
|
||||
|
||||
/* SCRIPT FLUSH history */
|
||||
commandHistory SCRIPT_FLUSH_History[] = {
|
||||
{"6.2.0","Added the `ASYNC` and `SYNC` flushing mode modifiers, as well as the **lazyfree-lazy-user-flush** configuration directive."},
|
||||
{"6.2.0","Added the `ASYNC` and `SYNC` flushing mode modifiers."},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -3609,6 +3618,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL CKQUORUM tips */
|
||||
#define SENTINEL_CKQUORUM_tips NULL
|
||||
|
||||
/* SENTINEL CKQUORUM argument table */
|
||||
struct redisCommandArg SENTINEL_CKQUORUM_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL CONFIG ********************/
|
||||
|
||||
/* SENTINEL CONFIG history */
|
||||
@@ -3617,6 +3632,26 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL CONFIG tips */
|
||||
#define SENTINEL_CONFIG_tips NULL
|
||||
|
||||
/* SENTINEL CONFIG set_or_get set_param_value argument table */
|
||||
struct redisCommandArg SENTINEL_CONFIG_set_or_get_set_param_value_Subargs[] = {
|
||||
{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL CONFIG set_or_get argument table */
|
||||
struct redisCommandArg SENTINEL_CONFIG_set_or_get_Subargs[] = {
|
||||
{"set_param_value",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_MULTIPLE,.subargs=SENTINEL_CONFIG_set_or_get_set_param_value_Subargs},
|
||||
{"parameter",ARG_TYPE_STRING,-1,"GET",NULL,NULL,CMD_ARG_MULTIPLE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL CONFIG argument table */
|
||||
struct redisCommandArg SENTINEL_CONFIG_Args[] = {
|
||||
{"set_or_get",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=SENTINEL_CONFIG_set_or_get_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL DEBUG ********************/
|
||||
|
||||
/* SENTINEL DEBUG history */
|
||||
@@ -3625,6 +3660,19 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL DEBUG tips */
|
||||
#define SENTINEL_DEBUG_tips NULL
|
||||
|
||||
/* SENTINEL DEBUG parameter_value argument table */
|
||||
struct redisCommandArg SENTINEL_DEBUG_parameter_value_Subargs[] = {
|
||||
{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL DEBUG argument table */
|
||||
struct redisCommandArg SENTINEL_DEBUG_Args[] = {
|
||||
{"parameter_value",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=SENTINEL_DEBUG_parameter_value_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL FAILOVER ********************/
|
||||
|
||||
/* SENTINEL FAILOVER history */
|
||||
@@ -3633,6 +3681,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL FAILOVER tips */
|
||||
#define SENTINEL_FAILOVER_tips NULL
|
||||
|
||||
/* SENTINEL FAILOVER argument table */
|
||||
struct redisCommandArg SENTINEL_FAILOVER_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL FLUSHCONFIG ********************/
|
||||
|
||||
/* SENTINEL FLUSHCONFIG history */
|
||||
@@ -3649,6 +3703,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL GET_MASTER_ADDR_BY_NAME tips */
|
||||
#define SENTINEL_GET_MASTER_ADDR_BY_NAME_tips NULL
|
||||
|
||||
/* SENTINEL GET_MASTER_ADDR_BY_NAME argument table */
|
||||
struct redisCommandArg SENTINEL_GET_MASTER_ADDR_BY_NAME_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL HELP ********************/
|
||||
|
||||
/* SENTINEL HELP history */
|
||||
@@ -3665,6 +3725,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL INFO_CACHE tips */
|
||||
#define SENTINEL_INFO_CACHE_tips NULL
|
||||
|
||||
/* SENTINEL INFO_CACHE argument table */
|
||||
struct redisCommandArg SENTINEL_INFO_CACHE_Args[] = {
|
||||
{"nodename",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL IS_MASTER_DOWN_BY_ADDR ********************/
|
||||
|
||||
/* SENTINEL IS_MASTER_DOWN_BY_ADDR history */
|
||||
@@ -3673,6 +3739,15 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL IS_MASTER_DOWN_BY_ADDR tips */
|
||||
#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_tips NULL
|
||||
|
||||
/* SENTINEL IS_MASTER_DOWN_BY_ADDR argument table */
|
||||
struct redisCommandArg SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args[] = {
|
||||
{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"current-epoch",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"runid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL MASTER ********************/
|
||||
|
||||
/* SENTINEL MASTER history */
|
||||
@@ -3681,6 +3756,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL MASTER tips */
|
||||
#define SENTINEL_MASTER_tips NULL
|
||||
|
||||
/* SENTINEL MASTER argument table */
|
||||
struct redisCommandArg SENTINEL_MASTER_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL MASTERS ********************/
|
||||
|
||||
/* SENTINEL MASTERS history */
|
||||
@@ -3697,6 +3778,15 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL MONITOR tips */
|
||||
#define SENTINEL_MONITOR_tips NULL
|
||||
|
||||
/* SENTINEL MONITOR argument table */
|
||||
struct redisCommandArg SENTINEL_MONITOR_Args[] = {
|
||||
{"name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"quorum",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL MYID ********************/
|
||||
|
||||
/* SENTINEL MYID history */
|
||||
@@ -3721,6 +3811,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL REMOVE tips */
|
||||
#define SENTINEL_REMOVE_tips NULL
|
||||
|
||||
/* SENTINEL REMOVE argument table */
|
||||
struct redisCommandArg SENTINEL_REMOVE_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL REPLICAS ********************/
|
||||
|
||||
/* SENTINEL REPLICAS history */
|
||||
@@ -3729,6 +3825,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL REPLICAS tips */
|
||||
#define SENTINEL_REPLICAS_tips NULL
|
||||
|
||||
/* SENTINEL REPLICAS argument table */
|
||||
struct redisCommandArg SENTINEL_REPLICAS_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL RESET ********************/
|
||||
|
||||
/* SENTINEL RESET history */
|
||||
@@ -3737,6 +3839,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL RESET tips */
|
||||
#define SENTINEL_RESET_tips NULL
|
||||
|
||||
/* SENTINEL RESET argument table */
|
||||
struct redisCommandArg SENTINEL_RESET_Args[] = {
|
||||
{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL SENTINELS ********************/
|
||||
|
||||
/* SENTINEL SENTINELS history */
|
||||
@@ -3745,6 +3853,12 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL SENTINELS tips */
|
||||
#define SENTINEL_SENTINELS_tips NULL
|
||||
|
||||
/* SENTINEL SENTINELS argument table */
|
||||
struct redisCommandArg SENTINEL_SENTINELS_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL SET ********************/
|
||||
|
||||
/* SENTINEL SET history */
|
||||
@@ -3753,6 +3867,20 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL SET tips */
|
||||
#define SENTINEL_SET_tips NULL
|
||||
|
||||
/* SENTINEL SET option_value argument table */
|
||||
struct redisCommandArg SENTINEL_SET_option_value_Subargs[] = {
|
||||
{"option",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL SET argument table */
|
||||
struct redisCommandArg SENTINEL_SET_Args[] = {
|
||||
{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"option_value",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=SENTINEL_SET_option_value_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** SENTINEL SIMULATE_FAILURE ********************/
|
||||
|
||||
/* SENTINEL SIMULATE_FAILURE history */
|
||||
@@ -3761,28 +3889,42 @@ struct redisCommand SCRIPT_Subcommands[] = {
|
||||
/* SENTINEL SIMULATE_FAILURE tips */
|
||||
#define SENTINEL_SIMULATE_FAILURE_tips NULL
|
||||
|
||||
/* SENTINEL SIMULATE_FAILURE mode argument table */
|
||||
struct redisCommandArg SENTINEL_SIMULATE_FAILURE_mode_Subargs[] = {
|
||||
{"crash-after-election",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"crash-after-promotion",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"help",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL SIMULATE_FAILURE argument table */
|
||||
struct redisCommandArg SENTINEL_SIMULATE_FAILURE_Args[] = {
|
||||
{"mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=SENTINEL_SIMULATE_FAILURE_mode_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* SENTINEL command table */
|
||||
struct redisCommand SENTINEL_Subcommands[] = {
|
||||
{"ckquorum","Check for a Sentinel quorum",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,SENTINEL_CKQUORUM_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"config","Configure Sentinel","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,SENTINEL_CONFIG_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"debug","List or update the current configurable parameters","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,SENTINEL_DEBUG_tips,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"failover","Force a failover",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,SENTINEL_FAILOVER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"ckquorum","Check for a Sentinel quorum",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,SENTINEL_CKQUORUM_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_CKQUORUM_Args},
|
||||
{"config","Configure Sentinel","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,SENTINEL_CONFIG_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_CONFIG_Args},
|
||||
{"debug","List or update the current configurable parameters","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,SENTINEL_DEBUG_tips,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_DEBUG_Args},
|
||||
{"failover","Force a failover",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,SENTINEL_FAILOVER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_FAILOVER_Args},
|
||||
{"flushconfig","Rewrite configuration file","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_FLUSHCONFIG_History,SENTINEL_FLUSHCONFIG_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"get-master-addr-by-name","Get port and address of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_GET_MASTER_ADDR_BY_NAME_History,SENTINEL_GET_MASTER_ADDR_BY_NAME_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"get-master-addr-by-name","Get port and address of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_GET_MASTER_ADDR_BY_NAME_History,SENTINEL_GET_MASTER_ADDR_BY_NAME_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_GET_MASTER_ADDR_BY_NAME_Args},
|
||||
{"help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_HELP_History,SENTINEL_HELP_tips,sentinelCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"info-cache","Get cached INFO from the instances in the deployment","O(N) where N is the number of instances","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_INFO_CACHE_History,SENTINEL_INFO_CACHE_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"is-master-down-by-addr","Check if a master is down","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_IS_MASTER_DOWN_BY_ADDR_History,SENTINEL_IS_MASTER_DOWN_BY_ADDR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"master","Shows the state of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MASTER_History,SENTINEL_MASTER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"info-cache","Get cached INFO from the instances in the deployment","O(N) where N is the number of instances","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_INFO_CACHE_History,SENTINEL_INFO_CACHE_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_INFO_CACHE_Args},
|
||||
{"is-master-down-by-addr","Check if a master is down","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_IS_MASTER_DOWN_BY_ADDR_History,SENTINEL_IS_MASTER_DOWN_BY_ADDR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args},
|
||||
{"master","Shows the state of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MASTER_History,SENTINEL_MASTER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_MASTER_Args},
|
||||
{"masters","List the monitored masters","O(N) where N is the number of masters","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MASTERS_History,SENTINEL_MASTERS_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"monitor","Start monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MONITOR_History,SENTINEL_MONITOR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"monitor","Start monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MONITOR_History,SENTINEL_MONITOR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_MONITOR_Args},
|
||||
{"myid","Get the Sentinel instance ID","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MYID_History,SENTINEL_MYID_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"pending-scripts","Get information about pending scripts",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_PENDING_SCRIPTS_History,SENTINEL_PENDING_SCRIPTS_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"remove","Stop monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REMOVE_History,SENTINEL_REMOVE_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"replicas","List the monitored replicas","O(N) where N is the number of replicas","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REPLICAS_History,SENTINEL_REPLICAS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"reset","Reset masters by name pattern","O(N) where N is the number of monitored masters","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_RESET_History,SENTINEL_RESET_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"sentinels","List the Sentinel instances","O(N) where N is the number of Sentinels","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SENTINELS_History,SENTINEL_SENTINELS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"set","Change the configuration of a monitored master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SET_History,SENTINEL_SET_tips,sentinelCommand,-5,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"simulate-failure","Simulate failover scenarios",NULL,"3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SIMULATE_FAILURE_History,SENTINEL_SIMULATE_FAILURE_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
|
||||
{"remove","Stop monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REMOVE_History,SENTINEL_REMOVE_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_REMOVE_Args},
|
||||
{"replicas","List the monitored replicas","O(N) where N is the number of replicas","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REPLICAS_History,SENTINEL_REPLICAS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_REPLICAS_Args},
|
||||
{"reset","Reset masters by name pattern","O(N) where N is the number of monitored masters","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_RESET_History,SENTINEL_RESET_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_RESET_Args},
|
||||
{"sentinels","List the Sentinel instances","O(N) where N is the number of Sentinels","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SENTINELS_History,SENTINEL_SENTINELS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SENTINELS_Args},
|
||||
{"set","Change the configuration of a monitored master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SET_History,SENTINEL_SET_tips,sentinelCommand,-5,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SET_Args},
|
||||
{"simulate-failure","Simulate failover scenarios",NULL,"3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SIMULATE_FAILURE_History,SENTINEL_SIMULATE_FAILURE_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SIMULATE_FAILURE_Args},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -4023,7 +4165,10 @@ struct redisCommandArg BGSAVE_Args[] = {
|
||||
#define COMMAND_DOCS_History NULL
|
||||
|
||||
/* COMMAND DOCS tips */
|
||||
#define COMMAND_DOCS_tips NULL
|
||||
const char *COMMAND_DOCS_tips[] = {
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* COMMAND DOCS argument table */
|
||||
struct redisCommandArg COMMAND_DOCS_Args[] = {
|
||||
@@ -4064,7 +4209,10 @@ commandHistory COMMAND_INFO_History[] = {
|
||||
};
|
||||
|
||||
/* COMMAND INFO tips */
|
||||
#define COMMAND_INFO_tips NULL
|
||||
const char *COMMAND_INFO_tips[] = {
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* COMMAND INFO argument table */
|
||||
struct redisCommandArg COMMAND_INFO_Args[] = {
|
||||
@@ -4078,7 +4226,10 @@ struct redisCommandArg COMMAND_INFO_Args[] = {
|
||||
#define COMMAND_LIST_History NULL
|
||||
|
||||
/* COMMAND LIST tips */
|
||||
#define COMMAND_LIST_tips NULL
|
||||
const char *COMMAND_LIST_tips[] = {
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* COMMAND LIST filterby argument table */
|
||||
struct redisCommandArg COMMAND_LIST_filterby_Subargs[] = {
|
||||
@@ -4099,7 +4250,7 @@ struct redisCommand COMMAND_Subcommands[] = {
|
||||
{"count","Get total number of Redis commands","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_COUNT_History,COMMAND_COUNT_tips,commandCountCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
|
||||
{"docs","Get array of specific Redis command documentation","O(N) where N is the number of commands to look up","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_DOCS_History,COMMAND_DOCS_tips,commandDocsCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_DOCS_Args},
|
||||
{"getkeys","Extract keys given a full Redis command","O(N) where N is the number of arguments to the command","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYS_History,COMMAND_GETKEYS_tips,commandGetKeysCommand,-4,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
|
||||
{"getkeysandflags","Extract keys given a full Redis command","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,COMMAND_GETKEYSANDFLAGS_tips,commandGetKeysAndFlagsCommand,-4,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
|
||||
{"getkeysandflags","Extract keys and access flags given a full Redis command","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,COMMAND_GETKEYSANDFLAGS_tips,commandGetKeysAndFlagsCommand,-4,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
|
||||
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_HELP_History,COMMAND_HELP_tips,commandHelpCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
|
||||
{"info","Get array of specific Redis command details, or all when no argument is given.","O(N) where N is the number of commands to look up","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_INFO_History,COMMAND_INFO_tips,commandInfoCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_INFO_Args},
|
||||
{"list","Get an array of Redis command names","O(N) where N is the total number of Redis commands","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_LIST_History,COMMAND_LIST_tips,commandListCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_LIST_Args},
|
||||
@@ -4113,7 +4264,7 @@ struct redisCommand COMMAND_Subcommands[] = {
|
||||
|
||||
/* COMMAND tips */
|
||||
const char *COMMAND_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -4259,7 +4410,7 @@ struct redisCommandArg FAILOVER_Args[] = {
|
||||
/* FLUSHALL history */
|
||||
commandHistory FLUSHALL_History[] = {
|
||||
{"4.0.0","Added the `ASYNC` flushing mode modifier."},
|
||||
{"6.2.0","Added the `SYNC` flushing mode modifier and the **lazyfree-lazy-user-flush** configuration directive."},
|
||||
{"6.2.0","Added the `SYNC` flushing mode modifier."},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -4288,7 +4439,7 @@ struct redisCommandArg FLUSHALL_Args[] = {
|
||||
/* FLUSHDB history */
|
||||
commandHistory FLUSHDB_History[] = {
|
||||
{"4.0.0","Added the `ASYNC` flushing mode modifier."},
|
||||
{"6.2.0","Added the `SYNC` flushing mode modifier and the **lazyfree-lazy-user-flush** configuration directive."},
|
||||
{"6.2.0","Added the `SYNC` flushing mode modifier."},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -4351,7 +4502,12 @@ NULL
|
||||
#define LATENCY_DOCTOR_History NULL
|
||||
|
||||
/* LATENCY DOCTOR tips */
|
||||
#define LATENCY_DOCTOR_tips NULL
|
||||
const char *LATENCY_DOCTOR_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
/********** LATENCY GRAPH ********************/
|
||||
|
||||
@@ -4359,7 +4515,12 @@ NULL
|
||||
#define LATENCY_GRAPH_History NULL
|
||||
|
||||
/* LATENCY GRAPH tips */
|
||||
#define LATENCY_GRAPH_tips NULL
|
||||
const char *LATENCY_GRAPH_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* LATENCY GRAPH argument table */
|
||||
struct redisCommandArg LATENCY_GRAPH_Args[] = {
|
||||
@@ -4381,7 +4542,12 @@ struct redisCommandArg LATENCY_GRAPH_Args[] = {
|
||||
#define LATENCY_HISTOGRAM_History NULL
|
||||
|
||||
/* LATENCY HISTOGRAM tips */
|
||||
#define LATENCY_HISTOGRAM_tips NULL
|
||||
const char *LATENCY_HISTOGRAM_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* LATENCY HISTOGRAM argument table */
|
||||
struct redisCommandArg LATENCY_HISTOGRAM_Args[] = {
|
||||
@@ -4395,7 +4561,12 @@ struct redisCommandArg LATENCY_HISTOGRAM_Args[] = {
|
||||
#define LATENCY_HISTORY_History NULL
|
||||
|
||||
/* LATENCY HISTORY tips */
|
||||
#define LATENCY_HISTORY_tips NULL
|
||||
const char *LATENCY_HISTORY_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* LATENCY HISTORY argument table */
|
||||
struct redisCommandArg LATENCY_HISTORY_Args[] = {
|
||||
@@ -4409,7 +4580,12 @@ struct redisCommandArg LATENCY_HISTORY_Args[] = {
|
||||
#define LATENCY_LATEST_History NULL
|
||||
|
||||
/* LATENCY LATEST tips */
|
||||
#define LATENCY_LATEST_tips NULL
|
||||
const char *LATENCY_LATEST_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
/********** LATENCY RESET ********************/
|
||||
|
||||
@@ -4417,7 +4593,11 @@ struct redisCommandArg LATENCY_HISTORY_Args[] = {
|
||||
#define LATENCY_RESET_History NULL
|
||||
|
||||
/* LATENCY RESET tips */
|
||||
#define LATENCY_RESET_tips NULL
|
||||
const char *LATENCY_RESET_tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* LATENCY RESET argument table */
|
||||
struct redisCommandArg LATENCY_RESET_Args[] = {
|
||||
@@ -4467,6 +4647,8 @@ struct redisCommandArg LOLWUT_Args[] = {
|
||||
/* MEMORY DOCTOR tips */
|
||||
const char *MEMORY_DOCTOR_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_shards",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -4486,6 +4668,8 @@ NULL
|
||||
/* MEMORY MALLOC_STATS tips */
|
||||
const char *MEMORY_MALLOC_STATS_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_shards",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -4495,7 +4679,11 @@ NULL
|
||||
#define MEMORY_PURGE_History NULL
|
||||
|
||||
/* MEMORY PURGE tips */
|
||||
#define MEMORY_PURGE_tips NULL
|
||||
const char *MEMORY_PURGE_tips[] = {
|
||||
"request_policy:all_shards",
|
||||
"response_policy:all_succeeded",
|
||||
NULL
|
||||
};
|
||||
|
||||
/********** MEMORY STATS ********************/
|
||||
|
||||
@@ -4505,6 +4693,8 @@ NULL
|
||||
/* MEMORY STATS tips */
|
||||
const char *MEMORY_STATS_tips[] = {
|
||||
"nondeterministic_output",
|
||||
"request_policy:all_shards",
|
||||
"response_policy:special",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -4556,7 +4746,10 @@ struct redisCommand MEMORY_Subcommands[] = {
|
||||
#define MODULE_LIST_History NULL
|
||||
|
||||
/* MODULE LIST tips */
|
||||
#define MODULE_LIST_tips NULL
|
||||
const char *MODULE_LIST_tips[] = {
|
||||
"nondeterministic_output_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
/********** MODULE LOAD ********************/
|
||||
|
||||
@@ -4573,6 +4766,35 @@ struct redisCommandArg MODULE_LOAD_Args[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** MODULE LOADEX ********************/
|
||||
|
||||
/* MODULE LOADEX history */
|
||||
#define MODULE_LOADEX_History NULL
|
||||
|
||||
/* MODULE LOADEX tips */
|
||||
#define MODULE_LOADEX_tips NULL
|
||||
|
||||
/* MODULE LOADEX configs argument table */
|
||||
struct redisCommandArg MODULE_LOADEX_configs_Subargs[] = {
|
||||
{"name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* MODULE LOADEX args argument table */
|
||||
struct redisCommandArg MODULE_LOADEX_args_Subargs[] = {
|
||||
{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* MODULE LOADEX argument table */
|
||||
struct redisCommandArg MODULE_LOADEX_Args[] = {
|
||||
{"path",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"configs",ARG_TYPE_BLOCK,-1,"CONFIG",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=MODULE_LOADEX_configs_Subargs},
|
||||
{"args",ARG_TYPE_BLOCK,-1,"ARGS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=MODULE_LOADEX_args_Subargs},
|
||||
{0}
|
||||
};
|
||||
|
||||
/********** MODULE UNLOAD ********************/
|
||||
|
||||
/* MODULE UNLOAD history */
|
||||
@@ -4592,6 +4814,7 @@ struct redisCommand MODULE_Subcommands[] = {
|
||||
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_HELP_History,MODULE_HELP_tips,moduleCommand,2,CMD_LOADING|CMD_STALE,0},
|
||||
{"list","List all modules loaded by the server","O(N) where N is the number of loaded modules.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LIST_History,MODULE_LIST_tips,moduleCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0},
|
||||
{"load","Load a module","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LOAD_History,MODULE_LOAD_tips,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_LOAD_Args},
|
||||
{"loadex","Load a module with extended parameters","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LOADEX_History,MODULE_LOADEX_tips,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_LOADEX_Args},
|
||||
{"unload","Unload a module","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_UNLOAD_History,MODULE_UNLOAD_tips,moduleCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_UNLOAD_Args},
|
||||
{0}
|
||||
};
|
||||
@@ -4607,12 +4830,7 @@ struct redisCommand MODULE_Subcommands[] = {
|
||||
/********** MONITOR ********************/
|
||||
|
||||
/* MONITOR history */
|
||||
commandHistory MONITOR_History[] = {
|
||||
{"6.0.0","`AUTH` excluded from the command's output."},
|
||||
{"6.2.0","`RESET` can be called to exit monitor mode."},
|
||||
{"6.2.4","`AUTH`, `HELLO`, `EVAL`, `EVAL_RO`, `EVALSHA` and `EVALSHA_RO` included in the command's output."},
|
||||
{0}
|
||||
};
|
||||
#define MONITOR_History NULL
|
||||
|
||||
/* MONITOR tips */
|
||||
#define MONITOR_tips NULL
|
||||
@@ -4627,7 +4845,7 @@ commandHistory MONITOR_History[] = {
|
||||
|
||||
/* PSYNC argument table */
|
||||
struct redisCommandArg PSYNC_Args[] = {
|
||||
{"replicationid",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"replicationid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
@@ -4651,7 +4869,7 @@ struct redisCommandArg PSYNC_Args[] = {
|
||||
/* REPLICAOF argument table */
|
||||
struct redisCommandArg REPLICAOF_Args[] = {
|
||||
{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -4683,7 +4901,7 @@ struct redisCommandArg REPLICAOF_Args[] = {
|
||||
|
||||
/* SHUTDOWN history */
|
||||
commandHistory SHUTDOWN_History[] = {
|
||||
{"7.0.0","Added the `NOW`, `FORCE` and `ABORT` modifiers. Introduced waiting for lagging replicas before exiting."},
|
||||
{"7.0.0","Added the `NOW`, `FORCE` and `ABORT` modifiers."},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -4717,7 +4935,7 @@ struct redisCommandArg SHUTDOWN_Args[] = {
|
||||
/* SLAVEOF argument table */
|
||||
struct redisCommandArg SLAVEOF_Args[] = {
|
||||
{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -6652,7 +6870,11 @@ struct redisCommandArg MSET_Args[] = {
|
||||
#define MSETNX_History NULL
|
||||
|
||||
/* MSETNX tips */
|
||||
#define MSETNX_tips NULL
|
||||
const char *MSETNX_tips[] = {
|
||||
"request_policy:multi_shard",
|
||||
"response_policy:agg_min",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* MSETNX key_value argument table */
|
||||
struct redisCommandArg MSETNX_key_value_Subargs[] = {
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
"container": "CLIENT",
|
||||
"function": "clientCommand",
|
||||
"history": [
|
||||
[
|
||||
"3.2.10",
|
||||
"Client pause prevents client pause and key eviction as well."
|
||||
],
|
||||
[
|
||||
"6.2.0",
|
||||
"`CLIENT PAUSE WRITE` mode added along with the `mode` option."
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"SHARDS": {
|
||||
"summary": "Get array of cluster slots to node mappings",
|
||||
"complexity": "O(N) where N is the total number of cluster nodes",
|
||||
"group": "cluster",
|
||||
"since": "7.0.0",
|
||||
"arity": 2,
|
||||
"container": "CLUSTER",
|
||||
"function": "clusterCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,9 @@
|
||||
"function": "clusterCommand",
|
||||
"deprecated_since": "5.0.0",
|
||||
"replaced_by": "`CLUSTER REPLICAS`",
|
||||
"doc_flags": [
|
||||
"DEPRECATED"
|
||||
],
|
||||
"command_flags": [
|
||||
"ADMIN",
|
||||
"STALE"
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
"arity": 2,
|
||||
"container": "CLUSTER",
|
||||
"function": "clusterCommand",
|
||||
"deprecated_since": "7.0.0",
|
||||
"replaced_by": "`CLUSTER SHARDS`",
|
||||
"doc_flags": [
|
||||
"DEPRECATED"
|
||||
],
|
||||
"history": [
|
||||
[
|
||||
"4.0.0",
|
||||
@@ -14,7 +19,7 @@
|
||||
],
|
||||
[
|
||||
"7.0.0",
|
||||
"Added additional networking metadata and added support for hostnames and unknown endpoints."
|
||||
"Added additional networking metadata field."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "command-name",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"GETKEYSANDFLAGS": {
|
||||
"summary": "Extract keys given a full Redis command",
|
||||
"summary": "Extract keys and access flags given a full Redis command",
|
||||
"complexity": "O(N) where N is the number of arguments to the command",
|
||||
"group": "server",
|
||||
"since": "7.0.0",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "command-name",
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "filterby",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"CONNECTION"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
],
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `SYNC` flushing mode modifier and the **lazyfree-lazy-user-flush** configuration directive."
|
||||
"Added the `SYNC` flushing mode modifier."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
],
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `SYNC` flushing mode modifier and the **lazyfree-lazy-user-flush** configuration directive."
|
||||
"Added the `SYNC` flushing mode modifier."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"command_flags": [
|
||||
"NOSCRIPT"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
],
|
||||
"acl_categories": [
|
||||
"SCRIPTING"
|
||||
],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"complexity": "O(1) (considering compilation time is redundant)",
|
||||
"group": "scripting",
|
||||
"since": "7.0.0",
|
||||
"arity": -5,
|
||||
"arity": -3,
|
||||
"container": "FUNCTION",
|
||||
"function": "functionLoadCommand",
|
||||
"command_flags": [
|
||||
@@ -20,26 +20,12 @@
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "engine-name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "library-name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "replace",
|
||||
"type": "pure-token",
|
||||
"token": "REPLACE",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "library-description",
|
||||
"type": "string",
|
||||
"token": "DESCRIPTION",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "function-code",
|
||||
"type": "string"
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
"SCRIPTING"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:ONE_SUCCEEDED"
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "event",
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "COMMAND",
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "event",
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "event",
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"container": "MEMORY",
|
||||
"function": "memoryCommand",
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"container": "MEMORY",
|
||||
"function": "memoryCommand",
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
"since": "4.0.0",
|
||||
"arity": 2,
|
||||
"container": "MEMORY",
|
||||
"function": "memoryCommand"
|
||||
"function": "memoryCommand",
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
"container": "MEMORY",
|
||||
"function": "memoryCommand",
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
"NONDETERMINISTIC_OUTPUT",
|
||||
"REQUEST_POLICY:ALL_SHARDS",
|
||||
"RESPONSE_POLICY:SPECIAL"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "key_or_empty_string",
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"command_flags": [
|
||||
"ADMIN",
|
||||
"NOSCRIPT"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT_ORDER"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"LOADEX": {
|
||||
"summary": "Load a module with extended parameters",
|
||||
"complexity": "O(1)",
|
||||
"group": "server",
|
||||
"since": "7.0.0",
|
||||
"arity": -3,
|
||||
"container": "MODULE",
|
||||
"function": "moduleCommand",
|
||||
"command_flags": [
|
||||
"NO_ASYNC_LOADING",
|
||||
"ADMIN",
|
||||
"NOSCRIPT",
|
||||
"PROTECTED"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "path",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "configs",
|
||||
"token": "CONFIG",
|
||||
"type": "block",
|
||||
"multiple": true,
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"token": "ARGS",
|
||||
"type": "block",
|
||||
"multiple": true,
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "arg",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,7 @@
|
||||
"since": "1.0.0",
|
||||
"arity": 1,
|
||||
"function": "monitorCommand",
|
||||
"history": [
|
||||
[
|
||||
"6.0.0",
|
||||
"`AUTH` excluded from the command's output."
|
||||
],
|
||||
[
|
||||
"6.2.0",
|
||||
"`RESET` can be called to exit monitor mode."
|
||||
],
|
||||
[
|
||||
"6.2.4",
|
||||
"`AUTH`, `HELLO`, `EVAL`, `EVAL_RO`, `EVALSHA` and `EVALSHA_RO` included in the command's output."
|
||||
]
|
||||
],
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"ADMIN",
|
||||
"NOSCRIPT",
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"acl_categories": [
|
||||
"STRING"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:MULTI_SHARD",
|
||||
"RESPONSE_POLICY:AGG_MIN"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"acl_categories": [
|
||||
"KEYSPACE"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"arguments": [
|
||||
{
|
||||
"name": "replicationid",
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"arguments": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"type": "string",
|
||||
"type": "pattern",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"SHARDCHANNELS": {
|
||||
"summary": "List active shard channels",
|
||||
"complexity": "O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short channels).",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"container": "PUBSUB",
|
||||
"function": "pubsubCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"type": "string",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"SHARDCHANNELS": {
|
||||
"summary": "List active shard channels",
|
||||
"complexity": "O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short channels).",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"container": "PUBSUB",
|
||||
"function": "pubsubCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"type": "pattern",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"SHARDNUMSUB": {
|
||||
"summary": "Get the count of subscribers for shard channels",
|
||||
"complexity": "O(N) for the SHARDNUMSUB subcommand, where N is the number of requested channels",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"container": "PUBSUB",
|
||||
"function": "pubsubCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"SHARDNUMSUB": {
|
||||
"summary": "Get the count of subscribers for shard channels",
|
||||
"complexity": "O(N) for the SHARDNUMSUB subcommand, where N is the number of requested channels",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"container": "PUBSUB",
|
||||
"function": "pubsubCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
"since": "1.0.0",
|
||||
"arity": 3,
|
||||
"function": "renameCommand",
|
||||
"history": [
|
||||
[
|
||||
"3.2.0",
|
||||
"The command no longer returns an error when source and destination names are the same."
|
||||
]
|
||||
],
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE"
|
||||
],
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"history": [
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `ASYNC` and `SYNC` flushing mode modifiers, as well as the **lazyfree-lazy-user-flush** configuration directive."
|
||||
"Added the `ASYNC` and `SYNC` flushing mode modifiers."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,36 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name":"set_or_get",
|
||||
"type":"oneof",
|
||||
"arguments":[
|
||||
{
|
||||
"name":"set_param_value",
|
||||
"token":"SET",
|
||||
"type":"block",
|
||||
"multiple":true,
|
||||
"arguments":[
|
||||
{
|
||||
"name":"parameter",
|
||||
"type":"string"
|
||||
},
|
||||
{
|
||||
"name":"value",
|
||||
"type":"string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"token":"GET",
|
||||
"multiple":true,
|
||||
"name":"parameter",
|
||||
"type":"string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,23 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "parameter_value",
|
||||
"type": "block",
|
||||
"multiple": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "parameter",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
"complexity": "O(N) where N is the number of instances",
|
||||
"group": "sentinel",
|
||||
"since": "3.2.0",
|
||||
"arity": 3,
|
||||
"arity": -3,
|
||||
"container": "SENTINEL",
|
||||
"function": "sentinelCommand",
|
||||
"command_flags": [
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "nodename",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "ip",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "current-epoch",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "runid",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,24 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "ip",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "quorum",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"type": "pattern"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,27 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "master-name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "option_value",
|
||||
"type": "block",
|
||||
"multiple": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "option",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,28 @@
|
||||
"ADMIN",
|
||||
"SENTINEL",
|
||||
"ONLY_SENTINEL"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "mode",
|
||||
"type": "oneof",
|
||||
"optional":true,
|
||||
"multiple":true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "crash-after-election",
|
||||
"type": "pure-token"
|
||||
},
|
||||
{
|
||||
"name": "crash-after-promotion",
|
||||
"type": "pure-token"
|
||||
},
|
||||
{
|
||||
"name": "help",
|
||||
"type": "pure-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"history": [
|
||||
[
|
||||
"7.0.0",
|
||||
"Added the `NOW`, `FORCE` and `ABORT` modifiers. Introduced waiting for lagging replicas before exiting."
|
||||
"Added the `NOW`, `FORCE` and `ABORT` modifiers."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "string"
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
"token": "GET",
|
||||
"name": "pattern",
|
||||
"key_spec_index": 1,
|
||||
"type": "string",
|
||||
"type": "pattern",
|
||||
"optional": true,
|
||||
"multiple": true,
|
||||
"multiple_token": true
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"token": "GET",
|
||||
"name": "pattern",
|
||||
"key_spec_index": 1,
|
||||
"type": "string",
|
||||
"type": "pattern",
|
||||
"optional": true,
|
||||
"multiple": true,
|
||||
"multiple_token": true
|
||||
|
||||
+43
-43
@@ -1,46 +1,46 @@
|
||||
{
|
||||
"SPUBLISH": {
|
||||
"summary": "Post a message to a shard channel",
|
||||
"complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": 3,
|
||||
"function": "spublishCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE",
|
||||
"FAST",
|
||||
"MAY_REPLICATE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
"SPUBLISH": {
|
||||
"summary": "Post a message to a shard channel",
|
||||
"complexity": "O(N) where N is the number of clients subscribed to the receiving shard channel.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": 3,
|
||||
"function": "spublishCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"LOADING",
|
||||
"STALE",
|
||||
"FAST",
|
||||
"MAY_REPLICATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
{
|
||||
"SSUBSCRIBE": {
|
||||
"summary": "Listen for messages published to the given shard channels",
|
||||
"complexity": "O(N) where N is the number of shard channels to subscribe to.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"function": "ssubscribeCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
"SSUBSCRIBE": {
|
||||
"summary": "Listen for messages published to the given shard channels",
|
||||
"complexity": "O(N) where N is the number of shard channels to subscribe to.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -2,
|
||||
"function": "ssubscribeCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": -1,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": -1,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
"since": "2.0.0",
|
||||
"arity": -2,
|
||||
"function": "subscribeCommand",
|
||||
"history": [
|
||||
[
|
||||
"6.2.0",
|
||||
"`RESET` can be called to exit subscribed state."
|
||||
]
|
||||
],
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"NOSCRIPT",
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
{
|
||||
"SUNSUBSCRIBE": {
|
||||
"summary": "Stop listening for messages posted to the given shard channels",
|
||||
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -1,
|
||||
"function": "sunsubscribeCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
"SUNSUBSCRIBE": {
|
||||
"summary": "Stop listening for messages posted to the given shard channels",
|
||||
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
|
||||
"group": "pubsub",
|
||||
"since": "7.0.0",
|
||||
"arity": -1,
|
||||
"function": "sunsubscribeCommand",
|
||||
"command_flags": [
|
||||
"PUBSUB",
|
||||
"NOSCRIPT",
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": -1,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"arguments": [
|
||||
{
|
||||
"name": "channel",
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"multiple": true
|
||||
}
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"NOT_KEY"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": -1,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+541
-333
File diff suppressed because it is too large
Load Diff
@@ -218,9 +218,12 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
|
||||
val->lru = old->lru;
|
||||
}
|
||||
/* Although the key is not really deleted from the database, we regard
|
||||
overwrite as two steps of unlink+add, so we still need to call the unlink
|
||||
callback of the module. */
|
||||
* overwrite as two steps of unlink+add, so we still need to call the unlink
|
||||
* callback of the module. */
|
||||
moduleNotifyKeyUnlink(key,old,db->id);
|
||||
/* We want to try to unblock any client using a blocking XREADGROUP */
|
||||
if (old->type == OBJ_STREAM)
|
||||
signalKeyAsReady(db,key,old->type);
|
||||
dictSetVal(db->dict, de, val);
|
||||
|
||||
if (server.lazyfree_lazy_server_del) {
|
||||
@@ -311,6 +314,9 @@ static int dbGenericDelete(redisDb *db, robj *key, int async) {
|
||||
robj *val = dictGetVal(de);
|
||||
/* Tells the module that the key has been unlinked from the database. */
|
||||
moduleNotifyKeyUnlink(key,val,db->id);
|
||||
/* We want to try to unblock any client using a blocking XREADGROUP */
|
||||
if (val->type == OBJ_STREAM)
|
||||
signalKeyAsReady(db,key,val->type);
|
||||
if (async) {
|
||||
freeObjAsync(key, val, db->id);
|
||||
dictSetVal(db->dict, de, NULL);
|
||||
@@ -551,6 +557,7 @@ void signalFlushedDb(int dbid, int async) {
|
||||
}
|
||||
|
||||
for (int j = startdb; j <= enddb; j++) {
|
||||
scanDatabaseForDeletedStreams(&server.db[j], NULL);
|
||||
touchAllWatchedKeysInDb(&server.db[j], NULL);
|
||||
}
|
||||
|
||||
@@ -1311,7 +1318,7 @@ void copyCommand(client *c) {
|
||||
* one or more blocked clients for B[LR]POP or other blocking commands
|
||||
* and signal the keys as ready if they are of the right type. See the comment
|
||||
* where the function is used for more info. */
|
||||
void scanDatabaseForReadyLists(redisDb *db) {
|
||||
void scanDatabaseForReadyKeys(redisDb *db) {
|
||||
dictEntry *de;
|
||||
dictIterator *di = dictGetSafeIterator(db->blocking_keys);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
@@ -1325,6 +1332,39 @@ void scanDatabaseForReadyLists(redisDb *db) {
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* Since we are unblocking XREADGROUP clients in the event the
|
||||
* key was deleted/overwritten we must do the same in case the
|
||||
* database was flushed/swapped. */
|
||||
void scanDatabaseForDeletedStreams(redisDb *emptied, redisDb *replaced_with) {
|
||||
/* Optimization: If no clients are in type BLOCKED_STREAM,
|
||||
* we can skip this loop. */
|
||||
if (!server.blocked_clients_by_type[BLOCKED_STREAM]) return;
|
||||
|
||||
dictEntry *de;
|
||||
dictIterator *di = dictGetSafeIterator(emptied->blocking_keys);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *key = dictGetKey(de);
|
||||
int was_stream = 0, is_stream = 0;
|
||||
|
||||
dictEntry *kde = dictFind(emptied->dict, key->ptr);
|
||||
if (kde) {
|
||||
robj *value = dictGetVal(kde);
|
||||
was_stream = value->type == OBJ_STREAM;
|
||||
}
|
||||
if (replaced_with) {
|
||||
dictEntry *kde = dictFind(replaced_with->dict, key->ptr);
|
||||
if (kde) {
|
||||
robj *value = dictGetVal(kde);
|
||||
is_stream = value->type == OBJ_STREAM;
|
||||
}
|
||||
}
|
||||
/* We want to try to unblock any client using a blocking XREADGROUP */
|
||||
if (was_stream && !is_stream)
|
||||
signalKeyAsReady(emptied, key, OBJ_STREAM);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* Swap two databases at runtime so that all clients will magically see
|
||||
* the new database even if already connected. Note that the client
|
||||
* structure c->db points to a given DB, so we need to be smarter and
|
||||
@@ -1345,6 +1385,10 @@ int dbSwapDatabases(int id1, int id2) {
|
||||
touchAllWatchedKeysInDb(db1, db2);
|
||||
touchAllWatchedKeysInDb(db2, db1);
|
||||
|
||||
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
|
||||
scanDatabaseForDeletedStreams(db1, db2);
|
||||
scanDatabaseForDeletedStreams(db2, db1);
|
||||
|
||||
/* Swap hash tables. Note that we don't swap blocking_keys,
|
||||
* ready_keys and watched_keys, since we want clients to
|
||||
* remain in the same DB they were. */
|
||||
@@ -1367,8 +1411,8 @@ int dbSwapDatabases(int id1, int id2) {
|
||||
* in dbAdd() when a list is created. So here we need to rescan
|
||||
* the list of clients blocked on lists and signal lists as ready
|
||||
* if needed. */
|
||||
scanDatabaseForReadyLists(db1);
|
||||
scanDatabaseForReadyLists(db2);
|
||||
scanDatabaseForReadyKeys(db1);
|
||||
scanDatabaseForReadyKeys(db2);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -1391,6 +1435,9 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
|
||||
* client watching keys. */
|
||||
touchAllWatchedKeysInDb(activedb, newdb);
|
||||
|
||||
/* Try to unblock any XREADGROUP clients if the key no longer exists. */
|
||||
scanDatabaseForDeletedStreams(activedb, newdb);
|
||||
|
||||
/* Swap hash tables. Note that we don't swap blocking_keys,
|
||||
* ready_keys and watched_keys, since clients
|
||||
* remain in the same DB they were. */
|
||||
@@ -1413,7 +1460,7 @@ void swapMainDbWithTempDb(redisDb *tempDb) {
|
||||
* in dbAdd() when a list is created. So here we need to rescan
|
||||
* the list of clients blocked on lists and signal lists as ready
|
||||
* if needed. */
|
||||
scanDatabaseForReadyLists(activedb);
|
||||
scanDatabaseForReadyKeys(activedb);
|
||||
}
|
||||
|
||||
trackingInvalidateKeysOnFlush(1);
|
||||
|
||||
+22
-9
@@ -482,10 +482,12 @@ void debugCommand(client *c) {
|
||||
" Show low level client eviction pools info (maxmemory-clients).",
|
||||
"PAUSE-CRON <0|1>",
|
||||
" Stop periodic cron job processing.",
|
||||
"REPLYBUFFER-PEAK-RESET-TIME <NEVER||RESET|time>",
|
||||
"REPLYBUFFER PEAK-RESET-TIME <NEVER||RESET|time>",
|
||||
" Sets the time (in milliseconds) to wait between client reply buffer peak resets.",
|
||||
" In case NEVER is provided the last observed peak will never be reset",
|
||||
" In case RESET is provided the peak reset time will be restored to the default value",
|
||||
"REPLYBUFFER RESIZING <0|1>",
|
||||
" Enable or disable the replay buffer resize cron job",
|
||||
NULL
|
||||
};
|
||||
addReplyHelp(c, help);
|
||||
@@ -793,6 +795,10 @@ NULL
|
||||
* also have a normal reply type after the attribute. */
|
||||
addReplyBulkCString(c,"Some real reply following the attribute");
|
||||
} else if (!strcasecmp(name,"push")) {
|
||||
if (c->resp < 3) {
|
||||
addReplyError(c,"RESP2 is not supported by this command");
|
||||
return;
|
||||
}
|
||||
addReplyPushLen(c,2);
|
||||
addReplyBulkCString(c,"server-cpu-usage");
|
||||
addReplyLongLong(c,42);
|
||||
@@ -962,14 +968,21 @@ NULL
|
||||
{
|
||||
server.pause_cron = atoi(c->argv[2]->ptr);
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"replybuffer-peak-reset-time") && c->argc == 3 ) {
|
||||
if (!strcasecmp(c->argv[2]->ptr, "never")) {
|
||||
server.reply_buffer_peak_reset_time = -1;
|
||||
} else if(!strcasecmp(c->argv[2]->ptr, "reset")) {
|
||||
server.reply_buffer_peak_reset_time = REPLY_BUFFER_DEFAULT_PEAK_RESET_TIME;
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"replybuffer") && c->argc == 4 ) {
|
||||
if(!strcasecmp(c->argv[2]->ptr, "peak-reset-time")) {
|
||||
if (!strcasecmp(c->argv[3]->ptr, "never")) {
|
||||
server.reply_buffer_peak_reset_time = -1;
|
||||
} else if(!strcasecmp(c->argv[3]->ptr, "reset")) {
|
||||
server.reply_buffer_peak_reset_time = REPLY_BUFFER_DEFAULT_PEAK_RESET_TIME;
|
||||
} else {
|
||||
if (getLongFromObjectOrReply(c, c->argv[3], &server.reply_buffer_peak_reset_time, NULL) != C_OK)
|
||||
return;
|
||||
}
|
||||
} else if(!strcasecmp(c->argv[2]->ptr,"resizing")) {
|
||||
server.reply_buffer_resizing_enabled = atoi(c->argv[3]->ptr);
|
||||
} else {
|
||||
if (getLongFromObjectOrReply(c, c->argv[2], &server.reply_buffer_peak_reset_time, NULL) != C_OK)
|
||||
return;
|
||||
addReplySubcommandSyntaxError(c);
|
||||
return;
|
||||
}
|
||||
addReply(c, shared.ok);
|
||||
} else {
|
||||
@@ -1943,7 +1956,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
|
||||
serverLog(LL_WARNING,
|
||||
"Accessing address: %p", (void*)info->si_addr);
|
||||
}
|
||||
if (info->si_code <= SI_USER && info->si_pid != -1) {
|
||||
if (info->si_code == SI_USER && info->si_pid != -1) {
|
||||
serverLog(LL_WARNING, "Killed by PID: %ld, UID: %d", (long) info->si_pid, info->si_uid);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -407,7 +407,7 @@ long activeDefragSdsListAndDict(list *l, dict *d, int dict_val_type) {
|
||||
* new pointer. Additionally, we try to defrag the dictEntry in that dict.
|
||||
* Oldkey mey be a dead pointer and should not be accessed (we get a
|
||||
* pre-calculated hash value). Newkey may be null if the key pointer wasn't
|
||||
* moved. Return value is the the dictEntry if found, or NULL if not found.
|
||||
* moved. Return value is the dictEntry if found, or NULL if not found.
|
||||
* NOTE: this is very ugly code, but it let's us avoid the complication of
|
||||
* doing a scan on another dict. */
|
||||
dictEntry* replaceSatelliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, uint64_t hash, long *defragged) {
|
||||
@@ -1196,7 +1196,7 @@ void activeDefragCycle(void) {
|
||||
cursor = dictScan(db->dict, cursor, defragScanCallback, defragDictBucketCallback, db);
|
||||
|
||||
/* Once in 16 scan iterations, 512 pointer reallocations. or 64 keys
|
||||
* (if we have a lot of pointers in one hash bucket or rehasing),
|
||||
* (if we have a lot of pointers in one hash bucket or rehashing),
|
||||
* check if we reached the time limit.
|
||||
* But regardless, don't start a new db in this loop, this is because after
|
||||
* the last db we call defragOtherGlobals, which must be done in one cycle */
|
||||
|
||||
@@ -508,6 +508,7 @@ void evalGenericCommand(client *c, int evalsha) {
|
||||
|
||||
scriptRunCtx rctx;
|
||||
if (scriptPrepareForRun(&rctx, lctx.lua_client, c, lua_cur_script, l->flags, ro) != C_OK) {
|
||||
lua_pop(lua,2); /* Remove the function and error handler. */
|
||||
return;
|
||||
}
|
||||
rctx.flags |= SCRIPT_EVAL_MODE; /* mark the current run as EVAL (as opposed to FCALL) so we'll
|
||||
|
||||
+116
-58
@@ -57,6 +57,12 @@ struct functionsLibCtx {
|
||||
dict *engines_stats; /* Per engine statistics */
|
||||
};
|
||||
|
||||
typedef struct functionsLibMataData {
|
||||
sds engine;
|
||||
sds name;
|
||||
sds code;
|
||||
} functionsLibMataData;
|
||||
|
||||
dictType engineDictType = {
|
||||
dictSdsCaseHash, /* hash function */
|
||||
dictSdsDup, /* key dup */
|
||||
@@ -124,7 +130,6 @@ static size_t functionMallocSize(functionInfo *fi) {
|
||||
|
||||
static size_t libraryMallocSize(functionLibInfo *li) {
|
||||
return zmalloc_size(li) + sdsZmallocSize(li->name)
|
||||
+ (li->desc ? sdsZmallocSize(li->desc) : 0)
|
||||
+ sdsZmallocSize(li->code);
|
||||
}
|
||||
|
||||
@@ -157,7 +162,6 @@ static void engineLibraryFree(functionLibInfo* li) {
|
||||
dictRelease(li->functions);
|
||||
sdsfree(li->name);
|
||||
sdsfree(li->code);
|
||||
if (li->desc) sdsfree(li->desc);
|
||||
zfree(li);
|
||||
}
|
||||
|
||||
@@ -265,14 +269,13 @@ int functionLibCreateFunction(sds name, void *function, functionLibInfo *li, sds
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
static functionLibInfo* engineLibraryCreate(sds name, engineInfo *ei, sds desc, sds code) {
|
||||
static functionLibInfo* engineLibraryCreate(sds name, engineInfo *ei, sds code) {
|
||||
functionLibInfo *li = zmalloc(sizeof(*li));
|
||||
*li = (functionLibInfo) {
|
||||
.name = sdsdup(name),
|
||||
.functions = dictCreate(&libraryFunctionDictType),
|
||||
.ei = ei,
|
||||
.code = sdsdup(code),
|
||||
.desc = desc ? sdsdup(desc) : NULL,
|
||||
};
|
||||
return li;
|
||||
}
|
||||
@@ -540,17 +543,11 @@ void functionListCommand(client *c) {
|
||||
}
|
||||
}
|
||||
++reply_len;
|
||||
addReplyMapLen(c, with_code? 5 : 4);
|
||||
addReplyMapLen(c, with_code? 4 : 3);
|
||||
addReplyBulkCString(c, "library_name");
|
||||
addReplyBulkCBuffer(c, li->name, sdslen(li->name));
|
||||
addReplyBulkCString(c, "engine");
|
||||
addReplyBulkCBuffer(c, li->ei->name, sdslen(li->ei->name));
|
||||
addReplyBulkCString(c, "description");
|
||||
if (li->desc) {
|
||||
addReplyBulkCBuffer(c, li->desc, sdslen(li->desc));
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
}
|
||||
|
||||
addReplyBulkCString(c, "functions");
|
||||
addReplyArrayLen(c, dictSize(li->functions));
|
||||
@@ -745,11 +742,11 @@ void functionRestoreCommand(client *c) {
|
||||
err = sdsnew("can not read data type");
|
||||
goto load_error;
|
||||
}
|
||||
if (type != RDB_OPCODE_FUNCTION) {
|
||||
if (type != RDB_OPCODE_FUNCTION && type != RDB_OPCODE_FUNCTION2) {
|
||||
err = sdsnew("given type is not a function");
|
||||
goto load_error;
|
||||
}
|
||||
if (rdbFunctionLoad(&payload, rdbver, functions_lib_ctx, RDBFLAGS_NONE, &err) != C_OK) {
|
||||
if (rdbFunctionLoad(&payload, rdbver, functions_lib_ctx, type, RDBFLAGS_NONE, &err) != C_OK) {
|
||||
if (!err) {
|
||||
err = sdsnew("failed loading the given functions payload");
|
||||
}
|
||||
@@ -868,36 +865,111 @@ static int functionsVerifyName(sds name) {
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Compile and save the given library, return C_OK on success and C_ERR on failure.
|
||||
* In case on failure the err out param is set with relevant error message */
|
||||
int functionsCreateWithLibraryCtx(sds lib_name,sds engine_name, sds desc, sds code,
|
||||
int replace, sds* err, functionsLibCtx *lib_ctx) {
|
||||
dictIterator *iter = NULL;
|
||||
dictEntry *entry = NULL;
|
||||
if (functionsVerifyName(lib_name)) {
|
||||
*err = sdsnew("Library names can only contain letters and numbers and must be at least one character long");
|
||||
int functionExtractLibMetaData(sds payload, functionsLibMataData *md, sds *err) {
|
||||
sds name = NULL;
|
||||
sds desc = NULL;
|
||||
sds engine = NULL;
|
||||
sds code = NULL;
|
||||
if (strncmp(payload, "#!", 2) != 0) {
|
||||
*err = sdsnew("Missing library metadata");
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
engineInfo *ei = dictFetchValue(engines, engine_name);
|
||||
if (!ei) {
|
||||
*err = sdsnew("Engine not found");
|
||||
char *shebang_end = strchr(payload, '\n');
|
||||
if (shebang_end == NULL) {
|
||||
*err = sdsnew("Invalid library metadata");
|
||||
return C_ERR;
|
||||
}
|
||||
size_t shebang_len = shebang_end - payload;
|
||||
sds shebang = sdsnewlen(payload, shebang_len);
|
||||
int numparts;
|
||||
sds *parts = sdssplitargs(shebang, &numparts);
|
||||
sdsfree(shebang);
|
||||
if (!parts || numparts == 0) {
|
||||
*err = sdsnew("Invalid library metadata");
|
||||
sdsfreesplitres(parts, numparts);
|
||||
return C_ERR;
|
||||
}
|
||||
engine = sdsdup(parts[0]);
|
||||
sdsrange(engine, 2, -1);
|
||||
for (int i = 1 ; i < numparts ; ++i) {
|
||||
sds part = parts[i];
|
||||
if (strncasecmp(part, "name=", 5) == 0) {
|
||||
if (name) {
|
||||
*err = sdscatfmt(sdsempty(), "Invalid metadata value, name argument was given multiple times");
|
||||
goto error;
|
||||
}
|
||||
name = sdsdup(part);
|
||||
sdsrange(name, 5, -1);
|
||||
continue;
|
||||
}
|
||||
*err = sdscatfmt(sdsempty(), "Invalid metadata value given: %s", part);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
*err = sdsnew("Library name was not given");
|
||||
goto error;
|
||||
}
|
||||
|
||||
sdsfreesplitres(parts, numparts);
|
||||
|
||||
md->name = name;
|
||||
md->code = sdsnewlen(shebang_end, sdslen(payload) - shebang_len);
|
||||
md->engine = engine;
|
||||
|
||||
return C_OK;
|
||||
|
||||
error:
|
||||
if (name) sdsfree(name);
|
||||
if (desc) sdsfree(desc);
|
||||
if (engine) sdsfree(engine);
|
||||
if (code) sdsfree(code);
|
||||
sdsfreesplitres(parts, numparts);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
void functionFreeLibMetaData(functionsLibMataData *md) {
|
||||
if (md->code) sdsfree(md->code);
|
||||
if (md->name) sdsfree(md->name);
|
||||
if (md->engine) sdsfree(md->engine);
|
||||
}
|
||||
|
||||
/* Compile and save the given library, return the loaded library name on success
|
||||
* and NULL on failure. In case on failure the err out param is set with relevant error message */
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx) {
|
||||
dictIterator *iter = NULL;
|
||||
dictEntry *entry = NULL;
|
||||
functionLibInfo *new_li = NULL;
|
||||
functionLibInfo *old_li = NULL;
|
||||
functionsLibMataData md = {0};
|
||||
if (functionExtractLibMetaData(code, &md, err) != C_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (functionsVerifyName(md.name)) {
|
||||
*err = sdsnew("Library names can only contain letters and numbers and must be at least one character long");
|
||||
goto error;
|
||||
}
|
||||
|
||||
engineInfo *ei = dictFetchValue(engines, md.engine);
|
||||
if (!ei) {
|
||||
*err = sdscatfmt(sdsempty(), "Engine '%S' not found", md.engine);
|
||||
goto error;
|
||||
}
|
||||
engine *engine = ei->engine;
|
||||
|
||||
functionLibInfo *old_li = dictFetchValue(lib_ctx->libraries, lib_name);
|
||||
old_li = dictFetchValue(lib_ctx->libraries, md.name);
|
||||
if (old_li && !replace) {
|
||||
*err = sdsnew("Library already exists");
|
||||
return C_ERR;
|
||||
*err = sdscatfmt(sdsempty(), "Library '%S' already exists", md.name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (old_li) {
|
||||
libraryUnlink(lib_ctx, old_li);
|
||||
}
|
||||
|
||||
functionLibInfo *new_li = engineLibraryCreate(lib_name, ei, desc, code);
|
||||
if (engine->create(engine->engine_ctx, new_li, code, err) != C_OK) {
|
||||
new_li = engineLibraryCreate(md.name, ei, code);
|
||||
if (engine->create(engine->engine_ctx, new_li, md.code, err) != C_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -925,48 +997,34 @@ int functionsCreateWithLibraryCtx(sds lib_name,sds engine_name, sds desc, sds co
|
||||
engineLibraryFree(old_li);
|
||||
}
|
||||
|
||||
return C_OK;
|
||||
sds loaded_lib_name = md.name;
|
||||
md.name = NULL;
|
||||
functionFreeLibMetaData(&md);
|
||||
|
||||
return loaded_lib_name;
|
||||
|
||||
error:
|
||||
if (iter) dictReleaseIterator(iter);
|
||||
engineLibraryFree(new_li);
|
||||
if (old_li) {
|
||||
libraryLink(lib_ctx, old_li);
|
||||
}
|
||||
return C_ERR;
|
||||
if (new_li) engineLibraryFree(new_li);
|
||||
if (old_li) libraryLink(lib_ctx, old_li);
|
||||
functionFreeLibMetaData(&md);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* FUNCTION LOAD <ENGINE NAME> <LIBRARY NAME>
|
||||
* [REPLACE] [DESC <LIBRARY DESCRIPTION>] <LIBRARY CODE>
|
||||
*
|
||||
* ENGINE NAME - name of the engine to use the run the library
|
||||
* LIBRARY NAME - name of the library
|
||||
* FUNCTION LOAD [REPLACE] <LIBRARY CODE>
|
||||
* REPLACE - optional, replace existing library
|
||||
* DESCRIPTION - optional, library description
|
||||
* LIBRARY CODE - library code to pass to the engine
|
||||
*/
|
||||
void functionLoadCommand(client *c) {
|
||||
robj *engine_name = c->argv[2];
|
||||
robj *library_name = c->argv[3];
|
||||
|
||||
int replace = 0;
|
||||
int argc_pos = 4;
|
||||
sds desc = NULL;
|
||||
int argc_pos = 2;
|
||||
while (argc_pos < c->argc - 1) {
|
||||
robj *next_arg = c->argv[argc_pos++];
|
||||
if (!strcasecmp(next_arg->ptr, "replace")) {
|
||||
replace = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp(next_arg->ptr, "description")) {
|
||||
if (argc_pos >= c->argc) {
|
||||
addReplyError(c, "Bad function description");
|
||||
return;
|
||||
}
|
||||
desc = c->argv[argc_pos++]->ptr;
|
||||
continue;
|
||||
}
|
||||
addReplyErrorFormat(c, "Unknown option given: %s", (char*)next_arg->ptr);
|
||||
return;
|
||||
}
|
||||
@@ -978,8 +1036,8 @@ void functionLoadCommand(client *c) {
|
||||
|
||||
robj *code = c->argv[argc_pos];
|
||||
sds err = NULL;
|
||||
if (functionsCreateWithLibraryCtx(library_name->ptr, engine_name->ptr,
|
||||
desc, code->ptr, replace, &err, curr_functions_lib_ctx) != C_OK)
|
||||
sds library_name = NULL;
|
||||
if (!(library_name = functionsCreateWithLibraryCtx(code->ptr, replace, &err, curr_functions_lib_ctx)))
|
||||
{
|
||||
addReplyErrorSds(c, err);
|
||||
return;
|
||||
@@ -987,7 +1045,7 @@ void functionLoadCommand(client *c) {
|
||||
/* Indicate that the command changed the data so it will be replicated and
|
||||
* counted as a data change (for persistence configuration) */
|
||||
server.dirty++;
|
||||
addReply(c, shared.ok);
|
||||
addReplyBulkSds(c, library_name);
|
||||
}
|
||||
|
||||
/* Return memory usage of all the engines combine */
|
||||
|
||||
+1
-3
@@ -106,12 +106,10 @@ struct functionLibInfo {
|
||||
dict *functions; /* Functions dictionary */
|
||||
engineInfo *ei; /* Pointer to the function engine */
|
||||
sds code; /* Library code */
|
||||
sds desc; /* Library description */
|
||||
};
|
||||
|
||||
int functionsRegisterEngine(const char *engine_name, engine *engine_ctx);
|
||||
int functionsCreateWithLibraryCtx(sds lib_name, sds engine_name, sds desc, sds code,
|
||||
int replace, sds* err, functionsLibCtx *lib_ctx);
|
||||
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx);
|
||||
unsigned long functionsMemory();
|
||||
unsigned long functionsMemoryOverhead();
|
||||
unsigned long functionsNum();
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@
|
||||
|
||||
/* Interleave lower bits of x and y, so the bits of x
|
||||
* are in the even positions and bits from y in the odd;
|
||||
* x and y must initially be less than 2**32 (65536).
|
||||
* x and y must initially be less than 2**32 (4294967296).
|
||||
* From: https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN
|
||||
*/
|
||||
static inline uint64_t interleave64(uint32_t xlo, uint32_t ylo) {
|
||||
|
||||
+12
-2
@@ -399,6 +399,11 @@ struct commandHelp {
|
||||
"Bind a hash slot to a specific node",
|
||||
12,
|
||||
"3.0.0" },
|
||||
{ "CLUSTER SHARDS",
|
||||
"",
|
||||
"Get array of cluster slots to node mappings",
|
||||
12,
|
||||
"7.0.0" },
|
||||
{ "CLUSTER SLAVES",
|
||||
"node-id",
|
||||
"List replica nodes of the specified master node",
|
||||
@@ -431,7 +436,7 @@ struct commandHelp {
|
||||
"2.8.13" },
|
||||
{ "COMMAND GETKEYSANDFLAGS",
|
||||
"",
|
||||
"Extract keys given a full Redis command",
|
||||
"Extract keys and access flags given a full Redis command",
|
||||
9,
|
||||
"7.0.0" },
|
||||
{ "COMMAND HELP",
|
||||
@@ -630,7 +635,7 @@ struct commandHelp {
|
||||
10,
|
||||
"7.0.0" },
|
||||
{ "FUNCTION LOAD",
|
||||
"engine-name library-name [REPLACE] [DESCRIPTION library-description] function-code",
|
||||
"[REPLACE] function-code",
|
||||
"Create a function with the given arguments (name, code, description)",
|
||||
10,
|
||||
"7.0.0" },
|
||||
@@ -1019,6 +1024,11 @@ struct commandHelp {
|
||||
"Load a module",
|
||||
9,
|
||||
"4.0.0" },
|
||||
{ "MODULE LOADEX",
|
||||
"path [CONFIG name value [name value ...]] [ARGS arg [arg ...]]",
|
||||
"Load a module with extended parameters",
|
||||
9,
|
||||
"7.0.0" },
|
||||
{ "MODULE UNLOAD",
|
||||
"name",
|
||||
"Unload a module",
|
||||
|
||||
+1
-1
@@ -958,7 +958,7 @@ unsigned char *lpPrependInteger(unsigned char *lp, long long lval) {
|
||||
return lpInsertInteger(lp, lval, p, LP_BEFORE, NULL);
|
||||
}
|
||||
|
||||
/* Append the specified element 'ele' of length 'len' at the end of the
|
||||
/* Append the specified element 'ele' of length 'size' at the end of the
|
||||
* listpack. It is implemented in terms of lpInsert(), so the return value is
|
||||
* the same as lpInsert(). */
|
||||
unsigned char *lpAppend(unsigned char *lp, unsigned char *ele, uint32_t size) {
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ void nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst) {
|
||||
|
||||
int main(void) {
|
||||
/* Obtain timezone and daylight info. */
|
||||
tzset(); /* Now 'timezome' global is populated. */
|
||||
tzset(); /* Now 'timezone' global is populated. */
|
||||
time_t t = time(NULL);
|
||||
struct tm *aux = localtime(&t);
|
||||
int daylight_active = aux->tm_isdst;
|
||||
|
||||
+759
-16
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "../redismodule.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
+74
-39
@@ -147,7 +147,6 @@ client *createClient(connection *conn) {
|
||||
c->ref_block_pos = 0;
|
||||
c->qb_pos = 0;
|
||||
c->querybuf = sdsempty();
|
||||
c->pending_querybuf = sdsempty();
|
||||
c->querybuf_peak = 0;
|
||||
c->reqtype = 0;
|
||||
c->argc = 0;
|
||||
@@ -167,6 +166,7 @@ client *createClient(connection *conn) {
|
||||
c->repl_start_cmd_stream_on_ack = 0;
|
||||
c->reploff = 0;
|
||||
c->read_reploff = 0;
|
||||
c->repl_applied = 0;
|
||||
c->repl_ack_off = 0;
|
||||
c->repl_ack_time = 0;
|
||||
c->repl_last_partial_write = 0;
|
||||
@@ -201,7 +201,7 @@ client *createClient(connection *conn) {
|
||||
c->pending_read_list_node = NULL;
|
||||
c->client_tracking_redirection = 0;
|
||||
c->client_tracking_prefixes = NULL;
|
||||
c->last_memory_usage = c->last_memory_usage_on_bucket_update = 0;
|
||||
c->last_memory_usage = 0;
|
||||
c->last_memory_type = CLIENT_TYPE_NORMAL;
|
||||
c->auth_callback = NULL;
|
||||
c->auth_callback_privdata = NULL;
|
||||
@@ -1568,7 +1568,6 @@ void freeClient(client *c) {
|
||||
|
||||
/* Free the query buffer */
|
||||
sdsfree(c->querybuf);
|
||||
sdsfree(c->pending_querybuf);
|
||||
c->querybuf = NULL;
|
||||
|
||||
/* Deallocate structures used to block on blocking ops. */
|
||||
@@ -1940,7 +1939,7 @@ int writeToClient(client *c, int handler_installed) {
|
||||
if (!clientHasPendingReplies(c)) {
|
||||
c->sentlen = 0;
|
||||
/* Note that writeToClient() is called in a threaded way, but
|
||||
* adDeleteFileEvent() is not thread safe: however writeToClient()
|
||||
* aeDeleteFileEvent() is not thread safe: however writeToClient()
|
||||
* is always called with handler_installed set to 0 from threads
|
||||
* so we are fine. */
|
||||
if (handler_installed) {
|
||||
@@ -1954,7 +1953,11 @@ int writeToClient(client *c, int handler_installed) {
|
||||
return C_ERR;
|
||||
}
|
||||
}
|
||||
updateClientMemUsage(c);
|
||||
/* Update client's memory usage after writing.
|
||||
* Since this isn't thread safe we do this conditionally. In case of threaded writes this is done in
|
||||
* handleClientsWithPendingWritesUsingThreads(). */
|
||||
if (io_threads_op == IO_THREADS_OP_IDLE)
|
||||
updateClientMemUsage(c);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -2128,7 +2131,7 @@ int processInlineBuffer(client *c) {
|
||||
* we got some desynchronization in the protocol, for example
|
||||
* because of a PSYNC gone bad.
|
||||
*
|
||||
* However the is an exception: masters may send us just a newline
|
||||
* However there is an exception: masters may send us just a newline
|
||||
* to keep the connection active. */
|
||||
if (querylen != 0 && c->flags & CLIENT_MASTER) {
|
||||
sdsfreesplitres(argv,argc);
|
||||
@@ -2292,8 +2295,12 @@ int processMultibulkBuffer(client *c) {
|
||||
}
|
||||
|
||||
c->qb_pos = newline-c->querybuf+2;
|
||||
if (ll >= PROTO_MBULK_BIG_ARG) {
|
||||
/* If we are going to read a large object from network
|
||||
if (!(c->flags & CLIENT_MASTER) && ll >= PROTO_MBULK_BIG_ARG) {
|
||||
/* When the client is not a master client (because master
|
||||
* client's querybuf can only be trimmed after data applied
|
||||
* and sent to replicas).
|
||||
*
|
||||
* If we are going to read a large object from network
|
||||
* try to make it likely that it will start at c->querybuf
|
||||
* boundary so that we can optimize object creation
|
||||
* avoiding a large copy of data.
|
||||
@@ -2324,10 +2331,11 @@ int processMultibulkBuffer(client *c) {
|
||||
c->argv = zrealloc(c->argv, sizeof(robj*)*c->argv_len);
|
||||
}
|
||||
|
||||
/* Optimization: if the buffer contains JUST our bulk element
|
||||
/* Optimization: if a non-master client's buffer contains JUST our bulk element
|
||||
* instead of creating a new object by *copying* the sds we
|
||||
* just use the current sds string. */
|
||||
if (c->qb_pos == 0 &&
|
||||
if (!(c->flags & CLIENT_MASTER) &&
|
||||
c->qb_pos == 0 &&
|
||||
c->bulklen >= PROTO_MBULK_BIG_ARG &&
|
||||
sdslen(c->querybuf) == (size_t)(c->bulklen+2))
|
||||
{
|
||||
@@ -2388,8 +2396,8 @@ void commandProcessed(client *c) {
|
||||
if (c->flags & CLIENT_MASTER) {
|
||||
long long applied = c->reploff - prev_offset;
|
||||
if (applied) {
|
||||
replicationFeedStreamFromMasterStream(c->pending_querybuf,applied);
|
||||
sdsrange(c->pending_querybuf,applied,-1);
|
||||
replicationFeedStreamFromMasterStream(c->querybuf+c->repl_applied,applied);
|
||||
c->repl_applied += applied;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2432,13 +2440,22 @@ int processCommandAndResetClient(client *c) {
|
||||
/* This function will execute any fully parsed commands pending on
|
||||
* the client. Returns C_ERR if the client is no longer valid after executing
|
||||
* the command, and C_OK for all other cases. */
|
||||
int processPendingCommandsAndResetClient(client *c) {
|
||||
int processPendingCommandAndInputBuffer(client *c) {
|
||||
if (c->flags & CLIENT_PENDING_COMMAND) {
|
||||
c->flags &= ~CLIENT_PENDING_COMMAND;
|
||||
if (processCommandAndResetClient(c) == C_ERR) {
|
||||
return C_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now process client if it has more data in it's buffer.
|
||||
*
|
||||
* Note: when a master client steps into this function,
|
||||
* it can always satisfy this condition, because its querbuf
|
||||
* contains data not applied. */
|
||||
if (c->querybuf && sdslen(c->querybuf) > 0) {
|
||||
return processInputBuffer(c);
|
||||
}
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -2510,8 +2527,26 @@ int processInputBuffer(client *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Trim to pos */
|
||||
if (c->qb_pos) {
|
||||
if (c->flags & CLIENT_MASTER) {
|
||||
/* If the client is a master, trim the querybuf to repl_applied,
|
||||
* since master client is very special, its querybuf not only
|
||||
* used to parse command, but also proxy to sub-replicas.
|
||||
*
|
||||
* Here are some scenarios we cannot trim to qb_pos:
|
||||
* 1. we don't receive complete command from master
|
||||
* 2. master client blocked cause of client pause
|
||||
* 3. io threads operate read, master client flagged with CLIENT_PENDING_COMMAND
|
||||
*
|
||||
* In these scenarios, qb_pos points to the part of the current command
|
||||
* or the beginning of next command, and the current command is not applied yet,
|
||||
* so the repl_applied is not equal to qb_pos. */
|
||||
if (c->repl_applied) {
|
||||
sdsrange(c->querybuf,c->repl_applied,-1);
|
||||
c->qb_pos -= c->repl_applied;
|
||||
c->repl_applied = 0;
|
||||
}
|
||||
} else if (c->qb_pos) {
|
||||
/* Trim to pos */
|
||||
sdsrange(c->querybuf,c->qb_pos,-1);
|
||||
c->qb_pos = 0;
|
||||
}
|
||||
@@ -2519,7 +2554,8 @@ int processInputBuffer(client *c) {
|
||||
/* Update client memory usage after processing the query buffer, this is
|
||||
* important in case the query buffer is big and wasn't drained during
|
||||
* the above loop (because of partially sent big commands). */
|
||||
updateClientMemUsage(c);
|
||||
if (io_threads_op == IO_THREADS_OP_IDLE)
|
||||
updateClientMemUsage(c);
|
||||
|
||||
return C_OK;
|
||||
}
|
||||
@@ -2546,16 +2582,22 @@ void readQueryFromClient(connection *conn) {
|
||||
if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1
|
||||
&& c->bulklen >= PROTO_MBULK_BIG_ARG)
|
||||
{
|
||||
ssize_t remaining = (size_t)(c->bulklen+2)-sdslen(c->querybuf);
|
||||
ssize_t remaining = (size_t)(c->bulklen+2)-(sdslen(c->querybuf)-c->qb_pos);
|
||||
big_arg = 1;
|
||||
|
||||
/* Note that the 'remaining' variable may be zero in some edge case,
|
||||
* for example once we resume a blocked client after CLIENT PAUSE. */
|
||||
if (remaining > 0) readlen = remaining;
|
||||
|
||||
/* Master client needs expand the readlen when meet BIG_ARG(see #9100),
|
||||
* but doesn't need align to the next arg, we can read more data. */
|
||||
if (c->flags & CLIENT_MASTER && readlen < PROTO_IOBUF_LEN)
|
||||
readlen = PROTO_IOBUF_LEN;
|
||||
}
|
||||
|
||||
qblen = sdslen(c->querybuf);
|
||||
if (big_arg || sdsalloc(c->querybuf) < PROTO_IOBUF_LEN) {
|
||||
if (!(c->flags & CLIENT_MASTER) && // master client's querybuf can grow greedy.
|
||||
(big_arg || sdsalloc(c->querybuf) < PROTO_IOBUF_LEN)) {
|
||||
/* When reading a BIG_ARG we won't be reading more than that one arg
|
||||
* into the query buffer, so we don't need to pre-allocate more than we
|
||||
* need, so using the non-greedy growing. For an initial allocation of
|
||||
@@ -2585,12 +2627,6 @@ void readQueryFromClient(connection *conn) {
|
||||
}
|
||||
freeClientAsync(c);
|
||||
goto done;
|
||||
} else if (c->flags & CLIENT_MASTER) {
|
||||
/* Append the query buffer to the pending (not applied) buffer
|
||||
* of the master. We'll use this buffer later in order to have a
|
||||
* copy of the string applied by the last command executed. */
|
||||
c->pending_querybuf = sdscatlen(c->pending_querybuf,
|
||||
c->querybuf+qblen,nread);
|
||||
}
|
||||
|
||||
sdsIncrLen(c->querybuf,nread);
|
||||
@@ -2868,8 +2904,6 @@ void clientCommand(client *c) {
|
||||
" Control the replies sent to the current connection.",
|
||||
"SETNAME <name>",
|
||||
" Assign the name <name> to the current connection.",
|
||||
"GETNAME",
|
||||
" Get the name of the current connection.",
|
||||
"UNBLOCK <clientid> [TIMEOUT|ERROR]",
|
||||
" Unblock the specified blocked client.",
|
||||
"TRACKING (ON|OFF) [REDIRECT <id>] [BCAST] [PREFIX <prefix> [...]]",
|
||||
@@ -3075,6 +3109,10 @@ NULL
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[2],&id,NULL)
|
||||
!= C_OK) return;
|
||||
struct client *target = lookupClientByID(id);
|
||||
/* Note that we never try to unblock a client blocked on a module command, which
|
||||
* doesn't have a timeout callback (even in the case of UNBLOCK ERROR).
|
||||
* The reason is that we assume that if a command doesn't expect to be timedout,
|
||||
* it also doesn't expect to be unblocked by CLIENT UNBLOCK */
|
||||
if (target && target->flags & CLIENT_BLOCKED && moduleBlockedClientMayTimeout(target)) {
|
||||
if (unblock_error)
|
||||
addReplyError(target,
|
||||
@@ -3464,7 +3502,11 @@ static void retainOriginalCommandVector(client *c) {
|
||||
* original_argv array. */
|
||||
void redactClientCommandArgument(client *c, int argc) {
|
||||
retainOriginalCommandVector(c);
|
||||
decrRefCount(c->argv[argc]);
|
||||
if (c->original_argv[argc] == shared.redacted) {
|
||||
/* This argument has already been redacted */
|
||||
return;
|
||||
}
|
||||
decrRefCount(c->original_argv[argc]);
|
||||
c->original_argv[argc] = shared.redacted;
|
||||
}
|
||||
|
||||
@@ -4165,8 +4207,8 @@ int handleClientsWithPendingWritesUsingThreads(void) {
|
||||
while((ln = listNext(&li))) {
|
||||
client *c = listNodeValue(ln);
|
||||
|
||||
/* Update the client in the mem usage buckets after we're done processing it in the io-threads */
|
||||
updateClientMemUsageBucket(c);
|
||||
/* Update the client in the mem usage after we're done processing it in the io-threads */
|
||||
updateClientMemUsage(c);
|
||||
|
||||
/* Install the write handler if there are pending writes in some
|
||||
* of the clients. */
|
||||
@@ -4274,17 +4316,10 @@ int handleClientsWithPendingReadsUsingThreads(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Once io-threads are idle we can update the client in the mem usage buckets */
|
||||
updateClientMemUsageBucket(c);
|
||||
/* Once io-threads are idle we can update the client in the mem usage */
|
||||
updateClientMemUsage(c);
|
||||
|
||||
if (processPendingCommandsAndResetClient(c) == C_ERR) {
|
||||
/* If the client is no longer valid, we avoid
|
||||
* processing the client later. So we just go
|
||||
* to the next. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processInputBuffer(c) == C_ERR) {
|
||||
if (processPendingCommandAndInputBuffer(c) == C_ERR) {
|
||||
/* If the client is no longer valid, we avoid
|
||||
* processing the client later. So we just go
|
||||
* to the next. */
|
||||
|
||||
+4
-1
@@ -1532,7 +1532,7 @@ NULL
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"stats") && c->argc == 2) {
|
||||
struct redisMemOverhead *mh = getMemoryOverheadData();
|
||||
|
||||
addReplyMapLen(c,26+mh->num_dbs);
|
||||
addReplyMapLen(c,27+mh->num_dbs);
|
||||
|
||||
addReplyBulkCString(c,"peak.allocated");
|
||||
addReplyLongLong(c,mh->peak_allocated);
|
||||
@@ -1552,6 +1552,9 @@ NULL
|
||||
addReplyBulkCString(c,"clients.normal");
|
||||
addReplyLongLong(c,mh->clients_normal);
|
||||
|
||||
addReplyBulkCString(c,"cluster.links");
|
||||
addReplyLongLong(c,mh->cluster_links);
|
||||
|
||||
addReplyBulkCString(c,"aof.buffer");
|
||||
addReplyLongLong(c,mh->aof_buffer);
|
||||
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -62,7 +62,7 @@ static inline void swapfunc (char *, char *, size_t, int);
|
||||
} while (--i > 0); \
|
||||
}
|
||||
|
||||
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
|
||||
#define SWAPINIT(a, es) swaptype = (uintptr_t)a % sizeof(long) || \
|
||||
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
|
||||
|
||||
static inline void
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user