feat: Graceful shutdown (#1801)

Fixes #442.
This commit is contained in:
Sergey Kuznetsov
2025-01-22 13:09:16 +00:00
committed by GitHub
parent 12e6fcc97e
commit 957028699b
41 changed files with 1073 additions and 191 deletions

View File

@@ -21,10 +21,14 @@
#include "util/LoggerFixtures.hpp"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <chrono>
#include <optional>
@@ -94,6 +98,38 @@ struct SyncAsioContextTest : virtual public NoLoggerFixture {
runContext();
}
template <typename F>
void
runSpawnWithTimeout(std::chrono::steady_clock::duration timeout, F&& f, bool allowMockLeak = false)
{
using namespace boost::asio;
boost::asio::io_context timerCtx;
steady_timer timer{timerCtx, timeout};
spawn(timerCtx, [this, &timer](yield_context yield) {
boost::system::error_code errorCode;
timer.async_wait(yield[errorCode]);
ctx_.stop();
EXPECT_TRUE(false) << "Test timed out";
});
std::thread timerThread{[&timerCtx]() { timerCtx.run(); }};
testing::MockFunction<void()> call;
if (allowMockLeak)
testing::Mock::AllowLeak(&call);
spawn(ctx_, [&](yield_context yield) {
f(yield);
call.Call();
});
EXPECT_CALL(call, Call());
runContext();
timerCtx.stop();
timerThread.join();
}
void
runContext()
{
@@ -108,6 +144,15 @@ struct SyncAsioContextTest : virtual public NoLoggerFixture {
ctx_.reset();
}
template <typename F>
static void
runSyncOperation(F&& f)
{
boost::asio::io_service ioc;
boost::asio::spawn(ioc, f);
ioc.run();
}
protected:
boost::asio::io_context ctx_;
};

View File

@@ -211,6 +211,8 @@ struct MockBackend : public BackendInterface {
MOCK_METHOD(void, doWriteLedgerObject, (std::string&&, std::uint32_t const, std::string&&), (override));
MOCK_METHOD(void, waitForWritesToFinish, (), (override));
MOCK_METHOD(bool, doFinishWrites, (), (override));
MOCK_METHOD(void, writeMPTHolders, (std::vector<MPTHolderData> const&), (override));

View File

@@ -23,7 +23,6 @@
#include "etl/Source.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "rpc/Errors.hpp"
#include "util/newconfig/ConfigDefinition.hpp"
#include "util/newconfig/ObjectView.hpp"
#include <boost/asio/io_context.hpp>
@@ -49,6 +48,7 @@
struct MockSource : etl::SourceBase {
MOCK_METHOD(void, run, (), (override));
MOCK_METHOD(void, stop, (boost::asio::yield_context), (override));
MOCK_METHOD(bool, isConnected, (), (const, override));
MOCK_METHOD(void, setForwarding, (bool), (override));
MOCK_METHOD(boost::json::object, toJson, (), (const, override));
@@ -89,6 +89,12 @@ public:
mock_->run();
}
void
stop(boost::asio::yield_context yield) override
{
mock_->stop(yield);
}
bool
isConnected() const override
{

View File

@@ -102,6 +102,8 @@ struct MockSubscriptionManager : feed::SubscriptionManagerInterface {
MOCK_METHOD(void, unsubProposedTransactions, (feed::SubscriberSharedPtr const&), (override));
MOCK_METHOD(boost::json::object, report, (), (const, override));
MOCK_METHOD(void, stop, (), (override));
};
template <template <typename> typename MockType = ::testing::NiceMock>

View File

@@ -54,7 +54,7 @@ struct MockConnectionImpl : web::ng::Connection {
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>;
MOCK_METHOD(ReceiveReturnType, receive, (boost::asio::yield_context), (override));
MOCK_METHOD(void, close, (boost::asio::yield_context));
MOCK_METHOD(void, close, (boost::asio::yield_context), (override));
};
using MockConnection = testing::NiceMock<MockConnectionImpl>;

View File

@@ -57,7 +57,7 @@ struct MockHttpConnectionImpl : web::ng::impl::UpgradableConnection {
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>;
MOCK_METHOD(ReceiveReturnType, receive, (boost::asio::yield_context), (override));
MOCK_METHOD(void, close, (boost::asio::yield_context));
MOCK_METHOD(void, close, (boost::asio::yield_context), (override));
using IsUpgradeRequestedReturnType = std::expected<bool, web::ng::Error>;
MOCK_METHOD(IsUpgradeRequestedReturnType, isUpgradeRequested, (boost::asio::yield_context), (override));

View File

@@ -47,7 +47,7 @@ struct MockWsConnectionImpl : web::ng::impl::WsConnectionBase {
using ReceiveReturnType = std::expected<web::ng::Request, web::ng::Error>;
MOCK_METHOD(ReceiveReturnType, receive, (boost::asio::yield_context), (override));
MOCK_METHOD(void, close, (boost::asio::yield_context));
MOCK_METHOD(void, close, (boost::asio::yield_context), (override));
using SendBufferReturnType = std::optional<web::ng::Error>;
MOCK_METHOD(SendBufferReturnType, sendBuffer, (boost::asio::const_buffer, boost::asio::yield_context), (override));