diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a5aa4fe7..50b20d34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,28 +1,26 @@ cmake_minimum_required (VERSION 3.11) +add_flags("-Wall -Wextra -Werror -Wpedantic") +include_directories (BEFORE include/) +include_directories (AFTER .) + find_package(Threads QUIET REQUIRED) -find_package(sisl QUIET REQUIRED) find_package(homestore QUIET REQUIRED) -link_directories(${spdk_LIB_DIRS} ${dpdk_LIB_DIRS}) - -set (COMMON_DEPS - homestore::homestore - sisl::sisl - ) +list(APPEND COMMON_DEPS homestore::homestore) -set(COMMON_TEST_DEPS - ${COMMON_DEPS} - GTest::gmock - ${spdk_LIBRARY_LIST} - ${dpdk_LIBRARY_LIST} - ) -add_flags("-Wall -Wextra -Werror -Wpedantic") - -include_directories (BEFORE .) -include_directories (BEFORE include/) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/lib) +if(BUILD_TESTING) +# This is a work-around for not being able to specify the link +# order in a conan recipe. We link these explicitly and thus +# need to specify the LINK path. They should only be needed +# to build a DSO (test executable) however. +link_directories(${spdk_LIB_DIRS} ${dpdk_LIB_DIRS}) +list(APPEND COMMON_TEST_DEPS + ${COMMON_DEPS} + GTest::gmock + ${spdk_LIBRARY_LIST} + ${dpdk_LIBRARY_LIST} +) +endif() add_subdirectory(lib) -#add_subdirectory(mocks) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 4789cfaa..17136e44 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -4,14 +4,14 @@ add_flags("-Wno-unused-parameter") add_library(${PROJECT_NAME}_core OBJECT) target_sources(${PROJECT_NAME}_core PRIVATE - homeobject_impl.cpp - blob_manager.cpp - shard_manager.cpp - pg_manager.cpp - ) + homeobject_impl.cpp + blob_manager.cpp + shard_manager.cpp + pg_manager.cpp +) target_link_libraries(${PROJECT_NAME}_core - ${COMMON_DEPS} - ) + ${COMMON_DEPS} +) if(BUILD_TESTING) add_subdirectory(tests) diff --git a/src/lib/homestore_backend/CMakeLists.txt b/src/lib/homestore_backend/CMakeLists.txt index 3908b87d..f4847296 100644 --- a/src/lib/homestore_backend/CMakeLists.txt +++ b/src/lib/homestore_backend/CMakeLists.txt @@ -2,31 +2,31 @@ cmake_minimum_required (VERSION 3.11) add_library ("${PROJECT_NAME}_homestore") target_sources("${PROJECT_NAME}_homestore" PRIVATE - hs_homeobject.cpp - hs_blob_manager.cpp - hs_shard_manager.cpp - hs_pg_manager.cpp - index_kv.cpp - heap_chunk_selector.cpp - replication_state_machine.cpp - $ - ) + hs_homeobject.cpp + hs_blob_manager.cpp + hs_shard_manager.cpp + hs_pg_manager.cpp + index_kv.cpp + heap_chunk_selector.cpp + replication_state_machine.cpp +) target_link_libraries("${PROJECT_NAME}_homestore" - ${COMMON_DEPS} - ) + ${COMMON_DEPS} +) if(BUILD_TESTING) - add_subdirectory(tests) +add_subdirectory(tests) - add_executable (homestore_test) - target_sources(homestore_test PRIVATE - $ - $ - ) - target_link_libraries(homestore_test - homeobject_homestore - ${COMMON_TEST_DEPS} - ) - add_test(NAME HomestoreTest COMMAND homestore_test -csv error --executor immediate) - set_property(TEST HomestoreTest PROPERTY RUN_SERIAL 1) +add_executable (homestore_test) +target_sources(homestore_test PRIVATE + $ + $ + $ +) +target_link_libraries(homestore_test + homeobject_homestore + ${COMMON_TEST_DEPS} +) +add_test(NAME HomestoreTest COMMAND homestore_test -csv error --executor immediate) +set_property(TEST HomestoreTest PROPERTY RUN_SERIAL 1) endif() diff --git a/src/lib/memory_backend/CMakeLists.txt b/src/lib/memory_backend/CMakeLists.txt index 01542dc6..60135b7c 100644 --- a/src/lib/memory_backend/CMakeLists.txt +++ b/src/lib/memory_backend/CMakeLists.txt @@ -3,22 +3,24 @@ cmake_minimum_required (VERSION 3.11) add_library ("${PROJECT_NAME}_memory") target_sources("${PROJECT_NAME}_memory" PRIVATE mem_homeobject.cpp - mem_blob_manager.cpp - mem_shard_manager.cpp + mem_blob_manager.cpp + mem_shard_manager.cpp mem_pg_manager.cpp - $ ) target_link_libraries("${PROJECT_NAME}_memory" - ${COMMON_DEPS} + ${COMMON_DEPS} ) if(BUILD_TESTING) add_executable (memory_test) -target_sources(memory_test PRIVATE $) +target_sources(memory_test PRIVATE + $ + $ +) target_link_libraries(memory_test homeobject_memory ${COMMON_TEST_DEPS} - ) +) add_test(NAME MemoryTestCPU COMMAND memory_test -csv error --executor cpu --num_iters 20000) add_test(NAME MemoryTestIO COMMAND memory_test -csv error --executor io --num_iters 20000) endif() diff --git a/src/lib/tests/CMakeLists.txt b/src/lib/tests/CMakeLists.txt index bc14b6e8..05ded4b7 100644 --- a/src/lib/tests/CMakeLists.txt +++ b/src/lib/tests/CMakeLists.txt @@ -6,7 +6,7 @@ target_sources(test_fixture PRIVATE ShardManagerTest.cpp PGManagerTest.cpp fixture_app.cpp - ) +) target_link_libraries(test_fixture ${COMMON_TEST_DEPS} - ) +) diff --git a/src/mocks/CMakeLists.txt b/src/mocks/CMakeLists.txt deleted file mode 100644 index 768990d7..00000000 --- a/src/mocks/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required (VERSION 3.11) - -add_library (home_replication_mock) -target_sources(home_replication_mock PRIVATE - repl_service.cpp - ) -target_link_libraries(home_replication_mock - ${COMMON_DEPS} - ) - -add_subdirectory(tests) diff --git a/src/mocks/mock_replica_set.hpp b/src/mocks/mock_replica_set.hpp deleted file mode 100644 index e8acd3dc..00000000 --- a/src/mocks/mock_replica_set.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "repl_service.h" - -namespace home_replication { - -class MockReplicaSet : public ReplicaSet { - std::string _g_id; - bool _is_leader{true}; - uint32_t _current_lsn{0}; - std::shared_ptr< ReplicaSetListener > _listener; - -public: - MockReplicaSet(std::string const& group_id, std::set< std::string, std::less<> >&& members) : - _g_id(group_id), _members(std::move(members)) {} - ~MockReplicaSet() override = default; - - std::set< std::string, std::less<> > _members; - - void write(const sisl::blob& head, const sisl::blob& key, const sisl::sg_list&, void* ctx) override { - // This is a mock replica set without really replication behaviors, so it just call - // ReplicaSetListener::on_pre_commit() and ReplicaSetListener::on_commit(); - if (_listener) { - _listener->on_pre_commit(_current_lsn, head, key, ctx); - home_replication::pba_list_t pba; - _listener->on_commit(_current_lsn, head, key, pba, ctx); - } - ++_current_lsn; - } - void transfer_pba_ownership(int64_t, const pba_list_t&) override {} - void send_data_service_response(sisl::io_blob_list_t const&, - boost::intrusive_ptr< sisl::GenericRpcData >&) override {} - void append_entry(nuraft::buffer const&) override {} - void set_leader() { _is_leader = true; } - void set_follower() { _is_leader = false; } - bool is_leader() const override { return _is_leader; } - std::string group_id() const override { return _g_id; } - void set_listener(std::shared_ptr< ReplicaSetListener > listener) { _listener = listener; } - - /// nuraft_mesg::mesg_state_mgr overrides - uint32_t get_logstore_id() const override { return 0u; } - std::shared_ptr< nuraft::state_machine > get_state_machine() { return nullptr; } - void permanent_destroy() override {} - void leave() override {} - /// - - /// nuraft::state_mgr overrides - std::shared_ptr< nuraft::cluster_config > load_config() override { return nullptr; } - void save_config(const nuraft::cluster_config&) override {} - void save_state(const nuraft::srv_state&) override {} - std::shared_ptr< nuraft::srv_state > read_state() override { return nullptr; } - std::shared_ptr< nuraft::log_store > load_log_store() override { return nullptr; } - int32_t server_id() override { return 0; } - void system_exit(const int) override {} - /// -}; - -} // namespace home_replication diff --git a/src/mocks/repl_decls.h b/src/mocks/repl_decls.h deleted file mode 100644 index cfb65a06..00000000 --- a/src/mocks/repl_decls.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include - -#include -#include -#include -#include -#include - -SISL_LOGGING_DECL(home_replication) - -#define HOMEREPL_LOG_MODS grpc_server, HOMESTORE_LOG_MODS, nuraft_mesg, nuraft, home_replication - -namespace home_replication { -using pba_t = uint64_t; -using pba_list_t = folly::small_vector< pba_t, 4 >; - -// Fully qualified domain pba, unique pba id across replica set -struct fully_qualified_pba { - fully_qualified_pba(uint32_t s, pba_t p, uint32_t sz) : server_id{s}, pba{p}, size{sz} {} - uint32_t server_id; - pba_t pba; - uint32_t size; // corresponding size of this pba; - std::string to_key_string() const { return fmt::format("{}_{}", std::to_string(server_id), std::to_string(pba)); } -}; -using fq_pba_list_t = folly::small_vector< fully_qualified_pba, 4 >; - -// data service api names -static std::string const SEND_DATA{"send_data"}; -static std::string const FETCH_DATA{"fetch_data"}; - -} // namespace home_replication diff --git a/src/mocks/repl_service.cpp b/src/mocks/repl_service.cpp deleted file mode 100644 index 905ad0f2..00000000 --- a/src/mocks/repl_service.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "mock_replica_set.hpp" -#include "repl_service.h" - -#include - -namespace home_replication { - -class MockReplicationService : public ReplicationService { - mutable std::shared_mutex _map_lock; - std::map< std::string, rs_ptr_t > _set_map; - -public: - explicit MockReplicationService() {} - ~MockReplicationService() override = default; - - folly::SemiFuture< set_var > create_replica_set(std::string const& group_id, - std::set< std::string, std::less<> >&& members) override; - set_var get_replica_set(std::string const& group_id) const override; - folly::SemiFuture< ReplServiceError > replace_member(std::string const& group_id, std::string const& member_out, - std::string const& member_in) const override; - void iterate_replica_sets(std::function< void(const rs_ptr_t&) > cb) const override; -}; - -folly::SemiFuture< ReplicationService::set_var > -MockReplicationService::create_replica_set(std::string const& group_id, - std::set< std::string, std::less<> >&& members) { - if (1 > members.size()) return folly::makeSemiFuture< set_var >(ReplServiceError::BAD_REQUEST); - - auto lk = std::scoped_lock(_map_lock); - if (auto [it, happened] = _set_map.try_emplace(group_id, nullptr); _set_map.end() != it) { - if (!happened) return folly::makeSemiFuture< set_var >(ReplServiceError::SERVER_ALREADY_EXISTS); - LOGDEBUG("Creating Pg [{}] of {} members", group_id, members.size()); - it->second = std::make_shared< MockReplicaSet >(group_id, std::move(members)); - return folly::makeSemiFuture< set_var >(it->second); - } - return folly::makeSemiFuture< set_var >(ReplServiceError::CANCELLED); -} - -ReplicationService::set_var MockReplicationService::get_replica_set(std::string const& group_id) const { - auto lk = std::scoped_lock(_map_lock); - if (auto it = _set_map.find(group_id); _set_map.end() != it) { return it->second; } - return ReplServiceError::SERVER_NOT_FOUND; -} - -folly::SemiFuture< ReplServiceError > MockReplicationService::replace_member(std::string const& group_id, - std::string const& old_member, - std::string const& new_member) const { - auto lk = std::scoped_lock(_map_lock); - if (auto it = _set_map.find(group_id); _set_map.end() != it) { - auto& repl_set = *std::dynamic_pointer_cast< MockReplicaSet >(it->second); - if (0 == repl_set._members.erase(old_member)) - return folly::makeSemiFuture(ReplServiceError::CANNOT_REMOVE_LEADER); - auto [_, happened] = repl_set._members.insert(new_member); - if (!happened) return folly::makeSemiFuture(ReplServiceError::CANCELLED); - return folly::makeSemiFuture(ReplServiceError::OK); - } - return folly::makeSemiFuture(ReplServiceError::SERVER_NOT_FOUND); -} - -void MockReplicationService::iterate_replica_sets(std::function< void(const rs_ptr_t&) >) const {} - -std::shared_ptr< ReplicationService > create_repl_service(on_replica_set_init_t&&) { - return std::make_shared< MockReplicationService >(); -} - -} // namespace home_replication diff --git a/src/mocks/repl_service.h b/src/mocks/repl_service.h deleted file mode 100644 index 0f8e7e6e..00000000 --- a/src/mocks/repl_service.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include - -#include "repl_decls.h" -#include "repl_set.h" - -namespace nuraft { -class state_machine; -} - -namespace home_replication { - -using rs_ptr_t = std::shared_ptr< ReplicaSet >; - -using ReplServiceError = nuraft::cmd_result_code; - -using on_replica_set_init_t = std::function< std::unique_ptr< ReplicaSetListener >(const rs_ptr_t& rs) >; - -class ReplicationService { -public: - virtual ~ReplicationService() = default; - - using set_var = std::variant< rs_ptr_t, ReplServiceError >; - - /// Sync APIs - virtual set_var get_replica_set(std::string const& group_id) const = 0; - virtual void iterate_replica_sets(std::function< void(const rs_ptr_t&) > cb) const = 0; - - /// Async APIs - virtual folly::SemiFuture< set_var > create_replica_set(std::string const& group_id, - std::set< std::string, std::less<> >&& members) = 0; - virtual folly::SemiFuture< ReplServiceError > - replace_member(std::string const& group_id, std::string const& member_out, std::string const& member_in) const = 0; -}; - -extern std::shared_ptr< ReplicationService > create_repl_service(on_replica_set_init_t&& init_cb); - -} // namespace home_replication diff --git a/src/mocks/repl_set.h b/src/mocks/repl_set.h deleted file mode 100644 index 76edefd9..00000000 --- a/src/mocks/repl_set.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include - -#include "repl_decls.h" - -namespace home_replication { - -// -// Callbacks to be implemented by ReplicaSet users. -// -class ReplicaSetListener { -public: - virtual ~ReplicaSetListener() = default; - - /// @brief Called when the log entry has been committed in the replica set. - /// - /// This function is called from a dedicated commit thread which is different from the original thread calling - /// replica_set::write(). There is only one commit thread, and lsn is guaranteed to be monotonically increasing. - /// - /// @param lsn - The log sequence number - /// @param header - Header originally passed with replica_set::write() api - /// @param key - Key originally passed with replica_set::write() api - /// @param pbas - List of pbas where data is written to the storage engine. - /// @param ctx - User contenxt passed as part of the replica_set::write() api - /// - virtual void on_commit(int64_t lsn, const sisl::blob& header, const sisl::blob& key, const pba_list_t& pbas, - void* ctx) = 0; - - /// @brief Called when the log entry has been received by the replica set. - /// - /// On recovery, this is called from a random worker thread before the raft server is started. It is - /// guaranteed to be serialized in log index order. - /// - /// On the leader, this is called from the same thread that replica_set::write() was called. - /// - /// On the follower, this is called when the follower has received the log entry. It is guaranteed to be serialized - /// in log sequence order. - /// - /// NOTE: Listener can choose to ignore this pre commit, however, typical use case of maintaining this is in-case - /// replica set needs to support strong consistent reads and follower needs to ignore any keys which are not being - /// currently in pre-commit, but yet to be committed. - /// - /// @param lsn - The log sequence number - /// @param header - Header originally passed with replica_set::write() api - /// @param key - Key originally passed with replica_set::write() api - /// @param ctx - User contenxt passed as part of the replica_set::write() api - virtual void on_pre_commit(int64_t lsn, const sisl::blob& header, const sisl::blob& key, void* ctx) = 0; - - /// @brief Called when the log entry has been rolled back by the replica set. - /// - /// This function is called on followers only when the log entry is going to be overwritten. This function is called - /// from a random worker thread, but is guaranteed to be serialized. - /// - /// For each log index, it is guaranteed that either on_commit() or on_rollback() is called but not both. - /// - /// NOTE: Listener should do the free any resources created as part of pre-commit. - /// - /// @param lsn - The log sequence number getting rolled back - /// @param header - Header originally passed with replica_set::write() api - /// @param key - Key originally passed with replica_set::write() api - /// @param ctx - User contenxt passed as part of the replica_set::write() api - virtual void on_rollback(int64_t lsn, const sisl::blob& header, const sisl::blob& key, void* ctx) = 0; - - /// @brief Called when the replica set is being stopped - virtual void on_replica_stop() = 0; -}; - -class ReplicaSet : public nuraft_mesg::mesg_state_mgr { -public: - virtual ~ReplicaSet() = default; - - /// @brief Replicate the data to the replica set. This method goes through the - /// following steps: - /// Step 1: Allocates pba from the storage engine to write the value into. Storage - /// engine returns a pba_list in cases where single contiguous blocks are not - /// available. For convenience, the comment will continue to refer pba_list as pba. - /// Step 2: Uses data channel to send the to all replicas - /// Step 3: Creates a log/journal entry with and calls nuraft to - /// append the entry and replicate using nuraft channel (also called header_channel). - /// - /// @param header - Blob representing the header (it is opaque and will be copied - /// as-is to the journal entry) - /// @param key - Blob representing the key (it is opaque and will be copied as-is to - /// the journal entry). We are tracking this seperately to support consistent read use - /// cases - /// @param value - vector of io buffers that contain value for the key - /// @param user_ctx - User supplied opaque context which will be passed to listener - /// callbacks - virtual void write(const sisl::blob& header, const sisl::blob& key, const sisl::sg_list& value, void* user_ctx) = 0; - - /// @brief After data is replicated and on_commit to the listener is called. the pbas - /// are implicityly transferred to listener. This call will transfer the ownership of - /// pba back to the replication service. This listener should never free the pbas on - /// its own and should always transfer the ownership after it is no longer useful. - /// - /// @param lsn - LSN of the old pba that is being transferred - /// @param pbas - PBAs to be transferred. - virtual void transfer_pba_ownership(int64_t lsn, const pba_list_t& pbas) = 0; - - /// @brief Send the final responce to the rpc client. - /// @param outgoing_buf - response buf to client - /// @param rpc_data - context provided by the rpc server - virtual void send_data_service_response(sisl::io_blob_list_t const& outgoing_buf, - boost::intrusive_ptr< sisl::GenericRpcData >& rpc_data) = 0; - - /// @brief Append an application/user specific message to the journal. - /// - /// @param buffer - Opaque buffer to be interpreted by the user - virtual void append_entry(nuraft::buffer const& b) = 0; - - /// @brief Checks if this replica is the leader in this replica set - /// @return true or false - virtual bool is_leader() const = 0; - - virtual std::string group_id() const = 0; -}; - -} // namespace home_replication diff --git a/src/mocks/tests/CMakeLists.txt b/src/mocks/tests/CMakeLists.txt deleted file mode 100644 index f70f06c2..00000000 --- a/src/mocks/tests/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required (VERSION 3.11) - -find_package(GTest REQUIRED) - -include_directories(BEFORE ${homeobject_INCLUDE_DIRS}) - -add_executable (replsvc_mock_test) -target_sources(replsvc_mock_test PRIVATE ReplServiceTest.cpp) -target_link_libraries(replsvc_mock_test - home_replication_mock - ${COMMON_TEST_DEPS} - GTest::gmock - ) -add_test(NAME MockReplServiceTest COMMAND replsvc_mock_test -csv critical) diff --git a/src/mocks/tests/ReplServiceTest.cpp b/src/mocks/tests/ReplServiceTest.cpp deleted file mode 100644 index f442ad6e..00000000 --- a/src/mocks/tests/ReplServiceTest.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "mocks/repl_service.h" - -using namespace std::chrono_literals; -using home_replication::ReplServiceError; - -SISL_LOGGING_INIT(logging, HOMEREPL_LOG_MODS) -SISL_OPTIONS_ENABLE(logging) - -class ReplServiceFixture : public ::testing::Test { -public: - void SetUp() override { - m_mock_svc = home_replication::create_repl_service([](auto&) { return nullptr; }); - - auto members = std::set< std::string, std::less<> >(); - members.insert("ourself"); - auto v = m_mock_svc->create_replica_set("test_fixture", std::move(members)) - .via(&folly::QueuedImmediateExecutor::instance()) - .get(); - ASSERT_TRUE(std::holds_alternative< home_replication::rs_ptr_t >(v)); - m_repl_set = std::get< home_replication::rs_ptr_t >(v); - ASSERT_TRUE(!!m_repl_set); - } - -protected: - std::shared_ptr< home_replication::ReplicationService > m_mock_svc; - home_replication::rs_ptr_t m_repl_set; -}; - -TEST_F(ReplServiceFixture, CreateEmptyReplSet) { - auto v = m_mock_svc->create_replica_set("0", std::set< std::string, std::less<> >()) - .via(&folly::QueuedImmediateExecutor::instance()) - .get(); - ASSERT_TRUE(std::holds_alternative< ReplServiceError >(v)); - EXPECT_EQ(std::get< ReplServiceError >(v), ReplServiceError::BAD_REQUEST); -} - -TEST_F(ReplServiceFixture, CreateDuplicateReplSet) { - auto members = std::set< std::string, std::less<> >(); - members.insert("them"); - auto v = m_mock_svc->create_replica_set("test_fixture", std::move(members)) - .via(&folly::QueuedImmediateExecutor::instance()) - .get(); - ASSERT_TRUE(std::holds_alternative< ReplServiceError >(v)); - EXPECT_EQ(std::get< ReplServiceError >(v), ReplServiceError::SERVER_ALREADY_EXISTS); -} - -int main(int argc, char* argv[]) { - int parsed_argc = argc; - ::testing::InitGoogleTest(&parsed_argc, argv); - SISL_OPTIONS_LOAD(parsed_argc, argv, logging); - sisl::logging::SetLogger(std::string(argv[0])); - return RUN_ALL_TESTS(); -}