├── .clang-format ├── .dir-locals.el ├── .github ├── dependabot.yml └── workflows │ ├── build-and-test.yml │ ├── cla-check.yml │ ├── coverity.yml │ ├── downstream.yml │ ├── latest-deps.yml │ ├── linting.yml │ ├── nolz4.yaml │ ├── packages.yml │ └── static.yml ├── .gitignore ├── AUTHORS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile.am ├── README.md ├── README_CH.md ├── SECURITY.md ├── VERSION ├── ac └── .gitignore ├── bt └── request ├── configure.ac ├── contrib └── build-static.sh ├── doc ├── faq.md ├── index.md └── protocol.md ├── dqlite.pc.in ├── include └── dqlite.h ├── m4 ├── .gitignore ├── attributes.m4 ├── ax_ac_append_to_file.m4 ├── ax_ac_print_to_file.m4 ├── ax_add_am_macro_static.m4 ├── ax_am_macros_static.m4 ├── ax_check_compile_flag.m4 ├── ax_check_gnu_make.m4 ├── ax_code_coverage.m4 ├── ax_compare_version.m4 ├── ax_file_escapes.m4 ├── ax_pthread.m4 └── pkg.m4 ├── resources └── stdbool.h ├── src ├── bind.c ├── bind.h ├── client │ ├── protocol.c │ └── protocol.h ├── command.c ├── command.h ├── config.c ├── config.h ├── conn.c ├── conn.h ├── db.c ├── db.h ├── dqlite.c ├── error.c ├── error.h ├── format.c ├── format.h ├── fsm.c ├── fsm.h ├── gateway.c ├── gateway.h ├── leader.c ├── leader.h ├── lib │ ├── addr.c │ ├── addr.h │ ├── assert.h │ ├── buffer.c │ ├── buffer.h │ ├── byte.h │ ├── fs.c │ ├── fs.h │ ├── queue.h │ ├── registry.h │ ├── serialize.h │ ├── sm.c │ ├── sm.h │ ├── threadpool.c │ ├── threadpool.h │ ├── transport.c │ └── transport.h ├── logger.c ├── logger.h ├── message.c ├── message.h ├── metrics.c ├── metrics.h ├── protocol.h ├── query.c ├── query.h ├── raft.h ├── raft │ ├── array.h │ ├── assert.h │ ├── byte.c │ ├── byte.h │ ├── callbacks.c │ ├── callbacks.h │ ├── client.c │ ├── compress.c │ ├── compress.h │ ├── configuration.c │ ├── configuration.h │ ├── convert.c │ ├── convert.h │ ├── election.c │ ├── election.h │ ├── entry.c │ ├── entry.h │ ├── err.c │ ├── err.h │ ├── fixture.c │ ├── flags.c │ ├── flags.h │ ├── heap.c │ ├── heap.h │ ├── log.c │ ├── log.h │ ├── membership.c │ ├── membership.h │ ├── progress.c │ ├── progress.h │ ├── raft.c │ ├── recv.c │ ├── recv.h │ ├── recv_append_entries.c │ ├── recv_append_entries.h │ ├── recv_append_entries_result.c │ ├── recv_append_entries_result.h │ ├── recv_install_snapshot.c │ ├── recv_install_snapshot.h │ ├── recv_request_vote.c │ ├── recv_request_vote.h │ ├── recv_request_vote_result.c │ ├── recv_request_vote_result.h │ ├── recv_timeout_now.c │ ├── recv_timeout_now.h │ ├── replication.c │ ├── replication.h │ ├── request.h │ ├── snapshot.c │ ├── snapshot.h │ ├── start.c │ ├── state.c │ ├── syscall.c │ ├── syscall.h │ ├── tick.c │ ├── tick.h │ ├── utils.h │ ├── uv.c │ ├── uv.h │ ├── uv_append.c │ ├── uv_encoding.c │ ├── uv_encoding.h │ ├── uv_finalize.c │ ├── uv_fs.c │ ├── uv_fs.h │ ├── uv_ip.c │ ├── uv_ip.h │ ├── uv_list.c │ ├── uv_metadata.c │ ├── uv_os.c │ ├── uv_os.h │ ├── uv_prepare.c │ ├── uv_recv.c │ ├── uv_segment.c │ ├── uv_send.c │ ├── uv_snapshot.c │ ├── uv_tcp.c │ ├── uv_tcp.h │ ├── uv_tcp_connect.c │ ├── uv_tcp_listen.c │ ├── uv_timer.c │ ├── uv_truncate.c │ ├── uv_work.c │ ├── uv_writer.c │ └── uv_writer.h ├── registry.c ├── registry.h ├── request.c ├── request.h ├── response.c ├── response.h ├── roles.c ├── roles.h ├── server.c ├── server.h ├── stmt.c ├── stmt.h ├── tracing.c ├── tracing.h ├── translate.c ├── translate.h ├── transport.c ├── transport.h ├── tuple.c ├── tuple.h ├── utils.h ├── vfs.c ├── vfs.h ├── vfs2.c └── vfs2.h └── test ├── integration ├── main.c ├── test_client.c ├── test_cluster.c ├── test_fsm.c ├── test_membership.c ├── test_node.c ├── test_role_management.c ├── test_server.c └── test_stress.c ├── lib ├── client.h ├── cluster.h ├── config.h ├── endpoint.c ├── endpoint.h ├── fault.c ├── fault.h ├── fs.c ├── fs.h ├── heap.c ├── heap.h ├── logger.c ├── logger.h ├── munit.c ├── munit.h ├── raft.h ├── raft_heap.c ├── raft_heap.h ├── registry.h ├── runner.h ├── server.c ├── server.h ├── sqlite.c ├── sqlite.h ├── stmt.h ├── util.h ├── uv.c ├── uv.h └── vfs.h ├── raft ├── fuzzy │ ├── main_core.c │ ├── test_election.c │ ├── test_liveness.c │ ├── test_membership.c │ └── test_replication.c ├── integration │ ├── append_helpers.h │ ├── main_core.c │ ├── main_uv.c │ ├── test_apply.c │ ├── test_assign.c │ ├── test_barrier.c │ ├── test_bootstrap.c │ ├── test_digest.c │ ├── test_election.c │ ├── test_fixture.c │ ├── test_heap.c │ ├── test_init.c │ ├── test_membership.c │ ├── test_recover.c │ ├── test_replication.c │ ├── test_snapshot.c │ ├── test_start.c │ ├── test_strerror.c │ ├── test_tick.c │ ├── test_transfer.c │ ├── test_uv_append.c │ ├── test_uv_bootstrap.c │ ├── test_uv_init.c │ ├── test_uv_load.c │ ├── test_uv_recover.c │ ├── test_uv_recv.c │ ├── test_uv_send.c │ ├── test_uv_set_term.c │ ├── test_uv_snapshot_put.c │ ├── test_uv_tcp_connect.c │ ├── test_uv_tcp_listen.c │ ├── test_uv_timer.c │ ├── test_uv_truncate.c │ ├── test_uv_truncate_snapshot.c │ ├── test_uv_work.c │ └── test_voter_contacts.c ├── lib │ ├── addrinfo.c │ ├── addrinfo.h │ ├── aio.c │ ├── aio.h │ ├── cluster.c │ ├── cluster.h │ ├── dir.c │ ├── dir.h │ ├── fault.c │ ├── fault.h │ ├── fs.sh │ ├── fsm.c │ ├── fsm.h │ ├── heap.c │ ├── heap.h │ ├── loop.c │ ├── loop.h │ ├── macros.h │ ├── munit.c │ ├── munit.h │ ├── runner.h │ ├── snapshot.h │ ├── tcp.c │ ├── tcp.h │ └── uv.h └── unit │ ├── main_core.c │ ├── main_uv.c │ ├── test_byte.c │ ├── test_compress.c │ ├── test_configuration.c │ ├── test_err.c │ ├── test_flags.c │ ├── test_log.c │ ├── test_queue.c │ ├── test_snapshot.c │ ├── test_uv_fs.c │ ├── test_uv_os.c │ └── test_uv_writer.c ├── test_error.c ├── test_integration.c └── unit ├── ext ├── test_uv.c └── test_uv_pool.c ├── lib ├── test_addr.c ├── test_buffer.c ├── test_byte.c ├── test_registry.c ├── test_serialize.c └── test_transport.c ├── main.c ├── test_command.c ├── test_concurrency.c ├── test_conn.c ├── test_gateway.c ├── test_registry.c ├── test_replication.c ├── test_request.c ├── test_role_management.c ├── test_sm.c ├── test_tuple.c ├── test_vfs.c ├── test_vfs2.c └── test_vfs_extra.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | BreakBeforeBraces: Custom 3 | BraceWrapping: 4 | AfterFunction: true 5 | AfterStruct: false 6 | Cpp11BracedListStyle: false 7 | IndentWidth: 8 8 | UseTab: ForContinuationAndIndentation 9 | PointerAlignment: Right 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((nil . ((fill-column . 80))) 2 | (c-mode . ((c-file-style . "linux-tabs-only") 3 | (flycheck-gcc-definitions . ("_GNU_SOURCE")) 4 | (flycheck-clang-definitions . ("_GNU_SOURCE"))))) 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | # for more info see: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "github-actions" 7 | directory: "/" # checks for workflow files in .github/workflows 8 | schedule: 9 | interval: "weekly" 10 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: CI Tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-and-test: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: 13 | - ubuntu-22.04 14 | - ubuntu-24.04 15 | compiler: 16 | - gcc 17 | - clang 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Set up dependencies 24 | run: | 25 | sudo apt update 26 | sudo apt install -y lcov libsqlite3-dev liblz4-dev libuv1-dev 27 | # TODO: remove once the mysterious hang is fixed 28 | sudo apt install -y gdb 29 | 30 | - name: Build dqlite 31 | env: 32 | CC: ${{ matrix.compiler }} 33 | run: | 34 | autoreconf -i 35 | ./configure --enable-debug --enable-code-coverage --enable-sanitize \ 36 | --enable-build-raft 37 | make -j$(nproc) check-norun 38 | 39 | - name: Test 40 | env: 41 | CC: ${{ matrix.compiler }} 42 | LIBDQLITE_TRACE: 1 43 | ASAN_OPTIONS: fast_unwind_on_malloc=0 44 | run: | 45 | make check || (cat test-suite.log && false) 46 | 47 | - name: Coverage 48 | env: 49 | CC: ${{ matrix.compiler }} 50 | if: ${{ matrix.os == 'ubuntu-22.04' && matrix.compiler == 'gcc' }} 51 | run: | 52 | make code-coverage-capture 53 | 54 | - name: Upload coverage to Codecov 55 | uses: codecov/codecov-action@v4 56 | with: 57 | verbose: true 58 | -------------------------------------------------------------------------------- /.github/workflows/cla-check.yml: -------------------------------------------------------------------------------- 1 | name: cla-check 2 | on: [pull_request] 3 | 4 | jobs: 5 | cla-check: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Check if CLA signed 9 | uses: canonical/has-signed-canonical-cla@v2 10 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Download Coverity Build Tool 15 | run: | 16 | wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=canonical/dqlite" -O cov-analysis-linux64.tar.gz 17 | mkdir cov-analysis-linux64 18 | tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 19 | env: 20 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 21 | 22 | - name: Install dependencies 23 | run: | 24 | sudo apt-get update -qq 25 | sudo apt-get install -qq gcc libsqlite3-dev liblz4-dev libuv1-dev 26 | 27 | - name: Run coverity 28 | run: | 29 | export PATH="$(pwd)/cov-analysis-linux64/bin:${PATH}" 30 | 31 | # Configure 32 | autoreconf -i 33 | mkdir build 34 | cd build 35 | ../configure --enable-build-raft 36 | 37 | # Build 38 | cov-build --dir cov-int make -j4 39 | tar czvf dqlite.tgz cov-int 40 | 41 | # Submit the results 42 | curl \ 43 | --form project=canonical/dqlite \ 44 | --form token=${TOKEN} \ 45 | --form email=mathieu.bordere@canonical.com \ 46 | --form file=@dqlite.tgz \ 47 | --form version=master \ 48 | --form description="${GITHUB_SHA}" \ 49 | https://scan.coverity.com/builds?project=canonical/dqlite 50 | env: 51 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 52 | 53 | -------------------------------------------------------------------------------- /.github/workflows/downstream.yml: -------------------------------------------------------------------------------- 1 | name: Downstream checks 2 | 3 | on: 4 | issue_comment: 5 | types: [created, edited] 6 | 7 | jobs: 8 | dqlite: 9 | if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, 'please test downstream') }} 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Install apt deps 13 | run: | 14 | sudo apt-get update -qq 15 | sudo apt-get install -qq automake libtool gcc make liblz4-dev libuv1-dev libsqlite3-dev 16 | 17 | - name: Check out libbacktrace 18 | uses: actions/checkout@v4 19 | with: 20 | repository: ianlancetaylor/libbacktrace 21 | path: libbacktrace 22 | 23 | - name: Install libbacktrace 24 | run: | 25 | cd libbacktrace 26 | autoreconf -i 27 | ./configure 28 | sudo make -j$(nproc) install 29 | sudo ldconfig 30 | 31 | - name: Check out dqlite 32 | uses: actions/checkout@v4 33 | with: 34 | ref: refs/pull/${{ github.event.issue.number }}/merge 35 | path: dqlite 36 | 37 | - name: Install dqlite 38 | run: | 39 | cd dqlite 40 | autoreconf -i 41 | ./configure --enable-debug --enable-sanitize --enable-backtrace --enable-build-raft 42 | sudo make -j$(nproc) 43 | sudo make install 44 | sudo ldconfig 45 | 46 | - name: Install Go 47 | uses: actions/setup-go@v5 48 | 49 | - name: Check out go-dqlite 50 | uses: actions/checkout@v4 51 | with: 52 | repository: canonical/go-dqlite 53 | path: go-dqlite 54 | 55 | - name: Test go-dqlite 56 | env: 57 | GO_DQLITE_MULTITHREAD: '1' 58 | run: | 59 | cd go-dqlite 60 | go get -tags libsqlite3 -t ./... 61 | go test -asan -v ./... 62 | VERBOSE=1 ASAN=-asan ./test/dqlite-demo.sh 63 | VERBOSE=1 ASAN=-asan DISK=1 ./test/dqlite-demo.sh 64 | VERBOSE=1 ASAN=-asan ./test/roles.sh 65 | VERBOSE=1 ASAN=-asan DISK=1 ./test/roles.sh 66 | VERBOSE=1 ASAN=-asan ./test/recover.sh 67 | VERBOSE=1 ASAN=-asan DISK=1 ./test/recover.sh 68 | 69 | jepsen: 70 | if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, 'please test downstream') }} 71 | uses: canonical/jepsen.dqlite/.github/workflows/test-build-run.yml@master 72 | with: 73 | dqlite-ref: refs/pull/${{ github.event.issue.number }}/head 74 | workloads: > 75 | ['append', 'bank', 'set'] 76 | nemeses: > 77 | ['none', 'partition', 'kill', 'stop', 'disk', 'member', 78 | 'partition,stop', 'partition,kill', 'partition,member', 79 | 'packet,stop', 'pause'] 80 | disk: > 81 | ['0'] 82 | -------------------------------------------------------------------------------- /.github/workflows/latest-deps.yml: -------------------------------------------------------------------------------- 1 | name: CI Tests (latest deps) 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-and-test: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Raise aio-max-nr 15 | run: | 16 | sysctl fs.aio-max-nr 17 | sudo sysctl -w fs.aio-max-nr=1000000 18 | 19 | - name: Install latest libuv 20 | run: | 21 | version="$(curl -L 'https://dist.libuv.org/dist' | grep -o 'v[0-9]\.[0-9]\{1,2\}\.[0-9]\{1,2\}' | sort -V -r | head -n1)" 22 | echo "Selected libuv $version" 23 | curl -LO "https://dist.libuv.org/dist/$version/libuv-$version.tar.gz" 24 | tar xzf "libuv-$version.tar.gz" 25 | cd "libuv-$version" 26 | sh autogen.sh 27 | ./configure 28 | make -j4 29 | sudo make install 30 | 31 | - name: Install latest liblz4 32 | run: | 33 | mkdir lz4 34 | cd lz4 35 | git init 36 | git remote add github 'https://github.com/lz4/lz4' 37 | git fetch github 'refs/tags/*:refs/tags/*' 38 | version="$(git tag | sort -V -r | head -n1)" 39 | echo "Selected lz4 $version" 40 | git checkout "$version" 41 | make -j4 42 | sudo make install 43 | 44 | - name: ldconfig 45 | run: | 46 | sudo ldconfig 47 | 48 | - name: Get latest SQLite 49 | run: | 50 | relative="$(curl -L 'https://sqlite.org/download.html' | grep '^PRODUCT' | grep 'amalgamation' | cut -d',' -f3)" 51 | curl -LO "https://sqlite.org/$relative" 52 | name="$(basename "$relative" .zip)" 53 | echo "Selected $name" 54 | unzip "$name.zip" 55 | cd "$name" 56 | cp sqlite3.{c,h} "$GITHUB_WORKSPACE" 57 | 58 | - name: Build dqlite 59 | run: | 60 | autoreconf -i 61 | ./configure --enable-debug --enable-sanitize --enable-build-raft --enable-build-sqlite 62 | make -j4 unit-test integration-test \ 63 | raft-core-fuzzy-test \ 64 | raft-core-integration-test \ 65 | raft-core-unit-test \ 66 | raft-uv-integration-test \ 67 | raft-uv-unit-test 68 | ldd .libs/libdqlite.so 69 | 70 | - name: Test 71 | run: | 72 | export LIBDQLITE_TRACE=1 73 | make check || (cat ./test-suite.log && false) 74 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 2 15 | - name: Install apt dependencies 16 | run: | 17 | sudo apt update 18 | sudo apt install -y libsqlite3-dev liblz4-dev libuv1-dev bear 19 | - uses: KyleMayes/install-llvm-action@master 20 | with: 21 | version: 17 22 | - name: Run clang-format 23 | run: | 24 | find . \( -name '*.c' -or -name '*.h' \) -not -name 'munit.*' -path ./llvm -prune | xargs ./llvm/bin/clang-format --style=file --dry-run -Werror 25 | -------------------------------------------------------------------------------- /.github/workflows/nolz4.yaml: -------------------------------------------------------------------------------- 1 | name: CI Tests (no liblz4) 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up dependencies 15 | run: | 16 | sudo apt update 17 | sudo apt install -y libsqlite3-dev libuv1-dev 18 | sudo apt remove -y liblz4-dev 19 | 20 | - name: Build dqlite (liblz4 not present) 21 | run: | 22 | autoreconf -i 23 | ./configure --enable-build-raft 24 | make -j$(nproc) 25 | make clean 26 | 27 | - name: Build dqlite (liblz4 requested and not present) 28 | run: | 29 | autoreconf -i 30 | ! ./configure --enable-build-raft --with-lz4 31 | - name: Install liblz4 32 | run: | 33 | sudo apt install liblz4-dev 34 | 35 | - name: Build dqlite (liblz4 present but ignored) 36 | run: | 37 | ./configure --enable-build-raft --without-lz4 38 | make -j$(nproc) 39 | ! ldd .libs/libdqlite.so | grep lz4 40 | -------------------------------------------------------------------------------- /.github/workflows/packages.yml: -------------------------------------------------------------------------------- 1 | name: Build PPA source packages 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build: 8 | if: github.repository == 'canonical/dqlite' 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | target: 13 | - focal 14 | - jammy 15 | - noble 16 | - oracular 17 | runs-on: ubuntu-20.04 18 | environment: 19 | name: ppa 20 | steps: 21 | - name: Clone the repositories 22 | run: | 23 | git clone https://github.com/canonical/dqlite 24 | git clone https://github.com/canonical/dqlite-ppa -b dqlite --depth 1 25 | 26 | - name: Setup dependencies 27 | run: | 28 | sudo apt-get update -qq 29 | sudo apt-get install -qq debhelper devscripts gnupg 30 | 31 | # Note for future maintainers: the secret key should be stored 32 | # in the GHA secret in ASCII-armored form, and must not be 33 | # password-protected. 34 | - name: Setup GPG signing key 35 | env: 36 | PPA_SECRET_KEY: ${{ secrets.PPA_SECRET_KEY }} 37 | run: | 38 | echo "$PPA_SECRET_KEY" > private-key.asc 39 | gpg --import --batch private-key.asc 40 | 41 | - name: Delete GPG signing key file 42 | if: always() 43 | run: | 44 | rm -f private-key.asc 45 | 46 | - name: Build source package 47 | env: 48 | DEBFULLNAME: "Github Actions" 49 | DEBEMAIL: "dqlitebot@lists.canonical.com" 50 | TARGET: ${{ matrix.target }} 51 | run: | 52 | cp -R dqlite-ppa/debian dqlite/ 53 | cd dqlite/ 54 | VERSION="$(git describe --tags | sed -e "s/^v//" -e "s/-/+git/")" 55 | dch --create \ 56 | --distribution ${TARGET} \ 57 | --package dqlite \ 58 | --newversion ${VERSION}~${TARGET}1 \ 59 | "Automatic build from Github" 60 | debuild -S -sa -d -k${{ vars.PPA_PUBLIC_KEY }} 61 | 62 | - name: Upload to Launchpad 63 | run: | 64 | dput -U -u ppa:dqlite/dev *.changes 65 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: CI Tests (musl build) 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-and-test: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install dependencies 15 | run: | 16 | sudo apt install -y build-essential automake libtool gettext autopoint tclsh tcl libsqlite3-dev pkg-config git 17 | 18 | - name: Build and test 19 | env: 20 | LIBDQLITE_TRACE: 1 21 | run: | 22 | contrib/build-static.sh || (cat ./test-suite.log && false) 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.gcda 3 | *.gcno 4 | *.la 5 | *.lo 6 | *.log 7 | *.o 8 | *.so 9 | *.trs 10 | .deps 11 | .dirstamp 12 | .libs 13 | Makefile 14 | Makefile.in 15 | aclocal.m4 16 | aminclude_static.am 17 | autom4te*.cache 18 | confdefs.h 19 | config.status 20 | configure 21 | coverage/ 22 | coverage.info 23 | unit-test 24 | integration-test 25 | dqlite.pc 26 | libtool 27 | stamp-h* 28 | sqlite3.c 29 | raft-core-fuzzy-test 30 | raft-core-integration-test 31 | raft-core-unit-test 32 | raft-uv-integration-test 33 | raft-uv-unit-test 34 | .cache/ 35 | compile_commands.json 36 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Unless mentioned otherwise in a specific file's header, all code in this 2 | project is released under the LGPL v3 license. 3 | 4 | The list of authors and contributors can be retrieved from the git 5 | commit history and in some cases, the file headers. 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | dqlite has adopted the [Ubuntu Code of Conduct](coc). 2 | 3 | [coc]: https://ubuntu.com/community/ethos/code-of-conduct 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to dqlite 2 | 3 | The dqlite team welcomes contributions via GitHub pull requests. To get your PR 4 | merged, you need to sign [Canonical's contributor license agreement 5 | (CLA)][cla]. This is straightforward to do once you have an account on 6 | [Launchpad][lp]; if you don't, you can create one [here][signup]. 7 | 8 | [cla]: https://ubuntu.com/legal/contributors 9 | [lp]: https://launchpad.net 10 | [signup]: https://launchpad.net/+login 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM debian:buster-slim as dqlite-lib-builder 2 | FROM ubuntu as dqlite-lib-builder 3 | ARG DEBIAN_FRONTEND="noninteractive" 4 | ENV TZ=Europe/London 5 | ENV LD_LIBRARY_PATH=/usr/local/lib 6 | ENV GOROOT=/usr/local/go 7 | ENV GOPATH=/go 8 | ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH 9 | 10 | RUN apt-get update && apt-get install -y git build-essential dh-autoreconf pkg-config libuv1-dev libsqlite3-dev liblz4-dev tcl8.6 wget 11 | 12 | WORKDIR /opt 13 | 14 | RUN git clone https://github.com/canonical/raft.git && \ 15 | git clone https://github.com/canonical/go-dqlite.git && \ 16 | wget -c https://golang.org/dl/go1.15.2.linux-amd64.tar.gz -O - | tar -xzf - -C /usr/local 17 | 18 | WORKDIR /opt/raft 19 | 20 | RUN autoreconf -i && ./configure && make && make install 21 | 22 | WORKDIR /opt/dqlite 23 | 24 | COPY . . 25 | 26 | RUN autoreconf -i && ./configure && make && make install 27 | 28 | WORKDIR /opt/go-dqlite 29 | 30 | RUN go get -d -v ./... && \ 31 | go install -tags libsqlite3 ./cmd/dqlite-demo && \ 32 | go install -tags libsqlite3 ./cmd/dqlite 33 | 34 | # FROM debian:buster-slim 35 | FROM ubuntu 36 | ARG DEBIAN_FRONTEND="noninteractive" 37 | ENV TZ=Europe/London 38 | ENV LD_LIBRARY_PATH=/usr/local/lib 39 | ENV PATH=/opt:$PATH 40 | 41 | COPY --from=dqlite-lib-builder /go/bin /opt/ 42 | COPY --from=dqlite-lib-builder /usr/local/lib /usr/local/lib 43 | COPY --from=dqlite-lib-builder \ 44 | /usr/lib/x86_64-linux-gnu/libuv.so \ 45 | /usr/lib/x86_64-linux-gnu/libuv.so.1\ 46 | /usr/lib/x86_64-linux-gnu/libuv.so.1.0.0\ 47 | /usr/lib/ 48 | 49 | COPY --from=dqlite-lib-builder \ 50 | /lib/x86_64-linux-gnu/libsqlite3.so \ 51 | /lib/x86_64-linux-gnu/libsqlite3.so.0 \ 52 | /usr/lib/x86_64-linux-gnu/ 53 | -------------------------------------------------------------------------------- /README_CH.md: -------------------------------------------------------------------------------- 1 | 2 | # dqlite 3 | 4 | [![CI Tests](https://github.com/canonical/dqlite/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/canonical/dqlite/actions/workflows/build-and-test.yml) [![codecov](https://codecov.io/gh/canonical/dqlite/branch/master/graph/badge.svg)](https://codecov.io/gh/canonical/dqlite) 5 | 6 | **注意**:中文文档有可能未及时更新,请以最新的英文[readme](./README.md)为准。 7 | 8 | [dqlite](https://dqlite.io)是一个用C语言开发的可嵌入的,支持流复制的数据库引擎,具备高可用性和自动故障转移功能。 9 | 10 | “dqlite”是“distributed SQLite”的简写,即分布式SQLite。意味着dqlite通过网络协议扩展SQLite,将应用程序的各个实例连接在一起,让它们作为一个高可用的集群,而不依赖外部数据库。 11 | 12 | ## 设计亮点 13 | 14 | - 使用[libuv](https://libuv.org/)实现异步单线程的事件循环机制 15 | 16 | - 针对SQLite 原始数据类型优化的自定义网络协议 17 | 18 | - 基于[Raft](https://raft.github.io/)算法的数据复制及其高效[C-raft](https://github.com/canonical/raft)实现 19 | 20 | ## license 21 | 22 | dqlite库是在略微修改的 LGPLv3 版本下发布的,其中包括一个版权例外,允许用户在他们的项目中静态链接这个库的代码并按照自己的条款发布最终作品。如有需要,请查看完整[license](https://github.com/canonical/dqlite/blob/master/LICENSE)文件。 23 | 24 | ## 兼容性 25 | 26 | dqlite 在 Linux 上运行,由于C-raft 的 libuv 后端的实现,需要一个支持 [native async 27 | I/O](https://man7.org/linux/man-pages/man2/io_setup.2.html) 的内核(注意不要和[POSIX AIO](https://man7.org/linux/man-pages/man7/aio.7.html)混淆)。 28 | 29 | ## 尝试使用 30 | 31 | 查看和了解dqlite的最简单方式是使用绑定了Go dqlite的demo样例程序,Go dqlite的使用可以参考它的项目文档[relevant 32 | documentation](https://github.com/canonical/go-dqlite#demo)。 33 | 34 | ## 视频 35 | 36 | 在 FOSDEM 2020 上有一个关于dqlite的演讲视频,您可以在[此处](https://fosdem.org/2020/schedule/event/dqlite/)观看。 37 | 38 | ## 网络协议 39 | 40 | 如果您想编写客户端,请参阅[网络协议](https://dqlite.io/docs/protocol)文档。 41 | 42 | ## 下载 43 | 44 | 如果您使用的是基于 Debian 的系统,您可以从 dqlite 的[dev PPA](https://launchpad.net/~dqlite/+archive/ubuntu/dev) 获得最新的开发版本: 45 | 46 | ```bash 47 | sudo add-apt-repository ppa:dqlite/dev 48 | sudo apt-get update 49 | sudo apt-get install libdqlite-dev 50 | ``` 51 | 52 | ## 源码构建 53 | 54 | 为了编译构建libdqlite,您需要准备: 55 | 56 | - 较新版本的libuv(v1.18.0或之后的版本) 57 | 58 | - 较新版本的sqlite3-dev 59 | 60 | - 构建好的[C-raft](https://github.com/canonical/raft)库 61 | 62 | 您的linux发行版应该已经为您提供了预构建的 libuv 共享库和 libsqlite3-dev,就不需要在下载了,否则还需要下载这两个依赖。 63 | 64 | 对于基于 Debian 的 Linux 发行版,您可以使用以下命令安装构建依赖项: 65 | 66 | ``` 67 | sudo apt install autoconf libuv1-dev liblz4-dev libtool pkg-config build-essential libsqlite3-dev 68 | ``` 69 | 70 | 编译raft库运行如下命令: 71 | 72 | ```bash 73 | git clone https://github.com/canonical/raft.git 74 | cd raft 75 | autoreconf -i 76 | ./configure 77 | make 78 | sudo make install 79 | cd .. 80 | ``` 81 | 82 | 所有依赖的库都下载好后,运行如下命令手动编译dqlite库: 83 | 84 | ```bash 85 | autoreconf -i 86 | ./configure 87 | make 88 | sudo make install 89 | ``` 90 | 91 | ## 注意事项 92 | 93 | 当环境变量LIBRAFT_TRACE在启动时被设置,将启用详细跟踪。 -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # How to report a security issue with dqlite 2 | 3 | If you find a security issue with dqlite, the best way to report it is using 4 | GitHub's private vulnerability reporting. [Here][advisory] is the form to 5 | submit a report, and [here][docs] is the detailed documentation for the GitHub 6 | feature. 7 | 8 | Once you submit a report, the dqlite team will work with you to figure out 9 | whether there is a security issue. If so, we will develop a fix, get a CVE 10 | assigned, and coordinating the release of the fix. The [Ubuntu Security 11 | disclosure and embargo policy][policy] contains more information about what you 12 | can expect during this phase, and what we expect from you. 13 | 14 | [advisory]: https://github.com/canonical/dqlite/security/advisories/new 15 | [docs]: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability 16 | [policy]: https://ubuntu.com/security/disclosure-policy 17 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 -------------------------------------------------------------------------------- /ac/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bt/request: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | 5 | libraft_path="${LIBRAFT_SO_PATH:-/usr/local/lib/libraft.so.2}" 6 | exec bpftrace -I resources -I include $@ - < 8 | 9 | struct request 10 | { 11 | void *data; 12 | int type; 13 | raft_index index; 14 | queue queue; 15 | }; 16 | 17 | uprobe:$libraft_path:lifecycleRequestStart 18 | { 19 | \$req = (struct request *)arg1; 20 | @start_request[\$req->data, \$req->type, \$req->index] = nsecs; 21 | } 22 | 23 | uprobe:$libraft_path:lifecycleRequestEnd 24 | { 25 | \$req = (struct request *)arg1; 26 | \$start = @start_request[\$req->data, \$req->type, \$req->index]; 27 | \$end = nsecs; 28 | @full[\$req->data, \$req->type, \$req->index] = (\$start, \$end); 29 | \$elapsed_msecs = (\$end - \$start) / 1000; 30 | @hist = lhist(\$elapsed_msecs, 100, 1000, 10); 31 | delete(@start_request[\$req->data, \$req->type, \$req->index]); 32 | } 33 | EOF 34 | -------------------------------------------------------------------------------- /doc/faq.md: -------------------------------------------------------------------------------- 1 | Moved to the [website project](https://dqlite.io/docs/faq). 2 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | Moved to the [website project](https://dqlite.io/docs). 2 | -------------------------------------------------------------------------------- /doc/protocol.md: -------------------------------------------------------------------------------- 1 | Moved to the [website project](https://dqlite.io/docs/protocol). 2 | -------------------------------------------------------------------------------- /dqlite.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: dqlite 7 | Description: Distributed SQLite engine 8 | Version: @PACKAGE_VERSION@ 9 | Libs: -L${libdir} -ldqlite 10 | Libs.private: @SQLITE_LIBS@ @UV_LIBS@ @RAFT_LIBS@ 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | *.m4 2 | !attributes.m4 3 | !ax_ac_append_to_file.m4 4 | !ax_ac_print_to_file.m4 5 | !ax_add_am_macro_static.m4 6 | !ax_am_macros_static.m4 7 | !ax_check_compile_flag.m4 8 | !ax_check_gnu_make.m4 9 | !ax_code_coverage.m4 10 | !ax_compare_version.m4 11 | !ax_file_escapes.m4 12 | !ax_pthread.m4 13 | !pkg.m4 14 | -------------------------------------------------------------------------------- /m4/ax_ac_append_to_file.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_ac_append_to_file.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_AC_APPEND_TO_FILE([FILE],[DATA]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Appends the specified data to the specified Autoconf is run. If you want 12 | # to append to a file when configure is run use AX_APPEND_TO_FILE instead. 13 | # 14 | # LICENSE 15 | # 16 | # Copyright (c) 2009 Allan Caffee 17 | # 18 | # Copying and distribution of this file, with or without modification, are 19 | # permitted in any medium without royalty provided the copyright notice 20 | # and this notice are preserved. This file is offered as-is, without any 21 | # warranty. 22 | 23 | #serial 10 24 | 25 | AC_DEFUN([AX_AC_APPEND_TO_FILE],[ 26 | AC_REQUIRE([AX_FILE_ESCAPES]) 27 | m4_esyscmd( 28 | AX_FILE_ESCAPES 29 | [ 30 | printf "%s" "$2" >> "$1" 31 | ]) 32 | ]) 33 | -------------------------------------------------------------------------------- /m4/ax_ac_print_to_file.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_ac_print_to_file.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_AC_PRINT_TO_FILE([FILE],[DATA]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Writes the specified data to the specified file when Autoconf is run. If 12 | # you want to print to a file when configure is run use AX_PRINT_TO_FILE 13 | # instead. 14 | # 15 | # LICENSE 16 | # 17 | # Copyright (c) 2009 Allan Caffee 18 | # 19 | # Copying and distribution of this file, with or without modification, are 20 | # permitted in any medium without royalty provided the copyright notice 21 | # and this notice are preserved. This file is offered as-is, without any 22 | # warranty. 23 | 24 | #serial 10 25 | 26 | AC_DEFUN([AX_AC_PRINT_TO_FILE],[ 27 | m4_esyscmd( 28 | AC_REQUIRE([AX_FILE_ESCAPES]) 29 | [ 30 | printf "%s" "$2" > "$1" 31 | ]) 32 | ]) 33 | -------------------------------------------------------------------------------- /m4/ax_add_am_macro_static.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_add_am_macro_static.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_ADD_AM_MACRO_STATIC([RULE]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Adds the specified rule to $AMINCLUDE. 12 | # 13 | # LICENSE 14 | # 15 | # Copyright (c) 2009 Tom Howard 16 | # Copyright (c) 2009 Allan Caffee 17 | # 18 | # Copying and distribution of this file, with or without modification, are 19 | # permitted in any medium without royalty provided the copyright notice 20 | # and this notice are preserved. This file is offered as-is, without any 21 | # warranty. 22 | 23 | #serial 8 24 | 25 | AC_DEFUN([AX_ADD_AM_MACRO_STATIC],[ 26 | AC_REQUIRE([AX_AM_MACROS_STATIC]) 27 | AX_AC_APPEND_TO_FILE(AMINCLUDE_STATIC,[$1]) 28 | ]) 29 | -------------------------------------------------------------------------------- /m4/ax_am_macros_static.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_am_macros_static.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_AM_MACROS_STATIC 8 | # 9 | # DESCRIPTION 10 | # 11 | # Adds support for macros that create Automake rules. You must manually 12 | # add the following line 13 | # 14 | # include $(top_srcdir)/aminclude_static.am 15 | # 16 | # to your Makefile.am files. 17 | # 18 | # LICENSE 19 | # 20 | # Copyright (c) 2009 Tom Howard 21 | # Copyright (c) 2009 Allan Caffee 22 | # 23 | # Copying and distribution of this file, with or without modification, are 24 | # permitted in any medium without royalty provided the copyright notice 25 | # and this notice are preserved. This file is offered as-is, without any 26 | # warranty. 27 | 28 | #serial 11 29 | 30 | AC_DEFUN([AMINCLUDE_STATIC],[aminclude_static.am]) 31 | 32 | AC_DEFUN([AX_AM_MACROS_STATIC], 33 | [ 34 | AX_AC_PRINT_TO_FILE(AMINCLUDE_STATIC,[ 35 | # ]AMINCLUDE_STATIC[ generated automatically by Autoconf 36 | # from AX_AM_MACROS_STATIC on ]m4_esyscmd([LC_ALL=C date])[ 37 | ]) 38 | ]) 39 | -------------------------------------------------------------------------------- /m4/ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 23 | # 24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2008 Guido U. Draheim 30 | # Copyright (c) 2011 Maarten Bosmans 31 | # 32 | # Copying and distribution of this file, with or without modification, are 33 | # permitted in any medium without royalty provided the copyright notice 34 | # and this notice are preserved. This file is offered as-is, without any 35 | # warranty. 36 | 37 | #serial 6 38 | 39 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 40 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 41 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 42 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 43 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 44 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 45 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 46 | [AS_VAR_SET(CACHEVAR,[yes])], 47 | [AS_VAR_SET(CACHEVAR,[no])]) 48 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 49 | AS_VAR_IF(CACHEVAR,yes, 50 | [m4_default([$2], :)], 51 | [m4_default([$3], :)]) 52 | AS_VAR_POPDEF([CACHEVAR])dnl 53 | ])dnl AX_CHECK_COMPILE_FLAGS 54 | -------------------------------------------------------------------------------- /m4/ax_file_escapes.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_file_escapes.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_FILE_ESCAPES 8 | # 9 | # DESCRIPTION 10 | # 11 | # Writes the specified data to the specified file. 12 | # 13 | # LICENSE 14 | # 15 | # Copyright (c) 2008 Tom Howard 16 | # 17 | # Copying and distribution of this file, with or without modification, are 18 | # permitted in any medium without royalty provided the copyright notice 19 | # and this notice are preserved. This file is offered as-is, without any 20 | # warranty. 21 | 22 | #serial 8 23 | 24 | AC_DEFUN([AX_FILE_ESCAPES],[ 25 | AX_DOLLAR="\$" 26 | AX_SRB="\\135" 27 | AX_SLB="\\133" 28 | AX_BS="\\\\" 29 | AX_DQ="\"" 30 | ]) 31 | -------------------------------------------------------------------------------- /resources/stdbool.h: -------------------------------------------------------------------------------- 1 | /*===---- stdbool.h - Standard header for booleans -------------------------=== 2 | * 3 | * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | * See https://llvm.org/LICENSE.txt for license information. 5 | * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | * 7 | *===-----------------------------------------------------------------------=== 8 | */ 9 | 10 | #ifndef __STDBOOL_H 11 | #define __STDBOOL_H 12 | 13 | #define __bool_true_false_are_defined 1 14 | 15 | #if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L 16 | /* FIXME: We should be issuing a deprecation warning here, but cannot yet due 17 | * to system headers which include this header file unconditionally. 18 | */ 19 | #elif !defined(__cplusplus) 20 | #define bool _Bool 21 | #define true 1 22 | #define false 0 23 | #elif defined(__GNUC__) && !defined(__STRICT_ANSI__) 24 | /* Define _Bool as a GNU extension. */ 25 | #define _Bool bool 26 | #if defined(__cplusplus) && __cplusplus < 201103L 27 | /* For C++98, define bool, false, true as a GNU extension. */ 28 | #define bool bool 29 | #define false false 30 | #define true true 31 | #endif 32 | #endif 33 | 34 | #endif /* __STDBOOL_H */ 35 | -------------------------------------------------------------------------------- /src/bind.c: -------------------------------------------------------------------------------- 1 | #include "bind.h" 2 | #include "tuple.h" 3 | 4 | /* Bind a single parameter. */ 5 | static int bind_one(sqlite3_stmt *stmt, int n, struct value *value) 6 | { 7 | int rc; 8 | 9 | switch (value->type) { 10 | case SQLITE_INTEGER: 11 | rc = sqlite3_bind_int64(stmt, n, value->integer); 12 | break; 13 | case SQLITE_FLOAT: 14 | rc = sqlite3_bind_double(stmt, n, value->real); 15 | break; 16 | case SQLITE_BLOB: 17 | rc = sqlite3_bind_blob(stmt, n, value->blob.base, 18 | (int)value->blob.len, 19 | SQLITE_STATIC); 20 | break; 21 | case SQLITE_NULL: 22 | rc = sqlite3_bind_null(stmt, n); 23 | break; 24 | case SQLITE_TEXT: 25 | rc = sqlite3_bind_text(stmt, n, value->text, -1, 26 | SQLITE_STATIC); 27 | break; 28 | case DQLITE_ISO8601: 29 | rc = sqlite3_bind_text(stmt, n, value->text, -1, 30 | SQLITE_STATIC); 31 | break; 32 | case DQLITE_BOOLEAN: 33 | rc = sqlite3_bind_int64(stmt, n, 34 | value->boolean == 0 ? 0 : 1); 35 | break; 36 | default: 37 | rc = DQLITE_PROTO; 38 | break; 39 | } 40 | 41 | return rc; 42 | } 43 | 44 | int bind__params(sqlite3_stmt *stmt, struct cursor *cursor, int format) 45 | { 46 | struct tuple_decoder decoder; 47 | unsigned long i; 48 | int rc; 49 | 50 | assert(format == TUPLE__PARAMS || format == TUPLE__PARAMS32); 51 | 52 | sqlite3_reset(stmt); 53 | 54 | /* If the payload has been fully consumed, it means there are no 55 | * parameters to bind. */ 56 | if (cursor->cap == 0) { 57 | return 0; 58 | } 59 | 60 | rc = tuple_decoder__init(&decoder, 0, format, cursor); 61 | if (rc != 0) { 62 | return rc; 63 | } 64 | for (i = 0; i < tuple_decoder__n(&decoder); i++) { 65 | struct value value; 66 | rc = tuple_decoder__next(&decoder, &value); 67 | if (rc != 0) { 68 | return rc; 69 | } 70 | rc = bind_one(stmt, (int)(i + 1), &value); 71 | if (rc != 0) { 72 | return rc; 73 | } 74 | } 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /src/bind.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Bind statement parameters decoding them from a client request payload. 3 | */ 4 | 5 | #ifndef BIND_H_ 6 | #define BIND_H_ 7 | 8 | #include 9 | 10 | #include "lib/serialize.h" 11 | 12 | /** 13 | * Bind the parameters of the given statement by decoding the given payload. 14 | */ 15 | int bind__params(sqlite3_stmt *stmt, struct cursor *cursor, int format); 16 | 17 | #endif /* BIND_H_*/ 18 | -------------------------------------------------------------------------------- /src/command.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Encode and decode dqlite Raft FSM commands. 3 | */ 4 | 5 | #ifndef COMMAND_H_ 6 | #define COMMAND_H_ 7 | 8 | #include "../include/dqlite.h" 9 | 10 | #include "lib/serialize.h" 11 | #include "raft.h" 12 | 13 | /* Command type codes */ 14 | enum { COMMAND_OPEN = 1, COMMAND_FRAMES, COMMAND_UNDO, COMMAND_CHECKPOINT }; 15 | 16 | /* Hold information about an array of WAL frames. */ 17 | struct frames 18 | { 19 | uint32_t n_pages; 20 | uint16_t page_size; 21 | uint16_t __unused__; 22 | /* TODO: because the sqlite3 replication APIs are asymmetrics, the 23 | * format differs between encode and decode. When encoding data is 24 | * expected to be a sqlite3_wal_replication_frame* array, and when 25 | * decoding it will be a pointer to raw memory which can be further 26 | * decoded with the command_frames__page_numbers() and 27 | * command_frames__pages() helpers. */ 28 | const void *data; 29 | }; 30 | 31 | typedef struct frames frames_t; 32 | 33 | /* Serialization definitions for a raft FSM command. */ 34 | #define COMMAND__DEFINE(LOWER, UPPER, _) \ 35 | SERIALIZE__DEFINE_STRUCT(command_##LOWER, COMMAND__##UPPER); 36 | 37 | #define COMMAND__OPEN(X, ...) X(text, filename, ##__VA_ARGS__) 38 | #define COMMAND__FRAMES(X, ...) \ 39 | X(text, filename, ##__VA_ARGS__) \ 40 | X(uint64, tx_id, ##__VA_ARGS__) \ 41 | X(uint32, truncate, ##__VA_ARGS__) \ 42 | X(uint8, is_commit, ##__VA_ARGS__) \ 43 | X(uint8, __unused1__, ##__VA_ARGS__) \ 44 | X(uint16, __unused2__, ##__VA_ARGS__) \ 45 | X(frames, frames, ##__VA_ARGS__) 46 | #define COMMAND__UNDO(X, ...) X(uint64, tx_id, ##__VA_ARGS__) 47 | #define COMMAND__CHECKPOINT(X, ...) X(text, filename, ##__VA_ARGS__) 48 | 49 | #define COMMAND__TYPES(X, ...) \ 50 | X(open, OPEN, __VA_ARGS__) \ 51 | X(frames, FRAMES, __VA_ARGS__) \ 52 | X(undo, UNDO, __VA_ARGS__) \ 53 | X(checkpoint, CHECKPOINT, __VA_ARGS__) 54 | 55 | COMMAND__TYPES(COMMAND__DEFINE); 56 | 57 | DQLITE_VISIBLE_TO_TESTS int command__encode(int type, 58 | const void *command, 59 | struct raft_buffer *buf); 60 | 61 | DQLITE_VISIBLE_TO_TESTS int command__decode(const struct raft_buffer *buf, 62 | int *type, 63 | void **command); 64 | 65 | DQLITE_VISIBLE_TO_TESTS int command_frames__page_numbers( 66 | const struct command_frames *c, 67 | unsigned long *page_numbers[]); 68 | 69 | DQLITE_VISIBLE_TO_TESTS void command_frames__pages( 70 | const struct command_frames *c, 71 | void **pages); 72 | 73 | #endif /* COMMAND_H_*/ 74 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "../include/dqlite.h" 7 | 8 | #include "./lib/assert.h" 9 | 10 | #include "config.h" 11 | #include "logger.h" 12 | 13 | /* Default heartbeat timeout in milliseconds. 14 | * 15 | * Clients will be disconnected if the server does not receive a heartbeat 16 | * message within this time. */ 17 | #define DEFAULT_HEARTBEAT_TIMEOUT 15000 18 | 19 | /* Default database page size in bytes. */ 20 | #define DEFAULT_PAGE_SIZE 4096 21 | 22 | /* Number of outstanding WAL frames after which a checkpoint is triggered as 23 | * soon as possible. */ 24 | #define DEFAULT_CHECKPOINT_THRESHOLD 1000 25 | 26 | /* For generating unique replication/VFS registration names. 27 | * 28 | * TODO: make this thread safe. */ 29 | static unsigned serial = 1; 30 | 31 | int config__init(struct config *c, 32 | dqlite_node_id id, 33 | const char *address, 34 | const char *raft_dir, 35 | const char *database_dir) 36 | { 37 | int rv; 38 | c->id = id; 39 | c->address = sqlite3_malloc((int)strlen(address) + 1); 40 | if (c->address == NULL) { 41 | return DQLITE_NOMEM; 42 | } 43 | strcpy(c->address, address); 44 | c->heartbeat_timeout = DEFAULT_HEARTBEAT_TIMEOUT; 45 | c->page_size = DEFAULT_PAGE_SIZE; 46 | c->checkpoint_threshold = DEFAULT_CHECKPOINT_THRESHOLD; 47 | rv = snprintf(c->name, sizeof c->name, "dqlite-%u", serial); 48 | assert(rv < (int)(sizeof c->name)); 49 | c->logger.data = NULL; 50 | c->logger.emit = loggerDefaultEmit; 51 | c->failure_domain = 0; 52 | c->weight = 0; 53 | 54 | snprintf(c->raft_dir, sizeof(c->raft_dir), "%s", (raft_dir != NULL) ? raft_dir : ""); 55 | snprintf(c->database_dir, sizeof(c->database_dir), "%s", database_dir); 56 | 57 | c->disk = false; 58 | c->voters = 3; 59 | c->standbys = 0; 60 | c->pool_thread_count = 4; 61 | serial++; 62 | return 0; 63 | } 64 | 65 | void config__close(struct config *c) 66 | { 67 | sqlite3_free(c->address); 68 | } 69 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_ 2 | #define CONFIG_H_ 3 | 4 | #include "logger.h" 5 | 6 | /** 7 | * Value object holding dqlite configuration. 8 | */ 9 | struct config { 10 | dqlite_node_id id; /* Unique instance ID */ 11 | char *address; /* Instance address */ 12 | unsigned heartbeat_timeout; /* In milliseconds */ 13 | unsigned page_size; /* Database page size */ 14 | unsigned checkpoint_threshold; /* In outstanding WAL frames */ 15 | struct logger logger; /* Custom logger */ 16 | char name[256]; /* VFS/replication registriatio name */ 17 | unsigned long long failure_domain; /* User-provided failure domain */ 18 | unsigned long long int weight; /* User-provided node weight */ 19 | char raft_dir[1024]; /* Directory used by raft */ 20 | char database_dir[1024]; /* Data dir for on-disk database */ 21 | bool disk; /* Disk-mode or not */ 22 | int voters; /* Target number of voters */ 23 | int standbys; /* Target number of standbys */ 24 | unsigned pool_thread_count; /* Number of threads in thread pool */ 25 | }; 26 | 27 | /** 28 | * Initialize the config object with required values and set the rest to sane 29 | * defaults. A copy will be made of the given @address. 30 | */ 31 | int config__init(struct config *c, 32 | dqlite_node_id id, 33 | const char *address, 34 | const char *raft_dir, 35 | const char *database_dir); 36 | 37 | /** 38 | * Release any memory held by the config object. 39 | */ 40 | void config__close(struct config *c); 41 | 42 | #endif /* DQLITE_OPTIONS_H */ 43 | -------------------------------------------------------------------------------- /src/conn.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Handle a single client connection. 3 | */ 4 | 5 | #ifndef DQLITE_CONN_H_ 6 | #define DQLITE_CONN_H_ 7 | 8 | #include "lib/buffer.h" 9 | #include "lib/queue.h" 10 | #include "lib/transport.h" 11 | 12 | #include "gateway.h" 13 | #include "message.h" 14 | #include "raft.h" 15 | 16 | /** 17 | * Callbacks. 18 | */ 19 | struct conn; 20 | typedef void (*conn_close_cb)(struct conn *c); 21 | 22 | struct conn 23 | { 24 | struct config *config; 25 | struct raft_uv_transport *uv_transport; /* Raft transport */ 26 | conn_close_cb close_cb; /* Close callback */ 27 | struct transport transport; /* Async network read/write */ 28 | struct gateway gateway; /* Request handler */ 29 | struct buffer read; /* Read buffer */ 30 | struct buffer write; /* Write buffer */ 31 | uint64_t protocol; /* Protocol format version */ 32 | struct message request; /* Request message meta data */ 33 | struct message response; /* Response message meta data */ 34 | struct handle handle; 35 | bool closed; 36 | queue queue; 37 | }; 38 | 39 | /** 40 | * Initialize and start a connection. 41 | * 42 | * If no error is returned, the connection should be considered started. Any 43 | * error occurring after this point will trigger the @close_cb callback. 44 | */ 45 | int conn__start(struct conn *c, 46 | struct config *config, 47 | struct uv_loop_s *loop, 48 | struct registry *registry, 49 | struct raft *raft, 50 | struct uv_stream_s *stream, 51 | struct raft_uv_transport *uv_transport, 52 | conn_close_cb close_cb); 53 | 54 | /** 55 | * Force closing the connection. The close callback will be invoked when it's 56 | * safe to release the memory of the connection object. 57 | */ 58 | void conn__stop(struct conn *c); 59 | 60 | #endif /* DQLITE_CONN_H_ */ 61 | -------------------------------------------------------------------------------- /src/db.h: -------------------------------------------------------------------------------- 1 | /** 2 | * State of a single database. 3 | */ 4 | 5 | #ifndef DB_H_ 6 | #define DB_H_ 7 | 8 | #include 9 | #include "lib/queue.h" 10 | 11 | #include "config.h" 12 | 13 | struct db 14 | { 15 | struct config *config; /* Dqlite configuration */ 16 | char *filename; /* Database filename */ 17 | char *path; /* Used for on-disk db */ 18 | uint32_t cookie; /* Used to bind to the pool's thread */ 19 | sqlite3 *follower; /* Follower connection */ 20 | queue leaders; /* Open leader connections */ 21 | unsigned tx_id; /* Current ongoing transaction ID, if any */ 22 | queue queue; /* Prev/next database, used by the registry */ 23 | int read_lock; /* Lock used by snapshots & checkpoints */ 24 | }; 25 | 26 | /** 27 | * Initialize a database object. 28 | * 29 | * The given @filename will be copied. 30 | * Return 0 on success. 31 | */ 32 | int db__init(struct db *db, struct config *config, const char *filename); 33 | 34 | /** 35 | * Release all memory associated with a database object. 36 | * 37 | * If the follower connection was opened, it will be closed. 38 | */ 39 | void db__close(struct db *db); 40 | 41 | /** 42 | * Open the follower connection associated with this database. 43 | */ 44 | int db__open_follower(struct db *db); 45 | 46 | #endif /* DB_H_*/ 47 | -------------------------------------------------------------------------------- /src/dqlite.c: -------------------------------------------------------------------------------- 1 | #include "../include/dqlite.h" 2 | 3 | #include "vfs.h" 4 | 5 | int dqlite_version_number(void) 6 | { 7 | return DQLITE_VERSION_NUMBER; 8 | } 9 | 10 | int dqlite_vfs_init(sqlite3_vfs *vfs, const char *name) 11 | { 12 | return VfsInit(vfs, name); 13 | } 14 | 15 | int dqlite_vfs_enable_disk(sqlite3_vfs *vfs) 16 | { 17 | return VfsEnableDisk(vfs); 18 | } 19 | 20 | void dqlite_vfs_close(sqlite3_vfs *vfs) 21 | { 22 | VfsClose(vfs); 23 | } 24 | 25 | int dqlite_vfs_poll(sqlite3_vfs *vfs, 26 | const char *filename, 27 | dqlite_vfs_frame **frames, 28 | unsigned *n) 29 | { 30 | return VfsPoll(vfs, filename, frames, n); 31 | } 32 | 33 | int dqlite_vfs_apply(sqlite3_vfs *vfs, 34 | const char *filename, 35 | unsigned n, 36 | unsigned long *page_numbers, 37 | void *frames) 38 | { 39 | return VfsApply(vfs, filename, n, page_numbers, frames); 40 | } 41 | 42 | int dqlite_vfs_abort(sqlite3_vfs *vfs, const char *filename) 43 | { 44 | return VfsAbort(vfs, filename); 45 | } 46 | 47 | int dqlite_vfs_snapshot(sqlite3_vfs *vfs, 48 | const char *filename, 49 | void **data, 50 | size_t *n) 51 | { 52 | return VfsSnapshot(vfs, filename, data, n); 53 | } 54 | 55 | int dqlite_vfs_snapshot_disk(sqlite3_vfs *vfs, 56 | const char *filename, 57 | struct dqlite_buffer bufs[], 58 | unsigned n) 59 | { 60 | return VfsSnapshotDisk(vfs, filename, bufs, n); 61 | } 62 | 63 | int dqlite_vfs_num_pages(sqlite3_vfs *vfs, const char *filename, unsigned *n) 64 | { 65 | return VfsDatabaseNumPages(vfs, filename, false, n); 66 | } 67 | 68 | int dqlite_vfs_shallow_snapshot(sqlite3_vfs *vfs, 69 | const char *filename, 70 | struct dqlite_buffer bufs[], 71 | unsigned n) 72 | { 73 | return VfsShallowSnapshot(vfs, filename, bufs, n); 74 | } 75 | 76 | int dqlite_vfs_restore(sqlite3_vfs *vfs, 77 | const char *filename, 78 | const void *data, 79 | size_t n) 80 | { 81 | return VfsRestore(vfs, filename, data, n); 82 | } 83 | 84 | int dqlite_vfs_restore_disk(sqlite3_vfs *vfs, 85 | const char *filename, 86 | const void *data, 87 | size_t main_size, 88 | size_t wal_size) 89 | { 90 | return VfsDiskRestore(vfs, filename, data, main_size, wal_size); 91 | } 92 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_ERROR_H 2 | #define DQLITE_ERROR_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | /* A message describing the last error occurred on an object */ 9 | typedef char *dqlite__error; 10 | 11 | /* Initialize the error with an empty message */ 12 | void dqlite__error_init(dqlite__error *e); 13 | 14 | /* Release the memory of the error message, if any is set */ 15 | void dqlite__error_close(dqlite__error *e); 16 | 17 | /* Set the error message */ 18 | void dqlite__error_printf(dqlite__error *e, const char *fmt, ...); 19 | 20 | /* Wrap an error with an additional message */ 21 | void dqlite__error_wrapf(dqlite__error *e, 22 | const dqlite__error *cause, 23 | const char *fmt, 24 | ...); 25 | 26 | /* Out of memory error */ 27 | void dqlite__error_oom(dqlite__error *e, const char *msg, ...); 28 | 29 | /* Wrap a system error */ 30 | void dqlite__error_sys(dqlite__error *e, const char *msg); 31 | 32 | /* Wrap an error from libuv */ 33 | void dqlite__error_uv(dqlite__error *e, int err, const char *msg); 34 | 35 | /* Copy the underlying error message. 36 | * 37 | * Client code is responsible of invoking sqlite3_free to deallocate the 38 | * returned string. 39 | */ 40 | int dqlite__error_copy(dqlite__error *e, char **msg); 41 | 42 | /* Whether the error is not set */ 43 | int dqlite__error_is_null(dqlite__error *e); 44 | 45 | /* Whether the error is due to client disconnection */ 46 | int dqlite__error_is_disconnect(dqlite__error *e); 47 | 48 | #endif /* DQLITE_ERROR_H */ 49 | -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | /* Utilities around SQLite file formats. 2 | * 3 | * See https://sqlite.org/fileformat.html. */ 4 | 5 | #ifndef FORMAT_H_ 6 | #define FORMAT_H_ 7 | 8 | #include 9 | #include 10 | 11 | /* Minumum and maximum page size. */ 12 | #define FORMAT__PAGE_SIZE_MIN 512 13 | #define FORMAT__PAGE_SIZE_MAX 65536 14 | 15 | /* Database header size. */ 16 | #define FORMAT__DB_HDR_SIZE 100 17 | 18 | /* Write ahead log header size. */ 19 | #define FORMAT__WAL_HDR_SIZE 32 20 | 21 | /* Write ahead log frame header size. */ 22 | #define FORMAT__WAL_FRAME_HDR_SIZE 24 23 | 24 | /* Number of reader marks in the wal index header. */ 25 | #define FORMAT__WAL_NREADER 5 26 | 27 | /* Given the page size, calculate the size of a full WAL frame (frame header 28 | * plus page data). */ 29 | #define formatWalCalcFrameSize(PAGE_SIZE) \ 30 | (FORMAT__WAL_FRAME_HDR_SIZE + PAGE_SIZE) 31 | 32 | /* Given the page size and the WAL file size, calculate the number of frames it 33 | * has. */ 34 | #define formatWalCalcFramesNumber(PAGE_SIZE, SIZE) \ 35 | ((SIZE - FORMAT__WAL_HDR_SIZE) / formatWalCalcFrameSize(PAGE_SIZE)) 36 | 37 | /* Given the page size, calculate the WAL page number of the frame starting at 38 | * the given offset. */ 39 | #define formatWalCalcFrameIndex(PAGE_SIZE, OFFSET) \ 40 | (formatWalCalcFramesNumber(PAGE_SIZE, OFFSET) + 1) 41 | 42 | /* Restart the header of a WAL file after a checkpoint. */ 43 | void formatWalRestartHeader(uint8_t *header); 44 | 45 | #endif /* FORMAT_H */ 46 | -------------------------------------------------------------------------------- /src/fsm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Dqlite Raft FSM 3 | */ 4 | 5 | #ifndef DQLITE_FSM_H_ 6 | #define DQLITE_FSM_H_ 7 | 8 | #include "config.h" 9 | #include "raft.h" 10 | #include "registry.h" 11 | 12 | /** 13 | * Initialize the given SQLite replication interface with dqlite's raft based 14 | * implementation. 15 | */ 16 | int fsm__init(struct raft_fsm *fsm, 17 | struct config *config, 18 | struct registry *registry); 19 | 20 | /** 21 | * Initialize the given SQLite replication interface with dqlite's on-disk 22 | * raft based implementation. 23 | */ 24 | int fsm__init_disk(struct raft_fsm *fsm, 25 | struct config *config, 26 | struct registry *registry); 27 | 28 | void fsm__close(struct raft_fsm *fsm); 29 | 30 | #endif /* DQLITE_REPLICATION_METHODS_H_ */ 31 | -------------------------------------------------------------------------------- /src/lib/addr.h: -------------------------------------------------------------------------------- 1 | #ifndef ADDR_H_ 2 | #define ADDR_H_ 3 | 4 | #include 5 | 6 | enum { 7 | /* Parse Unix socket addresses in @ notation */ 8 | DQLITE_ADDR_PARSE_UNIX = 1 << 0 9 | }; 10 | 11 | /** Parse a socket address from the string @input. 12 | * 13 | * On success, the resulting address is placed in @addr, and its size is placed 14 | * in @addr_len. If @addr is not large enough (based on the initial value of 15 | * @addr_len) to hold the result, DQLITE_ERROR is returned. 16 | * 17 | * @service should be a string representing a port number, e.g. "8080". 18 | * 19 | * @flags customizes the behavior of the function. Currently the only flag is 20 | * DQLITE_ADDR_PARSE_UNIX: when this is ORed in @flags, AddrParse will also 21 | * parse Unix socket addresses in the form `@NAME`, where NAME may be empty. 22 | * This creates a socket address in the (Linux-specific) "abstract namespace". 23 | */ 24 | int AddrParse(const char *input, 25 | struct sockaddr *addr, 26 | socklen_t *addr_len, 27 | const char *service, 28 | int flags); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/lib/assert.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Define the assert() macro, either as the standard one or the test one. 3 | */ 4 | 5 | #ifndef LIB_ASSERT_H_ 6 | #define LIB_ASSERT_H_ 7 | 8 | #if defined(DQLITE_TEST) 9 | #include "../../test/lib/munit.h" 10 | #define assert(expr) munit_assert(expr) 11 | #elif defined(DQLITE_ASSERT_WITH_BACKTRACE) 12 | #include /* for __assert_fail */ 13 | #include 14 | #include 15 | #undef assert 16 | #define assert(x) \ 17 | do { \ 18 | struct backtrace_state *state_; \ 19 | if (!(x)) { \ 20 | state_ = backtrace_create_state(NULL, 0, NULL, NULL); \ 21 | backtrace_print(state_, 0, stderr); \ 22 | __assert_fail(#x, __FILE__, __LINE__, __func__); \ 23 | } \ 24 | } while (0) 25 | #else 26 | #include 27 | #endif 28 | 29 | #endif /* LIB_ASSERT_H_ */ 30 | -------------------------------------------------------------------------------- /src/lib/buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "buffer.h" 6 | 7 | #include "../../include/dqlite.h" 8 | 9 | /* How large is the buffer currently */ 10 | #define SIZE(B) (B->n_pages * B->page_size) 11 | 12 | /* How many remaining bytes the buffer currently */ 13 | #define CAP(B) (SIZE(B) - B->offset) 14 | 15 | int buffer__init(struct buffer *b) 16 | { 17 | b->page_size = (unsigned)sysconf(_SC_PAGESIZE); 18 | b->n_pages = 1; 19 | b->data = malloc(SIZE(b)); 20 | if (b->data == NULL) { 21 | return DQLITE_NOMEM; 22 | } 23 | b->offset = 0; 24 | return 0; 25 | } 26 | 27 | void buffer__close(struct buffer *b) 28 | { 29 | free(b->data); 30 | } 31 | 32 | /* Ensure that the buffer has at least @size spare bytes */ 33 | static inline bool ensure(struct buffer *b, size_t size) 34 | { 35 | void *data; 36 | uint32_t n_pages = b->n_pages; 37 | 38 | /* Double the buffer until we have enough capacity */ 39 | while (size > CAP(b)) { 40 | b->n_pages *= 2; 41 | } 42 | 43 | /* CAP(b) was insufficient */ 44 | if (b->n_pages > n_pages) { 45 | data = realloc(b->data, SIZE(b)); 46 | if (data == NULL) { 47 | b->n_pages = n_pages; 48 | return false; 49 | } 50 | b->data = data; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | void *buffer__advance(struct buffer *b, size_t size) 57 | { 58 | void *cursor; 59 | 60 | if (!ensure(b, size)) { 61 | return NULL; 62 | } 63 | 64 | cursor = buffer__cursor(b, b->offset); 65 | b->offset += size; 66 | return cursor; 67 | } 68 | 69 | size_t buffer__offset(struct buffer *b) 70 | { 71 | return b->offset; 72 | } 73 | 74 | void *buffer__cursor(struct buffer *b, size_t offset) 75 | { 76 | return b->data + offset; 77 | } 78 | 79 | void buffer__reset(struct buffer *b) 80 | { 81 | b->offset = 0; 82 | } 83 | -------------------------------------------------------------------------------- /src/lib/buffer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * A dynamic buffer which can grow as needed when writing to it. 3 | * 4 | * The buffer size is always a multiple of the OS virtual memory page size, so 5 | * resizing the buffer *should* not incur in memory being copied. 6 | * 7 | * See https://stackoverflow.com/questions/16765389 8 | * 9 | * TODO: consider using mremap. 10 | */ 11 | 12 | #ifndef LIB_BUFFER_H_ 13 | #define LIB_BUFFER_H_ 14 | 15 | #include 16 | 17 | #include "../../include/dqlite.h" 18 | 19 | struct buffer 20 | { 21 | void *data; /* Allocated buffer */ 22 | unsigned page_size; /* Size of an OS page */ 23 | unsigned n_pages; /* Number of pages allocated */ 24 | size_t offset; /* Next byte to write in the buffer */ 25 | }; 26 | 27 | /** 28 | * Initialize the buffer. It will initially have 1 memory page. 29 | */ 30 | DQLITE_VISIBLE_TO_TESTS int buffer__init(struct buffer *b); 31 | 32 | /** 33 | * Release the memory of the buffer. 34 | */ 35 | DQLITE_VISIBLE_TO_TESTS void buffer__close(struct buffer *b); 36 | 37 | /** 38 | * Return a write cursor pointing to the next byte to write, ensuring that the 39 | * buffer has at least @size spare bytes. 40 | * 41 | * Return #NULL in case of out-of-memory errors. 42 | */ 43 | DQLITE_VISIBLE_TO_TESTS void *buffer__advance(struct buffer *b, size_t size); 44 | 45 | /** 46 | * Return the offset of next byte to write. 47 | */ 48 | DQLITE_VISIBLE_TO_TESTS size_t buffer__offset(struct buffer *b); 49 | 50 | /** 51 | * Return a write cursor pointing to the @offset'th byte of the buffer. 52 | */ 53 | DQLITE_VISIBLE_TO_TESTS void *buffer__cursor(struct buffer *b, size_t offset); 54 | 55 | /** 56 | * Reset the write offset of the buffer. 57 | */ 58 | DQLITE_VISIBLE_TO_TESTS void buffer__reset(struct buffer *b); 59 | 60 | #endif /* LIB_BUFFER_H_ */ 61 | -------------------------------------------------------------------------------- /src/lib/fs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../tracing.h" 8 | #include "fs.h" 9 | 10 | int FsEnsureDir(const char *path) 11 | { 12 | int rv; 13 | struct stat st = {0}; 14 | 15 | rv = stat(path, &st); 16 | if (rv == 0) { 17 | if (!S_ISDIR(st.st_mode)) { 18 | tracef("%s is not a directory", path); 19 | return -1; 20 | } 21 | } 22 | 23 | /* Directory does not exist */ 24 | if (rv == -1) { 25 | return mkdir(path, 0755); 26 | } 27 | 28 | return 0; 29 | } 30 | 31 | static int fsRemoveDirFilesNftwFn(const char *path, 32 | const struct stat *sb, 33 | int type, 34 | struct FTW *ftwb) 35 | { 36 | int rv; 37 | 38 | (void)sb; 39 | (void)type; 40 | (void)ftwb; 41 | 42 | rv = 0; 43 | 44 | /* Don't remove directory */ 45 | if (S_ISREG(sb->st_mode)) { 46 | rv = remove(path); 47 | } 48 | 49 | return rv; 50 | } 51 | 52 | int FsRemoveDirFiles(const char *path) 53 | { 54 | int rv; 55 | 56 | rv = nftw(path, fsRemoveDirFilesNftwFn, 10, 57 | FTW_DEPTH | FTW_MOUNT | FTW_PHYS); 58 | return rv; 59 | } 60 | -------------------------------------------------------------------------------- /src/lib/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_LIB_FS_H 2 | #define DQLITE_LIB_FS_H 3 | 4 | /* Create a directory if it does not already exist. */ 5 | int FsEnsureDir(const char *path); 6 | 7 | /* Removes all files from a directory. */ 8 | int FsRemoveDirFiles(const char *path); 9 | 10 | #endif /* DQLITE_LIB_FS_H */ 11 | -------------------------------------------------------------------------------- /src/lib/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_QUEUE_H_ 2 | #define LIB_QUEUE_H_ 3 | 4 | #include /* offsetof */ 5 | 6 | struct queue 7 | { 8 | struct queue *next; 9 | struct queue *prev; 10 | }; 11 | 12 | typedef struct queue queue; 13 | 14 | #define QUEUE_DATA(e, type, field) \ 15 | ((type *)((void *)((char *)(e)-offsetof(type, field)))) 16 | 17 | #define QUEUE_FOREACH(q, h) for ((q) = (h)->next; (q) != (h); (q) = (q)->next) 18 | 19 | static inline void queue_init(struct queue *q) 20 | { 21 | q->next = q; 22 | q->prev = q; 23 | } 24 | 25 | static inline int queue_empty(const struct queue *q) 26 | { 27 | return q == q->next; 28 | } 29 | 30 | static inline struct queue *queue_head(const struct queue *q) 31 | { 32 | return q->next; 33 | } 34 | 35 | static inline struct queue *queue_next(const struct queue *q) 36 | { 37 | return q->next; 38 | } 39 | 40 | static inline struct queue *queue_tail(const struct queue *q) 41 | { 42 | return q->prev; 43 | } 44 | 45 | static inline void queue_add(struct queue *h, struct queue *n) 46 | { 47 | h->prev->next = n->next; 48 | n->next->prev = h->prev; 49 | h->prev = n->prev; 50 | h->prev->next = h; 51 | } 52 | 53 | static inline void queue_split(struct queue *h, 54 | struct queue *q, 55 | struct queue *n) 56 | { 57 | n->prev = h->prev; 58 | n->prev->next = n; 59 | n->next = q; 60 | h->prev = q->prev; 61 | h->prev->next = h; 62 | q->prev = n; 63 | } 64 | 65 | static inline void queue_move(struct queue *h, struct queue *n) 66 | { 67 | if (queue_empty(h)) 68 | queue_init(n); 69 | else 70 | queue_split(h, h->next, n); 71 | } 72 | 73 | static inline void queue_insert_head(struct queue *h, struct queue *q) 74 | { 75 | q->next = h->next; 76 | q->prev = h; 77 | q->next->prev = q; 78 | h->next = q; 79 | } 80 | 81 | static inline void queue_insert_tail(struct queue *h, struct queue *q) 82 | { 83 | q->next = h; 84 | q->prev = h->prev; 85 | q->prev->next = q; 86 | h->prev = q; 87 | } 88 | 89 | static inline void queue_remove(struct queue *q) 90 | { 91 | q->prev->next = q->next; 92 | q->next->prev = q->prev; 93 | } 94 | 95 | #endif /* LIB_QUEUE_H_*/ 96 | -------------------------------------------------------------------------------- /src/lib/sm.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIB_SM__ 2 | #define __LIB_SM__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define BITS(state) (1ULL << (state)) 9 | 10 | #define CHECK(cond) sm_check((cond), __FILE__, __LINE__, #cond) 11 | 12 | #define SM_MAX_NAME_LENGTH 50 13 | #define SM_MAX_ATTR_LENGTH 100 14 | 15 | enum { 16 | SM_PREV_NONE = -1, 17 | /* sizeof(sm_conf::allowed * 8) */ 18 | SM_STATES_MAX = 64, 19 | /* flags */ 20 | SM_INITIAL = 1U << 0, 21 | SM_FAILURE = 1U << 1, 22 | SM_FINAL = 1U << 2, 23 | }; 24 | 25 | struct sm_conf 26 | { 27 | uint32_t flags; 28 | uint64_t allowed; 29 | const char *name; 30 | }; 31 | 32 | struct sm 33 | { 34 | int rc; 35 | int state; 36 | char name[SM_MAX_NAME_LENGTH]; 37 | uint64_t id; 38 | pid_t pid; 39 | bool (*is_locked)(const struct sm *); 40 | bool (*invariant)(const struct sm *, int); 41 | const struct sm_conf *conf; 42 | }; 43 | 44 | void sm_init(struct sm *m, 45 | bool (*invariant)(const struct sm *, int), 46 | /* optional, set NULL if not used */ 47 | bool (*is_locked)(const struct sm *), 48 | const struct sm_conf *conf, 49 | const char *name, 50 | int state); 51 | void sm_fini(struct sm *m); 52 | void sm_move(struct sm *m, int next_state); 53 | void sm_fail(struct sm *m, int fail_state, int rc); 54 | void sm_done(struct sm *m, int good_state, int bad_state, int rc); 55 | int sm_state(const struct sm *m); 56 | bool sm_check(bool b, const char *f, int n, const char *s); 57 | /* Relates one state machine to another for observability. */ 58 | void sm_relate(const struct sm *from, const struct sm *to); 59 | /** 60 | * Records an attribute of a state machine for observability. 61 | */ 62 | void sm_attr(const struct sm *m, const char *k, const char *fmt, ...); 63 | 64 | #endif /* __LIB_SM__ */ 65 | -------------------------------------------------------------------------------- /src/lib/transport.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously read and write buffer from and to the network. 3 | */ 4 | 5 | #ifndef LIB_TRANSPORT_H_ 6 | #define LIB_TRANSPORT_H_ 7 | 8 | #include 9 | 10 | #define TRANSPORT__BADSOCKET 1000 11 | 12 | /** 13 | * Callbacks. 14 | */ 15 | struct transport; 16 | typedef void (*transport_read_cb)(struct transport *t, int status); 17 | typedef void (*transport_write_cb)(struct transport *t, int status); 18 | typedef void (*transport_close_cb)(struct transport *t); 19 | 20 | /** 21 | * Light wrapper around a libuv stream handle, providing a more convenient way 22 | * to read a certain amount of bytes. 23 | */ 24 | struct transport 25 | { 26 | void *data; /* User defined */ 27 | struct uv_stream_s *stream; /* Data stream */ 28 | uv_buf_t read; /* Read buffer */ 29 | uv_write_t write; /* Write request */ 30 | transport_read_cb read_cb; /* Read callback */ 31 | transport_write_cb write_cb; /* Write callback */ 32 | transport_close_cb close_cb; /* Close callback */ 33 | }; 34 | 35 | /** 36 | * Initialize a transport of the appropriate type (TCP or PIPE) attached to the 37 | * given file descriptor. 38 | */ 39 | int transport__init(struct transport *t, struct uv_stream_s *stream); 40 | 41 | /** 42 | * Start closing by the transport. 43 | */ 44 | void transport__close(struct transport *t, transport_close_cb cb); 45 | 46 | /** 47 | * Read from the transport file descriptor until the given buffer is full. 48 | */ 49 | int transport__read(struct transport *t, uv_buf_t *buf, transport_read_cb cb); 50 | 51 | /** 52 | * Write the given buffer to the transport. 53 | */ 54 | int transport__write(struct transport *t, uv_buf_t *buf, transport_write_cb cb); 55 | 56 | /* Create an UV stream object from the given fd. */ 57 | int transport__stream(struct uv_loop_s *loop, 58 | int fd, 59 | struct uv_stream_s **stream); 60 | 61 | #endif /* LIB_TRANSPORT_H_ */ 62 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "logger.h" 5 | 6 | #define EMIT_BUF_LEN 1024 7 | 8 | void loggerDefaultEmit(void *data, int level, const char *fmt, va_list args) 9 | { 10 | char buf[EMIT_BUF_LEN]; 11 | char *cursor = buf; 12 | size_t n; 13 | 14 | (void)data; 15 | 16 | /* First, render the logging level. */ 17 | switch (level) { 18 | case DQLITE_DEBUG: 19 | sprintf(cursor, "[DEBUG]: "); 20 | break; 21 | case DQLITE_INFO: 22 | sprintf(cursor, "[INFO ]: "); 23 | break; 24 | case DQLITE_WARN: 25 | sprintf(cursor, "[WARN ]: "); 26 | break; 27 | case DQLITE_LOG_ERROR: 28 | sprintf(cursor, "[ERROR]: "); 29 | break; 30 | default: 31 | sprintf(cursor, "[ ]: "); 32 | break; 33 | }; 34 | 35 | cursor = buf + strlen(buf); 36 | 37 | /* Then render the message, possibly truncating it. */ 38 | n = EMIT_BUF_LEN - strlen(buf) - 1; 39 | vsnprintf(cursor, n, fmt, args); 40 | 41 | fprintf(stderr, "%s\n", buf); 42 | } 43 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H_ 2 | #define LOGGER_H_ 3 | 4 | #include "raft.h" 5 | 6 | #include "../include/dqlite.h" 7 | 8 | /* Log levels */ 9 | enum { DQLITE_DEBUG = 0, DQLITE_INFO, DQLITE_WARN, DQLITE_LOG_ERROR }; 10 | 11 | /* Function to emit log messages. */ 12 | typedef void (*dqlite_emit)(void *data, 13 | int level, 14 | const char *fmt, 15 | va_list args); 16 | 17 | struct logger 18 | { 19 | void *data; 20 | dqlite_emit emit; 21 | }; 22 | 23 | /* Default implementation of dqlite_emit, using stderr. */ 24 | void loggerDefaultEmit(void *data, int level, const char *fmt, va_list args); 25 | 26 | /* Emit a log message with a certain level. */ 27 | /* #define debugf(L, FORMAT, ...) \ */ 28 | /* logger__emit(L, DQLITE_DEBUG, FORMAT, ##__VA_ARGS__) */ 29 | #define debugf(C, FORMAT, ...) \ 30 | C->gateway.raft->io->emit(C->gateway.raft->io, RAFT_DEBUG, FORMAT, \ 31 | ##__VA_ARGS__) 32 | 33 | #endif /* LOGGER_H_ */ 34 | -------------------------------------------------------------------------------- /src/message.c: -------------------------------------------------------------------------------- 1 | #include "message.h" 2 | 3 | SERIALIZE__IMPLEMENT(message, MESSAGE); 4 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGE_H_ 2 | #define MESSAGE_H_ 3 | 4 | #include "lib/serialize.h" 5 | 6 | /** 7 | * Metadata about an incoming or outgoing RPC message. 8 | */ 9 | #define MESSAGE(X, ...) \ 10 | X(uint32, words, ##__VA_ARGS__) \ 11 | X(uint8, type, ##__VA_ARGS__) \ 12 | X(uint8, schema, ##__VA_ARGS__) \ 13 | X(uint16, extra, ##__VA_ARGS__) 14 | 15 | SERIALIZE__DEFINE(message, MESSAGE); 16 | 17 | #endif /* MESSAGE_H_x */ 18 | -------------------------------------------------------------------------------- /src/metrics.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "./lib/assert.h" 4 | 5 | #include "metrics.h" 6 | 7 | void dqlite__metrics_init(struct dqlite__metrics *m) 8 | { 9 | assert(m != NULL); 10 | 11 | m->requests = 0; 12 | m->duration = 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/metrics.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * Collect various performance metrics. 4 | * 5 | *****************************************************************************/ 6 | 7 | #ifndef DQLITE_METRICS_H 8 | #define DQLITE_METRICS_H 9 | 10 | #include 11 | 12 | struct dqlite__metrics 13 | { 14 | uint64_t requests; /* Total number of requests served. */ 15 | uint64_t duration; /* Total time spent to server requests. */ 16 | }; 17 | 18 | void dqlite__metrics_init(struct dqlite__metrics *m); 19 | 20 | #endif /* DQLITE_METRICS_H */ 21 | -------------------------------------------------------------------------------- /src/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_PROTOCOL_H_ 2 | #define DQLITE_PROTOCOL_H_ 3 | 4 | /* Special datatypes */ 5 | #define DQLITE_UNIXTIME 9 6 | #define DQLITE_ISO8601 10 7 | #define DQLITE_BOOLEAN 11 8 | 9 | #define DQLITE_PROTO 1001 /* Protocol error */ 10 | 11 | /* Role codes */ 12 | enum { DQLITE_VOTER, DQLITE_STANDBY, DQLITE_SPARE }; 13 | 14 | /* Current protocol version */ 15 | #define DQLITE_PROTOCOL_VERSION 1 16 | 17 | /* Legacly pre-1.0 version. */ 18 | #define DQLITE_PROTOCOL_VERSION_LEGACY 0x86104dd760433fe5 19 | 20 | /* Special value indicating that a batch of rows is over, but there are more. */ 21 | #define DQLITE_RESPONSE_ROWS_PART 0xeeeeeeeeeeeeeeee 22 | 23 | /* Special value indicating that the result set is complete. */ 24 | #define DQLITE_RESPONSE_ROWS_DONE 0xffffffffffffffff 25 | 26 | /* Request types */ 27 | enum { 28 | DQLITE_REQUEST_LEADER, 29 | DQLITE_REQUEST_CLIENT, 30 | DQLITE_REQUEST_HEARTBEAT, 31 | DQLITE_REQUEST_OPEN, 32 | DQLITE_REQUEST_PREPARE, 33 | DQLITE_REQUEST_EXEC, 34 | DQLITE_REQUEST_QUERY, 35 | DQLITE_REQUEST_FINALIZE, 36 | DQLITE_REQUEST_EXEC_SQL, 37 | DQLITE_REQUEST_QUERY_SQL, 38 | DQLITE_REQUEST_INTERRUPT, 39 | DQLITE_REQUEST_CONNECT, 40 | DQLITE_REQUEST_ADD, 41 | /* The PROMOTE and ASSIGN requests share a type tag. We expose it under 42 | * two names here to facilitate the macro shenanigans in request.h. */ 43 | DQLITE_REQUEST_PROMOTE_OR_ASSIGN, 44 | DQLITE_REQUEST_ASSIGN = DQLITE_REQUEST_PROMOTE_OR_ASSIGN, 45 | DQLITE_REQUEST_REMOVE, 46 | DQLITE_REQUEST_DUMP, 47 | DQLITE_REQUEST_CLUSTER, 48 | DQLITE_REQUEST_TRANSFER, 49 | DQLITE_REQUEST_DESCRIBE, 50 | DQLITE_REQUEST_WEIGHT 51 | }; 52 | 53 | #define DQLITE_REQUEST_CLUSTER_FORMAT_V0 0 /* ID and address */ 54 | #define DQLITE_REQUEST_CLUSTER_FORMAT_V1 1 /* ID, address and role */ 55 | 56 | #define DQLITE_REQUEST_DESCRIBE_FORMAT_V0 0 /* Failure domain and weight */ 57 | 58 | /* These apply to REQUEST_EXEC, REQUEST_EXEC_SQL, REQUEST_QUERY, and 59 | * REQUEST_QUERY_SQL. */ 60 | #define DQLITE_REQUEST_PARAMS_SCHEMA_V0 0 /* One-byte params count */ 61 | #define DQLITE_REQUEST_PARAMS_SCHEMA_V1 1 /* Four-byte params count */ 62 | 63 | /* These apply to REQUEST_PREPARE and RESPONSE_STMT. */ 64 | 65 | /* At most one statement in request, no tail offset in response */ 66 | #define DQLITE_PREPARE_STMT_SCHEMA_V0 0 67 | /* Any number of statements in request, tail offset in response */ 68 | #define DQLITE_PREPARE_STMT_SCHEMA_V1 1 69 | 70 | /* Response types */ 71 | enum { 72 | DQLITE_RESPONSE_FAILURE, 73 | DQLITE_RESPONSE_SERVER, 74 | DQLITE_RESPONSE_SERVER_LEGACY = DQLITE_RESPONSE_SERVER, 75 | DQLITE_RESPONSE_WELCOME, 76 | DQLITE_RESPONSE_SERVERS, 77 | DQLITE_RESPONSE_DB, 78 | DQLITE_RESPONSE_STMT, 79 | DQLITE_RESPONSE_STMT_WITH_OFFSET = DQLITE_RESPONSE_STMT, 80 | DQLITE_RESPONSE_RESULT, 81 | DQLITE_RESPONSE_ROWS, 82 | DQLITE_RESPONSE_EMPTY, 83 | DQLITE_RESPONSE_FILES, 84 | DQLITE_RESPONSE_METADATA 85 | }; 86 | 87 | #endif /* DQLITE_PROTOCOL_H_ */ 88 | -------------------------------------------------------------------------------- /src/query.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Step through a query progressively encoding a the row tuples. 3 | */ 4 | 5 | #ifndef QUERY_H_ 6 | #define QUERY_H_ 7 | 8 | #include 9 | 10 | #include "lib/buffer.h" 11 | #include "lib/serialize.h" 12 | 13 | /** 14 | * Step through the given query statement progressively encoding the yielded row 15 | * tuples, either until #SQLITE_DONE is returned or a full page of the given 16 | * buffer is filled. 17 | */ 18 | int query__batch(sqlite3_stmt *stmt, struct buffer *buffer); 19 | 20 | #endif /* QUERY_H_*/ 21 | -------------------------------------------------------------------------------- /src/raft/array.h: -------------------------------------------------------------------------------- 1 | /* Macros to manipulate contiguous arrays. */ 2 | 3 | #ifndef ARRAY_H_ 4 | #define ARRAY_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Append item I of type T to array A which currently has N items. 9 | * 10 | * A and N must both by pointers. Set RV to -1 in case of failure. */ 11 | #define ARRAY__APPEND(T, I, A, N, RV) \ 12 | { \ 13 | T *tmp_array; \ 14 | tmp_array = raft_realloc(*A, (*N + 1) * sizeof **A); \ 15 | if (tmp_array != NULL) { \ 16 | (*N)++; \ 17 | *A = tmp_array; \ 18 | (*A)[(*N) - 1] = I; \ 19 | RV = 0; \ 20 | } else { \ 21 | RV = -1; \ 22 | } \ 23 | } 24 | 25 | #endif /* ARRAY_H_ */ 26 | -------------------------------------------------------------------------------- /src/raft/assert.h: -------------------------------------------------------------------------------- 1 | /* Define the assert() macro, either as the standard one or the test one. */ 2 | 3 | #ifndef ASSERT_H_ 4 | #define ASSERT_H_ 5 | 6 | #if defined(RAFT_TEST) 7 | extern void munit_errorf_ex(const char *filename, 8 | int line, 9 | const char *format, 10 | ...); 11 | #define assert(expr) \ 12 | do { \ 13 | if (!expr) { \ 14 | munit_errorf_ex(__FILE__, __LINE__, \ 15 | "assertion failed: ", #expr); \ 16 | } \ 17 | } while (0) 18 | #elif defined(NDEBUG) 19 | #define assert(x) \ 20 | do { \ 21 | (void)sizeof(x); \ 22 | } while (0) 23 | #elif defined(RAFT_ASSERT_WITH_BACKTRACE) 24 | #include /* for __assert_fail */ 25 | #include 26 | #include 27 | #undef assert 28 | #define assert(x) \ 29 | do { \ 30 | struct backtrace_state *state_; \ 31 | if (!(x)) { \ 32 | state_ = backtrace_create_state(NULL, 0, NULL, NULL); \ 33 | backtrace_print(state_, 0, stderr); \ 34 | __assert_fail(#x, __FILE__, __LINE__, __func__); \ 35 | } \ 36 | } while (0) 37 | #else 38 | #include 39 | #endif 40 | 41 | #endif /* ASSERT_H_ */ 42 | -------------------------------------------------------------------------------- /src/raft/callbacks.c: -------------------------------------------------------------------------------- 1 | #include "callbacks.h" 2 | #include "heap.h" 3 | 4 | int raftInitCallbacks(struct raft *r) 5 | { 6 | r->callbacks = 0; 7 | struct raft_callbacks *cbs = RaftHeapCalloc(1, sizeof(*cbs)); 8 | if (cbs == NULL) { 9 | return RAFT_NOMEM; 10 | } 11 | r->callbacks = (uint64_t)(uintptr_t)cbs; 12 | return 0; 13 | } 14 | 15 | void raftDestroyCallbacks(struct raft *r) 16 | { 17 | RaftHeapFree((void *)(uintptr_t)r->callbacks); 18 | r->callbacks = 0; 19 | } 20 | 21 | struct raft_callbacks *raftGetCallbacks(struct raft *r) 22 | { 23 | return (void *)(uintptr_t)r->callbacks; 24 | } 25 | -------------------------------------------------------------------------------- /src/raft/callbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACKS_H_ 2 | #define CALLBACKS_H_ 3 | 4 | #include "../raft.h" 5 | 6 | struct raft_callbacks 7 | { 8 | raft_state_cb state_cb; 9 | }; 10 | 11 | int raftInitCallbacks(struct raft *r); 12 | void raftDestroyCallbacks(struct raft *r); 13 | struct raft_callbacks *raftGetCallbacks(struct raft *r); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/raft/compress.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPRESS_H_ 2 | #define COMPRESS_H_ 3 | 4 | #include "../raft.h" 5 | 6 | #ifdef LZ4F_HEADER_SIZE_MAX 7 | #define LZ4F_HEADER_SIZE_MAX_RAFT LZ4F_HEADER_SIZE_MAX 8 | #else 9 | #define LZ4F_HEADER_SIZE_MAX_RAFT 19UL 10 | #endif 11 | 12 | /* 13 | * Compresses the content of `bufs` into a newly allocated buffer that is 14 | * returned to the caller through `compressed`. Returns a non-0 value upon 15 | * failure. 16 | */ 17 | int Compress(struct raft_buffer bufs[], 18 | unsigned n_bufs, 19 | struct raft_buffer *compressed, 20 | char *errmsg); 21 | 22 | /* 23 | * Decompresses the content of `buf` into a newly allocated buffer that is 24 | * returned to the caller through `decompressed`. Returns a non-0 value upon 25 | * failure. 26 | */ 27 | int Decompress(struct raft_buffer buf, 28 | struct raft_buffer *decompressed, 29 | char *errmsg); 30 | 31 | /* Returns `true` if `data` is compressed, `false` otherwise. */ 32 | bool IsCompressed(const void *data, size_t sz); 33 | 34 | #endif /* COMPRESS_H_ */ 35 | -------------------------------------------------------------------------------- /src/raft/convert.h: -------------------------------------------------------------------------------- 1 | /* Convert from one state to another. */ 2 | 3 | #ifndef CONVERT_H_ 4 | #define CONVERT_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Convert from unavailable, or candidate or leader to follower. 9 | * 10 | * From Figure 3.1: 11 | * 12 | * If election timeout elapses without receiving AppendEntries RPC from 13 | * current leader or granting vote to candidate: convert to candidate. 14 | * 15 | * The above implies that we need to reset the election timer when converting to 16 | * follower. */ 17 | void convertToFollower(struct raft *r); 18 | 19 | /* Convert from follower to candidate, starting a new election. 20 | * 21 | * From Figure 3.1: 22 | * 23 | * On conversion to candidate, start election 24 | * 25 | * If the disrupt_leader flag is true, the server will set the disrupt leader 26 | * flag of the RequestVote messages it sends. */ 27 | int convertToCandidate(struct raft *r, bool disrupt_leader); 28 | 29 | /* Convert from candidate to leader. 30 | * 31 | * From Figure 3.1: 32 | * 33 | * Upon election: send initial empty AppendEntries RPC (heartbeat) to each 34 | * server. 35 | * 36 | * From Section 3.4: 37 | * 38 | * Once a candidate wins an election, it becomes leader. It then sends 39 | * heartbeat messages to all of the other servers to establish its authority 40 | * and prevent new elections. 41 | * 42 | * From Section 3.3: 43 | * 44 | * The leader maintains a nextIndex for each follower, which is the index 45 | * of the next log entry the leader will send to that follower. When a 46 | * leader first comes to power, it initializes all nextIndex values to the 47 | * index just after the last one in its log. */ 48 | int convertToLeader(struct raft *r); 49 | 50 | void convertToUnavailable(struct raft *r); 51 | 52 | #endif /* CONVERT_H_ */ 53 | -------------------------------------------------------------------------------- /src/raft/entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "assert.h" 5 | #include "entry.h" 6 | 7 | void entryBatchesDestroy(struct raft_entry *entries, const size_t n) 8 | { 9 | void *batch = NULL; 10 | size_t i; 11 | if (entries == NULL) { 12 | assert(n == 0); 13 | return; 14 | } 15 | assert(n > 0); 16 | for (i = 0; i < n; i++) { 17 | assert(entries[i].batch != NULL); 18 | if (entries[i].batch != batch) { 19 | batch = entries[i].batch; 20 | raft_free(batch); 21 | } 22 | } 23 | raft_free(entries); 24 | } 25 | 26 | int entryCopy(const struct raft_entry *src, struct raft_entry *dst) 27 | { 28 | dst->term = src->term; 29 | dst->type = src->type; 30 | dst->buf.len = src->buf.len; 31 | dst->buf.base = raft_malloc(dst->buf.len); 32 | if (dst->buf.len > 0 && dst->buf.base == NULL) { 33 | return RAFT_NOMEM; 34 | } 35 | memcpy(dst->buf.base, src->buf.base, dst->buf.len); 36 | dst->batch = NULL; 37 | return 0; 38 | } 39 | 40 | int entryBatchCopy(const struct raft_entry *src, 41 | struct raft_entry **dst, 42 | const size_t n) 43 | { 44 | size_t size = 0; 45 | void *batch; 46 | uint8_t *cursor; 47 | unsigned i; 48 | 49 | if (n == 0) { 50 | *dst = NULL; 51 | return 0; 52 | } 53 | 54 | /* Calculate the total size of the entries content and allocate the 55 | * batch. */ 56 | for (i = 0; i < n; i++) { 57 | size += src[i].buf.len; 58 | } 59 | 60 | batch = raft_malloc(size); 61 | if (batch == NULL) { 62 | return RAFT_NOMEM; 63 | } 64 | 65 | /* Copy the entries. */ 66 | *dst = raft_malloc(n * sizeof **dst); 67 | if (*dst == NULL) { 68 | raft_free(batch); 69 | return RAFT_NOMEM; 70 | } 71 | 72 | cursor = batch; 73 | 74 | for (i = 0; i < n; i++) { 75 | (*dst)[i].term = src[i].term; 76 | (*dst)[i].type = src[i].type; 77 | (*dst)[i].buf.base = cursor; 78 | (*dst)[i].buf.len = src[i].buf.len; 79 | (*dst)[i].batch = batch; 80 | memcpy((*dst)[i].buf.base, src[i].buf.base, src[i].buf.len); 81 | cursor += src[i].buf.len; 82 | } 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /src/raft/entry.h: -------------------------------------------------------------------------------- 1 | #ifndef ENTRY_H_ 2 | #define ENTRY_H_ 3 | 4 | #include "../raft.h" 5 | 6 | /* Release all memory associated with the given entries, including the array 7 | * itself. The entries are supposed to belong to one or more batches. */ 8 | void entryBatchesDestroy(struct raft_entry *entries, size_t n); 9 | 10 | /* Create a copy of a log entry, including its data. */ 11 | int entryCopy(const struct raft_entry *src, struct raft_entry *dst); 12 | 13 | /* Create a single batch of entries containing a copy of the given entries, 14 | * including their data. */ 15 | int entryBatchCopy(const struct raft_entry *src, 16 | struct raft_entry **dst, 17 | size_t n); 18 | 19 | #endif /* ENTRY_H */ 20 | -------------------------------------------------------------------------------- /src/raft/err.c: -------------------------------------------------------------------------------- 1 | #include "err.h" 2 | 3 | #include 4 | 5 | #include "../raft.h" 6 | #include "assert.h" 7 | 8 | #define WRAP_SEP ": " 9 | #define WRAP_SEP_LEN ((size_t)strlen(WRAP_SEP)) 10 | 11 | void errMsgWrap(char *e, const char *format) 12 | { 13 | size_t n = RAFT_ERRMSG_BUF_SIZE; 14 | size_t prefix_n; 15 | size_t prefix_and_sep_n; 16 | size_t trail_n; 17 | size_t i; 18 | 19 | /* Calculate the length of the prefix. */ 20 | prefix_n = strlen(format); 21 | 22 | /* If there isn't enough space for the ": " separator and at least one 23 | * character of the wrapped error message, then just print the prefix. 24 | */ 25 | if (prefix_n >= n - (WRAP_SEP_LEN + 1)) { 26 | /* We explicitly allow truncation here + silence clang about unknown 27 | * warning-group "-Wformat-truncation" */ 28 | #ifdef __GNUC__ 29 | #ifndef __clang__ 30 | #pragma GCC diagnostic push 31 | #pragma GCC diagnostic ignored "-Wformat-truncation" 32 | #endif 33 | #endif 34 | ErrMsgPrintf(e, "%s", format); 35 | #ifdef __GNUC__ 36 | #ifndef __clang__ 37 | #pragma GCC diagnostic pop 38 | #endif 39 | #endif 40 | return; 41 | } 42 | 43 | /* Right-shift the wrapped message, to make room for the prefix. */ 44 | prefix_and_sep_n = prefix_n + WRAP_SEP_LEN; 45 | trail_n = strnlen(e, n - prefix_and_sep_n - 1); 46 | memmove(e + prefix_and_sep_n, e, trail_n); 47 | e[prefix_and_sep_n + trail_n] = 0; 48 | 49 | /* Print the prefix. */ 50 | ErrMsgPrintf(e, "%s", format); 51 | 52 | /* Print the separator. 53 | * 54 | * Avoid using strncpy(e->msg + prefix_n, WRAP_SEP, WRAP_SEP_LEN) since 55 | * it generates a warning. */ 56 | for (i = 0; i < WRAP_SEP_LEN; i++) { 57 | e[prefix_n + i] = WRAP_SEP[i]; 58 | } 59 | } 60 | 61 | #define ERR_CODE_TO_STRING_CASE(CODE, MSG) \ 62 | case CODE: \ 63 | return MSG; 64 | 65 | const char *errCodeToString(int code) 66 | { 67 | switch (code) { 68 | ERR_CODE_TO_STRING_MAP(ERR_CODE_TO_STRING_CASE); 69 | default: 70 | return "unknown error"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/raft/flags.c: -------------------------------------------------------------------------------- 1 | #include "flags.h" 2 | 3 | inline raft_flags flagsSet(raft_flags in, raft_flags flags) 4 | { 5 | return in | flags; 6 | } 7 | 8 | inline raft_flags flagsClear(raft_flags in, raft_flags flags) 9 | { 10 | return in & (~flags); 11 | } 12 | 13 | inline bool flagsIsSet(raft_flags in, raft_flags flag) 14 | { 15 | return (bool)(in & flag); 16 | } 17 | -------------------------------------------------------------------------------- /src/raft/flags.h: -------------------------------------------------------------------------------- 1 | #ifndef FLAGS_H_ 2 | #define FLAGS_H_ 3 | 4 | #include "../raft.h" 5 | 6 | #define RAFT_DEFAULT_FEATURE_FLAGS (0) 7 | 8 | /* Adds the flags @flags to @in and returns the new flags. Multiple flags should 9 | * be combined using the `|` operator. */ 10 | raft_flags flagsSet(raft_flags in, raft_flags flags); 11 | 12 | /* Clears the flags @flags from @in and returns the new flags. Multiple flags 13 | * should be combined using the `|` operator. */ 14 | raft_flags flagsClear(raft_flags in, raft_flags flags); 15 | 16 | /* Returns `true` if the single flag @flag is set in @in, otherwise returns 17 | * `false`. */ 18 | bool flagsIsSet(raft_flags in, raft_flags flag); 19 | 20 | #endif /* FLAGS_H */ 21 | -------------------------------------------------------------------------------- /src/raft/heap.c: -------------------------------------------------------------------------------- 1 | #include "heap.h" 2 | 3 | #include 4 | 5 | #include "../raft.h" 6 | 7 | static void *defaultMalloc(void *data, size_t size) 8 | { 9 | (void)data; 10 | return malloc(size); 11 | } 12 | 13 | static void defaultFree(void *data, void *ptr) 14 | { 15 | (void)data; 16 | free(ptr); 17 | } 18 | 19 | static void *defaultCalloc(void *data, size_t nmemb, size_t size) 20 | { 21 | (void)data; 22 | return calloc(nmemb, size); 23 | } 24 | 25 | static void *defaultRealloc(void *data, void *ptr, size_t size) 26 | { 27 | (void)data; 28 | return realloc(ptr, size); 29 | } 30 | 31 | static void *defaultAlignedAlloc(void *data, size_t alignment, size_t size) 32 | { 33 | (void)data; 34 | return aligned_alloc(alignment, size); 35 | } 36 | 37 | static void defaultAlignedFree(void *data, size_t alignment, void *ptr) 38 | { 39 | (void)alignment; 40 | defaultFree(data, ptr); 41 | } 42 | 43 | static struct raft_heap defaultHeap = { 44 | NULL, /* data */ 45 | defaultMalloc, /* malloc */ 46 | defaultFree, /* free */ 47 | defaultCalloc, /* calloc */ 48 | defaultRealloc, /* realloc */ 49 | defaultAlignedAlloc, /* aligned_alloc */ 50 | defaultAlignedFree /* aligned_free */ 51 | }; 52 | 53 | static struct raft_heap *currentHeap = &defaultHeap; 54 | 55 | void *RaftHeapMalloc(size_t size) 56 | { 57 | return currentHeap->malloc(currentHeap->data, size); 58 | } 59 | 60 | void RaftHeapFree(void *ptr) 61 | { 62 | if (ptr == NULL) { 63 | return; 64 | } 65 | currentHeap->free(currentHeap->data, ptr); 66 | } 67 | 68 | void *RaftHeapCalloc(size_t nmemb, size_t size) 69 | { 70 | return currentHeap->calloc(currentHeap->data, nmemb, size); 71 | } 72 | 73 | void *RaftHeapRealloc(void *ptr, size_t size) 74 | { 75 | return currentHeap->realloc(currentHeap->data, ptr, size); 76 | } 77 | 78 | void *raft_malloc(size_t size) 79 | { 80 | return RaftHeapMalloc(size); 81 | } 82 | 83 | void raft_free(void *ptr) 84 | { 85 | RaftHeapFree(ptr); 86 | } 87 | 88 | void *raft_calloc(size_t nmemb, size_t size) 89 | { 90 | return RaftHeapCalloc(nmemb, size); 91 | } 92 | 93 | void *raft_realloc(void *ptr, size_t size) 94 | { 95 | return RaftHeapRealloc(ptr, size); 96 | } 97 | 98 | void *raft_aligned_alloc(size_t alignment, size_t size) 99 | { 100 | return currentHeap->aligned_alloc(currentHeap->data, alignment, size); 101 | } 102 | 103 | void raft_aligned_free(size_t alignment, void *ptr) 104 | { 105 | currentHeap->aligned_free(currentHeap->data, alignment, ptr); 106 | } 107 | 108 | void raft_heap_set(struct raft_heap *heap) 109 | { 110 | currentHeap = heap; 111 | } 112 | 113 | void raft_heap_set_default(void) 114 | { 115 | currentHeap = &defaultHeap; 116 | } 117 | 118 | const struct raft_heap *raft_heap_get(void) 119 | { 120 | return currentHeap; 121 | } 122 | -------------------------------------------------------------------------------- /src/raft/heap.h: -------------------------------------------------------------------------------- 1 | /* Internal heap APIs. */ 2 | 3 | #ifndef HEAP_H_ 4 | #define HEAP_H_ 5 | 6 | #include 7 | 8 | void *RaftHeapMalloc(size_t size); 9 | 10 | void *RaftHeapCalloc(size_t nmemb, size_t size); 11 | 12 | void *RaftHeapRealloc(void *ptr, size_t size); 13 | 14 | void RaftHeapFree(void *ptr); 15 | 16 | #endif /* HEAP_H_ */ 17 | -------------------------------------------------------------------------------- /src/raft/membership.h: -------------------------------------------------------------------------------- 1 | /* Membership-related APIs. */ 2 | 3 | #ifndef MEMBERSHIP_H_ 4 | #define MEMBERSHIP_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Helper returning an error if the configuration can't be changed, either 9 | * because this node is not the leader or because a configuration change is 10 | * already in progress. */ 11 | int membershipCanChangeConfiguration(struct raft *r); 12 | 13 | /* Populate the given configuration object with the most recent committed 14 | * configuration, the one contained in the entry at 15 | * r->configuration_committed_index. */ 16 | int membershipFetchLastCommittedConfiguration(struct raft *r, 17 | struct raft_configuration *conf); 18 | 19 | /* Update the information about the progress that the non-voting server 20 | * currently being promoted is making in catching with logs. 21 | * 22 | * Return false if the server being promoted did not yet catch-up with logs, and 23 | * true if it did. 24 | * 25 | * This function must be called only by leaders after a @raft_assign request 26 | * has been submitted. */ 27 | bool membershipUpdateCatchUpRound(struct raft *r); 28 | 29 | /* Update the local configuration replacing it with the content of the given 30 | * RAFT_CHANGE entry, which has just been received in as part of an 31 | * AppendEntries RPC request. The uncommitted configuration index will be 32 | * updated accordingly. 33 | * 34 | * It must be called only by followers. */ 35 | int membershipUncommittedChange(struct raft *r, 36 | const raft_index index, 37 | const struct raft_entry *entry); 38 | 39 | /* Rollback any promotion configuration change that was applied locally, but 40 | * failed to be committed. It must be called by followers after they receive an 41 | * AppendEntries RPC request that instructs them to evict the uncommitted entry 42 | * from their log. */ 43 | int membershipRollback(struct raft *r); 44 | 45 | /* Initialize the state of a leadership transfer request. */ 46 | void membershipLeadershipTransferInit(struct raft *r, 47 | struct raft_transfer *req, 48 | raft_id id, 49 | raft_transfer_cb cb); 50 | 51 | /* Start the leadership transfer by sending a TimeoutNow message to the target 52 | * server. */ 53 | int membershipLeadershipTransferStart(struct raft *r); 54 | 55 | /* Finish a leadership transfer (whether successful or not), resetting the 56 | * leadership transfer state and firing the user callback. */ 57 | void membershipLeadershipTransferClose(struct raft *r); 58 | 59 | #endif /* MEMBERSHIP_H_ */ 60 | -------------------------------------------------------------------------------- /src/raft/recv.h: -------------------------------------------------------------------------------- 1 | /* Receive an RPC message. */ 2 | 3 | #ifndef RECV_H_ 4 | #define RECV_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Callback to be passed to the raft_io implementation. It will be invoked upon 9 | * receiving an RPC message. */ 10 | void recvCb(struct raft_io *io, struct raft_message *message); 11 | 12 | /* Compare a request's term with the server's current term. 13 | * 14 | * The match output parameter will be set to 0 if the local term matches the 15 | * request's term, to -1 if the request's term is lower, and to 1 if the 16 | * request's term is higher. */ 17 | void recvCheckMatchingTerms(struct raft *r, raft_term term, int *match); 18 | 19 | /* Bump the current term and possibly step down from candidate or leader 20 | * state. */ 21 | int recvBumpCurrentTerm(struct raft *r, raft_term term); 22 | 23 | /* Common logic for RPC handlers, comparing the request's term with the server's 24 | * current term and possibly deciding to reject the request or step down from 25 | * candidate or leader. 26 | * 27 | * From Section 3.3: 28 | * 29 | * If a candidate or leader discovers that its term is out of date, it 30 | * immediately reverts to follower state. If a server receives a request with 31 | * a stale term number, it rejects the request. 32 | * 33 | * The match output parameter will be set to 0 if the local term matches the 34 | * request's term, to -1 if the request's term is lower, and to 1 if the 35 | * request's term was higher but we have successfully bumped the local one to 36 | * match it (and stepped down to follower in that case, if we were not 37 | * follower already). */ 38 | int recvEnsureMatchingTerms(struct raft *r, raft_term term, int *match); 39 | 40 | /* If different from the current one, update information about the current 41 | * leader. Must be called only by followers. */ 42 | int recvUpdateLeader(struct raft *r, raft_id id, const char *address); 43 | 44 | #endif /* RECV_H_ */ 45 | -------------------------------------------------------------------------------- /src/raft/recv_append_entries.h: -------------------------------------------------------------------------------- 1 | /* Receive an AppendEntries message. */ 2 | 3 | #ifndef RECV_APPEND_ENTRIES_H_ 4 | #define RECV_APPEND_ENTRIES_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Process an AppendEntries RPC from the given server. */ 9 | int recvAppendEntries(struct raft *r, 10 | raft_id id, 11 | const char *address, 12 | const struct raft_append_entries *args); 13 | 14 | #endif /* RECV_APPEND_ENTRIES_H_ */ 15 | -------------------------------------------------------------------------------- /src/raft/recv_append_entries_result.c: -------------------------------------------------------------------------------- 1 | #include "recv_append_entries_result.h" 2 | #include "../tracing.h" 3 | #include "assert.h" 4 | #include "configuration.h" 5 | #include "recv.h" 6 | #include "replication.h" 7 | 8 | int recvAppendEntriesResult(struct raft *r, 9 | const raft_id id, 10 | const char *address, 11 | const struct raft_append_entries_result *result) 12 | { 13 | int match; 14 | const struct raft_server *server; 15 | int rv; 16 | 17 | assert(r != NULL); 18 | assert(id > 0); 19 | assert(address != NULL); 20 | assert(result != NULL); 21 | 22 | tracef( 23 | "self:%llu from:%llu@%s last_log_index:%llu rejected:%llu " 24 | "term:%llu", 25 | r->id, id, address, result->last_log_index, result->rejected, 26 | result->term); 27 | 28 | if (r->state != RAFT_LEADER) { 29 | tracef("local server is not leader -> ignore"); 30 | return 0; 31 | } 32 | 33 | rv = recvEnsureMatchingTerms(r, result->term, &match); 34 | if (rv != 0) { 35 | return rv; 36 | } 37 | 38 | if (match < 0) { 39 | tracef("local term is higher -> ignore "); 40 | return 0; 41 | } 42 | 43 | /* If we have stepped down, abort here. 44 | * 45 | * From Figure 3.1: 46 | * 47 | * [Rules for Servers] All Servers: If RPC request or response 48 | * contains term T > currentTerm: set currentTerm = T, convert to 49 | * follower. 50 | */ 51 | if (match > 0) { 52 | assert(r->state == RAFT_FOLLOWER); 53 | return 0; 54 | } 55 | 56 | assert(result->term == r->current_term); 57 | 58 | /* Ignore responses from servers that have been removed */ 59 | server = configurationGet(&r->configuration, id); 60 | if (server == NULL) { 61 | tracef("unknown server -> ignore"); 62 | return 0; 63 | } 64 | 65 | /* Update the progress of this server, possibly sending further entries. 66 | */ 67 | rv = replicationUpdate(r, server, result); 68 | if (rv != 0) { 69 | return rv; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | #undef tracef 76 | -------------------------------------------------------------------------------- /src/raft/recv_append_entries_result.h: -------------------------------------------------------------------------------- 1 | /* Receive an AppendEntries result message. */ 2 | 3 | #ifndef RECV_APPEND_ENTRIES_RESULT_H_ 4 | #define RECV_APPEND_ENTRIES_RESULT_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Process an AppendEntries RPC result from the given server. */ 9 | int recvAppendEntriesResult(struct raft *r, 10 | raft_id id, 11 | const char *address, 12 | const struct raft_append_entries_result *result); 13 | 14 | #endif /* RECV_APPEND_ENTRIES_RESULT_H_ */ 15 | -------------------------------------------------------------------------------- /src/raft/recv_request_vote.h: -------------------------------------------------------------------------------- 1 | /* RequestVote RPC handler. */ 2 | 3 | #ifndef RECV_REQUEST_VOTE_H_ 4 | #define RECV_REQUEST_VOTE_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Process a RequestVote RPC from the given server. */ 9 | int recvRequestVote(struct raft *r, 10 | raft_id id, 11 | const char *address, 12 | const struct raft_request_vote *args); 13 | 14 | #endif /* RECV_REQUEST_VOTE_H_ */ 15 | -------------------------------------------------------------------------------- /src/raft/recv_request_vote_result.h: -------------------------------------------------------------------------------- 1 | /* Receive a RequestVote result. */ 2 | 3 | #ifndef RECV_REQUEST_VOTE_RESULT_H_ 4 | #define RECV_REQUEST_VOTE_RESULT_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Process a RequestVote RPC result from the given server. */ 9 | int recvRequestVoteResult(struct raft *r, 10 | raft_id id, 11 | const char *address, 12 | const struct raft_request_vote_result *result); 13 | 14 | #endif /* RAFT_RECV_REQUEST_VOTE_RESULT_H_ */ 15 | -------------------------------------------------------------------------------- /src/raft/recv_timeout_now.c: -------------------------------------------------------------------------------- 1 | #include "recv_timeout_now.h" 2 | 3 | #include "../tracing.h" 4 | #include "assert.h" 5 | #include "configuration.h" 6 | #include "convert.h" 7 | #include "log.h" 8 | #include "recv.h" 9 | 10 | int recvTimeoutNow(struct raft *r, 11 | const raft_id id, 12 | const char *address, 13 | const struct raft_timeout_now *args) 14 | { 15 | const struct raft_server *local_server; 16 | raft_index local_last_index; 17 | raft_term local_last_term; 18 | int match; 19 | int rv; 20 | 21 | assert(r != NULL); 22 | assert(id > 0); 23 | assert(args != NULL); 24 | 25 | (void)address; 26 | 27 | tracef( 28 | "self:%llu from:%llu@%s last_log_index:%llu last_log_term:%llu " 29 | "term:%llu", 30 | r->id, id, address, args->last_log_index, args->last_log_term, 31 | args->term); 32 | /* Ignore the request if we are not voters. */ 33 | local_server = configurationGet(&r->configuration, r->id); 34 | if (local_server == NULL || local_server->role != RAFT_VOTER) { 35 | tracef("non-voter"); 36 | return 0; 37 | } 38 | 39 | /* Ignore the request if we are not follower, or we have different 40 | * leader. */ 41 | if (r->state != RAFT_FOLLOWER || 42 | r->follower_state.current_leader.id != id) { 43 | tracef("Ignore - r->state:%d current_leader.id:%llu", r->state, 44 | r->follower_state.current_leader.id); 45 | return 0; 46 | } 47 | 48 | /* Possibly update our term. Ignore the request if it turns out we have 49 | * a higher term. */ 50 | rv = recvEnsureMatchingTerms(r, args->term, &match); 51 | if (rv != 0) { 52 | return rv; 53 | } 54 | if (match < 0) { 55 | return 0; 56 | } 57 | 58 | /* Ignore the request if we our log is not up-to-date. */ 59 | local_last_index = logLastIndex(r->log); 60 | local_last_term = logLastTerm(r->log); 61 | if (local_last_index != args->last_log_index || 62 | local_last_term != args->last_log_term) { 63 | return 0; 64 | } 65 | 66 | /* Finally, ignore the request if we're working on persisting some 67 | * entries. */ 68 | if (r->follower_state.append_in_flight_count > 0) { 69 | return 0; 70 | } 71 | 72 | /* Convert to candidate and start a new election. */ 73 | rv = convertToCandidate(r, true /* disrupt leader */); 74 | if (rv != 0) { 75 | return rv; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | #undef tracef 82 | -------------------------------------------------------------------------------- /src/raft/recv_timeout_now.h: -------------------------------------------------------------------------------- 1 | /* Receive a TimeoutNow message. */ 2 | 3 | #ifndef RECV_TIMEOUT_NOW_H_ 4 | #define RECV_TIMEOUT_NOW_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Process a TimeoutNow RPC from the given server. */ 9 | int recvTimeoutNow(struct raft *r, 10 | raft_id id, 11 | const char *address, 12 | const struct raft_timeout_now *args); 13 | 14 | #endif /* RECV_TIMEOUT_NOW_H_ */ 15 | -------------------------------------------------------------------------------- /src/raft/request.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUEST_H_ 2 | #define REQUEST_H_ 3 | 4 | #include "../lib/sm.h" /* struct sm */ 5 | #include "../raft.h" 6 | 7 | /** 8 | * State machine for a request to append an entry to the raft log. 9 | */ 10 | enum { 11 | REQUEST_START, 12 | REQUEST_COMPLETE, 13 | REQUEST_FAILED, 14 | REQUEST_NR, 15 | }; 16 | 17 | #define A(ident) BITS(REQUEST_##ident) 18 | #define S(ident, allowed_, flags_) \ 19 | [REQUEST_##ident] = { .name = #ident, .allowed = (allowed_), .flags = (flags_) } 20 | 21 | static const struct sm_conf request_states[REQUEST_NR] = { 22 | S(START, A(COMPLETE)|A(FAILED), SM_INITIAL), 23 | S(COMPLETE, 0, SM_FINAL), 24 | S(FAILED, 0, SM_FAILURE|SM_FINAL), 25 | }; 26 | 27 | #undef S 28 | #undef A 29 | 30 | static inline bool request_invariant(const struct sm *sm, int prev) 31 | { 32 | /* The next line exists because otherwise request_states would 33 | * be flagged as unused in translation units that include this 34 | * header but don't reference the static (i.e. that don't call 35 | * sm_init). We could avoid this by just declaring the static 36 | * in this header and defining it in request.c, but that splits 37 | * the details of the state machine up in an ugly way. */ 38 | (void)request_states; 39 | (void)sm; 40 | (void)prev; 41 | return true; 42 | } 43 | 44 | /* Abstract request type */ 45 | struct request { 46 | /* Must be kept in sync with RAFT__REQUEST in raft.h */ 47 | void *data; 48 | int type; 49 | raft_index index; 50 | queue queue; 51 | struct sm sm; 52 | uint8_t req_id[16]; 53 | uint8_t client_id[16]; 54 | uint8_t unique_id[16]; 55 | uint64_t reserved[4]; 56 | }; 57 | 58 | #endif /* REQUEST_H_ */ 59 | -------------------------------------------------------------------------------- /src/raft/snapshot.h: -------------------------------------------------------------------------------- 1 | #ifndef RAFT_SNAPSHOT_H_ 2 | #define RAFT_SNAPSHOT_H_ 3 | 4 | #include "../raft.h" 5 | 6 | /* Release all memory associated with the given snapshot. */ 7 | void snapshotClose(struct raft_snapshot *s); 8 | 9 | /* Like snapshotClose(), but also release the snapshot object itself. */ 10 | void snapshotDestroy(struct raft_snapshot *s); 11 | 12 | /* Restore a snapshot. 13 | * 14 | * This will reset the current state of the server as if the last entry 15 | * contained in the snapshot had just been persisted, committed and applied. 16 | * 17 | * The in-memory log must be empty when calling this function. 18 | * 19 | * If no error occurs, the memory of the snapshot object gets released. */ 20 | int snapshotRestore(struct raft *r, struct raft_snapshot *snapshot); 21 | 22 | /* Make a full deep copy of a snapshot object. 23 | * 24 | * All data buffers in the source snapshot will be compacted in a single buffer 25 | * in the destination snapshot. */ 26 | int snapshotCopy(const struct raft_snapshot *src, struct raft_snapshot *dst); 27 | 28 | #endif /* RAFT_SNAPSHOT_H */ 29 | -------------------------------------------------------------------------------- /src/raft/state.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | #include "configuration.h" 3 | #include "election.h" 4 | #include "log.h" 5 | #include "../lib/queue.h" 6 | 7 | int raft_state(struct raft *r) 8 | { 9 | return r->state; 10 | } 11 | 12 | void raft_leader(struct raft *r, raft_id *id, const char **address) 13 | { 14 | switch (r->state) { 15 | case RAFT_UNAVAILABLE: 16 | case RAFT_CANDIDATE: 17 | *id = 0; 18 | *address = NULL; 19 | return; 20 | case RAFT_FOLLOWER: 21 | *id = r->follower_state.current_leader.id; 22 | *address = r->follower_state.current_leader.address; 23 | return; 24 | case RAFT_LEADER: 25 | if (r->transfer != NULL) { 26 | *id = 0; 27 | *address = NULL; 28 | return; 29 | } 30 | *id = r->id; 31 | *address = r->address; 32 | return; 33 | } 34 | } 35 | 36 | raft_index raft_last_index(struct raft *r) 37 | { 38 | return logLastIndex(r->log); 39 | } 40 | 41 | raft_index raft_last_applied(struct raft *r) 42 | { 43 | return r->last_applied; 44 | } 45 | 46 | int raft_role(struct raft *r) 47 | { 48 | const struct raft_server *local = 49 | configurationGet(&r->configuration, r->id); 50 | if (local == NULL) { 51 | return -1; 52 | } 53 | return local->role; 54 | } 55 | -------------------------------------------------------------------------------- /src/raft/syscall.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | #if HAVE_LINUX_AIO_ABI_H || HAVE_LINUX_IO_URING_H 4 | #include 5 | #include 6 | #endif 7 | 8 | #if HAVE_LINUX_AIO_ABI_H 9 | int io_setup(unsigned nr_events, aio_context_t *ctx_idp) 10 | { 11 | return (int)syscall(__NR_io_setup, nr_events, ctx_idp); 12 | } 13 | 14 | int io_destroy(aio_context_t ctx_id) 15 | { 16 | return (int)syscall(__NR_io_destroy, ctx_id); 17 | } 18 | 19 | int io_submit(aio_context_t ctx_id, long nr, struct iocb **iocbpp) 20 | { 21 | return (int)syscall(__NR_io_submit, ctx_id, nr, iocbpp); 22 | } 23 | 24 | int io_getevents(aio_context_t ctx_id, 25 | long min_nr, 26 | long nr, 27 | struct io_event *events, 28 | struct timespec *timeout) 29 | { 30 | return (int)syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, 31 | timeout); 32 | } 33 | #endif 34 | 35 | #if HAVE_LINUX_IO_URING_H 36 | int io_uring_register(int fd, 37 | unsigned int opcode, 38 | const void *arg, 39 | unsigned int nr_args) 40 | { 41 | return (int)syscall(__NR_io_uring_register, fd, opcode, arg, nr_args); 42 | } 43 | 44 | int io_uring_setup(unsigned int entries, struct io_uring_params *p) 45 | { 46 | return (int)syscall(__NR_io_uring_setup, entries, p); 47 | } 48 | 49 | int io_uring_enter(int fd, 50 | unsigned int to_submit, 51 | unsigned int min_complete, 52 | unsigned int flags, 53 | sigset_t *sig) 54 | { 55 | return (int)syscall(__NR_io_uring_enter, fd, to_submit, min_complete, 56 | flags, sig, _NSIG / 8); 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /src/raft/syscall.h: -------------------------------------------------------------------------------- 1 | /* Wrappers for system calls not yet defined in libc. */ 2 | 3 | #ifndef SYSCALL_H_ 4 | #define SYSCALL_H_ 5 | 6 | #if HAVE_LINUX_AIO_ABI_H 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | #if HAVE_LINUX_IO_URING_H 13 | #include 14 | #endif 15 | 16 | #if HAVE_LINUX_AIO_ABI_H 17 | /* AIO */ 18 | int io_setup(unsigned nr_events, aio_context_t *ctx_idp); 19 | 20 | int io_destroy(aio_context_t ctx_id); 21 | 22 | int io_submit(aio_context_t ctx_id, long nr, struct iocb **iocbpp); 23 | 24 | int io_getevents(aio_context_t ctx_id, 25 | long min_nr, 26 | long nr, 27 | struct io_event *events, 28 | struct timespec *timeout); 29 | #endif 30 | 31 | #if HAVE_LINUX_IO_URING_H 32 | /* uring */ 33 | int io_uring_register(int fd, 34 | unsigned int opcode, 35 | const void *arg, 36 | unsigned int nr_args); 37 | 38 | int io_uring_setup(unsigned int entries, struct io_uring_params *p); 39 | 40 | int io_uring_enter(int fd, 41 | unsigned int to_submit, 42 | unsigned int min_complete, 43 | unsigned int flags, 44 | sigset_t *sig); 45 | #endif 46 | 47 | #endif /* SYSCALL_ */ 48 | -------------------------------------------------------------------------------- /src/raft/tick.h: -------------------------------------------------------------------------------- 1 | /* Logic to be invoked periodically. */ 2 | 3 | #ifndef TICK_H_ 4 | #define TICK_H_ 5 | 6 | #include "../raft.h" 7 | 8 | /* Callback to be passed to the @raft_io implementation. It notifies us that a 9 | * certain amount of time has elapsed and will be invoked periodically. */ 10 | void tickCb(struct raft_io *io); 11 | 12 | #endif /* TICK_H_ */ 13 | -------------------------------------------------------------------------------- /src/raft/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RAFT_UTILS_H_ 2 | #define RAFT_UTILS_H_ 3 | 4 | #include 5 | 6 | /* Various utility functions and macros */ 7 | 8 | #define LIKELY(x) __builtin_expect(!!(x), 1) 9 | #define UNLIKELY(x) __builtin_expect(!!(x), 0) 10 | 11 | #define DBG() fprintf(stderr, "%s:%d\n", __func__, __LINE__) 12 | 13 | #define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof(a)[0])) 14 | 15 | #endif /* RAFT_UTILS_H_ */ 16 | -------------------------------------------------------------------------------- /src/raft/uv_encoding.h: -------------------------------------------------------------------------------- 1 | /* Encoding routines for the the libuv-based @raft_io backend. */ 2 | 3 | #ifndef UV_ENCODING_H_ 4 | #define UV_ENCODING_H_ 5 | 6 | #include 7 | 8 | #include "../raft.h" 9 | 10 | /* Current disk format version. */ 11 | #define UV__DISK_FORMAT 1 12 | 13 | int uvEncodeMessage(const struct raft_message *message, 14 | uv_buf_t **bufs, 15 | unsigned *n_bufs); 16 | 17 | int uvDecodeMessage(uint16_t type, 18 | const uv_buf_t *header, 19 | struct raft_message *message, 20 | size_t *payload_len); 21 | 22 | int uvDecodeBatchHeader(const void *batch, 23 | struct raft_entry **entries, 24 | unsigned *n); 25 | 26 | int uvDecodeEntriesBatch(uint8_t *batch, 27 | size_t offset, 28 | struct raft_entry *entries, 29 | unsigned n); 30 | 31 | /** 32 | * The layout of the memory pointed at by a @batch pointer is the following: 33 | * 34 | * [8 bytes] Number of entries in the batch, little endian. 35 | * [header1] Header data of the first entry of the batch. 36 | * [ ... ] More headers 37 | * [headerN] Header data of the last entry of the batch. 38 | * [data1 ] Payload data of the first entry of the batch. 39 | * [ ... ] More data 40 | * [dataN ] Payload data of the last entry of the batch. 41 | * 42 | * An entry header is 16-byte long and has the following layout: 43 | * 44 | * [8 bytes] Term in which the entry was created, little endian. 45 | * [1 byte ] Message type (Either RAFT_COMMAND or RAFT_CHANGE) 46 | * [3 bytes] Currently unused. 47 | * [4 bytes] Size of the log entry data, little endian. 48 | * 49 | * A payload data section for an entry is simply a sequence of bytes of 50 | * arbitrary lengths, possibly padded with extra bytes to reach 8-byte boundary 51 | * (which means that all entry data pointers are 8-byte aligned). 52 | */ 53 | size_t uvSizeofBatchHeader(size_t n); 54 | 55 | void uvEncodeBatchHeader(const struct raft_entry *entries, 56 | unsigned n, 57 | void *buf); 58 | 59 | #endif /* UV_ENCODING_H_ */ 60 | -------------------------------------------------------------------------------- /src/raft/uv_ip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "../raft.h" 7 | 8 | #include "uv_ip.h" 9 | 10 | static const char *strCpyUntil(char *target, 11 | const char *source, 12 | size_t target_size, 13 | char separator) 14 | { 15 | size_t i; 16 | for (i = 0; i < target_size; ++i) { 17 | if (!source[i] || source[i] == separator) { 18 | target[i] = 0; 19 | return source + i; 20 | } else { 21 | target[i] = source[i]; 22 | } 23 | } 24 | return NULL; 25 | } 26 | 27 | int uvIpAddrSplit(const char *address, 28 | char *host, 29 | size_t host_size, 30 | char *service, 31 | size_t service_size) 32 | { 33 | char colon = ':'; 34 | const char *service_ptr = NULL; 35 | 36 | if (host) { 37 | service_ptr = strCpyUntil(host, address, host_size, colon); 38 | if (!service_ptr) { 39 | return RAFT_NAMETOOLONG; 40 | } 41 | } 42 | if (service) { 43 | if (!service_ptr) { 44 | service_ptr = strchr(address, colon); 45 | } 46 | if (!service_ptr || *service_ptr == 0 || 47 | *(++service_ptr) == 0) { 48 | service_ptr = "8080"; 49 | } 50 | if (!strCpyUntil(service, service_ptr, service_size, 0)) { 51 | return RAFT_NAMETOOLONG; 52 | } 53 | } 54 | return 0; 55 | } 56 | 57 | /* Synchronoues resolve hostname to IP address */ 58 | int uvIpResolveBindAddresses(const char *address, struct addrinfo **ai_result) 59 | { 60 | static struct addrinfo hints = { 61 | .ai_flags = AI_PASSIVE | AI_NUMERICSERV, 62 | .ai_family = AF_INET, 63 | .ai_socktype = SOCK_STREAM, 64 | .ai_protocol = 0}; 65 | char hostname[NI_MAXHOST]; 66 | char service[NI_MAXSERV]; 67 | int rv; 68 | 69 | rv = uvIpAddrSplit(address, hostname, sizeof(hostname), service, 70 | sizeof(service)); 71 | if (rv != 0) { 72 | return rv; 73 | } 74 | 75 | if (hostname[0]) { 76 | rv = getaddrinfo(hostname, service, &hints, ai_result); 77 | } else { 78 | rv = getaddrinfo(NULL, service, &hints, ai_result); 79 | } 80 | 81 | if (rv != 0) { 82 | return RAFT_IOERR; 83 | } 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /src/raft/uv_ip.h: -------------------------------------------------------------------------------- 1 | /* IP-related utils. */ 2 | 3 | #ifndef UV_IP_H_ 4 | #define UV_IP_H_ 5 | 6 | #include 7 | 8 | /* Split @address into @host and @service. */ 9 | int uvIpAddrSplit(const char *address, 10 | char *host, 11 | size_t host_size, 12 | char *service, 13 | size_t service_size); 14 | 15 | struct addrinfo; 16 | 17 | /* Synchronous resolve hostname to IP address */ 18 | int uvIpResolveBindAddresses(const char *address, struct addrinfo **ai_result); 19 | 20 | #endif /* UV_IP_H */ 21 | -------------------------------------------------------------------------------- /src/raft/uv_tcp.h: -------------------------------------------------------------------------------- 1 | #ifndef UV_TCP_H_ 2 | #define UV_TCP_H_ 3 | 4 | #include "../raft.h" 5 | #include "../lib/queue.h" 6 | 7 | /* Protocol version. */ 8 | #define UV__TCP_HANDSHAKE_PROTOCOL 1 9 | 10 | struct UvTcp 11 | { 12 | struct raft_uv_transport *transport; /* Interface object we implement */ 13 | struct uv_loop_s *loop; /* Event loop */ 14 | raft_id id; /* ID of this raft server */ 15 | const char *address; /* Address of this raft server */ 16 | unsigned n_listeners; /* Number of listener sockets */ 17 | struct uv_tcp_s *listeners; /* Listener sockets */ 18 | raft_uv_accept_cb accept_cb; /* Call after accepting a connection */ 19 | queue accepting; /* Connections being accepted */ 20 | queue connecting; /* Pending connection requests */ 21 | queue aborting; /* Connections being aborted */ 22 | bool closing; /* True after close() is called */ 23 | raft_uv_transport_close_cb 24 | close_cb; /* Call when it's safe to free us */ 25 | char *bind_address; /* Optional address:port to bind to */ 26 | }; 27 | 28 | /* Implementation of raft_uv_transport->listen. */ 29 | int UvTcpListen(struct raft_uv_transport *transport, raft_uv_accept_cb cb); 30 | 31 | /* Stop accepting new connection and close all connections being accepted. */ 32 | void UvTcpListenClose(struct UvTcp *t); 33 | 34 | /* Implementation of raft_uv_transport->connect. */ 35 | int UvTcpConnect(struct raft_uv_transport *transport, 36 | struct raft_uv_connect *req, 37 | raft_id id, 38 | const char *address, 39 | raft_uv_connect_cb cb); 40 | 41 | /* Abort all pending connection requests. */ 42 | void UvTcpConnectClose(struct UvTcp *t); 43 | 44 | /* Fire the transport close callback if the transport is closing and there's no 45 | * more pending callback. */ 46 | void UvTcpMaybeFireCloseCb(struct UvTcp *t); 47 | 48 | #endif /* UV_TCP_H_ */ 49 | -------------------------------------------------------------------------------- /src/raft/uv_timer.c: -------------------------------------------------------------------------------- 1 | #include "./uv.h" 2 | #include "./heap.h" 3 | 4 | 5 | static void uvTimerCallback(uv_timer_t* handle) { 6 | struct raft_timer *req = handle->data; 7 | req->cb(req); 8 | } 9 | 10 | static void uvTimerFree(uv_handle_t *handle) { 11 | RaftHeapFree(handle); 12 | } 13 | 14 | int UvTimerStart(struct raft_io *io, struct raft_timer *req, uint64_t timeout, uint64_t repeat, raft_timer_cb cb) { 15 | int rv; 16 | struct uv *uv = io->impl; 17 | assert(!uv->closing); 18 | 19 | uv_timer_t *timer = RaftHeapMalloc(sizeof *timer); 20 | if (timer == NULL) { 21 | return RAFT_NOMEM; 22 | } 23 | rv = uv_timer_init(uv->loop, timer); 24 | if (rv != 0) { 25 | goto error; 26 | } 27 | 28 | timer->data = req; 29 | rv = uv_timer_start(timer, uvTimerCallback, timeout, repeat); 30 | if (rv != 0) { 31 | goto error; 32 | } 33 | req->handle = timer; 34 | req->cb = cb; 35 | return RAFT_OK; 36 | 37 | error: 38 | RaftHeapFree(timer); 39 | return rv; 40 | } 41 | 42 | int UvTimerStop(struct raft_io *io, struct raft_timer *req) { 43 | (void)io; 44 | 45 | int rv; 46 | uv_timer_t *timer = req->handle; 47 | if (timer == NULL) { 48 | return RAFT_OK; 49 | } 50 | 51 | rv = uv_timer_stop(timer); 52 | if (rv != 0) { 53 | return rv; 54 | } 55 | uv_close((uv_handle_t*)timer, uvTimerFree); 56 | req->handle = NULL; 57 | return RAFT_OK; 58 | } 59 | -------------------------------------------------------------------------------- /src/raft/uv_work.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | #include "heap.h" 3 | #include "uv.h" 4 | 5 | struct uvAsyncWork 6 | { 7 | struct uv *uv; 8 | struct raft_io_async_work *req; 9 | struct uv_work_s work; 10 | int status; 11 | queue queue; 12 | }; 13 | 14 | static void uvAsyncWorkCb(uv_work_t *work) 15 | { 16 | struct uvAsyncWork *w = work->data; 17 | assert(w != NULL); 18 | int rv; 19 | rv = w->req->work(w->req); 20 | w->status = rv; 21 | } 22 | 23 | static void uvAsyncAfterWorkCb(uv_work_t *work, int status) 24 | { 25 | struct uvAsyncWork *w = work->data; 26 | struct raft_io_async_work *req = w->req; 27 | int req_status = w->status; 28 | struct uv *uv = w->uv; 29 | assert(status == 0); 30 | 31 | queue_remove(&w->queue); 32 | RaftHeapFree(w); 33 | req->cb(req, req_status); 34 | uvMaybeFireCloseCb(uv); 35 | } 36 | 37 | int UvAsyncWork(struct raft_io *io, 38 | struct raft_io_async_work *req, 39 | raft_io_async_work_cb cb) 40 | { 41 | struct uv *uv; 42 | struct uvAsyncWork *async_work; 43 | int rv; 44 | 45 | uv = io->impl; 46 | assert(!uv->closing); 47 | 48 | async_work = RaftHeapMalloc(sizeof *async_work); 49 | if (async_work == NULL) { 50 | rv = RAFT_NOMEM; 51 | goto err; 52 | } 53 | 54 | async_work->uv = uv; 55 | async_work->req = req; 56 | async_work->work.data = async_work; 57 | req->cb = cb; 58 | 59 | queue_insert_tail(&uv->async_work_reqs, &async_work->queue); 60 | rv = uv_queue_work(uv->loop, &async_work->work, uvAsyncWorkCb, 61 | uvAsyncAfterWorkCb); 62 | if (rv != 0) { 63 | queue_remove(&async_work->queue); 64 | tracef("async work: %s", uv_strerror(rv)); 65 | rv = RAFT_IOERR; 66 | goto err_after_req_alloc; 67 | } 68 | 69 | return 0; 70 | 71 | err_after_req_alloc: 72 | RaftHeapFree(async_work); 73 | err: 74 | assert(rv != 0); 75 | return rv; 76 | } 77 | 78 | #undef tracef 79 | -------------------------------------------------------------------------------- /src/raft/uv_writer.h: -------------------------------------------------------------------------------- 1 | /* Asynchronous API to write a file. */ 2 | 3 | #ifndef UV_WRITER_H_ 4 | #define UV_WRITER_H_ 5 | 6 | #include 7 | 8 | #include "err.h" 9 | #include "../lib/queue.h" 10 | #include "uv_os.h" 11 | 12 | /* Perform asynchronous writes to a single file. */ 13 | struct UvWriter; 14 | 15 | /* Callback called after the memory associated with a file handle can be 16 | * released. */ 17 | typedef void (*UvWriterCloseCb)(struct UvWriter *w); 18 | 19 | struct UvWriter 20 | { 21 | void *data; /* User data */ 22 | struct uv_loop_s *loop; /* Event loop */ 23 | uv_file fd; /* File handle */ 24 | bool async; /* Whether fully async I/O is supported */ 25 | aio_context_t ctx; /* KAIO handle */ 26 | struct io_event *events; /* Array of KAIO response objects */ 27 | unsigned n_events; /* Length of the events array */ 28 | int event_fd; /* Poll'ed to check if write is finished */ 29 | struct uv_poll_s 30 | event_poller; /* Poll event_fd for completed poll requests */ 31 | struct uv_check_s check; /* Check for completed threadpool requests */ 32 | UvWriterCloseCb close_cb; /* Close callback */ 33 | queue poll_queue; /* Pollable write requests */ 34 | queue work_queue; /* Threadpool write requests */ 35 | bool closing; /* Whether we're closing or closed */ 36 | char *errmsg; /* Description of last error */ 37 | }; 38 | 39 | /* Initialize a file writer. */ 40 | int UvWriterInit(struct UvWriter *w, 41 | struct uv_loop_s *loop, 42 | uv_file fd, 43 | bool direct /* Whether to use direct I/O */, 44 | bool async /* Whether async I/O is available */, 45 | unsigned max_concurrent_writes, 46 | char *errmsg); 47 | 48 | /* Close the given file and release all associated resources. */ 49 | void UvWriterClose(struct UvWriter *w, UvWriterCloseCb cb); 50 | 51 | /* Write request. */ 52 | struct UvWriterReq; 53 | 54 | /* Callback called after a write request has been completed. */ 55 | typedef void (*UvWriterReqCb)(struct UvWriterReq *req, int status); 56 | 57 | struct UvWriterReq 58 | { 59 | void *data; /* User data */ 60 | struct UvWriter *writer; /* Originating writer */ 61 | size_t len; /* Total number of bytes to write */ 62 | int status; /* Request result code */ 63 | struct uv_work_s work; /* To execute logic in the threadpool */ 64 | UvWriterReqCb cb; /* Callback to invoke upon request completion */ 65 | struct iocb iocb; /* KAIO request (for writing) */ 66 | char errmsg[256]; /* Error description (for thread-safety) */ 67 | queue queue; /* Prev/next links in the inflight queue */ 68 | }; 69 | 70 | /* Asynchronously write data to the underlying file. */ 71 | int UvWriterSubmit(struct UvWriter *w, 72 | struct UvWriterReq *req, 73 | const uv_buf_t bufs[], 74 | unsigned n, 75 | size_t offset, 76 | UvWriterReqCb cb); 77 | 78 | #endif /* UV_WRITER_H_ */ 79 | -------------------------------------------------------------------------------- /src/registry.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../include/dqlite.h" 4 | 5 | #include "lib/assert.h" 6 | 7 | #include "registry.h" 8 | 9 | void registry__init(struct registry *r, struct config *config) 10 | { 11 | r->config = config; 12 | queue_init(&r->dbs); 13 | } 14 | 15 | void registry__close(struct registry *r) 16 | { 17 | while (!queue_empty(&r->dbs)) { 18 | struct db *db; 19 | queue *head; 20 | head = queue_head(&r->dbs); 21 | queue_remove(head); 22 | db = QUEUE_DATA(head, struct db, queue); 23 | db__close(db); 24 | sqlite3_free(db); 25 | } 26 | } 27 | 28 | int registry__db_get(struct registry *r, const char *filename, struct db **db) 29 | { 30 | queue *head; 31 | QUEUE_FOREACH(head, &r->dbs) 32 | { 33 | *db = QUEUE_DATA(head, struct db, queue); 34 | if (strcmp((*db)->filename, filename) == 0) { 35 | return 0; 36 | } 37 | } 38 | *db = sqlite3_malloc(sizeof **db); 39 | if (*db == NULL) { 40 | return DQLITE_NOMEM; 41 | } 42 | db__init(*db, r->config, filename); 43 | queue_insert_tail(&r->dbs, &(*db)->queue); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/registry.h: -------------------------------------------------------------------------------- 1 | #ifndef REGISTRY_H_ 2 | #define REGISTRY_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "lib/queue.h" 9 | 10 | #include "db.h" 11 | 12 | struct registry 13 | { 14 | struct config *config; 15 | queue dbs; 16 | }; 17 | 18 | void registry__init(struct registry *r, struct config *config); 19 | void registry__close(struct registry *r); 20 | 21 | /** 22 | * Get the db with the given filename. If no one is registered, create one. 23 | */ 24 | int registry__db_get(struct registry *r, const char *filename, struct db **db); 25 | 26 | #endif /* REGISTRY_H_*/ 27 | -------------------------------------------------------------------------------- /src/request.c: -------------------------------------------------------------------------------- 1 | #include "request.h" 2 | 3 | #define REQUEST__IMPLEMENT(LOWER, UPPER, _) \ 4 | SERIALIZE__IMPLEMENT(request_##LOWER, REQUEST_##UPPER); 5 | 6 | REQUEST__TYPES(REQUEST__IMPLEMENT, ); 7 | 8 | SERIALIZE__IMPLEMENT(request_connect, REQUEST_CONNECT); 9 | SERIALIZE__IMPLEMENT(request_assign, REQUEST_ASSIGN); 10 | -------------------------------------------------------------------------------- /src/response.c: -------------------------------------------------------------------------------- 1 | #include "response.h" 2 | 3 | #define RESPONSE__IMPLEMENT(LOWER, UPPER, _) \ 4 | SERIALIZE__IMPLEMENT(response_##LOWER, RESPONSE_##UPPER); 5 | 6 | RESPONSE__TYPES(RESPONSE__IMPLEMENT, ); 7 | -------------------------------------------------------------------------------- /src/response.h: -------------------------------------------------------------------------------- 1 | #ifndef RESPONSE_H_ 2 | #define RESPONSE_H_ 3 | 4 | #include "lib/serialize.h" 5 | 6 | /** 7 | * Response types. 8 | */ 9 | 10 | #define RESPONSE_SERVER(X, ...) \ 11 | X(uint64, id, ##__VA_ARGS__) \ 12 | X(text, address, ##__VA_ARGS__) 13 | #define RESPONSE_SERVER_LEGACY(X, ...) X(text, address, ##__VA_ARGS__) 14 | #define RESPONSE_WELCOME(X, ...) X(uint64, heartbeat_timeout, ##__VA_ARGS__) 15 | #define RESPONSE_FAILURE(X, ...) \ 16 | X(uint64, code, ##__VA_ARGS__) \ 17 | X(text, message, ##__VA_ARGS__) 18 | #define RESPONSE_DB(X, ...) \ 19 | X(uint32, id, ##__VA_ARGS__) \ 20 | X(uint32, __pad__, ##__VA_ARGS__) 21 | #define RESPONSE_STMT(X, ...) \ 22 | X(uint32, db_id, ##__VA_ARGS__) \ 23 | X(uint32, id, ##__VA_ARGS__) \ 24 | X(uint64, params, ##__VA_ARGS__) 25 | #define RESPONSE_STMT_WITH_OFFSET(X, ...) \ 26 | X(uint32, db_id, ##__VA_ARGS__) \ 27 | X(uint32, id, ##__VA_ARGS__) \ 28 | X(uint64, params, ##__VA_ARGS__) \ 29 | X(uint64, offset, ##__VA_ARGS__) 30 | #define RESPONSE_RESULT(X, ...) \ 31 | X(uint64, last_insert_id, ##__VA_ARGS__) \ 32 | X(uint64, rows_affected, ##__VA_ARGS__) 33 | #define RESPONSE_ROWS(X, ...) X(uint64, eof, ##__VA_ARGS__) 34 | #define RESPONSE_EMPTY(X, ...) X(uint64, __unused__, ##__VA_ARGS__) 35 | #define RESPONSE_FILES(X, ...) X(uint64, n, ##__VA_ARGS__) 36 | #define RESPONSE_SERVERS(X, ...) X(uint64, n, ##__VA_ARGS__) 37 | #define RESPONSE_METADATA(X, ...) \ 38 | X(uint64, failure_domain, ##__VA_ARGS__) \ 39 | X(uint64, weight, ##__VA_ARGS__) 40 | 41 | #define RESPONSE__DEFINE(LOWER, UPPER, _) \ 42 | SERIALIZE__DEFINE(response_##LOWER, RESPONSE_##UPPER); 43 | 44 | #define RESPONSE__TYPES(X, ...) \ 45 | X(server, SERVER, __VA_ARGS__) \ 46 | X(server_legacy, SERVER_LEGACY, __VA_ARGS__) \ 47 | X(welcome, WELCOME, __VA_ARGS__) \ 48 | X(failure, FAILURE, __VA_ARGS__) \ 49 | X(db, DB, __VA_ARGS__) \ 50 | X(stmt, STMT, __VA_ARGS__) \ 51 | X(stmt_with_offset, STMT_WITH_OFFSET, __VA_ARGS__) \ 52 | X(result, RESULT, __VA_ARGS__) \ 53 | X(rows, ROWS, __VA_ARGS__) \ 54 | X(empty, EMPTY, __VA_ARGS__) \ 55 | X(files, FILES, __VA_ARGS__) \ 56 | X(servers, SERVERS, __VA_ARGS__) \ 57 | X(metadata, METADATA, __VA_ARGS__) 58 | 59 | RESPONSE__TYPES(RESPONSE__DEFINE); 60 | 61 | #endif /* RESPONSE_H_ */ 62 | -------------------------------------------------------------------------------- /src/roles.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_ROLE_MANAGEMENT_H 2 | #define DQLITE_ROLE_MANAGEMENT_H 3 | 4 | #include "server.h" 5 | 6 | struct all_node_info 7 | { 8 | uint64_t id; 9 | char *address; 10 | int role; 11 | bool online; 12 | uint64_t failure_domain; 13 | uint64_t weight; 14 | }; 15 | 16 | /* Determine what roles changes should be made to the cluster, without 17 | * side-effects. The given callback will be invoked for each computed change, 18 | * with first argument the node whose role should be adjusted, second argument 19 | * the node's new role, and third argument taken from the last argument of this 20 | * function. 21 | * 22 | * The memory pointed to by @cluster is "borrowed" and not freed by this 23 | * function, but it may be modified as part of this function's bookkeeping. */ 24 | void RolesComputeChanges(int voters, 25 | int standbys, 26 | struct all_node_info *cluster, 27 | unsigned n_cluster, 28 | dqlite_node_id my_id, 29 | void (*cb)(uint64_t, int, void *), 30 | void *arg); 31 | 32 | /* If necessary, try to assign new roles to nodes in the cluster to achieve 33 | * the configured number of voters and standbys. Polling the cluster and 34 | * assigning roles happens asynchronously. This can safely be called on any 35 | * server, but does nothing if called on a server that is not the leader. */ 36 | void RolesAdjust(struct dqlite_node *d); 37 | 38 | /* Begin a graceful shutdown of this node. Leadership and the voter role will 39 | * be transferred to other nodes if necessary, and then the callback will be 40 | * invoked on the loop thread. The callback's second argument will be 0 if the 41 | * handover succeeded and nonzero otherwise. */ 42 | void RolesHandover(struct dqlite_node *d, 43 | void (*cb)(struct dqlite_node *, int)); 44 | 45 | /* Drain the queue of changes computed by RoleManagementAdjust. This should be 46 | * done when the node is shutting down, to avoid a memory leak. */ 47 | void RolesCancelPendingChanges(struct dqlite_node *d); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/stmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "./lib/assert.h" 4 | #include "./tuple.h" 5 | 6 | #include "stmt.h" 7 | 8 | /* The maximum number of columns we expect (for bindings or rows) is 255, which 9 | * can fit in one byte. */ 10 | #define STMT__MAX_COLUMNS (1 << 8) - 1 11 | 12 | void stmt__init(struct stmt *s) 13 | { 14 | s->stmt = NULL; 15 | } 16 | 17 | void stmt__close(struct stmt *s) 18 | { 19 | if (s->stmt != NULL) { 20 | /* Ignore the return code, since it will be non-zero in case the 21 | * most rececent evaluation of the statement failed. */ 22 | sqlite3_finalize(s->stmt); 23 | } 24 | } 25 | 26 | const char *stmt__hash(struct stmt *stmt) 27 | { 28 | (void)stmt; 29 | return NULL; 30 | } 31 | 32 | REGISTRY_METHODS(stmt__registry, stmt); 33 | -------------------------------------------------------------------------------- /src/tracing.h: -------------------------------------------------------------------------------- 1 | /* Tracing functionality for dqlite */ 2 | 3 | #ifndef DQLITE_TRACING_H_ 4 | #define DQLITE_TRACING_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../include/dqlite.h" 13 | 14 | #include "utils.h" 15 | 16 | /* This global variable is only written once at startup and is only read 17 | * from there on. Users should not manipulate the value of this variable. */ 18 | DQLITE_VISIBLE_TO_TESTS extern bool _dqliteTracingEnabled; 19 | DQLITE_VISIBLE_TO_TESTS void stderrTracerEmit(const char *file, 20 | unsigned int line, 21 | const char *func, 22 | unsigned int level, 23 | const char *message); 24 | 25 | // FIXME same oreder as upper 26 | DQLITE_VISIBLE_TO_TESTS NOINLINE 27 | void _tracef0(const char *file, unsigned int line, const char *func, unsigned int level, const char *fmt, ...); 28 | 29 | #define tracef0(LEVEL, ...) _tracef0(__FILE__, __LINE__, __func__, LEVEL, __VA_ARGS__) 30 | 31 | enum dqlite_trace_level { 32 | /** Represents an invalid trace level */ 33 | TRACE_NONE, 34 | /** Lower-level information to debug and analyse incorrect behavior */ 35 | TRACE_DEBUG, 36 | /** Information about current system's state */ 37 | TRACE_INFO, 38 | /** 39 | * Condition which requires a special handling, something which doesn't 40 | * happen normally 41 | */ 42 | TRACE_WARN, 43 | /** Resource unavailable, no connectivity, invalid value, etc. */ 44 | TRACE_ERROR, 45 | /** System is not able to continue performing its basic function */ 46 | TRACE_FATAL, 47 | TRACE_NR, 48 | }; 49 | 50 | #define tracef(...) tracef0(TRACE_DEBUG, __VA_ARGS__) 51 | 52 | /* Enable tracing if the appropriate env variable is set, or disable tracing. */ 53 | DQLITE_VISIBLE_TO_TESTS void dqliteTracingMaybeEnable(bool enabled); 54 | 55 | #endif /* DQLITE_TRACING_H_ */ 56 | -------------------------------------------------------------------------------- /src/translate.c: -------------------------------------------------------------------------------- 1 | #include "translate.h" 2 | 3 | #include "assert.h" 4 | #include "leader.h" 5 | #include "protocol.h" 6 | #include "raft.h" 7 | 8 | /* Translate a raft error to a dqlite one. */ 9 | int translateRaftErrCode(int code) 10 | { 11 | switch (code) { 12 | case RAFT_NOTLEADER: 13 | return SQLITE_IOERR_NOT_LEADER; 14 | case RAFT_LEADERSHIPLOST: 15 | return SQLITE_IOERR_LEADERSHIP_LOST; 16 | case RAFT_CANTCHANGE: 17 | return SQLITE_BUSY; 18 | default: 19 | return SQLITE_ERROR; 20 | } 21 | } 22 | 23 | /* Translate a dqlite role code to its raft equivalent. */ 24 | int translateDqliteRole(int role) 25 | { 26 | switch (role) { 27 | case DQLITE_VOTER: 28 | return RAFT_VOTER; 29 | case DQLITE_STANDBY: 30 | return RAFT_STANDBY; 31 | case DQLITE_SPARE: 32 | return RAFT_SPARE; 33 | default: 34 | /* For backward compat with clients that don't set a 35 | * role. */ 36 | return DQLITE_VOTER; 37 | } 38 | } 39 | 40 | /* Translate a raft role code to its dqlite equivalent. */ 41 | int translateRaftRole(int role) 42 | { 43 | switch (role) { 44 | case RAFT_VOTER: 45 | return DQLITE_VOTER; 46 | case RAFT_STANDBY: 47 | return DQLITE_STANDBY; 48 | case RAFT_SPARE: 49 | return DQLITE_SPARE; 50 | default: 51 | assert(0); 52 | return -1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/translate.h: -------------------------------------------------------------------------------- 1 | /* Translate to/from dqlite types */ 2 | 3 | #ifndef DQLITE_TRANSLATE_H_ 4 | #define DQLITE_TRANSLATE_H_ 5 | 6 | /* Translate a raft error to a dqlite one. */ 7 | int translateRaftErrCode(int code); 8 | 9 | /* Translate a dqlite role code to its raft equivalent. */ 10 | int translateDqliteRole(int role); 11 | 12 | /* Translate a raft role code to its dqlite equivalent. */ 13 | int translateRaftRole(int role); 14 | 15 | #endif /* DQLITE_TRANSLATE_H_ */ 16 | -------------------------------------------------------------------------------- /src/transport.h: -------------------------------------------------------------------------------- 1 | /* Implementation of the raft_uv_transport interface, proxied by a dqlite 2 | * connection. 3 | * 4 | * Instead of having raft instances connect to each other directly, we pass a 5 | * custom connect function that causes dqlite to send a CONNECT request to the 6 | * dqlite server where the destination raft instance is running. That server 7 | * responds to the CONNECT request by forwarding the dqlite connection to its 8 | * raft instance, after which the raft-to-raft connection is transparent. */ 9 | #ifndef TRANSPORT_H_ 10 | #define TRANSPORT_H_ 11 | 12 | #include "raft.h" 13 | 14 | #include "../include/dqlite.h" 15 | 16 | int transportDefaultConnect(void *arg, const char *address, int *fd); 17 | 18 | int raftProxyInit(struct raft_uv_transport *transport, struct uv_loop_s *loop); 19 | 20 | void raftProxyClose(struct raft_uv_transport *transport); 21 | 22 | /* Invoke the accept callback configured on the transport object. */ 23 | void raftProxyAccept(struct raft_uv_transport *transport, 24 | raft_id id, 25 | const char *address, 26 | struct uv_stream_s *stream); 27 | 28 | /* Set a custom connect function. */ 29 | void raftProxySetConnectFunc(struct raft_uv_transport *transport, 30 | int (*f)(void *arg, const char *address, int *fd), 31 | void *arg); 32 | 33 | #endif /* TRANSPORT_H_*/ 34 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_UTILS_H_ 2 | #define DQLITE_UTILS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Various utility functions and macros */ 9 | 10 | #define PTR_TO_UINT64(p) ((uint64_t)((uintptr_t)(p))) 11 | #define UINT64_TO_PTR(u, ptr_type) ((ptr_type)((uintptr_t)(u))) 12 | 13 | #define LIKELY(x) __builtin_expect(!!(x), 1) 14 | #define UNLIKELY(x) __builtin_expect(!!(x), 0) 15 | #define IMPOSSIBLE(why) assert(false && why) 16 | 17 | #define DBG() fprintf(stderr, "%s:%d\n", __func__, __LINE__) 18 | 19 | #define CONTAINER_OF(e, type, field) \ 20 | ((type *)(uintptr_t)((char *)(e)-offsetof(type, field))) 21 | 22 | #define PRE(cond) assert((cond)) 23 | #define POST(cond) assert((cond)) 24 | #define ERGO(a, b) (!(a) || (b)) 25 | 26 | #define IN_1(E, X) E == X 27 | #define IN_2(E, X, ...) E == X || IN_1(E,__VA_ARGS__) 28 | #define IN_3(E, X, ...) E == X || IN_2(E,__VA_ARGS__) 29 | #define IN_4(E, X, ...) E == X || IN_3(E,__VA_ARGS__) 30 | #define IN_5(E, X, ...) E == X || IN_4(E,__VA_ARGS__) 31 | #define IN_6(E, X, ...) E == X || IN_5(E,__VA_ARGS__) 32 | #define IN_7(E, X, ...) E == X || IN_6(E,__VA_ARGS__) 33 | #define IN_8(E, X, ...) E == X || IN_7(E,__VA_ARGS__) 34 | #define IN_9(E, X, ...) E == X || IN_8(E,__VA_ARGS__) 35 | 36 | #define GET_IN_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME 37 | #define IN(E, ...) \ 38 | (GET_IN_MACRO(__VA_ARGS__,IN_9,IN_8,IN_7,IN_6,IN_5,IN_4,IN_3,IN_2,IN_1)(E,__VA_ARGS__)) 39 | 40 | #if defined(__has_attribute) && __has_attribute (noinline) 41 | # define NOINLINE __attribute__ ((noinline)) 42 | #else 43 | # define NOINLINE 44 | #endif 45 | 46 | static inline bool is_po2(unsigned long n) { 47 | return n > 0 && (n & (n - 1)) == 0; 48 | } 49 | 50 | #endif /* DQLITE_UTILS_H_ */ 51 | -------------------------------------------------------------------------------- /test/integration/main.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("integration") 4 | -------------------------------------------------------------------------------- /test/lib/config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Options object for tests. 3 | */ 4 | 5 | #ifndef TEST_OPTIONS_H 6 | #define TEST_OPTIONS_H 7 | 8 | #include "../../src/config.h" 9 | 10 | #include "logger.h" 11 | 12 | #define FIXTURE_CONFIG struct config config; 13 | 14 | #define SETUP_CONFIG \ 15 | { \ 16 | int rc; \ 17 | rc = config__init(&f->config, 1, "1", NULL, "dir"); \ 18 | munit_assert_int(rc, ==, 0); \ 19 | test_logger_setup(params, &f->config.logger); \ 20 | } 21 | 22 | #define TEAR_DOWN_CONFIG \ 23 | test_logger_tear_down(&f->config.logger); \ 24 | config__close(&f->config) 25 | 26 | #endif /* TEST_OPTIONS_H */ 27 | -------------------------------------------------------------------------------- /test/lib/endpoint.h: -------------------------------------------------------------------------------- 1 | /* Helpers to create and connect Unix or TCP sockets. */ 2 | 3 | #ifndef TEST_ENDPOINT_H 4 | #define TEST_ENDPOINT_H 5 | 6 | #include 7 | #include 8 | 9 | #include "munit.h" 10 | 11 | /* A few tests depend on knowing that certain reads and writes will not be short 12 | * and will happen immediately. */ 13 | #define TEST_SOCKET_MIN_BUF_SIZE 4096 14 | 15 | /* Munit parameter defining the socket type to use in test_endpoint_setup. 16 | * 17 | * If set to "unix" a pair of unix abstract sockets will be created. If set to 18 | * "tcp" a pair of TCP sockets using the loopback interface will be created. */ 19 | #define TEST_ENDPOINT_FAMILY "endpoint-family" 20 | 21 | /* Null-terminated list of legal values for TEST_ENDPOINT_FAMILY. Currently 22 | * "unix" and "tcp". */ 23 | extern char *test_endpoint_family_values[]; 24 | 25 | /* Listening socket endpoint. */ 26 | struct test_endpoint 27 | { 28 | char address[256]; /* Rendered address string. */ 29 | sa_family_t family; /* Address family (either AF_INET or AF_UNIX) */ 30 | int fd; /* Listening socket. */ 31 | union { /* Server address (either a TCP or Unix) */ 32 | struct sockaddr_in in_address; 33 | struct sockaddr_un un_address; 34 | }; 35 | }; 36 | 37 | /* Create a listening endpoint. 38 | * 39 | * This will bind a random address and start listening to it. */ 40 | void test_endpoint_setup(struct test_endpoint *e, 41 | const MunitParameter params[]); 42 | 43 | /* Tear down a listening endpoint. */ 44 | void test_endpoint_tear_down(struct test_endpoint *e); 45 | 46 | /* Establish a new client connection. */ 47 | int test_endpoint_connect(struct test_endpoint *e); 48 | 49 | /* Accept a new client connection. */ 50 | int test_endpoint_accept(struct test_endpoint *e); 51 | 52 | /* Connect and accept a connection, returning the pair of connected sockets. */ 53 | void test_endpoint_pair(struct test_endpoint *e, int *server, int *client); 54 | 55 | /* Return the endpoint address. */ 56 | const char *test_endpoint_address(struct test_endpoint *e); 57 | 58 | #endif /* TEST_ENDPOINT_H */ 59 | -------------------------------------------------------------------------------- /test/lib/fault.c: -------------------------------------------------------------------------------- 1 | #include "fault.h" 2 | #include "munit.h" 3 | 4 | void test_fault_init(struct test_fault *f) 5 | { 6 | f->countdown = -1; 7 | f->n = -1; 8 | f->enabled = false; 9 | } 10 | 11 | bool test_fault_tick(struct test_fault *f) 12 | { 13 | if (MUNIT_UNLIKELY(!f->enabled)) { 14 | return false; 15 | } 16 | 17 | /* If the initial delay parameter was set to -1, then never fail. This 18 | * is the most common case. */ 19 | if (MUNIT_LIKELY(f->countdown < 0)) { 20 | return false; 21 | } 22 | 23 | /* If we did not yet reach 'delay' ticks, then just decrease the 24 | * countdown. 25 | */ 26 | if (f->countdown > 0) { 27 | f->countdown--; 28 | return false; 29 | } 30 | 31 | munit_assert_int(f->countdown, ==, 0); 32 | 33 | /* We reached 'delay' ticks, let's see how many times we have to trigger 34 | * the fault, if any. */ 35 | 36 | if (f->n < 0) { 37 | /* Trigger the fault forever. */ 38 | return true; 39 | } 40 | 41 | if (f->n > 0) { 42 | /* Trigger the fault at least this time. */ 43 | f->n--; 44 | return true; 45 | } 46 | 47 | munit_assert_int(f->n, ==, 0); 48 | 49 | /* We reached 'repeat' ticks, let's stop triggering the fault. */ 50 | f->countdown--; 51 | 52 | return false; 53 | } 54 | 55 | void test_fault_config(struct test_fault *f, int delay, int repeat) 56 | { 57 | f->countdown = delay; 58 | f->n = repeat; 59 | } 60 | 61 | void test_fault_enable(struct test_fault *f) 62 | { 63 | f->enabled = true; 64 | } 65 | -------------------------------------------------------------------------------- /test/lib/fault.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper for test components supporting fault injection. 3 | */ 4 | 5 | #ifndef TEST_FAULT_H 6 | #define TEST_FAULT_H 7 | 8 | #include 9 | 10 | /** 11 | * Information about a fault that should occurr in a component. 12 | */ 13 | struct test_fault 14 | { 15 | int countdown; /* Trigger the fault when this counter gets to zero. */ 16 | int n; /* Repeat the fault this many times. Default is -1. */ 17 | bool enabled; /* Enable fault triggering. */ 18 | }; 19 | 20 | /** 21 | * Initialize a fault. 22 | */ 23 | void test_fault_init(struct test_fault *f); 24 | 25 | /** 26 | * Advance the counters of the fault. Return true if the fault should be 27 | * triggered, false otherwise. 28 | */ 29 | bool test_fault_tick(struct test_fault *f); 30 | 31 | /** 32 | * Configure the fault with the given values. 33 | */ 34 | void test_fault_config(struct test_fault *f, int delay, int repeat); 35 | 36 | /** 37 | * Enable fault triggering. 38 | */ 39 | void test_fault_enable(struct test_fault *f); 40 | 41 | #endif /* TEST_FAULT_H */ 42 | -------------------------------------------------------------------------------- /test/lib/fs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fs.h" 8 | #include "munit.h" 9 | 10 | char *test_dir_setup() 11 | { 12 | char *dir = munit_malloc(strlen(TEST__DIR_TEMPLATE) + 1); 13 | 14 | strcpy(dir, TEST__DIR_TEMPLATE); 15 | 16 | munit_assert_ptr_not_null(mkdtemp(dir)); 17 | 18 | return dir; 19 | } 20 | 21 | static int test__dir_tear_down_nftw_fn(const char *path, 22 | const struct stat *sb, 23 | int type, 24 | struct FTW *ftwb) 25 | { 26 | int rc; 27 | 28 | (void)sb; 29 | (void)type; 30 | (void)ftwb; 31 | 32 | rc = remove(path); 33 | munit_assert_int(rc, ==, 0); 34 | 35 | return 0; 36 | } 37 | 38 | void test_dir_tear_down(char *dir) 39 | { 40 | int rc; 41 | 42 | if (dir == NULL) { 43 | return; 44 | } 45 | 46 | rc = nftw(dir, test__dir_tear_down_nftw_fn, 10, 47 | FTW_DEPTH | FTW_MOUNT | FTW_PHYS); 48 | munit_assert_int(rc, ==, 0); 49 | free(dir); 50 | } 51 | -------------------------------------------------------------------------------- /test/lib/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_TEST_FS_H 2 | #define DQLITE_TEST_FS_H 3 | 4 | #define TEST__DIR_TEMPLATE "/tmp/dqlite-test-XXXXXX" 5 | 6 | /* Setup a temporary directory. */ 7 | char *test_dir_setup(void); 8 | 9 | /* Remove the temporary directory. */ 10 | void test_dir_tear_down(char *dir); 11 | 12 | #endif /* DQLITE_TEST_FS_H */ 13 | -------------------------------------------------------------------------------- /test/lib/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef DQLITE_TEST_HEAP_H 2 | #define DQLITE_TEST_HEAP_H 3 | 4 | #include "munit.h" 5 | 6 | /* Munit parameter defining the delay of the faulty memory implementation. */ 7 | #define TEST_HEAP_FAULT_DELAY "mem-fault-delay" 8 | 9 | /* Munit parameter defining the repeat of the faulty memory implementation. */ 10 | #define TEST_HEAP_FAULT_REPEAT "mem-fault-repeat" 11 | 12 | void test_heap_setup(const MunitParameter params[], void *user_data); 13 | void test_heap_tear_down(void *data); 14 | 15 | /* Configure the faulty memory management implementation so malloc()-related 16 | * functions start returning NULL pointers after 'delay' calls, and keep failing 17 | * for 'repeat' consecutive times. 18 | * 19 | * Note that the faults won't automatically take place, an explicit call to 20 | * test_mem_fault_enable() is needed. This allows configuration and actual 21 | * behavior to happen at different times (e.g. configure at test setup time and 22 | * enable at test case time). */ 23 | void test_heap_fault_config(int delay, int repeat); 24 | 25 | /* Enable the faulty behavior, which from this point on will honor the 26 | * parameters passed to test_mem_fault_config(). */ 27 | void test_heap_fault_enable(void); 28 | 29 | #define SETUP_HEAP test_heap_setup(params, user_data); 30 | #define TEAR_DOWN_HEAP test_heap_tear_down(data); 31 | 32 | #endif /* DQLITE_TEST_HEAP_H */ 33 | -------------------------------------------------------------------------------- /test/lib/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../../include/dqlite.h" 5 | 6 | #include "logger.h" 7 | #include "munit.h" 8 | 9 | void test_logger_emit(void *data, int level, const char *format, va_list args) 10 | { 11 | struct test_logger *t = data; 12 | char buf[1024]; 13 | const char *level_name; 14 | int i; 15 | 16 | (void)data; 17 | 18 | switch (level) { 19 | case DQLITE_DEBUG: 20 | level_name = "DEBUG"; 21 | break; 22 | case DQLITE_INFO: 23 | level_name = "INFO "; 24 | break; 25 | case DQLITE_WARN: 26 | level_name = "WARN "; 27 | break; 28 | case DQLITE_LOG_ERROR: 29 | level_name = "ERROR"; 30 | break; 31 | }; 32 | 33 | buf[0] = 0; 34 | 35 | sprintf(buf + strlen(buf), "%2d -> [%s] ", t->id, level_name); 36 | 37 | vsnprintf(buf + strlen(buf), 1024 - strlen(buf), format, args); 38 | munit_log(MUNIT_LOG_INFO, buf); 39 | return; 40 | 41 | snprintf(buf + strlen(buf), 1024 - strlen(buf), " "); 42 | 43 | for (i = strlen(buf); i < 85; i++) { 44 | buf[i] = ' '; 45 | } 46 | 47 | munit_log(MUNIT_LOG_INFO, buf); 48 | } 49 | 50 | void test_logger_setup(const MunitParameter params[], struct logger *l) 51 | { 52 | struct test_logger *t; 53 | 54 | (void)params; 55 | 56 | t = munit_malloc(sizeof *t); 57 | t->data = NULL; 58 | 59 | l->data = t; 60 | l->emit = test_logger_emit; 61 | } 62 | 63 | void test_logger_tear_down(struct logger *l) 64 | { 65 | free(l->data); 66 | } 67 | -------------------------------------------------------------------------------- /test/lib/logger.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Test logger. 3 | */ 4 | 5 | #ifndef TEST_LOGGER_H 6 | #define TEST_LOGGER_H 7 | 8 | #include "../../src/logger.h" 9 | 10 | #include "munit.h" 11 | 12 | void test_logger_setup(const MunitParameter params[], struct logger *l); 13 | void test_logger_tear_down(struct logger *l); 14 | 15 | struct test_logger 16 | { 17 | unsigned id; 18 | void *data; 19 | }; 20 | 21 | void test_logger_emit(void *data, int level, const char *fmt, va_list args); 22 | 23 | #define FIXTURE_LOGGER struct logger logger; 24 | #define SETUP_LOGGER test_logger_setup(params, &f->logger); 25 | #define TEAR_DOWN_LOGGER test_logger_tear_down(&f->logger); 26 | 27 | #endif /* TEST_LOGGER_H */ 28 | -------------------------------------------------------------------------------- /test/lib/raft_heap.c: -------------------------------------------------------------------------------- 1 | #include "../../src/raft.h" 2 | 3 | #include "fault.h" 4 | #include "raft_heap.h" 5 | 6 | struct heapFault 7 | { 8 | struct test_fault fault; 9 | const struct raft_heap *orig_heap; 10 | }; 11 | 12 | static struct heapFault faulty; 13 | 14 | static void *faultyMalloc(void *data, size_t size) 15 | { 16 | (void)data; 17 | if (test_fault_tick(&faulty.fault)) { 18 | return NULL; 19 | } else { 20 | return faulty.orig_heap->malloc(faulty.orig_heap->data, size); 21 | } 22 | } 23 | 24 | static void faultyFree(void *data, void *ptr) 25 | { 26 | (void)data; 27 | faulty.orig_heap->free(faulty.orig_heap->data, ptr); 28 | } 29 | 30 | static void *faultyCalloc(void *data, size_t nmemb, size_t size) 31 | { 32 | (void)data; 33 | if (test_fault_tick(&faulty.fault)) { 34 | return NULL; 35 | } else { 36 | return faulty.orig_heap->calloc(faulty.orig_heap->data, nmemb, 37 | size); 38 | } 39 | } 40 | 41 | static void *faultyRealloc(void *data, void *ptr, size_t size) 42 | { 43 | (void)data; 44 | if (test_fault_tick(&faulty.fault)) { 45 | return NULL; 46 | } else { 47 | return faulty.orig_heap->realloc(faulty.orig_heap->data, ptr, 48 | size); 49 | } 50 | } 51 | 52 | static void *faultyAlignedAlloc(void *data, size_t alignment, size_t size) 53 | { 54 | (void)data; 55 | if (test_fault_tick(&faulty.fault)) { 56 | return NULL; 57 | } else { 58 | return faulty.orig_heap->aligned_alloc(faulty.orig_heap->data, 59 | alignment, size); 60 | } 61 | } 62 | 63 | static void faultyAlignedFree(void *data, size_t alignment, void *ptr) 64 | { 65 | (void)data; 66 | (void)alignment; 67 | faulty.orig_heap->aligned_free(faulty.orig_heap->data, alignment, ptr); 68 | } 69 | 70 | void test_raft_heap_setup(const MunitParameter params[], void *user_data) 71 | { 72 | (void)params; 73 | (void)user_data; 74 | struct raft_heap *heap = munit_malloc(sizeof(*heap)); 75 | test_fault_init(&faulty.fault); 76 | faulty.orig_heap = raft_heap_get(); 77 | heap->data = NULL; 78 | heap->malloc = faultyMalloc; 79 | heap->free = faultyFree; 80 | heap->calloc = faultyCalloc; 81 | heap->realloc = faultyRealloc; 82 | heap->aligned_alloc = faultyAlignedAlloc; 83 | heap->aligned_free = faultyAlignedFree; 84 | raft_heap_set(heap); 85 | } 86 | 87 | void test_raft_heap_tear_down(void *data) 88 | { 89 | struct raft_heap *heap = (struct raft_heap *)raft_heap_get(); 90 | (void)data; 91 | raft_heap_set((struct raft_heap *)faulty.orig_heap); 92 | faulty.orig_heap = NULL; 93 | free(heap); 94 | } 95 | 96 | void test_raft_heap_fault_config(int delay, int repeat) 97 | { 98 | test_fault_config(&faulty.fault, delay, repeat); 99 | } 100 | 101 | void test_raft_heap_fault_enable(void) 102 | { 103 | test_fault_enable(&faulty.fault); 104 | } 105 | -------------------------------------------------------------------------------- /test/lib/raft_heap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Helpers for injecting failures into raft's allocator. 3 | */ 4 | 5 | #ifndef DQLITE_TEST_RAFT_HEAP_H 6 | #define DQLITE_TEST_RAFT_HEAP_H 7 | 8 | #include "munit.h" 9 | 10 | void test_raft_heap_setup(const MunitParameter params[], void *user_data); 11 | void test_raft_heap_tear_down(void *data); 12 | void test_raft_heap_fault_config(int delay, int repeat); 13 | void test_raft_heap_fault_enable(void); 14 | 15 | #endif /* DQLITE_TEST_RAFT_HEAP_H */ 16 | -------------------------------------------------------------------------------- /test/lib/registry.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_REGISTRY_H 2 | #define TEST_REGISTRY_H 3 | 4 | #include "../../src/registry.h" 5 | 6 | #define FIXTURE_REGISTRY struct registry registry 7 | #define SETUP_REGISTRY registry__init(&f->registry, &f->config) 8 | #define TEAR_DOWN_REGISTRY registry__close(&f->registry); 9 | 10 | #endif /* TEST_REGISTRY_H */ 11 | -------------------------------------------------------------------------------- /test/lib/server.h: -------------------------------------------------------------------------------- 1 | /* Setup fully blown servers running in standalone threads. */ 2 | 3 | #ifndef TEST_SERVER_H 4 | #define TEST_SERVER_H 5 | 6 | #include 7 | #include 8 | 9 | #include "../../src/client/protocol.h" 10 | 11 | #include "../../include/dqlite.h" 12 | 13 | #include "endpoint.h" 14 | #include "munit.h" 15 | 16 | #define SNAPSHOT_THRESHOLD_PARAM "snapshot-threshold" 17 | #define SNAPSHOT_COMPRESSION_PARAM "snapshot_compression" 18 | 19 | struct test_server 20 | { 21 | unsigned id; /* Server ID. */ 22 | char address[8]; /* Server address. */ 23 | char *dir; /* Data directory. */ 24 | dqlite_node *dqlite; /* Dqlite instance. */ 25 | bool role_management; 26 | struct client_proto client; /* Connected client. */ 27 | struct test_server *others[5]; /* Other servers, by ID-1. */ 28 | }; 29 | 30 | /* Initialize the test server. */ 31 | void test_server_setup(struct test_server *s, 32 | unsigned id, 33 | const MunitParameter params[]); 34 | 35 | /* Cleanup the test server. */ 36 | void test_server_tear_down(struct test_server *s); 37 | 38 | /* Set up the test server without running it. */ 39 | void test_server_prepare(struct test_server *s, const MunitParameter params[]); 40 | 41 | /* Run the test server after setting it up. */ 42 | void test_server_run(struct test_server *s); 43 | 44 | /* Start the test server. Equivalent to test_server_prepare + test_server_run. */ 45 | void test_server_start(struct test_server *s, const MunitParameter params[]); 46 | 47 | /* Stop the test server. */ 48 | void test_server_stop(struct test_server *s); 49 | 50 | /* Connect all the given the servers to each other. */ 51 | void test_server_network(struct test_server *servers, unsigned n_servers); 52 | 53 | /* Return a client connected to the server. */ 54 | struct client_proto *test_server_client(struct test_server *s); 55 | 56 | /* Closes and reopens a client connection to the server. */ 57 | void test_server_client_reconnect(struct test_server *s, 58 | struct client_proto *c); 59 | 60 | /* Opens a client connection to the server. */ 61 | void test_server_client_connect(struct test_server *s, struct client_proto *c); 62 | 63 | /* Closes a client connection to ther server. */ 64 | void test_server_client_close(struct test_server *s, struct client_proto *c); 65 | 66 | #endif /* TEST_SERVER_H */ 67 | -------------------------------------------------------------------------------- /test/lib/sqlite.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sqlite.h" 4 | 5 | void test_sqlite_setup(const MunitParameter params[]) 6 | { 7 | int rc; 8 | (void)params; 9 | rc = sqlite3_initialize(); 10 | if (rc != SQLITE_OK) { 11 | munit_errorf("sqlite_init(): %s", sqlite3_errstr(rc)); 12 | } 13 | rc = sqlite3_threadsafe(); 14 | if (!(rc == 1 || rc == 2)) { 15 | munit_errorf("sqlite3_threadsafe(): %d", rc); 16 | } 17 | } 18 | 19 | void test_sqlite_tear_down() 20 | { 21 | int rc; 22 | rc = sqlite3_shutdown(); 23 | if (rc != SQLITE_OK) { 24 | munit_errorf("sqlite_shutdown(): %s", sqlite3_errstr(rc)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/lib/sqlite.h: -------------------------------------------------------------------------------- 1 | /* Global SQLite configuration. */ 2 | 3 | #ifndef TEST_SQLITE_H 4 | #define TEST_SQLITE_H 5 | 6 | #include "munit.h" 7 | 8 | /* Setup SQLite global state. */ 9 | void test_sqlite_setup(const MunitParameter params[]); 10 | 11 | /* Teardown SQLite global state. */ 12 | void test_sqlite_tear_down(void); 13 | 14 | #define SETUP_SQLITE test_sqlite_setup(params); 15 | #define TEAR_DOWN_SQLITE test_sqlite_tear_down(); 16 | 17 | #endif /* TEST_SQLITE_H */ 18 | -------------------------------------------------------------------------------- /test/lib/stmt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Setup a test prepared statement. 3 | */ 4 | 5 | #ifndef TEST_STMT_H 6 | #define TEST_STMT_H 7 | 8 | #include 9 | 10 | #define FIXTURE_STMT sqlite3_stmt *stmt 11 | 12 | #define STMT_PREPARE(CONN, STMT, SQL) \ 13 | { \ 14 | int rc; \ 15 | rc = sqlite3_prepare_v2(CONN, SQL, -1, &STMT, NULL); \ 16 | munit_assert_int(rc, ==, 0); \ 17 | } 18 | 19 | #define STMT_FINALIZE(STMT) sqlite3_finalize(STMT) 20 | 21 | #define STMT_EXEC(CONN, SQL) \ 22 | { \ 23 | int rc; \ 24 | char *msg; \ 25 | rc = sqlite3_exec(CONN, SQL, NULL, NULL, &msg); \ 26 | munit_assert_int(rc, ==, SQLITE_OK); \ 27 | } 28 | 29 | #endif /* TEST_STMT_H */ 30 | -------------------------------------------------------------------------------- /test/lib/util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility macros and functions. 3 | */ 4 | 5 | #ifndef TEST_UTIL_H 6 | #define TEST_UTIL_H 7 | 8 | #include "munit.h" 9 | 10 | #include 11 | 12 | /* Wait a bounded time in seconds until a condition is true. */ 13 | #define AWAIT_TRUE(FN, ARG, SEC) \ 14 | do { \ 15 | struct timespec _start = {0}; \ 16 | struct timespec _end = {0}; \ 17 | clock_gettime(CLOCK_MONOTONIC, &_start); \ 18 | clock_gettime(CLOCK_MONOTONIC, &_end); \ 19 | while (!FN(ARG) && ((_end.tv_sec - _start.tv_sec) < SEC)) { \ 20 | clock_gettime(CLOCK_MONOTONIC, &_end); \ 21 | } \ 22 | if (!FN(ARG)) { \ 23 | return MUNIT_FAIL; \ 24 | } \ 25 | } while (0) 26 | 27 | #endif /* TEST_UTIL_H */ 28 | -------------------------------------------------------------------------------- /test/lib/uv.c: -------------------------------------------------------------------------------- 1 | #include "uv.h" 2 | 3 | #define TEST_UV_MAX_LOOP_RUN 10 /* Max n. of loop iterations upon teardown */ 4 | 5 | void test_uv_setup(const MunitParameter params[], struct uv_loop_s *l) 6 | { 7 | int rv; 8 | 9 | (void)params; 10 | 11 | rv = uv_loop_init(l); 12 | munit_assert_int(rv, ==, 0); 13 | } 14 | 15 | int test_uv_run(struct uv_loop_s *l, unsigned n) 16 | { 17 | unsigned i; 18 | int rv; 19 | 20 | munit_assert_int(n, >, 0); 21 | 22 | for (i = 0; i < n; i++) { 23 | rv = uv_run(l, UV_RUN_ONCE); 24 | if (rv < 0) { 25 | munit_errorf("uv_run: %s (%d)", uv_strerror(rv), rv); 26 | } 27 | if (rv == 0) { 28 | break; 29 | } 30 | } 31 | 32 | return rv; 33 | } 34 | 35 | void test_uv_stop(struct uv_loop_s *l) 36 | { 37 | unsigned n_handles; 38 | 39 | /* Spin a few times to trigger pending callbacks. */ 40 | n_handles = test_uv_run(l, TEST_UV_MAX_LOOP_RUN); 41 | if (n_handles > 0) { 42 | munit_errorf("loop has still %d pending active handles", 43 | n_handles); 44 | } 45 | } 46 | 47 | static void test_uv__walk_cb(uv_handle_t *handle, void *arg) 48 | { 49 | (void)arg; 50 | 51 | munit_logf(MUNIT_LOG_INFO, "handle %d", handle->type); 52 | } 53 | 54 | void test_uv_tear_down(struct uv_loop_s *l) 55 | { 56 | int rv; 57 | 58 | rv = uv_loop_close(l); 59 | if (rv != 0) { 60 | uv_walk(l, test_uv__walk_cb, NULL); 61 | munit_errorf("uv_loop_close: %s (%d)", uv_strerror(rv), rv); 62 | } 63 | 64 | rv = uv_replace_allocator(malloc, realloc, calloc, free); 65 | munit_assert_int(rv, ==, 0); 66 | } 67 | -------------------------------------------------------------------------------- /test/lib/uv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Add support for using the libuv loop in tests. 3 | */ 4 | 5 | #ifndef TEST_UV_H 6 | #define TEST_UV_H 7 | 8 | #include 9 | 10 | #include "munit.h" 11 | 12 | /* Max n. of loop iterations ran by a single function call */ 13 | #define TEST_UV_MAX_LOOP_RUN 10 14 | 15 | /** 16 | * Initialize the given libuv loop. 17 | */ 18 | void test_uv_setup(const MunitParameter params[], struct uv_loop_s *l); 19 | 20 | /** 21 | * Run the loop until there are no pending active handles. 22 | * 23 | * If there are still pending active handles after 10 loop iterations, the test 24 | * will fail. 25 | * 26 | * This is meant to be used in tear down functions. 27 | */ 28 | void test_uv_stop(struct uv_loop_s *l); 29 | 30 | /** 31 | * Tear down the loop making sure no active handles are left. 32 | */ 33 | void test_uv_tear_down(struct uv_loop_s *l); 34 | 35 | /** 36 | * Run the loop until there are no pending active handles or the given amount of 37 | * iterations is reached. 38 | * 39 | * Return non-zero if there are pending handles. 40 | */ 41 | int test_uv_run(struct uv_loop_s *l, unsigned n); 42 | 43 | /** 44 | * Run the loop until the given function returns true. 45 | * 46 | * If the loop exhausts all active handles or if #TEST_UV_MAX_LOOP_RUN is 47 | * reached without @f returning #true, the test fails. 48 | */ 49 | #define test_uv_run_until(DATA, FUNC) \ 50 | { \ 51 | unsigned i; \ 52 | int rv; \ 53 | for (i = 0; i < TEST_UV_MAX_LOOP_RUN; i++) { \ 54 | if (FUNC(DATA)) { \ 55 | break; \ 56 | } \ 57 | rv = uv_run(&f->loop, UV_RUN_ONCE); \ 58 | if (rv < 0) { \ 59 | munit_errorf("uv_run: %s", uv_strerror(rv)); \ 60 | } \ 61 | if (rv == 0) { \ 62 | if (FUNC(DATA)) { \ 63 | break; \ 64 | } \ 65 | munit_errorf( \ 66 | "uv_run: stopped after %u iterations", \ 67 | i + 1); \ 68 | } \ 69 | } \ 70 | if (i == TEST_UV_MAX_LOOP_RUN) { \ 71 | munit_errorf( \ 72 | "uv_run: condition not met in %d iterations", \ 73 | TEST_UV_MAX_LOOP_RUN); \ 74 | } \ 75 | } 76 | 77 | #endif /* TEST_UV_H */ 78 | -------------------------------------------------------------------------------- /test/lib/vfs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Setup an in-memory VFS instance to use in tests. 3 | */ 4 | 5 | #ifndef TEST_VFS_H 6 | #define TEST_VFS_H 7 | 8 | #include "../../src/vfs.h" 9 | 10 | #define FIXTURE_VFS struct sqlite3_vfs vfs; 11 | #define SETUP_VFS \ 12 | { \ 13 | int rv_; \ 14 | rv_ = VfsInit(&f->vfs, f->config.name); \ 15 | munit_assert_int(rv_, ==, 0); \ 16 | rv_ = sqlite3_vfs_register(&f->vfs, 0); \ 17 | munit_assert_int(rv_, ==, 0); \ 18 | } 19 | 20 | #define TEAR_DOWN_VFS \ 21 | { \ 22 | sqlite3_vfs_unregister(&f->vfs); \ 23 | VfsClose(&f->vfs); \ 24 | } 25 | 26 | #endif /* TEST_VFS_H */ 27 | -------------------------------------------------------------------------------- /test/raft/fuzzy/main_core.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | MunitSuite _main_suites[64]; 4 | int _main_suites_n = 0; 5 | 6 | /* Test runner executable */ 7 | int main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc)]) 8 | { 9 | MunitSuite suite = {(char *)"", NULL, _main_suites, 1, 0}; 10 | return munit_suite_main(&suite, (void *)"unit", argc, argv); 11 | } 12 | -------------------------------------------------------------------------------- /test/raft/integration/main_core.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("core") 4 | -------------------------------------------------------------------------------- /test/raft/integration/main_uv.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("uv") 4 | -------------------------------------------------------------------------------- /test/raft/integration/test_barrier.c: -------------------------------------------------------------------------------- 1 | #include "../lib/cluster.h" 2 | #include "../lib/runner.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * Fixture 7 | * 8 | *****************************************************************************/ 9 | 10 | struct fixture 11 | { 12 | FIXTURE_CLUSTER; 13 | }; 14 | 15 | static void *setUp(const MunitParameter params[], MUNIT_UNUSED void *user_data) 16 | { 17 | struct fixture *f = munit_malloc(sizeof *f); 18 | SETUP_CLUSTER(2); 19 | CLUSTER_BOOTSTRAP; 20 | CLUSTER_START; 21 | CLUSTER_ELECT(0); 22 | return f; 23 | } 24 | 25 | static void tearDown(void *data) 26 | { 27 | struct fixture *f = data; 28 | TEAR_DOWN_CLUSTER; 29 | free(f); 30 | } 31 | 32 | /****************************************************************************** 33 | * 34 | * Helper macros 35 | * 36 | *****************************************************************************/ 37 | 38 | struct result 39 | { 40 | int status; 41 | bool done; 42 | }; 43 | 44 | static void barrierCbAssertResult(struct raft_barrier *req, int status) 45 | { 46 | struct result *result = req->data; 47 | munit_assert_int(status, ==, result->status); 48 | result->done = true; 49 | } 50 | 51 | static bool barrierCbHasFired(struct raft_fixture *f, void *arg) 52 | { 53 | struct result *result = arg; 54 | (void)f; 55 | return result->done; 56 | } 57 | 58 | /* Submit a barrier request. */ 59 | #define BARRIER_SUBMIT(I) \ 60 | struct raft_barrier _req; \ 61 | struct result _result = {0, false}; \ 62 | int _rv; \ 63 | _req.data = &_result; \ 64 | _rv = raft_barrier(CLUSTER_RAFT(I), &_req, barrierCbAssertResult); \ 65 | munit_assert_int(_rv, ==, 0); 66 | 67 | /* Expect the barrier callback to fire with the given status. */ 68 | #define BARRIER_EXPECT(STATUS) _result.status = STATUS 69 | 70 | /* Wait until the barrier request completes. */ 71 | #define BARRIER_WAIT CLUSTER_STEP_UNTIL(barrierCbHasFired, &_result, 2000) 72 | 73 | /* Submit to the I'th server a barrier request and wait for the operation to 74 | * succeed. */ 75 | #define BARRIER(I) \ 76 | do { \ 77 | BARRIER_SUBMIT(I); \ 78 | BARRIER_WAIT; \ 79 | } while (0) 80 | 81 | /****************************************************************************** 82 | * 83 | * Success scenarios 84 | * 85 | *****************************************************************************/ 86 | 87 | SUITE(raft_barrier) 88 | 89 | TEST(raft_barrier, cb, setUp, tearDown, 0, NULL) 90 | { 91 | struct fixture *f = data; 92 | BARRIER(0); 93 | return MUNIT_OK; 94 | } 95 | -------------------------------------------------------------------------------- /test/raft/integration/test_bootstrap.c: -------------------------------------------------------------------------------- 1 | #include "../lib/cluster.h" 2 | #include "../lib/runner.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * Fixture holding a pristine raft instance. 7 | * 8 | *****************************************************************************/ 9 | 10 | struct fixture 11 | { 12 | FIXTURE_CLUSTER; 13 | }; 14 | 15 | static void *setUp(const MunitParameter params[], MUNIT_UNUSED void *user_data) 16 | { 17 | struct fixture *f = munit_malloc(sizeof *f); 18 | SETUP_CLUSTER(1); 19 | return f; 20 | } 21 | 22 | static void tearDown(void *data) 23 | { 24 | struct fixture *f = data; 25 | TEAR_DOWN_CLUSTER; 26 | free(f); 27 | } 28 | 29 | /****************************************************************************** 30 | * 31 | * Bootstrap tests. 32 | * 33 | *****************************************************************************/ 34 | 35 | SUITE(raft_bootstrap) 36 | 37 | /* Attempting to bootstrap an instance that's already started results in 38 | * RAFT_BUSY. */ 39 | TEST(raft_bootstrap, busy, setUp, tearDown, 0, NULL) 40 | { 41 | struct fixture *f = data; 42 | struct raft *raft; 43 | struct raft_configuration configuration; 44 | int rv; 45 | 46 | /* Bootstrap and the first server. */ 47 | CLUSTER_BOOTSTRAP_N_VOTING(1); 48 | CLUSTER_START; 49 | 50 | raft = CLUSTER_RAFT(0); 51 | CLUSTER_CONFIGURATION(&configuration); 52 | rv = raft_bootstrap(raft, &configuration); 53 | munit_assert_int(rv, ==, RAFT_BUSY); 54 | raft_configuration_close(&configuration); 55 | 56 | return MUNIT_OK; 57 | } 58 | -------------------------------------------------------------------------------- /test/raft/integration/test_digest.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/raft.h" 2 | #include "../lib/runner.h" 3 | 4 | SUITE(raft_digest) 5 | 6 | /* Generation of the ID of the bootstrap dqlite node. */ 7 | TEST(raft_digest, bootstrapServerId, NULL, NULL, 0, NULL) 8 | { 9 | const char *address = "127.0.0.1:65536"; 10 | unsigned long long id; 11 | id = raft_digest(address, 0); 12 | munit_assert_int(id, ==, 138882483); 13 | return MUNIT_OK; 14 | } 15 | -------------------------------------------------------------------------------- /test/raft/integration/test_heap.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/raft.h" 2 | 3 | #include "../lib/runner.h" 4 | 5 | /****************************************************************************** 6 | * 7 | * Default heap functions 8 | * 9 | *****************************************************************************/ 10 | 11 | SUITE(raft_heap) 12 | 13 | TEST(raft_heap, malloc, NULL, NULL, 0, NULL) 14 | { 15 | void *p; 16 | p = raft_malloc(8); 17 | munit_assert_ptr_not_null(p); 18 | raft_free(p); 19 | return MUNIT_OK; 20 | } 21 | 22 | TEST(raft_heap, calloc, NULL, NULL, 0, NULL) 23 | { 24 | void *p; 25 | p = raft_calloc(1, 8); 26 | munit_assert_ptr_not_null(p); 27 | munit_assert_int(*(uint64_t *)p, ==, 0); 28 | raft_free(p); 29 | return MUNIT_OK; 30 | } 31 | 32 | TEST(raft_heap, realloc, NULL, NULL, 0, NULL) 33 | { 34 | void *p; 35 | p = raft_realloc(NULL, 8); 36 | munit_assert_ptr_not_null(p); 37 | *(uint64_t *)p = 1; 38 | p = raft_realloc(p, 16); 39 | munit_assert_ptr_not_null(p); 40 | munit_assert_int(*(uint64_t *)p, ==, 1); 41 | raft_free(p); 42 | return MUNIT_OK; 43 | } 44 | 45 | TEST(raft_heap, aligned_alloc, NULL, NULL, 0, NULL) 46 | { 47 | void *p; 48 | p = raft_aligned_alloc(1024, 2048); 49 | munit_assert_ptr_not_null(p); 50 | munit_assert_int((uintptr_t)p % 1024, ==, 0); 51 | raft_free(p); 52 | return MUNIT_OK; 53 | } 54 | -------------------------------------------------------------------------------- /test/raft/integration/test_init.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/raft.h" 2 | #include "../lib/runner.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * raft_init 7 | * 8 | *****************************************************************************/ 9 | 10 | SUITE(raft_init) 11 | 12 | /* Incompatible raft->io and raft->fsm wrt async snapshots. */ 13 | TEST(raft_init, incompatIoFsmAsyncSnapshotNotNull, NULL, NULL, 0, NULL) 14 | { 15 | /* Set incompatible io and fsm versions and non-NULL snapshot_async fn */ 16 | struct raft r = {0}; 17 | struct raft_io io = {0}; 18 | struct raft_fsm fsm = {0}; 19 | io.version = 1; /* Too low */ 20 | io.async_work = (int (*)(struct raft_io *, struct raft_io_async_work *, 21 | raft_io_async_work_cb))(uintptr_t)0xDEADBEEF; 22 | fsm.version = 3; 23 | fsm.snapshot_async = (int (*)(struct raft_fsm *, struct raft_buffer **, 24 | unsigned int *))(uintptr_t)0xDEADBEEF; 25 | 26 | int rc; 27 | rc = raft_init(&r, &io, &fsm, 1, "1"); 28 | munit_assert_int(rc, ==, -1); 29 | munit_assert_string_equal( 30 | r.errmsg, 31 | "async snapshot requires io->version > 1 and async_work method."); 32 | return MUNIT_OK; 33 | } 34 | 35 | /* Incompatible raft->io and raft->fsm wrt async snapshots. */ 36 | TEST(raft_init, incompatIoFsmAsyncSnapshotNull, NULL, NULL, 0, NULL) 37 | { 38 | /* Set incompatible io and fsm versions and NULL snapshot_async fn */ 39 | struct raft r = {0}; 40 | struct raft_io io = {0}; 41 | struct raft_fsm fsm = {0}; 42 | io.version = 2; 43 | io.async_work = NULL; 44 | fsm.version = 3; 45 | fsm.snapshot_async = (int (*)(struct raft_fsm *, struct raft_buffer **, 46 | unsigned int *))(uintptr_t)0xDEADBEEF; 47 | 48 | int rc; 49 | rc = raft_init(&r, &io, &fsm, 1, "1"); 50 | munit_assert_int(rc, ==, -1); 51 | munit_assert_string_equal( 52 | r.errmsg, 53 | "async snapshot requires io->version > 1 and async_work method."); 54 | return MUNIT_OK; 55 | } 56 | 57 | TEST(raft_init, ioVersionNotSet, NULL, NULL, 0, NULL) 58 | { 59 | struct raft r = {0}; 60 | struct raft_io io = {0}; 61 | struct raft_fsm fsm = {0}; 62 | io.version = 0; 63 | fsm.version = 3; 64 | 65 | int rc; 66 | rc = raft_init(&r, &io, &fsm, 1, "1"); 67 | munit_assert_int(rc, ==, -1); 68 | munit_assert_string_equal(r.errmsg, "io->version must be set"); 69 | return MUNIT_OK; 70 | } 71 | 72 | TEST(raft_init, fsmVersionNotSet, NULL, NULL, 0, NULL) 73 | { 74 | struct raft r = {0}; 75 | struct raft_io io = {0}; 76 | struct raft_fsm fsm = {0}; 77 | io.version = 2; 78 | fsm.version = 0; 79 | 80 | int rc; 81 | rc = raft_init(&r, &io, &fsm, 1, "1"); 82 | munit_assert_int(rc, ==, -1); 83 | munit_assert_string_equal(r.errmsg, "fsm->version must be set"); 84 | return MUNIT_OK; 85 | } 86 | -------------------------------------------------------------------------------- /test/raft/integration/test_recover.c: -------------------------------------------------------------------------------- 1 | #include "../lib/cluster.h" 2 | #include "../lib/runner.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * Fixture holding a bootstrapped raft cluster. 7 | * 8 | *****************************************************************************/ 9 | 10 | struct fixture 11 | { 12 | FIXTURE_CLUSTER; 13 | }; 14 | 15 | static void *setUp(const MunitParameter params[], MUNIT_UNUSED void *user_data) 16 | { 17 | struct fixture *f = munit_malloc(sizeof *f); 18 | SETUP_CLUSTER(3); 19 | CLUSTER_BOOTSTRAP; 20 | return f; 21 | } 22 | 23 | static void tearDown(void *data) 24 | { 25 | struct fixture *f = data; 26 | TEAR_DOWN_CLUSTER; 27 | free(f); 28 | } 29 | 30 | /****************************************************************************** 31 | * 32 | * Recover tests. 33 | * 34 | *****************************************************************************/ 35 | 36 | SUITE(raft_recover) 37 | 38 | /* Attempting to recover a running instance results in RAFT_BUSY. */ 39 | TEST(raft_recover, busy, setUp, tearDown, 0, NULL) 40 | { 41 | struct fixture *f = data; 42 | struct raft *raft; 43 | struct raft_configuration configuration; 44 | int rv; 45 | 46 | /* Start all servers. */ 47 | CLUSTER_START; 48 | 49 | raft = CLUSTER_RAFT(0); 50 | CLUSTER_CONFIGURATION(&configuration); 51 | rv = raft_recover(raft, &configuration); 52 | munit_assert_int(rv, ==, RAFT_BUSY); 53 | raft_configuration_close(&configuration); 54 | 55 | return MUNIT_OK; 56 | } 57 | -------------------------------------------------------------------------------- /test/raft/integration/test_strerror.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/raft.h" 2 | #include "../lib/runner.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * raft_strerror 7 | * 8 | *****************************************************************************/ 9 | 10 | SUITE(raft_strerror) 11 | 12 | #define ERR_CODE_MAP(X) \ 13 | X(RAFT_NOMEM) \ 14 | X(RAFT_BADID) \ 15 | X(RAFT_DUPLICATEID) \ 16 | X(RAFT_DUPLICATEADDRESS) \ 17 | X(RAFT_BADROLE) \ 18 | X(RAFT_MALFORMED) \ 19 | X(RAFT_NOTLEADER) \ 20 | X(RAFT_LEADERSHIPLOST) \ 21 | X(RAFT_SHUTDOWN) \ 22 | X(RAFT_CANTBOOTSTRAP) \ 23 | X(RAFT_CANTCHANGE) \ 24 | X(RAFT_CORRUPT) \ 25 | X(RAFT_CANCELED) \ 26 | X(RAFT_NAMETOOLONG) \ 27 | X(RAFT_TOOBIG) \ 28 | X(RAFT_NOCONNECTION) \ 29 | X(RAFT_BUSY) \ 30 | X(RAFT_IOERR) 31 | 32 | #define TEST_CASE_STRERROR(CODE) \ 33 | TEST(raft_strerror, CODE, NULL, NULL, 0, NULL) \ 34 | { \ 35 | (void)data; \ 36 | (void)params; \ 37 | munit_assert_not_null(raft_strerror(CODE)); \ 38 | return MUNIT_OK; \ 39 | } 40 | 41 | ERR_CODE_MAP(TEST_CASE_STRERROR) 42 | 43 | TEST(raft_strerror, default, NULL, NULL, 0, NULL) 44 | { 45 | (void)data; 46 | (void)params; 47 | munit_assert_string_equal(raft_strerror(666), "unknown error"); 48 | return MUNIT_OK; 49 | } 50 | -------------------------------------------------------------------------------- /test/raft/integration/test_uv_recover.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | #include "../lib/uv.h" 3 | 4 | /****************************************************************************** 5 | * 6 | * Fixture 7 | * 8 | *****************************************************************************/ 9 | 10 | struct fixture 11 | { 12 | FIXTURE_UV_DEPS; 13 | FIXTURE_UV; 14 | }; 15 | 16 | static void *setUp(const MunitParameter params[], void *user_data) 17 | { 18 | struct fixture *f = munit_malloc(sizeof *f); 19 | SETUP_UV_DEPS; 20 | SETUP_UV; 21 | return f; 22 | } 23 | 24 | static void tearDown(void *data) 25 | { 26 | struct fixture *f = data; 27 | TEAR_DOWN_UV; 28 | TEAR_DOWN_UV_DEPS; 29 | free(f); 30 | } 31 | 32 | /****************************************************************************** 33 | * 34 | * raft_io->recover() 35 | * 36 | *****************************************************************************/ 37 | 38 | SUITE(recover) 39 | 40 | /* Invoke recover and assert that it fails with the given error. */ 41 | #define RECOVER_ERROR(RV, CONF) \ 42 | { \ 43 | int rv_; \ 44 | rv_ = f->io.recover(&f->io, CONF); \ 45 | munit_assert_int(rv_, ==, RV); \ 46 | } 47 | 48 | /* Invoke recover and assert that it succeeds */ 49 | #define RECOVER(CONF) RECOVER_ERROR(0, CONF) 50 | 51 | /* If the instance has been already initialized, an error is returned. */ 52 | /* A new configuration is saved as last entry on disk. */ 53 | TEST(recover, newConfiguration, setUp, tearDown, 0, NULL) 54 | { 55 | struct fixture *f = data; 56 | struct raft_configuration configuration1; 57 | struct raft_configuration configuration2; 58 | int rv; 59 | 60 | /* Bootstrap using an initial configuration */ 61 | raft_configuration_init(&configuration1); 62 | rv = raft_configuration_add(&configuration1, 1, "1", RAFT_VOTER); 63 | munit_assert_int(rv, ==, 0); 64 | rv = raft_configuration_add(&configuration1, 2, "2", RAFT_VOTER); 65 | munit_assert_int(rv, ==, 0); 66 | rv = f->io.bootstrap(&f->io, &configuration1); 67 | munit_assert_int(rv, ==, 0); 68 | 69 | /* Bootstrap using a different configuration */ 70 | raft_configuration_init(&configuration2); 71 | rv = raft_configuration_add(&configuration2, 1, "1", RAFT_VOTER); 72 | munit_assert_int(rv, ==, 0); 73 | 74 | RECOVER(&configuration2); 75 | 76 | raft_configuration_close(&configuration1); 77 | raft_configuration_close(&configuration2); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /test/raft/integration/test_uv_work.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../../../src/raft/uv.h" 4 | #include "../lib/dir.h" 5 | #include "../lib/loop.h" 6 | #include "../lib/runner.h" 7 | #include "../lib/uv.h" 8 | 9 | /****************************************************************************** 10 | * 11 | * Fixture 12 | * 13 | *****************************************************************************/ 14 | 15 | struct fixture 16 | { 17 | FIXTURE_UV_DEPS; 18 | FIXTURE_UV; 19 | }; 20 | 21 | struct result 22 | { 23 | int rv; /* Indicate success or failure of the work */ 24 | int counter; /* Proof that work was performed */ 25 | bool done; /* To check test termination */ 26 | }; 27 | 28 | /****************************************************************************** 29 | * 30 | * Set up and tear down. 31 | * 32 | *****************************************************************************/ 33 | 34 | static void *setUp(const MunitParameter params[], void *user_data) 35 | { 36 | struct fixture *f = munit_malloc(sizeof *f); 37 | SETUP_UV_DEPS; 38 | SETUP_UV; 39 | return f; 40 | } 41 | 42 | static void tearDownDeps(void *data) 43 | { 44 | struct fixture *f = data; 45 | if (f == NULL) { 46 | return; 47 | } 48 | TEAR_DOWN_UV_DEPS; 49 | free(f); 50 | } 51 | 52 | static void tearDown(void *data) 53 | { 54 | struct fixture *f = data; 55 | if (f == NULL) { 56 | return; 57 | } 58 | TEAR_DOWN_UV; 59 | tearDownDeps(f); 60 | } 61 | 62 | /****************************************************************************** 63 | * 64 | * UvAsyncWork 65 | * 66 | *****************************************************************************/ 67 | 68 | static void asyncWorkCbAssertResult(struct raft_io_async_work *req, int status) 69 | { 70 | struct result *r = req->data; 71 | munit_assert_int(status, ==, r->rv); 72 | munit_assert_int(r->counter, ==, 1); 73 | r->done = true; 74 | } 75 | 76 | static int asyncWorkFn(struct raft_io_async_work *req) 77 | { 78 | struct result *r = req->data; 79 | sleep(1); 80 | r->counter = 1; 81 | return r->rv; 82 | } 83 | 84 | SUITE(UvAsyncWork) 85 | 86 | static char *rvs[] = {"-1", "0", "1", "37", NULL}; 87 | static MunitParameterEnum rvs_params[] = { 88 | {"rv", rvs}, 89 | {NULL, NULL}, 90 | }; 91 | 92 | TEST(UvAsyncWork, work, setUp, tearDown, 0, rvs_params) 93 | { 94 | struct fixture *f = data; 95 | struct result res = {0}; 96 | struct raft_io_async_work req = {0}; 97 | res.rv = (int)strtol(munit_parameters_get(params, "rv"), NULL, 0); 98 | req.data = &res; 99 | req.work = asyncWorkFn; 100 | UvAsyncWork(&f->io, &req, asyncWorkCbAssertResult); 101 | LOOP_RUN_UNTIL(&res.done); 102 | return MUNIT_OK; 103 | } 104 | -------------------------------------------------------------------------------- /test/raft/lib/addrinfo.h: -------------------------------------------------------------------------------- 1 | /* Support for getaddrinfo mocking for test purposes. 2 | * 3 | * The addrinfo.c test fixture includes definitions of getaddrinfo and 4 | * freeaddinfo that override the libc definitions, adding usage checks and the 5 | * ability to inject responses. These additional features are activated by 6 | * adding SET_UP_ADDRINFO/TEAR_DOWN_ADDRINFO to the fixture constructor and 7 | * destructor. 8 | * 9 | * The overriding definitions of getaddrinfo and freeaddrinfo affect all code 10 | * that's linked with addrinfo.c, and we rely on being able to retrieve the 11 | * original libc definitions using dlsym. When libc is statically linked, this 12 | * is not possible, so we just arrange for the overriding definitions not to be 13 | * compiled and skip any tests that rely on getaddrinfo result injection. 14 | */ 15 | 16 | #ifndef TEST_ADDRINFO_H 17 | #define TEST_ADDRINFO_H 18 | 19 | #include "munit.h" 20 | 21 | #ifdef DQLITE_STATIC_LIBC 22 | 23 | /* Trickery to cause tests that use getaddrinfo result injection to be skipped 24 | * when building with WITH_STATIC_DEPS. */ 25 | #define ADDRINFO_TEST(S, C, SETUP, TEAR_DOWN, OPTIONS, PARAMS) \ 26 | TEST(S, C, SETUP, TEAR_DOWN, OPTIONS, PARAMS) \ 27 | { \ 28 | return MUNIT_SKIP; \ 29 | } \ 30 | static MUNIT_UNUSED MunitResult test_unused_##S##_##C( \ 31 | MUNIT_UNUSED const MunitParameter params[], MUNIT_UNUSED void *data) 32 | 33 | #else /* ifndef DQLITE_STATIC_LIBC */ 34 | 35 | #define ADDRINFO_TEST(S, C, SETUP, TEAR_DOWN, OPTIONS, PARAMS) \ 36 | TEST(S, C, SETUP, TEAR_DOWN, OPTIONS, PARAMS) 37 | 38 | #endif /* ifdef DQLITE_STATIC_LIBC ... else */ 39 | 40 | #define SET_UP_ADDRINFO AddrinfoInjectSetUp(params) 41 | #define TEAR_DOWN_ADDRINFO AddrinfoInjectTearDown() 42 | 43 | typedef struct AddrinfoResult 44 | { 45 | const char *ip; 46 | const int port; 47 | } AddrinfoResult_t; 48 | 49 | void AddrinfoInjectSetResponse(int rv, 50 | int num_results, 51 | const struct AddrinfoResult *results); 52 | 53 | void AddrinfoInjectSetUp(const MunitParameter params[]); 54 | void AddrinfoInjectTearDown(void); 55 | 56 | #endif // #ifndef TEST_ADDRINFO_H 57 | -------------------------------------------------------------------------------- /test/raft/lib/aio.c: -------------------------------------------------------------------------------- 1 | #include "aio.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "munit.h" 9 | 10 | int AioFill(aio_context_t *ctx, unsigned n) 11 | { 12 | char buf[256]; 13 | int fd; 14 | int rv; 15 | int limit; 16 | int used; 17 | 18 | /* Figure out how many events are available. */ 19 | fd = open("/proc/sys/fs/aio-max-nr", O_RDONLY); 20 | munit_assert_int(fd, !=, -1); 21 | 22 | rv = read(fd, buf, sizeof buf); 23 | munit_assert_int(rv, !=, -1); 24 | 25 | close(fd); 26 | 27 | limit = atoi(buf); 28 | munit_assert_int(limit, >, 0); 29 | 30 | /* Figure out how many events are in use. */ 31 | fd = open("/proc/sys/fs/aio-nr", O_RDONLY); 32 | munit_assert_int(fd, !=, -1); 33 | 34 | rv = read(fd, buf, sizeof buf); 35 | munit_assert_int(rv, !=, -1); 36 | 37 | close(fd); 38 | 39 | used = atoi(buf); 40 | munit_assert_int(used, >=, 0); 41 | 42 | /* Best effort check that nothing process is using AIO. Our own unit tests 43 | * case use up to 2 event slots at the time this function is called, so we 44 | * don't consider those. */ 45 | if (used > 2) { 46 | return -1; 47 | } 48 | 49 | rv = syscall(__NR_io_setup, limit - used - n, ctx); 50 | if (rv != 0) { 51 | /* The `limit - used - n` calculation is racy and io_setup can fail with 52 | * EAGAIN if in meantime another proces has reserved some events */ 53 | munit_assert_int(errno, ==, EAGAIN); 54 | return -1; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | void AioDestroy(aio_context_t ctx) 61 | { 62 | int rv; 63 | 64 | rv = syscall(__NR_io_destroy, ctx); 65 | munit_assert_int(rv, ==, 0); 66 | } 67 | -------------------------------------------------------------------------------- /test/raft/lib/aio.h: -------------------------------------------------------------------------------- 1 | /* Utilities around the Kernel AIO sub-system. */ 2 | #ifndef TEST_AIO_H 3 | #define TEST_AIO_H 4 | 5 | #include 6 | 7 | /* Fill the AIO subsystem resources by allocating a lot of events to the given 8 | * context, and leaving only @n events available for subsequent calls to 9 | * @io_setup. 10 | * 11 | * Return -1 if it looks like there is another process already using the AIO 12 | * subsystem, which would most probably make the calling test flaky because 13 | * there won't be exactly @n events available anymore. */ 14 | int AioFill(aio_context_t *ctx, unsigned n); 15 | 16 | /* Destroy the given AIO context. */ 17 | void AioDestroy(aio_context_t ctx); 18 | 19 | #endif /* TEST_AIO_H */ 20 | -------------------------------------------------------------------------------- /test/raft/lib/cluster.c: -------------------------------------------------------------------------------- 1 | #include "cluster.h" 2 | 3 | static void randomize(struct raft_fixture *f, unsigned i, int what) 4 | { 5 | struct raft *raft = raft_fixture_get(f, i); 6 | switch (what) { 7 | case RAFT_FIXTURE_TICK: 8 | /* TODO: provide an API to inspect how much time has elapsed since 9 | * the last election timer reset */ 10 | if (raft->election_timer_start == raft->io->time(raft->io)) { 11 | raft_fixture_set_randomized_election_timeout( 12 | f, i, 13 | munit_rand_int_range(raft->election_timeout, 14 | raft->election_timeout * 2)); 15 | } 16 | break; 17 | case RAFT_FIXTURE_DISK: 18 | raft_fixture_set_disk_latency(f, i, munit_rand_int_range(10, 25)); 19 | break; 20 | case RAFT_FIXTURE_NETWORK: 21 | raft_fixture_set_network_latency(f, i, 22 | munit_rand_int_range(25, 50)); 23 | break; 24 | default: 25 | munit_assert(0); 26 | break; 27 | } 28 | } 29 | 30 | void cluster_randomize_init(struct raft_fixture *f) 31 | { 32 | unsigned i; 33 | for (i = 0; i < raft_fixture_n(f); i++) { 34 | randomize(f, i, RAFT_FIXTURE_TICK); 35 | randomize(f, i, RAFT_FIXTURE_DISK); 36 | randomize(f, i, RAFT_FIXTURE_NETWORK); 37 | } 38 | } 39 | 40 | void cluster_randomize(struct raft_fixture *f, struct raft_fixture_event *event) 41 | { 42 | unsigned index = raft_fixture_event_server_index(event); 43 | int type = raft_fixture_event_type(event); 44 | randomize(f, index, type); 45 | } 46 | -------------------------------------------------------------------------------- /test/raft/lib/fault.c: -------------------------------------------------------------------------------- 1 | #include "fault.h" 2 | 3 | #include "munit.h" 4 | 5 | void FaultInit(struct Fault *f) 6 | { 7 | f->countdown = -1; 8 | f->n = -1; 9 | f->paused = false; 10 | } 11 | 12 | bool FaultTick(struct Fault *f) 13 | { 14 | if (MUNIT_UNLIKELY(f->paused)) { 15 | return false; 16 | } 17 | 18 | /* If the initial delay parameter was set to -1, then never fail. This is 19 | * the most common case. */ 20 | if (MUNIT_LIKELY(f->countdown < 0)) { 21 | return false; 22 | } 23 | 24 | /* If we did not yet reach 'delay' ticks, then just decrease the countdown. 25 | */ 26 | if (f->countdown > 0) { 27 | f->countdown--; 28 | return false; 29 | } 30 | 31 | munit_assert_int(f->countdown, ==, 0); 32 | 33 | /* We reached 'delay' ticks, let's see how many times we have to trigger the 34 | * fault, if any. */ 35 | 36 | if (f->n < 0) { 37 | /* Trigger the fault forever. */ 38 | return true; 39 | } 40 | 41 | if (f->n > 0) { 42 | /* Trigger the fault at least this time. */ 43 | f->n--; 44 | return true; 45 | } 46 | 47 | munit_assert_int(f->n, ==, 0); 48 | 49 | /* We reached 'repeat' ticks, let's stop triggering the fault. */ 50 | f->countdown--; 51 | 52 | return false; 53 | } 54 | 55 | void FaultConfig(struct Fault *f, int delay, int repeat) 56 | { 57 | f->countdown = delay; 58 | f->n = repeat; 59 | } 60 | 61 | void FaultPause(struct Fault *f) 62 | { 63 | f->paused = true; 64 | } 65 | 66 | void FaultResume(struct Fault *f) 67 | { 68 | f->paused = false; 69 | } 70 | -------------------------------------------------------------------------------- /test/raft/lib/fault.h: -------------------------------------------------------------------------------- 1 | /* Helper for test components supporting fault injection. */ 2 | 3 | #ifndef TEST_FAULT_H 4 | #define TEST_FAULT_H 5 | 6 | #include 7 | 8 | /* Information about a fault that should occur in a component. */ 9 | struct Fault 10 | { 11 | int countdown; /* Trigger the fault when this counter gets to zero. */ 12 | int n; /* Repeat the fault this many times. Default is -1. */ 13 | bool paused; /* Pause fault triggering. */ 14 | }; 15 | 16 | /* Initialize a fault. */ 17 | void FaultInit(struct Fault *f); 18 | 19 | /* Advance the counters of the fault. Return true if the fault should be 20 | * triggered, false otherwise. */ 21 | bool FaultTick(struct Fault *f); 22 | 23 | /* Configure the fault with the given values. */ 24 | void FaultConfig(struct Fault *f, int delay, int repeat); 25 | 26 | /* Pause triggering configured faults. */ 27 | void FaultPause(struct Fault *f); 28 | 29 | /* Resume triggering configured faults. */ 30 | void FaultResume(struct Fault *f); 31 | 32 | #endif /* TESTFAULT_H */ 33 | -------------------------------------------------------------------------------- /test/raft/lib/fs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # Setup loopback disk devices to test the raft I/O implementation against 4 | # various file systems. 5 | 6 | usage() { 7 | echo "usage: $0 setup|teardown [types]" 8 | } 9 | 10 | if [ "${#}" -lt 1 ]; then 11 | usage 12 | exit 1 13 | fi 14 | 15 | cmd="${1}" 16 | shift 17 | 18 | types="tmpfs" 19 | 20 | # Check if loop devices are available, we might be running inside an 21 | # unprivileged container 22 | if sudo losetup -f > /dev/null 2>&1; then 23 | types="$types ext4" 24 | 25 | if [ "$(which mkfs.btrfs)" != "" ]; then 26 | types="$types btrfs" 27 | fi 28 | 29 | if [ "$(which mkfs.xfs)" != "" ]; then 30 | types="$types xfs" 31 | fi 32 | 33 | if [ "$(which zfs)" != "" ]; then 34 | types="$types zfs" 35 | fi 36 | 37 | if [ "${#}" -gt 0 ]; then 38 | types="${@}" 39 | fi 40 | 41 | fi 42 | 43 | if [ "${cmd}" = "detect" ]; then 44 | vars="" 45 | for type in $types; do 46 | vars="${vars}RAFT_TMP_$(echo ${type} | tr [a-z] [A-Z])=./tmp/${type} " 47 | done 48 | echo $vars 49 | exit 0 50 | fi 51 | 52 | if [ "${cmd}" = "setup" ]; then 53 | mkdir ./tmp 54 | 55 | for type in $types; do 56 | echo -n "Creating $type loop device mount..." 57 | 58 | # Create the fs mount point 59 | mkdir "./tmp/${type}" 60 | 61 | if [ "$type" = "tmpfs" ]; then 62 | # For tmpfs we don't need a loopback disk device. 63 | sudo mount -t tmpfs -o size=32m tmpfs ./tmp/tmpfs 64 | else 65 | # Create a loopback disk device 66 | dd if=/dev/zero of="./tmp/.${type}" bs=4096 count=28672 > /dev/null 2>&1 67 | loop=$(sudo losetup -f) 68 | sudo losetup "${loop}" "./tmp/.${type}" 69 | 70 | # Initialize the file system 71 | if [ "$type" = "zfs" ]; then 72 | sudo zpool create raft "${loop}" 73 | sudo zfs create -o mountpoint=$(pwd)/tmp/zfs raft/zfs 74 | else 75 | sudo mkfs.${type} "${loop}" > /dev/null 2>&1 76 | sudo mount "${loop}" "./tmp/${type}" 77 | fi 78 | fi 79 | 80 | sudo chown $USER "./tmp/${type}" 81 | 82 | echo " done" 83 | done 84 | 85 | exit 0 86 | fi 87 | 88 | if [ "${cmd}" = "teardown" ]; then 89 | 90 | for type in $types; do 91 | echo -n "Deleting $type loop device mount..." 92 | 93 | sudo umount "./tmp/${type}" 94 | rm -rf "./tmp/${type}" 95 | 96 | if [ "$type" != "tmpfs" ]; then 97 | # For zfs we need to destroy the pool 98 | if [ "$type" = "zfs" ]; then 99 | sudo zpool destroy raft 100 | fi 101 | 102 | # For regular file systems, remove the loopback disk device. 103 | loop=$(sudo losetup -a | grep ".${type}" | cut -f 1 -d :) 104 | sudo losetup -d "${loop}" 105 | rm "./tmp/.${type}" 106 | fi 107 | 108 | echo " done" 109 | done 110 | 111 | rmdir ./tmp 112 | 113 | exit 0 114 | fi 115 | 116 | usage 117 | 118 | exit 1 119 | -------------------------------------------------------------------------------- /test/raft/lib/fsm.h: -------------------------------------------------------------------------------- 1 | /* Test implementation of the raft_fsm interface, with fault injection. 2 | * 3 | * The test FSM supports only two commands: setting x and setting y. */ 4 | 5 | #ifndef TEST_FSM_H 6 | #define TEST_FSM_H 7 | 8 | #include "../../../src/raft.h" 9 | 10 | void FsmInit(struct raft_fsm *fsm, int version); 11 | 12 | /* Same as FsmInit but with asynchronous snapshots */ 13 | void FsmInitAsync(struct raft_fsm *fsm, int version); 14 | 15 | void FsmClose(struct raft_fsm *fsm); 16 | 17 | /* Encode a command to set x to the given value. */ 18 | void FsmEncodeSetX(int value, struct raft_buffer *buf); 19 | 20 | /* Encode a command to add the given value to x. */ 21 | void FsmEncodeAddX(int value, struct raft_buffer *buf); 22 | 23 | /* Encode a command to set y to the given value. */ 24 | void FsmEncodeSetY(int value, struct raft_buffer *buf); 25 | 26 | /* Encode a command to add the given value to y. */ 27 | void FsmEncodeAddY(int value, struct raft_buffer *buf); 28 | 29 | /* Encode a snapshot of an FSM with the given values for x and y. */ 30 | void FsmEncodeSnapshot(int x, 31 | int y, 32 | struct raft_buffer *bufs[], 33 | unsigned *n_bufs); 34 | 35 | /* Return the current value of x or y. */ 36 | int FsmGetX(struct raft_fsm *fsm); 37 | int FsmGetY(struct raft_fsm *fsm); 38 | 39 | #endif /* TEST_FSM_H */ 40 | -------------------------------------------------------------------------------- /test/raft/lib/heap.h: -------------------------------------------------------------------------------- 1 | /* Add support for fault injection and leak detection to stdlib's malloc() 2 | * family. */ 3 | 4 | #ifndef TEST_HEAP_H 5 | #define TEST_HEAP_H 6 | 7 | #include "../../../src/raft.h" 8 | #include "munit.h" 9 | 10 | /* Munit parameter defining after how many API calls the test raft_heap 11 | * implementation should start failing and return errors. The default is -1, 12 | * meaning that no failure will ever occur. */ 13 | #define TEST_HEAP_FAULT_DELAY "heap-fault-delay" 14 | 15 | /* Munit parameter defining how many consecutive times API calls against the 16 | * test raft_heap implementation should keep failing after they started 17 | * failing. This parameter has an effect only if 'store-fail-delay' is 0 or 18 | * greater. The default is 1, and -1 means "keep failing forever". */ 19 | #define TEST_HEAP_FAULT_REPEAT "heap-fault-repeat" 20 | 21 | /* Macro helpers. */ 22 | #define FIXTURE_HEAP struct raft_heap heap 23 | #define SET_UP_HEAP HeapSetUp(params, &f->heap) 24 | #define TEAR_DOWN_HEAP HeapTearDown(&f->heap) 25 | #define HEAP_FAULT_ENABLE HeapFaultEnable(&f->heap) 26 | 27 | void HeapSetUp(const MunitParameter params[], struct raft_heap *h); 28 | void HeapTearDown(struct raft_heap *h); 29 | 30 | void HeapFaultConfig(struct raft_heap *h, int delay, int repeat); 31 | void HeapFaultEnable(struct raft_heap *h); 32 | 33 | #endif /* TEST_HEAP_H */ 34 | -------------------------------------------------------------------------------- /test/raft/lib/loop.c: -------------------------------------------------------------------------------- 1 | #include "loop.h" 2 | 3 | void test_loop_walk_cb(uv_handle_t *handle, void *arg) 4 | { 5 | (void)arg; 6 | munit_logf(MUNIT_LOG_INFO, "handle %s (%d)", uv_handle_type_name(handle->type), handle->type); 7 | } 8 | -------------------------------------------------------------------------------- /test/raft/lib/macros.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Miscellaneous test macros. 3 | */ 4 | 5 | #ifndef TEST_MACROS_H_ 6 | #define TEST_MACROS_H_ 7 | 8 | #define GET_2ND_ARG(arg1, arg2, ...) arg2 9 | #define GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 10 | #define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4 11 | #define GET_5TH_ARG(arg1, arg2, arg3, arg4, arg5, ...) arg5 12 | 13 | #endif /* TEST_MACROS_H_ */ 14 | -------------------------------------------------------------------------------- /test/raft/lib/snapshot.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Raft snapshot test helpers. 3 | */ 4 | 5 | #ifndef TEST_SNAPSHOT_H 6 | #define TEST_SNAPSHOT_H 7 | 8 | #include "../../../src/raft.h" 9 | 10 | #include "../../../src/raft/configuration.h" 11 | 12 | /** 13 | * Allocate and create the given snapshot, using the given @LAST_INDEX, 14 | * @LAST_TERM, the given @CONF, and generating an FSM snapshot using @X and @Y. 15 | */ 16 | #define CREATE_SNAPSHOT(SNAPSHOT, LAST_INDEX, LAST_TERM, CONF, CONF_INDEX, X, \ 17 | Y) \ 18 | SNAPSHOT = raft_malloc(sizeof *SNAPSHOT); \ 19 | munit_assert_ptr_not_null(SNAPSHOT); \ 20 | SNAPSHOT->index = LAST_INDEX; \ 21 | SNAPSHOT->term = LAST_TERM; \ 22 | SNAPSHOT->configuration = CONF; \ 23 | SNAPSHOT->configuration_index = CONF_INDEX; \ 24 | FsmEncodeSnapshot(X, Y, &SNAPSHOT->bufs, &SNAPSHOT->n_bufs) 25 | 26 | #endif /* TEST_CONFIGURATION_H */ 27 | -------------------------------------------------------------------------------- /test/raft/lib/uv.h: -------------------------------------------------------------------------------- 1 | /* Helpers around the libuv-based implementation of the raft_io interface. */ 2 | 3 | #ifndef TEST_UV_H 4 | #define TEST_UV_H 5 | 6 | #include "../../../src/raft.h" 7 | #include "dir.h" 8 | #include "heap.h" 9 | #include "loop.h" 10 | 11 | #define FIXTURE_UV_TRANSPORT struct raft_uv_transport transport 12 | #define SETUP_UV_TRANSPORT \ 13 | do { \ 14 | int rv_; \ 15 | f->transport.version = 1; \ 16 | rv_ = raft_uv_tcp_init(&f->transport, &f->loop); \ 17 | munit_assert_int(rv_, ==, 0); \ 18 | } while (0) 19 | #define TEAR_DOWN_UV_TRANSPORT raft_uv_tcp_close(&f->transport) 20 | 21 | #define FIXTURE_UV_DEPS \ 22 | FIXTURE_DIR; \ 23 | FIXTURE_HEAP; \ 24 | FIXTURE_LOOP; \ 25 | FIXTURE_UV_TRANSPORT 26 | #define SETUP_UV_DEPS \ 27 | SET_UP_DIR; \ 28 | SET_UP_HEAP; \ 29 | SETUP_LOOP; \ 30 | SETUP_UV_TRANSPORT 31 | #define TEAR_DOWN_UV_DEPS \ 32 | TEAR_DOWN_UV_TRANSPORT; \ 33 | TEAR_DOWN_LOOP; \ 34 | TEAR_DOWN_HEAP; \ 35 | TEAR_DOWN_DIR 36 | 37 | #define FIXTURE_UV struct raft_io io 38 | 39 | #define SETUP_UV \ 40 | do { \ 41 | int rv_; \ 42 | rv_ = raft_uv_init(&f->io, &f->loop, f->dir, &f->transport); \ 43 | munit_assert_int(rv_, ==, 0); \ 44 | raft_uv_set_auto_recovery(&f->io, false); \ 45 | rv_ = f->io.init(&f->io, 1, "127.0.0.1:9001"); \ 46 | munit_assert_int(rv_, ==, 0); \ 47 | } while (0) 48 | 49 | MUNIT_UNUSED static void uvCloseCb(struct raft_io *io) 50 | { 51 | bool *closed = io->data; 52 | *closed = true; 53 | } 54 | 55 | #define TEAR_DOWN_UV \ 56 | do { \ 57 | bool _closed = false; \ 58 | f->io.data = &_closed; \ 59 | f->io.close(&f->io, uvCloseCb); \ 60 | LOOP_RUN_UNTIL(&_closed); \ 61 | raft_uv_close(&f->io); \ 62 | } while (0) 63 | 64 | #endif /* TEST_UV_H */ 65 | -------------------------------------------------------------------------------- /test/raft/unit/main_core.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("core") 4 | -------------------------------------------------------------------------------- /test/raft/unit/main_uv.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("uv") 4 | -------------------------------------------------------------------------------- /test/raft/unit/test_err.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../../../src/raft/err.h" 5 | #include "../lib/heap.h" 6 | #include "../lib/runner.h" 7 | 8 | /* An error messages which is 249 characters. */ 9 | #define LONG_ERRMSG \ 10 | "boom boom boom boom boom boom boom boom boom boom boom boom boom boom " \ 11 | "boom boom boom boom boom boom boom boom boom boom boom boom boom boom " \ 12 | "boom boom boom boom boom boom boom boom boom boom boom boom boom boom " \ 13 | "boom boom boom boom boom boom boom boom" 14 | 15 | /****************************************************************************** 16 | * 17 | * ErrMsgPrintf 18 | * 19 | *****************************************************************************/ 20 | 21 | SUITE(ErrMsgPrintf) 22 | 23 | /* The format string has no parameters. */ 24 | TEST(ErrMsgPrintf, noParams, NULL, NULL, 0, NULL) 25 | { 26 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 27 | ErrMsgPrintf(errmsg, "boom"); 28 | munit_assert_string_equal(errmsg, "boom"); 29 | return MUNIT_OK; 30 | } 31 | 32 | /* The format string has parameters. */ 33 | TEST(ErrMsgPrintf, params, NULL, NULL, 0, NULL) 34 | { 35 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 36 | ErrMsgPrintf(errmsg, "boom %d", 123); 37 | munit_assert_string_equal(errmsg, "boom 123"); 38 | return MUNIT_OK; 39 | } 40 | 41 | /****************************************************************************** 42 | * 43 | * ErrMsgWrapf 44 | * 45 | *****************************************************************************/ 46 | 47 | SUITE(ErrMsgWrapf) 48 | 49 | /* The wrapping format string has no parameters. */ 50 | TEST(ErrMsgWrapf, noParams, NULL, NULL, 0, NULL) 51 | { 52 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 53 | ErrMsgPrintf(errmsg, "boom"); 54 | ErrMsgWrapf(errmsg, "no luck"); 55 | munit_assert_string_equal(errmsg, "no luck: boom"); 56 | return MUNIT_OK; 57 | } 58 | 59 | /* The wrapping format string has parameters. */ 60 | TEST(ErrMsgWrapf, params, NULL, NULL, 0, NULL) 61 | { 62 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 63 | ErrMsgPrintf(errmsg, "boom"); 64 | ErrMsgWrapf(errmsg, "no luck, %s", "joe"); 65 | munit_assert_string_equal(errmsg, "no luck, joe: boom"); 66 | return MUNIT_OK; 67 | } 68 | 69 | /* The wrapped error message gets partially truncated. */ 70 | TEST(ErrMsgWrapf, partialTruncate, NULL, NULL, 0, NULL) 71 | { 72 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 73 | ErrMsgPrintf(errmsg, "no luck"); 74 | ErrMsgWrapf(errmsg, LONG_ERRMSG); 75 | munit_assert_string_equal(errmsg, LONG_ERRMSG ": no l"); 76 | return MUNIT_OK; 77 | } 78 | 79 | /* The wrapped error message gets entirely truncated. */ 80 | TEST(ErrMsgWrapf, fullTruncate, NULL, NULL, 0, NULL) 81 | { 82 | char errmsg[RAFT_ERRMSG_BUF_SIZE]; 83 | ErrMsgPrintf(errmsg, "no luck"); 84 | ErrMsgWrapf(errmsg, LONG_ERRMSG " boom"); 85 | munit_assert_string_equal(errmsg, LONG_ERRMSG " boom"); 86 | return MUNIT_OK; 87 | } 88 | -------------------------------------------------------------------------------- /test/raft/unit/test_uv_os.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/raft/uv_os.h" 2 | #include "../lib/runner.h" 3 | 4 | SUITE(UvOsJoin) 5 | 6 | /* dir and filename have sensible lengths */ 7 | TEST(UvOsJoin, basic, NULL, NULL, 0, NULL) 8 | { 9 | int rv; 10 | const char *dir = "/home"; 11 | const char *filename = "testfile"; 12 | char path[UV__PATH_SZ]; 13 | rv = UvOsJoin(dir, filename, path); 14 | munit_assert_int(rv, ==, 0); 15 | munit_assert_string_equal(path, "/home/testfile"); 16 | return MUNIT_OK; 17 | } 18 | 19 | TEST(UvOsJoin, dirTooLong, NULL, NULL, 0, NULL) 20 | { 21 | int rv; 22 | char path[UV__PATH_SZ]; 23 | char dir[UV__DIR_LEN + 2]; /* Room for '\0' and then 1 char over limit. */ 24 | memset((char *)dir, '/', sizeof(dir)); 25 | dir[sizeof(dir) - 1] = '\0'; 26 | const char *filename = "testfile"; 27 | 28 | rv = UvOsJoin(dir, filename, path); 29 | munit_assert_int(rv, !=, 0); 30 | return MUNIT_OK; 31 | } 32 | 33 | TEST(UvOsJoin, filenameTooLong, NULL, NULL, 0, NULL) 34 | { 35 | int rv; 36 | char path[UV__PATH_SZ]; 37 | const char *dir = "testdir"; 38 | char filename[UV__FILENAME_LEN + 2]; 39 | memset((char *)filename, 'a', sizeof(filename)); 40 | filename[sizeof(filename) - 1] = '\0'; 41 | 42 | rv = UvOsJoin(dir, filename, path); 43 | munit_assert_int(rv, !=, 0); 44 | return MUNIT_OK; 45 | } 46 | 47 | TEST(UvOsJoin, dirAndFilenameTooLong, NULL, NULL, 0, NULL) 48 | { 49 | int rv; 50 | /* +2 to silence compilers that complain that dir & filename would overflow 51 | * path, but it's strictly not needed and doesn't influence the test. */ 52 | char path[UV__PATH_SZ + 2]; 53 | char dir[UV__DIR_LEN + 2]; 54 | memset((char *)dir, '/', sizeof(dir)); 55 | dir[sizeof(dir) - 1] = '\0'; 56 | 57 | char filename[UV__FILENAME_LEN + 2]; 58 | memset((char *)filename, 'a', sizeof(filename)); 59 | filename[sizeof(filename) - 1] = '\0'; 60 | 61 | rv = UvOsJoin(dir, filename, path); 62 | munit_assert_int(rv, !=, 0); 63 | return MUNIT_OK; 64 | } 65 | 66 | TEST(UvOsJoin, dirAndFilenameMax, NULL, NULL, 0, NULL) 67 | { 68 | int rv; 69 | char path[UV__PATH_SZ]; 70 | char dir[UV__DIR_LEN + 1]; 71 | memset((char *)dir, '/', sizeof(dir)); 72 | dir[sizeof(dir) - 1] = '\0'; 73 | 74 | char filename[UV__FILENAME_LEN + 1]; 75 | memset((char *)filename, 'a', sizeof(filename)); 76 | filename[sizeof(filename) - 1] = '\0'; 77 | 78 | rv = UvOsJoin(dir, filename, path); 79 | munit_assert_int(rv, ==, 0); 80 | char cmp_path[UV__DIR_LEN + UV__FILENAME_LEN + 1 + 1]; 81 | snprintf(cmp_path, UV__DIR_LEN + UV__FILENAME_LEN + 1 + 1, "%s/%s", dir, 82 | filename); 83 | munit_assert_string_equal(path, cmp_path); 84 | return MUNIT_OK; 85 | } 86 | -------------------------------------------------------------------------------- /test/unit/ext/test_uv_pool.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/lib/threadpool.h" 2 | #include "../../../src/utils.h" 3 | #include "../../lib/runner.h" 4 | #include "../../lib/uv.h" 5 | 6 | TEST_MODULE(ext_uv_pool); 7 | 8 | /****************************************************************************** 9 | * 10 | * threadpool 11 | * 12 | ******************************************************************************/ 13 | 14 | enum { WORK_ITEMS_NR = 50000 }; 15 | 16 | struct fixture { 17 | pool_work_t w; 18 | uv_loop_t loop; 19 | pool_t pool; 20 | }; 21 | 22 | static void loop_setup(struct fixture *f) 23 | { 24 | int rc; 25 | 26 | rc = uv_loop_init(&f->loop); 27 | munit_assert_int(rc, ==, 0); 28 | 29 | rc = pool_init(&f->pool, &f->loop, 4, POOL_QOS_PRIO_FAIR); 30 | munit_assert_int(rc, ==, 0); 31 | } 32 | 33 | static void bottom_work_cb(pool_work_t *w) 34 | { 35 | (void)w; 36 | } 37 | 38 | static void bottom_after_work_cb(pool_work_t *w) 39 | { 40 | static int count = 0; 41 | 42 | if (count == WORK_ITEMS_NR) 43 | pool_close(w->pool); 44 | 45 | count++; 46 | assert(w->type != WT_BAR); 47 | free(w); 48 | } 49 | 50 | static void after_work_cb(pool_work_t *w) 51 | { 52 | enum pool_work_type pwt; 53 | pool_work_t *work; 54 | unsigned int wt; 55 | unsigned int i; 56 | 57 | for (i = 0; i <= WORK_ITEMS_NR + 1 /* +WT_BAR */; i++) { 58 | work = calloc(1, sizeof(*work)); 59 | 60 | if (i < WORK_ITEMS_NR / 2) 61 | wt = WT_ORD1; 62 | else if (i == WORK_ITEMS_NR / 2) 63 | wt = WT_BAR; 64 | else 65 | wt = WT_ORD2; 66 | 67 | pwt = i % 2 == 0 ? wt : WT_UNORD; 68 | pool_queue_work(w->pool, work, i, pwt, bottom_work_cb, 69 | bottom_after_work_cb); 70 | } 71 | } 72 | 73 | static void work_cb(pool_work_t *w) 74 | { 75 | (void)w; 76 | } 77 | 78 | static void threadpool_tear_down(void *data) 79 | { 80 | int rc; 81 | struct fixture *f = data; 82 | 83 | pool_fini(&f->pool); 84 | rc = uv_loop_close(&f->loop); 85 | munit_assert_int(rc, ==, 0); 86 | free(f); 87 | } 88 | 89 | static void *threadpool_setup(const MunitParameter params[], void *user_data) 90 | { 91 | (void)params; 92 | (void)user_data; 93 | struct fixture *f = calloc(1, sizeof *f); 94 | loop_setup(f); 95 | return f; 96 | } 97 | 98 | TEST_SUITE(threadpool); 99 | TEST_SETUP(threadpool, threadpool_setup); 100 | TEST_TEAR_DOWN(threadpool, threadpool_tear_down); 101 | TEST_CASE(threadpool, sync, NULL) 102 | { 103 | (void)params; 104 | struct fixture *f = data; 105 | int rc; 106 | 107 | pool_queue_work(&f->pool, &f->w, 0, WT_UNORD, work_cb, after_work_cb); 108 | 109 | rc = uv_run(&f->loop, UV_RUN_DEFAULT); 110 | munit_assert_int(rc, ==, 0); 111 | 112 | return MUNIT_OK; 113 | } 114 | -------------------------------------------------------------------------------- /test/unit/lib/test_addr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../../../src/lib/addr.h" 5 | 6 | #include "../../lib/runner.h" 7 | 8 | TEST_MODULE(lib_addr); 9 | 10 | struct fixture 11 | { 12 | struct sockaddr_un addr_un; 13 | }; 14 | 15 | static void *setup(const MunitParameter params[], void *user_data) 16 | { 17 | struct fixture *f = munit_malloc(sizeof(*f)); 18 | (void)params; 19 | (void)user_data; 20 | return f; 21 | } 22 | 23 | static void tear_down(void *data) 24 | { 25 | struct fixture *f = data; 26 | free(f); 27 | } 28 | 29 | #define ASSERT_PARSE(ADDR, STATUS, FAMILY) \ 30 | socklen_t addr_len = sizeof(f->addr_un); \ 31 | int rv; \ 32 | rv = AddrParse(ADDR, (struct sockaddr *)&f->addr_un, &addr_len, \ 33 | "8080", DQLITE_ADDR_PARSE_UNIX); \ 34 | munit_assert_int(rv, ==, STATUS); \ 35 | munit_assert_int(f->addr_un.sun_family, ==, FAMILY) 36 | 37 | TEST_SUITE(parse); 38 | TEST_SETUP(parse, setup); 39 | TEST_TEAR_DOWN(parse, tear_down); 40 | 41 | TEST_CASE(parse, ipv4_no_port, NULL) 42 | { 43 | struct fixture *f = data; 44 | (void)params; 45 | ASSERT_PARSE("1.2.3.4", 0, AF_INET); 46 | return MUNIT_OK; 47 | } 48 | 49 | TEST_CASE(parse, ipv4_with_port, NULL) 50 | { 51 | struct fixture *f = data; 52 | (void)params; 53 | ASSERT_PARSE("127.0.0.1:9001", 0, AF_INET); 54 | return MUNIT_OK; 55 | } 56 | 57 | TEST_CASE(parse, ipv6_no_port, NULL) 58 | { 59 | struct fixture *f = data; 60 | (void)params; 61 | ASSERT_PARSE("::1", 0, AF_INET6); 62 | return MUNIT_OK; 63 | } 64 | 65 | TEST_CASE(parse, ipv6_with_port, NULL) 66 | { 67 | struct fixture *f = data; 68 | (void)params; 69 | ASSERT_PARSE("[2001:4860:4860::8888]:9001", 0, AF_INET6); 70 | return MUNIT_OK; 71 | } 72 | 73 | TEST_CASE(parse, unix, NULL) 74 | { 75 | struct fixture *f = data; 76 | (void)params; 77 | ASSERT_PARSE("@xyz", 0, AF_UNIX); 78 | return MUNIT_OK; 79 | } 80 | 81 | TEST_CASE(parse, unix_auto, NULL) 82 | { 83 | struct fixture *f = data; 84 | (void)params; 85 | ASSERT_PARSE("@", 0, AF_UNIX); 86 | return MUNIT_OK; 87 | } 88 | -------------------------------------------------------------------------------- /test/unit/lib/test_byte.c: -------------------------------------------------------------------------------- 1 | #include "../../../src/lib/byte.h" 2 | 3 | #include "../../lib/runner.h" 4 | 5 | TEST_MODULE(lib_addr); 6 | TEST_SUITE(endian); 7 | 8 | static uint16_t vfsFlip16(uint16_t v) 9 | { 10 | #if defined(DQLITE_BIG_ENDIAN) 11 | return v; 12 | #elif defined(DQLITE_LITTLE_ENDIAN) && defined(DQLITE_HAVE_BSWAP) 13 | defined(__GNUC__) && __GNUC__ >= 4 && 14 | __GNUC_MINOR__ >= 8 return __builtin_bswap16(v); 15 | #else 16 | union { 17 | uint16_t u; 18 | uint8_t v[4]; 19 | } s; 20 | 21 | s.v[0] = (uint8_t)(v >> 8); 22 | s.v[1] = (uint8_t)v; 23 | 24 | return s.u; 25 | #endif 26 | } 27 | 28 | static uint32_t vfsFlip32(uint32_t v) 29 | { 30 | #if defined(DQLITE_BIG_ENDIAN) 31 | return v; 32 | #elif defined(DQLITE_LITTLE_ENDIAN) && defined(DQLITE_HAVE_BSWAP) 33 | return __builtin_bswap32(v); 34 | #else 35 | union { 36 | uint32_t u; 37 | uint8_t v[4]; 38 | } s; 39 | 40 | s.v[0] = (uint8_t)(v >> 24); 41 | s.v[1] = (uint8_t)(v >> 16); 42 | s.v[2] = (uint8_t)(v >> 8); 43 | s.v[3] = (uint8_t)v; 44 | 45 | return s.u; 46 | #endif 47 | } 48 | 49 | static uint16_t vfsGet16(const uint8_t *buf) 50 | { 51 | union { 52 | uint16_t u; 53 | uint8_t v[2]; 54 | } s; 55 | 56 | s.v[0] = buf[0]; 57 | s.v[1] = buf[1]; 58 | 59 | return vfsFlip16(s.u); 60 | } 61 | 62 | static uint32_t vfsGet32(const uint8_t *buf) 63 | { 64 | union { 65 | uint32_t u; 66 | uint8_t v[4]; 67 | } s; 68 | 69 | s.v[0] = buf[0]; 70 | s.v[1] = buf[1]; 71 | s.v[2] = buf[2]; 72 | s.v[3] = buf[3]; 73 | 74 | return vfsFlip32(s.u); 75 | } 76 | 77 | static void vfsPut32(uint32_t v, uint8_t *buf) 78 | { 79 | uint32_t u = vfsFlip32(v); 80 | memcpy(buf, &u, sizeof u); 81 | } 82 | 83 | TEST_CASE(endian, get16, NULL) 84 | { 85 | (void)params; 86 | (void)data; 87 | uint16_t x, y; 88 | uint8_t buf[2]; 89 | for (x = 0; x < 1 << 8; x++) { 90 | for (y = 0; y < 1 << 8; y++) { 91 | buf[0] = (uint8_t)x; 92 | buf[1] = (uint8_t)y; 93 | munit_assert_uint16(ByteGetBe16(buf), ==, 94 | vfsGet16(buf)); 95 | } 96 | } 97 | return MUNIT_OK; 98 | } 99 | 100 | TEST_CASE(endian, get32, NULL) 101 | { 102 | (void)params; 103 | (void)data; 104 | uint8_t buf[4]; 105 | uint32_t i; 106 | for (i = 0; i < 1 << 16; i++) { 107 | munit_rand_memory(4, buf); 108 | munit_assert_uint32(ByteGetBe32(buf), ==, vfsGet32(buf)); 109 | } 110 | return MUNIT_OK; 111 | } 112 | 113 | TEST_CASE(endian, put32, NULL) 114 | { 115 | (void)params; 116 | (void)data; 117 | uint32_t v; 118 | uint8_t buf[4], vfs_buf[4]; 119 | uint32_t i; 120 | for (i = 0; i < (1 << 16); i++) { 121 | v = munit_rand_uint32(); 122 | BytePutBe32(v, buf); 123 | vfsPut32(v, vfs_buf); 124 | munit_assert_memory_equal(4, buf, vfs_buf); 125 | } 126 | return MUNIT_OK; 127 | } 128 | -------------------------------------------------------------------------------- /test/unit/main.c: -------------------------------------------------------------------------------- 1 | #include "../lib/runner.h" 2 | 3 | RUNNER("unit"); 4 | -------------------------------------------------------------------------------- /test/unit/test_command.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../../src/command.h" 4 | 5 | #include "../lib/runner.h" 6 | 7 | TEST_MODULE(command); 8 | 9 | /****************************************************************************** 10 | * 11 | * Open. 12 | * 13 | ******************************************************************************/ 14 | 15 | TEST_SUITE(open); 16 | 17 | TEST_CASE(open, encode, NULL) 18 | { 19 | struct command_open c; 20 | struct raft_buffer buf; 21 | int rc; 22 | (void)data; 23 | (void)params; 24 | c.filename = "test.db"; 25 | rc = command__encode(COMMAND_OPEN, &c, &buf); 26 | munit_assert_int(rc, ==, 0); 27 | munit_assert_int(buf.len, ==, 16); 28 | raft_free(buf.base); 29 | return MUNIT_OK; 30 | } 31 | 32 | TEST_CASE(open, decode, NULL) 33 | { 34 | struct command_open c1; 35 | void *c2; 36 | int type; 37 | struct raft_buffer buf; 38 | int rc; 39 | (void)data; 40 | (void)params; 41 | c1.filename = "db"; 42 | rc = command__encode(COMMAND_OPEN, &c1, &buf); 43 | munit_assert_int(rc, ==, 0); 44 | rc = command__decode(&buf, &type, &c2); 45 | munit_assert_int(rc, ==, 0); 46 | munit_assert_int(type, ==, COMMAND_OPEN); 47 | munit_assert_string_equal(((struct command_open *)c2)->filename, "db"); 48 | raft_free(c2); 49 | raft_free(buf.base); 50 | return MUNIT_OK; 51 | } 52 | -------------------------------------------------------------------------------- /test/unit/test_registry.c: -------------------------------------------------------------------------------- 1 | #include "../lib/config.h" 2 | #include "../lib/heap.h" 3 | #include "../lib/logger.h" 4 | #include "../lib/registry.h" 5 | #include "../lib/runner.h" 6 | #include "../lib/sqlite.h" 7 | #include "../lib/vfs.h" 8 | 9 | TEST_MODULE(registry); 10 | 11 | #define FIXTURE \ 12 | FIXTURE_LOGGER; \ 13 | FIXTURE_VFS; \ 14 | FIXTURE_CONFIG; \ 15 | FIXTURE_REGISTRY; 16 | 17 | #define SETUP \ 18 | SETUP_HEAP; \ 19 | SETUP_SQLITE; \ 20 | SETUP_LOGGER; \ 21 | SETUP_VFS; \ 22 | SETUP_CONFIG; \ 23 | SETUP_REGISTRY; 24 | 25 | #define TEAR_DOWN \ 26 | TEAR_DOWN_REGISTRY; \ 27 | TEAR_DOWN_CONFIG; \ 28 | TEAR_DOWN_VFS; \ 29 | TEAR_DOWN_LOGGER; \ 30 | TEAR_DOWN_SQLITE; \ 31 | TEAR_DOWN_HEAP; 32 | 33 | /****************************************************************************** 34 | * 35 | * db-related APIs. 36 | * 37 | ******************************************************************************/ 38 | 39 | struct db_fixture 40 | { 41 | FIXTURE; 42 | }; 43 | 44 | TEST_SUITE(db); 45 | TEST_SETUP(db) 46 | { 47 | struct db_fixture *f = munit_malloc(sizeof *f); 48 | SETUP; 49 | return f; 50 | } 51 | TEST_TEAR_DOWN(db) 52 | { 53 | struct db_fixture *f = data; 54 | TEAR_DOWN; 55 | free(f); 56 | } 57 | 58 | /* Get a db that didn't exist before. */ 59 | TEST_CASE(db, get_new, NULL) 60 | { 61 | struct db_fixture *f = data; 62 | struct db *db; 63 | (void)params; 64 | int rc; 65 | rc = registry__db_get(&f->registry, "test.db", &db); 66 | munit_assert_int(rc, ==, 0); 67 | munit_assert_string_equal(db->filename, "test.db"); 68 | return MUNIT_OK; 69 | } 70 | 71 | /* Get a previously registered db. */ 72 | TEST_CASE(db, get_existing, NULL) 73 | { 74 | struct db_fixture *f = data; 75 | struct db *db1; 76 | struct db *db2; 77 | (void)params; 78 | int rc; 79 | rc = registry__db_get(&f->registry, "test.db", &db1); 80 | munit_assert_int(rc, ==, 0); 81 | rc = registry__db_get(&f->registry, "test.db", &db2); 82 | munit_assert_int(rc, ==, 0); 83 | munit_assert_ptr_equal(db1, db2); 84 | return MUNIT_OK; 85 | } 86 | -------------------------------------------------------------------------------- /test/unit/test_sm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../../src/lib/sm.h" 4 | 5 | #include "../lib/runner.h" 6 | 7 | TEST_MODULE(sm); 8 | 9 | /****************************************************************************** 10 | * 11 | * SM. 12 | * 13 | ******************************************************************************/ 14 | 15 | TEST_SUITE(sm); 16 | 17 | /** 18 | * An example of simple state machine. 19 | * 20 | * TRANSIENT 21 | * | ^ 22 | * restarted | | crashed 23 | * V | 24 | * ONLINE--------+ checked 25 | * | <-------+ 26 | * stopped | 27 | * V 28 | * OFFLINE 29 | */ 30 | 31 | enum states { 32 | S_ONLINE, 33 | S_OFFLINE, 34 | S_TRANSIENT, 35 | S_NR, 36 | }; 37 | 38 | static const struct sm_conf op_states[S_NR] = { 39 | [S_ONLINE] = 40 | { 41 | .flags = SM_INITIAL, 42 | .name = "online", 43 | .allowed = BITS(S_ONLINE) | BITS(S_TRANSIENT) | BITS(S_OFFLINE), 44 | }, 45 | [S_TRANSIENT] = {.flags = SM_FAILURE, 46 | .name = "transient", 47 | .allowed = BITS(S_ONLINE)}, 48 | [S_OFFLINE] = 49 | { 50 | .flags = SM_FINAL, 51 | .name = "offline", 52 | .allowed = 0, 53 | }, 54 | }; 55 | 56 | enum triggers { 57 | T_RESTARTED, 58 | T_CRASHED, 59 | T_CHECKED, 60 | T_STOPPED, 61 | }; 62 | 63 | struct op_states_sm 64 | { 65 | struct sm sm; 66 | enum triggers sm_trigger; 67 | }; 68 | 69 | static bool sm_invariant(const struct sm *m, int prev_state) 70 | { 71 | struct op_states_sm *sm = CONTAINER_OF(m, struct op_states_sm, sm); 72 | 73 | return ERGO(sm_state(m) == S_ONLINE, 74 | ERGO(prev_state == SM_PREV_NONE, sm->sm_trigger == 0)) && 75 | ERGO(sm_state(m) == S_ONLINE, 76 | ERGO(prev_state == S_ONLINE, 77 | sm->sm_trigger == BITS(T_CHECKED)) && 78 | ERGO(prev_state == S_TRANSIENT, 79 | sm->sm_trigger == BITS(T_RESTARTED))) && 80 | ERGO(sm_state(m) == S_TRANSIENT, 81 | sm->sm_trigger == BITS(T_CRASHED) && m->rc == -42) && 82 | ERGO(sm_state(m) == S_OFFLINE, 83 | sm->sm_trigger == BITS(T_STOPPED)); 84 | } 85 | 86 | TEST_CASE(sm, simple, NULL) 87 | { 88 | (void)data; 89 | (void)params; 90 | struct op_states_sm sm = {}; 91 | struct sm *m = &sm.sm; 92 | 93 | sm_init(&sm.sm, sm_invariant, NULL, op_states, "test", S_ONLINE); 94 | 95 | sm.sm_trigger = BITS(T_CHECKED); 96 | sm_move(m, S_ONLINE); 97 | sm_move(m, S_ONLINE); 98 | sm_move(m, S_ONLINE); 99 | 100 | sm.sm_trigger = BITS(T_CRASHED); 101 | sm_fail(m, S_TRANSIENT, -42 /* -rc */); 102 | 103 | sm.sm_trigger = BITS(T_RESTARTED); 104 | sm_move(m, S_ONLINE); 105 | 106 | sm.sm_trigger = BITS(T_STOPPED); 107 | sm_move(m, S_OFFLINE); 108 | 109 | sm_fini(m); 110 | return 0; 111 | } 112 | --------------------------------------------------------------------------------