├── client ├── python │ ├── anna │ │ ├── __init__.py │ │ ├── common.py │ │ └── zmq_util.py │ ├── setup.py │ └── compile.sh └── cpp │ └── CMakeLists.txt ├── .gitmodules ├── tests ├── simple │ ├── expected │ ├── input │ └── test-simple.sh ├── include │ ├── CMakeLists.txt │ └── lattices │ │ ├── CMakeLists.txt │ │ ├── run_lattice_tests.cpp │ │ ├── test_bool_lattice.hpp │ │ ├── test_max_lattice.hpp │ │ ├── test_set_lattice.hpp │ │ ├── test_ordered_set_lattice.hpp │ │ └── test_map_lattice.hpp ├── kvs │ ├── test_rep_factor_change_handler.hpp │ ├── test_rep_factor_response_handler.hpp │ ├── CMakeLists.txt │ ├── run_server_handler_tests.cpp │ ├── test_self_depart_handler.hpp │ ├── test_node_depart_handler.hpp │ ├── test_gossip_handler.hpp │ ├── test_node_join_handler.hpp │ └── server_handler_base.hpp ├── mock │ ├── CMakeLists.txt │ ├── mock_hash_utils.cpp │ └── mock_hash_utils.hpp ├── CMakeLists.txt ├── route │ ├── CMakeLists.txt │ ├── test_seed_handler.hpp │ ├── test_membership_handler.hpp │ ├── run_routing_handler_tests.cpp │ ├── test_address_handler.hpp │ ├── test_replication_change_handler.hpp │ ├── test_replication_response_handler.hpp │ └── routing_handler_base.hpp └── test_all.cpp ├── conf ├── anna-base.yml └── anna-local.yml ├── scripts ├── run-tests.sh ├── travis │ ├── upload-codecov.sh │ ├── travis-build.sh │ └── docker-build.sh ├── stop-anna-local.sh ├── start-anna-local.sh └── build.sh ├── include ├── CMakeLists.txt ├── kvs_types.hpp ├── kvs │ ├── base_kv_store.hpp │ └── kvs_handlers.hpp ├── proto │ ├── benchmark.proto │ └── metadata.proto ├── hashers.hpp ├── monitor │ ├── monitoring_handlers.hpp │ ├── policies.hpp │ └── monitoring_utils.hpp ├── route │ └── routing_handlers.hpp ├── kvs_common.hpp ├── consistent_hash_map.hpp ├── hash_ring.hpp └── metadata.hpp ├── src ├── hash_ring │ └── CMakeLists.txt ├── CMakeLists.txt ├── route │ ├── CMakeLists.txt │ ├── seed_handler.cpp │ ├── replication_change_handler.cpp │ ├── address_handler.cpp │ ├── membership_handler.cpp │ └── replication_response_handler.cpp ├── monitor │ ├── CMakeLists.txt │ ├── elasticity.cpp │ ├── feedback_handler.cpp │ ├── depart_done_handler.cpp │ ├── storage_policy.cpp │ └── membership_handler.cpp ├── benchmark │ ├── CMakeLists.txt │ └── trigger.cpp └── kvs │ ├── CMakeLists.txt │ ├── node_depart_handler.cpp │ ├── management_node_response_handler.cpp │ ├── cache_ip_response_handler.cpp │ ├── self_depart_handler.cpp │ ├── gossip_handler.cpp │ ├── utils.cpp │ ├── user_request_handler.cpp │ ├── node_join_handler.cpp │ └── replication_change_handler.cpp ├── docs ├── local-mode.md └── building-anna.md ├── .travis.yml ├── dockerfiles ├── anna.dockerfile └── start-anna.sh ├── .gitignore ├── README.md └── CMakeLists.txt /client/python/anna/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | 2 | [submodule "common"] 3 | path = common 4 | url = https://github.com/hydro-project/common 5 | -------------------------------------------------------------------------------- /tests/simple/expected: -------------------------------------------------------------------------------- 1 | Success! 2 | 1 3 | Success! 4 | 2 5 | Success! 6 | 10 7 | Success! 8 | { 3 2 1 } 9 | Success! 10 | { 4 3 2 1 } 11 | Success! 12 | {test : 1} 13 | dep1 : {test1 : 1} 14 | hello 15 | -------------------------------------------------------------------------------- /tests/simple/input: -------------------------------------------------------------------------------- 1 | PUT a 1 2 | GET a 3 | PUT b 2 4 | GET b 5 | PUT a 10 6 | GET a 7 | PUT_SET set 1 2 3 8 | GET_SET set 9 | PUT_SET set 1 2 4 10 | GET_SET set 11 | PUT_CAUSAL c hello 12 | GET_CAUSAL c 13 | -------------------------------------------------------------------------------- /conf/anna-base.yml: -------------------------------------------------------------------------------- 1 | ebs: /ebs 2 | capacities: # in GB 3 | memory-cap: 45 4 | ebs-cap: 256 5 | threads: 6 | memory: 4 7 | ebs: 4 8 | routing: 4 9 | benchmark: 4 10 | replication: 11 | memory: 1 12 | ebs: 0 13 | minimum: 1 14 | local: 1 15 | policy: 16 | elasticity: true 17 | selective-rep: true 18 | tiering: false 19 | -------------------------------------------------------------------------------- /scripts/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | bash tests/simple/test-simple.sh 18 | cd build && make test 19 | -------------------------------------------------------------------------------- /tests/include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | ADD_SUBDIRECTORY(lattices) 18 | -------------------------------------------------------------------------------- /tests/kvs/test_rep_factor_change_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | // TODO: write actual tests 18 | -------------------------------------------------------------------------------- /tests/kvs/test_rep_factor_response_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | // TODO: write actual tests 18 | -------------------------------------------------------------------------------- /conf/anna-local.yml: -------------------------------------------------------------------------------- 1 | monitoring: 2 | mgmt_ip: 127.0.0.1 3 | ip: 127.0.0.1 4 | routing: 5 | monitoring: 6 | - 127.0.0.1 7 | ip: 127.0.0.1 8 | user: 9 | monitoring: 10 | - 127.0.0.1 11 | routing: 12 | - 127.0.0.1 13 | ip: 127.0.0.1 14 | server: 15 | monitoring: 16 | - 127.0.0.1 17 | routing: 18 | - 127.0.0.1 19 | seed_ip: 127.0.0.1 20 | public_ip: 127.0.0.1 21 | private_ip: 127.0.0.1 22 | mgmt_ip: "NULL" 23 | policy: 24 | elasticity: false 25 | selective-rep: false 26 | tiering: false 27 | ebs: ./ 28 | capacities: # in GB 29 | memory-cap: 1 30 | ebs-cap: 0 31 | threads: 32 | memory: 1 33 | ebs: 1 34 | routing: 1 35 | benchmark: 1 36 | replication: 37 | memory: 1 38 | ebs: 0 39 | minimum: 1 40 | local: 1 41 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6) 16 | 17 | ADD_SUBDIRECTORY(kvs) 18 | ADD_SUBDIRECTORY(route) 19 | ADD_SUBDIRECTORY(monitor) 20 | -------------------------------------------------------------------------------- /tests/mock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | ADD_LIBRARY(anna-mock STATIC mock_hash_utils.cpp) 18 | TARGET_LINK_LIBRARIES(anna-mock anna-hash-ring) 19 | -------------------------------------------------------------------------------- /scripts/travis/upload-codecov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # NOTE: This script assumes it is run from the project root directory. 18 | cd build 19 | make test-coverage 20 | lcov --list coverage.info 21 | bash <(curl -s https://codecov.io/bash) 22 | -------------------------------------------------------------------------------- /src/hash_ring/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | ADD_LIBRARY(anna-hash-ring STATIC hash_ring.cpp) 18 | TARGET_LINK_LIBRARIES(anna-hash-ring anna-proto ${KV_LIBRARY_DEPENDENCIES}) 19 | ADD_DEPENDENCIES(anna-hash-ring zeromq zeromqcpp) 20 | -------------------------------------------------------------------------------- /tests/include/lattices/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | ADD_EXECUTABLE(run_lattice_tests run_lattice_tests.cpp) 18 | TARGET_LINK_LIBRARIES (run_lattice_tests gtest gmock) 19 | ADD_DEPENDENCIES(run_lattice_tests gtest spdlog) 20 | 21 | ADD_TEST(NAME LatticeTest COMMAND run_lattice_tests) 22 | -------------------------------------------------------------------------------- /scripts/stop-anna-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | if [ -z "$1" ]; then 18 | echo "Usage: ./$0 remove-logs" 19 | exit 1 20 | fi 21 | 22 | while IFS='' read -r line || [[ -n "$line" ]] ; do 23 | kill $line 24 | done < "pids" 25 | 26 | if [ "$1" = "y" ]; then 27 | rm *log* 28 | fi 29 | 30 | rm conf/anna-config.yml 31 | rm pids 32 | -------------------------------------------------------------------------------- /docs/local-mode.md: -------------------------------------------------------------------------------- 1 | # Running Anna in Local Mode 2 | 3 | This document assumes you have already built Anna locally; you can find those docs [here](building-anna.md). Once you have built Anna, you can use the `scripts/start-anna-local.sh` script to start the KVS server. By default, the `conf/anna-local.yml` config file is used, which only specifies one routing thread and one storage thread. You are welcome to modify this file if you would like, but we generally do not recommend running more than one thread per process in local mode. 4 | 5 | `scripts/start-anna-local.sh {build} {start-cli}` takes two arguments. If `build` is set to `y` (or `yes`) the build scripts will be run before start the Anna server. If `{start-cli}` is specified, the interactive Anna CLI will be started in this context after the server is started. 6 | 7 | `scripts/stop-anna-local.sh {remove-logs}` can be used to stop the Anna server. If `remove-logs` is specified, all logs will be deleted. Otherwise they will be left in place -- this is primarily used for debugging purposes. 8 | -------------------------------------------------------------------------------- /client/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/cli) 18 | 19 | SET(LIBRARY_DEPENDENCIES 20 | protobuf::libprotobuf 21 | anna-proto 22 | zmq 23 | hydro-zmq 24 | yaml-cpp 25 | ) 26 | 27 | ADD_EXECUTABLE(anna-cli cli.cpp) 28 | TARGET_LINK_LIBRARIES(anna-cli ${LIBRARY_DEPENDENCIES}) 29 | ADD_DEPENDENCIES(anna-cli zeromq zeromqcpp) 30 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(KV_LIBRARY_DEPENDENCIES 18 | protobuf::libprotobuf 19 | anna-proto 20 | pthread 21 | zmq 22 | hydro-zmq 23 | yaml-cpp 24 | ) 25 | 26 | SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/target/kvs) 27 | 28 | ADD_SUBDIRECTORY(hash_ring) 29 | ADD_SUBDIRECTORY(kvs) 30 | ADD_SUBDIRECTORY(monitor) 31 | ADD_SUBDIRECTORY(route) 32 | ADD_SUBDIRECTORY(benchmark) 33 | -------------------------------------------------------------------------------- /tests/include/lattices/run_lattice_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "test_bool_lattice.hpp" 21 | #include "test_map_lattice.hpp" 22 | #include "test_max_lattice.hpp" 23 | #include "test_ordered_set_lattice.hpp" 24 | #include "test_set_lattice.hpp" 25 | 26 | int main(int argc, char *argv[]) { 27 | testing::InitGoogleTest(&argc, argv); 28 | return RUN_ALL_TESTS(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 18 | 19 | ADD_SUBDIRECTORY(mock) 20 | ADD_SUBDIRECTORY(include) 21 | ADD_SUBDIRECTORY(kvs) 22 | ADD_SUBDIRECTORY(route) 23 | 24 | INCLUDE_DIRECTORIES(mock) 25 | 26 | ADD_CUSTOM_TARGET(ctest ${CMAKE_CTEST_COMMAND}) 27 | 28 | IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") 29 | SETUP_TARGET_FOR_COVERAGE(test-coverage ctest coverage) 30 | ENDIF() 31 | -------------------------------------------------------------------------------- /src/route/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(ROUTING_SOURCE 18 | routing.cpp 19 | seed_handler.cpp 20 | membership_handler.cpp 21 | replication_response_handler.cpp 22 | replication_change_handler.cpp 23 | address_handler.cpp) 24 | 25 | ADD_EXECUTABLE(anna-route ${ROUTING_SOURCE}) 26 | TARGET_LINK_LIBRARIES(anna-route anna-hash-ring ${KV_LIBRARY_DEPENDENCIES}) 27 | ADD_DEPENDENCIES(anna-route anna-hash-ring zeromq zeromqcpp) 28 | -------------------------------------------------------------------------------- /scripts/travis/travis-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | SCRIPTS=("common/scripts/check-format.sh" "scripts/run-tests.sh") 18 | 19 | ./scripts/build.sh -bDebug -t -j2 20 | EXIT=$? 21 | if [[ $EXIT -ne 0 ]]; then 22 | echo "$SCRIPT failed with exit code $EXIT." 23 | exit $EXIT 24 | fi 25 | 26 | for SCRIPT in "${SCRIPTS[@]}"; do 27 | ./"$SCRIPT" 28 | EXIT=$? 29 | if [[ $EXIT -ne 0 ]]; then 30 | echo "$SCRIPT failed with exit code $EXIT." 31 | exit $EXIT 32 | fi 33 | done 34 | 35 | exit 0 36 | -------------------------------------------------------------------------------- /tests/kvs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | FILE(GLOB HANDLER_SOURCES "${CMAKE_SOURCE_DIR}/src/kvs/*handler.cpp") 18 | 19 | ADD_EXECUTABLE(run_server_handler_tests 20 | run_server_handler_tests.cpp 21 | ${HANDLER_SOURCES} 22 | ${CMAKE_SOURCE_DIR}/src/kvs/utils.cpp) 23 | 24 | TARGET_LINK_LIBRARIES(run_server_handler_tests gtest gmock 25 | anna-hash-ring zmq anna-mock hydro-zmq-mock) 26 | ADD_DEPENDENCIES(run_server_handler_tests gtest) 27 | 28 | ADD_TEST(NAME ServerTests COMMAND run_server_handler_tests) 29 | -------------------------------------------------------------------------------- /tests/route/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(ROUTE_SRC_DIR ${CMAKE_SOURCE_DIR}/src/route) 18 | 19 | FILE(GLOB HANDLER_SOURCES "${ROUTE_SRC_DIR}/*handler.cpp") 20 | 21 | ADD_EXECUTABLE(run_routing_handler_tests 22 | run_routing_handler_tests.cpp 23 | ${HANDLER_SOURCES}) 24 | 25 | TARGET_LINK_LIBRARIES(run_routing_handler_tests gtest gmock 26 | anna-hash-ring zmq anna-mock hydro-zmq-mock) 27 | ADD_DEPENDENCIES(run_routing_handler_tests gtest) 28 | 29 | ADD_TEST(NAME RouteTests COMMAND run_routing_handler_tests) 30 | -------------------------------------------------------------------------------- /src/monitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(MONITORING_SOURCE 18 | monitoring.cpp 19 | membership_handler.cpp 20 | depart_done_handler.cpp 21 | feedback_handler.cpp 22 | stats_helpers.cpp 23 | replication_helpers.cpp 24 | elasticity.cpp 25 | storage_policy.cpp 26 | movement_policy.cpp 27 | slo_policy.cpp) 28 | 29 | ADD_EXECUTABLE(anna-monitor ${MONITORING_SOURCE}) 30 | TARGET_LINK_LIBRARIES(anna-monitor anna-hash-ring ${KV_LIBRARY_DEPENDENCIES} 31 | anna-bench-proto) 32 | ADD_DEPENDENCIES(anna-monitor zeromq zeromqcpp) 33 | -------------------------------------------------------------------------------- /tests/mock/mock_hash_utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "mock_hash_utils.hpp" 16 | 17 | ServerThreadList MockHashRingUtil::get_responsible_threads( 18 | Address respond_address, const Key &key, bool metadata, 19 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 20 | map &key_replication_map, SocketCache &pushers, 21 | const vector &tiers, bool &succeed, unsigned &seed) { 22 | ServerThreadList threads; 23 | succeed = true; 24 | 25 | threads.push_back(ServerThread("127.0.0.1", "127.0.0.1", 0)); 26 | return threads; 27 | } 28 | -------------------------------------------------------------------------------- /scripts/travis/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Only build a new Docker image if this is a master branch build -- ignore this 18 | # for PR builds, because we don't want to update the docker image. 19 | if [[ "$TRAVIS_BRANCH" = "master" ]] && [[ "$TRAVIS_PULL_REQUEST" = "false" ]]; then 20 | docker pull hydroproject/base 21 | docker pull hydroproject/anna 22 | 23 | cd dockerfiles 24 | docker build . -f anna.dockerfile -t hydroproject/anna 25 | 26 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 27 | 28 | docker push hydroproject/anna 29 | fi 30 | -------------------------------------------------------------------------------- /src/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/target/benchmark) 18 | 19 | ADD_EXECUTABLE(anna-bench benchmark.cpp) 20 | TARGET_LINK_LIBRARIES(anna-bench anna-hash-ring ${KV_LIBRARY_DEPENDENCIES} 21 | anna-bench-proto) 22 | ADD_DEPENDENCIES(anna-bench zeromq zeromqcpp) 23 | 24 | ADD_EXECUTABLE(anna-bench-trigger trigger.cpp) 25 | TARGET_LINK_LIBRARIES(anna-bench-trigger anna-hash-ring ${KV_LIBRARY_DEPENDENCIES} 26 | anna-bench-proto) 27 | ADD_DEPENDENCIES(anna-bench-trigger anna-hash-ring zeromq zeromqcpp) 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | language: cpp 16 | sudo: required 17 | 18 | os: 19 | - linux 20 | 21 | dist: trusty 22 | 23 | compiler: 24 | - clang 25 | 26 | services: 27 | - docker 28 | 29 | env: 30 | global: 31 | - PROTOBUF_DIR="$HOME/protobuf" 32 | - PROTOBUF_VERSION=3.9.1 33 | - LCOV_VERSION=1.13 34 | 35 | cache: 36 | directories: 37 | - $PROTOBUF_DIR 38 | 39 | install: 40 | - ./common/scripts/travis/travis-install.sh 41 | 42 | script: 43 | - ./scripts/travis/travis-build.sh 44 | 45 | after_success: 46 | - ./scripts/travis/upload-codecov.sh 47 | - ./scripts/travis/docker-build.sh 48 | -------------------------------------------------------------------------------- /src/kvs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | 17 | SET(KVS_SOURCE 18 | server.cpp 19 | node_join_handler.cpp 20 | node_depart_handler.cpp 21 | self_depart_handler.cpp 22 | user_request_handler.cpp 23 | gossip_handler.cpp 24 | replication_response_handler.cpp 25 | replication_change_handler.cpp 26 | cache_ip_response_handler.cpp 27 | management_node_response_handler.cpp 28 | utils.cpp) 29 | 30 | ADD_EXECUTABLE(anna-kvs ${KVS_SOURCE}) 31 | TARGET_LINK_LIBRARIES(anna-kvs anna-hash-ring ${KV_LIBRARY_DEPENDENCIES}) 32 | ADD_DEPENDENCIES(anna-kvs hydro-zmq zeromq zeromqcpp) 33 | -------------------------------------------------------------------------------- /include/kvs_types.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_TYPES_HPP_ 16 | #define KVS_INCLUDE_TYPES_HPP_ 17 | 18 | #include "kvs_threads.hpp" 19 | #include "types.hpp" 20 | #include 21 | 22 | using StorageStats = map>; 23 | 24 | using OccupancyStats = map>>; 25 | 26 | using AccessStats = map>; 27 | 28 | using TimePoint = std::chrono::time_point; 29 | 30 | using ServerThreadList = vector; 31 | 32 | using ServerThreadSet = std::unordered_set; 33 | 34 | #endif // KVS_INCLUDE_TYPES_HPP_ 35 | -------------------------------------------------------------------------------- /dockerfiles/anna.dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM hydroproject/base:latest 16 | 17 | MAINTAINER Vikram Sreekanti version: 0.1 18 | 19 | ARG repo_org=hydro-project 20 | ARG source_branch=master 21 | ARG build_branch=docker-build 22 | 23 | USER root 24 | 25 | # Check out to the appropriate branch on the appropriate fork of the repository 26 | # and build Anna. 27 | WORKDIR $HYDRO_HOME/anna 28 | RUN git remote remove origin && git remote add origin https://github.com/$repo_org/anna 29 | RUN git fetch origin && git checkout -b $build_branch origin/$source_branch 30 | RUN bash scripts/build.sh -j4 -bRelease 31 | WORKDIR / 32 | 33 | COPY start-anna.sh / 34 | CMD bash start-anna.sh $SERVER_TYPE 35 | -------------------------------------------------------------------------------- /tests/mock/mock_hash_utils.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef TESTS_MOCK_MOCK_HASH_UTILS_HPP_ 16 | #define TESTS_MOCK_MOCK_HASH_UTILS_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | #include "zmq/zmq_util.hpp" 20 | 21 | class MockHashRingUtil : public HashRingUtilInterface { 22 | public: 23 | virtual ~MockHashRingUtil(){}; 24 | 25 | virtual ServerThreadList get_responsible_threads( 26 | Address respond_address, const Key &key, bool metadata, 27 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 28 | map &key_replication_map, SocketCache &pushers, 29 | const vector &tiers, bool &succeed, unsigned &seed); 30 | }; 31 | 32 | #endif // TESTS_MOCK_MOCK_HASH_UTILS_HPP_ 33 | -------------------------------------------------------------------------------- /scripts/start-anna-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | if [ -z "$1" ] && [ -z "$2" ]; then 18 | echo "Usage: ./$0 build start-user" 19 | echo "" 20 | echo "You must run this from the project root directory." 21 | exit 1 22 | fi 23 | 24 | if [ "$1" = "y" ] || [ "$1" = "yes" ]; then 25 | ./scripts/build.sh 26 | fi 27 | 28 | cp conf/anna-local.yml conf/anna-config.yml 29 | 30 | ./build/target/kvs/anna-monitor & 31 | MPID=$! 32 | ./build/target/kvs/anna-route & 33 | RPID=$! 34 | export SERVER_TYPE="memory" 35 | ./build/target/kvs/anna-kvs & 36 | SPID=$! 37 | 38 | echo $MPID > pids 39 | echo $RPID >> pids 40 | echo $SPID >> pids 41 | 42 | if [ "$2" = "y" ] || [ "$2" = "yes" ]; then 43 | ./build/cli/anna-cli conf/anna-local.yml 44 | fi 45 | -------------------------------------------------------------------------------- /tests/route/test_seed_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | TEST_F(RoutingHandlerTest, Seed) { 18 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 19 | 20 | string serialized = seed_handler(log_, global_hash_rings); 21 | 22 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 23 | 24 | ClusterMembership membership; 25 | membership.ParseFromString(serialized); 26 | 27 | EXPECT_EQ(membership.tiers_size(), 1); 28 | for (const auto &tier : membership.tiers()) { 29 | for (const auto &other : tier.servers()) { 30 | EXPECT_EQ(tier.tier_id(), Tier::MEMORY); 31 | EXPECT_EQ(other.private_ip(), ip); 32 | EXPECT_EQ(other.public_ip(), ip); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/route/test_membership_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | TEST_F(RoutingHandlerTest, Membership) { 18 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 19 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 20 | 21 | string message_base = Tier_Name(Tier::MEMORY) + ":127.0.0.2:127.0.0.2:0"; 22 | 23 | string serialized = "join:" + message_base; 24 | membership_handler(log_, serialized, pushers, global_hash_rings, thread_id, 25 | ip); 26 | 27 | vector messages = get_zmq_messages(); 28 | 29 | EXPECT_EQ(messages.size(), 1); 30 | EXPECT_EQ(messages[0], message_base); 31 | 32 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 6000); 33 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 2); 34 | } 35 | -------------------------------------------------------------------------------- /include/kvs/base_kv_store.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef INCLUDE_KVS_BASE_KV_STORE_HPP_ 16 | #define INCLUDE_KVS_BASE_KV_STORE_HPP_ 17 | 18 | #include "anna.pb.h" 19 | #include "lattices/core_lattices.hpp" 20 | 21 | template class KVStore { 22 | protected: 23 | MapLattice db; 24 | 25 | public: 26 | KVStore() {} 27 | 28 | KVStore(MapLattice &other) { db = other; } 29 | 30 | V get(const K &k, AnnaError &error) { 31 | if (!db.contains(k).reveal()) { 32 | error = AnnaError::KEY_DNE; 33 | } 34 | 35 | return db.at(k); 36 | } 37 | 38 | void put(const K &k, const V &v) { return db.at(k).merge(v); } 39 | 40 | unsigned size(const K &k) { return db.at(k).size().reveal(); } 41 | 42 | void remove(const K &k) { db.remove(k); } 43 | }; 44 | 45 | #endif // INCLUDE_KVS_BASE_KV_STORE_HPP_ 46 | -------------------------------------------------------------------------------- /src/route/seed_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | string seed_handler(logger log, GlobalRingMap &global_hash_rings) { 18 | log->info("Received a global hash ring membership request."); 19 | 20 | ClusterMembership membership; 21 | 22 | for (const auto &pair : global_hash_rings) { 23 | Tier tid = pair.first; 24 | GlobalHashRing hash_ring = pair.second; 25 | 26 | ClusterMembership_TierMembership *tier = membership.add_tiers(); 27 | tier->set_tier_id(tid); 28 | 29 | for (const ServerThread &st : hash_ring.get_unique_servers()) { 30 | auto server = tier->add_servers(); 31 | server->set_private_ip(st.private_ip()); 32 | server->set_public_ip(st.public_ip()); 33 | } 34 | } 35 | 36 | string serialized; 37 | membership.SerializeToString(&serialized); 38 | return serialized; 39 | } 40 | -------------------------------------------------------------------------------- /tests/simple/test-simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # Copyright 2019 U.C. Berkeley RISE Lab 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | if [ $# -gt 2 ]; then 19 | echo "Usage: $0 " 20 | echo "If no build option is specified, the test will default to not building." 21 | 22 | exit 1 23 | fi 24 | 25 | if [ -z "$1" ]; then 26 | BUILD="n" 27 | else 28 | BUILD=$1 29 | fi 30 | 31 | echo "Starting local server..." 32 | ./scripts/start-anna-local.sh $BUILD n 33 | 34 | echo "Running tests..." 35 | ./build/cli/anna-cli conf/anna-local.yml tests/simple/input > tmp.out 36 | 37 | DIFF=`diff tmp.out tests/simple/expected` 38 | 39 | if [ "$DIFF" != "" ]; then 40 | echo "Output did not match expected output (tests/simple/expected.out). Observed output was: " 41 | echo $DIFF 42 | CODE=1 43 | else 44 | echo "Test succeeded!" 45 | CODE=0 46 | fi 47 | 48 | rm tmp.out 49 | echo "Stopping local server..." 50 | ./scripts/stop-anna-local.sh y 51 | exit $CODE 52 | -------------------------------------------------------------------------------- /tests/kvs/run_server_handler_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "gmock/gmock.h" 19 | #include "gtest/gtest.h" 20 | 21 | #include "anna.pb.h" 22 | #include "kvs/server_utils.hpp" 23 | #include "metadata.pb.h" 24 | #include "types.hpp" 25 | 26 | #include "server_handler_base.hpp" 27 | #include "test_node_depart_handler.hpp" 28 | #include "test_node_join_handler.hpp" 29 | #include "test_self_depart_handler.hpp" 30 | #include "test_user_request_handler.hpp" 31 | 32 | unsigned kDefaultLocalReplication = 1; 33 | Tier kSelfTier = Tier::MEMORY; 34 | unsigned kThreadNum = 1; 35 | 36 | vector kSelfTierIdVector = {kSelfTier}; 37 | hmap kTierMetadata = {}; 38 | 39 | unsigned kEbsThreadNum = 1; 40 | unsigned kMemoryThreadNum = 1; 41 | unsigned kRoutingThreadNum = 1; 42 | 43 | int main(int argc, char *argv[]) { 44 | log_->set_level(spdlog::level::info); 45 | testing::InitGoogleTest(&argc, argv); 46 | return RUN_ALL_TESTS(); 47 | } 48 | -------------------------------------------------------------------------------- /client/python/anna/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Define port offsets for KVS and routing ports 16 | REQUEST_PULLING_BASE_PORT = 6460 17 | KEY_ADDRESS_BASE_PORT = 6760 18 | 19 | 20 | class Thread(): 21 | def __init__(self, ip, tid): 22 | self.ip = ip 23 | self.tid = tid 24 | 25 | self._base = 'tcp://*:' 26 | self._ip_base = 'tcp://' + self.ip + ':' 27 | 28 | def get_ip(self): 29 | return self.ip 30 | 31 | def get_tid(self): 32 | return self.tid 33 | 34 | 35 | class UserThread(Thread): 36 | def get_request_pull_connect_addr(self): 37 | return self._ip_base + str(self.tid + REQUEST_PULLING_BASE_PORT) 38 | 39 | def get_request_pull_bind_addr(self): 40 | return self._base + str(self.tid + REQUEST_PULLING_BASE_PORT) 41 | 42 | def get_key_address_connect_addr(self): 43 | return self._ip_base + str(self.tid + KEY_ADDRESS_BASE_PORT) 44 | 45 | def get_key_address_bind_addr(self): 46 | return self._base + str(self.tid + KEY_ADDRESS_BASE_PORT) 47 | -------------------------------------------------------------------------------- /client/python/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from distutils.core import setup 16 | import os 17 | from setuptools.command.install import install 18 | 19 | 20 | class InstallWrapper(install): 21 | def run(self): 22 | # compile the relevant protobufs 23 | self.compile_proto() 24 | 25 | # Run the standard PyPi copy 26 | install.run(self) 27 | 28 | # remove the compiled protobufs 29 | self.cleanup() 30 | 31 | def compile_proto(self): 32 | # compile the protobufs 33 | os.system('bash compile.sh') 34 | 35 | def cleanup(self): 36 | os.system('rm anna/anna_pb2.py') 37 | os.system('rm anna/shared_pb2.py') 38 | os.system('rm -rf build') 39 | os.system('rm -rf Anna.egg-info') 40 | 41 | 42 | setup( 43 | name='Anna', 44 | version='0.1', 45 | packages=['anna', ], 46 | license='Apache v2', 47 | long_description='Client for the Anna KVS', 48 | install_requires=['zmq', 'protobuf'], 49 | cmdclass={'install': InstallWrapper} 50 | ) 51 | -------------------------------------------------------------------------------- /include/proto/benchmark.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | // Client-generated feedback used for system monitoring and planning. 18 | message UserFeedback { 19 | // Observed latency measurements for individual keys. 20 | message KeyLatency { 21 | // The key for which latency is being reported. 22 | string key = 1; 23 | 24 | // The observed latency for this key. 25 | double latency = 2; 26 | } 27 | 28 | // A unique ID representing each individual client. 29 | string uid = 1; 30 | 31 | // Perceived latency across all requests made by this client. 32 | double latency = 2; 33 | 34 | // Notifies the monitoring system that the running benchmark has finished. 35 | bool finish = 3; 36 | 37 | // The perceived throughput across all keys. 38 | double throughput = 4; 39 | 40 | // Set during the benchmark warm-up phase to tell the monitoring system that 41 | // it should ignore policy decisions. 42 | bool warmup = 5; 43 | 44 | // Perceived latencies for individual keys. 45 | repeated KeyLatency key_latency = 6; 46 | } 47 | -------------------------------------------------------------------------------- /src/monitor/elasticity.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "monitor/monitoring_utils.hpp" 16 | 17 | void add_node(logger log, string tier, unsigned number, unsigned &adding, 18 | SocketCache &pushers, const Address &management_ip) { 19 | log->info("Adding {} node(s) in tier {}.", std::to_string(number), tier); 20 | 21 | string mgmt_addr = "tcp://" + management_ip + ":7001"; 22 | string message = "add:" + std::to_string(number) + ":" + tier; 23 | 24 | kZmqUtil->send_string(message, &pushers[mgmt_addr]); 25 | adding = number; 26 | } 27 | 28 | void remove_node(logger log, ServerThread &node, string tier, bool &removing, 29 | SocketCache &pushers, 30 | map &departing_node_map, 31 | MonitoringThread &mt) { 32 | auto connection_addr = node.self_depart_connect_address(); 33 | departing_node_map[node.private_ip()] = 34 | kTierMetadata[Tier::MEMORY].thread_number_; 35 | auto ack_addr = mt.depart_done_connect_address(); 36 | 37 | kZmqUtil->send_string(ack_addr, &pushers[connection_addr]); 38 | removing = true; 39 | } 40 | -------------------------------------------------------------------------------- /tests/include/lattices/test_bool_lattice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "lattices/core_lattices.hpp" 21 | #include "gtest/gtest.h" 22 | 23 | class BoolLatticeTest : public ::testing::Test { 24 | protected: 25 | BoolLattice *bl; 26 | BoolLatticeTest() { bl = new BoolLattice; } 27 | virtual ~BoolLatticeTest() { delete bl; } 28 | }; 29 | 30 | const int foo() { return 5; } 31 | 32 | TEST_F(BoolLatticeTest, Assign) { 33 | EXPECT_EQ(false, bl->reveal()); 34 | bl->assign(true); 35 | EXPECT_EQ(true, bl->reveal()); 36 | bl->assign(false); 37 | EXPECT_EQ(false, bl->reveal()); 38 | } 39 | 40 | TEST_F(BoolLatticeTest, MergeByValue) { 41 | EXPECT_EQ(false, bl->reveal()); 42 | bl->merge(true); 43 | EXPECT_EQ(true, bl->reveal()); 44 | bl->merge(false); 45 | EXPECT_EQ(true, bl->reveal()); 46 | } 47 | 48 | TEST_F(BoolLatticeTest, MergeByLattice) { 49 | EXPECT_EQ(false, bl->reveal()); 50 | bl->merge(BoolLattice(true)); 51 | EXPECT_EQ(true, bl->reveal()); 52 | bl->merge(BoolLattice(false)); 53 | EXPECT_EQ(true, bl->reveal()); 54 | } 55 | -------------------------------------------------------------------------------- /include/hashers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_HASHERS_HPP_ 16 | #define KVS_INCLUDE_HASHERS_HPP_ 17 | 18 | #include "kvs_threads.hpp" 19 | #include 20 | 21 | struct GlobalHasher { 22 | uint32_t operator()(const ServerThread &th) { 23 | // prepend a string to make the hash value different than 24 | // what it would be on the naked input 25 | return std::hash{}("GLOBAL" + th.virtual_id()); 26 | } 27 | 28 | uint32_t operator()(const Key &key) { 29 | // prepend a string to make the hash value different than 30 | // what it would be on the naked input 31 | return std::hash{}("GLOBAL" + key); 32 | } 33 | 34 | typedef uint32_t ResultType; 35 | }; 36 | 37 | struct LocalHasher { 38 | typedef std::hash::result_type ResultType; 39 | 40 | ResultType operator()(const ServerThread &th) { 41 | return std::hash{}(std::to_string(th.tid()) + "_" + 42 | std::to_string(th.virtual_num())); 43 | } 44 | 45 | ResultType operator()(const Key &key) { return std::hash{}(key); } 46 | }; 47 | 48 | #endif // KVS_INCLUDE_HASHERS_HPP_ 49 | -------------------------------------------------------------------------------- /client/python/anna/zmq_util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def send_request(req_obj, send_sock): 17 | req_string = req_obj.SerializeToString() 18 | 19 | send_sock.send(req_string) 20 | 21 | 22 | def recv_response(req_ids, rcv_sock, resp_class): 23 | responses = [] 24 | 25 | while len(responses) < len(req_ids): 26 | resp_obj = resp_class() 27 | resp = rcv_sock.recv() 28 | resp_obj.ParseFromString(resp) 29 | 30 | while resp_obj.response_id not in req_ids: 31 | resp_obj.Clear() 32 | resp_obj.ParseFromString(rcv_sock.recv()) 33 | 34 | responses.append(resp_obj) 35 | 36 | return responses 37 | 38 | 39 | class SocketCache(): 40 | def __init__(self, context, zmq_type): 41 | self.context = context 42 | self._cache = {} 43 | self.zmq_type = zmq_type 44 | 45 | def get(self, addr): 46 | if addr not in self._cache: 47 | sock = self.context.socket(self.zmq_type) 48 | sock.connect(addr) 49 | 50 | self._cache[addr] = sock 51 | 52 | return sock 53 | else: 54 | return self._cache[addr] 55 | -------------------------------------------------------------------------------- /tests/route/run_routing_handler_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "gtest/gtest.h" 21 | 22 | #include "anna.pb.h" 23 | #include "metadata.pb.h" 24 | #include "types.hpp" 25 | 26 | #include "routing_handler_base.hpp" 27 | #include "test_address_handler.hpp" 28 | #include "test_membership_handler.hpp" 29 | #include "test_replication_change_handler.hpp" 30 | #include "test_replication_response_handler.hpp" 31 | #include "test_seed_handler.hpp" 32 | 33 | unsigned kDefaultLocalReplication = 1; 34 | unsigned kDefaultGlobalMemoryReplication = 1; 35 | unsigned kDefaultGlobalEbsReplication = 1; 36 | unsigned kThreadNum = 1; 37 | 38 | Tier kSelfTier = Tier::ROUTING; 39 | 40 | vector kSelfTierIdVector = {kSelfTier}; 41 | hmap kTierMetadata = {}; 42 | 43 | unsigned kEbsThreadNum = 1; 44 | unsigned kMemoryThreadNum = 1; 45 | unsigned kRoutingThreadCount = 1; 46 | 47 | int main(int argc, char *argv[]) { 48 | log_->set_level(spdlog::level::off); 49 | testing::InitGoogleTest(&argc, argv); 50 | return RUN_ALL_TESTS(); 51 | } 52 | -------------------------------------------------------------------------------- /tests/route/test_address_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | TEST_F(RoutingHandlerTest, Address) { 18 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 19 | 20 | unsigned seed = 0; 21 | 22 | KeyAddressRequest req; 23 | req.set_request_id("1"); 24 | req.set_response_address("tcp://127.0.0.1:5000"); 25 | req.add_keys("key"); 26 | 27 | string serialized; 28 | req.SerializeToString(&serialized); 29 | 30 | address_handler(log_, serialized, pushers, rt, global_hash_rings, 31 | local_hash_rings, key_replication_map, pending_requests, 32 | seed); 33 | 34 | vector messages = get_zmq_messages(); 35 | 36 | EXPECT_EQ(messages.size(), 1); 37 | string serialized_resp = messages[0]; 38 | 39 | KeyAddressResponse resp; 40 | resp.ParseFromString(serialized_resp); 41 | 42 | EXPECT_EQ(resp.response_id(), "1"); 43 | EXPECT_EQ(resp.error(), 0); 44 | 45 | for (const KeyAddressResponse_KeyAddress &addr : resp.addresses()) { 46 | string key = addr.key(); 47 | EXPECT_EQ(key, "key"); 48 | for (const string &ip : addr.ips()) { 49 | EXPECT_EQ(ip, "tcp://127.0.0.1:6200"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/kvs/test_self_depart_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | TEST_F(ServerHandlerTest, SelfDepart) { 18 | unsigned seed = 0; 19 | vector
routing_ips; 20 | vector
monitoring_ips; 21 | 22 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 23 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 24 | 25 | string serialized = "tcp://127.0.0.2:6560"; 26 | 27 | self_depart_handler(thread_id, seed, ip, ip, log_, serialized, 28 | global_hash_rings, local_hash_rings, stored_key_map, 29 | key_replication_map, routing_ips, monitoring_ips, wt, 30 | pushers, serializers); 31 | 32 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 0); 33 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 0); 34 | 35 | vector zmq_messages = get_zmq_messages(); 36 | EXPECT_EQ(zmq_messages.size(), 1); 37 | EXPECT_EQ(zmq_messages[0], ip + "_" + ip + "_" + Tier_Name(kSelfTier)); 38 | } 39 | 40 | // TODO: test should add keys and make sure that they are gossiped elsewhere 41 | // TODO: test should make sure that depart messages are sent to the worker 42 | // threads 43 | -------------------------------------------------------------------------------- /client/python/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | cd anna 18 | protoc -I=../../../common/proto/ --python_out=. anna.proto shared.proto causal.proto cloudburst.proto 19 | 20 | if [[ "$OSTYPE" = "darwin"* ]]; then 21 | sed -i "" "s/import shared_pb2/from . import shared_pb2/g" anna_pb2.py 22 | sed -i "" "s/import shared_pb2/from . import shared_pb2/g" causal_pb2.py 23 | sed -i "" "s/import anna_pb2/from . import anna_pb2/g" causal_pb2.py 24 | sed -i "" "s/import cloudburst_pb2/from . import cloudburst_pb2/g" causal_pb2.py 25 | sed -i "" "s/import shared_pb2/from . import shared_pb2/g" cloudburst_pb2.py 26 | else # We assume other distributions are Linux. 27 | # NOTE: This is a hack that we need to use because our protobufs are 28 | # not properly packaged, and generally protobufs are supposed to be 29 | # compiled in the same package that they are defined. 30 | sed -i "s|import shared_pb2|from . import shared_pb2|g" anna_pb2.py 31 | sed -i "s|import shared_pb2|from . import shared_pb2|g" causal_pb2.py 32 | sed -i "s|import anna_pb2|from . import anna_pb2|g" causal_pb2.py 33 | sed -i "s|import cloudburst_pb2|from . import cloudburst_pb2|g" causal_pb2.py 34 | sed -i "s|import shared_pb2|from . import shared_pb2|g" cloudburst_pb2.py 35 | fi 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # project specific 16 | build 17 | vendor/gtest/build 18 | *log*.txt 19 | *tmp* 20 | 21 | # ignore compiled byte code 22 | target 23 | 24 | # ignore output files from testing 25 | output* 26 | 27 | # ignore standard Mac OS X files/dirs 28 | .DS_Store 29 | default.profaw 30 | 31 | ################################################################################ 32 | # vim 33 | ################################################################################ 34 | # swap 35 | [._]*.s[a-w][a-z] 36 | [._]s[a-w][a-z] 37 | # session 38 | Session.vim 39 | # temporary 40 | .netrwhist 41 | *~ 42 | # auto-generated tag files 43 | tags 44 | # syntastic 45 | .syntastic_clang_tidy_config 46 | .syntastic_cpp_config 47 | 48 | ################################################################################ 49 | # C++ 50 | ################################################################################ 51 | # Prerequisites 52 | *.d 53 | 54 | # Compiled Object files 55 | *.slo 56 | *.lo 57 | *.o 58 | *.obj 59 | 60 | # Precompiled Headers 61 | *.gch 62 | *.pch 63 | 64 | # Compiled Dynamic libraries 65 | *.so 66 | *.dylib 67 | *.dll 68 | 69 | # Fortran module files 70 | *.mod 71 | *.smod 72 | 73 | # Compiled Static libraries 74 | *.lai 75 | *.la 76 | *.a 77 | *.lib 78 | 79 | # Executables 80 | *.exe 81 | *.out 82 | *.app 83 | -------------------------------------------------------------------------------- /src/kvs/node_depart_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void node_depart_handler(unsigned thread_id, Address public_ip, 18 | Address private_ip, GlobalRingMap &global_hash_rings, 19 | logger log, string &serialized, SocketCache &pushers) { 20 | vector v; 21 | split(serialized, ':', v); 22 | 23 | Tier tier; 24 | Tier_Parse(v[0], &tier); 25 | Address departing_public_ip = v[1]; 26 | Address departing_private_ip = v[2]; 27 | log->info("Received departure for node {}/{} on tier {}.", 28 | departing_public_ip, departing_private_ip, tier); 29 | 30 | // update hash ring 31 | global_hash_rings[tier].remove(departing_public_ip, departing_private_ip, 0); 32 | 33 | if (thread_id == 0) { 34 | // tell all worker threads about the node departure 35 | for (unsigned tid = 1; tid < kThreadNum; tid++) { 36 | kZmqUtil->send_string(serialized, 37 | &pushers[ServerThread(public_ip, private_ip, tid) 38 | .node_depart_connect_address()]); 39 | } 40 | 41 | for (const auto &pair : global_hash_rings) { 42 | log->info("Hash ring for tier {} size is {}.", Tier_Name(pair.first), 43 | pair.second.size()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/building-anna.md: -------------------------------------------------------------------------------- 1 | # Building Anna 2 | 3 | **NOTE**: If you are trying to start an Anna cluster, that does not require you to build Anna on your local machine. You can find instructions for running a Hydro cluster [here](https://github.com/hydro-project/cluster/blob/master/docs/getting-started-aws.md). 4 | 5 | ## Prerequisites 6 | 7 | Anna has the Hydro common directory as a git submodule. After cloning anna, init and update the submodules to install the `commmon` subdirectory: 8 | ```bash 9 | git submodule init; git submodule update 10 | ``` 11 | 12 | In order to build Anna, there are a variety of C++ and other dependencies that are required. Most can be installed with standard package managers like `brew` on macOS and `apt` on Debian. Prepackaged scripts to install dependencies on Fedora, Debian, and macOS can be found in `common/scripts/install-dependencies(-osx).sh`. If you would like to customize the installed packages, everything except for CMake and Protobuf can be installed via standard package managers. Any version of Protobuf 3 should be supported, and we require CMake to be at least version 3.6. 13 | 14 | ## Running the Build Script 15 | 16 | Anna can be built with Clang (version 5 or newer) or gcc (version 7 or newer). `scripts/build.sh` automatically configures and runs the standard CMake build for you, with 3 clangs. 17 | 18 | * `-b` specifies the build type, either `Release` or `Debug`. 19 | * `-j` specifies the parallelism to be used by `make`. The default value is `-j1`. 20 | * `-t` enables testing; note that testing requires the build to be run in `Debug` mode. 21 | * `-g` builds the project using `g++` instead of `clang++`. 22 | 23 | By default, the script will run as `bash scripts/build.sh -bRelease -j1`. 24 | 25 | This will generate a variety of executables, primarily in `build/target`, which houses all of the KVS server executables, and in `build/client`, which has the CPP-based interactive CLI for Anna. Once Anna is built, you can run it in [local mode](local-mode.md). 26 | -------------------------------------------------------------------------------- /src/route/replication_change_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | void replication_change_handler(logger log, string &serialized, 18 | SocketCache &pushers, 19 | map &key_replication_map, 20 | unsigned thread_id, Address ip) { 21 | if (thread_id == 0) { 22 | // tell all worker threads about the replication factor change 23 | for (unsigned tid = 1; tid < kRoutingThreadCount; tid++) { 24 | kZmqUtil->send_string( 25 | serialized, &pushers[RoutingThread(ip, tid) 26 | .replication_change_connect_address()]); 27 | } 28 | } 29 | 30 | ReplicationFactorUpdate update; 31 | update.ParseFromString(serialized); 32 | 33 | for (const auto &key_rep : update.updates()) { 34 | Key key = key_rep.key(); 35 | log->info("Received a replication factor change for key {}.", key); 36 | 37 | for (const ReplicationFactor_ReplicationValue &global : key_rep.global()) { 38 | key_replication_map[key].global_replication_[global.tier()] = 39 | global.value(); 40 | } 41 | 42 | for (const ReplicationFactor_ReplicationValue &local : key_rep.local()) { 43 | key_replication_map[key].local_replication_[local.tier()] = local.value(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_all.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "gtest/gtest.h" 19 | 20 | #include "kvs.pb.h" 21 | #include "misc.pb.h" 22 | #include "replication.pb.h" 23 | #include "types.hpp" 24 | #include "utils/server_utils.hpp" 25 | 26 | #include "kvs/server_handler_base.hpp" 27 | #include "kvs/test_gossip_handler.hpp" 28 | #include "kvs/test_node_depart_handler.hpp" 29 | #include "kvs/test_node_join_handler.hpp" 30 | #include "kvs/test_rep_factor_change_handler.hpp" 31 | #include "kvs/test_rep_factor_response_handler.hpp" 32 | #include "kvs/test_self_depart_handler.hpp" 33 | #include "kvs/test_user_request_handler.hpp" 34 | 35 | #include "include/lattices/test_bool_lattice.hpp" 36 | #include "include/lattices/test_map_lattice.hpp" 37 | #include "include/lattices/test_max_lattice.hpp" 38 | #include "include/lattices/test_set_lattice.hpp" 39 | 40 | unsigned kDefaultLocalReplication = 1; 41 | unsigned kSelfTierId = kMemoryTierId; 42 | unsigned kThreadNum = 1; 43 | vector kSelfTierIdVector = {kSelfTierId}; 44 | map kTierMetadata = {}; 45 | 46 | unsigned kEbsThreadCount = 1; 47 | unsigned kMemoryThreadCount = 1; 48 | unsigned kRoutingThreadCount = 1; 49 | 50 | int main(int argc, char *argv[]) { 51 | log->set_level(spdlog::level::info); 52 | testing::InitGoogleTest(&argc, argv); 53 | return RUN_ALL_TESTS(); 54 | } 55 | -------------------------------------------------------------------------------- /include/monitor/monitoring_handlers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_MONITOR_MONITORING_HANDLERS_HPP_ 16 | #define KVS_INCLUDE_MONITOR_MONITORING_HANDLERS_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | #include "metadata.pb.h" 20 | 21 | void membership_handler(logger log, string &serialized, 22 | GlobalRingMap &global_hash_rings, 23 | unsigned &new_memory_count, unsigned &new_ebs_count, 24 | TimePoint &grace_start, vector
&routing_ips, 25 | StorageStats &memory_storage, StorageStats &ebs_storage, 26 | OccupancyStats &memory_occupancy, 27 | OccupancyStats &ebs_occupancy, 28 | map> &key_access_frequency); 29 | 30 | void depart_done_handler(logger log, string &serialized, 31 | map &departing_node_map, 32 | Address management_ip, bool &removing_memory_node, 33 | bool &removing_ebs_node, SocketCache &pushers, 34 | TimePoint &grace_start); 35 | 36 | void feedback_handler( 37 | string &serialized, map &user_latency, 38 | map &user_throughput, 39 | map> &latency_miss_ratio_map); 40 | 41 | #endif // KVS_INCLUDE_MONITOR_MONITORING_HANDLERS_HPP_ 42 | -------------------------------------------------------------------------------- /src/monitor/feedback_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "benchmark.pb.h" 16 | #include "monitor/monitoring_handlers.hpp" 17 | 18 | void feedback_handler( 19 | string &serialized, map &user_latency, 20 | map &user_throughput, 21 | map> &latency_miss_ratio_map) { 22 | UserFeedback fb; 23 | fb.ParseFromString(serialized); 24 | 25 | if (fb.finish()) { 26 | user_latency.erase(fb.uid()); 27 | } else { 28 | // collect latency and throughput feedback 29 | user_latency[fb.uid()] = fb.latency(); 30 | user_throughput[fb.uid()] = fb.throughput(); 31 | 32 | // collect replication factor adjustment factors 33 | for (const auto &key_latency_pair : fb.key_latency()) { 34 | Key key = key_latency_pair.key(); 35 | double observed_key_latency = key_latency_pair.latency(); 36 | 37 | if (latency_miss_ratio_map.find(key) == latency_miss_ratio_map.end()) { 38 | latency_miss_ratio_map[key].first = observed_key_latency / kSloWorst; 39 | latency_miss_ratio_map[key].second = 1; 40 | } else { 41 | latency_miss_ratio_map[key].first = 42 | (latency_miss_ratio_map[key].first * 43 | latency_miss_ratio_map[key].second + 44 | observed_key_latency / kSloWorst) / 45 | (latency_miss_ratio_map[key].second + 1); 46 | latency_miss_ratio_map[key].second += 1; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/kvs/test_node_depart_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | TEST_F(ServerHandlerTest, SimpleNodeDepart) { 18 | kThreadNum = 2; 19 | global_hash_rings[Tier::MEMORY].insert("127.0.0.2", "127.0.0.2", 0, 0); 20 | 21 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 6000); 22 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 2); 23 | 24 | string serialized = Tier_Name(Tier::MEMORY) + ":127.0.0.2:127.0.0.2"; 25 | node_depart_handler(thread_id, ip, ip, global_hash_rings, log_, serialized, 26 | pushers); 27 | 28 | vector messages = get_zmq_messages(); 29 | 30 | EXPECT_EQ(messages.size(), 1); 31 | EXPECT_EQ(messages[0], serialized); 32 | 33 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 34 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 35 | } 36 | 37 | TEST_F(ServerHandlerTest, FakeNodeDepart) { 38 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 39 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 40 | 41 | string serialized = std::to_string(Tier::MEMORY) + ":127.0.0.2:127.0.0.2"; 42 | node_depart_handler(thread_id, ip, ip, global_hash_rings, log_, serialized, 43 | pushers); 44 | 45 | vector messages = get_zmq_messages(); 46 | 47 | EXPECT_EQ(messages.size(), 0); 48 | 49 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 50 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 51 | } 52 | -------------------------------------------------------------------------------- /include/route/routing_handlers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef INCLUDE_ROUTE_ROUTING_HANDLERS_HPP_ 16 | #define INCLUDE_ROUTE_ROUTING_HANDLERS_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | #include "metadata.pb.h" 20 | 21 | string seed_handler(logger log, GlobalRingMap &global_hash_rings); 22 | 23 | void membership_handler(logger log, string &serialized, SocketCache &pushers, 24 | GlobalRingMap &global_hash_rings, unsigned thread_id, 25 | Address ip); 26 | 27 | void replication_response_handler( 28 | logger log, string &serialized, SocketCache &pushers, RoutingThread &rt, 29 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 30 | map &key_replication_map, 31 | map>> &pending_requests, unsigned &seed); 32 | 33 | void replication_change_handler(logger log, string &serialized, 34 | SocketCache &pushers, 35 | map &key_replication_map, 36 | unsigned thread_id, Address ip); 37 | 38 | void address_handler(logger log, string &serialized, SocketCache &pushers, 39 | RoutingThread &rt, GlobalRingMap &global_hash_rings, 40 | LocalRingMap &local_hash_rings, 41 | map &key_replication_map, 42 | map>> &pending_requests, 43 | unsigned &seed); 44 | 45 | #endif // INCLUDE_ROUTE_ROUTING_HANDLERS_HPP_ 46 | -------------------------------------------------------------------------------- /tests/include/lattices/test_max_lattice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "lattices/core_lattices.hpp" 21 | #include "gtest/gtest.h" 22 | 23 | template class MaxLatticeTest : public ::testing::Test { 24 | protected: 25 | MaxLattice *ml; 26 | MaxLatticeTest() { ml = new MaxLattice; } 27 | virtual ~MaxLatticeTest() { delete ml; } 28 | }; 29 | 30 | typedef ::testing::Types MaxTypes; 31 | TYPED_TEST_CASE(MaxLatticeTest, MaxTypes); 32 | 33 | TYPED_TEST(MaxLatticeTest, Assign) { 34 | EXPECT_EQ(0, this->ml->reveal()); 35 | this->ml->assign(10); 36 | EXPECT_EQ(10, this->ml->reveal()); 37 | this->ml->assign(5); 38 | EXPECT_EQ(5, this->ml->reveal()); 39 | } 40 | 41 | TYPED_TEST(MaxLatticeTest, MergeByValue) { 42 | EXPECT_EQ(0, this->ml->reveal()); 43 | this->ml->merge(10); 44 | EXPECT_EQ(10, this->ml->reveal()); 45 | this->ml->merge(5); 46 | EXPECT_EQ(10, this->ml->reveal()); 47 | } 48 | 49 | TYPED_TEST(MaxLatticeTest, MergeByLattice) { 50 | EXPECT_EQ(0, this->ml->reveal()); 51 | this->ml->merge(MaxLattice(10)); 52 | EXPECT_EQ(10, this->ml->reveal()); 53 | this->ml->merge(MaxLattice(5)); 54 | EXPECT_EQ(10, this->ml->reveal()); 55 | } 56 | 57 | TYPED_TEST(MaxLatticeTest, Add) { 58 | MaxLattice res = this->ml->add(5); 59 | EXPECT_EQ(5, res.reveal()); 60 | } 61 | 62 | TYPED_TEST(MaxLatticeTest, Subtract) { 63 | MaxLattice res = this->ml->subtract(5); 64 | EXPECT_EQ(-5, res.reveal()); 65 | } 66 | -------------------------------------------------------------------------------- /tests/kvs/test_gossip_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | TEST_F(ServerHandlerTest, SimpleGossipReceive) { 18 | Key key = "key"; 19 | string value = "value"; 20 | string put_request = put_key_request(key, value, ip); 21 | 22 | unsigned access_count = 0; 23 | unsigned seed = 0; 24 | unsigned error; 25 | auto now = std::chrono::system_clock::now(); 26 | 27 | EXPECT_EQ(local_changeset.size(), 0); 28 | 29 | gossip_handler(seed, put_request, global_hash_rings, local_hash_rings, 30 | key_size_map, pending_gossip, metadata_map, wt, serializer, 31 | pushers); 32 | 33 | EXPECT_EQ(pending_gossip.size(), 0); 34 | EXPECT_EQ(serializer->get(key, error).reveal().value, value); 35 | } 36 | 37 | TEST_F(ServerHandlerTest, GossipUpdate) { 38 | Key key = "key"; 39 | string value = "value1"; 40 | serializer->put(key, value, (unsigned)0); 41 | 42 | value = "value2"; 43 | 44 | string put_request = put_key_request(key, value, ip); 45 | 46 | unsigned access_count = 0; 47 | unsigned seed = 0; 48 | unsigned error; 49 | auto now = std::chrono::system_clock::now(); 50 | 51 | EXPECT_EQ(local_changeset.size(), 0); 52 | 53 | gossip_handler(seed, put_request, global_hash_rings, local_hash_rings, 54 | key_size_map, pending_gossip, metadata_map, wt, serializer, 55 | pushers); 56 | 57 | EXPECT_EQ(pending_gossip.size(), 0); 58 | EXPECT_EQ(serializer->get(key, error).reveal().value, value); 59 | } 60 | 61 | // TODO: test pending gossip 62 | // TODO: test gossip forwarding 63 | -------------------------------------------------------------------------------- /tests/include/lattices/test_set_lattice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "lattices/core_lattices.hpp" 21 | #include "gtest/gtest.h" 22 | 23 | class SetLatticeTest : public ::testing::Test { 24 | protected: 25 | SetLattice *sl; 26 | set set1 = {'a', 'b', 'c'}; 27 | set set2 = {'c', 'd', 'e'}; 28 | set set3 = {'a', 'd', 'e', 'b', 'c'}; 29 | SetLatticeTest() { sl = new SetLattice; } 30 | virtual ~SetLatticeTest() { delete sl; } 31 | }; 32 | 33 | const int flow_test_set() { return 5; } 34 | 35 | TEST_F(SetLatticeTest, Assign) { 36 | EXPECT_EQ(0, sl->size().reveal()); 37 | sl->assign(set1); 38 | EXPECT_EQ(3, sl->size().reveal()); 39 | EXPECT_EQ(set1, sl->reveal()); 40 | } 41 | 42 | TEST_F(SetLatticeTest, MergeByValue) { 43 | EXPECT_EQ(0, sl->size().reveal()); 44 | sl->merge(set1); 45 | EXPECT_EQ(3, sl->size().reveal()); 46 | EXPECT_EQ(set1, sl->reveal()); 47 | sl->merge(set2); 48 | EXPECT_EQ(5, sl->size().reveal()); 49 | EXPECT_EQ(set3, sl->reveal()); 50 | } 51 | 52 | TEST_F(SetLatticeTest, MergeByLattice) { 53 | EXPECT_EQ(0, sl->size().reveal()); 54 | sl->merge(SetLattice(set1)); 55 | EXPECT_EQ(3, sl->size().reveal()); 56 | EXPECT_EQ(set1, sl->reveal()); 57 | sl->merge(SetLattice(set2)); 58 | EXPECT_EQ(5, sl->size().reveal()); 59 | EXPECT_EQ(set3, sl->reveal()); 60 | } 61 | 62 | TEST_F(SetLatticeTest, Intersection) { 63 | sl->merge(set1); 64 | SetLattice res = sl->intersect(set2); 65 | EXPECT_EQ(set({'c'}), res.reveal()); 66 | } 67 | -------------------------------------------------------------------------------- /tests/route/test_replication_change_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | TEST_F(RoutingHandlerTest, ReplicationChange) { 18 | kRoutingThreadCount = 3; 19 | vector keys = {"key0, key1, key2"}; 20 | warmup_key_replication_map_to_defaults(keys); 21 | 22 | ReplicationFactorUpdate update; 23 | for (string key : keys) { 24 | ReplicationFactor *rf = update.add_updates(); 25 | rf->set_key(key); 26 | 27 | for (const Tier &tier : kAllTiers) { 28 | ReplicationFactor_ReplicationValue *rep_global = rf->add_global(); 29 | rep_global->set_tier(tier); 30 | rep_global->set_value(2); 31 | 32 | ReplicationFactor_ReplicationValue *rep_local = rf->add_local(); 33 | rep_local->set_tier(tier); 34 | rep_local->set_value(3); 35 | } 36 | } 37 | 38 | string serialized; 39 | update.SerializeToString(&serialized); 40 | 41 | replication_change_handler(log_, serialized, pushers, key_replication_map, 42 | thread_id, ip); 43 | 44 | vector messages = get_zmq_messages(); 45 | 46 | EXPECT_EQ(messages.size(), 2); 47 | for (unsigned i = 0; i < messages.size(); i++) { 48 | EXPECT_EQ(messages[i], serialized); 49 | } 50 | 51 | for (string key : keys) { 52 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::MEMORY], 2); 53 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::DISK], 2); 54 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::MEMORY], 3); 55 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::DISK], 3); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2019 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | args=( -j -b -t ) 18 | containsElement() { 19 | local e match="$1" 20 | shift 21 | for e; do [[ "$e" == "$match" ]] && return 0; done 22 | return 1 23 | } 24 | 25 | while getopts ":j:b:tg" opt; do 26 | case $opt in 27 | j ) 28 | MAKE_THREADS=$OPTARG 29 | if containsElement $OPTARG "${args[@]}" 30 | then 31 | echo "Missing argument to flag $opt" 32 | exit 1 33 | else 34 | echo "make set to run on $OPTARG threads" >&2 35 | fi 36 | ;; 37 | b ) 38 | TYPE=$OPTARG 39 | if containsElement $OPTARG "${args[@]}" 40 | then 41 | echo "Missing argument to flag $opt" 42 | exit 1 43 | else 44 | echo "build type set to $OPTARG" >&2 45 | fi 46 | ;; 47 | t ) 48 | TEST="-DBUILD_TEST=ON" 49 | echo "Testing enabled..." 50 | ;; 51 | g ) 52 | COMPILER="/usr/bin/g++" 53 | RUN_FORMAT="" 54 | echo "Compiler set to GNU g++..." 55 | ;; 56 | \? ) 57 | echo "Invalid option: -$OPTARG" >&2 58 | exit 1 59 | ;; 60 | esac 61 | done 62 | 63 | if [[ -z "$MAKE_THREADS" ]]; then MAKE_THREADS=2; fi 64 | if [[ -z "$TYPE" ]]; then TYPE=Release; fi 65 | if [[ -z "$TEST" ]]; then TEST=""; fi 66 | if [[ -z "$COMPILER" ]]; then 67 | COMPILER="/usr/bin/clang++" 68 | RUN_FORMAT="yes" 69 | fi 70 | 71 | rm -rf build 72 | mkdir build 73 | cd build 74 | 75 | cmake -std=c++11 "-GUnix Makefiles" -DCMAKE_BUILD_TYPE=$TYPE -DCMAKE_CXX_COMPILER=$COMPILER $TEST .. 76 | 77 | make -j${MAKE_THREADS} 78 | 79 | if [[ "$TYPE" = "Debug" ]] && [[ ! -z "$RUN_FORMAT" ]]; then 80 | make clang-format 81 | fi 82 | -------------------------------------------------------------------------------- /src/benchmark/trigger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "common.hpp" 18 | #include "hash_ring.hpp" 19 | #include "kvs_common.hpp" 20 | #include "threads.hpp" 21 | #include "yaml-cpp/yaml.h" 22 | 23 | // TODO(vikram): We probably don't want to have to define all of these here? 24 | ZmqUtil zmq_util; 25 | ZmqUtilInterface *kZmqUtil = &zmq_util; 26 | 27 | HashRingUtil hash_ring_util; 28 | HashRingUtilInterface *kHashRingUtil = &hash_ring_util; 29 | 30 | unsigned kBenchmarkThreadNum = 1; 31 | unsigned kRoutingThreadCount = 1; 32 | unsigned kDefaultLocalReplication = 1; 33 | 34 | int main(int argc, char *argv[]) { 35 | if (argc != 2) { 36 | std::cerr << "Usage: " << argv[0] << " " << std::endl; 37 | return 1; 38 | } 39 | 40 | unsigned thread_num = atoi(argv[1]); 41 | 42 | // read the YAML conf 43 | vector
benchmark_address; 44 | YAML::Node conf = YAML::LoadFile("conf/anna-config.yml"); 45 | YAML::Node benchmark = conf["benchmark"]; 46 | 47 | for (const YAML::Node &node : benchmark) { 48 | benchmark_address.push_back(node.as
()); 49 | } 50 | 51 | zmq::context_t context(1); 52 | SocketCache pushers(&context, ZMQ_PUSH); 53 | 54 | string command; 55 | while (true) { 56 | std::cout << "command> "; 57 | getline(std::cin, command); 58 | 59 | for (const Address address : benchmark_address) { 60 | for (unsigned tid = 0; tid < thread_num; tid++) { 61 | BenchmarkThread bt = BenchmarkThread(address, tid); 62 | 63 | kZmqUtil->send_string(command, 64 | &pushers[bt.benchmark_command_address()]); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/monitor/depart_done_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "monitor/monitoring_handlers.hpp" 16 | 17 | void depart_done_handler(logger log, string &serialized, 18 | map &departing_node_map, 19 | Address management_ip, bool &removing_memory_node, 20 | bool &removing_ebs_node, SocketCache &pushers, 21 | TimePoint &grace_start) { 22 | vector tokens; 23 | split(serialized, '_', tokens); 24 | 25 | Address departed_public_ip = tokens[0]; 26 | Address departed_private_ip = tokens[1]; 27 | unsigned tier_id = stoi(tokens[2]); 28 | 29 | if (departing_node_map.find(departed_private_ip) != 30 | departing_node_map.end()) { 31 | departing_node_map[departed_private_ip] -= 1; 32 | 33 | if (departing_node_map[departed_private_ip] == 0) { 34 | string ntype; 35 | if (tier_id == 1) { 36 | ntype = "memory"; 37 | removing_memory_node = false; 38 | } else { 39 | ntype = "ebs"; 40 | removing_ebs_node = false; 41 | } 42 | 43 | log->info("Removing {} node {}/{}.", ntype, departed_public_ip, 44 | departed_private_ip); 45 | 46 | string mgmt_addr = "tcp://" + management_ip + ":7001"; 47 | string message = "remove:" + departed_private_ip + ":" + ntype; 48 | 49 | kZmqUtil->send_string(message, &pushers[mgmt_addr]); 50 | 51 | // reset grace period timer 52 | grace_start = std::chrono::system_clock::now(); 53 | departing_node_map.erase(departed_private_ip); 54 | } 55 | } else { 56 | log->error("Missing entry in the depart done map."); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /include/kvs_common.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_KVS_COMMON_HPP_ 16 | #define KVS_INCLUDE_KVS_COMMON_HPP_ 17 | 18 | #include "kvs_types.hpp" 19 | #include "metadata.pb.h" 20 | 21 | const unsigned kMetadataReplicationFactor = 1; 22 | const unsigned kMetadataLocalReplicationFactor = 1; 23 | 24 | const unsigned kVirtualThreadNum = 3000; 25 | 26 | const vector kAllTiers = { 27 | Tier::MEMORY, 28 | Tier::DISK}; // TODO(vikram): Is there a better way to make this vector? 29 | 30 | const unsigned kSloWorst = 3000; 31 | 32 | // run-time constants 33 | extern Tier kSelfTier; 34 | extern vector kSelfTierIdVector; 35 | 36 | extern unsigned kMemoryNodeCapacity; 37 | extern unsigned kEbsNodeCapacity; 38 | 39 | // the number of threads running in this executable 40 | extern unsigned kThreadNum; 41 | extern unsigned kMemoryThreadCount; 42 | extern unsigned kEbsThreadCount; 43 | extern unsigned kRoutingThreadCount; 44 | 45 | extern unsigned kDefaultGlobalMemoryReplication; 46 | extern unsigned kDefaultGlobalEbsReplication; 47 | extern unsigned kDefaultLocalReplication; 48 | extern unsigned kMinimumReplicaNumber; 49 | 50 | inline void prepare_get_tuple(KeyRequest &req, Key key, 51 | LatticeType lattice_type) { 52 | KeyTuple *tp = req.add_tuples(); 53 | tp->set_key(std::move(key)); 54 | tp->set_lattice_type(std::move(lattice_type)); 55 | } 56 | 57 | inline void prepare_put_tuple(KeyRequest &req, Key key, 58 | LatticeType lattice_type, string payload) { 59 | KeyTuple *tp = req.add_tuples(); 60 | tp->set_key(std::move(key)); 61 | tp->set_lattice_type(std::move(lattice_type)); 62 | tp->set_payload(std::move(payload)); 63 | } 64 | 65 | #endif // KVS_INCLUDE_KVS_COMMON_HPP_ 66 | -------------------------------------------------------------------------------- /include/consistent_hash_map.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_CONSISTENT_HASH_MAP_HPP_ 16 | #define KVS_INCLUDE_CONSISTENT_HASH_MAP_HPP_ 17 | 18 | #include 19 | #include 20 | 21 | template >> 24 | class ConsistentHashMap { 25 | public: 26 | typedef typename Hash::ResultType size_type; 27 | typedef std::map, Alloc> map_type; 28 | typedef typename map_type::value_type value_type; 29 | typedef value_type &reference; 30 | typedef const value_type &const_reference; 31 | typedef typename map_type::iterator iterator; 32 | typedef Alloc allocator_type; 33 | 34 | public: 35 | ConsistentHashMap() {} 36 | 37 | ~ConsistentHashMap() {} 38 | 39 | public: 40 | std::size_t size() const { return nodes_.size(); } 41 | 42 | bool empty() const { return nodes_.empty(); } 43 | 44 | std::pair insert(const T &node) { 45 | size_type hash = hasher_(node); 46 | return nodes_.insert(value_type(hash, node)); 47 | } 48 | 49 | void erase(iterator it) { nodes_.erase(it); } 50 | 51 | std::size_t erase(const T &node) { 52 | size_type hash = hasher_(node); 53 | return nodes_.erase(hash); 54 | } 55 | 56 | iterator find(size_type hash) { 57 | if (nodes_.empty()) { 58 | return nodes_.end(); 59 | } 60 | 61 | iterator it = nodes_.lower_bound(hash); 62 | 63 | if (it == nodes_.end()) { 64 | it = nodes_.begin(); 65 | } 66 | 67 | return it; 68 | } 69 | 70 | iterator find(Key key) { return find(hasher_(key)); } 71 | 72 | iterator begin() { return nodes_.begin(); } 73 | 74 | iterator end() { return nodes_.end(); } 75 | 76 | private: 77 | Hash hasher_; 78 | map_type nodes_; 79 | }; 80 | 81 | #endif // KVS_INCLUDE_CONSISTENT_HASH_MAP_HPP_ 82 | -------------------------------------------------------------------------------- /tests/kvs/test_node_join_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | TEST_F(ServerHandlerTest, BasicNodeJoin) { 18 | unsigned seed = 0; 19 | kThreadNum = 2; 20 | set join_remove_set; 21 | AddressKeysetMap join_gossip_map; 22 | 23 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 24 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 25 | 26 | string serialized = Tier_Name(Tier::MEMORY) + ":127.0.0.2:127.0.0.2:0"; 27 | node_join_handler(thread_id, seed, ip, ip, log_, serialized, 28 | global_hash_rings, local_hash_rings, stored_key_map, 29 | key_replication_map, join_remove_set, pushers, wt, 30 | join_gossip_map, 0); 31 | 32 | vector messages = get_zmq_messages(); 33 | EXPECT_EQ(messages.size(), 2); 34 | EXPECT_EQ(messages[0], Tier_Name(kSelfTier) + ":" + ip + ":" + ip + ":0"); 35 | EXPECT_EQ(messages[1], serialized); 36 | 37 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 6000); 38 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 2); 39 | } 40 | 41 | TEST_F(ServerHandlerTest, DuplicateNodeJoin) { 42 | unsigned seed = 0; 43 | set join_remove_set; 44 | AddressKeysetMap join_gossip_map; 45 | 46 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 47 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 48 | 49 | string serialized = Tier_Name(Tier::MEMORY) + ":" + ip + ":" + ip + ":0"; 50 | node_join_handler(thread_id, seed, ip, ip, log_, serialized, 51 | global_hash_rings, local_hash_rings, stored_key_map, 52 | key_replication_map, join_remove_set, pushers, wt, 53 | join_gossip_map, 0); 54 | 55 | vector messages = get_zmq_messages(); 56 | EXPECT_EQ(messages.size(), 0); 57 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].size(), 3000); 58 | EXPECT_EQ(global_hash_rings[Tier::MEMORY].get_unique_servers().size(), 1); 59 | } 60 | -------------------------------------------------------------------------------- /tests/route/test_replication_response_handler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | TEST_F(RoutingHandlerTest, ReplicationResponse) { 18 | unsigned seed = 0; 19 | string key = "key"; 20 | vector keys = {"key"}; 21 | warmup_key_replication_map_to_defaults(keys); 22 | 23 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::MEMORY], 1); 24 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::DISK], 1); 25 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::MEMORY], 1); 26 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::DISK], 1); 27 | 28 | KeyResponse response; 29 | response.set_type(RequestType::PUT); 30 | KeyTuple *tp = response.add_tuples(); 31 | tp->set_key(get_metadata_key(key, MetadataType::replication)); 32 | tp->set_lattice_type(LatticeType::LWW); 33 | 34 | string metakey = key; 35 | ReplicationFactor rf; 36 | rf.set_key(key); 37 | 38 | for (const Tier &tier : kAllTiers) { 39 | ReplicationFactor_ReplicationValue *rep_global = rf.add_global(); 40 | rep_global->set_tier(tier); 41 | rep_global->set_value(2); 42 | 43 | ReplicationFactor_ReplicationValue *rep_local = rf.add_local(); 44 | rep_local->set_tier(tier); 45 | rep_local->set_value(3); 46 | } 47 | 48 | string repfactor; 49 | rf.SerializeToString(&repfactor); 50 | 51 | tp->set_payload(serialize(0, repfactor)); 52 | 53 | string serialized; 54 | response.SerializeToString(&serialized); 55 | 56 | replication_response_handler(log_, serialized, pushers, rt, global_hash_rings, 57 | local_hash_rings, key_replication_map, 58 | pending_requests, seed); 59 | 60 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::MEMORY], 2); 61 | EXPECT_EQ(key_replication_map[key].global_replication_[Tier::DISK], 2); 62 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::MEMORY], 3); 63 | EXPECT_EQ(key_replication_map[key].local_replication_[Tier::DISK], 3); 64 | } 65 | -------------------------------------------------------------------------------- /tests/include/lattices/test_ordered_set_lattice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "lattices/core_lattices.hpp" 21 | #include "gtest/gtest.h" 22 | 23 | class OrderedSetLatticeTest : public ::testing::Test { 24 | protected: 25 | OrderedSetLattice *sl; 26 | // Note that the order in these initializer lists is not preserved; 27 | // ordered_set will sort the items itself. 28 | ordered_set set1{'a', 'b', 'c'}; 29 | ordered_set set2{'c', 'd', 'e'}; 30 | ordered_set set3{'a', 'b', 'c', 'd', 'e'}; 31 | OrderedSetLatticeTest() { sl = new OrderedSetLattice; } 32 | virtual ~OrderedSetLatticeTest() { delete sl; } 33 | }; 34 | 35 | const int flow_test_ordered_set() { return 5; } 36 | 37 | TEST_F(OrderedSetLatticeTest, Assign) { 38 | EXPECT_EQ(0, sl->size().reveal()); 39 | sl->assign(set1); 40 | EXPECT_EQ(3, sl->size().reveal()); 41 | EXPECT_EQ(set1, sl->reveal()); 42 | } 43 | 44 | TEST_F(OrderedSetLatticeTest, MergeByValue) { 45 | EXPECT_EQ(0, sl->size().reveal()); 46 | sl->merge(set1); 47 | EXPECT_EQ(3, sl->size().reveal()); 48 | EXPECT_EQ(set1, sl->reveal()); 49 | sl->merge(set2); 50 | EXPECT_EQ(5, sl->size().reveal()); 51 | EXPECT_EQ(set3, sl->reveal()); 52 | } 53 | 54 | TEST_F(OrderedSetLatticeTest, MergeByLattice) { 55 | EXPECT_EQ(0, sl->size().reveal()); 56 | sl->merge(OrderedSetLattice(set1)); 57 | EXPECT_EQ(3, sl->size().reveal()); 58 | EXPECT_EQ(set1, sl->reveal()); 59 | sl->merge(OrderedSetLattice(set2)); 60 | EXPECT_EQ(5, sl->size().reveal()); 61 | EXPECT_EQ(set3, sl->reveal()); 62 | } 63 | 64 | TEST_F(OrderedSetLatticeTest, Intersection) { 65 | sl->merge(set1); 66 | OrderedSetLattice res = sl->intersect(set2); 67 | EXPECT_EQ(ordered_set({'c'}), res.reveal()); 68 | } 69 | 70 | TEST_F(OrderedSetLatticeTest, Ordering) { 71 | sl->merge(set2); 72 | EXPECT_EQ('c', *(sl->reveal().cbegin())); 73 | sl->merge(set1); 74 | EXPECT_EQ('a', *(sl->reveal().cbegin())); 75 | } 76 | -------------------------------------------------------------------------------- /tests/route/routing_handler_base.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "mock/mock_hash_utils.hpp" 16 | #include "mock_zmq_utils.hpp" 17 | 18 | MockZmqUtil mock_zmq_util; 19 | ZmqUtilInterface *kZmqUtil = &mock_zmq_util; 20 | 21 | MockHashRingUtil mock_hash_ring_util; 22 | HashRingUtilInterface *kHashRingUtil = &mock_hash_ring_util; 23 | 24 | std::shared_ptr log_ = 25 | spdlog::basic_logger_mt("mock_log", "mock_log.txt", true); 26 | 27 | class RoutingHandlerTest : public ::testing::Test { 28 | protected: 29 | Address ip = "127.0.0.1"; 30 | unsigned thread_id = 0; 31 | GlobalRingMap global_hash_rings; 32 | LocalRingMap local_hash_rings; 33 | map key_replication_map; 34 | map>> pending_requests; 35 | zmq::context_t context; 36 | SocketCache pushers = SocketCache(&context, ZMQ_PUSH); 37 | RoutingThread rt; 38 | 39 | RoutingHandlerTest() { 40 | rt = RoutingThread(ip, thread_id); 41 | global_hash_rings[Tier::MEMORY].insert(ip, ip, 0, thread_id); 42 | } 43 | 44 | public: 45 | void SetUp() { 46 | // reset all global variables 47 | kDefaultLocalReplication = 1; 48 | kDefaultGlobalMemoryReplication = 1; 49 | kDefaultGlobalEbsReplication = 1; 50 | kThreadNum = 1; 51 | } 52 | 53 | void TearDown() { 54 | // clear all the logged messages after each test 55 | mock_zmq_util.sent_messages.clear(); 56 | } 57 | 58 | vector get_zmq_messages() { return mock_zmq_util.sent_messages; } 59 | 60 | void warmup_key_replication_map_to_defaults(vector keys) { 61 | for (string key : keys) { 62 | key_replication_map[key].global_replication_[Tier::MEMORY] = 63 | kDefaultGlobalMemoryReplication; 64 | key_replication_map[key].global_replication_[Tier::DISK] = 65 | kDefaultGlobalEbsReplication; 66 | key_replication_map[key].local_replication_[Tier::MEMORY] = 67 | kDefaultLocalReplication; 68 | key_replication_map[key].local_replication_[Tier::DISK] = 69 | kDefaultLocalReplication; 70 | } 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /src/kvs/management_node_response_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void management_node_response_handler(string &serialized, 18 | set
&extant_caches, 19 | map> &cache_ip_to_keys, 20 | map> &key_to_cache_ips, 21 | GlobalRingMap &global_hash_rings, 22 | LocalRingMap &local_hash_rings, 23 | SocketCache &pushers, ServerThread &wt, 24 | unsigned &rid) { 25 | // Get the response. 26 | StringSet func_nodes; 27 | func_nodes.ParseFromString(serialized); 28 | 29 | // Update extant_caches with the response. 30 | set
deleted_caches = std::move(extant_caches); 31 | extant_caches = set
(); 32 | for (const auto &func_node : func_nodes.keys()) { 33 | deleted_caches.erase(func_node); 34 | extant_caches.insert(func_node); 35 | } 36 | 37 | // Process deleted caches 38 | // (cache IPs that we were tracking but were not in the newest list of 39 | // caches). 40 | for (const auto &cache_ip : deleted_caches) { 41 | cache_ip_to_keys.erase(cache_ip); 42 | for (auto &key_and_caches : key_to_cache_ips) { 43 | key_and_caches.second.erase(cache_ip); 44 | } 45 | } 46 | 47 | // Get the cached keys by cache IP. 48 | // First, prepare the requests for all the IPs we know about 49 | // and put them in an address request map. 50 | map addr_request_map; 51 | for (const auto &cacheip : extant_caches) { 52 | Key key = get_user_metadata_key(cacheip, UserMetadataType::cache_ip); 53 | prepare_metadata_get_request( 54 | key, global_hash_rings[Tier::MEMORY], local_hash_rings[Tier::MEMORY], 55 | addr_request_map, wt.cache_ip_response_connect_address(), rid); 56 | } 57 | 58 | // Loop over the address request map and execute all the requests. 59 | for (const auto &addr_request : addr_request_map) { 60 | send_request(addr_request.second, pushers[addr_request.first]); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /include/monitor/policies.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_MONITOR_POLICIES_HPP_ 16 | #define KVS_INCLUDE_MONITOR_POLICIES_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | 20 | extern bool kEnableTiering; 21 | extern bool kEnableElasticity; 22 | extern bool kEnableSelectiveRep; 23 | 24 | void storage_policy(logger log, GlobalRingMap &global_hash_rings, 25 | TimePoint &grace_start, SummaryStats &ss, 26 | unsigned &memory_node_count, unsigned &ebs_node_count, 27 | unsigned &new_memory_count, unsigned &new_ebs_count, 28 | bool &removing_ebs_node, Address management_ip, 29 | MonitoringThread &mt, 30 | map &departing_node_map, 31 | SocketCache &pushers); 32 | 33 | void movement_policy(logger log, GlobalRingMap &global_hash_rings, 34 | LocalRingMap &local_hash_rings, TimePoint &grace_start, 35 | SummaryStats &ss, unsigned &memory_node_count, 36 | unsigned &ebs_node_count, unsigned &new_memory_count, 37 | unsigned &new_ebs_count, Address management_ip, 38 | map &key_replication_map, 39 | map &key_access_summary, 40 | map &key_size, MonitoringThread &mt, 41 | SocketCache &pushers, zmq::socket_t &response_puller, 42 | vector
&routing_ips, unsigned &rid); 43 | 44 | void slo_policy(logger log, GlobalRingMap &global_hash_rings, 45 | LocalRingMap &local_hash_rings, TimePoint &grace_start, 46 | SummaryStats &ss, unsigned &memory_node_count, 47 | unsigned &new_memory_count, bool &removing_memory_node, 48 | Address management_ip, 49 | map &key_replication_map, 50 | map &key_access_summary, MonitoringThread &mt, 51 | map &departing_node_map, 52 | SocketCache &pushers, zmq::socket_t &response_puller, 53 | vector
&routing_ips, unsigned &rid, 54 | map> &latency_miss_ratio_map); 55 | 56 | #endif // KVS_INCLUDE_MONITOR_POLICIES_HPP_ 57 | -------------------------------------------------------------------------------- /tests/include/lattices/test_map_lattice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "lattices/core_lattices.hpp" 21 | #include "gtest/gtest.h" 22 | 23 | typedef map> charMaxIntMap; 24 | 25 | class MapLatticeTest : public ::testing::Test { 26 | protected: 27 | MapLattice> *mapl; 28 | charMaxIntMap map1 = {{'a', MaxLattice(10)}, {'b', MaxLattice(20)}}; 29 | charMaxIntMap map2 = {{'b', MaxLattice(30)}, {'c', MaxLattice(40)}}; 30 | charMaxIntMap map3 = {{'a', MaxLattice(10)}, 31 | {'b', MaxLattice(30)}, 32 | {'c', MaxLattice(40)}}; 33 | MapLatticeTest() { mapl = new MapLattice>; } 34 | virtual ~MapLatticeTest() { delete mapl; } 35 | void check_equality(charMaxIntMap m) { 36 | EXPECT_EQ(m.size(), mapl->size().reveal()); 37 | charMaxIntMap result = mapl->reveal(); 38 | for (auto it = result.begin(); it != result.end(); ++it) { 39 | ASSERT_FALSE(m.find(it->first) == m.end()); 40 | ASSERT_TRUE(m.find(it->first)->second == it->second); 41 | } 42 | } 43 | }; 44 | 45 | TEST_F(MapLatticeTest, Assign) { 46 | EXPECT_EQ(0, mapl->size().reveal()); 47 | mapl->assign(map1); 48 | check_equality(map1); 49 | } 50 | 51 | TEST_F(MapLatticeTest, MergeByValue) { 52 | EXPECT_EQ(0, mapl->size().reveal()); 53 | mapl->merge(map1); 54 | check_equality(map1); 55 | mapl->merge(map2); 56 | check_equality(map3); 57 | } 58 | 59 | TEST_F(MapLatticeTest, MergeByLattice) { 60 | EXPECT_EQ(0, mapl->size().reveal()); 61 | mapl->merge(MapLattice>(map1)); 62 | check_equality(map1); 63 | mapl->merge(MapLattice>(map2)); 64 | check_equality(map3); 65 | } 66 | 67 | TEST_F(MapLatticeTest, KeySet) { 68 | mapl->merge(map1); 69 | SetLattice res = mapl->key_set(); 70 | EXPECT_EQ(set({'a', 'b'}), res.reveal()); 71 | } 72 | 73 | TEST_F(MapLatticeTest, At) { 74 | mapl->merge(map1); 75 | MaxLattice res = mapl->at('a'); 76 | EXPECT_EQ(10, res.reveal()); 77 | } 78 | 79 | TEST_F(MapLatticeTest, Contains) { 80 | mapl->merge(map1); 81 | BoolLattice res = mapl->contains('a'); 82 | EXPECT_EQ(true, res.reveal()); 83 | res = mapl->contains('d'); 84 | EXPECT_EQ(false, res.reveal()); 85 | } 86 | -------------------------------------------------------------------------------- /src/kvs/cache_ip_response_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void cache_ip_response_handler(string &serialized, 18 | map> &cache_ip_to_keys, 19 | map> &key_to_cache_ips) { 20 | // The response will be a list of cache IPs and their responsible keys. 21 | KeyResponse response; 22 | response.ParseFromString(serialized); 23 | 24 | for (const auto &tuple : response.tuples()) { 25 | // tuple is a key-value pair from the KVS; 26 | // here, the key is the metadata key for the cache IP, 27 | // and the value is the list of keys that cache is responsible for. 28 | 29 | if (tuple.error() == AnnaError::NO_ERROR) { 30 | // Extract the cache IP. 31 | Address cache_ip = get_key_from_user_metadata(tuple.key()); 32 | 33 | // Extract the keys that the cache is responsible for. 34 | LWWValue lww_value; 35 | lww_value.ParseFromString(tuple.payload()); 36 | 37 | StringSet key_set; 38 | key_set.ParseFromString(lww_value.value()); 39 | 40 | // First, update key_to_cache_ips with dropped keys for this cache. 41 | 42 | // Figure out which keys are in the old set of keys for this IP 43 | // that are not in the new fresh set of keys for this IP. 44 | // (We can do this by destructively modifying the old set of keys 45 | // since we don't need it anymore.) 46 | set &old_keys_for_ip = cache_ip_to_keys[cache_ip]; 47 | for (const auto &cache_key : key_set.keys()) { 48 | old_keys_for_ip.erase(cache_key); 49 | } 50 | set &deleted_keys = old_keys_for_ip; 51 | 52 | // For the keys that have been deleted from this cache, 53 | // remove them from the key->caches mapping too. 54 | for (const auto &key : deleted_keys) { 55 | key_to_cache_ips[key].erase(cache_ip); 56 | } 57 | 58 | cache_ip_to_keys[cache_ip].clear(); 59 | 60 | // Now we can update cache_ip_to_keys, 61 | // as well as add new keys to key_to_cache_ips. 62 | for (const auto &cache_key : key_set.keys()) { 63 | cache_ip_to_keys[cache_ip].emplace(std::move(cache_key)); 64 | key_to_cache_ips[cache_key].insert(cache_ip); 65 | } 66 | } 67 | 68 | // We can also get error 1 (key does not exist) 69 | // or error 2 (node not responsible for key). 70 | // We just ignore these for now; 71 | // 1 means the cache has not told the kvs about any keys yet, 72 | // and 2 will be fixed on our next cached keys update interval. 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/monitor/storage_policy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "monitor/monitoring_utils.hpp" 16 | #include "monitor/policies.hpp" 17 | 18 | void storage_policy(logger log, GlobalRingMap &global_hash_rings, 19 | TimePoint &grace_start, SummaryStats &ss, 20 | unsigned &memory_node_count, unsigned &ebs_node_count, 21 | unsigned &new_memory_count, unsigned &new_ebs_count, 22 | bool &removing_ebs_node, Address management_ip, 23 | MonitoringThread &mt, 24 | map &departing_node_map, 25 | SocketCache &pushers) { 26 | // check storage consumption and trigger elasticity if necessary 27 | if (kEnableElasticity) { 28 | if (new_memory_count == 0 && ss.required_memory_node > memory_node_count) { 29 | auto time_elapsed = std::chrono::duration_cast( 30 | std::chrono::system_clock::now() - grace_start) 31 | .count(); 32 | if (time_elapsed > kGracePeriod) { 33 | add_node(log, "memory", kNodeAdditionBatchSize, new_memory_count, 34 | pushers, management_ip); 35 | } 36 | } 37 | 38 | if (kEnableTiering && new_ebs_count == 0 && 39 | ss.required_ebs_node > ebs_node_count) { 40 | auto time_elapsed = std::chrono::duration_cast( 41 | std::chrono::system_clock::now() - grace_start) 42 | .count(); 43 | if (time_elapsed > kGracePeriod) { 44 | add_node(log, "ebs", kNodeAdditionBatchSize, new_ebs_count, pushers, 45 | management_ip); 46 | } 47 | } 48 | 49 | if (kEnableTiering && 50 | ss.avg_ebs_consumption_percentage < kMinEbsNodeConsumption && 51 | !removing_ebs_node && 52 | ebs_node_count > 53 | std::max(ss.required_ebs_node, (unsigned)kMinEbsTierSize)) { 54 | auto time_elapsed = std::chrono::duration_cast( 55 | std::chrono::system_clock::now() - grace_start) 56 | .count(); 57 | 58 | if (time_elapsed > kGracePeriod) { 59 | // pick a random ebs node and send remove node command 60 | auto node = next(global_hash_rings[Tier::DISK].begin(), 61 | rand() % global_hash_rings[Tier::DISK].size()) 62 | ->second; 63 | remove_node(log, node, "ebs", removing_ebs_node, pushers, 64 | departing_node_map, mt); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Anna 2 | 3 | [![Build Status](https://travis-ci.com/hydro-project/anna.svg?branch=master)](https://travis-ci.com/hydro-project/anna) 4 | [![codecov](https://codecov.io/gh/hydro-project/anna/branch/master/graph/badge.svg)](https://codecov.io/gh/hydro-project/anna) 5 | [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | 7 | Anna is a low-latency, autoscaling key-value store developed in the [RISE Lab](https://rise.cs.berkeley.edu) at [UC Berkeley](https://berkeley.edu). 8 | 9 | ## Design 10 | 11 | The core design goal for Anna is to avoid expensive locking and lock-free atomic instructions, which have recently been [shown to be extremely inefficient](http://www.jmfaleiro.com/pubs/latch-free-cidr2017.pdf). Anna instead employs a wait-free, shared-nothing architecture, where each thread in the system is given a private memory buffer and is allowed to process requests unencumbered by coordination. To resolve potentially conflicting updates, Anna encapsulates all user data in [lattice](https://en.wikipedia.org/wiki/Lattice_(order)) data structures, which have associative, commutative, and idempotent merge functions. As a result, for workloads that can tolerate slightly stale data, Anna provides best-in-class performance. A more detailed description of the system design and the coordination-free consistency mechanisms, as well as an evaluation and comparison against other state-of-the-art systems can be found in our [ICDE 2018 paper](http://db.cs.berkeley.edu/jmh/papers/anna_ieee18.pdf). 12 | 13 | Anna also is designed to be a cloud-native, autoscaling system. When deployed in a cluster, Anna comes with a monitoring subsystem that tracks workload volume, and responds with three key policy decisions: (1) horizontal elasticity to add or remove resources from the cluster; (2) selective replication of hot keys; and (3) data movement across two storage tiers (memory- and disk-based) for cost efficiency. This enables Anna to maintain its extremely low latencies while also being orders of magnitude more cost efficient than systems like [AWS DynamoDB](https://aws.amazon.com/dynamodb). A more detailed description of the cloud-native design of the system can be found in our [VLDB 2019 paper](http://www.vikrams.io/papers/anna-vldb19.pdf). 14 | 15 | ## Using Anna 16 | 17 | To run the Anna KVS locally, you need to first need to install its dependencies, which you can do with the `install-dependencies*.sh` scripts in the `hydro-project/common` repo, which is a submodule of this repository. You can build the project, which `scripts/build.sh`, and you can use `scripts/start-anna-local.sh` and `scripts/stop-anna-local.sh` scripts to start and stop the KVS respectively. This repository has an interactive CLI ([source](client/cpp/cli.cpp), executable compiles to `build/cli/anna-cli`) as well as a Python client ([source](client/python/anna/client.py)). The `common` repository has an importable C++ client that can embed into other applications. 18 | 19 | More detailed instructions on [building](docs/building-anna.md) and [running](docs/local-mode.md) can be found in the [docs](docs) directory. This repository only explains how to run Anna on a single machine. For instructions on how to run Anna in cluster mode, please see the `hydro-project/cluster` [repository](https://github.com/hydro-project/cluster). 20 | 21 | ## License 22 | 23 | The Hydro Project is licensed under the [Apache v2 License](LICENSE). 24 | -------------------------------------------------------------------------------- /src/route/address_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | void address_handler(logger log, string &serialized, SocketCache &pushers, 18 | RoutingThread &rt, GlobalRingMap &global_hash_rings, 19 | LocalRingMap &local_hash_rings, 20 | map &key_replication_map, 21 | map>> &pending_requests, 22 | unsigned &seed) { 23 | KeyAddressRequest addr_request; 24 | addr_request.ParseFromString(serialized); 25 | 26 | KeyAddressResponse addr_response; 27 | addr_response.set_response_id(addr_request.request_id()); 28 | bool succeed; 29 | 30 | int num_servers = 0; 31 | for (const auto &pair : global_hash_rings) { 32 | num_servers += pair.second.size(); 33 | } 34 | 35 | bool respond = false; 36 | if (num_servers == 0) { 37 | addr_response.set_error(AnnaError::NO_SERVERS); 38 | 39 | for (const Key &key : addr_request.keys()) { 40 | KeyAddressResponse_KeyAddress *tp = addr_response.add_addresses(); 41 | tp->set_key(key); 42 | } 43 | 44 | respond = true; 45 | } else { // if there are servers, attempt to return the correct threads 46 | for (const Key &key : addr_request.keys()) { 47 | ServerThreadList threads = {}; 48 | 49 | if (key.length() > 50 | 0) { // Only run this code is the key is a valid string. 51 | // Otherwise, an empty response will be sent. 52 | for (const Tier &tier : kAllTiers) { 53 | threads = kHashRingUtil->get_responsible_threads( 54 | rt.replication_response_connect_address(), key, is_metadata(key), 55 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 56 | {tier}, succeed, seed); 57 | 58 | if (threads.size() > 0) { 59 | break; 60 | } 61 | 62 | if (!succeed) { // this means we don't have the replication factor for 63 | // the key 64 | pending_requests[key].push_back(std::pair( 65 | addr_request.response_address(), addr_request.request_id())); 66 | return; 67 | } 68 | } 69 | } 70 | 71 | KeyAddressResponse_KeyAddress *tp = addr_response.add_addresses(); 72 | tp->set_key(key); 73 | respond = true; 74 | 75 | for (const ServerThread &thread : threads) { 76 | tp->add_ips(thread.key_request_connect_address()); 77 | } 78 | } 79 | } 80 | 81 | if (respond) { 82 | string serialized; 83 | addr_response.SerializeToString(&serialized); 84 | 85 | kZmqUtil->send_string(serialized, 86 | &pushers[addr_request.response_address()]); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/kvs/self_depart_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void self_depart_handler(unsigned thread_id, unsigned &seed, Address public_ip, 18 | Address private_ip, logger log, string &serialized, 19 | GlobalRingMap &global_hash_rings, 20 | LocalRingMap &local_hash_rings, 21 | map &stored_key_map, 22 | map &key_replication_map, 23 | vector
&routing_ips, 24 | vector
&monitoring_ips, ServerThread &wt, 25 | SocketCache &pushers, SerializerMap &serializers) { 26 | log->info("This node is departing."); 27 | global_hash_rings[kSelfTier].remove(public_ip, private_ip, 0); 28 | 29 | // thread 0 notifies other nodes in the cluster (of all types) that it is 30 | // leaving the cluster 31 | if (thread_id == 0) { 32 | string msg = Tier_Name(kSelfTier) + ":" + public_ip + ":" + private_ip; 33 | 34 | for (const auto &pair : global_hash_rings) { 35 | GlobalHashRing hash_ring = pair.second; 36 | 37 | for (const ServerThread &st : hash_ring.get_unique_servers()) { 38 | kZmqUtil->send_string(msg, &pushers[st.node_depart_connect_address()]); 39 | } 40 | } 41 | 42 | msg = "depart:" + msg; 43 | 44 | // notify all routing nodes 45 | for (const string &address : routing_ips) { 46 | kZmqUtil->send_string( 47 | msg, &pushers[RoutingThread(address, 0).notify_connect_address()]); 48 | } 49 | 50 | // notify monitoring nodes 51 | for (const string &address : monitoring_ips) { 52 | kZmqUtil->send_string( 53 | msg, &pushers[MonitoringThread(address).notify_connect_address()]); 54 | } 55 | 56 | // tell all worker threads about the self departure 57 | for (unsigned tid = 1; tid < kThreadNum; tid++) { 58 | kZmqUtil->send_string(serialized, 59 | &pushers[ServerThread(public_ip, private_ip, tid) 60 | .self_depart_connect_address()]); 61 | } 62 | } 63 | 64 | AddressKeysetMap addr_keyset_map; 65 | bool succeed; 66 | 67 | for (const auto &key_pair : stored_key_map) { 68 | Key key = key_pair.first; 69 | ServerThreadList threads = kHashRingUtil->get_responsible_threads( 70 | wt.replication_response_connect_address(), key, is_metadata(key), 71 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 72 | kAllTiers, succeed, seed); 73 | 74 | if (succeed) { 75 | // since we already removed this node from the hash ring, no need to 76 | // exclude it explicitly 77 | for (const ServerThread &thread : threads) { 78 | addr_keyset_map[thread.gossip_connect_address()].insert(key); 79 | } 80 | } else { 81 | log->error("Missing key replication factor in node depart routine"); 82 | } 83 | } 84 | 85 | send_gossip(addr_keyset_map, pushers, serializers, stored_key_map); 86 | kZmqUtil->send_string(public_ip + "_" + private_ip + "_" + 87 | Tier_Name(kSelfTier), 88 | &pushers[serialized]); 89 | } 90 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 U.C. Berkeley RISE Lab 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 16 | PROJECT(Anna) 17 | 18 | SET(ANNA_VERSION_MAJOR 0) 19 | SET(ANNA_VERSION_MINOR 1) 20 | SET(ANNA_VERSION_PATCH 0) 21 | 22 | IF(NOT DEFINED BUILD_TEST) 23 | SET(BUILD_TEST OFF) 24 | ENDIF() 25 | 26 | IF(${BUILD_TEST}) 27 | ENABLE_TESTING() 28 | ENDIF() 29 | 30 | SET(CMAKE_CXX_STANDARD 11) 31 | SET(CMAKE_CXX_STANDARD_REQUIRED on) 32 | 33 | SET(VENDOR_DIR common/vendor) 34 | 35 | IF(${CMAKE_CXX_COMPILER} STREQUAL "/usr/bin/clang++") 36 | SET(CMAKE_CXX_FLAGS_COMMON 37 | "-std=c++11 \ 38 | -stdlib=libc++ -pthread") 39 | ENDIF() 40 | 41 | IF(${CMAKE_CXX_COMPILER} STREQUAL "/usr/bin/g++") 42 | SET(CMAKE_CXX_FLAGS_COMMON 43 | "-std=c++11 -pthread") 44 | ENDIF() 45 | 46 | SET(CMAKE_CXX_FLAGS_DEBUG 47 | "${CMAKE_CXX_FLAGS_DEBUG} \ 48 | ${CMAKE_CXX_FLAGS_COMMON} \ 49 | -g -O0 -fprofile-arcs -ftest-coverage") 50 | 51 | SET(CMAKE_CXX_FLAGS_RELEASE 52 | "${CMAKE_CXX_FLAGS_RELEASE} \ 53 | ${CMAKE_CXX_FLAGS_COMMON} \ 54 | -O3") 55 | 56 | ADD_SUBDIRECTORY(${VENDOR_DIR}/spdlog) 57 | ADD_SUBDIRECTORY(${VENDOR_DIR}/yamlcpp) 58 | ADD_SUBDIRECTORY(${VENDOR_DIR}/zeromq) 59 | ADD_SUBDIRECTORY(${VENDOR_DIR}/zeromqcpp) 60 | 61 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 62 | INCLUDE_DIRECTORIES(${SPDLOG_INCLUDE_DIRS}) 63 | INCLUDE_DIRECTORIES(${ZEROMQCPP_INCLUDE_DIRS}) 64 | INCLUDE_DIRECTORIES(${ZEROMQ_INCLUDE_DIRS}) 65 | INCLUDE_DIRECTORIES(${YAMLCPP_INCLUDE_DIRS}) 66 | INCLUDE_DIRECTORIES(common/include) 67 | INCLUDE_DIRECTORIES(include) 68 | 69 | INCLUDE(FindProtobuf) 70 | FIND_PACKAGE(Protobuf REQUIRED) 71 | INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) 72 | PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER 73 | ./common/proto/anna.proto 74 | ./common/proto/shared.proto 75 | ./include/proto/metadata.proto 76 | ) 77 | 78 | PROTOBUF_GENERATE_CPP(BPROTO_SRC BPROTO_HEADER 79 | ./include/proto/benchmark.proto 80 | ) 81 | 82 | ADD_LIBRARY(anna-proto ${PROTO_HEADER} ${PROTO_SRC}) 83 | ADD_LIBRARY(anna-bench-proto ${BPROTO_HEADER} ${BPROTO_SRC}) 84 | 85 | FILE(GLOB_RECURSE ZMQ_UTIL_SRC common/include/zmq/*.cpp) 86 | FILE(GLOB_RECURSE ZMQ_UTIL_HEADER common/include/zmq/*.hpp) 87 | ADD_LIBRARY(hydro-zmq STATIC ${ZMQ_UTIL_HEADER} ${ZMQ_UTIL_SRC}) 88 | ADD_DEPENDENCIES(hydro-zmq zeromq zeromqcpp spdlog) 89 | 90 | IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") 91 | INCLUDE(common/cmake/clang-format.cmake) 92 | INCLUDE(common/cmake/CodeCoverage.cmake) 93 | ENDIF() 94 | 95 | LINK_DIRECTORIES(${ZEROMQ_LINK_DIRS} ${YAMLCPP_LINK_DIRS}) 96 | 97 | ADD_SUBDIRECTORY(src) 98 | ADD_SUBDIRECTORY(client/cpp) 99 | 100 | IF(${BUILD_TEST}) 101 | INCLUDE(common/cmake/DownloadProject.cmake) 102 | DOWNLOAD_PROJECT(PROJ googletest 103 | GIT_REPOSITORY https://github.com/google/googletest.git 104 | GIT_TAG release-1.8.0 105 | UPDATE_DISCONNECTED 1 106 | ) 107 | 108 | ADD_SUBDIRECTORY(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 109 | 110 | INCLUDE_DIRECTORIES(common/mock) 111 | INCLUDE_DIRECTORIES(tests) 112 | ADD_SUBDIRECTORY(common/mock) 113 | ADD_SUBDIRECTORY(tests) 114 | ENDIF() 115 | -------------------------------------------------------------------------------- /src/kvs/gossip_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void gossip_handler(unsigned &seed, string &serialized, 18 | GlobalRingMap &global_hash_rings, 19 | LocalRingMap &local_hash_rings, 20 | map> &pending_gossip, 21 | map &stored_key_map, 22 | map &key_replication_map, 23 | ServerThread &wt, SerializerMap &serializers, 24 | SocketCache &pushers, logger log) { 25 | KeyRequest gossip; 26 | gossip.ParseFromString(serialized); 27 | 28 | bool succeed; 29 | map gossip_map; 30 | 31 | for (const KeyTuple &tuple : gossip.tuples()) { 32 | // first check if the thread is responsible for the key 33 | Key key = tuple.key(); 34 | ServerThreadList threads = kHashRingUtil->get_responsible_threads( 35 | wt.replication_response_connect_address(), key, is_metadata(key), 36 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 37 | kSelfTierIdVector, succeed, seed); 38 | 39 | if (succeed) { 40 | if (std::find(threads.begin(), threads.end(), wt) != 41 | threads.end()) { // this means this worker thread is one of the 42 | // responsible threads 43 | if (stored_key_map.find(key) != stored_key_map.end() && 44 | stored_key_map[key].type_ != tuple.lattice_type()) { 45 | log->error("Lattice type mismatch: {} from query but {} expected.", 46 | LatticeType_Name(tuple.lattice_type()), 47 | stored_key_map[key].type_); 48 | } else { 49 | process_put(tuple.key(), tuple.lattice_type(), tuple.payload(), 50 | serializers[tuple.lattice_type()], stored_key_map); 51 | } 52 | } else { 53 | if (is_metadata(key)) { // forward the gossip 54 | for (const ServerThread &thread : threads) { 55 | if (gossip_map.find(thread.gossip_connect_address()) == 56 | gossip_map.end()) { 57 | gossip_map[thread.gossip_connect_address()].set_type( 58 | RequestType::PUT); 59 | } 60 | 61 | prepare_put_tuple(gossip_map[thread.gossip_connect_address()], key, 62 | tuple.lattice_type(), tuple.payload()); 63 | } 64 | } else { 65 | kHashRingUtil->issue_replication_factor_request( 66 | wt.replication_response_connect_address(), key, 67 | global_hash_rings[Tier::MEMORY], local_hash_rings[Tier::MEMORY], 68 | pushers, seed); 69 | 70 | pending_gossip[key].push_back( 71 | PendingGossip(tuple.lattice_type(), tuple.payload())); 72 | } 73 | } 74 | } else { 75 | pending_gossip[key].push_back( 76 | PendingGossip(tuple.lattice_type(), tuple.payload())); 77 | } 78 | } 79 | 80 | // redirect gossip 81 | for (const auto &gossip_pair : gossip_map) { 82 | string serialized; 83 | gossip_pair.second.SerializeToString(&serialized); 84 | kZmqUtil->send_string(serialized, &pushers[gossip_pair.first]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/kvs/utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void send_gossip(AddressKeysetMap &addr_keyset_map, SocketCache &pushers, 18 | SerializerMap &serializers, 19 | map &stored_key_map) { 20 | map gossip_map; 21 | 22 | for (const auto &key_pair : addr_keyset_map) { 23 | string address = key_pair.first; 24 | RequestType type; 25 | RequestType_Parse("PUT", &type); 26 | gossip_map[address].set_type(type); 27 | 28 | for (const auto &key : key_pair.second) { 29 | LatticeType type; 30 | if (stored_key_map.find(key) == stored_key_map.end()) { 31 | // we don't have this key stored, so skip 32 | continue; 33 | } else { 34 | type = stored_key_map[key].type_; 35 | } 36 | 37 | auto res = process_get(key, serializers[type]); 38 | 39 | if (res.second == 0) { 40 | prepare_put_tuple(gossip_map[address], key, type, res.first); 41 | } 42 | } 43 | } 44 | 45 | // send gossip 46 | for (const auto &gossip_pair : gossip_map) { 47 | string serialized; 48 | gossip_pair.second.SerializeToString(&serialized); 49 | kZmqUtil->send_string(serialized, &pushers[gossip_pair.first]); 50 | } 51 | } 52 | 53 | std::pair process_get(const Key &key, 54 | Serializer *serializer) { 55 | AnnaError error = AnnaError::NO_ERROR; 56 | auto res = serializer->get(key, error); 57 | return std::pair(std::move(res), error); 58 | } 59 | 60 | void process_put(const Key &key, LatticeType lattice_type, 61 | const string &payload, Serializer *serializer, 62 | map &stored_key_map) { 63 | stored_key_map[key].size_ = serializer->put(key, payload); 64 | stored_key_map[key].type_ = std::move(lattice_type); 65 | } 66 | 67 | bool is_primary_replica(const Key &key, 68 | map &key_replication_map, 69 | GlobalRingMap &global_hash_rings, 70 | LocalRingMap &local_hash_rings, ServerThread &st) { 71 | if (key_replication_map[key].global_replication_[kSelfTier] == 0) { 72 | return false; 73 | } else { 74 | if (kSelfTier > Tier::MEMORY) { 75 | bool has_upper_tier_replica = false; 76 | for (const Tier &tier : kAllTiers) { 77 | if (tier < kSelfTier && 78 | key_replication_map[key].global_replication_[tier] > 0) { 79 | has_upper_tier_replica = true; 80 | } 81 | } 82 | if (has_upper_tier_replica) { 83 | return false; 84 | } 85 | } 86 | 87 | auto global_pos = global_hash_rings[kSelfTier].find(key); 88 | if (global_pos != global_hash_rings[kSelfTier].end() && 89 | st.private_ip().compare(global_pos->second.private_ip()) == 0) { 90 | auto local_pos = local_hash_rings[kSelfTier].find(key); 91 | 92 | if (local_pos != local_hash_rings[kSelfTier].end() && 93 | st.tid() == local_pos->second.tid()) { 94 | return true; 95 | } 96 | } 97 | 98 | return false; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/route/membership_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | void membership_handler(logger log, string &serialized, SocketCache &pushers, 18 | GlobalRingMap &global_hash_rings, unsigned thread_id, 19 | Address ip) { 20 | vector v; 21 | 22 | split(serialized, ':', v); 23 | string type = v[0]; 24 | 25 | Tier tier; 26 | Tier_Parse(v[1], &tier); 27 | Address new_server_public_ip = v[2]; 28 | Address new_server_private_ip = v[3]; 29 | 30 | if (type == "join") { 31 | // we only read the join count if it's a join message, not if it's a depart 32 | // message because the latter does not send a join count 33 | int join_count = stoi(v[4]); 34 | log->info("Received join from server {}/{} in tier {}.", 35 | new_server_public_ip, new_server_private_ip, 36 | std::to_string(tier)); 37 | 38 | // update hash ring 39 | bool inserted = global_hash_rings[tier].insert( 40 | new_server_public_ip, new_server_private_ip, join_count, 0); 41 | 42 | if (inserted) { 43 | if (thread_id == 0) { 44 | // gossip the new node address between server nodes to ensure 45 | // consistency 46 | for (const auto &pair : global_hash_rings) { 47 | const GlobalHashRing hash_ring = pair.second; 48 | 49 | // we send a message with everything but the join because that is 50 | // what the server nodes expect 51 | // NOTE: this seems like a bit of a hack right now -- should we have 52 | // a less ad-hoc way of doing message generation? 53 | string msg = v[1] + ":" + v[2] + ":" + v[3] + ":" + v[4]; 54 | 55 | for (const ServerThread &st : hash_ring.get_unique_servers()) { 56 | // if the node is not the newly joined node, send the ip of the 57 | // newly joined node 58 | if (st.private_ip().compare(new_server_private_ip) != 0) { 59 | kZmqUtil->send_string(msg, 60 | &pushers[st.node_join_connect_address()]); 61 | } 62 | } 63 | } 64 | 65 | // tell all worker threads about the message 66 | for (unsigned tid = 1; tid < kRoutingThreadCount; tid++) { 67 | kZmqUtil->send_string( 68 | serialized, 69 | &pushers[RoutingThread(ip, tid).notify_connect_address()]); 70 | } 71 | } 72 | } 73 | 74 | for (const auto &pair : global_hash_rings) { 75 | log->info("Hash ring for tier {} size is {}.", pair.first, 76 | pair.second.size()); 77 | } 78 | } else if (type == "depart") { 79 | log->info("Received depart from server {}/{}.", new_server_public_ip, 80 | new_server_private_ip, new_server_private_ip); 81 | global_hash_rings[tier].remove(new_server_public_ip, new_server_private_ip, 82 | 0); 83 | 84 | if (thread_id == 0) { 85 | // tell all worker threads about the message 86 | for (unsigned tid = 1; tid < kRoutingThreadCount; tid++) { 87 | kZmqUtil->send_string( 88 | serialized, 89 | &pushers[RoutingThread(ip, tid).notify_connect_address()]); 90 | } 91 | } 92 | 93 | for (const Tier &tier : kAllTiers) { 94 | log->info("Hash ring for tier {} size is {}.", Tier_Name(tier), 95 | global_hash_rings[tier].size()); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/route/replication_response_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "route/routing_handlers.hpp" 16 | 17 | void replication_response_handler( 18 | logger log, string &serialized, SocketCache &pushers, RoutingThread &rt, 19 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 20 | map &key_replication_map, 21 | map>> &pending_requests, unsigned &seed) { 22 | KeyResponse response; 23 | response.ParseFromString(serialized); 24 | // we assume tuple 0 because there should only be one tuple responding to a 25 | // replication factor request 26 | KeyTuple tuple = response.tuples(0); 27 | 28 | Key key = get_key_from_metadata(tuple.key()); 29 | 30 | AnnaError error = tuple.error(); 31 | 32 | if (error == AnnaError::NO_ERROR) { 33 | LWWValue lww_value; 34 | lww_value.ParseFromString(tuple.payload()); 35 | ReplicationFactor rep_data; 36 | rep_data.ParseFromString(lww_value.value()); 37 | 38 | for (const auto &global : rep_data.global()) { 39 | key_replication_map[key].global_replication_[global.tier()] = 40 | global.value(); 41 | } 42 | 43 | for (const auto &local : rep_data.local()) { 44 | key_replication_map[key].local_replication_[local.tier()] = local.value(); 45 | } 46 | } else if (error == AnnaError::KEY_DNE) { 47 | // this means that the receiving thread was responsible for the metadata 48 | // but didn't have any values stored -- we use the default rep factor 49 | init_replication(key_replication_map, key); 50 | } else if (error == AnnaError::WRONG_THREAD) { 51 | // this means that the node that received the rep factor request was not 52 | // responsible for that metadata 53 | auto respond_address = rt.replication_response_connect_address(); 54 | kHashRingUtil->issue_replication_factor_request( 55 | respond_address, key, global_hash_rings[Tier::MEMORY], 56 | local_hash_rings[Tier::MEMORY], pushers, seed); 57 | return; 58 | } else { 59 | log->error("Unexpected error type {} in replication factor response.", 60 | error); 61 | return; 62 | } 63 | 64 | // process pending key address requests 65 | if (pending_requests.find(key) != pending_requests.end()) { 66 | bool succeed; 67 | ServerThreadList threads = {}; 68 | 69 | for (const Tier &tier : kAllTiers) { 70 | threads = kHashRingUtil->get_responsible_threads( 71 | rt.replication_response_connect_address(), key, false, 72 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 73 | {tier}, succeed, seed); 74 | 75 | if (threads.size() > 0) { 76 | break; 77 | } 78 | 79 | if (!succeed) { 80 | log->error("Missing replication factor for key {}.", key); 81 | return; 82 | } 83 | } 84 | 85 | for (const auto &pending_key_req : pending_requests[key]) { 86 | KeyAddressResponse key_res; 87 | key_res.set_response_id(pending_key_req.second); 88 | auto *tp = key_res.add_addresses(); 89 | tp->set_key(key); 90 | 91 | for (const ServerThread &thread : threads) { 92 | tp->add_ips(thread.key_request_connect_address()); 93 | } 94 | 95 | string serialized; 96 | key_res.SerializeToString(&serialized); 97 | kZmqUtil->send_string(serialized, &pushers[pending_key_req.first]); 98 | } 99 | 100 | pending_requests.erase(key); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/monitor/membership_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "monitor/monitoring_handlers.hpp" 16 | 17 | void membership_handler( 18 | logger log, string &serialized, GlobalRingMap &global_hash_rings, 19 | unsigned &new_memory_count, unsigned &new_ebs_count, TimePoint &grace_start, 20 | vector
&routing_ips, StorageStats &memory_storage, 21 | StorageStats &ebs_storage, OccupancyStats &memory_occupancy, 22 | OccupancyStats &ebs_occupancy, 23 | map> &key_access_frequency) { 24 | vector v; 25 | 26 | split(serialized, ':', v); 27 | string type = v[0]; 28 | 29 | Tier tier; 30 | Tier_Parse(v[1], &tier); 31 | Address new_server_public_ip = v[2]; 32 | Address new_server_private_ip = v[3]; 33 | 34 | if (type == "join") { 35 | log->info("Received join from server {}/{} in tier {}.", 36 | new_server_public_ip, new_server_private_ip, 37 | std::to_string(tier)); 38 | if (tier == Tier::MEMORY) { 39 | global_hash_rings[tier].insert(new_server_public_ip, 40 | new_server_private_ip, 0, 0); 41 | 42 | if (new_memory_count > 0) { 43 | new_memory_count -= 1; 44 | } 45 | 46 | // reset grace period timer 47 | grace_start = std::chrono::system_clock::now(); 48 | } else if (tier == Tier::DISK) { 49 | global_hash_rings[tier].insert(new_server_public_ip, 50 | new_server_private_ip, 0, 0); 51 | 52 | if (new_ebs_count > 0) { 53 | new_ebs_count -= 1; 54 | } 55 | 56 | // reset grace period timer 57 | grace_start = std::chrono::system_clock::now(); 58 | } else if (tier == Tier::ROUTING) { 59 | routing_ips.push_back(new_server_public_ip); 60 | } else { 61 | log->error("Invalid tier: {}.", std::to_string(tier)); 62 | } 63 | 64 | for (const auto &pair : global_hash_rings) { 65 | log->info("Hash ring for tier {} is size {}.", pair.first, 66 | pair.second.size()); 67 | } 68 | } else if (type == "depart") { 69 | log->info("Received depart from server {}/{}.", new_server_public_ip, 70 | new_server_private_ip); 71 | 72 | // update hash ring 73 | global_hash_rings[tier].remove(new_server_public_ip, new_server_private_ip, 74 | 0); 75 | if (tier == Tier::MEMORY) { 76 | memory_storage.erase(new_server_private_ip); 77 | memory_occupancy.erase(new_server_private_ip); 78 | 79 | // NOTE: No const here because we are calling erase 80 | for (auto &key_access_pair : key_access_frequency) { 81 | for (unsigned i = 0; i < kMemoryThreadCount; i++) { 82 | key_access_pair.second.erase(new_server_private_ip + ":" + 83 | std::to_string(i)); 84 | } 85 | } 86 | } else if (tier == Tier::DISK) { 87 | ebs_storage.erase(new_server_private_ip); 88 | ebs_occupancy.erase(new_server_private_ip); 89 | 90 | // NOTE: No const here because we are calling erase 91 | for (auto &key_access_pair : key_access_frequency) { 92 | for (unsigned i = 0; i < kEbsThreadCount; i++) { 93 | key_access_pair.second.erase(new_server_private_ip + ":" + 94 | std::to_string(i)); 95 | } 96 | } 97 | } else { 98 | log->error("Invalid tier: {}.", std::to_string(tier)); 99 | } 100 | 101 | for (const auto &pair : global_hash_rings) { 102 | log->info("Hash ring for tier {} is size {}.", pair.first, 103 | pair.second.size()); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /dockerfiles/start-anna.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 U.C. Berkeley RISE Lab 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | if [ -z "$1" ]; then 18 | echo "No argument provided. Exiting." 19 | exit 1 20 | fi 21 | 22 | # A helper function that takes a space separated list and generates a string 23 | # that parses as a YAML list. 24 | gen_yml_list() { 25 | IFS=' ' read -r -a ARR <<< $1 26 | RESULT="" 27 | 28 | for IP in "${ARR[@]}"; do 29 | RESULT=$"$RESULT - $IP\n" 30 | done 31 | 32 | echo -e "$RESULT" 33 | } 34 | 35 | cd $HYDRO_HOME/anna 36 | mkdir -p conf 37 | 38 | # Check if the context that we are running in is EC2 or not. If it is, we 39 | # determine separate private and public IP addresses. Otherwise, we use the 40 | # same one for both. 41 | IS_EC2=`curl -s http://169.254.169.254` 42 | PRIVATE_IP=`ifconfig eth0 | grep 'inet' | grep -v inet6 | sed -e 's/^[ \t]*//' | cut -d' ' -f2` 43 | if [[ ! -z "$IS_EC2" ]]; then 44 | PUBLIC_IP=`curl http://169.254.169.254/latest/meta-data/public-ipv4` 45 | else 46 | PUBLIC_IP=$PRIVATE_IP 47 | fi 48 | 49 | # Download latest version of the code from relevant repository & branch -- if 50 | # none are specified, we use hydro-project/anna by default. 51 | git remote remove origin 52 | if [[ -z "$REPO_ORG" ]]; then 53 | REPO_ORG="hydro-project" 54 | fi 55 | 56 | if [[ -z "$REPO_BRANCH" ]]; then 57 | REPO_BRANCH="master" 58 | fi 59 | 60 | git remote add origin https://github.com/$REPO_ORG/anna 61 | while ! (git fetch -p origin) 62 | do 63 | echo "git fetch failed, retrying" 64 | done 65 | git checkout -b brnch origin/$REPO_BRANCH 66 | git submodule sync 67 | git submodule update 68 | 69 | # Compile the latest version of the code on the branch we just check out. 70 | cd build && make -j2 && cd .. 71 | 72 | # Do not start the server until conf/anna-config.yml has been copied onto this 73 | # pod -- if we start earlier, we won't now how to configure the system. 74 | while [[ ! -f "conf/anna-config.yml" ]]; do 75 | continue 76 | done 77 | 78 | # Tailor the config file to have process specific information. 79 | if [ "$1" = "mn" ]; then 80 | echo -e "monitoring:" >> conf/anna-config.yml 81 | echo -e " mgmt_ip: $MGMT_IP" >> conf/anna-config.yml 82 | echo -e " ip: $PRIVATE_IP" >> conf/anna-config.yml 83 | 84 | ./build/target/kvs/anna-monitor 85 | elif [ "$1" = "r" ]; then 86 | echo -e "routing:" >> conf/anna-config.yml 87 | echo -e " ip: $PRIVATE_IP" >> conf/anna-config.yml 88 | 89 | LST=$(gen_yml_list "$MON_IPS") 90 | echo -e " monitoring:" >> conf/anna-config.yml 91 | echo -e "$LST" >> conf/anna-config.yml 92 | 93 | ./build/target/kvs/anna-route 94 | elif [ "$1" = "b" ]; then 95 | echo -e "user:" >> conf/anna-config.yml 96 | echo -e " ip: $PRIVATE_IP" >> conf/anna-config.yml 97 | 98 | LST=$(gen_yml_list "$MON_IPS") 99 | echo -e " monitoring:" >> conf/anna-config.yml 100 | echo -e "$LST" >> conf/anna-config.yml 101 | 102 | LST=$(gen_yml_list "$ROUTING_IPS") 103 | echo -e " routing:" >> conf/anna-config.yml 104 | echo -e "$LST" >> conf/anna-config.yml 105 | 106 | ./build/target/benchmark/anna-bench 107 | else 108 | echo -e "server:" >> conf/anna-config.yml 109 | echo -e " seed_ip: $SEED_IP" >> conf/anna-config.yml 110 | echo -e " public_ip: $PUBLIC_IP" >> conf/anna-config.yml 111 | echo -e " private_ip: $PRIVATE_IP" >> conf/anna-config.yml 112 | echo -e " mgmt_ip: $MGMT_IP" >> conf/anna-config.yml 113 | 114 | LST=$(gen_yml_list "$MON_IPS") 115 | echo -e " monitoring:" >> conf/anna-config.yml 116 | echo -e "$LST" >> conf/anna-config.yml 117 | 118 | LST=$(gen_yml_list "$ROUTING_IPS") 119 | echo -e " routing:" >> conf/anna-config.yml 120 | echo -e "$LST" >> conf/anna-config.yml 121 | 122 | ./build/target/kvs/anna-kvs 123 | fi 124 | -------------------------------------------------------------------------------- /include/proto/metadata.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | // A message to capture the periodic reporting of each server thread's local 18 | // statistics; these are aggregated by the monioring system. 19 | message ServerThreadStatistics { 20 | // What percentage of the server thread's storage capacity is being consumed. 21 | uint64 storage_consumption = 1; 22 | 23 | // What percentage of the server thread's compute capacity is being consumed. 24 | double occupancy = 2; 25 | 26 | // The server thread's reporting epoch. 27 | uint32 epoch = 3; 28 | 29 | // How many key accesses were serviced during this epoch. 30 | uint32 access_count = 4; 31 | } 32 | 33 | // A message to capture the access frequencies of individual keys for a 34 | // particular server thread. 35 | message KeyAccessData { 36 | // A mapping from an individual key to its access count. 37 | message KeyCount { 38 | // The key being tracked. 39 | string key = 1; 40 | 41 | // The number of times this key was accessed during this epoch. 42 | uint32 access_count = 2; 43 | } 44 | // A list of all the key access frequencies tracked during this epoch. 45 | repeated KeyCount keys = 1; 46 | } 47 | 48 | // An enum representing all the tiers the system supports -- currently, a 49 | // memory tier and a disk-based tier. 50 | enum Tier { 51 | TIER_UNSPECIFIED = 0; 52 | 53 | // The value for the memory tier. 54 | MEMORY = 1; 55 | 56 | // The value for the disk-based tier. 57 | DISK = 2; 58 | 59 | // The value for the routing tier. 60 | ROUTING = 3; 61 | } 62 | 63 | // A message to track which physical servers are a part of which Anna 64 | // membership (memory, disk) tier. 65 | message ClusterMembership { 66 | // The representation the servers comprising an individual tier. 67 | message TierMembership { 68 | // The IP addresses for an individual server -- the private/public IP 69 | // distinction is specific to EC2-based deployments. 70 | message Server { 71 | // The public IP address for a server. 72 | string public_ip = 1; 73 | 74 | // The private IP address for a server. 75 | string private_ip = 2; 76 | } 77 | 78 | // The Tier represented by this message -- either MEMORY or DISK. 79 | Tier tier_id = 1; 80 | 81 | // The list of servers in this tier. 82 | repeated Server servers = 2; 83 | } 84 | 85 | // The set of all tiers in the system. 86 | repeated TierMembership tiers = 1; 87 | } 88 | 89 | // A message to track metadata about how large each key in the system is. 90 | message KeySizeData { 91 | // The size metadata for an individual key. 92 | message KeySize { 93 | // The key for which size metadata is being reported. 94 | string key = 1; 95 | 96 | // The size of the above key. 97 | uint32 size = 2; 98 | } 99 | 100 | // The list of key size metadata tuples being reported. 101 | repeated KeySize key_sizes = 1; 102 | } 103 | 104 | // A message that captures the replication factor for an individual key. 105 | message ReplicationFactor { 106 | // A message representing the replication level for a single key at a 107 | // single tier. 108 | message ReplicationValue { 109 | // The tier represented by this message. 110 | Tier tier = 1; 111 | 112 | // The replication level at this particular tier for this particular key. 113 | uint32 value = 2; 114 | } 115 | 116 | // The name of the key whose replication factor is being changed. 117 | string key = 1; 118 | 119 | // A set of mappings from individual tiers (MEMORY, DISK -- see Tier enum) 120 | // to the cross-machine replication factor at that tier. 121 | repeated ReplicationValue global = 2; 122 | 123 | // A set of mappings from individual tiers (MEMORY, DISK -- see Tier enum) 124 | // to the intra-machine replication factor at that tier. 125 | repeated ReplicationValue local = 3; 126 | } 127 | 128 | // A message to propagate changes to a set of keys' replication factors. 129 | message ReplicationFactorUpdate { 130 | // The set of replication factor updates being sent. 131 | repeated ReplicationFactor updates = 1; 132 | } 133 | -------------------------------------------------------------------------------- /tests/kvs/server_handler_base.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "mock/mock_hash_utils.hpp" 16 | #include "mock_zmq_utils.hpp" 17 | 18 | MockZmqUtil mock_zmq_util; 19 | ZmqUtilInterface *kZmqUtil = &mock_zmq_util; 20 | 21 | MockHashRingUtil mock_hash_ring_util; 22 | HashRingUtilInterface *kHashRingUtil = &mock_hash_ring_util; 23 | 24 | logger log_ = spdlog::basic_logger_mt("mock_log", "mock_log.txt", true); 25 | 26 | string kRequestId = "0"; 27 | 28 | class ServerHandlerTest : public ::testing::Test { 29 | protected: 30 | Address ip = "127.0.0.1"; 31 | unsigned thread_id = 0; 32 | GlobalRingMap global_hash_rings; 33 | LocalRingMap local_hash_rings; 34 | map stored_key_map; 35 | map key_replication_map; 36 | ServerThread wt; 37 | map> pending_requests; 38 | map> pending_gossip; 39 | map> key_access_tracker; 40 | set local_changeset; 41 | 42 | zmq::context_t context; 43 | SocketCache pushers = SocketCache(&context, ZMQ_PUSH); 44 | SerializerMap serializers; 45 | Serializer *lww_serializer; 46 | Serializer *set_serializer; 47 | Serializer *ordered_set_serializer; 48 | Serializer *sk_causal_serializer; 49 | Serializer *priority_serializer; 50 | MemoryLWWKVS *lww_kvs; 51 | MemorySetKVS *set_kvs; 52 | MemoryOrderedSetKVS *ordered_set_kvs; 53 | MemorySingleKeyCausalKVS *sk_causal_kvs; 54 | MemoryPriorityKVS *priority_kvs; 55 | 56 | ServerHandlerTest() { 57 | lww_kvs = new MemoryLWWKVS(); 58 | lww_serializer = new MemoryLWWSerializer(lww_kvs); 59 | 60 | set_kvs = new MemorySetKVS(); 61 | set_serializer = new MemorySetSerializer(set_kvs); 62 | 63 | ordered_set_kvs = new MemoryOrderedSetKVS(); 64 | ordered_set_serializer = new MemoryOrderedSetSerializer(ordered_set_kvs); 65 | 66 | sk_causal_kvs = new MemorySingleKeyCausalKVS(); 67 | sk_causal_serializer = new MemorySingleKeyCausalSerializer(sk_causal_kvs); 68 | 69 | priority_kvs = new MemoryPriorityKVS(); 70 | priority_serializer = new MemoryPrioritySerializer(priority_kvs); 71 | 72 | serializers[LatticeType::LWW] = lww_serializer; 73 | serializers[LatticeType::SET] = set_serializer; 74 | serializers[LatticeType::ORDERED_SET] = ordered_set_serializer; 75 | serializers[LatticeType::SINGLE_CAUSAL] = sk_causal_serializer; 76 | serializers[LatticeType::PRIORITY] = priority_serializer; 77 | 78 | wt = ServerThread(ip, ip, thread_id); 79 | global_hash_rings[Tier::MEMORY].insert(ip, ip, 0, thread_id); 80 | } 81 | 82 | virtual ~ServerHandlerTest() { 83 | delete lww_kvs; 84 | delete set_kvs; 85 | delete ordered_set_kvs; 86 | delete sk_causal_kvs; 87 | delete serializers[LatticeType::LWW]; 88 | delete serializers[LatticeType::SET]; 89 | delete serializers[LatticeType::ORDERED_SET]; 90 | delete serializers[LatticeType::SINGLE_CAUSAL]; 91 | delete serializers[LatticeType::PRIORITY]; 92 | } 93 | 94 | public: 95 | void SetUp() { 96 | // reset all global variables 97 | kDefaultLocalReplication = 1; 98 | kSelfTier = Tier::MEMORY; 99 | kThreadNum = 1; 100 | kSelfTierIdVector = {kSelfTier}; 101 | } 102 | 103 | void TearDown() { 104 | // clear all the logged messages after each test 105 | mock_zmq_util.sent_messages.clear(); 106 | } 107 | 108 | vector get_zmq_messages() { return mock_zmq_util.sent_messages; } 109 | 110 | // NOTE: Pass in an empty string to avoid putting something into the 111 | // serializer 112 | string get_key_request(Key key, string ip) { 113 | KeyRequest request; 114 | request.set_type(RequestType::GET); 115 | request.set_response_address(UserThread(ip, 0).response_connect_address()); 116 | request.set_request_id(kRequestId); 117 | 118 | KeyTuple *tp = request.add_tuples(); 119 | tp->set_key(std::move(key)); 120 | 121 | string request_str; 122 | request.SerializeToString(&request_str); 123 | 124 | return request_str; 125 | } 126 | 127 | string put_key_request(Key key, LatticeType lattice_type, string payload, 128 | string ip) { 129 | KeyRequest request; 130 | request.set_type(RequestType::PUT); 131 | request.set_response_address(UserThread(ip, 0).response_connect_address()); 132 | request.set_request_id(kRequestId); 133 | 134 | KeyTuple *tp = request.add_tuples(); 135 | tp->set_key(std::move(key)); 136 | tp->set_lattice_type(std::move(lattice_type)); 137 | tp->set_payload(std::move(payload)); 138 | 139 | string request_str; 140 | request.SerializeToString(&request_str); 141 | 142 | return request_str; 143 | } 144 | }; 145 | -------------------------------------------------------------------------------- /src/kvs/user_request_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void user_request_handler( 18 | unsigned &access_count, unsigned &seed, string &serialized, logger log, 19 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 20 | map> &pending_requests, 21 | map> &key_access_tracker, 22 | map &stored_key_map, 23 | map &key_replication_map, set &local_changeset, 24 | ServerThread &wt, SerializerMap &serializers, SocketCache &pushers) { 25 | KeyRequest request; 26 | request.ParseFromString(serialized); 27 | 28 | KeyResponse response; 29 | string response_id = request.request_id(); 30 | response.set_response_id(request.request_id()); 31 | 32 | response.set_type(request.type()); 33 | 34 | bool succeed; 35 | RequestType request_type = request.type(); 36 | string response_address = request.response_address(); 37 | 38 | for (const auto &tuple : request.tuples()) { 39 | // first check if the thread is responsible for the key 40 | Key key = tuple.key(); 41 | string payload = tuple.payload(); 42 | 43 | ServerThreadList threads = kHashRingUtil->get_responsible_threads( 44 | wt.replication_response_connect_address(), key, is_metadata(key), 45 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 46 | kSelfTierIdVector, succeed, seed); 47 | 48 | if (succeed) { 49 | if (std::find(threads.begin(), threads.end(), wt) == threads.end()) { 50 | if (is_metadata(key)) { 51 | // this means that this node is not responsible for this metadata key 52 | KeyTuple *tp = response.add_tuples(); 53 | 54 | tp->set_key(key); 55 | tp->set_lattice_type(tuple.lattice_type()); 56 | tp->set_error(AnnaError::WRONG_THREAD); 57 | } else { 58 | // if we don't know what threads are responsible, we issue a rep 59 | // factor request and make the request pending 60 | kHashRingUtil->issue_replication_factor_request( 61 | wt.replication_response_connect_address(), key, 62 | global_hash_rings[Tier::MEMORY], local_hash_rings[Tier::MEMORY], 63 | pushers, seed); 64 | 65 | pending_requests[key].push_back( 66 | PendingRequest(request_type, tuple.lattice_type(), payload, 67 | response_address, response_id)); 68 | } 69 | } else { // if we know the responsible threads, we process the request 70 | KeyTuple *tp = response.add_tuples(); 71 | tp->set_key(key); 72 | 73 | if (request_type == RequestType::GET) { 74 | if (stored_key_map.find(key) == stored_key_map.end() || 75 | stored_key_map[key].type_ == LatticeType::NONE) { 76 | 77 | tp->set_error(AnnaError::KEY_DNE); 78 | } else { 79 | auto res = process_get(key, serializers[stored_key_map[key].type_]); 80 | tp->set_lattice_type(stored_key_map[key].type_); 81 | tp->set_payload(res.first); 82 | tp->set_error(res.second); 83 | } 84 | } else if (request_type == RequestType::PUT) { 85 | if (tuple.lattice_type() == LatticeType::NONE) { 86 | log->error("PUT request missing lattice type."); 87 | } else if (stored_key_map.find(key) != stored_key_map.end() && 88 | stored_key_map[key].type_ != LatticeType::NONE && 89 | stored_key_map[key].type_ != tuple.lattice_type()) { 90 | log->error( 91 | "Lattice type mismatch for key {}: query is {} but we expect " 92 | "{}.", 93 | key, LatticeType_Name(tuple.lattice_type()), 94 | LatticeType_Name(stored_key_map[key].type_)); 95 | } else { 96 | process_put(key, tuple.lattice_type(), payload, 97 | serializers[tuple.lattice_type()], stored_key_map); 98 | 99 | local_changeset.insert(key); 100 | tp->set_lattice_type(tuple.lattice_type()); 101 | } 102 | } else { 103 | log->error("Unknown request type {} in user request handler.", 104 | request_type); 105 | } 106 | 107 | if (tuple.address_cache_size() > 0 && 108 | tuple.address_cache_size() != threads.size()) { 109 | tp->set_invalidate(true); 110 | } 111 | 112 | key_access_tracker[key].insert(std::chrono::system_clock::now()); 113 | access_count += 1; 114 | } 115 | } else { 116 | pending_requests[key].push_back( 117 | PendingRequest(request_type, tuple.lattice_type(), payload, 118 | response_address, response_id)); 119 | } 120 | } 121 | 122 | if (response.tuples_size() > 0 && request.response_address() != "") { 123 | string serialized_response; 124 | response.SerializeToString(&serialized_response); 125 | kZmqUtil->send_string(serialized_response, 126 | &pushers[request.response_address()]); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/kvs/node_join_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void node_join_handler(unsigned thread_id, unsigned &seed, Address public_ip, 18 | Address private_ip, logger log, string &serialized, 19 | GlobalRingMap &global_hash_rings, 20 | LocalRingMap &local_hash_rings, 21 | map &stored_key_map, 22 | map &key_replication_map, 23 | set &join_remove_set, SocketCache &pushers, 24 | ServerThread &wt, AddressKeysetMap &join_gossip_map, 25 | int self_join_count) { 26 | vector v; 27 | split(serialized, ':', v); 28 | 29 | Tier tier; 30 | Tier_Parse(v[0], &tier); 31 | Address new_server_public_ip = v[1]; 32 | Address new_server_private_ip = v[2]; 33 | int join_count = stoi(v[3]); 34 | 35 | // update global hash ring 36 | bool inserted = global_hash_rings[tier].insert( 37 | new_server_public_ip, new_server_private_ip, join_count, 0); 38 | 39 | if (inserted) { 40 | log->info( 41 | "Received a node join for tier {}. New node is {}. It's join counter " 42 | "is {}.", 43 | Tier_Name(tier), new_server_public_ip, join_count); 44 | 45 | // only thread 0 communicates with other nodes and receives join messages 46 | // and it communicates that information to non-0 threads on its own machine 47 | if (thread_id == 0) { 48 | // send my IP to the new server node 49 | kZmqUtil->send_string( 50 | Tier_Name(kSelfTier) + ":" + public_ip + ":" + private_ip + ":" + 51 | std::to_string(self_join_count), 52 | &pushers[ServerThread(new_server_public_ip, new_server_private_ip, 0) 53 | .node_join_connect_address()]); 54 | 55 | // gossip the new node address between server nodes to ensure consistency 56 | int index = 0; 57 | for (const auto &pair : global_hash_rings) { 58 | const GlobalHashRing hash_ring = pair.second; 59 | Tier tier = pair.first; 60 | 61 | for (const ServerThread &st : hash_ring.get_unique_servers()) { 62 | // if the node is not myself and not the newly joined node, send the 63 | // ip of the newly joined node in case of a race condition 64 | string server_ip = st.private_ip(); 65 | if (server_ip.compare(private_ip) != 0 && 66 | server_ip.compare(new_server_private_ip) != 0) { 67 | kZmqUtil->send_string(serialized, 68 | &pushers[st.node_join_connect_address()]); 69 | } 70 | } 71 | 72 | log->info("Hash ring for tier {} is size {}.", Tier_Name(tier), 73 | hash_ring.size()); 74 | } 75 | 76 | // tell all worker threads about the new node join 77 | for (unsigned tid = 1; tid < kThreadNum; tid++) { 78 | kZmqUtil->send_string(serialized, 79 | &pushers[ServerThread(public_ip, private_ip, tid) 80 | .node_join_connect_address()]); 81 | } 82 | } 83 | 84 | if (tier == kSelfTier) { 85 | bool succeed; 86 | 87 | for (const auto &key_pair : stored_key_map) { 88 | Key key = key_pair.first; 89 | ServerThreadList threads = kHashRingUtil->get_responsible_threads( 90 | wt.replication_response_connect_address(), key, is_metadata(key), 91 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 92 | kSelfTierIdVector, succeed, seed); 93 | 94 | if (succeed) { 95 | // there are two situations in which we gossip data to the joining 96 | // node: 97 | // 1) if the node is a new node and I am no longer responsible for 98 | // the key 99 | // 2) if the node is rejoining the cluster, and it is responsible for 100 | // the key 101 | // NOTE: This is currently inefficient because every server will 102 | // gossip the key currently -- we might be able to hack around the 103 | // has ring to do it more efficiently, but I'm leaving this here for 104 | // now 105 | bool rejoin_responsible = false; 106 | if (join_count > 0) { 107 | for (const ServerThread &thread : threads) { 108 | if (thread.private_ip().compare(new_server_private_ip) == 0) { 109 | join_gossip_map[thread.gossip_connect_address()].insert(key); 110 | } 111 | } 112 | } else if ((join_count == 0 && 113 | std::find(threads.begin(), threads.end(), wt) == 114 | threads.end())) { 115 | join_remove_set.insert(key); 116 | 117 | for (const ServerThread &thread : threads) { 118 | join_gossip_map[thread.gossip_connect_address()].insert(key); 119 | } 120 | } 121 | } else { 122 | log->error("Missing key replication factor in node join " 123 | "routine. This should never happen."); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /include/monitor/monitoring_utils.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_MONITOR_MONITORING_UTILS_HPP_ 16 | #define KVS_INCLUDE_MONITOR_MONITORING_UTILS_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | #include "metadata.pb.h" 20 | #include "requests.hpp" 21 | 22 | // define monitoring threshold (in second) 23 | const unsigned kMonitoringThreshold = 30; 24 | 25 | // define the grace period for triggering elasticity action (in second) 26 | const unsigned kGracePeriod = 120; 27 | 28 | // the default number of nodes to add concurrently for storage 29 | const unsigned kNodeAdditionBatchSize = 2; 30 | 31 | // define capacity for both tiers 32 | const double kMaxMemoryNodeConsumption = 0.6; 33 | const double kMinMemoryNodeConsumption = 0.3; 34 | const double kMaxEbsNodeConsumption = 0.75; 35 | const double kMinEbsNodeConsumption = 0.5; 36 | 37 | // define threshold for promotion/demotion 38 | const unsigned kKeyPromotionThreshold = 0; 39 | const unsigned kKeyDemotionThreshold = 1; 40 | 41 | // define minimum number of nodes for each tier 42 | const unsigned kMinMemoryTierSize = 1; 43 | const unsigned kMinEbsTierSize = 0; 44 | 45 | // value size in KB 46 | const unsigned kValueSize = 256; 47 | 48 | struct SummaryStats { 49 | void clear() { 50 | key_access_mean = 0; 51 | key_access_std = 0; 52 | total_memory_access = 0; 53 | total_ebs_access = 0; 54 | total_memory_consumption = 0; 55 | total_ebs_consumption = 0; 56 | max_memory_consumption_percentage = 0; 57 | max_ebs_consumption_percentage = 0; 58 | avg_memory_consumption_percentage = 0; 59 | avg_ebs_consumption_percentage = 0; 60 | required_memory_node = 0; 61 | required_ebs_node = 0; 62 | max_memory_occupancy = 0; 63 | min_memory_occupancy = 1; 64 | avg_memory_occupancy = 0; 65 | max_ebs_occupancy = 0; 66 | min_ebs_occupancy = 1; 67 | avg_ebs_occupancy = 0; 68 | min_occupancy_memory_public_ip = Address(); 69 | min_occupancy_memory_private_ip = Address(); 70 | avg_latency = 0; 71 | total_throughput = 0; 72 | } 73 | 74 | SummaryStats() { clear(); } 75 | double key_access_mean; 76 | double key_access_std; 77 | unsigned total_memory_access; 78 | unsigned total_ebs_access; 79 | unsigned long long total_memory_consumption; 80 | unsigned long long total_ebs_consumption; 81 | double max_memory_consumption_percentage; 82 | double max_ebs_consumption_percentage; 83 | double avg_memory_consumption_percentage; 84 | double avg_ebs_consumption_percentage; 85 | unsigned required_memory_node; 86 | unsigned required_ebs_node; 87 | double max_memory_occupancy; 88 | double min_memory_occupancy; 89 | double avg_memory_occupancy; 90 | double max_ebs_occupancy; 91 | double min_ebs_occupancy; 92 | double avg_ebs_occupancy; 93 | Address min_occupancy_memory_public_ip; 94 | Address min_occupancy_memory_private_ip; 95 | double avg_latency; 96 | double total_throughput; 97 | }; 98 | 99 | void collect_internal_stats( 100 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 101 | SocketCache &pushers, MonitoringThread &mt, zmq::socket_t &response_puller, 102 | logger log, unsigned &rid, 103 | map> &key_access_frequency, 104 | map &key_size, StorageStats &memory_storage, 105 | StorageStats &ebs_storage, OccupancyStats &memory_occupancy, 106 | OccupancyStats &ebs_occupancy, AccessStats &memory_access, 107 | AccessStats &ebs_access); 108 | 109 | void compute_summary_stats( 110 | map> &key_access_frequency, 111 | StorageStats &memory_storage, StorageStats &ebs_storage, 112 | OccupancyStats &memory_occupancy, OccupancyStats &ebs_occupancy, 113 | AccessStats &memory_access, AccessStats &ebs_access, 114 | map &key_access_summary, SummaryStats &ss, logger log, 115 | unsigned &server_monitoring_epoch); 116 | 117 | void collect_external_stats(map &user_latency, 118 | map &user_throughput, 119 | SummaryStats &ss, logger log); 120 | 121 | KeyReplication create_new_replication_vector(unsigned gm, unsigned ge, 122 | unsigned lm, unsigned le); 123 | 124 | void prepare_replication_factor_update( 125 | const Key &key, 126 | map &replication_factor_map, 127 | Address server_address, map &key_replication_map); 128 | 129 | void change_replication_factor(map &requests, 130 | GlobalRingMap &global_hash_rings, 131 | LocalRingMap &local_hash_rings, 132 | vector
&routing_ips, 133 | map &key_replication_map, 134 | SocketCache &pushers, MonitoringThread &mt, 135 | zmq::socket_t &response_puller, logger log, 136 | unsigned &rid); 137 | 138 | void add_node(logger log, string tier, unsigned number, unsigned &adding, 139 | SocketCache &pushers, const Address &management_ip); 140 | 141 | void remove_node(logger log, ServerThread &node, string tier, 142 | bool &removing_flag, SocketCache &pushers, 143 | map &departing_node_map, 144 | MonitoringThread &mt); 145 | 146 | #endif // KVS_INCLUDE_MONITOR_MONITORING_UTILS_HPP_ 147 | -------------------------------------------------------------------------------- /include/kvs/kvs_handlers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef INCLUDE_KVS_KVS_HANDLERS_HPP_ 16 | #define INCLUDE_KVS_KVS_HANDLERS_HPP_ 17 | 18 | #include "hash_ring.hpp" 19 | #include "metadata.pb.h" 20 | #include "requests.hpp" 21 | #include "server_utils.hpp" 22 | 23 | void node_join_handler(unsigned thread_id, unsigned &seed, Address public_ip, 24 | Address private_ip, logger log, string &serialized, 25 | GlobalRingMap &global_hash_rings, 26 | LocalRingMap &local_hash_rings, 27 | map &stored_key_map, 28 | map &key_replication_map, 29 | set &join_remove_set, SocketCache &pushers, 30 | ServerThread &wt, AddressKeysetMap &join_gossip_map, 31 | int self_join_count); 32 | 33 | void node_depart_handler(unsigned thread_id, Address public_ip, 34 | Address private_ip, GlobalRingMap &global_hash_rings, 35 | logger log, string &serialized, SocketCache &pushers); 36 | 37 | void self_depart_handler(unsigned thread_id, unsigned &seed, Address public_ip, 38 | Address private_ip, logger log, string &serialized, 39 | GlobalRingMap &global_hash_rings, 40 | LocalRingMap &local_hash_rings, 41 | map &stored_key_map, 42 | map &key_replication_map, 43 | vector
&routing_ips, 44 | vector
&monitoring_ips, ServerThread &wt, 45 | SocketCache &pushers, SerializerMap &serializers); 46 | 47 | void user_request_handler( 48 | unsigned &access_count, unsigned &seed, string &serialized, logger log, 49 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 50 | map> &pending_requests, 51 | map> &key_access_tracker, 52 | map &stored_key_map, 53 | map &key_replication_map, set &local_changeset, 54 | ServerThread &wt, SerializerMap &serializers, SocketCache &pushers); 55 | 56 | void gossip_handler(unsigned &seed, string &serialized, 57 | GlobalRingMap &global_hash_rings, 58 | LocalRingMap &local_hash_rings, 59 | map> &pending_gossip, 60 | map &stored_key_map, 61 | map &key_replication_map, 62 | ServerThread &wt, SerializerMap &serializers, 63 | SocketCache &pushers, logger log); 64 | 65 | void replication_response_handler( 66 | unsigned &seed, unsigned &access_count, logger log, string &serialized, 67 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 68 | map> &pending_requests, 69 | map> &pending_gossip, 70 | map> &key_access_tracker, 71 | map &stored_key_map, 72 | map &key_replication_map, set &local_changeset, 73 | ServerThread &wt, SerializerMap &serializers, SocketCache &pushers); 74 | 75 | void replication_change_handler( 76 | Address public_ip, Address private_ip, unsigned thread_id, unsigned &seed, 77 | logger log, string &serialized, GlobalRingMap &global_hash_rings, 78 | LocalRingMap &local_hash_rings, map &stored_key_map, 79 | map &key_replication_map, set &local_changeset, 80 | ServerThread &wt, SerializerMap &serializers, SocketCache &pushers); 81 | 82 | // Postcondition: 83 | // cache_ip_to_keys, key_to_cache_ips are both updated 84 | // with the IPs and their fresh list of repsonsible keys 85 | // in the serialized response. 86 | void cache_ip_response_handler(string &serialized, 87 | map> &cache_ip_to_keys, 88 | map> &key_to_cache_ips); 89 | 90 | void management_node_response_handler(string &serialized, 91 | set
&extant_caches, 92 | map> &cache_ip_to_keys, 93 | map> &key_to_cache_ips, 94 | GlobalRingMap &global_hash_rings, 95 | LocalRingMap &local_hash_rings, 96 | SocketCache &pushers, ServerThread &wt, 97 | unsigned &rid); 98 | 99 | void send_gossip(AddressKeysetMap &addr_keyset_map, SocketCache &pushers, 100 | SerializerMap &serializers, 101 | map &stored_key_map); 102 | 103 | std::pair process_get(const Key &key, 104 | Serializer *serializer); 105 | 106 | void process_put(const Key &key, LatticeType lattice_type, 107 | const string &payload, Serializer *serializer, 108 | map &stored_key_map); 109 | 110 | bool is_primary_replica(const Key &key, 111 | map &key_replication_map, 112 | GlobalRingMap &global_hash_rings, 113 | LocalRingMap &local_hash_rings, ServerThread &st); 114 | 115 | #endif // INCLUDE_KVS_KVS_HANDLERS_HPP_ 116 | -------------------------------------------------------------------------------- /include/hash_ring.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef INCLUDE_HASH_RING_HPP_ 16 | #define INCLUDE_HASH_RING_HPP_ 17 | 18 | #include "common.hpp" 19 | #include "consistent_hash_map.hpp" 20 | #include "hashers.hpp" 21 | #include "kvs_common.hpp" 22 | #include "metadata.hpp" 23 | 24 | template 25 | class HashRing : public ConsistentHashMap { 26 | public: 27 | HashRing() {} 28 | 29 | ~HashRing() {} 30 | 31 | public: 32 | ServerThreadSet get_unique_servers() const { return unique_servers; } 33 | 34 | bool insert(Address public_ip, Address private_ip, int join_count, 35 | unsigned tid) { 36 | ServerThread new_thread = ServerThread(public_ip, private_ip, tid, 0); 37 | 38 | if (unique_servers.find(new_thread) != unique_servers.end()) { 39 | // if we already have the server, only return true if it's rejoining 40 | if (server_join_count[private_ip] < join_count) { 41 | server_join_count[private_ip] = join_count; 42 | return true; 43 | } 44 | 45 | return false; 46 | } else { // otherwise, insert it into the hash ring for the first time 47 | unique_servers.insert(new_thread); 48 | server_join_count[private_ip] = join_count; 49 | 50 | for (unsigned virtual_num = 0; virtual_num < kVirtualThreadNum; 51 | virtual_num++) { 52 | ServerThread st = ServerThread(public_ip, private_ip, tid, virtual_num); 53 | ConsistentHashMap::insert(st); 54 | } 55 | 56 | return true; 57 | } 58 | } 59 | 60 | void remove(Address public_ip, Address private_ip, unsigned tid) { 61 | for (unsigned virtual_num = 0; virtual_num < kVirtualThreadNum; 62 | virtual_num++) { 63 | ServerThread st = ServerThread(public_ip, private_ip, tid, virtual_num); 64 | ConsistentHashMap::erase(st); 65 | } 66 | 67 | unique_servers.erase(ServerThread(public_ip, private_ip, tid, 0)); 68 | server_join_count.erase(private_ip); 69 | } 70 | 71 | private: 72 | ServerThreadSet unique_servers; 73 | map server_join_count; 74 | }; 75 | 76 | // These typedefs are for brevity, and they were introduced after we removed 77 | // TierIds and just used the Tier enum instead -- passing around hmap every time was tedious. 79 | typedef HashRing GlobalHashRing; 80 | typedef HashRing LocalHashRing; 81 | typedef hmap GlobalRingMap; 82 | typedef hmap LocalRingMap; 83 | 84 | class HashRingUtilInterface { 85 | public: 86 | virtual ServerThreadList get_responsible_threads( 87 | Address respond_address, const Key &key, bool metadata, 88 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 89 | map &key_replication_map, SocketCache &pushers, 90 | const vector &tiers, bool &succeed, unsigned &seed) = 0; 91 | 92 | ServerThreadList 93 | get_responsible_threads_metadata(const Key &key, 94 | GlobalHashRing &global_memory_hash_ring, 95 | LocalHashRing &local_memory_hash_ring); 96 | 97 | void issue_replication_factor_request(const Address &respond_address, 98 | const Key &key, 99 | GlobalHashRing &global_memory_hash_ring, 100 | LocalHashRing &local_memory_hash_ring, 101 | SocketCache &pushers, unsigned &seed); 102 | }; 103 | 104 | class HashRingUtil : public HashRingUtilInterface { 105 | public: 106 | virtual ServerThreadList get_responsible_threads( 107 | Address respond_address, const Key &key, bool metadata, 108 | GlobalRingMap &global_hash_rings, LocalRingMap &local_hash_rings, 109 | map &key_replication_map, SocketCache &pushers, 110 | const vector &tiers, bool &succeed, unsigned &seed); 111 | }; 112 | 113 | ServerThreadList responsible_global(const Key &key, unsigned global_rep, 114 | GlobalHashRing &global_hash_ring); 115 | 116 | set responsible_local(const Key &key, unsigned local_rep, 117 | LocalHashRing &local_hash_ring); 118 | 119 | Address prepare_metadata_request(const Key &key, 120 | GlobalHashRing &global_memory_hash_ring, 121 | LocalHashRing &local_memory_hash_ring, 122 | map &addr_request_map, 123 | Address response_address, unsigned &rid, 124 | RequestType type); 125 | 126 | void prepare_metadata_get_request(const Key &key, 127 | GlobalHashRing &global_memory_hash_ring, 128 | LocalHashRing &local_memory_hash_ring, 129 | map &addr_request_map, 130 | Address response_address, unsigned &rid); 131 | 132 | void prepare_metadata_put_request(const Key &key, const string &value, 133 | GlobalHashRing &global_memory_hash_ring, 134 | LocalHashRing &local_memory_hash_ring, 135 | map &addr_request_map, 136 | Address response_address, unsigned &rid); 137 | 138 | extern HashRingUtilInterface *kHashRingUtil; 139 | 140 | #endif // INCLUDE_HASH_RING_HPP_ 141 | -------------------------------------------------------------------------------- /src/kvs/replication_change_handler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "kvs/kvs_handlers.hpp" 16 | 17 | void replication_change_handler( 18 | Address public_ip, Address private_ip, unsigned thread_id, unsigned &seed, 19 | logger log, string &serialized, GlobalRingMap &global_hash_rings, 20 | LocalRingMap &local_hash_rings, map &stored_key_map, 21 | map &key_replication_map, set &local_changeset, 22 | ServerThread &wt, SerializerMap &serializers, SocketCache &pushers) { 23 | log->info("Received a replication factor change."); 24 | if (thread_id == 0) { 25 | // tell all worker threads about the replication factor change 26 | for (unsigned tid = 1; tid < kThreadNum; tid++) { 27 | kZmqUtil->send_string( 28 | serialized, &pushers[ServerThread(public_ip, private_ip, tid) 29 | .replication_change_connect_address()]); 30 | } 31 | } 32 | 33 | ReplicationFactorUpdate rep_change; 34 | rep_change.ParseFromString(serialized); 35 | 36 | AddressKeysetMap addr_keyset_map; 37 | set remove_set; 38 | 39 | // for every key, update the replication factor and check if the node is still 40 | // responsible for the key 41 | bool succeed; 42 | 43 | for (const ReplicationFactor &key_rep : rep_change.updates()) { 44 | Key key = key_rep.key(); 45 | // if this thread has the key stored before the change 46 | if (stored_key_map.find(key) != stored_key_map.end()) { 47 | ServerThreadList orig_threads = kHashRingUtil->get_responsible_threads( 48 | wt.replication_response_connect_address(), key, is_metadata(key), 49 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 50 | kAllTiers, succeed, seed); 51 | 52 | if (succeed) { 53 | // update the replication factor 54 | bool decrement = false; 55 | 56 | for (const auto &global : key_rep.global()) { 57 | if (global.value() < 58 | key_replication_map[key].global_replication_[global.tier()]) { 59 | decrement = true; 60 | } 61 | 62 | key_replication_map[key].global_replication_[global.tier()] = 63 | global.value(); 64 | } 65 | 66 | for (const auto &local : key_rep.local()) { 67 | if (local.value() < 68 | key_replication_map[key].local_replication_[local.tier()]) { 69 | decrement = true; 70 | } 71 | 72 | key_replication_map[key].local_replication_[local.tier()] = 73 | local.value(); 74 | } 75 | 76 | ServerThreadList threads = kHashRingUtil->get_responsible_threads( 77 | wt.replication_response_connect_address(), key, is_metadata(key), 78 | global_hash_rings, local_hash_rings, key_replication_map, pushers, 79 | kAllTiers, succeed, seed); 80 | 81 | if (succeed) { 82 | if (std::find(threads.begin(), threads.end(), wt) == 83 | threads.end()) { // this thread is no longer 84 | // responsible for this key 85 | remove_set.insert(key); 86 | 87 | // add all the new threads that this key should be sent to 88 | for (const ServerThread &thread : threads) { 89 | addr_keyset_map[thread.gossip_connect_address()].insert(key); 90 | } 91 | } 92 | 93 | // decrement represents whether the total global or local rep factor 94 | // has been reduced; if that's not the case, and I am the "first" 95 | // thread responsible for this key, then I gossip it to the new 96 | // threads that are responsible for it 97 | if (!decrement && orig_threads.begin()->id() == wt.id()) { 98 | std::unordered_set new_threads; 99 | 100 | for (const ServerThread &thread : threads) { 101 | if (std::find(orig_threads.begin(), orig_threads.end(), thread) == 102 | orig_threads.end()) { 103 | new_threads.insert(thread); 104 | } 105 | } 106 | 107 | for (const ServerThread &thread : new_threads) { 108 | addr_keyset_map[thread.gossip_connect_address()].insert(key); 109 | } 110 | } 111 | } else { 112 | log->error( 113 | "Missing key replication factor in rep factor change routine."); 114 | } 115 | } else { 116 | log->error( 117 | "Missing key replication factor in rep factor change routine."); 118 | 119 | // just update the replication factor 120 | for (const auto &global : key_rep.global()) { 121 | key_replication_map[key].global_replication_[global.tier()] = 122 | global.value(); 123 | } 124 | 125 | for (const auto &local : key_rep.local()) { 126 | key_replication_map[key].local_replication_[local.tier()] = 127 | local.value(); 128 | } 129 | } 130 | } else { 131 | // just update the replication factor 132 | for (const auto &global : key_rep.global()) { 133 | key_replication_map[key].global_replication_[global.tier()] = 134 | global.value(); 135 | } 136 | 137 | for (const auto &local : key_rep.local()) { 138 | key_replication_map[key].local_replication_[local.tier()] = 139 | local.value(); 140 | } 141 | } 142 | } 143 | 144 | send_gossip(addr_keyset_map, pushers, serializers, stored_key_map); 145 | 146 | // remove keys 147 | for (const string &key : remove_set) { 148 | serializers[stored_key_map[key].type_]->remove(key); 149 | stored_key_map.erase(key); 150 | local_changeset.erase(key); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /include/metadata.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 U.C. Berkeley RISE Lab 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef KVS_INCLUDE_METADATA_HPP_ 16 | #define KVS_INCLUDE_METADATA_HPP_ 17 | 18 | #include "metadata.pb.h" 19 | #include "threads.hpp" 20 | 21 | const string kMetadataTypeReplication = "replication"; 22 | 23 | struct TierEnumHash { 24 | template std::size_t operator()(T t) const { 25 | return static_cast(t); 26 | } 27 | }; 28 | 29 | struct KeyReplication { 30 | hmap global_replication_; 31 | hmap local_replication_; 32 | }; 33 | 34 | struct KeyProperty { 35 | unsigned size_; 36 | LatticeType type_; 37 | }; 38 | 39 | inline bool operator==(const KeyReplication &lhs, const KeyReplication &rhs) { 40 | for (const auto &pair : lhs.global_replication_) { 41 | Tier id = pair.first; 42 | 43 | if (rhs.global_replication_.find(id) == rhs.global_replication_.end()) { 44 | return false; 45 | } 46 | 47 | if (pair.second != rhs.global_replication_.at(id)) { 48 | return false; 49 | } 50 | } 51 | 52 | for (const auto &pair : lhs.local_replication_) { 53 | Tier id = pair.first; 54 | 55 | if (rhs.local_replication_.find(id) == rhs.local_replication_.end()) { 56 | return false; 57 | } 58 | 59 | if (pair.second != rhs.local_replication_.at(id)) { 60 | return false; 61 | } 62 | } 63 | 64 | return true; 65 | } 66 | 67 | // per-tier metadata 68 | struct TierMetadata { 69 | TierMetadata() 70 | : id_(Tier::MEMORY), thread_number_(1), default_replication_(1), 71 | node_capacity_(0) {} 72 | 73 | TierMetadata(Tier id, unsigned t_num, unsigned rep, 74 | unsigned long long node_capacity) 75 | : id_(id), thread_number_(t_num), default_replication_(rep), 76 | node_capacity_(node_capacity) {} 77 | 78 | Tier id_; 79 | 80 | unsigned thread_number_; 81 | 82 | unsigned default_replication_; 83 | 84 | unsigned long long node_capacity_; 85 | }; 86 | 87 | inline bool is_metadata(Key key) { 88 | vector v; 89 | split(key, '|', v); 90 | 91 | if (v[0] == kMetadataIdentifier) { 92 | return true; 93 | } else { 94 | return false; 95 | } 96 | } 97 | 98 | // NOTE: This needs to be here because it needs the definition of TierMetadata 99 | extern hmap kTierMetadata; 100 | 101 | enum MetadataType { replication, server_stats, key_access, key_size }; 102 | 103 | inline Key get_metadata_key(const ServerThread &st, Tier tier_id, 104 | unsigned thread_num, MetadataType type) { 105 | string metadata_type; 106 | 107 | switch (type) { 108 | case MetadataType::server_stats: 109 | metadata_type = "stats"; 110 | break; 111 | case MetadataType::key_access: 112 | metadata_type = "access"; 113 | break; 114 | case MetadataType::key_size: 115 | metadata_type = "size"; 116 | break; 117 | default: 118 | return ""; // this should never happen; see note below about 119 | // MetadataType::replication 120 | } 121 | 122 | return kMetadataIdentifier + kMetadataDelimiter + metadata_type + 123 | kMetadataDelimiter + st.public_ip() + kMetadataDelimiter + 124 | st.private_ip() + kMetadataDelimiter + std::to_string(thread_num) + 125 | kMetadataDelimiter + Tier_Name(tier_id); 126 | } 127 | 128 | // This version of the function should only be called with 129 | // certain types of MetadataType, 130 | // so if it's called with something else, we return 131 | // an empty string. 132 | // TODO: There should probably be a less silent error check. 133 | inline Key get_metadata_key(string data_key, MetadataType type) { 134 | if (type == MetadataType::replication) { 135 | return kMetadataIdentifier + kMetadataDelimiter + kMetadataTypeReplication + 136 | kMetadataDelimiter + data_key; 137 | } 138 | return ""; 139 | } 140 | 141 | // Inverse of get_metadata_key, returning just the key itself. 142 | // Precondition: metadata_key is actually a metadata key (output of 143 | // get_metadata_key). 144 | // TODO: same problem as get_metadata_key with the metadata types. 145 | inline Key get_key_from_metadata(Key metadata_key) { 146 | string::size_type n_id; 147 | string::size_type n_type; 148 | // Find the first delimiter; this skips over the metadata identifier. 149 | n_id = metadata_key.find(kMetadataDelimiter); 150 | // Find the second delimiter; this skips over the metadata type. 151 | n_type = metadata_key.find(kMetadataDelimiter, n_id + 1); 152 | string metadata_type = metadata_key.substr(n_id + 1, n_type - (n_id + 1)); 153 | if (metadata_type == kMetadataTypeReplication) { 154 | return metadata_key.substr(n_type + 1); 155 | } 156 | 157 | return ""; 158 | } 159 | 160 | // Precondition: key is from the non-data-key version of get_metadata_key. 161 | inline vector split_metadata_key(Key key) { 162 | vector tokens; 163 | split(key, kMetadataDelimiterChar, tokens); 164 | 165 | return tokens; 166 | } 167 | 168 | inline void warmup_key_replication_map_to_defaults( 169 | map &key_replication_map, 170 | unsigned &kDefaultGlobalMemoryReplication, 171 | unsigned &kDefaultGlobalEbsReplication, 172 | unsigned &kDefaultLocalReplication) { 173 | for (unsigned i = 1; i <= 1000000; i++) { 174 | // key is 8 bytes 175 | Key key = string(8 - std::to_string(i).length(), '0') + std::to_string(i); 176 | key_replication_map[key].global_replication_[Tier::MEMORY] = 177 | kDefaultGlobalMemoryReplication; 178 | key_replication_map[key].global_replication_[Tier::DISK] = 179 | kDefaultGlobalEbsReplication; 180 | key_replication_map[key].local_replication_[Tier::MEMORY] = 181 | kDefaultLocalReplication; 182 | key_replication_map[key].local_replication_[Tier::DISK] = 183 | kDefaultLocalReplication; 184 | } 185 | } 186 | 187 | inline void init_replication(map &key_replication_map, 188 | const Key &key) { 189 | for (const Tier &tier : kAllTiers) { 190 | key_replication_map[key].global_replication_[tier] = 191 | kTierMetadata[tier].default_replication_; 192 | key_replication_map[key].local_replication_[tier] = 193 | kDefaultLocalReplication; 194 | } 195 | } 196 | 197 | #endif // KVS_INCLUDE_METADATA_HPP_ 198 | --------------------------------------------------------------------------------