├── .github └── workflows │ ├── ci.yml │ └── daily.yml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── bin └── .gitignore ├── docs └── Using.md ├── include ├── raft.h ├── raft_log.h ├── raft_private.h └── raft_types.h ├── package.json ├── scripts └── amalgamate.sh ├── src ├── raft_log.c ├── raft_node.c ├── raft_server.c └── raft_server_properties.c └── tests ├── CuTest.c ├── CuTest.h ├── helpers.h ├── linked_list_queue.c ├── linked_list_queue.h ├── log_fuzzer.py ├── mock_send_functions.c ├── mock_send_functions.h ├── raft_cffi_builder.py ├── requirements.txt ├── test_log.c ├── test_log_impl.c ├── test_node.c ├── test_scenario.c ├── test_server.c ├── test_snapshotting.c └── virtraft2.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | run-tests: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Set up Python for testing 11 | uses: actions/setup-python@v1 12 | with: 13 | python-version: '3.9' 14 | architecture: 'x64' 15 | - name: Install Python dependencies 16 | run: 17 | python -m pip install -r tests/requirements.txt 18 | - name: Build 19 | run: make COVERAGE=1 20 | - name: Run tests 21 | run: | 22 | make tests 23 | make test_fuzzer 24 | make test_virtraft 25 | make gcov 26 | - name: Upload to codecov 27 | uses: codecov/codecov-action@v2 28 | 29 | address-sanitizer: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Run tests 34 | run: | 35 | make tests SANITIZER=address 36 | 37 | undefined-sanitizer: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v2 41 | - name: Run tests 42 | run: | 43 | make tests SANITIZER=undefined 44 | -------------------------------------------------------------------------------- /.github/workflows/daily.yml: -------------------------------------------------------------------------------- 1 | name: Daily 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | run-tests: 10 | if: github.repository == 'redislabs/raft' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: make 15 | run: make 16 | - name: Set up Python for testing 17 | uses: actions/setup-python@v1 18 | with: 19 | python-version: '3.9' 20 | architecture: 'x64' 21 | - name: Install Python dependencies 22 | run: 23 | python -m pip install -r tests/requirements.txt 24 | - name: Run tests 25 | run: 26 | make tests_full 27 | - name: Upload to codecov 28 | uses: codecov/codecov-action@v2 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.gcno 4 | *.gcda 5 | *.gcov 6 | *.dylib 7 | *~ 8 | libraft.a 9 | libraft.so 10 | .env/ 11 | .hypothesis/ 12 | **/__pycache__ 13 | cmake-build* 14 | .idea 15 | tests/.raft_cffi_built 16 | tests/raft_cffi.c 17 | build/ 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | addons: 3 | apt: 4 | packages: 5 | - "python3" 6 | - "python3-pip" 7 | before_install: 8 | - sudo pip install cpp-coveralls 9 | - sudo pip3 install cffi 10 | - sudo pip3 install colorama 11 | - sudo pip3 install coloredlogs 12 | - sudo pip3 install docopt 13 | - sudo pip3 install terminaltables 14 | script: 15 | - make tests 16 | - make test_virtraft 17 | after_success: 18 | - coveralls --verbose --exclude include --exclude tests --exclude CLinkedListQueue 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Library build : 2 | # mkdir build && cd build && cmake .. 3 | # make 4 | # Coverage : 5 | # mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Coverage 6 | # make && make lib_coverage 7 | # Tests : 8 | # mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug 9 | # make && make tests_full 10 | # SANITIZER : 11 | # mkdir build && cd build && cmake .. -DSANITIZER=address 12 | # make && make check 13 | 14 | project(raft C) 15 | cmake_minimum_required(VERSION 3.7.2) 16 | 17 | macro(define_test name) 18 | add_executable(${name} 19 | tests/${name}.c 20 | tests/CuTest.c 21 | tests/linked_list_queue.c 22 | tests/mock_send_functions.c) 23 | 24 | target_link_libraries(${name} raft) 25 | target_include_directories(${name} PRIVATE include/) 26 | add_test(NAME ${name} COMMAND $) 27 | endmacro() 28 | 29 | # ----------------------- Build Settings Start ------------------------------- # 30 | set(CMAKE_C_STANDARD 99) 31 | set(CMAKE_C_STANDARD_REQUIRED ON) 32 | set(CMAKE_C_EXTENSIONS OFF) 33 | 34 | enable_testing() 35 | 36 | if (NOT CMAKE_BUILD_TYPE) 37 | message(STATUS "No build type selected, defaulting to Release") 38 | set(CMAKE_BUILD_TYPE "Release") 39 | endif () 40 | 41 | message(STATUS "Main build type: ${CMAKE_BUILD_TYPE}") 42 | # ------------------------ Build Settings End -------------------------------- # 43 | 44 | 45 | # --------------------------- Build Helpers Start ---------------------------- # 46 | add_custom_target(check ${CMAKE_COMMAND} 47 | -E env CTEST_OUTPUT_ON_FAILURE=1 48 | ${CMAKE_CTEST_COMMAND} -C $ --verbose 49 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) 50 | # ---------------------------- Build Helpers End ----------------------------- # 51 | 52 | 53 | # --------------------------- Code Coverage Start ---------------------------- # 54 | if (${CMAKE_BUILD_TYPE} MATCHES "Coverage") 55 | add_compile_options(-fprofile-arcs -ftest-coverage) 56 | link_libraries(gcov) 57 | endif () 58 | 59 | add_custom_target(lib_coverage) 60 | add_custom_command( 61 | TARGET lib_coverage 62 | COMMAND lcov --capture --directory . 63 | --output-file coverage.info --rc lcov_branch_coverage=1 --rc lcov_excl_br_line='assert' 64 | COMMAND lcov --remove coverage.info '/usr/*' '*example*' '*test*' 65 | --output-file coverage.info --rc lcov_branch_coverage=1 --rc lcov_excl_br_line='assert' 66 | COMMAND lcov --list coverage.info --rc lcov_branch_coverage=1 --rc lcov_excl_br_line='assert' 67 | ) 68 | 69 | add_dependencies(lib_coverage check test_fuzzer test_virtraft) 70 | # ---------------------------- Code Coverage End ----------------------------- # 71 | 72 | 73 | # ------------------------------ Python Tests ---------------------------------# 74 | 75 | set(LIBPATH PYTHONPATH=${CMAKE_BINARY_DIR}/tests LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}) 76 | 77 | add_custom_target(test_fuzzer) 78 | add_custom_command(TARGET test_fuzzer 79 | COMMAND ${LIBPATH} python ${CMAKE_SOURCE_DIR}/tests/log_fuzzer.py 80 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) 81 | add_dependencies(test_fuzzer raft_cffi) 82 | 83 | add_custom_target(test_virtraft) 84 | add_custom_command(TARGET test_virtraft 85 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 86 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) 87 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) 88 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 2 -m 3 $(VIRTRAFT_OPTS) 89 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 3 -m 3 $(VIRTRAFT_OPTS) 90 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 4 -m 3 $(VIRTRAFT_OPTS) 91 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 5 -m 3 $(VIRTRAFT_OPTS) 92 | COMMAND ${LIBPATH} python3 ${CMAKE_SOURCE_DIR}/tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 6 -m 3 $(VIRTRAFT_OPTS)) 93 | add_dependencies(test_virtraft raft_cffi) 94 | 95 | add_custom_target(raft_cffi) 96 | add_custom_command(TARGET raft_cffi 97 | COMMAND python3 ${CMAKE_SOURCE_DIR}/tests/raft_cffi_builder.py --libname raft_shared --libdir ${CMAKE_BINARY_DIR} --includedir ${CMAKE_SOURCE_DIR}/include --tmpdir ${CMAKE_BINARY_DIR}/ 98 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) 99 | # ----------------------------- Python Tests End ----------------------------- # 100 | 101 | 102 | # ----------------------------- C Tests Start -------------------------------- # 103 | add_custom_target(tests_full) 104 | add_dependencies(tests_full check test_fuzzer test_virtraft) 105 | 106 | set(RAFT_SOURCE_FILES 107 | src/raft_log.c 108 | src/raft_server.c 109 | src/raft_node.c 110 | src/raft_server_properties.c) 111 | 112 | add_library(raft STATIC ${RAFT_SOURCE_FILES}) 113 | 114 | if (SANITIZER) 115 | if ("${SANITIZER}" STREQUAL "address") 116 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") 117 | add_link_options(-fsanitize=address) 118 | elseif ("${SANITIZER}" STREQUAL "undefined") 119 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") 120 | add_link_options(-fsanitize=undefined) 121 | elseif ("${SANITIZER}" STREQUAL "thread") 122 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") 123 | add_link_options(-fsanitize=thread) 124 | else () 125 | message(FATAL_ERROR "Unknown sanitizer: ${SANITIZER}") 126 | endif () 127 | 128 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize-recover=all -fno-omit-frame-pointer") 129 | message(STATUS "Using sanitizer: ${SANITIZER}") 130 | endif () 131 | 132 | target_compile_options(raft PRIVATE -g -Wall -Wextra -pedantic) 133 | target_include_directories(raft PRIVATE include) 134 | 135 | add_library(raft_shared SHARED ${RAFT_SOURCE_FILES}) 136 | target_include_directories(raft_shared PRIVATE include) 137 | set_property(TARGET raft_shared PROPERTY POSITION_INDEPENDENT_CODE ON) 138 | target_compile_options(raft_shared PRIVATE -g -Wall -Wextra -pedantic) 139 | 140 | define_test(test_log) 141 | define_test(test_log_impl) 142 | define_test(test_node) 143 | define_test(test_scenario) 144 | define_test(test_server) 145 | define_test(test_snapshotting) 146 | # ------------------------------ C Tests End --------------------------------- # 147 | 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Willem-Hendrik Thiart 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of its contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL WILLEM-HENDRIK THIART BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CONTRIB_DIR = . 2 | TEST_DIR = ./tests 3 | SRC_DIR = src 4 | BUILD_DIR = src 5 | BIN_DIR = bin 6 | LIB = -I libs 7 | INC = -I include 8 | GCOV_CFLAGS = -fprofile-arcs -ftest-coverage 9 | SHELL = /bin/bash 10 | CFLAGS += -Iinclude -fno-omit-frame-pointer -fno-common -fsigned-char -g -O2 -fPIC 11 | 12 | ifdef SANITIZER 13 | ifeq ($(SANITIZER),address) 14 | MALLOC = libc 15 | CFLAGS += -fsanitize=address -fno-sanitize-recover=all -fno-omit-frame-pointer 16 | LDFLAGS += -fsanitize=address 17 | else 18 | ifeq ($(SANITIZER),undefined) 19 | MALLOC = libc 20 | CFLAGS += -fsanitize=undefined -fno-sanitize-recover=all -fno-omit-frame-pointer 21 | LDFLAGS += -fsanitize=undefined 22 | else 23 | ifeq ($(SANITIZER),thread) 24 | CFLAGS += -fsanitize=thread -fno-sanitize-recover=all -fno-omit-frame-pointer 25 | LDFLAGS += -fsanitize=thread 26 | else 27 | $(error "unknown sanitizer=${SANITIZER}") 28 | endif 29 | endif 30 | endif 31 | endif 32 | 33 | ifeq ($(COVERAGE), 1) 34 | CFLAGS += $(GCOV_CFLAGS) 35 | TEST_CFLAGS = $(CFLAGS) 36 | else 37 | TEST_CFLAGS = $(CFLAGS) $(GCOV_CFLAGS) 38 | endif 39 | 40 | LIB_CFLAGS = $(CFLAGS) -Wextra -Wall -pedantic -Werror 41 | 42 | UNAME := $(shell uname) 43 | 44 | ifeq ($(UNAME), Darwin) 45 | ASANFLAGS = -fsanitize=address 46 | SHAREDFLAGS = -dynamiclib 47 | SHAREDEXT = dylib 48 | # We need to include the El Capitan specific /usr/includes, aargh 49 | CFLAGS += -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/ 50 | CFLAGS += -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include 51 | CFLAGS += $(ASANFLAGS) 52 | CFLAGS += -Wno-nullability-completeness 53 | else 54 | SHAREDFLAGS = -shared 55 | SHAREDEXT = so 56 | endif 57 | 58 | # Export LD_LIBRARY_PATH to allow the CFFI shared object to find libraft.so. 59 | export LD_LIBRARY_PATH=$(PWD) 60 | 61 | OBJECTS = \ 62 | $(BUILD_DIR)/raft_server.o \ 63 | $(BUILD_DIR)/raft_server_properties.o \ 64 | $(BUILD_DIR)/raft_node.o \ 65 | $(BUILD_DIR)/raft_log.o 66 | 67 | TEST_OBJECTS = $(patsubst $(BUILD_DIR)/%.o,$(BUILD_DIR)/test-%.o,$(OBJECTS)) 68 | 69 | TEST_HELPERS = \ 70 | $(TEST_DIR)/CuTest.o \ 71 | $(TEST_DIR)/linked_list_queue.o \ 72 | $(TEST_DIR)/mock_send_functions.o 73 | 74 | TESTS = $(wildcard $(TEST_DIR)/test_*.c) 75 | TEST_TARGETS = $(patsubst $(TEST_DIR)/%.c,$(BIN_DIR)/%,$(TESTS)) 76 | 77 | LIBRAFT_STATIC = libraft.a 78 | LIBRAFT_SHARED = libraft.$(SHAREDEXT) 79 | 80 | all: static shared 81 | 82 | shared: $(LIBRAFT_SHARED) 83 | 84 | static: $(LIBRAFT_STATIC) 85 | 86 | $(LIBRAFT_SHARED): $(OBJECTS) 87 | $(CC) $^ $(LDFLAGS) $(LIB_CFLAGS) -fPIC $(SHAREDFLAGS) -o $@ 88 | 89 | $(LIBRAFT_STATIC): $(OBJECTS) 90 | ar -r $@ $^ 91 | 92 | .PHONY: tests 93 | tests: $(TEST_TARGETS) 94 | gcov $(TEST_OBJECTS) 95 | 96 | .PHONY: gcov 97 | gcov: $(OBJECTS) 98 | gcov $(OBJECTS) $(TEST_OBJECTS) 99 | 100 | $(TEST_TARGETS): $(BIN_DIR)/%: $(TEST_OBJECTS) $(TEST_HELPERS) 101 | $(CC) $(TEST_CFLAGS) $(TEST_DIR)/$*.c $(LIB) $(INC) $^ -o $@ 102 | ./$@ 103 | 104 | $(BUILD_DIR)/test-%.o: $(SRC_DIR)/%.c 105 | $(CC) $(TEST_CFLAGS) $(INC) -c -o $@ $< 106 | 107 | $(TEST_DIR)/%.o: $(TEST_DIR)/%.c 108 | $(CC) $(TEST_CFLAGS) $(INC) -c -o $@ $< 109 | 110 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c 111 | $(CC) $(LIB_CFLAGS) $(INC) -c -o $@ $< 112 | 113 | .PHONY: test_helper 114 | test_helper: $(TEST_HELPERS) 115 | $(CC) $(TEST_CFLAGS) -o $@ 116 | 117 | # A file to signal we've built the CFFI module, because its name is not fixed. 118 | RAFT_CFFI_TARGET = tests/.raft_cffi_built 119 | $(RAFT_CFFI_TARGET): $(LIBRAFT_SHARED) 120 | python3 tests/raft_cffi_builder.py 121 | touch $(RAFT_CFFI_TARGET) 122 | 123 | .PHONY: test_fuzzer 124 | test_fuzzer: $(RAFT_CFFI_TARGET) 125 | python tests/log_fuzzer.py 126 | 127 | .PHONY: tests_full 128 | tests_full: 129 | make clean 130 | make tests 131 | make test_fuzzer 132 | make test_virtraft 133 | 134 | .PHONY: test_virtraft 135 | test_virtraft: $(RAFT_CFFI_TARGET) 136 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) 137 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 138 | python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) 139 | python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 140 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 2 -m 3 $(VIRTRAFT_OPTS) 141 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 2 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 142 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 3 -m 3 $(VIRTRAFT_OPTS) 143 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 3 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 144 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 4 -m 3 $(VIRTRAFT_OPTS) 145 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 4 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 146 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 5 -m 3 $(VIRTRAFT_OPTS) 147 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 5 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 148 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 6 -m 3 $(VIRTRAFT_OPTS) 149 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 6 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) 150 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --auto_flush $(VIRTRAFT_OPTS) 151 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 152 | python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --auto_flush $(VIRTRAFT_OPTS) 153 | python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 154 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 2 -m 3 --auto_flush $(VIRTRAFT_OPTS) 155 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 2 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 156 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 3 -m 3 --auto_flush $(VIRTRAFT_OPTS) 157 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 3 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 158 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 4 -m 3 --auto_flush $(VIRTRAFT_OPTS) 159 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 4 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 160 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 5 -m 3 --auto_flush $(VIRTRAFT_OPTS) 161 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 5 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 162 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 6 -m 3 --auto_flush $(VIRTRAFT_OPTS) 163 | python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 10 --drop_rate 5 -P 10 --seed 6 -m 3 --client_rate 0 --auto_flush $(VIRTRAFT_OPTS) 164 | 165 | .PHONY: amalgamation 166 | amalgamation: 167 | ./scripts/amalgamate.sh > raft.h 168 | 169 | .PHONY: infer 170 | infer: do_infer 171 | 172 | .PHONY: do_infer 173 | do_infer: 174 | make clean 175 | infer -- make 176 | 177 | clean: 178 | -@rm -f src/*.o bin/* src/*.gcda src/*.gcno *.gcno *.gcda *.gcov tests/*.o tests/*.gcda tests/*.gcno tests/raft_cffi.* $(RAFT_CFFI_TARGET) $(LIBRAFT_SHARED) $(LIBRAFT_STATIC) 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Daily CI](https://github.com/RedisLabs/raft/actions/workflows/daily.yml/badge.svg)](https://github.com/RedisLabs/raft/actions/workflows/daily.yml) 2 | [![codecov](https://codecov.io/gh/RedisLabs/raft/branch/master/graph/badge.svg?token=66M19HJ7K9)](https://codecov.io/gh/RedisLabs/raft) 3 | 4 | A complete implementation of the [Raft Consensus Algorithm](https://raft.github.io) as a C library, licensed under BSD. 5 | 6 | This is a fork of the original library created by Willem-Hendrik Thiart, which is now actively maintained by Redis Ltd. and used as part of [RedisRaft](https://github.com/redislabs/redisraft). 7 | 8 | See [raft.h](https://github.com/redislabs/raft/blob/master/include/raft.h) for full API documentation. 9 | 10 | Main Features 11 | ============= 12 | 13 | The library implements all basic features of Raft as well as some extensions, including: 14 | 15 | * Leader election 16 | * Log replication and FSM interface 17 | * Compaction and snapshot support 18 | * Cluster membership changes 19 | * Quorum check and step-down by leader 20 | * Pre-vote 21 | * Leadership transfer 22 | 23 | The library is completely abstract and it is up to the caller to implement things like: 24 | * RPC and networking 25 | * Log storage and access 26 | * Generation of snapshots from FSM state 27 | 28 | Getting Started 29 | =============== 30 | 31 | To build and run basic tests: 32 | 33 | ``` 34 | make tests 35 | ``` 36 | 37 | If you prefer, you can also use cmake: 38 | 39 | ``` 40 | mkdir build 41 | cd build 42 | cmake .. 43 | make 44 | make test 45 | ``` 46 | 47 | Tests 48 | ===== 49 | 50 | The library uses the following testing methods: 51 | 52 | * A simulator (virtraft2) is used to test the Raft invariants on unreliable networks 53 | * All bugs have regression tests 54 | * Many unit tests 55 | 56 | virtraft2 57 | --------- 58 | 59 | This cluster simulator checks the following: 60 | 61 | * Log Matching (servers must have matching logs) 62 | * State Machine Safety (applied entries have the same ID) 63 | * Election Safety (only one valid leader per term) 64 | * Current Index Validity (does the current index have an existing entry?) 65 | * Entry ID Monotonicity (entries aren't appended out of order) 66 | * Committed entry popping (committed entries are not popped from the log) 67 | * Log Accuracy (does the server's log match mirror an independent log?) 68 | * Deadlock detection (does the cluster continuously make progress?) 69 | 70 | Chaos generated by virtraft2: 71 | 72 | * Random bi-directional partitions between nodes 73 | * Message dropping 74 | * Message duplication 75 | * Membership change injection 76 | * Random compactions 77 | 78 | Run the simulator using: 79 | 80 | ``` 81 | mkdir .env 82 | virtualenv .env 83 | source .env/bin/activate 84 | pip install -r tests/requirements.txt 85 | make test_virtraft 86 | ``` 87 | 88 | Requirements 89 | --------- 90 | * Python 3.9 or later -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /docs/Using.md: -------------------------------------------------------------------------------- 1 | ## Table of Contents 2 | 3 | - [Introduction](#Introduction) 4 | - [Implementing callbacks](#Implementing-callbacks) 5 | * [Raft library callbacks](#Raft-library-callbacks) 6 | * [Log file callbacks](#Log-file-callbacks) 7 | - [Library Initialization](#Library-Initialization) 8 | - [Cluster Initialization](#Cluster-Initialization) 9 | - [Submit Requests](#Submit-requests) 10 | * [Submit entries](#Submit-entries) 11 | * [Submit read-only requests](#Submit-read-only-requests) 12 | - [Log compaction](#Log-compaction) 13 | - [Restore state after a restart](#Restore-state-after-a-restart) 14 | * [Restoring from snapshot](#Restoring-from-snapshot) 15 | * [Restoring log entries](#Restoring-log-entries) 16 | * [Restoring metadata](#Restoring-metadata) 17 | * [State restore example](#State-restore-example) 18 | - [Sending a snapshot and loading a snapshot as a follower](#Sending-a-snapshot-and-loading-a-snapshot-as-a-follower) 19 | * [Sending a snapshot](#Sending-a-snapshot) 20 | * [Receiving a snapshot](#Receiving-a-snapshot) 21 | * [Loading the received snapshot file](#Loading-the-received-snapshot-file) 22 | - [Adding and removing nodes](#Adding-and-removing-nodes) 23 | * [Adding a node](#Adding-a-node) 24 | * [Removing a node](#Removing-a-node) 25 | 26 | 27 | Introduction 28 | =============== 29 | 30 | This document contains examples of how to integrate the Raft library. 31 | 32 | > :bulb: In the code snippets, error handling is skipped. You should check the return code of the library functions all the time. 33 | 34 | > :bulb: In the code snippets, functions that need to be implemented by the application will use the "app_" prefix. e.g. app_function() 35 | 36 | 37 | Implementing callbacks 38 | ---------------- 39 | 40 | Raft library only provides core raft logic. The application should implement some set of callbacks for networking and storage. 41 | 42 | 43 | ### Raft library callbacks 44 | You provide your callbacks to the Raft server using `raft_set_callbacks()`. 45 | The application must implement all the mandatory callbacks. You can find more detailed info in the `raft.h`. 46 | 47 | ```c 48 | typedef struct 49 | { 50 | int (*raft_appylog_f) (raft_server_t* r, void *user_data, raft_entry_t *entry, raft_index_t entry_idx); 51 | int (*raft_persist_metadata_f) (raft_server_t *r, void *user_data, raft_term_t term, raft_node_id_t vote); 52 | raft_node_id_t (*raft_get_node_id_f) (raft_server_t *r, void *user_data, raft_entry_t *entry, raft_index_t entry_idx); 53 | int (*raft_node_has_sufficient_logs_f) (raft_server_t *r, void *user_data, raft_node_t *node); 54 | raft_time_t (*raft_timestamp_f) (raft_server_t *r, void *user_data); 55 | 56 | /* Networking */ 57 | int (*raft_send_requestvote_f) (raft_server_t *r, void *user_data, raft_node_t *node, raft_requestvote_req_t *req); 58 | int (*raft_send_appendentries_f) (raft_server_t *r, void *user_data, raft_node_t *node, raft_appendentries_req_t *req); 59 | int (*raft_send_snapshot_f) (raft_server_t *r, void *user_data, raft_node_t *node, raft_snapshot_req_t *req); 60 | int (*raft_send_timeoutnow_f) (raft_server_t *r, void *user_data, raft_node_t *node); 61 | 62 | /* Snapshot handling */ 63 | int (*raft_load_snapshot_f) (raft_server_t *r, void *user_data, raft_term_t snapshot_term, raft_index_t snapshot_index); 64 | int (*raft_get_snapshot_chunk_f) (raft_server_t *r, void *user_data, raft_node_t *node, raft_size_t offset, raft_snapshot_chunk_t *chunk); 65 | int (*raft_store_snapshot_chunk_f) (raft_server_t *r, void *user_data, raft_index_t snapshot_index, raft_size_t offset, raft_snapshot_chunk_t* chunk); 66 | int (*raft_clear_snapshot_f) (raft_server_t *r, void *user_data); 67 | 68 | /** 69 | * Optional callbacks: 70 | * 71 | * Notification on events, debug logging, message flow control etc. 72 | */ 73 | void (*raft_membership_event_f) (raft_server_t *r,void *user_data,raft_node_t *node,raft_entry_t *entry,raft_membership_e type); 74 | void (*raft_state_event_f) (raft_server_t *r, void *user_data, raft_state_e state); 75 | void (*raft_transfer_event_f) (raft_server_t *r, void *user_data, raft_leader_transfer_e result); 76 | void (*raft_log_f) (raft_server_t *r, void *user_data, const char *buf); 77 | int (*raft_backpressure_f) (raft_server_t* r, void *user_data, raft_node_t *node); 78 | raft_index_t (*raft_get_entries_to_send_f) (raft_server_t *r, void *user_data, raft_node_t *node, raft_index_t idx, raft_index_t entries_n, raft_entry_t **entries); 79 | 80 | } raft_cbs_t; 81 | ``` 82 | 83 | ### Log file callbacks 84 | 85 | The application must provide a log implementation and pass that implementation as a parameter to `raft_new_with_log()` call. 86 | Log implementation must implement all the callbacks. You can find more detailed info in the `raft.h`. 87 | 88 | ```c 89 | typedef struct raft_log_impl 90 | { 91 | void *(*init) (void *raft, void *arg); 92 | void (*free) (void *log); 93 | void (*reset) (void *log, raft_index_t first_idx, raft_term_t term); 94 | int (*append) (void *log, raft_entry_t *entry); 95 | int (*poll) (void *log, raft_index_t first_idx); 96 | int (*pop) (void *log, raft_index_t from_idx); 97 | raft_entry_t* (*get) (void *log, raft_index_t idx); 98 | raft_index_t (*get_batch) (void *log, raft_index_t idx, raft_index_t entries_n, raft_entry_t **entries); 99 | raft_index_t (*first_idx) (void *log); 100 | raft_index_t (*current_idx) (void *log); 101 | raft_index_t (*count) (void *log); 102 | int (*sync) (void *log); 103 | } raft_log_impl_t; 104 | ``` 105 | 106 | 107 | Library Initialization 108 | ---------------- 109 | 110 | You should initialize the Raft library on all nodes: 111 | 112 | ```c 113 | raft_server_t *r = raft_new_with_log(&log_impl, log_impl_arg); 114 | raft_set_callbacks(r, &callback_impls, app_arg); 115 | 116 | // Add own node as non-voting, e.g. node ID here is 999999, application generates node IDs. 117 | raft_add_non_voting_node(r, user_data_ptr, 999999, 1); 118 | ``` 119 | 120 | and call `raft_periodic()` at periodic intervals: 121 | 122 | ``` 123 | //e.g. Call every 100 milliseconds 124 | raft_periodic(r); 125 | ``` 126 | 127 | 128 | Cluster Initialization 129 | ------------------ 130 | 131 | You need to follow these steps when you want to initialize a cluster. (Preparing the cluster for the first time use) 132 | - You need to start a single node first. This node will be the first node in the cluster. Other nodes can join this node later. 133 | - Initialize the node, set term 1, and make the node leader. 134 | - Submit the configuration entry of the node. 135 | 136 | 137 | ```c 138 | 139 | // Raft Library initialization 140 | raft_server_t *r = raft_new_with_log(&log_impl, arg); 141 | raft_set_callbacks(r, &callback_impls, arg); 142 | raft_add_non_voting_node(r, NULL, 9999999, 1); // Add own node as non-voting 143 | 144 | // Bootstrap a cluster. 145 | // Become the leader and append the configuration entry for the own node. 146 | raft_set_current_term(r, 1); 147 | raft_become_leader(r); 148 | 149 | struct app_config cfg = { 150 | .id = 9999999, 151 | .addr = ...., // You should also put node address into the config entry 152 | }; 153 | 154 | raft_entry_t *ety = raft_entry_new(sizeof(cfg)); 155 | ety->type = RAFT_LOGTYPE_ADD_NODE; 156 | memcpy(ety->data, &cfg, sizeof(cfg)); 157 | 158 | raft_recv_entry(r, ety, NULL); 159 | raft_entry_release(ety); 160 | 161 | ``` 162 | 163 | Submit requests 164 | ------------------ 165 | 166 | #### Submit entries 167 | 168 | You can submit entries by calling `raft_recv_entry()`: 169 | 170 | ```c 171 | void app_submit(raft_server_t *r, void *client_request, size_t client_request_len) 172 | { 173 | raft_entry_t *ety = raft_entry_new(client_request_len); 174 | 175 | memcpy(ety->data, client_request, client_request_len); 176 | ety->type = RAFT_LOGTYPE_NORMAL; 177 | 178 | raft_recv_entry(r, ety, NULL); 179 | raft_entry_release(ety); 180 | } 181 | ``` 182 | 183 | When the Raft library commits the entry, it will call `applylog` callback for the entry: 184 | 185 | ```c 186 | int app_appylog_callback_impl(raft_server_t *r, 187 | void *user_data, 188 | raft_entry_t *entry, 189 | raft_index_t entry_index) 190 | { 191 | void *client_request = entry->data; 192 | size_t client_request_len = entry->data_len; 193 | 194 | app_execute_request(client_request, client_request_len); 195 | } 196 | ``` 197 | 198 | #### Submit read-only requests 199 | 200 | If your operation is read-only, you can execute it without writing to the log or without replicating it to the other nodes. Still, the Raft library must communicate with the other nodes to verify that local node is still the leader. For that reason, there is another API function to submit read-only requests. When it is safe to execute a request, Raft library will call the callback you provided: 201 | 202 | ```c 203 | void app_submit_readonly(raft_server_t *r, void *client_request) 204 | { 205 | raft_recv_read_request(r, app_readonly_op_callback, request); 206 | } 207 | 208 | void app_readonly_op_callback(void *arg, int can_read) 209 | { 210 | void *client_request = arg; 211 | 212 | if (can_read == 0) { 213 | // Cluster is down or this node is not the leader anymore. 214 | // We cannot execute the command. 215 | return; 216 | } 217 | 218 | app_execute_readonly(request); 219 | } 220 | 221 | ``` 222 | 223 | 224 | Log compaction 225 | ------------------ 226 | 227 | Over time, the log file will grow. The library does not initiate log compaction itself. The application should decide when to take a snapshot. (e.g. if the log file grows over a limit) 228 | 229 | These are the steps when you want to save a snapshot: 230 | 231 | - Call `raft_begin_snapshot()`. 232 | - Save `last_applied_term` and `last_applied_index` and node configuration into the snapshot. 233 | - Save the snapshot to the disk. 234 | - Call `raft_end_snapshot()` 235 | 236 | e.g 237 | ```c 238 | void app_take_snapshot(raft_server_t *r) 239 | { 240 | raft_begin_snapshot(r); 241 | 242 | app_serialize_into_snapshot(raft_get_last_applied_term()); 243 | app_serialize_into_snapshot(raft_get_last_applied_index()); 244 | 245 | for (int i = 0; i < raft_get_num_nodes(r); i++) { 246 | raft_node_t *n = raft_get_node_from_idx(r, i); 247 | /* Skip uncommitted nodes from the snapshot */ 248 | if (!raft_node_is_addition_committed(n)) { 249 | continue; 250 | } 251 | raft_node_id_t id = raft_get_node_id(n); 252 | int voting = raft_is_voting_committed(n); 253 | 254 | // You may also want to store address info for the node but skipping that 255 | // part in this example as it is not part of the library 256 | serialize_node_into_snapshot(id, voting); 257 | } 258 | 259 | app_save_snapshot_to_disk(); 260 | 261 | raft_end_snapshot(r); 262 | } 263 | 264 | ``` 265 | 266 | Restore state after a restart 267 | ------------------ 268 | 269 | Raft stores all its data in three files: Snapshot, log, and the metadata file (see `raft_persist_metadata_f` in `raft.h` for details). 270 | 271 | After a restart, we need to restore the state. The correct order would be: 272 | 273 | - Initialize the library (described above) 274 | - Restore snapshot (skip this step if you don't have a snapshot) 275 | - Restore log entries 276 | - Restore metadata 277 | 278 | 279 | ### Restoring from snapshot 280 | 281 | The application saves `last_applied_term` and `last_applied_index` along with node configuration into the snapshots. 282 | After loading the state into your application on a restart, you need to configure the Raft library from the configuration info in the snapshot and call `raft_restore_snapshot()`. 283 | e.g: 284 | 285 | ```c 286 | 287 | // As an example, assuming you read a list of nodes from the snapshot 288 | struct config { 289 | raft_node_id_t id; 290 | int voting; 291 | struct config *next; 292 | }; 293 | .... 294 | 295 | void app_configure_from_snapshot(raft_server_t *r, struct config *head) 296 | { 297 | struct config *cfg = head; 298 | 299 | while (cfg != NULL) { 300 | if (cfg->id == raft_get_nodeid(r)) { 301 | // This is our own node configuration 302 | raft_node_t *n = raft_get_node(r, cfg->id); 303 | raft_node_set_voting(n, cfg->voting); 304 | } else { 305 | if (cfg->voting) { 306 | raft_add_node(r, n, cfg->id, 0); 307 | } else { 308 | raft_add_non_voting_node(r, n, cfg->id, 0); 309 | } 310 | } 311 | cfg = cfg->next; 312 | } 313 | } 314 | 315 | void app_restore_snapshot(raft_server_t *r, 316 | struct config *head, 317 | raft_term_t last_applied_term 318 | raft_index_t last_applied_index) 319 | { 320 | app_configure_from_snapshot(r, head); 321 | raft_restore_snapshot(r, last_applied_term, last_applied_index); 322 | } 323 | 324 | ``` 325 | 326 | ### Restoring log entries 327 | After restoring the log file (restoring in your log implementation), you need to call one function: 328 | 329 | ```c 330 | raft_restore_log(r); 331 | ``` 332 | 333 | 334 | ### Restoring metadata 335 | As the final step, read the term and vote info from the metadata file and then call one library function: 336 | ``` 337 | raft_restore_metadata(r, term, vote); 338 | ``` 339 | 340 | ### State restore example 341 | 342 | If we put all steps together: 343 | 344 | ```c 345 | void app_restore_raft_library() 346 | { 347 | // Raft Library initialization 348 | raft_server_t *r = raft_new_with_log(..., ...); 349 | raft_add_non_voting_node(r, NULL, 1, 1); 350 | 351 | // Assuming you loaded snapshot into your application, 352 | // extracted node configuration list, 353 | // extracted last_applied_term and last_applied_index 354 | // See the example implementation above for this function. 355 | app_configure_from_snapshot(r, cfg); 356 | raft_restore_snapshot(r, snapshot_last_term, snapshot_last_index); 357 | 358 | app_load_logs_to_impl(); // Load log entries in your log implementation 359 | raft_restore_log(); 360 | 361 | app_read_metadata_file(); // Read metadata file and extract term and vote 362 | raft_restore_metadata(r, term, vote); 363 | } 364 | 365 | 366 | ``` 367 | 368 | 369 | 370 | Sending a snapshot and loading a snapshot as a follower 371 | ------------------ 372 | 373 | #### Sending a snapshot 374 | 375 | The leader node can send a snapshot to the follower node if the follower lags behind. When that happens, the Raft library will call `raft_get_snapshot_chunk_f` callback and require a chunk from the snapshot from the application. An example implementation would be: 376 | 377 | ```c 378 | 379 | // Assuming you have a pointer to snapshot 380 | // (e.g. you can use mmap'ed file or if the snapshot is small, you can have a 381 | // copy of the snapshot in memory) 382 | size_t snapshot_file_len; 383 | void *ptr_to_snapshot_file; 384 | 385 | int app_get_snapshot_chunk_impl(raft_server_t* raft, 386 | void *user_data, 387 | raft_node_t *node, 388 | raft_size_t offset, 389 | raft_snapshot_chunk_t *chunk) 390 | { 391 | // If you want to limit snapshot_req messages count in flight, you can 392 | // return RAFT_ERR_DONE here immediately. (backpressure). 393 | // e.g if (nodeimpl->msg_in_flight > 32) { return RAFT_ERR_DONE; } 394 | 395 | const int max_chunk_size = 32000; 396 | 397 | const raft_size_t remaining_bytes = snapshot_file_len - offset; 398 | chunk->len = MIN(max_chunk_size, remaining_bytes); 399 | if (chunk->len == 0) { 400 | /* All chunks are sent */ 401 | return RAFT_ERR_DONE; 402 | } 403 | 404 | chunk->data = (char *) ptr_to_snapshot_file + offset; 405 | chunk->last_chunk = (offset + chunk->len == snapshot_file_len); 406 | 407 | return 0; 408 | } 409 | ``` 410 | 411 | #### Receiving a snapshot 412 | 413 | The leader node can send a snapshot to the follower node. Raft library will call required callbacks when that happens: 414 | 415 | ```c 416 | // This callback is to clear the temporary snapshot file on the disk. 417 | // Remember, the leader can take a newer snapshot and start sending it. 418 | // In that case, the Raft library will instruct application to delete the partial 419 | // file of the previous snapshot. 420 | int raft_clear_snapshot_f(raft_server_t *raft, void *user_data); 421 | 422 | int raft_store_snapshot_chunk_f(raft_server_t *raft, void *user_data, raft_index_t snapshot_index, raft_size_t offset, raft_snapshot_chunk_t *chunk); 423 | ``` 424 | 425 | An example implementation of `raft_store_snapshot_chunk_f` would be: 426 | 427 | ```c 428 | 429 | int app_raft_store_snapshot_impl(raft_server_t *r, 430 | void *user_data, 431 | raft_index_t snapshot_index, 432 | raft_size_t offset, 433 | raft_snapshot_chunk_t *chunk) 434 | { 435 | int flags = O_WRONLY | O_CREAT; 436 | 437 | if (offset == 0) { 438 | flags |= O_TRUNC; 439 | } 440 | 441 | int fd = open("temp_snapshot_file.db", flags, S_IWUSR | S_IRUSR); 442 | if (fd == -1) { 443 | return -1; 444 | } 445 | 446 | off_t ret_offset = lseek(fd, offset, SEEK_SET); 447 | if (ret_offset != (off_t) offset) { 448 | close(fd); 449 | return -1; 450 | } 451 | 452 | size_t len = write(fd, chunk->data, chunk->len); 453 | if (len != chunk->len) { 454 | close(fd); 455 | return -1; 456 | } 457 | 458 | close(fd); 459 | return 0; 460 | } 461 | 462 | ``` 463 | 464 | #### Loading the received snapshot file 465 | 466 | When a snapshot is received fully, the Raft library will call `raft_load_snapshot_f` callback. 467 | 468 | These are the steps when you want to load the received snapshot. Inside the callback: 469 | 470 | - Call `raft_begin_load_snapshot()` 471 | - Read the snapshot into your application 472 | - Configure the Raft library from the application. 473 | - Call `raft_end_load_snapshot()` 474 | 475 | An example implementation would be: 476 | 477 | ```c 478 | int app_raft_load_snapshot(raft_server_t *r, 479 | void *user_data, 480 | raft_term_t snapshot_term, 481 | raft_index_t snapshot_index) 482 | { 483 | // See the function app_raft_store_snapshot() above. The assumption is 484 | // application stores incoming chunks in a file called "temp_snapshot_file.db". 485 | // If we are inside this callback, it means we've received all the chunks, and 486 | // we can rename it as the current snapshot file. 487 | int ret = rename("temp_snapshot_file.db", "final_snapshot_file.db"); 488 | if (ret != 0) { 489 | return -1; 490 | } 491 | 492 | ret = raft_begin_load_snapshot(r, snapshot_term, snapshot_index); 493 | if (ret != 0) { 494 | return ret; 495 | } 496 | 497 | // Configure the Raft library from the configuration in the snapshot 498 | // See the example implementation above for more details about this function 499 | app_configure_from_snapshot(); 500 | raft_end_load_snapshot(rr->raft); 501 | 502 | // Probably, you need to create mmap of the snapshot file or read it into the 503 | // memory to be prepared for the `raft_get_snapshot_chunk_f` callback. 504 | // See the example above for the `raft_get_snapshot_chunk_f` callback for more details. 505 | 506 | return 0; 507 | } 508 | ``` 509 | 510 | Adding and removing nodes 511 | ------------------ 512 | 513 | You can add or remove a node by submitting configuration change entries. Once the entry is applied, you can submit another configuration change entry. Only one configuration change is allowed at a time. 514 | 515 | #### Adding a node 516 | Adding a node is a two-step operation. In the first step, you wait until the new node catches up with the leader. A node with a slow connection may not catch up with the leader. In this case, adding the node without waiting to obtain enough logs would cause unavailability. To prevent that, adding a node is a two-step operation. 517 | 518 | e.g. 519 | A single node cluster adds a new node directly. The majority in the cluster becomes two. To commit an entry, we need to replicate it to both nodes, but we cannot replicate a new entry until the new node gets all the existing entries. So, the cluster cannot commit an entry until the new node catches up. 520 | 521 | 522 | Add a node in two steps: 523 | 524 | - Submit an entry with the type `RAFT_LOGTYPE_ADD_NONVOTING_NODE`. 525 | - The Raft Library will call `raft_node_has_sufficient_logs_f` callback once the new node obtains enough log entries and catches up with the leader. 526 | - Inside that callback, you can submit an entry with the type `RAFT_LOGTYPE_ADD_NODE`. 527 | - When that entry is applied, the configuration change will be final. 528 | 529 | e.g: 530 | 531 | ```c 532 | 533 | struct app_node_config { 534 | raft_node_id_t id; 535 | int port; 536 | char host[128]; 537 | }; 538 | 539 | // Step-0 This is an example implementation of `raft_get_node_id_f` callback. 540 | raft_node_id_t app_log_get_node_id(raft_server_t *r, 541 | void *user_data, 542 | raft_entry_t *entry, 543 | raft_index_t entry_idx) 544 | { 545 | struct app_node_config *cfg = (struct app_node_config *) entry->data; 546 | return cfg->id; 547 | } 548 | 549 | // Step-1 550 | void app_add_node(raft_server_t *r) 551 | { 552 | struct app_node_config cfg = { 553 | .id = 9999999, 554 | .port = 5000, 555 | .host = "localhost" 556 | }; 557 | 558 | raft_entry_req_t *entry = raft_entry_new(sizeof(cfg)); 559 | entry->type = RAFT_LOGTYPE_ADD_NONVOTING_NODE; 560 | memcpy(entry->data, &cfg, sizeof(cfg)); 561 | 562 | int e = raft_recv_entry(r, entry, NULL); 563 | if (e != 0) { 564 | // handle failure 565 | } 566 | raft_entry_release(entry); 567 | } 568 | 569 | // Step-2 (Callback will be called when the new node catches up) 570 | int app_node_has_sufficient_logs(raft_server_t *r, 571 | void *user_data, 572 | raft_node_t *raft_node) 573 | { 574 | struct app_node_config cfg = { 575 | .id = 9999999, 576 | .port = 5000, 577 | .host = "localhost" 578 | }; 579 | 580 | raft_entry_req_t *entry = raft_entry_new(sizeof(cfg)); 581 | entry->type = RAFT_LOGTYPE_ADD_NODE; 582 | memcpy(entry->data, &cfg, sizeof(cfg)); 583 | 584 | int e = raft_recv_entry(r, entry, &response); 585 | raft_entry_release(entry); 586 | 587 | return e; 588 | } 589 | 590 | // Step-3 This is an example implementation of `applylog` callback. 591 | int app_applylog(raft_server_t *r, 592 | void *user_data, 593 | raft_entry_t *entry, 594 | raft_index_t entry_idx) 595 | { 596 | struct app_node_config *cfg; 597 | 598 | switch (entry->type) { 599 | case RAFT_LOGTYPE_ADD_NONVOTING_NODE: 600 | cfg = (struct app_node_config *) entry->data; 601 | printf("node_id:%d added as non-voting node", cfg->id); 602 | break; 603 | case RAFT_LOGTYPE_REMOVE_NODE: 604 | cfg = (struct app_node_config *) entry->data; 605 | printf("node_id:%d added as voting node", cfg->id); 606 | break; 607 | 608 | return 0; 609 | } 610 | 611 | ``` 612 | 613 | #### Removing a node 614 | 615 | - Submit an entry with the type `RAFT_LOGTYPE_REMOVE_NODE` 616 | - Node will be removed when entry is applied. 617 | 618 | ```c 619 | 620 | struct app_node_config { 621 | raft_node_id_t id; 622 | }; 623 | 624 | // Step-0 This is an example implementation of `raft_get_node_id_f` callback. 625 | raft_node_id_t app_log_get_node_id(raft_server_t *r, 626 | void *user_data, 627 | raft_entry_t *entry, 628 | raft_index_t entry_idx) 629 | { 630 | struct app_node_config *cfg = (struct app_node_config *) entry->data; 631 | return cfg->id; 632 | } 633 | 634 | // Step-1 635 | void app_remove_node() 636 | { 637 | raft_entry_req_t *entry = raft_entry_new(sizeof(cfg)); 638 | entry->type = RAFT_LOGTYPE_REMOVE_NODE; 639 | memcpy(entry->data, &cfg, sizeof(cfg)); 640 | 641 | int e = raft_recv_entry(r, entry, &resp); 642 | if (e != 0) { 643 | // handle error 644 | } 645 | raft_entry_release(entry); 646 | } 647 | 648 | // Step-2 This is an example implementation of `applylog` callback. 649 | int app_applylog(raft_server_t *r, 650 | void *user_data, 651 | raft_entry_t *entry, 652 | raft_index_t entry_idx) 653 | { 654 | struct app_node_config *cfg; 655 | 656 | switch (entry->type) { 657 | case RAFT_LOGTYPE_REMOVE_NODE: 658 | cfg = (struct app_node_config *) entry->data; 659 | printf("node_id:%d has been removed", cfg->id); 660 | break; 661 | 662 | return 0; 663 | } 664 | 665 | ``` -------------------------------------------------------------------------------- /include/raft_log.h: -------------------------------------------------------------------------------- 1 | #ifndef RAFT_LOG_H_ 2 | #define RAFT_LOG_H_ 3 | 4 | #include "raft_types.h" 5 | 6 | /** Backward compatible callbacks for log events */ 7 | 8 | typedef struct { 9 | /** Callback for adding an entry to the log 10 | * For safety reasons this callback MUST flush the change to disk. 11 | * Return 0 on success. 12 | * Return RAFT_ERR_SHUTDOWN if you want the server to shutdown. */ 13 | 14 | raft_logentry_event_f log_offer; 15 | 16 | /** Callback for removing the oldest entry from the log 17 | * For safety reasons this callback MUST flush the change to disk. 18 | * @note The callback does not need to call raft_entry_release() as 19 | * no references are implicitly held. If access to the entry is 20 | * desired after the callback returns, raft_entry_hold() should be 21 | * used. 22 | */ 23 | raft_logentry_event_f log_poll; 24 | 25 | /** Callback for removing the youngest entry from the log 26 | * For safety reasons this callback MUST flush the change to disk. 27 | * @note The callback does not need to call raft_entry_release() as 28 | * no references are implicitly held. If access to the entry is 29 | * desired after the callback returns, raft_entry_hold() should be 30 | * used. 31 | */ 32 | raft_logentry_event_f log_pop; 33 | 34 | /** Callback called for every existing log entry when clearing the log. 35 | * If memory was malloc'd in log_offer and the entry doesn't get a chance 36 | * to go through log_poll or log_pop, this is the last chance to free it. 37 | */ 38 | raft_logentry_event_f log_clear; 39 | } raft_log_cbs_t; 40 | 41 | typedef struct raft_log raft_log_t; 42 | 43 | raft_log_t *raft_log_new(void); 44 | 45 | raft_log_t *raft_log_alloc(raft_index_t initial_size); 46 | 47 | void raft_log_set_callbacks(raft_log_t *me, raft_log_cbs_t *funcs, void *raft); 48 | 49 | void raft_log_free(raft_log_t *me); 50 | 51 | void raft_log_clear(raft_log_t *me); 52 | 53 | void raft_log_clear_entries(raft_log_t *me); 54 | 55 | /** 56 | * Add entry to log. 57 | * Don't add entry if we've already added this entry (based off ID) 58 | * Don't add entries with ID=0 59 | * @return 0 if unsuccessful; 1 otherwise */ 60 | int raft_log_append_entry(raft_log_t *me, raft_entry_t *c); 61 | 62 | /** 63 | * @return number of entries held within log */ 64 | raft_index_t raft_log_count(raft_log_t *me); 65 | 66 | /** 67 | * Delete all logs from this log onwards */ 68 | int raft_log_delete(raft_log_t *me, raft_index_t idx); 69 | 70 | /** 71 | * Empty the queue. */ 72 | void raft_log_empty(raft_log_t *me); 73 | 74 | /** 75 | * Remove oldest entry. Set *etyp to oldest entry on success. */ 76 | int raft_log_poll(raft_log_t *me, raft_entry_t **etyp); 77 | 78 | /** Get an array of entries from this index onwards. 79 | * This is used for batching. 80 | */ 81 | raft_entry_t **raft_log_get_from_idx(raft_log_t *me, 82 | raft_index_t idx, 83 | raft_index_t *n_etys); 84 | 85 | raft_entry_t *raft_log_get_at_idx(raft_log_t *me, raft_index_t idx); 86 | 87 | /** 88 | * @return youngest entry */ 89 | raft_entry_t *raft_log_peektail(raft_log_t *me); 90 | 91 | raft_index_t raft_log_get_current_idx(raft_log_t *me); 92 | 93 | int raft_log_load_from_snapshot(raft_log_t *me, 94 | raft_index_t idx, 95 | raft_term_t term); 96 | 97 | raft_index_t raft_log_get_base(raft_log_t *me); 98 | 99 | #endif /* RAFT_LOG_H_ */ 100 | -------------------------------------------------------------------------------- /include/raft_private.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, Willem-Hendrik Thiart 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | * 6 | * @file 7 | * @author Willem Thiart himself@willemthiart.com 8 | */ 9 | 10 | #ifndef RAFT_PRIVATE_H_ 11 | #define RAFT_PRIVATE_H_ 12 | 13 | #include "raft_types.h" 14 | 15 | struct raft_log_impl; 16 | 17 | typedef struct raft_read_request { 18 | raft_index_t read_idx; 19 | 20 | raft_msg_id_t msg_id; 21 | raft_read_request_callback_f cb; 22 | void *cb_arg; 23 | 24 | struct raft_read_request *next; 25 | } raft_read_request_t; 26 | 27 | struct raft_server { 28 | /* Persistent state: */ 29 | 30 | /* the server's best guess of what the current term is 31 | * starts at zero */ 32 | raft_term_t current_term; 33 | 34 | /* The candidate the server voted for in its current term, 35 | * or Nil if it hasn't voted for any. */ 36 | raft_node_id_t voted_for; 37 | 38 | /* log storage engine */ 39 | const struct raft_log_impl *log_impl; 40 | void *log; 41 | 42 | /* Volatile state: */ 43 | 44 | /* idx of highest log entry known to be committed */ 45 | raft_index_t commit_idx; 46 | 47 | /* idx of highest log entry applied to state machine */ 48 | raft_index_t last_applied_idx; 49 | 50 | /* term of the highest log entry applied to the state machine */ 51 | raft_term_t last_applied_term; 52 | 53 | /* follower/leader/candidate indicator */ 54 | raft_state_e state; 55 | 56 | /* amount of time left till timeout */ 57 | raft_time_t timeout_elapsed; 58 | 59 | /* timestamp in milliseconds */ 60 | raft_time_t timestamp; 61 | 62 | /* deadline to stop executing operations */ 63 | raft_time_t exec_deadline; 64 | 65 | /* non-zero if there are ready to be executed operations. */ 66 | int pending_operations; 67 | 68 | raft_node_t** nodes; 69 | int num_nodes; 70 | 71 | /* timer interval to check if we still have quorum */ 72 | raft_time_t quorum_timeout; 73 | 74 | /* latest quorum id for the previous quorum_timeout round */ 75 | raft_msg_id_t last_acked_msg_id; 76 | 77 | /* what this node thinks is the node ID of the current leader or 78 | * RAFT_NODE_ID_NONE if there isn't a known current leader. */ 79 | raft_node_id_t leader_id; 80 | 81 | /* callbacks */ 82 | raft_cbs_t cb; 83 | void* udata; 84 | 85 | /* my node ID */ 86 | raft_node_t* node; 87 | 88 | /* the log index which has a voting cfg change, otherwise -1 */ 89 | raft_index_t voting_cfg_change_log_idx; 90 | 91 | int snapshot_in_progress; 92 | 93 | /* Last compacted snapshot */ 94 | raft_index_t snapshot_last_idx; 95 | raft_term_t snapshot_last_term; 96 | 97 | /* Next index/term values stored during snapshot */ 98 | raft_index_t next_snapshot_last_idx; 99 | raft_term_t next_snapshot_last_term; 100 | 101 | /* Last included index of the incoming snapshot */ 102 | raft_index_t snapshot_recv_idx; 103 | 104 | /* Current offset of the incoming snapshot */ 105 | raft_size_t snapshot_recv_offset; 106 | 107 | /* Read requests that await a network round trip to confirm 108 | * we're still the leader. 109 | */ 110 | raft_msg_id_t msg_id; 111 | 112 | raft_read_request_t *read_queue_head; 113 | raft_read_request_t *read_queue_tail; 114 | 115 | raft_node_id_t node_transferring_leader_to; /* Leader transfer target. */ 116 | raft_time_t transfer_leader_time; /* Leader transfer timeout. */ 117 | int sent_timeout_now; /* If we've already sent timeout_now message. */ 118 | 119 | 120 | /* Index of the log entry that need to be written to the disk. Only useful 121 | * when auto flush is disabled. */ 122 | raft_index_t next_sync_index; 123 | 124 | raft_time_t election_timeout_rand; 125 | 126 | /* Configuration parameters */ 127 | 128 | raft_time_t election_timeout; /* Timeout for a node to start an election */ 129 | raft_time_t request_timeout; /* Heartbeat timeout */ 130 | int nonblocking_apply; /* Apply entries even when snapshot is in progress */ 131 | int auto_flush; /* Automatically call raft_flush() */ 132 | int log_enabled; /* Enable library logs */ 133 | int disable_apply; /* Do not apply entries, useful for testing */ 134 | 135 | raft_server_stats_t stats; 136 | }; 137 | 138 | int raft_election_start(raft_server_t *me, int skip_precandidate); 139 | 140 | int raft_become_candidate(raft_server_t *me); 141 | 142 | int raft_become_precandidate(raft_server_t *me); 143 | 144 | void raft_randomize_election_timeout(raft_server_t *me); 145 | 146 | void raft_update_quorum_meta(raft_server_t* me, raft_msg_id_t id); 147 | 148 | /** 149 | * @return 0 on error */ 150 | int raft_send_requestvote(raft_server_t* me, raft_node_t* node); 151 | 152 | int raft_send_appendentries(raft_server_t* me, raft_node_t* node); 153 | 154 | int raft_send_appendentries_all(raft_server_t* me); 155 | 156 | /** 157 | * Apply entry at lastApplied + 1. Entry becomes 'committed'. 158 | * @return 1 if entry committed, 0 otherwise */ 159 | int raft_apply_entry(raft_server_t* me); 160 | 161 | void raft_set_last_applied_idx(raft_server_t *me, raft_index_t idx); 162 | 163 | void raft_set_state(raft_server_t *me, int state); 164 | 165 | raft_node_t *raft_node_new(void *udata, raft_node_id_t id, int voting); 166 | 167 | void raft_node_free(raft_node_t *node); 168 | 169 | void raft_node_set_match_idx(raft_node_t *node, raft_index_t idx); 170 | 171 | raft_index_t raft_node_get_match_idx(raft_node_t *node); 172 | 173 | raft_index_t raft_node_get_next_idx(raft_node_t *node); 174 | 175 | void raft_node_clear_flags(raft_node_t *node); 176 | 177 | void raft_node_set_voted_for_me(raft_node_t *node, int vote); 178 | 179 | int raft_node_has_vote_for_me(raft_node_t *node); 180 | 181 | void raft_node_set_has_sufficient_logs(raft_node_t *node, int sufficient_logs); 182 | 183 | 184 | 185 | /** Check if a node has sufficient logs to be able to join the cluster. 186 | **/ 187 | int raft_node_has_sufficient_logs(raft_node_t *node); 188 | 189 | int raft_is_single_node_voting_cluster(raft_server_t *me); 190 | 191 | int raft_votes_is_majority(int nnodes, int nvotes); 192 | 193 | void raft_node_set_match_msgid(raft_node_t *node, raft_msg_id_t msgid); 194 | raft_msg_id_t raft_node_get_match_msgid(raft_node_t *node); 195 | 196 | void raft_node_set_next_msgid(raft_node_t *node, raft_msg_id_t msgid); 197 | raft_msg_id_t raft_node_get_next_msgid(raft_node_t *node); 198 | 199 | /* Heap functions */ 200 | extern void *(*raft_malloc)(size_t size); 201 | extern void *(*raft_calloc)(size_t nmemb, size_t size); 202 | extern void *(*raft_realloc)(void *ptr, size_t size); 203 | extern void (*raft_free)(void *ptr); 204 | 205 | /* update the max_seen_msg_id for this node */ 206 | void raft_node_update_max_seen_msg_id(raft_node_t *node, raft_msg_id_t msg_id); 207 | /* get the max message id this server has seen from its the specified node */ 208 | raft_msg_id_t raft_node_get_max_seen_msg_id(raft_node_t *node); 209 | /* get the server's current msg_id */ 210 | raft_msg_id_t raft_get_msg_id(raft_server_t *me); 211 | 212 | /* attempt to abort the leadership transfer */ 213 | void raft_reset_transfer_leader(raft_server_t* me, int timed_out); 214 | 215 | raft_size_t raft_node_get_snapshot_offset(raft_node_t *node); 216 | 217 | void raft_node_set_snapshot_offset(raft_node_t *node, raft_size_t offset); 218 | 219 | int raft_periodic_internal(raft_server_t *me, raft_time_t milliseconds); 220 | 221 | int raft_exec_operations(raft_server_t *me); 222 | 223 | /** Become follower. This may be used to give up leadership. It does not change 224 | * currentTerm. */ 225 | void raft_become_follower(raft_server_t *me); 226 | 227 | /** Determine if entry is voting configuration change. 228 | * @param[in] ety The entry to query. 229 | * @return 1 if this is a voting configuration change. */ 230 | int raft_entry_is_voting_cfg_change(raft_entry_t* ety); 231 | 232 | /** Determine if entry is configuration change. 233 | * @param[in] ety The entry to query. 234 | * @return 1 if this is a configuration change. */ 235 | int raft_entry_is_cfg_change(raft_entry_t* ety); 236 | 237 | /** Apply all entries up to the commit index 238 | * @return 239 | * 0 on success; 240 | * RAFT_ERR_SHUTDOWN when server MUST shutdown */ 241 | int raft_apply_all(raft_server_t* me); 242 | 243 | /** Set the commit idx. 244 | * This should be used to reload persistent state, ie. the commit_idx field. 245 | * @param[in] commit_idx The new commit index. */ 246 | void raft_set_commit_idx(raft_server_t *me, raft_index_t commit_idx); 247 | 248 | /** Vote for a server. 249 | * This should be used to reload persistent state, ie. the voted-for field. 250 | * @param[in] node The server to vote for 251 | * @return 252 | * 0 on success */ 253 | int raft_vote(raft_server_t* me, raft_node_t* node); 254 | 255 | /** Vote for a server. 256 | * This should be used to reload persistent state, ie. the voted-for field. 257 | * @param[in] nodeid The server to vote for by nodeid 258 | * @return 259 | * 0 on success */ 260 | int raft_vote_for_nodeid(raft_server_t* me, raft_node_id_t nodeid); 261 | 262 | 263 | /** 264 | * @return number of votes this server has received this election */ 265 | int raft_get_nvotes_for_me(raft_server_t* me); 266 | 267 | /** 268 | * @return currently elapsed timeout in milliseconds */ 269 | raft_time_t raft_get_timeout_elapsed(raft_server_t *me); 270 | 271 | /** Add an entry to the server's log. 272 | * This should be used to reload persistent state, ie. the commit log. 273 | * @param[in] ety The entry to be appended 274 | * @return 275 | * 0 on success; 276 | * RAFT_ERR_SHUTDOWN server should shutdown 277 | * RAFT_ERR_NOMEM memory allocation failure */ 278 | int raft_append_entry(raft_server_t* me, raft_entry_t* ety); 279 | 280 | /** Check if a voting change is in progress 281 | * @param[in] raft The Raft server 282 | * @return 1 if a voting change is in progress */ 283 | int raft_voting_change_is_in_progress(raft_server_t *me); 284 | 285 | #endif /* RAFT_PRIVATE_H_ */ 286 | -------------------------------------------------------------------------------- /include/raft_types.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RAFT_DEFS_H_ 3 | #define RAFT_DEFS_H_ 4 | 5 | /** 6 | * Unique entry ids are mostly used for debugging and nothing else, 7 | * so there is little harm if they collide. 8 | */ 9 | typedef int raft_entry_id_t; 10 | 11 | /** 12 | * Monotonic term counter. 13 | */ 14 | typedef long int raft_term_t; 15 | 16 | /** 17 | * Monotonic log entry index. 18 | * 19 | * This is also used to as an entry count size type. 20 | */ 21 | typedef long int raft_index_t; 22 | 23 | /** 24 | * Id used to group entries into sessions 25 | */ 26 | typedef unsigned long long raft_session_t; 27 | 28 | /** 29 | * Size type. This should be at least 64 bits. 30 | */ 31 | typedef unsigned long long raft_size_t; 32 | 33 | /** 34 | * Unique node identifier. 35 | */ 36 | typedef int raft_node_id_t; 37 | 38 | /** 39 | * Unique message identifier. 40 | */ 41 | typedef unsigned long raft_msg_id_t; 42 | 43 | /** 44 | * Time type. 45 | */ 46 | typedef long long raft_time_t; 47 | 48 | #endif /* RAFT_DEFS_H_ */ 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raft", 3 | "version": "0.2.0", 4 | "repo": "willemt/raft", 5 | "description": "C implementation of the Raft Consensus protocol, BSD licensed", 6 | "keywords": ["raft", "consensus", "protocol"], 7 | "src": [ 8 | "include/raft.h", 9 | "include/raft_log.h", 10 | "include/raft_private.h", 11 | "src/raft_log.c", 12 | "src/raft_node.c", 13 | "src/raft_server.c", 14 | "src/raft_server_properties.c" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /scripts/amalgamate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create amalgamated source file, prints to stdout 4 | 5 | echo '/* 6 | 7 | This source file is the amalgamated version of the original. 8 | Please see github.com/willemt/raft for the original version. 9 | ' 10 | git log | head -n1 | sed 's/commit/HEAD commit:/g' 11 | echo ' 12 | ' 13 | cat LICENSE 14 | echo ' 15 | */ 16 | ' 17 | 18 | echo ' 19 | #ifndef RAFT_AMALGAMATION_SH 20 | #define RAFT_AMALGAMATION_SH 21 | ' 22 | 23 | cat include/raft.h 24 | cat include/raft_*.h 25 | cat src/raft*.c | sed 's/#include "raft.*.h"//g' | sed 's/__/__raft__/g' 26 | 27 | echo '#endif /* RAFT_AMALGAMATIONE_SH */' 28 | -------------------------------------------------------------------------------- /src/raft_log.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, Willem-Hendrik Thiart 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | * 6 | * @file 7 | * @brief ADT for managing Raft log entries (aka entries) 8 | * @author Willem Thiart himself@willemthiart.com 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "raft.h" 15 | #include "raft_private.h" 16 | #include "raft_log.h" 17 | 18 | #define INITIAL_CAPACITY 10 19 | 20 | struct raft_log { 21 | /* size of array */ 22 | raft_index_t size; 23 | 24 | /* the amount of elements in the array */ 25 | raft_index_t count; 26 | 27 | /* position of the queue */ 28 | raft_index_t front, back; 29 | 30 | /* we compact the log, and thus need to increment the Base Log Index */ 31 | raft_index_t base; 32 | 33 | raft_entry_t **entries; 34 | 35 | /* callbacks */ 36 | raft_log_cbs_t cb; 37 | 38 | void *raft; 39 | }; 40 | 41 | static raft_index_t mod(raft_index_t a, raft_index_t b) 42 | { 43 | raft_index_t r = a % b; 44 | return r < 0 ? r + b : r; 45 | } 46 | 47 | static int ensure_capacity(raft_log_t *me) 48 | { 49 | raft_entry_t **temp; 50 | 51 | if (me->count < me->size) { 52 | return 0; 53 | } 54 | 55 | temp = raft_calloc(1, sizeof(*temp) * me->size * 2); 56 | if (!temp) { 57 | return RAFT_ERR_NOMEM; 58 | } 59 | 60 | for (raft_index_t i = 0, j = me->front; i < me->count; i++, j++) { 61 | if (j == me->size) { 62 | j = 0; 63 | } 64 | temp[i] = me->entries[j]; 65 | } 66 | 67 | /* clean up old entries */ 68 | raft_free(me->entries); 69 | 70 | me->size *= 2; 71 | me->entries = temp; 72 | me->front = 0; 73 | me->back = me->count; 74 | 75 | return 0; 76 | } 77 | 78 | int raft_log_load_from_snapshot(raft_log_t *me, 79 | raft_index_t idx, 80 | raft_term_t term) 81 | { 82 | (void) term; 83 | 84 | raft_log_clear_entries(me); 85 | raft_log_clear(me); 86 | me->base = idx; 87 | 88 | return 0; 89 | } 90 | 91 | raft_log_t *raft_log_alloc(raft_index_t initial_size) 92 | { 93 | raft_log_t *me = raft_calloc(1, sizeof(*me)); 94 | if (!me) { 95 | return NULL; 96 | } 97 | 98 | me->size = initial_size; 99 | raft_log_clear(me); 100 | 101 | me->entries = raft_calloc(1, sizeof(*me->entries) * me->size); 102 | if (!me->entries) { 103 | raft_free(me); 104 | return NULL; 105 | } 106 | 107 | return me; 108 | } 109 | 110 | raft_log_t *raft_log_new(void) 111 | { 112 | return raft_log_alloc(INITIAL_CAPACITY); 113 | } 114 | 115 | void raft_log_set_callbacks(raft_log_t *me, raft_log_cbs_t *funcs, void *raft) 116 | { 117 | me->raft = raft; 118 | me->cb = *funcs; 119 | } 120 | 121 | void raft_log_clear(raft_log_t *me) 122 | { 123 | me->count = 0; 124 | me->back = 0; 125 | me->front = 0; 126 | me->base = 0; 127 | } 128 | 129 | void raft_log_clear_entries(raft_log_t *me) 130 | { 131 | if (!me->count || !me->cb.log_clear) { 132 | return; 133 | } 134 | 135 | for (raft_index_t i = me->base; i <= me->base + me->count; i++) { 136 | me->cb.log_clear(me->raft, raft_get_udata(me->raft), 137 | me->entries[(me->front + i - me->base) % me->size], i); 138 | } 139 | } 140 | 141 | int raft_log_append_entry(raft_log_t *me, raft_entry_t *ety) 142 | { 143 | int e; 144 | raft_index_t idx = me->base + me->count + 1; 145 | 146 | e = ensure_capacity(me); 147 | if (e != 0) { 148 | return e; 149 | } 150 | 151 | if (me->cb.log_offer) { 152 | void *udata = raft_get_udata(me->raft); 153 | 154 | e = me->cb.log_offer(me->raft, udata, ety, idx); 155 | if (e != 0) { 156 | return e; 157 | } 158 | } 159 | 160 | me->entries[me->back] = ety; 161 | me->count++; 162 | me->back++; 163 | me->back = me->back % me->size; 164 | 165 | return 0; 166 | } 167 | 168 | raft_entry_t **raft_log_get_from_idx(raft_log_t *me, 169 | raft_index_t idx, 170 | raft_index_t *n_etys) 171 | { 172 | raft_index_t i; 173 | 174 | assert(0 <= idx - 1); 175 | 176 | if (me->base + me->count < idx || idx <= me->base) { 177 | *n_etys = 0; 178 | return NULL; 179 | } 180 | 181 | /* idx starts at 1 */ 182 | idx -= 1; 183 | 184 | i = (me->front + idx - me->base) % me->size; 185 | 186 | /* log entries until the end of the log */ 187 | *n_etys = (i < me->back) ? (me->back - i) : (me->size - i); 188 | 189 | return &me->entries[i]; 190 | } 191 | 192 | raft_entry_t *raft_log_get_at_idx(raft_log_t *me, raft_index_t idx) 193 | { 194 | raft_index_t i; 195 | 196 | if (idx == 0) { 197 | return NULL; 198 | } 199 | 200 | if (me->base + me->count < idx || idx <= me->base) { 201 | return NULL; 202 | } 203 | 204 | /* idx starts at 1 */ 205 | idx -= 1; 206 | 207 | i = (me->front + idx - me->base) % me->size; 208 | return me->entries[i]; 209 | } 210 | 211 | raft_index_t raft_log_count(raft_log_t *me) 212 | { 213 | return me->count; 214 | } 215 | 216 | static int log_delete(raft_log_t *me, raft_index_t idx) 217 | { 218 | if (idx == 0) { 219 | return -1; 220 | } 221 | 222 | if (idx < me->base) { 223 | idx = me->base; 224 | } 225 | 226 | for (; idx <= me->base + me->count && me->count;) { 227 | raft_index_t idx_tmp = me->base + me->count; 228 | raft_index_t back = mod(me->back - 1, me->size); 229 | 230 | if (me->cb.log_pop) { 231 | int e = me->cb.log_pop(me->raft, raft_get_udata(me->raft), 232 | me->entries[back], idx_tmp); 233 | if (e != 0) { 234 | return e; 235 | } 236 | } 237 | 238 | raft_entry_release(me->entries[back]); 239 | me->entries[back] = NULL; 240 | me->back = back; 241 | me->count--; 242 | } 243 | 244 | return 0; 245 | } 246 | 247 | int raft_log_delete(raft_log_t *me, raft_index_t idx) 248 | { 249 | return log_delete(me, idx); 250 | } 251 | 252 | int raft_log_poll(raft_log_t *me, raft_entry_t **etyp) 253 | { 254 | raft_index_t idx = me->base + 1; 255 | 256 | if (me->count == 0) { 257 | return -1; 258 | } 259 | 260 | raft_entry_t *elem = me->entries[me->front]; 261 | 262 | if (me->cb.log_poll) { 263 | int e = me->cb.log_poll(me->raft, raft_get_udata(me->raft), 264 | me->entries[me->front], idx); 265 | if (e != 0) { 266 | return e; 267 | } 268 | } 269 | 270 | raft_entry_release(me->entries[me->front]); 271 | 272 | me->entries[me->front] = NULL; 273 | me->front++; 274 | me->front = me->front % me->size; 275 | me->count--; 276 | me->base++; 277 | 278 | *etyp = elem; 279 | 280 | return 0; 281 | } 282 | 283 | raft_entry_t *raft_log_peektail(raft_log_t *me) 284 | { 285 | if (me->count == 0) { 286 | return NULL; 287 | } 288 | 289 | raft_index_t tail = (me->back == 0) ? (me->size - 1) : (me->back - 1); 290 | 291 | return me->entries[tail]; 292 | } 293 | 294 | void raft_log_empty(raft_log_t *me) 295 | { 296 | me->front = 0; 297 | me->back = 0; 298 | me->count = 0; 299 | } 300 | 301 | void raft_log_free(raft_log_t *me) 302 | { 303 | if (!me) { 304 | return; 305 | } 306 | 307 | for (raft_index_t i = 0; i < me->size; i++) { 308 | raft_entry_t *ety = me->entries[i]; 309 | if (ety) { 310 | raft_entry_release(ety); 311 | } 312 | } 313 | raft_free(me->entries); 314 | raft_free(me); 315 | } 316 | 317 | raft_index_t raft_log_get_current_idx(raft_log_t *me) 318 | { 319 | return raft_log_count(me) + me->base; 320 | } 321 | 322 | raft_index_t raft_log_get_base(raft_log_t *me) 323 | { 324 | return me->base; 325 | } 326 | 327 | /** 328 | * The following functions wrap raft_log.c implementation to make it compatible 329 | * with raft_log_impl_t binding. 330 | * 331 | * The rationale for doing this and not modifying raft_log.c directly is to 332 | * leave test_log.c intact. 333 | * 334 | * @todo Ideally test_log.c should be implemented (and extended) to use 335 | * raft_log_impl_t, so it can become a test harness for testing arbitrary 336 | * log implementations. 337 | */ 338 | 339 | static void *log_init(void *raft, void *arg) 340 | { 341 | raft_log_t *log = raft_log_new(); 342 | if (arg) { 343 | raft_log_set_callbacks(log, arg, raft); 344 | } 345 | return log; 346 | } 347 | 348 | static void log_free(void *log) 349 | { 350 | raft_log_free(log); 351 | } 352 | 353 | static void log_reset(void *log, raft_index_t first_idx, raft_term_t term) 354 | { 355 | (void) term; 356 | 357 | raft_log_clear_entries(log); 358 | raft_log_clear(log); 359 | 360 | assert(first_idx >= 1); 361 | ((raft_log_t *) log)->base = first_idx - 1; 362 | } 363 | 364 | static int log_append(void *log, raft_entry_t *entry) 365 | { 366 | int e = raft_log_append_entry(log, entry); 367 | if (e == 0) { 368 | raft_entry_hold(entry); 369 | } 370 | 371 | return e; 372 | } 373 | 374 | static raft_entry_t *log_get(void *log, raft_index_t idx) 375 | { 376 | raft_entry_t *e = raft_log_get_at_idx(log, idx); 377 | if (e != NULL) { 378 | raft_entry_hold(e); 379 | } 380 | 381 | return e; 382 | } 383 | 384 | static raft_index_t log_get_batch(void *log, 385 | raft_index_t idx, 386 | raft_index_t entries_n, 387 | raft_entry_t **entries) 388 | { 389 | raft_index_t n; 390 | raft_entry_t **r = raft_log_get_from_idx(log, idx, &n); 391 | 392 | if (!r || n < 1) { 393 | return 0; 394 | } 395 | 396 | if (n > entries_n) { 397 | n = entries_n; 398 | } 399 | 400 | for (raft_index_t i = 0; i < n; i++) { 401 | entries[i] = r[i]; 402 | raft_entry_hold(entries[i]); 403 | } 404 | 405 | return n; 406 | } 407 | 408 | static int log_pop(void *log, raft_index_t from_idx) 409 | { 410 | return log_delete(log, from_idx); 411 | } 412 | 413 | static int log_poll(void *log, raft_index_t first_idx) 414 | { 415 | while (raft_log_get_base(log) + 1 < first_idx) { 416 | raft_entry_t *ety; 417 | int e = raft_log_poll(log, &ety); 418 | if (e < 0) { 419 | return e; 420 | } 421 | } 422 | 423 | return 0; 424 | } 425 | 426 | static raft_index_t log_first_idx(void *log) 427 | { 428 | return raft_log_get_base(log) + 1; 429 | } 430 | 431 | static raft_index_t log_current_idx(void *log) 432 | { 433 | return raft_log_get_current_idx(log); 434 | } 435 | 436 | static raft_index_t log_count(void *log) 437 | { 438 | return raft_log_count(log); 439 | } 440 | 441 | static int log_sync(void *log) 442 | { 443 | (void) log; 444 | return 0; 445 | } 446 | 447 | const raft_log_impl_t raft_log_internal_impl = { 448 | .init = log_init, 449 | .free = log_free, 450 | .reset = log_reset, 451 | .append = log_append, 452 | .poll = log_poll, 453 | .pop = log_pop, 454 | .get = log_get, 455 | .get_batch = log_get_batch, 456 | .first_idx = log_first_idx, 457 | .current_idx = log_current_idx, 458 | .count = log_count, 459 | .sync = log_sync 460 | }; 461 | -------------------------------------------------------------------------------- /src/raft_node.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, Willem-Hendrik Thiart 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | * 6 | * @file 7 | * @brief Representation of a peer 8 | * @author Willem Thiart himself@willemthiart.com 9 | * @version 0.1 10 | */ 11 | 12 | #include "raft.h" 13 | #include "raft_private.h" 14 | 15 | #define RAFT_NODE_VOTED_FOR_ME (1 << 0) 16 | #define RAFT_NODE_VOTING (1 << 1) 17 | #define RAFT_NODE_HAS_SUFFICIENT_LOG (1 << 2) 18 | #define RAFT_NODE_INACTIVE (1 << 3) 19 | #define RAFT_NODE_VOTING_COMMITTED (1 << 4) 20 | #define RAFT_NODE_ADDITION_COMMITTED (1 << 5) 21 | 22 | struct raft_node { 23 | void *udata; 24 | 25 | raft_index_t next_idx; 26 | raft_index_t match_idx; 27 | 28 | raft_msg_id_t next_msgid; 29 | raft_msg_id_t match_msgid; 30 | raft_msg_id_t max_seen_msgid; 31 | 32 | int flags; 33 | 34 | raft_node_id_t id; 35 | 36 | /* Next snapshot offset to send to this node */ 37 | raft_size_t snapshot_offset; 38 | }; 39 | 40 | raft_node_t *raft_node_new(void *udata, raft_node_id_t id, int voting) 41 | { 42 | raft_node_t *me; 43 | 44 | me = raft_calloc(1, sizeof(*me)); 45 | if (!me) { 46 | return NULL; 47 | } 48 | 49 | me->udata = udata; 50 | me->next_idx = 1; 51 | me->match_idx = 0; 52 | me->id = id; 53 | raft_node_set_voting(me, voting); 54 | 55 | return me; 56 | } 57 | 58 | void raft_node_free(raft_node_t *node) 59 | { 60 | raft_free(node); 61 | } 62 | 63 | raft_index_t raft_node_get_next_idx(raft_node_t *node) 64 | { 65 | return node->next_idx; 66 | } 67 | 68 | void raft_node_set_next_idx(raft_node_t *node, raft_index_t idx) 69 | { 70 | /* log index begins at 1 */ 71 | node->next_idx = idx < 1 ? 1 : idx; 72 | } 73 | 74 | raft_index_t raft_node_get_match_idx(raft_node_t *node) 75 | { 76 | return node->match_idx; 77 | } 78 | 79 | void raft_node_set_match_idx(raft_node_t *node, raft_index_t idx) 80 | { 81 | node->match_idx = idx; 82 | } 83 | 84 | void raft_node_set_match_msgid(raft_node_t *node, raft_msg_id_t msgid) 85 | { 86 | node->match_msgid = msgid; 87 | } 88 | 89 | raft_msg_id_t raft_node_get_match_msgid(raft_node_t *node) 90 | { 91 | return node->match_msgid; 92 | } 93 | 94 | void raft_node_set_next_msgid(raft_node_t *node, raft_msg_id_t msgid) 95 | { 96 | node->next_msgid = msgid; 97 | } 98 | 99 | raft_msg_id_t raft_node_get_next_msgid(raft_node_t *node) 100 | { 101 | return node->next_msgid; 102 | } 103 | 104 | void raft_node_update_max_seen_msg_id(raft_node_t *node, raft_msg_id_t msg_id) 105 | { 106 | if (node->max_seen_msgid < msg_id) { 107 | node->max_seen_msgid = msg_id; 108 | } 109 | } 110 | 111 | raft_msg_id_t raft_node_get_max_seen_msg_id(raft_node_t *node) 112 | { 113 | return node->max_seen_msgid; 114 | } 115 | 116 | void* raft_node_get_udata(raft_node_t *node) 117 | { 118 | return node->udata; 119 | } 120 | 121 | void raft_node_set_udata(raft_node_t *node, void* udata) 122 | { 123 | node->udata = udata; 124 | } 125 | 126 | void raft_node_clear_flags(raft_node_t *node) 127 | { 128 | node->flags = 0; 129 | } 130 | 131 | void raft_node_set_voted_for_me(raft_node_t *node, int vote) 132 | { 133 | if (vote) { 134 | node->flags |= RAFT_NODE_VOTED_FOR_ME; 135 | } else { 136 | node->flags &= ~RAFT_NODE_VOTED_FOR_ME; 137 | } 138 | } 139 | 140 | int raft_node_has_vote_for_me(raft_node_t *node) 141 | { 142 | return (node->flags & RAFT_NODE_VOTED_FOR_ME) != 0; 143 | } 144 | 145 | void raft_node_set_voting(raft_node_t *node, int voting) 146 | { 147 | if (voting) { 148 | node->flags |= RAFT_NODE_VOTING; 149 | } else { 150 | node->flags &= ~RAFT_NODE_VOTING; 151 | } 152 | } 153 | 154 | int raft_node_is_voting(raft_node_t *node) 155 | { 156 | return (node->flags & RAFT_NODE_VOTING && 157 | !(node->flags & RAFT_NODE_INACTIVE)); 158 | } 159 | 160 | int raft_node_has_sufficient_logs(raft_node_t *node) 161 | { 162 | return (node->flags & RAFT_NODE_HAS_SUFFICIENT_LOG) != 0; 163 | } 164 | 165 | void raft_node_set_has_sufficient_logs(raft_node_t *node, int sufficient_logs) 166 | { 167 | if (sufficient_logs) { 168 | node->flags |= RAFT_NODE_HAS_SUFFICIENT_LOG; 169 | } else { 170 | node->flags &= ~RAFT_NODE_HAS_SUFFICIENT_LOG; 171 | } 172 | } 173 | 174 | void raft_node_set_active(raft_node_t *node, int active) 175 | { 176 | if (!active) { 177 | node->flags |= RAFT_NODE_INACTIVE; 178 | } else { 179 | node->flags &= ~RAFT_NODE_INACTIVE; 180 | } 181 | } 182 | 183 | int raft_node_is_active(raft_node_t *node) 184 | { 185 | return (node->flags & RAFT_NODE_INACTIVE) == 0; 186 | } 187 | 188 | void raft_node_set_voting_committed(raft_node_t *node, int voting) 189 | { 190 | if (voting) { 191 | node->flags |= RAFT_NODE_VOTING_COMMITTED; 192 | } else { 193 | node->flags &= ~RAFT_NODE_VOTING_COMMITTED; 194 | } 195 | } 196 | 197 | int raft_node_is_voting_committed(raft_node_t *node) 198 | { 199 | return (node->flags & RAFT_NODE_VOTING_COMMITTED) != 0; 200 | } 201 | 202 | raft_node_id_t raft_node_get_id(raft_node_t *node) 203 | { 204 | return node != NULL ? node->id : RAFT_NODE_ID_NONE; 205 | } 206 | 207 | void raft_node_set_addition_committed(raft_node_t *node, int committed) 208 | { 209 | if (committed) { 210 | node->flags |= RAFT_NODE_ADDITION_COMMITTED; 211 | } else { 212 | node->flags &= ~RAFT_NODE_ADDITION_COMMITTED; 213 | } 214 | } 215 | 216 | int raft_node_is_addition_committed(raft_node_t *node) 217 | { 218 | return (node->flags & RAFT_NODE_ADDITION_COMMITTED) != 0; 219 | } 220 | 221 | raft_size_t raft_node_get_snapshot_offset(raft_node_t *node) 222 | { 223 | return node->snapshot_offset; 224 | } 225 | 226 | void raft_node_set_snapshot_offset(raft_node_t *node, raft_size_t offset) 227 | { 228 | node->snapshot_offset = offset; 229 | } 230 | -------------------------------------------------------------------------------- /src/raft_server_properties.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013, Willem-Hendrik Thiart 3 | * Use of this source code is governed by a BSD-style license that can be 4 | * found in the LICENSE file. 5 | * 6 | * @file 7 | * @author Willem Thiart himself@willemthiart.com 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "raft.h" 14 | #include "raft_private.h" 15 | 16 | raft_node_id_t raft_get_nodeid(raft_server_t *me) 17 | { 18 | return raft_node_get_id(me->node); 19 | } 20 | 21 | int raft_get_num_nodes(raft_server_t *me) 22 | { 23 | return me->num_nodes; 24 | } 25 | 26 | int raft_get_num_voting_nodes(raft_server_t *me) 27 | { 28 | int num = 0; 29 | 30 | for (int i = 0; i < me->num_nodes; i++) { 31 | if (raft_node_is_voting(me->nodes[i])) { 32 | num++; 33 | } 34 | } 35 | 36 | return num; 37 | } 38 | 39 | raft_time_t raft_get_timeout_elapsed(raft_server_t *me) 40 | { 41 | return me->timeout_elapsed; 42 | } 43 | 44 | raft_index_t raft_get_log_count(raft_server_t *me) 45 | { 46 | return me->log_impl->count(me->log); 47 | } 48 | 49 | raft_node_id_t raft_get_voted_for(raft_server_t *me) 50 | { 51 | return me->voted_for; 52 | } 53 | 54 | int raft_set_current_term(raft_server_t *me, const raft_term_t term) 55 | { 56 | if (me->current_term >= term) { 57 | return 0; 58 | } 59 | 60 | if (me->cb.persist_metadata) { 61 | int e = me->cb.persist_metadata(me, me->udata, term, RAFT_NODE_ID_NONE); 62 | if (e != 0) { 63 | return e; 64 | } 65 | } 66 | 67 | me->current_term = term; 68 | me->voted_for = RAFT_NODE_ID_NONE; 69 | 70 | return 0; 71 | } 72 | 73 | raft_term_t raft_get_current_term(raft_server_t *me) 74 | { 75 | return me->current_term; 76 | } 77 | 78 | raft_index_t raft_get_current_idx(raft_server_t *me) 79 | { 80 | return me->log_impl->current_idx(me->log); 81 | } 82 | 83 | void raft_set_commit_idx(raft_server_t *me, raft_index_t idx) 84 | { 85 | assert(me->commit_idx <= idx); 86 | assert(idx <= raft_get_current_idx(me)); 87 | me->commit_idx = idx; 88 | } 89 | 90 | void raft_set_last_applied_idx(raft_server_t *me, raft_index_t idx) 91 | { 92 | me->last_applied_idx = idx; 93 | } 94 | 95 | raft_index_t raft_get_last_applied_idx(raft_server_t *me) 96 | { 97 | return me->last_applied_idx; 98 | } 99 | 100 | raft_term_t raft_get_last_applied_term(raft_server_t *me) 101 | { 102 | return me->last_applied_term; 103 | } 104 | 105 | raft_index_t raft_get_commit_idx(raft_server_t *me) 106 | { 107 | return me->commit_idx; 108 | } 109 | 110 | void raft_set_state(raft_server_t *me, int state) 111 | { 112 | if (state == RAFT_STATE_LEADER) { 113 | me->leader_id = raft_node_get_id(me->node); 114 | } 115 | me->state = state; 116 | } 117 | 118 | int raft_get_state(raft_server_t *me) 119 | { 120 | return me->state; 121 | } 122 | 123 | const char *raft_get_state_str(raft_server_t *me) 124 | { 125 | switch (me->state) { 126 | case RAFT_STATE_FOLLOWER: 127 | return "follower"; 128 | case RAFT_STATE_PRECANDIDATE: 129 | return "pre-candidate"; 130 | case RAFT_STATE_CANDIDATE: 131 | return "candidate"; 132 | case RAFT_STATE_LEADER: 133 | return "leader"; 134 | default: 135 | return "unknown"; 136 | } 137 | } 138 | 139 | const char *raft_get_error_str(int err) 140 | { 141 | switch (err) { 142 | case RAFT_ERR_NOT_LEADER: 143 | return "not leader"; 144 | case RAFT_ERR_ONE_VOTING_CHANGE_ONLY: 145 | return "one voting change only"; 146 | case RAFT_ERR_SHUTDOWN: 147 | return "shutdown"; 148 | case RAFT_ERR_NOMEM: 149 | return "out of memory"; 150 | case RAFT_ERR_SNAPSHOT_IN_PROGRESS: 151 | return "snapshot is in progress"; 152 | case RAFT_ERR_SNAPSHOT_ALREADY_LOADED: 153 | return "snapshot already loaded"; 154 | case RAFT_ERR_INVALID_NODEID: 155 | return "invalid node id"; 156 | case RAFT_ERR_LEADER_TRANSFER_IN_PROGRESS: 157 | return "leader transfer is in progress"; 158 | case RAFT_ERR_DONE: 159 | return "done"; 160 | case RAFT_ERR_NOTFOUND: 161 | return "not found"; 162 | case RAFT_ERR_MISUSE: 163 | return "misuse"; 164 | case RAFT_ERR_TRYAGAIN: 165 | return "try again"; 166 | default: 167 | return "unknown error"; 168 | } 169 | } 170 | 171 | raft_node_t *raft_get_node(raft_server_t *me, raft_node_id_t nodeid) 172 | { 173 | for (int i = 0; i < me->num_nodes; i++) { 174 | if (nodeid == raft_node_get_id(me->nodes[i])) { 175 | return me->nodes[i]; 176 | } 177 | } 178 | 179 | return NULL; 180 | } 181 | 182 | raft_node_t *raft_get_my_node(raft_server_t *me) 183 | { 184 | return me->node; 185 | } 186 | 187 | raft_node_t *raft_get_node_from_idx(raft_server_t *me, const raft_index_t idx) 188 | { 189 | return me->nodes[idx]; 190 | } 191 | 192 | raft_node_id_t raft_get_leader_id(raft_server_t *me) 193 | { 194 | return me->leader_id; 195 | } 196 | 197 | raft_node_t *raft_get_leader_node(raft_server_t *me) 198 | { 199 | return raft_get_node(me, me->leader_id); 200 | } 201 | 202 | void *raft_get_udata(raft_server_t *me) 203 | { 204 | return me->udata; 205 | } 206 | 207 | int raft_is_follower(raft_server_t *me) 208 | { 209 | return me->state == RAFT_STATE_FOLLOWER; 210 | } 211 | 212 | int raft_is_leader(raft_server_t *me) 213 | { 214 | return me->state == RAFT_STATE_LEADER; 215 | } 216 | 217 | int raft_is_precandidate(raft_server_t *me) 218 | { 219 | return me->state == RAFT_STATE_PRECANDIDATE; 220 | } 221 | 222 | int raft_is_candidate(raft_server_t *me) 223 | { 224 | return me->state == RAFT_STATE_CANDIDATE; 225 | } 226 | 227 | raft_term_t raft_get_last_log_term(raft_server_t *me) 228 | { 229 | raft_index_t current_idx = raft_get_current_idx(me); 230 | 231 | if (current_idx == 0) { 232 | return 0; 233 | } 234 | 235 | if (current_idx == me->snapshot_last_idx) { 236 | return me->snapshot_last_term; 237 | } 238 | 239 | raft_entry_t *ety = raft_get_entry_from_idx(me, current_idx); 240 | if (ety) { 241 | raft_term_t term = ety->term; 242 | raft_entry_release(ety); 243 | return term; 244 | } 245 | 246 | return 0; 247 | } 248 | 249 | int raft_snapshot_is_in_progress(raft_server_t *me) 250 | { 251 | return me->snapshot_in_progress; 252 | } 253 | 254 | int raft_is_apply_allowed(raft_server_t *me) 255 | { 256 | return !me->disable_apply && 257 | (!me->snapshot_in_progress || me->nonblocking_apply); 258 | } 259 | 260 | raft_entry_t *raft_get_last_applied_entry(raft_server_t *me) 261 | { 262 | if (me->last_applied_idx == 0) { 263 | return NULL; 264 | } 265 | return me->log_impl->get(me->log, me->last_applied_idx); 266 | } 267 | 268 | raft_index_t raft_get_snapshot_last_idx(raft_server_t *me) 269 | { 270 | return me->snapshot_last_idx; 271 | } 272 | 273 | raft_term_t raft_get_snapshot_last_term(raft_server_t *me) 274 | { 275 | return me->snapshot_last_term; 276 | } 277 | 278 | int raft_is_single_node_voting_cluster(raft_server_t *me) 279 | { 280 | return (raft_get_num_voting_nodes(me) == 1 && raft_node_is_voting(me->node)); 281 | } 282 | 283 | raft_msg_id_t raft_get_msg_id(raft_server_t *me) 284 | { 285 | return me->msg_id; 286 | } 287 | 288 | raft_node_id_t raft_get_transfer_leader(raft_server_t *me) 289 | { 290 | return me->node_transferring_leader_to; 291 | } 292 | 293 | void raft_get_server_stats(raft_server_t *me, raft_server_stats_t *stats) 294 | { 295 | *stats = me->stats; 296 | } 297 | -------------------------------------------------------------------------------- /tests/CuTest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "CuTest.h" 9 | 10 | /*-------------------------------------------------------------------------* 11 | * CuStr 12 | *-------------------------------------------------------------------------*/ 13 | 14 | char* CuStrAlloc(int size) 15 | { 16 | char* newStr = (char*) malloc( sizeof(char) * (size) ); 17 | return newStr; 18 | } 19 | 20 | char* CuStrCopy(const char* old) 21 | { 22 | int len = strlen(old); 23 | char* newStr = CuStrAlloc(len + 1); 24 | strcpy(newStr, old); 25 | return newStr; 26 | } 27 | 28 | /*-------------------------------------------------------------------------* 29 | * CuString 30 | *-------------------------------------------------------------------------*/ 31 | 32 | void CuStringInit(CuString* str) 33 | { 34 | str->length = 0; 35 | str->size = STRING_MAX; 36 | str->buffer = (char*) malloc(sizeof(char) * str->size); 37 | str->buffer[0] = '\0'; 38 | } 39 | 40 | CuString* CuStringNew(void) 41 | { 42 | CuString* str = (CuString*) malloc(sizeof(CuString)); 43 | str->length = 0; 44 | str->size = STRING_MAX; 45 | str->buffer = (char*) malloc(sizeof(char) * str->size); 46 | str->buffer[0] = '\0'; 47 | return str; 48 | } 49 | 50 | void CuStringFree(CuString *cu) 51 | { 52 | free(cu->buffer); 53 | free(cu); 54 | } 55 | 56 | void CuStringResize(CuString* str, int newSize) 57 | { 58 | str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize); 59 | str->size = newSize; 60 | } 61 | 62 | void CuStringAppend(CuString* str, const char* text) 63 | { 64 | int length; 65 | 66 | if (text == NULL) { 67 | text = "NULL"; 68 | } 69 | 70 | length = strlen(text); 71 | if (str->length + length + 1 >= str->size) 72 | CuStringResize(str, str->length + length + 1 + STRING_INC); 73 | str->length += length; 74 | strcat(str->buffer, text); 75 | } 76 | 77 | void CuStringAppendChar(CuString* str, char ch) 78 | { 79 | char text[2]; 80 | text[0] = ch; 81 | text[1] = '\0'; 82 | CuStringAppend(str, text); 83 | } 84 | 85 | void CuStringAppendFormat(CuString* str, const char* format, ...) 86 | { 87 | va_list argp; 88 | char buf[HUGE_STRING_LEN]; 89 | va_start(argp, format); 90 | vsprintf(buf, format, argp); 91 | va_end(argp); 92 | CuStringAppend(str, buf); 93 | } 94 | 95 | void CuStringInsert(CuString* str, const char* text, int pos) 96 | { 97 | int length = strlen(text); 98 | if (pos > str->length) 99 | pos = str->length; 100 | if (str->length + length + 1 >= str->size) 101 | CuStringResize(str, str->length + length + 1 + STRING_INC); 102 | memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); 103 | str->length += length; 104 | memcpy(str->buffer + pos, text, length); 105 | } 106 | 107 | /*-------------------------------------------------------------------------* 108 | * CuTest 109 | *-------------------------------------------------------------------------*/ 110 | 111 | void CuTestInit(CuTest* t, const char* name, TestFunction function) 112 | { 113 | t->name = CuStrCopy(name); 114 | t->failed = 0; 115 | t->ran = 0; 116 | t->message = NULL; 117 | t->function = function; 118 | t->jumpBuf = NULL; 119 | } 120 | 121 | CuTest* CuTestNew(const char* name, TestFunction function) 122 | { 123 | CuTest* tc = CU_ALLOC(CuTest); 124 | CuTestInit(tc, name, function); 125 | return tc; 126 | } 127 | 128 | void CuTestFree(CuTest *test) 129 | { 130 | free(test->name); 131 | free(test); 132 | } 133 | 134 | void CuTestRun(CuTest* tc) 135 | { 136 | #if 0 /* debugging */ 137 | printf(" running %s\n", tc->name); 138 | #endif 139 | jmp_buf buf; 140 | tc->jumpBuf = &buf; 141 | if (setjmp(buf) == 0) 142 | { 143 | tc->ran = 1; 144 | (tc->function)(tc); 145 | } 146 | tc->jumpBuf = 0; 147 | } 148 | 149 | static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) 150 | { 151 | char buf[HUGE_STRING_LEN]; 152 | 153 | sprintf(buf, "%s:%d: ", file, line); 154 | CuStringInsert(string, buf, 0); 155 | 156 | tc->failed = 1; 157 | tc->message = string->buffer; 158 | if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); 159 | } 160 | 161 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) 162 | { 163 | CuString string; 164 | 165 | CuStringInit(&string); 166 | if (message2 != NULL) 167 | { 168 | CuStringAppend(&string, message2); 169 | CuStringAppend(&string, ": "); 170 | } 171 | CuStringAppend(&string, message); 172 | CuFailInternal(tc, file, line, &string); 173 | } 174 | 175 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) 176 | { 177 | if (condition) return; 178 | CuFail_Line(tc, file, line, NULL, message); 179 | } 180 | 181 | void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 182 | const char* expected, const char* actual) 183 | { 184 | CuString string; 185 | if ((expected == NULL && actual == NULL) || 186 | (expected != NULL && actual != NULL && 187 | strcmp(expected, actual) == 0)) 188 | { 189 | return; 190 | } 191 | 192 | CuStringInit(&string); 193 | if (message != NULL) 194 | { 195 | CuStringAppend(&string, message); 196 | CuStringAppend(&string, ": "); 197 | } 198 | CuStringAppend(&string, "expected <"); 199 | CuStringAppend(&string, expected); 200 | CuStringAppend(&string, "> but was <"); 201 | CuStringAppend(&string, actual); 202 | CuStringAppend(&string, ">"); 203 | CuFailInternal(tc, file, line, &string); 204 | } 205 | 206 | void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 207 | int expected, int actual) 208 | { 209 | char buf[STRING_MAX]; 210 | if (expected == actual) return; 211 | sprintf(buf, "expected <%d> but was <%d>", expected, actual); 212 | CuFail_Line(tc, file, line, message, buf); 213 | } 214 | 215 | void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 216 | double expected, double actual, double delta) 217 | { 218 | char buf[STRING_MAX]; 219 | if (fabs(expected - actual) <= delta) return; 220 | sprintf(buf, "expected <%lf> but was <%lf>", expected, actual); 221 | CuFail_Line(tc, file, line, message, buf); 222 | } 223 | 224 | void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 225 | void* expected, void* actual) 226 | { 227 | char buf[STRING_MAX]; 228 | if (expected == actual) return; 229 | sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); 230 | CuFail_Line(tc, file, line, message, buf); 231 | } 232 | 233 | 234 | /*-------------------------------------------------------------------------* 235 | * CuSuite 236 | *-------------------------------------------------------------------------*/ 237 | 238 | void CuSuiteInit(CuSuite* testSuite) 239 | { 240 | testSuite->count = 0; 241 | testSuite->failCount = 0; 242 | } 243 | 244 | CuSuite* CuSuiteNew(void) 245 | { 246 | CuSuite* testSuite = CU_ALLOC(CuSuite); 247 | CuSuiteInit(testSuite); 248 | return testSuite; 249 | } 250 | 251 | void CuSuiteFree(CuSuite *testSuite) 252 | { 253 | for (int i = 0; i < testSuite->count; i++) { 254 | CuTestFree(testSuite->list[i]); 255 | } 256 | free(testSuite); 257 | } 258 | 259 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) 260 | { 261 | assert(testSuite->count < MAX_TEST_CASES); 262 | testSuite->list[testSuite->count] = testCase; 263 | testSuite->count++; 264 | } 265 | 266 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) 267 | { 268 | int i; 269 | for (i = 0 ; i < testSuite2->count ; ++i) 270 | { 271 | CuTest* testCase = testSuite2->list[i]; 272 | CuSuiteAdd(testSuite, testCase); 273 | } 274 | } 275 | 276 | void CuSuiteRun(CuSuite* testSuite) 277 | { 278 | int i; 279 | for (i = 0 ; i < testSuite->count ; ++i) 280 | { 281 | CuTest* testCase = testSuite->list[i]; 282 | CuTestRun(testCase); 283 | if (testCase->failed) { testSuite->failCount += 1; } 284 | } 285 | } 286 | 287 | void CuSuiteDetails(CuSuite* testSuite, CuString* details) 288 | { 289 | int i; 290 | int failCount = 0; 291 | 292 | CuStringAppendFormat(details, "%d..%d\n", 1, testSuite->count); 293 | 294 | for (i = 0 ; i < testSuite->count ; ++i) 295 | { 296 | CuTest* testCase = testSuite->list[i]; 297 | 298 | if (testCase->failed) 299 | { 300 | failCount++; 301 | CuStringAppendFormat(details, "not ok %d - %s #%s\n", 302 | i+1, testCase->name, testCase->message); 303 | } 304 | else 305 | { 306 | CuStringAppendFormat(details, "ok %d - %s\n", 307 | i+1, testCase->name); 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /tests/CuTest.h: -------------------------------------------------------------------------------- 1 | #ifndef CU_TEST_H 2 | #define CU_TEST_H 3 | 4 | #include 5 | #include 6 | 7 | /* CuString */ 8 | 9 | char* CuStrAlloc(int size); 10 | char* CuStrCopy(const char* old); 11 | 12 | #define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) 13 | 14 | #define HUGE_STRING_LEN 8192 15 | #define STRING_MAX 256 16 | #define STRING_INC 256 17 | 18 | typedef struct 19 | { 20 | int length; 21 | int size; 22 | char* buffer; 23 | } CuString; 24 | 25 | void CuStringInit(CuString* str); 26 | CuString* CuStringNew(void); 27 | void CuStringFree(CuString *cu); 28 | void CuStringRead(CuString* str, const char* path); 29 | void CuStringAppend(CuString* str, const char* text); 30 | void CuStringAppendChar(CuString* str, char ch); 31 | void CuStringAppendFormat(CuString* str, const char* format, ...); 32 | void CuStringInsert(CuString* str, const char* text, int pos); 33 | void CuStringResize(CuString* str, int newSize); 34 | 35 | /* CuTest */ 36 | 37 | typedef struct CuTest CuTest; 38 | 39 | typedef void (*TestFunction)(CuTest *); 40 | 41 | struct CuTest 42 | { 43 | char *name; 44 | TestFunction function; 45 | int failed; 46 | int ran; 47 | const char *message; 48 | jmp_buf *jumpBuf; 49 | }; 50 | 51 | void CuTestInit(CuTest* t, const char* name, TestFunction function); 52 | CuTest* CuTestNew(const char* name, TestFunction function); 53 | void CuTestFree(CuTest *test); 54 | void CuTestRun(CuTest* tc); 55 | 56 | /* Internal versions of assert functions -- use the public versions */ 57 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); 58 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); 59 | void CuAssertStrEquals_LineMsg(CuTest* tc, 60 | const char* file, int line, const char* message, 61 | const char* expected, const char* actual); 62 | void CuAssertIntEquals_LineMsg(CuTest* tc, 63 | const char* file, int line, const char* message, 64 | int expected, int actual); 65 | void CuAssertDblEquals_LineMsg(CuTest* tc, 66 | const char* file, int line, const char* message, 67 | double expected, double actual, double delta); 68 | void CuAssertPtrEquals_LineMsg(CuTest* tc, 69 | const char* file, int line, const char* message, 70 | void* expected, void* actual); 71 | 72 | /* public assert functions */ 73 | 74 | #define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms)) 75 | #define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) 76 | #define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) 77 | 78 | #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 79 | #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 80 | #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 81 | #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 82 | #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) 83 | #define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) 84 | #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 85 | #define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 86 | 87 | #define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL)) 88 | #define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL)) 89 | #define CuAssertPtrNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer expected",(p == NULL)) 90 | #define CuAssertPtrNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p == NULL)) 91 | 92 | /* CuSuite */ 93 | 94 | #define MAX_TEST_CASES 1024 95 | 96 | #define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) 97 | 98 | typedef struct 99 | { 100 | int count; 101 | CuTest* list[MAX_TEST_CASES]; 102 | int failCount; 103 | 104 | } CuSuite; 105 | 106 | 107 | void CuSuiteInit(CuSuite* testSuite); 108 | CuSuite* CuSuiteNew(void); 109 | void CuSuiteFree(CuSuite *testSuite); 110 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); 111 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); 112 | void CuSuiteRun(CuSuite* testSuite); 113 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary); 114 | void CuSuiteDetails(CuSuite* testSuite, CuString* details); 115 | 116 | #endif /* CU_TEST_H */ 117 | -------------------------------------------------------------------------------- /tests/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef _HELPERS_H 2 | #define _HELPERS_H 3 | 4 | static raft_entry_t *__MAKE_ENTRY(int id, raft_term_t term, const char *data) 5 | { 6 | raft_entry_t *ety = raft_entry_new(data ? strlen(data) + 1 : 0); 7 | ety->id = id; 8 | ety->term = term; 9 | if (data) { 10 | memcpy(ety->data, data, strlen(data) + 1); 11 | } 12 | return ety; 13 | } 14 | 15 | static raft_entry_t **__MAKE_ENTRY_ARRAY(int id, raft_term_t term, const char *data) 16 | { 17 | raft_entry_t **array = calloc(1, sizeof(raft_entry_t *)); 18 | array[0] = __MAKE_ENTRY(id, term, data); 19 | 20 | return array; 21 | } 22 | 23 | static raft_entry_t **__MAKE_ENTRY_ARRAY_SEQ_ID(int count, int start_id, raft_term_t term, const char *data) 24 | { 25 | raft_entry_t **array = calloc(count, sizeof(raft_entry_t *)); 26 | int i; 27 | 28 | for (i = 0; i < count; i++) { 29 | array[i] = __MAKE_ENTRY(start_id++, term, data); 30 | } 31 | 32 | return array; 33 | } 34 | 35 | static void __RAFT_APPEND_ENTRY(void *r, int id, raft_term_t term, const char *data) 36 | { 37 | raft_entry_t *e = __MAKE_ENTRY(id, term, data); 38 | raft_append_entry(r, e); 39 | raft_entry_release(e); 40 | raft_node_set_match_idx(raft_get_my_node(r), raft_get_current_idx(r)); 41 | } 42 | 43 | static void __RAFT_APPEND_ENTRIES_SEQ_ID(void *r, int count, int id, raft_term_t term, const char *data) 44 | { 45 | int i; 46 | for (i = 0; i < count; i++) { 47 | raft_entry_t *e = __MAKE_ENTRY(id++, term, data); 48 | raft_append_entry(r, e); 49 | raft_entry_release(e); 50 | } 51 | } 52 | 53 | static void __RAFT_APPEND_ENTRIES_SEQ_ID_TERM(void *r, int count, int id, raft_term_t term, const char *data) 54 | { 55 | int i; 56 | for (i = 0; i < count; i++) { 57 | raft_entry_t *e = __MAKE_ENTRY(id++, term++, data); 58 | raft_append_entry(r, e); 59 | raft_entry_release(e); 60 | } 61 | } 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /tests/linked_list_queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2011, Willem-Hendrik Thiart 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL WILLEM-HENDRIK THIART BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "linked_list_queue.h" 37 | 38 | void *llqueue_new() 39 | { 40 | linked_list_queue_t *qu; 41 | 42 | qu = calloc(1, sizeof(linked_list_queue_t)); 43 | return qu; 44 | } 45 | 46 | void llqueue_free( 47 | linked_list_queue_t * qu 48 | ) 49 | { 50 | llqnode_t *node; 51 | 52 | node = qu->head; 53 | 54 | while (node) 55 | { 56 | llqnode_t *prev; 57 | 58 | prev = node; 59 | node = node->next; 60 | free(prev); 61 | } 62 | free(qu); 63 | } 64 | 65 | void *llqueue_poll( 66 | linked_list_queue_t * qu 67 | ) 68 | { 69 | llqnode_t *node; 70 | 71 | void *item; 72 | 73 | if (qu->head == NULL) 74 | return NULL; 75 | 76 | node = qu->head; 77 | item = node->item; 78 | if (qu->tail == qu->head) 79 | qu->tail = NULL; 80 | qu->head = node->next; 81 | free(node); 82 | qu->count--; 83 | 84 | return item; 85 | } 86 | 87 | void llqueue_offer( 88 | linked_list_queue_t * qu, 89 | void *item 90 | ) 91 | { 92 | llqnode_t *node; 93 | 94 | node = malloc(sizeof(llqnode_t)); 95 | node->item = item; 96 | node->next = NULL; 97 | if (qu->tail) 98 | qu->tail->next = node; 99 | if (!qu->head) 100 | qu->head = node; 101 | qu->tail = node; 102 | qu->count++; 103 | } 104 | 105 | void *llqueue_remove_item( 106 | linked_list_queue_t * qu, 107 | const void *item 108 | ) 109 | { 110 | llqnode_t *node, *prev; 111 | 112 | prev = NULL; 113 | node = qu->head; 114 | 115 | while (node) 116 | { 117 | void *ritem; 118 | 119 | if (node->item == item) 120 | { 121 | if (node == qu->head) 122 | { 123 | return llqueue_poll(qu); 124 | } 125 | else 126 | { 127 | prev->next = node->next; 128 | if (node == qu->tail) 129 | { 130 | qu->tail = prev; 131 | } 132 | 133 | qu->count--; 134 | ritem = node->item; 135 | free(node); 136 | return ritem; 137 | } 138 | } 139 | 140 | prev = node; 141 | node = node->next; 142 | } 143 | 144 | return NULL; 145 | } 146 | 147 | void *llqueue_remove_item_via_cmpfunction( 148 | linked_list_queue_t * qu, 149 | const void *item, 150 | int (*cmp)(const void*, const void*) 151 | ) 152 | { 153 | llqnode_t *node, *prev; 154 | 155 | assert(cmp); 156 | assert(item); 157 | 158 | prev = NULL; 159 | node = qu->head; 160 | 161 | while (node) 162 | { 163 | void *ritem; 164 | 165 | if (0 == cmp(node->item,item)) 166 | { 167 | if (node == qu->head) 168 | { 169 | return llqueue_poll(qu); 170 | } 171 | else 172 | { 173 | prev->next = node->next; 174 | if (node == qu->tail) 175 | { 176 | qu->tail = prev; 177 | } 178 | 179 | qu->count--; 180 | ritem = node->item; 181 | free(node); 182 | return ritem; 183 | } 184 | } 185 | 186 | prev = node; 187 | node = node->next; 188 | } 189 | 190 | return NULL; 191 | } 192 | 193 | void *llqueue_get_item_via_cmpfunction( 194 | linked_list_queue_t * qu, 195 | const void *item, 196 | long (*cmp)(const void*, const void*) 197 | ) 198 | { 199 | llqnode_t *node; 200 | 201 | assert(cmp); 202 | assert(item); 203 | 204 | node = qu->head; 205 | 206 | while (node) 207 | { 208 | if (0 == cmp(node->item,item)) 209 | { 210 | return node->item; 211 | } 212 | 213 | node = node->next; 214 | } 215 | 216 | return NULL; 217 | } 218 | 219 | int llqueue_count( 220 | const linked_list_queue_t * qu 221 | ) 222 | { 223 | return qu->count; 224 | } 225 | -------------------------------------------------------------------------------- /tests/linked_list_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef LINKED_LIST_QUEUE_H 2 | #define LINKED_LIST_QUEUE_H 3 | 4 | typedef struct llqnode_s llqnode_t; 5 | 6 | struct llqnode_s 7 | { 8 | llqnode_t *next; 9 | void *item; 10 | }; 11 | 12 | typedef struct 13 | { 14 | llqnode_t *head, *tail; 15 | int count; 16 | } linked_list_queue_t; 17 | 18 | void *llqueue_new( 19 | ); 20 | 21 | void llqueue_free( 22 | linked_list_queue_t * qu 23 | ); 24 | 25 | void *llqueue_poll( 26 | linked_list_queue_t * qu 27 | ); 28 | 29 | void llqueue_offer( 30 | linked_list_queue_t * qu, 31 | void *item 32 | ); 33 | 34 | /** 35 | * remove this item, by comparing the memory address of the item */ 36 | void *llqueue_remove_item( 37 | linked_list_queue_t * qu, 38 | const void *item 39 | ); 40 | 41 | int llqueue_count( 42 | const linked_list_queue_t * qu 43 | ); 44 | 45 | /** 46 | * remove this item, by using the supplied compare function */ 47 | void *llqueue_remove_item_via_cmpfunction( 48 | linked_list_queue_t * qu, 49 | const void *item, 50 | int (*cmp)(const void*, const void*)); 51 | 52 | /** 53 | * get this item, by using the supplied compare function */ 54 | void *llqueue_get_item_via_cmpfunction( 55 | linked_list_queue_t * qu, 56 | const void *item, 57 | long (*cmp)(const void*, const void*)); 58 | 59 | #endif /* LINKED_LIST_QUEUE_H */ 60 | -------------------------------------------------------------------------------- /tests/log_fuzzer.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import unittest 3 | 4 | import raft_cffi 5 | 6 | from hypothesis import given 7 | from hypothesis.strategies import lists, just, integers, one_of 8 | 9 | 10 | commands = one_of( 11 | just('append'), 12 | just('poll'), 13 | integers(min_value=1, max_value=10), 14 | ) 15 | 16 | 17 | class Log(object): 18 | def __init__(self): 19 | self.entries = [] 20 | self.base = 0 21 | 22 | def append(self, ety): 23 | self.entries.append(ety) 24 | 25 | def poll(self): 26 | self.base += 1 27 | return self.entries.pop(0) 28 | 29 | def delete(self, idx): 30 | idx -= 1 31 | if idx < self.base: 32 | idx = self.base 33 | idx = max(idx - self.base, 0) 34 | del self.entries[idx:] 35 | 36 | def count(self): 37 | return len(self.entries) 38 | 39 | 40 | class CoreTestCase(unittest.TestCase): 41 | def setUp(self): 42 | super(CoreTestCase, self).setUp() 43 | self.r = raft_cffi 44 | 45 | @given(lists(commands)) 46 | def test_sanity_check(self, commands): 47 | r = self.r.lib 48 | 49 | unique_id = 1 50 | l = r.raft_log_alloc(1) 51 | 52 | log = Log() 53 | 54 | for cmd in commands: 55 | if cmd == 'append': 56 | entry = r.raft_entry_new(0) 57 | entry.id = unique_id 58 | unique_id += 1 59 | 60 | ret = r.raft_log_append_entry(l, entry) 61 | assert ret == 0 62 | 63 | log.append(entry) 64 | 65 | elif cmd == 'poll': 66 | entry_ptr = self.r.ffi.new('raft_entry_t**') 67 | 68 | if log.entries: 69 | ret = r.raft_log_poll(l, entry_ptr) 70 | assert ret == 0 71 | 72 | ety_expected = log.poll() 73 | assert entry_ptr[0].id == ety_expected.id 74 | 75 | elif isinstance(cmd, int): 76 | if log.entries: 77 | log.delete(cmd) 78 | ret = r.raft_log_delete(l, cmd) 79 | assert ret == 0 80 | 81 | else: 82 | assert False 83 | 84 | self.assertEqual(r.raft_log_count(l), log.count()) 85 | 86 | 87 | if __name__ == '__main__': 88 | unittest.main() 89 | -------------------------------------------------------------------------------- /tests/mock_send_functions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "CuTest.h" 14 | 15 | #include "linked_list_queue.h" 16 | 17 | #include "raft.h" 18 | #include "mock_send_functions.h" 19 | 20 | typedef struct 21 | { 22 | void* outbox; 23 | void* inbox; 24 | void* raft; 25 | } sender_t; 26 | 27 | typedef struct 28 | { 29 | void* data; 30 | int len; 31 | /* what type of message is it? */ 32 | int type; 33 | /* who sent this? */ 34 | raft_node_t* sender; 35 | } msg_t; 36 | 37 | static sender_t** __senders = NULL; 38 | static int __nsenders = 0; 39 | 40 | void senders_new() 41 | { 42 | __senders = NULL; 43 | __nsenders = 0; 44 | } 45 | 46 | static int __append_msg( 47 | sender_t* me, 48 | void* data, 49 | int type, 50 | int len, 51 | raft_node_t* node, 52 | raft_server_t* raft 53 | ) 54 | { 55 | msg_t* m = malloc(sizeof(msg_t)); 56 | m->type = type; 57 | m->len = len; 58 | m->data = malloc(len); 59 | m->sender = raft_get_node(raft, raft_get_nodeid(raft)); 60 | memcpy(m->data, data, len); 61 | llqueue_offer(me->outbox, m); 62 | 63 | /* give to peer */ 64 | sender_t* peer = raft_node_get_udata(node); 65 | if (peer) 66 | { 67 | msg_t* m2 = malloc(sizeof(msg_t)); 68 | memcpy(m2, m, sizeof(msg_t)); 69 | m2->sender = raft_get_node(peer->raft, raft_get_nodeid(raft)); 70 | llqueue_offer(peer->inbox, m2); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | int sender_requestvote(raft_server_t* raft, 77 | void* udata, raft_node_t* node, 78 | raft_requestvote_req_t* msg) 79 | { 80 | return __append_msg(udata, msg, RAFT_REQUESTVOTE_REQ, sizeof(*msg), node, 81 | raft); 82 | } 83 | 84 | int sender_requestvote_response(raft_server_t* raft, 85 | void* udata, raft_node_t* node, 86 | raft_requestvote_resp_t* msg) 87 | { 88 | return __append_msg(udata, msg, RAFT_REQUESTVOTE_RESP, sizeof(*msg), 89 | node, raft); 90 | } 91 | 92 | int sender_appendentries(raft_server_t* raft, 93 | void* udata, raft_node_t* node, 94 | raft_appendentries_req_t* msg) 95 | { 96 | raft_entry_req_t** entries = calloc(1, sizeof(raft_entry_req_t *) * msg->n_entries); 97 | int i; 98 | for (i = 0; i < msg->n_entries; i++) { 99 | entries[i] = msg->entries[i]; 100 | raft_entry_hold(entries[i]); 101 | } 102 | 103 | raft_entry_req_t** old_entries = msg->entries; 104 | msg->entries = entries; 105 | int ret = __append_msg(udata, msg, RAFT_APPENDENTRIES_REQ, sizeof(*msg), node, 106 | raft); 107 | msg->entries = old_entries; 108 | return ret; 109 | } 110 | 111 | int sender_appendentries_response(raft_server_t* raft, 112 | void* udata, raft_node_t* node, 113 | raft_appendentries_resp_t* msg) 114 | { 115 | return __append_msg(udata, msg, RAFT_APPENDENTRIES_RESP, 116 | sizeof(*msg), node, raft); 117 | } 118 | 119 | int sender_entries_response(raft_server_t* raft, 120 | void* udata, raft_node_t* node, 121 | raft_entry_resp_t* msg) 122 | { 123 | return __append_msg(udata, msg, RAFT_ENTRY_RESP, sizeof(*msg), node, raft); 124 | } 125 | 126 | void* sender_new(void* address) 127 | { 128 | sender_t* me = malloc(sizeof(sender_t)); 129 | me->outbox = llqueue_new(); 130 | me->inbox = llqueue_new(); 131 | __senders = realloc(__senders, sizeof(sender_t*) * (++__nsenders)); 132 | __senders[__nsenders - 1] = me; 133 | return me; 134 | } 135 | 136 | void* sender_poll_msg_data(void* s) 137 | { 138 | sender_t* me = s; 139 | msg_t* msg = llqueue_poll(me->outbox); 140 | void *data = msg ? msg->data : NULL; 141 | free(msg); 142 | return data; 143 | } 144 | 145 | void sender_set_raft(void* s, void* r) 146 | { 147 | sender_t* me = s; 148 | me->raft = r; 149 | } 150 | 151 | int sender_msgs_available(void* s) 152 | { 153 | sender_t* me = s; 154 | 155 | return 0 < llqueue_count(me->inbox); 156 | } 157 | 158 | void sender_poll_msgs(void* s) 159 | { 160 | sender_t* me = s; 161 | msg_t* m; 162 | 163 | while ((m = llqueue_poll(me->inbox))) 164 | { 165 | switch (m->type) 166 | { 167 | case RAFT_APPENDENTRIES_REQ: 168 | { 169 | raft_appendentries_req_t *ae = m->data; 170 | raft_appendentries_resp_t response; 171 | raft_recv_appendentries(me->raft, m->sender, m->data, &response); 172 | __append_msg(me, &response, RAFT_APPENDENTRIES_RESP, 173 | sizeof(response), m->sender, me->raft); 174 | for (raft_index_t i = 0; i < ae->n_entries; i++) { 175 | raft_entry_release(ae->entries[i]); 176 | } 177 | free(ae->entries); 178 | } 179 | break; 180 | case RAFT_APPENDENTRIES_RESP: 181 | raft_recv_appendentries_response(me->raft, m->sender, m->data); 182 | break; 183 | case RAFT_REQUESTVOTE_REQ: 184 | { 185 | raft_requestvote_resp_t response; 186 | raft_recv_requestvote(me->raft, m->sender, m->data, &response); 187 | __append_msg(me, &response, RAFT_REQUESTVOTE_RESP, 188 | sizeof(response), m->sender, me->raft); 189 | } 190 | break; 191 | case RAFT_REQUESTVOTE_RESP: 192 | raft_recv_requestvote_response(me->raft, m->sender, m->data); 193 | break; 194 | case RAFT_ENTRY_REQ: 195 | { 196 | raft_entry_resp_t response; 197 | raft_recv_entry(me->raft, m->data, &response); 198 | __append_msg(me, &response, RAFT_ENTRY_RESP, 199 | sizeof(response), m->sender, me->raft); 200 | } 201 | break; 202 | 203 | case RAFT_ENTRY_RESP: 204 | #if 0 205 | raft_recv_entry_response(me->raft, m->sender, m->data); 206 | #endif 207 | break; 208 | } 209 | 210 | free(m->data); 211 | free(m); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /tests/mock_send_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_SEND_FUNCTIONS_H 2 | #define MOCK_SEND_FUNCTIONS_H 3 | 4 | typedef enum 5 | { 6 | RAFT_REQUESTVOTE_REQ, 7 | RAFT_REQUESTVOTE_RESP, 8 | RAFT_APPENDENTRIES_REQ, 9 | RAFT_APPENDENTRIES_RESP, 10 | RAFT_ENTRY_REQ, 11 | RAFT_ENTRY_RESP, 12 | } raft_message_type_e; 13 | 14 | void senders_new(); 15 | 16 | void* sender_new(void* address); 17 | 18 | void* sender_poll_msg_data(void* s); 19 | 20 | void sender_poll_msgs(void* s); 21 | 22 | int sender_msgs_available(void* s); 23 | 24 | void sender_set_raft(void* s, void* r); 25 | 26 | int sender_requestvote(raft_server_t* raft, 27 | void* udata, raft_node_t* node, raft_requestvote_req_t* msg); 28 | 29 | int sender_requestvote_response(raft_server_t* raft, 30 | void* udata, raft_node_t* node, raft_requestvote_resp_t* msg); 31 | 32 | int sender_appendentries(raft_server_t* raft, 33 | void* udata, raft_node_t* node, raft_appendentries_req_t* msg); 34 | 35 | int sender_appendentries_response(raft_server_t* raft, 36 | void* udata, raft_node_t* node, raft_appendentries_resp_t* msg); 37 | 38 | int sender_entries(raft_server_t* raft, 39 | void* udata, raft_node_t* node, raft_entry_req_t* msg); 40 | 41 | int sender_entries_response(raft_server_t* raft, 42 | void* udata, raft_node_t* node, raft_entry_resp_t* msg); 43 | 44 | #endif /* MOCK_SEND_FUNCTIONS_H */ 45 | -------------------------------------------------------------------------------- /tests/raft_cffi_builder.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import subprocess 4 | import cffi 5 | 6 | 7 | def load(fname): 8 | tmpfile = 'tests/raft_cffi_tmp.h' 9 | # Strip C standard library headers as cffi cannot parse them 10 | with open(fname, "r") as f: 11 | lines = f.readlines() 12 | with open(tmpfile, "w+") as f: 13 | for line in lines: 14 | if '#include data = data; 41 | e->refs = 100; 42 | return e; 43 | } 44 | void *raft_entry_getdata(raft_entry_t *ety) { 45 | return *(void **) ety->data; 46 | } 47 | raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len) { 48 | raft_entry_t **t = malloc(len * sizeof(raft_entry_t *)); 49 | int i; 50 | for (i = 0; i < len; i++) { 51 | int sz = sizeof(raft_entry_t) + src[i]->data_len; 52 | t[i] = malloc(sz); 53 | memcpy(t[i], src[i], sz); 54 | } 55 | return t; 56 | } 57 | """, 58 | libraries=[args.libname], 59 | include_dirs=[args.includedir], 60 | extra_compile_args=["-UNDEBUG"], 61 | extra_link_args=["-L{}".format(args.libdir)] 62 | ) 63 | 64 | ffibuilder.cdef('void *malloc(size_t __size);') 65 | ffibuilder.cdef(load('include/raft.h')) 66 | ffibuilder.cdef(load('include/raft_private.h')) 67 | ffibuilder.cdef(load('include/raft_log.h')) 68 | 69 | ffibuilder.cdef('raft_entry_t *raft_entry_newdata(void *data);') 70 | ffibuilder.cdef('void *raft_entry_getdata(raft_entry_t *);') 71 | ffibuilder.cdef('raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len);') 72 | 73 | ffibuilder.compile(tmpdir=args.tmpdir, verbose=True) 74 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | cffi==1.15.0 2 | colorama==0.4.3 3 | coloredlogs==14.0 4 | docopt==0.6.2 5 | hypothesis==5.6.0 6 | terminaltables==3.1.0 7 | -------------------------------------------------------------------------------- /tests/test_log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "linked_list_queue.h" 10 | 11 | #include "raft.h" 12 | #include "raft_log.h" 13 | #include "raft_private.h" 14 | 15 | #include "helpers.h" 16 | 17 | static void __LOG_APPEND_ENTRY(void *l, int id, raft_term_t term, const char *data) 18 | { 19 | raft_entry_t *e = __MAKE_ENTRY(id, term, data); 20 | raft_entry_hold(e); /* need an extra ref because tests assume it lives on */ 21 | raft_log_append_entry(l, e); 22 | } 23 | 24 | static void __LOG_APPEND_ENTRIES_SEQ_ID(void *l, int count, int id, raft_term_t term, const char *data) 25 | { 26 | int i; 27 | for (i = 0; i < count; i++) { 28 | raft_entry_t *e = __MAKE_ENTRY(id++, term, data); 29 | raft_entry_hold(e); /* need an extra ref because tests assume it lives on */ 30 | raft_log_append_entry(l, e); 31 | } 32 | } 33 | 34 | static raft_node_id_t __get_node_id( 35 | raft_server_t* raft, 36 | void *udata, 37 | raft_entry_t *ety, 38 | raft_index_t ety_idx 39 | ) 40 | { 41 | return 0; 42 | } 43 | 44 | static int __log_offer( 45 | raft_server_t* raft, 46 | void *user_data, 47 | raft_entry_t *entry, 48 | raft_index_t entry_idx 49 | ) 50 | { 51 | CuAssertIntEquals((CuTest*)raft, 1, entry_idx); 52 | return 0; 53 | } 54 | 55 | static int __log_pop( 56 | raft_server_t* raft, 57 | void *user_data, 58 | raft_entry_t *entry, 59 | raft_index_t entry_idx 60 | ) 61 | { 62 | raft_entry_hold(entry); 63 | llqueue_offer(user_data, entry); 64 | return 0; 65 | } 66 | 67 | static int __log_pop_failing( 68 | raft_server_t* raft, 69 | void *user_data, 70 | raft_entry_t *entry, 71 | raft_index_t entry_idx 72 | ) 73 | { 74 | return -1; 75 | } 76 | 77 | raft_cbs_t funcs = { 78 | .get_node_id = __get_node_id}; 79 | 80 | raft_log_cbs_t log_funcs = { 81 | .log_pop = __log_pop 82 | }; 83 | 84 | void TestLog_new_is_empty(CuTest * tc) 85 | { 86 | void *l; 87 | 88 | l = raft_log_new(); 89 | CuAssertTrue(tc, 0 == raft_log_count(l)); 90 | raft_log_free(l); 91 | } 92 | 93 | void TestLog_append_is_not_empty(CuTest * tc) 94 | { 95 | void *l; 96 | void *r = raft_new(); 97 | raft_entry_t *e; 98 | 99 | l = raft_log_new(); 100 | raft_log_cbs_t funcs = { 101 | .log_offer = __log_offer 102 | }; 103 | raft_log_set_callbacks(l, &funcs, r); 104 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 105 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 106 | raft_log_poll(l, &e); 107 | raft_entry_release(e); 108 | 109 | raft_log_free(l); 110 | raft_destroy(r); 111 | } 112 | 113 | void TestLog_get_at_idx(CuTest * tc) 114 | { 115 | void *l; 116 | raft_entry_t *e1, *e2, *e3; 117 | 118 | l = raft_log_new(); 119 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 120 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 121 | 122 | e1 = raft_log_get_at_idx(l, 1); 123 | CuAssertIntEquals(tc, 1, e1->id); 124 | raft_entry_release(e1); 125 | 126 | e2 = raft_log_get_at_idx(l, 2); 127 | CuAssertIntEquals(tc, 2, e2->id); 128 | raft_entry_release(e2); 129 | 130 | e3 = raft_log_get_at_idx(l, 3); 131 | CuAssertIntEquals(tc, 3, e3->id); 132 | raft_entry_release(e3); 133 | 134 | raft_log_free(l); 135 | } 136 | 137 | void TestLog_get_at_idx_returns_null_where_out_of_bounds(CuTest * tc) 138 | { 139 | void *l; 140 | 141 | l = raft_log_new(); 142 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 0)); 143 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 1)); 144 | 145 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 146 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 2)); 147 | 148 | raft_entry_t *e; 149 | raft_log_poll(l, &e); 150 | raft_entry_release(e); 151 | 152 | raft_log_free(l); 153 | } 154 | 155 | void TestLog_delete(CuTest * tc) 156 | { 157 | void *l; 158 | raft_entry_t *e; 159 | 160 | void* queue = llqueue_new(); 161 | void *r = raft_new(); 162 | raft_cbs_t funcs = { 163 | .get_node_id = __get_node_id 164 | }; 165 | raft_log_cbs_t log_funcs = { 166 | .log_pop = __log_pop 167 | }; 168 | 169 | raft_set_callbacks(r, &funcs, queue); 170 | 171 | l = raft_log_new(); 172 | raft_log_set_callbacks(l, &log_funcs, r); 173 | 174 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 175 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 176 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 177 | 178 | e = raft_log_get_at_idx(l, 3); 179 | raft_log_delete(l, 3); 180 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 181 | raft_entry_release(e); 182 | e = llqueue_poll(queue); 183 | CuAssertIntEquals(tc, 3, e->id); 184 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 185 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 3)); 186 | raft_entry_release(e); 187 | 188 | e = raft_log_get_at_idx(l, 2); 189 | raft_log_delete(l, 2); 190 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 191 | raft_entry_release(e); 192 | e = llqueue_poll(queue); 193 | CuAssertIntEquals(tc, 2, e->id); 194 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 195 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 2)); 196 | raft_entry_release(e); 197 | 198 | e = raft_log_get_at_idx(l, 1); 199 | raft_log_delete(l, 1); 200 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 201 | raft_entry_release(e); 202 | e = llqueue_poll(queue); 203 | CuAssertIntEquals(tc, 1, e->id); 204 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 205 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 1)); 206 | raft_entry_release(e); 207 | 208 | raft_log_free(l); 209 | llqueue_free(queue); 210 | raft_destroy(r); 211 | } 212 | 213 | void TestLog_delete_onwards(CuTest * tc) 214 | { 215 | void* queue = llqueue_new(); 216 | void *r = raft_new(); 217 | raft_cbs_t funcs = { 218 | .get_node_id = __get_node_id}; 219 | raft_log_cbs_t log_funcs = { 220 | .log_pop = __log_pop 221 | }; 222 | raft_set_callbacks(r, &funcs, queue); 223 | 224 | void *l; 225 | 226 | l = raft_log_new(); 227 | raft_log_set_callbacks(l, &log_funcs, r); 228 | 229 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 230 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 231 | 232 | /* even 3 gets deleted */ 233 | raft_entry_t *e2 = raft_log_get_at_idx(l, 2); 234 | raft_entry_t *e3 = raft_log_get_at_idx(l, 3); 235 | 236 | raft_log_delete(l, 2); 237 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 238 | 239 | CuAssertIntEquals(tc, 1, raft_log_get_at_idx(l, 1)->id); 240 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 2)); 241 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 3)); 242 | 243 | raft_entry_release(e2); 244 | raft_entry_release(e3); 245 | 246 | raft_entry_t *e; 247 | 248 | while (raft_log_poll(l, &e) == 0) { 249 | raft_entry_release(e); 250 | } 251 | 252 | while ((e = llqueue_poll(queue)) != NULL) { 253 | raft_entry_release(e); 254 | } 255 | 256 | llqueue_free(queue); 257 | raft_log_free(l); 258 | raft_destroy(r); 259 | } 260 | 261 | void TestLog_delete_handles_log_pop_failure(CuTest * tc) 262 | { 263 | void *l; 264 | 265 | void* queue = llqueue_new(); 266 | void *r = raft_new(); 267 | raft_cbs_t funcs = { 268 | .get_node_id = __get_node_id}; 269 | raft_log_cbs_t log_funcs = { 270 | .log_pop = __log_pop_failing 271 | }; 272 | raft_set_callbacks(r, &funcs, queue); 273 | 274 | l = raft_log_new(); 275 | raft_log_set_callbacks(l, &log_funcs, r); 276 | 277 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 278 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 279 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 280 | 281 | CuAssertIntEquals(tc, -1, raft_log_delete(l, 3)); 282 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 283 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 284 | CuAssertIntEquals(tc, 3, ((raft_entry_t*) raft_log_peektail(l))->id); 285 | 286 | raft_entry_t *e; 287 | 288 | while (raft_log_poll(l, &e) == 0) { 289 | raft_entry_release(e); 290 | } 291 | 292 | while ((e = llqueue_poll(queue)) != NULL) { 293 | raft_entry_release(e); 294 | } 295 | 296 | llqueue_free(queue); 297 | raft_log_free(l); 298 | raft_destroy(r); 299 | } 300 | 301 | void TestLog_delete_fails_for_idx_zero(CuTest * tc) 302 | { 303 | void* queue = llqueue_new(); 304 | void *r = raft_new(); 305 | raft_cbs_t funcs = { 306 | .get_node_id = __get_node_id}; 307 | raft_log_cbs_t log_funcs = { 308 | .log_pop = __log_pop 309 | }; 310 | raft_set_callbacks(r, &funcs, queue); 311 | raft_log_set_callbacks(raft_get_log(r), &log_funcs, r); 312 | 313 | void *l; 314 | 315 | l = raft_log_alloc(1); 316 | raft_log_set_callbacks(l, &log_funcs, r); 317 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 4, 1, 0, NULL); 318 | CuAssertIntEquals(tc, raft_log_delete(l, 0), -1); 319 | 320 | raft_entry_t *e; 321 | 322 | while (raft_log_poll(l, &e) == 0) { 323 | raft_entry_release(e); 324 | } 325 | 326 | while ((e = llqueue_poll(queue)) != NULL) { 327 | raft_entry_release(e); 328 | } 329 | 330 | raft_log_free(l); 331 | llqueue_free(queue); 332 | raft_destroy(r); 333 | } 334 | 335 | void TestLog_poll(CuTest * tc) 336 | { 337 | void* queue = llqueue_new(); 338 | void *r = raft_new(); 339 | raft_cbs_t funcs = { 340 | .get_node_id = __get_node_id}; 341 | raft_log_cbs_t log_funcs = { 342 | .log_pop = __log_pop 343 | }; 344 | raft_set_callbacks(r, &funcs, queue); 345 | 346 | void *l; 347 | 348 | l = raft_log_new(); 349 | raft_log_set_callbacks(l, &log_funcs, r); 350 | 351 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 352 | CuAssertIntEquals(tc, 1, raft_log_get_current_idx(l)); 353 | 354 | __LOG_APPEND_ENTRY(l, 2, 0, NULL); 355 | CuAssertIntEquals(tc, 2, raft_log_get_current_idx(l)); 356 | 357 | __LOG_APPEND_ENTRY(l, 3, 0, NULL); 358 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 359 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 360 | 361 | raft_entry_t *ety; 362 | 363 | /* remove 1st */ 364 | ety = NULL; 365 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 366 | CuAssertTrue(tc, NULL != ety); 367 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 368 | CuAssertIntEquals(tc, ety->id, 1); 369 | CuAssertIntEquals(tc, 1, raft_log_get_base(l)); 370 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 1)); 371 | CuAssertTrue(tc, NULL != raft_log_get_at_idx(l, 2)); 372 | CuAssertTrue(tc, NULL != raft_log_get_at_idx(l, 3)); 373 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 374 | raft_entry_release(ety); 375 | 376 | /* remove 2nd */ 377 | ety = NULL; 378 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 379 | CuAssertTrue(tc, NULL != ety); 380 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 381 | CuAssertIntEquals(tc, ety->id, 2); 382 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 1)); 383 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 2)); 384 | CuAssertTrue(tc, NULL != raft_log_get_at_idx(l, 3)); 385 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 386 | raft_entry_release(ety); 387 | 388 | /* remove 3rd */ 389 | ety = NULL; 390 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 391 | CuAssertTrue(tc, NULL != ety); 392 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 393 | CuAssertIntEquals(tc, ety->id, 3); 394 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 1)); 395 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 2)); 396 | CuAssertTrue(tc, NULL == raft_log_get_at_idx(l, 3)); 397 | CuAssertIntEquals(tc, 3, raft_log_get_current_idx(l)); 398 | raft_entry_release(ety); 399 | 400 | llqueue_free(queue); 401 | raft_log_free(l); 402 | raft_destroy(r); 403 | } 404 | 405 | void TestLog_peektail(CuTest * tc) 406 | { 407 | void *l; 408 | 409 | l = raft_log_new(); 410 | 411 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 412 | CuAssertIntEquals(tc, 3, raft_log_count(l)); 413 | CuAssertIntEquals(tc, 3, raft_log_peektail(l)->id); 414 | 415 | raft_entry_t *e; 416 | 417 | while (raft_log_poll(l, &e) == 0) { 418 | raft_entry_release(e); 419 | } 420 | 421 | raft_log_free(l); 422 | } 423 | 424 | #if 0 425 | // TODO: duplicate testing not implemented yet 426 | void T_estlog_cant_append_duplicates(CuTest * tc) 427 | { 428 | void *l; 429 | raft_entry_t e; 430 | 431 | e.id = 1; 432 | 433 | l = log_new(); 434 | CuAssertTrue(tc, 1 == log_append_entry(l, &e)); 435 | CuAssertTrue(tc, 1 == log_count(l)); 436 | } 437 | #endif 438 | 439 | void TestLog_load_from_snapshot(CuTest * tc) 440 | { 441 | void *l; 442 | 443 | l = raft_log_new(); 444 | CuAssertIntEquals(tc, 0, raft_log_get_current_idx(l)); 445 | CuAssertIntEquals(tc, 0, raft_log_load_from_snapshot(l, 10, 5)); 446 | CuAssertIntEquals(tc, 10, raft_log_get_current_idx(l)); 447 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 448 | 449 | raft_log_free(l); 450 | } 451 | 452 | void TestLog_load_from_snapshot_clears_log(CuTest * tc) 453 | { 454 | void *l; 455 | 456 | l = raft_log_new(); 457 | 458 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 459 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 460 | CuAssertIntEquals(tc, 2, raft_log_get_current_idx(l)); 461 | 462 | raft_entry_t *e1 = raft_log_get_at_idx(l, 1); 463 | raft_entry_t *e2 = raft_log_get_at_idx(l, 2); 464 | 465 | CuAssertIntEquals(tc, 0, raft_log_load_from_snapshot(l, 10, 5)); 466 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 467 | CuAssertIntEquals(tc, 10, raft_log_get_current_idx(l)); 468 | 469 | raft_entry_release(e1); 470 | raft_entry_release(e2); 471 | 472 | raft_entry_t *e; 473 | 474 | while (raft_log_poll(l, &e) == 0) { 475 | raft_entry_release(e); 476 | } 477 | 478 | raft_log_free(l); 479 | } 480 | 481 | void TestLog_front_pushes_across_boundary(CuTest * tc) 482 | { 483 | void* queue = llqueue_new(); 484 | void *r = raft_new(); 485 | raft_set_callbacks(r, &funcs, queue); 486 | raft_log_set_callbacks(raft_get_log(r), &log_funcs, r); 487 | 488 | void *l; 489 | 490 | l = raft_log_alloc(1); 491 | raft_log_set_callbacks(l, &log_funcs, r); 492 | 493 | raft_entry_t* ety; 494 | 495 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 496 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 497 | CuAssertIntEquals(tc, ety->id, 1); 498 | raft_entry_release(ety); 499 | 500 | __LOG_APPEND_ENTRY(l, 2, 0, NULL); 501 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 502 | CuAssertIntEquals(tc, ety->id, 2); 503 | raft_entry_release(ety); 504 | 505 | llqueue_free(queue); 506 | raft_log_free(l); 507 | raft_destroy(r); 508 | } 509 | 510 | void TestLog_front_and_back_pushed_across_boundary_with_enlargement_required(CuTest * tc) 511 | { 512 | void *l; 513 | 514 | l = raft_log_alloc(1); 515 | 516 | raft_entry_t* ety; 517 | 518 | /* append */ 519 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 520 | 521 | /* poll */ 522 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 523 | CuAssertIntEquals(tc, ety->id, 1); 524 | raft_entry_release(ety); 525 | 526 | /* append */ 527 | __LOG_APPEND_ENTRY(l, 2, 0, NULL); 528 | 529 | /* poll */ 530 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 531 | CuAssertIntEquals(tc, ety->id, 2); 532 | raft_entry_release(ety); 533 | 534 | /* append append */ 535 | __LOG_APPEND_ENTRY(l, 3, 0, NULL); 536 | __LOG_APPEND_ENTRY(l, 4, 0, NULL); 537 | 538 | /* poll */ 539 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 540 | CuAssertIntEquals(tc, ety->id, 3); 541 | raft_entry_release(ety); 542 | 543 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 544 | CuAssertIntEquals(tc, ety->id, 4); 545 | raft_entry_release(ety); 546 | 547 | raft_log_free(l); 548 | } 549 | 550 | void TestLog_delete_after_polling(CuTest * tc) 551 | { 552 | void *l; 553 | 554 | l = raft_log_alloc(1); 555 | 556 | raft_entry_t* ety; 557 | 558 | /* append */ 559 | __LOG_APPEND_ENTRY(l, 1, 0, NULL); 560 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 561 | 562 | /* poll */ 563 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 564 | CuAssertIntEquals(tc, ety->id, 1); 565 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 566 | raft_entry_release(ety); 567 | 568 | /* append */ 569 | __LOG_APPEND_ENTRY(l, 2, 0, NULL); 570 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 571 | 572 | /* poll */ 573 | raft_entry_t *e = raft_log_get_at_idx(l, 2); 574 | CuAssertIntEquals(tc, raft_log_delete(l, 1), 0); 575 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 576 | raft_entry_release(e); 577 | 578 | raft_log_free(l); 579 | } 580 | 581 | void TestLog_delete_after_polling_from_double_append(CuTest * tc) 582 | { 583 | void* queue = llqueue_new(); 584 | void *r = raft_new(); 585 | raft_cbs_t funcs = { 586 | .get_node_id = __get_node_id}; 587 | raft_log_cbs_t log_funcs = { 588 | .log_pop = __log_pop 589 | }; 590 | raft_set_callbacks(r, &funcs, queue); 591 | 592 | void *l; 593 | 594 | l = raft_log_alloc(1); 595 | raft_log_set_callbacks(l, &log_funcs, r); 596 | 597 | raft_entry_t* ety; 598 | 599 | /* append append */ 600 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 601 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 602 | 603 | /* poll */ 604 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 605 | CuAssertIntEquals(tc, ety->id, 1); 606 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 607 | raft_entry_release(ety); 608 | 609 | /* append */ 610 | __LOG_APPEND_ENTRY(l, 3, 0, NULL); 611 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 612 | 613 | /* poll */ 614 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 615 | CuAssertIntEquals(tc, ety->id, 2); 616 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 617 | raft_entry_release(ety); 618 | 619 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 620 | CuAssertIntEquals(tc, ety->id, 3); 621 | CuAssertIntEquals(tc, 0, raft_log_count(l)); 622 | raft_entry_release(ety); 623 | 624 | llqueue_free(queue); 625 | raft_log_free(l); 626 | raft_destroy(r); 627 | } 628 | 629 | void TestLog_get_from_idx_with_base_off_by_one(CuTest * tc) 630 | { 631 | void* queue = llqueue_new(); 632 | void *r = raft_new(); 633 | raft_cbs_t funcs = { 634 | .get_node_id = __get_node_id}; 635 | raft_log_cbs_t log_funcs = { 636 | .log_pop = __log_pop 637 | }; 638 | raft_set_callbacks(r, &funcs, queue); 639 | 640 | void *l; 641 | 642 | l = raft_log_alloc(1); 643 | raft_log_set_callbacks(l, &log_funcs, r); 644 | 645 | raft_entry_t* ety; 646 | 647 | /* append append */ 648 | __LOG_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 649 | CuAssertIntEquals(tc, 2, raft_log_count(l)); 650 | 651 | /* poll */ 652 | CuAssertIntEquals(tc, raft_log_poll(l, (void *) &ety), 0); 653 | CuAssertIntEquals(tc, ety->id, 1); 654 | CuAssertIntEquals(tc, 1, raft_log_count(l)); 655 | raft_entry_release(ety); 656 | 657 | /* get off-by-one index */ 658 | long n_etys; 659 | CuAssertPtrEquals(tc, raft_log_get_from_idx(l, 1, &n_etys), NULL); 660 | CuAssertIntEquals(tc, n_etys, 0); 661 | 662 | /* now get the correct index */ 663 | raft_entry_t** e; 664 | e = raft_log_get_from_idx(l, 2, &n_etys); 665 | CuAssertPtrNotNull(tc, e); 666 | CuAssertIntEquals(tc, n_etys, 1); 667 | CuAssertIntEquals(tc, e[0]->id, 2); 668 | 669 | raft_entry_t *entry; 670 | while (raft_log_poll(l, &entry) == 0) { 671 | raft_entry_release(entry); 672 | } 673 | 674 | llqueue_free(queue); 675 | raft_log_free(l); 676 | raft_destroy(r); 677 | } 678 | 679 | int main(void) 680 | { 681 | CuString *output = CuStringNew(); 682 | CuSuite* suite = CuSuiteNew(); 683 | 684 | SUITE_ADD_TEST(suite, TestLog_new_is_empty); 685 | SUITE_ADD_TEST(suite, TestLog_append_is_not_empty); 686 | SUITE_ADD_TEST(suite, TestLog_get_at_idx); 687 | SUITE_ADD_TEST(suite, TestLog_get_at_idx_returns_null_where_out_of_bounds); 688 | SUITE_ADD_TEST(suite, TestLog_delete); 689 | SUITE_ADD_TEST(suite, TestLog_delete_onwards); 690 | SUITE_ADD_TEST(suite, TestLog_delete_handles_log_pop_failure); 691 | SUITE_ADD_TEST(suite, TestLog_delete_fails_for_idx_zero); 692 | SUITE_ADD_TEST(suite, TestLog_poll); 693 | SUITE_ADD_TEST(suite, TestLog_peektail); 694 | SUITE_ADD_TEST(suite, TestLog_load_from_snapshot); 695 | SUITE_ADD_TEST(suite, TestLog_load_from_snapshot_clears_log); 696 | SUITE_ADD_TEST(suite, TestLog_front_pushes_across_boundary); 697 | SUITE_ADD_TEST(suite, TestLog_front_and_back_pushed_across_boundary_with_enlargement_required); 698 | SUITE_ADD_TEST(suite, TestLog_delete_after_polling); 699 | SUITE_ADD_TEST(suite, TestLog_delete_after_polling_from_double_append); 700 | SUITE_ADD_TEST(suite, TestLog_get_from_idx_with_base_off_by_one); 701 | 702 | CuSuiteRun(suite); 703 | CuSuiteDetails(suite, output); 704 | printf("%s\n", output->buffer); 705 | 706 | int rc = suite->failCount == 0 ? 0 : 1; 707 | 708 | CuStringFree(output); 709 | CuSuiteFree(suite); 710 | 711 | return rc; 712 | } 713 | -------------------------------------------------------------------------------- /tests/test_log_impl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "linked_list_queue.h" 10 | 11 | #include "raft.h" 12 | #include "raft_private.h" 13 | #include "helpers.h" 14 | 15 | static raft_node_id_t __get_node_id( 16 | raft_server_t* raft, 17 | void *udata, 18 | raft_entry_t *ety, 19 | raft_index_t ety_idx 20 | ) 21 | { 22 | return 0; 23 | } 24 | 25 | static int __log_offer( 26 | raft_server_t* raft, 27 | void *user_data, 28 | raft_entry_t *entry, 29 | raft_index_t entry_idx 30 | ) 31 | { 32 | CuAssertIntEquals((CuTest*)raft, 1, entry_idx); 33 | return 0; 34 | } 35 | 36 | static int __log_pop( 37 | raft_server_t* raft, 38 | void *user_data, 39 | raft_entry_t *entry, 40 | raft_index_t entry_idx 41 | ) 42 | { 43 | raft_entry_t* copy = malloc(sizeof(*entry)); 44 | memcpy(copy, entry, sizeof(*entry)); 45 | llqueue_offer(user_data, copy); 46 | return 0; 47 | } 48 | 49 | static int __log_pop_failing( 50 | raft_server_t* raft, 51 | void *user_data, 52 | raft_entry_t *entry, 53 | raft_index_t entry_idx 54 | ) 55 | { 56 | return -1; 57 | } 58 | 59 | static const raft_log_impl_t *impl = &raft_log_internal_impl; 60 | 61 | static void __LOGIMPL_APPEND_ENTRY(void *l, int id, raft_term_t term, const char *data) 62 | { 63 | raft_entry_t *e = __MAKE_ENTRY(id, term, data); 64 | impl->append(l, e); 65 | raft_entry_release(e); 66 | } 67 | 68 | static void __LOGIMPL_APPEND_ENTRIES_SEQ_ID(void *l, int count, int id, raft_term_t term, const char *data) 69 | { 70 | int i; 71 | for (i = 0; i < count; i++) { 72 | raft_entry_t *e = __MAKE_ENTRY(id++, term, data); 73 | impl->append(l, e); 74 | raft_entry_release(e); 75 | } 76 | } 77 | 78 | void TestLogImpl_new_is_empty(CuTest * tc) 79 | { 80 | void *l; 81 | 82 | l = impl->init(NULL, NULL); 83 | CuAssertTrue(tc, 0 == impl->count(l)); 84 | CuAssertTrue(tc, 1 == impl->first_idx(l)); 85 | 86 | impl->free(l); 87 | } 88 | 89 | void TestLogImpl_append_is_not_empty(CuTest * tc) 90 | { 91 | void *l; 92 | void *r = raft_new(); 93 | 94 | l = impl->init(r, NULL); 95 | __LOGIMPL_APPEND_ENTRY(l, 1, 0, NULL); 96 | CuAssertIntEquals(tc, 1, impl->count(l)); 97 | 98 | impl->free(l); 99 | raft_destroy(r); 100 | } 101 | 102 | void TestLogImpl_get_at_idx(CuTest * tc) 103 | { 104 | void *l; 105 | raft_entry_t *e; 106 | 107 | l = impl->init(NULL, NULL); 108 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 109 | CuAssertIntEquals(tc, 3, impl->count(l)); 110 | 111 | e = impl->get(l, 1); 112 | CuAssertIntEquals(tc, 1, e->id); 113 | raft_entry_release(e); 114 | 115 | e = impl->get(l, 2); 116 | CuAssertIntEquals(tc, 2, e->id); 117 | raft_entry_release(e); 118 | 119 | e = impl->get(l, 3); 120 | CuAssertIntEquals(tc, 3, e->id); 121 | raft_entry_release(e); 122 | 123 | impl->free(l); 124 | } 125 | 126 | void TestLogImpl_get_at_idx_returns_null_where_out_of_bounds(CuTest * tc) 127 | { 128 | void *l; 129 | 130 | l = impl->init(NULL, NULL); 131 | CuAssertTrue(tc, NULL == impl->get(l, 0)); 132 | CuAssertTrue(tc, NULL == impl->get(l, 1)); 133 | 134 | __LOGIMPL_APPEND_ENTRY(l, 1, 0, NULL); 135 | CuAssertTrue(tc, NULL == impl->get(l, 2)); 136 | 137 | impl->free(l); 138 | } 139 | 140 | static void event_entry_enqueue(void *arg, raft_entry_t *e, raft_index_t idx) 141 | { 142 | raft_entry_hold(e); 143 | llqueue_offer(arg, e); 144 | } 145 | 146 | void TestLogImpl_pop(CuTest * tc) 147 | { 148 | void* queue = llqueue_new(); 149 | void *l; 150 | raft_entry_t *ety; 151 | 152 | void *r = raft_new(); 153 | raft_cbs_t funcs = { 154 | .get_node_id = __get_node_id}; 155 | 156 | raft_set_callbacks(r, &funcs, NULL); 157 | 158 | l = impl->init(r, NULL); 159 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 160 | CuAssertIntEquals(tc, 3, impl->count(l)); 161 | CuAssertIntEquals(tc, 3, impl->current_idx(l)); 162 | 163 | ety = impl->get(l, 3); 164 | event_entry_enqueue(queue, ety, 3); 165 | impl->pop(l, 3); 166 | CuAssertIntEquals(tc, 2, impl->count(l)); 167 | raft_entry_release(ety); 168 | 169 | ety = llqueue_poll(queue); 170 | CuAssertIntEquals(tc, 3, ety->id); 171 | CuAssertTrue(tc, NULL == impl->get(l, 3)); 172 | raft_entry_release(ety); 173 | 174 | impl->pop(l, 2); 175 | CuAssertIntEquals(tc, 1, impl->count(l)); 176 | CuAssertTrue(tc, NULL == impl->get(l, 2)); 177 | 178 | impl->pop(l, 1); 179 | CuAssertIntEquals(tc, 0, impl->count(l)); 180 | CuAssertTrue(tc, NULL == impl->get(l, 1)); 181 | 182 | llqueue_free(queue); 183 | impl->free(l); 184 | raft_destroy(r); 185 | } 186 | 187 | void TestLogImpl_pop_onwards(CuTest * tc) 188 | { 189 | void *r = raft_new(); 190 | 191 | void *l; 192 | raft_entry_t *e; 193 | 194 | l = impl->init(r, NULL); 195 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 3, 1, 0, NULL); 196 | CuAssertIntEquals(tc, 3, impl->count(l)); 197 | 198 | /* even 3 gets deleted */ 199 | impl->pop(l, 2); 200 | CuAssertIntEquals(tc, 1, impl->count(l)); 201 | 202 | e = impl->get(l, 1); 203 | CuAssertIntEquals(tc, 1, e->id); 204 | CuAssertTrue(tc, NULL == impl->get(l, 2)); 205 | CuAssertTrue(tc, NULL == impl->get(l, 3)); 206 | raft_entry_release(e); 207 | 208 | impl->free(l); 209 | raft_destroy(r); 210 | } 211 | 212 | 213 | void TestLogImpl_pop_fails_for_idx_zero(CuTest * tc) 214 | { 215 | void *r = raft_new(); 216 | 217 | void *l; 218 | 219 | l = impl->init(r, NULL); 220 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 4, 1, 0, NULL); 221 | CuAssertIntEquals(tc, impl->pop(l, 0), -1); 222 | 223 | impl->free(l); 224 | raft_destroy(r); 225 | } 226 | 227 | void TestLogImpl_poll(CuTest * tc) 228 | { 229 | void *l; 230 | void *r = raft_new(); 231 | raft_entry_t *e; 232 | 233 | 234 | l = impl->init(r, NULL); 235 | __LOGIMPL_APPEND_ENTRY(l, 1, 0, NULL); 236 | CuAssertIntEquals(tc, 1, impl->current_idx(l)); 237 | 238 | __LOGIMPL_APPEND_ENTRY(l, 2, 0, NULL); 239 | CuAssertIntEquals(tc, 2, impl->current_idx(l)); 240 | 241 | __LOGIMPL_APPEND_ENTRY(l, 3, 0, NULL); 242 | CuAssertIntEquals(tc, 3, impl->count(l)); 243 | CuAssertIntEquals(tc, 3, impl->current_idx(l)); 244 | 245 | /* remove 1st */ 246 | CuAssertIntEquals(tc, impl->poll(l, 2), 0); 247 | CuAssertIntEquals(tc, 2, impl->count(l)); 248 | CuAssertIntEquals(tc, 2, impl->first_idx(l)); 249 | CuAssertTrue(tc, NULL == impl->get(l, 1)); 250 | 251 | e = impl->get(l, 2); 252 | CuAssertTrue(tc, NULL != e); 253 | CuAssertIntEquals(tc, e->id, 2); 254 | raft_entry_release(e); 255 | 256 | e = impl->get(l, 3); 257 | CuAssertTrue(tc, NULL != e); 258 | CuAssertIntEquals(tc, e->id, 3); 259 | CuAssertIntEquals(tc, 3, impl->current_idx(l)); 260 | raft_entry_release(e); 261 | 262 | /* remove 2nd */ 263 | CuAssertIntEquals(tc, impl->poll(l, 3), 0); 264 | CuAssertIntEquals(tc, 1, impl->count(l)); 265 | CuAssertTrue(tc, NULL == impl->get(l, 1)); 266 | CuAssertTrue(tc, NULL == impl->get(l, 2)); 267 | 268 | e = impl->get(l, 3); 269 | CuAssertTrue(tc, NULL != e); 270 | CuAssertIntEquals(tc, e->id, 3); 271 | CuAssertIntEquals(tc, 3, impl->current_idx(l)); 272 | raft_entry_release(e); 273 | 274 | /* remove 3rd */ 275 | CuAssertIntEquals(tc, impl->poll(l, 4), 0); 276 | CuAssertIntEquals(tc, 0, impl->count(l)); 277 | CuAssertTrue(tc, NULL == impl->get(l, 1)); 278 | CuAssertTrue(tc, NULL == impl->get(l, 2)); 279 | CuAssertTrue(tc, NULL == impl->get(l, 3)); 280 | CuAssertIntEquals(tc, 3, impl->current_idx(l)); 281 | 282 | impl->free(l); 283 | raft_destroy(r); 284 | } 285 | 286 | void TestLogImpl_reset_after_compaction(CuTest * tc) 287 | { 288 | void *l; 289 | 290 | l = impl->init(NULL, NULL); 291 | CuAssertIntEquals(tc, 0, impl->current_idx(l)); 292 | 293 | /* Reset first_idx==1 indicates our next entry is going to be 10 */ 294 | impl->reset(l, 10, 1); 295 | 296 | /* Current index is 9 (first_idx - 1), but log is empty */ 297 | CuAssertIntEquals(tc, 9, impl->current_idx(l)); 298 | CuAssertIntEquals(tc, 0, impl->count(l)); 299 | 300 | impl->free(l); 301 | } 302 | 303 | void TestLogImpl_load_from_snapshot_clears_log(CuTest * tc) 304 | { 305 | void *l; 306 | 307 | l = impl->init(NULL, NULL); 308 | 309 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 310 | CuAssertIntEquals(tc, 2, impl->count(l)); 311 | CuAssertIntEquals(tc, 2, impl->current_idx(l)); 312 | 313 | impl->reset(l, 10, 1); 314 | CuAssertIntEquals(tc, 0, impl->count(l)); 315 | CuAssertIntEquals(tc, 9, impl->current_idx(l)); 316 | 317 | impl->free(l); 318 | } 319 | 320 | void TestLogImpl_pop_after_polling(CuTest * tc) 321 | { 322 | void *l; 323 | 324 | l = impl->init(NULL, NULL); 325 | 326 | /* append */ 327 | __LOGIMPL_APPEND_ENTRY(l, 1, 0, NULL); 328 | CuAssertIntEquals(tc, 1, impl->count(l)); 329 | CuAssertIntEquals(tc, 1, impl->first_idx(l)); 330 | CuAssertIntEquals(tc, 1, impl->current_idx(l)); 331 | 332 | /* poll */ 333 | CuAssertIntEquals(tc, impl->poll(l, 2), 0); 334 | CuAssertIntEquals(tc, 0, impl->count(l)); 335 | CuAssertIntEquals(tc, 2, impl->first_idx(l)); 336 | CuAssertIntEquals(tc, 1, impl->current_idx(l)); 337 | 338 | /* append */ 339 | __LOGIMPL_APPEND_ENTRY(l, 2, 0, NULL); 340 | CuAssertIntEquals(tc, 1, impl->count(l)); 341 | CuAssertIntEquals(tc, 2, impl->current_idx(l)); 342 | 343 | /* poll */ 344 | CuAssertIntEquals(tc, impl->pop(l, 1), 0); 345 | CuAssertIntEquals(tc, 0, impl->count(l)); 346 | CuAssertIntEquals(tc, 1, impl->current_idx(l)); 347 | 348 | impl->free(l); 349 | } 350 | 351 | void TestLogImpl_pop_after_polling_from_double_append(CuTest * tc) 352 | { 353 | void *l; 354 | void *r = raft_new(); 355 | raft_entry_t *e; 356 | 357 | 358 | l = impl->init(r, NULL); 359 | 360 | /* append append */ 361 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 362 | CuAssertIntEquals(tc, 2, impl->count(l)); 363 | 364 | /* poll */ 365 | CuAssertIntEquals(tc, impl->poll(l, 2), 0); 366 | e = impl->get(l, 2); 367 | CuAssertIntEquals(tc, e->id, 2); 368 | CuAssertIntEquals(tc, 1, impl->count(l)); 369 | raft_entry_release(e); 370 | 371 | /* append */ 372 | __LOGIMPL_APPEND_ENTRY(l, 3, 0, NULL); 373 | CuAssertIntEquals(tc, 2, impl->count(l)); 374 | 375 | /* pop */ 376 | CuAssertIntEquals(tc, impl->pop(l, 1), 0); 377 | CuAssertIntEquals(tc, 0, impl->count(l)); 378 | 379 | impl->free(l); 380 | raft_destroy(r); 381 | } 382 | 383 | void TestLogImpl_get_from_idx_with_base_off_by_one(CuTest * tc) 384 | { 385 | void *r = raft_new(); 386 | 387 | void *l; 388 | 389 | l = impl->init(r, NULL); 390 | 391 | /* append append */ 392 | __LOGIMPL_APPEND_ENTRIES_SEQ_ID(l, 2, 1, 0, NULL); 393 | CuAssertIntEquals(tc, 2, impl->count(l)); 394 | 395 | /* poll */ 396 | CuAssertIntEquals(tc, impl->poll(l, 2), 0); 397 | CuAssertIntEquals(tc, 1, impl->count(l)); 398 | 399 | /* get off-by-one index */ 400 | raft_entry_t *e[1] = {NULL}; 401 | CuAssertIntEquals(tc, impl->get_batch(l, 1, 1, e), 0); 402 | 403 | /* now get the correct index */ 404 | CuAssertIntEquals(tc, impl->get_batch(l, 2, 1, e), 1); 405 | CuAssertIntEquals(tc, e[0]->id, 2); 406 | raft_entry_release(e[0]); 407 | 408 | impl->free(l); 409 | raft_destroy(r); 410 | } 411 | 412 | int main(void) 413 | { 414 | CuString *output = CuStringNew(); 415 | CuSuite* suite = CuSuiteNew(); 416 | 417 | SUITE_ADD_TEST(suite, TestLogImpl_new_is_empty); 418 | SUITE_ADD_TEST(suite, TestLogImpl_append_is_not_empty); 419 | SUITE_ADD_TEST(suite, TestLogImpl_get_at_idx); 420 | SUITE_ADD_TEST(suite, TestLogImpl_get_at_idx_returns_null_where_out_of_bounds); 421 | SUITE_ADD_TEST(suite, TestLogImpl_pop); 422 | SUITE_ADD_TEST(suite, TestLogImpl_pop_onwards); 423 | SUITE_ADD_TEST(suite, TestLogImpl_pop_fails_for_idx_zero); 424 | SUITE_ADD_TEST(suite, TestLogImpl_poll); 425 | SUITE_ADD_TEST(suite, TestLogImpl_reset_after_compaction); 426 | SUITE_ADD_TEST(suite, TestLogImpl_load_from_snapshot_clears_log); 427 | SUITE_ADD_TEST(suite, TestLogImpl_pop_after_polling); 428 | SUITE_ADD_TEST(suite, TestLogImpl_pop_after_polling_from_double_append); 429 | SUITE_ADD_TEST(suite, TestLogImpl_get_from_idx_with_base_off_by_one); 430 | 431 | CuSuiteRun(suite); 432 | CuSuiteDetails(suite, output); 433 | printf("%s\n", output->buffer); 434 | 435 | int rc = suite->failCount == 0 ? 0 : 1; 436 | 437 | CuStringFree(output); 438 | CuSuiteFree(suite); 439 | 440 | return rc; 441 | } 442 | -------------------------------------------------------------------------------- /tests/test_node.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "raft.h" 10 | #include "raft_log.h" 11 | #include "raft_private.h" 12 | 13 | void TestRaft_node_set_nextIdx(CuTest * tc) 14 | { 15 | raft_node_t *p = raft_node_new((void *) 1, 1, 1); 16 | raft_node_set_next_idx(p, 3); 17 | CuAssertTrue(tc, 3 == raft_node_get_next_idx(p)); 18 | raft_node_free(p); 19 | } 20 | 21 | 22 | int main(void) 23 | { 24 | CuString *output = CuStringNew(); 25 | CuSuite* suite = CuSuiteNew(); 26 | 27 | SUITE_ADD_TEST(suite, TestRaft_node_set_nextIdx); 28 | 29 | CuSuiteRun(suite); 30 | CuSuiteDetails(suite, output); 31 | printf("%s\n", output->buffer); 32 | 33 | int rc = suite->failCount == 0 ? 0 : 1; 34 | 35 | CuStringFree(output); 36 | CuSuiteFree(suite); 37 | 38 | return rc; 39 | } 40 | -------------------------------------------------------------------------------- /tests/test_scenario.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "raft.h" 10 | #include "raft_log.h" 11 | #include "raft_private.h" 12 | #include "mock_send_functions.h" 13 | 14 | static int __raft_persist_metadata( 15 | raft_server_t *raft, 16 | void *udata, 17 | raft_term_t term, 18 | raft_node_id_t vote 19 | ) 20 | { 21 | return 0; 22 | } 23 | 24 | void TestRaft_scenario_leader_appears(CuTest * tc) 25 | { 26 | unsigned long i, j; 27 | raft_server_t *r[3]; 28 | void* sender[3]; 29 | 30 | senders_new(); 31 | 32 | for (j = 0; j < 3; j++) 33 | sender[j] = sender_new((void*)j); 34 | 35 | for (j = 0; j < 3; j++) 36 | { 37 | r[j] = raft_new(); 38 | sender_set_raft(sender[j], r[j]); 39 | raft_config(r[j], 1, RAFT_CONFIG_ELECTION_TIMEOUT, 500); 40 | raft_add_node(r[j], sender[0], 1, j==0); 41 | raft_add_node(r[j], sender[1], 2, j==1); 42 | raft_add_node(r[j], sender[2], 3, j==2); 43 | raft_set_callbacks(r[j], 44 | &((raft_cbs_t) { 45 | .send_requestvote = sender_requestvote, 46 | .send_appendentries = sender_appendentries, 47 | .persist_metadata = __raft_persist_metadata, 48 | .log = NULL 49 | }), sender[j]); 50 | } 51 | 52 | /* NOTE: important for 1st node to send vote request before others */ 53 | raft_periodic_internal(r[0], 1000); 54 | 55 | for (i = 0; i < 20; i++) 56 | { 57 | one_more_time: 58 | 59 | for (j = 0; j < 3; j++) 60 | sender_poll_msgs(sender[j]); 61 | 62 | for (j = 0; j < 3; j++) 63 | if (sender_msgs_available(sender[j])) 64 | goto one_more_time; 65 | 66 | for (j = 0; j < 3; j++) 67 | raft_periodic_internal(r[j], 100); 68 | } 69 | 70 | int leaders = 0; 71 | for (j = 0; j < 3; j++) 72 | if (raft_is_leader(r[j])) 73 | leaders += 1; 74 | 75 | CuAssertTrue(tc, 0 != leaders); 76 | CuAssertTrue(tc, 1 == leaders); 77 | } 78 | 79 | 80 | int main(void) 81 | { 82 | CuString *output = CuStringNew(); 83 | CuSuite* suite = CuSuiteNew(); 84 | 85 | SUITE_ADD_TEST(suite, TestRaft_scenario_leader_appears); 86 | 87 | CuSuiteRun(suite); 88 | CuSuiteDetails(suite, output); 89 | printf("%s\n", output->buffer); 90 | 91 | int rc = suite->failCount == 0 ? 0 : 1; 92 | 93 | CuStringFree(output); 94 | CuSuiteFree(suite); 95 | 96 | return rc; 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_snapshotting.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "CuTest.h" 8 | 9 | #include "raft.h" 10 | #include "raft_log.h" 11 | #include "raft_private.h" 12 | #include "mock_send_functions.h" 13 | 14 | #include "helpers.h" 15 | 16 | static int __raft_persist_metadata( 17 | raft_server_t* raft, 18 | void *udata, 19 | raft_term_t term, 20 | raft_node_id_t vote 21 | ) 22 | { 23 | return 0; 24 | } 25 | 26 | static int __raft_applylog( 27 | raft_server_t* raft, 28 | void *udata, 29 | raft_entry_t *ety, 30 | raft_index_t idx 31 | ) 32 | { 33 | return 0; 34 | } 35 | 36 | static int __raft_send_requestvote(raft_server_t* raft, 37 | void* udata, 38 | raft_node_t* node, 39 | raft_requestvote_req_t* msg) 40 | { 41 | return 0; 42 | } 43 | 44 | static int __raft_send_appendentries(raft_server_t* raft, 45 | void* udata, 46 | raft_node_t* node, 47 | raft_appendentries_req_t* msg) 48 | { 49 | return 0; 50 | } 51 | 52 | static int __raft_send_appendentries_capture(raft_server_t* raft, 53 | void* udata, 54 | raft_node_t* node, 55 | raft_appendentries_req_t* msg) 56 | { 57 | *((raft_appendentries_req_t*)udata) = *msg; 58 | return 0; 59 | } 60 | 61 | static int __raft_send_snapshot_increment(raft_server_t* raft, 62 | void* udata, 63 | raft_node_t* node, 64 | raft_snapshot_req_t *msg) 65 | { 66 | int *counter = udata; 67 | 68 | (*counter)++; 69 | return 0; 70 | } 71 | 72 | static int __raft_get_snapshot_chunk(raft_server_t* raft, 73 | void *user_data, 74 | raft_node_t* node, 75 | raft_size_t offset, 76 | raft_snapshot_chunk_t* chunk) 77 | { 78 | if (offset > 0) { 79 | return RAFT_ERR_DONE; 80 | } 81 | 82 | chunk->data = "test"; 83 | chunk->len = strlen("test"); 84 | chunk->last_chunk = 1; 85 | 86 | return 0; 87 | } 88 | 89 | static int __raft_store_snapshot_chunk(raft_server_t* raft, 90 | void *user_data, 91 | raft_index_t snapshot_index, 92 | raft_size_t offset, 93 | raft_snapshot_chunk_t* chunk) 94 | { 95 | return 0; 96 | } 97 | 98 | static int __raft_clear_snapshot(raft_server_t* raft, 99 | void *user_data) 100 | { 101 | return 0; 102 | } 103 | 104 | struct test_data 105 | { 106 | /* Config */ 107 | bool fail_load; 108 | bool fail_store_chunk; 109 | 110 | /* Stats */ 111 | int send; 112 | int get_chunk; 113 | int store_chunk; 114 | int store_chunk_fails; 115 | int clear; 116 | int load; 117 | int load_fails; 118 | }; 119 | 120 | static int test_send_snapshot_increment(raft_server_t* raft, 121 | void* udata, 122 | raft_node_t* node, 123 | raft_snapshot_req_t *msg) 124 | { 125 | struct test_data *t = udata; 126 | 127 | t->send++; 128 | return 0; 129 | } 130 | 131 | static int test_get_snapshot_chunk(raft_server_t* raft, 132 | void *user_data, 133 | raft_node_t* node, 134 | raft_size_t offset, 135 | raft_snapshot_chunk_t* chunk) 136 | { 137 | struct test_data *t = user_data; 138 | 139 | t->get_chunk++; 140 | 141 | if (offset > 0) { 142 | return RAFT_ERR_DONE; 143 | } 144 | 145 | chunk->data = "test"; 146 | chunk->len = strlen("test"); 147 | chunk->last_chunk = 1; 148 | 149 | return 0; 150 | } 151 | 152 | static int test_store_snapshot_chunk(raft_server_t* raft, 153 | void *user_data, 154 | raft_index_t snapshot_index, 155 | raft_size_t offset, 156 | raft_snapshot_chunk_t* chunk) 157 | { 158 | struct test_data *t = user_data; 159 | 160 | if (t->fail_store_chunk) { 161 | t->store_chunk_fails++; 162 | return -1; 163 | } 164 | 165 | t->store_chunk++; 166 | return 0; 167 | } 168 | 169 | static int test_clear_snapshot(raft_server_t* raft, 170 | void *user_data) 171 | { 172 | struct test_data *t = user_data; 173 | t->clear++; 174 | 175 | return 0; 176 | } 177 | 178 | static int test_load_snapshot(raft_server_t* raft, 179 | void *user_data, 180 | raft_term_t snapshot_term, 181 | raft_index_t snapshot_index) 182 | { 183 | struct test_data *t = user_data; 184 | 185 | if (t->fail_load) { 186 | t->load_fails++; 187 | return -1; 188 | } 189 | 190 | t->load++; 191 | 192 | raft_begin_load_snapshot(raft, snapshot_term, snapshot_index); 193 | raft_end_load_snapshot(raft); 194 | 195 | return 0; 196 | } 197 | 198 | static int max_election_timeout(int election_timeout) 199 | { 200 | return 2 * election_timeout; 201 | } 202 | 203 | // TODO: don't apply logs while snapshotting 204 | // TODO: don't cause elections while snapshotting 205 | 206 | void TestRaft_leader_begin_snapshot_fails_if_no_logs_to_compact(CuTest * tc) 207 | { 208 | raft_cbs_t funcs = { 209 | .send_appendentries = __raft_send_appendentries, 210 | }; 211 | 212 | void *r = raft_new(); 213 | raft_set_callbacks(r, &funcs, NULL); 214 | 215 | raft_entry_resp_t cr; 216 | 217 | raft_add_node(r, NULL, 1, 1); 218 | raft_add_node(r, NULL, 2, 0); 219 | 220 | /* I am the leader */ 221 | raft_set_state(r, RAFT_STATE_LEADER); 222 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 223 | 224 | /* entry message */ 225 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 226 | 227 | /* receive entry */ 228 | raft_recv_entry(r, ety, &cr); 229 | raft_entry_release(ety); 230 | 231 | ety = __MAKE_ENTRY(2, 1, "entry"); 232 | raft_recv_entry(r, ety, &cr); 233 | raft_entry_release(ety); 234 | 235 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 236 | CuAssertIntEquals(tc, -1, raft_begin_snapshot(r)); 237 | 238 | raft_set_commit_idx(r, 1); 239 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 240 | 241 | raft_destroy(r); 242 | } 243 | 244 | void TestRaft_leader_will_not_apply_entry_if_snapshot_is_in_progress(CuTest * tc) 245 | { 246 | raft_cbs_t funcs = { 247 | .send_appendentries = __raft_send_appendentries, 248 | }; 249 | 250 | void *r = raft_new(); 251 | raft_set_callbacks(r, &funcs, NULL); 252 | 253 | raft_entry_resp_t cr; 254 | 255 | raft_add_node(r, NULL, 1, 1); 256 | raft_add_node(r, NULL, 2, 0); 257 | 258 | /* I am the leader */ 259 | raft_set_state(r, RAFT_STATE_LEADER); 260 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 261 | 262 | /* entry message */ 263 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 264 | 265 | /* receive entry */ 266 | raft_recv_entry(r, ety, &cr); 267 | raft_recv_entry(r, ety, &cr); 268 | raft_entry_release(ety); 269 | raft_set_commit_idx(r, 1); 270 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 271 | 272 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 273 | CuAssertIntEquals(tc, 1, raft_get_last_applied_idx(r)); 274 | raft_set_commit_idx(r, 2); 275 | CuAssertIntEquals(tc, -1, raft_apply_entry(r)); 276 | CuAssertIntEquals(tc, 1, raft_get_last_applied_idx(r)); 277 | 278 | raft_destroy(r); 279 | } 280 | 281 | void TestRaft_leader_snapshot_end_fails_if_snapshot_not_in_progress(CuTest * tc) 282 | { 283 | raft_cbs_t funcs = { 284 | .send_appendentries = __raft_send_appendentries, 285 | }; 286 | 287 | void *r = raft_new(); 288 | raft_set_callbacks(r, &funcs, NULL); 289 | 290 | raft_add_node(r, NULL, 1, 1); 291 | raft_add_node(r, NULL, 2, 0); 292 | 293 | /* I am the leader */ 294 | raft_set_state(r, RAFT_STATE_LEADER); 295 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 296 | CuAssertIntEquals(tc, -1, raft_end_snapshot(r)); 297 | 298 | raft_destroy(r); 299 | } 300 | 301 | void TestRaft_leader_snapshot_begin_fails_if_less_than_2_logs_to_compact(CuTest * tc) 302 | { 303 | raft_cbs_t funcs = { 304 | .send_appendentries = __raft_send_appendentries, 305 | }; 306 | 307 | void *r = raft_new(); 308 | raft_set_callbacks(r, &funcs, NULL); 309 | 310 | raft_entry_resp_t cr; 311 | 312 | raft_add_node(r, NULL, 1, 1); 313 | raft_add_node(r, NULL, 2, 0); 314 | 315 | /* I am the leader */ 316 | raft_set_state(r, RAFT_STATE_LEADER); 317 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 318 | 319 | /* entry message */ 320 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 321 | 322 | /* receive entry */ 323 | raft_recv_entry(r, ety, &cr); 324 | raft_entry_release(ety); 325 | raft_set_commit_idx(r, 1); 326 | CuAssertIntEquals(tc, 1, raft_get_log_count(r)); 327 | CuAssertIntEquals(tc, -1, raft_begin_snapshot(r)); 328 | 329 | raft_destroy(r); 330 | } 331 | 332 | void TestRaft_leader_snapshot_end_succeeds_if_log_compacted(CuTest * tc) 333 | { 334 | raft_cbs_t funcs = { 335 | .persist_metadata = __raft_persist_metadata, 336 | .send_appendentries = __raft_send_appendentries, 337 | }; 338 | 339 | raft_server_t *r = raft_new(); 340 | raft_set_callbacks(r, &funcs, NULL); 341 | 342 | raft_entry_resp_t cr; 343 | 344 | raft_add_node(r, NULL, 1, 1); 345 | raft_add_node(r, NULL, 2, 0); 346 | 347 | /* I am the leader */ 348 | raft_set_state(r, RAFT_STATE_LEADER); 349 | raft_set_current_term(r, 1); 350 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 351 | 352 | /* entry message */ 353 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 354 | 355 | /* receive entry */ 356 | raft_recv_entry(r, ety, &cr); 357 | raft_entry_release(ety); 358 | 359 | ety = __MAKE_ENTRY(2, 1, "entry"); 360 | raft_recv_entry(r, ety, &cr); 361 | raft_entry_release(ety); 362 | 363 | ety = __MAKE_ENTRY(3, 1, "entry"); 364 | raft_recv_entry(r, ety, &cr); 365 | raft_entry_release(ety); 366 | 367 | raft_set_commit_idx(r, 2); 368 | 369 | CuAssertIntEquals(tc, 3, raft_get_log_count(r)); 370 | CuAssertIntEquals(tc, 2, raft_get_num_snapshottable_logs(r)); 371 | CuAssertIntEquals(tc, 1, raft_get_last_log_term(r)); 372 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 373 | CuAssertIntEquals(tc, 0, raft_end_snapshot(r)); 374 | CuAssertIntEquals(tc, 0, raft_get_num_snapshottable_logs(r)); 375 | CuAssertIntEquals(tc, 1, raft_get_log_count(r)); 376 | CuAssertIntEquals(tc, 2, raft_get_commit_idx(r)); 377 | CuAssertIntEquals(tc, 2, raft_get_last_applied_idx(r)); 378 | CuAssertIntEquals(tc, 1, raft_get_last_log_term(r)); 379 | CuAssertIntEquals(tc, 0, raft_periodic_internal(r, 1000)); 380 | 381 | 382 | /* the above test returns correct term as didn't snapshot all entries, makes sure works if we snapshot all */ 383 | 384 | /* snapshot doesn't work if only one entry to snapshot, so add another one */ 385 | ety = __MAKE_ENTRY(4, 1, "entry"); 386 | raft_recv_entry(r, ety, &cr); 387 | raft_entry_release(ety); 388 | 389 | raft_set_commit_idx(r, 4); 390 | CuAssertIntEquals(tc, 0, raft_periodic_internal(r, 1000)); 391 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 392 | CuAssertIntEquals(tc, 4, raft_get_commit_idx(r)); 393 | CuAssertIntEquals(tc, 3, r->log_impl->first_idx(r->log)); 394 | CuAssertIntEquals(tc, 2, raft_get_num_snapshottable_logs(r)); 395 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 396 | CuAssertIntEquals(tc, 0, raft_end_snapshot(r)); 397 | CuAssertIntEquals(tc, 0, raft_get_num_snapshottable_logs(r)); 398 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 399 | CuAssertIntEquals(tc, 4, raft_get_commit_idx(r)); 400 | CuAssertIntEquals(tc, 4, raft_get_last_applied_idx(r)); 401 | CuAssertIntEquals(tc, 1, raft_get_last_log_term(r)); 402 | 403 | raft_destroy(r); 404 | } 405 | 406 | void TestRaft_leader_snapshot_end_succeeds_if_log_compacted2(CuTest * tc) 407 | { 408 | raft_cbs_t funcs = { 409 | .persist_metadata = __raft_persist_metadata, 410 | .send_appendentries = __raft_send_appendentries, 411 | }; 412 | 413 | void *r = raft_new(); 414 | raft_set_callbacks(r, &funcs, NULL); 415 | 416 | raft_entry_resp_t cr; 417 | 418 | raft_add_node(r, NULL, 1, 1); 419 | raft_add_node(r, NULL, 2, 0); 420 | 421 | /* I am the leader */ 422 | raft_set_state(r, RAFT_STATE_LEADER); 423 | raft_set_current_term(r, 1); 424 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 425 | 426 | /* entry message */ 427 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 428 | 429 | /* receive entry */ 430 | raft_recv_entry(r, ety, &cr); 431 | raft_entry_release(ety); 432 | 433 | ety = __MAKE_ENTRY(2, 1, "entry"); 434 | raft_recv_entry(r, ety, &cr); 435 | raft_entry_release(ety); 436 | 437 | ety = __MAKE_ENTRY(3, 1, "entry"); 438 | raft_recv_entry(r, ety, &cr); 439 | raft_entry_release(ety); 440 | 441 | raft_set_commit_idx(r, 2); 442 | CuAssertIntEquals(tc, 3, raft_get_log_count(r)); 443 | CuAssertIntEquals(tc, 2, raft_get_num_snapshottable_logs(r)); 444 | 445 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 446 | CuAssertIntEquals(tc, 0, raft_end_snapshot(r)); 447 | CuAssertIntEquals(tc, 0, raft_get_num_snapshottable_logs(r)); 448 | CuAssertIntEquals(tc, 1, raft_get_log_count(r)); 449 | CuAssertIntEquals(tc, 2, raft_get_commit_idx(r)); 450 | CuAssertIntEquals(tc, 2, raft_get_last_applied_idx(r)); 451 | CuAssertIntEquals(tc, 0, raft_periodic_internal(r, 1000)); 452 | 453 | raft_destroy(r); 454 | } 455 | 456 | void TestRaft_joinee_needs_to_get_snapshot(CuTest * tc) 457 | { 458 | raft_cbs_t funcs = { 459 | .send_appendentries = __raft_send_appendentries, 460 | }; 461 | 462 | void *r = raft_new(); 463 | raft_set_callbacks(r, &funcs, NULL); 464 | 465 | raft_entry_resp_t cr; 466 | 467 | raft_add_node(r, NULL, 1, 1); 468 | raft_add_node(r, NULL, 2, 0); 469 | 470 | /* I am the leader */ 471 | raft_set_state(r, RAFT_STATE_LEADER); 472 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 473 | 474 | /* entry message */ 475 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 476 | 477 | /* receive entry */ 478 | raft_recv_entry(r, ety, &cr); 479 | raft_entry_release(ety); 480 | 481 | ety = __MAKE_ENTRY(2, 1, "entry"); 482 | raft_recv_entry(r, ety, &cr); 483 | raft_entry_release(ety); 484 | 485 | raft_set_commit_idx(r, 1); 486 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 487 | CuAssertIntEquals(tc, 1, raft_get_num_snapshottable_logs(r)); 488 | 489 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 490 | CuAssertIntEquals(tc, 1, raft_get_last_applied_idx(r)); 491 | CuAssertIntEquals(tc, -1, raft_apply_entry(r)); 492 | CuAssertIntEquals(tc, 1, raft_get_last_applied_idx(r)); 493 | 494 | raft_destroy(r); 495 | } 496 | 497 | void TestRaft_follower_load_from_snapshot(CuTest * tc) 498 | { 499 | raft_cbs_t funcs = { 500 | }; 501 | 502 | void *r = raft_new(); 503 | raft_set_callbacks(r, &funcs, NULL); 504 | 505 | raft_add_node(r, NULL, 1, 1); 506 | raft_add_node(r, NULL, 2, 0); 507 | 508 | raft_set_state(r, RAFT_STATE_FOLLOWER); 509 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 510 | 511 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 512 | CuAssertIntEquals(tc, 0, raft_begin_load_snapshot(r, 5, 5)); 513 | CuAssertIntEquals(tc, 0, raft_end_load_snapshot(r)); 514 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 515 | CuAssertIntEquals(tc, 0, raft_get_num_snapshottable_logs(r)); 516 | CuAssertIntEquals(tc, 5, raft_get_commit_idx(r)); 517 | CuAssertIntEquals(tc, 5, raft_get_last_applied_idx(r)); 518 | 519 | CuAssertIntEquals(tc, 0, raft_periodic_internal(r, 1000)); 520 | 521 | /* current idx means snapshot was unnecessary */ 522 | raft_entry_t *ety = __MAKE_ENTRY(2, 1, "entry"); 523 | raft_append_entry(r, ety); 524 | raft_entry_release(ety); 525 | 526 | ety = __MAKE_ENTRY(3, 1, "entry"); 527 | raft_append_entry(r, ety); 528 | raft_entry_release(ety); 529 | 530 | raft_set_commit_idx(r, 7); 531 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_begin_load_snapshot(r, 6, 5)); 532 | CuAssertIntEquals(tc, 7, raft_get_commit_idx(r)); 533 | 534 | raft_destroy(r); 535 | } 536 | 537 | void TestRaft_follower_load_from_snapshot_fails_if_term_is_0(CuTest * tc) 538 | { 539 | raft_cbs_t funcs = { 540 | }; 541 | 542 | void *r = raft_new(); 543 | raft_set_callbacks(r, &funcs, NULL); 544 | 545 | raft_add_node(r, NULL, 1, 1); 546 | raft_add_node(r, NULL, 2, 0); 547 | 548 | raft_set_state(r, RAFT_STATE_FOLLOWER); 549 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 550 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_begin_load_snapshot(r, 0, 5)); 551 | 552 | raft_destroy(r); 553 | } 554 | 555 | void TestRaft_follower_load_from_snapshot_fails_if_already_loaded(CuTest * tc) 556 | { 557 | raft_cbs_t funcs = { 558 | }; 559 | 560 | void *r = raft_new(); 561 | raft_set_callbacks(r, &funcs, NULL); 562 | 563 | raft_add_node(r, NULL, 1, 1); 564 | raft_add_node(r, NULL, 2, 0); 565 | 566 | raft_set_state(r, RAFT_STATE_FOLLOWER); 567 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 568 | 569 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 570 | CuAssertIntEquals(tc, 0, raft_begin_load_snapshot(r, 5, 5)); 571 | CuAssertIntEquals(tc, 0, raft_end_load_snapshot(r)); 572 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 573 | CuAssertIntEquals(tc, 0, raft_get_num_snapshottable_logs(r)); 574 | CuAssertIntEquals(tc, 5, raft_get_commit_idx(r)); 575 | CuAssertIntEquals(tc, 5, raft_get_last_applied_idx(r)); 576 | 577 | CuAssertIntEquals(tc, RAFT_ERR_SNAPSHOT_ALREADY_LOADED, raft_begin_load_snapshot(r, 5, 5)); 578 | 579 | raft_destroy(r); 580 | } 581 | 582 | void TestRaft_follower_load_from_snapshot_does_not_break_cluster_safety(CuTest * tc) 583 | { 584 | raft_cbs_t funcs = { 585 | }; 586 | 587 | void *r = raft_new(); 588 | raft_set_current_term(r, 1); 589 | 590 | raft_set_callbacks(r, &funcs, NULL); 591 | 592 | raft_add_node(r, NULL, 1, 1); 593 | raft_add_node(r, NULL, 2, 0); 594 | 595 | raft_set_state(r, RAFT_STATE_FOLLOWER); 596 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 597 | 598 | /* entry message */ 599 | __RAFT_APPEND_ENTRY(r, 1, 1, "entry"); 600 | __RAFT_APPEND_ENTRY(r, 2, 1, "entry"); 601 | __RAFT_APPEND_ENTRY(r, 3, 1, "entry"); 602 | 603 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_begin_load_snapshot(r, 2, 2)); 604 | 605 | raft_destroy(r); 606 | } 607 | 608 | void TestRaft_follower_load_from_snapshot_fails_if_log_is_newer(CuTest * tc) 609 | { 610 | raft_cbs_t funcs = { 611 | }; 612 | 613 | void *r = raft_new(); 614 | raft_set_callbacks(r, &funcs, NULL); 615 | 616 | raft_add_node(r, NULL, 1, 1); 617 | raft_add_node(r, NULL, 2, 0); 618 | 619 | raft_set_state(r, RAFT_STATE_FOLLOWER); 620 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 621 | 622 | raft_set_last_applied_idx(r, 5); 623 | 624 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_begin_load_snapshot(r, 2, 2)); 625 | 626 | raft_destroy(r); 627 | } 628 | 629 | void TestRaft_leader_sends_snapshot_when_node_next_index_was_compacted(CuTest* tc) 630 | { 631 | raft_cbs_t funcs = { 632 | .send_snapshot = __raft_send_snapshot_increment, 633 | .clear_snapshot = __raft_clear_snapshot, 634 | .store_snapshot_chunk = __raft_store_snapshot_chunk, 635 | .get_snapshot_chunk = __raft_get_snapshot_chunk 636 | }; 637 | 638 | int increment = 0; 639 | 640 | void *r = raft_new(); 641 | raft_set_current_term(r, 3); 642 | raft_set_callbacks(r, &funcs, &increment); 643 | 644 | raft_node_t* node; 645 | raft_add_node(r, NULL, 1, 1); 646 | raft_add_node(r, NULL, 2, 0); 647 | raft_add_node(r, NULL, 3, 0); 648 | 649 | /* entry 1 */ 650 | __RAFT_APPEND_ENTRIES_SEQ_ID(r, 3, 1, 1, "aaa"); 651 | CuAssertIntEquals(tc, 3, raft_get_current_idx(r)); 652 | 653 | /* compact entry 1 & 2 */ 654 | CuAssertIntEquals(tc, 0, raft_begin_load_snapshot(r, 2, 3)); 655 | CuAssertIntEquals(tc, 0, raft_end_load_snapshot(r)); 656 | CuAssertIntEquals(tc, 3, raft_get_current_idx(r)); 657 | 658 | /* reconfigure nodes based on config data "embedded in snapshot" */ 659 | node = raft_add_node(r, NULL, 2, 0); 660 | raft_add_node(r, NULL, 3, 0); 661 | 662 | /* node wants an entry that was compacted */ 663 | raft_node_set_next_idx(node, raft_get_current_idx(r)); 664 | 665 | raft_set_state(r, RAFT_STATE_LEADER); 666 | 667 | /* verify snapshot is sent */ 668 | int rc = raft_send_appendentries(r, node); 669 | CuAssertIntEquals(tc, 0, rc); 670 | CuAssertIntEquals(tc, 1, increment); 671 | 672 | /* update callbacks, verify correct appendreq is sent after the snapshot */ 673 | funcs = (raft_cbs_t) { 674 | .send_appendentries = __raft_send_appendentries_capture, 675 | }; 676 | 677 | raft_appendentries_req_t ae; 678 | raft_set_callbacks(r, &funcs, &ae); 679 | 680 | /* node wants an entry just one after the snapshot index */ 681 | raft_node_set_next_idx(node, raft_get_current_idx(r) + 1); 682 | 683 | CuAssertIntEquals(tc, 0, raft_send_appendentries(r, node)); 684 | CuAssertIntEquals(tc, 3, ae.term); 685 | CuAssertIntEquals(tc, 3, ae.prev_log_idx); 686 | CuAssertIntEquals(tc, 2, ae.prev_log_term); 687 | 688 | raft_destroy(r); 689 | } 690 | 691 | void TestRaft_recv_entry_fails_if_snapshot_in_progress(CuTest* tc) 692 | { 693 | raft_cbs_t funcs = { 694 | .send_appendentries = __raft_send_appendentries, 695 | }; 696 | 697 | void *r = raft_new(); 698 | raft_set_callbacks(r, &funcs, NULL); 699 | 700 | raft_entry_resp_t cr; 701 | 702 | raft_add_node(r, NULL, 1, 1); 703 | raft_add_node(r, NULL, 2, 0); 704 | 705 | /* I am the leader */ 706 | raft_set_state(r, RAFT_STATE_LEADER); 707 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 708 | 709 | /* entry message */ 710 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 711 | 712 | /* receive entry */ 713 | raft_recv_entry(r, ety, &cr); 714 | raft_entry_release(ety); 715 | 716 | ety = __MAKE_ENTRY(2, 1, "entry"); 717 | raft_recv_entry(r, ety, &cr); 718 | raft_entry_release(ety); 719 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 720 | 721 | raft_set_commit_idx(r, 1); 722 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 723 | 724 | ety = __MAKE_ENTRY(3, 1, "entry"); 725 | ety->type = RAFT_LOGTYPE_ADD_NODE; 726 | CuAssertIntEquals(tc, RAFT_ERR_SNAPSHOT_IN_PROGRESS, raft_recv_entry(r, ety, &cr)); 727 | raft_entry_release(ety); 728 | 729 | raft_destroy(r); 730 | } 731 | 732 | void TestRaft_recv_entry_succeeds_if_snapshot_nonblocking_apply_is_set(CuTest* tc) 733 | { 734 | raft_cbs_t funcs = { 735 | .send_appendentries = __raft_send_appendentries, 736 | }; 737 | 738 | void *r = raft_new(); 739 | raft_set_callbacks(r, &funcs, NULL); 740 | 741 | raft_entry_resp_t cr; 742 | 743 | raft_add_node(r, NULL, 1, 1); 744 | raft_add_node(r, NULL, 2, 0); 745 | 746 | /* I am the leader */ 747 | raft_set_state(r, RAFT_STATE_LEADER); 748 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 749 | 750 | /* entry message */ 751 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 752 | 753 | /* receive entry */ 754 | raft_recv_entry(r, ety, &cr); 755 | raft_entry_release(ety); 756 | 757 | ety = __MAKE_ENTRY(2, 1, "entry"); 758 | raft_recv_entry(r, ety, &cr); 759 | raft_entry_release(ety); 760 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 761 | 762 | raft_set_commit_idx(r, 1); 763 | raft_config(r, 1, RAFT_CONFIG_NONBLOCKING_APPLY, 1); 764 | 765 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 766 | 767 | ety = __MAKE_ENTRY(3, 1, "entry"); 768 | ety->type = RAFT_LOGTYPE_ADD_NODE; 769 | CuAssertIntEquals(tc, 0, raft_recv_entry(r, ety, &cr)); 770 | raft_entry_release(ety); 771 | 772 | raft_destroy(r); 773 | } 774 | 775 | 776 | void TestRaft_follower_recv_appendentries_is_successful_when_previous_log_idx_equals_snapshot_last_idx( 777 | CuTest * tc) 778 | { 779 | raft_cbs_t funcs = { 780 | .persist_metadata = __raft_persist_metadata, 781 | }; 782 | 783 | void *r = raft_new(); 784 | raft_set_callbacks(r, &funcs, NULL); 785 | 786 | raft_add_node(r, NULL, 1, 1); 787 | raft_add_node(r, NULL, 2, 0); 788 | 789 | CuAssertIntEquals(tc, 0, raft_begin_load_snapshot(r, 2, 2)); 790 | CuAssertIntEquals(tc, 0, raft_end_load_snapshot(r)); 791 | 792 | raft_appendentries_req_t ae; 793 | raft_appendentries_resp_t aer; 794 | 795 | memset(&ae, 0, sizeof(raft_appendentries_req_t)); 796 | ae.term = 3; 797 | ae.prev_log_idx = 2; 798 | ae.prev_log_term = 2; 799 | /* include entries */ 800 | ae.entries = __MAKE_ENTRY_ARRAY(3, 3, NULL); 801 | ae.n_entries = 1; 802 | CuAssertIntEquals(tc, 0, raft_recv_appendentries(r, raft_get_node(r, 2), &ae, &aer)); 803 | CuAssertIntEquals(tc, 1, aer.success); 804 | 805 | for (raft_index_t i = 0; i < ae.n_entries; i++) { 806 | raft_entry_release(ae.entries[i]); 807 | } 808 | raft_free(ae.entries); 809 | 810 | raft_destroy(r); 811 | } 812 | 813 | void TestRaft_leader_sends_appendentries_with_correct_prev_log_idx_when_snapshotted( 814 | CuTest * tc) 815 | { 816 | raft_cbs_t funcs = { 817 | .send_appendentries = sender_appendentries, 818 | .log = NULL 819 | }; 820 | 821 | void *sender = sender_new(NULL); 822 | void *r = raft_new(); 823 | raft_set_callbacks(r, &funcs, sender); 824 | CuAssertTrue(tc, NULL != raft_add_node(r, NULL, 1, 1)); 825 | CuAssertTrue(tc, NULL != raft_add_node(r, NULL, 2, 0)); 826 | 827 | CuAssertIntEquals(tc, 0, raft_begin_load_snapshot(r, 2, 4)); 828 | CuAssertIntEquals(tc, 0, raft_end_load_snapshot(r)); 829 | 830 | /* i'm leader */ 831 | raft_set_state(r, RAFT_STATE_LEADER); 832 | 833 | /* reload node configuration; we expect the app to decode it from 834 | * the loaded snapshot. 835 | */ 836 | raft_node_t* p = raft_add_node(r, NULL, 2, 0); 837 | CuAssertTrue(tc, NULL != p); 838 | raft_node_set_next_idx(p, 5); 839 | 840 | /* receive appendentries messages */ 841 | raft_send_appendentries(r, p); 842 | raft_appendentries_req_t* ae = sender_poll_msg_data(sender); 843 | CuAssertTrue(tc, NULL != ae); 844 | CuAssertIntEquals(tc, 2, ae->prev_log_term); 845 | CuAssertIntEquals(tc, 4, ae->prev_log_idx); 846 | 847 | for (raft_index_t i = 0; i < ae->n_entries; i++) { 848 | raft_entry_release(ae->entries[i]); 849 | } 850 | raft_free(ae->entries); 851 | raft_free(ae); 852 | 853 | raft_destroy(r); 854 | } 855 | 856 | void TestRaft_cancel_snapshot_restores_state(CuTest* tc) 857 | { 858 | raft_cbs_t funcs = { 859 | .send_appendentries = __raft_send_appendentries, 860 | }; 861 | 862 | void *r = raft_new(); 863 | raft_set_callbacks(r, &funcs, NULL); 864 | 865 | raft_entry_resp_t cr; 866 | 867 | raft_add_node(r, NULL, 1, 1); 868 | raft_add_node(r, NULL, 2, 0); 869 | 870 | /* I am the leader */ 871 | raft_set_state(r, RAFT_STATE_LEADER); 872 | CuAssertIntEquals(tc, 0, raft_get_log_count(r)); 873 | 874 | /* single entry */ 875 | raft_entry_req_t *ety = __MAKE_ENTRY(1, 1, "entry"); 876 | raft_recv_entry(r, ety, &cr); 877 | raft_entry_release(ety); 878 | 879 | ety = __MAKE_ENTRY(2, 1, "entry"); 880 | raft_recv_entry(r, ety, &cr); 881 | raft_entry_release(ety); 882 | raft_set_commit_idx(r, 2); 883 | 884 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 885 | CuAssertIntEquals(tc, 0, raft_end_snapshot(r)); 886 | 887 | /* more entries */ 888 | ety = __MAKE_ENTRY(3, 1, "entry"); 889 | raft_recv_entry(r, ety, &cr); 890 | raft_entry_release(ety); 891 | 892 | ety = __MAKE_ENTRY(4, 1, "entry"); 893 | raft_recv_entry(r, ety, &cr); 894 | raft_entry_release(ety); 895 | 896 | CuAssertIntEquals(tc, 2, raft_get_log_count(r)); 897 | CuAssertIntEquals(tc, 2, raft_get_snapshot_last_idx(r)); 898 | 899 | /* begin and cancel another snapshot */ 900 | raft_set_commit_idx(r, 4); 901 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 902 | CuAssertIntEquals(tc, 1, raft_snapshot_is_in_progress(r)); 903 | CuAssertIntEquals(tc, 0, raft_cancel_snapshot(r)); 904 | 905 | /* snapshot no longer in progress, index must not have changed */ 906 | CuAssertIntEquals(tc, 0, raft_snapshot_is_in_progress(r)); 907 | CuAssertIntEquals(tc, 2, raft_get_snapshot_last_idx(r)); 908 | 909 | raft_destroy(r); 910 | } 911 | 912 | void TestRaft_leader_sends_snapshot_if_log_was_compacted(CuTest* tc) 913 | { 914 | raft_cbs_t funcs = { 915 | .send_snapshot = __raft_send_snapshot_increment, 916 | .send_appendentries = __raft_send_appendentries, 917 | .clear_snapshot = __raft_clear_snapshot, 918 | .store_snapshot_chunk = __raft_store_snapshot_chunk, 919 | .get_snapshot_chunk = __raft_get_snapshot_chunk 920 | }; 921 | 922 | int send_snapshot_count = 0; 923 | 924 | void *r = raft_new(); 925 | raft_set_callbacks(r, &funcs, &send_snapshot_count); 926 | 927 | raft_node_t* node; 928 | raft_add_node(r, NULL, 1, 1); 929 | node = raft_add_node(r, NULL, 2, 0); 930 | raft_add_node(r, NULL, 3, 0); 931 | 932 | raft_set_state(r, RAFT_STATE_LEADER); 933 | raft_set_current_term(r, 1); 934 | 935 | /* entry 1 */ 936 | char *str = "aaa"; 937 | __RAFT_APPEND_ENTRY(r, 1, 1, str); 938 | 939 | /* entry 2 */ 940 | __RAFT_APPEND_ENTRY(r, 2, 1, str); 941 | 942 | /* entry 3 */ 943 | __RAFT_APPEND_ENTRY(r, 3, 1, str); 944 | CuAssertIntEquals(tc, 3, raft_get_current_idx(r)); 945 | 946 | /* compact entry 1 & 2 */ 947 | raft_set_commit_idx(r, 2); 948 | CuAssertIntEquals(tc, 0, raft_begin_snapshot(r)); 949 | CuAssertIntEquals(tc, 0, raft_end_snapshot(r)); 950 | CuAssertIntEquals(tc, 1, raft_get_log_count(r)); 951 | 952 | /* at this point a snapshot should have been sent; we'll continue 953 | * assuming the node was not available to get it. 954 | */ 955 | CuAssertIntEquals(tc, 2, send_snapshot_count); 956 | send_snapshot_count = 0; 957 | 958 | /* node wants an entry that was compacted */ 959 | raft_node_set_match_idx(node, 1); 960 | raft_node_set_next_idx(node, 2); 961 | 962 | raft_appendentries_resp_t aer = { 963 | .term = 1, 964 | .success = 0, 965 | .current_idx = 1 966 | }; 967 | 968 | raft_recv_appendentries_response(r, node, &aer); 969 | CuAssertIntEquals(tc, 1, send_snapshot_count); 970 | 971 | raft_destroy(r); 972 | } 973 | 974 | void TestRaft_clear_snapshot_on_leader_change(CuTest * tc) 975 | { 976 | raft_cbs_t funcs = { 977 | .send_snapshot = test_send_snapshot_increment, 978 | .send_appendentries = __raft_send_appendentries, 979 | .clear_snapshot = test_clear_snapshot, 980 | .load_snapshot = test_load_snapshot, 981 | .store_snapshot_chunk = test_store_snapshot_chunk, 982 | .get_snapshot_chunk = test_get_snapshot_chunk 983 | }; 984 | 985 | struct test_data data = {0}; 986 | 987 | void *r = raft_new(); 988 | raft_set_callbacks(r, &funcs, &data); 989 | raft_add_node(r, NULL, 1, 1); 990 | 991 | raft_snapshot_req_t msg = { 992 | .leader_id = 2, 993 | .snapshot_index = 1, 994 | .snapshot_term = 1, 995 | .msg_id = 1, 996 | .chunk.data = "tmp", 997 | .chunk.len = strlen("tmp"), 998 | .chunk.last_chunk = 0, 999 | }; 1000 | 1001 | raft_snapshot_resp_t resp = {0}; 1002 | 1003 | raft_recv_snapshot(r, NULL, &msg, &resp); 1004 | CuAssertIntEquals(tc, 1, data.store_chunk); 1005 | 1006 | msg.msg_id = 2; 1007 | msg.leader_id = 3; 1008 | 1009 | raft_recv_snapshot(r, NULL, &msg, &resp); 1010 | CuAssertIntEquals(tc, 1, data.clear); 1011 | CuAssertIntEquals(tc, 2, data.store_chunk); 1012 | 1013 | raft_destroy(r); 1014 | } 1015 | 1016 | void TestRaft_reject_wrong_offset(CuTest * tc) 1017 | { 1018 | raft_cbs_t funcs = { 1019 | .send_snapshot = test_send_snapshot_increment, 1020 | .send_appendentries = __raft_send_appendentries, 1021 | .clear_snapshot = test_clear_snapshot, 1022 | .load_snapshot = test_load_snapshot, 1023 | .store_snapshot_chunk = test_store_snapshot_chunk, 1024 | .get_snapshot_chunk = test_get_snapshot_chunk 1025 | }; 1026 | 1027 | struct test_data data = {0}; 1028 | 1029 | void *r = raft_new(); 1030 | raft_set_callbacks(r, &funcs, &data); 1031 | raft_add_node(r, NULL, 1, 1); 1032 | 1033 | raft_snapshot_req_t msg = { 1034 | .leader_id = 2, 1035 | .snapshot_index = 1, 1036 | .snapshot_term = 1, 1037 | .msg_id = 1, 1038 | .chunk.data = "tmp", 1039 | .chunk.offset = 0, 1040 | .chunk.len = 50, 1041 | .chunk.last_chunk = 0, 1042 | }; 1043 | 1044 | raft_snapshot_resp_t resp = {0}; 1045 | 1046 | raft_recv_snapshot(r, NULL, &msg, &resp); 1047 | CuAssertIntEquals(tc, 1, data.store_chunk); 1048 | 1049 | msg.chunk.offset = 80; 1050 | raft_recv_snapshot(r, NULL, &msg, &resp); 1051 | 1052 | CuAssertIntEquals(tc, 0, resp.success); 1053 | CuAssertIntEquals(tc, 50, resp.offset); 1054 | 1055 | raft_destroy(r); 1056 | } 1057 | 1058 | void TestRaft_set_last_chunk_on_duplicate(CuTest * tc) 1059 | { 1060 | raft_cbs_t funcs = { 1061 | .send_snapshot = test_send_snapshot_increment, 1062 | .send_appendentries = __raft_send_appendentries, 1063 | .clear_snapshot = test_clear_snapshot, 1064 | .load_snapshot = test_load_snapshot, 1065 | .store_snapshot_chunk = test_store_snapshot_chunk, 1066 | .get_snapshot_chunk = test_get_snapshot_chunk 1067 | }; 1068 | 1069 | struct test_data data = {0}; 1070 | 1071 | void *r = raft_new(); 1072 | raft_set_callbacks(r, &funcs, &data); 1073 | raft_add_node(r, NULL, 1, 1); 1074 | 1075 | raft_snapshot_req_t msg = { 1076 | .term = 1, 1077 | .leader_id = 2, 1078 | .snapshot_index = 5, 1079 | .snapshot_term = 1, 1080 | .msg_id = 1, 1081 | .chunk.data = "tmp", 1082 | .chunk.offset = 0, 1083 | .chunk.len = 50, 1084 | .chunk.last_chunk = 1, 1085 | }; 1086 | 1087 | raft_snapshot_resp_t resp = {0}; 1088 | 1089 | raft_recv_snapshot(r, NULL, &msg, &resp); 1090 | CuAssertIntEquals(tc, 1, data.store_chunk); 1091 | CuAssertIntEquals(tc, 1, data.load); 1092 | 1093 | raft_snapshot_req_t msg2 = { 1094 | .term = 1, 1095 | .leader_id = 2, 1096 | .snapshot_index = 4, 1097 | .snapshot_term = 1, 1098 | .msg_id = 1, 1099 | .chunk.offset = 0, 1100 | .chunk.len = 50, 1101 | .chunk.last_chunk = 0, 1102 | }; 1103 | 1104 | raft_recv_snapshot(r, NULL, &msg2, &resp); 1105 | 1106 | CuAssertIntEquals(tc, 1, resp.success); 1107 | CuAssertIntEquals(tc, 1, resp.last_chunk); 1108 | 1109 | raft_destroy(r); 1110 | } 1111 | 1112 | void TestRaft_set_last_chunk_if_log_is_more_advanced(CuTest * tc) 1113 | { 1114 | raft_cbs_t funcs = { 1115 | .send_snapshot = test_send_snapshot_increment, 1116 | .send_appendentries = __raft_send_appendentries, 1117 | .clear_snapshot = test_clear_snapshot, 1118 | .load_snapshot = test_load_snapshot, 1119 | .store_snapshot_chunk = test_store_snapshot_chunk, 1120 | .get_snapshot_chunk = test_get_snapshot_chunk 1121 | }; 1122 | 1123 | struct test_data data = {0}; 1124 | 1125 | void *r = raft_new(); 1126 | raft_set_current_term(r, 1); 1127 | raft_set_callbacks(r, &funcs, &data); 1128 | raft_add_node(r, NULL, 1, 1); 1129 | 1130 | __RAFT_APPEND_ENTRY(r, 1, 1, "entry"); 1131 | __RAFT_APPEND_ENTRY(r, 2, 1, "entry"); 1132 | __RAFT_APPEND_ENTRY(r, 4, 1, "entry"); 1133 | __RAFT_APPEND_ENTRY(r, 5, 1, "entry"); 1134 | __RAFT_APPEND_ENTRY(r, 6, 1, "entry"); 1135 | 1136 | raft_snapshot_req_t msg = { 1137 | .term = 1, 1138 | .leader_id = 2, 1139 | .snapshot_index = 5, 1140 | .snapshot_term = 1, 1141 | .msg_id = 1, 1142 | .chunk.data = "tmp", 1143 | .chunk.offset = 0, 1144 | .chunk.len = 50, 1145 | .chunk.last_chunk = 1, 1146 | }; 1147 | 1148 | raft_snapshot_resp_t resp = {0}; 1149 | 1150 | raft_recv_snapshot(r, NULL, &msg, &resp); 1151 | CuAssertIntEquals(tc, 0, data.store_chunk); 1152 | CuAssertIntEquals(tc, 0, data.load); 1153 | CuAssertIntEquals(tc, 1, resp.success); 1154 | CuAssertIntEquals(tc, 1, resp.last_chunk); 1155 | 1156 | raft_destroy(r); 1157 | } 1158 | 1159 | void TestRaft_report_fail_on_load_snapshot_error(CuTest * tc) 1160 | { 1161 | /** 1162 | * If load_snapshot() or store_snapshot_chunk() callback fails, node should 1163 | * return response as if like it didn't receive the current chunk. Leader 1164 | * will send the same chunk again and operation can be retried. 1165 | */ 1166 | raft_cbs_t funcs = { 1167 | .send_snapshot = test_send_snapshot_increment, 1168 | .send_appendentries = __raft_send_appendentries, 1169 | .clear_snapshot = test_clear_snapshot, 1170 | .load_snapshot = test_load_snapshot, 1171 | .store_snapshot_chunk = test_store_snapshot_chunk, 1172 | .get_snapshot_chunk = test_get_snapshot_chunk 1173 | }; 1174 | 1175 | struct test_data data = {0}; 1176 | 1177 | void *r = raft_new(); 1178 | raft_set_current_term(r, 1); 1179 | raft_set_callbacks(r, &funcs, &data); 1180 | raft_add_node(r, NULL, 1, 1); 1181 | raft_become_leader(r); 1182 | 1183 | __RAFT_APPEND_ENTRY(r, 1, 1, "entry"); 1184 | __RAFT_APPEND_ENTRY(r, 2, 1, "entry"); 1185 | __RAFT_APPEND_ENTRY(r, 4, 1, "entry"); 1186 | __RAFT_APPEND_ENTRY(r, 5, 1, "entry"); 1187 | __RAFT_APPEND_ENTRY(r, 6, 1, "entry"); 1188 | 1189 | raft_snapshot_req_t msg = { 1190 | .term = 1, 1191 | .leader_id = 2, 1192 | .snapshot_index = 7, 1193 | .snapshot_term = 1, 1194 | .msg_id = 1, 1195 | .chunk.data = "tmp", 1196 | .chunk.offset = 0, 1197 | .chunk.len = 50, 1198 | .chunk.last_chunk = 0, 1199 | }; 1200 | 1201 | raft_snapshot_resp_t resp = {0}; 1202 | raft_recv_snapshot(r, NULL, &msg, &resp); 1203 | 1204 | /* Inject failure to load_snapshot() callback. */ 1205 | data.fail_load = 1; 1206 | 1207 | raft_snapshot_req_t msg2 = { 1208 | .term = 1, 1209 | .leader_id = 2, 1210 | .snapshot_index = 7, 1211 | .snapshot_term = 1, 1212 | .msg_id = 1, 1213 | .chunk.data = "tmp", 1214 | .chunk.offset = 50, 1215 | .chunk.len = 30, 1216 | .chunk.last_chunk = 1, 1217 | }; 1218 | 1219 | raft_recv_snapshot(r, NULL, &msg2, &resp); 1220 | 1221 | CuAssertIntEquals(tc, 0, data.load); 1222 | CuAssertIntEquals(tc, 1, data.load_fails); 1223 | CuAssertIntEquals(tc, 0, resp.success); 1224 | CuAssertIntEquals(tc, 50, resp.offset); 1225 | CuAssertIntEquals(tc, 1, resp.last_chunk); 1226 | 1227 | /* Inject failure to store_chunk() callback. */ 1228 | data.fail_load = 0; 1229 | data.fail_store_chunk = 1; 1230 | raft_recv_snapshot(r, NULL, &msg2, &resp); 1231 | 1232 | CuAssertIntEquals(tc, 0, data.load); 1233 | CuAssertIntEquals(tc, 1, data.load_fails); 1234 | CuAssertIntEquals(tc, 0, resp.success); 1235 | CuAssertIntEquals(tc, 50, resp.offset); 1236 | CuAssertIntEquals(tc, 1, resp.last_chunk); 1237 | 1238 | /* Disable injected failures and verify operation is successful. */ 1239 | data.fail_store_chunk = 0; 1240 | raft_recv_snapshot(r, NULL, &msg2, &resp); 1241 | CuAssertIntEquals(tc, 1, data.load); 1242 | CuAssertIntEquals(tc, 1, resp.success); 1243 | CuAssertIntEquals(tc, 80, resp.offset); 1244 | CuAssertIntEquals(tc, 1, resp.last_chunk); 1245 | 1246 | raft_destroy(r); 1247 | } 1248 | 1249 | void TestRaft_restore_after_restart(CuTest * tc) 1250 | { 1251 | raft_server_t *r = raft_new(); 1252 | raft_add_node(r, NULL, 1, 1); 1253 | 1254 | /* Restore should fail if the term is not 0 */ 1255 | raft_set_current_term(r, 10); 1256 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_restore_snapshot(r, -1, 3)); 1257 | CuAssertIntEquals(tc, RAFT_ERR_MISUSE, raft_restore_snapshot(r, 11, 5)); 1258 | 1259 | raft_destroy(r); 1260 | 1261 | r = raft_new(); 1262 | raft_add_node(r, NULL, 1, 1); 1263 | 1264 | /* Restore should work on start-up */ 1265 | CuAssertIntEquals(tc, 0, raft_restore_snapshot(r, 11, 5)); 1266 | 1267 | raft_destroy(r); 1268 | } 1269 | 1270 | int main(void) 1271 | { 1272 | CuString *output = CuStringNew(); 1273 | CuSuite* suite = CuSuiteNew(); 1274 | 1275 | SUITE_ADD_TEST(suite, TestRaft_leader_begin_snapshot_fails_if_no_logs_to_compact); 1276 | SUITE_ADD_TEST(suite, TestRaft_leader_will_not_apply_entry_if_snapshot_is_in_progress); 1277 | SUITE_ADD_TEST(suite, TestRaft_leader_snapshot_end_fails_if_snapshot_not_in_progress); 1278 | SUITE_ADD_TEST(suite, TestRaft_leader_snapshot_begin_fails_if_less_than_2_logs_to_compact); 1279 | SUITE_ADD_TEST(suite, TestRaft_leader_snapshot_end_succeeds_if_log_compacted); 1280 | SUITE_ADD_TEST(suite, TestRaft_leader_snapshot_end_succeeds_if_log_compacted2); 1281 | SUITE_ADD_TEST(suite, TestRaft_joinee_needs_to_get_snapshot); 1282 | SUITE_ADD_TEST(suite, TestRaft_follower_load_from_snapshot); 1283 | SUITE_ADD_TEST(suite, TestRaft_follower_load_from_snapshot_fails_if_term_is_0); 1284 | SUITE_ADD_TEST(suite, TestRaft_follower_load_from_snapshot_fails_if_already_loaded); 1285 | SUITE_ADD_TEST(suite, TestRaft_follower_load_from_snapshot_does_not_break_cluster_safety); 1286 | SUITE_ADD_TEST(suite, TestRaft_follower_load_from_snapshot_fails_if_log_is_newer); 1287 | SUITE_ADD_TEST(suite, TestRaft_leader_sends_snapshot_when_node_next_index_was_compacted); 1288 | SUITE_ADD_TEST(suite, TestRaft_recv_entry_fails_if_snapshot_in_progress); 1289 | SUITE_ADD_TEST(suite, TestRaft_recv_entry_succeeds_if_snapshot_nonblocking_apply_is_set); 1290 | SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_is_successful_when_previous_log_idx_equals_snapshot_last_idx); 1291 | SUITE_ADD_TEST(suite, TestRaft_leader_sends_appendentries_with_correct_prev_log_idx_when_snapshotted); 1292 | SUITE_ADD_TEST(suite, TestRaft_cancel_snapshot_restores_state); 1293 | SUITE_ADD_TEST(suite, TestRaft_leader_sends_snapshot_if_log_was_compacted); 1294 | SUITE_ADD_TEST(suite, TestRaft_clear_snapshot_on_leader_change); 1295 | SUITE_ADD_TEST(suite, TestRaft_reject_wrong_offset); 1296 | SUITE_ADD_TEST(suite, TestRaft_set_last_chunk_on_duplicate); 1297 | SUITE_ADD_TEST(suite, TestRaft_set_last_chunk_if_log_is_more_advanced); 1298 | SUITE_ADD_TEST(suite, TestRaft_report_fail_on_load_snapshot_error); 1299 | SUITE_ADD_TEST(suite, TestRaft_restore_after_restart); 1300 | 1301 | CuSuiteRun(suite); 1302 | CuSuiteDetails(suite, output); 1303 | printf("%s\n", output->buffer); 1304 | 1305 | int rc = suite->failCount == 0 ? 0 : 1; 1306 | 1307 | CuStringFree(output); 1308 | CuSuiteFree(suite); 1309 | 1310 | return rc; 1311 | } 1312 | --------------------------------------------------------------------------------