From 984817865300a0c5d8d1cd444b8f3808d84b1a55 Mon Sep 17 00:00:00 2001 From: Brian Szmyd Date: Wed, 31 Jul 2024 17:26:11 -0600 Subject: [PATCH] Start scaffolding. --- CMakeLists.txt | 2 +- src/include/homeblocks/common.hpp | 25 +++++- src/include/homeblocks/homeblocks.hpp | 64 +++++++++++++++- src/include/homeblocks/volume_manager.hpp | 62 +++++++++++++++ src/lib/CMakeLists.txt | 1 + src/lib/homeblocks_impl.cpp | 20 ++++- src/lib/homeblocks_impl.hpp | 92 +++++++++++++++++++++++ src/lib/memory_backend/CMakeLists.txt | 1 + src/lib/memory_backend/mem_homeblocks.cpp | 15 ++++ src/lib/memory_backend/mem_homeblocks.hpp | 25 ++++++ src/lib/volume_manager.cpp | 17 +++++ 11 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 src/include/homeblocks/volume_manager.hpp create mode 100644 src/lib/homeblocks_impl.hpp create mode 100644 src/lib/memory_backend/mem_homeblocks.cpp create mode 100644 src/lib/memory_backend/mem_homeblocks.hpp create mode 100644 src/lib/volume_manager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a38eb14..5f9d5ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required (VERSION 3.11) -project (homeobject LANGUAGES CXX) +project (homeblocks LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) diff --git a/src/include/homeblocks/common.hpp b/src/include/homeblocks/common.hpp index 7eda6e9..b254a57 100644 --- a/src/include/homeblocks/common.hpp +++ b/src/include/homeblocks/common.hpp @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include +#include #include SISL_LOGGING_DECL(homeblocks); @@ -16,4 +20,23 @@ constexpr uint64_t Mi = Ki * Ki; constexpr uint64_t Gi = Ki * Mi; #endif -namespace homeblocks {} // namespace homeblocks +namespace homeblocks { + +using peer_id_t = boost::uuids::uuid; +using volume_id_t = boost::uuids::uuid; + +template < class E > +class Manager { +public: + template < typename T > + using Result = folly::Expected< T, E >; + template < typename T > + using AsyncResult = folly::SemiFuture< Result< T > >; + + using NullResult = Result< folly::Unit >; + using NullAsyncResult = AsyncResult< folly::Unit >; + + virtual ~Manager() = default; +}; + +} // namespace homeblocks diff --git a/src/include/homeblocks/homeblocks.hpp b/src/include/homeblocks/homeblocks.hpp index 3e4936a..4e4b147 100644 --- a/src/include/homeblocks/homeblocks.hpp +++ b/src/include/homeblocks/homeblocks.hpp @@ -1,5 +1,67 @@ #pragma once +#include + +#include + #include "common.hpp" -namespace homeblocks {} // namespace homeblocks +namespace homeblocks { + +class VolumeManager; + +ENUM(DevType, uint8_t, AUTO_DETECT = 1, HDD, NVME, UNSUPPORTED); +struct device_info_t { + explicit device_info_t(std::string name, DevType dtype = DevType::AUTO_DETECT) : + path{std::filesystem::canonical(name)}, type{dtype} {} + device_info_t() = default; + bool operator==(device_info_t const& rhs) const { return path == rhs.path && type == rhs.type; } + friend std::istream& operator>>(std::istream& input, device_info_t& di) { + std::string i_path, i_type; + std::getline(input, i_path, ':'); + std::getline(input, i_type); + di.path = std::filesystem::canonical(i_path); + if (i_type == "HDD") { + di.type = DevType::HDD; + } else if (i_type == "NVME") { + di.type = DevType::NVME; + } else { + di.type = DevType::AUTO_DETECT; + } + return input; + } + std::filesystem::path path; + DevType type; +}; + +class HomeBlocksApplication { +public: + virtual ~HomeBlocksApplication() = default; + + virtual bool spdk_mode() const = 0; + virtual uint32_t threads() const = 0; + virtual std::list< device_info_t > devices() const = 0; + + // Callback made after determining if a SvcId exists or not during initialization, will consume response + virtual peer_id_t discover_svcid(std::optional< peer_id_t > const& found) const = 0; +}; + +struct HomeBlocksStats { + uint64_t total_capacity_bytes{0}; + uint64_t used_capacity_bytes{0}; + std::string to_string() const { + return fmt::format("total_capacity_bytes={}, used_capacity_bytes={}", total_capacity_bytes, + used_capacity_bytes); + } +}; + +class HomeBlocks { +public: + virtual ~HomeBlocks() = default; + virtual peer_id_t our_uuid() const = 0; + virtual std::shared_ptr< VolumeManager > volume_manager() = 0; + virtual HomeBlocksStats get_stats() const = 0; +}; + +extern std::shared_ptr< HomeBlocks > init_homeobject(std::weak_ptr< HomeBlocksApplication >&& application); +} // namespace homeblocks diff --git a/src/include/homeblocks/volume_manager.hpp b/src/include/homeblocks/volume_manager.hpp new file mode 100644 index 0000000..07fb239 --- /dev/null +++ b/src/include/homeblocks/volume_manager.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#include + +#include +#include + +#include "common.hpp" + +namespace homeblocks { + +ENUM(VolumeError, uint16_t, UNKNOWN = 1, INVALID_ARG, TIMEOUT, UNKNOWN_VOLUME, UNSUPPORTED_OP, CRC_MISMATCH, + NO_SPACE_LEFT, DRIVE_WRITE_ERROR); + +struct VolumeInfo { + VolumeInfo(volume_id_t _id, uint64_t _num_bytes) : id(_id), size_bytes(_num_bytes) {} + + volume_id_t id; + uint64_t size_bytes{0}; + + auto operator<=>(VolumeInfo const& rhs) const { + return boost::uuids::hash_value(id) <=> boost::uuids::hash_value(rhs.id); + } + auto operator==(VolumeInfo const& rhs) const { return id == rhs.id; } +}; + +struct VolumeStats { + volume_id_t id; + + uint64_t used_bytes; // total number of bytes used by all shards on this Volume; + uint64_t avail_bytes; // total number of bytes available on this Volume; + + std::string to_string() { + return fmt::format("VolumeStats: id={} used_bytes={}, avail_bytes={}", boost::uuids::to_string(id), used_bytes, + avail_bytes); + } +}; + +class VolumeManager : public Manager< VolumeError > { +public: + virtual NullAsyncResult create_volume(VolumeInfo&& volume_info) = 0; + + /** + * Retrieves the statistics for a specific Volume identified by its ID. + * + * @param id The ID of the Volume. + * @param stats The reference to the VolumeStats object where the statistics will be stored. + * @return True if the statistics were successfully retrieved, false otherwise (e.g. id not found). + */ + virtual bool get_stats(volume_id_t id, VolumeStats& stats) const = 0; + + /** + * @brief Retrieves the list of volume_ids. + * + * This function retrieves the list of volume_ids and stores them in the provided vector. + * + * @param vol_ids The vector to store the volume ids. + */ + virtual void get_volume_ids(std::vector< volume_id_t >& vol_ids) const = 0; +}; + +} // namespace homeblocks diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index ed66c63..e33c075 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -5,6 +5,7 @@ add_flags("-Wno-unused-parameter") add_library(${PROJECT_NAME}_core OBJECT) target_sources(${PROJECT_NAME}_core PRIVATE homeblocks_impl.cpp + volume_manager.cpp ) target_link_libraries(${PROJECT_NAME}_core ${COMMON_DEPS} diff --git a/src/lib/homeblocks_impl.cpp b/src/lib/homeblocks_impl.cpp index d042126..98acc08 100644 --- a/src/lib/homeblocks_impl.cpp +++ b/src/lib/homeblocks_impl.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include "homeblocks_impl.hpp" SISL_OPTION_GROUP(homeblocks, (executor_type, "", "executor", "Executor to use for Future deferal", @@ -9,4 +9,20 @@ SISL_OPTION_GROUP(homeblocks, SISL_LOGGING_DEF(HOMEBLOCKS_LOG_MODS) -namespace homeobject {} // namespace homeobject +namespace homeblocks { +HomeBlocksImpl::HomeBlocksImpl(std::weak_ptr< HomeBlocksApplication >&& application) : + _application(std::move(application)) { + auto exe_type = SISL_OPTIONS["executor"].as< std::string >(); + std::transform(exe_type.begin(), exe_type.end(), exe_type.begin(), ::tolower); + + if ("immediate" == exe_type) [[likely]] + executor_ = &folly::QueuedImmediateExecutor::instance(); + else if ("io" == exe_type) + executor_ = folly::getGlobalIOExecutor(); + else if ("cpu" == exe_type) + executor_ = folly::getGlobalCPUExecutor(); + else + RELEASE_ASSERT(false, "Unknown Folly Executor type: [{}]", exe_type); + LOGI("initialized with [executor={}]", exe_type); +} +} // namespace homeblocks diff --git a/src/lib/homeblocks_impl.hpp b/src/lib/homeblocks_impl.hpp new file mode 100644 index 0000000..f623399 --- /dev/null +++ b/src/lib/homeblocks_impl.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#include +#include + +#define LOGT(...) LOGTRACEMOD(homeblocks, ##__VA_ARGS__) +#define LOGD(...) LOGDEBUGMOD(homeblocks, ##__VA_ARGS__) +#define LOGI(...) LOGINFOMOD(homeblocks, ##__VA_ARGS__) +#define LOGW(...) LOGWARNMOD(homeblocks, ##__VA_ARGS__) +#define LOGE(...) LOGERRORMOD(homeblocks, ##__VA_ARGS__) +#define LOGC(...) LOGCRITICALMOD(homeblocks, ##__VA_ARGS__) + +namespace homeblocks { + +template < typename T > +using shared = std::shared_ptr< T >; + +template < typename T > +using cshared = const std::shared_ptr< T >; + +template < typename T > +using unique = std::unique_ptr< T >; + +template < typename T > +using intrusive = boost::intrusive_ptr< T >; + +template < typename T > +using cintrusive = const boost::intrusive_ptr< T >; + +struct Volume { + explicit Volume(VolumeInfo info) : volume_info_(std::move(info)) {} + Volume(Volume const& volume) = delete; + Volume(Volume&& volume) = default; + Volume& operator=(Volume const& volume) = delete; + Volume& operator=(Volume&& volume) = default; + virtual ~Volume() = default; + + VolumeInfo volume_info_; +}; + +class HomeBlocksImpl : public HomeBlocks, public VolumeManager, public std::enable_shared_from_this< HomeBlocksImpl > { + virtual VolumeManager::NullAsyncResult _create_volume(VolumeInfo&& volume_info) = 0; + virtual bool _get_stats(volume_id_t id, VolumeStats& stats) const = 0; + virtual void _get_volume_ids(std::vector< volume_id_t >& vol_ids) const = 0; + + virtual HomeBlocksStats _get_stats() const = 0; + +protected: + peer_id_t _our_id; + + /// Our SvcId retrieval and SvcId->IP mapping + std::weak_ptr< HomeBlocksApplication > _application; + + folly::Executor::KeepAlive<> executor_; + + /// + mutable std::shared_mutex _volume_lock; + std::map< volume_id_t, unique< Volume > > _volume_map; + + + auto _defer() const { return folly::makeSemiFuture().via(executor_); } + +public: + explicit HomeBlocksImpl(std::weak_ptr< HomeBlocksApplication >&& application); + + ~HomeBlocksImpl() override = default; + HomeBlocksImpl(const HomeBlocksImpl&) = delete; + HomeBlocksImpl(HomeBlocksImpl&&) noexcept = delete; + HomeBlocksImpl& operator=(const HomeBlocksImpl&) = delete; + HomeBlocksImpl& operator=(HomeBlocksImpl&&) noexcept = delete; + + std::shared_ptr< VolumeManager > volume_manager() final; + + /// HomeBlocks + /// Returns the UUID of this HomeBlocks. + peer_id_t our_uuid() const final { return _our_id; } + HomeBlocksStats get_stats() const final { return _get_stats(); } + + /// VolumeManager + VolumeManager::NullAsyncResult create_volume(VolumeInfo&& vol_info) final; + + // see api comments in base class; + bool get_stats(volume_id_t id, VolumeStats& stats) const final; + void get_volume_ids(std::vector< volume_id_t >& vol_ids) const final; + + uint64_t get_current_timestamp(); +}; + +} // namespace homeblocks diff --git a/src/lib/memory_backend/CMakeLists.txt b/src/lib/memory_backend/CMakeLists.txt index f9b4af9..9945ff4 100644 --- a/src/lib/memory_backend/CMakeLists.txt +++ b/src/lib/memory_backend/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.11) add_library ("${PROJECT_NAME}_memory") target_sources("${PROJECT_NAME}_memory" PRIVATE + mem_homeblocks.cpp $ ) target_link_libraries("${PROJECT_NAME}_memory" diff --git a/src/lib/memory_backend/mem_homeblocks.cpp b/src/lib/memory_backend/mem_homeblocks.cpp new file mode 100644 index 0000000..6ccf042 --- /dev/null +++ b/src/lib/memory_backend/mem_homeblocks.cpp @@ -0,0 +1,15 @@ +#include "mem_homeblocks.hpp" + +namespace homeblocks { + +/// NOTE: We give ourselves the option to provide a different HR instance here than libhomeblocks.a +extern std::shared_ptr< HomeBlocks > init_homeblocks(std::weak_ptr< HomeBlocksApplication >&& application) { + return std::make_shared< MemoryHomeBlocks >(std::move(application)); +} + +MemoryHomeBlocks::MemoryHomeBlocks(std::weak_ptr< HomeBlocksApplication >&& application) : + HomeBlocksImpl::HomeBlocksImpl(std::move(application)) { + _our_id = _application.lock()->discover_svcid(std::nullopt); +} + +} // namespace homeblocks diff --git a/src/lib/memory_backend/mem_homeblocks.hpp b/src/lib/memory_backend/mem_homeblocks.hpp new file mode 100644 index 0000000..02e0b19 --- /dev/null +++ b/src/lib/memory_backend/mem_homeblocks.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include +#include "lib/homeblocks_impl.hpp" + +namespace homeblocks { + +class MemoryHomeBlocks : public HomeBlocksImpl { + // VolumeManager + VolumeManager::NullAsyncResult _create_volume(VolumeInfo&& vol_info) override; + + bool _get_stats(volume_id_t id, VolumeStats& stats) const override; + void _get_volume_ids(std::vector< volume_id_t >& volume_ids) const override; + + HomeBlocksStats _get_stats() const override; + +public: + MemoryHomeBlocks(std::weak_ptr< HomeBlocksApplication >&& application); + ~MemoryHomeBlocks() override = default; +}; + +} // namespace homeblocks diff --git a/src/lib/volume_manager.cpp b/src/lib/volume_manager.cpp new file mode 100644 index 0000000..d77f0ac --- /dev/null +++ b/src/lib/volume_manager.cpp @@ -0,0 +1,17 @@ +#include + +#include "homeblocks_impl.hpp" + +namespace homeblocks { + +std::shared_ptr< VolumeManager > HomeBlocksImpl::volume_manager() { return shared_from_this(); } + +VolumeManager::NullAsyncResult HomeBlocksImpl::create_volume(VolumeInfo&& vol_info) { + LOGI("[vol={}] is of capacity [{}B]", boost::uuids::to_string(vol_info.id), vol_info.size_bytes); + return _create_volume(std::move(vol_info)); +} + +bool HomeBlocksImpl::get_stats(volume_id_t id, VolumeStats& stats) const { return _get_stats(id, stats); } +void HomeBlocksImpl::get_volume_ids(std::vector< volume_id_t >& vol_ids) const { return _get_volume_ids(vol_ids); } + +} // namespace homeobject