Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29622276ec | ||
|
|
3d1d3e23ac | ||
|
|
941cdc2759 | ||
|
|
1a58981868 | ||
|
|
c7b3ce90f1 | ||
|
|
819d291bd7 | ||
|
|
a51eb05b18 | ||
|
|
986dbf716e | ||
|
|
d4439bd41c | ||
|
|
e26a769d96 |
@@ -1 +1 @@
|
||||
codespell==2.2.5
|
||||
codespell==2.2.4
|
||||
|
||||
+14
-32
@@ -7,7 +7,7 @@ jobs:
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx
|
||||
./runtest --verbose --tags -slow --dump-logs
|
||||
- name: module api test
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs
|
||||
run: ./runtest-moduleapi --verbose --dump-logs
|
||||
- name: validate commands.def up to date
|
||||
run: |
|
||||
touch src/commands/ping.json
|
||||
@@ -28,22 +28,22 @@ jobs:
|
||||
test-sanitizer-address:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
# build with TLS module just for compilation coverage
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror -DDEBUG_ASSERTIONS' BUILD_TLS=module
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror' BUILD_TLS=module
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx -y
|
||||
- name: test
|
||||
run: ./runtest --verbose --tags -slow --dump-logs
|
||||
- name: module api test
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs
|
||||
run: ./runtest-moduleapi --verbose --dump-logs
|
||||
|
||||
build-debian-old:
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:buster
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y build-essential
|
||||
@@ -52,51 +52,33 @@ jobs:
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386 gcc-multilib g++-multilib
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make REDIS_CFLAGS='-Werror' 32bit
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror' MALLOC=libc
|
||||
|
||||
build-centos-jemalloc:
|
||||
build-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: quay.io/centos/centos:stream9
|
||||
container: centos:7
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: |
|
||||
dnf -y install which gcc gcc-c++ make
|
||||
yum -y install gcc make
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
|
||||
build-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8 g++-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
|
||||
@@ -19,15 +19,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Creates and uploads a Coverity build on a schedule
|
||||
name: Coverity Scan
|
||||
on:
|
||||
schedule:
|
||||
# Run once daily, since below 500k LOC can have 21 builds per week, per https://scan.coverity.com/faq#frequency
|
||||
- cron: '0 0 * * *'
|
||||
# Support manual execution
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
coverity:
|
||||
if: github.repository == 'redis/redis'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- name: Download and extract the Coverity Build Tool
|
||||
run: |
|
||||
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=redis-unstable" -O cov-analysis-linux64.tar.gz
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
- name: Install Redis dependencies
|
||||
run: sudo apt install -y gcc tcl8.6 tclx procps libssl-dev
|
||||
- name: Build with cov-build
|
||||
run: cov-analysis-linux64/bin/cov-build --dir cov-int make
|
||||
- name: Upload the result
|
||||
run: |
|
||||
tar czvf cov-int.tgz cov-int
|
||||
curl \
|
||||
--form project=redis-unstable \
|
||||
--form email=${{ secrets.COVERITY_SCAN_EMAIL }} \
|
||||
--form token=${{ secrets.COVERITY_SCAN_TOKEN }} \
|
||||
--form file=@cov-int.tgz \
|
||||
https://scan.coverity.com/builds
|
||||
+126
-253
@@ -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,iothreads,ubuntu,centos,malloc,specific,fortify,reply-schema,oldTC'
|
||||
default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc,specific,fortify,reply-schema'
|
||||
skiptests:
|
||||
description: 'tests to skip (delete the ones you wanna keep, do not leave empty)'
|
||||
default: 'redis,modules,sentinel,cluster,unittest'
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -88,15 +88,14 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update && apt-get install -y make gcc-13 g++-13
|
||||
apt-get update && apt-get install -y make gcc-13
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror -DREDIS_TEST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
|
||||
- name: testprep
|
||||
run: apt-get install -y tcl8.6 tclx procps
|
||||
@@ -105,7 +104,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -132,7 +131,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -145,7 +144,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -169,7 +168,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -182,7 +181,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -206,13 +205,13 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386 g++ gcc-multilib g++-multilib
|
||||
sudo apt-get update && sudo apt-get install libc6-dev-i386
|
||||
make 32bit REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx
|
||||
@@ -223,7 +222,7 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./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}}
|
||||
@@ -250,7 +249,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -268,7 +267,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls --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: |
|
||||
@@ -294,7 +293,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -312,7 +311,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -338,7 +337,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -370,7 +369,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -448,7 +447,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -457,7 +456,7 @@ jobs:
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind g++ -y
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- 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}}
|
||||
@@ -478,7 +477,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -490,7 +489,7 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
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: |
|
||||
@@ -513,7 +512,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -522,7 +521,7 @@ jobs:
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 tclx valgrind g++ -y
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- 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}}
|
||||
@@ -543,7 +542,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -555,7 +554,7 @@ jobs:
|
||||
sudo apt-get install tcl8.6 tclx valgrind -y
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 --timeout 2400 --dump-logs ${{github.event.inputs.test_args}}
|
||||
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: |
|
||||
@@ -583,12 +582,12 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-DREDIS_TEST -Werror -DDEBUG_ASSERTIONS'
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-DREDIS_TEST -Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -598,7 +597,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -630,7 +629,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -645,7 +644,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -656,12 +655,12 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/redis-server test all --accurate
|
||||
|
||||
test-centos-jemalloc:
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'centos')
|
||||
container: quay.io/centos/centos:stream9
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -673,24 +672,22 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
dnf -y install which gcc make g++
|
||||
yum -y install gcc make
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
run: yum -y install which tcl tclx
|
||||
- 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: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -698,12 +695,12 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-centos-tls-module:
|
||||
test-centos7-tls-module:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: quay.io/centos/centos:stream9
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -715,18 +712,18 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
dnf -y install which gcc make openssl-devel openssl g++
|
||||
make BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
- name: testprep
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
yum -y install tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
@@ -735,7 +732,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -745,12 +742,12 @@ jobs:
|
||||
run: |
|
||||
./runtest-cluster --tls-module ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-centos-tls-module-no-tls:
|
||||
test-centos7-tls-module-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls')
|
||||
container: quay.io/centos/centos:stream9
|
||||
container: centos:7
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
@@ -762,18 +759,18 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
dnf -y install which gcc make openssl-devel openssl g++
|
||||
make BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
- name: testprep
|
||||
run: |
|
||||
dnf -y install epel-release
|
||||
dnf -y install tcl tcltls procps-ng /usr/bin/kill
|
||||
yum -y install tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
@@ -782,7 +779,7 @@ jobs:
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
@@ -808,7 +805,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -816,10 +813,10 @@ jobs:
|
||||
run: make REDIS_CFLAGS='-Werror'
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: ./runtest --accurate --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: ./runtest --accurate --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: ./runtest-moduleapi --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
|
||||
|
||||
test-macos-latest-sentinel:
|
||||
runs-on: macos-latest
|
||||
@@ -837,7 +834,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -863,7 +860,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -873,19 +870,13 @@ jobs:
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
build-macos:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-12, macos-14]
|
||||
runs-on: ${{ matrix.os }}
|
||||
test-freebsd:
|
||||
runs-on: macos-12
|
||||
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.skipjobs, 'freebsd') && !(contains(github.event.inputs.skiptests, 'redis') && contains(github.event.inputs.skiptests, 'modules'))
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
@@ -895,43 +886,83 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make REDIS_CFLAGS='-Werror -DREDIS_TEST'
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.1
|
||||
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 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:
|
||||
test-freebsd-sentinel:
|
||||
runs-on: macos-12
|
||||
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.skipjobs, 'freebsd') && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
timeout-minutes: 14400
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
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@v4
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: cross-platform-actions/action@v0.22.0
|
||||
uses: vmactions/freebsd-vm@v0.3.1
|
||||
with:
|
||||
operating_system: freebsd
|
||||
environment_variables: MAKE
|
||||
version: 13.2
|
||||
shell: bash
|
||||
run: |
|
||||
sudo pkg install -y bash gmake lang/tcl86 lang/tclx gcc
|
||||
gmake
|
||||
./runtest --single unit/keyspace --single unit/auth --single unit/networking --single unit/protocol
|
||||
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-12
|
||||
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
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: test
|
||||
uses: vmactions/freebsd-vm@v0.3.1
|
||||
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
|
||||
@@ -949,7 +980,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -964,7 +995,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -988,7 +1019,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -1003,7 +1034,7 @@ jobs:
|
||||
run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
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}}
|
||||
@@ -1027,7 +1058,7 @@ jobs:
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
@@ -1040,7 +1071,7 @@ jobs:
|
||||
run: ./runtest --log-req-res --no-latency --dont-clean --force-resp3 --tags -slow --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: CFLAGS='-Werror' ./runtest-moduleapi --log-req-res --no-latency --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
run: ./runtest-moduleapi --log-req-res --no-latency --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel --log-req-res --dont-clean --force-resp3 ${{github.event.inputs.cluster_test_args}}
|
||||
@@ -1054,161 +1085,3 @@ jobs:
|
||||
- name: validator
|
||||
run: ./utils/req-res-log-validator.py --verbose --fail-missing-reply-schemas ${{ (!contains(github.event.inputs.skiptests, 'redis') && !contains(github.event.inputs.skiptests, 'module') && !contains(github.event.inputs.sentinel, 'redis') && !contains(github.event.inputs.skiptests, 'cluster')) && github.event.inputs.test_args == '' && github.event.inputs.cluster_test_args == '' && '--fail-commands-not-all-hit' || '' }}
|
||||
|
||||
test-old-chain-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8 g++-4.8
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: apt-get install -y tcl tcltls tclx
|
||||
- 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: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-old-chain-tls-module:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls') && !contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8 g++-4.8 openssl libssl-dev
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make CC=gcc CXX=g++ BUILD_TLS=module REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
apt-get install -y tcl tcltls tclx
|
||||
./utils/gen-test-certs.sh
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs --tls-module --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 --tls-module ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-old-chain-tls-module-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
|
||||
!contains(github.event.inputs.skipjobs, 'tls') && !contains(github.event.inputs.skipjobs, 'oldTC')
|
||||
container: ubuntu:20.04
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- name: prep
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
|
||||
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
|
||||
echo "skipjobs: ${{github.event.inputs.skipjobs}}"
|
||||
echo "skiptests: ${{github.event.inputs.skiptests}}"
|
||||
echo "test_args: ${{github.event.inputs.test_args}}"
|
||||
echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y gnupg2
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list
|
||||
echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
|
||||
apt-get update
|
||||
apt-get install -y make gcc-4.8 g++-4.8 openssl libssl-dev
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100
|
||||
make BUILD_TLS=module CC=gcc REDIS_CFLAGS='-Werror'
|
||||
- name: testprep
|
||||
run: |
|
||||
apt-get install -y 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: |
|
||||
CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
@@ -23,7 +23,6 @@ jobs:
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--tags -slow
|
||||
- name: Archive redis log
|
||||
if: ${{ failure() }}
|
||||
@@ -37,12 +36,12 @@ jobs:
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
run: |
|
||||
./src/redis-server --cluster-enabled yes --daemonize yes --save "" --logfile external-redis-cluster.log \
|
||||
./src/redis-server --cluster-enabled yes --daemonize yes --save "" --logfile external-redis.log \
|
||||
--enable-protected-configs yes --enable-debug-command yes --enable-module-command yes
|
||||
- name: Create a single node cluster
|
||||
run: ./src/redis-cli cluster addslots $(for slot in {0..16383}; do echo $slot; done); sleep 5
|
||||
@@ -50,7 +49,6 @@ jobs:
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--cluster-mode \
|
||||
--tags -slow
|
||||
- name: Archive redis log
|
||||
@@ -58,28 +56,27 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-external-cluster-log
|
||||
path: external-redis-cluster.log
|
||||
path: external-redis.log
|
||||
|
||||
test-external-nodebug:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'schedule' || github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: make REDIS_CFLAGS=-Werror
|
||||
- name: Start redis-server
|
||||
run: |
|
||||
./src/redis-server --daemonize yes --save "" --logfile external-redis-nodebug.log
|
||||
./src/redis-server --daemonize yes --save "" --logfile external-redis.log
|
||||
- name: Run external test
|
||||
run: |
|
||||
./runtest \
|
||||
--host 127.0.0.1 --port 6379 \
|
||||
--verbose \
|
||||
--tags "-slow -needs:debug"
|
||||
- name: Archive redis log
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-external-redis-nodebug-log
|
||||
path: external-redis-nodebug.log
|
||||
name: test-external-redis-log
|
||||
path: external-redis.log
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
name: redis_docs_sync
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
redis_docs_sync:
|
||||
if: github.repository == 'redis/redis'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.DOCS_APP_ID }}
|
||||
private-key: ${{ secrets.DOCS_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Invoke workflow on redis/docs
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
RELEASE_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
LATEST_RELEASE=$(
|
||||
curl -Ls \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/redis/redis/releases/latest \
|
||||
| jq -r '.tag_name'
|
||||
)
|
||||
|
||||
if [[ "${LATEST_RELEASE}" == "${RELEASE_NAME}" ]]; then
|
||||
gh workflow run -R redis/docs redis_docs_sync.yaml -f release="${RELEASE_NAME}"
|
||||
fi
|
||||
@@ -12,9 +12,9 @@ jobs:
|
||||
reply-schemas-linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup nodejs
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
- name: Install packages
|
||||
run: npm install ajv
|
||||
- name: linter
|
||||
|
||||
@@ -16,10 +16,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: pip cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
|
||||
@@ -30,7 +30,6 @@ deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
deps/hdr_histogram/libhdrhistogram.a
|
||||
deps/fpconv/libfpconv.a
|
||||
deps/fast_float/libfast_float.a
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
|
||||
+402
-11
@@ -1,16 +1,407 @@
|
||||
Hello! This file is just a placeholder, since this is the "unstable" branch
|
||||
of Redis, the place where all the development happens.
|
||||
Redis 7.2 release notes
|
||||
=======================
|
||||
|
||||
There is no release notes for this branch, it gets forked into another branch
|
||||
every time there is a partial feature freeze in order to eventually create
|
||||
a new stable release.
|
||||
--------------------------------------------------------------------------------
|
||||
Upgrade urgency levels:
|
||||
|
||||
Usually "unstable" is stable enough for you to use it in development environments
|
||||
however you should never use it in production environments. It is possible
|
||||
to download the latest stable release here:
|
||||
LOW: No need to upgrade unless there are new features you want to use.
|
||||
MODERATE: Program an upgrade of the server, but it's not urgent.
|
||||
HIGH: There is a critical bug that may affect a subset of users. Upgrade!
|
||||
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
|
||||
SECURITY: There are security fixes in the release.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
https://download.redis.io/redis-stable.tar.gz
|
||||
================================================================================
|
||||
Redis 7.2.0 GA Released Mon Aug 15 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
More information is available at https://redis.io
|
||||
Upgrade urgency LOW: This is the first stable Release for Redis 7.2.
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* redis-cli in cluster mode handles `unknown-endpoint` (#12273)
|
||||
* Update request / response policy hints for a few commands (#12417)
|
||||
* Ensure that the function load timeout is disabled during loading from RDB/AOF and on replicas. (#12451)
|
||||
* Fix false success and a memory leak for ACL selector with bad parenthesis combination (#12452)
|
||||
* Fix the assertion when script timeout occurs after it signaled a blocked client (#12459)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Update MONITOR client's memory correctly for INFO and client-eviction (#12420)
|
||||
* The response of cluster nodes was unnecessarily adding an extra comma when no
|
||||
hostname was present. (#12411)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC3 Released Mon July 10 12:00:00 IDT 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the third Release Candidate for Redis 7.2.
|
||||
Upgrade urgency SECURITY: If you're using a previous release candidate of 7.2.
|
||||
|
||||
Security Fixes:
|
||||
* (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger
|
||||
a heap overflow in the cjson and cmsgpack libraries, and result in heap
|
||||
corruption and potentially remote code execution. The problem exists in all
|
||||
versions of Redis with Lua scripting support, starting from 2.6, and affects
|
||||
only authenticated and authorized users.
|
||||
* (CVE-2023-36824) Extracting key names from a command and a list of arguments
|
||||
may, in some cases, trigger a heap overflow and result in reading random heap
|
||||
memory, heap corruption and potentially remote code execution. Specifically:
|
||||
using COMMAND GETKEYS* and validation of key names in ACL rules.
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
New administrative and introspection commands and command arguments
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* Make SENTINEL CONFIG [SET|GET] variadic. (#10362)
|
||||
|
||||
Potentially Breaking / Behavior Changes
|
||||
=======================================
|
||||
|
||||
* Cluster SHARD IDs are no longer visible in the cluster nodes output,
|
||||
introduced in 7.2-RC1. (#10536, #12166)
|
||||
* When calling PUBLISH with a RESP3 client that's also subscribed to the same channel,
|
||||
the order is changed and the reply is sent before the published message (#12326)
|
||||
|
||||
New configuration options
|
||||
=========================
|
||||
|
||||
* Add a new loglevel "nothing" to disable logging (#12133)
|
||||
* Add cluster-announce-human-nodename - a unique identifier for a node that is
|
||||
be used in logs for debugging (#9564)
|
||||
|
||||
Other General Improvements
|
||||
==========================
|
||||
|
||||
* Allow CLUSTER SLOTS / SHARDS commands during loading (#12269)
|
||||
* Support TLS service when "tls-cluster" is not enabled and persist both plain
|
||||
and TLS port in nodes.conf (#12233)
|
||||
* Update SPOP and RESTORE commands to replicate unlink commands to replicas
|
||||
when the server is configured to use async server deletes (#12320)
|
||||
* Try lazyfree the temporary zset in ZUNION / ZINTER / ZDIFF (#12229)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Optimize PSUBSCRIBE and PUNSUBSCRIBE from O(N*M) to O(N) (#12298)
|
||||
* Optimize SCAN, SSCAN, HSCAN, ZSCAN commands (#12209)
|
||||
* Set Jemalloc --disable-cache-oblivious to reduce memory overhead (#12315)
|
||||
* Optimize ZINTERCARD to avoid create a temporary zset (#12229)
|
||||
* Optimize HRANDFIELD and ZRANDMEMBER listpack encoded (#12205)
|
||||
* Numerous other optimizations (#12155, #12082, #11626, #11944, #12316, #12250,
|
||||
#12177, #12185)
|
||||
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli: Handle RESP3 double responses that contain a NaN (#12254)
|
||||
* redis-cli: Support URIs with IPv6 (#11834)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Align semantics of the new (v7.2 RC2) RM_ReplyWithErrorFormat with RM_ReplyWithError.
|
||||
This is a breaking change that affects the generated error code. (#12321)
|
||||
* Forbid RM_AddPostNotificationJob on loading and on read-only replicas (#12304)
|
||||
* Add ability for module command filter to know which client is being handled (#12219)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Fix broken protocol when PUBLISH is used inside MULTI when the RESP3
|
||||
publishing client is also subscribed for the channel (#12326)
|
||||
* Fix WAIT to be effective after a blocked module command being unblocked (#12220)
|
||||
* Re-enable downscale rehashing while there is a fork child (#12276)
|
||||
* Fix possible hang in HRANDFIELD, SRANDMEMBER, ZRANDMEMBER when used with `<count>` (#12276)
|
||||
* Improve fairness issue in RANDOMKEY, HRANDFIELD, SRANDMEMBER, ZRANDMEMBER, SPOP, and eviction (#12276)
|
||||
* Cluster: fix a race condition where a slot migration may revert on a subsequent failover or node joining (#12344)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Fix XREADGROUP BLOCK with ">" from hanging (#12301)
|
||||
* Fix assertion when a blocked command is rejected when re-processed. (#12247)
|
||||
* Fix use after free on a blocking RM_Call. (#12342)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC2 Released Mon May 15 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the second Release Candidate for Redis 7.2.
|
||||
|
||||
INFO fields and introspection changes
|
||||
=====================================
|
||||
|
||||
* Add a few low level event loop metrics to help diagnose latency (#11963)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Minor performance improvement to SADD and HSET (#12019)
|
||||
|
||||
Platform / toolchain support related changes
|
||||
=================================================
|
||||
|
||||
* Upgrade to Jemalloc 5.3.0, resolves a rare fork child hang (#12115)
|
||||
* Fix a compiler fortification induced crash when used with link time optimizations (#11982)
|
||||
* Fix local clients detection, 127.*.*.* instead of 127.0.0.1 (#11664)
|
||||
* Report AOF failure status to systemd in shutdown (#12065)
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli: Reimplement and improve help hints based on actual command arg docs (#10515)
|
||||
* redis-cli: Add option --count for tuning SCAN based features (#12042)
|
||||
* redis-benchmark: Add --seed option to seed the random number generator (#11945)
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Add RM_RdbLoad and RM_RdbSave APIs (#11852)
|
||||
* Add RM_ReplyWithErrorFormat that can support format string (#11923)
|
||||
* Fix: Delete empty key when RM_ZsetAdd, RM_ZsetIncrby, RM_StreamAdd fail (#12129)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* LPOS with RANK set to LONG_MIN returning wrong result (#12167)
|
||||
* Avoid unnecessary full sync after master restart in a rare case (#12088)
|
||||
* Iterate clients fairly when processing background chores (#12025)
|
||||
* Avoid incorrect shrinking of query buffer when reading large data from clients (#12000)
|
||||
* Sentinel: Fix config rewrite error when old known-slave is used (#11775)
|
||||
* ACL: Disconnect pub-sub subscribers when revoking allchannels permission (#11992)
|
||||
* Add a missing fsync of AOF file in rare cases (#11973)
|
||||
|
||||
Fixes for issues in previous releases of Redis 7.2
|
||||
--------------------------------------------------
|
||||
|
||||
* Fix tracking of command duration metrics for MULTI, EVAL, WAIT and modules (#11970)
|
||||
|
||||
================================================================================
|
||||
Redis 7.2 RC1 Released Wed Mar 22 12:00:00 IST 2023
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: This is the first Release Candidate for Redis 7.2.
|
||||
|
||||
Redis Release Candidate (RC) versions are early versions that are made available
|
||||
for early adopters in the community to test them. We do not consider
|
||||
them suitable for production environments.
|
||||
|
||||
Introduction to the Redis 7.2 release
|
||||
=====================================
|
||||
|
||||
Redis 7.2 includes optimizations, several new commands, some improvements,
|
||||
bug fixes, and several new module APIs.
|
||||
|
||||
In particular, users should be aware of the following changes:
|
||||
|
||||
1. Redis 7.2 uses a new format (version 11) for RDB files, which is incompatible
|
||||
with older versions.
|
||||
2. See section about breaking changes mentioned below.
|
||||
3. If you use modules, see the module API breaking changes section below.
|
||||
|
||||
Here is a comprehensive list of changes in this release compared to 7.0.10.
|
||||
Each one includes the PR number that added it so that you can get more details
|
||||
at https://github.com/redis/redis/pull/<number>
|
||||
|
||||
New Features
|
||||
============
|
||||
|
||||
* Introduce WAITAOF command, to block the client until a specified number
|
||||
of Redises have synced all previous write commands to the AOF on disk,
|
||||
see https://redis.io/commands/waitaof/
|
||||
|
||||
New user commands or command arguments
|
||||
--------------------------------------
|
||||
|
||||
* WAITAOF blocks until writes have been synced to disk (#11713)
|
||||
* Add WITHSCORE option to ZRANK and ZREVRANK (#11235)
|
||||
|
||||
New administrative and introspection commands and command arguments
|
||||
-------------------------------------------------------------------
|
||||
|
||||
* CLIENT SETINFO lets client library report name and version Redis (#11758)
|
||||
* CLIENT NO-TOUCH for clients to run commands without affecting LRU/LFU of keys (#11483)
|
||||
* Introduce Shard IDs to logically group nodes in cluster mode based on
|
||||
replication. Shard IDs are automatically assigned and visible via
|
||||
`CLUSTER MYSHARDID`. (#10536)
|
||||
|
||||
Command replies that have been extended
|
||||
---------------------------------------
|
||||
|
||||
* ACL LOG - Add entry id, timestamp created, and timestamp last updated time (#11477)
|
||||
* COMMAND DOCS - Repurpose arg names as the unique ID (#11051)
|
||||
* CLIENT LIST has `T` flag to indicate CLIENT NO-TOUCH (#11483)
|
||||
* CLIENT LIST show lib-name, lib-ver (#11758)
|
||||
|
||||
Potentially Breaking / Behavior Changes
|
||||
=======================================
|
||||
|
||||
* Client side tracking for scripts now tracks the keys that are read by the
|
||||
script instead of the keys that are declared by the caller of EVAL / FCALL (#11770)
|
||||
* Freeze time sampling during command execution and in scripts (#10300)
|
||||
* When a blocked command is being unblocked, checks like ACL, OOM, etc are
|
||||
re-evaluated (#11012)
|
||||
* Unify ACL failure error message text and error codes (#11160)
|
||||
* Blocked stream command that's released when key no longer exists carries a
|
||||
different error code (#11012)
|
||||
* Command stats are updated for blocked commands only when / if the command
|
||||
actually executes (#11012)
|
||||
* The way ACL users are stored internally no longer removes redundant command
|
||||
and category rules, which may alter the way those rules are displayed as part
|
||||
of `ACL SAVE`, `ACL GETUSER` and `ACL LIST` (#11224)
|
||||
* Client connections created for TLS-based replication use SNI if possible (#11458)
|
||||
* Stream consumers: Re-purpose seen-time, add active-time (#11099)
|
||||
* XREADGROUP and X[AUTO]CLAIM create the consumer regardless of whether it was
|
||||
able to perform some reading/claiming (#11099)
|
||||
* ACL default newly created user set sanitize-payload flag in ACL LIST/GETUSER #11279
|
||||
* Fix HELLO command not to affect the client state unless successful (#11659)
|
||||
* Normalize `NAN` in replies to a single nan type, like we do with `inf` (#11597)
|
||||
|
||||
Deprecations
|
||||
============
|
||||
|
||||
* Mark the QUIT command as deprecated (#11439)
|
||||
* Delete RDB loading code for pre-release RDB formats (#11058)
|
||||
|
||||
Performance and resource utilization improvements
|
||||
=================================================
|
||||
|
||||
* Significant memory optimization of small list type keys (#11303)
|
||||
* Significant memory optimization for small set type keys (#11290)
|
||||
* Significant memory optimization for large sets (#11595)
|
||||
* Significant speed optimization in ZRANGE replies WITHSCORES in case of integer scores (#11779)
|
||||
* Significant speed optimization in double replies, mainly sorted sets commands (#10587)
|
||||
* Optimize the performance of commands with multiple keys in cluster mode (#11044)
|
||||
* Incrementally reclaim OS page cache of RDB file (#11248)
|
||||
* Improve memory management of cluster bus links when there is a large number of pending messages (#11343)
|
||||
* Minor performance improvement for workloads that use commands without pipelining (#11220)
|
||||
|
||||
Changes in CLI tools
|
||||
====================
|
||||
|
||||
* redis-cli accepts commands in subscribed mode (#11873)
|
||||
|
||||
Other General Improvements
|
||||
==========================
|
||||
|
||||
* WAIT now no longer waits for the replication offset after your last command,
|
||||
but rather the replication offset after your last write (#11713)
|
||||
* Automatically propagate node deletion to other nodes in a cluster when
|
||||
`CLUSTER FORGET` is called, allowing nodes to be deleted with a single call
|
||||
in most cases (#10869)
|
||||
* Blocking commands that were disallowed in scripts now behave in scripts the
|
||||
same they did in MULTI (#11568)
|
||||
|
||||
Platform / toolchain support related changes
|
||||
=================================================
|
||||
|
||||
* 32-bit builds compiled without HAVE_MALLOC_SIZE (not jemalloc or glibc)
|
||||
will consume more memory (#11595)
|
||||
* Use jemalloc by default also on ARM (#11407)
|
||||
* Adds stack trace and register dump support in crash report for illumos/solaris (#11335)
|
||||
|
||||
|
||||
New configuration options
|
||||
=========================
|
||||
|
||||
* locale-collate runtime config to control setlocale affecting Lua and SORT (#11059)
|
||||
* Add CONFIG SET and GET loglevel feature in Sentinel (#11214)
|
||||
|
||||
INFO fields and introspection changes
|
||||
=====================================
|
||||
|
||||
* Added 4 new info fields for authentication errors and commands denied access
|
||||
for keys, channels and commands (#11288)
|
||||
* INFO SERVER includes a list of listeners (#9320)
|
||||
|
||||
|
||||
Module API changes
|
||||
==================
|
||||
|
||||
* Make it possible for module commands to be part of ACL categories (#11708)
|
||||
* Add K flag to RM_Call to allow running blocking commands and set a callback to get the response (#11568)
|
||||
* Add RM_AddPostNotificationJob to allow writes after keyspace notification hooks (#11199)
|
||||
* RedisModule_Event_Key to notify about keys being unlinked together with reason and value (#9406)
|
||||
* Add RM_BlockClient[Set|Get]PrivateData to associate a module data with the blocked client (#11568)
|
||||
* APIs to allow modules to participate / handle AUTH validation (#11659)
|
||||
* RM_GetContextFlags supports a new flag: REDISMODULE_CTX_FLAGS_SERVER_STARTUP (#9320)
|
||||
* Add REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS and RedisModule_GetModuleOptionsAll (#11199)
|
||||
* RM_BlockClientOnKeysWithFlags allows module to request being unblocked when the key is deleted (#11310)
|
||||
* Introduce aux_save2 makes it possible to skip saving that field in the RDB and
|
||||
enable loading the file in the absence of the module (#11374)
|
||||
* Add a dry run flag to RM_Call to do validations before actual execution (#11158)
|
||||
* Add RM_Microseconds and RM_CachedMicroseconds (#11016)
|
||||
* Add RM_ACLAddLogEntryByUserName API to be used without a user object (#11659)
|
||||
* Make it possible to keep the RM_Call reply for longer than the context lifetime in case
|
||||
auto memory was not used (#11568)
|
||||
|
||||
Potentially Breaking Changes in Module API
|
||||
------------------------------------------
|
||||
|
||||
* RM_Call only enforces OOM on scripts if 'M' flag is set (#11425)
|
||||
* Block some specific characters in module command names (#11434)
|
||||
* Fix replication inconsistency on modules that uses keyspace notifications (#10969)
|
||||
* Prevent command, configs, data types registration after the onload handler (#11708)
|
||||
|
||||
Bug Fixes
|
||||
=========
|
||||
|
||||
* Introduce socket shutdown to properly disconnect a client while a fork is active (#11376)
|
||||
* CLIENT RESET clears the CLIENT NO-EVICT flag (#11483)
|
||||
* Reduce memory usage on strings loaded by a module from an RDB file (#11050)
|
||||
* Fix a bug where nodes in a cluster may not replicate or handle internal events for
|
||||
keys deleted when another node in the cluster claimed a slot (#11084)
|
||||
* Fix HINCRBYFLOAT not to create a key if the new value is invalid (#11149)
|
||||
* Make cluster config file saving atomic and fsync acl file saving (#10924)
|
||||
* WAIT command would not block if used in RM_Call (#11713)
|
||||
* Minor fixes to command metadata in COMMAND command (#11201, #10273)
|
||||
|
||||
|
||||
Thanks to all the users and developers who made this release possible.
|
||||
We'll follow up with more RC releases, until the code looks production ready
|
||||
and we don't get reports of serious issues for a while.
|
||||
|
||||
A special thank you for the amount of work put into this release by:
|
||||
|
||||
- Meir Shpilraien
|
||||
- Guy Benoish
|
||||
- Viktor Söderqvist
|
||||
- Zhu Binbin
|
||||
- Oran Agra
|
||||
- sundb
|
||||
- Ran Shidlansik
|
||||
- Zhenwei Pi
|
||||
- Jason Elbaum
|
||||
- Karthik Subbarao
|
||||
- Madelyn Olson
|
||||
- Huang Zhw
|
||||
- Ping Xie
|
||||
- Ozan Tezcan
|
||||
- Chen Tianjie
|
||||
- Deng Ju
|
||||
- Wen Hui
|
||||
- Brennan Cathcart
|
||||
- Itamar Haber
|
||||
- Shaya Potter
|
||||
- Roshan Khatri
|
||||
- Slava Koyfman
|
||||
- Zhu Tian
|
||||
- Moti Cohen
|
||||
- Arad Zilberstein
|
||||
- Basel Naamna
|
||||
- Mingyi Kang
|
||||
- Uri Yagelnik
|
||||
- Filipe Oliveira
|
||||
- Zhao Zhao
|
||||
- Valentino Geron
|
||||
- Yaacov Hazan
|
||||
- Adi Pinsky
|
||||
- David Carlier
|
||||
- Li Changjun
|
||||
|
||||
Happy hacking!
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ Examples of unacceptable behavior include:
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
* Publishing others’ private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
@@ -89,7 +89,7 @@ Attribution
|
||||
This Code of Conduct is adapted from the Contributor Covenant,
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
Community Impact Guidelines were inspired by Mozilla's code of conduct
|
||||
Community Impact Guidelines were inspired by Mozilla’s code of conduct
|
||||
enforcement ladder.
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
|
||||
+18
-80
@@ -1,82 +1,20 @@
|
||||
By contributing code to the Redis project in any form you agree to the Redis Software Grant and
|
||||
Contributor License Agreement attached below. Only contributions made under the Redis Software Grant
|
||||
and Contributor License Agreement may be accepted by Redis, and any contribution is subject to the
|
||||
terms of the Redis dual-license under RSALv2/SSPLv1 as described in the LICENSE.txt file included in
|
||||
the Redis source distribution.
|
||||
|
||||
# REDIS SOFTWARE GRANT AND CONTRIBUTOR LICENSE AGREEMENT
|
||||
|
||||
To specify the intellectual property license granted in any Contribution, Redis Ltd., ("**Redis**")
|
||||
requires a Software Grant and Contributor License Agreement ("**Agreement**"). This Agreement is for
|
||||
your protection as a contributor as well as the protection of Redis and its users; it does not
|
||||
change your rights to use your own Contribution for any other purpose.
|
||||
|
||||
By making any Contribution, You accept and agree to the following terms and conditions for the
|
||||
Contribution. Except for the license granted in this Agreement to Redis and the recipients of the
|
||||
software distributed by Redis, You reserve all right, title, and interest in and to Your
|
||||
Contribution.
|
||||
|
||||
1. **Definitions**
|
||||
|
||||
1.1. "**You**" (or "**Your**") means the copyright owner or legal entity authorized by the
|
||||
copyright owner that is entering into this Agreement with Redis. For legal entities, the entity
|
||||
making a Contribution and all other entities that Control, are Controlled by, or are under
|
||||
common Control with that entity are considered to be a single contributor. For the purposes of
|
||||
this definition, "**Control**" means (i) the power, direct or indirect, to cause the direction
|
||||
or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty
|
||||
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
1.2. "**Contribution**" means the code, documentation, or any original work of authorship,
|
||||
including any modifications or additions to an existing work described above.
|
||||
|
||||
2. "**Work**" means any software project stewarded by Redis.
|
||||
|
||||
3. **Grant of Copyright License**. Subject to the terms and conditions of this Agreement, You grant
|
||||
to Redis and to the recipients of the software distributed by Redis a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||
derivative works of, publicly display, publicly perform, sublicense, and distribute Your
|
||||
Contribution and such derivative works.
|
||||
|
||||
4. **Grant of Patent License**. Subject to the terms and conditions of this Agreement, You grant to
|
||||
Redis and to the recipients of the software distributed by Redis a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||
license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable by You that are necessarily
|
||||
infringed by Your Contribution alone or by a combination of Your Contribution with the Work to
|
||||
which such Contribution was submitted. If any entity institutes patent litigation against You or
|
||||
any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your
|
||||
Contribution, or the Work to which you have contributed, constitutes a direct or contributory
|
||||
patent infringement, then any patent licenses granted to the claimant entity under this Agreement
|
||||
for that Contribution or Work terminate as of the date such litigation is filed.
|
||||
|
||||
5. **Representations and Warranties**. You represent and warrant that: (i) You are legally entitled
|
||||
to grant the above licenses; and (ii) if You are an entity, each employee or agent designated by
|
||||
You is authorized to submit the Contribution on behalf of You; and (iii) your Contribution is
|
||||
Your original work, and that it will not infringe on any third party's intellectual property
|
||||
right(s).
|
||||
|
||||
6. **Disclaimer**. You are not expected to provide support for Your Contribution, except to the
|
||||
extent You desire to provide support. You may provide support for free, for a fee, or not at all.
|
||||
Unless required by applicable law or agreed to in writing, You provide Your Contribution on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
|
||||
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
7. **Enforceability**. Nothing in this Agreement will be construed as creating any joint venture,
|
||||
employment relationship, or partnership between You and Redis. If any provision of this Agreement
|
||||
is held to be unenforceable, the remaining provisions of this Agreement will not be affected.
|
||||
This represents the entire agreement between You and Redis relating to the Contribution.
|
||||
Note: by contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the COPYING file included in the Redis
|
||||
source distribution. You will include BSD license in the COPYING file within
|
||||
each source file that you contribute.
|
||||
|
||||
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
|
||||
|
||||
GitHub issues SHOULD ONLY BE USED to report bugs and for DETAILED feature
|
||||
requests. Everything else should be asked on Discord:
|
||||
Github issues SHOULD ONLY BE USED to report bugs, and for DETAILED feature
|
||||
requests. Everything else belongs to the Redis Google Group:
|
||||
|
||||
https://discord.com/invite/redis
|
||||
https://groups.google.com/forum/m/#!forum/Redis-db
|
||||
|
||||
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
|
||||
bugs in the GitHub issues system. We'll be delighted to help you and provide
|
||||
all the support on Discord.
|
||||
bugs in the Github issues system. We'll be very happy to help you and provide
|
||||
all the support in the mailing list.
|
||||
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
|
||||
@@ -95,24 +33,24 @@ straight away: if your feature is not a conceptual fit you'll lose a lot of
|
||||
time writing the code without any reason. Start by posting in the mailing list
|
||||
and creating an issue at Github with the description of, exactly, what you want
|
||||
to accomplish and why. Use cases are important for features to be accepted.
|
||||
Here you can see if there is consensus about your idea.
|
||||
Here you'll see if there is consensus about your idea.
|
||||
|
||||
2. If in step 1 you get an acknowledgment from the project leaders, use the
|
||||
following procedure to submit a patch:
|
||||
|
||||
a. Fork Redis on GitHub ( https://docs.github.com/en/github/getting-started-with-github/fork-a-repo )
|
||||
a. Fork Redis on github ( https://docs.github.com/en/github/getting-started-with-github/fork-a-repo )
|
||||
b. Create a topic branch (git checkout -b my_branch)
|
||||
c. Push to your branch (git push origin my_branch)
|
||||
d. Initiate a pull request on GitHub ( https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request )
|
||||
d. Initiate a pull request on github ( https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request )
|
||||
e. Done :)
|
||||
|
||||
3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait
|
||||
for a *very* long time. However this is not a lack of interest, as the project
|
||||
for a *very* long time. However this is not lack of interest, as the project
|
||||
gets more and more users, we find ourselves in a constant need to prioritize
|
||||
certain issues/PRs over others. If you think your issue/PR is very important
|
||||
try to popularize it, have other users commenting and sharing their point of
|
||||
view, and so forth. This helps.
|
||||
view and so forth. This helps.
|
||||
|
||||
4. For minor fixes - open a pull request on GitHub.
|
||||
4. For minor fixes just open a pull request on Github.
|
||||
|
||||
Additional information on the RSALv2/SSPLv1 dual-license is also found in the LICENSE.txt file.
|
||||
Thanks!
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-733
@@ -1,733 +0,0 @@
|
||||
Starting on March 20th, 2024, Redis follows a dual-licensing model with all Redis project code
|
||||
contributions under version 7.4 and subsequent releases governed by the Redis Software Grant and
|
||||
Contributor License Agreement. After this date, contributions are subject to the user's choice of
|
||||
the Redis Source Available License v2 (RSALv2) or the Server Side Public License v1 (SSPLv1), as
|
||||
follows:
|
||||
|
||||
|
||||
1. Redis Source Available License 2.0 (RSALv2) Agreement
|
||||
========================================================
|
||||
|
||||
Last Update: December 30, 2023
|
||||
|
||||
Acceptance
|
||||
----------
|
||||
|
||||
This Agreement sets forth the terms and conditions on which the Licensor
|
||||
makes available the Software. By installing, downloading, accessing,
|
||||
Using, or distributing any of the Software, You agree to all of the
|
||||
terms and conditions of this Agreement.
|
||||
|
||||
If You are receiving the Software on behalf of Your Company, You
|
||||
represent and warrant that You have the authority to agree to this
|
||||
Agreement on behalf of such entity.
|
||||
|
||||
The Licensor reserves the right to update this Agreement from time to
|
||||
time.
|
||||
|
||||
The terms below have the meanings set forth below for purposes of this
|
||||
Agreement:
|
||||
|
||||
Definitions
|
||||
-----------
|
||||
|
||||
Agreement: this Redis Source Available License 2.0 Agreement.
|
||||
|
||||
Control: ownership, directly or indirectly, of substantially all the
|
||||
assets of an entity, or the power to direct its management and policies
|
||||
by vote, contract, or otherwise.
|
||||
|
||||
License: the License as described in the License paragraph below.
|
||||
|
||||
Licensor: the entity offering these terms, which includes Redis Ltd. on
|
||||
behalf of itself and its subsidiaries and affiliates worldwide.
|
||||
|
||||
Modify, Modified, or Modification: copy from or adapt all or part of the
|
||||
work in a fashion requiring copyright permission other than making an
|
||||
exact copy. The resulting work is called a Modified version of the
|
||||
earlier work.
|
||||
|
||||
Redis: the Redis software as described in redis.com redis.io.
|
||||
|
||||
Software: certain Software components designed to work with Redis and
|
||||
provided to You under this Agreement.
|
||||
|
||||
Trademark: the trademarks, service marks, and any other similar rights.
|
||||
|
||||
Use: anything You do with the Software requiring one of Your Licenses.
|
||||
|
||||
You: the recipient of the Software, the individual or entity on whose
|
||||
behalf You are agreeing to this Agreement.
|
||||
|
||||
Your Company: any legal entity, sole proprietorship, or other kind of
|
||||
organization that You work for, plus all organizations that have control
|
||||
over, are under the control of, or are under common control with that
|
||||
organization.
|
||||
|
||||
Your Licenses: means all the Licenses granted to You for the Software
|
||||
under this Agreement.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The Licensor grants You a non-exclusive, royalty-free, worldwide,
|
||||
non-sublicensable, non-transferable license to use, copy, distribute,
|
||||
make available, and prepare derivative works of the Software, in each
|
||||
case subject to the limitations and conditions below.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
You may not make the functionality of the Software or a Modified version
|
||||
available to third parties as a service or distribute the Software or a
|
||||
Modified version in a manner that makes the functionality of the
|
||||
Software available to third parties.
|
||||
|
||||
Making the functionality of the Software or Modified version available
|
||||
to third parties includes, without limitation, enabling third parties to
|
||||
interact with the functionality of the Software or Modified version in
|
||||
distributed form or remotely through a computer network, offering a
|
||||
product or service, the value of which entirely or primarily derives
|
||||
from the value of the Software or Modified version, or offering a
|
||||
product or service that accomplishes for users the primary purpose of
|
||||
the Software or Modified version.
|
||||
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other
|
||||
notices of the Licensor in the Software. Any use of the Licensor's
|
||||
Trademarks is subject to applicable law.
|
||||
|
||||
Patents
|
||||
-------
|
||||
|
||||
The Licensor grants You a License, under any patent claims the Licensor
|
||||
can License, or becomes able to License, to make, have made, use, sell,
|
||||
offer for sale, import and have imported the Software, in each case
|
||||
subject to the limitations and conditions in this License. This License
|
||||
does not cover any patent claims that You cause to be infringed by
|
||||
Modifications or additions to the Software. If You or Your Company make
|
||||
any written claim that the Software infringes or contributes to
|
||||
infringement of any patent, your patent License for the Software granted
|
||||
under this Agreement ends immediately. If Your Company makes such a
|
||||
claim, your patent License ends immediately for work on behalf of Your
|
||||
Company.
|
||||
|
||||
Notices
|
||||
-------
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the Software
|
||||
from You also gets a copy of the terms and conditions in this Agreement.
|
||||
|
||||
If You modify the Software, You must include in any Modified copies of
|
||||
the Software prominent notices stating that You have Modified the
|
||||
Software.
|
||||
|
||||
No Other Rights
|
||||
---------------
|
||||
|
||||
The terms and conditions of this Agreement do not imply any Licenses
|
||||
other than those expressly granted in this Agreement.
|
||||
|
||||
Termination
|
||||
-----------
|
||||
|
||||
If You Use the Software in violation of this Agreement, such Use is not
|
||||
Licensed, and Your Licenses will automatically terminate. If the
|
||||
Licensor provides You with a notice of your violation, and You cease all
|
||||
violations of this License no later than 30 days after You receive that
|
||||
notice, Your Licenses will be reinstated retroactively. However, if You
|
||||
violate this Agreement after such reinstatement, any additional
|
||||
violation of this Agreement will cause your Licenses to terminate
|
||||
automatically and permanently.
|
||||
|
||||
No Liability
|
||||
------------
|
||||
|
||||
As far as the law allows, the Software comes as is, without any
|
||||
warranty or condition, and the Licensor will not be liable to You for
|
||||
any damages arising out of this Agreement or the Use or nature of the
|
||||
Software, under any kind of legal claim.
|
||||
|
||||
Governing Law and Jurisdiction
|
||||
------------------------------
|
||||
|
||||
If You are located in Asia, Pacific, Americas, or other jurisdictions
|
||||
not listed below, the Agreement will be construed and enforced in all
|
||||
respects in accordance with the laws of the State of California, U.S.A.,
|
||||
without reference to its choice of law rules. The courts located in the
|
||||
County of Santa Clara, California, have exclusive jurisdiction for all
|
||||
purposes relating to this Agreement.
|
||||
|
||||
If You are located in Israel, the Agreement will be construed and
|
||||
enforced in all respects in accordance with the laws of the State of
|
||||
Israel without reference to its choice of law rules. The courts located
|
||||
in the Central District of the State of Israel have exclusive
|
||||
jurisdiction for all purposes relating to this Agreement.
|
||||
|
||||
If You are located in Europe, United Kingdom, Middle East or Africa, the
|
||||
Agreement will be construed and enforced in all respects in accordance
|
||||
with the laws of England and Wales without reference to its choice of
|
||||
law rules. The competent courts located in London, England, have
|
||||
exclusive jurisdiction for all purposes relating to this Agreement.
|
||||
|
||||
|
||||
|
||||
2. Server Side Public License (SSPL)
|
||||
====================================
|
||||
|
||||
Server Side Public License
|
||||
VERSION 1, OCTOBER 16, 2018
|
||||
|
||||
Copyright (c) 2018 MongoDB, Inc.
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to Server Side Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on
|
||||
the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through a
|
||||
computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||
extent that it includes a convenient and prominently visible feature that
|
||||
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||
there is no warranty for the work (except to the extent that warranties
|
||||
are provided), that licensees may convey the work under this License, and
|
||||
how to view a copy of this License. If the interface presents a list of
|
||||
user commands or options, such as a menu, a prominent item in the list
|
||||
meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work for
|
||||
making modifications to it. "Object code" means any non-source form of a
|
||||
work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that is
|
||||
widely used among developers working in that language. The "System
|
||||
Libraries" of an executable work include anything, other than the work as
|
||||
a whole, that (a) is included in the normal form of packaging a Major
|
||||
Component, but which is not part of that Major Component, and (b) serves
|
||||
only to enable use of the work with that Major Component, or to implement
|
||||
a Standard Interface for which an implementation is available to the
|
||||
public in source code form. A "Major Component", in this context, means a
|
||||
major essential component (kernel, window system, and so on) of the
|
||||
specific operating system (if any) on which the executable work runs, or
|
||||
a compiler used to produce the work, or an object code interpreter used
|
||||
to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the
|
||||
source code needed to generate, install, and (for an executable work) run
|
||||
the object code and to modify the work, including scripts to control
|
||||
those activities. However, it does not include the work's System
|
||||
Libraries, or general-purpose tools or generally available free programs
|
||||
which are used unmodified in performing those activities but which are
|
||||
not part of the work. For example, Corresponding Source includes
|
||||
interface definition files associated with source files for the work, and
|
||||
the source code for shared libraries and dynamically linked subprograms
|
||||
that the work is specifically designed to require, such as by intimate
|
||||
data communication or control flow between those subprograms and other
|
||||
parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can
|
||||
regenerate automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program, subject to section 13. The
|
||||
output from running a covered work is covered by this License only if the
|
||||
output, given its content, constitutes a covered work. This License
|
||||
acknowledges your rights of fair use or other equivalent, as provided by
|
||||
copyright law. Subject to section 13, you may make, run and propagate
|
||||
covered works that you do not convey, without conditions so long as your
|
||||
license otherwise remains in force. You may convey covered works to
|
||||
others for the sole purpose of having them make modifications exclusively
|
||||
for you, or provide you with facilities for running those works, provided
|
||||
that you comply with the terms of this License in conveying all
|
||||
material for which you do not control copyright. Those thus making or
|
||||
running the covered works for you must do so exclusively on your
|
||||
behalf, under your direction and control, on terms that prohibit them
|
||||
from making any copies of your copyrighted material outside their
|
||||
relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the
|
||||
conditions stated below. Sublicensing is not allowed; section 10 makes it
|
||||
unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article 11
|
||||
of the WIPO copyright treaty adopted on 20 December 1996, or similar laws
|
||||
prohibiting or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention is
|
||||
effected by exercising rights under this License with respect to the
|
||||
covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's users,
|
||||
your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice; keep
|
||||
intact all notices stating that this License and any non-permissive terms
|
||||
added in accord with section 7 apply to the code; keep intact all notices
|
||||
of the absence of any warranty; and give all recipients a copy of this
|
||||
License along with the Program. You may charge any price or no price for
|
||||
each copy that you convey, and you may offer support or warranty
|
||||
protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the terms
|
||||
of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it,
|
||||
and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is released
|
||||
under this License and any conditions added under section 7. This
|
||||
requirement modifies the requirement in section 4 to "keep intact all
|
||||
notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this License to
|
||||
anyone who comes into possession of a copy. This License will therefore
|
||||
apply, along with any applicable section 7 additional terms, to the
|
||||
whole of the work, and all its parts, regardless of how they are
|
||||
packaged. This License gives no permission to license the work in any
|
||||
other way, but it does not invalidate such permission if you have
|
||||
separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your work
|
||||
need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work, and
|
||||
which are not combined with it such as to form a larger program, in or on
|
||||
a volume of a storage or distribution medium, is called an "aggregate" if
|
||||
the compilation and its resulting copyright are not used to limit the
|
||||
access or legal rights of the compilation's users beyond what the
|
||||
individual works permit. Inclusion of a covered work in an aggregate does
|
||||
not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of
|
||||
sections 4 and 5, provided that you also convey the machine-readable
|
||||
Corresponding Source under the terms of this License, in one of these
|
||||
ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium customarily
|
||||
used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a written
|
||||
offer, valid for at least three years and valid for as long as you
|
||||
offer spare parts or customer support for that product model, to give
|
||||
anyone who possesses the object code either (1) a copy of the
|
||||
Corresponding Source for all the software in the product that is
|
||||
covered by this License, on a durable physical medium customarily used
|
||||
for software interchange, for a price no more than your reasonable cost
|
||||
of physically performing this conveying of source, or (2) access to
|
||||
copy the Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This alternative is
|
||||
allowed only occasionally and noncommercially, and only if you received
|
||||
the object code with such an offer, in accord with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated place
|
||||
(gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to copy
|
||||
the object code is a network server, the Corresponding Source may be on
|
||||
a different server (operated by you or a third party) that supports
|
||||
equivalent copying facilities, provided you maintain clear directions
|
||||
next to the object code saying where to find the Corresponding Source.
|
||||
Regardless of what server hosts the Corresponding Source, you remain
|
||||
obligated to ensure that it is available for as long as needed to
|
||||
satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided you
|
||||
inform other peers where the object code and Corresponding Source of
|
||||
the work are being offered to the general public at no charge under
|
||||
subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be included
|
||||
in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as part
|
||||
of a transaction in which the right of possession and use of the User
|
||||
Product is transferred to the recipient in perpetuity or for a fixed term
|
||||
(regardless of how the transaction is characterized), the Corresponding
|
||||
Source conveyed under this section must be accompanied by the
|
||||
Installation Information. But this requirement does not apply if neither
|
||||
you nor any third party retains the ability to install modified object
|
||||
code on the User Product (for example, the work has been installed in
|
||||
ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access
|
||||
to a network may be denied when the modification itself materially
|
||||
and adversely affects the operation of the network or violates the
|
||||
rules and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in
|
||||
accord with this section must be in a format that is publicly documented
|
||||
(and with an implementation available to the public in source code form),
|
||||
and must require no special password or key for unpacking, reading or
|
||||
copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall be
|
||||
treated as though they were included in this License, to the extent that
|
||||
they are valid under applicable law. If additional permissions apply only
|
||||
to part of the Program, that part may be used separately under those
|
||||
permissions, but the entire Program remains governed by this License
|
||||
without regard to the additional permissions. When you convey a copy of
|
||||
a covered work, you may at your option remove any additional permissions
|
||||
from that copy, or from any part of it. (Additional permissions may be
|
||||
written to require their own removal in certain cases when you modify the
|
||||
work.) You may place additional permissions on material, added by you to
|
||||
a covered work, for which you have or can give appropriate copyright
|
||||
permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add
|
||||
to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some trade
|
||||
names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that material
|
||||
by anyone who conveys the material (or modified versions of it) with
|
||||
contractual assumptions of liability to the recipient, for any
|
||||
liability that these contractual assumptions directly impose on those
|
||||
licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further restriction,
|
||||
you may remove that term. If a license document contains a further
|
||||
restriction but permits relicensing or conveying under this License, you
|
||||
may add to a covered work material governed by the terms of that license
|
||||
document, provided that the further restriction does not survive such
|
||||
relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must
|
||||
place, in the relevant source files, a statement of the additional terms
|
||||
that apply to those files, or a notice indicating where to find the
|
||||
applicable terms. Additional terms, permissive or non-permissive, may be
|
||||
stated in the form of a separately written license, or stated as
|
||||
exceptions; the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or modify
|
||||
it is void, and will automatically terminate your rights under this
|
||||
License (including any patent licenses granted under the third paragraph
|
||||
of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally terminates
|
||||
your license, and (b) permanently, if the copyright holder fails to
|
||||
notify you of the violation by some reasonable means prior to 60 days
|
||||
after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated
|
||||
permanently if the copyright holder notifies you of the violation by some
|
||||
reasonable means, this is the first time you have received notice of
|
||||
violation of this License (for any work) from that copyright holder, and
|
||||
you cure the violation prior to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a
|
||||
copy of the Program. Ancillary propagation of a covered work occurring
|
||||
solely as a consequence of using peer-to-peer transmission to receive a
|
||||
copy likewise does not require acceptance. However, nothing other than
|
||||
this License grants you permission to propagate or modify any covered
|
||||
work. These actions infringe copyright if you do not accept this License.
|
||||
Therefore, by modifying or propagating a covered work, you indicate your
|
||||
acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives
|
||||
a license from the original licensors, to run, modify and propagate that
|
||||
work, subject to this License. You are not responsible for enforcing
|
||||
compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered work
|
||||
results from an entity transaction, each party to that transaction who
|
||||
receives a copy of the work also receives whatever licenses to the work
|
||||
the party's predecessor in interest had or could give under the previous
|
||||
paragraph, plus a right to possession of the Corresponding Source of the
|
||||
work from the predecessor in interest, if the predecessor has it or can
|
||||
get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights
|
||||
granted or affirmed under this License. For example, you may not impose a
|
||||
license fee, royalty, or other charge for exercise of rights granted
|
||||
under this License, and you may not initiate litigation (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that any patent claim
|
||||
is infringed by making, using, selling, offering for sale, or importing
|
||||
the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The work
|
||||
thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or
|
||||
controlled by the contributor, whether already acquired or hereafter
|
||||
acquired, that would be infringed by some manner, permitted by this
|
||||
License, of making, using, or selling its contributor version, but do not
|
||||
include claims that would be infringed only as a consequence of further
|
||||
modification of the contributor version. For purposes of this definition,
|
||||
"control" includes the right to grant patent sublicenses in a manner
|
||||
consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to make,
|
||||
use, sell, offer for sale, import and otherwise run, modify and propagate
|
||||
the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a party
|
||||
means to make such an agreement or commitment not to enforce a patent
|
||||
against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and
|
||||
the Corresponding Source of the work is not available for anyone to copy,
|
||||
free of charge and under the terms of this License, through a publicly
|
||||
available network server or other readily accessible means, then you must
|
||||
either (1) cause the Corresponding Source to be so available, or (2)
|
||||
arrange to deprive yourself of the benefit of the patent license for this
|
||||
particular work, or (3) arrange, in a manner consistent with the
|
||||
requirements of this License, to extend the patent license to downstream
|
||||
recipients. "Knowingly relying" means you have actual knowledge that, but
|
||||
for the patent license, your conveying the covered work in a country, or
|
||||
your recipient's use of the covered work in a country, would infringe
|
||||
one or more identifiable patents in that country that you have reason
|
||||
to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties receiving
|
||||
the covered work authorizing them to use, propagate, modify or convey a
|
||||
specific copy of the covered work, then the patent license you grant is
|
||||
automatically extended to all recipients of the covered work and works
|
||||
based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the
|
||||
scope of its coverage, prohibits the exercise of, or is conditioned on
|
||||
the non-exercise of one or more of the rights that are specifically
|
||||
granted under this License. You may not convey a covered work if you are
|
||||
a party to an arrangement with a third party that is in the business of
|
||||
distributing software, under which you make payment to the third party
|
||||
based on the extent of your activity of conveying the work, and under
|
||||
which the third party grants, to any of the parties who would receive the
|
||||
covered work from you, a discriminatory patent license (a) in connection
|
||||
with copies of the covered work conveyed by you (or copies made from
|
||||
those copies), or (b) primarily for and in connection with specific
|
||||
products or compilations that contain the covered work, unless you
|
||||
entered into that arrangement, or that patent license was granted, prior
|
||||
to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any
|
||||
implied license or other defenses to infringement that may otherwise be
|
||||
available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot use,
|
||||
propagate or convey a covered work so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations, then
|
||||
as a consequence you may not use, propagate or convey it at all. For
|
||||
example, if you agree to terms that obligate you to collect a royalty for
|
||||
further conveying from those to whom you convey the Program, the only way
|
||||
you could satisfy both those terms and this License would be to refrain
|
||||
entirely from conveying the Program.
|
||||
|
||||
13. Offering the Program as a Service.
|
||||
|
||||
If you make the functionality of the Program or a modified version
|
||||
available to third parties as a service, you must make the Service Source
|
||||
Code available via network download to everyone at no charge, under the
|
||||
terms of this License. Making the functionality of the Program or
|
||||
modified version available to third parties as a service includes,
|
||||
without limitation, enabling third parties to interact with the
|
||||
functionality of the Program or modified version remotely through a
|
||||
computer network, offering a service the value of which entirely or
|
||||
primarily derives from the value of the Program or modified version, or
|
||||
offering a service that accomplishes for users the primary purpose of the
|
||||
Program or modified version.
|
||||
|
||||
"Service Source Code" means the Corresponding Source for the Program or
|
||||
the modified version, and the Corresponding Source for all programs that
|
||||
you use to make the Program or modified version available as a service,
|
||||
including, without limitation, management software, user interfaces,
|
||||
application program interfaces, automation software, monitoring software,
|
||||
backup software, storage software and hosting software, all such that a
|
||||
user could run an instance of the service using the Service Source Code
|
||||
you make available.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
MongoDB, Inc. may publish revised and/or new versions of the Server Side
|
||||
Public License from time to time. Such new versions will be similar in
|
||||
spirit to the present version, but may differ in detail to address new
|
||||
problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies that a certain numbered version of the Server Side Public
|
||||
License "or any later version" applies to it, you have the option of
|
||||
following the terms and conditions either of that numbered version or of
|
||||
any later version published by MongoDB, Inc. If the Program does not
|
||||
specify a version number of the Server Side Public License, you may
|
||||
choose any version ever published by MongoDB, Inc.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of
|
||||
the Server Side Public License can be used, that proxy's public statement
|
||||
of acceptance of a version permanently authorizes you to choose that
|
||||
version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions.
|
||||
However, no additional obligations are imposed on any author or copyright
|
||||
holder as a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
|
||||
ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
|
||||
THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
|
||||
LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
|
||||
OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above
|
||||
cannot be given local legal effect according to their terms, reviewing
|
||||
courts shall apply local law that most closely approximates an absolute
|
||||
waiver of all civil liability in connection with the Program, unless a
|
||||
warranty or assumption of liability accompanies a copy of the Program in
|
||||
return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,16 +1,11 @@
|
||||
# Top level makefile, the real stuff is at ./src/Makefile and in ./modules/Makefile
|
||||
|
||||
SUBDIRS = src
|
||||
ifeq ($(BUILD_WITH_MODULES), yes)
|
||||
SUBDIRS += modules
|
||||
endif
|
||||
# Top level makefile, the real shit is at src/Makefile
|
||||
|
||||
default: all
|
||||
|
||||
.DEFAULT:
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done
|
||||
cd src && $(MAKE) $@
|
||||
|
||||
install:
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done
|
||||
cd src && $(MAKE) $@
|
||||
|
||||
.PHONY: install
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
This README is just a fast *quick start* document. You can find more detailed documentation at [redis.io](https://redis.io).
|
||||
|
||||
What is Redis?
|
||||
---
|
||||
--------------
|
||||
|
||||
Redis is often referred to as a *data structures* server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a *server-client* model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way.
|
||||
|
||||
@@ -16,21 +16,12 @@ Another good example is to think of Redis as a more complex version of memcached
|
||||
If you want to know more, this is a list of selected starting points:
|
||||
|
||||
* Introduction to Redis data types. https://redis.io/topics/data-types-intro
|
||||
* Try Redis directly inside your browser. https://try.redis.io
|
||||
* The full list of Redis commands. https://redis.io/commands
|
||||
* There is much more inside the official Redis documentation. https://redis.io/documentation
|
||||
|
||||
What is Redis Community Edition?
|
||||
---
|
||||
|
||||
Redis OSS was renamed Redis Community Edition (CE) with the v7.4 release.
|
||||
|
||||
Redis Ltd. also offers [Redis Software](https://redis.io/enterprise/), a self-managed software with additional compliance, reliability, and resiliency for enterprise scaling,
|
||||
and [Redis Cloud](https://redis.io/cloud/), a fully managed service integrated with Google Cloud, Azure, and AWS for production-ready apps.
|
||||
|
||||
Read more about the differences between Redis Community Edition and Redis [here](https://redis.io/comparisons/oss-vs-enterprise/).
|
||||
|
||||
Building Redis
|
||||
---
|
||||
--------------
|
||||
|
||||
Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, FreeBSD.
|
||||
We support big endian and little endian architectures, and both 32 bit
|
||||
@@ -74,7 +65,7 @@ installed):
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---
|
||||
---------
|
||||
|
||||
Redis has some dependencies which are included in the `deps` directory.
|
||||
`make` does not automatically rebuild dependencies even if something in
|
||||
@@ -94,7 +85,7 @@ those options are cached indefinitely until you issue a `make distclean`
|
||||
command.
|
||||
|
||||
Fixing problems building 32 bit binaries
|
||||
---
|
||||
---------
|
||||
|
||||
If after building Redis with a 32 bit target you need to rebuild it
|
||||
with a 64 bit target, or the other way around, you need to perform a
|
||||
@@ -108,7 +99,7 @@ the following steps:
|
||||
`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`
|
||||
|
||||
Allocator
|
||||
---
|
||||
---------
|
||||
|
||||
Selecting a non-default memory allocator when building Redis is done by setting
|
||||
the `MALLOC` environment variable. Redis is compiled and linked against libc
|
||||
@@ -125,7 +116,7 @@ To compile against jemalloc on Mac OS X systems, use:
|
||||
% make MALLOC=jemalloc
|
||||
|
||||
Monotonic clock
|
||||
---
|
||||
---------------
|
||||
|
||||
By default, Redis will build using the POSIX clock_gettime function as the
|
||||
monotonic clock source. On most modern systems, the internal processor clock
|
||||
@@ -137,7 +128,7 @@ To build with support for the processor's internal instruction clock, use:
|
||||
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
|
||||
|
||||
Verbose build
|
||||
---
|
||||
-------------
|
||||
|
||||
Redis will build with a user-friendly colorized output by default.
|
||||
If you want to see a more verbose output, use the following:
|
||||
@@ -145,7 +136,7 @@ If you want to see a more verbose output, use the following:
|
||||
% make V=1
|
||||
|
||||
Running Redis
|
||||
---
|
||||
-------------
|
||||
|
||||
To run Redis with the default configuration, just type:
|
||||
|
||||
@@ -167,14 +158,14 @@ as options using the command line. Examples:
|
||||
All the options in redis.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
Running Redis with TLS
|
||||
---
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use Redis with TLS.
|
||||
|
||||
Playing with Redis
|
||||
---
|
||||
------------------
|
||||
|
||||
You can use redis-cli to play with Redis. Start a redis-server instance,
|
||||
then in another terminal try the following:
|
||||
@@ -196,7 +187,7 @@ then in another terminal try the following:
|
||||
You can find the list of all the available commands at https://redis.io/commands.
|
||||
|
||||
Installing Redis
|
||||
---
|
||||
-----------------
|
||||
|
||||
In order to install Redis binaries into /usr/local/bin, just use:
|
||||
|
||||
@@ -224,35 +215,25 @@ You'll be able to stop and start Redis using the script named
|
||||
`/etc/init.d/redis_<portnumber>`, for instance `/etc/init.d/redis_6379`.
|
||||
|
||||
Code contributions
|
||||
---
|
||||
-----------------
|
||||
|
||||
By contributing code to the Redis project in any form, including sending a pull request via GitHub,
|
||||
a code fragment or patch via private email or public discussion groups, you agree to release your
|
||||
code under the terms of the [Redis Software Grant and Contributor License Agreement][1]. Redis software
|
||||
contains contributions to the original Redis core project, which are owned by their contributors and
|
||||
licensed under the 3BSD license. Any copy of that license in this repository applies only to those
|
||||
contributions. Redis releases all Redis Community Edition versions from 7.4.x and thereafter under the
|
||||
RSALv2/SSPL dual-license as described in the [LICENSE.txt][2] file included in the Redis Community Edition source distribution.
|
||||
Note: By contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the [COPYING][1] file included in the Redis
|
||||
source distribution.
|
||||
|
||||
Please see the [CONTRIBUTING.md][1] file in this source distribution for more information. For
|
||||
security bugs and vulnerabilities, please see [SECURITY.md][3].
|
||||
Please see the [CONTRIBUTING.md][2] file in this source distribution for more
|
||||
information. For security bugs and vulnerabilities, please see [SECURITY.md][3].
|
||||
|
||||
[1]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md
|
||||
[2]: https://github.com/redis/redis/blob/unstable/LICENSE.txt
|
||||
[1]: https://github.com/redis/redis/blob/unstable/COPYING
|
||||
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md
|
||||
[3]: https://github.com/redis/redis/blob/unstable/SECURITY.md
|
||||
|
||||
Redis Trademarks
|
||||
---
|
||||
|
||||
The purpose of a trademark is to identify the goods and services of a person or company without
|
||||
causing confusion. As the registered owner of its name and logo, Redis accepts certain limited uses
|
||||
of its trademarks but it has requirements that must be followed as described in its Trademark
|
||||
Guidelines available at: https://redis.com/legal/trademark-guidelines/.
|
||||
|
||||
Redis internals
|
||||
===
|
||||
|
||||
If you are reading this README you are likely in front of a GitHub page
|
||||
If you are reading this README you are likely in front of a Github page
|
||||
or you just untarred the Redis distribution tar ball. In both the cases
|
||||
you are basically one step away from the source code, so here we explain
|
||||
the Redis source code layout, what is in each file as a general idea, the
|
||||
@@ -268,7 +249,7 @@ Source code layout
|
||||
|
||||
The Redis root directory just contains this README, the Makefile which
|
||||
calls the real Makefile inside the `src` directory and an example
|
||||
configuration for Redis and Redis Sentinel. You can find a few shell
|
||||
configuration for Redis and Sentinel. You can find a few shell
|
||||
scripts that are used in order to execute the Redis, Redis Cluster and
|
||||
Redis Sentinel unit tests, which are implemented inside the `tests`
|
||||
directory.
|
||||
@@ -439,7 +420,7 @@ implementations are the following:
|
||||
* `lookupKeyRead()` and `lookupKeyWrite()` are used in order to get a pointer to the value associated to a given key, or `NULL` if the key does not exist.
|
||||
* `dbAdd()` and its higher level counterpart `setKey()` create a new key in a Redis database.
|
||||
* `dbDelete()` removes a key and its associated value.
|
||||
* `emptyData()` removes an entire single database or all the databases defined.
|
||||
* `emptyDb()` removes an entire single database or all the databases defined.
|
||||
|
||||
The rest of the file implements the generic commands exposed to the client.
|
||||
|
||||
@@ -477,9 +458,9 @@ Script
|
||||
|
||||
The script unit is composed of 3 units:
|
||||
* `script.c` - integration of scripts with Redis (commands execution, set replication/resp, ...)
|
||||
* `script_lua.c` - responsible to execute Lua code, uses `script.c` to interact with Redis from within the Lua code.
|
||||
* `function_lua.c` - contains the Lua engine implementation, uses `script_lua.c` to execute the Lua code.
|
||||
* `functions.c` - contains Redis Functions implementation (`FUNCTION` command), uses `functions_lua.c` if the function it wants to invoke needs the Lua engine.
|
||||
* `script_lua.c` - responsible to execute Lua code, uses script.c to interact with Redis from within the Lua code.
|
||||
* `function_lua.c` - contains the Lua engine implementation, uses script_lua.c to execute the Lua code.
|
||||
* `functions.c` - contains Redis Functions implementation (FUNCTION command), uses functions_lua.c if the function it wants to invoke needs the Lua engine.
|
||||
* `eval.c` - contains the `eval` implementation using `script_lua.c` to invoke the Lua code.
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
Copyright (c) 2006-Present, Redis Ltd. and Contributors
|
||||
All rights reserved.
|
||||
|
||||
Note: Continued Applicability of the BSD-3-Clause License
|
||||
|
||||
Despite the shift to the dual-licensing model with Redis Community Edition version 7.4 (RSALv2 or SSPLv1), portions of
|
||||
Redis Community Edition remain available subject to the BSD-3-Clause License (BSD). See below for the full BSD
|
||||
license:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||
and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+11
-7
@@ -2,7 +2,7 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Redis is generally backward compatible with very few exceptions, so we
|
||||
Redis is generally backwards compatible with very few exceptions, so we
|
||||
recommend users to always use the latest version to experience stability,
|
||||
performance and security.
|
||||
|
||||
@@ -11,18 +11,17 @@ unless this is not possible or feasible with a reasonable effort.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 7.4.x | :white_check_mark: |
|
||||
| 7.2.x | :white_check_mark: |
|
||||
| < 7.2.x | :x: |
|
||||
| 7.0.x | :white_check_mark: |
|
||||
| 6.2.x | :white_check_mark: |
|
||||
| < 6.2.x | :x: |
|
||||
| 6.0.x | :white_check_mark: |
|
||||
| < 6.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you've discovered a serious vulnerability, please contact the
|
||||
If you believe you’ve discovered a serious vulnerability, please contact the
|
||||
Redis core team at redis@redis.io. We will evaluate your report and if
|
||||
necessary issue a fix and an advisory. If the issue was previously undisclosed,
|
||||
we'll also mention your name in the credits.
|
||||
we’ll also mention your name in the credits.
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
@@ -35,5 +34,10 @@ This process involves providing an early notification about the vulnerability,
|
||||
its impact and mitigations to a short list of vendors under a time-limited
|
||||
embargo on public disclosure.
|
||||
|
||||
Vendors on the list are individuals or organizations that maintain Redis
|
||||
distributions or provide Redis as a service, who have third party users who
|
||||
will benefit from the vendor’s ability to prepare for a new version or deploy a
|
||||
fix early.
|
||||
|
||||
If you believe you should be on the list, please contact us and we will
|
||||
consider your request based on the above criteria.
|
||||
|
||||
Vendored
-7
@@ -42,7 +42,6 @@ distclean:
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fpconv && $(MAKE) clean) > /dev/null || true
|
||||
-(cd fast_float && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
@@ -75,12 +74,6 @@ fpconv: .make-prerequisites
|
||||
|
||||
.PHONY: fpconv
|
||||
|
||||
fast_float: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd fast_float && $(MAKE) libfast_float
|
||||
|
||||
.PHONY: fast_float
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# Make isinf() available
|
||||
LUA_CFLAGS= -D__C99FEATURES__=1
|
||||
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
# Fallback to gcc/g++ when $CC or $CXX is not in $PATH.
|
||||
CC ?= gcc
|
||||
CXX ?= g++
|
||||
|
||||
CFLAGS=-Wall -O3
|
||||
# This avoids loosing the fastfloat specific compile flags when we override the CFLAGS via the main project
|
||||
FASTFLOAT_CFLAGS=-std=c++11 -DFASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
LDFLAGS=
|
||||
|
||||
libfast_float: fast_float_strtod.o
|
||||
$(AR) -r libfast_float.a fast_float_strtod.o
|
||||
|
||||
32bit: CFLAGS += -m32
|
||||
32bit: LDFLAGS += -m32
|
||||
32bit: libfast_float
|
||||
|
||||
fast_float_strtod.o: fast_float_strtod.cpp
|
||||
$(CXX) $(CFLAGS) $(FASTFLOAT_CFLAGS) -c fast_float_strtod.cpp $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *.a
|
||||
rm -f *.h.gch
|
||||
rm -rf *.dSYM
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
README for fast_float v6.1.4
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
We're using the fast_float library[1] in our (compiled-in)
|
||||
floating-point fast_float_strtod implementation for faster and more
|
||||
portable parsing of 64 decimal strings.
|
||||
|
||||
The single file fast_float.h is an amalgamation of the entire library,
|
||||
which can be (re)generated with the amalgamate.py script (from the
|
||||
fast_float repository) via the command
|
||||
|
||||
```
|
||||
git clone https://github.com/fastfloat/fast_float
|
||||
cd fast_float
|
||||
git checkout v6.1.4
|
||||
python3 ./script/amalgamate.py --license=MIT \
|
||||
> $REDIS_SRC/deps/fast_float/fast_float.h
|
||||
```
|
||||
|
||||
[1]: https://github.com/fastfloat/fast_float
|
||||
Vendored
-3838
File diff suppressed because it is too large
Load Diff
-32
@@ -1,32 +0,0 @@
|
||||
#include "fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <cerrno>
|
||||
|
||||
/* Convert NPTR to a double using the fast_float library.
|
||||
*
|
||||
* This function behaves similarly to the standard strtod function, converting
|
||||
* the initial portion of the string pointed to by `nptr` to a `double` value,
|
||||
* using the fast_float library for high performance. If the conversion fails,
|
||||
* errno is set to EINVAL error code.
|
||||
*
|
||||
* @param nptr A pointer to the null-terminated byte string to be interpreted.
|
||||
* @param endptr A pointer to a pointer to character. If `endptr` is not NULL,
|
||||
* it will point to the character after the last character used
|
||||
* in the conversion.
|
||||
* @return The converted value as a double. If no valid conversion could
|
||||
* be performed, returns 0.0.
|
||||
* If ENDPTR is not NULL, a pointer to the character after the last one used
|
||||
* in the number is put in *ENDPTR. */
|
||||
extern "C" double fast_float_strtod(const char *nptr, char **endptr) {
|
||||
double result = 0.0;
|
||||
auto answer = fast_float::from_chars(nptr, nptr + strlen(nptr), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
errno = EINVAL; // Fallback to for other errors
|
||||
}
|
||||
if (endptr != NULL) {
|
||||
*endptr = (char *)answer.ptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef __FAST_FLOAT_STRTOD_H__
|
||||
#define __FAST_FLOAT_STRTOD_H__
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
double fast_float_strtod(const char *in, char **out);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __FAST_FLOAT_STRTOD_H__ */
|
||||
Vendored
+1
-1
@@ -108,7 +108,7 @@ to search and re-edit already inserted lines of text.
|
||||
|
||||
The followings are the history API calls:
|
||||
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
|
||||
Vendored
+11
-238
@@ -117,7 +117,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include "linenoise.h"
|
||||
|
||||
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
||||
@@ -135,18 +134,6 @@ static int atexit_registered = 0; /* Register atexit just 1 time. */
|
||||
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
|
||||
static int history_len = 0;
|
||||
static char **history = NULL;
|
||||
static int *history_sensitive = NULL; /* An array records whether each line in
|
||||
* history is sensitive. */
|
||||
|
||||
static int reverse_search_mode_enabled = 0;
|
||||
static int reverse_search_direction = 0; /* 1 means forward, -1 means backward. */
|
||||
static int cycle_to_next_search = 0; /* indicates whether to continue the search with CTRL+S or CTRL+R. */
|
||||
static char search_result[LINENOISE_MAX_LINE];
|
||||
static char search_result_friendly[LINENOISE_MAX_LINE];
|
||||
static int search_result_history_index = 0;
|
||||
static int search_result_start_offset = 0;
|
||||
static int ignore_once_hint = 0; /* Flag to ignore hint once, preventing it from interfering
|
||||
* with search results right after exiting search mode. */
|
||||
|
||||
/* The linenoiseState structure represents the state during line editing.
|
||||
* We pass this state to functions implementing specific editing
|
||||
@@ -156,7 +143,6 @@ struct linenoiseState {
|
||||
int ofd; /* Terminal stdout file descriptor. */
|
||||
char *buf; /* Edited line buffer. */
|
||||
size_t buflen; /* Edited line buffer size. */
|
||||
const char *origin_prompt; /* Original prompt, used to restore when exiting search mode. */
|
||||
const char *prompt; /* Prompt to display. */
|
||||
size_t plen; /* Prompt length. */
|
||||
size_t pos; /* Current cursor position. */
|
||||
@@ -167,13 +153,6 @@ struct linenoiseState {
|
||||
int history_index; /* The history index we are currently editing. */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int len; /* Length of the result string. */
|
||||
char *result; /* Search result string. */
|
||||
int search_term_index; /* Position of the search term in the history record. */
|
||||
int search_term_len; /* Length of the search term. */
|
||||
} linenoiseHistorySearchResult;
|
||||
|
||||
enum KEY_ACTION{
|
||||
KEY_NULL = 0, /* NULL */
|
||||
CTRL_A = 1, /* Ctrl+a */
|
||||
@@ -182,7 +161,6 @@ enum KEY_ACTION{
|
||||
CTRL_D = 4, /* Ctrl-d */
|
||||
CTRL_E = 5, /* Ctrl-e */
|
||||
CTRL_F = 6, /* Ctrl-f */
|
||||
CTRL_G = 7, /* Ctrl-g */
|
||||
CTRL_H = 8, /* Ctrl-h */
|
||||
TAB = 9, /* Tab */
|
||||
NL = 10, /* Enter typed before raw mode was enabled */
|
||||
@@ -191,8 +169,6 @@ enum KEY_ACTION{
|
||||
ENTER = 13, /* Enter */
|
||||
CTRL_N = 14, /* Ctrl-n */
|
||||
CTRL_P = 16, /* Ctrl-p */
|
||||
CTRL_R = 18, /* Ctrl-r */
|
||||
CTRL_S = 19, /* Ctrl-s */
|
||||
CTRL_T = 20, /* Ctrl-t */
|
||||
CTRL_U = 21, /* Ctrl+u */
|
||||
CTRL_W = 23, /* Ctrl+w */
|
||||
@@ -201,14 +177,8 @@ enum KEY_ACTION{
|
||||
};
|
||||
|
||||
static void linenoiseAtExit(void);
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
static void refreshLine(struct linenoiseState *l);
|
||||
static void refreshSearchResult(struct linenoiseState *ls);
|
||||
|
||||
static inline void resetSearchResult(void) {
|
||||
memset(search_result, 0, sizeof(search_result));
|
||||
memset(search_result_friendly, 0, sizeof(search_result_friendly));
|
||||
}
|
||||
|
||||
/* Debugging macro. */
|
||||
#if 0
|
||||
@@ -249,41 +219,6 @@ void linenoiseSetMultiLine(int ml) {
|
||||
mlmode = ml;
|
||||
}
|
||||
|
||||
#define REVERSE_SEARCH_PROMPT(direction) ((direction) == -1 ? "(reverse-i-search): " : "(i-search): ")
|
||||
|
||||
/* Enables the reverse search mode and refreshes the prompt. */
|
||||
static void enableReverseSearchMode(struct linenoiseState *l) {
|
||||
assert(reverse_search_mode_enabled != 1);
|
||||
reverse_search_mode_enabled = 1;
|
||||
l->origin_prompt = l->prompt;
|
||||
l->prompt = REVERSE_SEARCH_PROMPT(reverse_search_direction);
|
||||
refreshLine(l);
|
||||
}
|
||||
|
||||
/* This function disables the reverse search mode and returns the terminal to its original state.
|
||||
* If the 'discard' parameter is true, it discards the user's input search keyword and search result.
|
||||
* Otherwise, it copies the search result into 'buf', If there is no search result, it copies the
|
||||
* input search keyword instead. */
|
||||
static void disableReverseSearchMode(struct linenoiseState *l, char *buf, size_t buflen, int discard) {
|
||||
if (discard) {
|
||||
buf[0] = '\0';
|
||||
l->pos = l->len = 0;
|
||||
} else {
|
||||
ignore_once_hint = 1;
|
||||
if (strlen(search_result)) {
|
||||
strncpy(buf, search_result, buflen);
|
||||
buf[buflen-1] = '\0';
|
||||
l->pos = l->len = strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the state to non-search state. */
|
||||
reverse_search_mode_enabled = 0;
|
||||
l->prompt = l->origin_prompt;
|
||||
resetSearchResult();
|
||||
refreshLine(l);
|
||||
}
|
||||
|
||||
/* Return true if the terminal name is in the list of terminals we know are
|
||||
* not able to understand basic escape sequences. */
|
||||
static int isUnsupportedTerm(void) {
|
||||
@@ -296,12 +231,8 @@ static int isUnsupportedTerm(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Raw mode: 1960's magic. */
|
||||
/* Raw mode: 1960 magic shit. */
|
||||
static int enableRawMode(int fd) {
|
||||
if (getenv("FAKETTY_WITH_PROMPT") != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct termios raw;
|
||||
|
||||
if (!isatty(STDIN_FILENO)) goto fatal;
|
||||
@@ -370,9 +301,6 @@ static int getCursorPosition(int ifd, int ofd) {
|
||||
/* Try to get the number of columns in the current terminal, or assume 80
|
||||
* if it fails. */
|
||||
static int getColumns(int ifd, int ofd) {
|
||||
if (getenv("FAKETTY_WITH_PROMPT") != NULL) {
|
||||
goto failed;
|
||||
}
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
||||
@@ -564,13 +492,6 @@ static void abFree(struct abuf *ab) {
|
||||
* to the right of the prompt. */
|
||||
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||
char seq[64];
|
||||
|
||||
/* Show hits when not in reverse search mode and not instructed to ignore once. */
|
||||
if (reverse_search_mode_enabled || ignore_once_hint) {
|
||||
ignore_once_hint = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hintsCallback && plen+l->len < l->cols) {
|
||||
int color = -1, bold = 0;
|
||||
char *hint = hintsCallback(l->buf,&color,&bold);
|
||||
@@ -683,12 +604,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
refreshSearchResult(l);
|
||||
if (strlen(search_result) > 0) {
|
||||
abAppend(&ab, search_result_friendly, strlen(search_result_friendly));
|
||||
} else {
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
}
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
}
|
||||
|
||||
/* Show hits if any. */
|
||||
@@ -721,9 +637,6 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
|
||||
/* Set column. */
|
||||
col = (plen+(int)l->pos) % (int)l->cols;
|
||||
if (strlen(search_result) > 0) {
|
||||
col += search_result_start_offset;
|
||||
}
|
||||
lndebug("set col %d", 1+col);
|
||||
if (col)
|
||||
snprintf(seq,64,"\r\x1b[%dC", col);
|
||||
@@ -905,7 +818,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
|
||||
/* The latest history entry is always our current buffer, that
|
||||
* initially is just an empty string. */
|
||||
linenoiseHistoryAdd("", 0);
|
||||
linenoiseHistoryAdd("");
|
||||
|
||||
if (write(l.ofd,prompt,l.plen) == -1) return -1;
|
||||
while(1) {
|
||||
@@ -919,7 +832,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||
* there was an error reading from fd. Otherwise it will return the
|
||||
* character that should be handled next. */
|
||||
if (c == TAB && completionCallback != NULL && !reverse_search_mode_enabled) {
|
||||
if (c == 9 && completionCallback != NULL) {
|
||||
c = completeLine(&l);
|
||||
/* Return on errors */
|
||||
if (c < 0) return l.len;
|
||||
@@ -930,9 +843,6 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
switch(c) {
|
||||
case NL: /* enter, typed before raw mode was enabled */
|
||||
break;
|
||||
case TAB:
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 0);
|
||||
break;
|
||||
case ENTER: /* enter */
|
||||
history_len--;
|
||||
free(history[history_len]);
|
||||
@@ -945,14 +855,8 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
refreshLine(&l);
|
||||
hintsCallback = hc;
|
||||
}
|
||||
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 0);
|
||||
return (int)l.len;
|
||||
case CTRL_C: /* ctrl-c */
|
||||
if (reverse_search_mode_enabled) {
|
||||
disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
}
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case BACKSPACE: /* backspace */
|
||||
@@ -987,23 +891,6 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
case CTRL_P: /* ctrl-p */
|
||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
|
||||
break;
|
||||
case CTRL_R:
|
||||
case CTRL_S:
|
||||
reverse_search_direction = c == CTRL_R ? -1 : 1;
|
||||
if (reverse_search_mode_enabled) {
|
||||
/* cycle search results */
|
||||
cycle_to_next_search = 1;
|
||||
l.prompt = REVERSE_SEARCH_PROMPT(reverse_search_direction);
|
||||
refreshLine(&l);
|
||||
break;
|
||||
}
|
||||
buf[0] = '\0';
|
||||
l.pos = l.len = 0;
|
||||
enableReverseSearchMode(&l);
|
||||
break;
|
||||
case CTRL_G:
|
||||
if (reverse_search_mode_enabled) disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
case CTRL_N: /* ctrl-n */
|
||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
|
||||
break;
|
||||
@@ -1014,11 +901,6 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||
if (read(l.ifd,seq,1) == -1) break;
|
||||
if (read(l.ifd,seq+1,1) == -1) break;
|
||||
|
||||
if (reverse_search_mode_enabled) {
|
||||
disableReverseSearchMode(&l, buf, buflen, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* ESC [ sequences. */
|
||||
if (seq[0] == '[') {
|
||||
if (seq[1] >= '0' && seq[1] <= '9') {
|
||||
@@ -1185,14 +1067,14 @@ static char *linenoiseNoTTY(void) {
|
||||
* editing function or uses dummy fgets() so that you will be able to type
|
||||
* something even in the most desperate of the conditions. */
|
||||
char *linenoise(const char *prompt) {
|
||||
char buf[LINENOISE_MAX_LINE] = {0};
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
int count;
|
||||
|
||||
if (getenv("FAKETTY_WITH_PROMPT") == NULL && !isatty(STDIN_FILENO)) {
|
||||
if (!isatty(STDIN_FILENO)) {
|
||||
/* Not a tty: read from file / pipe. In this mode we don't want any
|
||||
* limit to the line size, so we call a function to handle that. */
|
||||
return linenoiseNoTTY();
|
||||
} else if (getenv("FAKETTY_WITH_PROMPT") == NULL && isUnsupportedTerm()) {
|
||||
} else if (isUnsupportedTerm()) {
|
||||
size_t len;
|
||||
|
||||
printf("%s",prompt);
|
||||
@@ -1230,7 +1112,6 @@ static void freeHistory(void) {
|
||||
for (j = 0; j < history_len; j++)
|
||||
free(history[j]);
|
||||
free(history);
|
||||
free(history_sensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1247,7 +1128,7 @@ static void linenoiseAtExit(void) {
|
||||
* histories, but will work well for a few hundred of entries.
|
||||
*
|
||||
* Using a circular buffer is smarter, but a bit more complex to handle. */
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive) {
|
||||
int linenoiseHistoryAdd(const char *line) {
|
||||
char *linecopy;
|
||||
|
||||
if (history_max_len == 0) return 0;
|
||||
@@ -1256,14 +1137,7 @@ int linenoiseHistoryAdd(const char *line, int is_sensitive) {
|
||||
if (history == NULL) {
|
||||
history = malloc(sizeof(char*)*history_max_len);
|
||||
if (history == NULL) return 0;
|
||||
history_sensitive = malloc(sizeof(int)*history_max_len);
|
||||
if (history_sensitive == NULL) {
|
||||
free(history);
|
||||
history = NULL;
|
||||
return 0;
|
||||
}
|
||||
memset(history,0,(sizeof(char*)*history_max_len));
|
||||
memset(history_sensitive,0,(sizeof(int)*history_max_len));
|
||||
}
|
||||
|
||||
/* Don't add duplicated lines. */
|
||||
@@ -1276,11 +1150,9 @@ int linenoiseHistoryAdd(const char *line, int is_sensitive) {
|
||||
if (history_len == history_max_len) {
|
||||
free(history[0]);
|
||||
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
|
||||
memmove(history_sensitive,history_sensitive+1,sizeof(int)*(history_max_len-1));
|
||||
history_len--;
|
||||
}
|
||||
history[history_len] = linecopy;
|
||||
history_sensitive[history_len] = is_sensitive;
|
||||
history_len++;
|
||||
return 1;
|
||||
}
|
||||
@@ -1291,7 +1163,6 @@ int linenoiseHistoryAdd(const char *line, int is_sensitive) {
|
||||
* than the amount of items already inside the history. */
|
||||
int linenoiseHistorySetMaxLen(int len) {
|
||||
char **new;
|
||||
int *new_sensitive;
|
||||
|
||||
if (len < 1) return 0;
|
||||
if (history) {
|
||||
@@ -1299,11 +1170,6 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||
|
||||
new = malloc(sizeof(char*)*len);
|
||||
if (new == NULL) return 0;
|
||||
new_sensitive = malloc(sizeof(int)*len);
|
||||
if (new_sensitive == NULL) {
|
||||
free(new);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we can't copy everything, free the elements we'll not use. */
|
||||
if (len < tocopy) {
|
||||
@@ -1313,13 +1179,9 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||
tocopy = len;
|
||||
}
|
||||
memset(new,0,sizeof(char*)*len);
|
||||
memset(new_sensitive,0,sizeof(int)*len);
|
||||
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
||||
memcpy(new_sensitive,history_sensitive+(history_len-tocopy), sizeof(int)*tocopy);
|
||||
free(history);
|
||||
free(history_sensitive);
|
||||
history = new;
|
||||
history_sensitive = new_sensitive;
|
||||
}
|
||||
history_max_len = len;
|
||||
if (history_len > history_max_len)
|
||||
@@ -1339,7 +1201,7 @@ int linenoiseHistorySave(const char *filename) {
|
||||
if (fp == NULL) return -1;
|
||||
fchmod(fileno(fp),S_IRUSR|S_IWUSR);
|
||||
for (j = 0; j < history_len; j++)
|
||||
if (!history_sensitive[j]) fprintf(fp,"%s\n",history[j]);
|
||||
fprintf(fp,"%s\n",history[j]);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
@@ -1361,97 +1223,8 @@ int linenoiseHistoryLoad(const char *filename) {
|
||||
p = strchr(buf,'\r');
|
||||
if (!p) p = strchr(buf,'\n');
|
||||
if (p) *p = '\0';
|
||||
linenoiseHistoryAdd(buf, 0);
|
||||
linenoiseHistoryAdd(buf);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function updates the search index based on the direction of the search.
|
||||
* Returns 0 if the beginning or end of the history is reached, otherwise, returns 1. */
|
||||
static int setNextSearchIndex(int *i) {
|
||||
if (reverse_search_direction == 1) {
|
||||
if (*i == history_len-1) return 0;
|
||||
*i = *i + 1;
|
||||
} else {
|
||||
if (*i <= 0) return 0;
|
||||
*i = *i - 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
linenoiseHistorySearchResult searchInHistory(char *search_term) {
|
||||
linenoiseHistorySearchResult result = {0};
|
||||
|
||||
if (!history_len || !strlen(search_term)) return result;
|
||||
|
||||
int i = cycle_to_next_search ? search_result_history_index :
|
||||
(reverse_search_direction == -1 ? history_len-1 : 0);
|
||||
|
||||
while (1) {
|
||||
char *found = strstr(history[i], search_term);
|
||||
|
||||
/* check if we found the same string at another index when cycling, this would be annoying to cycle through
|
||||
* as it might appear that cycling isn't working */
|
||||
int strings_are_the_same = cycle_to_next_search && strcmp(history[i], history[search_result_history_index]) == 0;
|
||||
|
||||
if (found && !strings_are_the_same) {
|
||||
int haystack_index = found - history[i];
|
||||
result.result = history[i];
|
||||
result.len = strlen(history[i]);
|
||||
result.search_term_index = haystack_index;
|
||||
result.search_term_len = strlen(search_term);
|
||||
search_result_history_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Exit if reached the end. */
|
||||
if (!setNextSearchIndex(&i)) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void refreshSearchResult(struct linenoiseState *ls) {
|
||||
if (!reverse_search_mode_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
linenoiseHistorySearchResult sr = searchInHistory(ls->buf);
|
||||
int found = sr.result && sr.len;
|
||||
|
||||
/* If the search term has not changed and we are cycling to the next search result
|
||||
* (using CTRL+R or CTRL+S), there is no need to reset the old search result. */
|
||||
if (!cycle_to_next_search || found)
|
||||
resetSearchResult();
|
||||
cycle_to_next_search = 0;
|
||||
|
||||
if (found) {
|
||||
char *bold = "\x1B[1m";
|
||||
char *normal = "\x1B[0m";
|
||||
|
||||
int size_needed = sr.search_term_index + sr.search_term_len + sr.len -
|
||||
(sr.search_term_index+sr.search_term_len) + sizeof(normal) + sizeof(bold) + sizeof(normal);
|
||||
if (size_needed > sizeof(search_result_friendly) - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate memory for the prefix, match, and suffix strings, one extra byte for `\0`. */
|
||||
char *prefix = calloc(sizeof(char), sr.search_term_index + 1);
|
||||
char *match = calloc(sizeof(char), sr.search_term_len + 1);
|
||||
char *suffix = calloc(sizeof(char), sr.len - (sr.search_term_index+sr.search_term_len) + 1);
|
||||
|
||||
memcpy(prefix, sr.result, sr.search_term_index);
|
||||
memcpy(match, sr.result + sr.search_term_index, sr.search_term_len);
|
||||
memcpy(suffix, sr.result + sr.search_term_index + sr.search_term_len,
|
||||
sr.len - (sr.search_term_index+sr.search_term_len));
|
||||
sprintf(search_result, "%s%s%s", prefix, match, suffix);
|
||||
sprintf(search_result_friendly, "%s%s%s%s%s%s", normal, prefix, bold, match, normal, suffix);
|
||||
|
||||
free(prefix);
|
||||
free(match);
|
||||
free(suffix);
|
||||
|
||||
search_result_start_offset = sr.search_term_index;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -58,7 +58,7 @@ void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line, int is_sensitive);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
|
||||
Vendored
+1
-8
@@ -234,17 +234,10 @@ static const luaL_Reg syslib[] = {
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#define UNUSED(V) ((void) V)
|
||||
|
||||
/* Only a subset is loaded currently, for sandboxing concerns. */
|
||||
static const luaL_Reg sandbox_syslib[] = {
|
||||
{"clock", os_clock},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_os (lua_State *L) {
|
||||
UNUSED(syslib);
|
||||
luaL_register(L, LUA_OSLIBNAME, sandbox_syslib);
|
||||
luaL_register(L, LUA_OSLIBNAME, syslib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Vendored
-1
@@ -132,7 +132,6 @@ static int bit_tohex(lua_State *L)
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if (n == INT32_MIN) n = INT32_MIN+1;
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
|
||||
SUBDIRS = redisjson redistimeseries redisbloom redisearch
|
||||
|
||||
define submake
|
||||
for dir in $(SUBDIRS); do $(MAKE) -C $$dir $(1); done
|
||||
endef
|
||||
|
||||
all: prepare_source
|
||||
$(call submake,$@)
|
||||
|
||||
get_source:
|
||||
$(call submake,$@)
|
||||
|
||||
prepare_source: get_source handle-werrors setup_environment
|
||||
|
||||
clean:
|
||||
$(call submake,$@)
|
||||
|
||||
distclean: clean_environment
|
||||
$(call submake,$@)
|
||||
|
||||
pristine:
|
||||
$(call submake,$@)
|
||||
|
||||
install:
|
||||
$(call submake,$@)
|
||||
|
||||
setup_environment: install-rust handle-werrors
|
||||
|
||||
clean_environment: uninstall-rust
|
||||
|
||||
# Keep all of the Rust stuff in one place
|
||||
install-rust:
|
||||
ifeq ($(INSTALL_RUST_TOOLCHAIN),yes)
|
||||
@RUST_VERSION=1.80.1; \
|
||||
ARCH="$$(uname -m)"; \
|
||||
if ldd --version 2>&1 | grep -q musl; then LIBC_TYPE="musl"; else LIBC_TYPE="gnu"; fi; \
|
||||
echo "Detected architecture: $${ARCH} and libc: $${LIBC_TYPE}"; \
|
||||
case "$${ARCH}" in \
|
||||
'x86_64') \
|
||||
if [ "$${LIBC_TYPE}" = "musl" ]; then \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-x86_64-unknown-linux-musl"; \
|
||||
RUST_SHA256="37bbec6a7b9f55fef79c451260766d281a7a5b9d2e65c348bbc241127cf34c8d"; \
|
||||
else \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-x86_64-unknown-linux-gnu"; \
|
||||
RUST_SHA256="85e936d5d36970afb80756fa122edcc99bd72a88155f6bdd514f5d27e778e00a"; \
|
||||
fi ;; \
|
||||
'aarch64') \
|
||||
if [ "$${LIBC_TYPE}" = "musl" ]; then \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-aarch64-unknown-linux-musl"; \
|
||||
RUST_SHA256="dd668c2d82f77c5458deb023932600fae633fff8d7f876330e01bc47e9976d17"; \
|
||||
else \
|
||||
RUST_INSTALLER="rust-$${RUST_VERSION}-aarch64-unknown-linux-gnu"; \
|
||||
RUST_SHA256="2e89bad7857711a1c11d017ea28fbfeec54076317763901194f8f5decbac1850"; \
|
||||
fi ;; \
|
||||
*) echo >&2 "Unsupported architecture: '$${ARCH}'"; exit 1 ;; \
|
||||
esac; \
|
||||
echo "Downloading and installing Rust standalone installer: $${RUST_INSTALLER}"; \
|
||||
wget --quiet -O $${RUST_INSTALLER}.tar.xz https://static.rust-lang.org/dist/$${RUST_INSTALLER}.tar.xz; \
|
||||
echo "$${RUST_SHA256} $${RUST_INSTALLER}.tar.xz" | sha256sum -c --quiet || { echo "Rust standalone installer checksum failed!"; exit 1; }; \
|
||||
tar -xf $${RUST_INSTALLER}.tar.xz; \
|
||||
(cd $${RUST_INSTALLER} && ./install.sh); \
|
||||
rm -rf $${RUST_INSTALLER}
|
||||
endif
|
||||
|
||||
uninstall-rust:
|
||||
ifeq ($(INSTALL_RUST_TOOLCHAIN),yes)
|
||||
@if [ -x "/usr/local/lib/rustlib/uninstall.sh" ]; then \
|
||||
echo "Uninstalling Rust using uninstall.sh script"; \
|
||||
rm -rf ~/.cargo; \
|
||||
/usr/local/lib/rustlib/uninstall.sh; \
|
||||
else \
|
||||
echo "WARNING: Rust toolchain not found or uninstall script is missing."; \
|
||||
fi
|
||||
endif
|
||||
|
||||
handle-werrors: get_source
|
||||
ifeq ($(DISABLE_WERRORS),yes)
|
||||
@echo "Disabling -Werror for all modules"
|
||||
@for dir in $(SUBDIRS); do \
|
||||
echo "Processing $$dir"; \
|
||||
find $$dir/src -type f \
|
||||
\( -name "Makefile" \
|
||||
-o -name "*.mk" \
|
||||
-o -name "CMakeLists.txt" \) \
|
||||
-exec sed -i 's/-Werror//g' {} +; \
|
||||
done
|
||||
endif
|
||||
|
||||
.PHONY: all clean distclean install $(SUBDIRS) setup_environment clean_environment install-rust uninstall-rust handle-werrors
|
||||
@@ -1,49 +0,0 @@
|
||||
PREFIX ?= /usr/local
|
||||
INSTALL_DIR ?= $(DESTDIR)$(PREFIX)/lib/redis/modules
|
||||
INSTALL ?= install
|
||||
|
||||
# This logic *partially* follows the current module build system. It is a bit awkward and
|
||||
# should be changed if/when the modules' build process is refactored.
|
||||
|
||||
ARCH_MAP_x86_64 := x64
|
||||
ARCH_MAP_i386 := x86
|
||||
ARCH_MAP_i686 := x86
|
||||
ARCH_MAP_aarch64 := arm64v8
|
||||
ARCH_MAP_arm64 := arm64v8
|
||||
|
||||
OS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH := $(ARCH_MAP_$(shell uname -m))
|
||||
ifeq ($(ARCH),)
|
||||
$(error Unrecognized CPU architecture $(shell uname -m))
|
||||
endif
|
||||
|
||||
FULL_VARIANT := $(OS)-$(ARCH)-release
|
||||
|
||||
# Common rules for all modules, based on per-module configuration
|
||||
|
||||
all: $(TARGET_MODULE)
|
||||
|
||||
$(TARGET_MODULE): get_source
|
||||
$(MAKE) -C $(SRC_DIR)
|
||||
|
||||
get_source: $(SRC_DIR)/.prepared
|
||||
|
||||
$(SRC_DIR)/.prepared:
|
||||
mkdir -p $(SRC_DIR)
|
||||
git clone --recursive --depth 1 --branch $(MODULE_VERSION) $(MODULE_REPO) $(SRC_DIR)
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
-$(MAKE) -C $(SRC_DIR) clean
|
||||
|
||||
distclean:
|
||||
-$(MAKE) -C $(SRC_DIR) distclean
|
||||
|
||||
pristine:
|
||||
-rm -rf $(SRC_DIR)
|
||||
|
||||
install: $(TARGET_MODULE)
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
$(INSTALL) -m 0755 -D $(TARGET_MODULE) $(INSTALL_DIR)
|
||||
|
||||
.PHONY: all clean distclean pristine install
|
||||
@@ -1,6 +0,0 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.1
|
||||
MODULE_REPO = https://github.com/redisbloom/redisbloom
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/redisbloom.so
|
||||
|
||||
include ../common.mk
|
||||
@@ -1,7 +0,0 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.1
|
||||
MODULE_REPO = https://github.com/redisearch/redisearch
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/search-community/redisearch.so
|
||||
|
||||
include ../common.mk
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.1
|
||||
MODULE_REPO = https://github.com/redisjson/redisjson
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/rejson.so
|
||||
|
||||
include ../common.mk
|
||||
|
||||
$(SRC_DIR)/.cargo_fetched:
|
||||
cd $(SRC_DIR) && cargo fetch
|
||||
|
||||
get_source: $(SRC_DIR)/.cargo_fetched
|
||||
@@ -1,6 +0,0 @@
|
||||
SRC_DIR = src
|
||||
MODULE_VERSION = v7.99.1
|
||||
MODULE_REPO = https://github.com/redistimeseries/redistimeseries
|
||||
TARGET_MODULE = $(SRC_DIR)/bin/$(FULL_VARIANT)/redistimeseries.so
|
||||
|
||||
include ../common.mk
|
||||
+27
-47
@@ -51,7 +51,6 @@
|
||||
#
|
||||
# loadmodule /path/to/my_module.so
|
||||
# loadmodule /path/to/other_module.so
|
||||
# loadmodule /path/to/args_module.so [arg [arg ...]]
|
||||
|
||||
################################## NETWORK #####################################
|
||||
|
||||
@@ -389,11 +388,6 @@ databases 16
|
||||
# ASCII art logo in startup logs by setting the following option to yes.
|
||||
always-show-logo no
|
||||
|
||||
# To avoid logging personal identifiable information (PII) into server log file,
|
||||
# uncomment the following:
|
||||
#
|
||||
# hide-user-data-from-log yes
|
||||
|
||||
# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
|
||||
# provide some runtime information. It is possible to disable this and leave
|
||||
# the process name as executed by setting the following to no.
|
||||
@@ -1168,8 +1162,7 @@ acllog-max-len 128
|
||||
# configuration directive.
|
||||
#
|
||||
# The default of 5 produces good enough results. 10 Approximates very closely
|
||||
# true LRU but costs more CPU. 3 is faster but not very accurate. The maximum
|
||||
# value that can be set is 64.
|
||||
# true LRU but costs more CPU. 3 is faster but not very accurate.
|
||||
#
|
||||
# maxmemory-samples 5
|
||||
|
||||
@@ -1291,27 +1284,38 @@ lazyfree-lazy-user-flush no
|
||||
# in different I/O threads. Since especially writing is so slow, normally
|
||||
# Redis users use pipelining in order to speed up the Redis performances per
|
||||
# core, and spawn multiple instances in order to scale more. Using I/O
|
||||
# threads it is possible to easily speedup several times Redis without resorting
|
||||
# threads it is possible to easily speedup two times Redis without resorting
|
||||
# to pipelining nor sharding of the instance.
|
||||
#
|
||||
# By default threading is disabled, we suggest enabling it only in machines
|
||||
# that have at least 4 or more cores, leaving at least one spare core.
|
||||
# We also recommend using threaded I/O only if you actually have performance
|
||||
# problems, with Redis instances being able to use a quite big percentage of
|
||||
# CPU time, otherwise there is no point in using this feature.
|
||||
# Using more than 8 threads is unlikely to help much. We also recommend using
|
||||
# threaded I/O only if you actually have performance problems, with Redis
|
||||
# instances being able to use a quite big percentage of CPU time, otherwise
|
||||
# there is no point in using this feature.
|
||||
#
|
||||
# So for instance if you have a four cores boxes, try to use 3 I/O
|
||||
# threads, if you have a 8 cores, try to use 7 threads. In order to
|
||||
# So for instance if you have a four cores boxes, try to use 2 or 3 I/O
|
||||
# threads, if you have a 8 cores, try to use 6 threads. In order to
|
||||
# enable I/O threads use the following configuration directive:
|
||||
#
|
||||
# io-threads 4
|
||||
#
|
||||
# Setting io-threads to 1 will just use the main thread as usual.
|
||||
# When I/O threads are enabled, we not only use threads for writes, that
|
||||
# is to thread the write(2) syscall and transfer the client buffers to the
|
||||
# socket, but also use threads for reads and protocol parsing.
|
||||
# When I/O threads are enabled, we only use threads for writes, that is
|
||||
# to thread the write(2) syscall and transfer the client buffers to the
|
||||
# socket. However it is also possible to enable threading of reads and
|
||||
# protocol parsing using the following configuration directive, by setting
|
||||
# it to yes:
|
||||
#
|
||||
# NOTE: If you want to test the Redis speedup using redis-benchmark, make
|
||||
# io-threads-do-reads no
|
||||
#
|
||||
# Usually threading reads doesn't help much.
|
||||
#
|
||||
# NOTE 1: This configuration directive cannot be changed at runtime via
|
||||
# CONFIG SET. Also, this feature currently does not work when SSL is
|
||||
# enabled.
|
||||
#
|
||||
# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make
|
||||
# sure you also run the benchmark itself in threaded mode, using the
|
||||
# --threads option to match the number of Redis threads, otherwise you'll not
|
||||
# be able to notice the improvements.
|
||||
@@ -1378,10 +1382,6 @@ disable-thp yes
|
||||
# If the AOF is enabled on startup Redis will load the AOF, that is the file
|
||||
# with the better durability guarantees.
|
||||
#
|
||||
# Note that changing this value in a config file of an existing database and
|
||||
# restarting the server can lead to data loss. A conversion needs to be done
|
||||
# by setting it via CONFIG command on a live server first.
|
||||
#
|
||||
# Please check https://redis.io/topics/persistence for more information.
|
||||
|
||||
appendonly no
|
||||
@@ -2070,7 +2070,7 @@ client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
# amount by default in order to avoid that a protocol desynchronization (for
|
||||
# instance due to a bug in the client) will lead to unbound memory usage in
|
||||
# the query buffer. However you can configure it here if you have very special
|
||||
# needs, such as a command with huge argument, or huge multi/exec requests or alike.
|
||||
# needs, such us huge multi/exec requests or alike.
|
||||
#
|
||||
# client-query-buffer-limit 1gb
|
||||
|
||||
@@ -2195,26 +2195,6 @@ rdb-save-incremental-fsync yes
|
||||
# lfu-log-factor 10
|
||||
# lfu-decay-time 1
|
||||
|
||||
|
||||
# The maximum number of new client connections accepted per event-loop cycle. This configuration
|
||||
# is set independently for TLS connections.
|
||||
#
|
||||
# By default, up to 10 new connection will be accepted per event-loop cycle for normal connections
|
||||
# and up to 1 new connection per event-loop cycle for TLS connections.
|
||||
#
|
||||
# Adjusting this to a larger number can slightly improve efficiency for new connections
|
||||
# at the risk of causing timeouts for regular commands on established connections. It is
|
||||
# not advised to change this without ensuring that all clients have limited connection
|
||||
# pools and exponential backoff in the case of command/connection timeouts.
|
||||
#
|
||||
# If your application is establishing a large number of new connections per second you should
|
||||
# also consider tuning the value of tcp-backlog, which allows the kernel to buffer more
|
||||
# pending connections before dropping or rejecting connections.
|
||||
#
|
||||
# max-new-connections-per-cycle 10
|
||||
# max-new-tls-connections-per-cycle 1
|
||||
|
||||
|
||||
########################### ACTIVE DEFRAGMENTATION #######################
|
||||
#
|
||||
# What is active defragmentation?
|
||||
@@ -2296,16 +2276,16 @@ jemalloc-bg-thread yes
|
||||
# the taskset command:
|
||||
#
|
||||
# Set redis server/io threads to cpu affinity 0,2,4,6:
|
||||
# server-cpulist 0-7:2
|
||||
# server_cpulist 0-7:2
|
||||
#
|
||||
# Set bio threads to cpu affinity 1,3:
|
||||
# bio-cpulist 1,3
|
||||
# bio_cpulist 1,3
|
||||
#
|
||||
# Set aof rewrite child process to cpu affinity 8,9,10,11:
|
||||
# aof-rewrite-cpulist 8-11
|
||||
# aof_rewrite_cpulist 8-11
|
||||
#
|
||||
# Set bgsave child process to cpu affinity 1,10,11
|
||||
# bgsave-cpulist 1,10-11
|
||||
# bgsave_cpulist 1,10-11
|
||||
|
||||
# In some cases redis will emit warnings and even refuse to start if it detects
|
||||
# that the system is in bad state, it is possible to suppress these warnings
|
||||
|
||||
@@ -55,5 +55,4 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/async_rm_call \
|
||||
--single unit/moduleapi/moduleauth \
|
||||
--single unit/moduleapi/rdbloadsave \
|
||||
--single unit/moduleapi/crash \
|
||||
"${@}"
|
||||
|
||||
+14
-28
@@ -1,9 +1,6 @@
|
||||
# Redis Makefile
|
||||
# Copyright (c) 2011-Present, Redis Ltd.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Licensed under your choice of the Redis Source Available License 2.0
|
||||
# (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
# Copyright (C) 2009 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
#
|
||||
# The Makefile composes the final FINAL_CFLAGS and FINAL_LDFLAGS using
|
||||
# what is needed for Redis plus the standard CFLAGS and LDFLAGS passed.
|
||||
@@ -19,22 +16,16 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
CLANG := $(findstring clang,$(shell sh -c '$(CC) --version | head -1'))
|
||||
|
||||
# Optimization flags. To override, the OPTIMIZATION variable can be passed, but
|
||||
# some automatic defaults are added to it. To specify optimization flags
|
||||
# explicitly without any defaults added, pass the OPT variable instead.
|
||||
OPTIMIZATION?=-O3
|
||||
ifeq ($(OPTIMIZATION),-O3)
|
||||
ifeq (clang,$(CLANG))
|
||||
OPTIMIZATION+=-flto
|
||||
REDIS_CFLAGS+=-flto
|
||||
else
|
||||
OPTIMIZATION+=-flto=auto
|
||||
REDIS_CFLAGS+=-flto=auto
|
||||
endif
|
||||
REDIS_LDFLAGS+=-O3 -flto
|
||||
endif
|
||||
ifneq ($(OPTIMIZATION),-O0)
|
||||
OPTIMIZATION+=-fno-omit-frame-pointer
|
||||
endif
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv fast_float
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
@@ -126,8 +117,8 @@ endif
|
||||
-include .make-settings
|
||||
|
||||
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(OPT) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm -lstdc++
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
# Linux ARM32 needs -latomic at linking time
|
||||
@@ -235,7 +226,7 @@ ifdef OPENSSL_PREFIX
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/fpconv -I../deps/fast_float
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram -I../deps/fpconv
|
||||
|
||||
# Determine systemd support and/or build preference (defaulting to auto-detection)
|
||||
BUILD_WITH_SYSTEMD=no
|
||||
@@ -354,7 +345,7 @@ endif
|
||||
|
||||
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX)
|
||||
REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX)
|
||||
REDIS_SERVER_OBJ=threads_mngr.o adlist.o quicklist.o ae.o anet.o dict.o ebuckets.o eventnotifier.o iothread.o mstr.o kvstore.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o cluster_legacy.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o unix.o logreqres.o
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o unix.o logreqres.o
|
||||
REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o redisassert.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o cli_commands.o
|
||||
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)
|
||||
@@ -409,7 +400,7 @@ endif
|
||||
|
||||
# redis-server
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/hdr_histogram/libhdrhistogram.a ../deps/fpconv/libfpconv.a ../deps/fast_float/libfast_float.a $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/hdr_histogram/libhdrhistogram.a ../deps/fpconv/libfpconv.a $(FINAL_LIBS)
|
||||
|
||||
# redis-sentinel
|
||||
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
|
||||
@@ -429,7 +420,7 @@ $(TLS_MODULE_NAME): $(REDIS_SERVER_NAME)
|
||||
|
||||
# redis-cli
|
||||
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
@@ -444,16 +435,11 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
|
||||
%.o: %.c .make-prerequisites
|
||||
$(REDIS_CC) -MMD -o $@ -c $<
|
||||
|
||||
# The following files are checked in and don't normally need to be rebuilt. They
|
||||
# are built only if python is available and their prereqs are modified.
|
||||
# The file commands.def is checked in and doesn't normally need to be rebuilt. It
|
||||
# is built only if python is available and its prereqs are modified.
|
||||
ifneq (,$(PYTHON))
|
||||
$(COMMANDS_DEF_FILENAME).def: commands/*.json ../utils/generate-command-code.py
|
||||
$(QUIET_GEN)$(PYTHON) ../utils/generate-command-code.py $(GEN_COMMANDS_FLAGS)
|
||||
|
||||
fmtargs.h: ../utils/generate-fmtargs.py
|
||||
$(QUITE_GEN)sed '/Everything below this line/,$$d' $@ > $@.tmp
|
||||
$(QUITE_GEN)$(PYTHON) ../utils/generate-fmtargs.py >> $@.tmp
|
||||
$(QUITE_GEN)mv $@.tmp $@
|
||||
endif
|
||||
|
||||
commands.c: $(COMMANDS_DEF_FILENAME).def
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018-Present, Redis Ltd.
|
||||
* Copyright (c) 2018, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
@@ -38,12 +59,10 @@ static rax *commandId = NULL; /* Command name to id mapping */
|
||||
|
||||
static unsigned long nextid = 0; /* Next command id that has not been assigned */
|
||||
|
||||
#define ACL_MAX_CATEGORIES 64 /* Maximum number of command categories */
|
||||
|
||||
struct ACLCategoryItem {
|
||||
char *name;
|
||||
const char *name;
|
||||
uint64_t flag;
|
||||
} ACLDefaultCommandCategories[] = { /* See redis.conf for details on each category. */
|
||||
} ACLCommandCategories[] = { /* See redis.conf for details on each category. */
|
||||
{"keyspace", ACL_CATEGORY_KEYSPACE},
|
||||
{"read", ACL_CATEGORY_READ},
|
||||
{"write", ACL_CATEGORY_WRITE},
|
||||
@@ -68,54 +87,6 @@ struct ACLCategoryItem {
|
||||
{NULL,0} /* Terminator. */
|
||||
};
|
||||
|
||||
static struct ACLCategoryItem *ACLCommandCategories = NULL;
|
||||
static size_t nextCommandCategory = 0; /* Index of the next command category to be added */
|
||||
|
||||
/* Implements the ability to add to the list of ACL categories at runtime. Since each ACL category
|
||||
* also requires a bit in the acl_categories flag, there is a limit to the number that can be added.
|
||||
* The new ACL categories occupy the remaining bits of acl_categories flag, other than the bits
|
||||
* occupied by the default ACL command categories.
|
||||
*
|
||||
* The optional `flag` argument allows the assignment of the `acl_categories` flag bit to the ACL category.
|
||||
* When adding a new category, except for the default ACL command categories, this arguments should be `0`
|
||||
* to allow the function to assign the next available `acl_categories` flag bit to the new ACL category.
|
||||
*
|
||||
* returns 1 -> Added, 0 -> Failed (out of space)
|
||||
*
|
||||
* This function is present here to gain access to the ACLCommandCategories array and add a new ACL category.
|
||||
*/
|
||||
int ACLAddCommandCategory(const char *name, uint64_t flag) {
|
||||
if (nextCommandCategory >= ACL_MAX_CATEGORIES) return 0;
|
||||
ACLCommandCategories[nextCommandCategory].name = zstrdup(name);
|
||||
ACLCommandCategories[nextCommandCategory].flag = flag != 0 ? flag : (1ULL<<nextCommandCategory);
|
||||
nextCommandCategory++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes ACLCommandCategories with default ACL categories and allocates space for
|
||||
* new ACL categories.
|
||||
*/
|
||||
void ACLInitCommandCategories(void) {
|
||||
ACLCommandCategories = zcalloc(sizeof(struct ACLCategoryItem) * (ACL_MAX_CATEGORIES + 1));
|
||||
for (int j = 0; ACLDefaultCommandCategories[j].flag; j++) {
|
||||
serverAssert(ACLAddCommandCategory(ACLDefaultCommandCategories[j].name, ACLDefaultCommandCategories[j].flag));
|
||||
}
|
||||
}
|
||||
|
||||
/* This function removes the specified number of categories from the trailing end of
|
||||
* the `ACLCommandCategories` array.
|
||||
* The purpose of this is to remove the categories added by modules that fail
|
||||
* during the onload function.
|
||||
*/
|
||||
void ACLCleanupCategoriesOnFailure(size_t num_acl_categories_added) {
|
||||
for (size_t j = nextCommandCategory - num_acl_categories_added; j < nextCommandCategory; j++) {
|
||||
zfree(ACLCommandCategories[j].name);
|
||||
ACLCommandCategories[j].name = NULL;
|
||||
ACLCommandCategories[j].flag = 0;
|
||||
}
|
||||
nextCommandCategory -= num_acl_categories_added;
|
||||
}
|
||||
|
||||
struct ACLUserFlag {
|
||||
const char *name;
|
||||
uint64_t flag;
|
||||
@@ -288,7 +259,7 @@ void *ACLListDupSds(void *item) {
|
||||
/* Structure used for handling key patterns with different key
|
||||
* based permissions. */
|
||||
typedef struct {
|
||||
int flags; /* The ACL key permission types for this key pattern */
|
||||
int flags; /* The CMD_KEYS_* flags for this key pattern */
|
||||
sds pattern; /* The pattern to match keys against */
|
||||
} keyPattern;
|
||||
|
||||
@@ -416,7 +387,7 @@ aclSelector *ACLUserGetRootSelector(user *u) {
|
||||
*
|
||||
* If the user with such name already exists NULL is returned. */
|
||||
user *ACLCreateUser(const char *name, size_t namelen) {
|
||||
if (raxFind(Users,(unsigned char*)name,namelen,NULL)) return NULL;
|
||||
if (raxFind(Users,(unsigned char*)name,namelen) != raxNotFound) return NULL;
|
||||
user *u = zmalloc(sizeof(*u));
|
||||
u->name = sdsnewlen(name,namelen);
|
||||
u->flags = USER_FLAG_DISABLED;
|
||||
@@ -485,7 +456,15 @@ void ACLFreeUserAndKillClients(user *u) {
|
||||
* this may result in some security hole: it's much
|
||||
* more defensive to set the default user and put
|
||||
* it in non authenticated mode. */
|
||||
deauthenticateAndCloseClient(c);
|
||||
c->user = DefaultUser;
|
||||
c->authenticated = 0;
|
||||
/* We will write replies to this client later, so we can't
|
||||
* close it directly even if async. */
|
||||
if (c == server.current_client) {
|
||||
c->flags |= CLIENT_CLOSE_AFTER_COMMAND;
|
||||
} else {
|
||||
freeClientAsync(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ACLFreeUser(u);
|
||||
@@ -510,6 +489,12 @@ void ACLCopyUser(user *dst, user *src) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Free all the users registered in the radix tree 'users' and free the
|
||||
* radix tree itself. */
|
||||
void ACLFreeUsersSet(rax *users) {
|
||||
raxFreeWithCallback(users,(void(*)(void*))ACLFreeUserAndKillClients);
|
||||
}
|
||||
|
||||
/* Given a command ID, this function set by reference 'word' and 'bit'
|
||||
* so that user->allowed_commands[word] will address the right word
|
||||
* where the corresponding bit for the provided ID is stored, and
|
||||
@@ -928,7 +913,7 @@ void ACLResetFirstArgs(aclSelector *selector) {
|
||||
selector->allowed_firstargs = NULL;
|
||||
}
|
||||
|
||||
/* Add a first-arg to the list of subcommands for the user 'u' and
|
||||
/* Add a first-arh to the list of subcommands for the user 'u' and
|
||||
* the command id specified. */
|
||||
void ACLAddAllowedFirstArg(aclSelector *selector, unsigned long id, const char *sub) {
|
||||
/* If this is the first first-arg to be configured for
|
||||
@@ -1066,7 +1051,7 @@ int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
|
||||
flags |= ACL_READ_PERMISSION;
|
||||
} else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) {
|
||||
flags |= ACL_WRITE_PERMISSION;
|
||||
} else if (op[offset] == '~' && flags) {
|
||||
} else if (op[offset] == '~') {
|
||||
offset++;
|
||||
break;
|
||||
} else {
|
||||
@@ -1413,7 +1398,6 @@ user *ACLCreateDefaultUser(void) {
|
||||
void ACLInit(void) {
|
||||
Users = raxNew();
|
||||
UsersToLoad = listCreate();
|
||||
ACLInitCommandCategories();
|
||||
listSetMatchMethod(UsersToLoad, ACLListMatchLoadedUser);
|
||||
ACLLog = listCreate();
|
||||
DefaultUser = ACLCreateDefaultUser();
|
||||
@@ -1423,7 +1407,7 @@ void ACLInit(void) {
|
||||
* otherwise C_ERR is returned and errno is set to:
|
||||
*
|
||||
* EINVAL: if the username-password do not match.
|
||||
* ENOENT: if the specified user does not exist at all.
|
||||
* ENONENT: if the specified user does not exist at all.
|
||||
*/
|
||||
int ACLCheckUserCredentials(robj *username, robj *password) {
|
||||
user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));
|
||||
@@ -1518,8 +1502,8 @@ unsigned long ACLGetCommandID(sds cmdname) {
|
||||
sds lowername = sdsdup(cmdname);
|
||||
sdstolower(lowername);
|
||||
if (commandId == NULL) commandId = raxNew();
|
||||
void *id;
|
||||
if (raxFind(commandId,(unsigned char*)lowername,sdslen(lowername),&id)) {
|
||||
void *id = raxFind(commandId,(unsigned char*)lowername,sdslen(lowername));
|
||||
if (id != raxNotFound) {
|
||||
sdsfree(lowername);
|
||||
return (unsigned long)id;
|
||||
}
|
||||
@@ -1550,8 +1534,8 @@ void ACLClearCommandID(void) {
|
||||
|
||||
/* Return an username by its name, or NULL if the user does not exist. */
|
||||
user *ACLGetUserByName(const char *name, size_t namelen) {
|
||||
void *myuser = NULL;
|
||||
raxFind(Users,(unsigned char*)name,namelen,&myuser);
|
||||
void *myuser = raxFind(Users,(unsigned char*)name,namelen);
|
||||
if (myuser == raxNotFound) return NULL;
|
||||
return myuser;
|
||||
}
|
||||
|
||||
@@ -1874,20 +1858,29 @@ int ACLCheckAllPerm(client *c, int *idxptr) {
|
||||
return ACLCheckAllUserCommandPerm(c->user, c->cmd, c->argv, c->argc, idxptr);
|
||||
}
|
||||
|
||||
/* If 'new' can access all channels 'original' could then return NULL;
|
||||
Otherwise return a list of channels that the new user can access */
|
||||
list *getUpcomingChannelList(user *new, user *original) {
|
||||
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
|
||||
* permissions specified via the upcoming argument, and kill them if so. */
|
||||
void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
|
||||
/* Do nothing if there are no subscribers. */
|
||||
if (!dictSize(server.pubsub_patterns) &&
|
||||
!dictSize(server.pubsub_channels) &&
|
||||
!dictSize(server.pubsubshard_channels))
|
||||
return;
|
||||
|
||||
listIter li, lpi;
|
||||
listNode *ln, *lpn;
|
||||
|
||||
/* Optimization: we check if any selector has all channel permissions. */
|
||||
robj *o;
|
||||
int kill = 0;
|
||||
|
||||
/* First optimization is we check if any selector has all channel
|
||||
* permissions. */
|
||||
listRewind(new->selectors,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
aclSelector *s = (aclSelector *) listNodeValue(ln);
|
||||
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return NULL;
|
||||
if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return;
|
||||
}
|
||||
|
||||
/* Next, check if the new list of channels
|
||||
/* Second optimization is to check if the new list of channels
|
||||
* is a strict superset of the original. This is done by
|
||||
* created an "upcoming" list of all channels that are in
|
||||
* the new user and checking each of the existing channels
|
||||
@@ -1925,87 +1918,58 @@ list *getUpcomingChannelList(user *new, user *original) {
|
||||
if (match) {
|
||||
/* All channels were matched, no need to kill clients. */
|
||||
listRelease(upcoming);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
/* Check if the client should be killed because it is subscribed to channels that were
|
||||
* permitted in the past, are not in the `upcoming` channel list. */
|
||||
int ACLShouldKillPubsubClient(client *c, list *upcoming) {
|
||||
robj *o;
|
||||
int kill = 0;
|
||||
|
||||
if (getClientType(c) == CLIENT_TYPE_PUBSUB) {
|
||||
/* Check for pattern violations. */
|
||||
dictIterator *di = dictGetIterator(c->pubsub_patterns);
|
||||
dictEntry *de;
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Check for channel violations. */
|
||||
if (!kill) {
|
||||
/* Check for global channels violation. */
|
||||
di = dictGetIterator(c->pubsub_channels);
|
||||
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
if (!kill) {
|
||||
/* Check for shard channels violation. */
|
||||
di = dictGetIterator(c->pubsubshard_channels);
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
if (kill) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the user's existing pub/sub clients violate the ACL pub/sub
|
||||
* permissions specified via the upcoming argument, and kill them if so. */
|
||||
void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
|
||||
/* Do nothing if there are no subscribers. */
|
||||
if (pubsubTotalSubscriptions() == 0)
|
||||
return;
|
||||
|
||||
list *channels = getUpcomingChannelList(new, original);
|
||||
/* If the new user's pubsub permissions are a strict superset of the original, return early. */
|
||||
if (!channels)
|
||||
return;
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
|
||||
}
|
||||
|
||||
/* Permissions have changed, so we need to iterate through all
|
||||
* the clients and disconnect those that are no longer valid.
|
||||
* Scan all connected clients to find the user's pub/subs. */
|
||||
listRewind(server.clients,&li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
client *c = listNodeValue(ln);
|
||||
if (c->user != original)
|
||||
continue;
|
||||
if (ACLShouldKillPubsubClient(c, channels))
|
||||
deauthenticateAndCloseClient(c);
|
||||
}
|
||||
kill = 0;
|
||||
|
||||
listRelease(channels);
|
||||
if (c->user == original && getClientType(c) == CLIENT_TYPE_PUBSUB) {
|
||||
/* Check for pattern violations. */
|
||||
dictIterator *di = dictGetIterator(c->pubsub_patterns);
|
||||
dictEntry *de;
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Check for channel violations. */
|
||||
if (!kill) {
|
||||
/* Check for global channels violation. */
|
||||
di = dictGetIterator(c->pubsub_channels);
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
if (!kill) {
|
||||
/* Check for shard channels violation. */
|
||||
di = dictGetIterator(c->pubsubshard_channels);
|
||||
while (!kill && ((de = dictNext(di)) != NULL)) {
|
||||
o = dictGetKey(de);
|
||||
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
|
||||
kill = (res == ACL_DENIED_CHANNEL);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
|
||||
/* Kill it. */
|
||||
if (kill) {
|
||||
freeClient(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
listRelease(upcoming);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
@@ -2412,46 +2376,11 @@ sds ACLLoadFromFile(const char *filename) {
|
||||
ACLFreeUser(new_default);
|
||||
raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
|
||||
raxRemove(old_users,(unsigned char*)"default",7,NULL);
|
||||
|
||||
/* If there are some subscribers, we need to check if we need to drop some clients. */
|
||||
rax *user_channels = NULL;
|
||||
if (pubsubTotalSubscriptions() > 0) {
|
||||
user_channels = raxNew();
|
||||
}
|
||||
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
|
||||
listRewind(server.clients,&li);
|
||||
while ((ln = listNext(&li)) != NULL) {
|
||||
client *c = listNodeValue(ln);
|
||||
/* a MASTER client can do everything (and user = NULL) so we can skip it */
|
||||
if (c->flags & CLIENT_MASTER)
|
||||
continue;
|
||||
user *original = c->user;
|
||||
list *channels = NULL;
|
||||
user *new = ACLGetUserByName(c->user->name, sdslen(c->user->name));
|
||||
if (new && user_channels) {
|
||||
if (!raxFind(user_channels, (unsigned char*)(new->name), sdslen(new->name), (void**)&channels)) {
|
||||
channels = getUpcomingChannelList(new, original);
|
||||
raxInsert(user_channels, (unsigned char*)(new->name), sdslen(new->name), channels, NULL);
|
||||
}
|
||||
}
|
||||
/* When the new channel list is NULL, it means the new user's channel list is a superset of the old user's list. */
|
||||
if (!new || (channels && ACLShouldKillPubsubClient(c, channels))) {
|
||||
deauthenticateAndCloseClient(c);
|
||||
continue;
|
||||
}
|
||||
c->user = new;
|
||||
}
|
||||
|
||||
if (user_channels)
|
||||
raxFreeWithCallback(user_channels, (void(*)(void*))listRelease);
|
||||
raxFreeWithCallback(old_users,(void(*)(void*))ACLFreeUser);
|
||||
ACLFreeUsersSet(old_users);
|
||||
sdsfree(errors);
|
||||
return NULL;
|
||||
} else {
|
||||
raxFreeWithCallback(Users,(void(*)(void*))ACLFreeUser);
|
||||
ACLFreeUsersSet(Users);
|
||||
Users = old_users;
|
||||
errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed");
|
||||
return errors;
|
||||
@@ -2629,15 +2558,6 @@ void ACLUpdateInfoMetrics(int reason){
|
||||
}
|
||||
}
|
||||
|
||||
static void trimACLLogEntriesToMaxLen(void) {
|
||||
while(listLength(ACLLog) > server.acllog_max_len) {
|
||||
listNode *ln = listLast(ACLLog);
|
||||
ACLLogEntry *le = listNodeValue(ln);
|
||||
ACLFreeLogEntry(le);
|
||||
listDelNode(ACLLog,ln);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adds a new entry in the ACL log, making sure to delete the old entry
|
||||
* if we reach the maximum length allowed for the log. This function attempts
|
||||
* to find similar entries in the current log in order to bump the counter of
|
||||
@@ -2657,11 +2577,6 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
|
||||
/* Update ACL info metrics */
|
||||
ACLUpdateInfoMetrics(reason);
|
||||
|
||||
if (server.acllog_max_len == 0) {
|
||||
trimACLLogEntriesToMaxLen();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a new entry. */
|
||||
struct ACLLogEntry *le = zmalloc(sizeof(*le));
|
||||
le->count = 1;
|
||||
@@ -2724,7 +2639,12 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
|
||||
* to its maximum size. */
|
||||
ACLLogEntryCount++; /* Incrementing the entry_id count to make each record in the log unique. */
|
||||
listAddNodeHead(ACLLog, le);
|
||||
trimACLLogEntriesToMaxLen();
|
||||
while(listLength(ACLLog) > server.acllog_max_len) {
|
||||
listNode *ln = listLast(ACLLog);
|
||||
ACLLogEntry *le = listNodeValue(ln);
|
||||
ACLFreeLogEntry(le);
|
||||
listDelNode(ACLLog,ln);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2762,6 +2682,7 @@ void aclCatWithFlags(client *c, dict *commands, uint64_t cflag, int *arraylen) {
|
||||
|
||||
while ((de = dictNext(di)) != NULL) {
|
||||
struct redisCommand *cmd = dictGetVal(de);
|
||||
if (cmd->flags & CMD_MODULE) continue;
|
||||
if (cmd->acl_categories & cflag) {
|
||||
addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
|
||||
(*arraylen)++;
|
||||
@@ -2847,7 +2768,8 @@ void aclCommand(client *c) {
|
||||
sds username = c->argv[2]->ptr;
|
||||
/* Check username validity. */
|
||||
if (ACLStringHasSpaces(username,sdslen(username))) {
|
||||
addReplyError(c, "Usernames can't contain spaces or null characters");
|
||||
addReplyErrorFormat(c,
|
||||
"Usernames can't contain spaces or null characters");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2865,10 +2787,6 @@ void aclCommand(client *c) {
|
||||
}
|
||||
return;
|
||||
} else if (!strcasecmp(sub,"deluser") && c->argc >= 3) {
|
||||
/* Initially redact all the arguments to not leak any information
|
||||
* about the users. */
|
||||
for (int j = 2; j < c->argc; j++) redactClientCommandArgument(c, j);
|
||||
|
||||
int deleted = 0;
|
||||
for (int j = 2; j < c->argc; j++) {
|
||||
sds username = c->argv[j]->ptr;
|
||||
@@ -2891,9 +2809,6 @@ void aclCommand(client *c) {
|
||||
}
|
||||
addReplyLongLong(c,deleted);
|
||||
} else if (!strcasecmp(sub,"getuser") && c->argc == 3) {
|
||||
/* Redact the username to not leak any information about the user. */
|
||||
redactClientCommandArgument(c, 2);
|
||||
|
||||
user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
|
||||
if (u == NULL) {
|
||||
addReplyNull(c);
|
||||
|
||||
+24
-5
@@ -1,10 +1,31 @@
|
||||
/* adlist.c - A generic doubly linked list implementation
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
@@ -55,8 +76,6 @@ void listEmpty(list *list)
|
||||
* This function can't fail. */
|
||||
void listRelease(list *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
listEmpty(list);
|
||||
zfree(list);
|
||||
}
|
||||
|
||||
+24
-3
@@ -1,10 +1,31 @@
|
||||
/* adlist.h - A generic doubly linked list implementation
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __ADLIST_H__
|
||||
|
||||
@@ -2,11 +2,32 @@
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ae.h"
|
||||
@@ -42,7 +63,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define INITIAL_EVENT 1024
|
||||
|
||||
aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
aeEventLoop *eventLoop;
|
||||
int i;
|
||||
@@ -50,9 +71,8 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
monotonicInit(); /* just in case the calling app didn't initialize */
|
||||
|
||||
if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
|
||||
eventLoop->nevents = setsize < INITIAL_EVENT ? setsize : INITIAL_EVENT;
|
||||
eventLoop->events = zmalloc(sizeof(aeFileEvent)*eventLoop->nevents);
|
||||
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*eventLoop->nevents);
|
||||
eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
|
||||
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
|
||||
eventLoop->setsize = setsize;
|
||||
eventLoop->timeEventHead = NULL;
|
||||
@@ -62,11 +82,10 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
eventLoop->beforesleep = NULL;
|
||||
eventLoop->aftersleep = NULL;
|
||||
eventLoop->flags = 0;
|
||||
memset(eventLoop->privdata, 0, sizeof(eventLoop->privdata));
|
||||
if (aeApiCreate(eventLoop) == -1) goto err;
|
||||
/* Events with mask == AE_NONE are not set. So let's initialize the
|
||||
* vector with it. */
|
||||
for (i = 0; i < eventLoop->nevents; i++)
|
||||
for (i = 0; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return eventLoop;
|
||||
|
||||
@@ -104,19 +123,20 @@ void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
|
||||
*
|
||||
* Otherwise AE_OK is returned and the operation is successful. */
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
|
||||
int i;
|
||||
|
||||
if (setsize == eventLoop->setsize) return AE_OK;
|
||||
if (eventLoop->maxfd >= setsize) return AE_ERR;
|
||||
if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
|
||||
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->setsize = setsize;
|
||||
|
||||
/* If the current allocated space is larger than the requested size,
|
||||
* we need to shrink it to the requested size. */
|
||||
if (setsize < eventLoop->nevents) {
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->nevents = setsize;
|
||||
}
|
||||
/* Make sure that if we created new slots, they are initialized with
|
||||
* an AE_NONE mask. */
|
||||
for (i = eventLoop->maxfd+1; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
@@ -129,8 +149,6 @@ void aeDeleteEventLoop(aeEventLoop *eventLoop) {
|
||||
aeTimeEvent *next_te, *te = eventLoop->timeEventHead;
|
||||
while (te) {
|
||||
next_te = te->next;
|
||||
if (te->finalizerProc)
|
||||
te->finalizerProc(eventLoop, te->clientData);
|
||||
zfree(te);
|
||||
te = next_te;
|
||||
}
|
||||
@@ -148,22 +166,6 @@ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
errno = ERANGE;
|
||||
return AE_ERR;
|
||||
}
|
||||
|
||||
/* Resize the events and fired arrays if the file
|
||||
* descriptor exceeds the current number of events. */
|
||||
if (unlikely(fd >= eventLoop->nevents)) {
|
||||
int newnevents = eventLoop->nevents;
|
||||
newnevents = (newnevents * 2 > fd + 1) ? newnevents * 2 : fd + 1;
|
||||
newnevents = (newnevents > eventLoop->setsize) ? eventLoop->setsize : newnevents;
|
||||
eventLoop->events = zrealloc(eventLoop->events, sizeof(aeFileEvent) * newnevents);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired, sizeof(aeFiredEvent) * newnevents);
|
||||
|
||||
/* Initialize new slots with an AE_NONE mask */
|
||||
for (int i = eventLoop->nevents; i < newnevents; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
eventLoop->nevents = newnevents;
|
||||
}
|
||||
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
|
||||
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
|
||||
@@ -331,7 +333,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
processed++;
|
||||
now = getMonotonicUs();
|
||||
if (retval != AE_NOMORE) {
|
||||
te->when = now + (monotime)retval * 1000;
|
||||
te->when = now + retval * 1000;
|
||||
} else {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
}
|
||||
@@ -341,8 +343,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
/* Process every pending file event, then every pending time event
|
||||
* (that may be registered by file event callbacks just processed).
|
||||
/* Process every pending time event, then every pending file event
|
||||
* (that may be registered by time event callbacks just processed).
|
||||
* Without special flags the function sleeps until some file event
|
||||
* fires, or when the next time event occurs (if any).
|
||||
*
|
||||
|
||||
@@ -2,11 +2,32 @@
|
||||
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
|
||||
* it in form of a library for easy reuse.
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __AE_H__
|
||||
@@ -79,7 +100,6 @@ typedef struct aeEventLoop {
|
||||
int maxfd; /* highest file descriptor currently registered */
|
||||
int setsize; /* max number of file descriptors tracked */
|
||||
long long timeEventNextId;
|
||||
int nevents; /* Size of Registered events */
|
||||
aeFileEvent *events; /* Registered events */
|
||||
aeFiredEvent *fired; /* Fired events */
|
||||
aeTimeEvent *timeEventHead;
|
||||
@@ -88,7 +108,6 @@ typedef struct aeEventLoop {
|
||||
aeBeforeSleepProc *beforesleep;
|
||||
aeBeforeSleepProc *aftersleep;
|
||||
int flags;
|
||||
void *privdata[2];
|
||||
} aeEventLoop;
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
+24
-3
@@ -1,10 +1,31 @@
|
||||
/* Linux epoll(2) based ae.c module
|
||||
*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+24
-3
@@ -1,10 +1,31 @@
|
||||
/* Select()-based ae.c module.
|
||||
*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+59
-142
@@ -1,10 +1,31 @@
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
@@ -61,7 +82,7 @@ int anetSetBlock(char *err, int fd, int non_block) {
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Check if this flag has been set or unset, if so,
|
||||
/* Check if this flag has been set or unset, if so,
|
||||
* then there is no need to call fcntl to set/unset it again. */
|
||||
if (!!(flags & O_NONBLOCK) == !!non_block)
|
||||
return ANET_OK;
|
||||
@@ -86,8 +107,8 @@ int anetBlock(char *err, int fd) {
|
||||
return anetSetBlock(err,fd,0);
|
||||
}
|
||||
|
||||
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
|
||||
* This function should be invoked for fd's on specific places
|
||||
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
|
||||
* This function should be invoked for fd's on specific places
|
||||
* where fork + execve system calls are called. */
|
||||
int anetCloexec(int fd) {
|
||||
int r;
|
||||
@@ -109,145 +130,57 @@ int anetCloexec(int fd) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Enable TCP keep-alive mechanism to detect dead peers,
|
||||
* TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT will be set accordingly. */
|
||||
/* Set TCP keep alive option to detect dead peers. The interval option
|
||||
* is only used for Linux as we are using Linux-specific APIs to set
|
||||
* the probe send time, interval, and count. */
|
||||
int anetKeepAlive(char *err, int fd, int interval)
|
||||
{
|
||||
int enabled = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)))
|
||||
int val = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
int idle;
|
||||
int intvl;
|
||||
int cnt;
|
||||
|
||||
/* There are platforms that are expected to support the full mechanism of TCP keep-alive,
|
||||
* we want the compiler to emit warnings of unused variables if the preprocessor directives
|
||||
* somehow fail, and other than those platforms, just omit these warnings if they happen.
|
||||
*/
|
||||
#if !(defined(_AIX) || defined(__APPLE__) || defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || defined(__illumos__) || defined(__linux__) || \
|
||||
defined(__NetBSD__) || defined(__sun))
|
||||
UNUSED(interval);
|
||||
UNUSED(idle);
|
||||
UNUSED(intvl);
|
||||
UNUSED(cnt);
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
/* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual
|
||||
* compared to other Unix-like systems.
|
||||
* Thus, we need to specialize it on Solaris.
|
||||
*
|
||||
* There are two keep-alive mechanisms on Solaris:
|
||||
* - By default, the first keep-alive probe is sent out after a TCP connection is idle for two hours.
|
||||
* If the peer does not respond to the probe within eight minutes, the TCP connection is aborted.
|
||||
* You can alter the interval for sending out the first probe using the socket option TCP_KEEPALIVE_THRESHOLD
|
||||
* in milliseconds or TCP_KEEPIDLE in seconds.
|
||||
* The system default is controlled by the TCP ndd parameter tcp_keepalive_interval. The minimum value is ten seconds.
|
||||
* The maximum is ten days, while the default is two hours. If you receive no response to the probe,
|
||||
* you can use the TCP_KEEPALIVE_ABORT_THRESHOLD socket option to change the time threshold for aborting a TCP connection.
|
||||
* The option value is an unsigned integer in milliseconds. The value zero indicates that TCP should never time out and
|
||||
* abort the connection when probing. The system default is controlled by the TCP ndd parameter tcp_keepalive_abort_interval.
|
||||
* The default is eight minutes.
|
||||
*
|
||||
* - The second implementation is activated if socket option TCP_KEEPINTVL and/or TCP_KEEPCNT are set.
|
||||
* The time between each consequent probes is set by TCP_KEEPINTVL in seconds.
|
||||
* The minimum value is ten seconds. The maximum is ten days, while the default is two hours.
|
||||
* The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response.
|
||||
*/
|
||||
|
||||
idle = interval;
|
||||
if (idle < 10) idle = 10; // kernel expects at least 10 seconds
|
||||
if (idle > 10*24*60*60) idle = 10*24*60*60; // kernel expects at most 10 days
|
||||
|
||||
/* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris
|
||||
* until version 11.4, but let's take a chance here. */
|
||||
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
intvl = idle/3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
cnt = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#else
|
||||
/* Fall back to the first implementation of tcp-alive mechanism for older Solaris,
|
||||
* simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`.
|
||||
*/
|
||||
idle *= 1000; // kernel expects milliseconds
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
|
||||
* but will be sent using the exponential backoff algorithm. */
|
||||
intvl = idle/3;
|
||||
cnt = 3;
|
||||
int time_to_abort = intvl * cnt;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ANET_OK;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPIDLE
|
||||
#ifdef __linux__
|
||||
/* Default settings are more or less garbage, with the keepalive time
|
||||
* set to 7200 by default on Linux and other Unix-like systems.
|
||||
* Modify settings to make the feature actually useful. */
|
||||
* set to 7200 by default on Linux. Modify settings to make the feature
|
||||
* actually useful. */
|
||||
|
||||
/* Send first probe after interval. */
|
||||
idle = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) {
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#elif defined(TCP_KEEPALIVE)
|
||||
/* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */
|
||||
idle = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPALIVE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPINTVL
|
||||
/* Send next probes after the specified interval. Note that we set the
|
||||
* delay as interval / 3, as we send three probes before detecting
|
||||
* an error (see the next setsockopt call). */
|
||||
intvl = interval/3;
|
||||
if (intvl == 0) intvl = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) {
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPCNT
|
||||
/* Consider the socket in error state after three we send three ACK
|
||||
* probes without getting a reply. */
|
||||
cnt = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) {
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
/* Set idle time with interval */
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPALIVE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#else
|
||||
((void) interval); /* Avoid unused var warning for non Linux systems. */
|
||||
#endif
|
||||
|
||||
return ANET_OK;
|
||||
@@ -306,11 +239,7 @@ int anetRecvTimeout(char *err, int fd, long long ms) {
|
||||
*
|
||||
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
|
||||
* that are actually already IPv4 or IPv6 addresses. This turns the function
|
||||
* into a validating / normalizing function.
|
||||
*
|
||||
* If the flag ANET_PREFER_IPV4 is set, IPv4 is preferred over IPv6.
|
||||
* If the flag ANET_PREFER_IPV6 is set, IPv6 is preferred over IPv4.
|
||||
* */
|
||||
* into a validating / normalizing function. */
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
int flags)
|
||||
{
|
||||
@@ -320,20 +249,9 @@ int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
||||
memset(&hints,0,sizeof(hints));
|
||||
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
if (flags & ANET_PREFER_IPV4 && !(flags & ANET_PREFER_IPV6)) {
|
||||
hints.ai_family = AF_INET;
|
||||
} else if (flags & ANET_PREFER_IPV6 && !(flags & ANET_PREFER_IPV4)) {
|
||||
hints.ai_family = AF_INET6;
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
|
||||
|
||||
rv = getaddrinfo(host, NULL, &hints, &info);
|
||||
if (rv != 0 && hints.ai_family != AF_UNSPEC) {
|
||||
/* Try the other IP version. */
|
||||
hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
|
||||
rv = getaddrinfo(host, NULL, &hints, &info);
|
||||
}
|
||||
if (rv != 0) {
|
||||
if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {
|
||||
anetSetError(err, "%s", gai_strerror(rv));
|
||||
return ANET_ERR;
|
||||
}
|
||||
@@ -499,16 +417,13 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
|
||||
return s;
|
||||
}
|
||||
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) {
|
||||
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
|
||||
if (bind(s,sa,len) == -1) {
|
||||
anetSetError(err, "bind: %s", strerror(errno));
|
||||
close(s);
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_LOCAL && perm)
|
||||
chmod(((struct sockaddr_un *) sa)->sun_path, perm);
|
||||
|
||||
if (listen(s, backlog) == -1) {
|
||||
anetSetError(err, "listen: %s", strerror(errno));
|
||||
close(s);
|
||||
@@ -552,7 +467,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
|
||||
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
@@ -593,8 +508,10 @@ int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
||||
memset(&sa,0,sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path));
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR)
|
||||
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
|
||||
return ANET_ERR;
|
||||
if (perm)
|
||||
chmod(sa.sun_path, perm);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+24
-5
@@ -1,10 +1,31 @@
|
||||
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
||||
*
|
||||
* Copyright (c) 2006-Present, Redis Ltd.
|
||||
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ANET_H
|
||||
@@ -19,8 +40,6 @@
|
||||
/* Flags used with certain functions. */
|
||||
#define ANET_NONE 0
|
||||
#define ANET_IP_ONLY (1<<0)
|
||||
#define ANET_PREFER_IPV4 (1<<1)
|
||||
#define ANET_PREFER_IPV6 (1<<2)
|
||||
|
||||
#if defined(__sun) || defined(_AIX)
|
||||
#define AF_LOCAL AF_UNIX
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
@@ -96,9 +117,7 @@ aofInfo *aofInfoDup(aofInfo *orig) {
|
||||
return ai;
|
||||
}
|
||||
|
||||
/* Format aofInfo as a string and it will be a line in the manifest.
|
||||
*
|
||||
* When update this format, make sure to update redis-check-aof as well. */
|
||||
/* Format aofInfo as a string and it will be a line in the manifest. */
|
||||
sds aofInfoFormat(sds buf, aofInfo *ai) {
|
||||
sds filename_repr = NULL;
|
||||
|
||||
@@ -814,7 +833,7 @@ int openNewIncrAofForAppend(void) {
|
||||
* is already synced at this point so fsync doesn't matter. */
|
||||
if (server.aof_fd != -1) {
|
||||
aof_background_fsync_and_close(server.aof_fd);
|
||||
server.aof_last_fsync = server.mstime;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
}
|
||||
server.aof_fd = newfd;
|
||||
|
||||
@@ -935,7 +954,7 @@ void stopAppendOnly(void) {
|
||||
if (redis_fsync(server.aof_fd) == -1) {
|
||||
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
|
||||
} else {
|
||||
server.aof_last_fsync = server.mstime;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
}
|
||||
close(server.aof_fd);
|
||||
|
||||
@@ -957,6 +976,18 @@ void stopAppendOnly(void) {
|
||||
int startAppendOnly(void) {
|
||||
serverAssert(server.aof_state == AOF_OFF);
|
||||
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
|
||||
server.aof_state = AOF_WAIT_REWRITE;
|
||||
if (hasActiveChildProcess() && server.child_type != CHILD_TYPE_AOF) {
|
||||
server.aof_rewrite_scheduled = 1;
|
||||
@@ -979,7 +1010,7 @@ int startAppendOnly(void) {
|
||||
return C_ERR;
|
||||
}
|
||||
}
|
||||
server.aof_last_fsync = server.mstime;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
/* If AOF fsync error in bio job, we just ignore it and log the event. */
|
||||
int aof_bio_fsync_status;
|
||||
atomicGet(server.aof_bio_fsync_status, aof_bio_fsync_status);
|
||||
@@ -997,29 +1028,6 @@ int startAppendOnly(void) {
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
void startAppendOnlyWithRetry(void) {
|
||||
unsigned int tries, max_tries = 10;
|
||||
for (tries = 0; tries < max_tries; ++tries) {
|
||||
if (startAppendOnly() == C_OK)
|
||||
break;
|
||||
serverLog(LL_WARNING, "Failed to enable AOF! Trying it again in one second.");
|
||||
sleep(1);
|
||||
}
|
||||
if (tries == max_tries) {
|
||||
serverLog(LL_WARNING, "FATAL: AOF can't be turned on. Exiting now.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called after "appendonly" config is changed. */
|
||||
void applyAppendOnlyConfig(void) {
|
||||
if (!server.aof_enabled && server.aof_state != AOF_OFF) {
|
||||
stopAppendOnly();
|
||||
} else if (server.aof_enabled && server.aof_state == AOF_OFF) {
|
||||
startAppendOnlyWithRetry();
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a wrapper to the write syscall in order to retry on short writes
|
||||
* or if the syscall gets interrupted. It could look strange that we retry
|
||||
* on short writes given that we are writing to a block device: normally if
|
||||
@@ -1078,7 +1086,7 @@ void flushAppendOnlyFile(int force) {
|
||||
* the data in page cache cannot be flushed in time. */
|
||||
if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
|
||||
server.aof_last_incr_fsync_offset != server.aof_last_incr_size &&
|
||||
server.mstime - server.aof_last_fsync >= 1000 &&
|
||||
server.unixtime > server.aof_last_fsync &&
|
||||
!(sync_in_progress = aofFsyncInProgress())) {
|
||||
goto try_fsync;
|
||||
|
||||
@@ -1091,13 +1099,6 @@ void flushAppendOnlyFile(int force) {
|
||||
{
|
||||
goto try_fsync;
|
||||
} else {
|
||||
/* All data is fsync'd already: Update fsynced_reploff_pending just in case.
|
||||
* This is needed to avoid a WAITAOF hang in case a module used RM_Call with the NO_AOF flag,
|
||||
* in which case master_repl_offset will increase but fsynced_reploff_pending won't be updated
|
||||
* (because there's no reason, from the AOF POV, to call fsync) and then WAITAOF may wait on
|
||||
* the higher offset (which contains data that was only propagated to replicas, and not to AOF) */
|
||||
if (!sync_in_progress && server.aof_fsync != AOF_FSYNC_NO)
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1113,9 +1114,9 @@ void flushAppendOnlyFile(int force) {
|
||||
if (server.aof_flush_postponed_start == 0) {
|
||||
/* No previous write postponing, remember that we are
|
||||
* postponing the flush and return. */
|
||||
server.aof_flush_postponed_start = server.mstime;
|
||||
server.aof_flush_postponed_start = server.unixtime;
|
||||
return;
|
||||
} else if (server.mstime - server.aof_flush_postponed_start < 2000) {
|
||||
} else if (server.unixtime - server.aof_flush_postponed_start < 2) {
|
||||
/* We were already waiting for fsync to finish, but for less
|
||||
* than two seconds this is still ok. Postpone again. */
|
||||
return;
|
||||
@@ -1264,15 +1265,15 @@ try_fsync:
|
||||
latencyEndMonitor(latency);
|
||||
latencyAddSampleIfNeeded("aof-fsync-always",latency);
|
||||
server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
|
||||
server.aof_last_fsync = server.mstime;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
} else if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
|
||||
server.mstime - server.aof_last_fsync >= 1000) {
|
||||
server.unixtime > server.aof_last_fsync) {
|
||||
if (!sync_in_progress) {
|
||||
aof_background_fsync(server.aof_fd);
|
||||
server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
|
||||
}
|
||||
server.aof_last_fsync = server.mstime;
|
||||
server.aof_last_fsync = server.unixtime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1858,7 +1859,6 @@ int rewriteSetObject(rio *r, robj *key, robj *o) {
|
||||
!rioWriteBulkString(r,"SADD",4) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
setTypeReleaseIterator(si);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1962,21 +1962,19 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
|
||||
*
|
||||
* The function returns 0 on error, non-zero on success. */
|
||||
static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {
|
||||
if ((hi->encoding == OBJ_ENCODING_LISTPACK) || (hi->encoding == OBJ_ENCODING_LISTPACK_EX)) {
|
||||
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
|
||||
unsigned char *vstr = NULL;
|
||||
unsigned int vlen = UINT_MAX;
|
||||
long long vll = LLONG_MAX;
|
||||
|
||||
hashTypeCurrentFromListpack(hi, what, &vstr, &vlen, &vll, NULL);
|
||||
hashTypeCurrentFromListpack(hi, what, &vstr, &vlen, &vll);
|
||||
if (vstr)
|
||||
return rioWriteBulkString(r, (char*)vstr, vlen);
|
||||
else
|
||||
return rioWriteBulkLongLong(r, vll);
|
||||
} else if (hi->encoding == OBJ_ENCODING_HT) {
|
||||
char *str;
|
||||
size_t len;
|
||||
hashTypeCurrentFromHashTable(hi, what, &str, &len, NULL);
|
||||
return rioWriteBulkString(r, str, len);
|
||||
sds value = hashTypeCurrentFromHashTable(hi, what);
|
||||
return rioWriteBulkString(r, value, sdslen(value));
|
||||
}
|
||||
|
||||
serverPanic("Unknown hash encoding");
|
||||
@@ -1986,60 +1984,37 @@ static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) {
|
||||
/* Emit the commands needed to rebuild a hash object.
|
||||
* The function returns 0 on error, 1 on success. */
|
||||
int rewriteHashObject(rio *r, robj *key, robj *o) {
|
||||
int res = 0; /*fail*/
|
||||
|
||||
hashTypeIterator *hi;
|
||||
long long count = 0, items = hashTypeLength(o, 0);
|
||||
long long count = 0, items = hashTypeLength(o);
|
||||
|
||||
int isHFE = hashTypeGetMinExpire(o, 0) != EB_EXPIRE_TIME_INVALID;
|
||||
hi = hashTypeInitIterator(o);
|
||||
while (hashTypeNext(hi) != C_ERR) {
|
||||
if (count == 0) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
|
||||
if (!isHFE) {
|
||||
while (hashTypeNext(hi, 0) != C_ERR) {
|
||||
if (count == 0) {
|
||||
int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ?
|
||||
AOF_REWRITE_ITEMS_PER_CMD : items;
|
||||
if (!rioWriteBulkCount(r, '*', 2 + cmd_items * 2) ||
|
||||
!rioWriteBulkString(r, "HMSET", 5) ||
|
||||
!rioWriteBulkObject(r, key))
|
||||
goto reHashEnd;
|
||||
}
|
||||
|
||||
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
|
||||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
|
||||
goto reHashEnd;
|
||||
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
} else {
|
||||
while (hashTypeNext(hi, 0) != C_ERR) {
|
||||
|
||||
char hmsetCmd[] = "*4\r\n$5\r\nHMSET\r\n";
|
||||
if ( (!rioWrite(r, hmsetCmd, sizeof(hmsetCmd) - 1)) ||
|
||||
(!rioWriteBulkObject(r, key)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE)) )
|
||||
goto reHashEnd;
|
||||
|
||||
if (hi->expire_time != EB_EXPIRE_TIME_INVALID) {
|
||||
char cmd[] = "*6\r\n$10\r\nHPEXPIREAT\r\n";
|
||||
if ( (!rioWrite(r, cmd, sizeof(cmd) - 1)) ||
|
||||
(!rioWriteBulkObject(r, key)) ||
|
||||
(!rioWriteBulkLongLong(r, hi->expire_time)) ||
|
||||
(!rioWriteBulkString(r, "FIELDS", 6)) ||
|
||||
(!rioWriteBulkString(r, "1", 1)) ||
|
||||
(!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY)) )
|
||||
goto reHashEnd;
|
||||
if (!rioWriteBulkCount(r,'*',2+cmd_items*2) ||
|
||||
!rioWriteBulkString(r,"HMSET",5) ||
|
||||
!rioWriteBulkObject(r,key))
|
||||
{
|
||||
hashTypeReleaseIterator(hi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) ||
|
||||
!rioWriteHashIteratorCursor(r, hi, OBJ_HASH_VALUE))
|
||||
{
|
||||
hashTypeReleaseIterator(hi);
|
||||
return 0;
|
||||
}
|
||||
if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0;
|
||||
items--;
|
||||
}
|
||||
|
||||
res = 1; /* success */
|
||||
|
||||
reHashEnd:
|
||||
hashTypeReleaseIterator(hi);
|
||||
return res;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Helper for rewriteStreamObject() that generates a bulk string into the
|
||||
@@ -2270,11 +2245,11 @@ werr:
|
||||
}
|
||||
|
||||
int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
dictIterator *di = NULL;
|
||||
dictEntry *de;
|
||||
int j;
|
||||
long key_count = 0;
|
||||
long long updated_time = 0;
|
||||
kvstoreIterator *kvs_it = NULL;
|
||||
|
||||
/* Record timestamp at the beginning of rewriting AOF. */
|
||||
if (server.aof_timestamp_enabled) {
|
||||
@@ -2287,16 +2262,17 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
||||
redisDb *db = server.db + j;
|
||||
if (kvstoreSize(db->keys) == 0) continue;
|
||||
redisDb *db = server.db+j;
|
||||
dict *d = db->dict;
|
||||
if (dictSize(d) == 0) continue;
|
||||
di = dictGetSafeIterator(d);
|
||||
|
||||
/* SELECT the new DB */
|
||||
if (rioWrite(aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkLongLong(aof,j) == 0) goto werr;
|
||||
|
||||
kvs_it = kvstoreIteratorInit(db->keys);
|
||||
/* Iterate this DB writing every entry */
|
||||
while((de = kvstoreIteratorNext(kvs_it)) != NULL) {
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
sds keystr;
|
||||
robj key, *o;
|
||||
long long expiretime;
|
||||
@@ -2361,12 +2337,13 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
if (server.rdb_key_save_delay)
|
||||
debugDelay(server.rdb_key_save_delay);
|
||||
}
|
||||
kvstoreIteratorRelease(kvs_it);
|
||||
dictReleaseIterator(di);
|
||||
di = NULL;
|
||||
}
|
||||
return C_OK;
|
||||
|
||||
werr:
|
||||
if (kvs_it) kvstoreIteratorRelease(kvs_it);
|
||||
if (di) dictReleaseIterator(di);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
@@ -2477,23 +2454,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
server.aof_lastbgrewrite_status = C_ERR;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if (server.aof_state == AOF_WAIT_REWRITE) {
|
||||
/* Wait for all bio jobs related to AOF to drain. This prevents a race
|
||||
* between updates to `fsynced_reploff_pending` of the worker thread, belonging
|
||||
* to the previous AOF, and the new one. This concern is specific for a full
|
||||
* sync scenario where we don't wanna risk the ACKed replication offset
|
||||
* jumping backwards or forward when switching to a different master. */
|
||||
bioDrainWorker(BIO_AOF_FSYNC);
|
||||
|
||||
/* Set the initial repl_offset, which will be applied to fsynced_reploff
|
||||
* when AOFRW finishes (after possibly being updated by a bio thread) */
|
||||
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
|
||||
server.fsynced_reploff = 0;
|
||||
}
|
||||
|
||||
server.stat_aof_rewrites++;
|
||||
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
|
||||
|
||||
+26
-5
@@ -1,16 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const char *ascii_logo =
|
||||
" _._ \n"
|
||||
" _.-``__ ''-._ \n"
|
||||
" _.-`` `. `_. ''-._ Redis Community Edition \n"
|
||||
" .-`` .-```. ```\\/ _.,_ ''-._ %s (%s/%d) %s bit\n"
|
||||
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
|
||||
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
|
||||
" ( ' , .-` | `, ) Running in %s mode\n"
|
||||
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
|
||||
" | `-._ `._ / _.-' | PID: %ld\n"
|
||||
|
||||
+26
-42
@@ -1,41 +1,16 @@
|
||||
/* This file implements atomic counters using c11 _Atomic, __atomic or __sync
|
||||
* macros if available, otherwise we will throw an error when compile.
|
||||
*
|
||||
* The exported interface is composed of the following macros:
|
||||
* The exported interface is composed of three macros:
|
||||
*
|
||||
* atomicIncr(var,count) -- Increment the atomic counter
|
||||
* atomicGetIncr(var,oldvalue_var,count) -- Get and increment the atomic counter
|
||||
* atomicIncrGet(var,newvalue_var,count) -- Increment and get the atomic counter new value
|
||||
* atomicDecr(var,count) -- Decrement the atomic counter
|
||||
* atomicGet(var,dstvar) -- Fetch the atomic counter value
|
||||
* atomicSet(var,value) -- Set the atomic counter value
|
||||
* atomicGetWithSync(var,value) -- 'atomicGet' with inter-thread synchronization
|
||||
* atomicSetWithSync(var,value) -- 'atomicSet' with inter-thread synchronization
|
||||
*
|
||||
* Atomic operations on flags.
|
||||
* Flag type can be int, long, long long or their unsigned counterparts.
|
||||
* The value of the flag can be 1 or 0.
|
||||
*
|
||||
* atomicFlagGetSet(var,oldvalue_var) -- Get and set the atomic counter value
|
||||
*
|
||||
* NOTE1: __atomic* and _Atomic implementations can be actually elaborated to support any value by changing the
|
||||
* hardcoded new value passed to __atomic_exchange* from 1 to @param count
|
||||
* i.e oldvalue_var = atomic_exchange_explicit(&var, count).
|
||||
* However, in order to be compatible with the __sync functions family, we can use only 0 and 1.
|
||||
* The only exchange alternative suggested by __sync is __sync_lock_test_and_set,
|
||||
* But as described by the gnu manual for __sync_lock_test_and_set():
|
||||
* https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
|
||||
* "A target may support reduced functionality here by which the only valid value to store is the immediate constant 1. The exact value
|
||||
* actually stored in *ptr is implementation defined."
|
||||
* Hence, we can't rely on it for a any value other than 1.
|
||||
* We eventually chose to implement this method with __sync_val_compare_and_swap since it satisfies functionality needed for atomicFlagGetSet
|
||||
* (if the flag was 0 -> set to 1, if it's already 1 -> do nothing, but the final result is that the flag is set),
|
||||
* and also it has a full barrier (__sync_lock_test_and_set has acquire barrier).
|
||||
*
|
||||
* NOTE2: Unlike other atomic type, which aren't guaranteed to be lock free, c11 atmoic_flag does.
|
||||
* To check whether a type is lock free, atomic_is_lock_free() can be used.
|
||||
* It can be considered to limit the flag type to atomic_flag to improve performance.
|
||||
*
|
||||
*
|
||||
* Never use return value from the macros, instead use the AtomicGetIncr()
|
||||
* if you need to get the current value and increment it atomically, like
|
||||
* in the following example:
|
||||
@@ -46,11 +21,32 @@
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2015-Present, Redis Ltd.
|
||||
* Copyright (c) 2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
@@ -97,8 +93,6 @@
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = atomic_fetch_add_explicit(&var,(count),memory_order_relaxed); \
|
||||
} while(0)
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = atomicIncr(var,count) + count
|
||||
#define atomicDecr(var,count) atomic_fetch_sub_explicit(&var,(count),memory_order_relaxed)
|
||||
#define atomicGet(var,dstvar) do { \
|
||||
dstvar = atomic_load_explicit(&var,memory_order_relaxed); \
|
||||
@@ -109,8 +103,6 @@
|
||||
} while(0)
|
||||
#define atomicSetWithSync(var,value) \
|
||||
atomic_store_explicit(&var,value,memory_order_seq_cst)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = atomic_exchange_explicit(&var,1,memory_order_relaxed)
|
||||
#define REDIS_ATOMIC_API "c11-builtin"
|
||||
|
||||
#elif !defined(__ATOMIC_VAR_FORCE_SYNC_MACROS) && \
|
||||
@@ -119,8 +111,6 @@
|
||||
/* Implementation using __atomic macros. */
|
||||
|
||||
#define atomicIncr(var,count) __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = __atomic_add_fetch(&var,(count),__ATOMIC_RELAXED)
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = __atomic_fetch_add(&var,(count),__ATOMIC_RELAXED); \
|
||||
} while(0)
|
||||
@@ -134,16 +124,12 @@
|
||||
} while(0)
|
||||
#define atomicSetWithSync(var,value) \
|
||||
__atomic_store_n(&var,value,__ATOMIC_SEQ_CST)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = __atomic_exchange_n(&var,1,__ATOMIC_RELAXED)
|
||||
#define REDIS_ATOMIC_API "atomic-builtin"
|
||||
|
||||
#elif defined(HAVE_ATOMIC)
|
||||
/* Implementation using __sync macros. */
|
||||
|
||||
#define atomicIncr(var,count) __sync_add_and_fetch(&var,(count))
|
||||
#define atomicIncrGet(var, newvalue_var, count) \
|
||||
newvalue_var = __sync_add_and_fetch(&var,(count))
|
||||
#define atomicGetIncr(var,oldvalue_var,count) do { \
|
||||
oldvalue_var = __sync_fetch_and_add(&var,(count)); \
|
||||
} while(0)
|
||||
@@ -163,8 +149,6 @@
|
||||
ANNOTATE_HAPPENS_BEFORE(&var); \
|
||||
while(!__sync_bool_compare_and_swap(&var,var,value,__sync_synchronize)); \
|
||||
} while(0)
|
||||
#define atomicFlagGetSet(var,oldvalue_var) \
|
||||
oldvalue_var = __sync_val_compare_and_swap(&var,0,1)
|
||||
#define REDIS_ATOMIC_API "sync-builtin"
|
||||
|
||||
#else
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* Background I/O service for Redis.
|
||||
*
|
||||
* This file implements operations that we need to perform in the background.
|
||||
* Currently there are 3 operations:
|
||||
* 1) a background close(2) system call. This is needed when the process is
|
||||
* the last owner of a reference to a file closing it means unlinking it, and
|
||||
* the deletion of the file is slow, blocking the server.
|
||||
* 2) AOF fsync
|
||||
* 3) lazyfree of memory
|
||||
* Currently there is only a single operation, that is a background close(2)
|
||||
* system call. This is needed as when the process is the last owner of a
|
||||
* reference to a file closing it means unlinking it, and the deletion of the
|
||||
* file is slow, blocking the server.
|
||||
*
|
||||
* In the future we'll either continue implementing new things we need or
|
||||
* we'll switch to libeio. However there are probably long term uses for this
|
||||
* file as we may want to put here Redis specific background tasks.
|
||||
* file as we may want to put here Redis specific background tasks (for instance
|
||||
* it is not impossible that we'll need a non blocking FLUSHDB/FLUSHALL
|
||||
* implementation).
|
||||
*
|
||||
* DESIGN
|
||||
* ------
|
||||
@@ -26,26 +26,42 @@
|
||||
* least-recently-inserted to the most-recently-inserted (older jobs processed
|
||||
* first).
|
||||
*
|
||||
* To let the creator of the job to be notified about the completion of the
|
||||
* operation, it will need to submit additional dummy job, coined as
|
||||
* completion job request that will be written back eventually, by the
|
||||
* background thread, into completion job response queue. This notification
|
||||
* layout can simplify flows that might submit more than one job, such as
|
||||
* in case of FLUSHALL which for a single command submits multiple jobs. It
|
||||
* is also correct because jobs are processed in FIFO fashion.
|
||||
* Currently there is no way for the creator of the job to be notified about
|
||||
* the completion of the operation, this will only be added when/if needed.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "server.h"
|
||||
#include "bio.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
static char* bio_worker_title[] = {
|
||||
"bio_close_file",
|
||||
@@ -60,9 +76,6 @@ static unsigned int bio_job_to_worker[] = {
|
||||
[BIO_AOF_FSYNC] = 1,
|
||||
[BIO_CLOSE_AOF] = 1,
|
||||
[BIO_LAZY_FREE] = 2,
|
||||
[BIO_COMP_RQ_CLOSE_FILE] = 0,
|
||||
[BIO_COMP_RQ_AOF_FSYNC] = 1,
|
||||
[BIO_COMP_RQ_LAZY_FREE] = 2
|
||||
};
|
||||
|
||||
static pthread_t bio_threads[BIO_WORKER_NUM];
|
||||
@@ -71,19 +84,6 @@ static pthread_cond_t bio_newjob_cond[BIO_WORKER_NUM];
|
||||
static list *bio_jobs[BIO_WORKER_NUM];
|
||||
static unsigned long bio_jobs_counter[BIO_NUM_OPS] = {0};
|
||||
|
||||
/* The bio_comp_list is used to hold completion job responses and to handover
|
||||
* to main thread to callback as notification for job completion. Main
|
||||
* thread will be triggered to read the list by signaling via writing to a pipe */
|
||||
static list *bio_comp_list;
|
||||
static pthread_mutex_t bio_mutex_comp;
|
||||
static int job_comp_pipe[2]; /* Pipe used to awake the event loop */
|
||||
|
||||
typedef struct bio_comp_item {
|
||||
comp_fn *func; /* callback after completion job will be processed */
|
||||
uint64_t arg; /* user data to be passed to the function */
|
||||
void *ptr; /* user pointer to be passed to the function */
|
||||
} bio_comp_item;
|
||||
|
||||
/* This structure represents a background Job. It is only used locally to this
|
||||
* file as the API does not expose the internals at all. */
|
||||
typedef union bio_job {
|
||||
@@ -107,16 +107,9 @@ typedef union bio_job {
|
||||
lazy_free_fn *free_fn; /* Function that will free the provided arguments */
|
||||
void *free_args[]; /* List of arguments to be passed to the free function */
|
||||
} free_args;
|
||||
struct {
|
||||
int type; /* header */
|
||||
comp_fn *fn; /* callback. Handover to main thread to cb as notify for job completion */
|
||||
uint64_t arg; /* callback arguments */
|
||||
void *ptr; /* callback pointer */
|
||||
} comp_rq;
|
||||
} bio_job;
|
||||
|
||||
void *bioProcessBackgroundJobs(void *arg);
|
||||
void bioPipeReadJobCompList(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||
|
||||
/* Make sure we have enough stack to perform all the things we do in the
|
||||
* main thread. */
|
||||
@@ -136,27 +129,6 @@ void bioInit(void) {
|
||||
bio_jobs[j] = listCreate();
|
||||
}
|
||||
|
||||
/* init jobs comp responses */
|
||||
bio_comp_list = listCreate();
|
||||
pthread_mutex_init(&bio_mutex_comp, NULL);
|
||||
|
||||
/* Create a pipe for background thread to be able to wake up the redis main thread.
|
||||
* Make the pipe non blocking. This is just a best effort aware mechanism
|
||||
* and we do not want to block not in the read nor in the write half.
|
||||
* Enable close-on-exec flag on pipes in case of the fork-exec system calls in
|
||||
* sentinels or redis servers. */
|
||||
if (anetPipe(job_comp_pipe, O_CLOEXEC|O_NONBLOCK, O_CLOEXEC|O_NONBLOCK) == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
"Can't create the pipe for bio thread: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Register a readable event for the pipe used to awake the event loop on job completion */
|
||||
if (aeCreateFileEvent(server.el, job_comp_pipe[0], AE_READABLE,
|
||||
bioPipeReadJobCompList, NULL) == AE_ERR) {
|
||||
serverPanic("Error registering the readable event for the bio pipe.");
|
||||
}
|
||||
|
||||
/* Set the stack size as by default it may be small in some system */
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_getstacksize(&attr,&stacksize);
|
||||
@@ -202,29 +174,6 @@ void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...) {
|
||||
bioSubmitJob(BIO_LAZY_FREE, job);
|
||||
}
|
||||
|
||||
void bioCreateCompRq(bio_worker_t assigned_worker, comp_fn *func, uint64_t user_data, void *user_ptr) {
|
||||
int type;
|
||||
switch (assigned_worker) {
|
||||
case BIO_WORKER_CLOSE_FILE:
|
||||
type = BIO_COMP_RQ_CLOSE_FILE;
|
||||
break;
|
||||
case BIO_WORKER_AOF_FSYNC:
|
||||
type = BIO_COMP_RQ_AOF_FSYNC;
|
||||
break;
|
||||
case BIO_WORKER_LAZY_FREE:
|
||||
type = BIO_COMP_RQ_LAZY_FREE;
|
||||
break;
|
||||
default:
|
||||
serverPanic("Invalid worker type in bioCreateCompRq().");
|
||||
}
|
||||
|
||||
bio_job *job = zmalloc(sizeof(*job));
|
||||
job->comp_rq.fn = func;
|
||||
job->comp_rq.arg = user_data;
|
||||
job->comp_rq.ptr = user_ptr;
|
||||
bioSubmitJob(type, job);
|
||||
}
|
||||
|
||||
void bioCreateCloseJob(int fd, int need_fsync, int need_reclaim_cache) {
|
||||
bio_job *job = zmalloc(sizeof(*job));
|
||||
job->fd_args.fd = fd;
|
||||
@@ -336,22 +285,6 @@ void *bioProcessBackgroundJobs(void *arg) {
|
||||
close(job->fd_args.fd);
|
||||
} else if (job_type == BIO_LAZY_FREE) {
|
||||
job->free_args.free_fn(job->free_args.free_args);
|
||||
} else if ((job_type == BIO_COMP_RQ_CLOSE_FILE) ||
|
||||
(job_type == BIO_COMP_RQ_AOF_FSYNC) ||
|
||||
(job_type == BIO_COMP_RQ_LAZY_FREE)) {
|
||||
bio_comp_item *comp_rsp = zmalloc(sizeof(bio_comp_item));
|
||||
comp_rsp->func = job->comp_rq.fn;
|
||||
comp_rsp->arg = job->comp_rq.arg;
|
||||
comp_rsp->ptr = job->comp_rq.ptr;
|
||||
|
||||
/* just write it to completion job responses */
|
||||
pthread_mutex_lock(&bio_mutex_comp);
|
||||
listAddNodeTail(bio_comp_list, comp_rsp);
|
||||
pthread_mutex_unlock(&bio_mutex_comp);
|
||||
|
||||
if (write(job_comp_pipe[1],"A",1) != 1) {
|
||||
/* Pipe is non-blocking, write() may fail if it's full. */
|
||||
}
|
||||
} else {
|
||||
serverPanic("Wrong job type in bioProcessBackgroundJobs().");
|
||||
}
|
||||
@@ -410,34 +343,3 @@ void bioKillThreads(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bioPipeReadJobCompList(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
UNUSED(el);
|
||||
UNUSED(mask);
|
||||
UNUSED(privdata);
|
||||
|
||||
char buf[128];
|
||||
list *tmp_list = NULL;
|
||||
|
||||
while (read(fd, buf, sizeof(buf)) == sizeof(buf));
|
||||
|
||||
/* Handle event loop events if pipe was written from event loop API */
|
||||
pthread_mutex_lock(&bio_mutex_comp);
|
||||
if (listLength(bio_comp_list)) {
|
||||
tmp_list = bio_comp_list;
|
||||
bio_comp_list = listCreate();
|
||||
}
|
||||
pthread_mutex_unlock(&bio_mutex_comp);
|
||||
|
||||
if (!tmp_list) return;
|
||||
|
||||
/* callback to all job completions */
|
||||
while (listLength(tmp_list)) {
|
||||
listNode *ln = listFirst(tmp_list);
|
||||
bio_comp_item *rsp = ln->value;
|
||||
listDelNode(tmp_list, ln);
|
||||
rsp->func(rsp->arg, rsp->ptr);
|
||||
zfree(rsp);
|
||||
}
|
||||
listRelease(tmp_list);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __BIO_H
|
||||
#define __BIO_H
|
||||
|
||||
typedef void lazy_free_fn(void *args[]);
|
||||
typedef void comp_fn(uint64_t user_data, void *user_ptr);
|
||||
|
||||
typedef enum bio_worker_t {
|
||||
BIO_WORKER_CLOSE_FILE = 0,
|
||||
BIO_WORKER_AOF_FSYNC,
|
||||
BIO_WORKER_LAZY_FREE,
|
||||
BIO_WORKER_NUM
|
||||
} bio_worker_t;
|
||||
|
||||
/* Background job opcodes */
|
||||
typedef enum bio_job_type_t {
|
||||
BIO_CLOSE_FILE = 0, /* Deferred close(2) syscall. */
|
||||
BIO_AOF_FSYNC, /* Deferred AOF fsync. */
|
||||
BIO_LAZY_FREE, /* Deferred objects freeing. */
|
||||
BIO_CLOSE_AOF,
|
||||
BIO_COMP_RQ_CLOSE_FILE, /* Job completion request, registered on close-file worker's queue */
|
||||
BIO_COMP_RQ_AOF_FSYNC, /* Job completion request, registered on aof-fsync worker's queue */
|
||||
BIO_COMP_RQ_LAZY_FREE, /* Job completion request, registered on lazy-free worker's queue */
|
||||
BIO_NUM_OPS
|
||||
} bio_job_type_t;
|
||||
|
||||
/* Exported API */
|
||||
void bioInit(void);
|
||||
@@ -40,7 +41,14 @@ void bioCreateCloseJob(int fd, int need_fsync, int need_reclaim_cache);
|
||||
void bioCreateCloseAofJob(int fd, long long offset, int need_reclaim_cache);
|
||||
void bioCreateFsyncJob(int fd, long long offset, int need_reclaim_cache);
|
||||
void bioCreateLazyFreeJob(lazy_free_fn free_fn, int arg_count, ...);
|
||||
void bioCreateCompRq(bio_worker_t assigned_worker, comp_fn *func, uint64_t user_data, void *user_ptr);
|
||||
|
||||
/* Background job opcodes */
|
||||
enum {
|
||||
BIO_CLOSE_FILE = 0, /* Deferred close(2) syscall. */
|
||||
BIO_AOF_FSYNC, /* Deferred AOF fsync. */
|
||||
BIO_LAZY_FREE, /* Deferred objects freeing. */
|
||||
BIO_CLOSE_AOF, /* Deferred close for AOF files. */
|
||||
BIO_NUM_OPS
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+68
-125
@@ -1,10 +1,31 @@
|
||||
/* Bit operations.
|
||||
*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
@@ -16,49 +37,18 @@
|
||||
/* Count number of bits set in the binary array pointed by 's' and long
|
||||
* 'count' bytes. The implementation of this function is required to
|
||||
* work with an input string length up to 512 MB or more (server.proto_max_bulk_len) */
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
long long redisPopcount(void *s, long count) {
|
||||
long long bits = 0;
|
||||
unsigned char *p = s;
|
||||
uint32_t *p4;
|
||||
#if defined(HAVE_POPCNT)
|
||||
int use_popcnt = __builtin_cpu_supports("popcnt"); /* Check if CPU supports POPCNT instruction. */
|
||||
#else
|
||||
int use_popcnt = 0; /* Assume CPU does not support POPCNT if
|
||||
* __builtin_cpu_supports() is not available. */
|
||||
#endif
|
||||
static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
|
||||
|
||||
/* Count initial bytes not aligned to 64-bit when using the POPCNT instruction,
|
||||
* otherwise align to 32-bit. */
|
||||
int align = use_popcnt ? 7 : 3;
|
||||
while ((unsigned long)p & align && count) {
|
||||
|
||||
/* Count initial bytes not aligned to 32 bit. */
|
||||
while((unsigned long)p & 3 && count) {
|
||||
bits += bitsinbyte[*p++];
|
||||
count--;
|
||||
}
|
||||
|
||||
if (likely(use_popcnt)) {
|
||||
/* Use separate counters to make the CPU think there are no
|
||||
* dependencies between these popcnt operations. */
|
||||
uint64_t cnt[4];
|
||||
memset(cnt, 0, sizeof(cnt));
|
||||
|
||||
/* Count bits 32 bytes at a time by using popcnt.
|
||||
* Unroll the loop to avoid the overhead of a single popcnt per iteration,
|
||||
* allowing the CPU to extract more instruction-level parallelism.
|
||||
* Reference: https://danluu.com/assembly-intrinsics/ */
|
||||
while (count >= 32) {
|
||||
cnt[0] += __builtin_popcountll(*(uint64_t*)(p));
|
||||
cnt[1] += __builtin_popcountll(*(uint64_t*)(p + 8));
|
||||
cnt[2] += __builtin_popcountll(*(uint64_t*)(p + 16));
|
||||
cnt[3] += __builtin_popcountll(*(uint64_t*)(p + 24));
|
||||
count -= 32;
|
||||
p += 32;
|
||||
}
|
||||
bits += cnt[0] + cnt[1] + cnt[2] + cnt[3];
|
||||
goto remain;
|
||||
}
|
||||
|
||||
/* Count bits 28 bytes at a time */
|
||||
p4 = (uint32_t*)p;
|
||||
while(count>=28) {
|
||||
@@ -95,10 +85,8 @@ long long redisPopcount(void *s, long count) {
|
||||
((aux6 + (aux6 >> 4)) & 0x0F0F0F0F) +
|
||||
((aux7 + (aux7 >> 4)) & 0x0F0F0F0F))* 0x01010101) >> 24;
|
||||
}
|
||||
p = (unsigned char*)p4;
|
||||
|
||||
remain:
|
||||
/* Count the remaining bytes. */
|
||||
p = (unsigned char*)p4;
|
||||
while(count--) bits += bitsinbyte[*p++];
|
||||
return bits;
|
||||
}
|
||||
@@ -489,27 +477,22 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {
|
||||
* bits to a string object. The command creates or pad with zeroes the string
|
||||
* 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.
|
||||
*
|
||||
* (Must provide all the arguments to the function)
|
||||
*/
|
||||
static robj *lookupStringForBitCommand(client *c, uint64_t maxbit,
|
||||
size_t *strOldSize, size_t *strGrowSize)
|
||||
{
|
||||
* an error is sent to the client. */
|
||||
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) {
|
||||
o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));
|
||||
dbAdd(c->db,c->argv[1],o);
|
||||
*strGrowSize = byte + 1;
|
||||
*strOldSize = 0;
|
||||
if (dirty) *dirty = 1;
|
||||
} else {
|
||||
o = dbUnshareStringValue(c->db,c->argv[1],o);
|
||||
*strOldSize = sdslen(o->ptr);
|
||||
size_t oldlen = sdslen(o->ptr);
|
||||
o->ptr = sdsgrowzero(o->ptr,byte+1);
|
||||
*strGrowSize = sdslen(o->ptr) - *strOldSize;
|
||||
if (dirty && oldlen != sdslen(o->ptr)) *dirty = 1;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
@@ -566,9 +549,8 @@ void setbitCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t strOldSize, strGrowSize;
|
||||
if ((o = lookupStringForBitCommand(c,bitoffset,&strOldSize,&strGrowSize)) == NULL)
|
||||
return;
|
||||
int dirty;
|
||||
if ((o = lookupStringForBitCommand(c,bitoffset,&dirty)) == NULL) return;
|
||||
|
||||
/* Get current values */
|
||||
byte = bitoffset >> 3;
|
||||
@@ -579,7 +561,7 @@ void setbitCommand(client *c) {
|
||||
/* 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 (strGrowSize || (!!bitval != on)) {
|
||||
if (dirty || (!!bitval != on)) {
|
||||
/* Update byte with new bit value. */
|
||||
byteval &= ~(1 << bit);
|
||||
byteval |= ((on & 0x1) << bit);
|
||||
@@ -587,13 +569,6 @@ void setbitCommand(client *c) {
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
|
||||
server.dirty++;
|
||||
|
||||
/* If this is not a new key (old size not 0) and size changed, then
|
||||
* update the keysizes histogram. Otherwise, the histogram already
|
||||
* updated in lookupStringForBitCommand() by calling dbAdd(). */
|
||||
if ((strOldSize > 0) && (strGrowSize != 0))
|
||||
updateKeysizesHist(c->db, getKeySlot(c->argv[1]->ptr), OBJ_STRING,
|
||||
strOldSize, strOldSize + strGrowSize);
|
||||
}
|
||||
|
||||
/* Return original value. */
|
||||
@@ -827,12 +802,25 @@ void bitcountCommand(client *c) {
|
||||
int isbit = 0;
|
||||
unsigned char first_byte_neg_mask = 0, last_byte_neg_mask = 0;
|
||||
|
||||
/* Lookup, check for type, and return 0 for non existing keys. */
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* Parse start/end range if any. */
|
||||
if (c->argc == 4 || c->argc == 5) {
|
||||
long long totlen = strlen;
|
||||
/* Make sure we will not overflow */
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)
|
||||
return;
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)
|
||||
return;
|
||||
/* Convert negative indexes */
|
||||
if (start < 0 && end < 0 && start > end) {
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
if (c->argc == 5) {
|
||||
if (!strcasecmp(c->argv[4]->ptr,"bit")) isbit = 1;
|
||||
else if (!strcasecmp(c->argv[4]->ptr,"byte")) isbit = 0;
|
||||
@@ -841,20 +829,6 @@ void bitcountCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
long long totlen = strlen;
|
||||
|
||||
/* Make sure we will not overflow */
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
|
||||
/* Convert negative indexes */
|
||||
if (start < 0 && end < 0 && start > end) {
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
if (isbit) totlen <<= 3;
|
||||
if (start < 0) start = totlen+start;
|
||||
if (end < 0) end = totlen+end;
|
||||
@@ -870,10 +844,6 @@ void bitcountCommand(client *c) {
|
||||
end >>= 3;
|
||||
}
|
||||
} else if (c->argc == 2) {
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
/* The whole string. */
|
||||
start = 0;
|
||||
end = strlen-1;
|
||||
@@ -883,12 +853,6 @@ void bitcountCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Return 0 for non existing keys. */
|
||||
if (o == NULL) {
|
||||
addReply(c, shared.czero);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Precondition: end >= 0 && end < strlen, so the only condition where
|
||||
* zero can be returned is: start > end. */
|
||||
if (start > end) {
|
||||
@@ -928,8 +892,21 @@ void bitposCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the key does not exist, from our point of view it is an infinite
|
||||
* array of 0 bits. If the user is looking for the first clear bit return 0,
|
||||
* If the user is looking for the first set bit, return -1. */
|
||||
if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
|
||||
addReplyLongLong(c, bit ? -1 : 0);
|
||||
return;
|
||||
}
|
||||
if (checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* Parse start/end range if any. */
|
||||
if (c->argc == 4 || c->argc == 5 || c->argc == 6) {
|
||||
long long totlen = strlen;
|
||||
/* Make sure we will not overflow */
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[3],&start,NULL) != C_OK)
|
||||
return;
|
||||
if (c->argc == 6) {
|
||||
@@ -944,22 +921,10 @@ void bitposCommand(client *c) {
|
||||
if (getLongLongFromObjectOrReply(c,c->argv[4],&end,NULL) != C_OK)
|
||||
return;
|
||||
end_given = 1;
|
||||
}
|
||||
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c, o, OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o, &strlen, llbuf);
|
||||
|
||||
/* Make sure we will not overflow */
|
||||
long long totlen = strlen;
|
||||
serverAssert(totlen <= LLONG_MAX >> 3);
|
||||
|
||||
if (c->argc < 5) {
|
||||
} else {
|
||||
if (isbit) end = (totlen<<3) + 7;
|
||||
else end = totlen-1;
|
||||
}
|
||||
|
||||
if (isbit) totlen <<= 3;
|
||||
/* Convert negative indexes */
|
||||
if (start < 0) start = totlen+start;
|
||||
@@ -976,11 +941,6 @@ void bitposCommand(client *c) {
|
||||
end >>= 3;
|
||||
}
|
||||
} else if (c->argc == 3) {
|
||||
/* Lookup, check for type. */
|
||||
o = lookupKeyRead(c->db, c->argv[1]);
|
||||
if (checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* The whole string. */
|
||||
start = 0;
|
||||
end = strlen-1;
|
||||
@@ -990,14 +950,6 @@ void bitposCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the key does not exist, from our point of view it is an infinite
|
||||
* array of 0 bits. If the user is looking for the first clear bit return 0,
|
||||
* If the user is looking for the first set bit, return -1. */
|
||||
if (o == NULL) {
|
||||
addReplyLongLong(c, bit ? -1 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* For empty ranges (start > end) we return -1 as an empty range does
|
||||
* not contain a 0 nor a 1. */
|
||||
if (start > end) {
|
||||
@@ -1078,8 +1030,7 @@ struct bitfieldOp {
|
||||
void bitfieldGeneric(client *c, int flags) {
|
||||
robj *o;
|
||||
uint64_t bitoffset;
|
||||
int j, numops = 0, changes = 0;
|
||||
size_t strOldSize, strGrowSize = 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;
|
||||
@@ -1173,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,&strOldSize,&strGrowSize)) == NULL) {
|
||||
highest_write_offset,&dirty)) == NULL) {
|
||||
zfree(ops);
|
||||
return;
|
||||
}
|
||||
@@ -1223,7 +1174,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
setSignedBitfield(o->ptr,thisop->offset,
|
||||
thisop->bits,newval);
|
||||
|
||||
if (strGrowSize || (oldval != newval))
|
||||
if (dirty || (oldval != newval))
|
||||
changes++;
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
@@ -1257,7 +1208,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
setUnsignedBitfield(o->ptr,thisop->offset,
|
||||
thisop->bits,newval);
|
||||
|
||||
if (strGrowSize || (oldval != newval))
|
||||
if (dirty || (oldval != newval))
|
||||
changes++;
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
@@ -1300,14 +1251,6 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
|
||||
/* If this is not a new key (old size not 0) and size changed, then
|
||||
* update the keysizes histogram. Otherwise, the histogram already
|
||||
* updated in lookupStringForBitCommand() by calling dbAdd(). */
|
||||
if ((strOldSize > 0) && (strGrowSize != 0))
|
||||
updateKeysizesHist(c->db, getKeySlot(c->argv[1]->ptr), OBJ_STRING,
|
||||
strOldSize, strOldSize + strGrowSize);
|
||||
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
|
||||
server.dirty += changes;
|
||||
|
||||
+45
-28
@@ -1,10 +1,31 @@
|
||||
/* blocked.c - generic support for blocking operations like BLPOP & WAIT.
|
||||
*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
@@ -68,7 +89,6 @@ void blockClient(client *c, int btype) {
|
||||
/* Master client should never be blocked unless pause or module */
|
||||
serverAssert(!(c->flags & CLIENT_MASTER &&
|
||||
btype != BLOCKED_MODULE &&
|
||||
btype != BLOCKED_LAZYFREE &&
|
||||
btype != BLOCKED_POSTPONE));
|
||||
|
||||
c->flags |= CLIENT_BLOCKED;
|
||||
@@ -176,8 +196,6 @@ void unblockClient(client *c, int queue_for_reprocessing) {
|
||||
c->postponed_list_node = NULL;
|
||||
} else if (c->bstate.btype == BLOCKED_SHUTDOWN) {
|
||||
/* No special cleanup. */
|
||||
} else if (c->bstate.btype == BLOCKED_LAZYFREE) {
|
||||
/* No special cleanup. */
|
||||
} else {
|
||||
serverPanic("Unknown btype in unblockClient().");
|
||||
}
|
||||
@@ -209,9 +227,7 @@ void unblockClient(client *c, int queue_for_reprocessing) {
|
||||
* send it a reply of some kind. After this function is called,
|
||||
* unblockClient() will be called with the same client as argument. */
|
||||
void replyToBlockedClientTimedOut(client *c) {
|
||||
if (c->bstate.btype == BLOCKED_LAZYFREE) {
|
||||
addReply(c, shared.ok); /* No reason lazy-free to fail */
|
||||
} else if (c->bstate.btype == BLOCKED_LIST ||
|
||||
if (c->bstate.btype == BLOCKED_LIST ||
|
||||
c->bstate.btype == BLOCKED_ZSET ||
|
||||
c->bstate.btype == BLOCKED_STREAM) {
|
||||
addReplyNullArray(c);
|
||||
@@ -268,16 +284,9 @@ void disconnectAllBlockedClients(void) {
|
||||
if (c->bstate.btype == BLOCKED_POSTPONE)
|
||||
continue;
|
||||
|
||||
if (c->bstate.btype == BLOCKED_LAZYFREE) {
|
||||
addReply(c, shared.ok); /* No reason lazy-free to fail */
|
||||
c->flags &= ~CLIENT_PENDING_COMMAND;
|
||||
unblockClient(c, 1);
|
||||
} else {
|
||||
|
||||
unblockClientOnError(c,
|
||||
"-UNBLOCKED force unblock from blocking operation, "
|
||||
"instance state changed (master -> replica?)");
|
||||
}
|
||||
unblockClientOnError(c,
|
||||
"-UNBLOCKED force unblock from blocking operation, "
|
||||
"instance state changed (master -> replica?)");
|
||||
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
|
||||
}
|
||||
}
|
||||
@@ -361,12 +370,7 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
list *l;
|
||||
int j;
|
||||
|
||||
if (!(c->flags & CLIENT_REPROCESSING_COMMAND)) {
|
||||
/* If the client is re-processing the command, we do not set the timeout
|
||||
* because we need to retain the client's original timeout. */
|
||||
c->bstate.timeout = timeout;
|
||||
}
|
||||
|
||||
c->bstate.timeout = timeout;
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
/* If the key already exists in the dictionary ignore it. */
|
||||
if (!(client_blocked_entry = dictAddRaw(c->bstate.keys,keys[j],NULL))) {
|
||||
@@ -388,6 +392,7 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
listAddNodeTail(l,c);
|
||||
dictSetVal(c->bstate.keys,client_blocked_entry,listLast(l));
|
||||
|
||||
|
||||
/* We need to add the key to blocking_keys_unblock_on_nokey, if the client
|
||||
* wants to be awakened if key is deleted (like XREADGROUP) */
|
||||
if (unblock_on_nokey) {
|
||||
@@ -698,9 +703,6 @@ static void moduleUnblockClientOnKey(client *c, robj *key) {
|
||||
* we want to remove the pending flag to indicate we already responded to the
|
||||
* command with timeout reply. */
|
||||
void unblockClientOnTimeout(client *c) {
|
||||
/* The client has been unlocked (in the moduleUnblocked list), return ASAP. */
|
||||
if (c->bstate.btype == BLOCKED_MODULE && isModuleClientUnblocked(c)) return;
|
||||
|
||||
replyToBlockedClientTimedOut(c);
|
||||
if (c->flags & CLIENT_PENDING_COMMAND)
|
||||
c->flags &= ~CLIENT_PENDING_COMMAND;
|
||||
@@ -718,6 +720,21 @@ void unblockClientOnError(client *c, const char *err_str) {
|
||||
unblockClient(c, 1);
|
||||
}
|
||||
|
||||
/* sets blocking_keys to the total number of keys which has at least one client blocked on them
|
||||
* sets blocking_keys_on_nokey to the total number of keys which has at least one client
|
||||
* blocked on them to be written or deleted */
|
||||
void totalNumberOfBlockingKeys(unsigned long *blocking_keys, unsigned long *bloking_keys_on_nokey) {
|
||||
unsigned long bkeys=0, bkeys_on_nokey=0;
|
||||
for (int j = 0; j < server.dbnum; j++) {
|
||||
bkeys += dictSize(server.db[j].blocking_keys);
|
||||
bkeys_on_nokey += dictSize(server.db[j].blocking_keys_unblock_on_nokey);
|
||||
}
|
||||
if (blocking_keys)
|
||||
*blocking_keys = bkeys;
|
||||
if (bloking_keys_on_nokey)
|
||||
*bloking_keys_on_nokey = bkeys_on_nokey;
|
||||
}
|
||||
|
||||
void blockedBeforeSleep(void) {
|
||||
/* Handle precise timeouts of blocked clients. */
|
||||
handleBlockedClientsTimeout();
|
||||
|
||||
+24
-3
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2021, Redis Labs Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
|
||||
+24
-3
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* Copyright (c) 2009-2021, Redis Labs Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SRC_CALL_REPLY_H_
|
||||
|
||||
+24
-3
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2016-Present, Redis Ltd.
|
||||
* Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
|
||||
+24
-39
@@ -1,16 +1,35 @@
|
||||
/* CLI (command line interface) common methods
|
||||
*
|
||||
* Copyright (c) 2020-Present, Redis Ltd.
|
||||
* Copyright (c) 2020, Redis Labs
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include "cli_common.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@@ -29,9 +48,6 @@
|
||||
|
||||
#define UNUSED(V) ((void) V)
|
||||
|
||||
char *redisGitSHA1(void);
|
||||
char *redisGitDirty(void);
|
||||
|
||||
/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
|
||||
* not building with TLS support.
|
||||
*/
|
||||
@@ -390,34 +406,3 @@ sds escapeJsonString(sds s, const char *p, size_t len) {
|
||||
}
|
||||
return sdscatlen(s,"\"",1);
|
||||
}
|
||||
|
||||
sds cliVersion(void) {
|
||||
sds version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
|
||||
|
||||
/* Add git commit and working tree status when available. */
|
||||
if (strtoll(redisGitSHA1(),NULL,16)) {
|
||||
version = sdscatprintf(version, " (git:%s", redisGitSHA1());
|
||||
if (strtoll(redisGitDirty(),NULL,10))
|
||||
version = sdscatprintf(version, "-dirty");
|
||||
version = sdscat(version, ")");
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
/* This is a wrapper to call redisConnect or redisConnectWithTimeout. */
|
||||
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv) {
|
||||
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||
return redisConnect(ip, port);
|
||||
} else {
|
||||
return redisConnectWithTimeout(ip, port, tv);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a wrapper to call redisConnectUnix or redisConnectUnixWithTimeout. */
|
||||
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv) {
|
||||
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||
return redisConnectUnix(path);
|
||||
} else {
|
||||
return redisConnectUnixWithTimeout(path, tv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,4 @@ void freeCliConnInfo(cliConnInfo connInfo);
|
||||
|
||||
sds escapeJsonString(sds s, const char *p, size_t len);
|
||||
|
||||
sds cliVersion(void);
|
||||
|
||||
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv);
|
||||
|
||||
#endif /* __CLICOMMON_H */
|
||||
|
||||
+6667
-676
File diff suppressed because it is too large
Load Diff
+402
-120
@@ -2,15 +2,22 @@
|
||||
#define __CLUSTER_H
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Redis cluster exported API.
|
||||
* Redis cluster data structures, defines, exported API.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#define CLUSTER_SLOT_MASK_BITS 14 /* Number of bits used for slot id. */
|
||||
#define CLUSTER_SLOTS (1<<CLUSTER_SLOT_MASK_BITS) /* Total number of slots in cluster mode, which is 16384. */
|
||||
#define CLUSTER_SLOT_MASK ((unsigned long long)(CLUSTER_SLOTS - 1)) /* Bit mask for slot id stored in LSB. */
|
||||
#define CLUSTER_SLOTS 16384
|
||||
#define CLUSTER_OK 0 /* Everything looks ok */
|
||||
#define CLUSTER_FAIL 1 /* The cluster can't work */
|
||||
#define CLUSTER_NAMELEN 40 /* sha1 hex length */
|
||||
#define CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */
|
||||
|
||||
/* The following defines are amount of time, sometimes expressed as
|
||||
* multiplicators of the node timeout value (when ending with MULT). */
|
||||
#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */
|
||||
#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */
|
||||
#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */
|
||||
#define CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */
|
||||
#define CLUSTER_SLAVE_MIGRATION_DELAY 5000 /* Delay for slave migration. */
|
||||
|
||||
/* Redirection errors returned by getNodeByQuery(). */
|
||||
#define CLUSTER_REDIR_NONE 0 /* Node can serve the request. */
|
||||
@@ -22,8 +29,77 @@
|
||||
#define CLUSTER_REDIR_DOWN_UNBOUND 6 /* -CLUSTERDOWN, unbound slot. */
|
||||
#define CLUSTER_REDIR_DOWN_RO_STATE 7 /* -CLUSTERDOWN, allow reads. */
|
||||
|
||||
typedef struct _clusterNode clusterNode;
|
||||
struct clusterState;
|
||||
struct clusterNode;
|
||||
|
||||
/* clusterLink encapsulates everything needed to talk with a remote node. */
|
||||
typedef struct clusterLink {
|
||||
mstime_t ctime; /* Link creation time */
|
||||
connection *conn; /* Connection to remote node */
|
||||
list *send_msg_queue; /* List of messages to be sent */
|
||||
size_t head_msg_send_offset; /* Number of bytes already sent of message at head of queue */
|
||||
unsigned long long send_msg_queue_mem; /* Memory in bytes used by message queue */
|
||||
char *rcvbuf; /* Packet reception buffer */
|
||||
size_t rcvbuf_len; /* Used size of rcvbuf */
|
||||
size_t rcvbuf_alloc; /* Allocated size of rcvbuf */
|
||||
struct clusterNode *node; /* Node related to this link. Initialized to NULL when unknown */
|
||||
int inbound; /* 1 if this link is an inbound link accepted from the related node */
|
||||
} clusterLink;
|
||||
|
||||
/* Cluster node flags and macros. */
|
||||
#define CLUSTER_NODE_MASTER 1 /* The node is a master */
|
||||
#define CLUSTER_NODE_SLAVE 2 /* The node is a slave */
|
||||
#define CLUSTER_NODE_PFAIL 4 /* Failure? Need acknowledge */
|
||||
#define CLUSTER_NODE_FAIL 8 /* The node is believed to be malfunctioning */
|
||||
#define CLUSTER_NODE_MYSELF 16 /* This node is myself */
|
||||
#define CLUSTER_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */
|
||||
#define CLUSTER_NODE_NOADDR 64 /* We don't know the address of this node */
|
||||
#define CLUSTER_NODE_MEET 128 /* Send a MEET message to this node */
|
||||
#define CLUSTER_NODE_MIGRATE_TO 256 /* Master eligible for replica migration. */
|
||||
#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failover. */
|
||||
#define CLUSTER_NODE_NULL_NAME "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
|
||||
|
||||
#define nodeIsMaster(n) ((n)->flags & CLUSTER_NODE_MASTER)
|
||||
#define nodeIsSlave(n) ((n)->flags & CLUSTER_NODE_SLAVE)
|
||||
#define nodeInHandshake(n) ((n)->flags & CLUSTER_NODE_HANDSHAKE)
|
||||
#define nodeHasAddr(n) (!((n)->flags & CLUSTER_NODE_NOADDR))
|
||||
#define nodeWithoutAddr(n) ((n)->flags & CLUSTER_NODE_NOADDR)
|
||||
#define nodeTimedOut(n) ((n)->flags & CLUSTER_NODE_PFAIL)
|
||||
#define nodeFailed(n) ((n)->flags & CLUSTER_NODE_FAIL)
|
||||
#define nodeCantFailover(n) ((n)->flags & CLUSTER_NODE_NOFAILOVER)
|
||||
|
||||
/* Reasons why a slave is not able to failover. */
|
||||
#define CLUSTER_CANT_FAILOVER_NONE 0
|
||||
#define CLUSTER_CANT_FAILOVER_DATA_AGE 1
|
||||
#define CLUSTER_CANT_FAILOVER_WAITING_DELAY 2
|
||||
#define CLUSTER_CANT_FAILOVER_EXPIRED 3
|
||||
#define CLUSTER_CANT_FAILOVER_WAITING_VOTES 4
|
||||
#define CLUSTER_CANT_FAILOVER_RELOG_PERIOD (10) /* seconds. */
|
||||
|
||||
/* clusterState todo_before_sleep flags. */
|
||||
#define CLUSTER_TODO_HANDLE_FAILOVER (1<<0)
|
||||
#define CLUSTER_TODO_UPDATE_STATE (1<<1)
|
||||
#define CLUSTER_TODO_SAVE_CONFIG (1<<2)
|
||||
#define CLUSTER_TODO_FSYNC_CONFIG (1<<3)
|
||||
#define CLUSTER_TODO_HANDLE_MANUALFAILOVER (1<<4)
|
||||
|
||||
/* Message types.
|
||||
*
|
||||
* Note that the PING, PONG and MEET messages are actually the same exact
|
||||
* kind of packet. PONG is the reply to ping, in the exact format as a PING,
|
||||
* while MEET is a special PING that forces the receiver to add the sender
|
||||
* as a node (if it is not already in the list). */
|
||||
#define CLUSTERMSG_TYPE_PING 0 /* Ping */
|
||||
#define CLUSTERMSG_TYPE_PONG 1 /* Pong (reply to Ping) */
|
||||
#define CLUSTERMSG_TYPE_MEET 2 /* Meet "let's join" message */
|
||||
#define CLUSTERMSG_TYPE_FAIL 3 /* Mark node xxx as failing */
|
||||
#define CLUSTERMSG_TYPE_PUBLISH 4 /* Pub/Sub Publish propagation */
|
||||
#define CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 5 /* May I failover? */
|
||||
#define CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 6 /* Yes, you have my vote */
|
||||
#define CLUSTERMSG_TYPE_UPDATE 7 /* Another node slots configuration */
|
||||
#define CLUSTERMSG_TYPE_MFSTART 8 /* Pause clients for manual failover */
|
||||
#define CLUSTERMSG_TYPE_MODULE 9 /* Module cluster API message. */
|
||||
#define CLUSTERMSG_TYPE_PUBLISHSHARD 10 /* Pub/Sub Publish shard propagation */
|
||||
#define CLUSTERMSG_TYPE_COUNT 11 /* Total number of message types. */
|
||||
|
||||
/* Flags that a module can set in order to prevent certain Redis Cluster
|
||||
* features to be enabled. Useful when implementing a different distributed
|
||||
@@ -32,133 +108,339 @@ struct clusterState;
|
||||
#define CLUSTER_MODULE_FLAG_NO_FAILOVER (1<<1)
|
||||
#define CLUSTER_MODULE_FLAG_NO_REDIRECTION (1<<2)
|
||||
|
||||
/* ---------------------- API exported outside cluster.c -------------------- */
|
||||
/* This structure represent elements of node->fail_reports. */
|
||||
typedef struct clusterNodeFailReport {
|
||||
struct clusterNode *node; /* Node reporting the failure condition. */
|
||||
mstime_t time; /* Time of the last report from this node. */
|
||||
} clusterNodeFailReport;
|
||||
|
||||
/* We have 16384 hash slots. The hash slot of a given key is obtained
|
||||
* as the least significant 14 bits of the crc16 of the key.
|
||||
typedef struct clusterNode {
|
||||
mstime_t ctime; /* Node object creation time. */
|
||||
char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
|
||||
char shard_id[CLUSTER_NAMELEN]; /* shard id, hex string, sha1-size */
|
||||
int flags; /* CLUSTER_NODE_... */
|
||||
uint64_t configEpoch; /* Last configEpoch observed for this node */
|
||||
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
|
||||
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 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 */
|
||||
struct clusterNode *slaveof; /* pointer to the master node. Note that it
|
||||
may be NULL even if the node is a slave
|
||||
if we don't have the master node in our
|
||||
tables. */
|
||||
unsigned long long last_in_ping_gossip; /* The number of the last carried in the ping gossip section */
|
||||
mstime_t ping_sent; /* Unix time we sent latest ping */
|
||||
mstime_t pong_received; /* Unix time we received the pong */
|
||||
mstime_t data_received; /* Unix time we received any data */
|
||||
mstime_t fail_time; /* Unix time when FAIL flag was set */
|
||||
mstime_t voted_time; /* Last time we voted for a slave of this master */
|
||||
mstime_t repl_offset_time; /* Unix time we received offset for this node */
|
||||
mstime_t orphaned_time; /* Starting time of orphaned master condition */
|
||||
long long repl_offset; /* Last known repl offset for this node. */
|
||||
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
|
||||
sds hostname; /* The known hostname for this node */
|
||||
sds human_nodename; /* The known human readable nodename for this node */
|
||||
int tcp_port; /* Latest known clients TCP port. */
|
||||
int tls_port; /* Latest known clients TLS port */
|
||||
int cport; /* Latest known cluster port of this node. */
|
||||
clusterLink *link; /* TCP/IP link established toward this node */
|
||||
clusterLink *inbound_link; /* TCP/IP link accepted from this node */
|
||||
list *fail_reports; /* List of nodes signaling this as failing */
|
||||
} clusterNode;
|
||||
|
||||
/* Slot to keys for a single slot. The keys in the same slot are linked together
|
||||
* using dictEntry metadata. */
|
||||
typedef struct slotToKeys {
|
||||
uint64_t count; /* Number of keys in the slot. */
|
||||
dictEntry *head; /* The first key-value entry in the slot. */
|
||||
} slotToKeys;
|
||||
|
||||
/* Slot to keys mapping for all slots, opaque outside this file. */
|
||||
struct clusterSlotToKeyMapping {
|
||||
slotToKeys by_slot[CLUSTER_SLOTS];
|
||||
};
|
||||
|
||||
/* Dict entry metadata for cluster mode, used for the Slot to Key API to form a
|
||||
* linked list of the entries belonging to the same slot. */
|
||||
typedef struct clusterDictEntryMetadata {
|
||||
dictEntry *prev; /* Prev entry with key in the same slot */
|
||||
dictEntry *next; /* Next entry with key in the same slot */
|
||||
} clusterDictEntryMetadata;
|
||||
|
||||
typedef struct {
|
||||
redisDb *db; /* A link back to the db this dict belongs to */
|
||||
} clusterDictMetadata;
|
||||
|
||||
typedef struct clusterState {
|
||||
clusterNode *myself; /* This node */
|
||||
uint64_t currentEpoch;
|
||||
int state; /* CLUSTER_OK, CLUSTER_FAIL, ... */
|
||||
int size; /* Num of master nodes with at least one slot */
|
||||
dict *nodes; /* Hash table of name -> clusterNode structures */
|
||||
dict *shards; /* Hash table of shard_id -> list (of nodes) structures */
|
||||
dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */
|
||||
clusterNode *migrating_slots_to[CLUSTER_SLOTS];
|
||||
clusterNode *importing_slots_from[CLUSTER_SLOTS];
|
||||
clusterNode *slots[CLUSTER_SLOTS];
|
||||
rax *slots_to_channels;
|
||||
/* The following fields are used to take the slave state on elections. */
|
||||
mstime_t failover_auth_time; /* Time of previous or next election. */
|
||||
int failover_auth_count; /* Number of votes received so far. */
|
||||
int failover_auth_sent; /* True if we already asked for votes. */
|
||||
int failover_auth_rank; /* This slave rank for current auth request. */
|
||||
uint64_t failover_auth_epoch; /* Epoch of the current election. */
|
||||
int cant_failover_reason; /* Why a slave is currently not able to
|
||||
failover. See the CANT_FAILOVER_* macros. */
|
||||
/* Manual failover state in common. */
|
||||
mstime_t mf_end; /* Manual failover time limit (ms unixtime).
|
||||
It is zero if there is no MF in progress. */
|
||||
/* Manual failover state of master. */
|
||||
clusterNode *mf_slave; /* Slave performing the manual failover. */
|
||||
/* Manual failover state of slave. */
|
||||
long long mf_master_offset; /* Master offset the slave needs to start MF
|
||||
or -1 if still not received. */
|
||||
int mf_can_start; /* If non-zero signal that the manual failover
|
||||
can start requesting masters vote. */
|
||||
/* The following fields are used by masters to take state on elections. */
|
||||
uint64_t lastVoteEpoch; /* Epoch of the last vote granted. */
|
||||
int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */
|
||||
/* Stats */
|
||||
/* Messages received and sent by type. */
|
||||
long long stats_bus_messages_sent[CLUSTERMSG_TYPE_COUNT];
|
||||
long long stats_bus_messages_received[CLUSTERMSG_TYPE_COUNT];
|
||||
long long stats_pfail_nodes; /* Number of nodes in PFAIL status,
|
||||
excluding nodes without address. */
|
||||
unsigned long long stat_cluster_links_buffer_limit_exceeded; /* Total number of cluster links freed due to exceeding buffer limit */
|
||||
|
||||
/* Bit map for slots that are no longer claimed by the owner in cluster PING
|
||||
* messages. During slot migration, the owner will stop claiming the slot after
|
||||
* the ownership transfer. Set the bit corresponding to the slot when a node
|
||||
* stops claiming the slot. This prevents spreading incorrect information (that
|
||||
* source still owns the slot) using UPDATE messages. */
|
||||
unsigned char owner_not_claiming_slot[CLUSTER_SLOTS / 8];
|
||||
} clusterState;
|
||||
|
||||
/* Redis cluster messages header */
|
||||
|
||||
/* Initially we don't know our "name", but we'll find it once we connect
|
||||
* to the first node, using the getsockname() function. Then we'll use this
|
||||
* address for all the next messages. */
|
||||
typedef struct {
|
||||
char nodename[CLUSTER_NAMELEN];
|
||||
uint32_t ping_sent;
|
||||
uint32_t pong_received;
|
||||
char ip[NET_IP_STR_LEN]; /* IP address last time it was seen */
|
||||
uint16_t port; /* primary port last time it was seen */
|
||||
uint16_t cport; /* cluster port last time it was seen */
|
||||
uint16_t flags; /* node->flags copy */
|
||||
uint16_t pport; /* secondary port last time it was seen */
|
||||
uint16_t notused1;
|
||||
} clusterMsgDataGossip;
|
||||
|
||||
typedef struct {
|
||||
char nodename[CLUSTER_NAMELEN];
|
||||
} clusterMsgDataFail;
|
||||
|
||||
typedef struct {
|
||||
uint32_t channel_len;
|
||||
uint32_t message_len;
|
||||
unsigned char bulk_data[8]; /* 8 bytes just as placeholder. */
|
||||
} clusterMsgDataPublish;
|
||||
|
||||
typedef struct {
|
||||
uint64_t configEpoch; /* Config epoch of the specified instance. */
|
||||
char nodename[CLUSTER_NAMELEN]; /* Name of the slots owner. */
|
||||
unsigned char slots[CLUSTER_SLOTS/8]; /* Slots bitmap. */
|
||||
} clusterMsgDataUpdate;
|
||||
|
||||
typedef struct {
|
||||
uint64_t module_id; /* ID of the sender module. */
|
||||
uint32_t len; /* ID of the sender module. */
|
||||
uint8_t type; /* Type from 0 to 255. */
|
||||
unsigned char bulk_data[3]; /* 3 bytes just as placeholder. */
|
||||
} clusterMsgModule;
|
||||
|
||||
/* The cluster supports optional extension messages that can be sent
|
||||
* along with ping/pong/meet messages to give additional info in a
|
||||
* consistent manner. */
|
||||
typedef enum {
|
||||
CLUSTERMSG_EXT_TYPE_HOSTNAME,
|
||||
CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME,
|
||||
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE,
|
||||
CLUSTERMSG_EXT_TYPE_SHARDID,
|
||||
} clusterMsgPingtypes;
|
||||
|
||||
/* Helper function for making sure extensions are eight byte aligned. */
|
||||
#define EIGHT_BYTE_ALIGN(size) ((((size) + 7) / 8) * 8)
|
||||
|
||||
typedef struct {
|
||||
char hostname[1]; /* The announced hostname, ends with \0. */
|
||||
} clusterMsgPingExtHostname;
|
||||
|
||||
typedef struct {
|
||||
char human_nodename[1]; /* The announced nodename, ends with \0. */
|
||||
} clusterMsgPingExtHumanNodename;
|
||||
|
||||
typedef struct {
|
||||
char name[CLUSTER_NAMELEN]; /* Node name. */
|
||||
uint64_t ttl; /* Remaining time to blacklist the node, in seconds. */
|
||||
} clusterMsgPingExtForgottenNode;
|
||||
|
||||
static_assert(sizeof(clusterMsgPingExtForgottenNode) % 8 == 0, "");
|
||||
|
||||
typedef struct {
|
||||
char shard_id[CLUSTER_NAMELEN]; /* The shard_id, 40 bytes fixed. */
|
||||
} clusterMsgPingExtShardId;
|
||||
|
||||
typedef struct {
|
||||
uint32_t length; /* Total length of this extension message (including this header) */
|
||||
uint16_t type; /* Type of this extension message (see clusterMsgPingExtTypes) */
|
||||
uint16_t unused; /* 16 bits of padding to make this structure 8 byte aligned. */
|
||||
union {
|
||||
clusterMsgPingExtHostname hostname;
|
||||
clusterMsgPingExtHumanNodename human_nodename;
|
||||
clusterMsgPingExtForgottenNode forgotten_node;
|
||||
clusterMsgPingExtShardId shard_id;
|
||||
} ext[]; /* Actual extension information, formatted so that the data is 8
|
||||
* byte aligned, regardless of its content. */
|
||||
} clusterMsgPingExt;
|
||||
|
||||
union clusterMsgData {
|
||||
/* PING, MEET and PONG */
|
||||
struct {
|
||||
/* Array of N clusterMsgDataGossip structures */
|
||||
clusterMsgDataGossip gossip[1];
|
||||
/* Extension data that can optionally be sent for ping/meet/pong
|
||||
* messages. We can't explicitly define them here though, since
|
||||
* the gossip array isn't the real length of the gossip data. */
|
||||
} ping;
|
||||
|
||||
/* FAIL */
|
||||
struct {
|
||||
clusterMsgDataFail about;
|
||||
} fail;
|
||||
|
||||
/* PUBLISH */
|
||||
struct {
|
||||
clusterMsgDataPublish msg;
|
||||
} publish;
|
||||
|
||||
/* UPDATE */
|
||||
struct {
|
||||
clusterMsgDataUpdate nodecfg;
|
||||
} update;
|
||||
|
||||
/* MODULE */
|
||||
struct {
|
||||
clusterMsgModule msg;
|
||||
} module;
|
||||
};
|
||||
|
||||
#define CLUSTER_PROTO_VER 1 /* Cluster bus protocol version. */
|
||||
|
||||
typedef struct {
|
||||
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
|
||||
uint32_t totlen; /* Total length of this message */
|
||||
uint16_t ver; /* Protocol version, currently set to 1. */
|
||||
uint16_t port; /* Primary port number (TCP or TLS). */
|
||||
uint16_t type; /* Message type */
|
||||
uint16_t count; /* Only used for some kind of messages. */
|
||||
uint64_t currentEpoch; /* The epoch accordingly to the sending node. */
|
||||
uint64_t configEpoch; /* The config epoch if it's a master, or the last
|
||||
epoch advertised by its master if it is a
|
||||
slave. */
|
||||
uint64_t offset; /* Master replication offset if node is a master or
|
||||
processed replication offset if node is a slave. */
|
||||
char sender[CLUSTER_NAMELEN]; /* Name of the sender node */
|
||||
unsigned char myslots[CLUSTER_SLOTS/8];
|
||||
char slaveof[CLUSTER_NAMELEN];
|
||||
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
|
||||
uint16_t extensions; /* Number of extensions sent along with this packet. */
|
||||
char notused1[30]; /* 30 bytes reserved for future usage. */
|
||||
uint16_t pport; /* Secondary port number: if primary port is TCP port, this is
|
||||
TLS port, and if primary port is TLS port, this is TCP port.*/
|
||||
uint16_t cport; /* Sender TCP cluster bus port */
|
||||
uint16_t flags; /* Sender node flags */
|
||||
unsigned char state; /* Cluster state from the POV of the sender */
|
||||
unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */
|
||||
union clusterMsgData data;
|
||||
} clusterMsg;
|
||||
|
||||
/* clusterMsg defines the gossip wire protocol exchanged among Redis cluster
|
||||
* members, which can be running different versions of redis-server bits,
|
||||
* especially during cluster rolling upgrades.
|
||||
*
|
||||
* However, if the key contains the {...} pattern, only the part between
|
||||
* { and } is hashed. This may be useful in the future to force certain
|
||||
* keys to be in the same node (assuming no resharding is in progress). */
|
||||
static inline unsigned int keyHashSlot(char *key, int keylen) {
|
||||
int s, e; /* start-end indexes of { and } */
|
||||
* Therefore, fields in this struct should remain at the same offset from
|
||||
* release to release. The static asserts below ensures that incompatible
|
||||
* changes in clusterMsg be caught at compile time.
|
||||
*/
|
||||
|
||||
for (s = 0; s < keylen; s++)
|
||||
if (key[s] == '{') break;
|
||||
static_assert(offsetof(clusterMsg, sig) == 0, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, totlen) == 4, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, ver) == 8, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, port) == 10, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, type) == 12, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, count) == 14, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, currentEpoch) == 16, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, configEpoch) == 24, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, offset) == 32, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, sender) == 40, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, myslots) == 80, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, slaveof) == 2128, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, myip) == 2168, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, extensions) == 2214, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, notused1) == 2216, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, pport) == 2246, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, cport) == 2248, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, flags) == 2250, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, state) == 2252, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, mflags) == 2253, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, data) == 2256, "unexpected field offset");
|
||||
|
||||
/* No '{' ? Hash the whole key. This is the base case. */
|
||||
if (likely(s == keylen)) return crc16(key,keylen) & 0x3FFF;
|
||||
#define CLUSTERMSG_MIN_LEN (sizeof(clusterMsg)-sizeof(union clusterMsgData))
|
||||
|
||||
/* '{' found? Check if we have the corresponding '}'. */
|
||||
for (e = s+1; e < keylen; e++)
|
||||
if (key[e] == '}') break;
|
||||
/* Message flags better specify the packet content or are used to
|
||||
* provide some information about the node state. */
|
||||
#define CLUSTERMSG_FLAG0_PAUSED (1<<0) /* Master paused for manual failover. */
|
||||
#define CLUSTERMSG_FLAG0_FORCEACK (1<<1) /* Give ACK to AUTH_REQUEST even if
|
||||
master is up. */
|
||||
#define CLUSTERMSG_FLAG0_EXT_DATA (1<<2) /* Message contains extension data */
|
||||
|
||||
/* No '}' or nothing between {} ? Hash the whole key. */
|
||||
if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
|
||||
|
||||
/* If we are here there is both a { and a } on its right. Hash
|
||||
* what is in the middle between { and }. */
|
||||
return crc16(key+s+1,e-s-1) & 0x3FFF;
|
||||
}
|
||||
|
||||
/* functions requiring mechanism specific implementations */
|
||||
/* ---------------------- API exported outside cluster.c -------------------- */
|
||||
void clusterInit(void);
|
||||
void clusterInitLast(void);
|
||||
void clusterInitListeners(void);
|
||||
void clusterCron(void);
|
||||
void clusterBeforeSleep(void);
|
||||
int verifyClusterConfigWithData(void);
|
||||
|
||||
int clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, const char *payload, uint32_t len);
|
||||
|
||||
void clusterUpdateMyselfFlags(void);
|
||||
void clusterUpdateMyselfIp(void);
|
||||
void clusterUpdateMyselfHostname(void);
|
||||
void clusterUpdateMyselfAnnouncedPorts(void);
|
||||
void clusterUpdateMyselfHumanNodename(void);
|
||||
|
||||
void clusterPropagatePublish(robj *channel, robj *message, int sharded);
|
||||
|
||||
unsigned long getClusterConnectionsCount(void);
|
||||
int isClusterHealthy(void);
|
||||
|
||||
sds clusterGenNodesDescription(client *c, int filter, int tls_primary);
|
||||
sds genClusterInfoString(void);
|
||||
/* handle implementation specific debug cluster commands. Return 1 if handled, 0 otherwise. */
|
||||
int handleDebugClusterCommand(client *c);
|
||||
const char **clusterDebugCommandExtendedHelp(void);
|
||||
/* handle implementation specific cluster commands. Return 1 if handled, 0 otherwise. */
|
||||
int clusterCommandSpecial(client *c);
|
||||
const char** clusterCommandExtendedHelp(void);
|
||||
|
||||
int clusterAllowFailoverCmd(client *c);
|
||||
void clusterPromoteSelfToMaster(void);
|
||||
int clusterManualFailoverTimeLimit(void);
|
||||
|
||||
void clusterCommandSlots(client * c);
|
||||
void clusterCommandMyId(client *c);
|
||||
void clusterCommandMyShardId(client *c);
|
||||
|
||||
sds clusterGenNodeDescription(client *c, clusterNode *node, int tls_primary);
|
||||
|
||||
int clusterNodeCoversSlot(clusterNode *n, int slot);
|
||||
int getNodeDefaultClientPort(clusterNode *n);
|
||||
int clusterNodeIsMyself(clusterNode *n);
|
||||
clusterNode *getMyClusterNode(void);
|
||||
char *getMyClusterId(void);
|
||||
int getClusterSize(void);
|
||||
int getMyShardSlotCount(void);
|
||||
int handleDebugClusterCommand(client *c);
|
||||
int clusterNodePending(clusterNode *node);
|
||||
int clusterNodeIsMaster(clusterNode *n);
|
||||
char **getClusterNodesList(size_t *numnodes);
|
||||
int clusterNodeIsMaster(clusterNode *n);
|
||||
char *clusterNodeIp(clusterNode *node);
|
||||
int clusterNodeIsSlave(clusterNode *node);
|
||||
clusterNode *clusterNodeGetSlaveof(clusterNode *node);
|
||||
clusterNode *clusterNodeGetMaster(clusterNode *node);
|
||||
char *clusterNodeGetName(clusterNode *node);
|
||||
int clusterNodeTimedOut(clusterNode *node);
|
||||
int clusterNodeIsFailing(clusterNode *node);
|
||||
int clusterNodeIsNoFailover(clusterNode *node);
|
||||
char *clusterNodeGetShardId(clusterNode *node);
|
||||
int clusterNodeNumSlaves(clusterNode *node);
|
||||
clusterNode *clusterNodeGetSlave(clusterNode *node, int slave_idx);
|
||||
clusterNode *getMigratingSlotDest(int slot);
|
||||
clusterNode *getImportingSlotSource(int slot);
|
||||
clusterNode *getNodeBySlot(int slot);
|
||||
int clusterNodeClientPort(clusterNode *n, int use_tls);
|
||||
char *clusterNodeHostname(clusterNode *node);
|
||||
const char *clusterNodePreferredEndpoint(clusterNode *n);
|
||||
long long clusterNodeReplOffset(clusterNode *node);
|
||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
|
||||
int verifyClusterNodeId(const char *name, int length);
|
||||
clusterNode *clusterLookupNode(const char *name, int length);
|
||||
|
||||
/* functions with shared implementations */
|
||||
clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, uint64_t cmd_flags, int *error_code);
|
||||
int clusterRedirectBlockedClientIfNeeded(client *c);
|
||||
void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_code);
|
||||
void migrateCloseTimedoutSockets(void);
|
||||
int patternHashSlot(char *pattern, int length);
|
||||
int verifyClusterConfigWithData(void);
|
||||
unsigned long getClusterConnectionsCount(void);
|
||||
int clusterSendModuleMessageToTarget(const char *target, uint64_t module_id, uint8_t type, const char *payload, uint32_t len);
|
||||
void clusterPropagatePublish(robj *channel, robj *message, int sharded);
|
||||
unsigned int keyHashSlot(char *key, int keylen);
|
||||
void slotToKeyAddEntry(dictEntry *entry, redisDb *db);
|
||||
void slotToKeyDelEntry(dictEntry *entry, redisDb *db);
|
||||
void slotToKeyReplaceEntry(dict *d, dictEntry *entry);
|
||||
void slotToKeyInit(redisDb *db);
|
||||
void slotToKeyFlush(redisDb *db);
|
||||
void slotToKeyDestroy(redisDb *db);
|
||||
void clusterUpdateMyselfFlags(void);
|
||||
void clusterUpdateMyselfIp(void);
|
||||
void slotToChannelAdd(sds channel);
|
||||
void slotToChannelDel(sds channel);
|
||||
void clusterUpdateMyselfHostname(void);
|
||||
void clusterUpdateMyselfAnnouncedPorts(void);
|
||||
sds clusterGenNodesDescription(client *c, int filter, int tls_primary);
|
||||
sds genClusterInfoString(void);
|
||||
void freeClusterLink(clusterLink *link);
|
||||
void clusterUpdateMyselfHumanNodename(void);
|
||||
int isValidAuxString(char *s, unsigned int length);
|
||||
void migrateCommand(client *c);
|
||||
void clusterCommand(client *c);
|
||||
ConnectionType *connTypeOfCluster(void);
|
||||
int getNodeDefaultClientPort(clusterNode *n);
|
||||
|
||||
void clusterGenNodesSlotsInfo(int filter);
|
||||
void clusterFreeNodesSlotsInfo(clusterNode *n);
|
||||
int clusterNodeSlotInfoCount(clusterNode *n);
|
||||
uint16_t clusterNodeSlotInfoEntry(clusterNode *n, int idx);
|
||||
int clusterNodeHasSlotInfo(clusterNode *n);
|
||||
|
||||
int clusterGetShardCount(void);
|
||||
void *clusterGetShardIterator(void);
|
||||
void *clusterNextShardHandle(void *shard_iterator);
|
||||
void clusterFreeShardIterator(void *shard_iterator);
|
||||
int clusterGetShardNodeCount(void *shard);
|
||||
void *clusterShardHandleGetNodeIterator(void *shard);
|
||||
clusterNode *clusterShardNodeIteratorNext(void *node_iterator);
|
||||
void clusterShardNodeIteratorFree(void *node_iterator);
|
||||
clusterNode *clusterShardNodeFirst(void *shard);
|
||||
|
||||
int clusterNodeTcpPort(clusterNode *node);
|
||||
int clusterNodeTlsPort(clusterNode *node);
|
||||
#endif /* __CLUSTER_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-Present, Redis Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (c) 2024-present, Valkey contributors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under your choice of the Redis Source Available License 2.0
|
||||
* (RSALv2) or the Server Side Public License v1 (SSPLv1).
|
||||
*
|
||||
* Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.
|
||||
*/
|
||||
|
||||
#ifndef CLUSTER_LEGACY_H
|
||||
#define CLUSTER_LEGACY_H
|
||||
|
||||
#define CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */
|
||||
|
||||
/* The following defines are amount of time, sometimes expressed as
|
||||
* multiplicators of the node timeout value (when ending with MULT). */
|
||||
#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */
|
||||
#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */
|
||||
#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */
|
||||
#define CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */
|
||||
#define CLUSTER_SLAVE_MIGRATION_DELAY 5000 /* Delay for slave migration. */
|
||||
|
||||
/* Reasons why a slave is not able to failover. */
|
||||
#define CLUSTER_CANT_FAILOVER_NONE 0
|
||||
#define CLUSTER_CANT_FAILOVER_DATA_AGE 1
|
||||
#define CLUSTER_CANT_FAILOVER_WAITING_DELAY 2
|
||||
#define CLUSTER_CANT_FAILOVER_EXPIRED 3
|
||||
#define CLUSTER_CANT_FAILOVER_WAITING_VOTES 4
|
||||
#define CLUSTER_CANT_FAILOVER_RELOG_PERIOD (10) /* seconds. */
|
||||
|
||||
/* clusterState todo_before_sleep flags. */
|
||||
#define CLUSTER_TODO_HANDLE_FAILOVER (1<<0)
|
||||
#define CLUSTER_TODO_UPDATE_STATE (1<<1)
|
||||
#define CLUSTER_TODO_SAVE_CONFIG (1<<2)
|
||||
#define CLUSTER_TODO_FSYNC_CONFIG (1<<3)
|
||||
#define CLUSTER_TODO_HANDLE_MANUALFAILOVER (1<<4)
|
||||
|
||||
/* clusterLink encapsulates everything needed to talk with a remote node. */
|
||||
typedef struct clusterLink {
|
||||
mstime_t ctime; /* Link creation time */
|
||||
connection *conn; /* Connection to remote node */
|
||||
list *send_msg_queue; /* List of messages to be sent */
|
||||
size_t head_msg_send_offset; /* Number of bytes already sent of message at head of queue */
|
||||
unsigned long long send_msg_queue_mem; /* Memory in bytes used by message queue */
|
||||
char *rcvbuf; /* Packet reception buffer */
|
||||
size_t rcvbuf_len; /* Used size of rcvbuf */
|
||||
size_t rcvbuf_alloc; /* Allocated size of rcvbuf */
|
||||
clusterNode *node; /* Node related to this link. Initialized to NULL when unknown */
|
||||
int inbound; /* 1 if this link is an inbound link accepted from the related node */
|
||||
} clusterLink;
|
||||
|
||||
/* Cluster node flags and macros. */
|
||||
#define CLUSTER_NODE_MASTER 1 /* The node is a master */
|
||||
#define CLUSTER_NODE_SLAVE 2 /* The node is a slave */
|
||||
#define CLUSTER_NODE_PFAIL 4 /* Failure? Need acknowledge */
|
||||
#define CLUSTER_NODE_FAIL 8 /* The node is believed to be malfunctioning */
|
||||
#define CLUSTER_NODE_MYSELF 16 /* This node is myself */
|
||||
#define CLUSTER_NODE_HANDSHAKE 32 /* We have still to exchange the first ping */
|
||||
#define CLUSTER_NODE_NOADDR 64 /* We don't know the address of this node */
|
||||
#define CLUSTER_NODE_MEET 128 /* Send a MEET message to this node */
|
||||
#define CLUSTER_NODE_MIGRATE_TO 256 /* Master eligible for replica migration. */
|
||||
#define CLUSTER_NODE_NOFAILOVER 512 /* Slave will not try to failover. */
|
||||
#define CLUSTER_NODE_EXTENSIONS_SUPPORTED 1024 /* This node supports extensions. */
|
||||
#define CLUSTER_NODE_NULL_NAME "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
|
||||
|
||||
#define nodeIsSlave(n) ((n)->flags & CLUSTER_NODE_SLAVE)
|
||||
#define nodeInHandshake(n) ((n)->flags & CLUSTER_NODE_HANDSHAKE)
|
||||
#define nodeHasAddr(n) (!((n)->flags & CLUSTER_NODE_NOADDR))
|
||||
#define nodeTimedOut(n) ((n)->flags & CLUSTER_NODE_PFAIL)
|
||||
#define nodeFailed(n) ((n)->flags & CLUSTER_NODE_FAIL)
|
||||
#define nodeCantFailover(n) ((n)->flags & CLUSTER_NODE_NOFAILOVER)
|
||||
#define nodeSupportsExtensions(n) ((n)->flags & CLUSTER_NODE_EXTENSIONS_SUPPORTED)
|
||||
|
||||
/* This structure represent elements of node->fail_reports. */
|
||||
typedef struct clusterNodeFailReport {
|
||||
clusterNode *node; /* Node reporting the failure condition. */
|
||||
mstime_t time; /* Time of the last report from this node. */
|
||||
} clusterNodeFailReport;
|
||||
|
||||
/* Redis cluster messages header */
|
||||
|
||||
/* Message types.
|
||||
*
|
||||
* Note that the PING, PONG and MEET messages are actually the same exact
|
||||
* kind of packet. PONG is the reply to ping, in the exact format as a PING,
|
||||
* while MEET is a special PING that forces the receiver to add the sender
|
||||
* as a node (if it is not already in the list). */
|
||||
#define CLUSTERMSG_TYPE_PING 0 /* Ping */
|
||||
#define CLUSTERMSG_TYPE_PONG 1 /* Pong (reply to Ping) */
|
||||
#define CLUSTERMSG_TYPE_MEET 2 /* Meet "let's join" message */
|
||||
#define CLUSTERMSG_TYPE_FAIL 3 /* Mark node xxx as failing */
|
||||
#define CLUSTERMSG_TYPE_PUBLISH 4 /* Pub/Sub Publish propagation */
|
||||
#define CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 5 /* May I failover? */
|
||||
#define CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 6 /* Yes, you have my vote */
|
||||
#define CLUSTERMSG_TYPE_UPDATE 7 /* Another node slots configuration */
|
||||
#define CLUSTERMSG_TYPE_MFSTART 8 /* Pause clients for manual failover */
|
||||
#define CLUSTERMSG_TYPE_MODULE 9 /* Module cluster API message. */
|
||||
#define CLUSTERMSG_TYPE_PUBLISHSHARD 10 /* Pub/Sub Publish shard propagation */
|
||||
#define CLUSTERMSG_TYPE_COUNT 11 /* Total number of message types. */
|
||||
|
||||
/* Initially we don't know our "name", but we'll find it once we connect
|
||||
* to the first node, using the getsockname() function. Then we'll use this
|
||||
* address for all the next messages. */
|
||||
typedef struct {
|
||||
char nodename[CLUSTER_NAMELEN];
|
||||
uint32_t ping_sent;
|
||||
uint32_t pong_received;
|
||||
char ip[NET_IP_STR_LEN]; /* IP address last time it was seen */
|
||||
uint16_t port; /* primary port last time it was seen */
|
||||
uint16_t cport; /* cluster port last time it was seen */
|
||||
uint16_t flags; /* node->flags copy */
|
||||
uint16_t pport; /* secondary port last time it was seen */
|
||||
uint16_t notused1;
|
||||
} clusterMsgDataGossip;
|
||||
|
||||
typedef struct {
|
||||
char nodename[CLUSTER_NAMELEN];
|
||||
} clusterMsgDataFail;
|
||||
|
||||
typedef struct {
|
||||
uint32_t channel_len;
|
||||
uint32_t message_len;
|
||||
unsigned char bulk_data[8]; /* 8 bytes just as placeholder. */
|
||||
} clusterMsgDataPublish;
|
||||
|
||||
typedef struct {
|
||||
uint64_t configEpoch; /* Config epoch of the specified instance. */
|
||||
char nodename[CLUSTER_NAMELEN]; /* Name of the slots owner. */
|
||||
unsigned char slots[CLUSTER_SLOTS/8]; /* Slots bitmap. */
|
||||
} clusterMsgDataUpdate;
|
||||
|
||||
typedef struct {
|
||||
uint64_t module_id; /* ID of the sender module. */
|
||||
uint32_t len; /* ID of the sender module. */
|
||||
uint8_t type; /* Type from 0 to 255. */
|
||||
unsigned char bulk_data[3]; /* 3 bytes just as placeholder. */
|
||||
} clusterMsgModule;
|
||||
|
||||
/* The cluster supports optional extension messages that can be sent
|
||||
* along with ping/pong/meet messages to give additional info in a
|
||||
* consistent manner. */
|
||||
typedef enum {
|
||||
CLUSTERMSG_EXT_TYPE_HOSTNAME,
|
||||
CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME,
|
||||
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE,
|
||||
CLUSTERMSG_EXT_TYPE_SHARDID,
|
||||
} clusterMsgPingtypes;
|
||||
|
||||
/* Helper function for making sure extensions are eight byte aligned. */
|
||||
#define EIGHT_BYTE_ALIGN(size) ((((size) + 7) / 8) * 8)
|
||||
|
||||
typedef struct {
|
||||
char hostname[1]; /* The announced hostname, ends with \0. */
|
||||
} clusterMsgPingExtHostname;
|
||||
|
||||
typedef struct {
|
||||
char human_nodename[1]; /* The announced nodename, ends with \0. */
|
||||
} clusterMsgPingExtHumanNodename;
|
||||
|
||||
typedef struct {
|
||||
char name[CLUSTER_NAMELEN]; /* Node name. */
|
||||
uint64_t ttl; /* Remaining time to blacklist the node, in seconds. */
|
||||
} clusterMsgPingExtForgottenNode;
|
||||
|
||||
static_assert(sizeof(clusterMsgPingExtForgottenNode) % 8 == 0, "");
|
||||
|
||||
typedef struct {
|
||||
char shard_id[CLUSTER_NAMELEN]; /* The shard_id, 40 bytes fixed. */
|
||||
} clusterMsgPingExtShardId;
|
||||
|
||||
typedef struct {
|
||||
uint32_t length; /* Total length of this extension message (including this header) */
|
||||
uint16_t type; /* Type of this extension message (see clusterMsgPingExtTypes) */
|
||||
uint16_t unused; /* 16 bits of padding to make this structure 8 byte aligned. */
|
||||
union {
|
||||
clusterMsgPingExtHostname hostname;
|
||||
clusterMsgPingExtHumanNodename human_nodename;
|
||||
clusterMsgPingExtForgottenNode forgotten_node;
|
||||
clusterMsgPingExtShardId shard_id;
|
||||
} ext[]; /* Actual extension information, formatted so that the data is 8
|
||||
* byte aligned, regardless of its content. */
|
||||
} clusterMsgPingExt;
|
||||
|
||||
union clusterMsgData {
|
||||
/* PING, MEET and PONG */
|
||||
struct {
|
||||
/* Array of N clusterMsgDataGossip structures */
|
||||
clusterMsgDataGossip gossip[1];
|
||||
/* Extension data that can optionally be sent for ping/meet/pong
|
||||
* messages. We can't explicitly define them here though, since
|
||||
* the gossip array isn't the real length of the gossip data. */
|
||||
} ping;
|
||||
|
||||
/* FAIL */
|
||||
struct {
|
||||
clusterMsgDataFail about;
|
||||
} fail;
|
||||
|
||||
/* PUBLISH */
|
||||
struct {
|
||||
clusterMsgDataPublish msg;
|
||||
} publish;
|
||||
|
||||
/* UPDATE */
|
||||
struct {
|
||||
clusterMsgDataUpdate nodecfg;
|
||||
} update;
|
||||
|
||||
/* MODULE */
|
||||
struct {
|
||||
clusterMsgModule msg;
|
||||
} module;
|
||||
};
|
||||
|
||||
#define CLUSTER_PROTO_VER 1 /* Cluster bus protocol version. */
|
||||
|
||||
typedef struct {
|
||||
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
|
||||
uint32_t totlen; /* Total length of this message */
|
||||
uint16_t ver; /* Protocol version, currently set to 1. */
|
||||
uint16_t port; /* Primary port number (TCP or TLS). */
|
||||
uint16_t type; /* Message type */
|
||||
uint16_t count; /* Only used for some kind of messages. */
|
||||
uint64_t currentEpoch; /* The epoch accordingly to the sending node. */
|
||||
uint64_t configEpoch; /* The config epoch if it's a master, or the last
|
||||
epoch advertised by its master if it is a
|
||||
slave. */
|
||||
uint64_t offset; /* Master replication offset if node is a master or
|
||||
processed replication offset if node is a slave. */
|
||||
char sender[CLUSTER_NAMELEN]; /* Name of the sender node */
|
||||
unsigned char myslots[CLUSTER_SLOTS/8];
|
||||
char slaveof[CLUSTER_NAMELEN];
|
||||
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
|
||||
uint16_t extensions; /* Number of extensions sent along with this packet. */
|
||||
char notused1[30]; /* 30 bytes reserved for future usage. */
|
||||
uint16_t pport; /* Secondary port number: if primary port is TCP port, this is
|
||||
TLS port, and if primary port is TLS port, this is TCP port.*/
|
||||
uint16_t cport; /* Sender TCP cluster bus port */
|
||||
uint16_t flags; /* Sender node flags */
|
||||
unsigned char state; /* Cluster state from the POV of the sender */
|
||||
unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */
|
||||
union clusterMsgData data;
|
||||
} clusterMsg;
|
||||
|
||||
/* clusterMsg defines the gossip wire protocol exchanged among Redis cluster
|
||||
* members, which can be running different versions of redis-server bits,
|
||||
* especially during cluster rolling upgrades.
|
||||
*
|
||||
* Therefore, fields in this struct should remain at the same offset from
|
||||
* release to release. The static asserts below ensures that incompatible
|
||||
* changes in clusterMsg be caught at compile time.
|
||||
*/
|
||||
|
||||
static_assert(offsetof(clusterMsg, sig) == 0, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, totlen) == 4, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, ver) == 8, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, port) == 10, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, type) == 12, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, count) == 14, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, currentEpoch) == 16, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, configEpoch) == 24, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, offset) == 32, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, sender) == 40, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, myslots) == 80, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, slaveof) == 2128, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, myip) == 2168, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, extensions) == 2214, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, notused1) == 2216, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, pport) == 2246, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, cport) == 2248, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, flags) == 2250, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, state) == 2252, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, mflags) == 2253, "unexpected field offset");
|
||||
static_assert(offsetof(clusterMsg, data) == 2256, "unexpected field offset");
|
||||
|
||||
#define CLUSTERMSG_MIN_LEN (sizeof(clusterMsg)-sizeof(union clusterMsgData))
|
||||
|
||||
/* Message flags better specify the packet content or are used to
|
||||
* provide some information about the node state. */
|
||||
#define CLUSTERMSG_FLAG0_PAUSED (1<<0) /* Master paused for manual failover. */
|
||||
#define CLUSTERMSG_FLAG0_FORCEACK (1<<1) /* Give ACK to AUTH_REQUEST even if
|
||||
master is up. */
|
||||
#define CLUSTERMSG_FLAG0_EXT_DATA (1<<2) /* Message contains extension data */
|
||||
|
||||
struct _clusterNode {
|
||||
mstime_t ctime; /* Node object creation time. */
|
||||
char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */
|
||||
char shard_id[CLUSTER_NAMELEN]; /* shard id, hex string, sha1-size */
|
||||
int flags; /* CLUSTER_NODE_... */
|
||||
uint64_t configEpoch; /* Last configEpoch observed for this node */
|
||||
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
|
||||
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 numslots; /* Number of slots handled by this node */
|
||||
int numslaves; /* Number of slave nodes, if this is a master */
|
||||
clusterNode **slaves; /* pointers to slave nodes */
|
||||
clusterNode *slaveof; /* pointer to the master node. Note that it
|
||||
may be NULL even if the node is a slave
|
||||
if we don't have the master node in our
|
||||
tables. */
|
||||
unsigned long long last_in_ping_gossip; /* The number of the last carried in the ping gossip section */
|
||||
mstime_t ping_sent; /* Unix time we sent latest ping */
|
||||
mstime_t pong_received; /* Unix time we received the pong */
|
||||
mstime_t data_received; /* Unix time we received any data */
|
||||
mstime_t fail_time; /* Unix time when FAIL flag was set */
|
||||
mstime_t voted_time; /* Last time we voted for a slave of this master */
|
||||
mstime_t repl_offset_time; /* Unix time we received offset for this node */
|
||||
mstime_t orphaned_time; /* Starting time of orphaned master condition */
|
||||
long long repl_offset; /* Last known repl offset for this node. */
|
||||
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
|
||||
sds hostname; /* The known hostname for this node */
|
||||
sds human_nodename; /* The known human readable nodename for this node */
|
||||
int tcp_port; /* Latest known clients TCP port. */
|
||||
int tls_port; /* Latest known clients TLS port */
|
||||
int cport; /* Latest known cluster port of this node. */
|
||||
clusterLink *link; /* TCP/IP link established toward this node */
|
||||
clusterLink *inbound_link; /* TCP/IP link accepted from this node */
|
||||
list *fail_reports; /* List of nodes signaling this as failing */
|
||||
};
|
||||
|
||||
struct clusterState {
|
||||
clusterNode *myself; /* This node */
|
||||
uint64_t currentEpoch;
|
||||
int state; /* CLUSTER_OK, CLUSTER_FAIL, ... */
|
||||
int size; /* Num of master nodes with at least one slot */
|
||||
dict *nodes; /* Hash table of name -> clusterNode structures */
|
||||
dict *shards; /* Hash table of shard_id -> list (of nodes) structures */
|
||||
dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */
|
||||
clusterNode *migrating_slots_to[CLUSTER_SLOTS];
|
||||
clusterNode *importing_slots_from[CLUSTER_SLOTS];
|
||||
clusterNode *slots[CLUSTER_SLOTS];
|
||||
/* The following fields are used to take the slave state on elections. */
|
||||
mstime_t failover_auth_time; /* Time of previous or next election. */
|
||||
int failover_auth_count; /* Number of votes received so far. */
|
||||
int failover_auth_sent; /* True if we already asked for votes. */
|
||||
int failover_auth_rank; /* This slave rank for current auth request. */
|
||||
uint64_t failover_auth_epoch; /* Epoch of the current election. */
|
||||
int cant_failover_reason; /* Why a slave is currently not able to
|
||||
failover. See the CANT_FAILOVER_* macros. */
|
||||
/* Manual failover state in common. */
|
||||
mstime_t mf_end; /* Manual failover time limit (ms unixtime).
|
||||
It is zero if there is no MF in progress. */
|
||||
/* Manual failover state of master. */
|
||||
clusterNode *mf_slave; /* Slave performing the manual failover. */
|
||||
/* Manual failover state of slave. */
|
||||
long long mf_master_offset; /* Master offset the slave needs to start MF
|
||||
or -1 if still not received. */
|
||||
int mf_can_start; /* If non-zero signal that the manual failover
|
||||
can start requesting masters vote. */
|
||||
/* The following fields are used by masters to take state on elections. */
|
||||
uint64_t lastVoteEpoch; /* Epoch of the last vote granted. */
|
||||
int todo_before_sleep; /* Things to do in clusterBeforeSleep(). */
|
||||
/* Stats */
|
||||
/* Messages received and sent by type. */
|
||||
long long stats_bus_messages_sent[CLUSTERMSG_TYPE_COUNT];
|
||||
long long stats_bus_messages_received[CLUSTERMSG_TYPE_COUNT];
|
||||
long long stats_pfail_nodes; /* Number of nodes in PFAIL status,
|
||||
excluding nodes without address. */
|
||||
unsigned long long stat_cluster_links_buffer_limit_exceeded; /* Total number of cluster links freed due to exceeding buffer limit */
|
||||
|
||||
/* Bit map for slots that are no longer claimed by the owner in cluster PING
|
||||
* messages. During slot migration, the owner will stop claiming the slot after
|
||||
* the ownership transfer. Set the bit corresponding to the slot when a node
|
||||
* stops claiming the slot. This prevents spreading incorrect information (that
|
||||
* source still owns the slot) using UPDATE messages. */
|
||||
unsigned char owner_not_claiming_slot[CLUSTER_SLOTS / 8];
|
||||
};
|
||||
|
||||
|
||||
#endif //CLUSTER_LEGACY_H
|
||||
+36
-434
@@ -1177,7 +1177,6 @@ commandHistory CLIENT_KILL_History[] = {
|
||||
{"3.2.0","Added `master` type in for `TYPE` option."},
|
||||
{"5.0.0","Replaced `slave` `TYPE` with `replica`. `slave` still supported for backward compatibility."},
|
||||
{"6.2.0","`LADDR` option."},
|
||||
{"7.4.0","`MAXAGE` option."},
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1214,13 +1213,12 @@ struct COMMAND_ARG CLIENT_KILL_filter_new_format_Subargs[] = {
|
||||
{MAKE_ARG("addr",ARG_TYPE_STRING,-1,"ADDR",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
|
||||
{MAKE_ARG("laddr",ARG_TYPE_STRING,-1,"LADDR",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
|
||||
{MAKE_ARG("skipme",ARG_TYPE_ONEOF,-1,"SKIPME",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLIENT_KILL_filter_new_format_skipme_Subargs},
|
||||
{MAKE_ARG("maxage",ARG_TYPE_INTEGER,-1,"MAXAGE",NULL,"7.4.0",CMD_ARG_OPTIONAL,0,NULL)},
|
||||
};
|
||||
|
||||
/* CLIENT KILL filter argument table */
|
||||
struct COMMAND_ARG CLIENT_KILL_filter_Subargs[] = {
|
||||
{MAKE_ARG("old-format",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,"2.8.12"),.display_text="ip:port"},
|
||||
{MAKE_ARG("new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,7,NULL),.subargs=CLIENT_KILL_filter_new_format_Subargs},
|
||||
{MAKE_ARG("new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,6,NULL),.subargs=CLIENT_KILL_filter_new_format_Subargs},
|
||||
};
|
||||
|
||||
/* CLIENT KILL argument table */
|
||||
@@ -1239,9 +1237,6 @@ commandHistory CLIENT_LIST_History[] = {
|
||||
{"6.2.0","Added `argv-mem`, `tot-mem`, `laddr` and `redir` fields and the optional `ID` filter."},
|
||||
{"7.0.0","Added `resp`, `multi-mem`, `rbs` and `rbp` fields."},
|
||||
{"7.0.3","Added `ssub` field."},
|
||||
{"7.2.0","Added `lib-name` and `lib-ver` fields."},
|
||||
{"7.4.0","Added `watch` field."},
|
||||
{"8.0.0","Added `io-thread` field."},
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1396,10 +1391,7 @@ struct COMMAND_ARG CLIENT_REPLY_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CLIENT SETINFO tips */
|
||||
const char *CLIENT_SETINFO_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define CLIENT_SETINFO_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -1427,10 +1419,7 @@ struct COMMAND_ARG CLIENT_SETINFO_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CLIENT SETNAME tips */
|
||||
const char *CLIENT_SETNAME_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define CLIENT_SETNAME_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -1548,14 +1537,14 @@ struct COMMAND_STRUCT CLIENT_Subcommands[] = {
|
||||
{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,0,CLIENT_HELP_Tips,0,clientCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_HELP_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("id","Returns the unique client ID of the connection.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_ID_History,0,CLIENT_ID_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_ID_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("info","Returns information about the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,0,CLIENT_INFO_Tips,1,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_INFO_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("kill","Terminates open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,6,CLIENT_KILL_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_KILL_Keyspecs,0,NULL,1),.args=CLIENT_KILL_Args},
|
||||
{MAKE_CMD("list","Lists open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,9,CLIENT_LIST_Tips,1,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_LIST_Keyspecs,0,NULL,2),.args=CLIENT_LIST_Args},
|
||||
{MAKE_CMD("kill","Terminates open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,5,CLIENT_KILL_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_KILL_Keyspecs,0,NULL,1),.args=CLIENT_KILL_Args},
|
||||
{MAKE_CMD("list","Lists open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,6,CLIENT_LIST_Tips,1,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_LIST_Keyspecs,0,NULL,2),.args=CLIENT_LIST_Args},
|
||||
{MAKE_CMD("no-evict","Sets the client eviction mode of the connection.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,0,CLIENT_NO_EVICT_Tips,0,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_NO_EVICT_Keyspecs,0,NULL,1),.args=CLIENT_NO_EVICT_Args},
|
||||
{MAKE_CMD("no-touch","Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,0,CLIENT_NO_TOUCH_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,CLIENT_NO_TOUCH_Keyspecs,0,NULL,1),.args=CLIENT_NO_TOUCH_Args},
|
||||
{MAKE_CMD("pause","Suspends commands processing.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,1,CLIENT_PAUSE_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_PAUSE_Keyspecs,0,NULL,2),.args=CLIENT_PAUSE_Args},
|
||||
{MAKE_CMD("reply","Instructs the server whether to reply to commands.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,0,CLIENT_REPLY_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_REPLY_Keyspecs,0,NULL,1),.args=CLIENT_REPLY_Args},
|
||||
{MAKE_CMD("setinfo","Sets information specific to the client or connection.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,0,CLIENT_SETINFO_Tips,2,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETINFO_Keyspecs,0,NULL,1),.args=CLIENT_SETINFO_Args},
|
||||
{MAKE_CMD("setname","Sets the connection name.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,0,CLIENT_SETNAME_Tips,2,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETNAME_Keyspecs,0,NULL,1),.args=CLIENT_SETNAME_Args},
|
||||
{MAKE_CMD("setinfo","Sets information specific to the client or connection.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,0,CLIENT_SETINFO_Tips,0,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETINFO_Keyspecs,0,NULL,1),.args=CLIENT_SETINFO_Args},
|
||||
{MAKE_CMD("setname","Sets the connection name.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,0,CLIENT_SETNAME_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETNAME_Keyspecs,0,NULL,1),.args=CLIENT_SETNAME_Args},
|
||||
{MAKE_CMD("tracking","Controls server-assisted client-side caching for the connection.","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,0,CLIENT_TRACKING_Tips,0,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKING_Keyspecs,0,NULL,7),.args=CLIENT_TRACKING_Args},
|
||||
{MAKE_CMD("trackinginfo","Returns information about server-assisted client-side caching for the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,0,CLIENT_TRACKINGINFO_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKINGINFO_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("unblock","Unblocks a client blocked by a blocking command from a different connection.","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,0,CLIENT_UNBLOCK_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_UNBLOCK_Keyspecs,0,NULL,2),.args=CLIENT_UNBLOCK_Args},
|
||||
@@ -2903,7 +2892,6 @@ struct COMMAND_ARG GEORADIUS_Args[] = {
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* GEORADIUSBYMEMBER history */
|
||||
commandHistory GEORADIUSBYMEMBER_History[] = {
|
||||
{"6.2.0","Added the `ANY` option for `COUNT`."},
|
||||
{"7.0.0","Added support for uppercase unit names."},
|
||||
};
|
||||
#endif
|
||||
@@ -2964,10 +2952,7 @@ struct COMMAND_ARG GEORADIUSBYMEMBER_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* GEORADIUSBYMEMBER_RO history */
|
||||
commandHistory GEORADIUSBYMEMBER_RO_History[] = {
|
||||
{"6.2.0","Added the `ANY` option for `COUNT`."},
|
||||
{"7.0.0","Added support for uppercase unit names."},
|
||||
};
|
||||
#define GEORADIUSBYMEMBER_RO_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
@@ -3021,7 +3006,6 @@ struct COMMAND_ARG GEORADIUSBYMEMBER_RO_Args[] = {
|
||||
/* GEORADIUS_RO history */
|
||||
commandHistory GEORADIUS_RO_History[] = {
|
||||
{"6.2.0","Added the `ANY` option for `COUNT`."},
|
||||
{"7.0.0","Added support for uppercase unit names."},
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -3306,119 +3290,6 @@ struct COMMAND_ARG HEXISTS_Args[] = {
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/********** HEXPIRE ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HEXPIRE history */
|
||||
#define HEXPIRE_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HEXPIRE tips */
|
||||
#define HEXPIRE_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HEXPIRE key specs */
|
||||
keySpec HEXPIRE_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HEXPIRE condition argument table */
|
||||
struct COMMAND_ARG HEXPIRE_condition_Subargs[] = {
|
||||
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HEXPIRE fields argument table */
|
||||
struct COMMAND_ARG HEXPIRE_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HEXPIRE argument table */
|
||||
struct COMMAND_ARG HEXPIRE_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=HEXPIRE_condition_Subargs},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HEXPIRE_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HEXPIREAT ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HEXPIREAT history */
|
||||
#define HEXPIREAT_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HEXPIREAT tips */
|
||||
#define HEXPIREAT_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HEXPIREAT key specs */
|
||||
keySpec HEXPIREAT_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HEXPIREAT condition argument table */
|
||||
struct COMMAND_ARG HEXPIREAT_condition_Subargs[] = {
|
||||
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HEXPIREAT fields argument table */
|
||||
struct COMMAND_ARG HEXPIREAT_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HEXPIREAT argument table */
|
||||
struct COMMAND_ARG HEXPIREAT_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=HEXPIREAT_condition_Subargs},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HEXPIREAT_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HEXPIRETIME ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HEXPIRETIME history */
|
||||
#define HEXPIRETIME_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HEXPIRETIME tips */
|
||||
#define HEXPIRETIME_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HEXPIRETIME key specs */
|
||||
keySpec HEXPIRETIME_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HEXPIRETIME fields argument table */
|
||||
struct COMMAND_ARG HEXPIRETIME_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HEXPIRETIME argument table */
|
||||
struct COMMAND_ARG HEXPIRETIME_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HEXPIRETIME_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HGET ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
@@ -3628,183 +3499,6 @@ struct COMMAND_ARG HMSET_Args[] = {
|
||||
{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=HMSET_data_Subargs},
|
||||
};
|
||||
|
||||
/********** HPERSIST ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HPERSIST history */
|
||||
#define HPERSIST_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HPERSIST tips */
|
||||
#define HPERSIST_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HPERSIST key specs */
|
||||
keySpec HPERSIST_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HPERSIST fields argument table */
|
||||
struct COMMAND_ARG HPERSIST_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPERSIST argument table */
|
||||
struct COMMAND_ARG HPERSIST_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HPERSIST_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HPEXPIRE ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HPEXPIRE history */
|
||||
#define HPEXPIRE_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HPEXPIRE tips */
|
||||
#define HPEXPIRE_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HPEXPIRE key specs */
|
||||
keySpec HPEXPIRE_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HPEXPIRE condition argument table */
|
||||
struct COMMAND_ARG HPEXPIRE_condition_Subargs[] = {
|
||||
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPEXPIRE fields argument table */
|
||||
struct COMMAND_ARG HPEXPIRE_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPEXPIRE argument table */
|
||||
struct COMMAND_ARG HPEXPIRE_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=HPEXPIRE_condition_Subargs},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HPEXPIRE_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HPEXPIREAT ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HPEXPIREAT history */
|
||||
#define HPEXPIREAT_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HPEXPIREAT tips */
|
||||
#define HPEXPIREAT_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HPEXPIREAT key specs */
|
||||
keySpec HPEXPIREAT_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HPEXPIREAT condition argument table */
|
||||
struct COMMAND_ARG HPEXPIREAT_condition_Subargs[] = {
|
||||
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPEXPIREAT fields argument table */
|
||||
struct COMMAND_ARG HPEXPIREAT_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPEXPIREAT argument table */
|
||||
struct COMMAND_ARG HPEXPIREAT_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=HPEXPIREAT_condition_Subargs},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HPEXPIREAT_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HPEXPIRETIME ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HPEXPIRETIME history */
|
||||
#define HPEXPIRETIME_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HPEXPIRETIME tips */
|
||||
#define HPEXPIRETIME_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HPEXPIRETIME key specs */
|
||||
keySpec HPEXPIRETIME_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HPEXPIRETIME fields argument table */
|
||||
struct COMMAND_ARG HPEXPIRETIME_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPEXPIRETIME argument table */
|
||||
struct COMMAND_ARG HPEXPIRETIME_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HPEXPIRETIME_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HPTTL ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HPTTL history */
|
||||
#define HPTTL_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HPTTL tips */
|
||||
const char *HPTTL_Tips[] = {
|
||||
"nondeterministic_output",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HPTTL key specs */
|
||||
keySpec HPTTL_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HPTTL fields argument table */
|
||||
struct COMMAND_ARG HPTTL_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HPTTL argument table */
|
||||
struct COMMAND_ARG HPTTL_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HPTTL_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HRANDFIELD ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
@@ -3865,7 +3559,6 @@ struct COMMAND_ARG HSCAN_Args[] = {
|
||||
{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
|
||||
{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
|
||||
{MAKE_ARG("novalues",ARG_TYPE_PURE_TOKEN,-1,"NOVALUES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
|
||||
};
|
||||
|
||||
/********** HSET ********************/
|
||||
@@ -3952,39 +3645,6 @@ struct COMMAND_ARG HSTRLEN_Args[] = {
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/********** HTTL ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* HTTL history */
|
||||
#define HTTL_History NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* HTTL tips */
|
||||
const char *HTTL_Tips[] = {
|
||||
"nondeterministic_output",
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* HTTL key specs */
|
||||
keySpec HTTL_Keyspecs[1] = {
|
||||
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
/* HTTL fields argument table */
|
||||
struct COMMAND_ARG HTTL_fields_Subargs[] = {
|
||||
{MAKE_ARG("numfields",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
|
||||
};
|
||||
|
||||
/* HTTL argument table */
|
||||
struct COMMAND_ARG HTTL_Args[] = {
|
||||
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HTTL_fields_Subargs},
|
||||
};
|
||||
|
||||
/********** HVALS ********************/
|
||||
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
@@ -6226,10 +5886,7 @@ struct COMMAND_ARG ACL_CAT_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL DELUSER tips */
|
||||
const char *ACL_DELUSER_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define ACL_DELUSER_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6403,10 +6060,7 @@ struct COMMAND_ARG ACL_LOG_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL SAVE tips */
|
||||
const char *ACL_SAVE_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define ACL_SAVE_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6426,10 +6080,7 @@ commandHistory ACL_SETUSER_History[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* ACL SETUSER tips */
|
||||
const char *ACL_SETUSER_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define ACL_SETUSER_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6480,7 +6131,7 @@ struct COMMAND_ARG ACL_SETUSER_Args[] = {
|
||||
/* ACL command table */
|
||||
struct COMMAND_STRUCT ACL_Subcommands[] = {
|
||||
{MAKE_CMD("cat","Lists the ACL categories, or the commands inside a category.","O(1) since the categories and commands are a fixed set.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_CAT_History,0,ACL_CAT_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_CAT_Keyspecs,0,NULL,1),.args=ACL_CAT_Args},
|
||||
{MAKE_CMD("deluser","Deletes ACL users, and terminates their connections.","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DELUSER_History,0,ACL_DELUSER_Tips,2,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DELUSER_Keyspecs,0,NULL,1),.args=ACL_DELUSER_Args},
|
||||
{MAKE_CMD("deluser","Deletes ACL users, and terminates their connections.","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DELUSER_History,0,ACL_DELUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DELUSER_Keyspecs,0,NULL,1),.args=ACL_DELUSER_Args},
|
||||
{MAKE_CMD("dryrun","Simulates the execution of a command by a user, without executing the command.","O(1).","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DRYRUN_History,0,ACL_DRYRUN_Tips,0,aclCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DRYRUN_Keyspecs,0,NULL,3),.args=ACL_DRYRUN_Args},
|
||||
{MAKE_CMD("genpass","Generates a pseudorandom, secure password that can be used to identify ACL users.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GENPASS_History,0,ACL_GENPASS_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GENPASS_Keyspecs,0,NULL,1),.args=ACL_GENPASS_Args},
|
||||
{MAKE_CMD("getuser","Lists the ACL rules of a user.","O(N). Where N is the number of password, command and pattern rules that the user has.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GETUSER_History,2,ACL_GETUSER_Tips,0,aclCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GETUSER_Keyspecs,0,NULL,1),.args=ACL_GETUSER_Args},
|
||||
@@ -6488,8 +6139,8 @@ struct COMMAND_STRUCT ACL_Subcommands[] = {
|
||||
{MAKE_CMD("list","Dumps the effective rules in ACL file format.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LIST_History,0,ACL_LIST_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LIST_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("load","Reloads the rules from the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOAD_History,0,ACL_LOAD_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOAD_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("log","Lists recent security events generated due to ACL rules.","O(N) with N being the number of entries shown.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOG_History,1,ACL_LOG_Tips,0,aclCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOG_Keyspecs,0,NULL,1),.args=ACL_LOG_Args},
|
||||
{MAKE_CMD("save","Saves the effective ACL rules in the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SAVE_History,0,ACL_SAVE_Tips,2,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("setuser","Creates and modifies an ACL user and its rules.","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SETUSER_History,2,ACL_SETUSER_Tips,2,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SETUSER_Keyspecs,0,NULL,2),.args=ACL_SETUSER_Args},
|
||||
{MAKE_CMD("save","Saves the effective ACL rules in the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SAVE_History,0,ACL_SAVE_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("setuser","Creates and modifies an ACL user and its rules.","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SETUSER_History,2,ACL_SETUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SETUSER_Keyspecs,0,NULL,2),.args=ACL_SETUSER_Args},
|
||||
{MAKE_CMD("users","Lists all ACL users.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_USERS_History,0,ACL_USERS_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_USERS_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("whoami","Returns the authenticated username of the current connection.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_WHOAMI_History,0,ACL_WHOAMI_Tips,0,aclCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_WHOAMI_Keyspecs,0,NULL,0)},
|
||||
{0}
|
||||
@@ -6795,10 +6446,7 @@ struct COMMAND_ARG CONFIG_GET_Args[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CONFIG RESETSTAT tips */
|
||||
const char *CONFIG_RESETSTAT_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define CONFIG_RESETSTAT_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6815,10 +6463,7 @@ const char *CONFIG_RESETSTAT_Tips[] = {
|
||||
|
||||
#ifndef SKIP_CMD_TIPS_TABLE
|
||||
/* CONFIG REWRITE tips */
|
||||
const char *CONFIG_REWRITE_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#define CONFIG_REWRITE_Tips NULL
|
||||
#endif
|
||||
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
@@ -6863,8 +6508,8 @@ struct COMMAND_ARG CONFIG_SET_Args[] = {
|
||||
struct COMMAND_STRUCT CONFIG_Subcommands[] = {
|
||||
{MAKE_CMD("get","Returns the effective values of configuration parameters.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_GET_History,1,CONFIG_GET_Tips,0,configGetCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_GET_Keyspecs,0,NULL,1),.args=CONFIG_GET_Args},
|
||||
{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_HELP_History,0,CONFIG_HELP_Tips,0,configHelpCommand,2,CMD_LOADING|CMD_STALE,0,CONFIG_HELP_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("resetstat","Resets the server's statistics.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,0,CONFIG_RESETSTAT_Tips,2,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_RESETSTAT_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("rewrite","Persists the effective configuration to file.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,0,CONFIG_REWRITE_Tips,2,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_REWRITE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("resetstat","Resets the server's statistics.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,0,CONFIG_RESETSTAT_Tips,0,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_RESETSTAT_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("rewrite","Persists the effective configuration to file.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,0,CONFIG_REWRITE_Tips,0,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_REWRITE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("set","Sets configuration parameters in-flight.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_SET_History,1,CONFIG_SET_Tips,2,configSetCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_SET_Keyspecs,0,NULL,1),.args=CONFIG_SET_Args},
|
||||
{0}
|
||||
};
|
||||
@@ -7217,7 +6862,7 @@ const char *LATENCY_LATEST_Tips[] = {
|
||||
/* LATENCY RESET tips */
|
||||
const char *LATENCY_RESET_Tips[] = {
|
||||
"request_policy:all_nodes",
|
||||
"response_policy:agg_sum",
|
||||
"response_policy:all_succeeded",
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -7647,27 +7292,10 @@ struct COMMAND_ARG PSYNC_Args[] = {
|
||||
#define REPLICAOF_Keyspecs NULL
|
||||
#endif
|
||||
|
||||
/* REPLICAOF args host_port argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_host_port_Subargs[] = {
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* REPLICAOF args no_one argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_no_one_Subargs[] = {
|
||||
{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("one",ARG_TYPE_PURE_TOKEN,-1,"ONE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* REPLICAOF args argument table */
|
||||
struct COMMAND_ARG REPLICAOF_args_Subargs[] = {
|
||||
{MAKE_ARG("host-port",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_host_port_Subargs},
|
||||
{MAKE_ARG("no-one",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_no_one_Subargs},
|
||||
};
|
||||
|
||||
/* REPLICAOF argument table */
|
||||
struct COMMAND_ARG REPLICAOF_Args[] = {
|
||||
{MAKE_ARG("args",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=REPLICAOF_args_Subargs},
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/********** RESTORE_ASKING ********************/
|
||||
@@ -7788,27 +7416,10 @@ struct COMMAND_ARG SHUTDOWN_Args[] = {
|
||||
#define SLAVEOF_Keyspecs NULL
|
||||
#endif
|
||||
|
||||
/* SLAVEOF args host_port argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_host_port_Subargs[] = {
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* SLAVEOF args no_one argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_no_one_Subargs[] = {
|
||||
{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("one",ARG_TYPE_PURE_TOKEN,-1,"ONE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/* SLAVEOF args argument table */
|
||||
struct COMMAND_ARG SLAVEOF_args_Subargs[] = {
|
||||
{MAKE_ARG("host-port",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_host_port_Subargs},
|
||||
{MAKE_ARG("no-one",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_no_one_Subargs},
|
||||
};
|
||||
|
||||
/* SLAVEOF argument table */
|
||||
struct COMMAND_ARG SLAVEOF_Args[] = {
|
||||
{MAKE_ARG("args",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SLAVEOF_args_Subargs},
|
||||
{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
};
|
||||
|
||||
/********** SLOWLOG GET ********************/
|
||||
@@ -8150,7 +7761,7 @@ struct COMMAND_ARG SINTERCARD_Args[] = {
|
||||
#ifndef SKIP_CMD_KEY_SPECS_TABLE
|
||||
/* SINTERSTORE key specs */
|
||||
keySpec SINTERSTORE_Keyspecs[2] = {
|
||||
{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
|
||||
{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -9712,7 +9323,7 @@ struct COMMAND_ARG XGROUP_CREATE_Args[] = {
|
||||
{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
|
||||
{MAKE_ARG("id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XGROUP_CREATE_id_selector_Subargs},
|
||||
{MAKE_ARG("mkstream",ARG_TYPE_PURE_TOKEN,-1,"MKSTREAM",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
|
||||
{MAKE_ARG("entriesread",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="entries-read"},
|
||||
{MAKE_ARG("entries-read",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
|
||||
};
|
||||
|
||||
/********** XGROUP CREATECONSUMER ********************/
|
||||
@@ -9877,7 +9488,7 @@ struct COMMAND_STRUCT XGROUP_Subcommands[] = {
|
||||
#ifndef SKIP_CMD_HISTORY_TABLE
|
||||
/* XINFO CONSUMERS history */
|
||||
commandHistory XINFO_CONSUMERS_History[] = {
|
||||
{"7.2.0","Added the `inactive` field, and changed the meaning of `idle`."},
|
||||
{"7.2.0","Added the `inactive` field."},
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -11020,25 +10631,22 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("ttl","Returns the expiration time in seconds of a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TTL_History,1,TTL_Tips,1,ttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TTL_Keyspecs,1,NULL,1),.args=TTL_Args},
|
||||
{MAKE_CMD("type","Determines the type of value stored at a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TYPE_History,0,TYPE_Tips,0,typeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TYPE_Keyspecs,1,NULL,1),.args=TYPE_Args},
|
||||
{MAKE_CMD("unlink","Asynchronously deletes one or more keys.","O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.","4.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,UNLINK_History,0,UNLINK_Tips,2,unlinkCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,UNLINK_Keyspecs,1,NULL,1),.args=UNLINK_Args},
|
||||
{MAKE_CMD("wait","Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAIT_History,0,WAIT_Tips,2,waitCommand,3,CMD_BLOCKING,ACL_CATEGORY_CONNECTION,WAIT_Keyspecs,0,NULL,2),.args=WAIT_Args},
|
||||
{MAKE_CMD("waitaof","Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAITAOF_History,0,WAITAOF_Tips,2,waitaofCommand,4,CMD_BLOCKING,ACL_CATEGORY_CONNECTION,WAITAOF_Keyspecs,0,NULL,3),.args=WAITAOF_Args},
|
||||
{MAKE_CMD("wait","Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAIT_History,0,WAIT_Tips,2,waitCommand,3,0,ACL_CATEGORY_CONNECTION,WAIT_Keyspecs,0,NULL,2),.args=WAIT_Args},
|
||||
{MAKE_CMD("waitaof","Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAITAOF_History,0,WAITAOF_Tips,2,waitaofCommand,4,CMD_NOSCRIPT,ACL_CATEGORY_CONNECTION,WAITAOF_Keyspecs,0,NULL,3),.args=WAITAOF_Args},
|
||||
/* geo */
|
||||
{MAKE_CMD("geoadd","Adds one or more members to a geospatial index. The key is created if it doesn't exist.","O(log(N)) for each item added, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOADD_History,1,GEOADD_Tips,0,geoaddCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEOADD_Keyspecs,1,NULL,4),.args=GEOADD_Args},
|
||||
{MAKE_CMD("geodist","Returns the distance between two members of a geospatial index.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEODIST_History,0,GEODIST_Tips,0,geodistCommand,-4,CMD_READONLY,ACL_CATEGORY_GEO,GEODIST_Keyspecs,1,NULL,4),.args=GEODIST_Args},
|
||||
{MAKE_CMD("geohash","Returns members from a geospatial index as geohash strings.","O(1) for each member requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOHASH_History,0,GEOHASH_Tips,0,geohashCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOHASH_Keyspecs,1,NULL,2),.args=GEOHASH_Args},
|
||||
{MAKE_CMD("geopos","Returns the longitude and latitude of members from a geospatial index.","O(1) for each member requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOPOS_History,0,GEOPOS_Tips,0,geoposCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOPOS_Keyspecs,1,NULL,2),.args=GEOPOS_Args},
|
||||
{MAKE_CMD("georadius","Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_History,2,GEORADIUS_Tips,0,georadiusCommand,-6,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUS_Keyspecs,3,georadiusGetKeys,11),.args=GEORADIUS_Args},
|
||||
{MAKE_CMD("georadiusbymember","Queries a geospatial index for members within a distance from a member, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_History,2,GEORADIUSBYMEMBER_Tips,0,georadiusbymemberCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_Keyspecs,3,georadiusGetKeys,10),.args=GEORADIUSBYMEMBER_Args},
|
||||
{MAKE_CMD("georadiusbymember_ro","Returns members from a geospatial index that are within a distance from a member.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_RO_History,2,GEORADIUSBYMEMBER_RO_Tips,0,georadiusbymemberroCommand,-5,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_RO_Keyspecs,1,NULL,9),.args=GEORADIUSBYMEMBER_RO_Args},
|
||||
{MAKE_CMD("georadius_ro","Returns members from a geospatial index that are within a distance from a coordinate.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_RO_History,2,GEORADIUS_RO_Tips,0,georadiusroCommand,-6,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUS_RO_Keyspecs,1,NULL,10),.args=GEORADIUS_RO_Args},
|
||||
{MAKE_CMD("georadiusbymember","Queries a geospatial index for members within a distance from a member, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_History,1,GEORADIUSBYMEMBER_Tips,0,georadiusbymemberCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_Keyspecs,3,georadiusGetKeys,10),.args=GEORADIUSBYMEMBER_Args},
|
||||
{MAKE_CMD("georadiusbymember_ro","Returns members from a geospatial index that are within a distance from a member.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_RO_History,0,GEORADIUSBYMEMBER_RO_Tips,0,georadiusbymemberroCommand,-5,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_RO_Keyspecs,1,NULL,9),.args=GEORADIUSBYMEMBER_RO_Args},
|
||||
{MAKE_CMD("georadius_ro","Returns members from a geospatial index that are within a distance from a coordinate.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_RO_History,1,GEORADIUS_RO_Tips,0,georadiusroCommand,-6,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUS_RO_Keyspecs,1,NULL,10),.args=GEORADIUS_RO_Args},
|
||||
{MAKE_CMD("geosearch","Queries a geospatial index for members inside an area of a box or a circle.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCH_History,1,GEOSEARCH_Tips,0,geosearchCommand,-7,CMD_READONLY,ACL_CATEGORY_GEO,GEOSEARCH_Keyspecs,1,NULL,8),.args=GEOSEARCH_Args},
|
||||
{MAKE_CMD("geosearchstore","Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCHSTORE_History,1,GEOSEARCHSTORE_Tips,0,geosearchstoreCommand,-8,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEOSEARCHSTORE_Keyspecs,2,NULL,7),.args=GEOSEARCHSTORE_Args},
|
||||
/* hash */
|
||||
{MAKE_CMD("hdel","Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","O(N) where N is the number of fields to be removed.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HDEL_History,1,HDEL_Tips,0,hdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,HDEL_Keyspecs,1,NULL,2),.args=HDEL_Args},
|
||||
{MAKE_CMD("hexists","Determines whether a field exists in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXISTS_History,0,HEXISTS_Tips,0,hexistsCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HEXISTS_Keyspecs,1,NULL,2),.args=HEXISTS_Args},
|
||||
{MAKE_CMD("hexpire","Set expiry for hash field using relative time to expire (seconds)","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRE_History,0,HEXPIRE_Tips,0,hexpireCommand,-6,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HEXPIRE_Keyspecs,1,NULL,4),.args=HEXPIRE_Args},
|
||||
{MAKE_CMD("hexpireat","Set expiry for hash field using an absolute Unix timestamp (seconds)","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIREAT_History,0,HEXPIREAT_Tips,0,hexpireatCommand,-6,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HEXPIREAT_Keyspecs,1,NULL,4),.args=HEXPIREAT_Args},
|
||||
{MAKE_CMD("hexpiretime","Returns the expiration time of a hash field as a Unix timestamp, in seconds.","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRETIME_History,0,HEXPIRETIME_Tips,0,hexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HEXPIRETIME_Keyspecs,1,NULL,2),.args=HEXPIRETIME_Args},
|
||||
{MAKE_CMD("hget","Returns the value of a field in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGET_History,0,HGET_Tips,0,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HGET_Keyspecs,1,NULL,2),.args=HGET_Args},
|
||||
{MAKE_CMD("hgetall","Returns all fields and values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETALL_History,0,HGETALL_Tips,1,hgetallCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HGETALL_Keyspecs,1,NULL,1),.args=HGETALL_Args},
|
||||
{MAKE_CMD("hincrby","Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBY_History,0,HINCRBY_Tips,0,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HINCRBY_Keyspecs,1,NULL,3),.args=HINCRBY_Args},
|
||||
@@ -11047,17 +10655,11 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("hlen","Returns the number of fields in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HLEN_History,0,HLEN_Tips,0,hlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HLEN_Keyspecs,1,NULL,1),.args=HLEN_Args},
|
||||
{MAKE_CMD("hmget","Returns the values of all fields in a hash.","O(N) where N is the number of fields being requested.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMGET_History,0,HMGET_Tips,0,hmgetCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HMGET_Keyspecs,1,NULL,2),.args=HMGET_Args},
|
||||
{MAKE_CMD("hmset","Sets the values of multiple fields.","O(N) where N is the number of fields being set.","2.0.0",CMD_DOC_DEPRECATED,"`HSET` with multiple field-value pairs","4.0.0","hash",COMMAND_GROUP_HASH,HMSET_History,0,HMSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HMSET_Keyspecs,1,NULL,2),.args=HMSET_Args},
|
||||
{MAKE_CMD("hpersist","Removes the expiration time for each specified field","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPERSIST_History,0,HPERSIST_Tips,0,hpersistCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,HPERSIST_Keyspecs,1,NULL,2),.args=HPERSIST_Args},
|
||||
{MAKE_CMD("hpexpire","Set expiry for hash field using relative time to expire (milliseconds)","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIRE_History,0,HPEXPIRE_Tips,0,hpexpireCommand,-6,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HPEXPIRE_Keyspecs,1,NULL,4),.args=HPEXPIRE_Args},
|
||||
{MAKE_CMD("hpexpireat","Set expiry for hash field using an absolute Unix timestamp (milliseconds)","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIREAT_History,0,HPEXPIREAT_Tips,0,hpexpireatCommand,-6,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HPEXPIREAT_Keyspecs,1,NULL,4),.args=HPEXPIREAT_Args},
|
||||
{MAKE_CMD("hpexpiretime","Returns the expiration time of a hash field as a Unix timestamp, in msec.","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIRETIME_History,0,HPEXPIRETIME_Tips,0,hpexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HPEXPIRETIME_Keyspecs,1,NULL,2),.args=HPEXPIRETIME_Args},
|
||||
{MAKE_CMD("hpttl","Returns the TTL in milliseconds of a hash field.","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPTTL_History,0,HPTTL_Tips,1,hpttlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HPTTL_Keyspecs,1,NULL,2),.args=HPTTL_Args},
|
||||
{MAKE_CMD("hrandfield","Returns one or more random fields from a hash.","O(N) where N is the number of fields returned","6.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HRANDFIELD_History,0,HRANDFIELD_Tips,1,hrandfieldCommand,-2,CMD_READONLY,ACL_CATEGORY_HASH,HRANDFIELD_Keyspecs,1,NULL,2),.args=HRANDFIELD_Args},
|
||||
{MAKE_CMD("hscan","Iterates over fields and values of a hash.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSCAN_History,0,HSCAN_Tips,1,hscanCommand,-3,CMD_READONLY,ACL_CATEGORY_HASH,HSCAN_Keyspecs,1,NULL,5),.args=HSCAN_Args},
|
||||
{MAKE_CMD("hscan","Iterates over fields and values of a hash.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSCAN_History,0,HSCAN_Tips,1,hscanCommand,-3,CMD_READONLY,ACL_CATEGORY_HASH,HSCAN_Keyspecs,1,NULL,4),.args=HSCAN_Args},
|
||||
{MAKE_CMD("hset","Creates or modifies the value of a field in a hash.","O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSET_History,1,HSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HSET_Keyspecs,1,NULL,2),.args=HSET_Args},
|
||||
{MAKE_CMD("hsetnx","Sets the value of a field in a hash only when the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSETNX_History,0,HSETNX_Tips,0,hsetnxCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HSETNX_Keyspecs,1,NULL,3),.args=HSETNX_Args},
|
||||
{MAKE_CMD("hstrlen","Returns the length of the value of a field.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSTRLEN_History,0,HSTRLEN_Tips,0,hstrlenCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HSTRLEN_Keyspecs,1,NULL,2),.args=HSTRLEN_Args},
|
||||
{MAKE_CMD("httl","Returns the TTL in seconds of a hash field.","O(N) where N is the number of specified fields","7.4.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HTTL_History,0,HTTL_Tips,1,httlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HTTL_Keyspecs,1,NULL,2),.args=HTTL_Args},
|
||||
{MAKE_CMD("hvals","Returns all values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HVALS_History,0,HVALS_Tips,1,hvalsCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HVALS_Keyspecs,1,NULL,1),.args=HVALS_Args},
|
||||
/* hyperloglog */
|
||||
{MAKE_CMD("pfadd","Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.","O(1) to add every element.","2.8.9",CMD_DOC_NONE,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFADD_History,0,PFADD_Tips,0,pfaddCommand,-2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HYPERLOGLOG,PFADD_Keyspecs,1,NULL,2),.args=PFADD_Args},
|
||||
@@ -11129,12 +10731,12 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("monitor","Listens for all requests received by the server in real-time.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MONITOR_History,0,MONITOR_Tips,0,monitorCommand,1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,MONITOR_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("psync","An internal command used in replication.",NULL,"2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,PSYNC_History,0,PSYNC_Tips,0,syncCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,PSYNC_Keyspecs,0,NULL,2),.args=PSYNC_Args},
|
||||
{MAKE_CMD("replconf","An internal command for configuring the replication stream.","O(1)","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLCONF_History,0,REPLCONF_Tips,0,replconfCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_ALLOW_BUSY,0,REPLCONF_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("replicaof","Configures a server as replica of another, or promotes it to a master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLICAOF_History,0,REPLICAOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,REPLICAOF_Keyspecs,0,NULL,1),.args=REPLICAOF_Args},
|
||||
{MAKE_CMD("replicaof","Configures a server as replica of another, or promotes it to a master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLICAOF_History,0,REPLICAOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,REPLICAOF_Keyspecs,0,NULL,2),.args=REPLICAOF_Args},
|
||||
{MAKE_CMD("restore-asking","An internal command for migrating keys in a cluster.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,RESTORE_ASKING_History,3,RESTORE_ASKING_Tips,0,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_ASKING,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,RESTORE_ASKING_Keyspecs,1,NULL,7),.args=RESTORE_ASKING_Args},
|
||||
{MAKE_CMD("role","Returns the replication role.","O(1)","2.8.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ROLE_History,0,ROLE_Tips,0,roleCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS,ROLE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("save","Synchronously saves the database(s) to disk.","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SAVE_History,0,SAVE_Tips,0,saveCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_NO_MULTI,0,SAVE_Keyspecs,0,NULL,0)},
|
||||
{MAKE_CMD("shutdown","Synchronously saves the database(s) to disk and shuts down the Redis server.","O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SHUTDOWN_History,1,SHUTDOWN_Tips,0,shutdownCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_NO_MULTI|CMD_SENTINEL|CMD_ALLOW_BUSY,0,SHUTDOWN_Keyspecs,0,NULL,4),.args=SHUTDOWN_Args},
|
||||
{MAKE_CMD("slaveof","Sets a Redis server as a replica of another, or promotes it to being a master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0","server",COMMAND_GROUP_SERVER,SLAVEOF_History,0,SLAVEOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,SLAVEOF_Keyspecs,0,NULL,1),.args=SLAVEOF_Args},
|
||||
{MAKE_CMD("slaveof","Sets a Redis server as a replica of another, or promotes it to being a master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0","server",COMMAND_GROUP_SERVER,SLAVEOF_History,0,SLAVEOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,SLAVEOF_Keyspecs,0,NULL,2),.args=SLAVEOF_Args},
|
||||
{MAKE_CMD("slowlog","A container for slow log commands.","Depends on subcommand.","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_History,0,SLOWLOG_Tips,0,NULL,-2,0,0,SLOWLOG_Keyspecs,0,NULL,0),.subcommands=SLOWLOG_Subcommands},
|
||||
{MAKE_CMD("swapdb","Swaps two Redis databases.","O(N) where N is the count of clients watching or blocking on keys from both databases.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SWAPDB_History,0,SWAPDB_Tips,0,swapdbCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,SWAPDB_Keyspecs,0,NULL,2),.args=SWAPDB_Args},
|
||||
{MAKE_CMD("sync","An internal command used in replication.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SYNC_History,0,SYNC_Tips,0,syncCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,SYNC_Keyspecs,0,NULL,0)},
|
||||
@@ -11148,7 +10750,7 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("sintercard","Returns the number of members of the intersect of multiple sets.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERCARD_History,0,SINTERCARD_Tips,0,sinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,SINTERCARD_Keyspecs,1,sintercardGetKeys,3),.args=SINTERCARD_Args},
|
||||
{MAKE_CMD("sinterstore","Stores the intersect of multiple sets in a key.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERSTORE_History,0,SINTERSTORE_Tips,0,sinterstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,SINTERSTORE_Keyspecs,2,NULL,2),.args=SINTERSTORE_Args},
|
||||
{MAKE_CMD("sismember","Determines whether a member belongs to a set.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SISMEMBER_History,0,SISMEMBER_Tips,0,sismemberCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SISMEMBER_Keyspecs,1,NULL,2),.args=SISMEMBER_Args},
|
||||
{MAKE_CMD("smembers","Returns all members of a set.","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMEMBERS_History,0,SMEMBERS_Tips,1,smembersCommand,2,CMD_READONLY,ACL_CATEGORY_SET,SMEMBERS_Keyspecs,1,NULL,1),.args=SMEMBERS_Args},
|
||||
{MAKE_CMD("smembers","Returns all members of a set.","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMEMBERS_History,0,SMEMBERS_Tips,1,sinterCommand,2,CMD_READONLY,ACL_CATEGORY_SET,SMEMBERS_Keyspecs,1,NULL,1),.args=SMEMBERS_Args},
|
||||
{MAKE_CMD("smismember","Determines whether multiple members belong to a set.","O(N) where N is the number of elements being checked for membership","6.2.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMISMEMBER_History,0,SMISMEMBER_Tips,0,smismemberCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SMISMEMBER_Keyspecs,1,NULL,2),.args=SMISMEMBER_Args},
|
||||
{MAKE_CMD("smove","Moves a member from one set to another.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMOVE_History,0,SMOVE_Tips,0,smoveCommand,4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SMOVE_Keyspecs,2,NULL,3),.args=SMOVE_Args},
|
||||
{MAKE_CMD("spop","Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.","Without the count argument O(1), otherwise O(N) where N is the value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SPOP_History,1,SPOP_Tips,1,spopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SPOP_Keyspecs,1,NULL,2),.args=SPOP_Args},
|
||||
@@ -11204,7 +10806,7 @@ struct COMMAND_STRUCT redisCommandTable[] = {
|
||||
{MAKE_CMD("xlen","Return the number of messages in a stream.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XLEN_History,0,XLEN_Tips,0,xlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STREAM,XLEN_Keyspecs,1,NULL,1),.args=XLEN_Args},
|
||||
{MAKE_CMD("xpending","Returns the information and entries from a stream consumer group's pending entries list.","O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XPENDING_History,1,XPENDING_Tips,1,xpendingCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,XPENDING_Keyspecs,1,NULL,3),.args=XPENDING_Args},
|
||||
{MAKE_CMD("xrange","Returns the messages from a stream within a range of IDs.","O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XRANGE_History,1,XRANGE_Tips,0,xrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XRANGE_Keyspecs,1,NULL,4),.args=XRANGE_Args},
|
||||
{MAKE_CMD("xread","Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREAD_History,0,XREAD_Tips,0,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY,ACL_CATEGORY_STREAM,XREAD_Keyspecs,1,xreadGetKeys,3),.args=XREAD_Args},
|
||||
{MAKE_CMD("xread","Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREAD_History,0,XREAD_Tips,0,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY|CMD_BLOCKING,ACL_CATEGORY_STREAM,XREAD_Keyspecs,1,xreadGetKeys,3),.args=XREAD_Args},
|
||||
{MAKE_CMD("xreadgroup","Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.","For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREADGROUP_History,0,XREADGROUP_Tips,0,xreadCommand,-7,CMD_BLOCKING|CMD_WRITE,ACL_CATEGORY_STREAM,XREADGROUP_Keyspecs,1,xreadGetKeys,5),.args=XREADGROUP_Args},
|
||||
{MAKE_CMD("xrevrange","Returns the messages from a stream within a range of IDs in reverse order.","O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREVRANGE_History,1,XREVRANGE_Tips,0,xrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XREVRANGE_Keyspecs,1,NULL,4),.args=XREVRANGE_Args},
|
||||
{MAKE_CMD("xsetid","An internal command for replicating stream values.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XSETID_History,1,XSETID_Tips,0,xsetidCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,XSETID_Keyspecs,1,NULL,4),.args=XSETID_Args},
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
This directory contains JSON files, one for each of Redis commands.
|
||||
|
||||
Each JSON contains all the information about the command itself, but these JSON files are not to be used directly!
|
||||
Any third party who needs access to command information must get it from `COMMAND INFO` and `COMMAND DOCS`.
|
||||
The output can be extracted in a JSON format by using `redis-cli --json`, in the same manner as in `utils/generate-commands-json.py`.
|
||||
|
||||
The JSON files are used to generate commands.def (and https://github.com/redis/redis-doc/blob/master/commands.json) in Redis, and
|
||||
despite looking similar to the output of `COMMAND` there are some fields and flags that are implicitly populated, and that's the
|
||||
reason one shouldn't rely on the raw files.
|
||||
|
||||
The structure of each JSON is somewhat documented in https://redis.io/commands/command-docs/ and https://redis.io/commands/command/
|
||||
|
||||
The `reply_schema` section is a standard JSON Schema (see https://json-schema.org/) that describes the reply of each command.
|
||||
It is designed to someday be used to auto-generate code in client libraries, but is not yet mature and is not exposed externally.
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"type": "integer",
|
||||
"description": "The number of users that were deleted"
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
},
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
[
|
||||
"6.2.0",
|
||||
"`LADDR` option."
|
||||
],
|
||||
[
|
||||
"7.4.0",
|
||||
"`MAXAGE` option."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
@@ -140,13 +136,6 @@
|
||||
"token": "NO"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"token": "MAXAGE",
|
||||
"name": "maxage",
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"since": "7.4.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -31,18 +31,6 @@
|
||||
[
|
||||
"7.0.3",
|
||||
"Added `ssub` field."
|
||||
],
|
||||
[
|
||||
"7.2.0",
|
||||
"Added `lib-name` and `lib-ver` fields."
|
||||
],
|
||||
[
|
||||
"7.4.0",
|
||||
"Added `watch` field."
|
||||
],
|
||||
[
|
||||
"8.0.0",
|
||||
"Added `io-thread` field."
|
||||
]
|
||||
],
|
||||
"command_flags": [
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
"STALE",
|
||||
"SENTINEL"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
],
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"description": "an even number element array specifying the start and end slot numbers for slot ranges owned by this shard",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
"LOADING",
|
||||
"STALE"
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"const": "OK"
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `ANY` option for `COUNT`."
|
||||
],
|
||||
[
|
||||
"7.0.0",
|
||||
"Added support for uppercase unit names."
|
||||
]
|
||||
],
|
||||
"deprecated_since": "6.2.0",
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
"function": "georadiusbymemberCommand",
|
||||
"get_keys_function": "georadiusGetKeys",
|
||||
"history": [
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `ANY` option for `COUNT`."
|
||||
],
|
||||
[
|
||||
"7.0.0",
|
||||
"Added support for uppercase unit names."
|
||||
|
||||
@@ -6,16 +6,6 @@
|
||||
"since": "3.2.10",
|
||||
"arity": -5,
|
||||
"function": "georadiusbymemberroCommand",
|
||||
"history": [
|
||||
[
|
||||
"6.2.0",
|
||||
"Added the `ANY` option for `COUNT`."
|
||||
],
|
||||
[
|
||||
"7.0.0",
|
||||
"Added support for uppercase unit names."
|
||||
]
|
||||
],
|
||||
"deprecated_since": "6.2.0",
|
||||
"replaced_by": "`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments",
|
||||
"doc_flags": [
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"HEXPIRE": {
|
||||
"summary": "Set expiry for hash field using relative time to expire (seconds)",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -6,
|
||||
"function": "hexpireCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"DENYOOM",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "Specified NX | XX | GT | LT condition not met",
|
||||
"const": 0
|
||||
},
|
||||
{
|
||||
"description": "Expiration time was set or updated.",
|
||||
"const": 1
|
||||
},
|
||||
{
|
||||
"description": "Field deleted because the specified expiration time is in the past.",
|
||||
"const": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "seconds",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "condition",
|
||||
"type": "oneof",
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "nx",
|
||||
"type": "pure-token",
|
||||
"token": "NX"
|
||||
},
|
||||
{
|
||||
"name": "xx",
|
||||
"type": "pure-token",
|
||||
"token": "XX"
|
||||
},
|
||||
{
|
||||
"name": "gt",
|
||||
"type": "pure-token",
|
||||
"token": "GT"
|
||||
},
|
||||
{
|
||||
"name": "lt",
|
||||
"type": "pure-token",
|
||||
"token": "LT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"HEXPIREAT": {
|
||||
"summary": "Set expiry for hash field using an absolute Unix timestamp (seconds)",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -6,
|
||||
"function": "hexpireatCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"DENYOOM",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "Specified NX | XX | GT | LT condition not met",
|
||||
"const": 0
|
||||
},
|
||||
{
|
||||
"description": "Expiration time was set or updated.",
|
||||
"const": 1
|
||||
},
|
||||
{
|
||||
"description": "Field deleted because the specified expiration time is in the past.",
|
||||
"const": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "unix-time-seconds",
|
||||
"type": "unix-time"
|
||||
},
|
||||
{
|
||||
"name": "condition",
|
||||
"type": "oneof",
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "nx",
|
||||
"type": "pure-token",
|
||||
"token": "NX"
|
||||
},
|
||||
{
|
||||
"name": "xx",
|
||||
"type": "pure-token",
|
||||
"token": "XX"
|
||||
},
|
||||
{
|
||||
"name": "gt",
|
||||
"type": "pure-token",
|
||||
"token": "GT"
|
||||
},
|
||||
{
|
||||
"name": "lt",
|
||||
"type": "pure-token",
|
||||
"token": "LT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"HEXPIRETIME": {
|
||||
"summary": "Returns the expiration time of a hash field as a Unix timestamp, in seconds.",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -5,
|
||||
"function": "hexpiretimeCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"READONLY",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RO",
|
||||
"ACCESS"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "The field exists but has no associated expire.",
|
||||
"const": -1
|
||||
},
|
||||
{
|
||||
"description": "Expiration Unix timestamp in seconds.",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"HPERSIST": {
|
||||
"summary": "Removes the expiration time for each specified field",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -5,
|
||||
"function": "hpersistCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "The field exists but has no associated expire.",
|
||||
"const": -1
|
||||
},
|
||||
{
|
||||
"description": "Expiration time was removed",
|
||||
"const": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"HPEXPIRE": {
|
||||
"summary": "Set expiry for hash field using relative time to expire (milliseconds)",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -6,
|
||||
"function": "hpexpireCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"DENYOOM",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "Specified NX | XX | GT | LT condition not met",
|
||||
"const": 0
|
||||
},
|
||||
{
|
||||
"description": "Expiration time was set or updated.",
|
||||
"const": 1
|
||||
},
|
||||
{
|
||||
"description": "Field deleted because the specified expiration time is in the past.",
|
||||
"const": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "milliseconds",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "condition",
|
||||
"type": "oneof",
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "nx",
|
||||
"type": "pure-token",
|
||||
"token": "NX"
|
||||
},
|
||||
{
|
||||
"name": "xx",
|
||||
"type": "pure-token",
|
||||
"token": "XX"
|
||||
},
|
||||
{
|
||||
"name": "gt",
|
||||
"type": "pure-token",
|
||||
"token": "GT"
|
||||
},
|
||||
{
|
||||
"name": "lt",
|
||||
"type": "pure-token",
|
||||
"token": "LT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"HPEXPIREAT": {
|
||||
"summary": "Set expiry for hash field using an absolute Unix timestamp (milliseconds)",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -6,
|
||||
"function": "hpexpireatCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"DENYOOM",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "Specified NX | XX | GT | LT condition not met",
|
||||
"const": 0
|
||||
},
|
||||
{
|
||||
"description": "Expiration time was set or updated.",
|
||||
"const": 1
|
||||
},
|
||||
{
|
||||
"description": "Field deleted because the specified expiration time is in the past.",
|
||||
"const": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "unix-time-milliseconds",
|
||||
"type": "unix-time"
|
||||
},
|
||||
{
|
||||
"name": "condition",
|
||||
"type": "oneof",
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "nx",
|
||||
"type": "pure-token",
|
||||
"token": "NX"
|
||||
},
|
||||
{
|
||||
"name": "xx",
|
||||
"type": "pure-token",
|
||||
"token": "XX"
|
||||
},
|
||||
{
|
||||
"name": "gt",
|
||||
"type": "pure-token",
|
||||
"token": "GT"
|
||||
},
|
||||
{
|
||||
"name": "lt",
|
||||
"type": "pure-token",
|
||||
"token": "LT"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"HPEXPIRETIME": {
|
||||
"summary": "Returns the expiration time of a hash field as a Unix timestamp, in msec.",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -5,
|
||||
"function": "hpexpiretimeCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"READONLY",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RO",
|
||||
"ACCESS"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "The field exists but has no associated expire.",
|
||||
"const": -1
|
||||
},
|
||||
{
|
||||
"description": "Expiration Unix timestamp in milliseconds.",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"HPTTL": {
|
||||
"summary": "Returns the TTL in milliseconds of a hash field.",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -5,
|
||||
"function": "hpttlCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"READONLY",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RO",
|
||||
"ACCESS"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "The field exists but has no associated expire.",
|
||||
"const": -1
|
||||
},
|
||||
{
|
||||
"description": "TTL in milliseconds.",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -56,12 +56,6 @@
|
||||
"name": "count",
|
||||
"type": "integer",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"token": "NOVALUES",
|
||||
"name": "novalues",
|
||||
"type": "pure-token",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
@@ -75,7 +69,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "list of key/value pairs from the hash where each even element is the key, and each odd element is the value, or when novalues option is on, a list of keys from the hash",
|
||||
"description": "list of key/value pairs from the hash where each even element is the key, and each odd element is the value",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"HTTL": {
|
||||
"summary": "Returns the TTL in seconds of a hash field.",
|
||||
"complexity": "O(N) where N is the number of specified fields",
|
||||
"group": "hash",
|
||||
"since": "7.4.0",
|
||||
"arity": -5,
|
||||
"function": "httlCommand",
|
||||
"history": [],
|
||||
"command_flags": [
|
||||
"READONLY",
|
||||
"FAST"
|
||||
],
|
||||
"acl_categories": [
|
||||
"HASH"
|
||||
],
|
||||
"command_tips": [
|
||||
"NONDETERMINISTIC_OUTPUT"
|
||||
],
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"RO",
|
||||
"ACCESS"
|
||||
],
|
||||
"begin_search": {
|
||||
"index": {
|
||||
"pos": 1
|
||||
}
|
||||
},
|
||||
"find_keys": {
|
||||
"range": {
|
||||
"lastkey": 0,
|
||||
"step": 1,
|
||||
"limit": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "Array of results. Returns empty array if the key does not exist.",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "The field does not exist.",
|
||||
"const": -2
|
||||
},
|
||||
{
|
||||
"description": "The field exists but has no associated expire.",
|
||||
"const": -1
|
||||
},
|
||||
{
|
||||
"description": "TTL in seconds.",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "key",
|
||||
"key_spec_index": 0
|
||||
},
|
||||
{
|
||||
"name": "fields",
|
||||
"token": "FIELDS",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "numfields",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "field",
|
||||
"type": "string",
|
||||
"multiple": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
],
|
||||
"command_tips": [
|
||||
"REQUEST_POLICY:ALL_NODES",
|
||||
"RESPONSE_POLICY:AGG_SUM"
|
||||
"RESPONSE_POLICY:ALL_SUCCEEDED"
|
||||
],
|
||||
"reply_schema": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -47,18 +47,9 @@
|
||||
"functions.caches": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overhead.db.hashtable.lut": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overhead.db.hashtable.rehashing": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overhead.total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"db.dict.rehashing.count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"keys.count": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -83,9 +74,6 @@
|
||||
"allocator.resident": {
|
||||
"type": "integer"
|
||||
},
|
||||
"allocator.muzzy": {
|
||||
"type": "integer"
|
||||
},
|
||||
"allocator-fragmentation.ratio": {
|
||||
"type": "number"
|
||||
},
|
||||
@@ -112,7 +100,7 @@
|
||||
}
|
||||
},
|
||||
"patternProperties": {
|
||||
"^db\\.\\d+$": {
|
||||
"^db.": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"overhead.hashtable.main": {
|
||||
@@ -120,6 +108,9 @@
|
||||
},
|
||||
"overhead.hashtable.expires": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overhead.hashtable.slot-to-keys": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -14,40 +14,12 @@
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "args",
|
||||
"type": "oneof",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host-port",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no-one",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "no",
|
||||
"type": "pure-token",
|
||||
"token": "NO"
|
||||
},
|
||||
{
|
||||
"name": "one",
|
||||
"type": "pure-token",
|
||||
"token": "ONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"SFLUSH": {
|
||||
"summary": "Remove all keys from selected range of slots.",
|
||||
"complexity": "O(N)+O(k) where N is the number of keys and k is the number of slots.",
|
||||
"group": "server",
|
||||
"since": "8.0.0",
|
||||
"arity": -3,
|
||||
"function": "sflushCommand",
|
||||
"command_flags": [
|
||||
"WRITE",
|
||||
"EXPERIMENTAL"
|
||||
],
|
||||
"acl_categories": [
|
||||
"KEYSPACE",
|
||||
"DANGEROUS"
|
||||
],
|
||||
"command_tips": [
|
||||
],
|
||||
"reply_schema": {
|
||||
"description": "List of slot ranges",
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"maxItems": 4294967295,
|
||||
"items": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"maxItems": 2,
|
||||
"items": [
|
||||
{
|
||||
"description": "start slot number",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "end slot number",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"name": "data",
|
||||
"type": "block",
|
||||
"multiple": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "slot-start",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"name": "slot-last",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flush-type",
|
||||
"type": "oneof",
|
||||
"optional": true,
|
||||
"arguments": [
|
||||
{
|
||||
"name": "async",
|
||||
"type": "pure-token",
|
||||
"token": "ASYNC"
|
||||
},
|
||||
{
|
||||
"name": "sync",
|
||||
"type": "pure-token",
|
||||
"token": "SYNC"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"key_specs": [
|
||||
{
|
||||
"flags": [
|
||||
"OW",
|
||||
"RW",
|
||||
"UPDATE"
|
||||
],
|
||||
"begin_search": {
|
||||
|
||||
@@ -19,40 +19,12 @@
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "args",
|
||||
"type": "oneof",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host-port",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no-one",
|
||||
"type": "block",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "no",
|
||||
"type": "pure-token",
|
||||
"token": "NO"
|
||||
},
|
||||
{
|
||||
"name": "one",
|
||||
"type": "pure-token",
|
||||
"token": "ONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"name": "host",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"reply_schema": {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"group": "set",
|
||||
"since": "1.0.0",
|
||||
"arity": 2,
|
||||
"function": "smembersCommand",
|
||||
"function": "sinterCommand",
|
||||
"command_flags": [
|
||||
"READONLY"
|
||||
],
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "GET option is specified, but no object was found",
|
||||
"description": "GET option is specified, but no object was found ",
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -117,15 +117,7 @@
|
||||
"description": "a list of sorted elements",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "GET option is specified, but no object was found",
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"arity": 3,
|
||||
"function": "waitCommand",
|
||||
"command_flags": [
|
||||
"BLOCKING"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"arity": 4,
|
||||
"function": "waitaofCommand",
|
||||
"command_flags": [
|
||||
"BLOCKING"
|
||||
"NOSCRIPT"
|
||||
],
|
||||
"acl_categories": [
|
||||
"CONNECTION"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user