Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d46171ff7 | ||
|
|
a8c0f9b1c4 | ||
|
|
36d515ef39 | ||
|
|
525c97d6cd | ||
|
|
4c200e0985 | ||
|
|
d4c0a970e7 | ||
|
|
1735e73a35 | ||
|
|
f9ccf90566 | ||
|
|
f54e3ea367 | ||
|
|
77bc0afad4 | ||
|
|
858db2645d | ||
|
|
06380e5fe1 | ||
|
|
deaaa992bd | ||
|
|
715f832b00 | ||
|
|
c8dd6744da | ||
|
|
b398021b2c | ||
|
|
c2077a2fbd | ||
|
|
a9995d2e54 | ||
|
|
81c6c7902d | ||
|
|
4ac449aee7 | ||
|
|
db2c3cd7e8 | ||
|
|
5f5eb0e1b0 | ||
|
|
045cc52c1f | ||
|
|
819bcc1f2a | ||
|
|
7fcbfac103 | ||
|
|
0bf6a0c375 | ||
|
|
62d281b522 | ||
|
|
9f4c6e6f15 | ||
|
|
6c26f91bcd | ||
|
|
932fc207ea | ||
|
|
0cba0ed30b | ||
|
|
b5ab1d64e2 | ||
|
|
3492615c6a | ||
|
|
f3a9d2e0cd | ||
|
|
a1978ce04c | ||
|
|
5123e2b3a1 | ||
|
|
687850a612 | ||
|
|
d4555a6e38 | ||
|
|
fc8e4804b2 | ||
|
|
1113488991 | ||
|
|
694fc59f2a | ||
|
|
7e91711086 | ||
|
|
ff7991951f | ||
|
|
d7c34a8ced | ||
|
|
c3343ddbd7 | ||
|
|
ad0be5666c | ||
|
|
b50f0cc21f | ||
|
|
48e5220d00 | ||
|
|
f054ef7872 | ||
|
|
2498e0fc1f | ||
|
+7 |
f5f1bd7605 |
No files matched your search
+17
-19
@@ -5,11 +5,11 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
test-ubuntu-latest:
|
||||
runs-on: self-hosted
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
- name: test-tls
|
||||
run: |
|
||||
sudo apt-get -y install tcl tcl-tls
|
||||
./runtest --clients 2 --verbose --tls
|
||||
./runtest --clients 1 --verbose --tls --config server-threads 3
|
||||
- name: cluster-test
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
./runtest-cluster --tls --config server-threads 3
|
||||
- name: sentinel test
|
||||
run: |
|
||||
./runtest-sentinel
|
||||
@@ -36,19 +36,24 @@ jobs:
|
||||
./runtest-rotation
|
||||
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make -j2
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
make -j2
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' -j2
|
||||
|
||||
@@ -56,18 +61,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' MALLOC=libc -j2
|
||||
build-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc-multilib g++-multilib libc6-dev-i386 lib32z1 uuid-dev:i386 libcurl4-openssl-dev:i386
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' 32bit -j2
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
.standard-pipeline:
|
||||
rules:
|
||||
- if: '$COVERAGE'
|
||||
when: never
|
||||
- if: '$ENDURANCE'
|
||||
when: never
|
||||
- if: '$CI_PIPELINE_SOURCE == "push"'
|
||||
|
||||
build:
|
||||
extends: .standard-pipeline
|
||||
tags:
|
||||
- docker
|
||||
stage: build
|
||||
script:
|
||||
- git submodule update --init
|
||||
- make distclean
|
||||
- make -j
|
||||
artifacts:
|
||||
paths:
|
||||
- src/
|
||||
|
||||
runtest:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- ./runtest --config server-threads 3
|
||||
|
||||
runtest-cluster:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- ./runtest-cluster
|
||||
|
||||
runtest-moduleapi:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- ./runtest-moduleapi
|
||||
|
||||
runtest-sentinel:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- ./runtest-sentinel
|
||||
|
||||
node-redis-test:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
rules:
|
||||
- when: never
|
||||
tags:
|
||||
- docker
|
||||
- ipv6
|
||||
stage: test
|
||||
script:
|
||||
- cp -pf src/keydb-server /usr/local/bin
|
||||
- cp -pf src/keydb-cli /usr/local/bin
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/node-redis.git
|
||||
- cd node-redis
|
||||
- npm install
|
||||
- npm run test
|
||||
|
||||
jedis-test:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
- ipv4
|
||||
stage: test
|
||||
script:
|
||||
- cp -pf src/keydb-server /usr/local/bin
|
||||
- cp -pf src/keydb-cli /usr/local/bin
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/jedis.git
|
||||
- cd jedis
|
||||
- make test
|
||||
|
||||
redis-rs-test:
|
||||
extends: .standard-pipeline
|
||||
dependencies:
|
||||
- build
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- cp -pf src/keydb-server /usr/local/bin
|
||||
- cp -pf src/keydb-cli /usr/local/bin
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/redis-rs.git
|
||||
- cd redis-rs
|
||||
- make test
|
||||
|
||||
endurance-test:
|
||||
rules:
|
||||
- if: '$ENDURANCE'
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- git submodule update --init
|
||||
- make distclean
|
||||
- make -j
|
||||
- ./runtest --config server-threads 3 --loop --stop
|
||||
|
||||
coverage-test:
|
||||
rules:
|
||||
- if: '$COVERAGE'
|
||||
tags:
|
||||
- docker
|
||||
stage: test
|
||||
script:
|
||||
- git submodule update --init
|
||||
- make distclean
|
||||
- make gcov -j
|
||||
- make install
|
||||
- ./runtest || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- ./runtest-cluster || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- ./runtest-sentinel || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- ./runtest-moduleapi || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/redis-rs.git
|
||||
- cd redis-rs
|
||||
- make test || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- cd ..
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/jedis.git
|
||||
- cd jedis
|
||||
- make test || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- cd ..
|
||||
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.eqalpha.com/keydb-dev/node-redis.git
|
||||
- cd node-redis
|
||||
- npm install
|
||||
- npm run test || true
|
||||
- pkill keydb-server || true
|
||||
- pkill stunnel || true
|
||||
- cd ..
|
||||
- geninfo -o KeyDB.info --no-external .
|
||||
- genhtml --legend -o lcov-html KeyDB.info
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
##### KeyDB is now a part of Snap Inc! Check out the announcement [here](https://docs.keydb.dev/news/2022/05/12/keydb-joins-snap)
|
||||
|
||||
##### [Release v6.3.0](https://github.com/EQ-Alpha/KeyDB/releases/tag/v6.3.0) is here with major improvements as we consolodate our Open Source and Enterprise offerings into a single BSD-3 licensed project. See our [roadmap](https://docs.keydb.dev/docs/coming-soon) for details.
|
||||
##### [Release v6.3.0](https://github.com/EQ-Alpha/KeyDB/releases/tag/v6.3.0) is here with major improvements as we consolidate our Open Source and Enterprise offerings into a single BSD-3 licensed project. See our [roadmap](https://docs.keydb.dev/docs/coming-soon) for details.
|
||||
|
||||
##### Want to extend KeyDB with Javascript? Try [ModJS](https://github.com/JohnSully/ModJS)
|
||||
|
||||
@@ -37,7 +37,7 @@ Because of this difference of opinion features which are right for KeyDB may not
|
||||
Project Support
|
||||
-------------------
|
||||
|
||||
The KeyDB team maintains this project as part of Snap Inc. KeyDB is used by Snap as part of its caching infrastructure and is fully open sourced. There is no separate commercial product and no paid support options available. We really value collaborating with the open source community and welcome PRs, bug reports, and open discussion. For community support or to get involved further with the project check out our community support options [here](https://docs.keydb.dev/docs/support) (slack, forum, meetup, github issues). Our team monitors these channlels regularly.
|
||||
The KeyDB team maintains this project as part of Snap Inc. KeyDB is used by Snap as part of its caching infrastructure and is fully open sourced. There is no separate commercial product and no paid support options available. We really value collaborating with the open source community and welcome PRs, bug reports, and open discussion. For community support or to get involved further with the project check out our community support options [here](https://docs.keydb.dev/docs/support) (slack, forum, meetup, github issues). Our team monitors these channels regularly.
|
||||
|
||||
|
||||
Additional Resources
|
||||
@@ -104,6 +104,12 @@ Avoid forwarding RREPLAY messages to other masters? WARNING: This setting is dan
|
||||
If you would like KeyDB to dump and load directly to AWS S3 this option specifies the bucket. Using this option with the traditional RDB options will result in KeyDB backing up twice to both locations. If both are specified KeyDB will first attempt to load from the local dump file and if that fails load from S3. This requires the AWS CLI tools to be installed and configured which are used under the hood to transfer the data.
|
||||
|
||||
|
||||
```
|
||||
storage-provider flash /path/to/flash
|
||||
```
|
||||
If you would like to use KeyDB FLASH storage, specify the storage medium followed by the directory path on your local SSD volume. Note that this feature is still considered experimental and should be used with discretion. See [FLASH Documentation](https://docs.keydb.dev/docs/flash) for more details on configuration and setting up your FLASH volume.
|
||||
|
||||
|
||||
Building KeyDB
|
||||
--------------
|
||||
|
||||
@@ -135,9 +141,9 @@ To append a suffix to KeyDB program names, use:
|
||||
***Note that the following dependencies may be needed:
|
||||
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
|
||||
|
||||
To buik=ld with TLS support, use:
|
||||
KeyDB by default is built with TLS enabled. To build without TLS support, use:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
% make BUILD_TLS=no
|
||||
|
||||
Running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
@@ -145,6 +151,12 @@ installed):
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
To build with KeyDB FLASH support, use:
|
||||
|
||||
% make ENABLE_FLASH=yes
|
||||
|
||||
***Note that the KeyDB FLASH feature is considered experimental (beta) and should used with discretion
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---------
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
# Doc: https://wiki.sc-corp.net/pages/viewpage.action?pageId=121500284
|
||||
version: 1
|
||||
machamp:
|
||||
keydb-build:
|
||||
# Optional - build counter is linked to the build def
|
||||
tag_template: 0.0.%build.counter%
|
||||
# Optional - value in seconds before a build is terminated, default is 3600 seconds
|
||||
timeout: 3600
|
||||
# Optional - update ghe or not, default to true
|
||||
update_ghe: true
|
||||
code_coverage: false
|
||||
# Required
|
||||
steps:
|
||||
make-build:
|
||||
type: cmd
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./machamp_scripts/build.sh
|
||||
tls-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest --clients 4 --verbose
|
||||
cluster-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-cluster
|
||||
sentinel-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-sentinel
|
||||
module-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-moduleapi
|
||||
rotation-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-rotation
|
||||
@@ -1,16 +0,0 @@
|
||||
# Doc: https://wiki.sc-corp.net/display/TOOL/ci.yaml+User+Guide
|
||||
version: 1
|
||||
on:
|
||||
pull_request:
|
||||
- workflows:
|
||||
# All builds that use machamp should use the defined `backend_workflow`
|
||||
- workflow_type: backend_workflow
|
||||
# references a build defined in build.yaml
|
||||
build_name: keydb-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
push:
|
||||
- branches: [keydbpro]
|
||||
workflows:
|
||||
- workflow_type: backend_workflow
|
||||
build_name: keydb-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
Vendored
+1
-1
@@ -95,7 +95,7 @@ JEMALLOC_LDFLAGS= $(LDFLAGS)
|
||||
|
||||
jemalloc: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd jemalloc && ./configure --with-version=5.2.1-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ --disable-cxx CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && ./configure --with-version=5.2.1-0-g0 --with-lg-quantum=3 --disable-cxx CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
Vendored
+1
-1
Submodule deps/rocksdb updated: e3169e3ea8...444b3f4845.
@@ -1 +0,0 @@
|
||||
GHE_PersonalAccessToken.txt
|
||||
@@ -1,126 +0,0 @@
|
||||
FROM ubuntu:20.04
|
||||
SHELL ["/bin/bash","-c"]
|
||||
RUN groupadd -r keydb && useradd -r -g keydb keydb
|
||||
# use gosu for easy step-down from root: https://github.com/tianon/gosu/releases
|
||||
ENV GOSU_VERSION 1.14
|
||||
RUN set -eux; \
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ca-certificates dirmngr gnupg wget; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
chmod +x /usr/local/bin/gosu; \
|
||||
gosu --version; \
|
||||
gosu nobody true
|
||||
# build KeyDB
|
||||
ARG KEYDB_DIR
|
||||
ARG MODSTATSD_DIR
|
||||
ARG MAKE_JOBS=""
|
||||
COPY $KEYDB_DIR /tmp/keydb-internal/
|
||||
COPY $MODSTATSD_DIR /tmp/modstatsd/
|
||||
RUN set -eux; \
|
||||
\
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -qqy --no-install-recommends \
|
||||
dpkg-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
nasm \
|
||||
autotools-dev \
|
||||
autoconf \
|
||||
libjemalloc-dev \
|
||||
tcl \
|
||||
tcl-dev \
|
||||
uuid-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libbz2-dev \
|
||||
libzstd-dev \
|
||||
liblz4-dev \
|
||||
libsnappy-dev \
|
||||
libssl-dev \
|
||||
git; \
|
||||
cd /tmp/modstatsd/; \
|
||||
make clean; \
|
||||
make; \
|
||||
mv modstatsd.so /usr/local/lib/; \
|
||||
cd /tmp/keydb-internal; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make distclean; \
|
||||
make -j$([ -z "$MAKE_JOBS" ] && nproc || echo "$MAKE_JOBS") BUILD_TLS=yes NO_LICENSE_CHECK=yes; \
|
||||
cd src; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
# clean up unused dependencies
|
||||
echo $savedAptMark; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
find /usr/local -type f -executable -exec ldd '{}' ';' \
|
||||
| awk '/=>/ { print $(NF-1) }' \
|
||||
| sed 's:.*/::' \
|
||||
| sort -u \
|
||||
| xargs -r dpkg-query --search \
|
||||
| cut -d: -f1 \
|
||||
| sort -u \
|
||||
| xargs -r apt-mark manual \
|
||||
; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
# create working directories and organize files
|
||||
RUN \
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/keydb-internal/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
echo -e "\nloadmodule /usr/local/lib/modstatsd.so" >> /etc/keydb/keydb.conf; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# first arg is '-f' or '--some-option'" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# or first arg is `something.conf`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec gosu keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
# set remaining image properties
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENV KEYDB_PRO_DIRECTORY=/usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server","/etc/keydb/keydb.conf"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
# KeyDB Docker Image (Snap Internal)
|
||||
|
||||
This docker image builds KeyDB within the image and cleans up afterwards. A few notes about the image:
|
||||
* use gosu to avoid running as root https://github.com/tianon/gosu
|
||||
* packages are installed, tracked and cleaned up to minimize container sizes
|
||||
* keydb-server binary symbols remain for troubleshooting. All other KeyDB binaries are stripped
|
||||
* keydb.conf added and linked to redis.conf for legacy compatibility and as default config file
|
||||
* use entrypoint and cmd for best practices. Remove protected-mode during build incase user specifies binary without .conf, or just wants append parameters
|
||||
|
||||
## Building the Docker Image Using Local KeyDB Directory
|
||||
|
||||
If you have a local keydb-internal repository you would like to generate the binaries from, use the command below. This will simply copy over all the files within the local keydb-internal repo and then build the image.
|
||||
|
||||
One issue with COPY is that it creates an additional layer. If we remove the source directory in a later layer the size of the original COPY layer remains creating a large image, hence we need to squash the layers into one layer. This feature is available with experimental mode enabled. This can be done by modifying /etc/docker/daemon.json to include `"experimental": true,`. You may also be able to pass at the build line as shown below.
|
||||
|
||||
Modify the KEYDB_DIR build argument to your local KeyDB repo and update your image tag in the line below
|
||||
|
||||
```
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker build --squash --build-arg KEYDB_DIR=. -t myImageName:imageTag -f ./docker-internal/Dockerfile .
|
||||
```
|
||||
|
||||
Please note that directories are relative to the docker build context. You can use the `-f /path/to/Dockerfile` to specify Dockerfile which will also set the build context, your repo location will be relative to it.
|
||||
|
||||
### Pushing
|
||||
|
||||
#### AWS
|
||||
There is a script ./build-and-publish.sh to build and push image. This script will push images to caching-infra AWS account and caching-infra GCP project. Make sure this script is not run using root/sudo otherwise you may not be able to access the correct AWS profile, even if everything else is configured correctly.
|
||||
|
||||
If you are pushing to ECR, then you need to add this profile config in your ```~/.aws/config```:
|
||||
|
||||
```
|
||||
[profile caching-infra-images-editor]
|
||||
role_arn = arn:aws:iam::520173307535:role/_Snap_ContainerEditor
|
||||
output = json
|
||||
region = us-east-1
|
||||
source_profile = default
|
||||
```
|
||||
and to get permission for assuming role [_Snap_ContainerEditor in account caching-infra](https://lease.sc-corp.net/v2/request_access/aws_resources/aws_account?resource=520173307535&roles=%5B_Snap_ContainerEditor%5D).
|
||||
|
||||
Also, if you are using image different from "520173307535.dkr.ecr.us-east-1.amazonaws.com/keydb", then you need to give access to that image to snap-core-prod aws account. That is account where all mesh services are running. Go to your image in AWS Console and add policy:
|
||||
```
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowImagePullApp",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::307862320347:root"
|
||||
},
|
||||
"Action": [
|
||||
"ecr:BatchCheckLayerAvailability",
|
||||
"ecr:BatchGetImage",
|
||||
"ecr:GetDownloadUrlForLayer"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GCP
|
||||
|
||||
In order to publish to GCP, you will need to get [Storage Admin Role in project caching-infra](https://lease.sc-corp.net/v2/request_access/gcp_resources/gcp_project?resource=caching-infra&roles=%5Broles/storage.admin%5D)
|
||||
|
||||
For reading image you will need to add your service account to [caching-infra project](https://lease.sc-corp.net/v2/view_iam?resourceType=PRJ&resource=caching-infra) with "Container Registry Service Agent" role.
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled ./build-and-publish.sh
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
If you see error:
|
||||
```
|
||||
#11 354.1 g++: fatal error: Killed signal terminated program cc1plus
|
||||
```
|
||||
most likely you are hitting memory constraint. If you are running docker build command from the above, then you can try to reduce number of jobs for "make" by adding "--build-arg MAKE_JOBS=<jobs>" argument to lower value (i.e. 2). If you are running ./build-and-publish.sh you can reduce the number of jobs by passing it in args:
|
||||
```
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled ./build-and-publish.sh -j 2
|
||||
```
|
||||
|
||||
## Building the Docker Image Using PAT & Clone
|
||||
|
||||
This image clones the keydb-internal repo, hence a GHE PAT token or SSH access is needed. See more on [obtaining GHE PAT](https://wiki.sc-corp.net/display/TOOL/Using+the+GHE+API#UsingtheGHEAPI-Step1:PersonalTokens). It is not secure to pass tokens/credentials as build-args, env variables, or COPYing then deleting, so we use secrets. This option is only available with the [Docker BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information)so the docker build kit must be enabled via `DOCKER_BUILDKIT=1`, or permanently by appending `"features": { "buildkit": true }` to /etc/docker/daemon.json.
|
||||
|
||||
#### To Build:
|
||||
|
||||
Run the command below updating your info as follows:
|
||||
* Pass in the branch name as a build argument
|
||||
* Tag your image to whatever you want to use
|
||||
* Add your PAT info to GHE_PersonalAccessToken.txt
|
||||
|
||||
```
|
||||
cd docker_PAT_build
|
||||
DOCKER_BUILDKIT=1 docker build --no-cache --build-arg BRANCH=keydbpro --secret id=PAT,src=GHE_PersonalAccessToken.txt . -t myImageName:imageTag
|
||||
```
|
||||
|
||||
For a detailed conventional build view pass `--progress=plain` while building. Note that `--no-cache` must be used to ensure your credentials are pulled in as they are not cached.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
If you are building on macOS or with docker frontend you may need to append the following line to the top of the Dockerfile:
|
||||
|
||||
```
|
||||
# syntax=docker/dockerfile:1.3
|
||||
```
|
||||
|
||||
## Using The Image
|
||||
|
||||
#### Bind port
|
||||
|
||||
Bind the container to the node/machine by passing the parameter `-p 6379:6379` when you run the docker container
|
||||
|
||||
#### Configuration file
|
||||
|
||||
By default KeyDB launches by specifiying its internal keydb.conf file at `/etc/keydb/keydb.conf`. If you would like to use your own config file you can link to the config file with `-v /path-to-config-file/keydb.conf:/etc/keydb/keydb.conf` then run the container whose default command is `keydb-server /etc/keydb/keydb.conf`
|
||||
|
||||
Alternatively specify any config file location, by specifying the location following the binary to override default: `docker run ThisDockerImage keydb-server /path/to/my/config-file/`. You can also just append parameters to default image with `docker run ThisDockerImage keydb-server /etc/keydb/keydb.conf --dir /data --server-threads 4`.
|
||||
|
||||
#### Persistent storage
|
||||
|
||||
If persistence is enabled, data is stored in the VOLUME /data, which can be used with --volumes-from some-volume-container or -v /docker/host/dir:/data (see [docs.docker volumes](https://docs.docker.com/storage/volumes/)).
|
||||
|
||||
#### Connect via keydb-cli
|
||||
|
||||
you can grab the ip of the container with `docker inspect --format '{{ .NetworkSettings.IPAddress }}' mycontainername` then run the following:
|
||||
|
||||
```
|
||||
docker run -it --rm ThisDockerImage keydb-cli -h <ipaddress-from-above> -p 6379
|
||||
```
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
set -o pipefail
|
||||
|
||||
# Color for displaying error messages
|
||||
red=`tput setaf 1`
|
||||
reset=`tput sgr0`
|
||||
|
||||
# Default value for the command line flag
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
FLAGS_tag="latest"
|
||||
FLAGS_provider="both"
|
||||
FLAGS_jobs=
|
||||
|
||||
timestamp() {
|
||||
date "+%m/%d %H:%M:%S"
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo "Usage:"
|
||||
echo " ./{script} {modstatsd-path} [flags]"
|
||||
echo ""
|
||||
echo "Flags:"
|
||||
echo " -p, --provider: name of the cloud provider, can be 'gcp', 'aws' or 'both' (default)"
|
||||
echo " -t, --tag: tag of the image. Default is 'latest'"
|
||||
echo " -j, --jobs: the number of jobs when making the build. Default is number of cores on this host"
|
||||
}
|
||||
|
||||
push_image () { # 1 - image, 2 - repo
|
||||
export IMAGE_WITH_REPO="$2/$1"
|
||||
docker tag $1 ${IMAGE_WITH_REPO}
|
||||
echo "`timestamp` publishing image ${IMAGE_WITH_REPO}"
|
||||
docker push "${IMAGE_WITH_REPO}"
|
||||
echo "`timestamp` image ${IMAGE_WITH_REPO} is pushed"
|
||||
}
|
||||
|
||||
#Process Module Path Flag
|
||||
MOD_STATSD_PATH="$1"
|
||||
if [[ -z ${MOD_STATSD_PATH} ]]
|
||||
then
|
||||
echo "Missing path to statsd module"
|
||||
# exit 1
|
||||
fi
|
||||
shift
|
||||
|
||||
# Processing flags
|
||||
while [ ! $# -eq 0 ]
|
||||
do
|
||||
# The shift below ensures the unprocessed flag is always at $1
|
||||
case "$1" in
|
||||
--help | -h)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
--provider | -p)
|
||||
FLAGS_provider="$2"
|
||||
if [[ ${FLAGS_provider} != "gcp" ]] && [[ ${FLAGS_provider} != "aws" ]] && [[ ${FLAGS_provider} != "both" ]];
|
||||
then
|
||||
echo "${red} For the flag -p/--provider, only valid values are [\"gcp\", \"aws\", \"both\"]."
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--tag | -t)
|
||||
FLAGS_tag="$2"
|
||||
shift
|
||||
;;
|
||||
--jobs | -j)
|
||||
FLAGS_jobs="$2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "${red}"
|
||||
echo "Unrecognized flag: $1."
|
||||
echo "Run with '--help' flag to see the supported flags."
|
||||
echo "${reset}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
echo "MOD_STATSD_PATH: ${MOD_STATSD_PATH}"
|
||||
echo "`timestamp` building image for ${FLAGS_tag}"
|
||||
export IMAGE_SUFFIX="keydb:${FLAGS_tag}"
|
||||
cp -r ${MOD_STATSD_PATH} ./modstatsd
|
||||
docker build --squash --build-arg MODSTATSD_DIR=docker-internal/modstatsd --build-arg KEYDB_DIR=. --build-arg MAKE_JOBS=${FLAGS_jobs} -t keydb:${FLAGS_tag} -f ${DIR}/Dockerfile ${DIR}/..
|
||||
rm -rf ./modstatsd
|
||||
|
||||
# Build and publish
|
||||
if [[ ${FLAGS_provider} == "aws" ]] || [[ ${FLAGS_provider} == "both" ]]
|
||||
then
|
||||
export ECR="520173307535.dkr.ecr.us-east-1.amazonaws.com"
|
||||
echo "`timestamp` Preparing to push image to AWS, ECR: ${ECR}"
|
||||
aws ecr get-login-password --profile caching-infra-images-editor --region us-east-1 | docker login --username AWS --password-stdin ${ECR}
|
||||
push_image ${IMAGE_SUFFIX} ${ECR}
|
||||
fi
|
||||
|
||||
if [[ ${FLAGS_provider} == "gcp" ]] || [[ ${FLAGS_provider} == "both" ]]
|
||||
then
|
||||
export GCR="gcr.io/caching-infra"
|
||||
echo "`timestamp` Preparing to push to GCP, GCR: ${GCR}"
|
||||
push_image ${IMAGE_SUFFIX} ${GCR}
|
||||
fi
|
||||
@@ -1,118 +0,0 @@
|
||||
FROM ubuntu:20.04
|
||||
SHELL ["/bin/bash","-c"]
|
||||
RUN groupadd -r keydb && useradd -r -g keydb keydb
|
||||
# use gosu for easy step-down from root: https://github.com/tianon/gosu/releases
|
||||
ENV GOSU_VERSION 1.14
|
||||
RUN set -eux; \
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ca-certificates dirmngr gnupg wget; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
chmod +x /usr/local/bin/gosu; \
|
||||
gosu --version; \
|
||||
gosu nobody true
|
||||
# build KeyDB
|
||||
ARG BRANCH
|
||||
RUN --mount=type=secret,id=PAT export $(cat /run/secrets/PAT | xargs); \
|
||||
set -eux; \
|
||||
\
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -qqy --no-install-recommends \
|
||||
dpkg-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
nasm \
|
||||
autotools-dev \
|
||||
autoconf \
|
||||
libjemalloc-dev \
|
||||
tcl \
|
||||
tcl-dev \
|
||||
uuid-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libbz2-dev \
|
||||
libzstd-dev \
|
||||
liblz4-dev \
|
||||
libsnappy-dev \
|
||||
libssl-dev \
|
||||
git; \
|
||||
cd /tmp && git clone --branch $BRANCH https://$PAT_ID:$TOKEN@github.sc-corp.net/Snapchat/keydb-internal.git --recursive; \
|
||||
cd /tmp/keydb-internal; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make -j$(nproc) BUILD_TLS=yes NO_LICENSE_CHECK=yes; \
|
||||
cd src; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
# clean up unused dependencies
|
||||
echo $savedAptMark; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
find /usr/local -type f -executable -exec ldd '{}' ';' \
|
||||
| awk '/=>/ { print $(NF-1) }' \
|
||||
| sed 's:.*/::' \
|
||||
| sort -u \
|
||||
| xargs -r dpkg-query --search \
|
||||
| cut -d: -f1 \
|
||||
| sort -u \
|
||||
| xargs -r apt-mark manual \
|
||||
; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
# create working directories and organize files
|
||||
RUN \
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/keydb-internal/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# first arg is '-f' or '--some-option'" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# or first arg is `something.conf`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec gosu keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
# set remaining image properties
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENV KEYDB_PRO_DIRECTORY=/usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server","/etc/keydb/keydb.conf"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
PAT_ID=
|
||||
TOKEN=
|
||||
@@ -1,24 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ARG RUNNER_VERSION=2.287.1
|
||||
ARG TOKEN
|
||||
ARG NAME=internal-runner
|
||||
|
||||
USER root
|
||||
|
||||
WORKDIR /actions-runner
|
||||
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y curl \
|
||||
&& curl -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
|
||||
&& tar xzf ./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
|
||||
&& ./bin/installdependencies.sh \
|
||||
&& rm ./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
|
||||
&& apt-get purge -y \
|
||||
&& chown 1000:1000 -R /actions-runner
|
||||
|
||||
USER 1000
|
||||
|
||||
RUN ./config.sh --url https://github.com/EQ-Alpha/KeyDB --token ${TOKEN} logout --name ${NAME} --unattended
|
||||
|
||||
ENTRYPOINT ["./run.sh"]
|
||||
@@ -1,44 +0,0 @@
|
||||
# Github action runner.
|
||||
|
||||
## Overview
|
||||
All details are [here](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners).
|
||||
In brief overview, self-hosted action runner is running on our hosts, connects to the github and waits for the
|
||||
jobs to execute. Bellow is instruction on how to create image for the runner and start it in our Kubernetes
|
||||
cluster.
|
||||
|
||||
## Creating a new runner.
|
||||
|
||||
1. Got [github](https://github.com/EQ-Alpha/KeyDB/settings/actions/runners/new?arch=x64&os=linux) to create a new runner.
|
||||
2. The link above should display a script for installing runner. It should be aligned with what we have in a
|
||||
[Dockerfile](https://github.sc-corp.net/Snapchat/keydb-internal/github-action-runner-docker/Dockerfile).
|
||||
3. Take the token from that script. Should be in "Configure" section:
|
||||
```
|
||||
./config.sh --url https://github.com/EQ-Alpha/KeyDB --token AUQJRVZIQCLO4ZQOZAOC3L3B7RHIU
|
||||
```
|
||||
4. Take the version of the runner from the "Download" section from file name. Example is "2.287.1",
|
||||
5. Build and public the image:
|
||||
```
|
||||
./build-and-publish.sh --token <token> --version <version>
|
||||
```
|
||||
Docker will register runner in Github during the build. If you got an error
|
||||
```A runner exists with the same name```, it means you've already built the image
|
||||
for runner with that name. In this case you can either give a new name to the runner
|
||||
(using ```--name <name>``` parameter to the build script) or delete existing runner in
|
||||
[github](https://github.com/EQ-Alpha/KeyDB/settings/actions/runners). Deleting the
|
||||
runner will break existing runners running in our infrastructure. You may want to
|
||||
delete them.
|
||||
6. The default image is "[gcr.io/caching-infra/keydb-github-action-runner](https://console.cloud.google.com/gcr/images/caching-infra/global/keydb-github-action-runner?project=caching-infra)"
|
||||
but you can set any other image full name to the building script (```--image <full-image-name>```).
|
||||
7. Deploy new image to cluster [caching-infra--t-us-east4--staging](https://switchboard.sc-corp.net/#/services/caching-infra/cloud-resource/caching-infra--t-us-east4--staging/manage?region=us-east4&provider=GOOGLE&project_id=caching-infra):
|
||||
* Configure kubectl for the cluster.
|
||||
* Delete existing runner using [deployment manifest](https://github.sc-corp.net/Snapchat/keydb-internal/github-action-runner-docker/deployment.yaml):
|
||||
```
|
||||
kubectl delete -f deployment.yaml
|
||||
```
|
||||
* Start a new runner:
|
||||
```
|
||||
kubectl apply -f deployment.yaml
|
||||
```
|
||||
Deployment manifest uses [gcr.io/caching-infra/keydb-github-action-runner](https://console.cloud.google.com/gcr/images/caching-infra/global/keydb-github-action-runner?project=caching-infra).
|
||||
If you specified another image name in build script, update deployment manifest with an appropriate image.
|
||||
8. Validate on [github](https://github.com/EQ-Alpha/KeyDB/settings/actions/runners) that new runner is online.
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# Color for displaying error messages
|
||||
red=`tput setaf 1`
|
||||
reset=`tput sgr0`
|
||||
|
||||
# Default value for the command line flag
|
||||
DEFAULT_VERSION="2.287.1"
|
||||
DEFAULT_IMG="gcr.io/caching-infra/keydb-github-action-runner:latest"
|
||||
DEFAULT_NAME="internal-runner"
|
||||
FLAGS_token=""
|
||||
FLAGS_version=$DEFAULT_VERSION
|
||||
FLAGS_img=$DEFAULT_IMG
|
||||
FLAGS_name=$DEFAULT_NAME
|
||||
|
||||
|
||||
timestamp() {
|
||||
date "+%m/%d %H:%M:%S"
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo "Usage:"
|
||||
echo " ./{script} [flags]"
|
||||
echo ""
|
||||
echo "Flags:"
|
||||
echo " -t, --token: Token taken from onboarding script from github. (required)"
|
||||
echo " -i, --image: Full docker image name you want to build and push. Default value is ${DEFAULT_IMG}"
|
||||
echo " -v, --version: Action runner version. Can be taked from onboarding script from github. Default version is ${DEFAULT_VERSION}."
|
||||
echo " -n, --name: Action runner name. Default name is ${DEFAULT_NAME}."
|
||||
}
|
||||
|
||||
# Processing flags
|
||||
while [ ! $# -eq 0 ]
|
||||
do
|
||||
# The shift below ensures the unprocessed flag is always at $1
|
||||
case "$1" in
|
||||
--help | -h)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
--image | -i)
|
||||
FLAGS_img="$2"
|
||||
shift
|
||||
;;
|
||||
--token | -t)
|
||||
FLAGS_token="$2"
|
||||
shift
|
||||
;;
|
||||
--version | -v)
|
||||
FLAGS_version="$2"
|
||||
shift
|
||||
;;
|
||||
--name | -n)
|
||||
FLAGS_name="$2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "${red}"
|
||||
echo "Unrecognized flag: $1."
|
||||
echo "Run with '--help' flag to see the supported flags."
|
||||
echo "${reset}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ ${FLAGS_token} == "" ]]; then
|
||||
echo "${red} ERROR:token is missing"
|
||||
echo ${reset}
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building image ${FLAGS_img} ..."
|
||||
docker build --build-arg TOKEN=${FLAGS_token} --build-arg RUNNER_VERSION=${FLAGS_version} --build-arg NAME=${FLAGS_name} -t ${FLAGS_img} .
|
||||
echo "Pushing image ${FLAGS_img}...."
|
||||
docker push ${FLAGS_img}
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: keydb-runner
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: keydb-runner
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: keydb-runner
|
||||
spec:
|
||||
containers:
|
||||
- name: keydb-runner
|
||||
image: gcr.io/caching-infra/keydb-github-action-runner:latest
|
||||
# Since it's 'latest' tag we're reusing it.
|
||||
imagePullPolicy: Always
|
||||
+18
-2
@@ -184,7 +184,9 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-client-key-file-pass secret
|
||||
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange,
|
||||
# required by older versions of OpenSSL (<3.0). Newer versions do not require
|
||||
# this configuration and recommend against it.
|
||||
#
|
||||
# tls-dh-params-file keydb.dh
|
||||
|
||||
@@ -2071,4 +2073,18 @@ replica-weighting-factor 2
|
||||
#
|
||||
# By default this is enabled
|
||||
#
|
||||
active-client-balancing yes
|
||||
active-client-balancing yes
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# make the build
|
||||
git submodule init && git submodule update
|
||||
make BUILD_TLS=yes -j$(nproc) KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror'
|
||||
|
||||
# gen-cert
|
||||
./utils/gen-test-certs.sh
|
||||
@@ -2063,3 +2063,17 @@ server-threads 2
|
||||
#
|
||||
# By default KeyDB sets this to 2.
|
||||
replica-weighting-factor 2
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
@@ -19,12 +19,17 @@ elif [ "$distributor" == "Ubuntu" ]; then
|
||||
fi
|
||||
codename=$(lsb_release --codename --short)
|
||||
date=$(date +%a," "%d" "%b" "%Y" "%T)
|
||||
|
||||
# overwrite debian bookworm version until updated
|
||||
if [ $codename == "bookworm" ]; then
|
||||
distname=+deb12u1
|
||||
fi
|
||||
pkg_name=keydb-$majorv:$version$distname
|
||||
|
||||
# create build tree
|
||||
cd ../../../
|
||||
tar -czvf keydb_$version.orig.tar.gz --force-local keydb-internal
|
||||
cd keydb-internal/pkg/deb/
|
||||
tar -czvf keydb_$version.orig.tar.gz --force-local KeyDB
|
||||
cd KeyDB/pkg/deb/
|
||||
mkdir -p $pkg_name/tmp
|
||||
if [[ "$codename" == "xenial" ]] || [[ "$codename" == "stretch" ]]; then
|
||||
cp -r debian_dh9 $pkg_name/tmp/debian
|
||||
|
||||
@@ -26,8 +26,8 @@ Build-Depends:
|
||||
libzstd-dev <!nocheck>
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://docs.keydb.dev/
|
||||
Vcs-Git: https://github.com/EQ-Alpha/KeyDB.git
|
||||
Vcs-Browser: https://github.com/EQ-Alpha/KeyDB
|
||||
Vcs-Git: https://github.com/Snapchat/KeyDB.git
|
||||
Vcs-Browser: https://github.com/Snapchat/KeyDB
|
||||
|
||||
Package: keydb
|
||||
Architecture: all
|
||||
|
||||
@@ -4,6 +4,7 @@ include /usr/share/dpkg/buildflags.mk
|
||||
|
||||
export BUILD_TLS=yes
|
||||
export USE_SYSTEMD=yes
|
||||
export ENABLE_FLASH=yes
|
||||
export CFLAGS CPPFLAGS LDFLAGS
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
|
||||
|
||||
@@ -25,8 +25,8 @@ Build-Depends:
|
||||
libzstd-dev <!nocheck>
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://docs.keydb.dev/
|
||||
Vcs-Git: https://github.com/EQ-Alpha/KeyDB.git
|
||||
Vcs-Browser: https://github.com/EQ-Alpha/KeyDB
|
||||
Vcs-Git: https://github.com/Snapchat/KeyDB.git
|
||||
Vcs-Browser: https://github.com/Snapchat/KeyDB
|
||||
|
||||
Package: keydb
|
||||
Architecture: all
|
||||
|
||||
@@ -10,6 +10,7 @@ include /usr/share/dpkg/buildflags.mk
|
||||
|
||||
export BUILD_TLS=yes
|
||||
export USE_SYSTEMD=yes
|
||||
export ENABLE_FLASH=yes
|
||||
export CFLAGS CPPFLAGS LDFLAGS
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
keydb (6:6.3.1-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This point release contains fixes to bugs related to expires, active-rep, and rdb saving
|
||||
* Issues fixed: #419, #422, #428
|
||||
* PRs: #426, #429, #431, #433
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Mon, 23 May 2022 20:00:37 +0000
|
||||
|
||||
keydb (6:6.3.0-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This release open sources KeyDB Enterprise features into the open source project along with PSYNC for active replication
|
||||
|
||||
@@ -77,7 +77,7 @@ RUN \
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/keydb-internal/keydb.conf /etc/keydb/; \
|
||||
cp /tmp/KeyDB/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
@@ -110,8 +110,3 @@ ENV KEYDB_PRO_DIRECTORY=/usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server","/etc/keydb/keydb.conf"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
FROM alpine:3.12
|
||||
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
|
||||
RUN addgroup -S -g 1000 keydb && adduser -S -G keydb -u 999 keydb
|
||||
RUN mkdir -p /etc/keydb
|
||||
ARG BRANCH
|
||||
RUN set -eux; \
|
||||
\
|
||||
apk add --no-cache su-exec; \
|
||||
apk add --no-cache --virtual .build-deps \
|
||||
coreutils \
|
||||
gcc \
|
||||
linux-headers \
|
||||
make \
|
||||
musl-dev \
|
||||
openssl-dev \
|
||||
git \
|
||||
util-linux-dev \
|
||||
curl-dev \
|
||||
g++ \
|
||||
libunwind-dev \
|
||||
bash \
|
||||
perl \
|
||||
git \
|
||||
bzip2-dev \
|
||||
zstd-dev \
|
||||
lz4-dev \
|
||||
snappy-dev \
|
||||
; \
|
||||
cd /tmp && git clone --branch $BRANCH https://github.com/Snapchat/KeyDB.git --recursive; \
|
||||
cd /tmp/KeyDB; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make -j$(nproc) BUILD_TLS=yes; \
|
||||
cd src; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel keydb-server; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
runDeps="$( \
|
||||
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
|
||||
| tr ',' '\n' \
|
||||
| sort -u \
|
||||
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
|
||||
)"; \
|
||||
apk add --no-network --virtual .keydb-rundeps $runDeps; \
|
||||
apk del --no-network .build-deps; \
|
||||
# create working directories and organize files
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/KeyDB/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# first arg is '-f' or '--some-option'" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# or first arg is `something.conf`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec su-exec keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server", "/etc/keydb/keydb.conf"]
|
||||
@@ -2063,3 +2063,17 @@ server-threads 2
|
||||
#
|
||||
# By default KeyDB sets this to 2.
|
||||
replica-weighting-factor 2
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
@@ -37,5 +37,6 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/hash \
|
||||
--single unit/moduleapi/zset \
|
||||
--single unit/moduleapi/stream \
|
||||
--single unit/moduleapi/load \
|
||||
--config server-threads 3 \
|
||||
"${@}"
|
||||
+14
-4
@@ -20,12 +20,12 @@ 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')
|
||||
OPTIMIZATION?=-O2 -flto
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram rocksdb
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-pedantic -DREDIS_STATIC=''
|
||||
CXX_STD=-std=c++14 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
|
||||
CXX_STD=-std=c++17 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
STD+=-Wno-c11-extensions
|
||||
else
|
||||
@@ -62,6 +62,7 @@ endif
|
||||
endif
|
||||
|
||||
USEASM?=true
|
||||
ENABLE_FLASH?=no
|
||||
|
||||
ifneq ($(strip $(SANITIZE)),)
|
||||
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE -fno-omit-frame-pointer
|
||||
@@ -71,6 +72,15 @@ ifneq ($(strip $(SANITIZE)),)
|
||||
USEASM=false
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_FLASH),yes)
|
||||
FINAL_LIBS+= -lz -lcrypto -lbz2 -lzstd -llz4 -lsnappy
|
||||
CXXFLAGS+= -I../deps/rocksdb/include/ -DENABLE_ROCKSDB
|
||||
STORAGE_OBJ+= storage/rocksdb.o storage/rocksdbfactory.o
|
||||
FINAL_LIBS+= ../deps/rocksdb/librocksdb.a
|
||||
DEPENDENCY_TARGETS+= rocksdb
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(CHECKED),true)
|
||||
CXXFLAGS+= -DCHECKED_BUILD
|
||||
endif
|
||||
@@ -366,7 +376,7 @@ endif
|
||||
|
||||
REDIS_SERVER_NAME=keydb-server$(PROG_SUFFIX)
|
||||
REDIS_SENTINEL_NAME=keydb-sentinel$(PROG_SUFFIX)
|
||||
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 t_nhash.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 scripting.o bio.o rio.o rand.o memtest.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 acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd_server.o timeout.o setcpuaffinity.o AsyncWorkQueue.o snapshot.o storage/rocksdb.o storage/rocksdbfactory.o storage/teststorageprovider.o keydbutils.o StorageCache.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
|
||||
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 t_nhash.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 scripting.o bio.o rio.o rand.o memtest.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 acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd_server.o timeout.o setcpuaffinity.o AsyncWorkQueue.o snapshot.o storage/teststorageprovider.o keydbutils.o StorageCache.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ) $(STORAGE_OBJ)
|
||||
KEYDB_SERVER_OBJ=SnapshotPayloadParseState.o
|
||||
REDIS_CLI_NAME=keydb-cli$(PROG_SUFFIX)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o motd_client.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
|
||||
@@ -427,7 +437,7 @@ endif
|
||||
|
||||
# keydb-server
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(KEYDB_SERVER_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/rocksdb/librocksdb.a $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)
|
||||
|
||||
# keydb-sentinel
|
||||
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
|
||||
|
||||
@@ -140,7 +140,7 @@ void SnapshotPayloadParseState::flushQueuedKeys() {
|
||||
auto &insertsInFlightTmp = insertsInFlight; // C++ GRRRRRRRRRRRRRRRR, we don't want to capute "this" because that's dangerous
|
||||
if (current_database < cserver.dbnum) {
|
||||
g_pserver->asyncworkqueue->AddWorkFunction([idb, vecqueuedKeys = std::move(this->vecqueuedKeys), vecqueuedKeysCb = std::move(this->vecqueuedKeysCb), vecqueuedVals = std::move(this->vecqueuedVals), vecqueuedValsCb = std::move(this->vecqueuedValsCb), &insertsInFlightTmp, pallocator = m_spallocator.release()]() mutable {
|
||||
g_pserver->db[idb]->bulkStorageInsert(vecqueuedKeys.data(), vecqueuedKeysCb.data(), vecqueuedVals.data(), vecqueuedValsCb.data(), vecqueuedKeys.size());
|
||||
g_pserver->db[idb]->bulkDirectStorageInsert(vecqueuedKeys.data(), vecqueuedKeysCb.data(), vecqueuedVals.data(), vecqueuedValsCb.data(), vecqueuedKeys.size());
|
||||
--insertsInFlightTmp;
|
||||
delete pallocator;
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ dictType dbStorageCacheType = {
|
||||
StorageCache::StorageCache(IStorage *storage, bool fCache)
|
||||
: m_spstorage(storage)
|
||||
{
|
||||
if (fCache)
|
||||
if (!g_pserver->flash_disable_key_cache && fCache)
|
||||
m_pdict = dictCreate(&dbStorageCacheType, nullptr);
|
||||
}
|
||||
|
||||
@@ -205,6 +205,7 @@ void StorageCache::beginWriteBatch() {
|
||||
}
|
||||
|
||||
void StorageCache::emergencyFreeCache() {
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
dict *d = m_pdict;
|
||||
m_pdict = nullptr;
|
||||
if (d != nullptr) {
|
||||
|
||||
+10
-5
@@ -716,11 +716,6 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
if (eventLoop->flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP) {
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE)) {
|
||||
@@ -731,6 +726,11 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
}
|
||||
|
||||
if (eventLoop->flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
/* Call the multiplexing API, will return only on timeout or when
|
||||
* some event fires. */
|
||||
numevents = aeApiPoll(eventLoop, tvp);
|
||||
@@ -859,6 +859,11 @@ void aeReleaseForkLock()
|
||||
g_forkLock.downgradeWrite();
|
||||
}
|
||||
|
||||
void aeForkLockInChild()
|
||||
{
|
||||
g_forkLock.setNotify(false);
|
||||
}
|
||||
|
||||
int aeThreadOwnsLock()
|
||||
{
|
||||
return fOwnLockOverride || g_lock.fOwnLock();
|
||||
|
||||
@@ -171,6 +171,7 @@ int aeTryAcquireLock(int fWeak);
|
||||
void aeThreadOffline();
|
||||
void aeReleaseLock();
|
||||
void aeReleaseForkLock();
|
||||
void aeForkLockInChild();
|
||||
int aeThreadOwnsLock();
|
||||
void aeSetThreadOwnsLockOverride(int fOverride);
|
||||
int aeLockContested(int threshold);
|
||||
|
||||
+1
-1
@@ -441,7 +441,7 @@ static int _anetTcpServer(char *err, int port, const char *bindaddr, int af, int
|
||||
char _port[6]; /* strlen("65535") */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
|
||||
snprintf(_port,6,"%d",port);
|
||||
snprintf(_port,sizeof(_port),"%d",port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
+11
-11
@@ -285,7 +285,7 @@ int startAppendOnly(void) {
|
||||
strerror(errno));
|
||||
return C_ERR;
|
||||
}
|
||||
if (hasActiveChildProcess() && g_pserver->child_type != CHILD_TYPE_AOF) {
|
||||
if (hasActiveChildProcessOrBGSave() && g_pserver->child_type != CHILD_TYPE_AOF) {
|
||||
g_pserver->aof_rewrite_scheduled = 1;
|
||||
serverLog(LL_WARNING,"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.");
|
||||
} else {
|
||||
@@ -438,7 +438,7 @@ void flushAppendOnlyFile(int force) {
|
||||
* useful for graphing / monitoring purposes. */
|
||||
if (sync_in_progress) {
|
||||
latencyAddSampleIfNeeded("aof-write-pending-fsync",latency);
|
||||
} else if (hasActiveChildProcess()) {
|
||||
} else if (hasActiveChildProcessOrBGSave()) {
|
||||
latencyAddSampleIfNeeded("aof-write-active-child",latency);
|
||||
} else {
|
||||
latencyAddSampleIfNeeded("aof-write-alone",latency);
|
||||
@@ -535,7 +535,7 @@ void flushAppendOnlyFile(int force) {
|
||||
try_fsync:
|
||||
/* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are
|
||||
* children doing I/O in the background. */
|
||||
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcess())
|
||||
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcessOrBGSave())
|
||||
return;
|
||||
|
||||
/* Perform the fsync if needed. */
|
||||
@@ -752,7 +752,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
* accumulate the differences between the child DB and the current one
|
||||
* in a buffer, so that when the child process will do its work we
|
||||
* can append the differences to the new append only file. */
|
||||
if (g_pserver->child_type == CHILD_TYPE_AOF)
|
||||
if (hasActiveChildProcess() && g_pserver->child_type == CHILD_TYPE_AOF)
|
||||
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
|
||||
|
||||
sdsfree(buf);
|
||||
@@ -1658,7 +1658,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
{ // BEGIN GOTO SCOPED VARIABLES
|
||||
/* Note that we have to use a different temp name here compared to the
|
||||
* one used by rewriteAppendOnlyFileBackground() function. */
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-%d.aof", (int) getpid());
|
||||
fp = fopen(tmpfile,"w");
|
||||
if (!fp) {
|
||||
serverLog(LL_WARNING, "Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s", strerror(errno));
|
||||
@@ -1887,7 +1887,7 @@ void aofClosePipes(void) {
|
||||
int rewriteAppendOnlyFileBackground(void) {
|
||||
pid_t childpid;
|
||||
|
||||
if (hasActiveChildProcess()) return C_ERR;
|
||||
if (hasActiveChildProcessOrBGSave()) return C_ERR;
|
||||
if (aofCreatePipes() != C_OK) return C_ERR;
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
@@ -1895,7 +1895,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
/* Child */
|
||||
redisSetProcTitle("keydb-aof-rewrite");
|
||||
redisSetCpuAffinity(g_pserver->aof_rewrite_cpulist);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
|
||||
sendChildCowInfo(CHILD_INFO_TYPE_AOF_COW_SIZE, "AOF rewrite");
|
||||
exitFromChild(0);
|
||||
@@ -1930,7 +1930,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
void bgrewriteaofCommand(client *c) {
|
||||
if (g_pserver->child_type == CHILD_TYPE_AOF) {
|
||||
addReplyError(c,"Background append only file rewriting already in progress");
|
||||
} else if (hasActiveChildProcess()) {
|
||||
} else if (hasActiveChildProcessOrBGSave()) {
|
||||
g_pserver->aof_rewrite_scheduled = 1;
|
||||
addReplyStatus(c,"Background append only file rewriting scheduled");
|
||||
} else if (rewriteAppendOnlyFileBackground() == C_OK) {
|
||||
@@ -1944,10 +1944,10 @@ void bgrewriteaofCommand(client *c) {
|
||||
void aofRemoveTempFile(pid_t childpid) {
|
||||
char tmpfile[256];
|
||||
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid);
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof", (int) childpid);
|
||||
bg_unlink(tmpfile);
|
||||
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) childpid);
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-%d.aof", (int) childpid);
|
||||
bg_unlink(tmpfile);
|
||||
}
|
||||
|
||||
@@ -1985,7 +1985,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
/* Flush the differences accumulated by the parent to the
|
||||
* rewritten AOF. */
|
||||
latencyStartMonitor(latency);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof",
|
||||
(int)g_pserver->child_pid);
|
||||
newfd = open(tmpfile,O_WRONLY|O_APPEND);
|
||||
if (newfd == -1) {
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@ handle_wrap:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {
|
||||
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, int bits, int owtype, int64_t *limit) {
|
||||
int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);
|
||||
int64_t min = (-max)-1;
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ typedef struct {
|
||||
* RDB / AOF saving process from the child to the parent (for instance
|
||||
* the amount of copy on write memory used) */
|
||||
void openChildInfoPipe(void) {
|
||||
serverAssert(g_pserver->child_info_pipe[0] == -1);
|
||||
if (pipe(g_pserver->child_info_pipe) == -1) {
|
||||
/* On error our two file descriptors should be still set to -1,
|
||||
* but we call anyway closeChildInfoPipe() since can't hurt. */
|
||||
|
||||
+1
-1
@@ -5107,7 +5107,7 @@ void createDumpPayload(rio *payload, robj_roptr o, robj *key) {
|
||||
serverAssert(rdbSaveObject(payload,o,key));
|
||||
char szT[32];
|
||||
uint64_t mvcc = mvccFromObj(o);
|
||||
snprintf(szT, 32, "%" PRIu64, mvcc);
|
||||
snprintf(szT, sizeof(szT), "%" PRIu64, mvcc);
|
||||
serverAssert(rdbSaveAuxFieldStrStr(payload,"mvcc-tstamp", szT) != -1);
|
||||
|
||||
/* Write the footer, this is how it looks like:
|
||||
|
||||
+48
-4
@@ -35,11 +35,13 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
const char *KEYDB_SET_VERSION = KEYDB_REAL_VERSION;
|
||||
size_t g_semiOrderedSetTargetBucketSize = 0; // Its a header only class so nowhere else for this to go
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Config file name-value maps.
|
||||
@@ -355,11 +357,17 @@ bool initializeStorageProvider(const char **err)
|
||||
return true;
|
||||
if (!strcasecmp(g_sdsProvider, "flash") && g_sdsArgs != nullptr)
|
||||
{
|
||||
#ifdef ENABLE_ROCKSDB
|
||||
// Create The Storage Factory (if necessary)
|
||||
serverLog(LL_NOTICE, "Initializing FLASH storage provider (this may take a long time)");
|
||||
adjustOpenFilesLimit();
|
||||
g_pserver->m_pstorageFactory = CreateRocksDBStorageFactory(g_sdsArgs, cserver.dbnum, cserver.storage_conf, cserver.storage_conf ? strlen(cserver.storage_conf) : 0);
|
||||
}
|
||||
#else
|
||||
serverLog(LL_WARNING, "To use the flash storage provider please compile KeyDB with ENABLE_FLASH=yes");
|
||||
serverLog(LL_WARNING, "Exiting due to the use of an unsupported storage provider");
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
else if (!strcasecmp(g_sdsProvider, "test") && g_sdsArgs == nullptr)
|
||||
{
|
||||
g_pserver->m_pstorageFactory = new (MALLOC_LOCAL) TestStorageFactory();
|
||||
@@ -761,6 +769,12 @@ void loadServerConfigFromString(char *config) {
|
||||
g_sdsProvider = sdsdup(argv[1]);
|
||||
if (argc > 2)
|
||||
g_sdsArgs = sdsdup(argv[2]);
|
||||
} else if (!strcasecmp(argv[0],"is-flash-enabled") && argc == 1) {
|
||||
#ifdef ENABLE_ROCKSDB
|
||||
exit(EXIT_SUCCESS);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
} else {
|
||||
err = "Bad directive or wrong number of arguments"; goto loaderr;
|
||||
}
|
||||
@@ -2443,6 +2457,32 @@ static int isValidAOFfilename(char *val, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isValidS3Bucket(char *s3bucket, const char **err) {
|
||||
int status = EXIT_FAILURE;
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
*err = "couldn't fork to call aws cli";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
execlp("aws", "aws", "s3", "ls", s3bucket);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
if (status != EXIT_SUCCESS) {
|
||||
*err = "could not access s3 bucket";
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Validate specified string is a valid proc-title-template */
|
||||
static int isValidProcTitleTemplate(char *val, const char **err) {
|
||||
if (!validateProcTitleTemplate(val)) {
|
||||
@@ -2575,7 +2615,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
|
||||
adjustOpenFilesLimit();
|
||||
if (g_pserver->maxclients != val) {
|
||||
static char msg[128];
|
||||
sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
|
||||
snprintf(msg, sizeof(msg), "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
|
||||
*err = msg;
|
||||
if (g_pserver->maxclients > prev) {
|
||||
g_pserver->maxclients = prev;
|
||||
@@ -2614,7 +2654,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
|
||||
|
||||
if (res != AE_OK){
|
||||
static char msg[128];
|
||||
sprintf(msg, "Failed to post the request to change setsize for Thread %d", iel);
|
||||
snprintf(msg, sizeof(msg),"Failed to post the request to change setsize for Thread %d", iel);
|
||||
*err = msg;
|
||||
return 0;
|
||||
}
|
||||
@@ -2766,7 +2806,7 @@ standardConfig configs[] = {
|
||||
createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG, g_pserver->aof_enabled, 0, NULL, updateAppendonly),
|
||||
createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, g_pserver->cluster_allow_reads_when_down, 0, NULL, NULL),
|
||||
createBoolConfig("delete-on-evict", NULL, MODIFIABLE_CONFIG, cserver.delete_on_evict, 0, NULL, NULL),
|
||||
createBoolConfig("use-fork", NULL, IMMUTABLE_CONFIG, cserver.fForkBgSave, 0, NULL, NULL),
|
||||
createBoolConfig("use-fork", NULL, IMMUTABLE_CONFIG, cserver.fForkBgSave, 1, NULL, NULL),
|
||||
createBoolConfig("io-threads-do-reads", NULL, IMMUTABLE_CONFIG, fDummy, 0, NULL, NULL),
|
||||
createBoolConfig("time-thread-priority", NULL, IMMUTABLE_CONFIG, cserver.time_thread_priority, 0, NULL, NULL),
|
||||
createBoolConfig("prefetch-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->prefetch_enabled, 1, NULL, NULL),
|
||||
@@ -2790,6 +2830,7 @@ standardConfig configs[] = {
|
||||
createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->cluster_announce_ip, NULL, NULL, NULL),
|
||||
createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->syslog_ident, "redis", NULL, NULL),
|
||||
createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->rdb_filename, CONFIG_DEFAULT_RDB_FILENAME, isValidDBfilename, NULL),
|
||||
createStringConfig("db-s3-object", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->rdb_s3bucketpath, NULL, isValidS3Bucket, NULL),
|
||||
createStringConfig("appendfilename", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->aof_filename, "appendonly.aof", isValidAOFfilename, NULL),
|
||||
createStringConfig("server_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->server_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bio_cpulist, NULL, NULL, NULL),
|
||||
@@ -2871,6 +2912,7 @@ standardConfig configs[] = {
|
||||
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
|
||||
createLongLongConfig("repl-backlog-disk-reserve", NULL, IMMUTABLE_CONFIG, 0, LLONG_MAX, cserver.repl_backlog_disk_size, 0, MEMORY_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("max-snapshot-slip", NULL, MODIFIABLE_CONFIG, 0, 5000, g_pserver->snapshot_slip, 400, 0, NULL, NULL),
|
||||
createLongLongConfig("max-rand-count", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX/2, g_pserver->rand_total_threshold, LONG_MAX/2, 0, NULL, NULL),
|
||||
|
||||
/* Unsigned Long Long configs */
|
||||
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
|
||||
@@ -2898,6 +2940,8 @@ standardConfig configs[] = {
|
||||
createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL),
|
||||
createBoolConfig("force-backlog-disk-reserve", NULL, MODIFIABLE_CONFIG, cserver.force_backlog_disk, 0, NULL, NULL),
|
||||
createBoolConfig("soft-shutdown", NULL, MODIFIABLE_CONFIG, g_pserver->config_soft_shutdown, 0, NULL, NULL),
|
||||
createBoolConfig("flash-disable-key-cache", NULL, MODIFIABLE_CONFIG, g_pserver->flash_disable_key_cache, 0, NULL, NULL),
|
||||
createSizeTConfig("semi-ordered-set-bucket-size", NULL, MODIFIABLE_CONFIG, 0, 1024, g_semiOrderedSetTargetBucketSize, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, g_pserver->tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */
|
||||
|
||||
+64
-30
@@ -93,7 +93,7 @@ static void lookupKeyUpdateObj(robj *val, int flags)
|
||||
/* Update the access time for the ageing algorithm.
|
||||
* Don't do it if we have a saving child, as this will trigger
|
||||
* a copy on write madness. */
|
||||
if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH))
|
||||
if (!hasActiveChildProcessOrBGSave() && !(flags & LOOKUP_NOTOUCH))
|
||||
{
|
||||
if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
||||
updateLFU(val);
|
||||
@@ -1059,7 +1059,7 @@ void keysCommand(client *c) {
|
||||
sds pattern = szFromObj(c->argv[1]);
|
||||
|
||||
const redisDbPersistentDataSnapshot *snapshot = nullptr;
|
||||
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED)))
|
||||
if (!(c->flags & (CLIENT_MULTI | CLIENT_BLOCKED | CLIENT_DENY_BLOCKING)))
|
||||
snapshot = c->db->createSnapshot(c->mvccCheckpoint, true /* fOptional */);
|
||||
if (snapshot != nullptr)
|
||||
{
|
||||
@@ -1573,6 +1573,12 @@ void moveCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Return zero if the key already exists in the target DB */
|
||||
if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
|
||||
addReply(c,shared.czero);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<expireEntry> spexpire;
|
||||
{ // scope pexpireOld
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
@@ -1587,12 +1593,6 @@ void moveCommand(client *c) {
|
||||
dbDelete(src,c->argv[1]);
|
||||
g_pserver->dirty++;
|
||||
|
||||
/* Return zero if the key already exists in the target DB */
|
||||
if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
|
||||
addReply(c,shared.czero);
|
||||
decrRefCount(o);
|
||||
return;
|
||||
}
|
||||
dbAdd(dst,c->argv[1],o);
|
||||
if (spexpire != nullptr) setExpire(c,dst,c->argv[1],std::move(*spexpire));
|
||||
|
||||
@@ -1737,6 +1737,9 @@ int dbSwapDatabases(int id1, int id2) {
|
||||
id2 < 0 || id2 >= cserver.dbnum) return C_ERR;
|
||||
if (id1 == id2) return C_OK;
|
||||
std::swap(g_pserver->db[id1], g_pserver->db[id2]);
|
||||
|
||||
//swap db's id too, otherwise db does not match its id
|
||||
std::swap(g_pserver->db[id1]->id, g_pserver->db[id2]->id);
|
||||
|
||||
/* Note that we don't swap blocking_keys,
|
||||
* ready_keys and watched_keys, since we want clients to
|
||||
@@ -1766,7 +1769,7 @@ int dbSwapDatabases(int id1, int id2) {
|
||||
|
||||
/* SWAPDB db1 db2 */
|
||||
void swapdbCommand(client *c) {
|
||||
int id1, id2;
|
||||
int id1, id2, oriId;
|
||||
|
||||
/* Not allowed in cluster mode: we have just DB 0 there. */
|
||||
if (g_pserver->cluster_enabled) {
|
||||
@@ -1783,6 +1786,9 @@ void swapdbCommand(client *c) {
|
||||
"invalid second DB index") != C_OK)
|
||||
return;
|
||||
|
||||
//get client's original db's id
|
||||
oriId=c->db->id;
|
||||
|
||||
/* Swap... */
|
||||
if (dbSwapDatabases(id1,id2) == C_ERR) {
|
||||
addReplyError(c,"DB index is out of range");
|
||||
@@ -1791,6 +1797,10 @@ void swapdbCommand(client *c) {
|
||||
RedisModuleSwapDbInfo si = {REDISMODULE_SWAPDBINFO_VERSION,(int32_t)id1,(int32_t)id2};
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_SWAPDB,0,&si);
|
||||
g_pserver->dirty++;
|
||||
|
||||
//set client's db to original db
|
||||
c->db=g_pserver->db[oriId];
|
||||
|
||||
addReply(c,shared.ok);
|
||||
}
|
||||
}
|
||||
@@ -1931,6 +1941,7 @@ void setExpire(client *c, redisDb *db, robj *key, expireEntry &&e)
|
||||
* is associated with this key (i.e. the key is non volatile) */
|
||||
expireEntry *redisDbPersistentDataSnapshot::getExpire(const char *key) {
|
||||
/* No expire? return ASAP */
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
if (expireSize() == 0)
|
||||
return nullptr;
|
||||
|
||||
@@ -2607,6 +2618,17 @@ void clusterStorageLoadCallback(const char *rgchkey, size_t cch, void *)
|
||||
slotToKeyUpdateKeyCore(rgchkey, cch, true /*add*/);
|
||||
}
|
||||
|
||||
void moduleLoadCallback(const char * rgchKey, size_t, void *data) {
|
||||
redisObjectStack keyobj;
|
||||
initStaticStringObject(keyobj, const_cast<char *>(rgchKey));
|
||||
moduleNotifyKeyspaceEvent(NOTIFY_LOADED, "loaded", &keyobj, *(int *)data);
|
||||
}
|
||||
|
||||
void moduleClusterLoadCallback(const char * rgchKey, size_t cchKey, void *data) {
|
||||
clusterStorageLoadCallback(rgchKey, cchKey, data);
|
||||
moduleLoadCallback(rgchKey, cchKey, data);
|
||||
}
|
||||
|
||||
void redisDb::initialize(int id)
|
||||
{
|
||||
redisDbPersistentData::initialize();
|
||||
@@ -2625,8 +2647,8 @@ void redisDb::storageProviderInitialize()
|
||||
{
|
||||
if (g_pserver->m_pstorageFactory != nullptr)
|
||||
{
|
||||
IStorageFactory::key_load_iterator itr = (g_pserver->cluster_enabled) ? clusterStorageLoadCallback : nullptr;
|
||||
this->setStorageProvider(StorageCache::create(g_pserver->m_pstorageFactory, id, itr, nullptr));
|
||||
IStorageFactory::key_load_iterator itr = g_pserver->cluster_enabled ? moduleClusterLoadCallback : moduleLoadCallback;
|
||||
this->setStorageProvider(StorageCache::create(g_pserver->m_pstorageFactory, id, itr, &id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2644,7 +2666,10 @@ bool redisDbPersistentData::insert(char *key, robj *o, bool fAssumeNew, dict_ite
|
||||
ensure(key);
|
||||
dictEntry *de;
|
||||
int res = dictAdd(m_pdict, key, o, &de);
|
||||
serverAssert(FImplies(fAssumeNew, res == DICT_OK));
|
||||
if (!FImplies(fAssumeNew, res == DICT_OK)) {
|
||||
serverLog(LL_WARNING,
|
||||
"Assumed new key %s existed in DB.", key);
|
||||
}
|
||||
if (res == DICT_OK)
|
||||
{
|
||||
#ifdef CHECKED_BUILD
|
||||
@@ -2713,8 +2738,11 @@ void redisDbPersistentData::clear(void(callback)(void*))
|
||||
m_cnewKeysPending = 0;
|
||||
m_fAllChanged++;
|
||||
}
|
||||
{
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
delete m_setexpire;
|
||||
m_setexpire = new (MALLOC_LOCAL) expireset();
|
||||
}
|
||||
if (m_spstorage != nullptr)
|
||||
m_spstorage->clear(callback);
|
||||
dictEmpty(m_pdictTombstone,callback);
|
||||
@@ -2847,7 +2875,8 @@ LNotFound:
|
||||
{
|
||||
dictAdd(m_pdict, sdsNewKey, o);
|
||||
o->SetFExpires(spexpire != nullptr);
|
||||
|
||||
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
if (spexpire != nullptr)
|
||||
{
|
||||
auto itr = m_setexpire->find(sdsKey);
|
||||
@@ -2870,6 +2899,7 @@ LNotFound:
|
||||
if (*pde != nullptr && dictGetVal(*pde) != nullptr)
|
||||
{
|
||||
robj *o = (robj*)dictGetVal(*pde);
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
serverAssert(o->FExpires() == (m_setexpire->find(sdsKey) != m_setexpire->end()));
|
||||
}
|
||||
}
|
||||
@@ -2991,8 +3021,16 @@ void redisDbPersistentData::processChangesAsync(std::atomic<int> &pendingJobs)
|
||||
});
|
||||
}
|
||||
|
||||
void redisDbPersistentData::bulkStorageInsert(char **rgKeys, size_t *rgcbKeys, char **rgVals, size_t *rgcbVals, size_t celem)
|
||||
/* This function is to bulk insert directly to storage provider bypassing in memory, assumes rgKeys and rgVals are not sds strings */
|
||||
void redisDbPersistentData::bulkDirectStorageInsert(char **rgKeys, size_t *rgcbKeys, char **rgVals, size_t *rgcbVals, size_t celem)
|
||||
{
|
||||
if (g_pserver->cluster_enabled) {
|
||||
aeAcquireLock();
|
||||
for (size_t i = 0; i < celem; i++) {
|
||||
slotToKeyUpdateKeyCore(rgKeys[i], rgcbKeys[i], 1);
|
||||
}
|
||||
aeReleaseLock();
|
||||
}
|
||||
m_spstorage->bulkInsert(rgKeys, rgcbKeys, rgVals, rgcbVals, celem);
|
||||
}
|
||||
|
||||
@@ -3234,6 +3272,7 @@ std::unique_ptr<expireEntry> deserializeExpire(sds key, const char *str, size_t
|
||||
|
||||
sds serializeStoredObjectAndExpire(redisDbPersistentData *db, const char *key, robj_roptr o)
|
||||
{
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
auto itrExpire = db->setexpire()->find(key);
|
||||
const expireEntry *pexpire = nullptr;
|
||||
if (itrExpire != db->setexpire()->end())
|
||||
@@ -3254,7 +3293,7 @@ int dbnumFromDb(redisDb *db)
|
||||
serverPanic("invalid database pointer");
|
||||
}
|
||||
|
||||
bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command, bool fExecOK)
|
||||
void redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command)
|
||||
{
|
||||
if (m_spstorage == nullptr) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
@@ -3266,7 +3305,7 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
const char *cmd = szFromObj(command.argv[0]);
|
||||
if (!strcasecmp(cmd, "set") || !strcasecmp(cmd, "get")) {
|
||||
if (c->db->m_spdbSnapshotHOLDER != nullptr)
|
||||
return false; // this is dangerous enough without a snapshot around
|
||||
return; // this is dangerous enough without a snapshot around
|
||||
auto h = dictSdsHash(szFromObj(command.argv[1]));
|
||||
for (int iht = 0; iht < 2; ++iht) {
|
||||
auto hT = h & c->db->m_pdict->ht[iht].sizemask;
|
||||
@@ -3286,7 +3325,7 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
AeLocker lock;
|
||||
@@ -3296,7 +3335,11 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
getKeysResult result = GETKEYS_RESULT_INIT;
|
||||
auto cmd = lookupCommand(szFromObj(command.argv[0]));
|
||||
if (cmd == nullptr)
|
||||
return false; // Bad command? It's not for us to judge, just bail
|
||||
return; // Bad command? It's not for us to judge, just bail
|
||||
|
||||
if (command.argc < std::abs(cmd->arity))
|
||||
return; // Invalid number of args
|
||||
|
||||
int numkeys = getKeysFromCommand(cmd, command.argv, command.argc, &result);
|
||||
for (int ikey = 0; ikey < numkeys; ++ikey)
|
||||
{
|
||||
@@ -3328,7 +3371,6 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
}
|
||||
}
|
||||
|
||||
bool fNoInsert = false;
|
||||
if (!vecInserts.empty()) {
|
||||
lock.arm(c);
|
||||
for (auto &tuple : vecInserts)
|
||||
@@ -3344,19 +3386,18 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
// While unlocked this was already ensured
|
||||
decrRefCount(o);
|
||||
sdsfree(sharedKey);
|
||||
fNoInsert = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spexpire != nullptr) {
|
||||
if (spexpire->when() < mstime()) {
|
||||
fNoInsert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dictAdd(m_pdict, sharedKey, o);
|
||||
o->SetFExpires(spexpire != nullptr);
|
||||
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
if (spexpire != nullptr)
|
||||
{
|
||||
auto itr = m_setexpire->find(sharedKey);
|
||||
@@ -3377,12 +3418,5 @@ bool redisDbPersistentData::prefetchKeysAsync(client *c, parsed_command &command
|
||||
lock.disarm();
|
||||
}
|
||||
|
||||
if (fExecOK && !fNoInsert && cmd->proc == getCommand && !vecInserts.empty()) {
|
||||
robj *o = std::get<1>(vecInserts[0]);
|
||||
if (o != nullptr) {
|
||||
addReplyBulk(c, o);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
+4
-4
@@ -341,10 +341,10 @@ void mallctl_int(client *c, robj **argv, int argc) {
|
||||
}
|
||||
size_t sz = sizeof(old);
|
||||
while (sz > 0) {
|
||||
if ((ret=je_mallctl(szFromObj(argv[0]), &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) {
|
||||
if ((ret=mallctl(szFromObj(argv[0]), &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) {
|
||||
if (ret == EPERM && argc > 1) {
|
||||
/* if this option is write only, try just writing to it. */
|
||||
if (!(ret=je_mallctl(szFromObj(argv[0]), NULL, 0, &val, sz))) {
|
||||
if (!(ret=mallctl(szFromObj(argv[0]), NULL, 0, &val, sz))) {
|
||||
addReply(c, shared.ok);
|
||||
return;
|
||||
}
|
||||
@@ -375,7 +375,7 @@ void mallctl_string(client *c, robj **argv, int argc) {
|
||||
char *old;
|
||||
size_t sz = sizeof(old);
|
||||
/* for strings, it seems we need to first get the old value, before overriding it. */
|
||||
if ((rret=je_mallctl(szFromObj(argv[0]), &old, &sz, NULL, 0))) {
|
||||
if ((rret=mallctl(szFromObj(argv[0]), &old, &sz, NULL, 0))) {
|
||||
/* return error unless this option is write only. */
|
||||
if (!(rret == EPERM && argc > 1)) {
|
||||
addReplyErrorFormat(c,"%s", strerror(rret));
|
||||
@@ -387,7 +387,7 @@ void mallctl_string(client *c, robj **argv, int argc) {
|
||||
char **valref = &val;
|
||||
if ((!strcmp(val,"VOID")))
|
||||
valref = NULL, sz = 0;
|
||||
wret = je_mallctl(szFromObj(argv[0]), NULL, 0, valref, sz);
|
||||
wret = mallctl(szFromObj(argv[0]), NULL, 0, valref, sz);
|
||||
}
|
||||
if (!rret)
|
||||
addReplyBulkCString(c, old);
|
||||
|
||||
+1
-1
@@ -1577,7 +1577,7 @@ char *stringFromLongLong(long long value) {
|
||||
int len;
|
||||
char *s;
|
||||
|
||||
len = sprintf(buf,"%lld",value);
|
||||
len = snprintf(buf,sizeof(buf),"%lld",value);
|
||||
s = zmalloc(len+1);
|
||||
memcpy(s, buf, len);
|
||||
s[len] = '\0';
|
||||
|
||||
+3
-3
@@ -112,15 +112,15 @@ int endianconvTest(int argc, char *argv[], int accurate) {
|
||||
UNUSED(argv);
|
||||
UNUSED(accurate);
|
||||
|
||||
sprintf(buf,"ciaoroma");
|
||||
snprintf(buf,sizeof(buf),"ciaoroma");
|
||||
memrev16(buf);
|
||||
printf("%s\n", buf);
|
||||
|
||||
sprintf(buf,"ciaoroma");
|
||||
snprintf(buf,sizeof(buf),"ciaoroma");
|
||||
memrev32(buf);
|
||||
printf("%s\n", buf);
|
||||
|
||||
sprintf(buf,"ciaoroma");
|
||||
snprintf(buf,sizeof(buf),"ciaoroma");
|
||||
memrev64(buf);
|
||||
printf("%s\n", buf);
|
||||
|
||||
|
||||
+2
-1
@@ -380,7 +380,7 @@ size_t freeMemoryGetNotCountedMemory(void) {
|
||||
|
||||
/* also don't count the replication backlog memory
|
||||
* that's where the replication clients get their memory from */
|
||||
overhead += g_pserver->repl_backlog_size;
|
||||
overhead += g_pserver->repl_backlog_size - g_pserver->repl_backlog_config_size;
|
||||
|
||||
if (g_pserver->aof_state != AOF_OFF) {
|
||||
overhead += sdsalloc(g_pserver->aof_buf)+aofRewriteBufferSize();
|
||||
@@ -771,6 +771,7 @@ int performEvictions(bool fPreSnapshot) {
|
||||
if (db->removeCachedValue(bestkey, &deT)) {
|
||||
mem_freed += splazy->addEntry(db->dictUnsafeKeyOnly(), deT);
|
||||
ckeysFailed = 0;
|
||||
g_pserver->stat_evictedkeys++;
|
||||
}
|
||||
else {
|
||||
delta = 0;
|
||||
|
||||
@@ -481,6 +481,7 @@ void expireSlaveKeys(void) {
|
||||
if (slaveKeysWithExpire == NULL ||
|
||||
dictSize(slaveKeysWithExpire) == 0) return;
|
||||
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
int cycles = 0, noexpire = 0;
|
||||
mstime_t start = mstime();
|
||||
while(1) {
|
||||
|
||||
@@ -16,7 +16,17 @@ class GarbageCollector
|
||||
struct EpochHolder
|
||||
{
|
||||
uint64_t tstamp;
|
||||
std::vector<std::unique_ptr<T>> m_vecObjs;
|
||||
std::unique_ptr<std::vector<std::unique_ptr<T>>> m_spvecObjs;
|
||||
|
||||
EpochHolder() {
|
||||
m_spvecObjs = std::make_unique<std::vector<std::unique_ptr<T>>>();
|
||||
}
|
||||
|
||||
// Support move operators
|
||||
EpochHolder(EpochHolder &&other) = default;
|
||||
EpochHolder &operator=(EpochHolder &&) = default;
|
||||
|
||||
|
||||
|
||||
bool operator<(uint64_t tstamp) const
|
||||
{
|
||||
@@ -108,12 +118,12 @@ public:
|
||||
{
|
||||
EpochHolder e;
|
||||
e.tstamp = m_epochNext+1;
|
||||
e.m_vecObjs.push_back(std::move(sp));
|
||||
e.m_spvecObjs->push_back(std::move(sp));
|
||||
m_listepochs.emplace_back(std::move(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
itr->m_vecObjs.push_back(std::move(sp));
|
||||
itr->m_spvecObjs->push_back(std::move(sp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,4 +133,4 @@ private:
|
||||
std::list<EpochHolder> m_listepochs;
|
||||
std::unordered_set<uint64_t> m_setepochOutstanding;
|
||||
uint64_t m_epochNext = 0;
|
||||
};
|
||||
};
|
||||
@@ -904,7 +904,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
while (self_threads < config.max_threads) {
|
||||
for (int i = 0; i < config.numclients; i++) {
|
||||
sprintf(command, "SET %d %s\r\n", self_threads * config.numclients + i, set_value);
|
||||
snprintf(command, sizeof(command), "SET %d %s\r\n", self_threads * config.numclients + i, set_value);
|
||||
createClient(command, strlen(command), NULL,self_threads);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,9 @@ int TimerCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
|
||||
|
||||
for (int j = 0; j < 10; j++) {
|
||||
int delay = rand() % 5000;
|
||||
char *buf = RedisModule_Alloc(256);
|
||||
snprintf(buf,256,"After %d", delay);
|
||||
int bufsize = 256;
|
||||
char *buf = RedisModule_Alloc(bufsize);
|
||||
snprintf(buf,bufsize,"After %d", delay);
|
||||
RedisModuleTimerID tid = RedisModule_CreateTimer(ctx,delay,timerHandler,buf);
|
||||
REDISMODULE_NOT_USED(tid);
|
||||
}
|
||||
|
||||
+6
-8
@@ -145,7 +145,7 @@ client *createClient(connection *conn, int iel) {
|
||||
client_id = g_pserver->next_client_id.fetch_add(1);
|
||||
c->iel = iel;
|
||||
c->id = client_id;
|
||||
sprintf(c->lock.szName, "client %" PRIu64, client_id);
|
||||
snprintf(c->lock.szName, sizeof(c->lock.szName), "client %" PRIu64, client_id);
|
||||
c->resp = 2;
|
||||
c->conn = conn;
|
||||
c->name = NULL;
|
||||
@@ -777,11 +777,11 @@ void setDeferredAggregateLen(client *c, void *node, long length, char prefix) {
|
||||
* we return NULL in addReplyDeferredLen() */
|
||||
if (node == NULL) return;
|
||||
char lenstr[128];
|
||||
size_t lenstr_len = sprintf(lenstr, "%c%ld\r\n", prefix, length);
|
||||
size_t lenstr_len = snprintf(lenstr, sizeof(lenstr), "%c%ld\r\n", prefix, length);
|
||||
setDeferredReply(c, node, lenstr, lenstr_len);
|
||||
} else {
|
||||
char lenstr[128];
|
||||
int lenstr_len = sprintf(lenstr, "%c%ld\r\n", prefix, length);
|
||||
int lenstr_len = snprintf(lenstr, sizeof(lenstr), "%c%ld\r\n", prefix, length);
|
||||
|
||||
size_t idxSplice = (size_t)node;
|
||||
serverAssert(idxSplice <= c->replyAsync->used);
|
||||
@@ -1999,9 +1999,9 @@ void ProcessPendingAsyncWrites()
|
||||
* writes may have been signalled without having been copied to the replyAsync buffer,
|
||||
* thus causing the buffer to be NULL */
|
||||
if (c->replyAsync != nullptr){
|
||||
int size = c->replyAsync->used;
|
||||
size_t size = c->replyAsync->used;
|
||||
|
||||
if (listLength(c->reply) == 0 && size <= (PROTO_REPLY_CHUNK_BYTES - c->bufpos)) {
|
||||
if (listLength(c->reply) == 0 && size <= static_cast<size_t>(PROTO_REPLY_CHUNK_BYTES - c->bufpos)) {
|
||||
memcpy(c->buf + c->bufpos, c->replyAsync->buf(), size);
|
||||
c->bufpos += size;
|
||||
} else {
|
||||
@@ -2595,9 +2595,7 @@ void parseClientCommandBuffer(client *c) {
|
||||
(g_pserver->m_pstorageFactory || aeLockContested(cserver.cthreads/2) || cserver.cthreads == 1) && !GlobalLocksAcquired()) {
|
||||
auto &query = c->vecqueuedcmd.back();
|
||||
if (query.argc > 0 && query.argc == query.argcMax) {
|
||||
if (c->db->prefetchKeysAsync(c, query, c->vecqueuedcmd.size() == 1)) {
|
||||
c->vecqueuedcmd.erase(c->vecqueuedcmd.begin());
|
||||
}
|
||||
c->db->prefetchKeysAsync(c, query);
|
||||
}
|
||||
}
|
||||
c->reqtype = 0;
|
||||
|
||||
@@ -41,10 +41,3 @@ void operator delete(void *p, std::size_t) noexcept
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(USE_JEMALLOC)
|
||||
extern "C" size_t malloc_usable_size(void *ptr)
|
||||
{
|
||||
return zmalloc_usable_size(ptr);
|
||||
}
|
||||
#endif
|
||||
+4
-3
@@ -1091,7 +1091,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
|
||||
mem_total += g_pserver->initial_memory_usage;
|
||||
|
||||
mem = 0;
|
||||
if (g_pserver->repl_backlog)
|
||||
if (g_pserver->repl_backlog && g_pserver->repl_backlog != g_pserver->repl_backlog_disk)
|
||||
mem += zmalloc_size(g_pserver->repl_backlog);
|
||||
mh->repl_backlog = mem;
|
||||
mem_total += mem;
|
||||
@@ -1140,7 +1140,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
|
||||
db->size() * sizeof(robj);
|
||||
mh->db[mh->num_dbs].overhead_ht_main = mem;
|
||||
mem_total+=mem;
|
||||
|
||||
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
mem = db->setexpire()->estimated_bytes_used();
|
||||
mh->db[mh->num_dbs].overhead_ht_expires = mem;
|
||||
@@ -1541,7 +1541,7 @@ void memoryCommand(client *c) {
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"malloc-stats") && c->argc == 2) {
|
||||
#if defined(USE_JEMALLOC)
|
||||
sds info = sdsempty();
|
||||
je_malloc_stats_print(inputCatSds, &info, NULL);
|
||||
malloc_stats_print(inputCatSds, &info, NULL);
|
||||
addReplyVerbatim(c,info,sdslen(info),"txt");
|
||||
sdsfree(info);
|
||||
#else
|
||||
@@ -1668,6 +1668,7 @@ robj *deserializeStoredObjectCore(const void *data, size_t cb)
|
||||
robj *deserializeStoredObject(const redisDbPersistentData *db, const char *key, const void *data, size_t cb)
|
||||
{
|
||||
robj *o = deserializeStoredObjectCore(data, cb);
|
||||
std::unique_lock<fastlock> ul(g_expireLock);
|
||||
o->SetFExpires(db->setexpire()->exists(key));
|
||||
return o;
|
||||
}
|
||||
|
||||
+134
-58
@@ -1163,7 +1163,7 @@ int rdbSaveKeyValuePair(rio *rdb, robj_roptr key, robj_roptr val, const expireEn
|
||||
|
||||
char szT[32];
|
||||
if (g_pserver->fActiveReplica) {
|
||||
snprintf(szT, 32, "%" PRIu64, mvccFromObj(val));
|
||||
snprintf(szT, sizeof(szT), "%" PRIu64, mvccFromObj(val));
|
||||
if (rdbSaveAuxFieldStrStr(rdb,"mvcc-tstamp", szT) == -1) return -1;
|
||||
}
|
||||
|
||||
@@ -1190,7 +1190,7 @@ int rdbSaveKeyValuePair(rio *rdb, robj_roptr key, robj_roptr val, const expireEn
|
||||
{
|
||||
if (itr.subkey() == nullptr)
|
||||
continue; // already saved
|
||||
snprintf(szT, 32, "%lld", itr.when());
|
||||
snprintf(szT, sizeof(szT), "%lld", itr.when());
|
||||
rdbSaveAuxFieldStrStr(rdb,"keydb-subexpire-key",itr.subkey());
|
||||
rdbSaveAuxFieldStrStr(rdb,"keydb-subexpire-when",szT);
|
||||
}
|
||||
@@ -1334,7 +1334,7 @@ int rdbSaveRio(rio *rdb, const redisDbPersistentDataSnapshot **rgpdb, int *error
|
||||
if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_BEFORE_RDB) == -1) goto werr;
|
||||
|
||||
for (j = 0; j < cserver.dbnum; j++) {
|
||||
const redisDbPersistentDataSnapshot *db = rgpdb[j];
|
||||
const redisDbPersistentDataSnapshot *db = rgpdb != nullptr ? rgpdb[j] : g_pserver->db[j];
|
||||
if (db->size() == 0) continue;
|
||||
|
||||
/* Write the SELECT DB opcode */
|
||||
@@ -1490,7 +1490,7 @@ int rdbSaveFile(char *filename, const redisDbPersistentDataSnapshot **rgpdb, rdb
|
||||
rio rdb;
|
||||
int error = 0;
|
||||
|
||||
snprintf(tmpfile,256,"temp-%d-%d.rdb", getpid(), g_pserver->rdbThreadVars.tmpfileNum);
|
||||
getTempFileName(tmpfile, g_pserver->rdbThreadVars.tmpfileNum);
|
||||
fp = fopen(tmpfile,"w");
|
||||
if (!fp) {
|
||||
char *cwdp = getcwd(cwd,MAXPATHLEN);
|
||||
@@ -1660,12 +1660,14 @@ int launchRdbSaveThread(pthread_t &child, rdbSaveInfo *rsi)
|
||||
pthread_attr_t tattr;
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
|
||||
openChildInfoPipe();
|
||||
if (pthread_create(&child, &tattr, rdbSaveThread, args)) {
|
||||
pthread_attr_destroy(&tattr);
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
g_pserver->db[idb]->endSnapshot(args->rgpdb[idb]);
|
||||
args->~rdbSaveThreadArgs();
|
||||
zfree(args);
|
||||
closeChildInfoPipe();
|
||||
return C_ERR;
|
||||
}
|
||||
pthread_attr_destroy(&tattr);
|
||||
@@ -1679,24 +1681,23 @@ int rdbSaveBackground(rdbSaveInfo *rsi) {
|
||||
pthread_t child;
|
||||
long long start;
|
||||
|
||||
if (hasActiveChildProcess()) return C_ERR;
|
||||
if (hasActiveChildProcessOrBGSave()) return C_ERR;
|
||||
|
||||
g_pserver->dirty_before_bgsave = g_pserver->dirty;
|
||||
g_pserver->lastbgsave_try = time(NULL);
|
||||
openChildInfoPipe();
|
||||
|
||||
start = ustime();
|
||||
latencyStartMonitor(g_pserver->rdb_save_latency);
|
||||
|
||||
|
||||
g_pserver->stat_fork_time = ustime()-start;
|
||||
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
|
||||
if (launchRdbSaveThread(child, rsi) != C_OK) {
|
||||
closeChildInfoPipe();
|
||||
g_pserver->lastbgsave_status = C_ERR;
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
g_pserver->stat_fork_time = ustime()-start;
|
||||
g_pserver->stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / g_pserver->stat_fork_time / (1024*1024*1024); /* GB per second. */
|
||||
latencyAddSampleIfNeeded("fork",g_pserver->stat_fork_time/1000);
|
||||
serverLog(LL_NOTICE,"Background saving started");
|
||||
g_pserver->rdb_save_time_start = time(NULL);
|
||||
@@ -1714,7 +1715,7 @@ void getTempFileName(char tmpfile[], int tmpfileNum) {
|
||||
char tmpfileNumString[214];
|
||||
|
||||
/* Generate temp rdb file name using aync-signal safe functions. */
|
||||
int pid_len = ll2string(pid, sizeof(pid), getpid());
|
||||
int pid_len = ll2string(pid, sizeof(pid), g_pserver->in_fork_child ? getppid() : getpid());
|
||||
int tmpfileNum_len = ll2string(tmpfileNumString, sizeof(tmpfileNumString), tmpfileNum);
|
||||
strcpy(tmpfile, "temp-");
|
||||
strncpy(tmpfile+5, pid, pid_len);
|
||||
@@ -3601,6 +3602,8 @@ static void backgroundSaveDoneHandlerDisk(int exitcode, bool fCancelled) {
|
||||
g_pserver->dirty = g_pserver->dirty - g_pserver->dirty_before_bgsave;
|
||||
g_pserver->lastsave = time(NULL);
|
||||
g_pserver->lastbgsave_status = C_OK;
|
||||
latencyEndMonitor(g_pserver->rdb_save_latency);
|
||||
latencyAddSampleIfNeeded("rdb-save",g_pserver->rdb_save_latency);
|
||||
} else if (!fCancelled && exitcode != 0) {
|
||||
serverLog(LL_WARNING, "Background saving error");
|
||||
g_pserver->lastbgsave_status = C_ERR;
|
||||
@@ -3691,7 +3694,7 @@ void killRDBChild(bool fSynchronous) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
if (cserver.fForkBgSave) {
|
||||
kill(g_pserver->rdb_child_pid,SIGUSR1);
|
||||
kill(g_pserver->child_pid,SIGUSR1);
|
||||
} else {
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = true;
|
||||
if (g_pserver->rdb_child_type == RDB_CHILD_TYPE_SOCKET) {
|
||||
@@ -3782,7 +3785,7 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
|
||||
int pipefds[2];
|
||||
rdbSaveSocketThreadArgs *args = nullptr;
|
||||
|
||||
if (hasActiveChildProcess()) return C_ERR;
|
||||
if (hasActiveChildProcessOrBGSave()) return C_ERR;
|
||||
|
||||
/* Even if the previous fork child exited, don't start a new one until we
|
||||
* drained the pipe. */
|
||||
@@ -3829,57 +3832,130 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
|
||||
}
|
||||
|
||||
/* Create the child process. */
|
||||
openChildInfoPipe();
|
||||
if (cserver.fForkBgSave) {
|
||||
pid_t childpid;
|
||||
if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) {
|
||||
/* Child */
|
||||
int retval, dummy;
|
||||
rio rdb;
|
||||
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
args->rgpdb[idb] = g_pserver->db[idb]->createSnapshot(getMvccTstamp(), false /*fOptional*/);
|
||||
rioInitWithFd(&rdb,args->rdb_pipe_write);
|
||||
|
||||
g_pserver->rdbThreadVars.tmpfileNum++;
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
|
||||
pthread_attr_t tattr;
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
|
||||
if (pthread_create(&child, &tattr, rdbSaveToSlavesSocketsThread, args)) {
|
||||
pthread_attr_destroy(&tattr);
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
redisSetProcTitle("keydb-rdb-to-slaves");
|
||||
redisSetCpuAffinity(g_pserver->bgsave_cpulist);
|
||||
|
||||
/* Undo the state change. The caller will perform cleanup on
|
||||
* all the slaves in BGSAVE_START state, but an early call to
|
||||
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
|
||||
listRewind(g_pserver->slaves,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
client *replica = (client*)ln->value;
|
||||
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
|
||||
replica->replstate = SLAVE_STATE_WAIT_BGSAVE_START;
|
||||
retval = rdbSaveRioWithEOFMark(&rdb,nullptr,nullptr,rsi);
|
||||
if (retval == C_OK && rioFlush(&rdb) == 0)
|
||||
retval = C_ERR;
|
||||
|
||||
if (retval == C_OK) {
|
||||
sendChildCowInfo(CHILD_INFO_TYPE_RDB_COW_SIZE, "RDB");
|
||||
}
|
||||
}
|
||||
close(args->rdb_pipe_write);
|
||||
close(g_pserver->rdb_pipe_read);
|
||||
zfree(g_pserver->rdb_pipe_conns);
|
||||
close(args->safe_to_exit_pipe);
|
||||
g_pserver->rdb_pipe_conns = NULL;
|
||||
g_pserver->rdb_pipe_numconns = 0;
|
||||
g_pserver->rdb_pipe_numconns_writing = 0;
|
||||
args->rsi.~rdbSaveInfo();
|
||||
zfree(args);
|
||||
closeChildInfoPipe();
|
||||
return C_ERR;
|
||||
}
|
||||
pthread_attr_destroy(&tattr);
|
||||
g_pserver->child_type = CHILD_TYPE_RDB;
|
||||
|
||||
serverLog(LL_NOTICE,"Background RDB transfer started");
|
||||
g_pserver->rdb_save_time_start = time(NULL);
|
||||
serverAssert(!g_pserver->rdbThreadVars.fRdbThreadActive);
|
||||
g_pserver->rdbThreadVars.rdb_child_thread = child;
|
||||
g_pserver->rdbThreadVars.fRdbThreadActive = true;
|
||||
g_pserver->rdb_child_type = RDB_CHILD_TYPE_SOCKET;
|
||||
aePostFunction(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el, []{
|
||||
if (aeCreateFileEvent(serverTL->el, g_pserver->rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {
|
||||
serverPanic("Unrecoverable error creating server.rdb_pipe_read file event.");
|
||||
rioFreeFd(&rdb);
|
||||
/* wake up the reader, tell it we're done. */
|
||||
close(args->rdb_pipe_write);
|
||||
close(g_pserver->rdb_child_exit_pipe); /* close write end so that we can detect the close on the parent. */
|
||||
/* hold exit until the parent tells us it's safe. we're not expecting
|
||||
* to read anything, just get the error when the pipe is closed. */
|
||||
dummy = read(args->safe_to_exit_pipe, pipefds, 1);
|
||||
UNUSED(dummy);
|
||||
exitFromChild((retval == C_OK) ? 0 : 1);
|
||||
} else {
|
||||
/* Parent */
|
||||
close(args->safe_to_exit_pipe);
|
||||
if (childpid == -1) {
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
|
||||
/* Undo the state change. The caller will perform cleanup on
|
||||
* all the slaves in BGSAVE_START state, but an early call to
|
||||
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
|
||||
listRewind(g_pserver->slaves,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
client *replica = (client*)ln->value;
|
||||
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
|
||||
replica->replstate = SLAVE_STATE_WAIT_BGSAVE_START;
|
||||
}
|
||||
}
|
||||
close(args->rdb_pipe_write);
|
||||
close(g_pserver->rdb_pipe_read);
|
||||
zfree(g_pserver->rdb_pipe_conns);
|
||||
g_pserver->rdb_pipe_conns = NULL;
|
||||
g_pserver->rdb_pipe_numconns = 0;
|
||||
g_pserver->rdb_pipe_numconns_writing = 0;
|
||||
args->rsi.~rdbSaveInfo();
|
||||
zfree(args);
|
||||
} else {
|
||||
serverLog(LL_NOTICE,"Background RDB transfer started by pid %ld",
|
||||
(long)childpid);
|
||||
g_pserver->rdb_save_time_start = time(NULL);
|
||||
g_pserver->rdb_child_type = RDB_CHILD_TYPE_SOCKET;
|
||||
g_pserver->rdbThreadVars.fRdbThreadActive = true;
|
||||
updateDictResizePolicy();
|
||||
close(args->rdb_pipe_write); /* close write in parent so that it can detect the close on the child. */
|
||||
aePostFunction(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el, []{
|
||||
if (aeCreateFileEvent(serverTL->el, g_pserver->rdb_pipe_read, AE_READABLE, rdbPipeReadHandler, nullptr) == AE_ERR) {
|
||||
serverPanic("Unrecoverable error creating g_pserver->rdb_pipe_read file event.");
|
||||
}
|
||||
});
|
||||
}
|
||||
return (childpid == -1) ? C_ERR : C_OK;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
openChildInfoPipe();
|
||||
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
args->rgpdb[idb] = g_pserver->db[idb]->createSnapshot(getMvccTstamp(), false /*fOptional*/);
|
||||
|
||||
g_pserver->rdbThreadVars.tmpfileNum++;
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
|
||||
pthread_attr_t tattr;
|
||||
pthread_attr_init(&tattr);
|
||||
pthread_attr_setstacksize(&tattr, 1 << 23); // 8 MB
|
||||
if (pthread_create(&child, &tattr, rdbSaveToSlavesSocketsThread, args)) {
|
||||
pthread_attr_destroy(&tattr);
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
|
||||
/* Undo the state change. The caller will perform cleanup on
|
||||
* all the slaves in BGSAVE_START state, but an early call to
|
||||
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
|
||||
listRewind(g_pserver->slaves,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
client *replica = (client*)ln->value;
|
||||
if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
|
||||
replica->replstate = SLAVE_STATE_WAIT_BGSAVE_START;
|
||||
}
|
||||
}
|
||||
close(args->rdb_pipe_write);
|
||||
close(g_pserver->rdb_pipe_read);
|
||||
zfree(g_pserver->rdb_pipe_conns);
|
||||
close(args->safe_to_exit_pipe);
|
||||
g_pserver->rdb_pipe_conns = NULL;
|
||||
g_pserver->rdb_pipe_numconns = 0;
|
||||
g_pserver->rdb_pipe_numconns_writing = 0;
|
||||
args->rsi.~rdbSaveInfo();
|
||||
zfree(args);
|
||||
closeChildInfoPipe();
|
||||
return C_ERR;
|
||||
}
|
||||
pthread_attr_destroy(&tattr);
|
||||
g_pserver->child_type = CHILD_TYPE_RDB;
|
||||
|
||||
serverLog(LL_NOTICE,"Background RDB transfer started");
|
||||
g_pserver->rdb_save_time_start = time(NULL);
|
||||
serverAssert(!g_pserver->rdbThreadVars.fRdbThreadActive);
|
||||
g_pserver->rdbThreadVars.rdb_child_thread = child;
|
||||
g_pserver->rdbThreadVars.fRdbThreadActive = true;
|
||||
g_pserver->rdb_child_type = RDB_CHILD_TYPE_SOCKET;
|
||||
aePostFunction(g_pserver->rgthreadvar[IDX_EVENT_LOOP_MAIN].el, []{
|
||||
if (aeCreateFileEvent(serverTL->el, g_pserver->rdb_pipe_read, AE_READABLE, rdbPipeReadHandler, nullptr) == AE_ERR) {
|
||||
serverPanic("Unrecoverable error creating server.rdb_pipe_read file event.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return C_OK; /* Unreached. */
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ int rdbLoad(rdbSaveInfo *rsi, int rdbflags);
|
||||
int rdbLoadFile(const char *filename, rdbSaveInfo *rsi, int rdbflags);
|
||||
int rdbSaveBackground(rdbSaveInfo *rsi);
|
||||
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
|
||||
void getTempFileName(char tmpfile[], int tmpfileNum);
|
||||
void rdbRemoveTempFile(pid_t childpid, int from_signal);
|
||||
int rdbSave(const redisDbPersistentDataSnapshot **rgpdb, rdbSaveInfo *rsi);
|
||||
int rdbSaveFile(char *filename, const redisDbPersistentDataSnapshot **rgpdb, rdbSaveInfo *rsi);
|
||||
|
||||
+9
-2
@@ -8,6 +8,7 @@ class readWriteLock {
|
||||
int m_readCount = 0;
|
||||
int m_writeCount = 0;
|
||||
bool m_writeWaiting = false;
|
||||
bool m_notify = true;
|
||||
public:
|
||||
readWriteLock(const char *name) : m_readLock(name), m_writeLock(name) {}
|
||||
|
||||
@@ -65,7 +66,8 @@ public:
|
||||
void releaseRead() {
|
||||
std::unique_lock<fastlock> rm(m_readLock);
|
||||
m_readCount--;
|
||||
m_cv.notify_all();
|
||||
if (m_notify)
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void releaseWrite(bool exclusive = true) {
|
||||
@@ -74,7 +76,8 @@ public:
|
||||
if (exclusive)
|
||||
m_writeLock.unlock();
|
||||
m_writeCount--;
|
||||
m_cv.notify_all();
|
||||
if (m_notify)
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void downgradeWrite(bool exclusive = true) {
|
||||
@@ -82,6 +85,10 @@ public:
|
||||
acquireRead();
|
||||
}
|
||||
|
||||
void setNotify(bool notify) {
|
||||
m_notify = notify;
|
||||
}
|
||||
|
||||
bool hasReader() {
|
||||
return m_readCount > 0;
|
||||
}
|
||||
|
||||
+16
-16
@@ -3293,8 +3293,8 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
|
||||
argv_len = zcalloc(argc * sizeof(size_t), MALLOC_LOCAL);
|
||||
char portstr[255];
|
||||
char timeoutstr[255];
|
||||
snprintf(portstr, 10, "%d", target->port);
|
||||
snprintf(timeoutstr, 10, "%d", timeout);
|
||||
snprintf(portstr, sizeof(portstr), "%d", target->port);
|
||||
snprintf(timeoutstr, sizeof(timeoutstr), "%d", timeout);
|
||||
argv[0] = "MIGRATE";
|
||||
argv_len[0] = 7;
|
||||
argv[1] = target->ip;
|
||||
@@ -5485,8 +5485,8 @@ static int clusterManagerCommandRebalance(int argc, char **argv) {
|
||||
listAddNodeTail(lsrc, src);
|
||||
table = clusterManagerComputeReshardTable(lsrc, numslots);
|
||||
listRelease(lsrc);
|
||||
int table_len = (int) listLength(table);
|
||||
if (!table || table_len != numslots) {
|
||||
int table_len = 0;
|
||||
if (!table || (table_len = (int) listLength(table)) != numslots) {
|
||||
clusterManagerLogErr("*** Assertion failed: Reshard table "
|
||||
"!= number of slots");
|
||||
result = 0;
|
||||
@@ -6825,7 +6825,7 @@ static long getLongInfoField(char *info, char *field) {
|
||||
|
||||
/* Convert number of bytes into a human readable string of the form:
|
||||
* 100B, 2G, 100M, 4K, and so forth. */
|
||||
void bytesToHuman(char *s, long long n) {
|
||||
void bytesToHuman(char *s, long long n, size_t bufsize) {
|
||||
double d;
|
||||
|
||||
if (n < 0) {
|
||||
@@ -6835,17 +6835,17 @@ void bytesToHuman(char *s, long long n) {
|
||||
}
|
||||
if (n < 1024) {
|
||||
/* Bytes */
|
||||
sprintf(s,"%lldB",n);
|
||||
snprintf(s,bufsize,"%lldB",n);
|
||||
return;
|
||||
} else if (n < (1024*1024)) {
|
||||
d = (double)n/(1024);
|
||||
sprintf(s,"%.2fK",d);
|
||||
snprintf(s,bufsize,"%.2fK",d);
|
||||
} else if (n < (1024LL*1024*1024)) {
|
||||
d = (double)n/(1024*1024);
|
||||
sprintf(s,"%.2fM",d);
|
||||
snprintf(s,bufsize,"%.2fM",d);
|
||||
} else if (n < (1024LL*1024*1024*1024)) {
|
||||
d = (double)n/(1024LL*1024*1024);
|
||||
sprintf(s,"%.2fG",d);
|
||||
snprintf(s,bufsize,"%.2fG",d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6875,38 +6875,38 @@ static void statMode(void) {
|
||||
for (j = 0; j < 20; j++) {
|
||||
long k;
|
||||
|
||||
sprintf(buf,"db%d:keys",j);
|
||||
snprintf(buf,sizeof(buf),"db%d:keys",j);
|
||||
k = getLongInfoField(reply->str,buf);
|
||||
if (k == LONG_MIN) continue;
|
||||
aux += k;
|
||||
}
|
||||
sprintf(buf,"%ld",aux);
|
||||
snprintf(buf,sizeof(buf),"%ld",aux);
|
||||
printf("%-11s",buf);
|
||||
|
||||
/* Used memory */
|
||||
aux = getLongInfoField(reply->str,"used_memory");
|
||||
bytesToHuman(buf,aux);
|
||||
bytesToHuman(buf,aux,sizeof(buf));
|
||||
printf("%-8s",buf);
|
||||
|
||||
/* Clients */
|
||||
aux = getLongInfoField(reply->str,"connected_clients");
|
||||
sprintf(buf,"%ld",aux);
|
||||
snprintf(buf,sizeof(buf),"%ld",aux);
|
||||
printf(" %-8s",buf);
|
||||
|
||||
/* Blocked (BLPOPPING) Clients */
|
||||
aux = getLongInfoField(reply->str,"blocked_clients");
|
||||
sprintf(buf,"%ld",aux);
|
||||
snprintf(buf,sizeof(buf),"%ld",aux);
|
||||
printf("%-8s",buf);
|
||||
|
||||
/* Requests */
|
||||
aux = getLongInfoField(reply->str,"total_commands_processed");
|
||||
sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
|
||||
snprintf(buf,sizeof(buf),"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
|
||||
printf("%-19s",buf);
|
||||
requests = aux;
|
||||
|
||||
/* Connections */
|
||||
aux = getLongInfoField(reply->str,"total_connections_received");
|
||||
sprintf(buf,"%ld",aux);
|
||||
snprintf(buf,sizeof(buf),"%ld",aux);
|
||||
printf(" %-12s",buf);
|
||||
|
||||
/* Children */
|
||||
|
||||
@@ -97,9 +97,6 @@ extern "C" {
|
||||
#define CC_FORCE (1<<0) /* Re-connect if already connected. */
|
||||
#define CC_QUIET (1<<1) /* Don't log connecting errors. */
|
||||
|
||||
struct clusterManagerLink;
|
||||
typedef struct clusterManagerLink clusterManagerLink;
|
||||
|
||||
/* Dict Helpers */
|
||||
|
||||
uint64_t dictSdsHash(const void *key);
|
||||
|
||||
@@ -326,6 +326,7 @@ static const RedisModuleEvent
|
||||
#define REDISMODULE_SUBEVENT_LOADING_ENDED 3
|
||||
#define REDISMODULE_SUBEVENT_LOADING_FAILED 4
|
||||
#define _REDISMODULE_SUBEVENT_LOADING_NEXT 5
|
||||
#define REDISMODULE_SUBEVENT_LOADING_FLASH_START 6
|
||||
|
||||
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED 0
|
||||
#define REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED 1
|
||||
|
||||
+27
-10
@@ -190,7 +190,7 @@ int bg_unlink(const char *filename) {
|
||||
bool createDiskBacklog() {
|
||||
// Lets create some disk backed pages and add them here
|
||||
std::string path = "./repl-backlog-temp" + std::to_string(gettid());
|
||||
#ifdef __APPLE__
|
||||
#if (defined __APPLE__ || defined __FreeBSD__)
|
||||
int fd = open(path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
#else
|
||||
int fd = open(path.c_str(), O_CREAT | O_RDWR | O_LARGEFILE, S_IRUSR | S_IWUSR);
|
||||
@@ -1132,9 +1132,17 @@ public:
|
||||
replica->repl_put_online_on_ack = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
for (auto replica : replicas) {
|
||||
// Close the connection to force a resync
|
||||
freeClientAsync(replica);
|
||||
}
|
||||
replicas.clear();
|
||||
}
|
||||
};
|
||||
|
||||
int rdbSaveSnapshotForReplication(struct rdbSaveInfo *rsi) {
|
||||
int rdbSaveSnapshotForReplication(rdbSaveInfo *rsi) {
|
||||
// TODO: This needs to be on a background thread
|
||||
int retval = C_OK;
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
@@ -1227,7 +1235,11 @@ int rdbSaveSnapshotForReplication(struct rdbSaveInfo *rsi) {
|
||||
retval = C_ERR;
|
||||
break;
|
||||
}
|
||||
serverAssert(count == snapshotDeclaredCount);
|
||||
if (count != snapshotDeclaredCount) {
|
||||
serverLog(LL_WARNING, "Replication BUG: Count of keys sent does not match actual count. Aborting full sync.");
|
||||
replBuf.abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
replBuf.end();
|
||||
@@ -1526,7 +1538,7 @@ void syncCommand(client *c) {
|
||||
} else {
|
||||
/* We don't have a BGSAVE in progress, let's start one. Diskless
|
||||
* or disk-based mode is determined by replica's capacity. */
|
||||
if (!hasActiveChildProcess()) {
|
||||
if (!hasActiveChildProcessOrBGSave()) {
|
||||
startBgsaveForReplication(c->slave_capa);
|
||||
} else {
|
||||
serverLog(LL_NOTICE,
|
||||
@@ -3724,7 +3736,7 @@ retry_connect:
|
||||
while(maxtries--) {
|
||||
auto dt = std::chrono::system_clock::now().time_since_epoch();
|
||||
auto dtMillisecond = std::chrono::duration_cast<std::chrono::milliseconds>(dt);
|
||||
snprintf(tmpfile,256,
|
||||
snprintf(tmpfile,sizeof(tmpfile),
|
||||
"temp-%d.%ld.rdb",(int)dtMillisecond.count(),(long int)getpid());
|
||||
dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);
|
||||
if (dfd != -1) break;
|
||||
@@ -3785,9 +3797,11 @@ int connectWithMaster(redisMaster *mi) {
|
||||
connSetPrivateData(mi->repl_transfer_s, mi);
|
||||
if (connConnect(mi->repl_transfer_s, mi->masterhost, mi->masterport,
|
||||
NET_FIRST_BIND_ADDR, syncWithMaster) == C_ERR) {
|
||||
int sev = g_pserver->enable_multimaster ? LL_NOTICE : LL_WARNING; // with multimaster its not unheard of to intentiallionall have downed masters
|
||||
serverLog(sev,"Unable to connect to MASTER: %s",
|
||||
connGetLastError(mi->repl_transfer_s));
|
||||
const char *err = "Unknown Error";
|
||||
if (mi->repl_transfer_s->last_errno != 0)
|
||||
err = connGetLastError(mi->repl_transfer_s);
|
||||
int sev = g_pserver->enable_multimaster ? LL_NOTICE : LL_WARNING; // with multimaster its not unheard of to intentionally have downed masters
|
||||
serverLog(sev, "Unable to connect to MASTER: %s", err);
|
||||
connClose(mi->repl_transfer_s);
|
||||
mi->repl_transfer_s = NULL;
|
||||
return C_ERR;
|
||||
@@ -4972,7 +4986,7 @@ void replicationStartPendingFork(void) {
|
||||
* In case of diskless replication, we make sure to wait the specified
|
||||
* number of seconds (according to configuration) so that other slaves
|
||||
* have the time to arrive before we start streaming. */
|
||||
if (!hasActiveChildProcess()) {
|
||||
if (!hasActiveChildProcessOrBGSave()) {
|
||||
time_t idle, max_idle = 0;
|
||||
int slaves_waiting = 0;
|
||||
int mincapa = -1;
|
||||
@@ -5640,7 +5654,10 @@ void flushReplBacklogToClients()
|
||||
replica->repl_end_off = g_pserver->master_repl_offset;
|
||||
|
||||
/* Only if the there isn't already a pending write do we prepare the client to write */
|
||||
serverAssert(replica->repl_curr_off != g_pserver->master_repl_offset);
|
||||
if (replica->repl_curr_off == g_pserver->master_repl_offset) {
|
||||
serverLog(LL_DEBUG, "Pending write when it's on repl_offset=%lld", g_pserver->master_repl_offset);
|
||||
continue;
|
||||
}
|
||||
prepareClientToWrite(replica);
|
||||
}
|
||||
if (fAsyncWrite)
|
||||
|
||||
@@ -1264,6 +1264,9 @@ void scriptingInit(int setup) {
|
||||
|
||||
/* Finally set the table as 'redis' global var. */
|
||||
lua_setglobal(lua,"redis");
|
||||
/* Set table as 'keydb' global var as well */
|
||||
lua_getglobal(lua,"redis");
|
||||
lua_setglobal(lua,"keydb");
|
||||
|
||||
/* Replace math.random and math.randomseed with our implementations. */
|
||||
lua_getglobal(lua,"math");
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace keydbutils
|
||||
template<>
|
||||
size_t hash(const sdsview &);
|
||||
}
|
||||
extern size_t g_semiOrderedSetTargetBucketSize;
|
||||
|
||||
template<typename T, typename T_KEY = T, bool MEMMOVE_SAFE = false>
|
||||
class semiorderedset
|
||||
@@ -41,11 +42,14 @@ class semiorderedset
|
||||
size_t idxRehash = (1ULL << bits_min);
|
||||
int cfPauseRehash = 0;
|
||||
|
||||
constexpr size_t targetElementsPerBucket()
|
||||
inline size_t targetElementsPerBucket()
|
||||
{
|
||||
// Aim for roughly 4 cache lines per bucket (determined by imperical testing)
|
||||
// lower values are faster but use more memory
|
||||
return std::max((64/sizeof(T))*8, (size_t)2);
|
||||
if (g_semiOrderedSetTargetBucketSize == 0)
|
||||
return std::max((64/sizeof(T))*8, (size_t)2);
|
||||
else
|
||||
return g_semiOrderedSetTargetBucketSize;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+66
-53
@@ -1706,7 +1706,7 @@ int redisDbPersistentData::incrementallyRehash() {
|
||||
* for dict.c to resize the hash tables accordingly to the fact we have an
|
||||
* active fork child running. */
|
||||
void updateDictResizePolicy(void) {
|
||||
if (!hasActiveChildProcess() || (g_pserver->FRdbSaveInProgress() && !cserver.fForkBgSave))
|
||||
if (!hasActiveChildProcess())
|
||||
dictEnableResize();
|
||||
else
|
||||
dictDisableResize();
|
||||
@@ -1725,7 +1725,11 @@ const char *strChildType(int type) {
|
||||
/* Return true if there are active children processes doing RDB saving,
|
||||
* AOF rewriting, or some side process spawned by a loaded module. */
|
||||
int hasActiveChildProcess() {
|
||||
return g_pserver->FRdbSaveInProgress() || g_pserver->child_pid != -1;
|
||||
return g_pserver->child_pid != -1;
|
||||
}
|
||||
|
||||
int hasActiveChildProcessOrBGSave() {
|
||||
return g_pserver->FRdbSaveInProgress() || hasActiveChildProcess();
|
||||
}
|
||||
|
||||
void resetChildState() {
|
||||
@@ -2074,7 +2078,7 @@ void databasesCron(bool fMainThread) {
|
||||
/* Perform hash tables rehashing if needed, but only if there are no
|
||||
* other processes saving the DB on disk. Otherwise rehashing is bad
|
||||
* as will cause a lot of copy-on-write of memory pages. */
|
||||
if (!hasActiveChildProcess() || g_pserver->FRdbSaveInProgress()) {
|
||||
if (!hasActiveChildProcess()) {
|
||||
/* We use global counters so if we stop the computation at a given
|
||||
* DB we'll be able to start from the successive in the next
|
||||
* cron loop iteration. */
|
||||
@@ -2215,6 +2219,7 @@ void checkChildrenDone(void) {
|
||||
g_pserver->rdbThreadVars.fRdbThreadCancel = false;
|
||||
g_pserver->rdbThreadVars.fDone = false;
|
||||
if (exitcode == 0) receiveChildInfo();
|
||||
closeChildInfoPipe();
|
||||
}
|
||||
}
|
||||
else if ((pid = waitpid(-1, &statloc, WNOHANG)) != 0) {
|
||||
@@ -2468,14 +2473,14 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
|
||||
/* Start a scheduled AOF rewrite if this was requested by the user while
|
||||
* a BGSAVE was in progress. */
|
||||
if (!hasActiveChildProcess() &&
|
||||
if (!hasActiveChildProcessOrBGSave() &&
|
||||
g_pserver->aof_rewrite_scheduled)
|
||||
{
|
||||
rewriteAppendOnlyFileBackground();
|
||||
}
|
||||
|
||||
/* Check if a background saving or AOF rewrite in progress terminated. */
|
||||
if (hasActiveChildProcess() || ldbPendingChildren())
|
||||
if (hasActiveChildProcessOrBGSave() || ldbPendingChildren())
|
||||
{
|
||||
run_with_period(1000) receiveChildInfo();
|
||||
checkChildrenDone();
|
||||
@@ -2517,7 +2522,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
|
||||
/* Trigger an AOF rewrite if needed. */
|
||||
if (g_pserver->aof_state == AOF_ON &&
|
||||
!hasActiveChildProcess() &&
|
||||
!hasActiveChildProcessOrBGSave() &&
|
||||
g_pserver->aof_rewrite_perc &&
|
||||
g_pserver->aof_current_size > g_pserver->aof_rewrite_min_size)
|
||||
{
|
||||
@@ -2601,7 +2606,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
* Note: this code must be after the replicationCron() call above so
|
||||
* make sure when refactoring this file to keep this order. This is useful
|
||||
* because we want to give priority to RDB savings for replication. */
|
||||
if (!hasActiveChildProcess() &&
|
||||
if (!hasActiveChildProcessOrBGSave() &&
|
||||
g_pserver->rdb_bgsave_scheduled &&
|
||||
(g_pserver->unixtime-g_pserver->lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||
|
||||
g_pserver->lastbgsave_status == C_OK))
|
||||
@@ -2815,9 +2820,6 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
|
||||
|
||||
runAndPropogateToReplicas(processClients);
|
||||
|
||||
/* Handle precise timeouts of blocked clients. */
|
||||
handleBlockedClientsTimeout();
|
||||
|
||||
/* Just call a subset of vital functions in case we are re-entering
|
||||
* the event loop from processEventsWhileBlocked(). Note that in this
|
||||
* case we keep track of the number of events we are processing, since
|
||||
@@ -4067,12 +4069,6 @@ void initServer(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* We have to initialize storage providers after the cluster has been initialized */
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
{
|
||||
g_pserver->db[idb]->storageProviderInitialize();
|
||||
}
|
||||
|
||||
saveMasterStatusToStorage(false); // eliminate the repl-offset field
|
||||
|
||||
/* Initialize ACL default password if it exists */
|
||||
@@ -4085,6 +4081,15 @@ void initServer(void) {
|
||||
* Thread Local Storage initialization collides with dlopen call.
|
||||
* see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */
|
||||
void InitServerLast() {
|
||||
|
||||
/* We have to initialize storage providers after the cluster has been initialized */
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_LOADING, REDISMODULE_SUBEVENT_LOADING_FLASH_START, NULL);
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
{
|
||||
g_pserver->db[idb]->storageProviderInitialize();
|
||||
}
|
||||
moduleFireServerEvent(REDISMODULE_EVENT_LOADING, REDISMODULE_SUBEVENT_LOADING_ENDED, NULL);
|
||||
|
||||
bioInit();
|
||||
set_jemalloc_bg_thread(cserver.jemalloc_bg_thread);
|
||||
g_pserver->initial_memory_usage = zmalloc_used_memory();
|
||||
@@ -5070,6 +5075,8 @@ bool client::asyncCommand(std::function<void(const redisDbPersistentDataSnapshot
|
||||
std::function<void(const redisDbPersistentDataSnapshot *)> &&postFn)
|
||||
{
|
||||
serverAssert(FCorrectThread(this));
|
||||
if (serverTL->in_eval)
|
||||
return false; // we cannot block clients in EVAL
|
||||
const redisDbPersistentDataSnapshot *snapshot = nullptr;
|
||||
if (!(this->flags & (CLIENT_MULTI | CLIENT_BLOCKED)))
|
||||
snapshot = this->db->createSnapshot(this->mvccCheckpoint, false /* fOptional */);
|
||||
@@ -5165,7 +5172,7 @@ int prepareForShutdown(int flags) {
|
||||
* to unlink file actully) in background thread.
|
||||
* The temp rdb file fd may won't be closed when redis exits quickly,
|
||||
* but OS will close this fd when process exits. */
|
||||
rdbRemoveTempFile(g_pserver->child_pid, 0);
|
||||
rdbRemoveTempFile(g_pserver->rdbThreadVars.tmpfileNum, 0);
|
||||
}
|
||||
|
||||
/* Kill module child if there is one. */
|
||||
@@ -5465,30 +5472,30 @@ NULL
|
||||
|
||||
/* Convert an amount of bytes into a human readable string in the form
|
||||
* of 100B, 2G, 100M, 4K, and so forth. */
|
||||
void bytesToHuman(char *s, unsigned long long n) {
|
||||
void bytesToHuman(char *s, unsigned long long n, size_t bufsize) {
|
||||
double d;
|
||||
|
||||
if (n < 1024) {
|
||||
/* Bytes */
|
||||
sprintf(s,"%lluB",n);
|
||||
snprintf(s,bufsize,"%lluB",n);
|
||||
} else if (n < (1024*1024)) {
|
||||
d = (double)n/(1024);
|
||||
sprintf(s,"%.2fK",d);
|
||||
snprintf(s,bufsize,"%.2fK",d);
|
||||
} else if (n < (1024LL*1024*1024)) {
|
||||
d = (double)n/(1024*1024);
|
||||
sprintf(s,"%.2fM",d);
|
||||
snprintf(s,bufsize,"%.2fM",d);
|
||||
} else if (n < (1024LL*1024*1024*1024)) {
|
||||
d = (double)n/(1024LL*1024*1024);
|
||||
sprintf(s,"%.2fG",d);
|
||||
snprintf(s,bufsize,"%.2fG",d);
|
||||
} else if (n < (1024LL*1024*1024*1024*1024)) {
|
||||
d = (double)n/(1024LL*1024*1024*1024);
|
||||
sprintf(s,"%.2fT",d);
|
||||
snprintf(s,bufsize,"%.2fT",d);
|
||||
} else if (n < (1024LL*1024*1024*1024*1024*1024)) {
|
||||
d = (double)n/(1024LL*1024*1024*1024*1024);
|
||||
sprintf(s,"%.2fP",d);
|
||||
snprintf(s,bufsize,"%.2fP",d);
|
||||
} else {
|
||||
/* Let's hope we never need this */
|
||||
sprintf(s,"%lluB",n);
|
||||
snprintf(s,bufsize,"%lluB",n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5664,13 +5671,13 @@ sds genRedisInfoString(const char *section) {
|
||||
if (zmalloc_used > g_pserver->stat_peak_memory)
|
||||
g_pserver->stat_peak_memory = zmalloc_used;
|
||||
|
||||
bytesToHuman(hmem,zmalloc_used);
|
||||
bytesToHuman(peak_hmem,g_pserver->stat_peak_memory);
|
||||
bytesToHuman(total_system_hmem,total_system_mem);
|
||||
bytesToHuman(used_memory_lua_hmem,memory_lua);
|
||||
bytesToHuman(used_memory_scripts_hmem,mh->lua_caches);
|
||||
bytesToHuman(used_memory_rss_hmem,g_pserver->cron_malloc_stats.process_rss);
|
||||
bytesToHuman(maxmemory_hmem,g_pserver->maxmemory);
|
||||
bytesToHuman(hmem,zmalloc_used,sizeof(hmem));
|
||||
bytesToHuman(peak_hmem,g_pserver->stat_peak_memory,sizeof(peak_hmem));
|
||||
bytesToHuman(total_system_hmem,total_system_mem,sizeof(total_system_hmem));
|
||||
bytesToHuman(used_memory_lua_hmem,memory_lua,sizeof(used_memory_lua_hmem));
|
||||
bytesToHuman(used_memory_scripts_hmem,mh->lua_caches,sizeof(used_memory_scripts_hmem));
|
||||
bytesToHuman(used_memory_rss_hmem,g_pserver->cron_malloc_stats.process_rss,sizeof(used_memory_rss_hmem));
|
||||
bytesToHuman(maxmemory_hmem,g_pserver->maxmemory,sizeof(maxmemory_hmem));
|
||||
|
||||
if (sections++) info = sdscat(info,"\r\n");
|
||||
info = sdscatprintf(info,
|
||||
@@ -6557,7 +6564,8 @@ void usage(void) {
|
||||
|
||||
void redisAsciiArt(void) {
|
||||
#include "asciilogo.h"
|
||||
char *buf = (char*)zmalloc(1024*16, MALLOC_LOCAL);
|
||||
size_t bufsize = 1024*16;
|
||||
char *buf = (char*)zmalloc(bufsize, MALLOC_LOCAL);
|
||||
const char *mode;
|
||||
|
||||
if (g_pserver->cluster_enabled) mode = "cluster";
|
||||
@@ -6579,7 +6587,7 @@ void redisAsciiArt(void) {
|
||||
);
|
||||
} else {
|
||||
sds motd = fetchMOTD(true, cserver.enable_motd);
|
||||
snprintf(buf,1024*16,ascii_logo,
|
||||
snprintf(buf,bufsize,ascii_logo,
|
||||
KEYDB_REAL_VERSION,
|
||||
redisGitSHA1(),
|
||||
strtol(redisGitDirty(),NULL,10) > 0,
|
||||
@@ -6882,6 +6890,7 @@ int redisFork(int purpose) {
|
||||
latencyAddSampleIfNeeded("fork-lock",(ustime()-startWriteLock)/1000);
|
||||
if ((childpid = fork()) == 0) {
|
||||
/* Child */
|
||||
aeForkLockInChild();
|
||||
aeReleaseForkLock();
|
||||
g_pserver->in_fork_child = purpose;
|
||||
setOOMScoreAdj(CONFIG_OOM_BGCHILD);
|
||||
@@ -7564,6 +7573,29 @@ int main(int argc, char **argv) {
|
||||
|
||||
validateConfiguration();
|
||||
|
||||
if (!g_pserver->sentinel_mode) {
|
||||
#ifdef __linux__
|
||||
linuxMemoryWarnings();
|
||||
#if defined (__arm64__)
|
||||
int ret;
|
||||
if ((ret = linuxMadvFreeForkBugCheck())) {
|
||||
if (ret == 1)
|
||||
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
|
||||
"Please upgrade to the latest stable kernel.");
|
||||
else
|
||||
serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
|
||||
"Your system could be affected, please report this error.");
|
||||
if (!checkIgnoreWarning("ARM64-COW-BUG")) {
|
||||
serverLog(LL_WARNING,"KeyDB will now exit to prevent data corruption. "
|
||||
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif /* __arm64__ */
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
|
||||
const char *err;
|
||||
if (!initializeStorageProvider(&err))
|
||||
{
|
||||
@@ -7589,25 +7621,6 @@ int main(int argc, char **argv) {
|
||||
if (!g_pserver->sentinel_mode) {
|
||||
/* Things not needed when running in Sentinel mode. */
|
||||
serverLog(LL_WARNING,"Server initialized");
|
||||
#ifdef __linux__
|
||||
linuxMemoryWarnings();
|
||||
#if defined (__arm64__)
|
||||
int ret;
|
||||
if ((ret = linuxMadvFreeForkBugCheck())) {
|
||||
if (ret == 1)
|
||||
serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
|
||||
"Please upgrade to the latest stable kernel.");
|
||||
else
|
||||
serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
|
||||
"Your system could be affected, please report this error.");
|
||||
if (!checkIgnoreWarning("ARM64-COW-BUG")) {
|
||||
serverLog(LL_WARNING,"KeyDB will now exit to prevent data corruption. "
|
||||
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif /* __arm64__ */
|
||||
#endif /* __linux__ */
|
||||
moduleInitModulesSystemLast();
|
||||
moduleLoadFromQueue();
|
||||
ACLLoadUsersAtStartup();
|
||||
|
||||
+20
-8
@@ -1204,12 +1204,12 @@ public:
|
||||
void disableKeyCache();
|
||||
bool keycacheIsEnabled();
|
||||
|
||||
bool prefetchKeysAsync(client *c, struct parsed_command &command, bool fExecOK);
|
||||
void prefetchKeysAsync(client *c, struct parsed_command &command);
|
||||
|
||||
bool FSnapshot() const { return m_spdbSnapshotHOLDER != nullptr; }
|
||||
|
||||
std::unique_ptr<const StorageCache> CloneStorageCache() { return std::unique_ptr<const StorageCache>(m_spstorage->clone()); }
|
||||
void bulkStorageInsert(char **rgKeys, size_t *rgcbKeys, char **rgVals, size_t *rgcbVals, size_t celem);
|
||||
void bulkDirectStorageInsert(char **rgKeys, size_t *rgcbKeys, char **rgVals, size_t *rgcbVals, size_t celem);
|
||||
|
||||
dict_iter find_cached_threadsafe(const char *key) const;
|
||||
|
||||
@@ -1370,7 +1370,7 @@ struct redisDb : public redisDbPersistentDataSnapshot
|
||||
using redisDbPersistentData::FRehashing;
|
||||
using redisDbPersistentData::FTrackingChanges;
|
||||
using redisDbPersistentData::CloneStorageCache;
|
||||
using redisDbPersistentData::bulkStorageInsert;
|
||||
using redisDbPersistentData::bulkDirectStorageInsert;
|
||||
|
||||
public:
|
||||
const redisDbPersistentDataSnapshot *createSnapshot(uint64_t mvccCheckpoint, bool fOptional) {
|
||||
@@ -1909,6 +1909,12 @@ struct MasterSaveInfo {
|
||||
masterhost = sdsstring(sdsdup(mi.masterhost));
|
||||
masterport = mi.masterport;
|
||||
}
|
||||
MasterSaveInfo(const MasterSaveInfo &other) {
|
||||
masterhost = other.masterhost;
|
||||
masterport = other.masterport;
|
||||
memcpy(master_replid, other.master_replid, sizeof(master_replid));
|
||||
master_initial_offset = other.master_initial_offset;
|
||||
}
|
||||
|
||||
MasterSaveInfo &operator=(const MasterSaveInfo &other) {
|
||||
masterhost = other.masterhost;
|
||||
@@ -2464,7 +2470,8 @@ struct redisServer {
|
||||
time_t lastbgsave_try; /* Unix time of last attempted bgsave */
|
||||
time_t rdb_save_time_last; /* Time used by last RDB save run. */
|
||||
time_t rdb_save_time_start; /* Current RDB save start time. */
|
||||
pid_t rdb_child_pid = -1; /* Used only during fork bgsave */
|
||||
mstime_t rdb_save_latency; /* Used to track end to end latency of rdb save*/
|
||||
pid_t rdb_child_pid = -1; /* Used only during fork bgsave */
|
||||
int rdb_bgsave_scheduled; /* BGSAVE when possible if true. */
|
||||
int rdb_child_type; /* Type of save by active child. */
|
||||
int lastbgsave_status; /* C_OK or C_ERR */
|
||||
@@ -2722,9 +2729,13 @@ struct redisServer {
|
||||
long long repl_batch_offStart = -1;
|
||||
long long repl_batch_idxStart = -1;
|
||||
|
||||
long long rand_total_threshold;
|
||||
|
||||
int config_soft_shutdown = false;
|
||||
bool soft_shutdown = false;
|
||||
|
||||
int flash_disable_key_cache = false;
|
||||
|
||||
/* Lock Contention Ring Buffer */
|
||||
static const size_t s_lockContentionSamples = 64;
|
||||
uint16_t rglockSamples[s_lockContentionSamples];
|
||||
@@ -3248,6 +3259,7 @@ void receiveChildInfo(void);
|
||||
void executeWithoutGlobalLock(std::function<void()> func);
|
||||
int redisFork(int type);
|
||||
int hasActiveChildProcess();
|
||||
int hasActiveChildProcessOrBGSave();
|
||||
void resetChildState();
|
||||
int isMutuallyExclusiveChildType(int type);
|
||||
|
||||
@@ -3886,13 +3898,13 @@ void incrementMvccTstamp();
|
||||
|
||||
#if __GNUC__ >= 7 && !defined(NO_DEPRECATE_FREE)
|
||||
[[deprecated]]
|
||||
void *calloc(size_t count, size_t size);
|
||||
void *calloc(size_t count, size_t size) noexcept;
|
||||
[[deprecated]]
|
||||
void free(void *ptr);
|
||||
void free(void *ptr) noexcept;
|
||||
[[deprecated]]
|
||||
void *malloc(size_t size);
|
||||
void *malloc(size_t size) noexcept;
|
||||
[[deprecated]]
|
||||
void *realloc(void *ptr, size_t size);
|
||||
void *realloc(void *ptr, size_t size) noexcept;
|
||||
#endif
|
||||
|
||||
/* Debugging stuff */
|
||||
|
||||
+6
-4
@@ -194,8 +194,8 @@ void sortCommand(client *c) {
|
||||
list *operations;
|
||||
unsigned int outputlen = 0;
|
||||
int desc = 0, alpha = 0;
|
||||
long limit_start = 0, limit_count = -1, start, end;
|
||||
int j, dontsort = 0, vectorlen;
|
||||
long limit_start = 0, limit_count = -1, start, end, vectorlen;
|
||||
int j, dontsort = 0;
|
||||
int getop = 0; /* GET operation counter */
|
||||
int int_conversion_error = 0;
|
||||
int syntax_error = 0;
|
||||
@@ -321,8 +321,10 @@ void sortCommand(client *c) {
|
||||
default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
|
||||
}
|
||||
|
||||
/* Perform LIMIT start,count sanity checking. */
|
||||
start = (limit_start < 0) ? 0 : limit_start;
|
||||
/* Perform LIMIT start,count sanity checking.
|
||||
* And avoid integer overflow by limiting inputs to object sizes. */
|
||||
start = std::min(std::max(limit_start, (long)0), vectorlen);
|
||||
limit_count = std::min(std::max(limit_count, (long)-1), vectorlen);
|
||||
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
|
||||
if (start >= vectorlen) {
|
||||
start = vectorlen-1;
|
||||
|
||||
@@ -100,14 +100,14 @@ bool RocksDBStorageProvider::erase(const char *key, size_t cchKey)
|
||||
{
|
||||
rocksdb::Status status;
|
||||
std::unique_lock<fastlock> l(m_lock);
|
||||
if (!FKeyExists(key, cchKey))
|
||||
return false;
|
||||
if (m_spbatch != nullptr)
|
||||
{
|
||||
status = m_spbatch->Delete(m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FKeyExists(key, cchKey))
|
||||
return false;
|
||||
status = m_spdb->Delete(WriteOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cchKey));
|
||||
}
|
||||
if (status.ok())
|
||||
@@ -202,12 +202,12 @@ rocksdb::WriteOptions RocksDBStorageProvider::WriteOptions() const
|
||||
void RocksDBStorageProvider::beginWriteBatch()
|
||||
{
|
||||
m_lock.lock();
|
||||
m_spbatch = std::make_unique<rocksdb::WriteBatch>();
|
||||
m_spbatch = std::make_unique<rocksdb::WriteBatchWithIndex>();
|
||||
}
|
||||
|
||||
void RocksDBStorageProvider::endWriteBatch()
|
||||
{
|
||||
m_spdb->Write(WriteOptions(), m_spbatch.get());
|
||||
m_spdb->Write(WriteOptions(), m_spbatch.get()->GetWriteBatch());
|
||||
m_spbatch = nullptr;
|
||||
m_lock.unlock();
|
||||
}
|
||||
@@ -230,5 +230,7 @@ void RocksDBStorageProvider::flush()
|
||||
bool RocksDBStorageProvider::FKeyExists(const char *key, size_t cch) const
|
||||
{
|
||||
rocksdb::PinnableSlice slice;
|
||||
if (m_spbatch)
|
||||
return m_spbatch->GetFromBatchAndDB(m_spdb.get(), ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
|
||||
return m_spdb->Get(ReadOptions(), m_spcolfamily.get(), rocksdb::Slice(key, cch), &slice).ok();
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <memory>
|
||||
#include "../IStorage.h"
|
||||
#include <rocksdb/db.h>
|
||||
#include <rocksdb/utilities/write_batch_with_index.h>
|
||||
#include "../fastlock.h"
|
||||
|
||||
#define INTERNAL_KEY_PREFIX "\x00\x04\x03\x00\x05\x02\x04"
|
||||
@@ -15,7 +16,7 @@ class RocksDBStorageProvider : public IStorage
|
||||
{
|
||||
RocksDBStorageFactory *m_pfactory;
|
||||
std::shared_ptr<rocksdb::DB> m_spdb; // Note: This must be first so it is deleted last
|
||||
std::unique_ptr<rocksdb::WriteBatch> m_spbatch;
|
||||
std::unique_ptr<rocksdb::WriteBatchWithIndex> m_spbatch;
|
||||
const rocksdb::Snapshot *m_psnapshot = nullptr;
|
||||
std::shared_ptr<rocksdb::ColumnFamilyHandle> m_spcolfamily;
|
||||
rocksdb::ReadOptions m_readOptionsTemplate;
|
||||
|
||||
@@ -47,6 +47,7 @@ rocksdb::Options RocksDBStorageFactory::RocksDbOptions()
|
||||
options.create_if_missing = true;
|
||||
options.create_missing_column_families = true;
|
||||
options.info_log_level = rocksdb::ERROR_LEVEL;
|
||||
options.max_total_wal_size = 1 * 1024 * 1024 * 1024;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -729,6 +729,10 @@ void hincrbyfloatCommand(client *c) {
|
||||
unsigned int vlen;
|
||||
|
||||
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
|
||||
if (isnan(incr) || isinf(incr)) {
|
||||
addReplyError(c,"value is NaN or Infinity");
|
||||
return;
|
||||
}
|
||||
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
|
||||
if (hashTypeGetValue(o,szFromObj(c->argv[2]),&vstr,&vlen,&ll) == C_OK) {
|
||||
if (vstr) {
|
||||
@@ -1062,6 +1066,8 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
|
||||
addReplyBulkCBuffer(c, key, sdslen(key));
|
||||
if (withvalues)
|
||||
addReplyBulkCBuffer(c, value, sdslen(value));
|
||||
if (c->flags & CLIENT_CLOSE_ASAP)
|
||||
break;
|
||||
}
|
||||
} else if (hash->encoding == OBJ_ENCODING_ZIPLIST) {
|
||||
ziplistEntry *keys, *vals = NULL;
|
||||
@@ -1075,6 +1081,8 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
|
||||
count -= sample_count;
|
||||
ziplistRandomPairs((unsigned char*)ptrFromObj(hash), sample_count, keys, vals);
|
||||
harndfieldReplyWithZiplist(c, sample_count, keys, vals);
|
||||
if (c->flags & CLIENT_CLOSE_ASAP)
|
||||
break;
|
||||
}
|
||||
zfree(keys);
|
||||
zfree(vals);
|
||||
@@ -1221,12 +1229,17 @@ void hrandfieldCommand(client *c) {
|
||||
ziplistEntry ele;
|
||||
|
||||
if (c->argc >= 3) {
|
||||
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
|
||||
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
|
||||
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withvalues"))) {
|
||||
addReplyErrorObject(c,shared.syntaxerr);
|
||||
return;
|
||||
} else if (c->argc == 4)
|
||||
} else if (c->argc == 4) {
|
||||
withvalues = 1;
|
||||
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
|
||||
addReplyError(c,"value is out of range");
|
||||
return;
|
||||
}
|
||||
}
|
||||
hrandfieldWithCountCommand(c, l, withvalues);
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -263,7 +263,7 @@ sds writeJsonValue(sds output, const char *valIn, size_t cchIn) {
|
||||
serverAssert(!FSimpleJsonEscapeCh(valIn[ich]));
|
||||
if (FExtendedJsonEscapeCh(valIn[ich])) {
|
||||
dst[ichDst++] = '\\'; dst[ichDst++] = 'u';
|
||||
sprintf(dst + ichDst, "%4x", valIn[ich]);
|
||||
snprintf(dst + ichDst, cchIn+cchEscapeExtra-ichDst, "%4x", valIn[ich]);
|
||||
ichDst += 4;
|
||||
} else {
|
||||
dst[ichDst++] = valIn[ich];
|
||||
|
||||
+7
-1
@@ -672,7 +672,11 @@ void srandmemberWithCountCommand(client *c) {
|
||||
|
||||
dict *d;
|
||||
|
||||
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
|
||||
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
|
||||
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
|
||||
addReplyError(c,"value is out of range");
|
||||
return;
|
||||
}
|
||||
if (l >= 0) {
|
||||
count = (unsigned long) l;
|
||||
} else {
|
||||
@@ -706,6 +710,8 @@ void srandmemberWithCountCommand(client *c) {
|
||||
} else {
|
||||
addReplyBulkCBuffer(c,ele,sdslen(ele));
|
||||
}
|
||||
if (c->flags & CLIENT_CLOSE_ASAP)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+7
-7
@@ -789,13 +789,13 @@ int64_t streamTrim(stream *s, streamAddTrimArgs *args) {
|
||||
* update it after (and if) we actually remove the entry */
|
||||
unsigned char *pcopy = p;
|
||||
|
||||
int flags = lpGetInteger(p);
|
||||
int64_t flags = lpGetInteger(p);
|
||||
p = lpNext(lp, p); /* Skip flags. */
|
||||
int to_skip;
|
||||
int64_t to_skip;
|
||||
|
||||
int ms_delta = lpGetInteger(p);
|
||||
int64_t ms_delta = lpGetInteger(p);
|
||||
p = lpNext(lp, p); /* Skip ID ms delta */
|
||||
int seq_delta = lpGetInteger(p);
|
||||
int64_t seq_delta = lpGetInteger(p);
|
||||
p = lpNext(lp, p); /* Skip ID seq delta */
|
||||
|
||||
streamID currid = {0}; /* For MINID */
|
||||
@@ -1125,7 +1125,7 @@ int streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields) {
|
||||
* the first entry emitted for this listpack, then we already
|
||||
* emitted the current entry, and have to go back to the previous
|
||||
* one. */
|
||||
int lp_count = lpGetInteger(si->lp_ele);
|
||||
int64_t lp_count = lpGetInteger(si->lp_ele);
|
||||
while(lp_count--) si->lp_ele = lpPrev(si->lp,si->lp_ele);
|
||||
/* Seek lp-count of prev entry. */
|
||||
si->lp_ele = lpPrev(si->lp,si->lp_ele);
|
||||
@@ -1155,7 +1155,7 @@ int streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields) {
|
||||
|
||||
/* Get the flags entry. */
|
||||
si->lp_flags = si->lp_ele;
|
||||
int flags = lpGetInteger(si->lp_ele);
|
||||
int64_t flags = lpGetInteger(si->lp_ele);
|
||||
si->lp_ele = lpNext(si->lp,si->lp_ele); /* Seek ID. */
|
||||
|
||||
/* Get the ID: it is encoded as difference between the master
|
||||
@@ -1264,7 +1264,7 @@ void streamIteratorRemoveEntry(streamIterator *si, streamID *current) {
|
||||
* deleted entries in the listpack header.
|
||||
*
|
||||
* We start flagging: */
|
||||
int flags = lpGetInteger(si->lp_flags);
|
||||
int64_t flags = lpGetInteger(si->lp_flags);
|
||||
flags |= STREAM_ITEM_FLAG_DELETED;
|
||||
lp = lpReplaceInteger(lp,&si->lp_flags,flags);
|
||||
|
||||
|
||||
+11
-6
@@ -38,8 +38,14 @@ int getGenericCommand(client *c);
|
||||
* String Commands
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static int checkStringLength(client *c, long long size) {
|
||||
if (!(c->flags & CLIENT_MASTER) && size > g_pserver->proto_max_bulk_len) {
|
||||
static int checkStringLength(client *c, long long size, long long append) {
|
||||
if (c->flags & CLIENT_MASTER)
|
||||
return C_OK;
|
||||
/* 'uint64_t' cast is there just to prevent undefined behavior on overflow */
|
||||
long long total = (uint64_t)size + append;
|
||||
/* Test configured max-bulk-len represending a limit of the biggest string object,
|
||||
* and also test for overflow. */
|
||||
if (total > g_pserver->proto_max_bulk_len || total < size || total < append) {
|
||||
addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
|
||||
return C_ERR;
|
||||
}
|
||||
@@ -445,7 +451,7 @@ void setrangeCommand(client *c) {
|
||||
}
|
||||
|
||||
/* Return when the resulting string exceeds allowed size */
|
||||
if (checkStringLength(c,offset+sdslen(value)) != C_OK)
|
||||
if (checkStringLength(c,offset,sdslen(value)) != C_OK)
|
||||
return;
|
||||
|
||||
o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
|
||||
@@ -465,7 +471,7 @@ void setrangeCommand(client *c) {
|
||||
}
|
||||
|
||||
/* Return when the resulting string exceeds allowed size */
|
||||
if (checkStringLength(c,offset+sdslen(value)) != C_OK)
|
||||
if (checkStringLength(c,offset,sdslen(value)) != C_OK)
|
||||
return;
|
||||
|
||||
/* Create a copy when the object is shared or encoded. */
|
||||
@@ -685,8 +691,7 @@ void appendCommand(client *c) {
|
||||
|
||||
/* "append" is an argument, so always an sds */
|
||||
append = c->argv[2];
|
||||
totlen = stringObjectLen(o)+sdslen((sds)ptrFromObj(append));
|
||||
if (checkStringLength(c,totlen) != C_OK)
|
||||
if (checkStringLength(c,stringObjectLen(o),sdslen((sds)ptrFromObj(append))) != C_OK)
|
||||
return;
|
||||
|
||||
/* Append the value */
|
||||
|
||||
+11
-2
@@ -4054,6 +4054,8 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
|
||||
addReplyBulkCBuffer(c, key, sdslen(key));
|
||||
if (withscores)
|
||||
addReplyDouble(c, *(double*)dictGetVal(de));
|
||||
if (c->flags & CLIENT_CLOSE_ASAP)
|
||||
break;
|
||||
}
|
||||
} else if (zsetobj->encoding == OBJ_ENCODING_ZIPLIST) {
|
||||
ziplistEntry *keys, *vals = NULL;
|
||||
@@ -4067,6 +4069,8 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
|
||||
count -= sample_count;
|
||||
ziplistRandomPairs((unsigned char*)ptrFromObj(zsetobj), sample_count, keys, vals);
|
||||
zarndmemberReplyWithZiplist(c, sample_count, keys, vals);
|
||||
if (c->flags & CLIENT_CLOSE_ASAP)
|
||||
break;
|
||||
}
|
||||
zfree(keys);
|
||||
zfree(vals);
|
||||
@@ -4210,12 +4214,17 @@ void zrandmemberCommand(client *c) {
|
||||
ziplistEntry ele;
|
||||
|
||||
if (c->argc >= 3) {
|
||||
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
|
||||
if (getRangeLongFromObjectOrReply(c,c->argv[2],-LONG_MAX,LONG_MAX,&l,NULL) != C_OK) return;
|
||||
if (c->argc > 4 || (c->argc == 4 && strcasecmp(szFromObj(c->argv[3]),"withscores"))) {
|
||||
addReplyErrorObject(c,shared.syntaxerr);
|
||||
return;
|
||||
} else if (c->argc == 4)
|
||||
} else if (c->argc == 4) {
|
||||
withscores = 1;
|
||||
if (l < -g_pserver->rand_total_threshold || l > g_pserver->rand_total_threshold) {
|
||||
addReplyError(c,"value is out of range");
|
||||
return;
|
||||
}
|
||||
}
|
||||
zrandmemberWithCountCommand(c, l, withscores);
|
||||
return;
|
||||
}
|
||||
|
||||
+42
-8
@@ -46,6 +46,9 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
#include <openssl/decoder.h>
|
||||
#endif
|
||||
|
||||
#define REDIS_TLS_PROTO_TLSv1 (1<<0)
|
||||
#define REDIS_TLS_PROTO_TLSv1_1 (1<<1)
|
||||
@@ -154,14 +157,13 @@ void tlsInit(void) {
|
||||
*/
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
OPENSSL_config(NULL);
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
#elif OPENSSL_VERSION_NUMBER < 0x10101000L
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
|
||||
#else
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG|OPENSSL_INIT_ATFORK, NULL);
|
||||
#endif
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
#ifdef USE_CRYPTO_LOCKS
|
||||
initCryptoLocks();
|
||||
@@ -364,20 +366,46 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
|
||||
if (ctx_config->prefer_server_ciphers)
|
||||
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
#if defined(SSL_CTX_set_ecdh_auto)
|
||||
#if ((OPENSSL_VERSION_NUMBER < 0x30000000L) && defined(SSL_CTX_set_ecdh_auto))
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
#endif
|
||||
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
|
||||
|
||||
if (ctx_config->dh_params_file) {
|
||||
FILE *dhfile = fopen(ctx_config->dh_params_file, "r");
|
||||
DH *dh = NULL;
|
||||
if (!dhfile) {
|
||||
serverLog(LL_WARNING, "Failed to load %s: %s", ctx_config->dh_params_file, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
dh = PEM_read_DHparams(dhfile, NULL, NULL, NULL);
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
||||
EVP_PKEY *pkey = NULL;
|
||||
OSSL_DECODER_CTX *dctx = OSSL_DECODER_CTX_new_for_pkey(
|
||||
&pkey, "PEM", NULL, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
|
||||
if (!dctx) {
|
||||
serverLog(LL_WARNING, "No decoder for DH params.");
|
||||
fclose(dhfile);
|
||||
goto error;
|
||||
}
|
||||
if (!OSSL_DECODER_from_fp(dctx, dhfile)) {
|
||||
serverLog(LL_WARNING, "%s: failed to read DH params.", ctx_config->dh_params_file);
|
||||
OSSL_DECODER_CTX_free(dctx);
|
||||
fclose(dhfile);
|
||||
goto error;
|
||||
}
|
||||
|
||||
OSSL_DECODER_CTX_free(dctx);
|
||||
fclose(dhfile);
|
||||
|
||||
if (SSL_CTX_set0_tmp_dh_pkey(ctx, pkey) <= 0) {
|
||||
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
|
||||
serverLog(LL_WARNING, "Failed to load DH params file: %s: %s", ctx_config->dh_params_file, errbuf);
|
||||
EVP_PKEY_free(pkey);
|
||||
goto error;
|
||||
}
|
||||
/* Not freeing pkey, it is owned by OpenSSL now */
|
||||
#else
|
||||
DH *dh = PEM_read_DHparams(dhfile, NULL, NULL, NULL);
|
||||
fclose(dhfile);
|
||||
if (!dh) {
|
||||
serverLog(LL_WARNING, "%s: failed to read DH params.", ctx_config->dh_params_file);
|
||||
@@ -392,6 +420,11 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
|
||||
}
|
||||
|
||||
DH_free(dh);
|
||||
#endif
|
||||
} else {
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
||||
SSL_CTX_set_dh_auto(ctx, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If a client-side certificate is configured, create an explicit client context */
|
||||
@@ -583,8 +616,9 @@ bool tlsValidateCertificateName(tls_connection* conn){
|
||||
/* If neither the CN nor the SANs match, update the SSL error and return false */
|
||||
conn->c.last_errno = 0;
|
||||
if (conn->ssl_error) zfree(conn->ssl_error);
|
||||
conn->ssl_error = (char*)zmalloc(512);
|
||||
snprintf(conn->ssl_error, 512, "Client CN (%s) and SANs not found in allowlist.", commonName);
|
||||
size_t bufsize = 512;
|
||||
conn->ssl_error = (char*)zmalloc(bufsize);
|
||||
snprintf(conn->ssl_error, bufsize, "Client CN (%s) and SANs not found in allowlist.", commonName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -1704,17 +1704,17 @@ static unsigned char *createIntList() {
|
||||
unsigned char *zl = ziplistNew();
|
||||
char buf[32];
|
||||
|
||||
sprintf(buf, "100");
|
||||
snprintf(buf, sizeof(buf), "100");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
|
||||
sprintf(buf, "128000");
|
||||
snprintf(buf, sizeof(buf), "128000");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
|
||||
sprintf(buf, "-100");
|
||||
snprintf(buf, sizeof(buf), "-100");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
|
||||
sprintf(buf, "4294967296");
|
||||
snprintf(buf, sizeof(buf), "4294967296");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_HEAD);
|
||||
sprintf(buf, "non integer");
|
||||
snprintf(buf, sizeof(buf), "non integer");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
|
||||
sprintf(buf, "much much longer non integer");
|
||||
snprintf(buf, sizeof(buf), "much much longer non integer");
|
||||
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), ZIPLIST_TAIL);
|
||||
return zl;
|
||||
}
|
||||
@@ -2228,7 +2228,7 @@ int ziplistTest(int argc, char **argv, int accurate) {
|
||||
char buf[32];
|
||||
int i,len;
|
||||
for (i = 0; i < 1000; i++) {
|
||||
len = sprintf(buf,"%d",i);
|
||||
len = snprintf(buf,sizeof(buf),"%d",i);
|
||||
zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL);
|
||||
}
|
||||
for (i = 0; i < 1000; i++) {
|
||||
@@ -2375,13 +2375,13 @@ int ziplistTest(int argc, char **argv, int accurate) {
|
||||
} else {
|
||||
switch(rand() % 3) {
|
||||
case 0:
|
||||
buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
|
||||
buflen = snprintf(buf,sizeof(buf),"%lld",(0LL + rand()) >> 20);
|
||||
break;
|
||||
case 1:
|
||||
buflen = sprintf(buf,"%lld",(0LL + rand()));
|
||||
buflen = snprintf(buf,sizeof(buf),"%lld",(0LL + rand()));
|
||||
break;
|
||||
case 2:
|
||||
buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
|
||||
buflen = snprintf(buf,sizeof(buf),"%lld",(0LL + rand()) << 20);
|
||||
break;
|
||||
default:
|
||||
assert(NULL);
|
||||
@@ -2410,7 +2410,7 @@ int ziplistTest(int argc, char **argv, int accurate) {
|
||||
|
||||
assert(ziplistGet(p,&sstr,&slen,&sval));
|
||||
if (sstr == NULL) {
|
||||
buflen = sprintf(buf,"%lld",sval);
|
||||
buflen = snprintf(buf,sizeof(buf),"%lld",sval);
|
||||
} else {
|
||||
buflen = slen;
|
||||
memcpy(buf,sstr,buflen);
|
||||
|
||||
+16
-16
@@ -80,12 +80,12 @@ static_assert((PREFIX_SIZE % 16) == 0, "Our prefix must be modulo 16-bytes or ou
|
||||
#define realloc(ptr,size, type) tc_realloc(ptr,size)
|
||||
#define free(ptr) tc_free(ptr)
|
||||
#elif defined(USE_JEMALLOC)
|
||||
#define malloc(size, type) je_malloc(size)
|
||||
#define calloc(count,size,type) je_calloc(count,size)
|
||||
#define realloc(ptr,size,type) je_realloc(ptr,size)
|
||||
#define free(ptr) je_free(ptr)
|
||||
#define mallocx(size,flags) je_mallocx(size,flags)
|
||||
#define dallocx(ptr,flags) je_dallocx(ptr,flags)
|
||||
#define malloc(size, type) malloc(size)
|
||||
#define calloc(count,size,type) calloc(count,size)
|
||||
#define realloc(ptr,size,type) realloc(ptr,size)
|
||||
#define free(ptr) free(ptr)
|
||||
#define mallocx(size,flags) mallocx(size,flags)
|
||||
#define dallocx(ptr,flags) dallocx(ptr,flags)
|
||||
#else
|
||||
#define malloc(size, type) malloc(size)
|
||||
#define calloc(count,size,type) calloc(count,size)
|
||||
@@ -370,7 +370,7 @@ size_t zmalloc_get_rss(void) {
|
||||
int fd, count;
|
||||
char *p, *x;
|
||||
|
||||
snprintf(filename,256,"/proc/%ld/stat",(long) getpid());
|
||||
snprintf(filename,sizeof(filename),"/proc/%ld/stat",(long) getpid());
|
||||
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
|
||||
if (read(fd,buf,4096) <= 0) {
|
||||
close(fd);
|
||||
@@ -462,7 +462,7 @@ size_t zmalloc_get_rss(void) {
|
||||
char filename[256];
|
||||
int fd;
|
||||
|
||||
snprintf(filename,256,"/proc/%ld/psinfo",(long) getpid());
|
||||
snprintf(filename,sizeof(filename),"/proc/%ld/psinfo",(long) getpid());
|
||||
|
||||
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
|
||||
if (ioctl(fd, PIOCPSINFO, &info) == -1) {
|
||||
@@ -494,17 +494,17 @@ int zmalloc_get_allocator_info(size_t *allocated,
|
||||
*allocated = *resident = *active = 0;
|
||||
/* Update the statistics cached by mallctl. */
|
||||
sz = sizeof(epoch);
|
||||
je_mallctl("epoch", &epoch, &sz, &epoch, sz);
|
||||
mallctl("epoch", &epoch, &sz, &epoch, sz);
|
||||
sz = sizeof(size_t);
|
||||
/* Unlike RSS, this does not include RSS from shared libraries and other non
|
||||
* heap mappings. */
|
||||
je_mallctl("stats.resident", resident, &sz, NULL, 0);
|
||||
mallctl("stats.resident", resident, &sz, NULL, 0);
|
||||
/* Unlike resident, this doesn't not include the pages jemalloc reserves
|
||||
* for re-use (purge will clean that). */
|
||||
je_mallctl("stats.active", active, &sz, NULL, 0);
|
||||
mallctl("stats.active", active, &sz, NULL, 0);
|
||||
/* Unlike zmalloc_used_memory, this matches the stats.resident by taking
|
||||
* into account all allocations done by this process (not only zmalloc). */
|
||||
je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
|
||||
mallctl("stats.allocated", allocated, &sz, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -512,7 +512,7 @@ void set_jemalloc_bg_thread(int enable) {
|
||||
/* let jemalloc do purging asynchronously, required when there's no traffic
|
||||
* after flushdb */
|
||||
char val = !!enable;
|
||||
je_mallctl("background_thread", NULL, 0, &val, 1);
|
||||
mallctl("background_thread", NULL, 0, &val, 1);
|
||||
}
|
||||
|
||||
int jemalloc_purge() {
|
||||
@@ -520,9 +520,9 @@ int jemalloc_purge() {
|
||||
char tmp[32];
|
||||
unsigned narenas = 0;
|
||||
size_t sz = sizeof(unsigned);
|
||||
if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) {
|
||||
sprintf(tmp, "arena.%d.purge", narenas);
|
||||
if (!je_mallctl(tmp, NULL, 0, NULL, 0))
|
||||
if (!mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) {
|
||||
snprintf(tmp, sizeof(tmp), "arena.%d.purge", narenas);
|
||||
if (!mallctl(tmp, NULL, 0, NULL, 0))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) je_malloc_usable_size(p)
|
||||
#define zmalloc_size(p) malloc_usable_size(p)
|
||||
#else
|
||||
#error "Newer version of jemalloc required"
|
||||
#endif
|
||||
|
||||
@@ -6,29 +6,31 @@ proc prepare_value {size} {
|
||||
return $_v
|
||||
}
|
||||
|
||||
start_server {tags {"replication-fast"} overrides {storage-provider {flash ./rocks.db.master} databases 256}} {
|
||||
set slave [srv 0 client]
|
||||
set slave_host [srv 0 host]
|
||||
set slave_port [srv 0 port]
|
||||
start_server {tags {} overrides {storage-provider {flash ./rocks.db.replica} databases 256}} {
|
||||
set master [srv 0 client]
|
||||
set master_host [srv 0 host]
|
||||
set master_port [srv 0 port]
|
||||
if {$::flash_enabled} {
|
||||
start_server {tags {"replication-fast"} overrides {storage-provider {flash ./rocks.db.master} databases 256}} {
|
||||
set slave [srv 0 client]
|
||||
set slave_host [srv 0 host]
|
||||
set slave_port [srv 0 port]
|
||||
start_server {tags {} overrides {storage-provider {flash ./rocks.db.replica} databases 256}} {
|
||||
set master [srv 0 client]
|
||||
set master_host [srv 0 host]
|
||||
set master_port [srv 0 port]
|
||||
|
||||
test "fast replication with large value" {
|
||||
set _v [prepare_value [expr 64*1024*1024]]
|
||||
# $master set key $_v
|
||||
test "fast replication with large value" {
|
||||
set _v [prepare_value [expr 64*1024*1024]]
|
||||
# $master set key $_v
|
||||
|
||||
$slave replicaof $master_host $master_port
|
||||
wait_for_condition 50 300 {
|
||||
[lindex [$slave role] 0] eq {slave} &&
|
||||
[string match {*master_link_status:up*} [$slave info replication]]
|
||||
} else {
|
||||
fail "Can't turn the instance into a replica"
|
||||
$slave replicaof $master_host $master_port
|
||||
wait_for_condition 50 300 {
|
||||
[lindex [$slave role] 0] eq {slave} &&
|
||||
[string match {*master_link_status:up*} [$slave info replication]]
|
||||
} else {
|
||||
fail "Can't turn the instance into a replica"
|
||||
}
|
||||
|
||||
assert_equal [$slave debug digest] [$master debug digest]
|
||||
$slave replicaof no one
|
||||
}
|
||||
|
||||
assert_equal [$slave debug digest] [$master debug digest]
|
||||
$slave replicaof no one
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,128 +8,130 @@
|
||||
# If reconnect is > 0, the test actually try to break the connection and
|
||||
# reconnect with the master, otherwise just the initial synchronization is
|
||||
# checked for consistency.
|
||||
proc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} {
|
||||
start_server [list tags {"repl"} overrides [list storage-provider {flash .rocks.db.m} repl-backlog-size 64m]] {
|
||||
start_server [list tags {flash} overrides [list storage-provider {flash ./rocks.db} delete-on-evict no storage-flush-period 10]] {
|
||||
if {$::flash_enabled} {
|
||||
proc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} {
|
||||
start_server [list tags {"repl"} overrides [list storage-provider {flash .rocks.db.m} repl-backlog-size 64m]] {
|
||||
start_server [list tags {flash} overrides [list storage-provider {flash ./rocks.db} delete-on-evict no storage-flush-period 10]] {
|
||||
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
set master [srv -1 client]
|
||||
set master_host [srv -1 host]
|
||||
set master_port [srv -1 port]
|
||||
set slave [srv 0 client]
|
||||
|
||||
set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
|
||||
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
|
||||
set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
|
||||
set load_handle0 [start_bg_complex_data $master_host $master_port 9 100000]
|
||||
set load_handle1 [start_bg_complex_data $master_host $master_port 11 100000]
|
||||
set load_handle2 [start_bg_complex_data $master_host $master_port 12 100000]
|
||||
|
||||
test {Slave should be able to synchronize with the master} {
|
||||
$slave slaveof $master_host $master_port
|
||||
wait_for_condition 50 100 {
|
||||
[lindex [r role] 0] eq {slave} &&
|
||||
[lindex [r role] 3] eq {connected}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
test {Slave should be able to synchronize with the master} {
|
||||
$slave slaveof $master_host $master_port
|
||||
wait_for_condition 50 100 {
|
||||
[lindex [r role] 0] eq {slave} &&
|
||||
[lindex [r role] 3] eq {connected}
|
||||
} else {
|
||||
fail "Replication not started."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check that the background clients are actually writing.
|
||||
test {Detect write load to master} {
|
||||
wait_for_condition 50 1000 {
|
||||
[$master dbsize] > 100
|
||||
} else {
|
||||
fail "Can't detect write load from background clients."
|
||||
# Check that the background clients are actually writing.
|
||||
test {Detect write load to master} {
|
||||
wait_for_condition 50 1000 {
|
||||
[$master dbsize] > 100
|
||||
} else {
|
||||
fail "Can't detect write load from background clients."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "Test replication partial resync: $descr (diskless: $mdl, $sdl, reconnect: $reconnect)" {
|
||||
# Now while the clients are writing data, break the maste-slave
|
||||
# link multiple times.
|
||||
if ($reconnect) {
|
||||
for {set j 0} {$j < $duration*10} {incr j} {
|
||||
after 100
|
||||
# catch {puts "MASTER [$master dbsize] keys, REPLICA [$slave dbsize] keys"}
|
||||
test "Test replication partial resync: $descr (diskless: $mdl, $sdl, reconnect: $reconnect)" {
|
||||
# Now while the clients are writing data, break the maste-slave
|
||||
# link multiple times.
|
||||
if ($reconnect) {
|
||||
for {set j 0} {$j < $duration*10} {incr j} {
|
||||
after 100
|
||||
# catch {puts "MASTER [$master dbsize] keys, REPLICA [$slave dbsize] keys"}
|
||||
|
||||
if {($j % 20) == 0} {
|
||||
catch {
|
||||
$slave debug restart
|
||||
if {($j % 20) == 0} {
|
||||
catch {
|
||||
$slave debug restart
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stop_bg_complex_data $load_handle0
|
||||
stop_bg_complex_data $load_handle1
|
||||
stop_bg_complex_data $load_handle2
|
||||
stop_bg_complex_data $load_handle0
|
||||
stop_bg_complex_data $load_handle1
|
||||
stop_bg_complex_data $load_handle2
|
||||
|
||||
# Wait for the slave to reach the "online"
|
||||
# state from the POV of the master.
|
||||
set retry 5000
|
||||
while {$retry} {
|
||||
set info [$master info]
|
||||
if {[string match {*slave0:*state=online*} $info]} {
|
||||
break
|
||||
} else {
|
||||
incr retry -1
|
||||
after 100
|
||||
# Wait for the slave to reach the "online"
|
||||
# state from the POV of the master.
|
||||
set retry 5000
|
||||
while {$retry} {
|
||||
set info [$master info]
|
||||
if {[string match {*slave0:*state=online*} $info]} {
|
||||
break
|
||||
} else {
|
||||
incr retry -1
|
||||
after 100
|
||||
}
|
||||
}
|
||||
if {$retry == 0} {
|
||||
error "assertion:Slave not correctly synchronized"
|
||||
}
|
||||
}
|
||||
if {$retry == 0} {
|
||||
error "assertion:Slave not correctly synchronized"
|
||||
}
|
||||
|
||||
# Wait that slave acknowledge it is online so
|
||||
# we are sure that DBSIZE and DEBUG DIGEST will not
|
||||
# fail because of timing issues. (-LOADING error)
|
||||
wait_for_condition 5000 100 {
|
||||
[lindex [$slave role] 3] eq {connected}
|
||||
} else {
|
||||
fail "Slave still not connected after some time"
|
||||
}
|
||||
# Wait that slave acknowledge it is online so
|
||||
# we are sure that DBSIZE and DEBUG DIGEST will not
|
||||
# fail because of timing issues. (-LOADING error)
|
||||
wait_for_condition 5000 100 {
|
||||
[lindex [$slave role] 3] eq {connected}
|
||||
} else {
|
||||
fail "Slave still not connected after some time"
|
||||
}
|
||||
|
||||
set retry 20
|
||||
while {$retry && ([$master debug digest] ne [$slave debug digest])}\
|
||||
{
|
||||
after 1000
|
||||
incr retry -1
|
||||
}
|
||||
assert {[$master dbsize] > 0}
|
||||
set retry 20
|
||||
while {$retry && ([$master debug digest] ne [$slave debug digest])}\
|
||||
{
|
||||
after 1000
|
||||
incr retry -1
|
||||
}
|
||||
assert {[$master dbsize] > 0}
|
||||
|
||||
if {[$master debug digest] ne [$slave debug digest]} {
|
||||
set csv1 [csvdump r]
|
||||
set csv2 [csvdump {r -1}]
|
||||
set fd [open /tmp/repldump1.txt w]
|
||||
puts -nonewline $fd $csv1
|
||||
close $fd
|
||||
set fd [open /tmp/repldump2.txt w]
|
||||
puts -nonewline $fd $csv2
|
||||
close $fd
|
||||
puts "Master - Replica inconsistency"
|
||||
puts "Run diff -u against /tmp/repldump*.txt for more info"
|
||||
if {[$master debug digest] ne [$slave debug digest]} {
|
||||
set csv1 [csvdump r]
|
||||
set csv2 [csvdump {r -1}]
|
||||
set fd [open /tmp/repldump1.txt w]
|
||||
puts -nonewline $fd $csv1
|
||||
close $fd
|
||||
set fd [open /tmp/repldump2.txt w]
|
||||
puts -nonewline $fd $csv2
|
||||
close $fd
|
||||
puts "Master - Replica inconsistency"
|
||||
puts "Run diff -u against /tmp/repldump*.txt for more info"
|
||||
}
|
||||
assert_equal [r debug digest] [r -1 debug digest]
|
||||
eval $cond
|
||||
}
|
||||
assert_equal [r debug digest] [r -1 debug digest]
|
||||
eval $cond
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach mdl {no yes} {
|
||||
foreach sdl {disabled swapdb} {
|
||||
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
|
||||
} $mdl $sdl 0
|
||||
foreach mdl {no yes} {
|
||||
foreach sdl {disabled swapdb} {
|
||||
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
|
||||
} $mdl $sdl 0
|
||||
|
||||
test_psync {ok psync} 6 100000000 3600 0 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
} $mdl $sdl 1
|
||||
test_psync {ok psync} 6 100000000 3600 0 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
} $mdl $sdl 1
|
||||
|
||||
test_psync {no backlog} 6 100 3600 0.5 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
} $mdl $sdl 1
|
||||
test_psync {no backlog} 6 100 3600 0.5 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
} $mdl $sdl 1
|
||||
|
||||
test_psync {ok after delay} 3 100000000 3600 3 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
} $mdl $sdl 1
|
||||
test_psync {ok after delay} 3 100000000 3600 3 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
} $mdl $sdl 1
|
||||
|
||||
test_psync {backlog expired} 3 100000000 1 3 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
} $mdl $sdl 1
|
||||
test_psync {backlog expired} 3 100000000 1 3 {
|
||||
assert {[s -1 sync_partial_err] > 0}
|
||||
} $mdl $sdl 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,8 @@ TEST_MODULES = \
|
||||
defragtest.so \
|
||||
hash.so \
|
||||
zset.so \
|
||||
stream.so
|
||||
stream.so \
|
||||
load.so
|
||||
|
||||
|
||||
.PHONY: all
|
||||
|
||||
@@ -212,6 +212,7 @@ void loadingCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void
|
||||
case REDISMODULE_SUBEVENT_LOADING_RDB_START: keyname = "loading-rdb-start"; break;
|
||||
case REDISMODULE_SUBEVENT_LOADING_AOF_START: keyname = "loading-aof-start"; break;
|
||||
case REDISMODULE_SUBEVENT_LOADING_REPL_START: keyname = "loading-repl-start"; break;
|
||||
case REDISMODULE_SUBEVENT_LOADING_FLASH_START: keyname = "loading-flash-start"; break;
|
||||
case REDISMODULE_SUBEVENT_LOADING_ENDED: keyname = "loading-end"; break;
|
||||
case REDISMODULE_SUBEVENT_LOADING_FAILED: keyname = "loading-failed"; break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/* Server hooks API example
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2019, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_EXPERIMENTAL_API
|
||||
#include "redismodule.h"
|
||||
|
||||
size_t count, finalCount;
|
||||
|
||||
/* Client state change callback. */
|
||||
void loadCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) {
|
||||
REDISMODULE_NOT_USED(ctx);
|
||||
REDISMODULE_NOT_USED(e);
|
||||
REDISMODULE_NOT_USED(data);
|
||||
|
||||
if (sub == REDISMODULE_SUBEVENT_LOADING_FLASH_START || sub == REDISMODULE_SUBEVENT_LOADING_RDB_START || sub == REDISMODULE_SUBEVENT_LOADING_AOF_START || sub == REDISMODULE_SUBEVENT_LOADING_REPL_START) {
|
||||
count = 0;
|
||||
finalCount = 0;
|
||||
} else if (sub == REDISMODULE_SUBEVENT_LOADING_ENDED) {
|
||||
finalCount = count;
|
||||
}
|
||||
}
|
||||
|
||||
int loadKeyCallback(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) {
|
||||
REDISMODULE_NOT_USED(ctx);
|
||||
REDISMODULE_NOT_USED(type);
|
||||
REDISMODULE_NOT_USED(event);
|
||||
|
||||
const char *keyname = RedisModule_StringPtrLen(key, NULL);
|
||||
|
||||
RedisModule_Log(ctx, REDISMODULE_LOGLEVEL_NOTICE, "Loaded key: %s", keyname);
|
||||
|
||||
count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LoadCount_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
|
||||
|
||||
if (argc != 1) return RedisModule_WrongArity(ctx);
|
||||
|
||||
RedisModule_ReplyWithLongLong(ctx, finalCount);
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
/* This function must be present on each Redis module. It is used in order to
|
||||
* register the commands into the Redis server. */
|
||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||
REDISMODULE_NOT_USED(argv);
|
||||
REDISMODULE_NOT_USED(argc);
|
||||
|
||||
if (RedisModule_Init(ctx,"load",1,REDISMODULE_APIVER_1)
|
||||
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
||||
|
||||
RedisModule_SubscribeToServerEvent(ctx,
|
||||
RedisModuleEvent_Loading, loadCallback);
|
||||
RedisModule_SubscribeToKeyspaceEvents(ctx,
|
||||
REDISMODULE_NOTIFY_LOADED, loadKeyCallback);
|
||||
|
||||
if (RedisModule_CreateCommand(ctx, "load.count",
|
||||
LoadCount_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
+36
-5
@@ -66,6 +66,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
|
||||
interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
|
||||
}
|
||||
|
||||
# On recent versions of tcl-tls/OpenSSL, reading from a dropped connection
|
||||
# results with an error we need to catch and mimic the old behavior.
|
||||
proc ::redis::redis_safe_read {fd len} {
|
||||
if {$len == -1} {
|
||||
set err [catch {set val [read $fd]} msg]
|
||||
} else {
|
||||
set err [catch {set val [read $fd $len]} msg]
|
||||
}
|
||||
if {!$err} {
|
||||
return $val
|
||||
}
|
||||
if {[string match "*connection abort*" $msg]} {
|
||||
return {}
|
||||
}
|
||||
error $msg
|
||||
}
|
||||
|
||||
proc ::redis::redis_safe_gets {fd} {
|
||||
if {[catch {set val [gets $fd]} msg]} {
|
||||
if {[string match "*connection abort*" $msg]} {
|
||||
return {}
|
||||
}
|
||||
error $msg
|
||||
}
|
||||
return $val
|
||||
}
|
||||
|
||||
# This is a wrapper to the actual dispatching procedure that handles
|
||||
# reconnection if needed.
|
||||
proc ::redis::__dispatch__ {id method args} {
|
||||
@@ -146,6 +173,10 @@ proc ::redis::__method__read {id fd} {
|
||||
::redis::redis_read_reply $id $fd
|
||||
}
|
||||
|
||||
proc ::redis::__method__rawread {id fd {len -1}} {
|
||||
return [redis_safe_read $fd $len]
|
||||
}
|
||||
|
||||
proc ::redis::__method__write {id fd buf} {
|
||||
::redis::redis_write $fd $buf
|
||||
}
|
||||
@@ -192,8 +223,8 @@ proc ::redis::redis_writenl {fd buf} {
|
||||
}
|
||||
|
||||
proc ::redis::redis_readnl {fd len} {
|
||||
set buf [read $fd $len]
|
||||
read $fd 2 ; # discard CR LF
|
||||
set buf [redis_safe_read $fd $len]
|
||||
redis_safe_read $fd 2 ; # discard CR LF
|
||||
return $buf
|
||||
}
|
||||
|
||||
@@ -239,11 +270,11 @@ proc ::redis::redis_read_map {id fd} {
|
||||
}
|
||||
|
||||
proc ::redis::redis_read_line fd {
|
||||
string trim [gets $fd]
|
||||
string trim [redis_safe_gets $fd]
|
||||
}
|
||||
|
||||
proc ::redis::redis_read_null fd {
|
||||
gets $fd
|
||||
redis_safe_gets $fd
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -260,7 +291,7 @@ proc ::redis::redis_read_reply {id fd} {
|
||||
}
|
||||
|
||||
while {1} {
|
||||
set type [read $fd 1]
|
||||
set type [redis_safe_read $fd 1]
|
||||
switch -exact -- $type {
|
||||
_ {return [redis_read_null $fd]}
|
||||
: -
|
||||
|
||||
@@ -40,6 +40,12 @@ proc assert_failed {expected_err detail} {
|
||||
error "assertion:$expected_err $detail"
|
||||
}
|
||||
|
||||
proc assert_not_equal {value expected {detail ""}} {
|
||||
if {!($expected ne $value)} {
|
||||
assert_failed "Expected '$value' not equal to '$expected'" $detail
|
||||
}
|
||||
}
|
||||
|
||||
proc assert_equal {value expected {detail ""}} {
|
||||
if {$expected ne $value} {
|
||||
assert_failed "Expected '$value' to be equal to '$expected'" $detail
|
||||
|
||||
@@ -739,6 +739,16 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
}
|
||||
}
|
||||
|
||||
# Check if we compiled with flash
|
||||
set status [catch {exec src/keydb-server --is-flash-enabled}]
|
||||
if {$status == 0} {
|
||||
puts "KeyDB was built with FLASH, including FLASH tests"
|
||||
set ::flash_enabled 1
|
||||
} else {
|
||||
puts "KeyDB was not built with FLASH. Excluding FLASH tests"
|
||||
set ::flash_enabled 0
|
||||
}
|
||||
|
||||
set filtered_tests {}
|
||||
|
||||
# Set the filtered tests to be the short list (single_tests) if exists.
|
||||
|
||||
+160
-159
@@ -1,170 +1,171 @@
|
||||
start_server [list tags {flash} overrides [list storage-provider {flash ./rocks.db} delete-on-evict no storage-flush-period 10]] {
|
||||
if {$::flash_enabled} {
|
||||
start_server [list tags {flash} overrides [list storage-provider {flash ./rocks.db} delete-on-evict no storage-flush-period 10]] {
|
||||
|
||||
test { FLASH - GET works after eviction } {
|
||||
r set testkey foo
|
||||
r flushall cache
|
||||
assert_equal {foo} [r get testkey]
|
||||
}
|
||||
test { FLASH - GET works after eviction } {
|
||||
r set testkey foo
|
||||
r flushall cache
|
||||
assert_equal {foo} [r get testkey]
|
||||
}
|
||||
|
||||
test { DEL of nonexistant key returns 0 } {
|
||||
test { DEL of nonexistant key returns 0 } {
|
||||
r flushall
|
||||
assert_equal {0} [r del foobar]
|
||||
assert_equal {0} [r dbsize] "Key count is accurate after non-existant delete"
|
||||
}
|
||||
|
||||
test { DEL of flushed key works } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
assert_equal {1} [r dbsize] "Only one key after first insert"
|
||||
r flushall cache
|
||||
assert_equal {foo} [r get testkey] "Value still there after flushing cache"
|
||||
r del testkey
|
||||
assert_equal {0} [r dbsize] "No keys after delete"
|
||||
}
|
||||
|
||||
test { SET of existing but flushed key works } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
assert_equal {1} [r dbsize] "Only one key after first insert"
|
||||
r flushall cache
|
||||
assert_equal {1} [r dbsize] "Only one key after flushall cache"
|
||||
r set testkey bar
|
||||
assert_equal {1} [r dbsize] "Only one key after overwrite"
|
||||
assert_equal {bar} [r get testkey]
|
||||
}
|
||||
|
||||
test { SET of existing but flushed key with EXPIRE works } {
|
||||
r flushall
|
||||
assert_equal {0} [r del foobar]
|
||||
assert_equal {0} [r dbsize] "Key count is accurate after non-existant delete"
|
||||
}
|
||||
|
||||
test { DEL of flushed key works } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
assert_equal {1} [r dbsize] "Only one key after first insert"
|
||||
r flushall cache
|
||||
assert_equal {foo} [r get testkey] "Value still there after flushing cache"
|
||||
r del testkey
|
||||
assert_equal {0} [r dbsize] "No keys after delete"
|
||||
}
|
||||
|
||||
test { SET of existing but flushed key works } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
assert_equal {0} [r dbsize]
|
||||
r set testkey foo ex 10000
|
||||
assert_equal {1} [r dbsize] "Only one key after first insert"
|
||||
r flushall cache
|
||||
assert_equal {1} [r dbsize] "Only one key after flushall cache"
|
||||
r set testkey bar
|
||||
r set testkey bar ex 10000
|
||||
assert_equal {1} [r dbsize] "Only one key after overwrite"
|
||||
assert_equal {bar} [r get testkey]
|
||||
}
|
||||
|
||||
test { SET of existing but flushed key with EXPIRE works } {
|
||||
r flushall
|
||||
assert_equal {0} [r dbsize]
|
||||
r set testkey foo ex 10000
|
||||
assert_equal {1} [r dbsize] "Only one key after first insert"
|
||||
r flushall cache
|
||||
assert_equal {1} [r dbsize] "Only one key after flushall cache"
|
||||
r set testkey bar ex 10000
|
||||
assert_equal {1} [r dbsize] "Only one key after overwrite"
|
||||
assert_equal {bar} [r get testkey]
|
||||
assert [expr [r ttl testkey] > 0]
|
||||
}
|
||||
|
||||
test { EXPIRE of existing but flushed key } {
|
||||
r flushall
|
||||
assert_equal {0} [r dbsize]
|
||||
r set testkey foo
|
||||
assert_equal {1} [r dbsize]
|
||||
r flushall cache
|
||||
r expire testkey 10000
|
||||
assert_equal {1} [r dbsize]
|
||||
assert_equal {foo} [r get testkey]
|
||||
assert [expr [r ttl testkey] > 0]
|
||||
}
|
||||
|
||||
test { CREATE and UPDATE in transaction, key count is accurate } {
|
||||
r flushall
|
||||
r multi
|
||||
r set testkey 2
|
||||
r incr testkey
|
||||
r exec
|
||||
assert_equal {1} [r dbsize]
|
||||
assert_equal {3} [r get testkey]
|
||||
}
|
||||
|
||||
test { EXPIRE key count is accurate } {
|
||||
r flushall
|
||||
r set testkey foo ex 1
|
||||
r flushall cache
|
||||
assert_equal {1} [r dbsize]
|
||||
after 1500
|
||||
assert_equal {0} [r dbsize]
|
||||
}
|
||||
|
||||
test { SUBKEY EXPIRE persists after cache flush } {
|
||||
r flushall
|
||||
r sadd testkey foo bar baz
|
||||
r expiremember testkey foo 10000
|
||||
r flushall cache
|
||||
assert [expr [r ttl testkey foo] > 0]
|
||||
}
|
||||
|
||||
test { LIST pop works after flushing cache } {
|
||||
r flushall
|
||||
r lpush testkey foo
|
||||
r flushall cache
|
||||
assert_equal {foo} [r lpop testkey]
|
||||
}
|
||||
|
||||
test { DIGEST string the same after flushing cache } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
r set testkey1 foo ex 10000
|
||||
set expectedDigest [r debug digest]
|
||||
r flushall cache
|
||||
assert_equal $expectedDigest [r debug digest]
|
||||
}
|
||||
|
||||
test { DIGEST list the same after flushing cache } {
|
||||
r flushall
|
||||
r lpush testkey foo bar
|
||||
set expectedDigest [r debug digest]
|
||||
r flushall cache
|
||||
assert_equal $expectedDigest [r debug digest]
|
||||
}
|
||||
|
||||
test { DELETE of flushed set member persists after another flush } {
|
||||
r flushall
|
||||
r sadd set1 val1 val2 val3
|
||||
assert_equal {3} [r scard set1]
|
||||
r flushall cache
|
||||
r srem set1 val1
|
||||
r flushall cache
|
||||
assert_equal {2} [r scard set1]
|
||||
}
|
||||
|
||||
r flushall
|
||||
# If a weak storage memory model is set, wait for any pending snapshot writes to finish
|
||||
after 500
|
||||
foreach policy {
|
||||
allkeys-random allkeys-lru allkeys-lfu
|
||||
} {
|
||||
test "FLASH - is eviction working without data loss (successfully stored to flash)? (policy $policy)" {
|
||||
# Get the current memory limit and calculate a new limit.
|
||||
# Set limit to 100M.
|
||||
set used [s used_memory]
|
||||
set limit [expr {$used+60*1024*1024}]
|
||||
r config set maxmemory $limit
|
||||
r config set maxmemory-policy $policy
|
||||
# Now add keys equivalent to 1024b until the limit is almost reached.
|
||||
set numkeys 0
|
||||
r set first val
|
||||
while 1 {
|
||||
r set $numkeys xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
incr numkeys
|
||||
if {[s used_memory]+1024 >= $limit} {
|
||||
break
|
||||
}
|
||||
}
|
||||
# Add additional keys to force eviction
|
||||
# should still be under the limit for maxmemory, however all keys set should still exist between flash and memory
|
||||
# check same number of keys exist in addition to values of first and last keys
|
||||
set err 0
|
||||
set extra_keys [expr floor([expr ($limit * 0.4) / 1024])]
|
||||
for {set j 0} {$j < $extra_keys} {incr j} {
|
||||
catch {
|
||||
r set p2$j xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
} err
|
||||
assert {$err == {OK}}
|
||||
}
|
||||
if {[log_file_matches [srv 0 stdout] "*Failed to evict*"]} {
|
||||
fail "Server did not evict cleanly (detected full flush)"
|
||||
}
|
||||
r set last val
|
||||
set dbsize [r dbsize]
|
||||
assert {[s used_memory] < ($limit*1.2)}
|
||||
assert {$dbsize == $numkeys+$extra_keys+2}
|
||||
assert {[r get first] == {val}}
|
||||
assert {[r get last] == {val}}
|
||||
r flushall
|
||||
assert [expr [r ttl testkey] > 0]
|
||||
}
|
||||
|
||||
test { EXPIRE of existing but flushed key } {
|
||||
r flushall
|
||||
assert_equal {0} [r dbsize]
|
||||
r set testkey foo
|
||||
assert_equal {1} [r dbsize]
|
||||
r flushall cache
|
||||
r expire testkey 10000
|
||||
assert_equal {1} [r dbsize]
|
||||
assert_equal {foo} [r get testkey]
|
||||
assert [expr [r ttl testkey] > 0]
|
||||
}
|
||||
|
||||
test { CREATE and UPDATE in transaction, key count is accurate } {
|
||||
r flushall
|
||||
r multi
|
||||
r set testkey 2
|
||||
r incr testkey
|
||||
r exec
|
||||
assert_equal {1} [r dbsize]
|
||||
assert_equal {3} [r get testkey]
|
||||
}
|
||||
|
||||
test { EXPIRE key count is accurate } {
|
||||
r flushall
|
||||
r set testkey foo ex 1
|
||||
r flushall cache
|
||||
assert_equal {1} [r dbsize]
|
||||
after 1500
|
||||
assert_equal {0} [r dbsize]
|
||||
}
|
||||
|
||||
test { SUBKEY EXPIRE persists after cache flush } {
|
||||
r flushall
|
||||
r sadd testkey foo bar baz
|
||||
r expiremember testkey foo 10000
|
||||
r flushall cache
|
||||
assert [expr [r ttl testkey foo] > 0]
|
||||
}
|
||||
|
||||
test { LIST pop works after flushing cache } {
|
||||
r flushall
|
||||
r lpush testkey foo
|
||||
r flushall cache
|
||||
assert_equal {foo} [r lpop testkey]
|
||||
}
|
||||
|
||||
test { DIGEST string the same after flushing cache } {
|
||||
r flushall
|
||||
r set testkey foo
|
||||
r set testkey1 foo ex 10000
|
||||
set expectedDigest [r debug digest]
|
||||
r flushall cache
|
||||
assert_equal $expectedDigest [r debug digest]
|
||||
}
|
||||
|
||||
test { DIGEST list the same after flushing cache } {
|
||||
r flushall
|
||||
r lpush testkey foo bar
|
||||
set expectedDigest [r debug digest]
|
||||
r flushall cache
|
||||
assert_equal $expectedDigest [r debug digest]
|
||||
}
|
||||
|
||||
test { DELETE of flushed set member persists after another flush } {
|
||||
r flushall
|
||||
r sadd set1 val1 val2 val3
|
||||
assert_equal {3} [r scard set1]
|
||||
r flushall cache
|
||||
r srem set1 val1
|
||||
r flushall cache
|
||||
assert_equal {2} [r scard set1]
|
||||
}
|
||||
|
||||
r flushall
|
||||
# If a weak storage memory model is set, wait for any pending snapshot writes to finish
|
||||
after 500
|
||||
foreach policy {
|
||||
allkeys-random allkeys-lru allkeys-lfu
|
||||
} {
|
||||
test "FLASH - is eviction working without data loss (successfully stored to flash)? (policy $policy)" {
|
||||
# Get the current memory limit and calculate a new limit.
|
||||
# Set limit to 100M.
|
||||
set used [s used_memory]
|
||||
set limit [expr {$used+60*1024*1024}]
|
||||
r config set maxmemory $limit
|
||||
r config set maxmemory-policy $policy
|
||||
# Now add keys equivalent to 1024b until the limit is almost reached.
|
||||
set numkeys 0
|
||||
r set first val
|
||||
while 1 {
|
||||
r set $numkeys xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
incr numkeys
|
||||
if {[s used_memory]+1024 >= $limit} {
|
||||
break
|
||||
}
|
||||
}
|
||||
# Add additional keys to force eviction
|
||||
# should still be under the limit for maxmemory, however all keys set should still exist between flash and memory
|
||||
# check same number of keys exist in addition to values of first and last keys
|
||||
set err 0
|
||||
set extra_keys [expr floor([expr ($limit * 0.4) / 1024])]
|
||||
for {set j 0} {$j < $extra_keys} {incr j} {
|
||||
catch {
|
||||
r set p2$j xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
} err
|
||||
assert {$err == {OK}}
|
||||
}
|
||||
if {[log_file_matches [srv 0 stdout] "*Failed to evict*"]} {
|
||||
fail "Server did not evict cleanly (detected full flush)"
|
||||
}
|
||||
r set last val
|
||||
set dbsize [r dbsize]
|
||||
assert {[s used_memory] < ($limit*1.2)}
|
||||
assert {$dbsize == $numkeys+$extra_keys+2}
|
||||
assert {[r get first] == {val}}
|
||||
assert {[r get last] == {val}}
|
||||
r flushall
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -162,15 +162,16 @@ start_server {tags {"introspection"}} {
|
||||
aof_rewrite_cpulist
|
||||
time-thread-priority
|
||||
bgsave_cpulist
|
||||
storage-cache-mode
|
||||
storage-provider-options
|
||||
use-fork
|
||||
storage-cache-mode
|
||||
storage-provider-options
|
||||
use-fork
|
||||
multi-master
|
||||
active-replica
|
||||
bind
|
||||
set-proc-title
|
||||
repl-backlog-disk-reserve
|
||||
tls-allowlist
|
||||
db-s3-object
|
||||
}
|
||||
|
||||
if {!$::tls} {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
set testmodule [file normalize tests/modules/load.so]
|
||||
|
||||
if {$::flash_enabled} {
|
||||
start_server [list tags [list "modules"] overrides [list storage-provider {flash ./rocks.db.master} databases 256 loadmodule $testmodule]] {
|
||||
test "Module is notified of keys loaded from flash" {
|
||||
r flushall
|
||||
r set foo bar
|
||||
r set bar foo
|
||||
r set foobar barfoo
|
||||
assert_equal [r load.count] 0
|
||||
r debug reload
|
||||
assert_equal [r load.count] 3
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
|
||||
|
||||
# Read nothing
|
||||
set fd [$rd channel]
|
||||
assert_equal {} [read $fd]
|
||||
assert_equal {} [$rd rawread]
|
||||
}
|
||||
|
||||
# Note: This test assumes that what's written with one write, will be read by redis in one read.
|
||||
@@ -151,8 +151,7 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
|
||||
assert_equal "PONG" [r ping]
|
||||
set clients [r client list]
|
||||
assert_no_match "*name=multicommands*" $clients
|
||||
set fd [$rd2 channel]
|
||||
assert_equal {} [read $fd]
|
||||
assert_equal {} [$rd2 rawread]
|
||||
}
|
||||
|
||||
test {Execute transactions completely even if client output buffer limit is enforced} {
|
||||
@@ -183,4 +182,12 @@ start_server {tags {"obuf-limits"} overrides { server-threads 1 }} {
|
||||
assert_equal "v2" [r get k2]
|
||||
assert_equal "v3" [r get k3]
|
||||
}
|
||||
|
||||
test "Obuf limit, HRANDFIELD with huge count stopped mid-run" {
|
||||
r config set client-output-buffer-limit {normal 1000000 0 0}
|
||||
r hset myhash a b
|
||||
catch {r hrandfield myhash -999999999} e
|
||||
assert_match "*I/O error*" $e
|
||||
reconnect
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,15 @@ start_server {tags {"scripting"}} {
|
||||
r eval {return redis.call('get',KEYS[1])} 1 mykey
|
||||
} {myval}
|
||||
|
||||
test {EVAL - keys command works? } {
|
||||
r eval {return redis.call('keys', 'test')} 0
|
||||
}
|
||||
|
||||
test {EVAL - KeyDB global works } {
|
||||
r eval {return keydb.call('get', KEYS[1])} 1 mykey
|
||||
assert_equal [r eval {return redis.call('get',KEYS[1])} 1 mykey] [r eval {return keydb.call('get', KEYS[1])} 1 mykey]
|
||||
}
|
||||
|
||||
test {EVALSHA - Can we call a SHA1 if already defined?} {
|
||||
r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey
|
||||
} {myval}
|
||||
@@ -126,6 +135,10 @@ start_server {tags {"scripting"}} {
|
||||
r select 9
|
||||
set res
|
||||
} {original value}
|
||||
|
||||
test {EVAL background command} {
|
||||
r eval {redis.call("SCAN", "0", "MATCH", "key*", "COUNT", 1000)} 0
|
||||
} {}
|
||||
|
||||
if 0 {
|
||||
test {EVAL - Script can't run more than configured time limit} {
|
||||
|
||||
@@ -315,4 +315,15 @@ start_server {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {SETRANGE with huge offset} {
|
||||
r lpush L 2 1 0
|
||||
# expecting a different outcome on 32 and 64 bit systems
|
||||
foreach value {9223372036854775807 2147483647} {
|
||||
catch {[r sort_ro L by a limit 2 $value]} res
|
||||
if {![string match "2" $res] && ![string match "*out of range*" $res]} {
|
||||
assert_not_equal $res "expecting an error or 2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,13 @@ start_server {tags {"hash"}} {
|
||||
r hrandfield myhash 0
|
||||
} {}
|
||||
|
||||
test "HRANDFIELD count overflow" {
|
||||
r hmset myhash a 1
|
||||
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854770000 withvalues}
|
||||
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854775808 withvalues}
|
||||
assert_error {*value is out of range*} {r hrandfield myhash -9223372036854775808}
|
||||
} {}
|
||||
|
||||
test "HRANDFIELD with <count> against non existing key" {
|
||||
r hrandfield nonexisting_key 100
|
||||
} {}
|
||||
@@ -826,4 +833,8 @@ start_server {tags {"hash"}} {
|
||||
set _ $k
|
||||
} {ZIP_INT_8B 127 ZIP_INT_16B 32767 ZIP_INT_32B 2147483647 ZIP_INT_64B 9223372036854775808 ZIP_INT_IMM_MIN 0 ZIP_INT_IMM_MAX 12}
|
||||
|
||||
test {HINCRBYFLOAT does not allow NaN or Infinity} {
|
||||
assert_error "*value is NaN or Infinity*" {r hincrbyfloat hfoo field +inf}
|
||||
assert_equal 0 [r exists hfoo]
|
||||
}
|
||||
}
|
||||
@@ -588,6 +588,11 @@ start_server {
|
||||
r srandmember nonexisting_key 100
|
||||
} {}
|
||||
|
||||
test "SRANDMEMBER count overflow" {
|
||||
r sadd myset a
|
||||
assert_error {*value is out of range*} {r srandmember myset -9223372036854775808}
|
||||
} {}
|
||||
|
||||
# Make sure we can distinguish between an empty array and a null response
|
||||
r readraw 1
|
||||
|
||||
|
||||
@@ -172,6 +172,15 @@ start_server {
|
||||
assert_equal [r XRANGE mystream - +] {{3-0 {f v}} {4-0 {f v}} {5-0 {f v}}}
|
||||
}
|
||||
|
||||
test {XTRIM with MINID option, big delta from master record} {
|
||||
r DEL mystream
|
||||
r XADD mystream 1-0 f v
|
||||
r XADD mystream 1641544570597-0 f v
|
||||
r XADD mystream 1641544570597-1 f v
|
||||
r XTRIM mystream MINID 1641544570597-0
|
||||
assert_equal [r XRANGE mystream - +] {{1641544570597-0 {f v}} {1641544570597-1 {f v}}}
|
||||
}
|
||||
|
||||
test {XADD mass insertion and XLEN} {
|
||||
r DEL mystream
|
||||
r multi
|
||||
|
||||
@@ -574,4 +574,14 @@ start_server {tags {"string"}} {
|
||||
test {LCS indexes with match len and minimum match len} {
|
||||
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches
|
||||
} {{{1 222} {13 234} 222}}
|
||||
|
||||
test {SETRANGE with huge offset} {
|
||||
foreach value {9223372036854775807 2147483647} {
|
||||
catch {[r setrange K $value A]} res
|
||||
# expecting a different error on 32 and 64 bit systems
|
||||
if {![string match "*string exceeds maximum allowed size*" $res] && ![string match "*out of range*" $res]} {
|
||||
assert_equal $res "expecting an error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1714,6 +1714,13 @@ start_server {tags {"zset"}} {
|
||||
r zrandmember nonexisting_key 100
|
||||
} {}
|
||||
|
||||
test "ZRANDMEMBER count overflow" {
|
||||
r zadd myzset 0 a
|
||||
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854770000 withscores}
|
||||
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854775808 withscores}
|
||||
assert_error {*value is out of range*} {r zrandmember myzset -9223372036854775808}
|
||||
} {}
|
||||
|
||||
# Make sure we can distinguish between an empty array and a null response
|
||||
r readraw 1
|
||||
|
||||
|
||||
@@ -26,7 +26,11 @@ then
|
||||
while [ $((PORT < ENDPORT)) != "0" ]; do
|
||||
PORT=$((PORT+1))
|
||||
echo "Starting $PORT"
|
||||
$BIN_PATH/keydb-server --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
|
||||
if [ "$2" == "flash" ]
|
||||
then
|
||||
ADDITIONAL_OPTIONS="--save \"\" \"\" \"\" --semi-ordered-set-bucket-size 8 --client-output-buffer-limit replica 1 1 0 --maxmemory 100000000 --storage-provider flash ./$PORT.flash"
|
||||
fi
|
||||
$BIN_PATH/keydb-server --server-threads 4 --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
@@ -97,6 +101,8 @@ then
|
||||
rm -rf appendonly*.aof
|
||||
rm -rf dump*.rdb
|
||||
rm -rf nodes*.conf
|
||||
rm -rf *.flash
|
||||
rm -rf temp*.rdb
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
+9
-9
@@ -1,27 +1,27 @@
|
||||
# keydb-copy.rb - Copyright (C) 2009-2010 Salvatore Sanfilippo
|
||||
# redis-copy.rb - Copyright (C) 2009-2010 Salvatore Sanfilippo
|
||||
# BSD license, See the COPYING file for more information.
|
||||
#
|
||||
# Copy the whole dataset from one Redis instance to another one
|
||||
# Copy the whole dataset from one KeyDB/Redis instance to another one
|
||||
#
|
||||
# WARNING: this utility is deprecated and serves as a legacy adapter
|
||||
# for the more-robust keydb-copy gem.
|
||||
# for the more-robust redis-copy gem.
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
def redisCopy(opts={})
|
||||
src = "#{opts[:srchost]}:#{opts[:srcport]}"
|
||||
dst = "#{opts[:dsthost]}:#{opts[:dstport]}"
|
||||
`keydb-copy #{src.shellescape} #{dst.shellescape}`
|
||||
`redis-copy #{src.shellescape} #{dst.shellescape}`
|
||||
rescue Errno::ENOENT
|
||||
$stderr.puts 'This utility requires the keydb-copy executable',
|
||||
'from the keydb-copy gem on https://rubygems.org',
|
||||
'To install it, run `gem install keydb-copy`.'
|
||||
$stderr.puts 'This utility requires the redis-copy executable',
|
||||
'from the redis-copy gem on https://rubygems.org',
|
||||
'To install it, run `gem install redis-copy`.'
|
||||
exit 1
|
||||
end
|
||||
|
||||
$stderr.puts "This utility is deprecated. Use the keydb-copy gem instead."
|
||||
$stderr.puts "This utility is deprecated. Use the redis-copy gem instead."
|
||||
if ARGV.length != 4
|
||||
puts "Usage: keydb-copy.rb <srchost> <srcport> <dsthost> <dstport>"
|
||||
puts "Usage: redis-copy.rb <srchost> <srcport> <dsthost> <dstport>"
|
||||
exit 1
|
||||
end
|
||||
puts "WARNING: it's up to you to FLUSHDB the destination host before to continue, press any key when ready."
|
||||
|
||||
Reference in new issue
Block a user