├── .github └── workflows │ └── build.yml ├── .gitignore ├── .travis.yml ├── BUILD ├── CMakeLists.txt ├── LICENSE ├── README.md ├── WORKSPACE ├── bazel └── braft.bzl ├── benchmark └── README.md ├── build_in_travis_ci.sh ├── cmake └── FindGperftools.cmake ├── docs ├── cn │ ├── benchmark.md │ ├── cli.md │ ├── client.md │ ├── overview.md │ ├── paxos_protocol.md │ ├── qjm.md │ ├── raft_protocol.md │ ├── replication.md │ ├── server.md │ ├── witness.md │ └── zab_protocol.md └── images │ ├── 2pc_choice.png │ ├── all_rep.png │ ├── asymmetric_partition.png │ ├── benchmark.png │ ├── benchmark0.png │ ├── chain_rep.png │ ├── chain_rep2.png │ ├── conflict_choices.png │ ├── distributed_state_machine.png │ ├── flow.png │ ├── leader_term.png │ ├── log_compaction.png │ ├── log_recovery.png │ ├── log_replication.png │ ├── log_stats.png │ ├── logs.png │ ├── member_change_procedure.png │ ├── member_change_stats.png │ ├── membership.png │ ├── multi_paxos.png │ ├── pp1.png │ ├── pp2.png │ ├── pp3.png │ ├── proposal_number.png │ ├── qjm_overview.png │ ├── raft.png │ ├── raft_stat.png │ ├── split_votes.png │ ├── symmetric_partition.png │ ├── tree_rep.png │ ├── zab_broadcast.jpg │ ├── zab_broadcast_algo.png │ ├── zab_discovery.png │ ├── zab_fast_election.png │ ├── zab_recovery_algo.png │ ├── zab_recovery_phase.png │ ├── zab_stats.jpg │ └── zab_sync.png ├── example ├── README.md ├── atomic │ ├── CMakeLists.txt │ ├── atomic.proto │ ├── client.cpp │ ├── jepsen_control.sh │ ├── run_client.sh │ ├── run_server.sh │ ├── server.cpp │ ├── stop.sh │ └── test.cpp ├── block │ ├── CMakeLists.txt │ ├── block.proto │ ├── client.cpp │ ├── run_client.sh │ ├── run_server.sh │ ├── server.cpp │ └── stop.sh ├── counter │ ├── CMakeLists.txt │ ├── client.cpp │ ├── counter.proto │ ├── run_client.sh │ ├── run_server.sh │ ├── server.cpp │ └── stop.sh └── shflags ├── glog.BUILD ├── jepsen ├── CHANGELOG.md ├── README.md ├── doc │ └── intro.md ├── project.clj ├── src │ └── jepsen │ │ └── atomic.clj ├── target │ ├── classes │ │ └── META-INF │ │ │ └── maven │ │ │ └── jepsen.atomic │ │ │ └── jepsen.atomic │ │ │ └── pom.properties │ └── stale │ │ └── leiningen.core.classpath.extract-native-dependencies └── test │ └── jepsen │ └── atomic_test.clj ├── leveldb.BUILD ├── openssl.BUILD ├── src ├── CMakeLists.txt └── braft │ ├── ballot.cpp │ ├── ballot.h │ ├── ballot_box.cpp │ ├── ballot_box.h │ ├── builtin_service.proto │ ├── builtin_service_impl.cpp │ ├── builtin_service_impl.h │ ├── cli.cpp │ ├── cli.h │ ├── cli.proto │ ├── cli_service.cpp │ ├── cli_service.h │ ├── closure_helper.h │ ├── closure_queue.cpp │ ├── closure_queue.h │ ├── configuration.cpp │ ├── configuration.h │ ├── configuration_manager.cpp │ ├── configuration_manager.h │ ├── enum.proto │ ├── errno.proto │ ├── file_reader.cpp │ ├── file_reader.h │ ├── file_service.cpp │ ├── file_service.h │ ├── file_service.proto │ ├── file_system_adaptor.cpp │ ├── file_system_adaptor.h │ ├── fsm_caller.cpp │ ├── fsm_caller.h │ ├── fsync.cpp │ ├── fsync.h │ ├── lease.cpp │ ├── lease.h │ ├── local_file_meta.proto │ ├── local_storage.proto │ ├── log.cpp │ ├── log.h │ ├── log_entry.cpp │ ├── log_entry.h │ ├── log_manager.cpp │ ├── log_manager.h │ ├── macros.h │ ├── memory_log.cpp │ ├── memory_log.h │ ├── node.cpp │ ├── node.h │ ├── node_manager.cpp │ ├── node_manager.h │ ├── protobuf_file.cpp │ ├── protobuf_file.h │ ├── raft.cpp │ ├── raft.h │ ├── raft.proto │ ├── raft_meta.cpp │ ├── raft_meta.h │ ├── raft_service.cpp │ ├── raft_service.h │ ├── remote_file_copier.cpp │ ├── remote_file_copier.h │ ├── repeated_timer_task.cpp │ ├── repeated_timer_task.h │ ├── replicator.cpp │ ├── replicator.h │ ├── route_table.cpp │ ├── route_table.h │ ├── snapshot.cpp │ ├── snapshot.h │ ├── snapshot_executor.cpp │ ├── snapshot_executor.h │ ├── snapshot_throttle.cpp │ ├── snapshot_throttle.h │ ├── storage.cpp │ ├── storage.h │ ├── util.cpp │ └── util.h ├── test ├── CMakeLists.txt ├── memory_file_system_adaptor.h ├── run_tests.sh ├── sstream_workaround.h ├── test_ballot.cpp ├── test_ballot_box.cpp ├── test_checksum.cpp ├── test_cli.cpp ├── test_configuration.cpp ├── test_file_service.cpp ├── test_file_system_adaptor.cpp ├── test_fsm_caller.cpp ├── test_fsync.cpp ├── test_leader_lease.cpp ├── test_log.cpp ├── test_log_entry.cpp ├── test_log_manager.cpp ├── test_memory_storage.cpp ├── test_meta.cpp ├── test_node.cpp ├── test_protobuf_file.cpp ├── test_repeated_timer_task.cpp ├── test_snapshot.cpp ├── test_snapshot_executor.cpp ├── test_storage.cpp ├── test_throttle.cpp ├── test_util.cpp └── util.h ├── tools ├── CMakeLists.txt ├── braft_cli.cpp └── patch_from_baidu.sh └── zlib.BUILD /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: [push, pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | build-and-test: 9 | name: >- 10 | CI 11 | ${{ matrix.os }} 12 | ${{ matrix.compiler }} 13 | ${{ matrix.build_tool}} 14 | unittest ${{ matrix.with_test }} 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | compiler: [clang, gcc] 20 | os: [ubuntu-20.04] 21 | build_tool: [cmake, bazel] 22 | with_test: [true, false] 23 | exclude: 24 | # Not support test using bazel 25 | - build_tool: bazel 26 | with_test: true 27 | - build_tool: cmake 28 | with_test: false 29 | include: 30 | - compiler: clang 31 | CC: clang 32 | CXX: clang++ 33 | - compiler: gcc 34 | CC: gcc 35 | CXX: g++ 36 | 37 | env: 38 | CMAKE_BUILD_DIR: ${{ github.workspace }}/bld 39 | CC: ${{ matrix.CC }} 40 | CXX: ${{ matrix.CXX }} 41 | steps: 42 | - uses: actions/checkout@v2 43 | with: 44 | submodules: true 45 | 46 | - name: Install dependencies on Linux 47 | if: ${{ runner.os == 'Linux' }} 48 | run: | 49 | sudo apt-get update 50 | sudo apt-get install -qq libgflags-dev \ 51 | libprotobuf-dev libprotoc-dev protobuf-compiler \ 52 | libleveldb-dev libgoogle-perftools-dev 53 | sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo env "PATH=$PATH" cmake . && sudo make && sudo mv ./lib/libgtest* /usr/lib/ 54 | - name: Install bazel 55 | if: ${{ matrix.build_tool == 'bazel' }} 56 | run: | 57 | sudo apt-get install python-dev 58 | wget https://github.com/bazelbuild/bazel/releases/download/0.25.2/bazel-0.25.2-installer-linux-x86_64.sh 59 | chmod a+x bazel-0.25.2-installer-linux-x86_64.sh 60 | ./bazel-0.25.2-installer-linux-x86_64.sh --user 61 | export PATH="$PATH:$HOME/bin" 62 | - name: Install brpc 63 | working-directory: ${{ github.workspace }} 64 | if: ${{ matrix.build_tool == 'cmake' }} 65 | run: | 66 | mkdir -p thirdparty 67 | git clone https://github.com/brpc/brpc.git thirdparty/brpc && cd thirdparty/brpc && mkdir -p bld && cd bld && cmake .. && make -j4 && sudo make install 68 | - name: Build with cmake 69 | if: ${{ matrix.build_tool == 'cmake' }} 70 | run: | 71 | cmake -S "${{ github.workspace }}" -B "${{ env.CMAKE_BUILD_DIR }}" -DBUILD_UNIT_TESTS=ON 72 | cmake --build "${{ env.CMAKE_BUILD_DIR }}" 73 | - name: Build with bazel 74 | if: ${{ matrix.build_tool == 'bazel' }} 75 | run: | 76 | ~/.bazel/bin/bazel build -c opt --copt -DHAVE_ZLIB=1 //... 77 | - name: Run Tests 78 | if: ${{ matrix.with_test }} 79 | id: test-braft 80 | working-directory: ${{ github.workspace }}/bld/test 81 | run: | 82 | ulimit -c unlimited -S 83 | sh ../../test/run_tests.sh 84 | - name: Collect failure info 85 | if: ${{ steps.test-braft.conclusion == 'failure'}} 86 | run: | 87 | COREFILE=$(find . -maxdepth 1 -name "core*" | head -n 1) 88 | gdb -c "$COREFILE" example -ex "thread apply all bt" -ex "set pagination 0" -batch 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files without extension. 2 | # If you need to git-add a file without extension, add -f 3 | * 4 | !*.* 5 | !*/ 6 | 7 | *.o 8 | *.a 9 | *.log 10 | *.pb.cc 11 | *.pb.h 12 | *.prof 13 | *.so 14 | *.out 15 | /runtime 16 | /output 17 | /test/output 18 | 19 | # Ignore hidden files 20 | .* 21 | *.swp 22 | 23 | # Ignore CMake files 24 | CMakeCache.txt 25 | CMakeFiles 26 | CMakeScripts 27 | Testing 28 | cmake_install.cmake 29 | install_manifest.txt 30 | compile_commands.json 31 | CTestTestfile.cmake 32 | /build 33 | /third-party 34 | 35 | # Github aciton 36 | !.github 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: required 4 | 5 | compiler: 6 | - clang 7 | - gcc 8 | 9 | env: 10 | - PURPOSE=compile 11 | - PURPOSE=unittest 12 | - PURPOSE=compile-with-bazel 13 | 14 | before_install: 15 | - wget --no-clobber https://github.com/bazelbuild/bazel/releases/download/0.25.2/bazel_0.25.2-linux-x86_64.deb 16 | - sudo dpkg -i bazel_0.25.2-linux-x86_64.deb 17 | 18 | install: 19 | - sudo apt-get install -qq realpath libgflags-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev libgoogle-perftools-dev 20 | - sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo env "PATH=$PATH" cmake . && sudo make && sudo mv libgtest* /usr/lib/ && cd - 21 | - sudo apt-get install -y gdb 22 | - if [[ "$PURPOSE" != "compile-with-bazel" ]]; then git clone https://github.com/brpc/brpc.git && mkdir -p brpc/bld && cd brpc/bld && cmake .. && make -j4 && sudo make install && cd - ; fi 23 | 24 | before_script: 25 | - ulimit -c unlimited -S 26 | 27 | script: 28 | - if [[ "$PURPOSE" == "compile-with-bazel" ]]; then bazel build -c opt --copt -DHAVE_ZLIB=1 //... ; fi 29 | - sh build_in_travis_ci.sh 30 | 31 | after_failure: 32 | - COREFILE=$(find . -maxdepth 1 -name "core*" | head -n 1) 33 | - if [[ -f "$COREFILE" ]]; then gdb -c "$COREFILE" example -ex "thread apply all bt" -ex "set pagination 0" -batch; fi 34 | 35 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) 2 | 3 | exports_files(["LICENSE"]) 4 | 5 | load(":bazel/braft.bzl", "braft_proto_library") 6 | 7 | cc_library( 8 | name = "braft", 9 | srcs = glob([ 10 | "src/braft/*.cpp", 11 | ]), 12 | hdrs = glob([ 13 | "src/braft/*.h", 14 | ]), 15 | includes = [ 16 | "src", 17 | ], 18 | defines = [], 19 | copts = [ 20 | "-DGFLAGS=gflags", 21 | "-DOS_LINUX", 22 | "-DSNAPPY", 23 | "-DHAVE_SSE42", 24 | "-DNDEBUG", 25 | "-D__STDC_FORMAT_MACROS", 26 | "-fno-omit-frame-pointer", 27 | "-momit-leaf-frame-pointer", 28 | "-msse4.2", 29 | "-pthread", 30 | "-Wsign-compare", 31 | "-Wno-unused-parameter", 32 | "-Wno-unused-variable", 33 | "-Woverloaded-virtual", 34 | "-Wnon-virtual-dtor", 35 | "-Wno-missing-field-initializers", 36 | "-std=c++11", 37 | "-DGFLAGS_NS=google", 38 | ], 39 | linkopts = [ 40 | "-lm", 41 | "-lpthread", 42 | ], 43 | deps = [ 44 | "@com_github_brpc_brpc//:brpc", 45 | "@com_github_gflags_gflags//:gflags", 46 | "@com_github_google_glog//:glog", 47 | "@com_google_protobuf//:protobuf", 48 | "@zlib//:zlib", 49 | ":cc_braft_internal_proto", 50 | ], 51 | visibility = ["//visibility:public"], 52 | ) 53 | 54 | braft_proto_library( 55 | name = "cc_braft_internal_proto", 56 | srcs = glob([ 57 | "src/braft/*.proto", 58 | ]), 59 | include = "src", 60 | visibility = ["//visibility:public"], 61 | ) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/baidu/braft.svg?branch=master)](https://travis-ci.org/baidu/braft) 2 | 3 | --- 4 | 5 | # Overview 6 | An industrial-grade C++ implementation of [RAFT consensus algorithm](https://raft.github.io/) and [replicated state machine](https://en.wikipedia.org/wiki/State_machine_replication) based on [brpc](https://github.com/brpc/brpc). braft is designed and implemented for scenarios demanding for high workload and low overhead of latency, with the consideration for easy-to-understand concepts so that engineers inside Baidu can build their own distributed systems individually and correctly. 7 | 8 | It's widely used inside Baidu to build highly-available systems, such as: 9 | * Storage systems: Key-Value, Block, Object, File ... 10 | * SQL storages: HA MySQL cluster, distributed transactions, NewSQL systems ... 11 | * Meta services: Various master modules, Lock services ... 12 | 13 | # Getting Started 14 | 15 | * Build [brpc](https://github.com/brpc/brpc/blob/master/docs/cn/getting_started.md) which is the main dependency of braft. 16 | 17 | * Compile braft with cmake 18 | 19 | ```shell 20 | $ mkdir bld && cd bld && cmake .. && make 21 | ``` 22 | 23 | * Play braft with [examples](./example). 24 | 25 | * Installing from vcpkg 26 | 27 | You can download and install `braft` using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: 28 | ```sh 29 | git clone https://github.com/Microsoft/vcpkg.git 30 | cd vcpkg 31 | ./bootstrap-vcpkg.sh 32 | ./vcpkg integrate install 33 | ./vcpkg install braft 34 | ``` 35 | The `braft` port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. 36 | 37 | # Docs 38 | 39 | * Read [overview](./docs/cn/overview.md) to know what you can do with braft. 40 | * Read [benchmark](./docs/cn/benchmark.md) to have a quick view about performance of braft 41 | * [Build Service based on braft](./docs/cn/server.md) 42 | * [Access Service based on braft](./docs/cn/client.md) 43 | * [Cli tools](./docs/cn/cli.md) 44 | * [Replication Model](./docs/cn/replication.md) 45 | * Consensus protocol: 46 | * [RAFT](./docs/cn/raft_protocol.md) 47 | * [Paxos](./docs/cn/paxos_protocol.md) 48 | * [ZAB](./docs/cn/zab_protocol.md) 49 | * [QJM](./docs/cn/qjm.md) 50 | 51 | # Discussion 52 | 53 | * Add Weixin id ***zhengpf__87*** or ***xiongk_2049*** with a verification message '**braft**', then you will be invited into the discussion group. 54 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "com_github_brpc_braft") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | 5 | http_archive( 6 | name = "com_google_googletest", 7 | strip_prefix = "googletest-0fe96607d85cf3a25ac40da369db62bbee2939a5", 8 | url = "https://github.com/google/googletest/archive/0fe96607d85cf3a25ac40da369db62bbee2939a5.tar.gz", 9 | ) 10 | 11 | bind( 12 | name = "gtest", 13 | actual = "@com_google_googletest//:gtest", 14 | ) 15 | 16 | http_archive( 17 | name = "com_google_protobuf", 18 | strip_prefix = "protobuf-3.6.1.3", 19 | sha256 = "9510dd2afc29e7245e9e884336f848c8a6600a14ae726adb6befdb4f786f0be2", 20 | type = "zip", 21 | url = "https://github.com/protocolbuffers/protobuf/archive/v3.6.1.3.zip", 22 | ) 23 | 24 | http_archive( 25 | name = "com_github_gflags_gflags", 26 | strip_prefix = "gflags-46f73f88b18aee341538c0dfc22b1710a6abedef", 27 | url = "https://github.com/gflags/gflags/archive/46f73f88b18aee341538c0dfc22b1710a6abedef.tar.gz", 28 | ) 29 | 30 | bind( 31 | name = "gflags", 32 | actual = "@com_github_gflags_gflags//:gflags", 33 | ) 34 | 35 | http_archive( 36 | name = "com_github_google_glog", 37 | build_file = "//:glog.BUILD", 38 | strip_prefix = "glog-a6a166db069520dbbd653c97c2e5b12e08a8bb26", 39 | url = "https://github.com/google/glog/archive/a6a166db069520dbbd653c97c2e5b12e08a8bb26.tar.gz" 40 | ) 41 | 42 | bind( 43 | name = "glog", 44 | actual = "@com_github_google_glog//:glog", 45 | ) 46 | 47 | http_archive( 48 | name = "com_github_google_leveldb", 49 | build_file = "//:leveldb.BUILD", 50 | strip_prefix = "leveldb-a53934a3ae1244679f812d998a4f16f2c7f309a6", 51 | url = "https://github.com/google/leveldb/archive/a53934a3ae1244679f812d998a4f16f2c7f309a6.tar.gz" 52 | ) 53 | 54 | http_archive( 55 | name = "com_github_brpc_brpc", 56 | sha256 = "58a06997ae07c1654979fb8356884481ab9803b60de04c1b341d986e2b62220d", 57 | strip_prefix = "brpc-1.1.0", 58 | url = "https://github.com/apache/incubator-brpc/archive/refs/tags/1.1.0.tar.gz" 59 | ) 60 | 61 | bind( 62 | name = "brpc", 63 | actual = "@com_github_brpc_brpc//:brpc", 64 | ) 65 | 66 | bind( 67 | name = "butil", 68 | actual = "@com_github_brpc_brpc//:butil", 69 | ) 70 | 71 | new_local_repository( 72 | name = "openssl", 73 | path = "/usr", 74 | build_file = "//:openssl.BUILD", 75 | ) 76 | 77 | bind( 78 | name = "ssl", 79 | actual = "@openssl//:ssl" 80 | ) 81 | 82 | new_local_repository( 83 | name = "zlib", 84 | build_file = "//:zlib.BUILD", 85 | path = "/usr", 86 | ) 87 | -------------------------------------------------------------------------------- /bazel/braft.bzl: -------------------------------------------------------------------------------- 1 | load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library") 2 | 3 | def braft_proto_library(name, srcs, deps=[], include=None, visibility=None, testonly=0): 4 | native.filegroup(name=name + "_proto_srcs", 5 | srcs=srcs, 6 | visibility=visibility,) 7 | cc_proto_library(name=name, 8 | srcs=srcs, 9 | deps=deps, 10 | cc_libs=["@com_google_protobuf//:protobuf"], 11 | include=include, 12 | protoc="@com_google_protobuf//:protoc", 13 | default_runtime="@com_google_protobuf//:protobuf", 14 | testonly=testonly, 15 | visibility=visibility,) -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | Will be opensourced soon 2 | -------------------------------------------------------------------------------- /build_in_travis_ci.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$PURPOSE" ]; then 2 | echo "PURPOSE must be set" 3 | exit 1 4 | fi 5 | if [ -z "$CXX" ]; then 6 | echo "CXX must be set" 7 | exit 1 8 | fi 9 | if [ -z "$CC" ]; then 10 | echo "CC must be set" 11 | exit 1 12 | fi 13 | 14 | runcmd(){ 15 | eval $@ 16 | [[ $? != 0 ]] && { 17 | exit 1 18 | } 19 | return 0 20 | } 21 | 22 | echo "build combination: PURPOSE=$PURPOSE CXX=$CXX CC=$CC" 23 | 24 | rm -rf bld && mkdir bld && cd bld 25 | if [ "$PURPOSE" = "compile" ]; then 26 | if ! cmake ..; then 27 | echo "Fail to generate Makefile by cmake" 28 | exit 1 29 | fi 30 | make -j4 31 | elif [ "$PURPOSE" = "unittest" ]; then 32 | if ! cmake -DBUILD_UNIT_TESTS=ON ..; then 33 | echo "Fail to generate Makefile by cmake" 34 | exit 1 35 | fi 36 | make -j4 && cd test && sh ../../test/run_tests.sh && cd ../ 37 | fi 38 | -------------------------------------------------------------------------------- /cmake/FindGperftools.cmake: -------------------------------------------------------------------------------- 1 | # Tries to find Gperftools. 2 | # 3 | # Usage of this module as follows: 4 | # 5 | # find_package(Gperftools) 6 | # 7 | # Variables used by this module, they can change the default behaviour and need 8 | # to be set before calling find_package: 9 | # 10 | # Gperftools_ROOT_DIR Set this variable to the root installation of 11 | # Gperftools if the module has problems finding 12 | # the proper installation path. 13 | # 14 | # Variables defined by this module: 15 | # 16 | # GPERFTOOLS_FOUND System has Gperftools libs/headers 17 | # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler) 18 | # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers 19 | 20 | find_library(GPERFTOOLS_TCMALLOC 21 | NAMES tcmalloc 22 | HINTS ${Gperftools_ROOT_DIR}/lib) 23 | 24 | find_library(GPERFTOOLS_PROFILER 25 | NAMES profiler 26 | HINTS ${Gperftools_ROOT_DIR}/lib) 27 | 28 | find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER 29 | NAMES tcmalloc_and_profiler 30 | HINTS ${Gperftools_ROOT_DIR}/lib) 31 | 32 | find_path(GPERFTOOLS_INCLUDE_DIR 33 | NAMES gperftools/heap-profiler.h 34 | HINTS ${Gperftools_ROOT_DIR}/include) 35 | 36 | set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER}) 37 | 38 | include(FindPackageHandleStandardArgs) 39 | find_package_handle_standard_args( 40 | Gperftools 41 | DEFAULT_MSG 42 | GPERFTOOLS_LIBRARIES 43 | GPERFTOOLS_INCLUDE_DIR) 44 | 45 | mark_as_advanced( 46 | Gperftools_ROOT_DIR 47 | GPERFTOOLS_TCMALLOC 48 | GPERFTOOLS_PROFILER 49 | GPERFTOOLS_TCMALLOC_AND_PROFILER 50 | GPERFTOOLS_LIBRARIES 51 | GPERFTOOLS_INCLUDE_DIR) 52 | -------------------------------------------------------------------------------- /docs/cn/benchmark.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 前言 4 | 5 | 几乎所有的框架、模块、类库, 都会把高性能作为最重要的标签之一(当然, braft也不例外)。但是常常开发者对于性能理解只是停留在吞吐或者QPS的数字表面,性能测试变成了想方设法刷数字的游戏,而不考虑场景是否符合实际应用场景。常见的『提升性能数字』的方法有以下两类: 6 | 7 | * **Batch:** 系统主动等request数量达到一定数量或者等一个超时时间, 合成一个request发给后端系统, 取决于batch_size / request_size 的值, 能将"QPS"提升几十~几百倍. (这点后面再详细解释)。 8 | * **Client不限制的异步发送**: 这样能让系统一直处于高负载状态,不存在任何的等待,规避了一些系统调用和线程同步开销。 9 | 10 | 这些设计虽然能跑出不错的benchmark数据,但是完全偏离了实际的应用场景。以batch为例, batch的问题在于本质上并没有去指导如何提升系统的QPS。在性能测试中,并发度通常很高,会有源源不断的请求进来,所以每个请求并不需要等待多长时间就能满足batch size的条件, 然而在真实场景中,并发度并没有这么高,这样会导致每个请求都必须要『等待』一个设定的值, latency无法达到最优解。而这时候工程师往往会沉浸在优化超时、batch size等调参工作,从而忽略了分析系统瓶颈这类真正有意义的事情。另外硬件性能的逐渐提升,网络和磁盘本身的延迟越来越短, 这套机制无法兼顾低负载下的延迟和高负载的吞吐。 11 | 12 | 在braft中,我们主要采用了以下几点方法来提高的性能: 13 | 14 | - 数据流是全并发的, leader写本地磁盘和向follower复制数据是完全并发的。 15 | - 尽可能的提高局部性,充分发挥不同层面的cache的作用 16 | - 尽可能隔离不同硬件的访问,通过流水线的形式提高吞吐 17 | - 尽可能的降低锁临界区大小, 关键路径上采用lock-free/wait-free算法. 18 | 19 | # 测试场景 20 | 21 | 不同的用户状态机性能千差万别, 在这个场景中,我们先剥离用户状态机的影响,由client起多个线程通过同步RPC向server写一定大小的log,server调用libraft的接口向复制组提交日志, 当server收到raft日志成功提交的事件之后, 回复client,采集client上的吞吐和延时数据. 对比组为fio以同样的线程数顺序写本地磁盘, 通过这样的测试,我们希望能够看到,使用了raft之后,对比写本地磁盘,应用会牺牲多少的性能数据来换取更高的数据安全性. 22 | 23 | 如果没有特殊说明, 默认client的线程均为100, 写文件默认打开fsync 24 | 25 | ## vs FIO (2016.3) 26 | 27 | fio测试命令(其中-bs=xxx会替换成测试对应的size): 28 | 29 | ``` 30 | ./fio -filename=./10G.data -iodepth 100 -thread -rw=write -ioengine=libaio --direct=1 -sync=1 -bs=512 -size=10G -numjobs=1 -runtime=120 -group_reporting -name=mytest 31 | ``` 32 | 33 | 34 | 35 | **硬件信息** 36 | 37 | CPU: 12核, Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz 38 | 39 | 磁盘: LENOVO SAS 300G, 随机写IOPS~=800, 顺序写吞吐~=200M/s 40 | 41 | 网卡:万兆网卡, 未开启网卡多队列 42 | 43 | 副本数: 3 44 | 45 | ![img](../images/benchmark0.png) 46 | 47 | ## vs Logcabin(2017.2) 48 | 49 | 由于目前业界很少有能单独使用的RAFT实现,我们对Logcabin进行了一些调整, 增加了rpc 服务并且等日志同步完成就立即回复client(patch部分即将开源). 并且统计了client端的QPS, 具体数据如下图所示。 50 | 51 | ![img](../images/benchmark.png) 52 | -------------------------------------------------------------------------------- /docs/cn/cli.md: -------------------------------------------------------------------------------- 1 | braft提供了一系列API用来控制复制主或者具体节点, 可以选择在程序了调用[API](../../src/braft/cli.h)或者使用[braft_cli](../../tools/braft_cli.cpp)来给节点发远程控制命令 2 | 3 | # API 4 | 5 | ```cpp 6 | // Add a new peer into the replicating group which consists of |conf|. 7 | // Returns OK on success, error information otherwise. 8 | butil::Status add_peer(const GroupId& group_id, const Configuration& conf, 9 | const PeerId& peer_id, const CliOptions& options); 10 | // Remove a peer from the replicating group which consists of |conf|. 11 | // Returns OK on success, error information otherwise. 12 | butil::Status remove_peer(const GroupId& group_id, const Configuration& conf, 13 | const PeerId& peer_id, const CliOptions& options); 14 | // Gracefully change the peers of the replication group. 15 | butil::Status change_peers(const GroupId& group_id, const Configuration& conf, 16 | const Configuration& new_peers, 17 | const CliOptions& options); 18 | // Transfer the leader of the replication group to the target peer 19 | butil::Status transfer_leader(const GroupId& group_id, const Configuration& conf, 20 | const PeerId& peer, const CliOptions& options); 21 | // Reset the peer set of the target peer 22 | butil::Status reset_peer(const GroupId& group_id, const PeerId& peer_id, 23 | const Configuration& new_conf, 24 | const CliOptions& options); 25 | // Ask the peer to dump a snapshot immediately. 26 | butil::Status snapshot(const GroupId& group_id, const PeerId& peer_id, 27 | const CliOptions& options); 28 | ``` 29 | 30 | # braft_cli 31 | 32 | braft_cli提供了命令行工具, 作用和API类似 33 | 34 | ```shell 35 | braft_cli: Usage: braft_cli [Command] [OPTIONS...] 36 | Command: 37 | add_peer --group=$group_id --peer=$adding_peer --conf=$current_conf 38 | remove_peer --group=$group_id --peer=$removing_peer --conf=$current_conf 39 | change_peers --group=$group_id --conf=$current_conf --new_peers=$new_peers 40 | reset_peer --group=$group_id --peer==$target_peer --new_peers=$new_peers 41 | snapshot --group=$group_id --peer=$target_peer 42 | transfer_leader --group=$group_id --peer=$target_leader --conf=$current_conf 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/cn/client.md: -------------------------------------------------------------------------------- 1 | braft并不能直接被任何client访问, 本文主要是说明一个能访问braft节点的client需要那些要素。 2 | 3 | # Example 4 | 5 | [client side code](../../example/counter/client.cpp) of Counter 6 | 7 | # 总体流程 8 | 9 | 要访问braft的主节点,需要做这么一些事情: 10 | 11 | * 需要知道这个复制组有哪些节点, 这个可以通过配置列表,记录在dns,或者提供某些naming service如集群的master,redis, zookeeper, etcd等。 12 | * 查询Leader位置 13 | * 感知Leader变化 14 | * 向Leader发起RPC. 15 | 16 | ## RouteTable 17 | 18 | braft提供了[RouteTable](../../src/braft/route_table.h)功能,命名空间在braft::rtb, 可以帮助你的进程记录和追踪某个节点的主节点位置, 包含以下功能 19 | 20 | ```cpp 21 | // Update configuration of group in route table 22 | int update_configuration(const GroupId& group, const Configuration& conf); 23 | int update_configuration(const GroupId& group, const std::string& conf_str); 24 | // Get the cached leader of group. 25 | // Returns: 26 | // 0 : success 27 | // 1 : Not sure about the leader 28 | // -1, otherwise 29 | int select_leader(const GroupId& group, PeerId* leader); 30 | // Update leader 31 | int update_leader(const GroupId& group, const PeerId& leader); 32 | int update_leader(const GroupId& group, const std::string& leader_str); 33 | // Blocking the thread until query_leader finishes 34 | butil::Status refresh_leader(const GroupId& group, int timeout_ms); 35 | // Remove this group from route table 36 | int remove_group(const GroupId& group); 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/cn/overview.md: -------------------------------------------------------------------------------- 1 | # 分布式一致性 2 | 3 | 分布式一致性(distributed consensus) 是分布式系统中最基本的问题, 用来保证一个分布式系统的可靠性以及容灾能力。简单的来讲,就是如何在多个机器间对某一个值达成一致, 并且当达成一致之后,无论之后这些机器间发生怎样的故障,这个值能保持不变。 4 | 5 | 抽象定义上, 一个分布式系统里的所有进程要确定一个值v,如果这个系统满足如下几个性质, 就可以认为它解决了分布式一致性问题, 分别是: 6 | 7 | - Termination: 所有正常的进程都会决定v具体的值,不会出现一直在循环的进程。 8 | - Validity: 任何正常的进程确定的值v', 那么v'肯定是某个进程提交的。比如随机数生成器就不满足这个性质. 9 | - Agreement: 所有正常的进程选择的值都是一样的。 10 | 11 | # 一致性状态机 12 | 13 | 对于一个无限增长的序列a[1, 2, 3…], 如果对于任意整数i, a[i]的值满足分布式一致性,这个系统就满足一致性状态机的要求。 14 | 15 | 基本上所有的系统都会有源源不断的操作, 这时候单独对某个特定的值达成一致是不够的。为了真实系统保证所有的副本的一致性,通常会把操作转化为[write-ahead-log](https://en.wikipedia.org/wiki/Write-ahead_logging)(简称WAL). 然后让系统的所有副本对WAL保持一致, 这样每个进程按照顺序执行WAL里的操作,就能保证最终的状态是一致的。 16 | 17 | ![img](../images/distributed_state_machine.png) 18 | 19 | # RAFT 20 | 21 | RAFT是一种新型易于理解的分布式一致性复制协议,由斯坦福大学的Diego Ongaro和John Ousterhout[提出](http://wiki.baidu.com/download/attachments/142056196/raft.pdf?version=1&modificationDate=1457941130000&api=v2),作为[RAMCloud](https://ramcloud.atlassian.net/wiki/display/RAM/RAMCloud)项目中的中心协调组件。Raft是一种Leader-Based的Multi-Paxos变种,相比Paxos、Zab、View Stamped Replication等协议提供了更完整更清晰的协议描述,并提供了清晰的节点增删描述。 22 | 23 | Raft作为复制状态机,是分布式系统中最核心最基础的组件,提供命令在多个节点之间有序复制和执行,当多个节点初始状态一致的时候,保证节点之间状态一致。系统只要多数节点存活就可以正常处理,它允许消息的延迟、丢弃和乱序,但是不允许消息的篡改(非拜占庭场景)。 24 | 25 | ![img](../images/raft.png) 26 | 27 | Raft可以解决分布式理论中的CP,即一致性和分区容忍性,并不能解决Available的问题。其中包含分布式系统中一些通常的功能: 28 | 29 | - Leader Election 30 | - Log Replication 31 | - Membership Change 32 | - Log Compaction 33 | 34 | # RAFT可以做什么 35 | 36 | 通过RAFT提供的一致性状态机,可以解决复制、修复、节点管理等问题,极大的简化当前分布式系统的设计与实现,让开发者只关注于业务逻辑,将其抽象实现成对应的状态机即可。基于这套框架,可以构建很多分布式应用: 37 | 38 | - 分布式锁服务,比如Zookeeper 39 | - 分布式存储系统,比如分布式消息队列、分布式块系统、分布式文件系统、分布式表格系统等 40 | - 高可靠元信息管理,比如各类Master模块的HA 41 | 42 | # 为什么要做BRAFT 43 | 44 | 当前公司的很多系统,要么存在单点问题,要么存在复制数据安全问题,要么存在复制一致性问题,要么存在复制延迟问题。这些问题使得很多系统开发维护困难,很多时候影响业务的发展。RAFT算法在很大程度上能够解决上面这些问题,高性能的CP复制框架可以解决复制的一致性和延迟问题,对于availability可以在设计中做些consistency的折中,提供多副本读取来实现高可用。 45 | 46 | RAFT协议从2013年出来,社区涌现了非常多的[实现](http://raft.github.io/),但是其中大部分都是实验性质的,缺乏Membership Changes和Log Compaction等功能。少数较为靠谱的实现都是作为具体Service实现的一部分,没有封装成一个通用的基础库形式。其中大部分的RAFT实现都是采用线程网络模型,即一个peer之间的连接使用一个线程维护,对于多线程的调用处理也比较粗糙,这样不适合一个进程中维护大量RAFT复制实例。 47 | 48 | 一个良好的RAFT算法实现能够对上层屏蔽细节, 让开发者从复杂的异常处理中解放出来, 专注于自己的业务逻辑,像写单机程序一样构建分布式系统。虽然RAFT算法本身虽然以易于理解著称,但是要实现正确还是得面临复杂的异常处理,并发事件,必须妥善的解决所有的race conditon以及ABA problem, 于此同时还得保证足够优秀的性能。braft在保证正确性和高性能的同时, 还需要保证接口足够的简单易用. 49 | 50 | # Supported features of BRAFT 51 | 52 | * Leader election. 53 | * Replication and recovery. 54 | * Snapshot and log compaction. 55 | * Membership management. 56 | * Fully concurrent replication. 57 | * Fault tolerance. 58 | * Asymmetric network partition tolerance. 59 | * Workaround when quorate peers are dead. 60 | -------------------------------------------------------------------------------- /docs/cn/replication.md: -------------------------------------------------------------------------------- 1 | 复制模型是分布式系统中一个核心组件,每种复制模型都有自己的优缺点,在设计分布式系统的时候,需要结合业务评估各个业务模型,选择最合适的模型。常见的复制模型包括:链式复制、树形复制、分发复制等。 2 | 3 | # 链式复制 4 | 5 | 链式复制是使用最广泛的复制模型,要将数据复制到全部节点之后,再向client应答成功。链式复制在发展过程中,从基本链式复制发展出了多种改进版本,来改进复制延迟。 6 | 7 | ## 基本链式复制 8 | 9 | 最原始的链式复制中,从Client写入开始,要一个节点写入之后再转发给下一个节点。基本链式复制中,Tail节点是最后一个更新,只有读取这个Tail节点才能实现强一致的复制。 10 | 11 | 整个复制过程的延迟为: 12 | 13 | ``` 14 | rtt1/2 + io1 + rtt2/2 + io2 + rtt3/2 + io3 + rtt4/2 15 | ``` 16 | 17 | 基本链式复制中,任意节点如果出现IO慢将会导致复制卡顿。 18 | 19 | 20 | 21 | ![img](../images/chain_rep.png) 22 | 23 | ## 改进链式复制 24 | 25 | 基本链式复制中复制过程需要每个节点依次完成IO才能完成,其中IO的时间占比较多,所以后来出现了很多改进版来优化复制延迟。hdfs中的复制模型就是典型的一种改进链式复制,即每个节点只需要等到下游节点都写入成功,并且本地写入成功之后就可以向上游Ack,由Head节点向Client回复写入成功,读取Head节点实现强一致性复制。 26 | 27 | 整个复制过程延迟为: 28 | 29 | ``` 30 | rtt1 + Max(io1, rtt2 + Max(io2, rtt3 + io3)) 31 | ``` 32 | 33 | 虽然改进链式复制改进了延迟,但是依然需要全部节点全部完成写入之后才能向Client应答,任何一个节点IO慢都会造成复制卡顿。 34 | 35 | ![img](../images/chain_rep2.png) 36 | 37 | # 树形复制 38 | 39 | 针对链式复制需要全部节点都要完成写入才能向Client应答,后面业界又发展了一些其他的复制方案,其中树形复制是最典型的一个。Client先向Primary写入,Primary再向Secondary进行转发,Primary写入成功并且至少一个Secondary写入成功就向Client进行Ack。这里面Primary写入本地的时机,有几种不同的实现:Primary先写入本地再向Secondary进行复制;Primary写入本地和向Secondary复制同时进行;Primary先向Secondary复制,其中一个应答之后再写入本地。 40 | 41 | 我们这里选择延迟最优的Primary写入本地和向Secondary同时复制的方式,其中复制过程延迟为: 42 | 43 | ``` 44 | rtt1 + Max(io1, Min(rtt2 + io2, rtt3 + io3)) 45 | ``` 46 | 47 | 树形复制相比链式复制,无需全部节点写入成功,只需要包括Primary节点在内的多数节点写入成功即可,因此只有Primary节点IO慢才会导致复制卡顿。 48 | 49 | 实际上树形复制还可以优化,当Primary收到两个Secondary写入成功之后就向Client应答,Primary将不再等待未完成的写入,而是直接将更改应用到业务中,这样Client能通过Primary读取到最新的变更。通过这个优化可以解决Primary节点IO慢的复制卡顿问题。 50 | 51 | ![img](../images/tree_rep.png) 52 | 53 | # 分发复制 54 | 55 | 在上面的复制模型中都或多或少的有慢节点的问题,因此在一些追求复制延迟的场景下,需要一种完全解决慢节点的复制方案,分发复制就是其中的一种。分发复制中节点都是对等的,Client直接向各个节点直接进行分发写入,节点之间并不进行通信复制,只要写入多数节点成功,就判为写入成功。 56 | 57 | 整个复制流程的延迟为: 58 | 59 | ``` 60 | Median(rtt1 + io1, rtt2 + io2, rtt3 + io3) 61 | ``` 62 | 63 | 分发复制很好的解决了慢节点的问题,但是也有很大的局限性,主要面临两个问题: 64 | 65 | - 不能同时有两个Writer,多个Writer同时更新就是一个paxos问题。因此一般都是由外部系统来选出一个唯一的Writer,避免多个写入同时发生,保证数据的一致性。 66 | - 另外一个问题就是如何读到最新的数据。因为节点都是对等的,且没有互相通信。Reader只有读取全部节点,根据节点返回的version或者是index之类的才能判断出哪些节点返回的数据是有效的,效率较低;或者是由Writer进行选择上次返回成功的节点进行读取。 67 | 68 | ![img](../images/all_rep.png) 69 | 70 | # 总结 71 | 72 | 对上面几种复制模型做一下简单对比,可以根据业务模型做出对应的选择。 73 | 74 | - 分发复制只有在单写者的情况下比较有优势,使用范围较为有限。 75 | - 链式复制有较高的吞吐,但延迟较高且无法规避慢节点。 76 | - 树形复制,是吞吐和延迟的一个比较好的折中。 77 | 78 | | | 吞吐 | 延迟 | 慢节点 | 多写 | 实时读取 | 79 | | ---- | ---------- | ---- | --------- | ---- | ----------------------- | 80 | | 链式复制 | 1 * 网卡带宽 | 高 | 全部节点 | 支持 | Head节点或Tail节点 | 81 | | 树形复制 | 1/2 * 网卡带宽 | 中 | Primary节点 | 支持 | Primary节点 | 82 | | 分发复制 | 1/3 * 网卡带宽 | 低 | 无 | 不支持 | 读取全部节点选择多数,或由Writer节点决定 | -------------------------------------------------------------------------------- /docs/cn/witness.md: -------------------------------------------------------------------------------- 1 | witness 副本只作为仲裁者进行投票,不保存实际的业务数据。 2 | ## 实现方案 3 | 对于witness的实现,需要考虑部署方式。 对于2+1部署,如果不允许witness当选选主,那么当主节点异常宕机的时候,如果wintess拥有比另外一个副本更新的entry,那么会导致选主失败,为了提高可用性,需要考虑允许witness短时间内允许当选为主,wintess成为主以后再主动transfer leader给另一个副本。**通过允许witness临时成为主可以提高系统的可用性** 。 4 | 5 | 对于4+1 的部署方式,实现相对简单,只需要让witness不能当选为主即可,因为即便主节点故障,依然至少有一个副本拥有最新的entry从而可以当选为主。由于witness不能当选为主,因此在同步raft log的时候也可以不需要同步log data给witness。当4+1部署的时候,如果不允许witness当选为主,那么最多只能容忍一个节点故障,如果允许witness临时当选为主,那么可以容忍两个节点故障。允许witness当选为主时,实现 6 | 则与2+1部署一致。 7 | 8 | ## 详细实现 9 | ### witness不允许当选为主 10 | 当witness不允许当选为主时,只需要在初始化Node的时候禁止election_timeout timer进行初始化即可,同时可以无需进行data复制。 11 | 12 | ### witness允许临时当选为主 13 | 允许witness当选为主可以提升服务的可用性。具体实现为: 14 | * 设置raft_enable_witness_to_leader flag为true,允许witness临时选举为主 15 | * election_timeout设置为正常节点的两倍,在主节点异常宕机的时候,允许witness发起选主,同时由于election_timeout比数据副本大,可以保证数据副本优先被选为主,只有数据副本选主失败时,witness才会主动发起选主。 16 | * witness当选为主时,禁止安装快照请求,避免从节点获取到空快照覆盖原有的业务数据 17 | * 新增witness副本时, witness向leader发送install sanpshot请求,如果replicator本身是witness,则无需进行data文件的复制,只需复制最新的entry即可。 18 | 19 | ## witness 使用注意事项 20 | * 如果不允许witness当选为主时,相比原有raft方式部署,服务可用性会明显降低 21 | * 当允许witness临时当选为主时,极端情况下,可能导致从节点无法获取到最新的log entry从而导致数据丢失。 22 | 例如: 23 | ``` 24 | 2+1的时候,日志为 [1, 8],某一时刻 replica1(leader) [1, 8] replica2 [1, 5] witness[4,8]。witness snapshot save,truncate 数据到 [7,8]。replica1(leader) 挂了,replica2 [1, 5] 和 witness 的数据接不上了, 此时会导致日志丢失。 25 | ``` 26 | 用户在使用witness的时候,需要评估witness带来的可用性降低以及可能丢失部分最新数据的风险。 27 | 如果业务无法接受数据丢失,可以自定义实现LogStorage, 只有半数以上副本拥有entry时,witness才能truncate 该entry之前的log。 28 | -------------------------------------------------------------------------------- /docs/images/2pc_choice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/2pc_choice.png -------------------------------------------------------------------------------- /docs/images/all_rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/all_rep.png -------------------------------------------------------------------------------- /docs/images/asymmetric_partition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/asymmetric_partition.png -------------------------------------------------------------------------------- /docs/images/benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/benchmark.png -------------------------------------------------------------------------------- /docs/images/benchmark0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/benchmark0.png -------------------------------------------------------------------------------- /docs/images/chain_rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/chain_rep.png -------------------------------------------------------------------------------- /docs/images/chain_rep2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/chain_rep2.png -------------------------------------------------------------------------------- /docs/images/conflict_choices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/conflict_choices.png -------------------------------------------------------------------------------- /docs/images/distributed_state_machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/distributed_state_machine.png -------------------------------------------------------------------------------- /docs/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/flow.png -------------------------------------------------------------------------------- /docs/images/leader_term.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/leader_term.png -------------------------------------------------------------------------------- /docs/images/log_compaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/log_compaction.png -------------------------------------------------------------------------------- /docs/images/log_recovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/log_recovery.png -------------------------------------------------------------------------------- /docs/images/log_replication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/log_replication.png -------------------------------------------------------------------------------- /docs/images/log_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/log_stats.png -------------------------------------------------------------------------------- /docs/images/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/logs.png -------------------------------------------------------------------------------- /docs/images/member_change_procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/member_change_procedure.png -------------------------------------------------------------------------------- /docs/images/member_change_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/member_change_stats.png -------------------------------------------------------------------------------- /docs/images/membership.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/membership.png -------------------------------------------------------------------------------- /docs/images/multi_paxos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/multi_paxos.png -------------------------------------------------------------------------------- /docs/images/pp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/pp1.png -------------------------------------------------------------------------------- /docs/images/pp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/pp2.png -------------------------------------------------------------------------------- /docs/images/pp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/pp3.png -------------------------------------------------------------------------------- /docs/images/proposal_number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/proposal_number.png -------------------------------------------------------------------------------- /docs/images/qjm_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/qjm_overview.png -------------------------------------------------------------------------------- /docs/images/raft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/raft.png -------------------------------------------------------------------------------- /docs/images/raft_stat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/raft_stat.png -------------------------------------------------------------------------------- /docs/images/split_votes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/split_votes.png -------------------------------------------------------------------------------- /docs/images/symmetric_partition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/symmetric_partition.png -------------------------------------------------------------------------------- /docs/images/tree_rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/tree_rep.png -------------------------------------------------------------------------------- /docs/images/zab_broadcast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_broadcast.jpg -------------------------------------------------------------------------------- /docs/images/zab_broadcast_algo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_broadcast_algo.png -------------------------------------------------------------------------------- /docs/images/zab_discovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_discovery.png -------------------------------------------------------------------------------- /docs/images/zab_fast_election.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_fast_election.png -------------------------------------------------------------------------------- /docs/images/zab_recovery_algo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_recovery_algo.png -------------------------------------------------------------------------------- /docs/images/zab_recovery_phase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_recovery_phase.png -------------------------------------------------------------------------------- /docs/images/zab_stats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_stats.jpg -------------------------------------------------------------------------------- /docs/images/zab_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baidu/braft/ab0017f0b98d429138d83a04d3ed351197d671a9/docs/images/zab_sync.png -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Brief introduction of examples: 2 | 3 | * Counter: A integer which can be added to by a given number in each request. 4 | * Atomic: : A integer which supports exchange and compare_exchange operation. 5 | * Block: A single block device supports randomly read/write concurrently. 6 | 7 | # Build steps 8 | 9 | ```shell 10 | example=counter|atomic|block 11 | cd $example && cmake . && make 12 | ``` 13 | 14 | # Run Server 15 | 16 | ```sh 17 | bash run_server.sh 18 | ``` 19 | 20 | * Default number of servers in the group is `3`, changed by `--server_num` 21 | * Servers run on `./runtime/` which is reused, add --clean to cleanup storage 22 | 23 | # Run Client 24 | 25 | ```sh 26 | bash run_client.sh 27 | ``` 28 | 29 | * Default concurrency of client is 1, changed by `--thread_num` 30 | * If server_num of run_server.sh has been changed, specify run_client to make it consistent. 31 | * Add `--log_each_request` if you want detailed information of each request. 32 | 33 | -------------------------------------------------------------------------------- /example/atomic/atomic.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package example; 3 | option cc_generic_services = true; 4 | 5 | message GetRequest { 6 | required int64 id = 1; 7 | }; 8 | 9 | message ExchangeRequest { 10 | required int64 id = 1; 11 | required int64 value = 2; 12 | }; 13 | 14 | message CompareExchangeRequest { 15 | required int64 id = 1; 16 | required int64 expected_value = 2; 17 | required int64 new_value = 3; 18 | }; 19 | 20 | message AtomicResponse { 21 | required bool success = 1; 22 | optional int64 id = 2; 23 | optional int64 new_value = 3; 24 | optional int64 old_value = 4; 25 | optional string redirect = 5; 26 | }; 27 | 28 | service AtomicService { 29 | rpc get(GetRequest) returns (AtomicResponse); 30 | rpc exchange(ExchangeRequest) returns (AtomicResponse); 31 | rpc compare_exchange(CompareExchangeRequest) returns (AtomicResponse); 32 | }; 33 | -------------------------------------------------------------------------------- /example/atomic/jepsen_control.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ulimit -n 10000 4 | ulimit -c unlimited 5 | 6 | nodes="172.17.0.2:8700 172.17.0.3:8700 172.17.0.4:8700 172.17.0.5:8700 172.17.0.6:8700" 7 | #self_ip=`ifconfig | grep "inet addr" | grep -v "127.0.0.1" | awk '{print substr($2,6,16)}'` 8 | self_ip=`ifconfig | grep "mask" | grep -v "127.0.0.1" | awk '{print $2}'` 9 | self_node=${self_ip}":8700" 10 | 11 | peers="" 12 | exclude_peers="" 13 | # gen peers and exclude_peers 14 | for node in $nodes 15 | do 16 | #echo $node 17 | if [ -z $peers ];then 18 | peers=$node 19 | else 20 | peers=$peers","$node 21 | fi 22 | 23 | if [ $node == $self_node ];then 24 | continue 25 | fi 26 | if [ -z $exclude_peers ];then 27 | exclude_peers=$node 28 | else 29 | exclude_peers=$exclude_peers","$node 30 | fi 31 | done 32 | 33 | function help { 34 | echo "Usage: jepsen_control.sh boot|start|stop|restart|join|leave" 35 | exit 1 36 | } 37 | 38 | if [ $# -ne 1 ];then 39 | help 40 | fi 41 | 42 | case $1 in 43 | boot) 44 | echo "boot atomic_server "${self_node} 45 | killall -9 atomic_server 46 | rm -rf log data run.log core.* && mkdir log 47 | #./atomic_server -raft_sync=true -bthread_concurrency=24 -crash_on_fatal_log=true -port=8700 > run.log 2>&1 & 48 | ./atomic_server -raft_sync=true -bthread_concurrency=24 --log_dir=log -port=8700 > run.log 2>&1 & 49 | sleep 1 50 | ./braft_cli reset_peer --group=Atomic --peer=${self_node} --new_peers=${peers} 51 | ;; 52 | start) 53 | echo "start atomic_server "${self_node} 54 | rm -rf log data run.log core.* && mkdir log 55 | #./atomic_server -raft_sync=true -bthread_concurrency=24 -crash_on_fatal_log=true -port=8700 > run.log 2>&1 & 56 | ./atomic_server -raft_sync=true -bthread_concurrency=24 --log_dir=log -port=8700 > run.log 2>&1 & 57 | ;; 58 | stop) 59 | echo "stop atomic_server "${self_node} 60 | killall -9 atomic_server 61 | ;; 62 | restart) 63 | echo "restart atomic_server "${self_node} 64 | killall -9 atomic_server 65 | #./atomic_server -raft_sync=true -bthread_concurrency=24 -crash_on_fatal_log=true -port=8700 > run.log 2>&1 & 66 | ./atomic_server -raft_sync=true -bthread_concurrency=24 --log_dir=log -port=8700 > run.log 2>&1 & 67 | ;; 68 | join) 69 | echo "add atomic_server "${self_node} 70 | ./braft_cli add_peer --group=Atomic --peer=${self_node} --conf=${exclude_peers} 71 | ;; 72 | leave) 73 | echo "remove atomic_server "${self_node} 74 | ./braft_cli remove_peer --group=Atomic --peer=${self_node} --conf=${peers} 75 | ;; 76 | *) 77 | help 78 | ;; 79 | esac 80 | 81 | -------------------------------------------------------------------------------- /example/atomic/run_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | 23 | # define command-line flags 24 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 25 | DEFINE_integer add_percentage 100 'Percentage of fetch_add operation' 26 | DEFINE_integer bthread_concurrency '8' 'Number of worker pthreads' 27 | DEFINE_integer server_port 8300 "Port of the first server" 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_integer thread_num 1 'Number of sending thread' 30 | DEFINE_string log_each_request 'false' 'Print log for each request' 31 | DEFINE_string valgrind 'false' 'Run in valgrind' 32 | DEFINE_string use_bthread "true" "Use bthread to send request" 33 | 34 | FLAGS "$@" || exit 1 35 | 36 | # hostname prefers ipv6 37 | IP=`hostname -i | awk '{print $NF}'` 38 | 39 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 40 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 41 | fi 42 | 43 | raft_peers="" 44 | for ((i=0; i<$FLAGS_server_num; ++i)); do 45 | raft_peers="${raft_peers}${IP}:$((${FLAGS_server_port}+i)):0," 46 | done 47 | 48 | export TCMALLOC_SAMPLE_PARAMETER=524288 49 | 50 | ${VALGRIND} ./atomic_client \ 51 | --add_percentage=${FLAGS_add_percentage} \ 52 | --bthread_concurrency=${FLAGS_bthread_concurrency} \ 53 | --conf="${raft_peers}" \ 54 | --log_each_request=${FLAGS_log_each_request} \ 55 | --thread_num=${FLAGS_thread_num} \ 56 | --use_bthread=${FLAGS_use_bthread} \ 57 | 58 | -------------------------------------------------------------------------------- /example/atomic/run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | # define command-line flags 23 | DEFINE_string crash_on_fatal 'true' 'Crash on fatal log' 24 | DEFINE_integer bthread_concurrency '18' 'Number of worker pthreads' 25 | DEFINE_string sync 'true' 'fsync each time' 26 | DEFINE_string valgrind 'false' 'Run in valgrind' 27 | DEFINE_integer max_segment_size '8388608' 'Max segment size' 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 30 | DEFINE_integer port 8300 "Port of the first server" 31 | 32 | # parse the command-line 33 | FLAGS "$@" || exit 1 34 | eval set -- "${FLAGS_ARGV}" 35 | 36 | # The alias for printing to stderr 37 | alias error=">&2 echo atomic: " 38 | 39 | # hostname prefers ipv6 40 | IP=`hostname -i | awk '{print $NF}'` 41 | 42 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 43 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 44 | fi 45 | 46 | raft_peers="" 47 | for ((i=0; i<$FLAGS_server_num; ++i)); do 48 | raft_peers="${raft_peers}${IP}:$((${FLAGS_port}+i)):0," 49 | done 50 | 51 | if [ "$FLAGS_clean" == "0" ]; then 52 | rm -rf runtime 53 | fi 54 | 55 | export TCMALLOC_SAMPLE_PARAMETER=524288 56 | 57 | for ((i=0; i<$FLAGS_server_num; ++i)); do 58 | mkdir -p runtime/$i 59 | cp ./atomic_server runtime/$i 60 | cd runtime/$i 61 | ${VALGRIND} ./atomic_server \ 62 | -bthread_concurrency=${FLAGS_bthread_concurrency}\ 63 | -crash_on_fatal_log=${FLAGS_crash_on_fatal} \ 64 | -raft_max_segment_size=${FLAGS_max_segment_size} \ 65 | -raft_sync=${FLAGS_sync} \ 66 | -port=$((${FLAGS_port}+i)) -conf="${raft_peers}" > std.log 2>&1 & 67 | cd ../.. 68 | done 69 | -------------------------------------------------------------------------------- /example/atomic/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | 19 | killall -9 atomic_server 20 | -------------------------------------------------------------------------------- /example/block/block.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package example; 3 | option cc_generic_services = true; 4 | 5 | message BlockRequest { 6 | required int64 offset = 1; 7 | optional int32 size = 2; 8 | } 9 | 10 | message BlockResponse { 11 | required bool success = 1; 12 | optional string redirect = 2; 13 | } 14 | 15 | service BlockService { 16 | rpc write(BlockRequest) returns(BlockResponse); 17 | rpc read(BlockRequest) returns(BlockResponse); 18 | }; 19 | -------------------------------------------------------------------------------- /example/block/run_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | 23 | # define command-line flags 24 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 25 | DEFINE_integer write_percentage 100 'Percentage of write operation' 26 | DEFINE_integer bthread_concurrency '8' 'Number of worker pthreads' 27 | DEFINE_integer server_port 8200 "Port of the first server" 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_integer thread_num 1 'Number of sending thread' 30 | DEFINE_string crash_on_fatal 'true' 'Crash on fatal log' 31 | DEFINE_string log_each_request 'false' 'Print log for each request' 32 | DEFINE_string valgrind 'false' 'Run in valgrind' 33 | DEFINE_string use_bthread "true" "Use bthread to send request" 34 | 35 | FLAGS "$@" || exit 1 36 | 37 | # hostname prefers ipv6 38 | IP=`hostname -i | awk '{print $NF}'` 39 | 40 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 41 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 42 | fi 43 | 44 | raft_peers="" 45 | for ((i=0; i<$FLAGS_server_num; ++i)); do 46 | raft_peers="${raft_peers}${IP}:$((${FLAGS_server_port}+i)):0," 47 | done 48 | 49 | export TCMALLOC_SAMPLE_PARAMETER=524288 50 | 51 | ${VALGRIND} ./block_client \ 52 | --write_percentage=${FLAGS_write_percentage} \ 53 | --bthread_concurrency=${FLAGS_bthread_concurrency} \ 54 | --conf="${raft_peers}" \ 55 | --crash_on_fatal_log=${FLAGS_crash_on_fatal} \ 56 | --log_each_request=${FLAGS_log_each_request} \ 57 | --thread_num=${FLAGS_thread_num} \ 58 | --use_bthread=${FLAGS_use_bthread} \ 59 | 60 | -------------------------------------------------------------------------------- /example/block/run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | # define command-line flags 23 | DEFINE_string crash_on_fatal 'true' 'Crash on fatal log' 24 | DEFINE_integer bthread_concurrency '18' 'Number of worker pthreads' 25 | DEFINE_string sync 'true' 'fsync each time' 26 | DEFINE_string valgrind 'false' 'Run in valgrind' 27 | DEFINE_integer max_segment_size '8388608' 'Max segment size' 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 30 | DEFINE_integer port 8200 "Port of the first server" 31 | 32 | # parse the command-line 33 | FLAGS "$@" || exit 1 34 | eval set -- "${FLAGS_ARGV}" 35 | 36 | # The alias for printing to stderr 37 | alias error=">&2 echo block: " 38 | 39 | # hostname prefers ipv6 40 | IP=`hostname -i | awk '{print $NF}'` 41 | 42 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 43 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 44 | fi 45 | 46 | raft_peers="" 47 | for ((i=0; i<$FLAGS_server_num; ++i)); do 48 | raft_peers="${raft_peers}${IP}:$((${FLAGS_port}+i)):0," 49 | done 50 | 51 | if [ "$FLAGS_clean" == "0" ]; then 52 | rm -rf runtime 53 | fi 54 | 55 | export TCMALLOC_SAMPLE_PARAMETER=524288 56 | 57 | for ((i=0; i<$FLAGS_server_num; ++i)); do 58 | mkdir -p runtime/$i 59 | cp ./block_server runtime/$i 60 | cd runtime/$i 61 | ${VALGRIND} ./block_server \ 62 | -bthread_concurrency=${FLAGS_bthread_concurrency}\ 63 | -crash_on_fatal_log=${FLAGS_crash_on_fatal} \ 64 | -raft_max_segment_size=${FLAGS_max_segment_size} \ 65 | -raft_sync=${FLAGS_sync} \ 66 | -port=$((${FLAGS_port}+i)) -conf="${raft_peers}" > std.log 2>&1 & 67 | cd ../.. 68 | done 69 | -------------------------------------------------------------------------------- /example/block/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | killall -9 block_server 4 | -------------------------------------------------------------------------------- /example/counter/counter.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package example; 3 | option cc_generic_services = true; 4 | 5 | message Snapshot { 6 | required int64 value = 1; 7 | }; 8 | 9 | message FetchAddRequest { 10 | required int64 value = 1; 11 | }; 12 | 13 | message CounterResponse { 14 | required bool success = 1; 15 | optional int64 value = 2; 16 | optional string redirect = 3; 17 | }; 18 | 19 | message GetRequest { 20 | }; 21 | 22 | service CounterService { 23 | rpc fetch_add(FetchAddRequest) returns (CounterResponse); 24 | rpc get(GetRequest) returns (CounterResponse); 25 | }; 26 | -------------------------------------------------------------------------------- /example/counter/run_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | 23 | # define command-line flags 24 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 25 | DEFINE_integer add_percentage 100 'Percentage of fetch_add operation' 26 | DEFINE_integer bthread_concurrency '8' 'Number of worker pthreads' 27 | DEFINE_integer server_port 8100 "Port of the first server" 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_integer thread_num 1 'Number of sending thread' 30 | DEFINE_string crash_on_fatal 'true' 'Crash on fatal log' 31 | DEFINE_string log_each_request 'false' 'Print log for each request' 32 | DEFINE_string valgrind 'false' 'Run in valgrind' 33 | DEFINE_string use_bthread "true" "Use bthread to send request" 34 | 35 | FLAGS "$@" || exit 1 36 | 37 | # hostname prefers ipv6 38 | IP=`hostname -i | awk '{print $NF}'` 39 | 40 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 41 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 42 | fi 43 | 44 | raft_peers="" 45 | for ((i=0; i<$FLAGS_server_num; ++i)); do 46 | raft_peers="${raft_peers}${IP}:$((${FLAGS_server_port}+i)):0," 47 | done 48 | 49 | export TCMALLOC_SAMPLE_PARAMETER=524288 50 | 51 | ${VALGRIND} ./counter_client \ 52 | --add_percentage=${FLAGS_add_percentage} \ 53 | --bthread_concurrency=${FLAGS_bthread_concurrency} \ 54 | --conf="${raft_peers}" \ 55 | --crash_on_fatal_log=${FLAGS_crash_on_fatal} \ 56 | --log_each_request=${FLAGS_log_each_request} \ 57 | --thread_num=${FLAGS_thread_num} \ 58 | --use_bthread=${FLAGS_use_bthread} \ 59 | 60 | -------------------------------------------------------------------------------- /example/counter/run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # source shflags from current directory 18 | mydir="${BASH_SOURCE%/*}" 19 | if [[ ! -d "$mydir" ]]; then mydir="$PWD"; fi 20 | . $mydir/../shflags 21 | 22 | # define command-line flags 23 | DEFINE_string crash_on_fatal 'true' 'Crash on fatal log' 24 | DEFINE_integer bthread_concurrency '18' 'Number of worker pthreads' 25 | DEFINE_string sync 'true' 'fsync each time' 26 | DEFINE_string valgrind 'false' 'Run in valgrind' 27 | DEFINE_integer max_segment_size '8388608' 'Max segment size' 28 | DEFINE_integer server_num '3' 'Number of servers' 29 | DEFINE_boolean clean 1 'Remove old "runtime" dir before running' 30 | DEFINE_integer port 8100 "Port of the first server" 31 | 32 | # parse the command-line 33 | FLAGS "$@" || exit 1 34 | eval set -- "${FLAGS_ARGV}" 35 | 36 | # The alias for printing to stderr 37 | alias error=">&2 echo counter: " 38 | 39 | # hostname prefers ipv6 40 | IP=`hostname -i | awk '{print $NF}'` 41 | 42 | if [ "$FLAGS_valgrind" == "true" ] && [ $(which valgrind) ] ; then 43 | VALGRIND="valgrind --tool=memcheck --leak-check=full" 44 | fi 45 | 46 | raft_peers="" 47 | for ((i=0; i<$FLAGS_server_num; ++i)); do 48 | raft_peers="${raft_peers}${IP}:$((${FLAGS_port}+i)):0," 49 | done 50 | 51 | if [ "$FLAGS_clean" == "0" ]; then 52 | rm -rf runtime 53 | fi 54 | 55 | export TCMALLOC_SAMPLE_PARAMETER=524288 56 | 57 | for ((i=0; i<$FLAGS_server_num; ++i)); do 58 | mkdir -p runtime/$i 59 | cp ./counter_server runtime/$i 60 | cd runtime/$i 61 | ${VALGRIND} ./counter_server \ 62 | -bthread_concurrency=${FLAGS_bthread_concurrency}\ 63 | -crash_on_fatal_log=${FLAGS_crash_on_fatal} \ 64 | -raft_max_segment_size=${FLAGS_max_segment_size} \ 65 | -raft_sync=${FLAGS_sync} \ 66 | -port=$((${FLAGS_port}+i)) -conf="${raft_peers}" > std.log 2>&1 & 67 | cd ../.. 68 | done 69 | -------------------------------------------------------------------------------- /example/counter/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #=============================================================================== 3 | # 4 | # FILE: stop.sh 5 | # 6 | # USAGE: ./stop.sh 7 | # 8 | # DESCRIPTION: 9 | # 10 | # OPTIONS: --- 11 | # REQUIREMENTS: --- 12 | # BUGS: --- 13 | # NOTES: --- 14 | # AUTHOR: WangYao (), wangyao02@baidu.com 15 | # COMPANY: Baidu.com, Inc 16 | # VERSION: 1.0 17 | # CREATED: 2015年10月30日 17时50分43秒 CST 18 | # REVISION: --- 19 | #=============================================================================== 20 | 21 | killall -9 counter_server 22 | -------------------------------------------------------------------------------- /glog.BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) 2 | 3 | cc_library( 4 | name = "glog", 5 | srcs = [ 6 | "src/base/commandlineflags.h", 7 | "src/base/googleinit.h", 8 | "src/demangle.cc", 9 | "src/logging.cc", 10 | "src/raw_logging.cc", 11 | "src/symbolize.cc", 12 | "src/utilities.cc", 13 | "src/vlog_is_on.cc", 14 | ], 15 | hdrs = [ 16 | "raw_logging_h", 17 | "src/base/mutex.h", 18 | "src/demangle.h", 19 | "src/symbolize.h", 20 | "src/utilities.h", 21 | "src/glog/log_severity.h", 22 | ":config_h", 23 | ":logging_h", 24 | ":stl_logging_h", 25 | ":vlog_is_on_h", 26 | ], 27 | copts = [ 28 | # Disable warnings that exists in glog 29 | "-Wno-sign-compare", 30 | "-Wno-unused-local-typedefs", 31 | # Inject google namespace as "google" 32 | "-D_START_GOOGLE_NAMESPACE_='namespace google {'", 33 | "-D_END_GOOGLE_NAMESPACE_='}'", 34 | "-DGOOGLE_NAMESPACE='google'", 35 | # Allows src/base/mutex.h to include pthread.h. 36 | "-DHAVE_PTHREAD", 37 | # Allows src/logging.cc to determine the host name. 38 | "-DHAVE_SYS_UTSNAME_H", 39 | # System header files enabler for src/utilities.cc 40 | # Enable system calls from syscall.h 41 | "-DHAVE_SYS_SYSCALL_H", 42 | # Enable system calls from sys/time.h 43 | "-DHAVE_SYS_TIME_H", 44 | "-DHAVE_STDINT_H", 45 | "-DHAVE_STRING_H", 46 | # For logging.cc 47 | "-DHAVE_PREAD", 48 | "-DHAVE_FCNTL", 49 | "-DHAVE_SYS_TYPES_H", 50 | # Allows syslog support 51 | "-DHAVE_SYSLOG_H", 52 | # GFlags 53 | "-isystem $(GENDIR)/external/com_github_gflags_gflags/", 54 | "-DHAVE_LIB_GFLAGS", 55 | # Necessary for creating soft links of log files 56 | "-DHAVE_UNISTD_H", 57 | ], 58 | includes = [ 59 | ".", 60 | "src", 61 | ], 62 | visibility = ["//visibility:public"], 63 | deps = [ 64 | "//external:gflags", 65 | ], 66 | ) 67 | 68 | # Below are the generation rules that generates the necessary header 69 | # files for glog. Originally they are generated by CMAKE 70 | # configure_file() command, which replaces certain template 71 | # placeholders in the .in files with provided values. 72 | 73 | # gen_sh is a bash script that provides the values for generated 74 | # header files. Under the hood it is just a wrapper over sed. 75 | genrule( 76 | name = "gen_sh", 77 | outs = [ 78 | "gen.sh", 79 | ], 80 | cmd = """ 81 | cat > $@ <<"EOF" 82 | #! /bin/sh 83 | sed -e 's/@ac_cv_have_unistd_h@/1/g' \ 84 | -e 's/@ac_cv_have_stdint_h@/1/g' \ 85 | -e 's/@ac_cv_have_systypes_h@/1/g' \ 86 | -e 's/@ac_cv_have_libgflags_h@/1/g' \ 87 | -e 's/@ac_cv_have_uint16_t@/1/g' \ 88 | -e 's/@ac_cv_have___builtin_expect@/1/g' \ 89 | -e 's/@ac_cv_have_.*@/0/g' \ 90 | -e 's/@ac_google_start_namespace@/namespace google {/g' \ 91 | -e 's/@ac_google_end_namespace@/}/g' \ 92 | -e 's/@ac_google_namespace@/google/g' \ 93 | -e 's/@ac_cv___attribute___noinline@/__attribute__((noinline))/g' \ 94 | -e 's/@ac_cv___attribute___noreturn@/__attribute__((noreturn))/g' \ 95 | -e 's/@ac_cv___attribute___printf_4_5@/__attribute__((__format__ (__printf__, 4, 5)))/g' 96 | EOF""", 97 | ) 98 | 99 | genrule( 100 | name = "config_h", 101 | srcs = [ 102 | "src/config.h.cmake.in", 103 | ], 104 | outs = [ 105 | "config.h", 106 | ], 107 | cmd = "awk '{ gsub(/^#cmakedefine/, \"//cmakedefine\"); print; }' $(<) > $(@)", 108 | ) 109 | 110 | genrule( 111 | name = "logging_h", 112 | srcs = [ 113 | "src/glog/logging.h.in", 114 | ], 115 | outs = [ 116 | "glog/logging.h", 117 | ], 118 | cmd = "$(location :gen_sh) < $(<) > $(@)", 119 | tools = [":gen_sh"], 120 | ) 121 | 122 | genrule( 123 | name = "raw_logging_h", 124 | srcs = [ 125 | "src/glog/raw_logging.h.in", 126 | ], 127 | outs = [ 128 | "glog/raw_logging.h", 129 | ], 130 | cmd = "$(location :gen_sh) < $(<) > $(@)", 131 | tools = [":gen_sh"], 132 | ) 133 | 134 | genrule( 135 | name = "stl_logging_h", 136 | srcs = [ 137 | "src/glog/stl_logging.h.in", 138 | ], 139 | outs = [ 140 | "glog/stl_logging.h", 141 | ], 142 | cmd = "$(location :gen_sh) < $(<) > $(@)", 143 | tools = [":gen_sh"], 144 | ) 145 | 146 | genrule( 147 | name = "vlog_is_on_h", 148 | srcs = [ 149 | "src/glog/vlog_is_on.h.in", 150 | ], 151 | outs = [ 152 | "glog/vlog_is_on.h", 153 | ], 154 | cmd = "$(location :gen_sh) < $(<) > $(@)", 155 | tools = [":gen_sh"], 156 | ) 157 | -------------------------------------------------------------------------------- /jepsen/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2016-04-04 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2016-04-04 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/jepsen.atomic/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/jepsen.atomic/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /jepsen/README.md: -------------------------------------------------------------------------------- 1 | # jepsen.atomic 2 | 3 | A Clojure library designed to ... well, that part is up to you. 4 | 5 | ## Usage 6 | 7 | 1. install apache-maven sun-java8 lein-2.6.1 8 | 2. prepare 6 hosts, 1 control host, 5 test hosts 9 | test hosts named n1, n2, n3, n4, n5 10 | add n1 n2 n3 n4 n5 to /etc/hosts 11 | 3. make control host can ssh to test hosts 12 | make password root:root 13 | ~/.ssh/known_hosts setting 14 | /etc/ssh/sshd_config allow: PermitRootLogin yes 15 | /etc/sudoers disable: #Defaults requiretty 16 | 6. lein deps && lein test 17 | 18 | [NOTE] iptables command invalid in jepsen/src/net.clj, remove -w. then lein install 19 | most code copy from elasticsearch and logcabin 20 | 21 | ## License 22 | 23 | Copyright © 2016 FIXME 24 | 25 | Distributed under the Eclipse Public License either version 1.0 or (at 26 | your option) any later version. 27 | -------------------------------------------------------------------------------- /jepsen/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to jepsen.atomic 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /jepsen/project.clj: -------------------------------------------------------------------------------- 1 | (defproject jepsen.atomic "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"] 7 | [gnuplot "0.1.1"] 8 | [clj-ssh "0.5.14"] 9 | [jepsen "0.1.0"] 10 | [avout "0.5.4"] 11 | [cheshire "5.4.0"]]) 12 | -------------------------------------------------------------------------------- /jepsen/target/classes/META-INF/maven/jepsen.atomic/jepsen.atomic/pom.properties: -------------------------------------------------------------------------------- 1 | #Leiningen 2 | #Sun Feb 11 15:29:31 UTC 2018 3 | version=0.1.0-SNAPSHOT 4 | groupId=jepsen.atomic 5 | artifactId=jepsen.atomic 6 | -------------------------------------------------------------------------------- /jepsen/test/jepsen/atomic_test.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.atomic-test 2 | (:use jepsen.atomic 3 | jepsen.core 4 | jepsen.tests 5 | clojure.test 6 | clojure.pprint) 7 | (:require [clojure.string :as str] 8 | [jepsen.util :as util] 9 | [jepsen.os.debian :as debian] 10 | [jepsen.checker :as checker] 11 | [jepsen.checker.timeline :as timeline] 12 | [jepsen.model :as model] 13 | [jepsen.generator :as gen] 14 | [jepsen.nemesis :as nemesis] 15 | [jepsen.store :as store] 16 | [jepsen.report :as report] 17 | [jepsen.core :as jepsen])) 18 | 19 | ;(deftest partition-test 20 | ; (let [test (run! 21 | ; (assoc 22 | ; noop-test 23 | ; :name "atomic" 24 | ; :db (db) 25 | ; :client (cas-client) 26 | ; :model (model/cas-register) 27 | ; :checker (checker/compose {:html timeline/html 28 | ; :linear checker/linearizable}) 29 | ; :nemesis (nemesis/partition-random-halves) 30 | ; :generator (gen/phases 31 | ; (->> gen/cas 32 | ; (gen/delay 1/2) 33 | ; (gen/nemesis 34 | ; (gen/seq 35 | ; (cycle [(gen/sleep 10) 36 | ; {:type :info :f :start} 37 | ; (gen/sleep 10) 38 | ; {:type :info :f :stop}]))) 39 | ; (gen/time-limit 120)) 40 | ; (gen/nemesis 41 | ; (gen/once {:type :info :f :stop})) 42 | ; ; (gen/sleep 10) 43 | ; (gen/clients 44 | ; (gen/once {:type :invoke :f :read}))) 45 | ; :ssh {:username "root" 46 | ; :password "bcetest" 47 | ; :strict-host-key-checking "false"}))] 48 | ; (is (:valid? (:results test))) 49 | ; (report/linearizability (:linear (:results test))))) 50 | 51 | (defn run-set-test! 52 | "Runs a test around set creation and dumps some results to the report/ dir" 53 | [t] 54 | (let [test (jepsen/run! t)] 55 | (is (:valid? (:results test))) 56 | (report/linearizability (:linear (:results test))))) 57 | 58 | (deftest create-crash 59 | (run-set-test! (create-crash-test))) 60 | 61 | (deftest create-configuration 62 | (run-set-test! (create-configuration-test))) 63 | 64 | (deftest create-pause 65 | (run-set-test! (create-pause-test))) 66 | 67 | (deftest create-partition 68 | (run-set-test! (create-partition-test))) 69 | 70 | (deftest create-bridge 71 | (run-set-test! (create-bridge-test))) 72 | 73 | -------------------------------------------------------------------------------- /leveldb.BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | 4 | config_setting( 5 | name = "darwin", 6 | values = {"cpu": "darwin"}, 7 | visibility = ["//visibility:public"], 8 | ) 9 | 10 | SOURCES = ["db/builder.cc", 11 | "db/c.cc", 12 | "db/dbformat.cc", 13 | "db/db_impl.cc", 14 | "db/db_iter.cc", 15 | "db/dumpfile.cc", 16 | "db/filename.cc", 17 | "db/log_reader.cc", 18 | "db/log_writer.cc", 19 | "db/memtable.cc", 20 | "db/repair.cc", 21 | "db/table_cache.cc", 22 | "db/version_edit.cc", 23 | "db/version_set.cc", 24 | "db/write_batch.cc", 25 | "table/block_builder.cc", 26 | "table/block.cc", 27 | "table/filter_block.cc", 28 | "table/format.cc", 29 | "table/iterator.cc", 30 | "table/merger.cc", 31 | "table/table_builder.cc", 32 | "table/table.cc", 33 | "table/two_level_iterator.cc", 34 | "util/arena.cc", 35 | "util/bloom.cc", 36 | "util/cache.cc", 37 | "util/coding.cc", 38 | "util/comparator.cc", 39 | "util/crc32c.cc", 40 | "util/env.cc", 41 | "util/env_posix.cc", 42 | "util/filter_policy.cc", 43 | "util/hash.cc", 44 | "util/histogram.cc", 45 | "util/logging.cc", 46 | "util/options.cc", 47 | "util/status.cc", 48 | "port/port_posix.cc", 49 | "port/port_posix_sse.cc", 50 | "helpers/memenv/memenv.cc", 51 | ] 52 | 53 | cc_library( 54 | name = "leveldb", 55 | srcs = SOURCES, 56 | hdrs = glob([ 57 | "helpers/memenv/*.h", 58 | "util/*.h", 59 | "port/*.h", 60 | "port/win/*.h", 61 | "table/*.h", 62 | "db/*.h", 63 | "include/leveldb/*.h" 64 | ], 65 | exclude = [ 66 | "**/*test.*", 67 | ]), 68 | includes = [ 69 | "include/", 70 | ], 71 | copts = [ 72 | "-fno-builtin-memcmp", 73 | "-DLEVELDB_PLATFORM_POSIX=1", 74 | "-DLEVELDB_ATOMIC_PRESENT", 75 | ], 76 | defines = [ 77 | "LEVELDB_PLATFORM_POSIX", 78 | ] + select({ 79 | ":darwin": ["OS_MACOSX"], 80 | "//conditions:default": [], 81 | }), 82 | ) -------------------------------------------------------------------------------- /openssl.BUILD: -------------------------------------------------------------------------------- 1 | package( 2 | default_visibility=["//visibility:public"] 3 | ) 4 | 5 | config_setting( 6 | name = "macos", 7 | values = { 8 | "cpu": "darwin", 9 | }, 10 | visibility = ["//visibility:private"], 11 | ) 12 | 13 | cc_library( 14 | name = "crypto", 15 | srcs = select({ 16 | ":macos": ["lib/libcrypto.dylib"], 17 | "//conditions:default": [] 18 | }), 19 | linkopts = select({ 20 | ":macos" : [], 21 | "//conditions:default": ["-lcrypto"], 22 | }), 23 | ) 24 | 25 | cc_library( 26 | name = "ssl", 27 | hdrs = select({ 28 | ":macos": glob(["include/openssl/*.h"]), 29 | "//conditions:default": [] 30 | }), 31 | srcs = select ({ 32 | ":macos": ["lib/libssl.dylib"], 33 | "//conditions:default": [] 34 | }), 35 | includes = ["include"], 36 | linkopts = select({ 37 | ":macos" : [], 38 | "//conditions:default": ["-lssl"], 39 | }), 40 | deps = [":crypto"] 41 | ) 42 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(BUILD_UNIT_TESTS) 2 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNIT_TEST") 3 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUNIT_TEST") 4 | elseif(NOT DEBUG) 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG") 7 | endif() 8 | 9 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 10 | include_directories(${CMAKE_SOURCE_DIR}/src) 11 | 12 | add_library(OBJ_LIB OBJECT ${SOURCES}) 13 | 14 | set_property(TARGET ${OBJ_LIB} PROPERTY POSITION_INDEPENDENT_CODE 1) 15 | add_library(braft-shared SHARED $) 16 | add_library(braft-static STATIC $) 17 | target_link_libraries(braft-shared ${DYNAMIC_LIB}) 18 | target_link_libraries(braft-static ${DYNAMIC_LIB}) 19 | 20 | SET_TARGET_PROPERTIES(braft-static PROPERTIES OUTPUT_NAME braft CLEAN_DIRECT_OUTPUT 1) 21 | SET_TARGET_PROPERTIES(braft-shared PROPERTIES OUTPUT_NAME braft CLEAN_DIRECT_OUTPUT 1) 22 | 23 | install(TARGETS braft-static 24 | RUNTIME DESTINATION bin 25 | LIBRARY DESTINATION lib${LIBSUFFIX} 26 | ARCHIVE DESTINATION lib${LIBSUFFIX} 27 | ) 28 | 29 | install(TARGETS braft-shared 30 | RUNTIME DESTINATION bin 31 | LIBRARY DESTINATION lib${LIBSUFFIX} 32 | ARCHIVE DESTINATION lib${LIBSUFFIX} 33 | ) 34 | -------------------------------------------------------------------------------- /src/braft/ballot.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include "braft/ballot.h" 18 | 19 | namespace braft { 20 | 21 | Ballot::Ballot() : _quorum(0), _old_quorum(0) {} 22 | Ballot::~Ballot() {} 23 | 24 | int Ballot::init(const Configuration& conf, const Configuration* old_conf) { 25 | _peers.clear(); 26 | _old_peers.clear(); 27 | _quorum = 0; 28 | _old_quorum = 0; 29 | 30 | _peers.reserve(conf.size()); 31 | for (Configuration::const_iterator 32 | iter = conf.begin(); iter != conf.end(); ++iter) { 33 | _peers.push_back(*iter); 34 | } 35 | _quorum = _peers.size() / 2 + 1; 36 | if (!old_conf) { 37 | return 0; 38 | } 39 | _old_peers.reserve(old_conf->size()); 40 | for (Configuration::const_iterator 41 | iter = old_conf->begin(); iter != old_conf->end(); ++iter) { 42 | _old_peers.push_back(*iter); 43 | } 44 | _old_quorum = _old_peers.size() / 2 + 1; 45 | return 0; 46 | } 47 | 48 | Ballot::PosHint Ballot::grant(const PeerId& peer, PosHint hint) { 49 | std::vector::iterator iter; 50 | iter = find_peer(peer, _peers, hint.pos0); 51 | if (iter != _peers.end()) { 52 | if (!iter->found) { 53 | iter->found = true; 54 | --_quorum; 55 | } 56 | hint.pos0 = iter - _peers.begin(); 57 | } else { 58 | hint.pos0 = -1; 59 | } 60 | 61 | if (_old_peers.empty()) { 62 | hint.pos1 = -1; 63 | return hint; 64 | } 65 | 66 | iter = find_peer(peer, _old_peers, hint.pos1); 67 | 68 | if (iter != _old_peers.end()) { 69 | if (!iter->found) { 70 | iter->found = true; 71 | --_old_quorum; 72 | } 73 | hint.pos1 = iter - _old_peers.begin(); 74 | } else { 75 | hint.pos1 = -1; 76 | } 77 | 78 | return hint; 79 | } 80 | 81 | void Ballot::grant(const PeerId& peer) { 82 | grant(peer, PosHint()); 83 | } 84 | 85 | } // namespace braft 86 | -------------------------------------------------------------------------------- /src/braft/ballot.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_BALLOT_H 18 | #define BRAFT_BALLOT_H 19 | 20 | #include "braft/configuration.h" 21 | 22 | namespace braft { 23 | 24 | class Ballot { 25 | public: 26 | struct PosHint { 27 | PosHint() : pos0(-1), pos1(-1) {} 28 | int pos0; 29 | int pos1; 30 | }; 31 | 32 | Ballot(); 33 | ~Ballot(); 34 | void swap(Ballot& rhs) { 35 | _peers.swap(rhs._peers); 36 | std::swap(_quorum, rhs._quorum); 37 | _old_peers.swap(rhs._old_peers); 38 | std::swap(_old_quorum, rhs._old_quorum); 39 | } 40 | 41 | int init(const Configuration& conf, const Configuration* old_conf); 42 | PosHint grant(const PeerId& peer, PosHint hint); 43 | void grant(const PeerId& peer); 44 | bool granted() const { return _quorum <= 0 && _old_quorum <= 0; } 45 | private: 46 | struct UnfoundPeerId { 47 | UnfoundPeerId(const PeerId& peer_id) : peer_id(peer_id), found(false) {} 48 | PeerId peer_id; 49 | bool found; 50 | bool operator==(const PeerId& id) const { 51 | return peer_id == id; 52 | } 53 | }; 54 | std::vector::iterator find_peer( 55 | const PeerId& peer, std::vector& peers, int pos_hint) { 56 | if (pos_hint < 0 || pos_hint >= (int)peers.size() 57 | || peers[pos_hint].peer_id != peer) { 58 | for (std::vector::iterator 59 | iter = peers.begin(); iter != peers.end(); ++iter) { 60 | if (*iter == peer) { 61 | return iter; 62 | } 63 | } 64 | return peers.end(); 65 | } 66 | return peers.begin() + pos_hint; 67 | } 68 | std::vector _peers; 69 | int _quorum; 70 | std::vector _old_peers; 71 | int _old_quorum; 72 | }; 73 | 74 | }; 75 | 76 | #endif //BRAFT_BALLOT_H 77 | -------------------------------------------------------------------------------- /src/braft/ballot_box.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_BALLOT_BOX_H 18 | #define BRAFT_BALLOT_BOX_H 19 | 20 | #include // int64_t 21 | #include // std::set 22 | #include 23 | #include // butil::atomic 24 | #include "braft/raft.h" 25 | #include "braft/util.h" 26 | #include "braft/ballot.h" 27 | 28 | namespace braft { 29 | 30 | class FSMCaller; 31 | class ClosureQueue; 32 | 33 | struct BallotBoxOptions { 34 | BallotBoxOptions() 35 | : waiter(NULL) 36 | , closure_queue(NULL) 37 | {} 38 | FSMCaller* waiter; 39 | ClosureQueue* closure_queue; 40 | }; 41 | 42 | struct BallotBoxStatus { 43 | BallotBoxStatus() 44 | : committed_index(0), pending_index(0), pending_queue_size(0) 45 | {} 46 | int64_t committed_index; 47 | int64_t pending_index; 48 | int64_t pending_queue_size; 49 | }; 50 | 51 | class BallotBox { 52 | public: 53 | BallotBox(); 54 | ~BallotBox(); 55 | 56 | int init(const BallotBoxOptions& options); 57 | 58 | // Called by leader, otherwise the behavior is undefined 59 | // Set logs in [first_log_index, last_log_index] are stable at |peer|. 60 | int commit_at(int64_t first_log_index, int64_t last_log_index, 61 | const PeerId& peer); 62 | 63 | // Called when the leader steps down, otherwise the behavior is undefined 64 | // When a leader steps down, the uncommitted user applications should 65 | // fail immediately, which the new leader will deal whether to commit or 66 | // truncate. 67 | int clear_pending_tasks(); 68 | 69 | // Called when a candidate becomes the new leader, otherwise the behavior is 70 | // undefined. 71 | // According to the raft algorithm, the logs from pervious terms can't be 72 | // committed until a log at the new term becomes committed, so 73 | // |new_pending_index| should be |last_log_index| + 1. 74 | int reset_pending_index(int64_t new_pending_index); 75 | 76 | // Called by leader, otherwise the behavior is undefined 77 | // Store application context before replication. 78 | int append_pending_task(const Configuration& conf, 79 | const Configuration* old_conf, 80 | Closure* closure); 81 | 82 | // Called by follower, otherwise the behavior is undefined. 83 | // Set committed index received from leader 84 | int set_last_committed_index(int64_t last_committed_index); 85 | 86 | int64_t last_committed_index() 87 | { return _last_committed_index.load(butil::memory_order_acquire); } 88 | 89 | void describe(std::ostream& os, bool use_html); 90 | 91 | void get_status(BallotBoxStatus* ballot_box_status); 92 | 93 | private: 94 | 95 | FSMCaller* _waiter; 96 | ClosureQueue* _closure_queue; 97 | raft_mutex_t _mutex; 98 | butil::atomic _last_committed_index; 99 | int64_t _pending_index; 100 | std::deque _pending_meta_queue; 101 | 102 | }; 103 | 104 | } // namespace braft 105 | 106 | #endif //BRAFT_BALLOT_BOX_H 107 | -------------------------------------------------------------------------------- /src/braft/builtin_service.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | option cc_generic_services = true; 4 | 5 | message IndexRequest {}; 6 | message IndexResponse {}; 7 | 8 | service raft_stat { 9 | rpc default_method(IndexRequest) returns (IndexResponse); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/braft/builtin_service_impl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Ge,Jun(gejun@baiud.com) 17 | 18 | #include "braft/builtin_service_impl.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "braft/node.h" 25 | #include "braft/replicator.h" 26 | #include "braft/node_manager.h" 27 | 28 | namespace braft { 29 | 30 | void RaftStatImpl::GetTabInfo(brpc::TabInfoList* info_list) const { 31 | brpc::TabInfo* info = info_list->add(); 32 | info->tab_name = "raft"; 33 | info->path = "/raft_stat"; 34 | } 35 | 36 | void RaftStatImpl::default_method(::google::protobuf::RpcController* controller, 37 | const ::braft::IndexRequest* /*request*/, 38 | ::braft::IndexResponse* /*response*/, 39 | ::google::protobuf::Closure* done) { 40 | brpc::ClosureGuard done_guard(done); 41 | brpc::Controller* cntl = (brpc::Controller*)controller; 42 | std::string group_id = cntl->http_request().unresolved_path(); 43 | std::vector > nodes; 44 | if (group_id.empty()) { 45 | global_node_manager->get_all_nodes(&nodes); 46 | } else { 47 | global_node_manager->get_nodes_by_group_id(group_id, &nodes); 48 | } 49 | const bool html = brpc::UseHTML(cntl->http_request()); 50 | if (html) { 51 | cntl->http_response().set_content_type("text/html"); 52 | } else { 53 | cntl->http_response().set_content_type("text/plain"); 54 | } 55 | butil::IOBufBuilder os; 56 | if (html) { 57 | os << "\n" 58 | << "\n" 59 | << brpc::TabsHead() << ""; 60 | cntl->server()->PrintTabsBody(os, "raft"); 61 | } 62 | if (nodes.empty()) { 63 | if (html) { 64 | os << ""; 65 | } 66 | os.move_to(cntl->response_attachment()); 67 | return; 68 | } 69 | 70 | std::string prev_group_id; 71 | const char *newline = html ? "
" : "\r\n"; 72 | for (size_t i = 0; i < nodes.size(); ++i) { 73 | const NodeId node_id = nodes[i]->node_id(); 74 | group_id = node_id.group_id; 75 | if (group_id != prev_group_id) { 76 | if (html) { 77 | os << "

" << group_id << "

"; 78 | } else { 79 | os << "[" << group_id << "]" << newline; 80 | } 81 | prev_group_id = group_id; 82 | } 83 | nodes[i]->describe(os, html); 84 | os << newline; 85 | } 86 | if (html) { 87 | os << ""; 88 | } 89 | os.move_to(cntl->response_attachment()); 90 | } 91 | 92 | } // namespace braft 93 | -------------------------------------------------------------------------------- /src/braft/builtin_service_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Ge,Jun(gejun@baiud.com) 17 | 18 | #ifndef BRAFT_BUILTIN_SERVICE_IMPL_H 19 | #define BRAFT_BUILTIN_SERVICE_IMPL_H 20 | 21 | #include "braft/builtin_service.pb.h" 22 | #include 23 | 24 | namespace braft { 25 | 26 | class RaftStatImpl : public raft_stat, public brpc::Tabbed { 27 | public: 28 | void default_method(::google::protobuf::RpcController* controller, 29 | const ::braft::IndexRequest* request, 30 | ::braft::IndexResponse* response, 31 | ::google::protobuf::Closure* done); 32 | 33 | void GetTabInfo(brpc::TabInfoList*) const; 34 | }; 35 | 36 | } // namespace braft 37 | 38 | #endif //BRAFT_BUILTIN_SERVICE_IMPL_H 39 | -------------------------------------------------------------------------------- /src/braft/cli.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_CLI_H 18 | #define BRAFT_CLI_H 19 | 20 | #include "braft/raft.h" 21 | 22 | namespace braft { 23 | namespace cli { 24 | 25 | struct CliOptions { 26 | int timeout_ms; 27 | int max_retry; 28 | CliOptions() : timeout_ms(-1), max_retry(3) {} 29 | }; 30 | 31 | // Add a new peer into the replicating group which consists of |conf|. 32 | // Returns OK on success, error information otherwise. 33 | butil::Status add_peer(const GroupId& group_id, const Configuration& conf, 34 | const PeerId& peer_id, const CliOptions& options); 35 | 36 | // Remove a peer from the replicating group which consists of |conf|. 37 | // Returns OK on success, error information otherwise. 38 | butil::Status remove_peer(const GroupId& group_id, const Configuration& conf, 39 | const PeerId& peer_id, const CliOptions& options); 40 | 41 | // Gracefully change the peers of the replication group. 42 | butil::Status change_peers(const GroupId& group_id, const Configuration& conf, 43 | const Configuration& new_peers, 44 | const CliOptions& options); 45 | 46 | // Transfer the leader of the replication group to the target peer 47 | butil::Status transfer_leader(const GroupId& group_id, const Configuration& conf, 48 | const PeerId& peer, const CliOptions& options); 49 | 50 | // Reset the peer set of the target peer 51 | butil::Status reset_peer(const GroupId& group_id, const PeerId& peer_id, 52 | const Configuration& new_conf, 53 | const CliOptions& options); 54 | 55 | // Ask the peer to dump a snapshot immediately. 56 | butil::Status snapshot(const GroupId& group_id, const PeerId& peer_id, 57 | const CliOptions& options); 58 | 59 | } // namespace cli 60 | } // namespace braft 61 | 62 | #endif //BRAFT_CLI_H 63 | -------------------------------------------------------------------------------- /src/braft/cli.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | 4 | option cc_generic_services = true; 5 | 6 | message AddPeerRequest { 7 | required string group_id = 1; 8 | required string leader_id = 2; 9 | required string peer_id = 3; 10 | } 11 | 12 | message AddPeerResponse { 13 | repeated string old_peers = 1; 14 | repeated string new_peers = 2; 15 | } 16 | 17 | message RemovePeerRequest { 18 | required string group_id = 1; 19 | required string leader_id = 2; 20 | required string peer_id = 3; 21 | } 22 | 23 | message RemovePeerResponse { 24 | repeated string old_peers = 1; 25 | repeated string new_peers = 2; 26 | } 27 | 28 | message ChangePeersRequest { 29 | required string group_id = 1; 30 | required string leader_id = 2; 31 | repeated string new_peers = 3; 32 | } 33 | 34 | message ChangePeersResponse { 35 | repeated string old_peers = 1; 36 | repeated string new_peers = 2; 37 | } 38 | 39 | message SnapshotRequest { 40 | required string group_id = 1; 41 | optional string peer_id = 2; 42 | }; 43 | 44 | message ResetPeerRequest { 45 | required string group_id = 1; 46 | required string peer_id = 2; 47 | repeated string old_peers = 3; 48 | repeated string new_peers = 4; 49 | } 50 | 51 | message TransferLeaderRequest { 52 | required string group_id = 1; 53 | required string leader_id = 2; 54 | optional string peer_id = 3; 55 | } 56 | 57 | message TransferLeaderResponse {} 58 | 59 | message ResetPeerResponse { 60 | } 61 | 62 | message SnapshotResponse { 63 | } 64 | 65 | message GetLeaderRequest { 66 | required string group_id = 1; 67 | optional string peer_id = 2; 68 | } 69 | 70 | message GetLeaderResponse { 71 | required string leader_id = 1; 72 | } 73 | 74 | // service 75 | service CliService { 76 | rpc add_peer(AddPeerRequest) returns (AddPeerResponse); 77 | rpc remove_peer(RemovePeerRequest) returns (RemovePeerResponse); 78 | rpc change_peers(ChangePeersRequest) returns (ChangePeersResponse); 79 | rpc reset_peer(ResetPeerRequest) returns (ResetPeerResponse); 80 | rpc snapshot(SnapshotRequest) returns (SnapshotResponse); 81 | rpc get_leader(GetLeaderRequest) returns (GetLeaderResponse); 82 | rpc transfer_leader(TransferLeaderRequest) returns (TransferLeaderResponse); 83 | }; 84 | -------------------------------------------------------------------------------- /src/braft/cli_service.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_CLI_SERVICE_H 18 | #define BRAFT_CLI_SERVICE_H 19 | 20 | #include 21 | #include "braft/cli.pb.h" // CliService 22 | #include "braft/node.h" // NodeImpl 23 | 24 | namespace braft { 25 | 26 | class CliServiceImpl : public CliService { 27 | public: 28 | void add_peer(::google::protobuf::RpcController* controller, 29 | const ::braft::AddPeerRequest* request, 30 | ::braft::AddPeerResponse* response, 31 | ::google::protobuf::Closure* done); 32 | void remove_peer(::google::protobuf::RpcController* controller, 33 | const ::braft::RemovePeerRequest* request, 34 | ::braft::RemovePeerResponse* response, 35 | ::google::protobuf::Closure* done); 36 | void reset_peer(::google::protobuf::RpcController* controller, 37 | const ::braft::ResetPeerRequest* request, 38 | ::braft::ResetPeerResponse* response, 39 | ::google::protobuf::Closure* done); 40 | void snapshot(::google::protobuf::RpcController* controller, 41 | const ::braft::SnapshotRequest* request, 42 | ::braft::SnapshotResponse* response, 43 | ::google::protobuf::Closure* done); 44 | void get_leader(::google::protobuf::RpcController* controller, 45 | const ::braft::GetLeaderRequest* request, 46 | ::braft::GetLeaderResponse* response, 47 | ::google::protobuf::Closure* done); 48 | void change_peers(::google::protobuf::RpcController* controller, 49 | const ::braft::ChangePeersRequest* request, 50 | ::braft::ChangePeersResponse* response, 51 | ::google::protobuf::Closure* done); 52 | void transfer_leader(::google::protobuf::RpcController* controller, 53 | const ::braft::TransferLeaderRequest* request, 54 | ::braft::TransferLeaderResponse* response, 55 | ::google::protobuf::Closure* done); 56 | private: 57 | butil::Status get_node(scoped_refptr* node, 58 | const GroupId& group_id, 59 | const std::string& peer_id); 60 | }; 61 | 62 | } 63 | 64 | #endif //BRAFT_CLI_SERVICE_H 65 | -------------------------------------------------------------------------------- /src/braft/closure_queue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include 18 | #include "braft/closure_queue.h" 19 | #include "braft/raft.h" 20 | 21 | namespace braft { 22 | 23 | ClosureQueue::ClosureQueue(bool usercode_in_pthread) 24 | : _first_index(0) 25 | , _usercode_in_pthread(usercode_in_pthread) 26 | {} 27 | 28 | ClosureQueue::~ClosureQueue() { 29 | clear(); 30 | } 31 | 32 | void ClosureQueue::clear() { 33 | std::deque saved_queue; 34 | { 35 | BAIDU_SCOPED_LOCK(_mutex); 36 | saved_queue.swap(_queue); 37 | _first_index = 0; 38 | } 39 | bool run_bthread = false; 40 | for (std::deque::iterator 41 | it = saved_queue.begin(); it != saved_queue.end(); ++it) { 42 | if (*it) { 43 | (*it)->status().set_error(EPERM, "leader stepped down"); 44 | run_closure_in_bthread_nosig(*it, _usercode_in_pthread); 45 | run_bthread = true; 46 | } 47 | } 48 | if (run_bthread) { 49 | bthread_flush(); 50 | } 51 | } 52 | 53 | void ClosureQueue::reset_first_index(int64_t first_index) { 54 | BAIDU_SCOPED_LOCK(_mutex); 55 | CHECK(_queue.empty()); 56 | _first_index = first_index; 57 | } 58 | 59 | void ClosureQueue::append_pending_closure(Closure* c) { 60 | BAIDU_SCOPED_LOCK(_mutex); 61 | _queue.push_back(c); 62 | } 63 | 64 | int ClosureQueue::pop_closure_until(int64_t index, 65 | std::vector *out, int64_t *out_first_index) { 66 | out->clear(); 67 | BAIDU_SCOPED_LOCK(_mutex); 68 | if (_queue.empty() || index < _first_index) { 69 | *out_first_index = index + 1; 70 | return 0; 71 | } 72 | if (index > _first_index + (int64_t)_queue.size() - 1) { 73 | CHECK(false) << "Invalid index=" << index 74 | << " _first_index=" << _first_index 75 | << " _closure_queue_size=" << _queue.size(); 76 | return -1; 77 | } 78 | *out_first_index = _first_index; 79 | for (int64_t i = _first_index; i <= index; ++i) { 80 | out->push_back(_queue.front()); 81 | _queue.pop_front(); 82 | } 83 | _first_index = index + 1; 84 | return 0; 85 | } 86 | 87 | } // namespace braft 88 | -------------------------------------------------------------------------------- /src/braft/closure_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_CLOSURE_QUEUE_H 18 | #define BRAFT_CLOSURE_QUEUE_H 19 | 20 | #include "braft/util.h" 21 | 22 | namespace braft { 23 | 24 | // Holding the closure waiting for the commitment of logs 25 | class ClosureQueue { 26 | public: 27 | explicit ClosureQueue(bool usercode_in_pthread); 28 | ~ClosureQueue(); 29 | 30 | // Clear all the pending closure and run done 31 | void clear(); 32 | 33 | // Called when a candidate becomes the new leader, otherwise the behavior is 34 | // undefined. 35 | // Reset the first index of the coming pending closures to |first_index| 36 | void reset_first_index(int64_t first_index); 37 | 38 | // Called by leader, otherwise the behavior is undefined 39 | // Append the closure 40 | void append_pending_closure(Closure* c); 41 | 42 | // Pop all the closure until |index| (included) into out in the same order 43 | // of their indexes, |out_first_index| would be assigned the index of out[0] if 44 | // out is not empty, index + 1 otherwise. 45 | int pop_closure_until(int64_t index, 46 | std::vector *out, int64_t *out_first_index); 47 | private: 48 | // TODO: a spsc lock-free queue would help 49 | raft_mutex_t _mutex; 50 | int64_t _first_index; 51 | std::deque _queue; 52 | bool _usercode_in_pthread; 53 | 54 | }; 55 | 56 | } // namespace braft 57 | 58 | #endif //BRAFT_CLOSURE_QUEUE_H 59 | -------------------------------------------------------------------------------- /src/braft/configuration.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Wang,Yao(wangyao02@baidu.com) 16 | // Zhangyi Chen(chenzhangyi01@baidu.com) 17 | 18 | #include "braft/configuration.h" 19 | #include 20 | #include 21 | 22 | namespace braft { 23 | 24 | std::ostream& operator<<(std::ostream& os, const Configuration& a) { 25 | std::vector peers; 26 | a.list_peers(&peers); 27 | for (size_t i = 0; i < peers.size(); i++) { 28 | os << peers[i]; 29 | if (i < peers.size() - 1) { 30 | os << ","; 31 | } 32 | } 33 | return os; 34 | } 35 | 36 | int Configuration::parse_from(butil::StringPiece conf) { 37 | reset(); 38 | std::string peer_str; 39 | for (butil::StringSplitter sp(conf.begin(), conf.end(), ','); sp; ++sp) { 40 | braft::PeerId peer; 41 | peer_str.assign(sp.field(), sp.length()); 42 | if (peer.parse(peer_str) != 0) { 43 | LOG(ERROR) << "Fail to parse " << peer_str; 44 | return -1; 45 | } 46 | add_peer(peer); 47 | } 48 | return 0; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/braft/configuration_manager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include "braft/configuration_manager.h" 18 | 19 | namespace braft { 20 | 21 | int ConfigurationManager::add(const ConfigurationEntry& entry) { 22 | if (!_configurations.empty()) { 23 | if (_configurations.back().id.index >= entry.id.index) { 24 | CHECK(false) << "Did you forget to call truncate_suffix before " 25 | " the last log index goes back"; 26 | return -1; 27 | } 28 | } 29 | _configurations.push_back(entry); 30 | return 0; 31 | } 32 | 33 | void ConfigurationManager::truncate_prefix(const int64_t first_index_kept) { 34 | while (!_configurations.empty() 35 | && _configurations.front().id.index < first_index_kept) { 36 | _configurations.pop_front(); 37 | } 38 | } 39 | 40 | void ConfigurationManager::truncate_suffix(const int64_t last_index_kept) { 41 | while (!_configurations.empty() 42 | && _configurations.back().id.index > last_index_kept) { 43 | _configurations.pop_back(); 44 | } 45 | } 46 | 47 | void ConfigurationManager::set_snapshot(const ConfigurationEntry& entry) { 48 | CHECK_GE(entry.id, _snapshot.id); 49 | _snapshot = entry; 50 | } 51 | 52 | void ConfigurationManager::get(int64_t last_included_index, 53 | ConfigurationEntry* conf) { 54 | if (_configurations.empty()) { 55 | CHECK_GE(last_included_index, _snapshot.id.index); 56 | *conf = _snapshot; 57 | return; 58 | } 59 | std::deque::iterator it; 60 | for (it = _configurations.begin(); it != _configurations.end(); ++it) { 61 | if (it->id.index > last_included_index) { 62 | break; 63 | } 64 | } 65 | if (it == _configurations.begin()) { 66 | *conf = _snapshot; 67 | return; 68 | } 69 | --it; 70 | *conf = *it; 71 | } 72 | 73 | const ConfigurationEntry& ConfigurationManager::last_configuration() const { 74 | if (!_configurations.empty()) { 75 | return _configurations.back(); 76 | } 77 | return _snapshot; 78 | } 79 | 80 | } // namespace braft 81 | -------------------------------------------------------------------------------- /src/braft/configuration_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_CONFIGURATION_MANAGER_H 18 | #define BRAFT_CONFIGURATION_MANAGER_H 19 | 20 | #include "braft/configuration.h" // Configuration 21 | #include "braft/log_entry.h" // LogId 22 | 23 | namespace braft { 24 | 25 | struct ConfigurationEntry { 26 | LogId id; 27 | Configuration conf; 28 | Configuration old_conf; 29 | 30 | ConfigurationEntry() {} 31 | ConfigurationEntry(const LogEntry& entry) { 32 | id = entry.id; 33 | conf = *(entry.peers); 34 | if (entry.old_peers) { 35 | old_conf = *(entry.old_peers); 36 | } 37 | } 38 | 39 | bool stable() const { return old_conf.empty(); } 40 | bool empty() const { return conf.empty(); } 41 | void list_peers(std::set* peers) { 42 | peers->clear(); 43 | conf.append_peers(peers); 44 | old_conf.append_peers(peers); 45 | } 46 | bool contains(const PeerId& peer) const 47 | { return conf.contains(peer) || old_conf.contains(peer); } 48 | }; 49 | 50 | // Manager the history of configuration changing 51 | class ConfigurationManager { 52 | public: 53 | ConfigurationManager() {} 54 | ~ConfigurationManager() {} 55 | 56 | // add new configuration at index 57 | int add(const ConfigurationEntry& entry); 58 | 59 | // [1, first_index_kept) are being discarded 60 | void truncate_prefix(int64_t first_index_kept); 61 | 62 | // (last_index_kept, infinity) are being discarded 63 | void truncate_suffix(int64_t last_index_kept); 64 | 65 | void set_snapshot(const ConfigurationEntry& snapshot); 66 | 67 | void get(int64_t last_included_index, ConfigurationEntry* entry); 68 | 69 | const ConfigurationEntry& last_configuration() const; 70 | 71 | private: 72 | 73 | std::deque _configurations; 74 | ConfigurationEntry _snapshot; 75 | }; 76 | 77 | } // namespace braft 78 | 79 | #endif //BRAFT_CONFIGURATION_MANAGER_H 80 | -------------------------------------------------------------------------------- /src/braft/enum.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | 4 | enum EntryType { 5 | ENTRY_TYPE_UNKNOWN = 0; 6 | ENTRY_TYPE_NO_OP = 1; 7 | ENTRY_TYPE_DATA = 2; 8 | ENTRY_TYPE_CONFIGURATION= 3; 9 | }; 10 | 11 | enum ErrorType { 12 | ERROR_TYPE_NONE = 0; 13 | ERROR_TYPE_LOG = 1; 14 | ERROR_TYPE_STABLE = 2; 15 | ERROR_TYPE_SNAPSHOT = 3; 16 | ERROR_TYPE_STATE_MACHINE = 4; 17 | }; 18 | -------------------------------------------------------------------------------- /src/braft/errno.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | 4 | enum RaftError { 5 | // Start from 5000, to avoid HTTP error and RPC error (0 ~100, 1000 ~ 6 | // 3000); 7 | 8 | // All Kinds of Timeout(Including Election_timeout, Timeout_now, Stepdown_timeout) 9 | ERAFTTIMEDOUT = 10001; 10 | 11 | ESTATEMACHINE = 10002; // Bad User State Machine 12 | ECATCHUP = 10003; // Catchup Failed 13 | 14 | // Trigger step_down(Not All) 15 | ELEADERREMOVED = 10004; // Configuration_change_done When 16 | //Leader Is Not In The New Configuration 17 | ESETPEER = 10005; // Set_peer 18 | ENODESHUTDOWN = 10006; // Shut_down 19 | EHIGHERTERMREQUEST = 10007; // Receive Higher Term Requests 20 | EHIGHERTERMRESPONSE = 10008; // Receive Higher Term Response 21 | EBADNODE = 10009; // Node Is In Error 22 | EVOTEFORCANDIDATE = 10010; // Node Votes For Some Candidate 23 | ENEWLEADER = 10011; // Follower(without leader) or Candidate Receives 24 | // Append_entries/Install_snapshot Request from a new leader 25 | ELEADERCONFLICT = 10012; // More Than One Leader In One Term 26 | 27 | // Trigger on_leader_stop 28 | ETRANSFERLEADERSHIP = 10013; // Leader Transfer Leadership To A Follower 29 | // The log at the given index is deleted 30 | ELOGDELETED = 10014; 31 | // No available user log to read 32 | ENOMOREUSERLOG = 10015; 33 | // Raft node in readonly mode 34 | EREADONLY = 10016; 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /src/braft/file_reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Zheng,Pengfei(zhengpengfei@baidu.com) 17 | // Xiong,Kai(xiongkai@baidu.com) 18 | // Yang,Guodong(yangguodong01@baidu.com) 19 | 20 | #include "braft/file_reader.h" 21 | #include "braft/util.h" 22 | 23 | namespace braft { 24 | 25 | LocalDirReader::~LocalDirReader() { 26 | if (_current_file) { 27 | _current_file->close(); 28 | delete _current_file; 29 | _current_file = NULL; 30 | } 31 | _fs->close_snapshot(_path); 32 | } 33 | 34 | bool LocalDirReader::open() { 35 | return _fs->open_snapshot(_path); 36 | } 37 | 38 | int LocalDirReader::read_file(butil::IOBuf* out, 39 | const std::string &filename, 40 | off_t offset, 41 | size_t max_count, 42 | bool read_partly, 43 | size_t* read_count, 44 | bool* is_eof) const { 45 | return read_file_with_meta(out, filename, NULL, offset, 46 | max_count, read_count, is_eof); 47 | } 48 | 49 | int LocalDirReader::read_file_with_meta(butil::IOBuf* out, 50 | const std::string &filename, 51 | google::protobuf::Message* file_meta, 52 | off_t offset, 53 | size_t max_count, 54 | size_t* read_count, 55 | bool* is_eof) const { 56 | std::unique_lock lck(_mutex); 57 | if (_is_reading) { 58 | // Just let follower retry, if there already a reading request in process. 59 | lck.unlock(); 60 | BRAFT_VLOG << "A courrent read file is in process, path: " << _path; 61 | return EAGAIN; 62 | } 63 | int ret = EINVAL; 64 | if (filename != _current_filename) { 65 | if (!_eof_reached || offset != 0) { 66 | lck.unlock(); 67 | BRAFT_VLOG << "Out of order read request, path: " << _path 68 | << " filename: " << filename << " offset: " << offset 69 | << " max_count: " << max_count; 70 | return EINVAL; 71 | } 72 | if (_current_file) { 73 | _current_file->close(); 74 | delete _current_file; 75 | _current_file = NULL; 76 | _current_filename.clear(); 77 | } 78 | std::string file_path(_path + "/" + filename); 79 | butil::File::Error e; 80 | FileAdaptor* file = _fs->open(file_path, O_RDONLY | O_CLOEXEC, file_meta, &e); 81 | if (!file) { 82 | return file_error_to_os_error(e); 83 | } 84 | _current_filename = filename; 85 | _current_file = file; 86 | _eof_reached = false; 87 | } 88 | _is_reading = true; 89 | lck.unlock(); 90 | 91 | do { 92 | butil::IOPortal buf; 93 | ssize_t nread = _current_file->read(&buf, offset, max_count); 94 | if (nread < 0) { 95 | ret = EIO; 96 | break; 97 | } 98 | ret = 0; 99 | *read_count = nread; 100 | *is_eof = false; 101 | if ((size_t)nread < max_count) { 102 | *is_eof = true; 103 | } else { 104 | ssize_t size = _current_file->size(); 105 | if (size < 0) { 106 | return EIO; 107 | } 108 | if (size == ssize_t(offset + max_count)) { 109 | *is_eof = true; 110 | } 111 | } 112 | out->swap(buf); 113 | } while (false); 114 | 115 | lck.lock(); 116 | _is_reading = false; 117 | if (!ret) { 118 | _eof_reached = *is_eof; 119 | } 120 | return ret; 121 | } 122 | 123 | } // namespace braft 124 | -------------------------------------------------------------------------------- /src/braft/file_reader.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Zheng,Pengfei(zhengpengfei@baidu.com) 17 | // Xiong,Kai(xiongkai@baidu.com) 18 | // Yang,Guodong(yangguodong01@baidu.com) 19 | 20 | #ifndef BRAFT_FILE_READER_H 21 | #define BRAFT_FILE_READER_H 22 | 23 | #include // std::set 24 | #include // butil::RefCountedThreadsafe 25 | #include // butil::IOBuf 26 | #include "braft/macros.h" 27 | #include "braft/file_system_adaptor.h" 28 | 29 | namespace braft { 30 | 31 | // Abstract class to read data from a file 32 | // All the const method should be thread safe 33 | class FileReader : public butil::RefCountedThreadSafe { 34 | friend class butil::RefCountedThreadSafe; 35 | public: 36 | // Read data from filename at |offset| (from the start of the file) for at 37 | // most |max_count| bytes to |out|. Reading part of a file is allowed if 38 | // |read_partly| is TRUE. If successfully read the file, |read_count| 39 | // is the actual read count, it's maybe smaller than |max_count| if the 40 | // request is throttled or reach the end of the file. In the case of 41 | // reaching the end of the file, |is_eof| is also set to true. 42 | // Returns 0 on success, the error otherwise 43 | virtual int read_file(butil::IOBuf* out, 44 | const std::string &filename, 45 | off_t offset, 46 | size_t max_count, 47 | bool read_partly, 48 | size_t* read_count, 49 | bool* is_eof) const = 0; 50 | // Get the path of this reader 51 | virtual const std::string& path() const = 0; 52 | protected: 53 | FileReader() {} 54 | virtual ~FileReader() {} 55 | }; 56 | 57 | // Read files within a local directory 58 | class LocalDirReader : public FileReader { 59 | public: 60 | LocalDirReader(FileSystemAdaptor* fs, const std::string& path) 61 | : _path(path), _fs(fs), _current_file(NULL), _is_reading(false), _eof_reached(true) 62 | {} 63 | virtual ~LocalDirReader(); 64 | 65 | // Open a snapshot for read 66 | virtual bool open(); 67 | 68 | // Read data from filename at |offset| (from the start of the file) for at 69 | // most |max_count| bytes to |out|. Reading part of a file is allowed if 70 | // |read_partly| is TRUE. If successfully read the file, |read_count| 71 | // is the actual read count, it's maybe smaller than |max_count| if the 72 | // request is throttled or reach the end of the file. In the case of 73 | // reaching the end of the file, |is_eof| is also set to true. 74 | // Returns 0 on success, the error otherwise 75 | virtual int read_file(butil::IOBuf* out, 76 | const std::string &filename, 77 | off_t offset, 78 | size_t max_count, 79 | bool read_partly, 80 | size_t* read_count, 81 | bool* is_eof) const; 82 | virtual const std::string& path() const { return _path; } 83 | protected: 84 | int read_file_with_meta(butil::IOBuf* out, 85 | const std::string &filename, 86 | google::protobuf::Message* file_meta, 87 | off_t offset, 88 | size_t max_count, 89 | size_t* read_count, 90 | bool* is_eof) const; 91 | const scoped_refptr& file_system() const { return _fs; } 92 | 93 | private: 94 | mutable raft_mutex_t _mutex; 95 | std::string _path; 96 | scoped_refptr _fs; 97 | mutable FileAdaptor* _current_file; 98 | mutable std::string _current_filename; 99 | mutable bool _is_reading; 100 | mutable bool _eof_reached; 101 | }; 102 | 103 | } // namespace braft 104 | 105 | #endif //BRAFT_FILE_READER_H 106 | -------------------------------------------------------------------------------- /src/braft/file_service.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include "braft/file_service.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "braft/util.h" 27 | 28 | namespace braft { 29 | 30 | DEFINE_bool(raft_file_check_hole, false, "file service check hole switch, default disable"); 31 | 32 | void FileServiceImpl::get_file(::google::protobuf::RpcController* controller, 33 | const ::braft::GetFileRequest* request, 34 | ::braft::GetFileResponse* response, 35 | ::google::protobuf::Closure* done) { 36 | scoped_refptr reader; 37 | brpc::ClosureGuard done_gurad(done); 38 | brpc::Controller* cntl = (brpc::Controller*)controller; 39 | std::unique_lock lck(_mutex); 40 | Map::const_iterator iter = _reader_map.find(request->reader_id()); 41 | if (iter == _reader_map.end()) { 42 | lck.unlock(); 43 | cntl->SetFailed(ENOENT, "Fail to find reader=%" PRId64, request->reader_id()); 44 | return; 45 | } 46 | // Don't touch iter ever after 47 | reader = iter->second; 48 | lck.unlock(); 49 | BRAFT_VLOG << "get_file for " << cntl->remote_side() << " path=" << reader->path() 50 | << " filename=" << request->filename() 51 | << " offset=" << request->offset() << " count=" << request->count(); 52 | 53 | if (request->count() <= 0 || request->offset() < 0) { 54 | cntl->SetFailed(brpc::EREQUEST, "Invalid request=%s", 55 | request->ShortDebugString().c_str()); 56 | return; 57 | } 58 | 59 | butil::IOBuf buf; 60 | bool is_eof = false; 61 | size_t read_count = 0; 62 | 63 | const int rc = reader->read_file( 64 | &buf, request->filename(), 65 | request->offset(), request->count(), 66 | request->read_partly(), 67 | &read_count, 68 | &is_eof); 69 | if (rc != 0) { 70 | cntl->SetFailed(rc, "Fail to read from path=%s filename=%s : %s", 71 | reader->path().c_str(), request->filename().c_str(), berror(rc)); 72 | return; 73 | } 74 | 75 | response->set_eof(is_eof); 76 | response->set_read_size(read_count); 77 | // skip empty data 78 | if (buf.size() == 0) { 79 | return; 80 | } 81 | 82 | FileSegData seg_data; 83 | if (!FLAGS_raft_file_check_hole) { 84 | seg_data.append(buf, request->offset()); 85 | } else { 86 | off_t buf_off = request->offset(); 87 | while (!buf.empty()) { 88 | butil::StringPiece p = buf.backing_block(0); 89 | if (!is_zero(p.data(), p.size())) { 90 | butil::IOBuf piece_buf; 91 | buf.cutn(&piece_buf, p.size()); 92 | seg_data.append(piece_buf, buf_off); 93 | } else { 94 | // skip zero IOBuf block 95 | buf.pop_front(p.size()); 96 | } 97 | buf_off += p.size(); 98 | } 99 | } 100 | cntl->response_attachment().swap(seg_data.data()); 101 | } 102 | 103 | FileServiceImpl::FileServiceImpl() { 104 | _next_id = ((int64_t)getpid() << 45) | (butil::gettimeofday_us() << 17 >> 17); 105 | } 106 | 107 | int FileServiceImpl::add_reader(FileReader* reader, int64_t* reader_id) { 108 | BAIDU_SCOPED_LOCK(_mutex); 109 | *reader_id = _next_id++; 110 | _reader_map[*reader_id] = reader; 111 | return 0; 112 | } 113 | 114 | int FileServiceImpl::remove_reader(int64_t reader_id) { 115 | BAIDU_SCOPED_LOCK(_mutex); 116 | return _reader_map.erase(reader_id) == 1 ? 0 : -1; 117 | } 118 | 119 | } // namespace braft 120 | -------------------------------------------------------------------------------- /src/braft/file_service.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_FILE_SERVICE_H 18 | #define BRAFT_FILE_SERVICE_H 19 | 20 | #include 21 | #include "braft/file_service.pb.h" 22 | #include "braft/file_reader.h" 23 | #include "braft/util.h" 24 | 25 | namespace braft { 26 | 27 | class BAIDU_CACHELINE_ALIGNMENT FileServiceImpl : public FileService { 28 | public: 29 | static FileServiceImpl* GetInstance() { 30 | return Singleton::get(); 31 | } 32 | void get_file(::google::protobuf::RpcController* controller, 33 | const ::braft::GetFileRequest* request, 34 | ::braft::GetFileResponse* response, 35 | ::google::protobuf::Closure* done); 36 | int add_reader(FileReader* reader, int64_t* reader_id); 37 | int remove_reader(int64_t reader_id); 38 | private: 39 | friend struct DefaultSingletonTraits; 40 | FileServiceImpl(); 41 | ~FileServiceImpl() {} 42 | typedef std::map > Map; 43 | raft_mutex_t _mutex; 44 | int64_t _next_id; 45 | Map _reader_map; 46 | }; 47 | 48 | inline FileServiceImpl* file_service() 49 | { return FileServiceImpl::GetInstance(); } 50 | 51 | inline int file_service_add(FileReader* reader, int64_t* reader_id) { 52 | FileServiceImpl* const fs = file_service(); 53 | return fs->add_reader(reader, reader_id); 54 | } 55 | 56 | inline int file_service_remove(int64_t reader_id) { 57 | FileServiceImpl* const fs = file_service(); 58 | return fs->remove_reader(reader_id); 59 | } 60 | 61 | } // namespace braft 62 | 63 | #endif //BRAFT_FILE_SERVICE_H 64 | -------------------------------------------------------------------------------- /src/braft/file_service.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | option cc_generic_services = true; 4 | 5 | message GetFileRequest { 6 | required int64 reader_id = 1; 7 | required string filename = 2; 8 | required int64 count = 3; 9 | required int64 offset = 4; 10 | optional bool read_partly = 5; 11 | } 12 | 13 | message GetFileResponse { 14 | // Data is in attachment 15 | required bool eof = 1; 16 | optional int64 read_size = 2; 17 | } 18 | 19 | service FileService { 20 | rpc get_file(GetFileRequest) returns (GetFileResponse); 21 | } 22 | -------------------------------------------------------------------------------- /src/braft/fsync.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include "braft/fsync.h" 18 | #include //BRPC_VALIDATE_GFLAG 19 | 20 | namespace braft { 21 | 22 | DEFINE_bool(raft_use_fsync_rather_than_fdatasync, 23 | true, 24 | "Use fsync rather than fdatasync to flush page cache"); 25 | 26 | BRPC_VALIDATE_GFLAG(raft_use_fsync_rather_than_fdatasync, 27 | brpc::PassValidate); 28 | 29 | } // namespace braft 30 | -------------------------------------------------------------------------------- /src/braft/fsync.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Xiong,Kai(xiongkai@baidu.com) 17 | 18 | #ifndef BRAFT_FSYNC_H 19 | #define BRAFT_FSYNC_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include "braft/storage.h" 25 | 26 | namespace braft { 27 | 28 | DECLARE_bool(raft_use_fsync_rather_than_fdatasync); 29 | 30 | inline int raft_fsync(int fd) { 31 | if (FLAGS_raft_use_fsync_rather_than_fdatasync) { 32 | return fsync(fd); 33 | } else { 34 | #ifdef __APPLE__ 35 | return fcntl(fd, F_FULLFSYNC); 36 | #else 37 | return fdatasync(fd); 38 | #endif 39 | } 40 | } 41 | 42 | inline bool raft_sync_meta() { 43 | return FLAGS_raft_sync || FLAGS_raft_sync_meta; 44 | } 45 | 46 | } // namespace braft 47 | 48 | #endif //BRAFT_FSYNC_H 49 | -------------------------------------------------------------------------------- /src/braft/lease.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Pengfei Zheng (zhengpengfei@baidu.com) 16 | 17 | #include 18 | #include 19 | #include "braft/lease.h" 20 | 21 | namespace braft { 22 | 23 | DEFINE_bool(raft_enable_leader_lease, false, 24 | "Enable or disable leader lease. only when all peers in a raft group " 25 | "set this configuration to true, leader lease check and vote are safe."); 26 | BRPC_VALIDATE_GFLAG(raft_enable_leader_lease, ::brpc::PassValidate); 27 | 28 | void LeaderLease::init(int64_t election_timeout_ms) { 29 | _election_timeout_ms = election_timeout_ms; 30 | } 31 | 32 | void LeaderLease::on_leader_start(int64_t term) { 33 | BAIDU_SCOPED_LOCK(_mutex); 34 | ++_lease_epoch; 35 | _term = term; 36 | _last_active_timestamp = 0; 37 | } 38 | 39 | void LeaderLease::on_leader_stop() { 40 | BAIDU_SCOPED_LOCK(_mutex); 41 | _last_active_timestamp = 0; 42 | _term = 0; 43 | } 44 | 45 | void LeaderLease::on_lease_start(int64_t expect_lease_epoch, int64_t last_active_timestamp) { 46 | BAIDU_SCOPED_LOCK(_mutex); 47 | if (_term == 0 || expect_lease_epoch != _lease_epoch) { 48 | return; 49 | } 50 | _last_active_timestamp = last_active_timestamp; 51 | } 52 | 53 | void LeaderLease::renew(int64_t last_active_timestamp) { 54 | BAIDU_SCOPED_LOCK(_mutex); 55 | _last_active_timestamp = last_active_timestamp; 56 | } 57 | 58 | void LeaderLease::get_lease_info(LeaseInfo* lease_info) { 59 | lease_info->term = 0; 60 | lease_info->lease_epoch = 0; 61 | if (!FLAGS_raft_enable_leader_lease) { 62 | lease_info->state = LeaderLease::DISABLED; 63 | return; 64 | } 65 | 66 | BAIDU_SCOPED_LOCK(_mutex); 67 | if (_term == 0) { 68 | lease_info->state = LeaderLease::EXPIRED; 69 | return; 70 | } 71 | if (_last_active_timestamp == 0) { 72 | lease_info->state = LeaderLease::NOT_READY; 73 | return; 74 | } 75 | if (butil::monotonic_time_ms() < _last_active_timestamp + _election_timeout_ms) { 76 | lease_info->term = _term; 77 | lease_info->lease_epoch = _lease_epoch; 78 | lease_info->state = LeaderLease::VALID; 79 | } else { 80 | lease_info->state = LeaderLease::SUSPECT; 81 | } 82 | } 83 | 84 | int64_t LeaderLease::lease_epoch() { 85 | BAIDU_SCOPED_LOCK(_mutex); 86 | return _lease_epoch; 87 | } 88 | 89 | void LeaderLease::reset_election_timeout_ms(int64_t election_timeout_ms) { 90 | BAIDU_SCOPED_LOCK(_mutex); 91 | _election_timeout_ms = election_timeout_ms; 92 | } 93 | 94 | void FollowerLease::init(int64_t election_timeout_ms, int64_t max_clock_drift_ms) { 95 | _election_timeout_ms = election_timeout_ms; 96 | _max_clock_drift_ms = max_clock_drift_ms; 97 | // When the node restart, we are not sure when the lease will be expired actually, 98 | // so just be conservative. 99 | _last_leader_timestamp = butil::monotonic_time_ms(); 100 | } 101 | 102 | void FollowerLease::renew(const PeerId& leader_id) { 103 | _last_leader = leader_id; 104 | _last_leader_timestamp = butil::monotonic_time_ms(); 105 | } 106 | 107 | int64_t FollowerLease::last_leader_timestamp() { 108 | return _last_leader_timestamp; 109 | } 110 | 111 | int64_t FollowerLease::votable_time_from_now() { 112 | if (!FLAGS_raft_enable_leader_lease) { 113 | return 0; 114 | } 115 | 116 | int64_t now = butil::monotonic_time_ms(); 117 | int64_t votable_timestamp = _last_leader_timestamp + _election_timeout_ms + 118 | _max_clock_drift_ms; 119 | if (now >= votable_timestamp) { 120 | return 0; 121 | } 122 | return votable_timestamp - now; 123 | } 124 | 125 | const PeerId& FollowerLease::last_leader() { 126 | return _last_leader; 127 | } 128 | 129 | bool FollowerLease::expired() { 130 | return butil::monotonic_time_ms() - _last_leader_timestamp 131 | >= _election_timeout_ms + _max_clock_drift_ms; 132 | } 133 | 134 | void FollowerLease::reset() { 135 | _last_leader = PeerId(); 136 | _last_leader_timestamp = 0; 137 | } 138 | 139 | void FollowerLease::expire() { 140 | _last_leader_timestamp = 0; 141 | } 142 | 143 | void FollowerLease::reset_election_timeout_ms(int64_t election_timeout_ms, 144 | int64_t max_clock_drift_ms) { 145 | _election_timeout_ms = election_timeout_ms; 146 | _max_clock_drift_ms = max_clock_drift_ms; 147 | } 148 | 149 | } // namespace braft 150 | -------------------------------------------------------------------------------- /src/braft/lease.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Pengfei Zheng (zhengpengfei@baidu.com) 16 | 17 | #ifndef PUBLIC_RAFT_LEASE_H 18 | #define PUBLIC_RAFT_LEASE_H 19 | 20 | #include "braft/util.h" 21 | 22 | namespace braft { 23 | 24 | class LeaderLease { 25 | public: 26 | enum InternalState { 27 | DISABLED, 28 | EXPIRED, 29 | NOT_READY, 30 | VALID, 31 | SUSPECT, 32 | }; 33 | 34 | struct LeaseInfo { 35 | InternalState state; 36 | int64_t term; 37 | int64_t lease_epoch; 38 | }; 39 | 40 | LeaderLease() 41 | : _election_timeout_ms(0) 42 | , _last_active_timestamp(0) 43 | , _term(0) 44 | , _lease_epoch(0) 45 | {} 46 | 47 | void init(int64_t election_timeout_ms); 48 | void on_leader_start(int64_t term); 49 | void on_leader_stop(); 50 | void on_lease_start(int64_t expect_lease_epoch, int64_t last_active_timestamp); 51 | void get_lease_info(LeaseInfo* lease_info); 52 | void renew(int64_t last_active_timestamp); 53 | int64_t lease_epoch(); 54 | void reset_election_timeout_ms(int64_t election_timeout_ms); 55 | 56 | private: 57 | raft_mutex_t _mutex; 58 | int64_t _election_timeout_ms; 59 | int64_t _last_active_timestamp; 60 | int64_t _term; 61 | int64_t _lease_epoch; 62 | }; 63 | 64 | class FollowerLease { 65 | public: 66 | FollowerLease() 67 | : _election_timeout_ms(0), _max_clock_drift_ms(0) 68 | , _last_leader_timestamp(0) 69 | {} 70 | 71 | void init(int64_t election_timeout_ms, int64_t max_clock_drift_ms); 72 | void renew(const PeerId& leader_id); 73 | int64_t votable_time_from_now(); 74 | const PeerId& last_leader(); 75 | bool expired(); 76 | void reset(); 77 | void expire(); 78 | void reset_election_timeout_ms(int64_t election_timeout_ms, int64_t max_clock_drift_ms); 79 | int64_t last_leader_timestamp(); 80 | 81 | private: 82 | int64_t _election_timeout_ms; 83 | int64_t _max_clock_drift_ms; 84 | PeerId _last_leader; 85 | int64_t _last_leader_timestamp; 86 | }; 87 | 88 | } // namespace braft 89 | 90 | #endif // PUBLIC_RAFT_LEADER_LEASE_H 91 | -------------------------------------------------------------------------------- /src/braft/local_file_meta.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package braft; 3 | 4 | enum FileSource { 5 | FILE_SOURCE_LOCAL = 0; 6 | FILE_SOURCE_REFERENCE = 1; 7 | } 8 | 9 | message LocalFileMeta { 10 | optional bytes user_meta = 1; 11 | optional FileSource source = 2; 12 | optional string checksum = 3; 13 | } 14 | -------------------------------------------------------------------------------- /src/braft/local_storage.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | 3 | import "braft/raft.proto"; 4 | import "braft/local_file_meta.proto"; 5 | 6 | package braft; 7 | 8 | message ConfigurationPBMeta { 9 | repeated string peers = 1; 10 | repeated string old_peers = 2; 11 | }; 12 | 13 | message LogPBMeta { 14 | required int64 first_log_index = 1; 15 | }; 16 | 17 | message StablePBMeta { 18 | required int64 term = 1; 19 | required string votedfor = 2; 20 | }; 21 | 22 | message LocalSnapshotPbMeta { 23 | message File { 24 | required string name = 1; 25 | optional LocalFileMeta meta = 2; 26 | }; 27 | optional SnapshotMeta meta = 1; 28 | repeated File files = 2; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/braft/log_entry.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include "braft/log_entry.h" 18 | #include "braft/local_storage.pb.h" 19 | 20 | namespace braft { 21 | 22 | bvar::Adder g_nentries("raft_num_log_entries"); 23 | 24 | LogEntry::LogEntry(): type(ENTRY_TYPE_UNKNOWN), peers(NULL), old_peers(NULL) { 25 | g_nentries << 1; 26 | } 27 | 28 | LogEntry::~LogEntry() { 29 | g_nentries << -1; 30 | delete peers; 31 | delete old_peers; 32 | } 33 | 34 | butil::Status parse_configuration_meta(const butil::IOBuf& data, LogEntry* entry) { 35 | butil::Status status; 36 | ConfigurationPBMeta meta; 37 | butil::IOBufAsZeroCopyInputStream wrapper(data); 38 | if (!meta.ParseFromZeroCopyStream(&wrapper)) { 39 | status.set_error(EINVAL, "Fail to parse ConfigurationPBMeta"); 40 | return status; 41 | } 42 | entry->peers = new std::vector; 43 | for (int j = 0; j < meta.peers_size(); ++j) { 44 | entry->peers->push_back(PeerId(meta.peers(j))); 45 | } 46 | if (meta.old_peers_size() > 0) { 47 | entry->old_peers = new std::vector; 48 | for (int i = 0; i < meta.old_peers_size(); i++) { 49 | entry->old_peers->push_back(PeerId(meta.old_peers(i))); 50 | } 51 | } 52 | return status; 53 | } 54 | 55 | butil::Status serialize_configuration_meta(const LogEntry* entry, butil::IOBuf& data) { 56 | butil::Status status; 57 | ConfigurationPBMeta meta; 58 | for (size_t i = 0; i < entry->peers->size(); ++i) { 59 | meta.add_peers((*(entry->peers))[i].to_string()); 60 | } 61 | if (entry->old_peers) { 62 | for (size_t i = 0; i < entry->old_peers->size(); ++i) { 63 | meta.add_old_peers((*(entry->old_peers))[i].to_string()); 64 | } 65 | } 66 | butil::IOBufAsZeroCopyOutputStream wrapper(&data); 67 | if (!meta.SerializeToZeroCopyStream(&wrapper)) { 68 | status.set_error(EINVAL, "Fail to serialize ConfigurationPBMeta"); 69 | } 70 | return status; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/braft/log_entry.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_LOG_ENTRY_H 18 | #define BRAFT_LOG_ENTRY_H 19 | 20 | #include // butil::IOBuf 21 | #include // butil::RefCountedThreadSafe 22 | #include // fmix64 23 | #include "braft/configuration.h" 24 | #include "braft/raft.pb.h" 25 | #include "braft/util.h" 26 | 27 | namespace braft { 28 | 29 | // Log identifier 30 | struct LogId { 31 | LogId() : index(0), term(0) {} 32 | LogId(int64_t index_, int64_t term_) : index(index_), term(term_) {} 33 | int64_t index; 34 | int64_t term; 35 | }; 36 | 37 | // term start from 1, log index start from 1 38 | struct LogEntry : public butil::RefCountedThreadSafe { 39 | public: 40 | EntryType type; // log type 41 | LogId id; 42 | std::vector* peers; // peers 43 | std::vector* old_peers; // peers 44 | butil::IOBuf data; 45 | 46 | LogEntry(); 47 | 48 | private: 49 | DISALLOW_COPY_AND_ASSIGN(LogEntry); 50 | friend class butil::RefCountedThreadSafe; 51 | virtual ~LogEntry(); 52 | }; 53 | 54 | // Comparators 55 | 56 | inline bool operator==(const LogId& lhs, const LogId& rhs) { 57 | return lhs.index == rhs.index && lhs.term == rhs.term; 58 | } 59 | 60 | inline bool operator!=(const LogId& lhs, const LogId& rhs) { 61 | return !(lhs == rhs); 62 | } 63 | 64 | inline bool operator<(const LogId& lhs, const LogId& rhs) { 65 | if (lhs.term == rhs.term) { 66 | return lhs.index < rhs.index; 67 | } 68 | return lhs.term < rhs.term; 69 | } 70 | 71 | inline bool operator>(const LogId& lhs, const LogId& rhs) { 72 | if (lhs.term == rhs.term) { 73 | return lhs.index > rhs.index; 74 | } 75 | return lhs.term > rhs.term; 76 | } 77 | 78 | inline bool operator<=(const LogId& lhs, const LogId& rhs) { 79 | return !(lhs > rhs); 80 | } 81 | 82 | inline bool operator>=(const LogId& lhs, const LogId& rhs) { 83 | return !(lhs < rhs); 84 | } 85 | 86 | struct LogIdHasher { 87 | size_t operator()(const LogId& id) const { 88 | return butil::fmix64(id.index) ^ butil::fmix64(id.term); 89 | } 90 | }; 91 | 92 | inline std::ostream& operator<<(std::ostream& os, const LogId& id) { 93 | os << "(index=" << id.index << ",term=" << id.term << ')'; 94 | return os; 95 | } 96 | 97 | butil::Status parse_configuration_meta(const butil::IOBuf& data, LogEntry* entry); 98 | 99 | butil::Status serialize_configuration_meta(const LogEntry* entry, butil::IOBuf& data); 100 | 101 | } // namespace braft 102 | 103 | #endif //BRAFT_LOG_ENTRY_H 104 | -------------------------------------------------------------------------------- /src/braft/macros.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_MACROS_H 18 | #define BRAFT_MACROS_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #define BRAFT_VLOG_IS_ON VLOG_IS_ON(89) 25 | #define BRAFT_VLOG VLOG(89) 26 | #define BRAFT_VPLOG VPLOG(89) 27 | #define BRAFT_VLOG_IF(cond) VLOG_IF(89, (cond)) 28 | #define BRAFT_VPLOG_IF(cond) VPLOG_IF(89, (cond)) 29 | 30 | //#define USE_BTHREAD_MUTEX 31 | 32 | #ifdef USE_BTHREAD_MUTEX 33 | 34 | #include 35 | 36 | namespace braft { 37 | typedef ::bthread::Mutex raft_mutex_t; 38 | } // namespace braft 39 | 40 | #else // USE_BTHREAD_MUTEX 41 | 42 | #include 43 | namespace braft { 44 | typedef ::butil::Mutex raft_mutex_t; 45 | } // namespace braft 46 | 47 | #endif // USE_BTHREAD_MUTEX 48 | 49 | #ifdef UNIT_TEST 50 | #define BRAFT_MOCK virtual 51 | #else 52 | #define BRAFT_MOCK 53 | #endif 54 | 55 | #define BRAFT_GET_ARG3(arg1, arg2, arg3, ...) arg3 56 | 57 | #define BRAFT_RETURN_IF1(expr, rc) \ 58 | do { \ 59 | if ((expr)) { \ 60 | return (rc); \ 61 | } \ 62 | } while (0) 63 | 64 | #define BRAFT_RETURN_IF0(expr) \ 65 | do { \ 66 | if ((expr)) { \ 67 | return; \ 68 | } \ 69 | } while (0) 70 | 71 | #define BRAFT_RETURN_IF(expr, args...) \ 72 | BRAFT_GET_ARG3(1, ##args, BRAFT_RETURN_IF1, BRAFT_RETURN_IF0)(expr, ##args) 73 | 74 | #endif //BRAFT_MACROS_H 75 | -------------------------------------------------------------------------------- /src/braft/memory_log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Qin,Duohao(qinduohao@baidu.com) 16 | 17 | #ifndef BRAFT_MEMORY_LOG_H 18 | #define BRAFT_MEMORY_LOG_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "braft/log_entry.h" 26 | #include "braft/storage.h" 27 | #include "braft/util.h" 28 | 29 | namespace braft { 30 | 31 | class BAIDU_CACHELINE_ALIGNMENT MemoryLogStorage : public LogStorage { 32 | public: 33 | typedef std::deque MemoryData; 34 | 35 | MemoryLogStorage(const std::string& path) 36 | : _path(path), 37 | _first_log_index(1), 38 | _last_log_index(0) {} 39 | MemoryLogStorage() 40 | : _first_log_index(1), 41 | _last_log_index(0) {} 42 | 43 | virtual ~MemoryLogStorage() { reset(1); } 44 | 45 | virtual int init(ConfigurationManager* configuration_manager); 46 | 47 | // first log index in log 48 | virtual int64_t first_log_index() { 49 | return _first_log_index.load(butil::memory_order_acquire); 50 | } 51 | 52 | // last log index in log 53 | virtual int64_t last_log_index() { 54 | return _last_log_index.load(butil::memory_order_acquire); 55 | } 56 | 57 | // get logentry by index 58 | virtual LogEntry* get_entry(const int64_t index); 59 | 60 | // get logentry's term by index 61 | virtual int64_t get_term(const int64_t index); 62 | 63 | // append entries to log 64 | virtual int append_entry(const LogEntry* entry); 65 | 66 | // append entries to log and update IOMetric, return append success number 67 | virtual int append_entries(const std::vector& entries, IOMetric* metric); 68 | 69 | // delete logs from storage's head, [first_log_index, first_index_kept) will be discarded 70 | virtual int truncate_prefix(const int64_t first_index_kept); 71 | 72 | // delete uncommitted logs from storage's tail, (last_index_kept, last_log_index] will be discarded 73 | virtual int truncate_suffix(const int64_t last_index_kept); 74 | 75 | // Drop all the existing logs and reset next log index to |next_log_index|. 76 | // This function is called after installing snapshot from leader 77 | virtual int reset(const int64_t next_log_index); 78 | 79 | // Create an instance of this kind of LogStorage with the parameters encoded 80 | // in |uri| 81 | // Return the address referenced to the instance on success, NULL otherwise. 82 | virtual LogStorage* new_instance(const std::string& uri) const; 83 | 84 | // GC an instance of this kind of LogStorage with the parameters encoded 85 | // in |uri| 86 | virtual butil::Status gc_instance(const std::string& uri) const; 87 | 88 | private: 89 | std::string _path; 90 | butil::atomic _first_log_index; 91 | butil::atomic _last_log_index; 92 | MemoryData _log_entry_data; 93 | raft_mutex_t _mutex; 94 | }; 95 | 96 | } // namespace braft 97 | 98 | #endif //~BRAFT_MEMORY_LOG_H 99 | -------------------------------------------------------------------------------- /src/braft/node_manager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_NODE_MANAGER_H 18 | #define BRAFT_NODE_MANAGER_H 19 | 20 | #include 21 | #include 22 | #include "braft/raft.h" 23 | #include "braft/util.h" 24 | 25 | namespace braft { 26 | 27 | class NodeImpl; 28 | 29 | class NodeManager { 30 | public: 31 | static NodeManager* GetInstance() { 32 | return Singleton::get(); 33 | } 34 | 35 | // add raft node 36 | bool add(NodeImpl* node); 37 | 38 | // remove raft node 39 | bool remove(NodeImpl* node); 40 | 41 | // get node by group_id and peer_id 42 | scoped_refptr get(const GroupId& group_id, const PeerId& peer_id); 43 | 44 | // get all the nodes of |group_id| 45 | void get_nodes_by_group_id(const GroupId& group_id, 46 | std::vector >* nodes); 47 | 48 | void get_all_nodes(std::vector >* nodes); 49 | 50 | // Add service to |server| at |listen_addr| 51 | int add_service(brpc::Server* server, 52 | const butil::EndPoint& listen_addr); 53 | 54 | // Return true if |addr| is reachable by a RPC Server 55 | bool server_exists(butil::EndPoint addr); 56 | 57 | // Remove the addr from _addr_set when the backing service is destroyed 58 | void remove_address(butil::EndPoint addr); 59 | 60 | private: 61 | NodeManager(); 62 | ~NodeManager(); 63 | DISALLOW_COPY_AND_ASSIGN(NodeManager); 64 | friend struct DefaultSingletonTraits; 65 | 66 | // TODO(chenzhangyi01): replace std::map with FlatMap 67 | // To make implementation simplicity, we use two maps here, although 68 | // it works practically with only one GroupMap 69 | typedef std::map > NodeMap; 70 | typedef std::multimap GroupMap; 71 | struct Maps { 72 | NodeMap node_map; 73 | GroupMap group_map; 74 | }; 75 | // Functor to modify DBD 76 | static size_t _add_node(Maps&, const NodeImpl* node); 77 | static size_t _remove_node(Maps&, const NodeImpl* node); 78 | 79 | butil::DoublyBufferedData _nodes; 80 | 81 | raft_mutex_t _mutex; 82 | std::set _addr_set; 83 | }; 84 | 85 | #define global_node_manager NodeManager::GetInstance() 86 | 87 | } // namespace braft 88 | 89 | #endif // BRAFT_NODE_MANAGER_H 90 | -------------------------------------------------------------------------------- /src/braft/protobuf_file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Wang,Yao(wangyao02@baidu.com) 16 | 17 | #include 18 | #include 19 | 20 | #include "braft/protobuf_file.h" 21 | 22 | namespace braft { 23 | 24 | ProtoBufFile::ProtoBufFile(const char* path, FileSystemAdaptor* fs) 25 | : _path(path), _fs(fs) { 26 | if (_fs == NULL) { 27 | _fs = default_file_system(); 28 | } 29 | } 30 | 31 | ProtoBufFile::ProtoBufFile(const std::string& path, FileSystemAdaptor* fs) 32 | : _path(path), _fs(fs) { 33 | if (_fs == NULL) { 34 | _fs = default_file_system(); 35 | } 36 | } 37 | 38 | int ProtoBufFile::save(const google::protobuf::Message* message, bool sync) { 39 | std::string tmp_path(_path); 40 | tmp_path.append(".tmp"); 41 | 42 | butil::File::Error e; 43 | FileAdaptor* file = _fs->open(tmp_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, NULL, &e); 44 | if (!file) { 45 | LOG(WARNING) << "open file failed, path: " << _path 46 | << ": " << butil::File::ErrorToString(e); 47 | return -1; 48 | } 49 | std::unique_ptr > guard(file); 50 | 51 | // serialize msg 52 | butil::IOBuf header_buf; 53 | butil::IOBuf msg_buf; 54 | butil::IOBufAsZeroCopyOutputStream msg_wrapper(&msg_buf); 55 | message->SerializeToZeroCopyStream(&msg_wrapper); 56 | 57 | // write len 58 | int32_t header_len = butil::HostToNet32(msg_buf.length()); 59 | header_buf.append(&header_len, sizeof(int32_t)); 60 | if (sizeof(int32_t) != file->write(header_buf, 0)) { 61 | LOG(WARNING) << "write len failed, path: " << tmp_path; 62 | return -1; 63 | } 64 | 65 | ssize_t len = msg_buf.size(); 66 | if (len != file->write(msg_buf, sizeof(int32_t))) { 67 | LOG(WARNING) << "write failed, path: " << tmp_path; 68 | return -1; 69 | } 70 | 71 | // sync 72 | if (sync) { 73 | if (!file->sync()) { 74 | LOG(WARNING) << "sync failed, path: " << tmp_path; 75 | return -1; 76 | } 77 | } 78 | 79 | // rename 80 | if (!_fs->rename(tmp_path, _path)) { 81 | LOG(WARNING) << "rename failed, old: " << tmp_path << " , new: " << _path; 82 | return -1; 83 | } 84 | return 0; 85 | } 86 | 87 | int ProtoBufFile::load(google::protobuf::Message* message) { 88 | butil::File::Error e; 89 | FileAdaptor* file = _fs->open(_path, O_RDONLY, NULL, &e); 90 | if (!file) { 91 | LOG(WARNING) << "open file failed, path: " << _path 92 | << ": " << butil::File::ErrorToString(e); 93 | return -1; 94 | } 95 | 96 | std::unique_ptr > guard(file); 97 | 98 | // len 99 | butil::IOPortal header_buf; 100 | if (sizeof(int32_t) != file->read(&header_buf, 0, sizeof(int32_t))) { 101 | LOG(WARNING) << "read len failed, path: " << _path; 102 | return -1; 103 | } 104 | int32_t len = 0; 105 | header_buf.copy_to(&len, sizeof(int32_t)); 106 | int32_t left_len = butil::NetToHost32(len); 107 | 108 | // read protobuf data 109 | butil::IOPortal msg_buf; 110 | if (left_len != file->read(&msg_buf, sizeof(int32_t), left_len)) { 111 | LOG(WARNING) << "read body failed, path: " << _path; 112 | return -1; 113 | } 114 | 115 | // parse msg 116 | butil::IOBufAsZeroCopyInputStream msg_wrapper(msg_buf); 117 | message->ParseFromZeroCopyStream(&msg_wrapper); 118 | 119 | return 0; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/braft/protobuf_file.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Wang,Yao(wangyao02@baidu.com) 16 | 17 | #ifndef BRAFT_PROTOBUF_FILE_H 18 | #define BRAFT_PROTOBUF_FILE_H 19 | 20 | #include 21 | #include 22 | #include "braft/file_system_adaptor.h" 23 | 24 | namespace braft { 25 | 26 | // protobuf file format: 27 | // len [4B, in network order] 28 | // protobuf data 29 | class ProtoBufFile { 30 | public: 31 | ProtoBufFile(const char* path, FileSystemAdaptor* fs = NULL); 32 | ProtoBufFile(const std::string& path, FileSystemAdaptor* fs = NULL); 33 | ~ProtoBufFile() {} 34 | 35 | int save(const ::google::protobuf::Message* message, bool sync); 36 | int load(::google::protobuf::Message* message); 37 | 38 | private: 39 | std::string _path; 40 | scoped_refptr _fs; 41 | }; 42 | 43 | } 44 | 45 | #endif //~BRAFT_PROTOBUF_FILE_H 46 | -------------------------------------------------------------------------------- /src/braft/raft.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | 3 | import "braft/enum.proto"; 4 | 5 | package braft; 6 | option cc_generic_services = true; 7 | 8 | // data store in baidu-rpc's attachment 9 | message EntryMeta { 10 | required int64 term = 1; 11 | required EntryType type = 2; 12 | repeated string peers = 3; 13 | optional int64 data_len = 4; 14 | // Don't change field id of `old_peers' in the consideration of backward 15 | // compatibility 16 | repeated string old_peers = 5; 17 | }; 18 | 19 | message TermLeader { 20 | required string peer_id = 1; 21 | required int64 term = 2; 22 | }; 23 | 24 | message RequestVoteRequest { 25 | required string group_id = 1; 26 | required string server_id = 2; 27 | required string peer_id = 3; 28 | required int64 term = 4; 29 | required int64 last_log_term = 5; 30 | required int64 last_log_index = 6; 31 | optional TermLeader disrupted_leader = 7; 32 | }; 33 | 34 | message RequestVoteResponse { 35 | required int64 term = 1; 36 | required bool granted = 2; 37 | optional bool disrupted = 3; 38 | optional int64 previous_term = 4; 39 | optional bool rejected_by_lease = 5; 40 | }; 41 | 42 | message AppendEntriesRequest { 43 | required string group_id = 1; 44 | required string server_id = 2; 45 | required string peer_id = 3; 46 | required int64 term = 4; 47 | required int64 prev_log_term = 5; 48 | required int64 prev_log_index = 6; 49 | repeated EntryMeta entries = 7; 50 | required int64 committed_index = 8; 51 | }; 52 | 53 | message AppendEntriesResponse { 54 | required int64 term = 1; 55 | required bool success = 2; 56 | optional int64 last_log_index = 3; 57 | optional bool readonly = 4; 58 | }; 59 | 60 | message SnapshotMeta { 61 | required int64 last_included_index = 1; 62 | required int64 last_included_term = 2; 63 | repeated string peers = 3; 64 | repeated string old_peers = 4; 65 | } 66 | 67 | message InstallSnapshotRequest { 68 | required string group_id = 1; 69 | required string server_id = 2; 70 | required string peer_id = 3; 71 | required int64 term = 4; 72 | required SnapshotMeta meta = 5; 73 | required string uri = 6; 74 | }; 75 | 76 | message InstallSnapshotResponse { 77 | required int64 term = 1; 78 | required bool success = 2; 79 | }; 80 | 81 | message TimeoutNowRequest { 82 | required string group_id = 1; 83 | required string server_id = 2; 84 | required string peer_id = 3; 85 | required int64 term = 4; 86 | optional bool old_leader_stepped_down = 5; 87 | } 88 | 89 | message TimeoutNowResponse { 90 | required int64 term = 1; 91 | required bool success = 2; 92 | } 93 | 94 | service RaftService { 95 | rpc pre_vote(RequestVoteRequest) returns (RequestVoteResponse); 96 | 97 | rpc request_vote(RequestVoteRequest) returns (RequestVoteResponse); 98 | 99 | rpc append_entries(AppendEntriesRequest) returns (AppendEntriesResponse); 100 | 101 | rpc install_snapshot(InstallSnapshotRequest) returns (InstallSnapshotResponse); 102 | 103 | rpc timeout_now(TimeoutNowRequest) returns (TimeoutNowResponse); 104 | }; 105 | 106 | -------------------------------------------------------------------------------- /src/braft/raft_service.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Wang,Yao(wangyao02@baidu.com) 16 | 17 | #ifndef BRAFT_RAFT_SERVICE_H 18 | #define BRAFT_RAFT_SERVICE_H 19 | 20 | #include "braft/raft.pb.h" 21 | 22 | namespace braft { 23 | 24 | class RaftServiceImpl : public RaftService { 25 | public: 26 | explicit RaftServiceImpl(butil::EndPoint addr) 27 | : _addr(addr) {} 28 | ~RaftServiceImpl(); 29 | 30 | void pre_vote(google::protobuf::RpcController* controller, 31 | const RequestVoteRequest* request, 32 | RequestVoteResponse* response, 33 | google::protobuf::Closure* done); 34 | 35 | void request_vote(google::protobuf::RpcController* controller, 36 | const RequestVoteRequest* request, 37 | RequestVoteResponse* response, 38 | google::protobuf::Closure* done); 39 | 40 | void append_entries(google::protobuf::RpcController* controller, 41 | const AppendEntriesRequest* request, 42 | AppendEntriesResponse* response, 43 | google::protobuf::Closure* done); 44 | 45 | void install_snapshot(google::protobuf::RpcController* controller, 46 | const InstallSnapshotRequest* request, 47 | InstallSnapshotResponse* response, 48 | google::protobuf::Closure* done); 49 | void timeout_now(::google::protobuf::RpcController* controller, 50 | const ::braft::TimeoutNowRequest* request, 51 | ::braft::TimeoutNowResponse* response, 52 | ::google::protobuf::Closure* done); 53 | private: 54 | butil::EndPoint _addr; 55 | }; 56 | 57 | } 58 | 59 | #endif //~BRAFT_RAFT_SERVICE_H 60 | -------------------------------------------------------------------------------- /src/braft/remote_file_copier.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Zheng,Pengfei(zhengpengfei@baidu.com) 17 | // Xiong,Kai(xiongkai@baidu.com) 18 | 19 | #ifndef BRAFT_REMOTE_FILE_COPIER_H 20 | #define BRAFT_REMOTE_FILE_COPIER_H 21 | 22 | #include 23 | #include 24 | #include "braft/file_service.pb.h" 25 | #include "braft/util.h" 26 | #include "braft/snapshot_throttle.h" 27 | 28 | namespace braft { 29 | 30 | DECLARE_bool(raft_enable_throttle_when_install_snapshot); 31 | 32 | struct CopyOptions { 33 | CopyOptions(); 34 | int max_retry; 35 | long retry_interval_ms; 36 | long timeout_ms; 37 | }; 38 | 39 | inline CopyOptions::CopyOptions() 40 | : max_retry(3) 41 | , retry_interval_ms(1000) // 1s 42 | , timeout_ms(10L * 1000) // 10s 43 | {} 44 | 45 | class FileAdaptor; 46 | class FileSystemAdaptor; 47 | class LocalSnapshotWriter; 48 | 49 | class RemoteFileCopier { 50 | public: 51 | // Stands for a copying session 52 | class Session : public butil::RefCountedThreadSafe { 53 | public: 54 | Session(); 55 | ~Session(); 56 | // Cancel the copy process 57 | void cancel(); 58 | // Wait until this file was copied from the remote reader 59 | void join(); 60 | 61 | const butil::Status& status() const { return _st; } 62 | private: 63 | friend class RemoteFileCopier; 64 | friend class Closure; 65 | struct Closure : google::protobuf::Closure { 66 | void Run() { 67 | owner->on_rpc_returned(); 68 | } 69 | Session* owner; 70 | }; 71 | void on_rpc_returned(); 72 | void send_next_rpc(); 73 | void on_finished(); 74 | static void on_timer(void* arg); 75 | static void* send_next_rpc_on_timedout(void* arg); 76 | 77 | raft_mutex_t _mutex; 78 | butil::Status _st; 79 | brpc::Channel* _channel; 80 | std::string _dest_path; 81 | FileAdaptor* _file; 82 | int _retry_times; 83 | bool _finished; 84 | brpc::CallId _rpc_call; 85 | butil::IOBuf* _buf; 86 | bthread_timer_t _timer; 87 | CopyOptions _options; 88 | Closure _done; 89 | brpc::Controller _cntl; 90 | GetFileRequest _request; 91 | GetFileResponse _response; 92 | bthread::CountdownEvent _finish_event; 93 | scoped_refptr _throttle; 94 | int64_t _throttle_token_acquire_time_us; 95 | }; 96 | 97 | RemoteFileCopier(); 98 | int init(const std::string& uri, FileSystemAdaptor* fs, 99 | SnapshotThrottle* throttle); 100 | 101 | // Copy `source' from remote to dest 102 | int copy_to_file(const std::string& source, 103 | const std::string& dest_path, 104 | const CopyOptions* options); 105 | int copy_to_iobuf(const std::string& source, 106 | butil::IOBuf* dest_buf, 107 | const CopyOptions* options); 108 | scoped_refptr start_to_copy_to_file( 109 | const std::string& source, 110 | const std::string& dest_path, 111 | const CopyOptions* options); 112 | scoped_refptr start_to_copy_to_iobuf( 113 | const std::string& source, 114 | butil::IOBuf* dest_buf, 115 | const CopyOptions* options); 116 | private: 117 | int read_piece_of_file(butil::IOBuf* buf, const std::string& source, 118 | off_t offset, size_t max_count, 119 | long timeout_ms, bool* is_eof); 120 | DISALLOW_COPY_AND_ASSIGN(RemoteFileCopier); 121 | brpc::Channel _channel; 122 | int64_t _reader_id; 123 | scoped_refptr _fs; 124 | scoped_refptr _throttle; 125 | }; 126 | 127 | } // namespace braft 128 | 129 | #endif //BRAFT_REMOTE_FILE_COPIER_H 130 | -------------------------------------------------------------------------------- /src/braft/repeated_timer_task.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | // Ma,Jingwei(majingwei@baidu.com) 17 | 18 | #ifndef BRAFT_REPEATED_TIMER_TASK_H 19 | #define BRAFT_REPEATED_TIMER_TASK_H 20 | 21 | #include 22 | #include "braft/macros.h" 23 | 24 | namespace braft { 25 | 26 | // Repeated scheduled timer task 27 | class RepeatedTimerTask{ 28 | DISALLOW_COPY_AND_ASSIGN(RepeatedTimerTask); 29 | public: 30 | RepeatedTimerTask(); 31 | virtual ~RepeatedTimerTask(); 32 | // Initialize timer task 33 | int init(int timeout_ms); 34 | 35 | // Start the timer 36 | void start(); 37 | 38 | // Run timer function once now 39 | void run_once_now(); 40 | 41 | // Stop the timer 42 | void stop(); 43 | 44 | // Reset the timer, and schedule it in the initial timeout_ms 45 | void reset(); 46 | 47 | // Reset the timer and schedule it in |timeout_ms| 48 | void reset(int timeout_ms); 49 | 50 | // Destroy the timer 51 | void destroy(); 52 | 53 | // Describe the current status of timer 54 | void describe(std::ostream& os, bool use_html); 55 | 56 | protected: 57 | 58 | // Invoked everytime when it reaches the timeout 59 | virtual void run() = 0; 60 | 61 | // Invoked when the timer is finally destroyed 62 | virtual void on_destroy() = 0; 63 | 64 | virtual int adjust_timeout_ms(int timeout_ms) { 65 | return timeout_ms; 66 | } 67 | 68 | private: 69 | 70 | static void on_timedout(void* arg); 71 | static void* run_on_timedout_in_new_thread(void* arg); 72 | void on_timedout(); 73 | void schedule(std::unique_lock& lck); 74 | 75 | raft_mutex_t _mutex; 76 | bthread_timer_t _timer; 77 | timespec _next_duetime; 78 | int _timeout_ms; 79 | bool _stopped; 80 | bool _running; 81 | bool _destroyed; 82 | bool _invoking; 83 | }; 84 | 85 | } // namespace braft 86 | 87 | #endif //BRAFT_REPEATED_TIMER_TASK_H 88 | -------------------------------------------------------------------------------- /src/braft/route_table.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen (chenzhangyi01@baidu.com) 16 | 17 | #ifndef BRAFT_ROUTE_TABLE_H 18 | #define BRAFT_ROUTE_TABLE_H 19 | 20 | #include "braft/configuration.h" // Configuration 21 | #include "braft/raft.h" 22 | 23 | // Maintain routes to raft groups 24 | 25 | namespace braft { 26 | namespace rtb { 27 | 28 | // Update configuration of group in route table 29 | int update_configuration(const GroupId& group, const Configuration& conf); 30 | int update_configuration(const GroupId& group, const std::string& conf_str); 31 | 32 | // Get the cached leader of group. 33 | // Returns: 34 | // 0 : success 35 | // 1 : Not sure about the leader 36 | // -1, otherwise 37 | int select_leader(const GroupId& group, PeerId* leader); 38 | 39 | // Update leader 40 | int update_leader(const GroupId& group, const PeerId& leader); 41 | int update_leader(const GroupId& group, const std::string& leader_str); 42 | 43 | // Blocking the thread until query_leader finishes 44 | butil::Status refresh_leader(const GroupId& group, int timeout_ms); 45 | 46 | // Remove this group from route table 47 | int remove_group(const GroupId& group); 48 | 49 | } // namespace rtb 50 | } // namespace braft 51 | 52 | #endif //BRAFT_ROUTE_TABLE_H 53 | -------------------------------------------------------------------------------- /src/braft/snapshot_throttle.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Xiong,Kai(xiongkai@baidu.com) 16 | 17 | #ifndef BRAFT_SNAPSHOT_THROTTLE_H 18 | #define BRAFT_SNAPSHOT_THROTTLE_H 19 | 20 | #include // butil::RefCountedThreadSafe 21 | #include "braft/util.h" 22 | 23 | namespace braft { 24 | 25 | // Abstract class with the function of throttling during heavy disk reading/writing 26 | class SnapshotThrottle : public butil::RefCountedThreadSafe { 27 | public: 28 | SnapshotThrottle() {} 29 | virtual ~SnapshotThrottle() {} 30 | // Get available throughput after throttled 31 | // Must be thread-safe 32 | virtual size_t throttled_by_throughput(int64_t bytes) = 0; 33 | virtual bool add_one_more_task(bool is_leader) = 0; 34 | virtual void finish_one_task(bool is_leader) = 0; 35 | virtual int64_t get_retry_interval_ms() = 0; 36 | 37 | // After a throttled request finish, |return_unused_throughput| is called to 38 | // return back unsed tokens (throghput). Default implementation do nothing. 39 | // There are two situations we can optimize: 40 | // case 1: The follower and leader both try to throttle the same request, and 41 | // only one of them permit the request. No actual IO and bandwidth consumed, 42 | // the acquired tokens are wasted. 43 | // case 2: We acquired some tokens, but only part of them are used, because of 44 | // the file reach the eof, or the file contains holes. 45 | virtual void return_unused_throughput( 46 | int64_t acquired, int64_t consumed, int64_t elaspe_time_us) {} 47 | private: 48 | DISALLOW_COPY_AND_ASSIGN(SnapshotThrottle); 49 | friend class butil::RefCountedThreadSafe; 50 | }; 51 | 52 | // SnapshotThrottle with throughput threshold used in install_snapshot 53 | class ThroughputSnapshotThrottle : public SnapshotThrottle { 54 | public: 55 | ThroughputSnapshotThrottle(int64_t throttle_throughput_bytes, int64_t check_cycle); 56 | int64_t get_throughput() const { return _throttle_throughput_bytes; } 57 | int64_t get_cycle() const { return _check_cycle; } 58 | size_t throttled_by_throughput(int64_t bytes); 59 | bool add_one_more_task(bool is_leader); 60 | void finish_one_task(bool is_leader); 61 | int64_t get_retry_interval_ms() { return 1000 / _check_cycle + 1;} 62 | void return_unused_throughput( 63 | int64_t acquired, int64_t consumed, int64_t elaspe_time_us); 64 | 65 | private: 66 | ~ThroughputSnapshotThrottle(); 67 | // user defined throughput threshold for raft, bytes per second 68 | int64_t _throttle_throughput_bytes; 69 | // user defined check cycles of throughput per second 70 | int64_t _check_cycle; 71 | // the num of tasks doing install_snapshot 72 | int _snapshot_task_num; 73 | int64_t _last_throughput_check_time_us; 74 | int64_t _cur_throughput_bytes; 75 | raft_mutex_t _mutex; 76 | }; 77 | 78 | inline int64_t caculate_check_time_us(int64_t current_time_us, 79 | int64_t check_cycle) { 80 | int64_t base_aligning_time_us = 1000 * 1000 / check_cycle; 81 | return current_time_us / base_aligning_time_us * base_aligning_time_us; 82 | } 83 | 84 | } // namespace braft 85 | 86 | #endif // BRAFT_SNAPSHOT_THROTTLE_H 87 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Gperftools) 2 | include_directories(${GPERFTOOLS_INCLUDE_DIR}) 3 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 4 | include_directories(${CMAKE_SOURCE_DIR}/test) 5 | 6 | find_path(GTEST_HEADER NAMES gtest/gtest.h) 7 | find_library(GTEST_LIB NAMES gtest) 8 | find_library(GTEST_MAIN_LIB NAMES gtest_main) 9 | 10 | set(CMAKE_CPP_FLAGS "-DGFLAGS_NS=${GFLAGS_NS}") 11 | set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -D__const__=__unused__ -D_GNU_SOURCE -DUSE_SYMBOLIZE -DNO_TCMALLOC -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -DUNIT_TEST -g -Dprivate=public -Dprotected=public -D__STRICT_ANSI__ -include sstream_workaround.h") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CPP_FLAGS} -O2 -pipe -Wall -W -fPIC -fstrict-aliasing -Wno-invalid-offsetof -Wno-unused-parameter -fno-omit-frame-pointer -Wno-unused-result") 13 | use_cxx11() 14 | 15 | # bthread_* functions are used in logging.cc, and they need to be marked as 16 | # weak symbols explicitly in Darwin system. 17 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 18 | set(DYNAMIC_LIB ${DYNAMIC_LIB} 19 | "-Wl,-U,_bthread_getspecific" 20 | "-Wl,-U,_bthread_setspecific" 21 | "-Wl,-U,_bthread_key_create") 22 | endif() 23 | 24 | file(GLOB TEST_BRAFT_SRCS "test_*.cpp") 25 | foreach(BRAFT_UT ${TEST_BRAFT_SRCS}) 26 | get_filename_component(BRAFT_UT_WE ${BRAFT_UT} NAME_WE) 27 | add_executable(${BRAFT_UT_WE} ${BRAFT_UT} 28 | $) 29 | target_link_libraries(${BRAFT_UT_WE} 30 | #"-Xlinker \"-(\"" 31 | ${GTEST_MAIN_LIB} 32 | ${GTEST_LIB} 33 | ${GPERFTOOLS_LIBRARY} 34 | ${DYNAMIC_LIB} 35 | # "-Xlinker \"-)\"" 36 | ) 37 | endforeach() 38 | -------------------------------------------------------------------------------- /test/run_tests.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash -f 2 | test_num=0 3 | failed_test="" 4 | rc=0 5 | test_bins="test_*" 6 | for test_bin in $test_bins; do 7 | test_num=$((test_num + 1)) 8 | >&2 echo "[runtest] $test_bin" 9 | ./$test_bin 10 | rc=$? 11 | if [ $rc -ne 0 ]; then 12 | failed_test="$test_bin" 13 | break; 14 | fi 15 | done 16 | if [ $test_num -eq 0 ]; then 17 | >&2 echo "[runtest] Cannot find any tests" 18 | exit 1 19 | fi 20 | if [ -z "$failed_test" ]; then 21 | >&2 echo "[runtest] $test_num succeeded" 22 | elif [ $test_num -gt 1 ]; then 23 | >&2 echo "[runtest] '$failed_test' failed, $((test_num-1)) succeeded" 24 | else 25 | >&2 echo "[runtest] '$failed_test' failed" 26 | fi 27 | exit $rc 28 | -------------------------------------------------------------------------------- /test/sstream_workaround.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Baidu, Inc. 2 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 3 | 4 | #ifndef BUTIL_TEST_SSTREAM_WORKAROUND 5 | #define BUTIL_TEST_SSTREAM_WORKAROUND 6 | 7 | // defining private as public makes it fail to compile sstream with gcc5.x like this: 8 | // "error: ‘struct std::__cxx11::basic_stringbuf<_CharT, _Traits, _Alloc>:: 9 | // __xfer_bufptrs’ redeclared with different access" 10 | 11 | #ifdef private 12 | # undef private 13 | # include 14 | # define private public 15 | #else 16 | # include 17 | #endif 18 | 19 | #endif // BUTIL_TEST_SSTREAM_WORKAROUND 20 | -------------------------------------------------------------------------------- /test/test_ballot.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Authors: Zhangyi Chen(chenzhangyi01@baidu.com) 16 | 17 | #include 18 | #include "braft/ballot.h" 19 | 20 | class BallotTest : public testing::Test {}; 21 | 22 | TEST(BallotTest, sanity) { 23 | braft::PeerId peer1("127.0.0.1:1"); 24 | braft::PeerId peer2("127.0.0.1:2"); 25 | braft::PeerId peer3("127.0.0.1:3"); 26 | braft::PeerId peer4("127.0.0.1:4"); 27 | braft::Configuration conf; 28 | conf.add_peer(peer1); 29 | conf.add_peer(peer2); 30 | conf.add_peer(peer3); 31 | braft::Ballot bl; 32 | ASSERT_EQ(0, bl.init(conf, NULL)); 33 | ASSERT_EQ(2, bl._quorum); 34 | ASSERT_EQ(0, bl._old_quorum); 35 | bl.grant(peer1); 36 | ASSERT_EQ(1, bl._quorum); 37 | braft::Ballot::PosHint hint = bl.grant(peer1, braft::Ballot::PosHint()); 38 | ASSERT_EQ(1, bl._quorum); 39 | hint = bl.grant(peer1, hint); 40 | ASSERT_EQ(1, bl._quorum); 41 | hint = bl.grant(peer4, hint); 42 | ASSERT_EQ(1, bl._quorum); 43 | hint = bl.grant(peer2, hint); 44 | ASSERT_TRUE(bl.granted()); 45 | } 46 | 47 | TEST(BallotTest, joint_consensus_same_conf) { 48 | braft::PeerId peer1("127.0.0.1:1"); 49 | braft::PeerId peer2("127.0.0.1:2"); 50 | braft::PeerId peer3("127.0.0.1:3"); 51 | braft::PeerId peer4("127.0.0.1:4"); 52 | braft::Configuration conf; 53 | conf.add_peer(peer1); 54 | conf.add_peer(peer2); 55 | conf.add_peer(peer3); 56 | braft::Ballot bl; 57 | ASSERT_EQ(0, bl.init(conf, &conf)); 58 | ASSERT_EQ(2, bl._quorum); 59 | ASSERT_EQ(2, bl._old_quorum); 60 | bl.grant(peer1); 61 | ASSERT_EQ(1, bl._quorum); 62 | ASSERT_EQ(1, bl._old_quorum); 63 | braft::Ballot::PosHint hint = bl.grant(peer1, braft::Ballot::PosHint()); 64 | ASSERT_EQ(1, bl._quorum); 65 | ASSERT_EQ(1, bl._old_quorum); 66 | hint = bl.grant(peer1, hint); 67 | ASSERT_EQ(1, bl._quorum); 68 | ASSERT_EQ(1, bl._old_quorum); 69 | hint = bl.grant(peer4, hint); 70 | ASSERT_EQ(1, bl._quorum); 71 | ASSERT_EQ(1, bl._old_quorum); 72 | ASSERT_FALSE(bl.granted()); 73 | hint = bl.grant(peer2, hint); 74 | ASSERT_TRUE(bl.granted()); 75 | hint = bl.grant(peer3, hint); 76 | ASSERT_EQ(-1, bl._quorum); 77 | ASSERT_EQ(-1, bl._old_quorum); 78 | } 79 | 80 | TEST(BallotTest, joint_consensus_different_conf) { 81 | braft::PeerId peer1("127.0.0.1:1"); 82 | braft::PeerId peer2("127.0.0.1:2"); 83 | braft::PeerId peer3("127.0.0.1:3"); 84 | braft::PeerId peer4("127.0.0.1:4"); 85 | braft::Configuration conf; 86 | conf.add_peer(peer1); 87 | conf.add_peer(peer2); 88 | conf.add_peer(peer3); 89 | braft::Configuration conf2; 90 | conf2.add_peer(peer1); 91 | conf2.add_peer(peer2); 92 | conf2.add_peer(peer3); 93 | conf2.add_peer(peer4); 94 | braft::Ballot bl; 95 | ASSERT_EQ(0, bl.init(conf, &conf2)); 96 | bl.grant(peer1); 97 | bl.grant(peer2); 98 | ASSERT_FALSE(bl.granted()); 99 | ASSERT_EQ(0, bl._quorum); 100 | ASSERT_EQ(1, bl._old_quorum); 101 | bl.grant(peer4); 102 | ASSERT_TRUE(bl.granted()); 103 | } 104 | -------------------------------------------------------------------------------- /test/test_checksum.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | 3 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 4 | // Date: 2016/04/11 12:15:37 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class ChecksumTest : public testing::Test { 11 | protected: 12 | void SetUp() {} 13 | void TearDown() {} 14 | }; 15 | 16 | TEST_F(ChecksumTest, benchmark) { 17 | char data[4096]; 18 | for (size_t i = 0; i < ARRAY_SIZE(data); ++i) { 19 | data[i] = butil::fast_rand_in('a', 'z'); 20 | } 21 | butil::Timer timer; 22 | const size_t N = 10000; 23 | timer.start(); 24 | for (size_t i = 0; i < N; ++i) { 25 | braft::murmurhash32(data, sizeof(data)); 26 | } 27 | timer.stop(); 28 | const long mur_elp = timer.u_elapsed(); 29 | 30 | timer.start(); 31 | for (size_t i = 0; i < N; ++i) { 32 | butil::crc32c::Value(data, sizeof(data)); 33 | } 34 | timer.stop(); 35 | const long crc_elp = timer.u_elapsed(); 36 | 37 | LOG(INFO) << "murmurhash32_TP=" << sizeof(data) * N / (double)mur_elp << "MB/s" 38 | << " base_crc32_TP=" << sizeof(data) * N / (double)crc_elp << "MB/s"; 39 | LOG(INFO) << "base_is_fast_crc32_support=" << butil::crc32c::IsFastCrc32Supported(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /test/test_configuration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================================================== 3 | * 4 | * Filename: test_configuration.cpp 5 | * 6 | * Description: 7 | * 8 | * Version: 1.0 9 | * Created: 2015年10月22日 15时16分31秒 10 | * Revision: none 11 | * Compiler: gcc 12 | * 13 | * Author: WangYao (fisherman), wangyao02@baidu.com 14 | * Company: Baidu, Inc 15 | * 16 | * ===================================================================================== 17 | */ 18 | 19 | #include 20 | #include 21 | #include "braft/raft.h" 22 | #include "braft/configuration_manager.h" 23 | 24 | class TestUsageSuits : public testing::Test { 25 | protected: 26 | void SetUp() {} 27 | void TearDown() {} 28 | }; 29 | 30 | TEST_F(TestUsageSuits, PeerId) { 31 | braft::PeerId id1; 32 | ASSERT_TRUE(id1.is_empty()); 33 | 34 | ASSERT_NE(0, id1.parse("1.1.1.1::")); 35 | ASSERT_TRUE(id1.is_empty()); 36 | 37 | ASSERT_EQ(0, id1.parse("1.1.1.1:1000:")); 38 | LOG(INFO) << "id:" << id1.to_string(); 39 | LOG(INFO) << "id:" << id1; 40 | 41 | ASSERT_EQ(0, id1.parse("1.1.1.1:1000:0")); 42 | LOG(INFO) << "id:" << id1.to_string(); 43 | LOG(INFO) << "id:" << id1; 44 | 45 | ASSERT_EQ(0, id1.parse("1.1.1.1:1000:0:0")); 46 | LOG(INFO) << "id:" << id1.to_string(); 47 | LOG(INFO) << "id:" << id1; 48 | ASSERT_FALSE(id1.is_witness()); 49 | 50 | ASSERT_EQ(0, id1.parse("1.1.1.1:1000:0:1")); 51 | LOG(INFO) << "id:" << id1.to_string(); 52 | LOG(INFO) << "id:" << id1; 53 | ASSERT_TRUE(id1.is_witness()); 54 | 55 | ASSERT_EQ(-1, id1.parse("1.1.1.1:1000:0:2")); 56 | 57 | ASSERT_EQ(0, id1.parse("1.1.1.1:1000")); 58 | LOG(INFO) << "id:" << id1.to_string(); 59 | LOG(INFO) << "id:" << id1; 60 | 61 | braft::PeerId id2(id1); 62 | LOG(INFO) << "id:" << id2; 63 | 64 | braft::PeerId id3("1.2.3.4:1000:0"); 65 | LOG(INFO) << "id:" << id3; 66 | } 67 | 68 | TEST_F(TestUsageSuits, Configuration) { 69 | braft::Configuration conf1; 70 | ASSERT_TRUE(conf1.empty()); 71 | std::vector peers; 72 | peers.push_back(braft::PeerId("1.1.1.1:1000:0")); 73 | peers.push_back(braft::PeerId("1.1.1.1:1000:1")); 74 | peers.push_back(braft::PeerId("1.1.1.1:1000:2")); 75 | conf1 = peers; 76 | LOG(INFO) << conf1; 77 | 78 | ASSERT_TRUE(conf1.contains(braft::PeerId("1.1.1.1:1000:0"))); 79 | ASSERT_FALSE(conf1.contains(braft::PeerId("1.1.1.1:2000:0"))); 80 | 81 | std::vector peers2; 82 | peers2.push_back(braft::PeerId("1.1.1.1:1000:0")); 83 | peers2.push_back(braft::PeerId("1.1.1.1:1000:1")); 84 | ASSERT_TRUE(conf1.contains(peers2)); 85 | peers2.push_back(braft::PeerId("1.1.1.1:2000:1")); 86 | ASSERT_FALSE(conf1.contains(peers2)); 87 | 88 | ASSERT_FALSE(conf1.equals(peers2)); 89 | ASSERT_TRUE(conf1.equals(peers)); 90 | 91 | braft::Configuration conf2(peers); 92 | conf2.remove_peer(braft::PeerId("1.1.1.1:1000:1")); 93 | conf2.add_peer(braft::PeerId("1.1.1.1:1000:3")); 94 | ASSERT_FALSE(conf2.contains(braft::PeerId("1.1.1.1:1000:1"))); 95 | ASSERT_TRUE(conf2.contains(braft::PeerId("1.1.1.1:1000:3"))); 96 | 97 | std::set peer_set; 98 | conf2.list_peers(&peer_set); 99 | ASSERT_EQ(peer_set.size(), 3); 100 | std::vector peer_vector; 101 | conf2.list_peers(&peer_vector); 102 | ASSERT_EQ(peer_vector.size(), 3); 103 | } 104 | 105 | TEST_F(TestUsageSuits, ConfigurationManager) { 106 | braft::ConfigurationManager conf_manager; 107 | 108 | braft::ConfigurationEntry it1; 109 | conf_manager.get(10, &it1); 110 | ASSERT_EQ(it1.id, braft::LogId(0, 0)); 111 | ASSERT_TRUE(it1.conf.empty()); 112 | ASSERT_EQ(braft::LogId(0, 0), conf_manager.last_configuration().id); 113 | braft::ConfigurationEntry entry; 114 | std::vector peers; 115 | peers.push_back(braft::PeerId("1.1.1.1:1000:0")); 116 | peers.push_back(braft::PeerId("1.1.1.1:1000:1")); 117 | peers.push_back(braft::PeerId("1.1.1.1:1000:2")); 118 | entry.conf = peers; 119 | entry.id = braft::LogId(8, 1); 120 | conf_manager.add(entry); 121 | ASSERT_EQ(braft::LogId(8, 1), conf_manager.last_configuration().id); 122 | 123 | conf_manager.get(10, &it1); 124 | ASSERT_EQ(it1.id, entry.id); 125 | 126 | conf_manager.truncate_suffix(7); 127 | ASSERT_EQ(braft::LogId(0, 0), conf_manager.last_configuration().id); 128 | 129 | entry.id = braft::LogId(10, 1); 130 | entry.conf = peers; 131 | conf_manager.add(entry); 132 | peers.push_back(braft::PeerId("1.1.1.1:1000:3")); 133 | entry.id = braft::LogId(20, 1); 134 | entry.conf = peers; 135 | conf_manager.add(entry); 136 | ASSERT_EQ(braft::LogId(20, 1), conf_manager.last_configuration().id); 137 | 138 | conf_manager.truncate_prefix(15); 139 | ASSERT_EQ(braft::LogId(20, 1), conf_manager.last_configuration().id); 140 | 141 | conf_manager.truncate_prefix(25); 142 | ASSERT_EQ(braft::LogId(0, 0), conf_manager.last_configuration().id); 143 | 144 | } 145 | -------------------------------------------------------------------------------- /test/test_file_service.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Baidu.com, Inc. All Rights Reserved 2 | 3 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 4 | // Date: 2015/11/06 15:40:45 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include "braft/file_service.h" 13 | #include "braft/util.h" 14 | #include "braft/remote_file_copier.h" 15 | #include "braft/file_system_adaptor.h" 16 | 17 | namespace braft { 18 | DECLARE_bool(raft_file_check_hole); 19 | } 20 | 21 | int g_port = 0; 22 | class FileServiceTest : public testing::Test { 23 | protected: 24 | void SetUp() { 25 | ASSERT_EQ(0, _server.AddService(braft::file_service(), 26 | brpc::SERVER_DOESNT_OWN_SERVICE)); 27 | for (int i = 10000; i < 60000; i++) { 28 | if (0 == _server.Start(i, NULL)) { 29 | g_port = i; 30 | break; 31 | } 32 | } 33 | ASSERT_NE(0, g_port); 34 | } 35 | void TearDown() { 36 | _server.Stop(0); 37 | _server.Join(); 38 | } 39 | brpc::Server _server; 40 | }; 41 | 42 | TEST_F(FileServiceTest, sanity) { 43 | braft::FileSystemAdaptor* fs = braft::default_file_system(); 44 | scoped_refptr reader(new braft::LocalDirReader(fs, "a")); 45 | int64_t reader_id = 0; 46 | ASSERT_EQ(0, braft::file_service_add(reader.get(), &reader_id)); 47 | std::string uri; 48 | butil::string_printf(&uri, "remote://127.0.0.1:%d/%" PRId64, g_port, reader_id); 49 | braft::RemoteFileCopier copier; 50 | { 51 | std::string bad_uri; 52 | butil::string_printf(&bad_uri, "local://127.0.0.1:%d/123456", g_port); 53 | ASSERT_NE(0, copier.init(bad_uri, fs, NULL)); 54 | 55 | bad_uri.clear(); 56 | butil::string_printf(&bad_uri, "remote://127.0.0.1:%d//123456", g_port); 57 | ASSERT_NE(0, copier.init(bad_uri, fs, NULL)); 58 | 59 | bad_uri.clear(); 60 | butil::string_printf(&bad_uri, "remote://127.0.1:%d//123456", g_port); 61 | ASSERT_NE(0, copier.init(bad_uri, fs, NULL)); 62 | 63 | ASSERT_NE(0, copier.init("remote://127.0.0.1//123456", fs, NULL)); 64 | } 65 | ASSERT_EQ(0, copier.init(uri, fs, NULL)); 66 | 67 | // normal copy dir 68 | system("chmod -R 755 ./a; chmod -R 755 ./b"); 69 | ASSERT_EQ(0, system("rm -rf a; rm -rf b; mkdir a; mkdir a/b; echo '123' > a/c")); 70 | ASSERT_TRUE(butil::CreateDirectory(butil::FilePath("./b"))); 71 | ASSERT_EQ(0, copier.copy_to_file("c", "./b/c", NULL)); 72 | butil::IOBuf c_data; 73 | ASSERT_EQ(0, copier.copy_to_iobuf("c", &c_data, NULL)); 74 | ASSERT_TRUE(c_data.equals("123\n")) << c_data.to_string(); 75 | // Copy Directory is not allowed 76 | ASSERT_NE(0, copier.copy_to_file("b", "./b/b", NULL)); 77 | 78 | // Copy non-existed file 79 | ASSERT_NE(0, copier.copy_to_file("d", "./b/d", NULL)); 80 | 81 | // src no permission read 82 | ASSERT_EQ(0, system("chmod 000 a/c")); 83 | ASSERT_NE(0, copier.copy_to_file("c", "./b/cc", NULL)); 84 | ASSERT_EQ(0, system("chmod -R 755 ./a")); 85 | 86 | ASSERT_EQ(0, braft::file_service_remove(reader_id)); 87 | 88 | // Copy after reader is remove 89 | ASSERT_NE(0, copier.copy_to_file("c", "./b/d", NULL)); 90 | ASSERT_EQ(0, system("rm -rf a; rm -rf b;")); 91 | } 92 | 93 | TEST_F(FileServiceTest, hole_file) { 94 | int ret = 0; 95 | ASSERT_EQ(0, system("rm -rf a; rm -rf b; rm -rf c; mkdir a;")); 96 | 97 | LOG(INFO) << "build hole file"; 98 | int fd = ::open("./a/hole.data", O_CREAT | O_TRUNC | O_WRONLY, 0644); 99 | ASSERT_GE(fd, 0); 100 | for (int i = 0; i < 1000; i++) { 101 | char buf[16*1024] = {0}; 102 | snprintf(buf, sizeof(buf), "hello %d", i); 103 | ssize_t nwritten = pwrite(fd, buf, strlen(buf), 128 * 1024 * i); 104 | ASSERT_EQ(static_cast(nwritten), strlen(buf)); 105 | } 106 | ::close(fd); 107 | braft::FileSystemAdaptor* fs = braft::default_file_system(); 108 | scoped_refptr reader(new braft::LocalDirReader(fs, "a")); 109 | int64_t reader_id = 0; 110 | ASSERT_EQ(0, braft::file_service_add(reader.get(), &reader_id)); 111 | 112 | braft::RemoteFileCopier copier; 113 | std::string uri; 114 | butil::string_printf(&uri, "remote://127.0.0.1:%d/%" PRId64, g_port, reader_id); 115 | // normal init 116 | braft::FLAGS_raft_file_check_hole = false; 117 | ASSERT_EQ(0, copier.init(uri, fs, NULL)); 118 | ASSERT_TRUE(butil::CreateDirectory(butil::FilePath("./b"))); 119 | ASSERT_EQ(0, copier.copy_to_file("hole.data", "./b/hole.data", NULL)); 120 | ret = system("diff ./a/hole.data ./b/hole.data"); 121 | ASSERT_EQ(0, ret); 122 | 123 | braft::FLAGS_raft_file_check_hole = true; 124 | ASSERT_TRUE(butil::CreateDirectory(butil::FilePath("./c"))); 125 | ASSERT_EQ(0, copier.copy_to_file("hole.data", "./c/hole.data", NULL)); 126 | ret = system("diff ./a/hole.data ./c/hole.data"); 127 | ASSERT_EQ(0, ret); 128 | } 129 | -------------------------------------------------------------------------------- /test/test_fsync.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | 3 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 4 | // Date: 2016/02/23 16:22:15 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class FsyncTest : public testing::Test { 16 | }; 17 | 18 | TEST_F(FsyncTest, benchmark_append) { 19 | butil::fd_guard fd(::open("fsync.data", O_RDWR | O_CREAT | O_TRUNC, 0644)); 20 | ASSERT_NE(-1, fd); 21 | char buf[1024]; 22 | butil::Timer timer; 23 | const size_t N = 1000; 24 | timer.start(); 25 | for (size_t i = 0; i < N; ++i) { 26 | int left = 1024; 27 | while (left > 0) { 28 | ssize_t nw = write(fd, buf, left); 29 | ASSERT_NE(-1, nw); 30 | left -= nw; 31 | } 32 | fsync(fd); 33 | } 34 | timer.stop(); 35 | LOG(INFO) << "fsync takes " << timer.u_elapsed(); 36 | timer.start(); 37 | fd.reset(-1); 38 | fd.reset(::open("fsync.data", O_RDWR | O_CREAT | O_TRUNC, 0644)); 39 | for (size_t i = 0; i < N; ++i) { 40 | int left = 1024; 41 | while (left > 0) { 42 | ssize_t nw = write(fd, buf, left); 43 | ASSERT_NE(-1, nw); 44 | left -= nw; 45 | } 46 | #ifdef __APPLE__ 47 | fcntl(fd, F_FULLFSYNC); 48 | #else 49 | fdatasync(fd); 50 | #endif 51 | } 52 | timer.stop(); 53 | LOG(INFO) << "fdatasync takes " << timer.u_elapsed(); 54 | } 55 | 56 | TEST_F(FsyncTest, benchmark_randomly_write) { 57 | } 58 | -------------------------------------------------------------------------------- /test/test_log_entry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================================================== 3 | * 4 | * Filename: test_log_entry.cpp 5 | * 6 | * Description: 7 | * 8 | * Version: 1.0 9 | * Created: 2015年11月27日 11时11分35秒 10 | * Revision: none 11 | * Compiler: gcc 12 | * 13 | * Author: WangYao (fisherman), wangyao02@baidu.com 14 | * Company: Baidu, Inc 15 | * 16 | * ===================================================================================== 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "braft/log_entry.h" 23 | 24 | class TestUsageSuits : public testing::Test { 25 | protected: 26 | void SetUp() {} 27 | void TearDown() {} 28 | }; 29 | 30 | TEST_F(TestUsageSuits, LogEntry) { 31 | braft::LogEntry* entry = new braft::LogEntry(); 32 | std::vector peers; 33 | peers.push_back(braft::PeerId("1.2.3.4:1000")); 34 | peers.push_back(braft::PeerId("1.2.3.4:2000")); 35 | peers.push_back(braft::PeerId("1.2.3.4:3000")); 36 | entry->type = braft::ENTRY_TYPE_CONFIGURATION; 37 | entry->peers = new std::vector(peers); 38 | 39 | entry->AddRef(); 40 | entry->Release(); 41 | entry->Release(); 42 | 43 | entry = new braft::LogEntry(); 44 | entry->type = braft::ENTRY_TYPE_DATA; 45 | butil::IOBuf buf; 46 | buf.append("hello, world"); 47 | entry->data = buf; 48 | entry->data = buf; 49 | 50 | entry->Release(); 51 | } 52 | -------------------------------------------------------------------------------- /test/test_memory_storage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | 3 | // Author: qinduohao@baidu.com 4 | // Date: 2017/05/23 5 | 6 | #include 7 | #include "braft/memory_log.h" 8 | 9 | namespace braft { 10 | extern void global_init_once_or_die(); 11 | }; 12 | 13 | class MemStorageTest : public testing::Test { 14 | protected: 15 | void SetUp() { 16 | system("rm -rf data"); 17 | braft::global_init_once_or_die(); 18 | } 19 | void TearDown() {} 20 | }; 21 | 22 | TEST_F(MemStorageTest, init) { 23 | braft::LogStorage* log_storage = braft::LogStorage::create("memory://data/log"); 24 | ASSERT_TRUE(log_storage); 25 | braft::ConfigurationManager cm; 26 | ASSERT_EQ(0, log_storage->init(&cm)); 27 | ASSERT_FALSE(braft::LogStorage::create("hdfs://data/log")); 28 | ASSERT_FALSE(braft::LogStorage::create("://data/log")); 29 | ASSERT_FALSE(braft::LogStorage::create("data/log")); 30 | ASSERT_FALSE(braft::LogStorage::create(" ://data/log")); 31 | } 32 | 33 | TEST_F(MemStorageTest, entry_operation) { 34 | braft::LogStorage* log_storage = braft::LogStorage::create("memory://data/log"); 35 | ASSERT_TRUE(log_storage); 36 | braft::ConfigurationManager cm; 37 | ASSERT_EQ(0, log_storage->init(&cm)); 38 | braft::LogEntry* entry = new braft::LogEntry(); 39 | entry->data.append("hello world"); 40 | entry->id = braft::LogId(1, 1); 41 | entry->type = braft::ENTRY_TYPE_DATA; 42 | std::vector entries; 43 | entries.push_back(entry); 44 | ASSERT_EQ(1u, log_storage->append_entries(entries, NULL)); 45 | 46 | ASSERT_EQ(1, log_storage->first_log_index()); 47 | ASSERT_EQ(1, log_storage->last_log_index()); 48 | entry = log_storage->get_entry(1); 49 | ASSERT_TRUE(entry); 50 | ASSERT_EQ("hello world", entry->data.to_string()); 51 | ASSERT_EQ(braft::LogId(1, 1), entry->id); 52 | int64_t term = log_storage->get_term(1); 53 | ASSERT_EQ(1, term); 54 | entry->Release(); 55 | entry = NULL; 56 | 57 | ASSERT_EQ(0, log_storage->reset(10)); 58 | ASSERT_EQ(10, log_storage->first_log_index()); 59 | ASSERT_EQ(9, log_storage->last_log_index()); 60 | delete log_storage; 61 | } 62 | 63 | TEST_F(MemStorageTest, trunk_operation) { 64 | braft::LogStorage* log_storage = braft::LogStorage::create("memory://data/log"); 65 | ASSERT_TRUE(log_storage); 66 | braft::ConfigurationManager cm; 67 | ASSERT_EQ(0, log_storage->init(&cm)); 68 | std::vector entries; 69 | braft::LogEntry* entry = new braft::LogEntry(); 70 | entry->data.append("hello world"); 71 | entry->id = braft::LogId(1, 1); 72 | entry->type = braft::ENTRY_TYPE_DATA; 73 | entries.push_back(entry); 74 | braft::LogEntry* entry1 = new braft::LogEntry(); 75 | entry1->data.append("hello world"); 76 | entry1->id = braft::LogId(2, 1); 77 | entry1->type = braft::ENTRY_TYPE_DATA; 78 | entries.push_back(entry1); 79 | braft::LogEntry* entry2 = new braft::LogEntry(); 80 | entry2->data.append("hello world"); 81 | entry2->id = braft::LogId(3, 1); 82 | entry2->type = braft::ENTRY_TYPE_DATA; 83 | entries.push_back(entry2); 84 | ASSERT_EQ(3u, log_storage->append_entries(entries, NULL)); 85 | 86 | size_t ret = log_storage->truncate_suffix(2); 87 | ASSERT_EQ(0, ret); 88 | ASSERT_EQ(1, log_storage->first_log_index()); 89 | ASSERT_EQ(2, log_storage->last_log_index()); 90 | 91 | ret = log_storage->truncate_prefix(2); 92 | ASSERT_EQ(0, ret); 93 | ASSERT_EQ(2, log_storage->first_log_index()); 94 | ASSERT_EQ(2, log_storage->last_log_index()); 95 | entry1->Release(); 96 | delete log_storage; 97 | } 98 | -------------------------------------------------------------------------------- /test/test_protobuf_file.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================================================== 3 | * 4 | * Filename: test_protobuf_file.cpp 5 | * 6 | * Description: 7 | * 8 | * Version: 1.0 9 | * Created: 2015/09/22 19:48:31 10 | * Revision: none 11 | * Compiler: gcc 12 | * 13 | * Author: WangYao (fisherman), wangyao02@baidu.com 14 | * Company: Baidu, Inc 15 | * 16 | * ===================================================================================== 17 | */ 18 | 19 | #include 20 | #include "braft/local_storage.pb.h" 21 | #include "braft/protobuf_file.h" 22 | 23 | class TestUsageSuits : public testing::Test { 24 | protected: 25 | void SetUp() {} 26 | void TearDown() {} 27 | }; 28 | 29 | TEST_F(TestUsageSuits, protobuf_file) { 30 | int ret = 0; 31 | 32 | braft::ProtoBufFile pb_file("./log.meta"); 33 | braft::LogPBMeta meta; 34 | meta.set_first_log_index(1234); 35 | 36 | ret = pb_file.save(static_cast(&meta), false); 37 | ASSERT_EQ(ret, 0); 38 | 39 | { 40 | braft::LogPBMeta new_meta; 41 | ret = pb_file.load(&new_meta); 42 | ASSERT_EQ(ret, 0); 43 | 44 | ASSERT_EQ(new_meta.first_log_index(), 1234); 45 | } 46 | 47 | ret = pb_file.save(&meta, true); 48 | ASSERT_EQ(ret, 0); 49 | 50 | { 51 | braft::LogPBMeta new_meta; 52 | ret = pb_file.load(&new_meta); 53 | ASSERT_EQ(ret, 0); 54 | 55 | ASSERT_EQ(new_meta.first_log_index(), 1234); 56 | 57 | new_meta.PrintDebugString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/test_repeated_timer_task.cpp: -------------------------------------------------------------------------------- 1 | // libraft - Quorum-based replication of states across machines. 2 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 3 | 4 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 5 | // Date: 2016/11/02 21:12:26 6 | 7 | #include 8 | #include "braft/repeated_timer_task.h" 9 | 10 | class RepeatedTimerTaskTest : public testing::Test { 11 | }; 12 | 13 | class MockTimer : public braft::RepeatedTimerTask { 14 | protected: 15 | MockTimer() 16 | : _blocking(false) 17 | , _run_times(0) 18 | , _on_destroy_times(0) 19 | , _adjust_timeout_times(0) 20 | {} 21 | ~MockTimer() { destroy(); } 22 | 23 | void run() { 24 | ++_run_times; 25 | while (_blocking) { 26 | usleep(100); 27 | } 28 | } 29 | 30 | void on_destroy() { 31 | ++_on_destroy_times; 32 | } 33 | 34 | int adjust_timeout_ms(int timeout_ms) { 35 | ++_adjust_timeout_times; 36 | return timeout_ms; 37 | } 38 | 39 | bool _blocking; 40 | int _run_times; 41 | int _on_destroy_times; 42 | int _adjust_timeout_times; 43 | }; 44 | 45 | TEST_F(RepeatedTimerTaskTest, sanity) { 46 | MockTimer timer; 47 | ASSERT_EQ(0, timer.init(10)); 48 | timer.start(); 49 | usleep(100500); 50 | const int run_times = timer._run_times; 51 | LOG(INFO) << "run_times=" << run_times; 52 | ASSERT_TRUE(run_times >= 8 && run_times <= 11) << run_times; 53 | timer.stop(); 54 | usleep(10000); 55 | ASSERT_LE(abs(run_times - timer._run_times), 1); 56 | timer.destroy(); 57 | timer.destroy(); 58 | ASSERT_EQ(1, timer._on_destroy_times); 59 | } 60 | 61 | TEST_F(RepeatedTimerTaskTest, stop_while_running) { 62 | MockTimer timer; 63 | ASSERT_EQ(0, timer.init(10)); 64 | timer._blocking = true; 65 | timer.start(); 66 | usleep(100000); 67 | ASSERT_EQ(1, timer._run_times); 68 | timer.stop(); 69 | timer._blocking = false; 70 | usleep(1000); 71 | ASSERT_EQ(1, timer._run_times); 72 | timer.destroy(); 73 | timer.destroy(); 74 | ASSERT_EQ(1, timer._on_destroy_times); 75 | } 76 | 77 | TEST_F(RepeatedTimerTaskTest, destroy_while_running) { 78 | MockTimer timer; 79 | ASSERT_EQ(0, timer.init(10)); 80 | timer._blocking = true; 81 | timer.start(); 82 | usleep(100000); 83 | ASSERT_EQ(1, timer._run_times); 84 | timer.destroy(); 85 | ASSERT_EQ(0, timer._on_destroy_times); 86 | timer._blocking = false; 87 | usleep(100000); 88 | ASSERT_EQ(1, timer._on_destroy_times); 89 | ASSERT_EQ(1, timer._run_times); 90 | timer.destroy(); 91 | ASSERT_EQ(1, timer._on_destroy_times); 92 | timer.destroy(); 93 | ASSERT_EQ(1, timer._on_destroy_times); 94 | } 95 | 96 | TEST_F(RepeatedTimerTaskTest, restart_while_running) { 97 | MockTimer timer; 98 | ASSERT_EQ(0, timer.init(10)); 99 | timer._blocking = true; 100 | timer.start(); 101 | usleep(100000); 102 | ASSERT_EQ(1, timer._run_times); 103 | timer.stop(); 104 | usleep(50000); 105 | timer.start(); 106 | usleep(50000); 107 | ASSERT_EQ(1, timer._run_times); 108 | timer.destroy(); 109 | ASSERT_EQ(0, timer._on_destroy_times); 110 | timer._blocking = false; 111 | usleep(50000); 112 | timer.destroy(); 113 | ASSERT_EQ(1, timer._on_destroy_times); 114 | ASSERT_EQ(1, timer._run_times); 115 | } 116 | -------------------------------------------------------------------------------- /test/test_storage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Baidu.com, Inc. All Rights Reserved 2 | 3 | // Author: Zhangyi Chen (chenzhangyi01@baidu.com) 4 | // Date: 2016/03/29 10:35:06 5 | 6 | #include 7 | #include "braft/storage.h" 8 | 9 | namespace braft { 10 | extern void global_init_once_or_die(); 11 | }; 12 | 13 | class StorageTest : public testing::Test { 14 | protected: 15 | void SetUp() { 16 | system("rm -rf data"); 17 | braft::global_init_once_or_die(); 18 | } 19 | void TearDown() {} 20 | }; 21 | 22 | TEST_F(StorageTest, sanity) { 23 | // LogStorage 24 | braft::LogStorage* log_storage = braft::LogStorage::create("local://data/log"); 25 | ASSERT_TRUE(log_storage); 26 | braft::ConfigurationManager cm; 27 | ASSERT_EQ(0, log_storage->init(&cm)); 28 | ASSERT_FALSE(braft::LogStorage::create("hdfs://data/log")); 29 | ASSERT_FALSE(braft::LogStorage::create("://data/log")); 30 | ASSERT_FALSE(braft::LogStorage::create("data/log")); 31 | ASSERT_FALSE(braft::LogStorage::create(" ://data/log")); 32 | 33 | // RaftMetaStorage 34 | braft::RaftMetaStorage* meta_storage 35 | = braft::RaftMetaStorage::create("local://data/raft_meta"); 36 | ASSERT_TRUE(meta_storage); 37 | ASSERT_TRUE(meta_storage->init().ok()); 38 | ASSERT_FALSE(braft::RaftMetaStorage::create("hdfs://data/raft_meta")); 39 | ASSERT_FALSE(braft::RaftMetaStorage::create("://data/raft_meta")); 40 | ASSERT_FALSE(braft::RaftMetaStorage::create("data/raft_meta")); 41 | ASSERT_FALSE(braft::RaftMetaStorage::create(" ://data/raft_meta")); 42 | 43 | // SnapshotStorage 44 | braft::SnapshotStorage* snapshot_storage 45 | = braft::SnapshotStorage::create("local://data/snapshot"); 46 | ASSERT_TRUE(snapshot_storage); 47 | ASSERT_EQ(0, snapshot_storage->init()); 48 | ASSERT_FALSE(braft::SnapshotStorage::create("hdfs://data/snapshot")); 49 | ASSERT_FALSE(braft::SnapshotStorage::create("://data/snapshot")); 50 | ASSERT_FALSE(braft::SnapshotStorage::create("data/snapshot")); 51 | ASSERT_FALSE(braft::SnapshotStorage::create(" ://data/snapshot")); 52 | 53 | } 54 | 55 | TEST_F(StorageTest, extra_space_should_be_trimmed) { 56 | // LogStorage 57 | braft::LogStorage* log_storage = braft::LogStorage::create("local://data/log"); 58 | ASSERT_TRUE(log_storage); 59 | braft::ConfigurationManager cm; 60 | ASSERT_EQ(0, log_storage->init(&cm)); 61 | braft::LogEntry* entry = new braft::LogEntry(); 62 | entry->data.append("hello world"); 63 | entry->id = braft::LogId(1, 1); 64 | entry->type = braft::ENTRY_TYPE_DATA; 65 | std::vector entries; 66 | entries.push_back(entry); 67 | ASSERT_EQ(1u, log_storage->append_entries(entries, NULL)); 68 | entry->Release(); 69 | delete log_storage; 70 | 71 | // reopen 72 | log_storage = braft::LogStorage::create(" local://./ data// // log ////"); 73 | ASSERT_EQ(0, log_storage->init(&cm)); 74 | 75 | ASSERT_EQ(1, log_storage->first_log_index()); 76 | ASSERT_EQ(1, log_storage->last_log_index()); 77 | entry = log_storage->get_entry(1); 78 | ASSERT_TRUE(entry); 79 | ASSERT_EQ("hello world", entry->data.to_string()); 80 | ASSERT_EQ(braft::LogId(1, 1), entry->id); 81 | entry->Release(); 82 | entry = NULL; 83 | } 84 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -D__const__=__unused__ -pipe -W -Wall -Wno-unused-parameter -fPIC -fno-omit-frame-pointer") 2 | 3 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/output/bin) 4 | 5 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 6 | add_executable(braft_cli braft_cli.cpp) 7 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 8 | target_link_libraries(braft_cli 9 | braft-static 10 | ${DYNAMIC_LIB} 11 | ) 12 | else() 13 | target_link_libraries(braft_cli 14 | "-Xlinker \"-(\"" 15 | braft-static 16 | ${DYNAMIC_LIB} 17 | "-Xlinker \"-)\"" 18 | ) 19 | endif() -------------------------------------------------------------------------------- /tools/patch_from_baidu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2018 Baidu.com, Inc. All Rights Reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | if [ -z "$1" ]; then 18 | echo "Usage1: patch_from_github PATCHFILE (generated by 'diff diff --no-prefix')" 19 | echo "Usage2: patch_from_github GIT_DIR COMMIT" 20 | exit 1 21 | fi 22 | PATCHFILE=$1 23 | if [ -d "$1/.git" ]; then 24 | if [ -z "$2" ]; then 25 | echo "Second argument must be a git commit" 26 | exit 1 27 | fi 28 | CURRENT_DIR=`pwd` 29 | GIT_DIR=$1 30 | COMMIT=$2 31 | PATCHFILE=$CURRENT_DIR/$COMMIT.patch 32 | echo "*** Generating diff of $GIT_DIR@$COMMIT" 33 | cd $GIT_DIR && git diff --no-prefix $COMMIT^! > $PATCHFILE 34 | cd $CURRENT_DIR 35 | fi 36 | 37 | MODIFIED_PATCHFILE=$(basename $(basename $PATCHFILE .patch) .diff).from_github.patch 38 | 39 | cat $PATCHFILE | sed -e 's/src\/baidu\/rpc\//src\/brpc\//g' \ 40 | -e 's/\ $MODIFIED_PATCHFILE 51 | EXTRA_ARGS= 52 | if [ -z "$DO_RUN" ]; then 53 | EXTRA_ARGS="--dry-run $EXTRA_ARGS" 54 | echo "*** This is a dry-run. To really apply, run: DO_RUN=1 tools/patch_from_github $1" 55 | fi 56 | 57 | patch -p0 -u -l $EXTRA_ARGS < $MODIFIED_PATCHFILE 58 | if [ ! -z "$DO_RUN" ]; then 59 | REJ_FILES=`find . -name "*.rej"` 60 | if [ ! -z "$REJ_FILES" ]; then 61 | echo "==== The patching is not done yet! Apply following rej files manually ====" 62 | echo $REJ_FILES 63 | fi 64 | fi 65 | -------------------------------------------------------------------------------- /zlib.BUILD: -------------------------------------------------------------------------------- 1 | package( 2 | default_visibility=["//visibility:public"] 3 | ) 4 | 5 | cc_library( 6 | name = "zlib", 7 | linkopts = ["-lz"], 8 | ) 9 | --------------------------------------------------------------------------------