From bce02310ede0216e6cfc3a1e390f79ea01d0569b Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 2 May 2024 17:00:47 -0400 Subject: [PATCH 1/5] Add mmap error_t codes. --- include/bitcoin/database/error.hpp | 9 +++++ src/error.cpp | 9 +++++ test/error.cpp | 63 ++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/include/bitcoin/database/error.hpp b/include/bitcoin/database/error.hpp index eee3ea0c..c2646ed3 100644 --- a/include/bitcoin/database/error.hpp +++ b/include/bitcoin/database/error.hpp @@ -52,6 +52,15 @@ enum error_t : uint8_t unload_locked, unload_failure, + // mmap + disk_full, + mmap_failure, + mremap_failure, + munmap_failure, + madvise_failure, + ftruncate_failure, + fsync_failure, + /// locks transactor_lock, process_lock, diff --git a/src/error.cpp b/src/error.cpp index fb61a77e..dc378739 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -45,6 +45,15 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { unload_locked, "unloading locked file" }, { unload_failure, "file failed to unload" }, + // mmap + { disk_full, "disk full" }, + { mmap_failure, "mmap failure" }, + { mremap_failure, "mremap failure" }, + { munmap_failure, "munmap failure" }, + { madvise_failure, "madvise failure" }, + { ftruncate_failure, "ftruncate failure" }, + { fsync_failure, "fsync failure" }, + // locks { transactor_lock, "transactor lock failure" }, { process_lock, "process lock failure" }, diff --git a/test/error.cpp b/test/error.cpp index 3a298a15..52c76b93 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -158,6 +158,69 @@ BOOST_AUTO_TEST_CASE(error_t__code__unload_failure__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "file failed to unload"); } +BOOST_AUTO_TEST_CASE(error_t__code__disk_full__true_exected_message) +{ + constexpr auto value = error::disk_full; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "disk full"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__mmap_failure__true_exected_message) +{ + constexpr auto value = error::mmap_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "mmap failure"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__mremap_failure__true_exected_message) +{ + constexpr auto value = error::mremap_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "mremap failure"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__munmap_failure__true_exected_message) +{ + constexpr auto value = error::munmap_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "munmap failure"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__madvise_failure__true_exected_message) +{ + constexpr auto value = error::madvise_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "madvise failure"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__ftruncate_failure__true_exected_message) +{ + constexpr auto value = error::ftruncate_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "ftruncate failure"); +} + +BOOST_AUTO_TEST_CASE(error_t__code__fsync_failure__true_exected_message) +{ + constexpr auto value = error::fsync_failure; + const auto ec = code(value); + BOOST_REQUIRE(ec); + BOOST_REQUIRE(ec == value); + BOOST_REQUIRE_EQUAL(ec.message(), "fsync failure"); +} + BOOST_AUTO_TEST_CASE(error_t__code__transactor_lock__true_exected_message) { constexpr auto value = error::transactor_lock; From c88f0f0069bcca1786675910335b2400d5a6eb22 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 2 May 2024 17:02:25 -0400 Subject: [PATCH 2/5] Add error set/get to storage interface and map map impl. --- .../database/memory/interfaces/storage.hpp | 8 ++- include/bitcoin/database/memory/map.hpp | 26 ++++++--- src/memory/map.cpp | 58 ++++++++++++++----- test/memory/map.cpp | 26 +++++++++ test/mocks/chunk_storage.cpp | 11 +++- test/mocks/chunk_storage.hpp | 4 +- 6 files changed, 110 insertions(+), 23 deletions(-) diff --git a/include/bitcoin/database/memory/interfaces/storage.hpp b/include/bitcoin/database/memory/interfaces/storage.hpp index fa58a0c8..206fd3da 100644 --- a/include/bitcoin/database/memory/interfaces/storage.hpp +++ b/include/bitcoin/database/memory/interfaces/storage.hpp @@ -44,7 +44,7 @@ class storage virtual code load() NOEXCEPT = 0; /// Flush memory map to disk, suspend writes for call, must be loaded. - virtual code flush() const NOEXCEPT = 0; + virtual code flush() NOEXCEPT = 0; /// Flush, unmap and truncate to logical, restartable, idempotent. virtual code unload() NOEXCEPT = 0; @@ -66,6 +66,12 @@ class storage /// Get r/w access to start/offset of memory map (or null). virtual memory_ptr get(size_t offset=zero) const NOEXCEPT = 0; + + /// Get the current error condition. + virtual code get_error() const NOEXCEPT = 0; + + /// Clear the error condition. + virtual void clear_error() NOEXCEPT = 0; }; } // namespace database diff --git a/include/bitcoin/database/memory/map.hpp b/include/bitcoin/database/memory/map.hpp index 4703b7d8..48be2f46 100644 --- a/include/bitcoin/database/memory/map.hpp +++ b/include/bitcoin/database/memory/map.hpp @@ -20,12 +20,14 @@ #define LIBBITCOIN_DATABASE_MEMORY_MAP_HPP #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -65,7 +67,7 @@ class BCD_API map code load() NOEXCEPT override; /// Flush memory map to disk, suspend writes for call, must be loaded. - code flush() const NOEXCEPT override; + code flush() NOEXCEPT override; /// Flush, unmap and truncate to logical, restartable, idempotent. code unload() NOEXCEPT override; @@ -88,7 +90,14 @@ class BCD_API map /// Get r/w access to start/offset of memory map (or null). memory_ptr get(size_t offset=zero) const NOEXCEPT override; + /// Get the current error condition. + code get_error() const NOEXCEPT override; + + /// Clear the error condition. + void clear_error() NOEXCEPT override; + protected: + void set_first_code(const error::error_t& value) NOEXCEPT; size_t to_capacity(size_t required) const NOEXCEPT { BC_PUSH_WARNING(NO_STATIC_CAST) @@ -105,7 +114,7 @@ class BCD_API map using access = accessor; // Mapping utilities. - bool flush_() const NOEXCEPT; + bool flush_() NOEXCEPT; bool unmap_() NOEXCEPT; bool map_() NOEXCEPT; bool remap_(size_t size) NOEXCEPT; @@ -119,17 +128,20 @@ class BCD_API map // Protected by remap_mutex. // requires remap_mutex_ exclusive lock for write. // requires remap_mutex_ minimum shared lock for flush/read. - uint8_t* memory_map_; + uint8_t* memory_map_{}; mutable std::shared_mutex remap_mutex_{}; // Protected by field_mutex. // fields require field_mutex_ exclusive lock for write. // fields require minimum field_mutex_ shared lock for flush/read. - int opened_; - bool loaded_; - size_t capacity_; - size_t logical_; + int opened_{ file::invalid }; + bool loaded_{}; + size_t capacity_{}; + size_t logical_{}; mutable std::shared_mutex field_mutex_{}; + + // This is thread safe; + std::atomic error_{ error::success }; }; } // namespace database diff --git a/src/memory/map.cpp b/src/memory/map.cpp index 22bd2abb..ef52cc1f 100644 --- a/src/memory/map.cpp +++ b/src/memory/map.cpp @@ -44,14 +44,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) using namespace system; map::map(const path& filename, size_t minimum, size_t expansion) NOEXCEPT - : filename_(filename), - minimum_(minimum), - expansion_(expansion), - memory_map_(nullptr), - opened_(file::invalid), - loaded_(false), - capacity_(zero), - logical_(zero) + : filename_(filename), minimum_(minimum), expansion_(expansion) { } @@ -142,7 +135,7 @@ code map::load() NOEXCEPT } // Suspend writes before calling. -code map::flush() const NOEXCEPT +code map::flush() NOEXCEPT { // Prevent unload, resize, remap. std::shared_lock map_lock(remap_mutex_); @@ -234,6 +227,7 @@ size_t map::allocate(size_t chunk) NOEXCEPT // TODO: Could loop over a try lock here and log deadlock warning. std::unique_lock remap_lock(remap_mutex_); + // If disk_full set, suspend writes, create space, clear, and restart. if (!remap_(size)) return storage::eof; } @@ -266,13 +260,30 @@ memory_ptr map::get(size_t offset) const NOEXCEPT return ptr; } +code map::get_error() const NOEXCEPT +{ + return error_.load(); +} + +void map::clear_error() NOEXCEPT +{ + error_ = error::success; +} + +// Read-write protected by atomic, write-write protected by remap_mutex. +void map::set_first_code(const error::error_t& value) NOEXCEPT +{ + if (!error_) + error_.store(value); +} + // private, mman wrappers, not thread safe // ---------------------------------------------------------------------------- constexpr auto fail = -1; // Never results in unmapped. -bool map::flush_() const NOEXCEPT +bool map::flush_() NOEXCEPT { // msync should not be required on modern linux, see linus et al. // stackoverflow.com/questions/5902629/mmap-msync-and-linux-process-termination @@ -280,12 +291,12 @@ bool map::flush_() const NOEXCEPT // unmap (and therefore msync) must be called before ftruncate. // "To flush all the dirty pages plus the metadata for the file and ensure // that they are physically written to disk..." - return (::msync(memory_map_, logical_, MS_SYNC) != fail) + const auto success = (::msync(memory_map_, logical_, MS_SYNC) != fail) && (::fsync(opened_) != fail); #elif defined(F_FULLFSYNC) // macOS msync fails with zero logical size (but we are no longer calling). // non-standard macOS behavior: news.ycombinator.com/item?id=30372218 - return ::fcntl(opened_, F_FULLFSYNC, 0) != fail; + const auto success = ::fcntl(opened_, F_FULLFSYNC, 0) != fail; #else // Linux: fsync "transfers ("flushes") all modified in-core data of // (i.e., modified buffer cache pages for) the file referred to by the @@ -293,8 +304,13 @@ bool map::flush_() const NOEXCEPT // can be retrieved even if the system crashes or is rebooted. This // includes writing through or flushing a disk cache if present. The // call blocks until the device reports that transfer has completed." - return ::fsync(opened_) != fail; + const auto success = ::fsync(opened_) != fail; #endif + + if (!success) + set_first_code(error::fsync_failure); + + return success; } // Always results in unmapped. @@ -316,6 +332,8 @@ bool map::unmap_() NOEXCEPT #endif && (::munmap(memory_map_, capacity_) != fail); #endif + if (!success) + set_first_code(error::munmap_failure); capacity_ = zero; memory_map_ = nullptr; @@ -334,6 +352,7 @@ bool map::map_() NOEXCEPT size = minimum_; if (::ftruncate(opened_, size) == fail) { + set_first_code(error::ftruncate_failure); unmap_(); return false; } @@ -356,11 +375,22 @@ bool map::remap_(size_t size) NOEXCEPT #if !defined(HAVE_MSC) && !defined(MREMAP_MAYMOVE) // macOS: unmap before ftruncate sets new size. if (!unmap_()) + { + set_first_code(error::mremap_failure); return false; + } #endif if (::ftruncate(opened_, size) == fail) { + // Disk full is the only restartable store failure (no unmap). + if (errno == ENOSPC) + { + set_first_code(error::disk_full); + return false; + } + + set_first_code(error::ftruncate_failure); unmap_(); return false; } @@ -389,12 +419,14 @@ bool map::finalize_(size_t size) NOEXCEPT { capacity_ = zero; memory_map_ = nullptr; + set_first_code(error::mmap_failure); return false; } // TODO: madvise with large length value fails on linux, does 0 imply all? if (::madvise(memory_map_, 0, MADV_RANDOM) == fail) { + set_first_code(error::madvise_failure); unmap_(); return false; } diff --git a/test/memory/map.cpp b/test/memory/map.cpp index cd9fcae3..9ccd3a7f 100644 --- a/test/memory/map.cpp +++ b/test/memory/map.cpp @@ -47,6 +47,7 @@ BOOST_AUTO_TEST_CASE(map__file__always__expected) const std::string file = TEST_PATH; map instance(file); BOOST_REQUIRE_EQUAL(instance.file(), file); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__open__opened__false) @@ -59,6 +60,7 @@ BOOST_AUTO_TEST_CASE(map__open__opened__false) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE_EQUAL(instance.open(), error::open_open); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__open__no_file__false) @@ -68,6 +70,7 @@ BOOST_AUTO_TEST_CASE(map__open__no_file__false) BOOST_REQUIRE(!test::exists(file)); BOOST_REQUIRE_EQUAL(instance.open(), error::open_failure); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__close__open__true) @@ -78,6 +81,7 @@ BOOST_AUTO_TEST_CASE(map__close__open__true) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE(instance.is_open()); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__close__closed__true) @@ -99,6 +103,7 @@ BOOST_AUTO_TEST_CASE(map__close__loaded__false) BOOST_REQUIRE_EQUAL(instance.close(), error::close_loaded); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__properties__open_close__expected) @@ -123,6 +128,7 @@ BOOST_AUTO_TEST_CASE(map__properties__open_close__expected) BOOST_REQUIRE(!instance.is_loaded()); BOOST_REQUIRE_EQUAL(instance.size(), 0u); BOOST_REQUIRE_EQUAL(instance.capacity(), 0u); + BOOST_REQUIRE(!instance.get_error()); BOOST_REQUIRE(test::exists(file)); } @@ -154,6 +160,7 @@ BOOST_AUTO_TEST_CASE(map__properties__load_unload__expected) BOOST_REQUIRE(!instance.is_loaded()); BOOST_REQUIRE_EQUAL(instance.size(), 0u); BOOST_REQUIRE_EQUAL(instance.capacity(), 0u); + BOOST_REQUIRE(!instance.get_error()); BOOST_REQUIRE(test::exists(file)); } @@ -167,6 +174,7 @@ BOOST_AUTO_TEST_CASE(map__load__unloaded__true) BOOST_REQUIRE_EQUAL(instance.load(), error::success); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__load__shared__false) @@ -182,6 +190,7 @@ BOOST_AUTO_TEST_CASE(map__load__shared__false) memory.reset(); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__load__loaded__false) @@ -194,6 +203,7 @@ BOOST_AUTO_TEST_CASE(map__load__loaded__false) BOOST_REQUIRE_EQUAL(instance.load(), error::load_loaded); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__unload__unloaded__true) @@ -204,6 +214,7 @@ BOOST_AUTO_TEST_CASE(map__unload__unloaded__true) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__unload__loaded__true) @@ -215,6 +226,7 @@ BOOST_AUTO_TEST_CASE(map__unload__loaded__true) BOOST_REQUIRE_EQUAL(instance.load(), error::success); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__capacity__default__expected) @@ -230,6 +242,7 @@ BOOST_AUTO_TEST_CASE(map__capacity__default__expected) BOOST_REQUIRE_EQUAL(instance.capacity(), default_minimum_capacity); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__truncate__unloaded__false) @@ -240,6 +253,7 @@ BOOST_AUTO_TEST_CASE(map__truncate__unloaded__false) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE(!instance.truncate(42)); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } // Truncate is no longer capacty based. @@ -258,6 +272,7 @@ BOOST_AUTO_TEST_CASE(map__truncate__unloaded__false) //// BOOST_REQUIRE_EQUAL(instance.size(), to_half(size)); //// BOOST_REQUIRE_EQUAL(instance.unload(), error::success); //// BOOST_REQUIRE_EQUAL(instance.close(), error::success); +//// BOOST_REQUIRE(!instance.get_error()); ////} BOOST_AUTO_TEST_CASE(map__allocate__unloaded__false) @@ -268,6 +283,7 @@ BOOST_AUTO_TEST_CASE(map__allocate__unloaded__false) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE_EQUAL(instance.allocate(42), map::eof); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__allocate__loaded__expected_capacity) @@ -283,6 +299,7 @@ BOOST_AUTO_TEST_CASE(map__allocate__loaded__expected_capacity) BOOST_REQUIRE_EQUAL(instance.capacity(), capacity); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__allocate__add_overflow__eof) @@ -296,6 +313,7 @@ BOOST_AUTO_TEST_CASE(map__allocate__add_overflow__eof) BOOST_REQUIRE_EQUAL(instance.allocate(max_size_t), storage::eof); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__allocate__minimum_no_expansion__expected_capacity) @@ -312,6 +330,7 @@ BOOST_AUTO_TEST_CASE(map__allocate__minimum_no_expansion__expected_capacity) BOOST_REQUIRE_EQUAL(instance.capacity(), capacity); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__allocate__no_minimum_expansion__expected_capacity) @@ -334,6 +353,7 @@ BOOST_AUTO_TEST_CASE(map__allocate__no_minimum_expansion__expected_capacity) BOOST_REQUIRE_EQUAL(instance.capacity(), capacity); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__get__unloaded__false) @@ -344,6 +364,7 @@ BOOST_AUTO_TEST_CASE(map__get__unloaded__false) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE(!instance.get()); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__get__loaded__success) @@ -356,6 +377,7 @@ BOOST_AUTO_TEST_CASE(map__get__loaded__success) BOOST_REQUIRE(instance.get(instance.allocate(1))); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__flush__unloaded__false) @@ -366,6 +388,7 @@ BOOST_AUTO_TEST_CASE(map__flush__unloaded__false) BOOST_REQUIRE_EQUAL(instance.open(), error::success); BOOST_REQUIRE_EQUAL(instance.flush(), error::flush_unloaded); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__flush__loaded__true) @@ -378,6 +401,7 @@ BOOST_AUTO_TEST_CASE(map__flush__loaded__true) BOOST_REQUIRE_EQUAL(instance.flush(), error::success); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__write__read__expected) @@ -399,6 +423,7 @@ BOOST_AUTO_TEST_CASE(map__write__read__expected) memory.reset(); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(map__unload__shared__false) @@ -414,6 +439,7 @@ BOOST_AUTO_TEST_CASE(map__unload__shared__false) memory.reset(); BOOST_REQUIRE_EQUAL(instance.unload(), error::success); BOOST_REQUIRE_EQUAL(instance.close(), error::success); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/mocks/chunk_storage.cpp b/test/mocks/chunk_storage.cpp index 2fa530bb..5748bb64 100644 --- a/test/mocks/chunk_storage.cpp +++ b/test/mocks/chunk_storage.cpp @@ -64,7 +64,7 @@ code chunk_storage::load() NOEXCEPT return error::success; } -code chunk_storage::flush() const NOEXCEPT +code chunk_storage::flush() NOEXCEPT { return error::success; } @@ -128,6 +128,15 @@ memory_ptr chunk_storage::get(size_t offset) const NOEXCEPT return ptr; } +code chunk_storage::get_error() const NOEXCEPT +{ + return {}; +} + +void chunk_storage::clear_error() NOEXCEPT +{ +} + BC_POP_WARNING() } // namespace test diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp index 3729af84..2de5c487 100644 --- a/test/mocks/chunk_storage.hpp +++ b/test/mocks/chunk_storage.hpp @@ -41,7 +41,7 @@ class chunk_storage code open() NOEXCEPT override; code close() NOEXCEPT override; code load() NOEXCEPT override; - code flush() const NOEXCEPT override; + code flush() NOEXCEPT override; code unload() NOEXCEPT override; const std::filesystem::path& file() const NOEXCEPT override; size_t capacity() const NOEXCEPT override; @@ -49,6 +49,8 @@ class chunk_storage bool truncate(size_t size) NOEXCEPT override; size_t allocate(size_t chunk) NOEXCEPT override; memory_ptr get(size_t offset=zero) const NOEXCEPT override; + code get_error() const NOEXCEPT override; + void clear_error() NOEXCEPT override; private: system::data_chunk local_; From f81ff7f0f59a2ce3e2c7b298140914e31432305c Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 2 May 2024 17:03:05 -0400 Subject: [PATCH 3/5] Add error set/get manager and containing arraymap/hashmap. --- .../database/impl/primitives/arraymap.ipp | 15 ++++ .../database/impl/primitives/hashmap.ipp | 15 ++++ .../database/impl/primitives/manager.ipp | 15 ++++ .../bitcoin/database/primitives/arraymap.hpp | 9 +++ .../bitcoin/database/primitives/hashmap.hpp | 9 +++ .../bitcoin/database/primitives/manager.hpp | 6 ++ test/primitives/arraymap.cpp | 47 +++++++++++- test/primitives/hashmap.cpp | 73 +++++++++++++++++-- test/primitives/manager.cpp | 23 ++++++ 9 files changed, 202 insertions(+), 10 deletions(-) diff --git a/include/bitcoin/database/impl/primitives/arraymap.ipp b/include/bitcoin/database/impl/primitives/arraymap.ipp index a073026d..44a7fe27 100644 --- a/include/bitcoin/database/impl/primitives/arraymap.ipp +++ b/include/bitcoin/database/impl/primitives/arraymap.ipp @@ -106,6 +106,21 @@ bool CLASS::truncate(const Link& count) NOEXCEPT // query interface // ---------------------------------------------------------------------------- +TEMPLATE +code CLASS::get_error() const NOEXCEPT +{ + return manager_.get_error(); +} + +TEMPLATE +void CLASS::clear_error() NOEXCEPT +{ + manager_.clear_error(); +} + +// query interface +// ---------------------------------------------------------------------------- + TEMPLATE template > bool CLASS::get(const Link& link, Element& element) const NOEXCEPT diff --git a/include/bitcoin/database/impl/primitives/hashmap.ipp b/include/bitcoin/database/impl/primitives/hashmap.ipp index d17f1168..69ac051a 100644 --- a/include/bitcoin/database/impl/primitives/hashmap.ipp +++ b/include/bitcoin/database/impl/primitives/hashmap.ipp @@ -106,6 +106,21 @@ Link CLASS::count() const NOEXCEPT // query interface // ---------------------------------------------------------------------------- +TEMPLATE +code CLASS::get_error() const NOEXCEPT +{ + return manager_.get_error(); +} + +TEMPLATE +void CLASS::clear_error() NOEXCEPT +{ + manager_.clear_error(); +} + +// query interface +// ---------------------------------------------------------------------------- + TEMPLATE Link CLASS::top(const Link& link) const NOEXCEPT { diff --git a/include/bitcoin/database/impl/primitives/manager.ipp b/include/bitcoin/database/impl/primitives/manager.ipp index 39378b99..e173f570 100644 --- a/include/bitcoin/database/impl/primitives/manager.ipp +++ b/include/bitcoin/database/impl/primitives/manager.ipp @@ -86,6 +86,21 @@ memory_ptr CLASS::get(const Link& value) const NOEXCEPT return file_.get(link_to_position(value)); } +// Errors. +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_error() const NOEXCEPT +{ + return file_.get_error(); +} + +TEMPLATE +void CLASS::clear_error() NOEXCEPT +{ + file_.clear_error(); +} + // private // ---------------------------------------------------------------------------- diff --git a/include/bitcoin/database/primitives/arraymap.hpp b/include/bitcoin/database/primitives/arraymap.hpp index 5c003755..734ae3cc 100644 --- a/include/bitcoin/database/primitives/arraymap.hpp +++ b/include/bitcoin/database/primitives/arraymap.hpp @@ -68,6 +68,15 @@ class arraymap /// Reduce count as specified. bool truncate(const Link& count) NOEXCEPT; + /// Errors. + /// ----------------------------------------------------------------------- + + /// Get the current error condition. + code get_error() const NOEXCEPT; + + /// Clear the error condition. + void clear_error() NOEXCEPT; + /// Query interface. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/primitives/hashmap.hpp b/include/bitcoin/database/primitives/hashmap.hpp index 49e8c1de..b24ef2d5 100644 --- a/include/bitcoin/database/primitives/hashmap.hpp +++ b/include/bitcoin/database/primitives/hashmap.hpp @@ -74,6 +74,15 @@ class hashmap /// Count of records (or body file bytes if slab). Link count() const NOEXCEPT; + /// Errors. + /// ----------------------------------------------------------------------- + + /// Get the current error condition. + code get_error() const NOEXCEPT; + + /// Clear the error condition. + void clear_error() NOEXCEPT; + /// Query interface, iterator is not thread safe. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/primitives/manager.hpp b/include/bitcoin/database/primitives/manager.hpp index 17e3bb38..054ccf2d 100644 --- a/include/bitcoin/database/primitives/manager.hpp +++ b/include/bitcoin/database/primitives/manager.hpp @@ -61,6 +61,12 @@ class manager /// Return memory object for the full memory map. memory_ptr get() const NOEXCEPT; + /// Get the current error condition. + code get_error() const NOEXCEPT; + + /// Clear the error condition. + void clear_error() NOEXCEPT; + private: static constexpr auto is_slab = (Size == max_size_t); static constexpr size_t link_to_position(const Link& link) NOEXCEPT; diff --git a/test/primitives/arraymap.cpp b/test/primitives/arraymap.cpp index fd6b6db4..356094b0 100644 --- a/test/primitives/arraymap.cpp +++ b/test/primitives/arraymap.cpp @@ -30,20 +30,20 @@ class arraymap_ using base::arraymap; ////using reader_ptr = std::shared_ptr; ////using writer_ptr = std::shared_ptr; - + //// ////reader_ptr getter_(const Link& link) const NOEXCEPT ////{ //// using namespace system; //// const auto ptr = manager_.get(link); //// if (!ptr) //// return {}; - + //// //// istream stream{ *ptr }; //// const auto source = std::make_shared(stream); //// if constexpr (!is_slab) { source->set_limit(Size); } //// return source; ////} - + //// ////writer_ptr creater_(const Link& size=one) NOEXCEPT ////{ //// using namespace system; @@ -51,7 +51,7 @@ class arraymap_ //// const auto ptr = manager_.get(link); //// if (!ptr) //// return {}; - + //// //// iostream stream{ *ptr }; //// const auto sink = std::make_shared(stream); //// if constexpr (!is_slab) { sink.set_limit(Size * size); } @@ -78,6 +78,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_construct__empty__expected) test::chunk_storage body_store{ body_file }; const record_table instance{ head_store, body_store }; BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_construct__non_empty__expected) @@ -89,6 +90,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_construct__non_empty__expected) test::chunk_storage body_store{ body_file }; const record_table instance{ head_store, body_store }; BOOST_REQUIRE_EQUAL(body_file.size(), body_size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_getter__terminal__false) @@ -99,6 +101,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_getter__terminal__false) test::chunk_storage body_store{ body_file }; const record_table instance{ head_store, body_store }; ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_getter__empty__exhausted) @@ -110,6 +113,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_getter__empty__exhausted) const record_table instance{ head_store, body_store }; ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); + BOOST_REQUIRE(!instance.get_error()); } // slab arraymap @@ -123,6 +127,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_construct__empty__expected) test::chunk_storage body_store{ body_file }; const slab_table instance{ head_store, body_store }; BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_construct__non_empty__expected) @@ -134,6 +139,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_construct__non_empty__expected) test::chunk_storage body_store{ body_file }; const slab_table instance{ head_store, body_store }; BOOST_REQUIRE_EQUAL(body_file.size(), body_size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_getter__terminal__false) @@ -144,6 +150,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_getter__terminal__false) test::chunk_storage body_store{ body_file }; const slab_table instance{ head_store, body_store }; ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_getter__empty__exhausted) @@ -155,6 +162,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_getter__empty__exhausted) const slab_table instance{ head_store, body_store }; ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); + BOOST_REQUIRE(!instance.get_error()); } // push/found/at (protected interface positive tests) @@ -278,6 +286,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__terminal__invalid) little_record record{}; BOOST_REQUIRE(!instance.get(link5::terminal, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_get__empty__invalid) @@ -290,6 +299,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__empty__invalid) little_record record{}; BOOST_REQUIRE(!instance.get(0, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_get__populated__valid) @@ -303,6 +313,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__populated__valid) little_record record{}; BOOST_REQUIRE(instance.get(0, record)); BOOST_REQUIRE_EQUAL(record.value, 0x04030201_u32); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_put__get__expected) @@ -320,6 +331,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_put__get__expected) const data_chunk expected_file{ 0xa1, 0xb2, 0xc3, 0xd4 }; BOOST_REQUIRE_EQUAL(body_file, expected_file); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_count__truncate__expected) @@ -342,6 +354,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_count__truncate__expected) BOOST_REQUIRE(instance.truncate(0)); BOOST_REQUIRE_EQUAL(instance.count(), 0u); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_put_link__multiple__expected) @@ -371,6 +384,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_put_link__multiple__expected) const data_chunk expected_file{ 0xa1, 0xb2, 0xc3, 0xd4, 0xd4, 0xc3, 0xb2, 0xa1 }; BOOST_REQUIRE_EQUAL(body_file, expected_file); + BOOST_REQUIRE(!instance.get_error()); } class little_slab @@ -430,6 +444,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_put__get__expected) const data_chunk expected_file{ 0xa1, 0xb2, 0xc3, 0xd4 }; BOOST_REQUIRE_EQUAL(body_file, expected_file); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_count__truncate__expected) @@ -452,6 +467,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_count__truncate__expected) BOOST_REQUIRE(instance.truncate(0)); BOOST_REQUIRE_EQUAL(instance.count(), 0u); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_put_link__multiple__expected) @@ -481,6 +497,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_put_link__multiple__expected) const data_chunk expected_file{ 0xa1, 0xb2, 0xc3, 0xd4, 0xd4, 0xc3, 0xb2, 0xa1 }; BOOST_REQUIRE_EQUAL(body_file, expected_file); + BOOST_REQUIRE(!instance.get_error()); } // advertises 32 but reads/writes 64 @@ -516,6 +533,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__excess__false) record_excess record{}; BOOST_REQUIRE(!instance.get(zero, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_put_link__excess__false) @@ -526,6 +544,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_put_link__excess__false) test::chunk_storage body_store{ body_file }; arraymap instance{ head_store, body_store }; BOOST_REQUIRE(instance.put_link(record_excess{ 0xa1b2c3d4_u32 }).is_terminal()); + BOOST_REQUIRE(!instance.get_error()); } // advertises 32 but reads/writes 64 @@ -584,6 +603,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_get__excess__true) // Excess read allowed to eof here (reader has only knowledge of size). slab_excess record{}; BOOST_REQUIRE(instance.get(zero, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_get__file_excess__false) @@ -597,6 +617,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_get__file_excess__false) // Excess read disallowed to here (past eof). file_excess record{}; BOOST_REQUIRE(!instance.get(zero, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_put_link__excess__false) @@ -607,6 +628,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_put_link__excess__false) test::chunk_storage body_store{ body_file }; arraymap instance{ head_store, body_store }; BOOST_REQUIRE(instance.put_link(slab_excess{ 0xa1b2c3d4_u32 }).is_terminal()); + BOOST_REQUIRE(!instance.get_error()); } // record create/close/backup/restore/verify @@ -624,6 +646,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_verify__empty_files__expected) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_file.size(), link5::size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_head_file__failure) @@ -637,6 +660,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_head_file__failure) BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE_EQUAL(head_file.size(), one); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_body_file__body_zeroed) @@ -651,6 +675,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_body_file__body_zeroed) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_file.size(), link5::size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__create__zero) @@ -662,6 +687,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__create__zero) record_table instance{ head_store, body_store }; BOOST_REQUIRE(instance.create()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_close__zero) @@ -673,6 +699,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_close__zero) record_table instance{ head_store, body_store }; BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_close__two) @@ -686,6 +713,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_close__two) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0200000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_backup__two) @@ -699,6 +727,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_backup__two) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.backup()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0200000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_restore__truncates) @@ -712,6 +741,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_restore__truncates) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__non_empty_restore__truncates) @@ -726,6 +756,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__non_empty_restore__truncates) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE_EQUAL(body_file, base16_chunk("12345678")); + BOOST_REQUIRE(!instance.get_error()); } // slab create/close/backup/restore/verify @@ -743,6 +774,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_verify__empty_files__expected) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_file.size(), link5::size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_head_file__failure) @@ -756,6 +788,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_head_file__failure) BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE_EQUAL(head_file.size(), one); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_body_file__body_zeroed) @@ -770,6 +803,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_body_file__body_zeroed) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_file.size(), link5::size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__create__zero) @@ -781,6 +815,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__create__zero) slab_table instance{ head_store, body_store }; BOOST_REQUIRE(instance.create()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__empty_close__zero) @@ -805,6 +840,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__two_close__two) body_file = base16_chunk("1234"); BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0200000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__two_backup__two) @@ -818,6 +854,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__two_backup__two) body_file = base16_chunk("1234"); BOOST_REQUIRE(instance.backup()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0200000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__empty_restore__truncates) @@ -831,6 +868,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__empty_restore__truncates) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__non_empty_restore__truncates) @@ -845,6 +883,7 @@ BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__non_empty_restore__truncates) body_file = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE_EQUAL(body_file, base16_chunk("123456")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/primitives/hashmap.cpp b/test/primitives/hashmap.cpp index 3209e62b..55fdc708 100644 --- a/test/primitives/hashmap.cpp +++ b/test/primitives/hashmap.cpp @@ -30,24 +30,24 @@ class hashmap_ using hashmap::hashmap; ////using reader_ptr = std::shared_ptr; ////using finalizer_ptr = std::shared_ptr; - + //// ////reader_ptr getter_(const Key& key) const NOEXCEPT ////{ //// return getter_(base::it(key).self()); ////} - + //// ////reader_ptr getter_(const Link& link) const NOEXCEPT ////{ //// const auto ptr = manager_.get(link); //// if (!ptr) //// return {}; - + //// //// system::istream stream{ *ptr }; //// const auto source = std::make_shared(stream); //// if constexpr (!is_slab) { source->set_limit(Size); } //// return source; ////} - + //// ////finalizer_ptr creater_(const Key& key, const Link& size=bc::one) NOEXCEPT ////{ //// using namespace system; @@ -55,7 +55,7 @@ class hashmap_ //// const auto ptr = manager_.get(link); //// if (!ptr) //// return {}; - + //// //// iostream stream{ *ptr }; //// const auto sink = std::make_shared(stream); //// sink->skip_bytes(Link::size); @@ -65,7 +65,7 @@ class hashmap_ //// auto& next = unsafe_array_cast(ptr->begin()); //// return head_.push(link, next, index); //// }); - + //// //// if constexpr (!is_slab) { sink->set_limit(Size * size); } //// return sink; ////} @@ -101,6 +101,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_construct__empty__expected) test::chunk_storage body_store{}; const record_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_construct__non_empty__expected) @@ -111,6 +112,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_construct__non_empty__expected) body_store.buffer().resize(body_size); const record_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE_EQUAL(body_store.buffer().size(), body_size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_getter__terminal__false) @@ -120,6 +122,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_getter__terminal__false) record_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__exhausted) @@ -130,6 +133,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__exhausted) BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__false) @@ -140,6 +144,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__false) BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); + BOOST_REQUIRE(!instance.get_error()); } // slab hashmap @@ -151,6 +156,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_construct__empty__expected) test::chunk_storage body_store{}; const slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_construct__non_empty__expected_enabled) @@ -162,6 +168,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_construct__non_empty__expected_enabled) const slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE_EQUAL(body_store.buffer().size(), body_size); BOOST_REQUIRE(instance.enabled()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_getter__terminal__false) @@ -171,6 +178,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_getter__terminal__false) slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__exhausted) @@ -181,6 +189,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__exhausted) BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__false) @@ -191,6 +200,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__false) BOOST_REQUIRE(instance.create()); ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__enabled__non_empty_slab_zero_buckets__false) @@ -201,6 +211,7 @@ BOOST_AUTO_TEST_CASE(hashmap__enabled__non_empty_slab_zero_buckets__false) body_store.buffer().resize(body_size); const slab_table instance{ head_store, body_store, 0 }; BOOST_REQUIRE(!instance.enabled()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__enabled__empty_slab_one_bucket__false) @@ -209,6 +220,7 @@ BOOST_AUTO_TEST_CASE(hashmap__enabled__empty_slab_one_bucket__false) test::chunk_storage body_store{}; slab_table instance{ head_store, body_store, 1 }; BOOST_REQUIRE(!instance.enabled()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__enabled__empty_slab_nonzero_buckets__true) @@ -217,6 +229,7 @@ BOOST_AUTO_TEST_CASE(hashmap__enabled__empty_slab_nonzero_buckets__true) test::chunk_storage body_store{}; slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.enabled()); + BOOST_REQUIRE(!instance.get_error()); } // push/found/at (protected interface positive tests) @@ -457,6 +470,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get__terminal__invalid) little_record record{}; BOOST_REQUIRE(!instance.get(link5::terminal, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_get__empty__invalid) @@ -467,6 +481,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get__empty__invalid) little_record record{}; BOOST_REQUIRE(!instance.get(0, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_get__populated__valid) @@ -485,6 +500,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get__populated__valid) little_record record{}; BOOST_REQUIRE(instance.get(0, record)); BOOST_REQUIRE_EQUAL(record.value, 0x04030201_u32); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_put__multiple__expected) @@ -526,6 +542,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_put__multiple__expected) 0xd4, 0xc3, 0xb2, 0xa1 }; BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_file); + BOOST_REQUIRE(!instance.get_error()); } class little_slab @@ -616,6 +633,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_put__multiple__expected) 0xd4, 0xc3, 0xb2, 0xa1 }; BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_file); + BOOST_REQUIRE(!instance.get_error()); } // advertises 32 but reads/writes 64 @@ -652,6 +670,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get__excess__false) record_excess record{}; BOOST_REQUIRE(!instance.get(0, record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_get_key__excess__expected) @@ -664,6 +683,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get_key__excess__expected) constexpr key1 key{ 0x41 }; BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); BOOST_REQUIRE_EQUAL(instance.get_key(0), key); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_put__excess__false) @@ -675,6 +695,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_put__excess__false) constexpr key1 key{ 0x41 }; BOOST_REQUIRE(instance.put_link(key, record_excess{ 0xa1b2c3d4_u32 }).is_terminal()); + BOOST_REQUIRE(!instance.get_error()); } // advertises 32 but reads/writes 64 @@ -736,6 +757,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_get__excess__true) // Excess read allowed to eof here (reader has only knowledge of size). slab_excess slab{}; BOOST_REQUIRE(instance.get(0, slab)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_get_key__excess__expected) @@ -750,6 +772,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_get_key__excess__expected) BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); BOOST_REQUIRE_EQUAL(instance.get_key(0), key); BOOST_REQUIRE_EQUAL(instance.get_key(10), key); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_get__file_excess__false) @@ -765,6 +788,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_get__file_excess__false) // Excess read disallowed to here (past eof). slab_excess slab{}; BOOST_REQUIRE(!instance.get(0, slab)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_put__excess__false) @@ -786,6 +810,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_top__default__terminal) BOOST_REQUIRE(instance.create()); BOOST_REQUIRE(instance.top(0).is_terminal()); BOOST_REQUIRE(instance.top(19).is_terminal()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_top__past_end__terminal) @@ -828,6 +853,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_top__existing__expected) BOOST_REQUIRE(instance.top(17).is_terminal()); BOOST_REQUIRE(!instance.top(18).is_terminal()); BOOST_REQUIRE(!instance.top(19).is_terminal()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_exists__exists__true) @@ -841,6 +867,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_exists__exists__true) BOOST_REQUIRE(!instance.exists(key)); BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); BOOST_REQUIRE(instance.exists(key)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_exists__exists__true) @@ -854,6 +881,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_exists__exists__true) BOOST_REQUIRE(!instance.exists(key)); BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); BOOST_REQUIRE(instance.exists(key)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_first__exists__true) @@ -868,6 +896,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_first__exists__true) const auto link = instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }); BOOST_REQUIRE(!link.is_terminal()); BOOST_REQUIRE_EQUAL(instance.first(key), link); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_first__exists__true) @@ -882,6 +911,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_first__exists__true) const auto link = instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }); BOOST_REQUIRE(!link.is_terminal()); BOOST_REQUIRE_EQUAL(instance.first(key), link); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_it__exists__non_terminal) @@ -898,6 +928,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_it__exists__non_terminal) big_record record{}; BOOST_REQUIRE(instance.get(instance.it(key).self(), record)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_it__multiple__iterated) @@ -960,6 +991,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_it__multiple__iterated) BOOST_REQUIRE_EQUAL(record.value, 0x000000c1_u32); BOOST_REQUIRE(!it_c.advance()); BOOST_REQUIRE(!instance.get(it_c.self(), record)); + BOOST_REQUIRE(!instance.get_error()); // [0000000000] //[b] 0500000000 @@ -1031,6 +1063,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate__terminal__expected) BOOST_REQUIRE(instance.create()); BOOST_REQUIRE(instance.allocate(link5::terminal).is_terminal()); BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate__record__expected) @@ -1041,6 +1074,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate__record__expected) BOOST_REQUIRE(instance.create()); BOOST_REQUIRE_EQUAL(instance.allocate(1), 0u); BOOST_REQUIRE_EQUAL(body_store.buffer().size(), element_size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate__3_records__expected) @@ -1053,6 +1087,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate__3_records__expected) BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 3u * element_size); BOOST_REQUIRE_EQUAL(instance.allocate(1), 3u); BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 4u * element_size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate__slab__allocated) @@ -1065,6 +1100,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate__slab__allocated) BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 42u); BOOST_REQUIRE_EQUAL(instance.allocate(24), 42); BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 42u + 24u); + BOOST_REQUIRE(!instance.get_error()); } class flex_record @@ -1121,6 +1157,7 @@ BOOST_AUTO_TEST_CASE(hashmap__set_commit__record__expected) BOOST_REQUIRE(instance.commit(link, key1)); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_set_commit__record__expected) @@ -1144,6 +1181,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_set_commit__record__expected) BOOST_REQUIRE(instance.commit(link, key1)); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_put1__record__expected) @@ -1162,6 +1200,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_put1__record__expected) BOOST_REQUIRE(instance.put(link, key1, flex_record{ 0x01020304_u32 })); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_put2__record__expected) @@ -1175,6 +1214,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_put2__record__expected) BOOST_REQUIRE(instance.put(key1, flex_record{ 0x01020304_u32 })); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__set_commit_link__slab__expected) @@ -1197,6 +1237,7 @@ BOOST_AUTO_TEST_CASE(hashmap__set_commit_link__slab__expected) BOOST_REQUIRE(!instance.commit_link(link, key1).is_terminal()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_set_commit__slab__expected) @@ -1219,6 +1260,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_set_commit__slab__expected) BOOST_REQUIRE(instance.commit(link, key1)); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_put1__slab__expected) @@ -1239,6 +1281,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_put1__slab__expected) BOOST_REQUIRE(instance.put(link, key1, flex_slab{ 0x01020304_u32 })); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_file, base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__allocate_put2__slab__expected) @@ -1254,6 +1297,7 @@ BOOST_AUTO_TEST_CASE(hashmap__allocate_put2__slab__expected) BOOST_REQUIRE(instance.put(key1, flex_slab{ 0x01020304_u32 })); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("00000000000000000000ffffffffff")); BOOST_REQUIRE_EQUAL(body_file, base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(!instance.get_error()); } // record create/close/backup/restore/verify @@ -1269,6 +1313,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_verify__empty_files__expected) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_store.buffer().size(), header_size); BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_create__non_empty_head_file__failure) @@ -1282,6 +1327,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_create__non_empty_head_file__failure) BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE_EQUAL(head_file.size(), 1u); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_create__non_empty_body_file__body_zeroed) @@ -1305,6 +1351,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__create__zero) record_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.create()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_body_count__empty_close__zero) @@ -1316,6 +1363,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__empty_close__zero) record_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_body_count__two_close__two) @@ -1327,6 +1375,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__two_close__two) body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_body_count__two_backup__two) @@ -1338,6 +1387,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__two_backup__two) body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); BOOST_REQUIRE(instance.backup()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_body_count__empty_restore__truncates) @@ -1349,6 +1399,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__empty_restore__truncates) body_store.buffer() = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__record_body_count__non_empty_restore__truncates) @@ -1361,6 +1412,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_body_count__non_empty_restore__truncates) body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("11223344556677889900112233445566778899")); + BOOST_REQUIRE(!instance.get_error()); } // slab create/close/backup/restore/verify @@ -1376,6 +1428,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_verify__empty_files__expected) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_store.buffer().size(), header_size); BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_create__non_empty_head_file__failure) @@ -1389,6 +1442,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_create__non_empty_head_file__failure) BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE_EQUAL(head_file.size(), 1u); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_create__non_empty_body_file__body_zeroed) @@ -1403,6 +1457,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_create__non_empty_body_file__body_zeroed) BOOST_REQUIRE(instance.verify()); BOOST_REQUIRE_EQUAL(head_file.size(), header_size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__create__zero) @@ -1412,6 +1467,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__create__zero) slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.create()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__empty_close__zero) @@ -1423,6 +1479,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__empty_close__zero) slab_table instance{ head_store, body_store, buckets }; BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__two_close__two) @@ -1434,6 +1491,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__two_close__two) body_store.buffer() = base16_chunk("1234"); BOOST_REQUIRE(instance.close()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__two_backup__two) @@ -1445,6 +1503,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__two_backup__two) body_store.buffer() = base16_chunk("1234"); BOOST_REQUIRE(instance.backup()); BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__empty_restore__truncates) @@ -1456,6 +1515,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__empty_restore__truncates) body_store.buffer() = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE(body_store.buffer().empty()); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__non_empty_restore__truncates) @@ -1468,6 +1528,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_body_count__non_empty_restore__truncates) body_store.buffer() = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("123456")); + BOOST_REQUIRE(!instance.get_error()); } ////std::cout << head_file << std::endl << std::endl; diff --git a/test/primitives/manager.cpp b/test/primitives/manager.cpp index dee753a8..9570bcda 100644 --- a/test/primitives/manager.cpp +++ b/test/primitives/manager.cpp @@ -34,6 +34,7 @@ BOOST_AUTO_TEST_CASE(manager__count__empty_slab__zero) test::chunk_storage file; const manager, key1, max_size_t> instance(file); BOOST_REQUIRE(is_zero(instance.count())); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__count__non_empty_slab__expected) @@ -45,6 +46,7 @@ BOOST_AUTO_TEST_CASE(manager__count__non_empty_slab__expected) // Slab sizing is byte-based (arbitrary, links are file offsets). const manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.count(), expected); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__terminal_slab__false_unchanged) @@ -54,6 +56,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__terminal_slab__false_unchanged) manager, key1, max_size_t> instance(file); BOOST_REQUIRE(!instance.truncate(linkage<4>::terminal)); BOOST_REQUIRE_EQUAL(instance.count(), zero); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__overflow_slab__false_unchanged) @@ -64,6 +67,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__overflow_slab__false_unchanged) manager, key1, max_size_t> instance(file); BOOST_REQUIRE(!instance.truncate(add1(size))); BOOST_REQUIRE_EQUAL(instance.count(), size); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__half_full_slab__true_changed) @@ -82,6 +86,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__half_full_slab__true_changed) BOOST_REQUIRE(instance.truncate(0)); BOOST_REQUIRE_EQUAL(instance.count(), 0u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__eof_slab__terminal_unchanged) @@ -91,6 +96,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__eof_slab__terminal_unchanged) manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.allocate(storage::eof), linkage<7>::terminal); BOOST_REQUIRE_EQUAL(instance.count(), zero); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__terminal_slab__terminal_unchanged) @@ -100,6 +106,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__terminal_slab__terminal_unchanged) manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.allocate(linkage<4>::terminal), linkage<4>::terminal); BOOST_REQUIRE_EQUAL(instance.count(), zero); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__empty_slab__expected) @@ -110,6 +117,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__empty_slab__expected) manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.allocate(expected), zero); BOOST_REQUIRE_EQUAL(instance.count(), expected); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__non_empty_slab__expected) @@ -120,6 +128,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__non_empty_slab__expected) manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.allocate(to_half(expected)), to_half(expected)); BOOST_REQUIRE_EQUAL(instance.count(), expected); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__get__terminal_slab__terminal) @@ -130,6 +139,7 @@ BOOST_AUTO_TEST_CASE(manager__get__terminal_slab__terminal) const manager, key1, max_size_t> instance(file); BOOST_REQUIRE_EQUAL(instance.count(), size); BOOST_REQUIRE(!instance.get(linkage<2>::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__get__slab__expected) @@ -148,6 +158,7 @@ BOOST_AUTO_TEST_CASE(manager__get__slab__expected) BOOST_REQUIRE_EQUAL(*instance.get(1)->begin(), 0x01_u8); BOOST_REQUIRE_EQUAL(*instance.get(2)->begin(), 0x02_u8); BOOST_REQUIRE_EQUAL(*instance.get(9)->begin(), 0x09_u8); + BOOST_REQUIRE(!instance.get_error()); } // records @@ -157,6 +168,7 @@ BOOST_AUTO_TEST_CASE(manager__count__empty_record__zero) test::chunk_storage file; const manager, key1, 42> instance(file); BOOST_REQUIRE(is_zero(instance.count())); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__count__1_record__expected) @@ -170,6 +182,7 @@ BOOST_AUTO_TEST_CASE(manager__count__1_record__expected) // Record sizing is record count-based (links are record counters). const manager, key1, bytes> instance(file); BOOST_REQUIRE_EQUAL(instance.count(), 1u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__count__33_record__expected) @@ -181,6 +194,7 @@ BOOST_AUTO_TEST_CASE(manager__count__33_record__expected) test::chunk_storage file(buffer); const manager, key2, bytes> instance(file); BOOST_REQUIRE_EQUAL(instance.count(), 33u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__terminal_record__false_unchanged) @@ -190,6 +204,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__terminal_record__false_unchanged) manager, key0, 5u> instance(file); BOOST_REQUIRE(!instance.truncate(linkage<2>::terminal)); BOOST_REQUIRE_EQUAL(instance.count(), zero); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__overflow_record__false_unchanged) @@ -200,6 +215,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__overflow_record__false_unchanged) BOOST_REQUIRE_EQUAL(instance.count(), 1u); BOOST_REQUIRE(!instance.truncate(2)); BOOST_REQUIRE_EQUAL(instance.count(), 1u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__truncate__half_full_record__true_changed) @@ -217,6 +233,7 @@ BOOST_AUTO_TEST_CASE(manager__truncate__half_full_record__true_changed) BOOST_REQUIRE(instance.truncate(0)); BOOST_REQUIRE_EQUAL(instance.count(), 0u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__terminal_empty_record__terminal_unchanged) @@ -226,6 +243,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__terminal_empty_record__terminal_unchange manager, key0, 5u> instance(file); BOOST_REQUIRE_EQUAL(instance.allocate(linkage<2>::terminal), linkage<2>::terminal); BOOST_REQUIRE_EQUAL(instance.count(), zero); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__terminal_non_empty_record__expected) @@ -237,6 +255,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__terminal_non_empty_record__expected) BOOST_REQUIRE_EQUAL(instance.count(), 2u); BOOST_REQUIRE_EQUAL(instance.allocate(linkage<2>::terminal), linkage<2>::terminal); BOOST_REQUIRE_EQUAL(instance.count(), 2u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__empty_record__expected) @@ -248,6 +267,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__empty_record__expected) BOOST_REQUIRE_EQUAL(instance.count(), 1u); BOOST_REQUIRE_EQUAL(instance.allocate(2), 1u); BOOST_REQUIRE_EQUAL(instance.count(), 3u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__allocate__non_empty_record__expected) @@ -259,6 +279,7 @@ BOOST_AUTO_TEST_CASE(manager__allocate__non_empty_record__expected) BOOST_REQUIRE_EQUAL(instance.count(), 2u); BOOST_REQUIRE_EQUAL(instance.allocate(2), 2u); BOOST_REQUIRE_EQUAL(instance.count(), 4u); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__get__terminal_record__terminal) @@ -268,6 +289,7 @@ BOOST_AUTO_TEST_CASE(manager__get__terminal_record__terminal) const manager, key0, 5u> instance(file); BOOST_REQUIRE_EQUAL(instance.count(), 2u); BOOST_REQUIRE(!instance.get(linkage<2>::terminal)); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_CASE(manager__get__record__expected) @@ -283,6 +305,7 @@ BOOST_AUTO_TEST_CASE(manager__get__record__expected) BOOST_REQUIRE_EQUAL(instance.count(), 2u); BOOST_REQUIRE_EQUAL(*instance.get(0)->begin(), 0x00_u8); BOOST_REQUIRE_EQUAL(*instance.get(1)->begin(), 0x06_u8); + BOOST_REQUIRE(!instance.get_error()); } BOOST_AUTO_TEST_SUITE_END() From fd0de967b9d8d47dbb9fe3f4713e9afe7b6674e2 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 2 May 2024 19:45:25 -0400 Subject: [PATCH 4/5] Add error condition methods, rename disassociate() to setter. --- .../database/impl/primitives/arraymap.ipp | 2 +- .../bitcoin/database/impl/query/archive.ipp | 2 +- include/bitcoin/database/impl/query/query.ipp | 6 ++ include/bitcoin/database/impl/store.ipp | 62 +++++++++++++++++++ include/bitcoin/database/query.hpp | 6 +- include/bitcoin/database/store.hpp | 6 ++ test/query/archive.cpp | 8 +-- test/query/extent.cpp | 8 +++ test/store.cpp | 10 +++ 9 files changed, 103 insertions(+), 7 deletions(-) diff --git a/include/bitcoin/database/impl/primitives/arraymap.ipp b/include/bitcoin/database/impl/primitives/arraymap.ipp index 44a7fe27..cf7a139b 100644 --- a/include/bitcoin/database/impl/primitives/arraymap.ipp +++ b/include/bitcoin/database/impl/primitives/arraymap.ipp @@ -103,7 +103,7 @@ bool CLASS::truncate(const Link& count) NOEXCEPT return manager_.truncate(count); } -// query interface +// error condition // ---------------------------------------------------------------------------- TEMPLATE diff --git a/include/bitcoin/database/impl/query/archive.ipp b/include/bitcoin/database/impl/query/archive.ipp index ee52d92b..20cb0092 100644 --- a/include/bitcoin/database/impl/query/archive.ipp +++ b/include/bitcoin/database/impl/query/archive.ipp @@ -951,7 +951,7 @@ txs_link CLASS::set_link(const transactions& txs, } TEMPLATE -bool CLASS::dissasociate(const header_link& link) NOEXCEPT +bool CLASS::set_dissasociated(const header_link& link) NOEXCEPT { if (link.is_terminal()) return false; diff --git a/include/bitcoin/database/impl/query/query.ipp b/include/bitcoin/database/impl/query/query.ipp index 0406dca1..ae1bbadc 100644 --- a/include/bitcoin/database/impl/query/query.ipp +++ b/include/bitcoin/database/impl/query/query.ipp @@ -74,6 +74,12 @@ CLASS::query(Store& value) NOEXCEPT { } +TEMPLATE +bool CLASS::is_full() const NOEXCEPT +{ + return store_.get_error(error::disk_full); +} + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/store.ipp b/include/bitcoin/database/impl/store.ipp index c982cb8b..25629d5b 100644 --- a/include/bitcoin/database/impl/store.ipp +++ b/include/bitcoin/database/impl/store.ipp @@ -37,6 +37,9 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) // so establish 1 as the minimum value (which also implies disabled). constexpr auto nonzero = 1_u32; +// public +// ---------------------------------------------------------------------------- + TEMPLATE CLASS::store(const settings& config) NOEXCEPT : configuration_(config), @@ -396,6 +399,65 @@ const typename CLASS::transactor CLASS::get_transactor() NOEXCEPT return transactor{ transactor_mutex_ }; } +TEMPLATE +bool CLASS::get_error(const code& ec) const NOEXCEPT +{ + // A disk full error will not leave a flush lock, but others will. + // There may be other error codes as well so check all. + + bool found{}; + const auto match = [&ec, &found](const auto& storage) NOEXCEPT + { + const auto error = storage.get_error(); + if (error == ec) found = true; + return !error || found; + }; + + return match(header_body_) + && match(point_body_) + && match(input_body_) + && match(output_body_) + && match(puts_body_) + && match(tx_body_) + && match(txs_body_) + && match(address_body_) + && match(candidate_body_) + && match(confirmed_body_) + && match(spend_body_) + && match(strong_tx_body_) + && match(validated_bk_body_) + && match(validated_tx_body_) + && match(neutrino_body_) + ////&& match(bootstrap_body_) + ////&& match(buffer_body_) + && found; +} + +TEMPLATE +void CLASS::clear_error() NOEXCEPT +{ + header_body_.clear_error(); + point_body_.clear_error(); + input_body_.clear_error(); + output_body_.clear_error(); + puts_body_.clear_error(); + tx_body_.clear_error(); + txs_body_.clear_error(); + address_body_.clear_error(); + candidate_body_.clear_error(); + confirmed_body_.clear_error(); + spend_body_.clear_error(); + strong_tx_body_.clear_error(); + validated_bk_body_.clear_error(); + validated_tx_body_.clear_error(); + neutrino_body_.clear_error(); + ////bootstrap_body_.clear_error(); + ////buffer_body_.clear_error(); +} + +// protected +// ---------------------------------------------------------------------------- + TEMPLATE code CLASS::open_load(const event_handler& handler) NOEXCEPT { diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index e46817ed..1c5d01b3 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -53,6 +53,7 @@ using two_counts = std::pair; struct strong_pair { header_link block; tx_link tx; }; using strong_pairs = std_vector; +// Writers (non-const) are only: push_, pop_, set_ and initialize. template class query { @@ -81,6 +82,9 @@ class query query(Store& value) NOEXCEPT; + /// True if disk is full and no other store error condition. + bool is_full() const NOEXCEPT; + /// Initialization (natural-keyed). /// ----------------------------------------------------------------------- /// Not reliable during organization. @@ -296,7 +300,7 @@ class query txs_link set_link(const transactions& txs, const header_link& link, size_t size) NOEXCEPT; tx_link set_link(const transaction& tx) NOEXCEPT; - bool dissasociate(const header_link& link) NOEXCEPT; + bool set_dissasociated(const header_link& link) NOEXCEPT; /// Chain state. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp index 6fff17d5..b9fae125 100644 --- a/include/bitcoin/database/store.hpp +++ b/include/bitcoin/database/store.hpp @@ -66,6 +66,12 @@ class store /// Get a transactor object. const transactor get_transactor() NOEXCEPT; + /// Detect the specified exclusive error condition. + bool get_error(const code& ec) const NOEXCEPT; + + /// Clear all error conditions. + void clear_error() NOEXCEPT; + /// Archives. table::header header; table::point point; diff --git a/test/query/archive.cpp b/test/query/archive.cpp index 34f825b0..53946f34 100644 --- a/test/query/archive.cpp +++ b/test/query/archive.cpp @@ -761,7 +761,7 @@ BOOST_AUTO_TEST_CASE(query_archive__set_block_txs__get_block__expected) BOOST_REQUIRE_EQUAL(hashes, test::genesis.transaction_hashes(false)); BOOST_REQUIRE(!query.is_malleable(0)); - BOOST_REQUIRE(query.dissasociate(0)); + BOOST_REQUIRE(query.set_dissasociated(0)); BOOST_REQUIRE(!query.is_associated(0)); BOOST_REQUIRE(!query.is_malleable(0)); } @@ -983,9 +983,9 @@ BOOST_AUTO_TEST_CASE(query_archive__is_malleable__malleable__true) BOOST_REQUIRE(query.is_malleated(block3)); // Disassociate 3 blocks. - BOOST_REQUIRE(query.dissasociate(query.to_header(block1.hash()))); - BOOST_REQUIRE(query.dissasociate(query.to_header(block2.hash()))); - BOOST_REQUIRE(query.dissasociate(query.to_header(block3.hash()))); + BOOST_REQUIRE(query.set_dissasociated(query.to_header(block1.hash()))); + BOOST_REQUIRE(query.set_dissasociated(query.to_header(block2.hash()))); + BOOST_REQUIRE(query.set_dissasociated(query.to_header(block3.hash()))); // Verify all 3 not associated. BOOST_REQUIRE(!query.is_associated(1)); diff --git a/test/query/extent.cpp b/test/query/extent.cpp index 881da839..37e9f3f5 100644 --- a/test/query/extent.cpp +++ b/test/query/extent.cpp @@ -43,6 +43,14 @@ BOOST_FIXTURE_TEST_SUITE(query_extent_tests, query_extent_setup_fixture) // nop event handler. const auto events = [](auto, auto) {}; +BOOST_AUTO_TEST_CASE(query__is_full__chunk_store__false) +{ + const settings configuration{}; + test::chunk_store store{ configuration }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!query.is_full()); +} + BOOST_AUTO_TEST_CASE(query_extent__sizes__genesis__expected) { settings settings{}; diff --git a/test/store.cpp b/test/store.cpp index a1196f16..f57cee3f 100644 --- a/test/store.cpp +++ b/test/store.cpp @@ -112,6 +112,16 @@ BOOST_AUTO_TEST_CASE(store__paths__default_configuration__expected) BOOST_REQUIRE_EQUAL(instance.process_lock_file(), "bitcoin/process.lock"); } +// get_error +// ---------------------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(store__get_error__disk_full__false) +{ + const settings configuration{}; + test::map_store instance{ configuration }; + BOOST_REQUIRE(!instance.get_error(error::disk_full)); +} + // create // ---------------------------------------------------------------------------- From 5526a87248e06f8534b6de83d0eb6866b54223f7 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Fri, 3 May 2024 13:44:20 -0400 Subject: [PATCH 5/5] Fix error suppression in spend key generation. --- .../bitcoin/database/impl/query/archive.ipp | 85 ++++++++++++------- include/bitcoin/database/query.hpp | 4 - 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/include/bitcoin/database/impl/query/archive.ipp b/include/bitcoin/database/impl/query/archive.ipp index 20cb0092..a99d2687 100644 --- a/include/bitcoin/database/impl/query/archive.ipp +++ b/include/bitcoin/database/impl/query/archive.ipp @@ -656,25 +656,6 @@ typename CLASS::inputs_ptr CLASS::get_spenders(const tx_link& link, return get_spenders(to_output(link, output_index)); } -TEMPLATE -inline point_link CLASS::set_link_(const hash_digest& point_hash) NOEXCEPT -{ - if (point_hash == system::null_hash) - return {}; - - // Reuse if archived (always - this is a compression, not a guard). - auto point_fk = to_point(point_hash); - if (!point_fk.is_terminal()) - return point_fk; - - // This write is NOT transacted as it is only called from set_link(tx) - const table::point::record empty{}; - if (!store_.point.put_link(point_fk, point_hash, empty)) - return {}; - - return point_fk; -} - // TODO: rename/change spend to archive table. // The only multitable write, all archive except header, also address. TEMPLATE @@ -725,14 +706,38 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT // Commit input record. // Safe allocation failure, blob linked by unindexed spend. input_link input_fk{}; - if (!store_.input.put_link(input_fk, table::input::put_ref{ {}, *in })) + if (!store_.input.put_link(input_fk, table::input::put_ref + { + {}, + *in + })) + { return {}; + } - // Create point and accumulate spend keys. - // Safe allocation failure, duplicates are guarded and expected. + // Input point aliases. const auto& prevout = in->point(); - spends.push_back(table::spend::compose(set_link_(prevout.hash()), - prevout.index())); + const auto& hash = prevout.hash(); + + // Get or create prevout hash in point table (reduces duplicates). + point_link hash_fk{}; + if (hash != null_hash) + { + hash_fk = to_point(hash); + if (hash_fk.is_terminal()) + { + // Safe allocation failure, duplicates limited but expected. + if (!store_.point.put_link(hash_fk, hash, table::point::record + { + })) + { + return {}; + } + } + } + + // Accumulate spend keys in order (terminal for any null point). + spends.push_back(table::spend::compose(hash_fk, prevout.index())); // Write spend record. // Safe allocation failure, index is deferred because invalid tx_fk. @@ -747,7 +752,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT return {}; } - // Acumulate input (spend) in order. + // Acumulate spends (input references) in order. puts.spend_fks.push_back(spend_fk.value++); } @@ -766,7 +771,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT return {}; } - // Acumulate output in order. + // Acumulate outputs in order. puts.out_fks.push_back(output_fk); } @@ -792,10 +797,10 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT } // Commit spends to search. - // Safe allocation failure, unindexed txs linked by spend, others - // unlinked. A replay of committed spends without indexed tx will appear as - // double spends, but the spend cannot be confirmed without the indexed tx. - // Spends without indexed txs should be suppressed by c/s interface query. + // Safe allocation failure, unindexed txs linked by spend, others unlinked. + // A replay of committed spends without indexed tx will appear as double + // spends, but the spend cannot be confirmed without the indexed tx. Spends + // without indexed txs should be suppressed by c/s interface query. for (const auto& spend: views_reverse(spends)) { --spend_fk.value; @@ -826,7 +831,7 @@ tx_link CLASS::set_link(const transaction& tx) NOEXCEPT } // Commit tx to search. - // Clean single allocation failure, see also above. (e.g. disk full). + // Clean single allocation failure (e.g. disk full). return store_.tx.commit_link(tx_fk, key); // ======================================================================== } @@ -945,8 +950,15 @@ txs_link CLASS::set_link(const transactions& txs, // ======================================================================== const auto scope = store_.get_transactor(); + // Header link is the key for the txs table. // Clean single allocation failure (e.g. disk full). - return store_.txs.put_link(link, table::txs::slab{ {}, malleable, wire, links }); + return store_.txs.put_link(link, table::txs::slab + { + {}, + malleable, + wire, + links + }); // ======================================================================== } @@ -961,8 +973,15 @@ bool CLASS::set_dissasociated(const header_link& link) NOEXCEPT // ======================================================================== const auto scope = store_.get_transactor(); + // Header link is the key for the txs table. // Clean single allocation failure (e.g. disk full). - return store_.txs.put_link(link, table::txs::slab{ {}, malleable, {}, {} }); + return store_.txs.put(link, table::txs::slab + { + {}, + malleable, + {}, + {} + }); // ======================================================================== } diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 1c5d01b3..edc9eae5 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -423,10 +423,6 @@ class query inline strong_pair to_strong(const hash_digest& tx_hash) const NOEXCEPT; inline strong_pairs to_strongs(const hash_digest& tx_hash) const NOEXCEPT; - /// Archival - /// ----------------------------------------------------------------------- - point_link set_link_(const hash_digest& point_hash) NOEXCEPT; - /// Validate. /// ----------------------------------------------------------------------- inline code to_block_code(linkage::integer value) const NOEXCEPT;