mirror of
				https://github.com/Xahau/xahaud.git
				synced 2025-11-04 02:35:48 +00:00 
			
		
		
		
	added configurable NuDB block size support in xahaud (#601)
This commit is contained in:
		@@ -965,6 +965,7 @@ if (tests)
 | 
			
		||||
    src/test/nodestore/Basics_test.cpp
 | 
			
		||||
    src/test/nodestore/DatabaseShard_test.cpp
 | 
			
		||||
    src/test/nodestore/Database_test.cpp
 | 
			
		||||
    src/test/nodestore/NuDBFactory_test.cpp
 | 
			
		||||
    src/test/nodestore/Timing_test.cpp
 | 
			
		||||
    src/test/nodestore/import_test.cpp
 | 
			
		||||
    src/test/nodestore/varint_test.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -1127,6 +1127,39 @@
 | 
			
		||||
#                           it must be defined with the same value in both
 | 
			
		||||
#                           sections.
 | 
			
		||||
#
 | 
			
		||||
#   Optional keys for NuDB only:
 | 
			
		||||
#
 | 
			
		||||
#       nudb_block_size     EXPERIMENTAL: Block size in bytes for NuDB storage.
 | 
			
		||||
#                           Must be a power of 2 between 4096 and 32768. Default is 4096.
 | 
			
		||||
#
 | 
			
		||||
#                           This parameter controls the fundamental storage unit
 | 
			
		||||
#                           size for NuDB's internal data structures. The choice
 | 
			
		||||
#                           of block size can significantly impact performance
 | 
			
		||||
#                           depending on your storage hardware and filesystem:
 | 
			
		||||
#
 | 
			
		||||
#                           - 4096 bytes: Optimal for most standard SSDs and
 | 
			
		||||
#                             traditional filesystems (ext4, NTFS, HFS+).
 | 
			
		||||
#                             Provides good balance of performance and storage
 | 
			
		||||
#                             efficiency. Recommended for most deployments.
 | 
			
		||||
#
 | 
			
		||||
#                           - 8192-16384 bytes: May improve performance on
 | 
			
		||||
#                             high-end NVMe SSDs and copy-on-write filesystems
 | 
			
		||||
#                             like ZFS or Btrfs that benefit from larger block
 | 
			
		||||
#                             alignment. Can reduce metadata overhead for large
 | 
			
		||||
#                             databases.
 | 
			
		||||
#
 | 
			
		||||
#                           - 32768 bytes (32K): Maximum supported block size
 | 
			
		||||
#                             for high-performance scenarios with very fast
 | 
			
		||||
#                             storage. May increase memory usage and reduce
 | 
			
		||||
#                             efficiency for smaller databases.
 | 
			
		||||
#
 | 
			
		||||
#                           Note: This setting cannot be changed after database
 | 
			
		||||
#                           creation without rebuilding the entire database.
 | 
			
		||||
#                           Choose carefully based on your hardware and expected
 | 
			
		||||
#                           database size.
 | 
			
		||||
#
 | 
			
		||||
#                           Example: nudb_block_size=4096
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#       These keys modify the behavior of online_delete, and thus are only
 | 
			
		||||
#       relevant if online_delete is defined and non-zero:
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
#include <ripple/nodestore/Types.h>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -175,6 +176,14 @@ public:
 | 
			
		||||
    virtual int
 | 
			
		||||
    fdRequired() const = 0;
 | 
			
		||||
 | 
			
		||||
    /** Get the block size for backends that support it
 | 
			
		||||
     */
 | 
			
		||||
    virtual std::optional<std::size_t>
 | 
			
		||||
    getBlockSize() const
 | 
			
		||||
    {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Returns read and write stats.
 | 
			
		||||
 | 
			
		||||
        @note The Counters struct is specific to and only used
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/contract.h>
 | 
			
		||||
#include <ripple/beast/core/LexicalCast.h>
 | 
			
		||||
#include <ripple/nodestore/Factory.h>
 | 
			
		||||
#include <ripple/nodestore/Manager.h>
 | 
			
		||||
#include <ripple/nodestore/impl/DecodedBlob.h>
 | 
			
		||||
@@ -31,6 +32,7 @@
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <nudb/nudb.hpp>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
@@ -48,6 +50,7 @@ public:
 | 
			
		||||
    size_t const keyBytes_;
 | 
			
		||||
    std::size_t const burstSize_;
 | 
			
		||||
    std::string const name_;
 | 
			
		||||
    std::size_t const blockSize_;
 | 
			
		||||
    nudb::store db_;
 | 
			
		||||
    std::atomic<bool> deletePath_;
 | 
			
		||||
    Scheduler& scheduler_;
 | 
			
		||||
@@ -62,6 +65,7 @@ public:
 | 
			
		||||
        , keyBytes_(keyBytes)
 | 
			
		||||
        , burstSize_(burstSize)
 | 
			
		||||
        , name_(get(keyValues, "path"))
 | 
			
		||||
        , blockSize_(parseBlockSize(name_, keyValues, journal))
 | 
			
		||||
        , deletePath_(false)
 | 
			
		||||
        , scheduler_(scheduler)
 | 
			
		||||
    {
 | 
			
		||||
@@ -81,6 +85,7 @@ public:
 | 
			
		||||
        , keyBytes_(keyBytes)
 | 
			
		||||
        , burstSize_(burstSize)
 | 
			
		||||
        , name_(get(keyValues, "path"))
 | 
			
		||||
        , blockSize_(parseBlockSize(name_, keyValues, journal))
 | 
			
		||||
        , db_(context)
 | 
			
		||||
        , deletePath_(false)
 | 
			
		||||
        , scheduler_(scheduler)
 | 
			
		||||
@@ -110,6 +115,12 @@ public:
 | 
			
		||||
        return name_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::optional<std::size_t>
 | 
			
		||||
    getBlockSize() const override
 | 
			
		||||
    {
 | 
			
		||||
        return blockSize_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    open(bool createIfMissing, uint64_t appType, uint64_t uid, uint64_t salt)
 | 
			
		||||
        override
 | 
			
		||||
@@ -137,7 +148,7 @@ public:
 | 
			
		||||
                uid,
 | 
			
		||||
                salt,
 | 
			
		||||
                keyBytes_,
 | 
			
		||||
                nudb::block_size(kp),
 | 
			
		||||
                blockSize_,
 | 
			
		||||
                0.50,
 | 
			
		||||
                ec);
 | 
			
		||||
            if (ec == nudb::errc::file_exists)
 | 
			
		||||
@@ -362,6 +373,56 @@ public:
 | 
			
		||||
    {
 | 
			
		||||
        return 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static std::size_t
 | 
			
		||||
    parseBlockSize(
 | 
			
		||||
        std::string const& name,
 | 
			
		||||
        Section const& keyValues,
 | 
			
		||||
        beast::Journal journal)
 | 
			
		||||
    {
 | 
			
		||||
        using namespace boost::filesystem;
 | 
			
		||||
        auto const folder = path(name);
 | 
			
		||||
        auto const kp = (folder / "nudb.key").string();
 | 
			
		||||
 | 
			
		||||
        std::size_t const defaultSize =
 | 
			
		||||
            nudb::block_size(kp);  // Default 4K from NuDB
 | 
			
		||||
        std::size_t blockSize = defaultSize;
 | 
			
		||||
        std::string blockSizeStr;
 | 
			
		||||
 | 
			
		||||
        if (!get_if_exists(keyValues, "nudb_block_size", blockSizeStr))
 | 
			
		||||
        {
 | 
			
		||||
            return blockSize;  // Early return with default
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            std::size_t const parsedBlockSize =
 | 
			
		||||
                beast::lexicalCastThrow<std::size_t>(blockSizeStr);
 | 
			
		||||
 | 
			
		||||
            // Validate: must be power of 2 between 4K and 32K
 | 
			
		||||
            if (parsedBlockSize < 4096 || parsedBlockSize > 32768 ||
 | 
			
		||||
                (parsedBlockSize & (parsedBlockSize - 1)) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream s;
 | 
			
		||||
                s << "Invalid nudb_block_size: " << parsedBlockSize
 | 
			
		||||
                  << ". Must be power of 2 between 4096 and 32768.";
 | 
			
		||||
                Throw<std::runtime_error>(s.str());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            JLOG(journal.info())
 | 
			
		||||
                << "Using custom NuDB block size: " << parsedBlockSize
 | 
			
		||||
                << " bytes";
 | 
			
		||||
            return parsedBlockSize;
 | 
			
		||||
        }
 | 
			
		||||
        catch (std::exception const& e)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream s;
 | 
			
		||||
            s << "Invalid nudb_block_size value: " << blockSizeStr
 | 
			
		||||
              << ". Error: " << e.what();
 | 
			
		||||
            Throw<std::runtime_error>(s.str());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										357
									
								
								src/test/nodestore/NuDBFactory_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								src/test/nodestore/NuDBFactory_test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,357 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    This file is part of rippled: https://github.com/ripple/rippled
 | 
			
		||||
    Copyright (c) 2012, 2013 Ripple Labs Inc.
 | 
			
		||||
 | 
			
		||||
    Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
    purpose  with  or without fee is hereby granted, provided that the above
 | 
			
		||||
    copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
 | 
			
		||||
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
    ANY  SPECIAL ,  DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#include <test/nodestore/TestBase.h>
 | 
			
		||||
#include <test/unit_test/SuiteJournal.h>
 | 
			
		||||
 | 
			
		||||
#include <ripple/basics/BasicConfig.h>
 | 
			
		||||
#include <ripple/basics/ByteUtilities.h>
 | 
			
		||||
#include <ripple/beast/utility/temp_dir.h>
 | 
			
		||||
#include <ripple/nodestore/DummyScheduler.h>
 | 
			
		||||
#include <ripple/nodestore/Manager.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace ripple {
 | 
			
		||||
namespace NodeStore {
 | 
			
		||||
 | 
			
		||||
class NuDBFactory_test : public TestBase
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    // Helper function to create a Section with specified parameters
 | 
			
		||||
    Section
 | 
			
		||||
    createSection(std::string const& path, std::string const& blockSize = "")
 | 
			
		||||
    {
 | 
			
		||||
        Section params;
 | 
			
		||||
        params.set("type", "nudb");
 | 
			
		||||
        params.set("path", path);
 | 
			
		||||
        if (!blockSize.empty())
 | 
			
		||||
            params.set("nudb_block_size", blockSize);
 | 
			
		||||
        return params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Helper function to create a backend and test basic functionality
 | 
			
		||||
    bool
 | 
			
		||||
    testBackendFunctionality(
 | 
			
		||||
        Section const& params,
 | 
			
		||||
        std::size_t expectedBlocksize)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            test::SuiteJournal journal("NuDBFactory_test", *this);
 | 
			
		||||
 | 
			
		||||
            auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                params, megabytes(4), scheduler, journal);
 | 
			
		||||
 | 
			
		||||
            if (!BEAST_EXPECT(backend))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (!BEAST_EXPECT(backend->getBlockSize() == expectedBlocksize))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            backend->open();
 | 
			
		||||
 | 
			
		||||
            if (!BEAST_EXPECT(backend->isOpen()))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            // Test basic store/fetch functionality
 | 
			
		||||
            auto batch = createPredictableBatch(10, 12345);
 | 
			
		||||
            storeBatch(*backend, batch);
 | 
			
		||||
 | 
			
		||||
            Batch copy;
 | 
			
		||||
            fetchCopyOfBatch(*backend, ©, batch);
 | 
			
		||||
 | 
			
		||||
            backend->close();
 | 
			
		||||
 | 
			
		||||
            return areBatchesEqual(batch, copy);
 | 
			
		||||
        }
 | 
			
		||||
        catch (...)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Helper function to test log messages
 | 
			
		||||
    void
 | 
			
		||||
    testLogMessage(
 | 
			
		||||
        Section const& params,
 | 
			
		||||
        beast::severities::Severity level,
 | 
			
		||||
        std::string const& expectedMessage)
 | 
			
		||||
    {
 | 
			
		||||
        test::StreamSink sink(level);
 | 
			
		||||
        beast::Journal journal(sink);
 | 
			
		||||
 | 
			
		||||
        DummyScheduler scheduler;
 | 
			
		||||
        auto backend = Manager::instance().make_Backend(
 | 
			
		||||
            params, megabytes(4), scheduler, journal);
 | 
			
		||||
 | 
			
		||||
        std::string logOutput = sink.messages().str();
 | 
			
		||||
        BEAST_EXPECT(logOutput.find(expectedMessage) != std::string::npos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    void
 | 
			
		||||
    testDefaultBlockSize()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Default block size (no nudb_block_size specified)");
 | 
			
		||||
 | 
			
		||||
        beast::temp_dir tempDir;
 | 
			
		||||
        auto params = createSection(tempDir.path());
 | 
			
		||||
 | 
			
		||||
        // Should work with default 4096 block size
 | 
			
		||||
        BEAST_EXPECT(testBackendFunctionality(params, 4096));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testValidBlockSizes()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Valid block sizes");
 | 
			
		||||
 | 
			
		||||
        std::vector<std::size_t> validSizes = {4096, 8192, 16384, 32768};
 | 
			
		||||
 | 
			
		||||
        for (auto const& size : validSizes)
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), to_string(size));
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(testBackendFunctionality(params, size));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testInvalidBlockSizes()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Invalid block sizes");
 | 
			
		||||
 | 
			
		||||
        std::vector<std::string> invalidSizes = {
 | 
			
		||||
            "2048",    // Too small
 | 
			
		||||
            "1024",    // Too small
 | 
			
		||||
            "65536",   // Too large
 | 
			
		||||
            "131072",  // Too large
 | 
			
		||||
            "5000",    // Not power of 2
 | 
			
		||||
            "6000",    // Not power of 2
 | 
			
		||||
            "10000",   // Not power of 2
 | 
			
		||||
            "0",       // Zero
 | 
			
		||||
            "-1",      // Negative
 | 
			
		||||
            "abc",     // Non-numeric
 | 
			
		||||
            "4k",      // Invalid format
 | 
			
		||||
            "4096.5"   // Decimal
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for (auto const& size : invalidSizes)
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), size);
 | 
			
		||||
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            test::SuiteJournal journal("NuDBFactory_test", *this);
 | 
			
		||||
 | 
			
		||||
            // Should throw exception for invalid sizes
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
                // If we get here, the test failed
 | 
			
		||||
                BEAST_EXPECT(false);
 | 
			
		||||
            }
 | 
			
		||||
            catch (std::exception const& e)
 | 
			
		||||
            {
 | 
			
		||||
                // Expected exception
 | 
			
		||||
                std::string error{e.what()};
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    error.find("Invalid nudb_block_size") != std::string::npos);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testLogMessages()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Log message verification");
 | 
			
		||||
 | 
			
		||||
        // Test valid custom block size logging
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), "8192");
 | 
			
		||||
 | 
			
		||||
            testLogMessage(
 | 
			
		||||
                params,
 | 
			
		||||
                beast::severities::kInfo,
 | 
			
		||||
                "Using custom NuDB block size: 8192");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Test invalid block size exception message
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), "5000");
 | 
			
		||||
 | 
			
		||||
            test::StreamSink sink(beast::severities::kWarning);
 | 
			
		||||
            beast::Journal journal(sink);
 | 
			
		||||
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
                fail();
 | 
			
		||||
            }
 | 
			
		||||
            catch (std::exception const& e)
 | 
			
		||||
            {
 | 
			
		||||
                std::string logOutput{e.what()};
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    logOutput.find("Invalid nudb_block_size: 5000") !=
 | 
			
		||||
                    std::string::npos);
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    logOutput.find(
 | 
			
		||||
                        "Must be power of 2 between 4096 and 32768") !=
 | 
			
		||||
                    std::string::npos);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Test non-numeric value exception message
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), "invalid");
 | 
			
		||||
 | 
			
		||||
            test::StreamSink sink(beast::severities::kWarning);
 | 
			
		||||
            beast::Journal journal(sink);
 | 
			
		||||
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
 | 
			
		||||
                fail();
 | 
			
		||||
            }
 | 
			
		||||
            catch (std::exception const& e)
 | 
			
		||||
            {
 | 
			
		||||
                std::string logOutput{e.what()};
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    logOutput.find("Invalid nudb_block_size value: invalid") !=
 | 
			
		||||
                    std::string::npos);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testPowerOfTwoValidation()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Power of 2 validation logic");
 | 
			
		||||
 | 
			
		||||
        // Test edge cases around valid range
 | 
			
		||||
        std::vector<std::pair<std::string, bool>> testCases = {
 | 
			
		||||
            {"4095", false},   // Just below minimum
 | 
			
		||||
            {"4096", true},    // Minimum valid
 | 
			
		||||
            {"4097", false},   // Just above minimum, not power of 2
 | 
			
		||||
            {"8192", true},    // Valid power of 2
 | 
			
		||||
            {"8193", false},   // Just above valid power of 2
 | 
			
		||||
            {"16384", true},   // Valid power of 2
 | 
			
		||||
            {"32768", true},   // Maximum valid
 | 
			
		||||
            {"32769", false},  // Just above maximum
 | 
			
		||||
            {"65536", false}   // Power of 2 but too large
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for (auto const& [size, shouldWork] : testCases)
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), size);
 | 
			
		||||
 | 
			
		||||
            test::StreamSink sink(beast::severities::kWarning);
 | 
			
		||||
            beast::Journal journal(sink);
 | 
			
		||||
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
                BEAST_EXPECT(shouldWork);
 | 
			
		||||
            }
 | 
			
		||||
            catch (std::exception const& e)
 | 
			
		||||
            {
 | 
			
		||||
                std::string logOutput{e.what()};
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    logOutput.find("Invalid nudb_block_size") !=
 | 
			
		||||
                    std::string::npos);
 | 
			
		||||
                BEAST_EXPECT(!shouldWork);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testDataPersistence()
 | 
			
		||||
    {
 | 
			
		||||
        testcase("Data persistence with different block sizes");
 | 
			
		||||
 | 
			
		||||
        std::vector<std::string> blockSizes = {
 | 
			
		||||
            "4096", "8192", "16384", "32768"};
 | 
			
		||||
 | 
			
		||||
        for (auto const& size : blockSizes)
 | 
			
		||||
        {
 | 
			
		||||
            beast::temp_dir tempDir;
 | 
			
		||||
            auto params = createSection(tempDir.path(), size);
 | 
			
		||||
 | 
			
		||||
            DummyScheduler scheduler;
 | 
			
		||||
            test::SuiteJournal journal("NuDBFactory_test", *this);
 | 
			
		||||
 | 
			
		||||
            // Create test data
 | 
			
		||||
            auto batch = createPredictableBatch(50, 54321);
 | 
			
		||||
 | 
			
		||||
            // Store data
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
                backend->open();
 | 
			
		||||
                storeBatch(*backend, batch);
 | 
			
		||||
                backend->close();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Retrieve data in new backend instance
 | 
			
		||||
            {
 | 
			
		||||
                auto backend = Manager::instance().make_Backend(
 | 
			
		||||
                    params, megabytes(4), scheduler, journal);
 | 
			
		||||
                backend->open();
 | 
			
		||||
 | 
			
		||||
                Batch copy;
 | 
			
		||||
                fetchCopyOfBatch(*backend, ©, batch);
 | 
			
		||||
 | 
			
		||||
                BEAST_EXPECT(areBatchesEqual(batch, copy));
 | 
			
		||||
                backend->close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    run() override
 | 
			
		||||
    {
 | 
			
		||||
        testDefaultBlockSize();
 | 
			
		||||
        testValidBlockSizes();
 | 
			
		||||
        testInvalidBlockSizes();
 | 
			
		||||
        testLogMessages();
 | 
			
		||||
        testPowerOfTwoValidation();
 | 
			
		||||
        testDataPersistence();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
BEAST_DEFINE_TESTSUITE(NuDBFactory, ripple_core, ripple);
 | 
			
		||||
 | 
			
		||||
}  // namespace NodeStore
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
		Reference in New Issue
	
	Block a user