#include "backend.h" #include "reopen.h" #include #include #include #include #include #include #include #include namespace { struct TMockLogBackend : public TLogBackend { void WriteData(const TLogRecord& rec) override { BytesWritten.fetch_add(rec.Len); } void ReopenLog() override { NumReopens.fetch_add(1); } std::atomic BytesWritten{0}; std::atomic NumReopens{0}; }; void WriteData(TReopenLogBackend& log, const TString& data) { log.WriteData(TLogRecord(ELogPriority::TLOG_INFO, data.data(), data.size())); } } Y_UNIT_TEST_SUITE(ReopenLogSuite) { Y_UNIT_TEST(TestSimple) { constexpr ui64 limit = 5; const auto testData = {"test", "dshkafhuadshfiasuh", "log", "data"}; constexpr ui64 expectedReopens = 2; // considering the limit, the first reopen after the second string and one more at the end auto mockHolder = MakeHolder(); auto& mock = *mockHolder; TReopenLogBackend log(std::move(mockHolder), limit); ui64 expectedWritten = 0; for (const TString str : testData) { WriteData(log, str); expectedWritten += str.size(); } UNIT_ASSERT(mock.BytesWritten.load() == expectedWritten); UNIT_ASSERT(mock.NumReopens.load() == expectedReopens); } Y_UNIT_TEST(TestSingleThreaded) { constexpr ui64 limit = 1_KB; constexpr ui64 numLogs = 123; constexpr ui64 logSize = 1_KB / 4; static_assert((limit / logSize) * logSize == limit); // should be divisible for this test constexpr ui64 expectedWritten = numLogs * logSize; constexpr ui64 expectedReopens = expectedWritten / limit; auto mockHolder = MakeHolder(); auto& mock = *mockHolder; TReopenLogBackend log(std::move(mockHolder), limit); for (ui64 i = 0; i < numLogs; ++i) { WriteData(log, TString(logSize, 'a')); } UNIT_ASSERT(mock.BytesWritten.load() == expectedWritten); UNIT_ASSERT(mock.NumReopens.load() == expectedReopens); } Y_UNIT_TEST(TestMultiThreaded) { constexpr ui64 limit = 1_KB; constexpr ui64 numLogsPerThread = 123; constexpr ui64 numThreads = 12; constexpr ui64 logSize = 1_KB / 4; static_assert((limit / logSize) * logSize == limit); // should be divisible for this test constexpr ui64 expectedWritten = numLogsPerThread * numThreads * logSize; // can't guarantee consistent number of reopens every N bytes in multithreaded setting constexpr ui64 minExpectedReopens = limit / logSize; constexpr ui64 maxExpectedReopens = expectedWritten / limit; auto mockHolder = MakeHolder(); auto& mock = *mockHolder; TReopenLogBackend log(std::move(mockHolder), limit); std::barrier barrier(numThreads); const auto job = [&]() { barrier.arrive_and_wait(); for (ui64 i = 0; i < numLogsPerThread; ++i) { WriteData(log, TString(logSize, 'a')); } }; std::vector> jobs; for (ui64 i = 0; i < numThreads; ++i) { jobs.emplace_back(std::async(std::launch::async, job)); } for (auto& res : jobs) { res.wait(); } UNIT_ASSERT(mock.BytesWritten.load() == expectedWritten); UNIT_ASSERT(mock.NumReopens.load() >= minExpectedReopens); UNIT_ASSERT(mock.NumReopens.load() <= maxExpectedReopens); } Y_UNIT_TEST(TestZeroThrows) { UNIT_ASSERT_EXCEPTION(TReopenLogBackend(MakeHolder(), 0), std::exception); } }