├── .github ├── codespell │ ├── config.txt │ └── skiplist.txt └── workflows │ ├── ci.yml │ ├── daily.yml │ ├── jepsen.yml │ └── redis-suite.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── benchmark ├── benchmark.json └── redisraft_cluster.sh ├── deps ├── common │ ├── crc16.c │ ├── crc16.h │ ├── redismodule.h │ ├── sc_crc32.c │ ├── sc_crc32.h │ ├── sc_list.c │ └── sc_list.h ├── hiredis │ ├── .github │ │ ├── release-drafter-config.yml │ │ └── workflows │ │ │ ├── build.yml │ │ │ ├── release-drafter.yml │ │ │ └── test.yml │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── CMakeLists.txt │ ├── COPYING │ ├── Makefile │ ├── README.md │ ├── adapters │ │ ├── ae.h │ │ ├── glib.h │ │ ├── ivykis.h │ │ ├── libev.h │ │ ├── libevent.h │ │ ├── libhv.h │ │ ├── libsdevent.h │ │ ├── libuv.h │ │ ├── macosx.h │ │ ├── poll.h │ │ ├── qt.h │ │ └── redismoduleapi.h │ ├── alloc.c │ ├── alloc.h │ ├── appveyor.yml │ ├── async.c │ ├── async.h │ ├── async_private.h │ ├── dict.c │ ├── dict.h │ ├── examples │ │ ├── CMakeLists.txt │ │ ├── example-ae.c │ │ ├── example-glib.c │ │ ├── example-ivykis.c │ │ ├── example-libev.c │ │ ├── example-libevent-ssl.c │ │ ├── example-libevent.c │ │ ├── example-libhv.c │ │ ├── example-libsdevent.c │ │ ├── example-libuv.c │ │ ├── example-macosx.c │ │ ├── example-poll.c │ │ ├── example-push.c │ │ ├── example-qt.cpp │ │ ├── example-qt.h │ │ ├── example-redismoduleapi.c │ │ ├── example-ssl.c │ │ └── example.c │ ├── fmacros.h │ ├── fuzzing │ │ └── format_command_fuzzer.c │ ├── hiredis-config.cmake.in │ ├── hiredis.c │ ├── hiredis.h │ ├── hiredis.pc.in │ ├── hiredis.targets │ ├── hiredis_ssl-config.cmake.in │ ├── hiredis_ssl.h │ ├── hiredis_ssl.pc.in │ ├── net.c │ ├── net.h │ ├── read.c │ ├── read.h │ ├── sds.c │ ├── sds.h │ ├── sdsalloc.h │ ├── sockcompat.c │ ├── sockcompat.h │ ├── ssl.c │ ├── test.c │ ├── test.sh │ └── win32.h └── raft │ ├── .github │ └── workflows │ │ ├── ci.yml │ │ └── daily.yml │ ├── .gitignore │ ├── .travis.yml │ ├── CMakeLists.txt │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── bin │ └── .gitignore │ ├── docs │ └── Using.md │ ├── include │ ├── raft.h │ ├── raft_log.h │ ├── raft_private.h │ └── raft_types.h │ ├── package.json │ ├── scripts │ └── amalgamate.sh │ ├── src │ ├── raft_log.c │ ├── raft_node.c │ ├── raft_server.c │ └── raft_server_properties.c │ └── tests │ ├── CuTest.c │ ├── CuTest.h │ ├── helpers.h │ ├── linked_list_queue.c │ ├── linked_list_queue.h │ ├── log_fuzzer.py │ ├── mock_send_functions.c │ ├── mock_send_functions.h │ ├── raft_cffi_builder.py │ ├── requirements.txt │ ├── test_log.c │ ├── test_log_impl.c │ ├── test_node.c │ ├── test_scenario.c │ ├── test_server.c │ ├── test_snapshotting.c │ └── virtraft2.py ├── docs ├── Deployment.md ├── Development.md ├── ExternalSharding.md ├── Introduction.md ├── Sharding.md ├── TLS.md ├── TOC.md └── Using.md ├── jepsen ├── README.md ├── aws │ ├── README.md │ ├── main.tf │ ├── output.tf │ ├── scripts │ │ ├── install_control.sh │ │ └── install_node.sh │ └── variables.tf └── docker │ ├── README.md │ ├── control │ ├── Dockerfile │ └── entrypoint.sh │ ├── docker-compose.yml │ ├── genkeys.sh │ └── node │ ├── Dockerfile │ └── entrypoint.sh ├── licenses ├── RSALv2.txt └── SSPLv1.txt ├── src ├── .clang-format ├── blocked.c ├── clientstate.c ├── cluster.c ├── commands.c ├── common.c ├── config.c ├── connection.c ├── entrycache.c ├── entrycache.h ├── file.c ├── file.h ├── fsync.c ├── join.c ├── log.c ├── log.h ├── metadata.c ├── metadata.h ├── migrate.c ├── multi.c ├── node.c ├── node_addr.c ├── proxy.c ├── raft.c ├── redisraft.c ├── redisraft.h ├── serialization.c ├── serialization_utils.c ├── snapshot.c ├── sort.c ├── threadpool.c ├── util.c └── version.h ├── tests ├── integration │ ├── __init__.py │ ├── conftest.py │ ├── modules │ │ ├── .clang-format │ │ └── hellomodule.c │ ├── pytest.ini │ ├── raftlog.py │ ├── requirements.txt │ ├── sandbox.py │ ├── test_blocking.py │ ├── test_config.py │ ├── test_elle.py │ ├── test_fuzzing.py │ ├── test_log.py │ ├── test_membership.py │ ├── test_migrate.py │ ├── test_multi.py │ ├── test_pubsub.py │ ├── test_random.py │ ├── test_sanity.py │ ├── test_sharding.py │ ├── test_snapshots.py │ └── workload.py ├── monitor.sh ├── redis-suite │ ├── run.sh │ └── skip.txt ├── tmp │ └── .gitignore └── unit │ ├── .clang-format │ ├── dut_premble.h │ ├── main.c │ ├── test.h │ ├── test_file.c │ ├── test_log.c │ ├── test_serialization.c │ └── test_util.c └── utils ├── create-cluster └── create-cluster ├── create-shard-groups └── create-shard-groups ├── deploy-aws ├── README.md ├── ansible.cfg ├── ansible │ ├── roles │ │ ├── common │ │ │ ├── tasks │ │ │ │ └── main.yml │ │ │ └── vars │ │ │ │ └── main.yml │ │ ├── control │ │ │ ├── defaults │ │ │ │ └── main.yml │ │ │ ├── tasks │ │ │ │ ├── cluster.yml │ │ │ │ ├── install.yml │ │ │ │ ├── main.yml │ │ │ │ └── memtier_benchmark.yml │ │ │ ├── templates │ │ │ │ └── cluster.sh.j2 │ │ │ └── vars │ │ │ │ └── main.yml │ │ └── node │ │ │ ├── defaults │ │ │ └── main.yml │ │ │ ├── handlers │ │ │ └── main.yml │ │ │ ├── tasks │ │ │ ├── config.yml │ │ │ ├── install.yml │ │ │ └── main.yml │ │ │ ├── templates │ │ │ ├── redis_conf.j2 │ │ │ ├── systemd_redisraft.j2 │ │ │ └── systemd_redisraft_target.j2 │ │ │ └── vars │ │ │ └── main.yml │ └── site.yml ├── create_instances.sh ├── hosts.tpl ├── main.tf ├── output.tf ├── sample.tfvars └── variables.tf └── gen-test-certs.sh /.github/codespell/config.txt: -------------------------------------------------------------------------------- 1 | [codespell] 2 | quiet-level = 2 3 | skip = *cmake*,*build/*,*deps*,*env*,*git*,*aws/*,*jepsen/docker/store/*,*.log,*.db*,*/tmp/*,*idea*,*.idx*,*.rdb,core.* 4 | ignore-words = .github/codespell/skiplist.txt 5 | builtin = code,rare,clear,names,informal 6 | -------------------------------------------------------------------------------- /.github/codespell/skiplist.txt: -------------------------------------------------------------------------------- 1 | smove 2 | ro 3 | stdio 4 | edn 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test-ubuntu: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Install build dependencies 11 | run: sudo apt-get update && sudo apt-get install -y build-essential autoconf automake libtool cmake lcov 12 | - name: Build 13 | run: | 14 | mkdir build && cd build 15 | cmake .. -DCMAKE_BUILD_TYPE=Coverage -DPYTEST_OPTS="--redis-executable=redis/src/redis-server -v" 16 | make 17 | - name: Checkout Redis 18 | uses: actions/checkout@v2 19 | with: 20 | repository: 'redis/redis' 21 | ref: 'unstable' 22 | path: 'redis' 23 | - name: Build Redis 24 | run: cd redis && make -j 4 gcov 25 | - name: Setup Python for testing 26 | uses: actions/setup-python@v1 27 | with: 28 | python-version: '3.9' 29 | architecture: 'x64' 30 | - name: Install Python dependencies 31 | run: 32 | python -m pip install -r tests/integration/requirements.txt 33 | - name: Run tests 34 | run: | 35 | cd build 36 | make coverage 37 | - name: Generate coverage info 38 | run: 39 | geninfo -o coverage.info . 40 | - name: Upload to codecov 41 | uses: codecov/codecov-action@v2 42 | 43 | test-sanitizer: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/checkout@v2 47 | - name: Install build dependencies 48 | run: sudo apt-get update && sudo apt-get install -y build-essential autoconf automake libtool cmake lcov 49 | - name: Build 50 | run: | 51 | mkdir build && cd build 52 | cmake .. -DSANITIZER=address -DPYTEST_OPTS="--redis-executable=redis/src/redis-server -v" 53 | make 54 | - name: Checkout Redis 55 | uses: actions/checkout@v2 56 | with: 57 | repository: 'redis/redis' 58 | ref: 'unstable' 59 | path: 'redis' 60 | - name: Build Redis 61 | run: cd redis && make -j 4 SANITIZER=address 62 | - name: Setup Python for testing 63 | uses: actions/setup-python@v1 64 | with: 65 | python-version: '3.9' 66 | architecture: 'x64' 67 | - name: Install Python dependencies 68 | run: 69 | python -m pip install -r tests/integration/requirements.txt 70 | - name: Run tests 71 | run: | 72 | cd build 73 | make tests 74 | 75 | test-style: 76 | runs-on: ubuntu-22.04 77 | steps: 78 | - uses: actions/checkout@v2 79 | - name: Install build dependencies 80 | run: sudo apt-get update && sudo apt-get install -y build-essential autoconf automake libtool cmake lcov clang-format colordiff 81 | - name: Setup Python for testing 82 | uses: actions/setup-python@v1 83 | with: 84 | python-version: '3.9' 85 | architecture: 'x64' 86 | - name: Install Python dependencies 87 | run: 88 | python -m pip install -r tests/integration/requirements.txt 89 | - name: Check Style - src 90 | run: | 91 | clang-format -n -Werror -style=file:src/.clang-format src/* || true 92 | colordiff -u <(cat src/*) <(clang-format -style=file:src/.clang-format src/*) 93 | - name: Check Style - unit tests 94 | run: | 95 | clang-format -n -Werror -style=file:tests/unit/.clang-format tests/unit/* || true 96 | colordiff -u <(cat tests/unit/*) <(clang-format -style=file:tests/unit/.clang-format tests/unit/*) 97 | - name: Check Style - integration tests 98 | run: | 99 | pycodestyle --show-source tests 100 | - name: Spellcheck 101 | run: 102 | codespell --config=./.github/codespell/config.txt 103 | -------------------------------------------------------------------------------- /.github/workflows/jepsen.yml: -------------------------------------------------------------------------------- 1 | name: Jepsen 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | inputs: 8 | redisraft_repo: 9 | description: 'redisraft repo owner and name' 10 | required: false 11 | default: 'redisLabs/redisraft' 12 | redisraft_branch: 13 | description: 'redisraft git branch or sha to use' 14 | required: false 15 | default: 'master' 16 | redis_repo: 17 | description: 'redis repo owner and name' 18 | required: false 19 | default: 'redis/redis' 20 | redis_branch: 21 | description: 'redis git branch or sha to use' 22 | required: false 23 | default: 'unstable' 24 | jepsen-repo: 25 | description: 'Jepsen repository, e.g redislabs/jepsen-redisraft' 26 | default: 'redislabs/jepsen-redisraft' 27 | required: false 28 | 29 | jobs: 30 | jepsen: 31 | runs-on: ubuntu-20.04 32 | if: | 33 | github.event_name == 'workflow_dispatch' || 34 | (github.event_name == 'schedule' && github.repository == 'redislabs/redisraft') 35 | steps: 36 | - uses: actions/checkout@v1 37 | - name: Set repository variables 38 | env: 39 | DEFAULT_REDIS_REPO: redis/redis 40 | DEFAULT_REDIS_BRANCH: unstable 41 | DEFAULT_REDISRAFT_REPO: redislabs/redisraft 42 | DEFAULT_REDISRAFT_BRANCH: master 43 | DEFAULT_JEPSEN_REPO: 'redislabs/jepsen-redisraft' 44 | run: | 45 | echo "REDIS_REPO=${{github.event.inputs.redis_repo || env.DEFAULT_REDIS_REPO}}" >> $GITHUB_ENV 46 | echo "REDIS_BRANCH=${{github.event.inputs.redis_branch || env.DEFAULT_REDIS_BRANCH}}" >> $GITHUB_ENV 47 | echo "REDISRAFT_REPO=${{github.event.inputs.redisraft_repo || env.DEFAULT_REDISRAFT_REPO}}" >> $GITHUB_ENV 48 | echo "REDISRAFT_BRANCH=${{github.event.inputs.redisraft_branch || env.DEFAULT_REDISRAFT_BRANCH}}" >> $GITHUB_ENV 49 | echo "JEPSEN_REPO=${{ github.event.inputs.jepsen-repo || env.DEFAULT_JEPSEN_REPO }}" >> $GITHUB_ENV 50 | - name: Install ripgrep 51 | run: sudo apt-get install ripgrep 52 | - name: Configure core_pattern 53 | run: echo "core.%p" | sudo tee /proc/sys/kernel/core_pattern 54 | - name: Build containers 55 | run: cd jepsen/docker && ./genkeys.sh && docker-compose build --build-arg JEPSEN_REPO=${{ env.JEPSEN_REPO }} 56 | - name: Start containers 57 | run: cd jepsen/docker && docker-compose up -d 58 | - name: Run test 59 | run: | 60 | docker exec -w /jepsen jepsen-control \ 61 | lein run test-all \ 62 | --ssh-private-key /root/.ssh/id_rsa \ 63 | --follower-proxy \ 64 | --time-limit 600 \ 65 | --test-count 20 \ 66 | --concurrency 4n \ 67 | --nemesis kill,pause,partition,member \ 68 | --redis-repo https://github.com/${{ env.REDIS_REPO }} \ 69 | --redis-version ${{ env.REDIS_BRANCH }} \ 70 | --raft-repo https://github.com/${{ env.REDISRAFT_REPO }} \ 71 | --raft-version ${{ env.REDISRAFT_BRANCH }} | rg --passthrough '^0 failures' 72 | - name: Archive Jepsen results 73 | uses: actions/upload-artifact@v2 74 | if: failure() || cancelled() 75 | with: 76 | name: jepsen-results 77 | path: ./jepsen/docker/store 78 | -------------------------------------------------------------------------------- /.github/workflows/redis-suite.yml: -------------------------------------------------------------------------------- 1 | name: Redis Test Suite 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | inputs: 8 | redis_repo: 9 | description: 'redis repo owner and name' 10 | required: false 11 | default: 'redis/redis' 12 | redis_branch: 13 | description: 'redis git branch or sha to use' 14 | required: false 15 | default: 'unstable' 16 | redisraft_repo: 17 | description: 'redisraft repo owner and name' 18 | required: false 19 | default: 'redislabs/redisraft' 20 | redisraft_branch: 21 | description: 'redisraft git branch or sha to use' 22 | required: false 23 | default: 'master' 24 | 25 | jobs: 26 | redis-suite: 27 | runs-on: ubuntu-latest 28 | if: | 29 | github.event_name == 'workflow_dispatch' || 30 | (github.event_name == 'schedule' && github.repository == 'redislabs/redisraft') 31 | steps: 32 | - name: Set variables 33 | env: 34 | DEFAULT_REDIS_REPO: redis/redis 35 | DEFAULT_REDIS_BRANCH: unstable 36 | DEFAULT_REDISRAFT_REPO: redislabs/redisraft 37 | DEFAULT_REDISRAFT_BRANCH: master 38 | run: | 39 | echo "REDIS_REPO=${{github.event.inputs.redis_repo || env.DEFAULT_REDIS_REPO}}" >> $GITHUB_ENV 40 | echo "REDIS_BRANCH=${{github.event.inputs.redis_branch || env.DEFAULT_REDIS_BRANCH}}" >> $GITHUB_ENV 41 | echo "REDISRAFT_REPO=${{github.event.inputs.redisraft_repo || env.DEFAULT_REDISRAFT_REPO}}" >> $GITHUB_ENV 42 | echo "REDISRAFT_BRANCH=${{github.event.inputs.redisraft_branch || env.DEFAULT_REDISRAFT_BRANCH}}" >> $GITHUB_ENV 43 | - uses: actions/checkout@v2 44 | with: 45 | repository: ${{ env.REDISRAFT_REPO }} 46 | ref: ${{ env.REDISRAFT_BRANCH }} 47 | - name: Install build dependencies 48 | run: sudo apt-get install -y build-essential autoconf automake libtool cmake lcov 49 | - name: Build 50 | run: | 51 | mkdir build && cd build 52 | cmake .. 53 | make 54 | - name: Checkout Redis 55 | uses: actions/checkout@v2 56 | with: 57 | repository: ${{ env.REDIS_REPO }} 58 | ref: ${{ env.REDIS_BRANCH }} 59 | path: 'redis' 60 | - name: Build Redis 61 | run: cd redis && make -j 4 62 | - name: Install test dependencies 63 | run: sudo apt-get install -y tcl8.6 64 | - name: Run tests 65 | env: 66 | REDIS_DIR: ${{ github.workspace }}/redis 67 | run: ./tests/redis-suite/run.sh 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.o 2 | **/*.gcda 3 | **/*.gcno 4 | tests/tests_main 5 | tests/tmp/ 6 | **/__pycache__ 7 | **/*.swp 8 | .build/ 9 | core.* 10 | *.so 11 | **/dump.rdb 12 | cscope.*out 13 | tests/lcov.info 14 | *.rdb 15 | .env/ 16 | .vscode/ 17 | .ccls* 18 | jepsen/docker/secret/ 19 | .idea/ 20 | *.dbx 21 | *.log 22 | *.db 23 | *.idx 24 | cmake-build* 25 | utils/create-cluster/*.log 26 | utils/create-cluster/*.db 27 | utils/create-cluster/*.db.idx 28 | venv 29 | buildinfo.h 30 | build/* 31 | jepsen/docker/store/* 32 | tests/tls/* 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Except as otherwise specified in the source code headers for specific files, the source code in this repository is made available to you under your choice of 2 | (i) Redis Source Available License 2.0 (RSALv2) or (ii) the Server Side Public License v1 (SSPLv1) 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RedisRaft 2 | 3 | > :warning: RedisRaft is still being developed and is not yet ready for any real production use. Please do not use it for any mission critical purpose at this time. 4 | 5 | ### Strongly-Consistent Redis Deployments 6 | 7 | RedisRaft is a Redis module that implements the [Raft Consensus 8 | Algorithm](https://raft.github.io/), making it possible to create strongly-consistent clusters of Redis servers. 9 | 10 | The Raft algorithm is provided by a [standalone Raft 11 | library](https://github.com/redislabs/raft). This is a fork of the original library created by Willem-Hendrik Thiart, which is now actively maintained by Redis Ltd. 12 | 13 | ## Main Features 14 | 15 | * Strong consistency (in the language of [CAP](https://en.wikipedia.org/wiki/CAP_theorem), this system prioritizes consistency and partition-tolerance). 16 | * Support for most Redis data types and commands 17 | * Dynamic cluster configuration (adding / removing nodes) 18 | * Snapshots for log compaction 19 | * Configurable quorum or fast reads 20 | 21 | ## Getting Started 22 | 23 | ### Building 24 | 25 | To compile the module, you will need: 26 | * Build essentials (a compiler, GNU make, etc.) 27 | * CMake 28 | * GNU autotools (autoconf, automake, libtool). 29 | 30 | To build: 31 | 32 | git clone https://github.com/RedisLabs/redisraft.git 33 | cd redisraft 34 | mkdir build && cd build 35 | cmake .. 36 | make 37 | 38 | `redisraft.so` will be created under the project directory. 39 | 40 | ### Creating a RedisRaft Cluster 41 | 42 | RedisRaft requires Redis build from the 'unstable' branch. Build Redis first: 43 | 44 | git clone https://github.com/redis/redis 45 | cd redis 46 | make 47 | make install 48 | 49 | To create a three-node cluster, start the first node: 50 | 51 | redis-server \ 52 | --port 5001 --dbfilename raft1.rdb \ 53 | --loadmodule /redisraft.so \ 54 | --raft.log-filename raftlog1.db \ 55 | --raft.addr localhost:5001 56 | 57 | Then initialize the cluster: 58 | 59 | redis-cli -p 5001 raft.cluster init 60 | 61 | Now start the second node, and run the `RAFT.CLUSTER JOIN` command to join it to the existing cluster: 62 | 63 | redis-server \ 64 | --port 5002 --dbfilename raft2.rdb \ 65 | --loadmodule /redisraft.so \ 66 | --raft.log-filename raftlog2.db \ 67 | --raft.addr localhost:5002 68 | 69 | redis-cli -p 5002 RAFT.CLUSTER JOIN localhost:5001 70 | 71 | Now add the third node in the same way: 72 | 73 | redis-server \ 74 | --port 5003 --dbfilename raft3.rdb \ 75 | --loadmodule /redisraft.so \ 76 | --raft.log-filename raftlog3.db 77 | --raft.addr localhost:5003 78 | 79 | redis-cli -p 5003 RAFT.CLUSTER JOIN localhost:5001 80 | 81 | To query the cluster state, run the `INFO raft` command: 82 | 83 | redis-cli -p 5001 INFO raft 84 | 85 | Now you can start using this RedisRaft cluster. All [supported Redis commands](docs/Using.md) will be executed in a strongly-consistent manner using the Raft protocol. 86 | 87 | ## Documentation 88 | 89 | Please consult the [documentation](docs/TOC.md) for more information. 90 | 91 | ## License 92 | 93 | RedisRaft is licensed under the [Redis Source Available License 2.0 (RSALv2)](https://redis.com/legal/rsalv2-agreement) or the [Server Side Public License v1 (SSPLv1)](https://www.mongodb.com/licensing/server-side-public-license). -------------------------------------------------------------------------------- /benchmark/benchmark.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redisraft-benchmark", 3 | "configuration": { 4 | "memtier_benchmark": { 5 | "binary": "../memtier_benchmark/memtier_benchmark", 6 | "threads": 1, 7 | "clients": 50, 8 | "test_time": 10 9 | } 10 | }, 11 | "targets": [ 12 | { 13 | "name": "redis", 14 | "binary": "../redis/src/redis-server", 15 | "args": [] 16 | }, 17 | { 18 | "name": "redis-aof-no-fsync", 19 | "binary": "../redis/src/redis-server", 20 | "args": ["--appendonly", "yes", "--appendfsync", "no"] 21 | }, 22 | { 23 | "name": "redis-aof-fsync-always", 24 | "binary": "../redis/src/redis-server", 25 | "args": ["--appendonly", "yes", "--appendfsync", "always"] 26 | }, 27 | { 28 | "name": "redis-raft-no-fsync", 29 | "binary": "./benchmark/redisraft_cluster.sh", 30 | "args": [ 31 | "--redis", "../redis/src/redis-server", 32 | "--raftmodule", "redisraft.so", 33 | "--modulearg", "--raft.log-fsync", "no", 34 | "--nodes", "3" 35 | ] 36 | }, 37 | { 38 | "name": "redis-raft-fsync", 39 | "binary": "./benchmark/redisraft_cluster.sh", 40 | "args": [ 41 | "--redis", "../redis/src/redis-server", 42 | "--raftmodule", "redisraft.so", 43 | "--nodes", "3" 44 | ] 45 | } 46 | ], 47 | "benchmarks": [ 48 | { 49 | "name": "default", 50 | "args": [] 51 | } 52 | ] 53 | } 54 | 55 | -------------------------------------------------------------------------------- /benchmark/redisraft_cluster.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Redis Ltd. 2020 - present 4 | # Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 5 | # the Server Side Public License v1 (SSPLv1). 6 | 7 | usage() { 8 | echo "usage: redisraft_cluster.sh [--redis ] [--raftmodule ]" 9 | echo " [--modulearg ] [--nodes ] [--port ] [redis arguments]" 10 | exit 2 11 | } 12 | 13 | panic() { 14 | echo "Error: $*" 15 | exit 1 16 | } 17 | echo $* > cluster 18 | redis_args=() 19 | module_args=() 20 | 21 | while [ $# -gt 0 ]; do 22 | case $1 in 23 | --redis) 24 | shift 25 | redis=$1 26 | ;; 27 | --raftmodule) 28 | shift 29 | raftmodule=$1 30 | ;; 31 | --modulearg) 32 | shift 33 | module_args+=($1) 34 | ;; 35 | --nodes) 36 | shift 37 | nodes=$1 38 | ;; 39 | --port) 40 | shift 41 | port=$1 42 | ;; 43 | *) 44 | redis_args+=($1) 45 | ;; 46 | esac 47 | shift 48 | done 49 | 50 | # Check required arguments 51 | for arg in redis raftmodule nodes port; 52 | do 53 | if [ -z "${!arg}" ]; then 54 | echo "Error: --${arg} is required." 55 | usage 56 | fi 57 | done 58 | 59 | # Check redis-cli 60 | redis-cli --version >/dev/null 2>&1 61 | if [ $? != 0 ]; then 62 | echo "Error: redis-cli not found/not working." 63 | exit 3 64 | fi 65 | 66 | # Run servers 67 | procs=() 68 | kill_procs() { 69 | kill ${procs[@]} 70 | wait ${procs[@]} 71 | } 72 | trap "kill_procs" EXIT 73 | export LD_LIBRARY_PATH=`pwd` # For redisraft.so in case it's not abspath 74 | for n in $(seq ${nodes}); do 75 | p=$((${port} + $n - 1)) 76 | raftlog=redisraft${n}.db 77 | rm -f ${raftlog} ${raftlog}.idx 78 | ${redis} --loadmodule ${raftmodule} \ 79 | --raft.id ${n} \ 80 | --raft.addr 127.0.0.1:${p} \ 81 | --raft.log-filename redisraft${n}.db \ 82 | --raft.follower-proxy yes \ 83 | ${module_args[@]} \ 84 | --logfile redis${n}.log \ 85 | --port ${p} \ 86 | ${redis_args[@]} & 87 | procs+=($!) 88 | done 89 | 90 | # Wait for things to settle 91 | sleep 1 92 | 93 | # Create cluster 94 | redis-cli -p ${port} RAFT.CLUSTER INIT || panic "Failed to configure cluster" 95 | for n in $(seq 2 ${nodes}); do 96 | p=$((${port} + $n - 1)) 97 | redis-cli -p ${p} RAFT.CLUSTER JOIN 127.0.0.1:${port} 98 | done 99 | 100 | # Wait to be killed 101 | sleep infinity 102 | -------------------------------------------------------------------------------- /deps/common/crc16.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2019 Georges Menie (www.menie.org) 3 | * All rights reserved. 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the University of California, Berkeley nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "crc16.h" 29 | 30 | /* CRC16 implementation acording to CCITT standards */ 31 | 32 | static const unsigned short crc16tab[256]= { 33 | 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 34 | 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 35 | 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 36 | 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 37 | 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 38 | 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 39 | 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 40 | 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 41 | 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 42 | 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 43 | 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 44 | 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 45 | 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 46 | 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 47 | 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 48 | 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 49 | 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 50 | 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 51 | 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 52 | 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 53 | 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 54 | 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 55 | 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 56 | 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 57 | 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 58 | 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 59 | 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 60 | 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 61 | 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 62 | 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 63 | 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 64 | 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 65 | }; 66 | 67 | unsigned short crc16_ccitt(const char *buf, int len) 68 | { 69 | register int counter; 70 | register unsigned short crc = 0; 71 | for( counter = 0; counter < len; counter++) 72 | crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; 73 | return crc; 74 | } 75 | -------------------------------------------------------------------------------- /deps/common/crc16.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2019 Georges Menie (www.menie.org) 3 | * All rights reserved. 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * 7 | * * Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * * Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * * Neither the name of the University of California, Berkeley nor the 13 | * names of its contributors may be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _CRC16_H_ 29 | #define _CRC16_H_ 30 | 31 | unsigned short crc16_ccitt(const char *buf, int len); 32 | 33 | #endif /* _CRC16_H_ */ 34 | -------------------------------------------------------------------------------- /deps/common/sc_crc32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD-3-Clause 3 | * 4 | * Copyright 2021 Ozan Tezcan 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 3. Neither the name of the copyright holder nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 24 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 25 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef SC_CRC32_H 33 | #define SC_CRC32_H 34 | 35 | #include 36 | #include 37 | 38 | #define SC_CRC32_VERSION "2.0.0" 39 | 40 | /** 41 | * Call once globally. 42 | */ 43 | void sc_crc32_init(void); 44 | 45 | /** 46 | * @param crc initial value, if you're not calculating crc from partial buffers, 47 | * it should be zero. 48 | * @param buf buf 49 | * @param len len 50 | * @return crc value 51 | */ 52 | uint32_t sc_crc32(uint32_t crc, const void *buf, size_t len); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /deps/hiredis/.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | name-template: '$NEXT_MAJOR_VERSION' 2 | tag-template: 'v$NEXT_MAJOR_VERSION' 3 | autolabeler: 4 | - label: 'maintenance' 5 | files: 6 | - '*.md' 7 | - '.github/*' 8 | - label: 'bug' 9 | branch: 10 | - '/bug-.+' 11 | - label: 'maintenance' 12 | branch: 13 | - '/maintenance-.+' 14 | - label: 'feature' 15 | branch: 16 | - '/feature-.+' 17 | categories: 18 | - title: 'Breaking Changes' 19 | labels: 20 | - 'breakingchange' 21 | 22 | - title: '🧪 Experimental Features' 23 | labels: 24 | - 'experimental' 25 | - title: '🚀 New Features' 26 | labels: 27 | - 'feature' 28 | - 'enhancement' 29 | - title: '🐛 Bug Fixes' 30 | labels: 31 | - 'fix' 32 | - 'bugfix' 33 | - 'bug' 34 | - 'BUG' 35 | - title: '🧰 Maintenance' 36 | label: 'maintenance' 37 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 38 | exclude-labels: 39 | - 'skip-changelog' 40 | template: | 41 | ## Changes 42 | 43 | $CHANGES 44 | 45 | ## Contributors 46 | We'd like to thank all the contributors who worked on this release! 47 | 48 | $CONTRIBUTORS 49 | 50 | -------------------------------------------------------------------------------- /deps/hiredis/.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: release-drafter-config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /deps/hiredis/.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | full-build: 11 | name: Build all, plus default examples, run tests against redis 12 | runs-on: ubuntu-latest 13 | env: 14 | # the docker image used by the test.sh 15 | REDIS_DOCKER: redis:alpine 16 | 17 | steps: 18 | - name: Install prerequisites 19 | run: sudo apt-get update && sudo apt-get install -y libev-dev libevent-dev libglib2.0-dev libssl-dev valgrind 20 | - uses: actions/checkout@v3 21 | - name: Run make 22 | run: make all examples 23 | - name: Run unittests 24 | run: make check 25 | - name: Run tests with valgrind 26 | env: 27 | TEST_PREFIX: valgrind --error-exitcode=100 28 | SKIPS_ARG: --skip-throughput 29 | run: make check 30 | 31 | build-32-bit: 32 | name: Build and test minimal 32 bit linux 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Install prerequisites 36 | run: sudo apt-get update && sudo apt-get install gcc-multilib 37 | - uses: actions/checkout@v3 38 | - name: Run make 39 | run: make all 40 | env: 41 | PLATFORM_FLAGS: -m32 42 | - name: Run unittests 43 | env: 44 | REDIS_DOCKER: redis:alpine 45 | run: make check 46 | 47 | build-arm: 48 | name: Cross-compile and test arm linux with Qemu 49 | runs-on: ubuntu-latest 50 | strategy: 51 | matrix: 52 | include: 53 | - name: arm 54 | toolset: arm-linux-gnueabi 55 | emulator: qemu-arm 56 | - name: aarch64 57 | toolset: aarch64-linux-gnu 58 | emulator: qemu-aarch64 59 | 60 | steps: 61 | - name: Install qemu 62 | if: matrix.emulator 63 | run: sudo apt-get update && sudo apt-get install -y qemu-user 64 | - name: Install platform toolset 65 | if: matrix.toolset 66 | run: sudo apt-get install -y gcc-${{matrix.toolset}} 67 | - uses: actions/checkout@v3 68 | - name: Run make 69 | run: make all 70 | env: 71 | CC: ${{matrix.toolset}}-gcc 72 | AR: ${{matrix.toolset}}-ar 73 | - name: Run unittests 74 | env: 75 | REDIS_DOCKER: redis:alpine 76 | TEST_PREFIX: ${{matrix.emulator}} -L /usr/${{matrix.toolset}}/ 77 | run: make check 78 | 79 | build-windows: 80 | name: Build and test on windows 64 bit Intel 81 | runs-on: windows-latest 82 | steps: 83 | - uses: microsoft/setup-msbuild@v1.0.2 84 | - uses: actions/checkout@v3 85 | - name: Run CMake (shared lib) 86 | run: cmake -Wno-dev CMakeLists.txt 87 | - name: Build hiredis (shared lib) 88 | run: MSBuild hiredis.vcxproj /p:Configuration=Debug 89 | - name: Run CMake (static lib) 90 | run: cmake -Wno-dev CMakeLists.txt -DBUILD_SHARED_LIBS=OFF 91 | - name: Build hiredis (static lib) 92 | run: MSBuild hiredis.vcxproj /p:Configuration=Debug 93 | - name: Build hiredis-test 94 | run: MSBuild hiredis-test.vcxproj /p:Configuration=Debug 95 | # use memurai, redis compatible server, since it is easy to install. Can't 96 | # install official redis containers on the windows runner 97 | - name: Install Memurai redis server 98 | run: choco install -y memurai-developer.install 99 | - name: Run tests 100 | run: Debug\hiredis-test.exe 101 | -------------------------------------------------------------------------------- /deps/hiredis/.gitignore: -------------------------------------------------------------------------------- 1 | /hiredis-test 2 | /examples/hiredis-example* 3 | /*.o 4 | /*.so 5 | /*.dylib 6 | /*.a 7 | /*.pc 8 | *.dSYM 9 | tags 10 | -------------------------------------------------------------------------------- /deps/hiredis/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | os: 7 | - linux 8 | - osx 9 | 10 | dist: bionic 11 | 12 | branches: 13 | only: 14 | - staging 15 | - trying 16 | - master 17 | - /^release\/.*$/ 18 | 19 | install: 20 | - if [ "$TRAVIS_COMPILER" != "mingw" ]; then 21 | wget https://github.com/redis/redis/archive/6.0.6.tar.gz; 22 | tar -xzvf 6.0.6.tar.gz; 23 | pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd; 24 | fi; 25 | 26 | before_script: 27 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then 28 | curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg; 29 | sudo installer -pkg MacPorts-2.6.2-10.13-HighSierra.pkg -target /; 30 | export PATH=$PATH:/opt/local/bin && sudo port -v selfupdate; 31 | sudo port -N install openssl redis; 32 | fi; 33 | 34 | addons: 35 | apt: 36 | packages: 37 | - libc6-dbg 38 | - libc6-dev 39 | - libc6:i386 40 | - libc6-dev-i386 41 | - libc6-dbg:i386 42 | - gcc-multilib 43 | - g++-multilib 44 | - libssl-dev 45 | - libssl-dev:i386 46 | - valgrind 47 | 48 | env: 49 | - BITS="32" 50 | - BITS="64" 51 | 52 | script: 53 | - EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON"; 54 | if [ "$TRAVIS_OS_NAME" == "osx" ]; then 55 | if [ "$BITS" == "32" ]; then 56 | CFLAGS="-m32 -Werror"; 57 | CXXFLAGS="-m32 -Werror"; 58 | LDFLAGS="-m32"; 59 | EXTRA_CMAKE_OPTS=; 60 | else 61 | CFLAGS="-Werror"; 62 | CXXFLAGS="-Werror"; 63 | fi; 64 | else 65 | TEST_PREFIX="valgrind --track-origins=yes --leak-check=full"; 66 | if [ "$BITS" == "32" ]; then 67 | CFLAGS="-m32 -Werror"; 68 | CXXFLAGS="-m32 -Werror"; 69 | LDFLAGS="-m32"; 70 | EXTRA_CMAKE_OPTS=; 71 | else 72 | CFLAGS="-Werror"; 73 | CXXFLAGS="-Werror"; 74 | fi; 75 | fi; 76 | export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS 77 | - make && make clean; 78 | if [ "$TRAVIS_OS_NAME" == "osx" ]; then 79 | if [ "$BITS" == "64" ]; then 80 | OPENSSL_PREFIX="$(ls -d /usr/local/Cellar/openssl@1.1/*)" USE_SSL=1 make; 81 | fi; 82 | else 83 | USE_SSL=1 make; 84 | fi; 85 | - mkdir build/ && cd build/ 86 | - cmake .. ${EXTRA_CMAKE_OPTS} 87 | - make VERBOSE=1 88 | - if [ "$BITS" == "64" ]; then 89 | TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V; 90 | else 91 | SKIPS_AS_FAILS=1 ctest -V; 92 | fi; 93 | 94 | jobs: 95 | include: 96 | # Windows MinGW cross compile on Linux 97 | - os: linux 98 | dist: xenial 99 | compiler: mingw 100 | addons: 101 | apt: 102 | packages: 103 | - ninja-build 104 | - gcc-mingw-w64-x86-64 105 | - g++-mingw-w64-x86-64 106 | script: 107 | - mkdir build && cd build 108 | - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on 109 | - ninja -v 110 | 111 | # Windows MSVC 2017 112 | - os: windows 113 | compiler: msvc 114 | env: 115 | - MATRIX_EVAL="CC=cl.exe && CXX=cl.exe" 116 | before_install: 117 | - eval "${MATRIX_EVAL}" 118 | install: 119 | - choco install ninja 120 | - choco install -y memurai-developer 121 | script: 122 | - mkdir build && cd build 123 | - cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&&' 124 | cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON '&&' ninja -v 125 | - ./hiredis-test.exe 126 | -------------------------------------------------------------------------------- /deps/hiredis/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011, Salvatore Sanfilippo 2 | Copyright (c) 2010-2011, Pieter Noordhuis 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Redis nor the names of its contributors may be used 17 | to endorse or promote products derived from this software without specific 18 | prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /deps/hiredis/adapters/ivykis.h: -------------------------------------------------------------------------------- 1 | #ifndef __HIREDIS_IVYKIS_H__ 2 | #define __HIREDIS_IVYKIS_H__ 3 | #include 4 | #include "../hiredis.h" 5 | #include "../async.h" 6 | 7 | typedef struct redisIvykisEvents { 8 | redisAsyncContext *context; 9 | struct iv_fd fd; 10 | } redisIvykisEvents; 11 | 12 | static void redisIvykisReadEvent(void *arg) { 13 | redisAsyncContext *context = (redisAsyncContext *)arg; 14 | redisAsyncHandleRead(context); 15 | } 16 | 17 | static void redisIvykisWriteEvent(void *arg) { 18 | redisAsyncContext *context = (redisAsyncContext *)arg; 19 | redisAsyncHandleWrite(context); 20 | } 21 | 22 | static void redisIvykisAddRead(void *privdata) { 23 | redisIvykisEvents *e = (redisIvykisEvents*)privdata; 24 | iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); 25 | } 26 | 27 | static void redisIvykisDelRead(void *privdata) { 28 | redisIvykisEvents *e = (redisIvykisEvents*)privdata; 29 | iv_fd_set_handler_in(&e->fd, NULL); 30 | } 31 | 32 | static void redisIvykisAddWrite(void *privdata) { 33 | redisIvykisEvents *e = (redisIvykisEvents*)privdata; 34 | iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); 35 | } 36 | 37 | static void redisIvykisDelWrite(void *privdata) { 38 | redisIvykisEvents *e = (redisIvykisEvents*)privdata; 39 | iv_fd_set_handler_out(&e->fd, NULL); 40 | } 41 | 42 | static void redisIvykisCleanup(void *privdata) { 43 | redisIvykisEvents *e = (redisIvykisEvents*)privdata; 44 | 45 | iv_fd_unregister(&e->fd); 46 | hi_free(e); 47 | } 48 | 49 | static int redisIvykisAttach(redisAsyncContext *ac) { 50 | redisContext *c = &(ac->c); 51 | redisIvykisEvents *e; 52 | 53 | /* Nothing should be attached when something is already attached */ 54 | if (ac->ev.data != NULL) 55 | return REDIS_ERR; 56 | 57 | /* Create container for context and r/w events */ 58 | e = (redisIvykisEvents*)hi_malloc(sizeof(*e)); 59 | if (e == NULL) 60 | return REDIS_ERR; 61 | 62 | e->context = ac; 63 | 64 | /* Register functions to start/stop listening for events */ 65 | ac->ev.addRead = redisIvykisAddRead; 66 | ac->ev.delRead = redisIvykisDelRead; 67 | ac->ev.addWrite = redisIvykisAddWrite; 68 | ac->ev.delWrite = redisIvykisDelWrite; 69 | ac->ev.cleanup = redisIvykisCleanup; 70 | ac->ev.data = e; 71 | 72 | /* Initialize and install read/write events */ 73 | IV_FD_INIT(&e->fd); 74 | e->fd.fd = c->fd; 75 | e->fd.handler_in = redisIvykisReadEvent; 76 | e->fd.handler_out = redisIvykisWriteEvent; 77 | e->fd.handler_err = NULL; 78 | e->fd.cookie = e->context; 79 | 80 | iv_fd_register(&e->fd); 81 | 82 | return REDIS_OK; 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /deps/hiredis/adapters/libhv.h: -------------------------------------------------------------------------------- 1 | #ifndef __HIREDIS_LIBHV_H__ 2 | #define __HIREDIS_LIBHV_H__ 3 | 4 | #include 5 | #include "../hiredis.h" 6 | #include "../async.h" 7 | 8 | typedef struct redisLibhvEvents { 9 | hio_t *io; 10 | htimer_t *timer; 11 | } redisLibhvEvents; 12 | 13 | static void redisLibhvHandleEvents(hio_t* io) { 14 | redisAsyncContext* context = (redisAsyncContext*)hevent_userdata(io); 15 | int events = hio_events(io); 16 | int revents = hio_revents(io); 17 | if (context && (events & HV_READ) && (revents & HV_READ)) { 18 | redisAsyncHandleRead(context); 19 | } 20 | if (context && (events & HV_WRITE) && (revents & HV_WRITE)) { 21 | redisAsyncHandleWrite(context); 22 | } 23 | } 24 | 25 | static void redisLibhvAddRead(void *privdata) { 26 | redisLibhvEvents* events = (redisLibhvEvents*)privdata; 27 | hio_add(events->io, redisLibhvHandleEvents, HV_READ); 28 | } 29 | 30 | static void redisLibhvDelRead(void *privdata) { 31 | redisLibhvEvents* events = (redisLibhvEvents*)privdata; 32 | hio_del(events->io, HV_READ); 33 | } 34 | 35 | static void redisLibhvAddWrite(void *privdata) { 36 | redisLibhvEvents* events = (redisLibhvEvents*)privdata; 37 | hio_add(events->io, redisLibhvHandleEvents, HV_WRITE); 38 | } 39 | 40 | static void redisLibhvDelWrite(void *privdata) { 41 | redisLibhvEvents* events = (redisLibhvEvents*)privdata; 42 | hio_del(events->io, HV_WRITE); 43 | } 44 | 45 | static void redisLibhvCleanup(void *privdata) { 46 | redisLibhvEvents* events = (redisLibhvEvents*)privdata; 47 | 48 | if (events->timer) 49 | htimer_del(events->timer); 50 | 51 | hio_close(events->io); 52 | hevent_set_userdata(events->io, NULL); 53 | 54 | hi_free(events); 55 | } 56 | 57 | static void redisLibhvTimeout(htimer_t* timer) { 58 | hio_t* io = (hio_t*)hevent_userdata(timer); 59 | redisAsyncHandleTimeout((redisAsyncContext*)hevent_userdata(io)); 60 | } 61 | 62 | static void redisLibhvSetTimeout(void *privdata, struct timeval tv) { 63 | redisLibhvEvents* events; 64 | uint32_t millis; 65 | hloop_t* loop; 66 | 67 | events = (redisLibhvEvents*)privdata; 68 | millis = tv.tv_sec * 1000 + tv.tv_usec / 1000; 69 | 70 | if (millis == 0) { 71 | /* Libhv disallows zero'd timers so treat this as a delete or NO OP */ 72 | if (events->timer) { 73 | htimer_del(events->timer); 74 | events->timer = NULL; 75 | } 76 | } else if (events->timer == NULL) { 77 | /* Add new timer */ 78 | loop = hevent_loop(events->io); 79 | events->timer = htimer_add(loop, redisLibhvTimeout, millis, 1); 80 | hevent_set_userdata(events->timer, events->io); 81 | } else { 82 | /* Update existing timer */ 83 | htimer_reset(events->timer, millis); 84 | } 85 | } 86 | 87 | static int redisLibhvAttach(redisAsyncContext* ac, hloop_t* loop) { 88 | redisContext *c = &(ac->c); 89 | redisLibhvEvents *events; 90 | hio_t* io = NULL; 91 | 92 | if (ac->ev.data != NULL) { 93 | return REDIS_ERR; 94 | } 95 | 96 | /* Create container struct to keep track of our io and any timer */ 97 | events = (redisLibhvEvents*)hi_malloc(sizeof(*events)); 98 | if (events == NULL) { 99 | return REDIS_ERR; 100 | } 101 | 102 | io = hio_get(loop, c->fd); 103 | if (io == NULL) { 104 | hi_free(events); 105 | return REDIS_ERR; 106 | } 107 | 108 | hevent_set_userdata(io, ac); 109 | 110 | events->io = io; 111 | events->timer = NULL; 112 | 113 | ac->ev.addRead = redisLibhvAddRead; 114 | ac->ev.delRead = redisLibhvDelRead; 115 | ac->ev.addWrite = redisLibhvAddWrite; 116 | ac->ev.delWrite = redisLibhvDelWrite; 117 | ac->ev.cleanup = redisLibhvCleanup; 118 | ac->ev.scheduleTimer = redisLibhvSetTimeout; 119 | ac->ev.data = events; 120 | 121 | return REDIS_OK; 122 | } 123 | #endif 124 | -------------------------------------------------------------------------------- /deps/hiredis/alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Michael Grunder 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "fmacros.h" 32 | #include "alloc.h" 33 | #include 34 | #include 35 | 36 | hiredisAllocFuncs hiredisAllocFns = { 37 | .mallocFn = malloc, 38 | .callocFn = calloc, 39 | .reallocFn = realloc, 40 | .strdupFn = strdup, 41 | .freeFn = free, 42 | }; 43 | 44 | /* Override hiredis' allocators with ones supplied by the user */ 45 | hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { 46 | hiredisAllocFuncs orig = hiredisAllocFns; 47 | 48 | hiredisAllocFns = *override; 49 | 50 | return orig; 51 | } 52 | 53 | /* Reset allocators to use libc defaults */ 54 | void hiredisResetAllocators(void) { 55 | hiredisAllocFns = (hiredisAllocFuncs) { 56 | .mallocFn = malloc, 57 | .callocFn = calloc, 58 | .reallocFn = realloc, 59 | .strdupFn = strdup, 60 | .freeFn = free, 61 | }; 62 | } 63 | 64 | #ifdef _WIN32 65 | 66 | void *hi_malloc(size_t size) { 67 | return hiredisAllocFns.mallocFn(size); 68 | } 69 | 70 | void *hi_calloc(size_t nmemb, size_t size) { 71 | /* Overflow check as the user can specify any arbitrary allocator */ 72 | if (SIZE_MAX / size < nmemb) 73 | return NULL; 74 | 75 | return hiredisAllocFns.callocFn(nmemb, size); 76 | } 77 | 78 | void *hi_realloc(void *ptr, size_t size) { 79 | return hiredisAllocFns.reallocFn(ptr, size); 80 | } 81 | 82 | char *hi_strdup(const char *str) { 83 | return hiredisAllocFns.strdupFn(str); 84 | } 85 | 86 | void hi_free(void *ptr) { 87 | hiredisAllocFns.freeFn(ptr); 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /deps/hiredis/alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Michael Grunder 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef HIREDIS_ALLOC_H 32 | #define HIREDIS_ALLOC_H 33 | 34 | #include /* for size_t */ 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | /* Structure pointing to our actually configured allocators */ 42 | typedef struct hiredisAllocFuncs { 43 | void *(*mallocFn)(size_t); 44 | void *(*callocFn)(size_t,size_t); 45 | void *(*reallocFn)(void*,size_t); 46 | char *(*strdupFn)(const char*); 47 | void (*freeFn)(void*); 48 | } hiredisAllocFuncs; 49 | 50 | hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); 51 | void hiredisResetAllocators(void); 52 | 53 | #ifndef _WIN32 54 | 55 | /* Hiredis' configured allocator function pointer struct */ 56 | extern hiredisAllocFuncs hiredisAllocFns; 57 | 58 | static inline void *hi_malloc(size_t size) { 59 | return hiredisAllocFns.mallocFn(size); 60 | } 61 | 62 | static inline void *hi_calloc(size_t nmemb, size_t size) { 63 | /* Overflow check as the user can specify any arbitrary allocator */ 64 | if (SIZE_MAX / size < nmemb) 65 | return NULL; 66 | 67 | return hiredisAllocFns.callocFn(nmemb, size); 68 | } 69 | 70 | static inline void *hi_realloc(void *ptr, size_t size) { 71 | return hiredisAllocFns.reallocFn(ptr, size); 72 | } 73 | 74 | static inline char *hi_strdup(const char *str) { 75 | return hiredisAllocFns.strdupFn(str); 76 | } 77 | 78 | static inline void hi_free(void *ptr) { 79 | hiredisAllocFns.freeFn(ptr); 80 | } 81 | 82 | #else 83 | 84 | void *hi_malloc(size_t size); 85 | void *hi_calloc(size_t nmemb, size_t size); 86 | void *hi_realloc(void *ptr, size_t size); 87 | char *hi_strdup(const char *str); 88 | void hi_free(void *ptr); 89 | 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif /* HIREDIS_ALLOC_H */ 97 | -------------------------------------------------------------------------------- /deps/hiredis/appveyor.yml: -------------------------------------------------------------------------------- 1 | # Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) 2 | environment: 3 | matrix: 4 | - CYG_BASH: C:\cygwin64\bin\bash 5 | CC: gcc 6 | - CYG_BASH: C:\cygwin\bin\bash 7 | CC: gcc 8 | CFLAGS: -m32 9 | CXXFLAGS: -m32 10 | LDFLAGS: -m32 11 | 12 | clone_depth: 1 13 | 14 | # Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail 15 | init: 16 | - git config --global core.autocrlf input 17 | 18 | # Install needed build dependencies 19 | install: 20 | - '%CYG_BASH% -lc "cygcheck -dc cygwin"' 21 | 22 | build_script: 23 | - 'echo building...' 24 | - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 3 | * Copyright (c) 2010-2011, Pieter Noordhuis 4 | * 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * * Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * * Neither the name of Redis nor the names of its contributors may be used 16 | * to endorse or promote products derived from this software without 17 | * specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 23 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | * POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __HIREDIS_ASYNC_PRIVATE_H 33 | #define __HIREDIS_ASYNC_PRIVATE_H 34 | 35 | #define _EL_ADD_READ(ctx) \ 36 | do { \ 37 | refreshTimeout(ctx); \ 38 | if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ 39 | } while (0) 40 | #define _EL_DEL_READ(ctx) do { \ 41 | if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ 42 | } while(0) 43 | #define _EL_ADD_WRITE(ctx) \ 44 | do { \ 45 | refreshTimeout(ctx); \ 46 | if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ 47 | } while (0) 48 | #define _EL_DEL_WRITE(ctx) do { \ 49 | if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ 50 | } while(0) 51 | #define _EL_CLEANUP(ctx) do { \ 52 | if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ 53 | ctx->ev.cleanup = NULL; \ 54 | } while(0) 55 | 56 | static inline void refreshTimeout(redisAsyncContext *ctx) { 57 | #define REDIS_TIMER_ISSET(tvp) \ 58 | (tvp && ((tvp)->tv_sec || (tvp)->tv_usec)) 59 | 60 | #define REDIS_EL_TIMER(ac, tvp) \ 61 | if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \ 62 | (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \ 63 | } 64 | 65 | if (ctx->c.flags & REDIS_CONNECTED) { 66 | REDIS_EL_TIMER(ctx, ctx->c.command_timeout); 67 | } else { 68 | REDIS_EL_TIMER(ctx, ctx->c.connect_timeout); 69 | } 70 | } 71 | 72 | void __redisAsyncDisconnect(redisAsyncContext *ac); 73 | void redisProcessCallbacks(redisAsyncContext *ac); 74 | 75 | #endif /* __HIREDIS_ASYNC_PRIVATE_H */ 76 | -------------------------------------------------------------------------------- /deps/hiredis/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | # Check for GLib 3 | 4 | PKG_CHECK_MODULES(GLIB2 glib-2.0) 5 | if (GLIB2_FOUND) 6 | INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS}) 7 | LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS}) 8 | ADD_EXECUTABLE(example-glib example-glib.c) 9 | TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES}) 10 | ENDIF(GLIB2_FOUND) 11 | 12 | FIND_PATH(LIBEV ev.h 13 | HINTS /usr/local /usr/opt/local 14 | ENV LIBEV_INCLUDE_DIR) 15 | 16 | if (LIBEV) 17 | # Just compile and link with libev 18 | ADD_EXECUTABLE(example-libev example-libev.c) 19 | TARGET_LINK_LIBRARIES(example-libev hiredis ev) 20 | ENDIF() 21 | 22 | FIND_PATH(LIBEVENT event.h) 23 | if (LIBEVENT) 24 | ADD_EXECUTABLE(example-libevent example-libevent.c) 25 | TARGET_LINK_LIBRARIES(example-libevent hiredis event) 26 | ENDIF() 27 | 28 | FIND_PATH(LIBHV hv/hv.h) 29 | IF (LIBHV) 30 | ADD_EXECUTABLE(example-libhv example-libhv.c) 31 | TARGET_LINK_LIBRARIES(example-libhv hiredis hv) 32 | ENDIF() 33 | 34 | FIND_PATH(LIBUV uv.h) 35 | IF (LIBUV) 36 | ADD_EXECUTABLE(example-libuv example-libuv.c) 37 | TARGET_LINK_LIBRARIES(example-libuv hiredis uv) 38 | ENDIF() 39 | 40 | FIND_PATH(LIBSDEVENT systemd/sd-event.h) 41 | IF (LIBSDEVENT) 42 | ADD_EXECUTABLE(example-libsdevent example-libsdevent.c) 43 | TARGET_LINK_LIBRARIES(example-libsdevent hiredis systemd) 44 | ENDIF() 45 | 46 | IF (APPLE) 47 | FIND_LIBRARY(CF CoreFoundation) 48 | ADD_EXECUTABLE(example-macosx example-macosx.c) 49 | TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF}) 50 | ENDIF() 51 | 52 | IF (ENABLE_SSL) 53 | ADD_EXECUTABLE(example-ssl example-ssl.c) 54 | TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl) 55 | ENDIF() 56 | 57 | ADD_EXECUTABLE(example example.c) 58 | TARGET_LINK_LIBRARIES(example hiredis) 59 | 60 | ADD_EXECUTABLE(example-push example-push.c) 61 | TARGET_LINK_LIBRARIES(example-push hiredis) 62 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-ae.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* Put event loop in the global scope, so it can be explicitly stopped */ 11 | static aeEventLoop *loop; 12 | 13 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 14 | redisReply *reply = r; 15 | if (reply == NULL) return; 16 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 17 | 18 | /* Disconnect after receiving the reply to GET */ 19 | redisAsyncDisconnect(c); 20 | } 21 | 22 | void connectCallback(const redisAsyncContext *c, int status) { 23 | if (status != REDIS_OK) { 24 | printf("Error: %s\n", c->errstr); 25 | aeStop(loop); 26 | return; 27 | } 28 | 29 | printf("Connected...\n"); 30 | } 31 | 32 | void disconnectCallback(const redisAsyncContext *c, int status) { 33 | if (status != REDIS_OK) { 34 | printf("Error: %s\n", c->errstr); 35 | aeStop(loop); 36 | return; 37 | } 38 | 39 | printf("Disconnected...\n"); 40 | aeStop(loop); 41 | } 42 | 43 | int main (int argc, char **argv) { 44 | signal(SIGPIPE, SIG_IGN); 45 | 46 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 47 | if (c->err) { 48 | /* Let *c leak for now... */ 49 | printf("Error: %s\n", c->errstr); 50 | return 1; 51 | } 52 | 53 | loop = aeCreateEventLoop(64); 54 | redisAeAttach(loop, c); 55 | redisAsyncSetConnectCallback(c,connectCallback); 56 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 57 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 58 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 59 | aeMain(loop); 60 | return 0; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-glib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static GMainLoop *mainloop; 8 | 9 | static void 10 | connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, 11 | int status) 12 | { 13 | if (status != REDIS_OK) { 14 | g_printerr("Failed to connect: %s\n", ac->errstr); 15 | g_main_loop_quit(mainloop); 16 | } else { 17 | g_printerr("Connected...\n"); 18 | } 19 | } 20 | 21 | static void 22 | disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, 23 | int status) 24 | { 25 | if (status != REDIS_OK) { 26 | g_error("Failed to disconnect: %s", ac->errstr); 27 | } else { 28 | g_printerr("Disconnected...\n"); 29 | g_main_loop_quit(mainloop); 30 | } 31 | } 32 | 33 | static void 34 | command_cb(redisAsyncContext *ac, 35 | gpointer r, 36 | gpointer user_data G_GNUC_UNUSED) 37 | { 38 | redisReply *reply = r; 39 | 40 | if (reply) { 41 | g_print("REPLY: %s\n", reply->str); 42 | } 43 | 44 | redisAsyncDisconnect(ac); 45 | } 46 | 47 | gint 48 | main (gint argc G_GNUC_UNUSED, 49 | gchar *argv[] G_GNUC_UNUSED) 50 | { 51 | redisAsyncContext *ac; 52 | GMainContext *context = NULL; 53 | GSource *source; 54 | 55 | ac = redisAsyncConnect("127.0.0.1", 6379); 56 | if (ac->err) { 57 | g_printerr("%s\n", ac->errstr); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | source = redis_source_new(ac); 62 | mainloop = g_main_loop_new(context, FALSE); 63 | g_source_attach(source, context); 64 | 65 | redisAsyncSetConnectCallback(ac, connect_cb); 66 | redisAsyncSetDisconnectCallback(ac, disconnect_cb); 67 | redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); 68 | redisAsyncCommand(ac, command_cb, NULL, "GET key"); 69 | 70 | g_main_loop_run(mainloop); 71 | 72 | return EXIT_SUCCESS; 73 | } 74 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-ivykis.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | redisReply *reply = r; 12 | if (reply == NULL) return; 13 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 14 | 15 | /* Disconnect after receiving the reply to GET */ 16 | redisAsyncDisconnect(c); 17 | } 18 | 19 | void connectCallback(const redisAsyncContext *c, int status) { 20 | if (status != REDIS_OK) { 21 | printf("Error: %s\n", c->errstr); 22 | return; 23 | } 24 | printf("Connected...\n"); 25 | } 26 | 27 | void disconnectCallback(const redisAsyncContext *c, int status) { 28 | if (status != REDIS_OK) { 29 | printf("Error: %s\n", c->errstr); 30 | return; 31 | } 32 | printf("Disconnected...\n"); 33 | } 34 | 35 | int main (int argc, char **argv) { 36 | #ifndef _WIN32 37 | signal(SIGPIPE, SIG_IGN); 38 | #endif 39 | 40 | iv_init(); 41 | 42 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 43 | if (c->err) { 44 | /* Let *c leak for now... */ 45 | printf("Error: %s\n", c->errstr); 46 | return 1; 47 | } 48 | 49 | redisIvykisAttach(c); 50 | redisAsyncSetConnectCallback(c,connectCallback); 51 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 52 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 53 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 54 | 55 | iv_main(); 56 | 57 | iv_deinit(); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | redisReply *reply = r; 12 | if (reply == NULL) return; 13 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 14 | 15 | /* Disconnect after receiving the reply to GET */ 16 | redisAsyncDisconnect(c); 17 | } 18 | 19 | void connectCallback(const redisAsyncContext *c, int status) { 20 | if (status != REDIS_OK) { 21 | printf("Error: %s\n", c->errstr); 22 | return; 23 | } 24 | printf("Connected...\n"); 25 | } 26 | 27 | void disconnectCallback(const redisAsyncContext *c, int status) { 28 | if (status != REDIS_OK) { 29 | printf("Error: %s\n", c->errstr); 30 | return; 31 | } 32 | printf("Disconnected...\n"); 33 | } 34 | 35 | int main (int argc, char **argv) { 36 | #ifndef _WIN32 37 | signal(SIGPIPE, SIG_IGN); 38 | #endif 39 | 40 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 41 | if (c->err) { 42 | /* Let *c leak for now... */ 43 | printf("Error: %s\n", c->errstr); 44 | return 1; 45 | } 46 | 47 | redisLibevAttach(EV_DEFAULT_ c); 48 | redisAsyncSetConnectCallback(c,connectCallback); 49 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 50 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 51 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 52 | ev_loop(EV_DEFAULT_ 0); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libevent-ssl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 12 | redisReply *reply = r; 13 | if (reply == NULL) return; 14 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 15 | 16 | /* Disconnect after receiving the reply to GET */ 17 | redisAsyncDisconnect(c); 18 | } 19 | 20 | void connectCallback(const redisAsyncContext *c, int status) { 21 | if (status != REDIS_OK) { 22 | printf("Error: %s\n", c->errstr); 23 | return; 24 | } 25 | printf("Connected...\n"); 26 | } 27 | 28 | void disconnectCallback(const redisAsyncContext *c, int status) { 29 | if (status != REDIS_OK) { 30 | printf("Error: %s\n", c->errstr); 31 | return; 32 | } 33 | printf("Disconnected...\n"); 34 | } 35 | 36 | int main (int argc, char **argv) { 37 | #ifndef _WIN32 38 | signal(SIGPIPE, SIG_IGN); 39 | #endif 40 | 41 | struct event_base *base = event_base_new(); 42 | if (argc < 5) { 43 | fprintf(stderr, 44 | "Usage: %s [ca]\n", argv[0]); 45 | exit(1); 46 | } 47 | 48 | const char *value = argv[1]; 49 | size_t nvalue = strlen(value); 50 | 51 | const char *hostname = argv[2]; 52 | int port = atoi(argv[3]); 53 | 54 | const char *cert = argv[4]; 55 | const char *certKey = argv[5]; 56 | const char *caCert = argc > 5 ? argv[6] : NULL; 57 | 58 | redisSSLContext *ssl; 59 | redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE; 60 | 61 | redisInitOpenSSL(); 62 | 63 | ssl = redisCreateSSLContext(caCert, NULL, 64 | cert, certKey, NULL, &ssl_error); 65 | if (!ssl) { 66 | printf("Error: %s\n", redisSSLContextGetError(ssl_error)); 67 | return 1; 68 | } 69 | 70 | redisAsyncContext *c = redisAsyncConnect(hostname, port); 71 | if (c->err) { 72 | /* Let *c leak for now... */ 73 | printf("Error: %s\n", c->errstr); 74 | return 1; 75 | } 76 | if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) { 77 | printf("SSL Error!\n"); 78 | exit(1); 79 | } 80 | 81 | redisLibeventAttach(c,base); 82 | redisAsyncSetConnectCallback(c,connectCallback); 83 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 84 | redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue); 85 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 86 | event_base_dispatch(base); 87 | 88 | redisFreeSSLContext(ssl); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libevent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | redisReply *reply = r; 12 | if (reply == NULL) { 13 | if (c->errstr) { 14 | printf("errstr: %s\n", c->errstr); 15 | } 16 | return; 17 | } 18 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 19 | 20 | /* Disconnect after receiving the reply to GET */ 21 | redisAsyncDisconnect(c); 22 | } 23 | 24 | void connectCallback(const redisAsyncContext *c, int status) { 25 | if (status != REDIS_OK) { 26 | printf("Error: %s\n", c->errstr); 27 | return; 28 | } 29 | printf("Connected...\n"); 30 | } 31 | 32 | void disconnectCallback(const redisAsyncContext *c, int status) { 33 | if (status != REDIS_OK) { 34 | printf("Error: %s\n", c->errstr); 35 | return; 36 | } 37 | printf("Disconnected...\n"); 38 | } 39 | 40 | int main (int argc, char **argv) { 41 | #ifndef _WIN32 42 | signal(SIGPIPE, SIG_IGN); 43 | #endif 44 | 45 | struct event_base *base = event_base_new(); 46 | redisOptions options = {0}; 47 | REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); 48 | struct timeval tv = {0}; 49 | tv.tv_sec = 1; 50 | options.connect_timeout = &tv; 51 | 52 | 53 | redisAsyncContext *c = redisAsyncConnectWithOptions(&options); 54 | if (c->err) { 55 | /* Let *c leak for now... */ 56 | printf("Error: %s\n", c->errstr); 57 | return 1; 58 | } 59 | 60 | redisLibeventAttach(c,base); 61 | redisAsyncSetConnectCallback(c,connectCallback); 62 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 63 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 64 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 65 | event_base_dispatch(base); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libhv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | redisReply *reply = r; 12 | if (reply == NULL) return; 13 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 14 | 15 | /* Disconnect after receiving the reply to GET */ 16 | redisAsyncDisconnect(c); 17 | } 18 | 19 | void debugCallback(redisAsyncContext *c, void *r, void *privdata) { 20 | (void)privdata; 21 | redisReply *reply = r; 22 | 23 | if (reply == NULL) { 24 | printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error"); 25 | return; 26 | } 27 | 28 | redisAsyncDisconnect(c); 29 | } 30 | 31 | void connectCallback(const redisAsyncContext *c, int status) { 32 | if (status != REDIS_OK) { 33 | printf("Error: %s\n", c->errstr); 34 | return; 35 | } 36 | printf("Connected...\n"); 37 | } 38 | 39 | void disconnectCallback(const redisAsyncContext *c, int status) { 40 | if (status != REDIS_OK) { 41 | printf("Error: %s\n", c->errstr); 42 | return; 43 | } 44 | printf("Disconnected...\n"); 45 | } 46 | 47 | int main (int argc, char **argv) { 48 | #ifndef _WIN32 49 | signal(SIGPIPE, SIG_IGN); 50 | #endif 51 | 52 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 53 | if (c->err) { 54 | /* Let *c leak for now... */ 55 | printf("Error: %s\n", c->errstr); 56 | return 1; 57 | } 58 | 59 | hloop_t* loop = hloop_new(HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS); 60 | redisLibhvAttach(c, loop); 61 | redisAsyncSetTimeout(c, (struct timeval){.tv_sec = 0, .tv_usec = 500000}); 62 | redisAsyncSetConnectCallback(c,connectCallback); 63 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 64 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 65 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 66 | redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %d", 1); 67 | hloop_run(loop); 68 | hloop_free(&loop); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libsdevent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void debugCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | (void)privdata; 12 | redisReply *reply = r; 13 | if (reply == NULL) { 14 | /* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */ 15 | printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error"); 16 | return; 17 | } 18 | /* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/ 19 | redisAsyncDisconnect(c); 20 | } 21 | 22 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 23 | redisReply *reply = r; 24 | if (reply == NULL) { 25 | printf("`GET key` error: %s\n", c->errstr ? c->errstr : "unknown error"); 26 | return; 27 | } 28 | printf("`GET key` result: argv[%s]: %s\n", (char*)privdata, reply->str); 29 | 30 | /* start another request that demonstrate timeout */ 31 | redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5); 32 | } 33 | 34 | void connectCallback(const redisAsyncContext *c, int status) { 35 | if (status != REDIS_OK) { 36 | printf("connect error: %s\n", c->errstr); 37 | return; 38 | } 39 | printf("Connected...\n"); 40 | } 41 | 42 | void disconnectCallback(const redisAsyncContext *c, int status) { 43 | if (status != REDIS_OK) { 44 | printf("disconnect because of error: %s\n", c->errstr); 45 | return; 46 | } 47 | printf("Disconnected...\n"); 48 | } 49 | 50 | int main (int argc, char **argv) { 51 | signal(SIGPIPE, SIG_IGN); 52 | 53 | struct sd_event *event; 54 | sd_event_default(&event); 55 | 56 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 57 | if (c->err) { 58 | printf("Error: %s\n", c->errstr); 59 | redisAsyncFree(c); 60 | return 1; 61 | } 62 | 63 | redisLibsdeventAttach(c,event); 64 | redisAsyncSetConnectCallback(c,connectCallback); 65 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 66 | redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0}); 67 | 68 | /* 69 | In this demo, we first `set key`, then `get key` to demonstrate the basic usage of libsdevent adapter. 70 | Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request. 71 | Because we have set a 1 second timeout to the connection, the command will always fail with a 72 | timeout error, which is shown in the `debugCallback`. 73 | */ 74 | 75 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 76 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 77 | 78 | /* sd-event does not quit when there are no handlers registered. Manually exit after 1.5 seconds */ 79 | sd_event_source *s; 80 | sd_event_add_time_relative(event, &s, CLOCK_MONOTONIC, 1500000, 1, NULL, NULL); 81 | 82 | sd_event_loop(event); 83 | sd_event_source_disable_unref(s); 84 | sd_event_unref(event); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-libuv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void debugCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | (void)privdata; //unused 12 | redisReply *reply = r; 13 | if (reply == NULL) { 14 | /* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */ 15 | printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error"); 16 | return; 17 | } 18 | /* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/ 19 | redisAsyncDisconnect(c); 20 | } 21 | 22 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 23 | redisReply *reply = r; 24 | if (reply == NULL) { 25 | printf("`GET key` error: %s\n", c->errstr ? c->errstr : "unknown error"); 26 | return; 27 | } 28 | printf("`GET key` result: argv[%s]: %s\n", (char*)privdata, reply->str); 29 | 30 | /* start another request that demonstrate timeout */ 31 | redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5); 32 | } 33 | 34 | void connectCallback(const redisAsyncContext *c, int status) { 35 | if (status != REDIS_OK) { 36 | printf("connect error: %s\n", c->errstr); 37 | return; 38 | } 39 | printf("Connected...\n"); 40 | } 41 | 42 | void disconnectCallback(const redisAsyncContext *c, int status) { 43 | if (status != REDIS_OK) { 44 | printf("disconnect because of error: %s\n", c->errstr); 45 | return; 46 | } 47 | printf("Disconnected...\n"); 48 | } 49 | 50 | int main (int argc, char **argv) { 51 | #ifndef _WIN32 52 | signal(SIGPIPE, SIG_IGN); 53 | #endif 54 | 55 | uv_loop_t* loop = uv_default_loop(); 56 | 57 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 58 | if (c->err) { 59 | /* Let *c leak for now... */ 60 | printf("Error: %s\n", c->errstr); 61 | return 1; 62 | } 63 | 64 | redisLibuvAttach(c,loop); 65 | redisAsyncSetConnectCallback(c,connectCallback); 66 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 67 | redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0}); 68 | 69 | /* 70 | In this demo, we first `set key`, then `get key` to demonstrate the basic usage of libuv adapter. 71 | Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request. 72 | Because we have set a 1 second timeout to the connection, the command will always fail with a 73 | timeout error, which is shown in the `debugCallback`. 74 | */ 75 | 76 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 77 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 78 | 79 | uv_run(loop, UV_RUN_DEFAULT); 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-macosx.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Дмитрий Бахвалов on 13.07.15. 3 | // Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. 4 | // 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 13 | redisReply *reply = r; 14 | if (reply == NULL) return; 15 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 16 | 17 | /* Disconnect after receiving the reply to GET */ 18 | redisAsyncDisconnect(c); 19 | } 20 | 21 | void connectCallback(const redisAsyncContext *c, int status) { 22 | if (status != REDIS_OK) { 23 | printf("Error: %s\n", c->errstr); 24 | return; 25 | } 26 | printf("Connected...\n"); 27 | } 28 | 29 | void disconnectCallback(const redisAsyncContext *c, int status) { 30 | if (status != REDIS_OK) { 31 | printf("Error: %s\n", c->errstr); 32 | return; 33 | } 34 | CFRunLoopStop(CFRunLoopGetCurrent()); 35 | printf("Disconnected...\n"); 36 | } 37 | 38 | int main (int argc, char **argv) { 39 | signal(SIGPIPE, SIG_IGN); 40 | 41 | CFRunLoopRef loop = CFRunLoopGetCurrent(); 42 | if( !loop ) { 43 | printf("Error: Cannot get current run loop\n"); 44 | return 1; 45 | } 46 | 47 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 48 | if (c->err) { 49 | /* Let *c leak for now... */ 50 | printf("Error: %s\n", c->errstr); 51 | return 1; 52 | } 53 | 54 | redisMacOSAttach(c, loop); 55 | 56 | redisAsyncSetConnectCallback(c,connectCallback); 57 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 58 | 59 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 60 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 61 | 62 | CFRunLoopRun(); 63 | 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-poll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | /* Put in the global scope, so that loop can be explicitly stopped */ 11 | static int exit_loop = 0; 12 | 13 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 14 | redisReply *reply = r; 15 | if (reply == NULL) return; 16 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 17 | 18 | /* Disconnect after receiving the reply to GET */ 19 | redisAsyncDisconnect(c); 20 | } 21 | 22 | void connectCallback(const redisAsyncContext *c, int status) { 23 | if (status != REDIS_OK) { 24 | printf("Error: %s\n", c->errstr); 25 | exit_loop = 1; 26 | return; 27 | } 28 | 29 | printf("Connected...\n"); 30 | } 31 | 32 | void disconnectCallback(const redisAsyncContext *c, int status) { 33 | exit_loop = 1; 34 | if (status != REDIS_OK) { 35 | printf("Error: %s\n", c->errstr); 36 | return; 37 | } 38 | 39 | printf("Disconnected...\n"); 40 | } 41 | 42 | int main (int argc, char **argv) { 43 | signal(SIGPIPE, SIG_IGN); 44 | 45 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 46 | if (c->err) { 47 | /* Let *c leak for now... */ 48 | printf("Error: %s\n", c->errstr); 49 | return 1; 50 | } 51 | 52 | redisPollAttach(c); 53 | redisAsyncSetConnectCallback(c,connectCallback); 54 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 55 | redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); 56 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 57 | while (!exit_loop) 58 | { 59 | redisPollTick(c, 0.1); 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-qt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | #include 5 | #include 6 | 7 | #include "example-qt.h" 8 | 9 | void getCallback(redisAsyncContext *, void * r, void * privdata) { 10 | 11 | redisReply * reply = static_cast(r); 12 | ExampleQt * ex = static_cast(privdata); 13 | if (reply == nullptr || ex == nullptr) return; 14 | 15 | cout << "key: " << reply->str << endl; 16 | 17 | ex->finish(); 18 | } 19 | 20 | void ExampleQt::run() { 21 | 22 | m_ctx = redisAsyncConnect("localhost", 6379); 23 | 24 | if (m_ctx->err) { 25 | cerr << "Error: " << m_ctx->errstr << endl; 26 | redisAsyncFree(m_ctx); 27 | emit finished(); 28 | } 29 | 30 | m_adapter.setContext(m_ctx); 31 | 32 | redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); 33 | redisAsyncCommand(m_ctx, getCallback, this, "GET key"); 34 | } 35 | 36 | int main (int argc, char **argv) { 37 | 38 | QCoreApplication app(argc, argv); 39 | 40 | ExampleQt example(argv[argc-1]); 41 | 42 | QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); 43 | QTimer::singleShot(0, &example, SLOT(run())); 44 | 45 | return app.exec(); 46 | } 47 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-qt.h: -------------------------------------------------------------------------------- 1 | #ifndef __HIREDIS_EXAMPLE_QT_H 2 | #define __HIREDIS_EXAMPLE_QT_H 3 | 4 | #include 5 | 6 | class ExampleQt : public QObject { 7 | 8 | Q_OBJECT 9 | 10 | public: 11 | ExampleQt(const char * value, QObject * parent = 0) 12 | : QObject(parent), m_value(value) {} 13 | 14 | signals: 15 | void finished(); 16 | 17 | public slots: 18 | void run(); 19 | 20 | private: 21 | void finish() { emit finished(); } 22 | 23 | private: 24 | const char * m_value; 25 | redisAsyncContext * m_ctx; 26 | RedisQtAdapter m_adapter; 27 | 28 | friend 29 | void getCallback(redisAsyncContext *, void *, void *); 30 | }; 31 | 32 | #endif /* !__HIREDIS_EXAMPLE_QT_H */ 33 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-redismoduleapi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void debugCallback(redisAsyncContext *c, void *r, void *privdata) { 11 | (void)privdata; //unused 12 | redisReply *reply = r; 13 | if (reply == NULL) { 14 | /* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */ 15 | printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error"); 16 | return; 17 | } 18 | /* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/ 19 | redisAsyncDisconnect(c); 20 | } 21 | 22 | void getCallback(redisAsyncContext *c, void *r, void *privdata) { 23 | redisReply *reply = r; 24 | if (reply == NULL) { 25 | if (c->errstr) { 26 | printf("errstr: %s\n", c->errstr); 27 | } 28 | return; 29 | } 30 | printf("argv[%s]: %s\n", (char*)privdata, reply->str); 31 | 32 | /* start another request that demonstrate timeout */ 33 | redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5); 34 | } 35 | 36 | void connectCallback(const redisAsyncContext *c, int status) { 37 | if (status != REDIS_OK) { 38 | printf("Error: %s\n", c->errstr); 39 | return; 40 | } 41 | printf("Connected...\n"); 42 | } 43 | 44 | void disconnectCallback(const redisAsyncContext *c, int status) { 45 | if (status != REDIS_OK) { 46 | printf("Error: %s\n", c->errstr); 47 | return; 48 | } 49 | printf("Disconnected...\n"); 50 | } 51 | 52 | /* 53 | * This example requires Redis 7.0 or above. 54 | * 55 | * 1- Compile this file as a shared library. Directory of "redismodule.h" must 56 | * be in the include path. 57 | * gcc -fPIC -shared -I../../redis/src/ -I.. example-redismoduleapi.c -o example-redismoduleapi.so 58 | * 59 | * 2- Load module: 60 | * redis-server --loadmodule ./example-redismoduleapi.so value 61 | */ 62 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 63 | 64 | int ret = RedisModule_Init(ctx, "example-redismoduleapi", 1, REDISMODULE_APIVER_1); 65 | if (ret != REDISMODULE_OK) { 66 | printf("error module init \n"); 67 | return REDISMODULE_ERR; 68 | } 69 | 70 | if (redisModuleCompatibilityCheck() != REDIS_OK) { 71 | printf("Redis 7.0 or above is required! \n"); 72 | return REDISMODULE_ERR; 73 | } 74 | 75 | redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); 76 | if (c->err) { 77 | /* Let *c leak for now... */ 78 | printf("Error: %s\n", c->errstr); 79 | return 1; 80 | } 81 | 82 | size_t len; 83 | const char *val = RedisModule_StringPtrLen(argv[argc-1], &len); 84 | 85 | RedisModuleCtx *module_ctx = RedisModule_GetDetachedThreadSafeContext(ctx); 86 | redisModuleAttach(c, module_ctx); 87 | redisAsyncSetConnectCallback(c,connectCallback); 88 | redisAsyncSetDisconnectCallback(c,disconnectCallback); 89 | redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0}); 90 | 91 | /* 92 | In this demo, we first `set key`, then `get key` to demonstrate the basic usage of the adapter. 93 | Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request. 94 | Because we have set a 1 second timeout to the connection, the command will always fail with a 95 | timeout error, which is shown in the `debugCallback`. 96 | */ 97 | 98 | redisAsyncCommand(c, NULL, NULL, "SET key %b", val, len); 99 | redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /deps/hiredis/examples/example-ssl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #ifdef _MSC_VER 9 | #include /* For struct timeval */ 10 | #endif 11 | 12 | int main(int argc, char **argv) { 13 | unsigned int j; 14 | redisSSLContext *ssl; 15 | redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE; 16 | redisContext *c; 17 | redisReply *reply; 18 | if (argc < 4) { 19 | printf("Usage: %s [ca]\n", argv[0]); 20 | exit(1); 21 | } 22 | const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; 23 | int port = atoi(argv[2]); 24 | const char *cert = argv[3]; 25 | const char *key = argv[4]; 26 | const char *ca = argc > 4 ? argv[5] : NULL; 27 | 28 | redisInitOpenSSL(); 29 | ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error); 30 | if (!ssl || ssl_error != REDIS_SSL_CTX_NONE) { 31 | printf("SSL Context error: %s\n", redisSSLContextGetError(ssl_error)); 32 | exit(1); 33 | } 34 | 35 | struct timeval tv = { 1, 500000 }; // 1.5 seconds 36 | redisOptions options = {0}; 37 | REDIS_OPTIONS_SET_TCP(&options, hostname, port); 38 | options.connect_timeout = &tv; 39 | c = redisConnectWithOptions(&options); 40 | 41 | if (c == NULL || c->err) { 42 | if (c) { 43 | printf("Connection error: %s\n", c->errstr); 44 | redisFree(c); 45 | } else { 46 | printf("Connection error: can't allocate redis context\n"); 47 | } 48 | exit(1); 49 | } 50 | 51 | if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { 52 | printf("Couldn't initialize SSL!\n"); 53 | printf("Error: %s\n", c->errstr); 54 | redisFree(c); 55 | exit(1); 56 | } 57 | 58 | /* PING server */ 59 | reply = redisCommand(c,"PING"); 60 | printf("PING: %s\n", reply->str); 61 | freeReplyObject(reply); 62 | 63 | /* Set a key */ 64 | reply = redisCommand(c,"SET %s %s", "foo", "hello world"); 65 | printf("SET: %s\n", reply->str); 66 | freeReplyObject(reply); 67 | 68 | /* Set a key using binary safe API */ 69 | reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); 70 | printf("SET (binary API): %s\n", reply->str); 71 | freeReplyObject(reply); 72 | 73 | /* Try a GET and two INCR */ 74 | reply = redisCommand(c,"GET foo"); 75 | printf("GET foo: %s\n", reply->str); 76 | freeReplyObject(reply); 77 | 78 | reply = redisCommand(c,"INCR counter"); 79 | printf("INCR counter: %lld\n", reply->integer); 80 | freeReplyObject(reply); 81 | /* again ... */ 82 | reply = redisCommand(c,"INCR counter"); 83 | printf("INCR counter: %lld\n", reply->integer); 84 | freeReplyObject(reply); 85 | 86 | /* Create a list of numbers, from 0 to 9 */ 87 | reply = redisCommand(c,"DEL mylist"); 88 | freeReplyObject(reply); 89 | for (j = 0; j < 10; j++) { 90 | char buf[64]; 91 | 92 | snprintf(buf,64,"%u",j); 93 | reply = redisCommand(c,"LPUSH mylist element-%s", buf); 94 | freeReplyObject(reply); 95 | } 96 | 97 | /* Let's check what we have inside the list */ 98 | reply = redisCommand(c,"LRANGE mylist 0 -1"); 99 | if (reply->type == REDIS_REPLY_ARRAY) { 100 | for (j = 0; j < reply->elements; j++) { 101 | printf("%u) %s\n", j, reply->element[j]->str); 102 | } 103 | } 104 | freeReplyObject(reply); 105 | 106 | /* Disconnects and frees the context */ 107 | redisFree(c); 108 | 109 | redisFreeSSLContext(ssl); 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /deps/hiredis/fmacros.h: -------------------------------------------------------------------------------- 1 | #ifndef __HIREDIS_FMACRO_H 2 | #define __HIREDIS_FMACRO_H 3 | 4 | #ifndef _AIX 5 | #define _XOPEN_SOURCE 600 6 | #define _POSIX_C_SOURCE 200112L 7 | #endif 8 | 9 | #if defined(__APPLE__) && defined(__MACH__) 10 | /* Enable TCP_KEEPALIVE */ 11 | #define _DARWIN_C_SOURCE 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /deps/hiredis/fuzzing/format_command_fuzzer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020, Salvatore Sanfilippo 3 | * Copyright (c) 2020, Pieter Noordhuis 4 | * Copyright (c) 2020, Matt Stancliff , 5 | * Jan-Erik Rediger 6 | * 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * * Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of Redis nor the names of its contributors may be used 18 | * to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include "hiredis.h" 37 | 38 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 39 | char *new_str, *cmd; 40 | 41 | if (size < 3) 42 | return 0; 43 | 44 | new_str = malloc(size+1); 45 | if (new_str == NULL) 46 | return 0; 47 | 48 | memcpy(new_str, data, size); 49 | new_str[size] = '\0'; 50 | 51 | if (redisFormatCommand(&cmd, new_str) != -1) 52 | hi_free(cmd); 53 | 54 | free(new_str); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /deps/hiredis/hiredis-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") 4 | 5 | IF (NOT TARGET hiredis::@hiredis_export_name@) 6 | INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake) 7 | ENDIF() 8 | 9 | SET(hiredis_LIBRARIES hiredis::@hiredis_export_name@) 10 | SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR}) 11 | 12 | check_required_components(hiredis) 13 | 14 | -------------------------------------------------------------------------------- /deps/hiredis/hiredis.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | install_libdir=@CMAKE_INSTALL_LIBDIR@ 3 | exec_prefix=${prefix} 4 | libdir=${exec_prefix}/${install_libdir} 5 | includedir=${prefix}/include 6 | pkgincludedir=${includedir}/hiredis 7 | 8 | Name: hiredis 9 | Description: Minimalistic C client library for Redis. 10 | Version: @PROJECT_VERSION@ 11 | Libs: -L${libdir} -lhiredis 12 | Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64 13 | -------------------------------------------------------------------------------- /deps/hiredis/hiredis.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)\..\..\include;%(AdditionalIncludeDirectories) 6 | 7 | 8 | $(MSBuildThisFileDirectory)\..\..\lib;%(AdditionalLibraryDirectories) 9 | 10 | 11 | -------------------------------------------------------------------------------- /deps/hiredis/hiredis_ssl-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") 4 | 5 | include(CMakeFindDependencyMacro) 6 | find_dependency(OpenSSL) 7 | 8 | IF (NOT TARGET hiredis::hiredis_ssl) 9 | INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake) 10 | ENDIF() 11 | 12 | SET(hiredis_ssl_LIBRARIES hiredis::hiredis_ssl) 13 | SET(hiredis_ssl_INCLUDE_DIRS ${hiredis_ssl_INCLUDEDIR}) 14 | 15 | check_required_components(hiredis_ssl) 16 | 17 | -------------------------------------------------------------------------------- /deps/hiredis/hiredis_ssl.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | install_libdir=@CMAKE_INSTALL_LIBDIR@ 3 | exec_prefix=${prefix} 4 | libdir=${exec_prefix}/${install_libdir} 5 | includedir=${prefix}/include 6 | pkgincludedir=${includedir}/hiredis 7 | 8 | Name: hiredis_ssl 9 | Description: SSL Support for hiredis. 10 | Version: @PROJECT_VERSION@ 11 | Requires: hiredis 12 | Libs: -L${libdir} -lhiredis_ssl 13 | Libs.private: -lssl -lcrypto 14 | -------------------------------------------------------------------------------- /deps/hiredis/net.h: -------------------------------------------------------------------------------- 1 | /* Extracted from anet.c to work properly with Hiredis error reporting. 2 | * 3 | * Copyright (c) 2009-2011, Salvatore Sanfilippo 4 | * Copyright (c) 2010-2014, Pieter Noordhuis 5 | * Copyright (c) 2015, Matt Stancliff , 6 | * Jan-Erik Rediger 7 | * 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * * Neither the name of Redis nor the names of its contributors may be used 19 | * to endorse or promote products derived from this software without 20 | * specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef __NET_H 36 | #define __NET_H 37 | 38 | #include "hiredis.h" 39 | 40 | void redisNetClose(redisContext *c); 41 | ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); 42 | ssize_t redisNetWrite(redisContext *c); 43 | 44 | int redisCheckSocketError(redisContext *c); 45 | int redisContextSetTimeout(redisContext *c, const struct timeval tv); 46 | int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); 47 | int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, 48 | const struct timeval *timeout, 49 | const char *source_addr); 50 | int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); 51 | int redisKeepAlive(redisContext *c, int interval); 52 | int redisCheckConnectDone(redisContext *c, int *completed); 53 | 54 | int redisSetTcpNoDelay(redisContext *c); 55 | int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /deps/hiredis/sdsalloc.h: -------------------------------------------------------------------------------- 1 | /* SDSLib 2.0 -- A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2015, Salvatore Sanfilippo 4 | * Copyright (c) 2015, Oran Agra 5 | * Copyright (c) 2015, Redis Labs, Inc 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /* SDS allocator selection. 34 | * 35 | * This file is used in order to change the SDS allocator at compile time. 36 | * Just define the following defines to what you want to use. Also add 37 | * the include of your alternate allocator if needed (not needed in order 38 | * to use the default libc allocator). */ 39 | 40 | #include "alloc.h" 41 | 42 | #define s_malloc hi_malloc 43 | #define s_realloc hi_realloc 44 | #define s_free hi_free 45 | -------------------------------------------------------------------------------- /deps/hiredis/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ue 2 | 3 | REDIS_SERVER=${REDIS_SERVER:-redis-server} 4 | REDIS_PORT=${REDIS_PORT:-56379} 5 | REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443} 6 | TEST_SSL=${TEST_SSL:-0} 7 | SKIPS_AS_FAILS=${SKIPS_AS_FAILS:-0} 8 | ENABLE_DEBUG_CMD= 9 | SSL_TEST_ARGS= 10 | SKIPS_ARG=${SKIPS_ARG:-} 11 | REDIS_DOCKER=${REDIS_DOCKER:-} 12 | 13 | # We need to enable the DEBUG command for redis-server >= 7.0.0 14 | REDIS_MAJOR_VERSION="$(redis-server --version|awk -F'[^0-9]+' '{ print $2 }')" 15 | if [ "$REDIS_MAJOR_VERSION" -gt "6" ]; then 16 | ENABLE_DEBUG_CMD="enable-debug-command local" 17 | fi 18 | 19 | tmpdir=$(mktemp -d) 20 | PID_FILE=${tmpdir}/hiredis-test-redis.pid 21 | SOCK_FILE=${tmpdir}/hiredis-test-redis.sock 22 | 23 | if [ "$TEST_SSL" = "1" ]; then 24 | SSL_CA_CERT=${tmpdir}/ca.crt 25 | SSL_CA_KEY=${tmpdir}/ca.key 26 | SSL_CERT=${tmpdir}/redis.crt 27 | SSL_KEY=${tmpdir}/redis.key 28 | 29 | openssl genrsa -out ${tmpdir}/ca.key 4096 30 | openssl req \ 31 | -x509 -new -nodes -sha256 \ 32 | -key ${SSL_CA_KEY} \ 33 | -days 3650 \ 34 | -subj '/CN=Hiredis Test CA' \ 35 | -out ${SSL_CA_CERT} 36 | openssl genrsa -out ${SSL_KEY} 2048 37 | openssl req \ 38 | -new -sha256 \ 39 | -key ${SSL_KEY} \ 40 | -subj '/CN=Hiredis Test Cert' | \ 41 | openssl x509 \ 42 | -req -sha256 \ 43 | -CA ${SSL_CA_CERT} \ 44 | -CAkey ${SSL_CA_KEY} \ 45 | -CAserial ${tmpdir}/ca.txt \ 46 | -CAcreateserial \ 47 | -days 365 \ 48 | -out ${SSL_CERT} 49 | 50 | SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}" 51 | fi 52 | 53 | cleanup() { 54 | if [ -n "${REDIS_DOCKER}" ] ; then 55 | docker kill redis-test-server 56 | else 57 | set +e 58 | kill $(cat ${PID_FILE}) 59 | fi 60 | rm -rf ${tmpdir} 61 | } 62 | trap cleanup INT TERM EXIT 63 | 64 | # base config 65 | cat > ${tmpdir}/redis.conf <> ${tmpdir}/redis.conf <> ${tmpdir}/redis.conf < /* for struct timeval */ 6 | 7 | #ifndef inline 8 | #define inline __inline 9 | #endif 10 | 11 | #ifndef strcasecmp 12 | #define strcasecmp stricmp 13 | #endif 14 | 15 | #ifndef strncasecmp 16 | #define strncasecmp strnicmp 17 | #endif 18 | 19 | #ifndef va_copy 20 | #define va_copy(d,s) ((d) = (s)) 21 | #endif 22 | 23 | #ifndef snprintf 24 | #define snprintf c99_snprintf 25 | 26 | __inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) 27 | { 28 | int count = -1; 29 | 30 | if (size != 0) 31 | count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); 32 | if (count == -1) 33 | count = _vscprintf(format, ap); 34 | 35 | return count; 36 | } 37 | 38 | __inline int c99_snprintf(char* str, size_t size, const char* format, ...) 39 | { 40 | int count; 41 | va_list ap; 42 | 43 | va_start(ap, format); 44 | count = c99_vsnprintf(str, size, format, ap); 45 | va_end(ap); 46 | 47 | return count; 48 | } 49 | #endif 50 | #endif /* _MSC_VER */ 51 | 52 | #ifdef _WIN32 53 | #define strerror_r(errno,buf,len) strerror_s(buf,len,errno) 54 | #endif /* _WIN32 */ 55 | 56 | #endif /* _WIN32_HELPER_INCLUDE */ 57 | -------------------------------------------------------------------------------- /deps/raft/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run-tests: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Set up Python for testing 11 | uses: actions/setup-python@v1 12 | with: 13 | python-version: '3.9' 14 | architecture: 'x64' 15 | - name: Install Python dependencies 16 | run: 17 | python -m pip install -r tests/requirements.txt 18 | - name: Build 19 | run: make COVERAGE=1 20 | - name: Run tests 21 | run: | 22 | make tests 23 | make test_fuzzer 24 | make test_virtraft 25 | make gcov 26 | - name: Upload to codecov 27 | uses: codecov/codecov-action@v2 28 | -------------------------------------------------------------------------------- /deps/raft/.github/workflows/daily.yml: -------------------------------------------------------------------------------- 1 | name: Daily 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | run-tests: 10 | if: github.repository == 'redislabs/raft' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: make 15 | run: make 16 | - name: Set up Python for testing 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: '3.9' 20 | architecture: 'x64' 21 | - name: Install Python dependencies 22 | run: 23 | python -m pip install -r tests/requirements.txt 24 | - name: Run tests 25 | run: 26 | make tests_full 27 | - name: Upload to codecov 28 | uses: codecov/codecov-action@v2 29 | -------------------------------------------------------------------------------- /deps/raft/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.gcno 4 | *.gcda 5 | *.gcov 6 | *.dylib 7 | *~ 8 | libraft.a 9 | libraft.so 10 | .env/ 11 | .hypothesis/ 12 | **/__pycache__ 13 | cmake-build* 14 | .idea 15 | tests/.raft_cffi_built 16 | tests/raft_cffi.c 17 | build/ 18 | -------------------------------------------------------------------------------- /deps/raft/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | addons: 3 | apt: 4 | packages: 5 | - "python3" 6 | - "python3-pip" 7 | before_install: 8 | - sudo pip install cpp-coveralls 9 | - sudo pip3 install cffi 10 | - sudo pip3 install colorama 11 | - sudo pip3 install coloredlogs 12 | - sudo pip3 install docopt 13 | - sudo pip3 install terminaltables 14 | script: 15 | - make tests 16 | - make test_virtraft 17 | after_success: 18 | - coveralls --verbose --exclude include --exclude tests --exclude CLinkedListQueue 19 | -------------------------------------------------------------------------------- /deps/raft/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Willem-Hendrik Thiart 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of its contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL WILLEM-HENDRIK THIART BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /deps/raft/README.md: -------------------------------------------------------------------------------- 1 | [![Daily CI](https://github.com/RedisLabs/raft/actions/workflows/daily.yml/badge.svg)](https://github.com/RedisLabs/raft/actions/workflows/daily.yml) 2 | [![codecov](https://codecov.io/gh/RedisLabs/raft/branch/master/graph/badge.svg?token=66M19HJ7K9)](https://codecov.io/gh/RedisLabs/raft) 3 | 4 | A complete implementation of the [Raft Consensus Algorithm](https://raft.github.io) as a C library, licensed under BSD. 5 | 6 | This is a fork of the original library created by Willem-Hendrik Thiart, which is now actively maintained by Redis Ltd. and used as part of [RedisRaft](https://github.com/redislabs/redisraft). 7 | 8 | See [raft.h](https://github.com/redislabs/raft/blob/master/include/raft.h) for full API documentation. 9 | 10 | Main Features 11 | ============= 12 | 13 | The library implements all basic features of Raft as well as some extensions, including: 14 | 15 | * Leader election 16 | * Log replication and FSM interface 17 | * Compaction and snapshot support 18 | * Cluster membership changes 19 | * Quorum check and step-down by leader 20 | * Pre-vote 21 | * Leadership transfer 22 | 23 | The library is completely abstract and it is up to the caller to implement things like: 24 | * RPC and networking 25 | * Log storage and access 26 | * Generation of snapshots from FSM state 27 | 28 | Getting Started 29 | =============== 30 | 31 | To build and run basic tests: 32 | 33 | ``` 34 | make tests 35 | ``` 36 | 37 | If you prefer, you can also use cmake: 38 | 39 | ``` 40 | mkdir build 41 | cd build 42 | cmake .. 43 | make 44 | make test 45 | ``` 46 | 47 | Tests 48 | ===== 49 | 50 | The library uses the following testing methods: 51 | 52 | * A simulator (virtraft2) is used to test the Raft invariants on unreliable networks 53 | * All bugs have regression tests 54 | * Many unit tests 55 | 56 | virtraft2 57 | --------- 58 | 59 | This cluster simulator checks the following: 60 | 61 | * Log Matching (servers must have matching logs) 62 | * State Machine Safety (applied entries have the same ID) 63 | * Election Safety (only one valid leader per term) 64 | * Current Index Validity (does the current index have an existing entry?) 65 | * Entry ID Monotonicity (entries aren't appended out of order) 66 | * Committed entry popping (committed entries are not popped from the log) 67 | * Log Accuracy (does the server's log match mirror an independent log?) 68 | * Deadlock detection (does the cluster continuously make progress?) 69 | 70 | Chaos generated by virtraft2: 71 | 72 | * Random bi-directional partitions between nodes 73 | * Message dropping 74 | * Message duplication 75 | * Membership change injection 76 | * Random compactions 77 | 78 | Run the simulator using: 79 | 80 | ``` 81 | mkdir .env 82 | virtualenv .env 83 | source .env/bin/activate 84 | pip install -r tests/requirements.txt 85 | make test_virtraft 86 | ``` 87 | 88 | Requirements 89 | --------- 90 | * Python 3.9 or later -------------------------------------------------------------------------------- /deps/raft/bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /deps/raft/include/raft_log.h: -------------------------------------------------------------------------------- 1 | #ifndef RAFT_LOG_H_ 2 | #define RAFT_LOG_H_ 3 | 4 | #include "raft_types.h" 5 | 6 | /** Backward compatible callbacks for log events */ 7 | 8 | typedef struct { 9 | /** Callback for adding an entry to the log 10 | * For safety reasons this callback MUST flush the change to disk. 11 | * Return 0 on success. 12 | * Return RAFT_ERR_SHUTDOWN if you want the server to shutdown. */ 13 | 14 | raft_logentry_event_f log_offer; 15 | 16 | /** Callback for removing the oldest entry from the log 17 | * For safety reasons this callback MUST flush the change to disk. 18 | * @note The callback does not need to call raft_entry_release() as 19 | * no references are implicitly held. If access to the entry is 20 | * desired after the callback returns, raft_entry_hold() should be 21 | * used. 22 | */ 23 | raft_logentry_event_f log_poll; 24 | 25 | /** Callback for removing the youngest entry from the log 26 | * For safety reasons this callback MUST flush the change to disk. 27 | * @note The callback does not need to call raft_entry_release() as 28 | * no references are implicitly held. If access to the entry is 29 | * desired after the callback returns, raft_entry_hold() should be 30 | * used. 31 | */ 32 | raft_logentry_event_f log_pop; 33 | 34 | /** Callback called for every existing log entry when clearing the log. 35 | * If memory was malloc'd in log_offer and the entry doesn't get a chance 36 | * to go through log_poll or log_pop, this is the last chance to free it. 37 | */ 38 | raft_logentry_event_f log_clear; 39 | } raft_log_cbs_t; 40 | 41 | typedef struct raft_log raft_log_t; 42 | 43 | raft_log_t *raft_log_new(void); 44 | 45 | raft_log_t *raft_log_alloc(raft_index_t initial_size); 46 | 47 | void raft_log_set_callbacks(raft_log_t *me, raft_log_cbs_t *funcs, void *raft); 48 | 49 | void raft_log_free(raft_log_t *me); 50 | 51 | void raft_log_clear(raft_log_t *me); 52 | 53 | void raft_log_clear_entries(raft_log_t *me); 54 | 55 | /** 56 | * Add entry to log. 57 | * Don't add entry if we've already added this entry (based off ID) 58 | * Don't add entries with ID=0 59 | * @return 0 if unsuccessful; 1 otherwise */ 60 | int raft_log_append_entry(raft_log_t *me, raft_entry_t *c); 61 | 62 | /** 63 | * @return number of entries held within log */ 64 | raft_index_t raft_log_count(raft_log_t *me); 65 | 66 | /** 67 | * Delete all logs from this log onwards */ 68 | int raft_log_delete(raft_log_t *me, raft_index_t idx); 69 | 70 | /** 71 | * Empty the queue. */ 72 | void raft_log_empty(raft_log_t *me); 73 | 74 | /** 75 | * Remove oldest entry. Set *etyp to oldest entry on success. */ 76 | int raft_log_poll(raft_log_t *me, raft_entry_t **etyp); 77 | 78 | /** Get an array of entries from this index onwards. 79 | * This is used for batching. 80 | */ 81 | raft_entry_t **raft_log_get_from_idx(raft_log_t *me, 82 | raft_index_t idx, 83 | raft_index_t *n_etys); 84 | 85 | raft_entry_t *raft_log_get_at_idx(raft_log_t *me, raft_index_t idx); 86 | 87 | /** 88 | * @return youngest entry */ 89 | raft_entry_t *raft_log_peektail(raft_log_t *me); 90 | 91 | raft_index_t raft_log_get_current_idx(raft_log_t *me); 92 | 93 | int raft_log_load_from_snapshot(raft_log_t *me, 94 | raft_index_t idx, 95 | raft_term_t term); 96 | 97 | raft_index_t raft_log_get_base(raft_log_t *me); 98 | 99 | #endif /* RAFT_LOG_H_ */ 100 | -------------------------------------------------------------------------------- /deps/raft/include/raft_types.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RAFT_DEFS_H_ 3 | #define RAFT_DEFS_H_ 4 | 5 | /** 6 | * Unique entry ids are mostly used for debugging and nothing else, 7 | * so there is little harm if they collide. 8 | */ 9 | typedef int raft_entry_id_t; 10 | 11 | /** 12 | * Monotonic term counter. 13 | */ 14 | typedef long int raft_term_t; 15 | 16 | /** 17 | * Monotonic log entry index. 18 | * 19 | * This is also used to as an entry count size type. 20 | */ 21 | typedef long int raft_index_t; 22 | 23 | /** 24 | * Id used to group entries into sessions 25 | */ 26 | typedef unsigned long long raft_session_t; 27 | 28 | /** 29 | * Size type. This should be at least 64 bits. 30 | */ 31 | typedef unsigned long long raft_size_t; 32 | 33 | /** 34 | * Unique node identifier. 35 | */ 36 | typedef int raft_node_id_t; 37 | 38 | /** 39 | * Unique message identifier. 40 | */ 41 | typedef unsigned long raft_msg_id_t; 42 | 43 | /** 44 | * Time type. 45 | */ 46 | typedef long long raft_time_t; 47 | 48 | #endif /* RAFT_DEFS_H_ */ 49 | -------------------------------------------------------------------------------- /deps/raft/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raft", 3 | "version": "0.2.0", 4 | "repo": "willemt/raft", 5 | "description": "C implementation of the Raft Consensus protocol, BSD licensed", 6 | "keywords": ["raft", "consensus", "protocol"], 7 | "src": [ 8 | "include/raft.h", 9 | "include/raft_log.h", 10 | "include/raft_private.h", 11 | "src/raft_log.c", 12 | "src/raft_node.c", 13 | "src/raft_server.c", 14 | "src/raft_server_properties.c" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /deps/raft/scripts/amalgamate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create amalgamated source file, prints to stdout 4 | 5 | echo '/* 6 | 7 | This source file is the amalgamated version of the original. 8 | Please see github.com/willemt/raft for the original version. 9 | ' 10 | git log | head -n1 | sed 's/commit/HEAD commit:/g' 11 | echo ' 12 | ' 13 | cat LICENSE 14 | echo ' 15 | */ 16 | ' 17 | 18 | echo ' 19 | #ifndef RAFT_AMALGAMATION_SH 20 | #define RAFT_AMALGAMATION_SH 21 | ' 22 | 23 | cat include/raft.h 24 | cat include/raft_*.h 25 | cat src/raft*.c | sed 's/#include "raft.*.h"//g' | sed 's/__/__raft__/g' 26 | 27 | echo '#endif /* RAFT_AMALGAMATIONE_SH */' 28 | -------------------------------------------------------------------------------- /deps/raft/tests/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef _HELPERS_H 2 | #define _HELPERS_H 3 | 4 | static raft_entry_t *__MAKE_ENTRY(int id, raft_term_t term, const char *data) 5 | { 6 | raft_entry_t *ety = raft_entry_new(data ? strlen(data) : 0); 7 | ety->id = id; 8 | ety->term = term; 9 | if (data) { 10 | memcpy(ety->data, data, strlen(data)); 11 | } 12 | return ety; 13 | } 14 | 15 | static raft_entry_t **__MAKE_ENTRY_ARRAY(int id, raft_term_t term, const char *data) 16 | { 17 | raft_entry_t **array = calloc(1, sizeof(raft_entry_t *)); 18 | array[0] = __MAKE_ENTRY(id, term, data); 19 | 20 | return array; 21 | } 22 | 23 | static raft_entry_t **__MAKE_ENTRY_ARRAY_SEQ_ID(int count, int start_id, raft_term_t term, const char *data) 24 | { 25 | raft_entry_t **array = calloc(count, sizeof(raft_entry_t *)); 26 | int i; 27 | 28 | for (i = 0; i < count; i++) { 29 | array[i] = __MAKE_ENTRY(start_id++, term, data); 30 | } 31 | 32 | return array; 33 | } 34 | 35 | static void __RAFT_APPEND_ENTRY(void *r, int id, raft_term_t term, const char *data) 36 | { 37 | raft_entry_t *e = __MAKE_ENTRY(id, term, data); 38 | raft_append_entry(r, e); 39 | raft_node_set_match_idx(raft_get_my_node(r), raft_get_current_idx(r)); 40 | } 41 | 42 | static void __RAFT_APPEND_ENTRIES_SEQ_ID(void *r, int count, int id, raft_term_t term, const char *data) 43 | { 44 | int i; 45 | for (i = 0; i < count; i++) { 46 | raft_entry_t *e = __MAKE_ENTRY(id++, term, data); 47 | raft_append_entry(r, e); 48 | } 49 | } 50 | 51 | static void __RAFT_APPEND_ENTRIES_SEQ_ID_TERM(void *r, int count, int id, raft_term_t term, const char *data) 52 | { 53 | int i; 54 | for (i = 0; i < count; i++) { 55 | raft_entry_t *e = __MAKE_ENTRY(id++, term++, data); 56 | raft_append_entry(r, e); 57 | } 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /deps/raft/tests/linked_list_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef LINKED_LIST_QUEUE_H 2 | #define LINKED_LIST_QUEUE_H 3 | 4 | typedef struct llqnode_s llqnode_t; 5 | 6 | struct llqnode_s 7 | { 8 | llqnode_t *next; 9 | void *item; 10 | }; 11 | 12 | typedef struct 13 | { 14 | llqnode_t *head, *tail; 15 | int count; 16 | } linked_list_queue_t; 17 | 18 | void *llqueue_new( 19 | ); 20 | 21 | void llqueue_free( 22 | linked_list_queue_t * qu 23 | ); 24 | 25 | void *llqueue_poll( 26 | linked_list_queue_t * qu 27 | ); 28 | 29 | void llqueue_offer( 30 | linked_list_queue_t * qu, 31 | void *item 32 | ); 33 | 34 | /** 35 | * remove this item, by comparing the memory address of the item */ 36 | void *llqueue_remove_item( 37 | linked_list_queue_t * qu, 38 | const void *item 39 | ); 40 | 41 | int llqueue_count( 42 | const linked_list_queue_t * qu 43 | ); 44 | 45 | /** 46 | * remove this item, by using the supplied compare function */ 47 | void *llqueue_remove_item_via_cmpfunction( 48 | linked_list_queue_t * qu, 49 | const void *item, 50 | int (*cmp)(const void*, const void*)); 51 | 52 | /** 53 | * get this item, by using the supplied compare function */ 54 | void *llqueue_get_item_via_cmpfunction( 55 | linked_list_queue_t * qu, 56 | const void *item, 57 | long (*cmp)(const void*, const void*)); 58 | 59 | #endif /* LINKED_LIST_QUEUE_H */ 60 | -------------------------------------------------------------------------------- /deps/raft/tests/log_fuzzer.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import unittest 3 | 4 | import raft_cffi 5 | 6 | from hypothesis import given 7 | from hypothesis.strategies import lists, just, integers, one_of 8 | 9 | 10 | commands = one_of( 11 | just('append'), 12 | just('poll'), 13 | integers(min_value=1, max_value=10), 14 | ) 15 | 16 | 17 | class Log(object): 18 | def __init__(self): 19 | self.entries = [] 20 | self.base = 0 21 | 22 | def append(self, ety): 23 | self.entries.append(ety) 24 | 25 | def poll(self): 26 | self.base += 1 27 | return self.entries.pop(0) 28 | 29 | def delete(self, idx): 30 | idx -= 1 31 | if idx < self.base: 32 | idx = self.base 33 | idx = max(idx - self.base, 0) 34 | del self.entries[idx:] 35 | 36 | def count(self): 37 | return len(self.entries) 38 | 39 | 40 | class CoreTestCase(unittest.TestCase): 41 | def setUp(self): 42 | super(CoreTestCase, self).setUp() 43 | self.r = raft_cffi 44 | 45 | @given(lists(commands)) 46 | def test_sanity_check(self, commands): 47 | r = self.r.lib 48 | 49 | unique_id = 1 50 | l = r.raft_log_alloc(1) 51 | 52 | log = Log() 53 | 54 | for cmd in commands: 55 | if cmd == 'append': 56 | entry = r.raft_entry_new(0) 57 | entry.id = unique_id 58 | unique_id += 1 59 | 60 | ret = r.raft_log_append_entry(l, entry) 61 | assert ret == 0 62 | 63 | log.append(entry) 64 | 65 | elif cmd == 'poll': 66 | entry_ptr = self.r.ffi.new('raft_entry_t**') 67 | 68 | if log.entries: 69 | ret = r.raft_log_poll(l, entry_ptr) 70 | assert ret == 0 71 | 72 | ety_expected = log.poll() 73 | assert entry_ptr[0].id == ety_expected.id 74 | 75 | elif isinstance(cmd, int): 76 | if log.entries: 77 | log.delete(cmd) 78 | ret = r.raft_log_delete(l, cmd) 79 | assert ret == 0 80 | 81 | else: 82 | assert False 83 | 84 | self.assertEqual(r.raft_log_count(l), log.count()) 85 | 86 | 87 | if __name__ == '__main__': 88 | unittest.main() 89 | -------------------------------------------------------------------------------- /deps/raft/tests/mock_send_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_SEND_FUNCTIONS_H 2 | #define MOCK_SEND_FUNCTIONS_H 3 | 4 | typedef enum 5 | { 6 | RAFT_REQUESTVOTE_REQ, 7 | RAFT_REQUESTVOTE_RESP, 8 | RAFT_APPENDENTRIES_REQ, 9 | RAFT_APPENDENTRIES_RESP, 10 | RAFT_ENTRY_REQ, 11 | RAFT_ENTRY_RESP, 12 | } raft_message_type_e; 13 | 14 | void senders_new(); 15 | 16 | void* sender_new(void* address); 17 | 18 | void* sender_poll_msg_data(void* s); 19 | 20 | void sender_poll_msgs(void* s); 21 | 22 | int sender_msgs_available(void* s); 23 | 24 | void sender_set_raft(void* s, void* r); 25 | 26 | int sender_requestvote(raft_server_t* raft, 27 | void* udata, raft_node_t* node, raft_requestvote_req_t* msg); 28 | 29 | int sender_requestvote_response(raft_server_t* raft, 30 | void* udata, raft_node_t* node, raft_requestvote_resp_t* msg); 31 | 32 | int sender_appendentries(raft_server_t* raft, 33 | void* udata, raft_node_t* node, raft_appendentries_req_t* msg); 34 | 35 | int sender_appendentries_response(raft_server_t* raft, 36 | void* udata, raft_node_t* node, raft_appendentries_resp_t* msg); 37 | 38 | int sender_entries(raft_server_t* raft, 39 | void* udata, raft_node_t* node, raft_entry_req_t* msg); 40 | 41 | int sender_entries_response(raft_server_t* raft, 42 | void* udata, raft_node_t* node, raft_entry_resp_t* msg); 43 | 44 | #endif /* MOCK_SEND_FUNCTIONS_H */ 45 | -------------------------------------------------------------------------------- /deps/raft/tests/raft_cffi_builder.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import subprocess 4 | import cffi 5 | 6 | 7 | def load(fname): 8 | tmpfile = 'tests/raft_cffi_tmp.h' 9 | # Strip C standard library headers as cffi cannot parse them 10 | with open(fname, "r") as f: 11 | lines = f.readlines() 12 | with open(tmpfile, "w+") as f: 13 | for line in lines: 14 | if '#include data = data; 41 | e->refs = 100; 42 | return e; 43 | } 44 | void *raft_entry_getdata(raft_entry_t *ety) { 45 | return *(void **) ety->data; 46 | } 47 | raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len) { 48 | raft_entry_t **t = malloc(len * sizeof(raft_entry_t *)); 49 | int i; 50 | for (i = 0; i < len; i++) { 51 | int sz = sizeof(raft_entry_t) + src[i]->data_len; 52 | t[i] = malloc(sz); 53 | memcpy(t[i], src[i], sz); 54 | } 55 | return t; 56 | } 57 | """, 58 | libraries=[args.libname], 59 | include_dirs=[args.includedir], 60 | extra_compile_args=["-UNDEBUG"], 61 | extra_link_args=["-L{}".format(args.libdir)] 62 | ) 63 | 64 | ffibuilder.cdef('void *malloc(size_t __size);') 65 | ffibuilder.cdef(load('include/raft.h')) 66 | ffibuilder.cdef(load('include/raft_private.h')) 67 | ffibuilder.cdef(load('include/raft_log.h')) 68 | 69 | ffibuilder.cdef('raft_entry_t *raft_entry_newdata(void *data);') 70 | ffibuilder.cdef('void *raft_entry_getdata(raft_entry_t *);') 71 | ffibuilder.cdef('raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len);') 72 | 73 | ffibuilder.compile(tmpdir=args.tmpdir, verbose=True) 74 | -------------------------------------------------------------------------------- /deps/raft/tests/requirements.txt: -------------------------------------------------------------------------------- 1 | cffi==1.15.0 2 | colorama==0.4.3 3 | coloredlogs==14.0 4 | docopt==0.6.2 5 | hypothesis==5.6.0 6 | terminaltables==3.1.0 7 | -------------------------------------------------------------------------------- /deps/raft/tests/test_node.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "raft.h" 10 | #include "raft_log.h" 11 | #include "raft_private.h" 12 | 13 | void TestRaft_node_set_nextIdx(CuTest * tc) 14 | { 15 | raft_node_t *p = raft_node_new((void *) 1, 1, 1); 16 | raft_node_set_next_idx(p, 3); 17 | CuAssertTrue(tc, 3 == raft_node_get_next_idx(p)); 18 | } 19 | 20 | 21 | int main(void) 22 | { 23 | CuString *output = CuStringNew(); 24 | CuSuite* suite = CuSuiteNew(); 25 | 26 | SUITE_ADD_TEST(suite, TestRaft_node_set_nextIdx); 27 | 28 | CuSuiteRun(suite); 29 | CuSuiteDetails(suite, output); 30 | printf("%s\n", output->buffer); 31 | 32 | return suite->failCount == 0 ? 0 : 1; 33 | } 34 | -------------------------------------------------------------------------------- /deps/raft/tests/test_scenario.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "raft.h" 10 | #include "raft_log.h" 11 | #include "raft_private.h" 12 | #include "mock_send_functions.h" 13 | 14 | static int __raft_persist_metadata( 15 | raft_server_t *raft, 16 | void *udata, 17 | raft_term_t term, 18 | raft_node_id_t vote 19 | ) 20 | { 21 | return 0; 22 | } 23 | 24 | void TestRaft_scenario_leader_appears(CuTest * tc) 25 | { 26 | unsigned long i, j; 27 | raft_server_t *r[3]; 28 | void* sender[3]; 29 | 30 | senders_new(); 31 | 32 | for (j = 0; j < 3; j++) 33 | sender[j] = sender_new((void*)j); 34 | 35 | for (j = 0; j < 3; j++) 36 | { 37 | r[j] = raft_new(); 38 | sender_set_raft(sender[j], r[j]); 39 | raft_config(r[j], 1, RAFT_CONFIG_ELECTION_TIMEOUT, 500); 40 | raft_add_node(r[j], sender[0], 1, j==0); 41 | raft_add_node(r[j], sender[1], 2, j==1); 42 | raft_add_node(r[j], sender[2], 3, j==2); 43 | raft_set_callbacks(r[j], 44 | &((raft_cbs_t) { 45 | .send_requestvote = sender_requestvote, 46 | .send_appendentries = sender_appendentries, 47 | .persist_metadata = __raft_persist_metadata, 48 | .log = NULL 49 | }), sender[j]); 50 | } 51 | 52 | /* NOTE: important for 1st node to send vote request before others */ 53 | raft_periodic_internal(r[0], 1000); 54 | 55 | for (i = 0; i < 20; i++) 56 | { 57 | one_more_time: 58 | 59 | for (j = 0; j < 3; j++) 60 | sender_poll_msgs(sender[j]); 61 | 62 | for (j = 0; j < 3; j++) 63 | if (sender_msgs_available(sender[j])) 64 | goto one_more_time; 65 | 66 | for (j = 0; j < 3; j++) 67 | raft_periodic_internal(r[j], 100); 68 | } 69 | 70 | int leaders = 0; 71 | for (j = 0; j < 3; j++) 72 | if (raft_is_leader(r[j])) 73 | leaders += 1; 74 | 75 | CuAssertTrue(tc, 0 != leaders); 76 | CuAssertTrue(tc, 1 == leaders); 77 | } 78 | 79 | 80 | int main(void) 81 | { 82 | CuString *output = CuStringNew(); 83 | CuSuite* suite = CuSuiteNew(); 84 | 85 | SUITE_ADD_TEST(suite, TestRaft_scenario_leader_appears); 86 | 87 | CuSuiteRun(suite); 88 | CuSuiteDetails(suite, output); 89 | printf("%s\n", output->buffer); 90 | 91 | return suite->failCount == 0 ? 0 : 1; 92 | } 93 | -------------------------------------------------------------------------------- /docs/ExternalSharding.md: -------------------------------------------------------------------------------- 1 | RedisRaft External Sharding Support 2 | =================================== 3 | 4 | ## Introduction 5 | 6 | Initially, RedisRaft's [sharding mechanism](Sharding.md) used inter-cluster communication to learn about node configuration changes that occurred on its peer clusters containing other shards. 7 | While, much of the terminology hasn't changed, this mechanism for communicating between RedisRaft clusters was only effective at transmitting node changes. 8 | If one wanted to reconfigure which RedisRaft Clusters own which shards, one wasn't able to do it. 9 | 10 | In practice, we are moving to a mechanism that allows an external orchestrator to setup individual distinct RedisRaft clusters and dynamically configure them with which redis slots should be owned by them. 11 | In addition, this configuration process would also inform the cluster of the other RedisRaft clusters that provide other slot ranges, and the nodes that belong to them. 12 | In this mechanism, no communication would happen between RedisRaft clusters during normal stable operations 13 | 14 | ## Usage 15 | 16 | Setting up a RedisRaft cluster for use with external sharding follows the same pattern as setting up a standalone RedisRaft Cluster. 17 | The only addition module argument is `external-sharding yes`. 18 | 19 | In order to reconfigure the global cluster configuration, one would issue the same RAFT.SHARDGROUP REPLACE command to every cluster. 20 | 21 | The basic format of RAFT.SHARDGROUP REPLACE is 22 | 23 | ``` 24 | RAFT.SHARDGROUP REPLACE \ 25 | \ 26 | \ 27 | ... 28 | 29 | ``` 30 | 31 | where CLUSTER_CONFIGURATION is defined in the same manner as RAFT.SHARDGROUP ADD 32 | 33 | ``` 34 | \ 35 | \ 36 | \ 37 | ... 38 | \ 39 | \ 40 | ... 41 | 42 | ``` 43 | 44 | SLOT_RANGE_CONFIGURATION defined as 45 | 46 | ``` 47 | 48 | ``` 49 | 50 | START_SLOT and END_SLOT can be 0 to 16383 and are defined inclusively. While SLOT_TYPE can be 1, 2, or 3 which correspond to RedisCluster's STABLE, Migrating, and Importing definitions respectively. 51 | 52 | Currently, RedisRaft requires that the SLOT_TYPE just be 1 (STABLE). In the future, when slot migration is supported between clusters, administrators will be able to set them to migrating and importing types. 53 | 54 | NODE_CONFIGURATION is defined as 55 | 56 | ``` 57 | 58 | ``` 59 | 60 | RedisRaft will validate the full configuration passed to a RAFT.SHARDGROUP REPLACE command to ensure that its internally consistent. 61 | Namely, that every defined slot is in a consistent configuration. Only one cluster can be defined with a slot if its stable, and while two clusters can be defined to be importing and migrating, respectively, there can be only one cluster of each. -------------------------------------------------------------------------------- /docs/TLS.md: -------------------------------------------------------------------------------- 1 | # RedisRaft TLS Support 2 | 3 | RedisRaft's TLS support mirrors Redis's native TLS support. 4 | 5 | One runs the underlying redis server in the same manner that one would run a redis server with TLS 6 | 7 | ``` 8 | redis-server --tls-port --port 0 --tls-cert-file --tls-key-file --tls-ca-cert-file --tls-ca-cert-dir 9 | ``` 10 | 11 | In addition, one has to pass the `tls-enabled yes` option to the redisraft module 12 | 13 | ``` 14 | --loadmodule ./redisraft.so --raft.tls-enabled yes 15 | ``` 16 | 17 | ## Configuring RedisRaft TLS support 18 | 19 | RedisRaft is configured by reusing the Redis server's TLS configuration. 20 | 21 | ### Configuring RedisRaft via Redis. 22 | 23 | | Config Key Name | Meaning | 24 | |--------------------------|-----------------------------------------------------------------------------------------------------------| 25 | | tls-ca-cert-file | File Containing the CA Certificate | 26 | | tls-ca-cert-dir | Directory Containing the CA Certificate | 27 | | tls-key-file | File Containing the PEM encoded private key file | 28 | | tls-client-key-file | File Containing the PEM encoded private key file for client connections (overrides value of tls-key-file) | 29 | | tls-cert-file | File Containing the PEM encoded public signed CERT | 30 | | tls-client-cert-file | File Containing the PEM encoded public signed CERT (overrides tls-cert-file) | | 31 | | tls-key-file-pass | String containing password to decrypt the private key, if encrypted | 32 | | tls-client-key-file-pass | String containing password to decrypt the private key if using client key field | 33 | -------------------------------------------------------------------------------- /docs/TOC.md: -------------------------------------------------------------------------------- 1 | Table Of Contents 2 | ================= 3 | 4 | * [Introduction](Introduction.md): A high-level introduction to RedisRaft. 5 | * [Using RedisRaft](Using.md): A developer's guide to RedisRaft. 6 | * [Deployment Guide](Deployment.md): How to plan, deploy, configure, and manage a RedisRaft cluster. 7 | * [Sharding](Sharding.md): Sharding support for RedisRaft. 8 | * [Developing RedisRaft](Development.md): More information about the design and implementation of the RedisRaft module. 9 | -------------------------------------------------------------------------------- /jepsen/README.md: -------------------------------------------------------------------------------- 1 | Jepsen Tests Environment 2 | ======================== 3 | 4 | Here you will find tools to make it easier to run the Jepsen RedisRaft tests. 5 | 6 | ## Docker 7 | 8 | The `docker` directory contains everything you need to run a local test using 9 | docker-compose. 10 | 11 | ## Native AWS Environment 12 | 13 | The `aws` directory contains a Terraform script that automates setting up the 14 | test environment as native AWS instances running Debian. 15 | -------------------------------------------------------------------------------- /jepsen/aws/README.md: -------------------------------------------------------------------------------- 1 | # RedisRaft/Jepsen Tests on AWS 2 | 3 | This is a Terraform script to launch a Jepsen test environment on AWS. 4 | It launches and configures 5 cluster test nodes and one control node. 5 | 6 | All nodes run Debian. 7 | 8 | ## Getting started 9 | 10 | Set up your terraform environment and run: 11 | 12 | terraform -var aws_key_pair_name= apply 13 | 14 | ## Connecting 15 | 16 | Once launched, connect to the control node: 17 | 18 | ssh admin@ 19 | 20 | ## Installing Jepsen 21 | 22 | Clone the Jepsen Redis test. You may need the auto generated SSH key file 23 | authorized to do that in GitHub. 24 | 25 | git clone https://github.com/redislabs/jepsen-redisraft jepsen 26 | cd jepsen 27 | lein install 28 | 29 | ## Running a test 30 | 31 | To run a test: 32 | 33 | lein run test-all --ssh-private-key ~/.ssh/id_rsa \ 34 | --nodes-file ~/nodes.txt \ 35 | --follower-proxy \ 36 | --time-limit 600 \ 37 | --test-count 50 \ 38 | --concurrency 4n \ 39 | --redis-repo https://github.com/redis/redis \ 40 | --redis-version unstable \ 41 | --raft-repo https://github.com/redislabs/redisraft \ 42 | --raft-version master 43 | -------------------------------------------------------------------------------- /jepsen/aws/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "default" 3 | region = var.aws_region 4 | } 5 | 6 | resource "aws_vpc" "vpc" { 7 | cidr_block = "10.0.0.0/16" 8 | 9 | tags = { 10 | Name = "vpc-${var.project}" 11 | } 12 | } 13 | 14 | resource "aws_internet_gateway" "gateway" { 15 | vpc_id = aws_vpc.vpc.id 16 | 17 | tags = { 18 | Name = "igw-${var.project}" 19 | } 20 | } 21 | 22 | resource "aws_route" "route" { 23 | route_table_id = aws_vpc.vpc.main_route_table_id 24 | destination_cidr_block = "0.0.0.0/0" 25 | gateway_id = aws_internet_gateway.gateway.id 26 | } 27 | 28 | resource "aws_subnet" "main" { 29 | count = 1 30 | vpc_id = aws_vpc.vpc.id 31 | cidr_block = "10.0.1.0/24" 32 | map_public_ip_on_launch = true 33 | availability_zone = var.aws_az 34 | } 35 | 36 | resource "aws_security_group" "default" { 37 | name = "${var.project}_security_group" 38 | description = "Security group for ${var.project}" 39 | vpc_id = aws_vpc.vpc.id 40 | 41 | egress { 42 | from_port = 0 43 | to_port = 0 44 | protocol = -1 45 | cidr_blocks = ["0.0.0.0/0"] 46 | } 47 | 48 | ingress { 49 | from_port = 22 50 | to_port = 22 51 | protocol = "tcp" 52 | cidr_blocks = ["0.0.0.0/0"] 53 | } 54 | 55 | ingress { 56 | from_port = 6379 57 | to_port = 6379 58 | protocol = "tcp" 59 | cidr_blocks = [aws_vpc.vpc.cidr_block] 60 | } 61 | } 62 | 63 | data "aws_ami" "debian" { 64 | most_recent = true 65 | 66 | filter { 67 | name = "name" 68 | values = ["debian-10-amd64-*"] 69 | } 70 | 71 | owners = ["379101102735"] 72 | } 73 | 74 | resource "tls_private_key" "sshkey" { 75 | algorithm = "RSA" 76 | rsa_bits = 4096 77 | } 78 | 79 | resource "aws_instance" "node" { 80 | count = var.nodes_count 81 | 82 | ami = data.aws_ami.debian.id 83 | instance_type = var.aws_instance_type 84 | 85 | subnet_id = aws_subnet.main[0].id 86 | vpc_security_group_ids = [aws_security_group.default.id] 87 | 88 | key_name = var.aws_key_pair_name 89 | 90 | user_data = <<-EOT 91 | ${file("scripts/install_node.sh")} 92 | sudo mkdir -p /root/.ssh 93 | echo '${tls_private_key.sshkey.public_key_openssh}' | sudo tee -a /root/.ssh/authorized_keys > /dev/null 94 | echo '${tls_private_key.sshkey.private_key_pem}' | sudo tee /root/.ssh/id_rsa > /dev/null 95 | ssh-keyscan -t rsa github.com | sudo tee -a /root/.ssh/known_hosts 96 | sudo chmod 0600 /root/.ssh/id_rsa 97 | EOT 98 | 99 | root_block_device { 100 | volume_type = var.aws_volume_type 101 | volume_size = var.node_volume_size 102 | } 103 | 104 | tags = { 105 | Name = "${var.project}-node-${count.index}" 106 | } 107 | } 108 | 109 | 110 | 111 | resource "aws_instance" "control" { 112 | ami = data.aws_ami.debian.id 113 | instance_type = var.aws_instance_type 114 | 115 | subnet_id = aws_subnet.main[0].id 116 | vpc_security_group_ids = [aws_security_group.default.id] 117 | 118 | key_name = var.aws_key_pair_name 119 | 120 | tags = { 121 | Name = "${var.project}-control" 122 | } 123 | 124 | root_block_device { 125 | volume_type = var.aws_volume_type 126 | volume_size = var.control_volume_size 127 | } 128 | 129 | user_data = <<-EOT 130 | ${file("scripts/install_control.sh")} 131 | 132 | mkdir -p ~${var.control_user}/.ssh 133 | echo '${tls_private_key.sshkey.private_key_pem}' > ~${var.control_user}/.ssh/id_rsa 134 | chmod 0600 ~${var.control_user}/.ssh/id_rsa 135 | chown -R ${var.control_user}:${var.control_user} ~${var.control_user}/.ssh 136 | 137 | %{ for ip in aws_instance.node.*.private_ip } 138 | echo ${ip} >> ~${var.control_user}/nodes.txt 139 | %{ endfor } 140 | chown ${var.control_user}:${var.control_user} ~${var.control_user}/nodes.txt 141 | EOT 142 | } 143 | 144 | -------------------------------------------------------------------------------- /jepsen/aws/output.tf: -------------------------------------------------------------------------------- 1 | output "control-addr" { 2 | value = aws_instance.control.public_ip 3 | } 4 | -------------------------------------------------------------------------------- /jepsen/aws/scripts/install_control.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get -y -q update 3 | sudo apt-get install -qqy \ 4 | openjdk-8-jre \ 5 | libjna-java \ 6 | git \ 7 | gnuplot \ 8 | wget \ 9 | vim \ 10 | graphviz 11 | 12 | export LEIN_ROOT=true 13 | sudo wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 14 | sudo mv lein /usr/bin/lein 15 | sudo chmod 0755 /usr/bin/lein 16 | lein self-install 17 | -------------------------------------------------------------------------------- /jepsen/aws/scripts/install_node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get -y -q update 3 | sudo apt-get install -qqy \ 4 | apt-transport-https \ 5 | software-properties-common \ 6 | build-essential \ 7 | bzip2 \ 8 | curl \ 9 | faketime \ 10 | iproute \ 11 | iptables \ 12 | iputils-ping \ 13 | libzip4 \ 14 | logrotate \ 15 | man \ 16 | man-db \ 17 | net-tools \ 18 | ntpdate \ 19 | psmisc \ 20 | python \ 21 | rsyslog \ 22 | sudo \ 23 | tar \ 24 | unzip \ 25 | vim \ 26 | wget \ 27 | tcpdump \ 28 | git 29 | 30 | -------------------------------------------------------------------------------- /jepsen/aws/variables.tf: -------------------------------------------------------------------------------- 1 | variable "project" { 2 | default = "jepsen" 3 | } 4 | 5 | variable "nodes_count" { 6 | default = 5 7 | } 8 | 9 | variable "control_user" { 10 | default = "admin" 11 | } 12 | 13 | variable "aws_region" { 14 | default = "eu-west-1" 15 | } 16 | 17 | variable "aws_az" { 18 | default = "eu-west-1a" 19 | } 20 | 21 | variable "aws_instance_type" { 22 | default = "m5.large" 23 | } 24 | 25 | variable "aws_volume_type" { 26 | default = "gp2" 27 | } 28 | 29 | variable "node_volume_size" { 30 | default = "20" 31 | } 32 | 33 | variable "control_volume_size" { 34 | default = "100" 35 | } 36 | 37 | variable "aws_key_pair_name" { 38 | type = string 39 | description = "Key pair to use for instances" 40 | } 41 | -------------------------------------------------------------------------------- /jepsen/docker/README.md: -------------------------------------------------------------------------------- 1 | # RedisRaft/Jepsen Tests on Docker 2 | 3 | This is a `docker-compose` setup for running a RedisRaft/Jepsen test on a docker 4 | environment. 5 | 6 | The environment consists of a single control container and 5 cluster containers 7 | running RedisRaft. All containers are based on Debian. 8 | 9 | ## Getting started 10 | 11 | First, you need to generate SSH keys that will be populated and used for the 12 | control container to access the cluster containers. 13 | 14 | Run: 15 | 16 | ./genkeys.sh 17 | 18 | This should result with a `secret/` directory created with configuration files 19 | in it. 20 | 21 | Next, build the images: 22 | 23 | docker-compose build 24 | 25 | And start the environment: 26 | 27 | docker-compose up -d 28 | 29 | ## Running 30 | 31 | To run a test: 32 | 33 | docker exec -w /jepsen jepsen-control \ 34 | lein run test-all --ssh-private-key /root/.ssh/id_rsa \ 35 | --follower-proxy \ 36 | --time-limit 600 \ 37 | --test-count 50 \ 38 | --concurrency 4n \ 39 | --nemesis partition,pause,kill,member \ 40 | --redis-repo https://github.com/redis/redis \ 41 | --redis-version unstable \ 42 | --raft-repo https://github.com/redislabs/redisraft \ 43 | --raft-version master 44 | 45 | ## Test results 46 | 47 | The `jepsen-control` container runs a built-in web server which is exposed on 48 | http://localhost:8080 and can be used to look at test results. 49 | 50 |

51 |

52 | 53 | 54 | -------------------------------------------------------------------------------- /jepsen/docker/control/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | 3 | # 4 | # Jepsen dependencies 5 | # 6 | RUN apt-get -y -q update && \ 7 | apt-get install -qqy \ 8 | openjdk-11-jdk \ 9 | libjna-java \ 10 | git \ 11 | gnuplot \ 12 | wget \ 13 | vim \ 14 | graphviz 15 | 16 | ENV LEIN_ROOT true 17 | RUN wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein && \ 18 | mv lein /usr/bin && \ 19 | chmod +x /usr/bin/lein && \ 20 | lein self-install 21 | 22 | ARG JEPSEN_REPO=redislabs/jepsen-redisraft 23 | RUN git clone https://github.com/${JEPSEN_REPO} /jepsen && \ 24 | cd /jepsen && \ 25 | lein install 26 | 27 | ADD entrypoint.sh /entrypoint.sh 28 | RUN chmod 0755 /entrypoint.sh 29 | 30 | EXPOSE 8080 31 | ENTRYPOINT ["/entrypoint.sh"] 32 | -------------------------------------------------------------------------------- /jepsen/docker/control/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | mkdir -p /root/.ssh 5 | chmod 0700 /root/.ssh 6 | 7 | if [ -n "$PRIVATE_KEY" ]; then 8 | echo "$PRIVATE_KEY" | sed 's/\\n/\ 9 | /g' > /root/.ssh/id_rsa 10 | chmod 0600 /root/.ssh/id_rsa 11 | fi 12 | 13 | echo "$KNOWN_HOSTS" | sed 's/\\n/\ 14 | /g' > /root/.ssh/known_hosts 15 | chmod 0600 /root/.ssh/known_hosts 16 | 17 | cd /jepsen 18 | lein run serve 19 | -------------------------------------------------------------------------------- /jepsen/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | control: 4 | container_name: jepsen-control 5 | hostname: control 6 | build: ./control 7 | env_file: 8 | - ./secret/keys.env 9 | ports: 10 | - "127.0.0.1:8080:8080" 11 | links: 12 | - n1 13 | - n2 14 | - n3 15 | - n4 16 | - n5 17 | volumes: 18 | - ./store:/jepsen/store 19 | n1: 20 | container_name: jepsen-n1 21 | build: ./node 22 | privileged: true 23 | hostname: n1 24 | env_file: ./secret/keys.env 25 | n2: 26 | extends: n1 27 | container_name: jepsen-n2 28 | hostname: n2 29 | n3: 30 | extends: n1 31 | container_name: jepsen-n3 32 | hostname: n3 33 | n4: 34 | extends: n1 35 | container_name: jepsen-n4 36 | hostname: n4 37 | n5: 38 | extends: n1 39 | container_name: jepsen-n5 40 | hostname: n5 41 | -------------------------------------------------------------------------------- /jepsen/docker/genkeys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [ -d ./secret ] || mkdir secret 3 | if [ -f ./secret/id_rsa ]; then 4 | echo "Keys already created; rm ./secret/* to recreate!" 5 | exit 1 6 | fi 7 | 8 | ssh-keygen -t rsa -N "" -m PEM -f ./secret/id_rsa 9 | ( 10 | echo "# File is auto-generated by genkeys.sh" 11 | echo "AUTHORIZED_KEYS=$(cat ./secret/id_rsa.pub)" 12 | # Only needed if using git+ssh 13 | # echo "KNOWN_HOSTS=$(ssh-keyscan -t rsa github.com)" 14 | echo "PRIVATE_KEY=$(cat ./secret/id_rsa | sed 's/$/\\n/g' | tr -d '\n')" 15 | ) > ./secret/keys.env 16 | -------------------------------------------------------------------------------- /jepsen/docker/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | 3 | # Install packages 4 | RUN apt-get update && \ 5 | apt-get -y install \ 6 | openssh-server \ 7 | pwgen \ 8 | && \ 9 | mkdir -p /var/run/sshd && \ 10 | sed -i "s/UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config && \ 11 | sed -i "s/PermitRootLogin without-password/PermitRootLogin yes/g" /etc/ssh/sshd_config 12 | 13 | # Install Jepsen deps 14 | RUN apt-get update && \ 15 | apt-get -y install \ 16 | apt-transport-https \ 17 | software-properties-common \ 18 | build-essential \ 19 | bzip2 \ 20 | curl \ 21 | faketime \ 22 | iproute2 \ 23 | iptables \ 24 | iputils-ping \ 25 | libzip4 \ 26 | logrotate \ 27 | man \ 28 | man-db \ 29 | net-tools \ 30 | ntpdate \ 31 | psmisc \ 32 | python \ 33 | rsyslog \ 34 | sudo \ 35 | tar \ 36 | unzip \ 37 | vim \ 38 | wget \ 39 | tcpdump \ 40 | git \ 41 | cmake \ 42 | automake \ 43 | autoconf \ 44 | libtool \ 45 | && \ 46 | apt-get remove -y --purge --auto-remove systemd 47 | 48 | ADD entrypoint.sh /entrypoint.sh 49 | RUN chmod 0755 /entrypoint.sh 50 | 51 | EXPOSE 22 52 | ENTRYPOINT ["/entrypoint.sh"] 53 | -------------------------------------------------------------------------------- /jepsen/docker/node/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -z "$AUTHORIZED_KEYS" ]; then 3 | echo "ERROR: No AUTHORIZED_KEYS environment was setup!" 4 | exit 1 5 | fi 6 | 7 | mkdir -p /root/.ssh 8 | chmod 0700 /root/.ssh 9 | echo "${AUTHORIZED_KEYS}" > /root/.ssh/authorized_keys 10 | chmod 0600 /root/.ssh/authorized_keys 11 | 12 | if [ -n "$PRIVATE_KEY" ]; then 13 | echo "$PRIVATE_KEY" | sed 's/\\n/\ 14 | /g' > /root/.ssh/id_rsa 15 | chmod 0600 /root/.ssh/id_rsa 16 | fi 17 | 18 | if [ -n "$KNOWN_HOSTS" ]; then 19 | echo "$KNOWN_HOSTS" | sed 's/\\n/\ 20 | /g' > /root/.ssh/known_hosts 21 | chmod 0600 /root/.ssh/known_hosts 22 | fi 23 | 24 | exec /usr/sbin/sshd -D 25 | -------------------------------------------------------------------------------- /src/clientstate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2022 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "redisraft.h" 8 | 9 | ClientState *ClientStateGetById(RedisRaftCtx *rr, unsigned long long client_id) 10 | { 11 | return RedisModule_DictGetC(rr->client_state, &client_id, sizeof(client_id), NULL); 12 | } 13 | 14 | ClientState *ClientStateGet(RedisRaftCtx *rr, RedisModuleCtx *ctx) 15 | { 16 | unsigned long long client_id = RedisModule_GetClientId(ctx); 17 | return ClientStateGetById(rr, client_id); 18 | } 19 | 20 | void ClientStateAlloc(RedisRaftCtx *rr, unsigned long long client_id) 21 | { 22 | ClientState *clientState = RedisModule_Calloc(sizeof(ClientState), 1); 23 | int ret = RedisModule_DictSetC(rr->client_state, &client_id, sizeof(client_id), clientState); 24 | RedisModule_Assert(ret == REDISMODULE_OK); 25 | } 26 | 27 | void ClientStateFree(RedisRaftCtx *rr, unsigned long long client_id) 28 | { 29 | ClientState *state = NULL; 30 | 31 | RedisModule_DictDelC(rr->client_state, &client_id, sizeof(client_id), &state); 32 | 33 | if (state != NULL) { 34 | ClientStateReset(state); 35 | RedisModule_Free(state); 36 | } 37 | } 38 | 39 | void ClientStateSetBlockedReq(RedisRaftCtx *rr, raft_session_t client_id, RaftReq *req) 40 | { 41 | ClientState *cs = RedisModule_DictGetC(rr->client_state, &client_id, sizeof(client_id), NULL); 42 | cs->blocked_req = req; 43 | } 44 | 45 | void BlockedReqResetById(RedisRaftCtx *rr, raft_session_t client_id) 46 | { 47 | ClientState *cs = RedisModule_DictGetC(rr->client_state, &client_id, sizeof(client_id), NULL); 48 | if (cs) { 49 | cs->blocked_req = NULL; 50 | } 51 | } 52 | 53 | void MultiStateReset(MultiState *multi_state) 54 | { 55 | RaftRedisCommandArrayFree(&multi_state->cmds); 56 | multi_state->active = false; 57 | multi_state->error = false; 58 | } 59 | 60 | void ClientStateReset(ClientState *client_state) 61 | { 62 | MultiStateReset(&client_state->multi_state); 63 | } 64 | -------------------------------------------------------------------------------- /src/entrycache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #ifndef REDISRAFT_ENTRYCACHE_H 8 | #define REDISRAFT_ENTRYCACHE_H 9 | 10 | #include "raft.h" 11 | 12 | #include 13 | 14 | typedef struct EntryCache { 15 | raft_index_t size; /* Size of ptrs */ 16 | raft_index_t len; /* Number of entries in cache */ 17 | raft_index_t start_idx; /* Log index of first entry */ 18 | raft_index_t start; /* ptrs array index of first entry */ 19 | unsigned long int entries_memsize; /* Total memory used by entries */ 20 | raft_entry_t **ptrs; 21 | } EntryCache; 22 | 23 | EntryCache *EntryCacheNew(raft_index_t initial_size); 24 | void EntryCacheFree(EntryCache *cache); 25 | void EntryCacheAppend(EntryCache *cache, raft_entry_t *ety, raft_index_t idx); 26 | raft_entry_t *EntryCacheGet(EntryCache *cache, raft_index_t idx); 27 | long EntryCacheDeleteHead(EntryCache *cache, raft_index_t idx); 28 | long EntryCacheDeleteTail(EntryCache *cache, raft_index_t index); 29 | long EntryCacheCompact(EntryCache *cache, size_t max_memory, raft_index_t limit); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2022 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #ifndef REDISRAFT_FILE_H 8 | #define REDISRAFT_FILE_H 9 | 10 | #include 11 | #include 12 | 13 | /* File implementation with a userspace buffer. 14 | * 15 | * Main differences over the standard library FILE implementation: 16 | * 17 | * * Works with O_APPEND mode only. It is a deliberate decision to keep the 18 | * implementation simple. 19 | * * The standard library FILE API requires extra care when you want to use it 20 | * together with POSIX IO functions, e.g., need fflush() before ftruncate(). 21 | * This implementation tries to do these things automatically. 22 | * * Tracks read/write offset internally. Getting read/write offset is fast 23 | * as it doesn't make a system call for them. 24 | * * This implementation is not thread-safe while the standard library 25 | * implementation is. 26 | */ 27 | 28 | typedef struct File { 29 | int fd; /* File descriptor */ 30 | char *rpos; /* In read mode, current read position of the buffer. */ 31 | char *rend; /* In read mode, end position of the buffer. */ 32 | size_t roffset; /* Read offset relative to the head of the file. */ 33 | char *wpos; /* In write mode, current write position of the buffer. */ 34 | size_t woffset; /* Write offset relative to the head of the file. */ 35 | char *buf; /* Userspace buffer. */ 36 | } File; 37 | 38 | void FileInit(File *file); 39 | int FileTerm(File *file); 40 | 41 | int FileOpen(File *file, const char *filename, int flags); 42 | int FileFlush(File *file); 43 | int FileFsync(File *file); 44 | 45 | int FileSetReadOffset(File *file, size_t offset); 46 | size_t FileGetReadOffset(File *file); 47 | 48 | ssize_t FileGets(File *file, void *buf, size_t cap); 49 | ssize_t FileRead(File *file, void *buf, size_t cap); 50 | ssize_t FileWrite(File *file, void *buf, size_t len); 51 | size_t FileSize(File *file); 52 | int FileTruncate(File *file, size_t len); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2022 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #ifndef REDISRAFT_LOG_H 8 | #define REDISRAFT_LOG_H 9 | 10 | #include "file.h" 11 | #include "raft.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | extern raft_log_impl_t LogImpl; 19 | 20 | typedef struct LogPage { 21 | char dbid[64]; /* DB unique ID, TODO: size should be RAFT_DBID_LEN + 1, will be fixed with RR-148 */ 22 | raft_node_id_t node_id; /* Node ID */ 23 | raft_term_t prev_log_term; /* Entry term that comes just before this page. */ 24 | raft_index_t prev_log_idx; /* Entry index that comes just before this page. */ 25 | raft_index_t num_entries; /* Entries in log */ 26 | raft_index_t index; /* Index of last entry */ 27 | char filename[PATH_MAX]; /* Log file name */ 28 | char idxfilename[PATH_MAX]; /* Index file name */ 29 | File file; /* Log file */ 30 | File idxfile; /* Index file descriptor */ 31 | long current_crc; /* Current running crc value for the log file */ 32 | } LogPage; 33 | 34 | typedef struct Log { 35 | char dbid[64]; /* DB unique ID, TODO: size should be RAFT_DBID_LEN + 1, will be fixed with RR-148 */ 36 | raft_node_id_t node_id; /* Node ID */ 37 | LogPage *pages[2]; /* Log files. Second page will be created on log compaction */ 38 | raft_index_t fsync_index; /* Last entry index included in the latest fsync() call */ 39 | uint64_t fsync_count; /* Count of fsync() calls */ 40 | uint64_t fsync_max; /* Slowest fsync() call in microseconds */ 41 | uint64_t fsync_total; /* Total time fsync() calls consumed in microseconds */ 42 | } Log; 43 | 44 | void LogInit(Log *log); 45 | void LogTerm(Log *log); 46 | 47 | int LogCreate(Log *log, const char *filename, const char *dbid, 48 | raft_node_id_t node_id, raft_term_t prev_log_term, 49 | raft_index_t prev_log_index); 50 | int LogOpen(Log *log, const char *filename); 51 | 52 | raft_node_id_t LogNodeId(Log *log); 53 | const char *LogDbid(Log *log); 54 | 55 | int LogAppend(Log *log, raft_entry_t *entry); 56 | int LogLoadEntries(Log *log); 57 | int LogSync(Log *log, bool sync); 58 | int LogFlush(Log *log); 59 | int LogCurrentFd(Log *log); 60 | raft_entry_t *LogGet(Log *log, raft_index_t idx); 61 | int LogDelete(Log *log, raft_index_t from_idx); 62 | int LogReset(Log *log, raft_index_t index, raft_term_t term); 63 | raft_term_t LogPrevLogTerm(Log *log); 64 | raft_index_t LogPrevLogIndex(Log *log); 65 | raft_index_t LogCount(Log *log); 66 | raft_index_t LogFirstIdx(Log *log); 67 | raft_index_t LogCurrentIdx(Log *log); 68 | size_t LogFileSize(Log *log); 69 | void LogArchiveFiles(Log *log); 70 | 71 | int LogCompactionBegin(Log *log); 72 | void LogCompactionEnd(Log *log); 73 | bool LogCompactionStarted(Log *log); 74 | raft_index_t LogCompactionIdx(Log *log); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/metadata.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2022 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #ifndef REDISRAFT_METADATA_H 8 | #define REDISRAFT_METADATA_H 9 | 10 | #include "raft.h" 11 | 12 | /* Raft metadata file to store last voted node id and the current term.*/ 13 | 14 | typedef struct Metadata { 15 | char *filename; 16 | char dbid[64]; 17 | raft_node_id_t node_id; 18 | raft_term_t term; 19 | raft_node_id_t vote; 20 | } Metadata; 21 | 22 | void MetadataInit(Metadata *m); 23 | void MetadataTerm(Metadata *m); 24 | void MetadataSetClusterConfig(Metadata *m, const char *filename, char *dbid, 25 | raft_node_id_t node_id); 26 | void MetadataArchiveFile(Metadata *m); 27 | int MetadataRead(Metadata *m, const char *filename); 28 | int MetadataWrite(Metadata *m, raft_term_t term, raft_node_id_t vote); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/node_addr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "redisraft.h" 8 | 9 | #include 10 | #include 11 | 12 | /* Attempt to parse a node address in the form of : 13 | * and populate the result NodeAddr. Returns true if successful. 14 | */ 15 | bool NodeAddrParse(const char *node_addr, size_t node_addr_len, NodeAddr *result) 16 | { 17 | char buf[32] = {0}; 18 | char *endptr; 19 | unsigned long l; 20 | 21 | /* Split */ 22 | const char *colon = node_addr + node_addr_len; 23 | while (colon > node_addr && *colon != ':') { 24 | colon--; 25 | } 26 | if (*colon != ':') { 27 | return false; 28 | } 29 | 30 | /* Get port */ 31 | size_t portlen = node_addr_len - (colon + 1 - node_addr); 32 | if (portlen >= sizeof(buf) || portlen < 1) { 33 | return false; 34 | } 35 | 36 | strncpy(buf, colon + 1, portlen); 37 | l = strtoul(buf, &endptr, 10); 38 | if (*endptr != '\0' || l < 1 || l > 65535) { 39 | return false; 40 | } 41 | result->port = l; 42 | 43 | /* Get addr */ 44 | size_t addrlen = colon - node_addr; 45 | if (addrlen >= sizeof(result->host)) { 46 | addrlen = sizeof(result->host) - 1; 47 | } 48 | memcpy(result->host, node_addr, addrlen); 49 | result->host[addrlen] = '\0'; 50 | 51 | return true; 52 | } 53 | 54 | /* Compare two NodeAddr structs */ 55 | bool NodeAddrEqual(const NodeAddr *a1, const NodeAddr *a2) 56 | { 57 | return (a1->port == a2->port && !strcmp(a1->host, a2->host)); 58 | } 59 | 60 | /* Add a NodeAddrListElement to a chain of elements. If an existing element with the same 61 | * address already exists, nothing is done. The addr pointer provided is copied into newly 62 | * allocated memory, caller should free addr if necessary. 63 | */ 64 | void NodeAddrListAddElement(NodeAddrListElement **head, const NodeAddr *addr) 65 | { 66 | while (*head != NULL) { 67 | if (NodeAddrEqual(&(*head)->addr, addr)) { 68 | return; 69 | } 70 | 71 | head = &(*head)->next; 72 | } 73 | 74 | *head = RedisModule_Calloc(1, sizeof(NodeAddrListElement)); 75 | (*head)->addr = *addr; 76 | } 77 | 78 | /* Concat a NodeAddrList to another NodeAddrList */ 79 | void NodeAddrListConcat(NodeAddrListElement **head, const NodeAddrListElement *other) 80 | { 81 | const NodeAddrListElement *e = other; 82 | 83 | while (e != NULL) { 84 | NodeAddrListAddElement(head, &e->addr); 85 | e = e->next; 86 | } 87 | } 88 | 89 | /* Free a linked list of NodeAddrListElement */ 90 | void NodeAddrListFree(NodeAddrListElement *head) 91 | { 92 | NodeAddrListElement *t; 93 | 94 | while (head != NULL) { 95 | t = head->next; 96 | RedisModule_Free(head); 97 | head = t; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/proxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "redisraft.h" 8 | 9 | static RRStatus hiredisReplyToModule(redisReply *reply, RedisModuleCtx *ctx) 10 | { 11 | switch (reply->type) { 12 | case REDIS_REPLY_STRING: 13 | RedisModule_ReplyWithStringBuffer(ctx, reply->str, reply->len); 14 | break; 15 | case REDIS_REPLY_ARRAY: 16 | RedisModule_ReplyWithArray(ctx, reply->elements); 17 | for (size_t i = 0; i < reply->elements; i++) { 18 | if (hiredisReplyToModule(reply->element[i], ctx) != RR_OK) { 19 | RedisModule_ReplyWithError(ctx, "ERR bad reply from leader"); 20 | } 21 | } 22 | break; 23 | case REDIS_REPLY_INTEGER: 24 | RedisModule_ReplyWithLongLong(ctx, reply->integer); 25 | break; 26 | case REDIS_REPLY_NIL: 27 | RedisModule_ReplyWithNull(ctx); 28 | break; 29 | case REDIS_REPLY_STATUS: 30 | RedisModule_ReplyWithSimpleString(ctx, reply->str); 31 | break; 32 | case REDIS_REPLY_ERROR: 33 | RedisModule_ReplyWithError(ctx, reply->str); 34 | break; 35 | default: 36 | return RR_ERROR; 37 | } 38 | 39 | return RR_OK; 40 | } 41 | 42 | static void handleProxiedCommandResponse(redisAsyncContext *c, void *r, void *privdata) 43 | { 44 | RaftReq *req = privdata; 45 | redisReply *reply = r; 46 | 47 | redis_raft.proxy_outstanding_reqs--; 48 | NodeDismissPendingResponse(req->r.redis.proxy_node); 49 | 50 | if (!reply) { 51 | /* Connection have dropped. The state of the request is unknown at this point 52 | * and this must be reflected to the user. 53 | * 54 | * Ideally the connection should be dropped but Module API does not provide for that. 55 | */ 56 | ConnMarkDisconnected(req->r.redis.proxy_node->conn); 57 | RedisModule_ReplyWithError(req->ctx, "TIMEOUT no reply from leader"); 58 | redis_raft.proxy_failed_responses++; 59 | goto exit; 60 | } 61 | 62 | if (RedisModule_BlockedClientDisconnected(req->ctx)) { 63 | goto exit; 64 | } 65 | 66 | if (hiredisReplyToModule(reply, req->ctx) != RR_OK) { 67 | RedisModule_ReplyWithError(req->ctx, "ERR bad reply from leader"); 68 | } 69 | 70 | exit: 71 | RaftReqFree(req); 72 | } 73 | 74 | RRStatus ProxyCommand(RedisRaftCtx *rr, RedisModuleCtx *ctx, 75 | RaftRedisCommandArray *cmds, Node *leader) 76 | { 77 | redisAsyncContext *rc; 78 | 79 | if (!ConnIsConnected(leader->conn) || !(rc = ConnGetRedisCtx(leader->conn))) { 80 | rr->proxy_failed_reqs++; 81 | return RR_ERROR; 82 | } 83 | 84 | RaftReq *req = RaftReqInit(ctx, RR_GENERIC); 85 | req->r.redis.proxy_node = leader; 86 | 87 | raft_entry_t *entry = RaftRedisCommandArraySerialize(cmds); 88 | int ret = redisAsyncCommand(rc, handleProxiedCommandResponse, req, 89 | "RAFT.ENTRY %b", entry->data, entry->data_len); 90 | raft_entry_release(entry); 91 | 92 | if (ret != REDIS_OK) { 93 | RaftReqFree(req); 94 | rr->proxy_failed_reqs++; 95 | return RR_ERROR; 96 | } 97 | 98 | NodeAddPendingResponse(leader, true); 99 | rr->proxy_reqs++; 100 | rr->proxy_outstanding_reqs++; 101 | 102 | return RR_OK; 103 | } 104 | -------------------------------------------------------------------------------- /src/threadpool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2022 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "redisraft.h" 8 | 9 | #include 10 | #include 11 | 12 | /* Thread main loop */ 13 | static void *loop(void *arg) 14 | { 15 | ThreadPool *pool = arg; 16 | 17 | while (true) { 18 | pthread_mutex_lock(&pool->mtx); 19 | while (sc_list_is_empty(&pool->tasks) && pool->shutdown == 0) { 20 | int rc = pthread_cond_wait(&pool->cond, &pool->mtx); 21 | RedisModule_Assert(rc == 0); 22 | } 23 | 24 | if (pool->shutdown) { 25 | pthread_mutex_unlock(&pool->mtx); 26 | return NULL; 27 | } 28 | 29 | struct sc_list *elem = sc_list_pop_head(&pool->tasks); 30 | struct Task *t = sc_list_entry(elem, struct Task, entry); 31 | 32 | pthread_mutex_unlock(&pool->mtx); 33 | 34 | t->run(t->arg); 35 | 36 | RedisModule_Free(t); 37 | } 38 | } 39 | 40 | /* Initializes thread pool with `thread_count` threads. */ 41 | void threadPoolInit(ThreadPool *pool, int thread_count) 42 | { 43 | *pool = (ThreadPool){0}; 44 | 45 | pool->threads = RedisModule_Alloc(sizeof(*pool->threads) * thread_count); 46 | pool->thread_count = thread_count; 47 | pool->mtx = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; 48 | sc_list_init(&pool->tasks); 49 | 50 | int rc = pthread_cond_init(&pool->cond, NULL); 51 | if (rc != 0) { 52 | PANIC("pthread_cond_init(): %s \n", strerror(rc)); 53 | } 54 | 55 | for (int i = 0; i < thread_count; i++) { 56 | pthread_attr_t attr; 57 | 58 | rc = pthread_attr_init(&attr); 59 | if (rc != 0) { 60 | PANIC("pthread_attr_init(): %s", strerror(rc)); 61 | } 62 | 63 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 64 | 65 | rc = pthread_create(&pool->threads[i], &attr, loop, pool); 66 | if (rc != 0) { 67 | PANIC("pthread_create(): %s", strerror(rc)); 68 | } 69 | 70 | pthread_attr_destroy(&attr); 71 | } 72 | } 73 | 74 | /* Add task to the pool. A random thread will call the callback with the arg 75 | * provided */ 76 | void threadPoolAdd(ThreadPool *pool, void *arg, void (*run)(void *arg)) 77 | { 78 | struct Task *t = RedisModule_Alloc(sizeof(*t)); 79 | 80 | t->arg = arg; 81 | t->run = run; 82 | sc_list_init(&t->entry); 83 | 84 | pthread_mutex_lock(&pool->mtx); 85 | sc_list_add_tail(&pool->tasks, &t->entry); 86 | pthread_cond_signal(&pool->cond); 87 | pthread_mutex_unlock(&pool->mtx); 88 | } 89 | 90 | /* Shutdown threadpool gracefully, waiting each thread to join */ 91 | void threadPoolShutdown(ThreadPool *pool) 92 | { 93 | pthread_mutex_lock(&pool->mtx); 94 | pool->shutdown = 1; 95 | pthread_cond_broadcast(&pool->cond); 96 | pthread_mutex_unlock(&pool->mtx); 97 | 98 | for (int i = 0; i < pool->thread_count; i++) { 99 | void *ret; 100 | int rc = pthread_join(pool->threads[i], &ret); 101 | if (rc != 0) { 102 | PANIC("pthread_join() : %s", strerror(rc)); 103 | } 104 | } 105 | 106 | struct sc_list *it, *tmp; 107 | 108 | sc_list_foreach_safe (&pool->tasks, tmp, it) { 109 | struct Task *t = sc_list_entry(it, struct Task, entry); 110 | RedisModule_Free(t); 111 | } 112 | } -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2021 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "buildinfo.h" 8 | #define REDISRAFT_VERSION "255.255.255" 9 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedisLabs/redisraft/ade4aa8e6aa5c3b21678a1998309825f06567d4f/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/modules/hellomodule.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "redismodule.h" 8 | 9 | int cmdHello(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 10 | { 11 | RedisModule_ReplyWithSimpleString(ctx, "HELLO"); 12 | return REDISMODULE_OK; 13 | } 14 | 15 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) 16 | { 17 | int rc = RedisModule_Init(ctx, "hellomodule", 1, REDISMODULE_APIVER_1); 18 | if (rc != REDISMODULE_OK) { 19 | return REDISMODULE_ERR; 20 | } 21 | 22 | rc = RedisModule_CreateCommand(ctx, "hellomodule", cmdHello, "write deny-oom", 0, 0, 0); 23 | if (rc != REDISMODULE_OK) { 24 | return REDISMODULE_ERR; 25 | } 26 | 27 | return REDISMODULE_OK; 28 | } 29 | -------------------------------------------------------------------------------- /tests/integration/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | log_file=tests/tmp/tests.log 3 | log_file_format=%(message)s 4 | log_file_level=debug 5 | timeout=600 6 | markers = 7 | elle_test: marks a test as able to use elle background workers 8 | key_hash_tag: what key hash tag should be used to force slot usage 9 | num_elle_keys: number of keys ElleWorker should make use of 10 | 11 | -------------------------------------------------------------------------------- /tests/integration/requirements.txt: -------------------------------------------------------------------------------- 1 | redis==3.4.1 2 | pytest==7.1.2 3 | pytest-timeout==1.3.4 4 | codespell==2.2.2 5 | pycodestyle==2.9.1 6 | retry==0.9.2 7 | -------------------------------------------------------------------------------- /tests/integration/workload.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright Redis Ltd. 2020 - present 3 | Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) 4 | or the Server Side Public License v1 (SSPLv1). 5 | """ 6 | 7 | import threading 8 | from redis import RedisError 9 | 10 | 11 | class AbstractWork(object): 12 | def __init__(self, thread_id, client): 13 | self.thread_id = thread_id 14 | self.client = client 15 | 16 | 17 | class MultiWithLargeReply(AbstractWork): 18 | def __init__(self, *args, **kwargs): 19 | super(MultiWithLargeReply, self).__init__(*args, **kwargs) 20 | self.counter_key = 'counter-%s' % self.thread_id 21 | self.list_key = 'list-%s' % self.thread_id 22 | 23 | def do_work(self, iteration): 24 | try: 25 | pipeline = self.client.pipeline(transaction=True) 26 | pipeline.incr(self.counter_key) 27 | pipeline.rpush(self.list_key, iteration) 28 | for _ in range(1): 29 | pipeline.lrange(self.list_key, 0, -1) 30 | reply = pipeline.execute() 31 | return reply[0] == reply[1] # count == length of list 32 | except RedisError: 33 | pass 34 | return True 35 | 36 | 37 | class MonotonicIncrCheck(AbstractWork): 38 | def __init__(self, *args, **kwargs): 39 | super(MonotonicIncrCheck, self).__init__(*args, **kwargs) 40 | self.counter_key = 'counter-%s' % self.thread_id 41 | self.val = 0 42 | 43 | def do_work(self, _iteration): 44 | try: 45 | new_val = self.client.incr(self.counter_key) 46 | if new_val <= self.val: 47 | return False 48 | self.val = new_val 49 | except RedisError: 50 | pass 51 | return True 52 | 53 | 54 | class Workload(object): 55 | def __init__(self): 56 | self._terminate = False 57 | self._threads = [] 58 | self._iters = 0 59 | self._failures = 0 60 | 61 | def start(self, thread_count, cluster, work_class): 62 | for thread_id in range(thread_count): 63 | _thread = threading.Thread(target=self._worker, 64 | args=[thread_id + 1, 65 | work_class, 66 | cluster.random_node().client]) 67 | _thread.start() 68 | self._threads.append(_thread) 69 | 70 | def _worker(self, thread_id, work_class, client): 71 | worker = work_class(thread_id, client) 72 | iteration = 1 73 | while not self._terminate: 74 | if not worker.do_work(iteration): 75 | self._failures += 1 76 | self._iters += 1 77 | iteration += 1 78 | 79 | def terminate(self): 80 | self._terminate = True 81 | while self._threads: 82 | self._threads.pop().join() 83 | 84 | def stop(self): 85 | self.terminate() 86 | assert self._iters > 0 87 | assert self._failures == 0 88 | 89 | def stats(self): 90 | return 'Iterations: %s; Failures: %s' % (self._iters, self._failures) 91 | -------------------------------------------------------------------------------- /tests/monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Redis Ltd. 2020 - present 4 | # Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 5 | # the Server Side Public License v1 (SSPLv1). 6 | 7 | NODES=3 8 | while true; do 9 | for n in $(seq $NODES); do 10 | echo "---- NODE $n ----" 11 | redis-cli -p $(( 5000 + $n )) info raft 12 | done 13 | echo "===" 14 | sleep 1 15 | done 16 | -------------------------------------------------------------------------------- /tests/redis-suite/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Redis Ltd. 2020 - present 4 | # Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 5 | # the Server Side Public License v1 (SSPLv1). 6 | 7 | REDIS_DIR=${REDIS_DIR:-${PWD}/../redis} 8 | 9 | export REDIS_SERVER_BINARY=${REDIS_DIR}/src/redis-server 10 | export REDIS_CLI_BINARY=${REDIS_DIR}/src/redis-cli 11 | export ADDITIONAL_OPTIONS="--raft.log-fsync no" 12 | 13 | setup() { 14 | pushd ./utils/create-cluster 15 | ./create-cluster stop 16 | ./create-cluster clean 17 | ./create-cluster start 18 | ./create-cluster create 19 | popd 20 | } 21 | 22 | teardown() { 23 | pushd ./utils/create-cluster 24 | ./create-cluster stop 25 | popd 26 | } 27 | 28 | run_tests() { 29 | local tests_dir=${PWD}/tests/redis-suite 30 | pushd $REDIS_DIR 31 | ./runtest \ 32 | --host 127.0.0.1 \ 33 | --port 5001 \ 34 | --singledb \ 35 | --ignore-encoding \ 36 | --ignore-digest \ 37 | --skipfile ${tests_dir}/skip.txt \ 38 | --tags -needs:repl \ 39 | --tags -needs:debug \ 40 | --tags -needs:save \ 41 | --tags -needs:reset \ 42 | --tags -needs:config-maxmemory \ 43 | --tags -needs:latency \ 44 | --tags -stream \ 45 | --tags -pause \ 46 | --tags -tracking \ 47 | --tags -cli \ 48 | --tags -querybuf \ 49 | $* 50 | local retcode=$? 51 | popd 52 | 53 | return $retcode 54 | } 55 | 56 | # Make sure we're running from the right place 57 | if [ ! -f tests/redis-suite/run.sh ]; then 58 | echo Please run this script from the top level directory. 59 | exit 1 60 | fi 61 | 62 | setup 63 | run_tests $* 64 | retcode=$? 65 | teardown 66 | 67 | exit $retcode 68 | -------------------------------------------------------------------------------- /tests/redis-suite/skip.txt: -------------------------------------------------------------------------------- 1 | -- doesn't work, as command appears as "raft", not "blpop" 2 | Blocking command accounted only once in commandstats after timeout 3 | 4 | -- Streams not supported 5 | -- See: https://github.com/RedisLabs/redisraft/issues/59 6 | /.*XREAD.* 7 | /.*XADD.* 8 | /.*XRANGE.* 9 | COPY basic usage for stream 10 | COPY basic usage for stream-cgroups 11 | Keyspace notifications: stream events test 12 | lazy free a stream with all types of metadata 13 | lazy free a stream with deleted cgroup 14 | -- depends on streams 15 | Blocking commands ignores the timeout 16 | 17 | -- Timeouts and termination of Lua scripts is not possible when scripts are 18 | -- replicated and executed by individual nodes. 19 | /.*SCRIPT KILL.* 20 | Timedout script does not cause a false dead client 21 | Timedout script link is still usable after Lua returns 22 | /function kill 23 | /script kill 24 | /test wrong subcommand 25 | 26 | -- RAFT command prefix shows up in SLOWLOG. 27 | SLOWLOG - Rewritten commands are logged as their original command 28 | SLOWLOG - blocking command is reported only after unblocked 29 | 30 | -- RAFT command prefix shows up in MONITOR 31 | MONITOR can log executed commands 32 | MONITOR can log commands issued by the scripting engine 33 | MONITOR can log commands issued by functions 34 | MONITOR correctly handles multi-exec cases 35 | MONITOR log blocked command only once 36 | 37 | -- TODO: check what's wrong 38 | UNLINK can reclaim memory in background 39 | 40 | -- ACL test fails because we prepend "raft" string to the command 41 | Script ACL check 42 | 43 | -- WATCH (multi/exec) not supported 44 | /.*MULTI.* 45 | /.*EXEC.* 46 | /.*WATCH.* 47 | SMOVE only notify dstset when the addition is successful 48 | FLUSHALL is able to touch the watched keys 49 | FLUSHDB is able to touch the watched keys 50 | client evicted due to watched key list 51 | FLUSHALL does not touch non affected keys 52 | FLUSHDB does not touch non affected keys 53 | SWAPDB is able to touch the watched keys that exist 54 | SWAPDB is able to touch the watched keys that do not exist 55 | 56 | -- After fixing this: https://github.com/RedisLabs/redisraft/issues/367 57 | -- We don't need to skip this test as it doesn't actually configure a replica. 58 | not enough good replicas 59 | 60 | -- We can't handle normal plain eval (without flags) the same way as redis aborting oom on demand 61 | Consistent eval error reporting 62 | 63 | 64 | -- These three tests were introduced by https://github.com/redis/redis/pull/12326 65 | -- Probably, it needs a fix to make them work when commands are executed via RM_Call() 66 | -- See related ticket: https://redislabs.atlassian.net/browse/RR-323 67 | publish to self inside multi 68 | publish to self inside script 69 | unsubscribe inside multi, and publish to self -------------------------------------------------------------------------------- /tests/tmp/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedisLabs/redisraft/ade4aa8e6aa5c3b21678a1998309825f06567d4f/tests/tmp/.gitignore -------------------------------------------------------------------------------- /tests/unit/dut_premble.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct RedisModuleString; 13 | 14 | static inline const char *mock_StringPtrLen(const struct RedisModuleString *s, size_t *len) 15 | { 16 | *len = strlen((char *) s); 17 | return (const char *) s; 18 | } 19 | 20 | static inline struct RedisModuleString *mock_CreateString(const char *s, size_t len) 21 | { 22 | char *buf = malloc(len + 1); 23 | memcpy(buf, s, len); 24 | buf[len] = '\0'; 25 | return (struct RedisModuleString *) buf; 26 | } 27 | 28 | static inline char *mock_Strdup(const char *s) 29 | { 30 | size_t len = strlen(s); 31 | char *buf = malloc(len + 1); 32 | memcpy(buf, s, len); 33 | buf[len] = '\0'; 34 | return buf; 35 | } 36 | 37 | #define RedisModule_Alloc(size) malloc(size) 38 | #define RedisModule_Calloc(nmemb, size) calloc(nmemb, size) 39 | #define RedisModule_Realloc(ptr, size) realloc(ptr, size) 40 | #define RedisModule_Free(ptr) free(ptr) 41 | #define RedisModule_StringPtrLen(__s, __len) mock_StringPtrLen(__s, __len) 42 | #define RedisModule_CreateString(__ctx, __s, __len) mock_CreateString(__s, __len) 43 | #define RedisModule_FreeString(__ctx, __s) free(__s) 44 | #define RedisModule_MonotonicMicroseconds() 0 45 | #define RedisModule_Strdup(__s) mock_Strdup(__s) 46 | #define RedisModule_Log(...) 47 | -------------------------------------------------------------------------------- /tests/unit/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | void test_util(void); 8 | void test_serialization(void); 9 | void test_file(void); 10 | void test_log(void); 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | test_util(); 15 | test_serialization(); 16 | test_file(); 17 | test_log(); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /tests/unit/test.h: -------------------------------------------------------------------------------- 1 | #ifndef REDISRAFT_TEST_H 2 | #define REDISRAFT_TEST_H 3 | 4 | #define test_run(fn) \ 5 | do { \ 6 | struct timespec ts; \ 7 | unsigned long long start, end; \ 8 | printf("[ Running ] %s \n", #fn); \ 9 | \ 10 | clock_gettime(CLOCK_MONOTONIC, &ts); \ 11 | start = ts.tv_sec * (unsigned long long) 1000000000 + ts.tv_nsec; \ 12 | \ 13 | fn(); \ 14 | \ 15 | clock_gettime(CLOCK_MONOTONIC, &ts); \ 16 | end = ts.tv_sec * (unsigned long long) 1000000000 + ts.tv_nsec; \ 17 | \ 18 | printf("[ Passed ] %s in %llu nanoseconds \n", #fn, end - start); \ 19 | } while (0); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /tests/unit/test_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Redis Ltd. 2020 - present 3 | * Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 4 | * the Server Side Public License v1 (SSPLv1). 5 | */ 6 | 7 | #include "test.h" 8 | 9 | #include "../src/redisraft.h" 10 | 11 | #include 12 | #include 13 | 14 | static void test_raftreq_str() 15 | { 16 | for (int i = 1; i < RR_RAFTREQ_MAX; i++) { 17 | assert(RaftReqTypeStr[i] != NULL); 18 | } 19 | } 20 | 21 | static void test_parse_slots() 22 | { 23 | char slots[REDIS_RAFT_HASH_SLOTS] = {0}; 24 | parseHashSlots(slots, "0"); 25 | assert(slots[0] == 1); 26 | for (size_t i = 1; i < REDIS_RAFT_HASH_SLOTS; i++) { 27 | assert(slots[i] == 0); 28 | } 29 | memset(slots, 0, sizeof(slots)); 30 | parseHashSlots(slots, "0-10"); 31 | for (size_t i = 0; i < 11; i++) { 32 | assert(slots[i] == 1); 33 | } 34 | for (size_t i = 11; i < REDIS_RAFT_HASH_SLOTS; i++) { 35 | assert(slots[i] == 0); 36 | } 37 | memset(slots, 0, sizeof(slots)); 38 | parseHashSlots(slots, "0,5-10,16,58-62,100"); 39 | assert(slots[0] == 1); 40 | for (size_t i = 1; i < 5; i++) { 41 | assert(slots[i] == 0); 42 | } 43 | for (size_t i = 5; i < 11; i++) { 44 | assert(slots[i] == 1); 45 | } 46 | for (size_t i = 11; i < 16; i++) { 47 | assert(slots[i] == 0); 48 | } 49 | assert(slots[16] == 1); 50 | for (size_t i = 17; i < 58; i++) { 51 | assert(slots[i] == 0); 52 | } 53 | for (size_t i = 58; i < 63; i++) { 54 | assert(slots[i] == 1); 55 | } 56 | for (size_t i = 63; i < 100; i++) { 57 | assert(slots[i] == 0); 58 | } 59 | assert(slots[100] == 1); 60 | for (size_t i = 101; i < REDIS_RAFT_HASH_SLOTS; i++) { 61 | assert(slots[i] == 0); 62 | } 63 | } 64 | 65 | void test_util() 66 | { 67 | test_run(test_raftreq_str); 68 | test_run(test_parse_slots); 69 | } 70 | -------------------------------------------------------------------------------- /utils/deploy-aws/README.md: -------------------------------------------------------------------------------- 1 | AWS Deployment 2 | ============== 3 | 4 | This directory contains Terraform and Ansible configuration to assist with 5 | automated deployment on AWS. 6 | 7 | The deployment includes N replicas on dedicated EC2 instances, deployed in a 8 | standalone multi-AZ VPC and a single control node from which benchmarks and 9 | other tests can be performed. 10 | 11 | Getting Started 12 | --------------- 13 | 14 | ### Prerequisites 15 | 16 | 1. Terraform: download and install [here](https://www.terraform.io/downloads.html). 17 | 2. Ansible: on Ubuntu, use `apt-get install ansible ansible-mitogen`. 18 | 19 | ### Prepare Terraform configuration 20 | 21 | Copy `sample.tfvars` to `myfile.tfvars` and modify the configuration: 22 | 23 | * Use an existing EC2 key pair name in `key_pair_name`. 24 | * If `region` is changed, make sure `vpc-azs` are updated accordingly. 25 | * Consult `variables.tf` for more information. 26 | 27 | Also, make sure your `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment 28 | variables are set with your AWS credentials. 29 | 30 | ### Deploy 31 | 32 | Run: 33 | 34 | terraform init 35 | terraform apply -var-file=myfile.tfvars 36 | 37 | When completed, the outputs include the public IPs of the control and cluster 38 | nodes. 39 | 40 | ### Login to control node and run benchmarks 41 | 42 | To login to the control node, you will have to have the SSH private key 43 | available and run: 44 | 45 | ssh ubuntu@ 46 | 47 | You can now run `memtier_benchmark`. In order to get information about 48 | endpoints, use: 49 | 50 | ./cluster.sh endpoints 51 | 52 | You can then run a quick benchmark: 53 | 54 | memtier_benchmark --cluster -s -p 55 | 56 | ### Changing configuration 57 | 58 | To change a configuration parameter across all nodes, use: 59 | 60 | ./cluster.sh config set 61 | 62 | Tearing down 63 | ------------ 64 | 65 | To tear down the environment, simply run: 66 | 67 | terraform destroy -var-file=myfile.tfvars 68 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | strategy = mitogen_linear 3 | host_key_checking = False 4 | inventory = ansible/inventory/hosts.cfg 5 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Install core dependencies for building and running stuff. 4 | 5 | - name: Install core packages 6 | apt: name={{ packages }} state=latest update_cache=true 7 | become: true 8 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/common/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | packages: 3 | - build-essential 4 | - libsystemd-dev 5 | - cmake 6 | - autoconf 7 | - automake 8 | - libtool 9 | - libssl-dev 10 | - libevent-dev 11 | - libpcre3-dev 12 | - zlib1g-dev 13 | - pkg-config 14 | - redis-tools 15 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | memtier_benchmark_url: https://github.com/redislabs/memtier_benchmark 3 | memtier_benchmark_version: master 4 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/tasks/cluster.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create cluster script 3 | template: 4 | src: cluster.sh.j2 5 | dest: "{{ ansible_env.HOME }}/cluster.sh" 6 | mode: 0755 7 | 8 | - name: Run cluster init 9 | shell: | 10 | {{ ansible_env.HOME }}/cluster.sh init 11 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Checkout memtier_benchmark 3 | git: 4 | repo: "{{ memtier_benchmark_url }}" 5 | dest: "{{ memtier_benchmark_build_dir }}" 6 | version: "{{ memtier_benchmark_version }}" 7 | 8 | - name: Bootstrap memtier_benchmark autotools 9 | command: 10 | chdir: "{{ memtier_benchmark_build_dir }}" 11 | cmd: autoreconf -ivf 12 | creates: configure 13 | 14 | - name: Configure memtier_benchmark autotools 15 | command: 16 | chdir: "{{ memtier_benchmark_build_dir }}" 17 | cmd: ./configure 18 | creates: Makefile 19 | register: configure 20 | 21 | - name: Build memtier_benchmark 22 | make: 23 | chdir: "{{ memtier_benchmark_build_dir }}" 24 | register: make 25 | when: configure.changed 26 | 27 | - name: Install memtier_benchmark 28 | make: 29 | chdir: "{{ memtier_benchmark_build_dir }}" 30 | target: install 31 | when: make.changed 32 | become: true 33 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: memtier_benchmark.yml 3 | - include: cluster.yml 4 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/tasks/memtier_benchmark.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Checkout memtier_benchmark 3 | git: 4 | repo: "{{ memtier_benchmark_url }}" 5 | dest: "{{ memtier_benchmark_build_dir }}" 6 | version: "{{ memtier_benchmark_version }}" 7 | 8 | - name: Bootstrap memtier_benchmark autotools 9 | command: 10 | chdir: "{{ memtier_benchmark_build_dir }}" 11 | cmd: autoreconf -ivf 12 | creates: configure 13 | 14 | - name: Configure memtier_benchmark autotools 15 | command: 16 | chdir: "{{ memtier_benchmark_build_dir }}" 17 | cmd: ./configure 18 | creates: Makefile 19 | register: configure 20 | 21 | - name: Build memtier_benchmark 22 | command: 23 | chdir: "{{ memtier_benchmark_build_dir }}" 24 | cmd: make -j 25 | creates: memtier_benchmark 26 | register: make 27 | when: configure.changed 28 | 29 | - name: Install memtier_benchmark 30 | make: 31 | chdir: "{{ memtier_benchmark_build_dir }}" 32 | target: install 33 | when: make.changed 34 | become: true 35 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/templates/cluster.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # {{ ansible_managed }} 3 | 4 | # ---- Configuration starts here ---- 5 | 6 | # Node Configuration 7 | NODES="{% for n in groups['nodes'] -%} {{ loop.index }} {% endfor %}" 8 | declare -A NODE_ADDR 9 | {% for n in groups['nodes'] %} 10 | NODE_ADDR[{{ loop.index }}]={{ hostvars[n].ansible_default_ipv4.address }} 11 | {% endfor %} 12 | 13 | # Instance Configuration 14 | PORTS="{% for i in instances -%} {{ i.port }} {% endfor %}" 15 | declare -A INSTANCE_LEADER_NODE 16 | {% for i in instances %} 17 | INSTANCE_LEADER_NODE[{{ i.port }}]={{ i.leader_node }} 18 | {% endfor %} 19 | 20 | # ---- Configuration ends here ---- 21 | 22 | init() { 23 | set -e 24 | 25 | echo "=== Creating cluster on leader nodes ===" 26 | for port in ${PORTS}; do 27 | local addr=${NODE_ADDR[${INSTANCE_LEADER_NODE[$port]}]} 28 | echo -n "- $addr:$port :: " 29 | redis-cli -h ${NODE_ADDR[${INSTANCE_LEADER_NODE[$port]}]} -p $port RAFT.CLUSTER INIT 30 | done 31 | 32 | echo "" 33 | echo "=== Joining other nodes ===" 34 | for port in ${PORTS}; do 35 | local leader_node=${INSTANCE_LEADER_NODE[$port]} 36 | local leader_addr=${NODE_ADDR[$leader_node]} 37 | for node in ${NODES}; do 38 | local addr=${NODE_ADDR[$node]} 39 | [ $node = $leader_node ] && continue # Skip leaders 40 | 41 | echo -n "- $addr:$port -> $leader_addr:$port :: " 42 | redis-cli -h ${addr} -p ${port} RAFT.CLUSTER JOIN ${leader_addr}:${port} 43 | done 44 | done 45 | 46 | echo "" 47 | echo "=== Linking shardgroups ===" 48 | for target_port in ${PORTS}; do 49 | for source_port in ${PORTS}; do 50 | [ $target_port = $source_port ] && continue 51 | local target_port_leader_node=${INSTANCE_LEADER_NODE[$target_port]} 52 | local target_port_leader_addr=${NODE_ADDR[$target_port_leader_node]} 53 | 54 | local source_port_leader_node=${INSTANCE_LEADER_NODE[$source_port]} 55 | local source_port_leader_addr=${NODE_ADDR[$source_port_leader_node]} 56 | 57 | echo -n "- ${target_port_leader_addr}:${target_port} -> ${source_port_leader_addr}:${source_port} :: " 58 | redis-cli \ 59 | -h ${target_port_leader_addr} -p ${target_port} \ 60 | RAFT.SHARDGROUP LINK ${source_port_leader_addr}:${source_port} 61 | done 62 | done 63 | } 64 | 65 | run_command() { 66 | set -e 67 | 68 | for node in $NODES; do 69 | local node_addr=${NODE_ADDR[$node]} 70 | for port in $PORTS; do 71 | echo -n "- $node_addr:$port :: " 72 | redis-cli -h $node_addr -p $port $* 73 | done 74 | done 75 | } 76 | 77 | endpoints() { 78 | echo "" 79 | echo "NODE ADDR PORT" 80 | for node in $NODES; do 81 | for port in $PORTS; do 82 | printf "%-5s %-15s %s\n" $node ${NODE_ADDR[$node]} $port 83 | done 84 | done 85 | } 86 | 87 | usage() { 88 | echo "usage:" 89 | echo "" 90 | echo " cluster.sh init" 91 | echo " Perform first-time cluster initialization." 92 | echo "" 93 | echo " cluster.sh endpoints" 94 | echo " Print information about cluster endpoints." 95 | echo "" 96 | echo " cluster.sh call " 97 | echo " Call a command across all cluster nodes." 98 | exit 2 99 | } 100 | 101 | case "$1" in 102 | init) 103 | init 104 | ;; 105 | endpoints) 106 | endpoints 107 | ;; 108 | call) 109 | shift 110 | run_command $* 111 | ;; 112 | *) 113 | usage 114 | ;; 115 | esac 116 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/control/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | home_dir: "{{ ansible_env.HOME }}" 3 | build_dir: "{{ home_dir }}/build" 4 | memtier_benchmark_build_dir: "{{ build_dir }}/memtier_benchmark" 5 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | redisraft_url: https://github.com/redislabs/redisraft 3 | redisraft_version: master 4 | redis_url: https://github.com/redis/redis 5 | redis_version: unstable 6 | install_dir: /usr/local 7 | runtime_dir: /var/lib/redisraft 8 | runtime_user: redisraft 9 | redisraft_config: 10 | raft.log-fsync: "yes" 11 | raft.quorum-reads: "yes" 12 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reload_systemd 3 | systemd: 4 | daemon_reload: true 5 | become: true 6 | 7 | - name: start_redisraft 8 | systemd: 9 | state: started 10 | name: redisraft.target 11 | become: true 12 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create instance directories 3 | file: 4 | path: "{{ runtime_dir }}/redisraft-{{ item['port'] }}" 5 | owner: "{{ runtime_user }}" 6 | group: "{{ runtime_user }}" 7 | mode: 0755 8 | state: directory 9 | with_items: "{{ instances }}" 10 | become: true 11 | 12 | - name: Create instance config files 13 | template: 14 | src: redis_conf.j2 15 | dest: "{{ runtime_dir }}/redisraft-{{ item['port'] }}/redis.conf" 16 | owner: "{{ runtime_user }}" 17 | group: "{{ runtime_user }}" 18 | mode: 0644 19 | with_items: "{{ instances }}" 20 | become: true 21 | 22 | - name: RedisRaft systemd unit file 23 | template: 24 | src: systemd_redisraft.j2 25 | dest: /lib/systemd/system/redisraft@.service 26 | owner: root 27 | group: root 28 | mode: 0644 29 | notify: 30 | - reload_systemd 31 | - start_redisraft 32 | become: true 33 | 34 | - name: RedisRaft systemd target file 35 | template: 36 | src: systemd_redisraft_target.j2 37 | dest: /lib/systemd/system/redisraft.target 38 | owner: root 39 | group: root 40 | mode: 0644 41 | notify: 42 | - reload_systemd 43 | become: true 44 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create RedisRaft module target dir 3 | file: 4 | path: "{{ install_dir }}/lib" 5 | owner: root 6 | group: root 7 | mode: 0755 8 | state: directory 9 | become: true 10 | 11 | - name: Create user 12 | user: 13 | name: "{{ runtime_user }}" 14 | shell: /bin/bash 15 | state: present 16 | become: true 17 | 18 | - name: Checkout Redis 19 | git: 20 | repo: "{{ redis_url }}" 21 | dest: "{{ redis_build_dir }}" 22 | version: "{{ redis_version }}" 23 | register: checkout_redis 24 | 25 | - name: Build Redis 26 | command: 27 | chdir: "{{ redis_build_dir }}" 28 | cmd: make -j 29 | creates: redis_server 30 | register: build_redis 31 | when: checkout_redis.changed 32 | 33 | - name: Install Redis 34 | make: 35 | chdir: "{{ redis_build_dir }}" 36 | target: install 37 | params: 38 | PREFIX: "{{ install_dir }}" 39 | become: true 40 | when: build_redis.changed 41 | 42 | - name: Checkout RedisRaft 43 | git: 44 | repo: "{{ redisraft_url }}" 45 | dest: "{{ redisraft_build_dir }}" 46 | version: "{{ redisraft_version }}" 47 | recursive: true 48 | register: checkout_redisraft 49 | 50 | - name: Create RedisRaft build dir 51 | file: 52 | path: "{{ redisraft_build_dir }}/build" 53 | state: directory 54 | register: builddir_redisraft 55 | when: checkout_redisraft.changed 56 | 57 | - name: Build RedisRaft 58 | shell: | 59 | cd {{ redisraft_build_dir }}/build 60 | cmake .. 61 | make -j 62 | register: build_redisraft 63 | when: builddir_redisraft.changed 64 | 65 | - name: Install RedisRaft module 66 | copy: 67 | src: "{{ redisraft_build_dir }}/redisraft.so" 68 | remote_src: true 69 | dest: "{{ install_dir }}/lib/redisraft.so" 70 | mode: '0755' 71 | owner: 'root' 72 | become: true 73 | when: build_redisraft.changed 74 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: install.yml 3 | - include: config.yml 4 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/templates/redis_conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | dir {{ runtime_dir }}/redisraft-{{ item['port'] }} 4 | save "" 5 | appendonly no 6 | supervised auto 7 | port {{ item['port'] }} 8 | protected-mode no 9 | dbfilename redis.rdb 10 | loadmodule {{ install_dir }}/lib/redisraft.so 11 | raft.addr {{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:{{ item['port'] }} 12 | raft.slot-config {{ item['slot-config'] }} 13 | raft.sharding yes 14 | {% for k, v in redisraft_config.items() -%} 15 | {{ k }} {{ v }} 16 | {% endfor %} 17 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/templates/systemd_redisraft.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | [Unit] 4 | Description=RedisRaft Instance 5 | After=network.target 6 | 7 | [Service] 8 | WorkingDirectory={{ runtime_dir }}/redisraft-%i 9 | Type=notify 10 | LimitNOFILE=65535 11 | User={{ runtime_user }} 12 | ExecStart={{ install_dir }}/bin/redis-server {{ runtime_dir }}/redisraft-%i/redis.conf 13 | StandardOutput=file:/{{ runtime_dir }}/redisraft-%i/stderr.log 14 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/templates/systemd_redisraft_target.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | [Unit] 4 | Description=RedisRaft Cluster 5 | Requires={% for i in instances -%} redisraft@{{ i.port }}.service {% endfor %} 6 | 7 | [Install] 8 | WantedBy=multi-user.target 9 | 10 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/roles/node/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | home_dir: "{{ ansible_env.HOME }}" 3 | build_dir: "{{ home_dir }}/build" 4 | redisraft_build_dir: "{{ build_dir }}/redisraft" 5 | redis_build_dir: "{{ build_dir }}/redis" 6 | -------------------------------------------------------------------------------- /utils/deploy-aws/ansible/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Deploy everything 4 | 5 | - name: Deploy RedisRaft Cluster 6 | hosts: nodes 7 | remote_user: ubuntu 8 | 9 | roles: 10 | - common 11 | - node 12 | 13 | - name: Deploy Control node 14 | hosts: control 15 | remote_user: ubuntu 16 | 17 | roles: 18 | - common 19 | - control 20 | -------------------------------------------------------------------------------- /utils/deploy-aws/create_instances.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright Redis Ltd. 2020 - present 4 | # Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or 5 | # the Server Side Public License v1 (SSPLv1). 6 | 7 | SHARDS=9 8 | NODES=3 9 | BASE_PORT=5000 10 | OUTFILE=ansible/instances.yml 11 | 12 | abort() { 13 | echo "Error: $*" 14 | exit 1 15 | } 16 | 17 | gen_instances() { 18 | local slots_per_shard=$((16384 / SHARDS)) 19 | echo "instances:" 20 | for ((i = 1; i <= SHARDS; i++)); do 21 | local port=$((BASE_PORT + i - 1)) 22 | local start_slot=$(((i-1) * slots_per_shard)) 23 | local leader_node=$(((i % NODES) + 1)) 24 | if [ $i == $SHARDS ]; then 25 | end_slot=16383 26 | else 27 | end_slot=$((start_slot + slots_per_shard - 1)) 28 | fi 29 | 30 | echo " - {\"port\": $port, \"slot-config\": "$start_slot:$end_slot", \"leader_node\": $leader_node}" 31 | done 32 | } 33 | 34 | while [ $# -gt 0 ]; do 35 | case "$1" in 36 | --nodes) 37 | shift 38 | [ $# -gt 0 ] || abort "Missing --nodes argument" 39 | NODES=$1 40 | ;; 41 | --shards) 42 | shift 43 | [ $# -gt 0 ] || abort "Missing --shards argument" 44 | SHARDS=$1 45 | ;; 46 | --base-port) 47 | shift 48 | [ $# -gt 0 ] || abort "Missing --base-port argument" 49 | BASE_PORT=$1 50 | ;; 51 | --outfile) 52 | shift 53 | [ $# -gt 0 ] || abort "Missing --outfile argument" 54 | OUTFILE="$1" 55 | ;; 56 | *) 57 | abort "Unknown argument $1" 58 | ;; 59 | esac 60 | shift 61 | done 62 | 63 | echo "Creating instances.yml with the following configuration:" 64 | echo "Nodes: $NODES" 65 | echo "Shards: $SHARDS" 66 | echo "Base Port: $BASE_PORT" 67 | 68 | gen_instances > $OUTFILE 69 | -------------------------------------------------------------------------------- /utils/deploy-aws/hosts.tpl: -------------------------------------------------------------------------------- 1 | [nodes] 2 | %{ for i in nodes ~} 3 | ${i.public_ip} 4 | %{ endfor ~} 5 | 6 | [control] 7 | %{ for i in control ~} 8 | ${i.public_ip} 9 | %{ endfor ~} 10 | 11 | -------------------------------------------------------------------------------- /utils/deploy-aws/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | 5 | resource "aws_vpc" "vpc" { 6 | cidr_block = var.vpc-cidr 7 | 8 | tags = { 9 | Name = "vpc-${var.name}" 10 | } 11 | } 12 | 13 | resource "aws_internet_gateway" "gateway" { 14 | vpc_id = aws_vpc.vpc.id 15 | 16 | tags = { 17 | Name = "igw-${var.name}" 18 | } 19 | } 20 | 21 | resource "aws_route" "route" { 22 | route_table_id = aws_vpc.vpc.main_route_table_id 23 | destination_cidr_block = "0.0.0.0/0" 24 | gateway_id = aws_internet_gateway.gateway.id 25 | } 26 | 27 | resource "aws_subnet" "main" { 28 | count = "${length(var.vpc-subnets)}" 29 | vpc_id = aws_vpc.vpc.id 30 | cidr_block = "${var.vpc-subnets[count.index]}" 31 | map_public_ip_on_launch = true 32 | availability_zone = "${var.vpc-azs[count.index]}" 33 | } 34 | 35 | resource "aws_security_group" "default" { 36 | name = "security_group_${var.name}" 37 | description = "Security group for ${var.name}" 38 | vpc_id = aws_vpc.vpc.id 39 | 40 | egress { 41 | from_port = 0 42 | to_port = 0 43 | protocol = -1 44 | cidr_blocks = ["0.0.0.0/0"] 45 | } 46 | 47 | ingress { 48 | from_port = 22 49 | to_port = 22 50 | protocol = "tcp" 51 | cidr_blocks = ["0.0.0.0/0"] 52 | } 53 | 54 | ingress { 55 | from_port = 0 56 | to_port = 65535 57 | protocol = "tcp" 58 | cidr_blocks = [aws_vpc.vpc.cidr_block] 59 | } 60 | } 61 | 62 | data "aws_ami" "ubuntu" { 63 | most_recent = true 64 | 65 | filter { 66 | name = "name" 67 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 68 | } 69 | 70 | owners = ["099720109477"] 71 | } 72 | 73 | resource "aws_instance" "node" { 74 | count = var.replicas 75 | ami = data.aws_ami.ubuntu.id 76 | instance_type = var.instance_type 77 | 78 | subnet_id = aws_subnet.main[count.index].id 79 | vpc_security_group_ids = [aws_security_group.default.id] 80 | 81 | key_name = var.key_pair_name 82 | 83 | tags = { 84 | Name = "${var.name}-node-${count.index}" 85 | } 86 | 87 | root_block_device { 88 | volume_type = var.volume_type 89 | volume_size = var.volume_size 90 | } 91 | } 92 | 93 | resource "aws_instance" "control" { 94 | ami = data.aws_ami.ubuntu.id 95 | instance_type = var.control_instance_type 96 | 97 | subnet_id = aws_subnet.main[0].id 98 | vpc_security_group_ids = [aws_security_group.default.id] 99 | 100 | key_name = var.key_pair_name 101 | 102 | tags = { 103 | Name = "${var.name}-control" 104 | } 105 | 106 | root_block_device { 107 | volume_type = var.control_volume_type 108 | volume_size = var.control_volume_size 109 | } 110 | } 111 | 112 | #### 113 | #### Ansible part. 114 | #### 115 | 116 | # Create an inventory 117 | resource "local_file" "hosts_cfg" { 118 | content = templatefile("${path.module}/hosts.tpl", 119 | { 120 | nodes = aws_instance.node.* 121 | control = aws_instance.control.* 122 | } 123 | ) 124 | filename = "ansible/inventory/hosts.cfg" 125 | } 126 | 127 | # Create instances.yml, used by Ansible to generate configuration. 128 | resource "null_resource" "instances_yml" { 129 | provisioner "local-exec" { 130 | command = "./create_instances.sh --shards ${var.shards} --nodes ${var.replicas} --base-port ${var.base_port}" 131 | } 132 | } 133 | 134 | # Run Ansible to deploy cluster and control nodes 135 | resource "null_resource" "ansible" { 136 | depends_on = [aws_instance.control, aws_instance.node] 137 | provisioner "local-exec" { 138 | command = "ansible-playbook --extra-vars @ansible/instances.yml ansible/site.yml" 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /utils/deploy-aws/output.tf: -------------------------------------------------------------------------------- 1 | output "cluster_node_private_addr" { 2 | description = "Private addresses of cluster nodes" 3 | value = aws_instance.node.*.private_ip 4 | } 5 | 6 | output "cluster_node_public_addr" { 7 | description = "Public addresses of cluster nodes" 8 | value = aws_instance.node.*.public_ip 9 | } 10 | 11 | output "control_node_public_addr" { 12 | description = "Public address of the control node" 13 | value = aws_instance.control.public_ip 14 | } 15 | -------------------------------------------------------------------------------- /utils/deploy-aws/sample.tfvars: -------------------------------------------------------------------------------- 1 | key_pair_name = "" 2 | replicas = 3 3 | region = "eu-west-1" 4 | vpc-azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"] 5 | instance_type = "c5.2xlarge" 6 | volume_type = "gp3" 7 | control_instance_type = "c5.xlarge" 8 | -------------------------------------------------------------------------------- /utils/deploy-aws/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "Suffix appended to resource names by default" 3 | default = "redisraft" 4 | } 5 | 6 | variable "region" { 7 | description = "AWS Region to run in" 8 | } 9 | 10 | variable "replicas" { 11 | description = "Number of instances to set up" 12 | default = 3 13 | } 14 | 15 | variable "vpc-cidr" { 16 | description = "AWS VPC CIDR block to use" 17 | default = "10.0.0.0/16" 18 | } 19 | 20 | variable "vpc-azs" { 21 | type = list 22 | description = "AWS AZs to run in" 23 | } 24 | 25 | variable "vpc-subnets" { 26 | type = list 27 | description = "Subnet addresses to use" 28 | default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] 29 | } 30 | 31 | variable "instance_type" { 32 | description = "AWS EC2 instance type for cluster nodes" 33 | default = "t3.medium" 34 | } 35 | 36 | variable "volume_type" { 37 | description = "AWS EBS volume type for cluster nodes" 38 | default = "gp2" 39 | } 40 | 41 | variable "volume_size" { 42 | description = "AWS EBS volume size (GB) for cluster nodes" 43 | default = "20" 44 | } 45 | 46 | variable "control_instance_type" { 47 | description = "AWS EC2 instance type for control node" 48 | default = "t3.medium" 49 | } 50 | 51 | variable "control_volume_type" { 52 | description = "AWS EBS volume type for control node" 53 | default = "gp2" 54 | } 55 | 56 | variable "control_volume_size" { 57 | description = "AWS EBS volume size (GB) for control node" 58 | default = "20" 59 | } 60 | 61 | variable "key_pair_name" { 62 | type = string 63 | description = "Key pair to use for instances" 64 | } 65 | 66 | variable "user" { 67 | description = "OS user we use" 68 | default = "ubuntu" 69 | } 70 | 71 | variable "base_port" { 72 | description = "Base port we use" 73 | default = 5000 74 | } 75 | 76 | variable "shards" { 77 | description = "Number of shards" 78 | default = 9 79 | } 80 | 81 | -------------------------------------------------------------------------------- /utils/gen-test-certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate some test certificates which are used by the regression test suite: 4 | # 5 | # tests/tls/ca.{crt,key} Self signed CA certificate. 6 | # tests/tls/redis.{crt,key} A certificate with no key usage/policy restrictions. 7 | # tests/tls/client.{crt,key} A certificate restricted for SSL client usage. 8 | # tests/tls/server.{crt,key} A certificate restricted for SSL server usage. 9 | # tests/tls/redis.dh DH Params file. 10 | 11 | generate_cert() { 12 | local dir="$1" 13 | local name="$2" 14 | local cn="$3" 15 | local opts="$4" 16 | local use_passphrase="$5" 17 | 18 | if [ "$use_passphrase" == true ]; then 19 | local gen_opts="-aes256 -passout pass:redisraft" 20 | local req_opts="-passin pass:redisraft" 21 | fi 22 | 23 | local keyfile="${dir}/${name}.key" 24 | local certfile="${dir}/${name}.crt" 25 | 26 | [ -f $keyfile ] || openssl genrsa $gen_opts -out $keyfile 2048 27 | openssl req \ 28 | -new -sha256 \ 29 | $req_opts \ 30 | -subj "/O=Redis Test/CN=$cn" \ 31 | -key $keyfile | \ 32 | openssl x509 \ 33 | -req -sha256 \ 34 | -CA "${dir}/ca.crt" \ 35 | -CAkey "${dir}/ca.key" \ 36 | -CAserial "${dir}/ca.txt" \ 37 | -CAcreateserial \ 38 | -days 365 \ 39 | $opts \ 40 | -out $certfile 41 | } 42 | 43 | DIR="${1:-tests/tls}" 44 | 45 | mkdir -p "$DIR" 46 | 47 | [ -f "$DIR/ca.key" ] || openssl genrsa -out "${DIR}/ca.key" 4096 48 | openssl req \ 49 | -x509 -new -nodes -sha256 \ 50 | -key "${DIR}/ca.key" \ 51 | -days 3650 \ 52 | -subj '/O=Redis Test/CN=Certificate Authority' \ 53 | -out "${DIR}/ca.crt" 54 | 55 | cat > "${DIR}/openssl.cnf" <<_END_ 56 | [ server_cert ] 57 | keyUsage = digitalSignature, keyEncipherment 58 | nsCertType = server 59 | 60 | [ client_cert ] 61 | keyUsage = digitalSignature, keyEncipherment 62 | nsCertType = client 63 | _END_ 64 | 65 | generate_cert "$DIR" client "Client-only" "-extfile ${DIR}/openssl.cnf -extensions client_cert" false 66 | generate_cert "$DIR" server "Server-only" "-extfile ${DIR}/openssl.cnf -extensions server_cert" true 67 | generate_cert "$DIR" redis "Generic-cert" "" true 68 | 69 | [ -f $DIR/redis.dh ] || openssl dhparam -out $DIR/redis.dh 2048 70 | 71 | c_rehash ${DIR} 72 | --------------------------------------------------------------------------------