From d240888ee7dc4cdfff68c8f4464ffc668d5bc999 Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Mon, 5 Aug 2024 19:36:07 +0300 Subject: [PATCH 1/9] session_pool_ut --- cmake/testing.cmake | 11 +- tests/unit/CMakeLists.txt | 1 + tests/unit/sessions_pool/CMakeLists.txt | 12 + tests/unit/sessions_pool/sessions_pool_ut.cpp | 451 ++++++++++++++++++ tests/unit/sessions_pool/ydb_common_ut.h | 367 ++++++++++++++ tests/unit/sessions_pool/ydb_keys_ut.h | 132 +++++ 6 files changed, 973 insertions(+), 1 deletion(-) create mode 100644 tests/unit/sessions_pool/CMakeLists.txt create mode 100644 tests/unit/sessions_pool/sessions_pool_ut.cpp create mode 100644 tests/unit/sessions_pool/ydb_common_ut.h create mode 100644 tests/unit/sessions_pool/ydb_keys_ut.h diff --git a/cmake/testing.cmake b/cmake/testing.cmake index ab20c3169a..1608fdfd84 100644 --- a/cmake/testing.cmake +++ b/cmake/testing.cmake @@ -69,8 +69,13 @@ endfunction() function(add_ydb_test) set(opts GTEST) +<<<<<<< HEAD set(oneval_args NAME WORKING_DIRECTORY OUTPUT_DIRECTORY) set(multival_args INCLUDE_DIRS SOURCES LINK_LIBRARIES LABELS TEST_ARG) +======= + set(oneval_args NAME) + set(multival_args SPLIT_FACTOR INCLUDE_DIRS SOURCES LINK_LIBRARIES LABELS) +>>>>>>> 273b4fe0c (session_pool_ut) cmake_parse_arguments(YDB_TEST "${opts}" "${oneval_args}" @@ -112,12 +117,16 @@ function(add_ydb_test) ) endif() + if(NOT DEFINED YDB_TEST_SPLIT_FACTOR) + set(YDB_TEST_SPLIT_FACTOR "1") + endif() + set_property( TARGET ${YDB_TEST_NAME} PROPERTY SPLIT_FACTOR - 1 + ${YDB_TEST_SPLIT_FACTOR} ) if (YDB_TEST_GTEST) add_yunittest( diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 1f0962419e..96ef94f4d1 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(client) add_subdirectory(library) +add_subdirectory(sessions_pool) diff --git a/tests/unit/sessions_pool/CMakeLists.txt b/tests/unit/sessions_pool/CMakeLists.txt new file mode 100644 index 0000000000..ab01de968f --- /dev/null +++ b/tests/unit/sessions_pool/CMakeLists.txt @@ -0,0 +1,12 @@ +add_ydb_test(NAME sessions_pool_ut + SPLIT_FACTOR 12 + SOURCES + sessions_pool_ut.cpp + LINK_LIBRARIES + yutil + cpp-testing-unittest_main + YDB-CPP-SDK::Table + api-grpc + LABELS + unit +) \ No newline at end of file diff --git a/tests/unit/sessions_pool/sessions_pool_ut.cpp b/tests/unit/sessions_pool/sessions_pool_ut.cpp new file mode 100644 index 0000000000..14df2a207c --- /dev/null +++ b/tests/unit/sessions_pool/sessions_pool_ut.cpp @@ -0,0 +1,451 @@ +#include +#include + +#include + +#include + +#include +#include + +using namespace NYdb; +using namespace NYdb::NTable; + +class TDefaultTestSetup { +public: + TDefaultTestSetup(ui32 maxActiveSessions) + : Driver_(NYdb::TDriver( + TDriverConfig().SetEndpoint( + std::getenv("YDB_ENDPOINT") + ) + )) + , Client_( + Driver_, + TClientSettings().SessionPoolSettings( + TSessionPoolSettings() + .MaxActiveSessions(maxActiveSessions) + .KeepAliveIdleThreshold(TDuration::MilliSeconds(10)) + .CloseIdleThreshold(TDuration::MilliSeconds(10)) + ) + ) + { + } + + ~TDefaultTestSetup() { + Driver_.Stop(true); + } + + NYdb::NTable::TTableClient& GetClient() { + return Client_; + } + +private: + NYdb::TDriver Driver_; + NYdb::NTable::TTableClient Client_; +}; + + +enum class EAction: ui8 { + CreateFuture, + ExtractValue, + Return +}; +using TPlan = std::vector>; + + +void CheckPlan(TPlan plan) { + std::unordered_map sessions; + for (const auto& [action, sessionId]: plan) { + if (action == EAction::CreateFuture) { + UNIT_ASSERT(!sessions.contains(sessionId)); + } else { + UNIT_ASSERT(sessions.contains(sessionId)); + switch (sessions.at(sessionId)) { + case EAction::CreateFuture: { + UNIT_ASSERT(action == EAction::ExtractValue); + break; + } + case EAction::ExtractValue: { + UNIT_ASSERT(action == EAction::Return); + break; + } + default: { + UNIT_ASSERT(false); + } + } + } + sessions[sessionId] = action; + } +} + +void RunPlan(const TPlan& plan, NYdb::NTable::TTableClient& client) { + std::unordered_map> sessionFutures; + std::unordered_map sessions; + + ui32 requestedSessions = 0; + + for (const auto& [action, sessionId]: plan) { + switch (action) { + case EAction::CreateFuture: { + sessionFutures.emplace(sessionId, client.GetSession()); + ++requestedSessions; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (requestedSessions > client.GetActiveSessionsLimit()) { + UNIT_ASSERT(client.GetActiveSessionCount() == client.GetActiveSessionsLimit()); + } + UNIT_ASSERT(!sessionFutures.at(sessionId).HasValue()); + break; + } + case EAction::ExtractValue: { + auto it = sessionFutures.find(sessionId); + auto session = it->second.ExtractValueSync(); + sessionFutures.erase(it); + sessions.emplace(sessionId, std::move(session)); + break; + } + case EAction::Return: { + sessions.erase(sessionId); + --requestedSessions; + break; + } + } + UNIT_ASSERT(client.GetActiveSessionCount() <= client.GetActiveSessionsLimit()); + UNIT_ASSERT(client.GetActiveSessionCount() >= static_cast(sessions.size())); + UNIT_ASSERT(client.GetActiveSessionCount() <= static_cast(sessions.size() + sessionFutures.size())); + } +} + +int GetRand(std::mt19937& rng, int min, int max) { + std::uniform_int_distribution dist(min, max); + return dist(rng); +} + + +TPlan GenerateRandomPlan(ui32 numSessions) { + TPlan plan; + std::random_device dev; + std::mt19937 rng(dev()); + + for (ui32 i = 0; i < numSessions; ++i) { + std::uniform_int_distribution dist(0, plan.size()); + ui32 prevPos = 0; + for (EAction action: {EAction::CreateFuture, EAction::ExtractValue, EAction::Return}) { + int pos = GetRand(rng, prevPos, plan.size()); + plan.emplace(plan.begin() + pos, std::make_pair(action, i)); + prevPos = pos + 1; + } + } + return plan; +} + + +Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { + Y_UNIT_TEST(Get1Session) { + TDefaultTestSetup setup(1); + auto& client = setup.GetClient(); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionsLimit(), 1); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 0); + + { + //TCreateSessionResult + auto session = client.GetSession().ExtractValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(session.GetStatus(), EStatus::SUCCESS); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 0); + } + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 1); + } + + void TestWaitQueue(NYdb::NTable::TTableClient& client, ui32 activeSessionsLimit) { + std::vector> sessionFutures; + std::vector sessions; + + // exhaust the pool + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessions.emplace_back(client.GetSession().ExtractValueSync()); + } + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); + + // next should be in the wait queue + for (ui32 i = 0; i < activeSessionsLimit * 10; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); + + // next should be a fake session + { + auto brokenSession = client.GetSession().ExtractValueSync(); + UNIT_ASSERT(!brokenSession.IsSuccess()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + for (auto& sessionFuture: sessionFutures) { + UNIT_ASSERT(!sessionFuture.HasValue()); + } + + for (auto& sessionFuture: sessionFutures) { + sessions.erase(sessions.begin()); + sessions.emplace_back(sessionFuture.ExtractValueSync()); + } + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); + } + + Y_UNIT_TEST(WaitQueue1) { + ui32 activeSessionsLimit = 1; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + TestWaitQueue(client, activeSessionsLimit); + } + + Y_UNIT_TEST(WaitQueue10) { + ui32 activeSessionsLimit = 10; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + TestWaitQueue(client, activeSessionsLimit); + } + + Y_UNIT_TEST(RunSmallPlan) { + TDefaultTestSetup setup(1); + auto& client = setup.GetClient(); + + TPlan plan{ + {EAction::CreateFuture, 1}, + {EAction::ExtractValue, 1}, + {EAction::CreateFuture, 2}, + {EAction::Return, 1}, + {EAction::ExtractValue, 2}, + {EAction::Return, 2} + }; + CheckPlan(plan); + RunPlan(plan, client); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 1); + } + + Y_UNIT_TEST(CustomPlan) { + TDefaultTestSetup setup(1); + auto& client = setup.GetClient(); + + TPlan plan{ + {EAction::CreateFuture, 1} + }; + CheckPlan(plan); + RunPlan(plan, client); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + } + + ui32 RunStressTestSync(ui32 n, ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { + std::vector> sessionFutures; + std::vector sessions; + std::mt19937 rng(0); + ui32 successCount = 0; + + for (ui32 i = 0; i < activeSessionsLimit * 12; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + + for (ui32 i = 0; i < n; ++i) { + switch (static_cast(GetRand(rng, 0, 2))) { + case EAction::CreateFuture: { + sessionFutures.emplace_back(client.GetSession()); + break; + } + case EAction::ExtractValue: { + if (sessionFutures.empty()) { + break; + } + auto ind = GetRand(rng, 0, sessionFutures.size() - 1); + auto sessionFuture = sessionFutures[ind]; + if (sessionFuture.HasValue()) { + auto session = sessionFuture.ExtractValueSync(); + if (session.IsSuccess()) { + ++successCount; + } + sessions.emplace_back(std::move(session)); + sessionFutures.erase(sessionFutures.begin() + ind); + break; + } + break; + } + case EAction::Return: { + if (sessions.empty()) { + break; + } + auto ind = GetRand(rng, 0, sessions.size() - 1); + sessions.erase(sessions.begin() + ind); + break; + } + } + } + return successCount; + } + + Y_UNIT_TEST(StressTestSync1) { + ui32 activeSessionsLimit = 1; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + RunStressTestSync(1000, activeSessionsLimit, client); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); + } + + Y_UNIT_TEST(StressTestSync10) { + ui32 activeSessionsLimit = 10; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + RunStressTestSync(1000, activeSessionsLimit, client); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); + } + + ui32 RunStressTestAsync(ui32 n, ui32 nThreads, NYdb::NTable::TTableClient& client) { + std::atomic successCount(0); + std::atomic jobIndex(0); + + auto job = [&client, &successCount, &jobIndex, n]() mutable { + std::mt19937 rng(++jobIndex); + for (ui32 i = 0; i < n; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + auto sessionFuture = client.GetSession(); + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + auto session = sessionFuture.ExtractValueSync(); + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + successCount += session.IsSuccess(); + } + }; + + IThreadFactory* pool = SystemThreadFactory(); + std::vector> threads; + threads.resize(nThreads); + for (ui32 i = 0; i < nThreads; i++) { + threads[i] = pool->Run(job); + } + for (ui32 i = 0; i < nThreads; i++) { + threads[i]->Join(); + } + + return successCount; + } + + Y_UNIT_TEST(StressTestAsync1) { + ui32 activeSessionsLimit = 1; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + RunStressTestAsync(100, 10, client); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); + } + + Y_UNIT_TEST(StressTestAsync10) { + ui32 activeSessionsLimit = 10; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + RunStressTestAsync(1000, 10, client); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); + } + + void TestPeriodicTask(ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { + std::vector> sessionFutures; + std::vector sessions; + + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessions.emplace_back(client.GetSession().ExtractValueSync()); + UNIT_ASSERT_VALUES_EQUAL(sessions.back().IsSuccess(), true); + } + + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + for (auto& sessionFuture : sessionFutures) { + UNIT_ASSERT(!sessionFuture.HasValue()); + } + + // Wait for wait session timeout + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + for (auto& sessionFuture : sessionFutures) { + UNIT_ASSERT(sessionFuture.HasValue()); + UNIT_ASSERT(!sessionFuture.ExtractValueSync().IsSuccess()); + } + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); + + sessionFutures.clear(); + sessions.clear(); + + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); + } + + Y_UNIT_TEST(PeriodicTask1) { + ui32 activeSessionsLimit = 1; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + TestPeriodicTask(activeSessionsLimit, client); + } + + Y_UNIT_TEST(PeriodicTask10) { + ui32 activeSessionsLimit = 10; + + TDefaultTestSetup setup(activeSessionsLimit); + auto& client = setup.GetClient(); + + TestPeriodicTask(activeSessionsLimit, client); + } + + Y_UNIT_TEST(FailTest) { + // This test reproduces bug from KIKIMR-18063 + TDefaultTestSetup setup(1); + auto& client = setup.GetClient(); + + auto sessionFromPool = client.GetSession().ExtractValueSync(); + auto futureInWaitPool = client.GetSession(); + + { + auto standaloneSessionThatWillBeBroken = client.CreateSession().ExtractValueSync(); + auto res = standaloneSessionThatWillBeBroken.GetSession().ExecuteDataQuery("SELECT COUNT(*) FROM `Root/Test`;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), + NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); + } + } +} diff --git a/tests/unit/sessions_pool/ydb_common_ut.h b/tests/unit/sessions_pool/ydb_common_ut.h new file mode 100644 index 0000000000..befad4d787 --- /dev/null +++ b/tests/unit/sessions_pool/ydb_common_ut.h @@ -0,0 +1,367 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ydb_keys_ut.h" + +#include // for WriteCSV() +#include + +#include + +using namespace NKikimr; +namespace NYdb { + +using namespace Tests; +using namespace NYdb; + +struct TKikimrTestSettings { + static constexpr bool SSL = false; + static constexpr bool AUTH = false; + static constexpr bool PrecreatePools = true; + static constexpr bool EnableSystemViews = true; + + static TString GetCaCrt() { return NYdbSslTestData::CaCrt; } + static TString GetServerCrt() { return NYdbSslTestData::ServerCrt; } + static TString GetServerKey() { return NYdbSslTestData::ServerKey; } + + static NKikimr::TCertificateAuthorizationParams GetCertAuthParams() {return {}; } +}; + +struct TKikimrTestWithAuth : TKikimrTestSettings { + static constexpr bool AUTH = true; +}; + +struct TKikimrTestWithAuthAndSsl : TKikimrTestWithAuth { + static constexpr bool SSL = true; +}; + +struct TKikimrTestWithServerCert : TKikimrTestWithAuthAndSsl { + static constexpr bool SSL = true; + + static const TCertAndKey& GetCACertAndKey() { + static const TCertAndKey ca = GenerateCA(TProps::AsCA()); + return ca; + } + + static const TCertAndKey& GetServerCert() { + static const TCertAndKey server = GenerateSignedCert(GetCACertAndKey(), TProps::AsServer()); + return server; + } + + static TString GetCaCrt() { + return GetCACertAndKey().Certificate.c_str(); + } + + static TString GetServerCrt() { + return GetServerCert().Certificate.c_str(); + } + + static TString GetServerKey() { + return GetServerCert().PrivateKey.c_str(); + } +}; + +struct TKikimrTestNoSystemViews : TKikimrTestSettings { + static constexpr bool EnableSystemViews = false; +}; + +template +class TBasicKikimrWithGrpcAndRootSchema { +public: + TBasicKikimrWithGrpcAndRootSchema( + NKikimrConfig::TAppConfig appConfig = {}, + const TVector& kqpSettings = {}, + TAutoPtr logBackend = {}, + bool enableYq = false, + TAppPrepare::TFnReg udfFrFactory = nullptr, + std::function builder = nullptr, + ui16 dynamicNodeCount = 2, ui16 nodeCount = 0) + { + ui16 port = PortManager.GetPort(2134); + ui16 grpc = PortManager.GetPort(2135); + + NKikimrProto::TAuthConfig authConfig = appConfig.GetAuthConfig(); + authConfig.SetUseBuiltinDomain(true); + ServerSettings = new TServerSettings(port, authConfig); + ServerSettings->SetGrpcPort(grpc); + ServerSettings->SetLogBackend(logBackend); + ServerSettings->SetDomainName("Root"); + ServerSettings->SetDynamicNodeCount(dynamicNodeCount); + if (nodeCount > 0) + ServerSettings->SetNodeCount(nodeCount); + if (TestSettings::PrecreatePools) { + ServerSettings->AddStoragePool("ssd"); + ServerSettings->AddStoragePool("hdd"); + ServerSettings->AddStoragePool("hdd1"); + ServerSettings->AddStoragePool("hdd2"); + } else { + ServerSettings->AddStoragePoolType("ssd"); + ServerSettings->AddStoragePoolType("hdd"); + ServerSettings->AddStoragePoolType("hdd1"); + ServerSettings->AddStoragePoolType("hdd2"); + } + ServerSettings->AppConfig->MergeFrom(appConfig); + ServerSettings->FeatureFlags = appConfig.GetFeatureFlags(); + ServerSettings->SetKqpSettings(kqpSettings); + ServerSettings->SetEnableDataColumnForIndexTable(true); + ServerSettings->SetEnableNotNullColumns(true); + ServerSettings->SetEnableSystemViews(TestSettings::EnableSystemViews); + ServerSettings->SetEnableYq(enableYq); + ServerSettings->Formats = new TFormatFactory; + ServerSettings->PQConfig = appConfig.GetPQConfig(); + if (appConfig.HasMeteringConfig() && appConfig.GetMeteringConfig().HasMeteringFilePath()) { + ServerSettings->SetMeteringFilePath(appConfig.GetMeteringConfig().GetMeteringFilePath()); + } + ServerSettings->RegisterGrpcService("dummy"); + if (udfFrFactory) { + ServerSettings->SetFrFactory(udfFrFactory); + } + if (builder) { + builder(*ServerSettings);; + } + + ServerCertificateFile.Write(TestSettings::GetServerCrt().data(), TestSettings::GetServerCrt().size()); + ServerSettings->ServerCertFilePath = ServerCertificateFile.Name(); + + Server_.Reset(new TServer(*ServerSettings)); + Tenants_.Reset(new Tests::TTenants(Server_)); + + //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_PROXY_SCHEME_CACHE, NActors::NLog::PRI_DEBUG); + //Server_->GetRuntime()->SetLogPriority(NKikimrServices::SCHEME_BOARD_REPLICA, NActors::NLog::PRI_DEBUG); + Server_->GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NActors::NLog::PRI_INFO); + //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_PROXY, NActors::NLog::PRI_DEBUG); + //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_OLAPSHARD, NActors::NLog::PRI_DEBUG); + //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_COLUMNSHARD, NActors::NLog::PRI_DEBUG); + if (enableYq) { + Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQL_PROXY, NActors::NLog::PRI_DEBUG); + Server_->GetRuntime()->SetLogPriority(NKikimrServices::KQP_COMPUTE, NActors::NLog::PRI_DEBUG); + Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQ_CONTROL_PLANE_STORAGE, NActors::NLog::PRI_DEBUG); + Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQ_CONTROL_PLANE_PROXY, NActors::NLog::PRI_DEBUG); + } + + NYdbGrpc::TServerOptions grpcOption; + if (TestSettings::AUTH) { + grpcOption.SetUseAuth(true); + } + grpcOption.SetPort(grpc); + if (TestSettings::SSL) { + NYdbGrpc::TSslData sslData; + sslData.Cert = TestSettings::GetServerCrt(); + sslData.Key = TestSettings::GetServerKey(); + sslData.Root =TestSettings::GetCaCrt(); + sslData.DoRequestClientCertificate = appConfig.GetClientCertificateAuthorization().GetRequestClientCertificate(); + + grpcOption.SetSslData(sslData); + } + Server_->EnableGRpc(grpcOption); + + TClient annoyingClient(*ServerSettings); + if (ServerSettings->AppConfig->GetDomainsConfig().GetSecurityConfig().GetEnforceUserTokenRequirement()) { + annoyingClient.SetSecurityToken("root@builtin"); + } + annoyingClient.InitRootScheme("Root"); + GRpcPort_ = grpc; + } + + ui16 GetPort() { + return GRpcPort_; + } + + TPortManager& GetPortManager() { + return PortManager; + } + + void ResetSchemeCache(TString path, ui32 nodeIndex = 0) { + TTestActorRuntime* runtime = Server_->GetRuntime(); + TClient annoyingClient(*ServerSettings); + annoyingClient.RefreshPathCache(runtime, path, nodeIndex); + } + + TTestActorRuntime* GetRuntime() { + return Server_->GetRuntime(); + } + + Tests::TServer& GetServer() { + return *Server_; + } + + TServerSettings::TPtr ServerSettings; + Tests::TServer::TPtr Server_; + THolder Tenants_; +private: + TPortManager PortManager; + ui16 GRpcPort_; + TTempFileHandle ServerCertificateFile; +}; + +struct TTestOlap { + static constexpr const char * StoreName = "OlapStore"; + static constexpr const char * TableName = "OlapTable"; + static constexpr const char * TablePath = "/Root/OlapStore/OlapTable"; + + static std::shared_ptr ArrowSchema( + std::shared_ptr tsType = arrow::timestamp(arrow::TimeUnit::TimeUnit::MICRO)) + { + return std::make_shared( + std::vector>{ + arrow::field("timestamp", tsType, false), + arrow::field("resource_type", arrow::utf8()), + arrow::field("resource_id", arrow::utf8()), + arrow::field("uid", arrow::utf8(), false), + arrow::field("level", arrow::int32()), + arrow::field("message", arrow::utf8()), + arrow::field("json_payload", arrow::binary()), + arrow::field("ingested_at", tsType), + arrow::field("saved_at", tsType), + arrow::field("request_id", arrow::utf8()) + }); + } + + static std::vector> PublicSchema(bool sort = false) { + std::vector> schema = { + { "timestamp", NYdb::EPrimitiveType::Timestamp }, + { "resource_type", NYdb::EPrimitiveType::Utf8 }, + { "resource_id", NYdb::EPrimitiveType::Utf8 }, + { "uid", NYdb::EPrimitiveType::Utf8 }, + { "level", NYdb::EPrimitiveType::Int32 }, + { "message", NYdb::EPrimitiveType::Utf8 }, + { "json_payload", NYdb::EPrimitiveType::JsonDocument }, + { "ingested_at", NYdb::EPrimitiveType::Timestamp }, + { "saved_at", NYdb::EPrimitiveType::Timestamp }, + { "request_id", NYdb::EPrimitiveType::Utf8 } + }; + + if (sort) { + std::sort(schema.begin(), schema.end(), [&](const std::pair& x, + const std::pair& y) { + return x.first < y.first; + }); + } + + return schema; + } + + static void CreateTable(const TServerSettings& settings, ui32 shards = 2, + const TString& storeName = StoreName, const TString& tableName = TableName) { + TString tableDescr = Sprintf(R"( + Name: "%s" + ColumnShardCount: 4 + SchemaPresets { + Name: "default" + Schema { + Columns { Name: "timestamp" Type: "Timestamp" NotNull : true } + Columns { Name: "resource_type" Type: "Utf8" } + Columns { Name: "resource_id" Type: "Utf8" } + Columns { Name: "uid" Type: "Utf8" NotNull : true } + Columns { Name: "level" Type: "Int32" } + Columns { Name: "message" Type: "Utf8" } + Columns { Name: "json_payload" Type: "JsonDocument" } + Columns { Name: "ingested_at" Type: "Timestamp" } + Columns { Name: "saved_at" Type: "Timestamp" } + Columns { Name: "request_id" Type: "Utf8" } + KeyColumnNames: "timestamp" + KeyColumnNames: "uid" + Engine: COLUMN_ENGINE_REPLACING_TIMESERIES + } + } + )", storeName.c_str()); + + TClient annoyingClient(settings); + annoyingClient.SetSecurityToken("root@builtin"); + NMsgBusProxy::EResponseStatus status = annoyingClient.CreateOlapStore("/Root", tableDescr); + UNIT_ASSERT_VALUES_EQUAL(status, NMsgBusProxy::EResponseStatus::MSTATUS_OK); + status = annoyingClient.CreateColumnTable("/Root", Sprintf(R"( + Name: "%s/%s" + ColumnShardCount : %d + Sharding { + HashSharding { + Function: HASH_FUNCTION_CLOUD_LOGS + Columns: ["timestamp", "uid"] + } + } + )", storeName.c_str(), tableName.c_str(), shards)); + UNIT_ASSERT_VALUES_EQUAL(status, NMsgBusProxy::EResponseStatus::MSTATUS_OK); + } + + static std::shared_ptr SampleBatch(bool strTimestamps = false, ui32 rowsCount = 100) { + auto schema = ArrowSchema(); + if (strTimestamps) { + schema = ArrowSchema(arrow::binary()); + } + auto builders = NArrow::MakeBuilders(schema, rowsCount); + + for (size_t i = 0; i < rowsCount; ++i) { + std::string s(std::to_string(i)); + + // WriteCSV() does not support Timestamp yet, make CSV via strings + if (strTimestamps) { + std::string us = s; + while (us.size() < 6) { + us = std::string("0") + us; + } + + std::string ts = std::string("1970-01-01T00:00:00.") + std::string(us.data(), us.size()); + + Y_ABORT_UNLESS(NArrow::Append(*builders[0], ts)); + Y_ABORT_UNLESS(NArrow::Append(*builders[7], ts)); + Y_ABORT_UNLESS(NArrow::Append(*builders[8], ts)); + } else { + Y_ABORT_UNLESS(NArrow::Append(*builders[0], i)); + Y_ABORT_UNLESS(NArrow::Append(*builders[7], i)); + Y_ABORT_UNLESS(NArrow::Append(*builders[8], i)); + } + Y_ABORT_UNLESS(NArrow::Append(*builders[1], s)); + Y_ABORT_UNLESS(NArrow::Append(*builders[2], s)); + Y_ABORT_UNLESS(NArrow::Append(*builders[3], s)); + Y_ABORT_UNLESS(NArrow::Append(*builders[4], i)); + Y_ABORT_UNLESS(NArrow::Append(*builders[5], s + "str")); + Y_ABORT_UNLESS(NArrow::Append(*builders[6], "{ \"value\": " + s + " }")); + Y_ABORT_UNLESS(NArrow::Append(*builders[9], s + "str")); + } + + return arrow::RecordBatch::Make(schema, rowsCount, NArrow::Finish(std::move(builders))); + } + + static TString ToCSV(const std::shared_ptr& batch, bool withHeader = false) { + auto res1 = arrow::io::BufferOutputStream::Create(); + Y_ABORT_UNLESS(res1.ok()); + std::shared_ptr outStream = *res1; + + arrow::csv::WriteOptions options = arrow::csv::WriteOptions::Defaults(); + options.include_header = withHeader; + + auto status = arrow::csv::WriteCSV(*batch, options, outStream.get()); + Y_ABORT_UNLESS(status.ok(), "%s", status.ToString().c_str()); + + auto res2 = outStream->Finish(); + Y_ABORT_UNLESS(res2.ok()); + + std::shared_ptr buffer = *res2; + TString out((const char*)buffer->data(), buffer->size()); + //Cerr << out; + return out; + } +}; + +using TKikimrWithGrpcAndRootSchema = TBasicKikimrWithGrpcAndRootSchema; +using TKikimrWithGrpcAndRootSchemaWithAuth = TBasicKikimrWithGrpcAndRootSchema; +using TKikimrWithGrpcAndRootSchemaWithAuthAndSsl = TBasicKikimrWithGrpcAndRootSchema; +using TKikimrWithGrpcAndRootSchemaNoSystemViews = TBasicKikimrWithGrpcAndRootSchema; + +Ydb::StatusIds::StatusCode WaitForStatus( + std::shared_ptr channel, const TString& opId, + TString* error = nullptr, + int retries = 10, + TDuration sleepDuration = NYdb::ITERATION_DURATION +); + +} diff --git a/tests/unit/sessions_pool/ydb_keys_ut.h b/tests/unit/sessions_pool/ydb_keys_ut.h new file mode 100644 index 0000000000..af4ad152fc --- /dev/null +++ b/tests/unit/sessions_pool/ydb_keys_ut.h @@ -0,0 +1,132 @@ +#pragma once + +#include + +// This file contains self signed ca and server cert for +// grpc ut +// Valid for 3560 days +namespace NYdbSslTestData { + +const std::string CaCrt = R"___( +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIJAMrBPGTKCsO/MA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV +BAYTAlJVMQwwCgYDVQQIDANSdXMxCzAJBgNVBAcMAllhMQ0wCwYDVQQKDARUZXN0 +MQ0wCwYDVQQLDARUZXN0MRAwDgYDVQQDDAdSb290IENBMB4XDTE4MDYwNTE0NDMz +MFoXDTI4MDYwMjE0NDMzMFowWDELMAkGA1UEBhMCUlUxDDAKBgNVBAgMA1J1czEL +MAkGA1UEBwwCWWExDTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxEDAOBgNV +BAMMB1Jvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAM9Pi +KaewiIWqcgcWmpedv0hSWRGvMrrwoeab9a9SbvZcJoZaKla11kg4+SFb9uUoxKYk +sa4VCCHeeIwzdVuChjcliFxi+oIoqXehF+ezqqU5qWczyMM1+Gq6Dvoqc0duzBXD +nB6fYfe6mKchx8GRDb8XyGiNVTWXZBeLtDmAPFonr5qfsFNaDLWVtAcNdSYxhJSH +Mj4Ua+MdRK9QuNn2YXc37fp7VJ5VK8kHXyRnUXADMMZUPXj6MRbKp6bpMeKYvqUy +KciXB5WIjRCsD4q53/a08wMChwqxGuBNGGrLqmGRHsb0WrJeuzXPvYkOiEJdFDvH +TS3cLS/V/kSKFmfGJ9VAyFrN59zczoxTRvrNKMDa/2d5vYBgnkmGlRqyLMLld/vL +uA7YilOhDrjrf/8JjtFKxTMwrhUrRg036iWbrkwD76aywnY4cE9FEff6aXqVkVnE +EOCPi2GOh/oO5ZlJgK8D+FPdkf7chdoyGFyHHhjSE5j4FzvSzSIuNA5KqRjFnYhm +6fQYVNvoXO4Q53VBvCof1lzDy7jDAeugcIihd+ccJnHuAkfyI7p/3I1ScdL2mKVs +oifQcagf7V7nqpiOaPgpBl6Bm5pgP1oJgaELrBG5L1SiW9BzjBiUCryOspJb6t6R +S1AB3ni4BBm0QXSWM39GZyt5Iucb9l4cn5lpeQIDAQABo1AwTjAdBgNVHQ4EFgQU +xwtpEVC+iu4iPSnIaXFcVLBpQggwHwYDVR0jBBgwFoAUxwtpEVC+iu4iPSnIaXFc +VLBpQggwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAZDBp3To967TU +jg8wy9V18LeP32Fwfd1DR1Q4R0aujEfyYFNyQlDNPCRqN84n8P4Elt3Q6GFeyvRR +Kfp9Yl3/+Hm37K5YYi4YM/Av+jKknHqkmMDnB530o/Plvh2Rfpsc9WgNjbQT8VQ6 +QtReVS7anre9HY8/PMhN9ZZlozvJlnb0Hn9XfprJ3ozALAqVoUcCUnvq8/FyCRuK +NZW1jLWWxxhHiwluONoBnzEj34muzAVZ/3zB0ZIFl7qoIufq+pIDpuV+5j/GqnU/ +WSBHPOFBlbUOnp6MDmJxo1VXgbRn4Xvn+7Q5yAUId6h6cr8VHpFoFsLKoeRXebkO +BCcIOlLvhVMaDQHdLM9BThxe/s+Cr1QJ8I7WBcs1v/DiGCSBzMDlXxv3P8rN4NAJ +kUPT9s7jUPOzlnsEo0lZb8a5HVHJYx903U3ruJKOtNV0S+DvQLGAdIr9kgIB/nuH +mYqYZPYHgXak1TCtkdS1opmM7Y7LCC/mMLBdZ+CqZ6Xz1be4l55z0VKqh8Eia5/O +dk+s9cl/PCKOW8e77Aw9LlDwCoQV8ObidfCGaCp6kGFka3cChc7hu0/y0e3HVdF6 +q0shVparVk1Gu5b//FkkO21kDBK4soL2uayaOVPrpcryT/UInEgaq1i9cYFDsbVF +3aRN6nfiRZ49DH71I/CnhIy6d5/pwD4= +-----END CERTIFICATE----- +)___"; + +const std::string ServerCrt = R"___( +-----BEGIN CERTIFICATE----- +MIIFKDCCAxACAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCUlUxDDAKBgNV +BAgMA1J1czELMAkGA1UEBwwCWWExDTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRl +c3QxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTgwNjA1MTQ0MzMyWhcNMjgwNjAyMTQ0 +MzMyWjBcMQswCQYDVQQGEwJSVTEMMAoGA1UECAwDUnVzMQswCQYDVQQHDAJZYTEN +MAsGA1UECgwEVGVzdDEPMA0GA1UECwwGU2VydmVyMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDiq7OYi9moBz3SQ8eW +YHK0L47IC+gmYKm87KZpph7CcDLmtqRhLUVGy1DhE6AdcMJqPD02+ddcqO/XnZCg +/8nTv7jbNyN7FkDLASfmRnzeG3XVWJgC2OKbu7zhwNLg48CHdXuwkxB7HzvQptB8 +3pXg7vccTYG7fm+lpUxy4OZRr43qmcPqXvh5f9G72xm1Q6O2/OSibxxab/xcaAIC +h3UGkXaLJykd8LKLF5kbKxM0rXT9TJ4FNQYhtCD99O5VVaRvtnNfDTG28WrhXD5S +tzDau99x7MWfj6DV6pG29j4udcmadhng8qyhIoYpYKkiKzI3Qz1AZTeX7KDQjTAV +yb3HMRZw6U2dxvX4yZLkKAAjWRTSzCJGf8Y8g4wj4s0NCVEOlJ4vx4PvMh39aQye +ha0pMs+Q0NkmKUYNmXqv71DsbWIkJDjvWqC/X+wREaGZykF1o+kGXFDQnObCEaX3 +Ctz92s7zWXsI2q7Bkw50ksS0LPb9ukD3XWSh6L0tTN+C6Lhxv+gwTqJu1XTv9toJ +Jm52RuS2UWZ25noYshUMas2sVM0Ak83R+XW2RSvOibGSoHXSpcW+bXnkwVxIdgNo +o7E4skSbaGUKYUpk2rvJI/D2HdLgYwnD9zGSe09mbqRfBPlb0KO16L4RsWtrH6EX +L60VoFDikVV6Rw1Ec2cP0dKD+QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAehG3o +Axyd7SXlq4QFB1y1062BU2+keI515pRfyAIpaMa8gDA17El6FLYkYhYro6QhfuSD +aNf6l681LiQZr9Xje6dXO77QR5fe1u72ocRzdE9d/XaYeHocHHwBwfAHRt7kSGgt +emYO0mwju9MxgJ3buvUvvJCjyWPEpaE2ot/cIeEU/4DzqVdWtGhvtUD/w1vGUV4d +F6K4k1Q1tl8ZN+mmZKYa9ppHTx92iDC3ZLH0tU8FA1sblmrBZWwDRimUSijuFM/W ++YS0C0+F+4DyDqJ/1oI3Y+BO3BY5A+GPhEbITH+sZLHUAWBIgRKXekmJg/1HuPNu +OsqKsnVMbgtFmitfg5HSUx99maEOlWEjbdFClH22Wpdxudlrq8IV0wCI5ZTvaP+y +pQfeAqYJOSBmFHiF30Rt0jYVRM4CEwqtqP7WCKZlQQQuVW+hnZFDcY0RkynQPJNM +VW9+pdUAXUHlIxn6HxSfZISLJeGihLlnKwnnjIj++oWT8tgQH8pBVmU8PSPLnLxH +zNllxqSFryAgXBmLeVymWu34nEL2MqASDsswDcMiJj0M9w1EHs/seis0PYB/c54g +/Y5Cgk2sRuiRWOr39H0Z49SfxqkMnmbjw0iLlopN4QFBQUqLmitJUoAZiHiwJPJc +4/YLQABjdGmF5lfq8xHflkOa83Wm3H5I5WgWGQ== +-----END CERTIFICATE----- +)___"; + +const std::string ServerKey = R"___( +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEA4quzmIvZqAc90kPHlmBytC+OyAvoJmCpvOymaaYewnAy5rak +YS1FRstQ4ROgHXDCajw9NvnXXKjv152QoP/J07+42zcjexZAywEn5kZ83ht11ViY +Atjim7u84cDS4OPAh3V7sJMQex870KbQfN6V4O73HE2Bu35vpaVMcuDmUa+N6pnD +6l74eX/Ru9sZtUOjtvzkom8cWm/8XGgCAod1BpF2iycpHfCyixeZGysTNK10/Uye +BTUGIbQg/fTuVVWkb7ZzXw0xtvFq4Vw+Urcw2rvfcezFn4+g1eqRtvY+LnXJmnYZ +4PKsoSKGKWCpIisyN0M9QGU3l+yg0I0wFcm9xzEWcOlNncb1+MmS5CgAI1kU0swi +Rn/GPIOMI+LNDQlRDpSeL8eD7zId/WkMnoWtKTLPkNDZJilGDZl6r+9Q7G1iJCQ4 +71qgv1/sERGhmcpBdaPpBlxQ0JzmwhGl9wrc/drO81l7CNquwZMOdJLEtCz2/bpA +911koei9LUzfgui4cb/oME6ibtV07/baCSZudkbktlFmduZ6GLIVDGrNrFTNAJPN +0fl1tkUrzomxkqB10qXFvm155MFcSHYDaKOxOLJEm2hlCmFKZNq7ySPw9h3S4GMJ +w/cxkntPZm6kXwT5W9Cjtei+EbFrax+hFy+tFaBQ4pFVekcNRHNnD9HSg/kCAwEA +AQKCAgBOWWU5vFVetCoVTOJnQy1CxRGIaj0zTsQ9DluzNv143glqIAWPpXNFti9d +rUfyBTDeQbYzE4bye15z2/3K+L9Nlv6Rn2x/NkDtKpgdC45Lw5gmR3o7ubYSeIEg +U2NQ4siAygYYEa1nsXMeexqjntiVqGP3/35xTZHP4uQa7UwPPixCxCWpFGy0qo7X +bTNkqV2keaOZ1egqBn1nf3f6YdH8lDkyfjXDKJi+ZUjB0FDSK4a9q0cPq7VT3wxs +W8Yp7vFov9r/JvNhNe9ouFa+hp23basdmObycVX5uxvk7xatPn+SCXKGg7tR8zoG +gWfU7LNt0KsSYCooNF2d2L+fOF2FHuXffsHM090bf0GE5J829CN/H6Wa44NI9LDn +wmtTXoHOWLqKL+iPj12HfMsx5zhcdMBY1qo7vIBZfR1mYyeWcuAAApBJWED9Z5tJ +pEpIVBRgKq6Q/Tr66gTmU5FLRCFKlbIjD2sZdMl/Rkp1rskNdjRLw5cDhn2X+MyM +cRdF0YkJjRph/DnPP/z2aQgx3/NfjXpEhWtd7o0Ipkp4zrPqZ6yyTThY2VYgT2Ce +PzBBy1Ib+avIXU1EExkFahaijFALq4h4c60bstoTfoGq12Y2B512FJKtwB8+80Ih +Jlrtr4vAsWzndTxpIIrBSgQxy/F5lDcxPg0J2xFOr9DbGC34pQKCAQEA/JV3hLCV +GhciZPzsZHb7gBfkC1l2dn1y4QdnaTdVO4n2p/l4k5ucvuHjWqrBzEiO/CxcfRAg +bqUqs/IKwJ4TLvQWAk5zwAe4diuy/jV5VY8ruOiFc56QhieYuog0asPkP2LRucVq +skunljIZNoHLUTfs80ggMmEHBuewv2gnrNuWPp1cNbjCstivpeRuUwKmL2bdQfFA +TyQQHWn9eItNhRqX7MNr4fz1QUUSUt7CWYYoDhJC/Oo45320AjQR/Lms2QYvz6iG +CzVmIJxSqC7Oykz6gVcKu+R+RdO8DLRb+fIaA7QgAX8/lxLlJuKVn5F8WfaAzRQ6 +yKmbI9+HFifGDwKCAQEA5byDrObyWTL4BTeRyFlHRYF6cTe2xBv/Kuk4Ec6zHaaV +E39PfTJn2t0BkDvK9FXtF9sjKTmhsjg3mmgMNOz8Ll2/z4df6/u4zjy1dZ309zyh +yDWcP4tW8Hj6kl8PFStdlRXxIBMt/Gpsf4w45q2cNmqdMU4VBgwhycp87fq7EWJX +0zPDJnzOHo/twG+tsl/ZzQsHyxPC54oG3uFKWnT8azJTBgGeAD+huoONGk44TpOj +uV6LQpIS+q+vwRLmSaMeAiEG2N/IbMEU6l1gbz9ohfcASqFIVmxht2AE1GpD0I1o +XlxeoP0uSP9TtSuhSHfXowjaD/IBxzMJRtsA+X5ddwKCAQEAy5JbpaH8Se7jValT +jRUoVnDq5wrPo2gwMpWZDv/9veLP3Un/mFgO2PmOGAEP+Olx9GR8ln9s5EBSTn2B +lQTSSUGIi4tXVynhzbwioyfOBttBTeJ5zFm7+aPoQE6OkI4ZY8ztY2BtQg4fn7n5 +AClUCL2eR+WVrYTt+O67UUlM0NCaIxUIwHOM2EA0MOwOzvCPqByrrv4V6rMSGeLW +21TKwcBROg224YjS0iwtPIU09ppdphmpy9Wqz0hM0InPBXVQjgmidydIAbij+xyC +sfIn0HyCWcQhbpYV/4lLQqIKj0RFGz8NnKdGRSiBb/mmxdin9Inr/V2Uky2/UAZU +BdNAmQKCAQAYZPcacf+D5zyc2TS6sHg38jK9OOxIUKy8sr0Ibwln+ZtU0azwH10V +yWf0F9VKMqGVaeiG5R69XOjSlX/OUufISJ4ofDh8R2NtStb94UL0ydRn/QFVYgde +S4pX5o4kclFilkzfWgoFBov00z/rhr3SrWl5pc/nr3wbAExZvMkGZIns3E85lAET +D7dwOquYCEOJWUV/k96bVXW7TvLlPgzbmSFlvuA3KIqU0ok2JN4nwdedxGNHM1me +ku83sjkP0qlKEpW2i7Stj6cX58hop7QCnaLDSfLzcljB7wk0QQBoccuGUYqez6ON +jsclsrdSiZ81Kah2Dv2PWGUAyBqHY5qrAoIBAQCtjsqk71UkhTi5LB2ism1aGAme +pKMY459g57wmSEWE/+rBWOBY5CZ7FMV1W+P1bXqydZOr6ptBEUjM4hW6YcsKmDtk +g/LriBtDnPkd/QvrrpOZnj8rrtFyOF/wMx2jFwp6Nes+QtRqQk0RXerWDSbsn9vL +xSN/nSB6IoYs5rlC2TRoxCHqEGPSGImi4hhpLhIrArpqTH8+OhuRRVwtMO8Qo2fS +LEbMU1ARtp9TVPVktwsdNOnCeyynJnQiB6fL6QnpQYFY3xyO1MuBSjruTKLqorPM +pqUJavKfAG3qyVCaGn4msSaPDNnG5QH7eCpbuemWgPDmx/br4fdx/U7VH8HE +-----END RSA PRIVATE KEY----- +)___"; + +}; // namespace NYdbSslTestData From 10f598b65a5324b8ffbce97d65843a97f9c3210a Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 19:29:29 +0300 Subject: [PATCH 2/9] sessons_it & sessoins_pool_it --- tests/integration/CMakeLists.txt | 2 + tests/integration/sessions/CMakeLists.txt | 12 + tests/integration/sessions/sessions_it.cpp | 639 ++++++++++++++++++ .../sessions_pool/CMakeLists.txt | 6 +- .../sessions_pool/sessions_pool_it.cpp} | 0 tests/unit/CMakeLists.txt | 3 +- tests/unit/sessions_pool/ydb_common_ut.h | 367 ---------- tests/unit/sessions_pool/ydb_keys_ut.h | 132 ---- 8 files changed, 657 insertions(+), 504 deletions(-) create mode 100644 tests/integration/sessions/CMakeLists.txt create mode 100644 tests/integration/sessions/sessions_it.cpp rename tests/{unit => integration}/sessions_pool/CMakeLists.txt (63%) rename tests/{unit/sessions_pool/sessions_pool_ut.cpp => integration/sessions_pool/sessions_pool_it.cpp} (100%) delete mode 100644 tests/unit/sessions_pool/ydb_common_ut.h delete mode 100644 tests/unit/sessions_pool/ydb_keys_ut.h diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 9d65935a2d..78d656ade6 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -1,3 +1,5 @@ add_subdirectory(basic_example) add_subdirectory(bulk_upsert) add_subdirectory(server_restart) +add_subdirectory(sessions) +add_subdirectory(sessions_pool) diff --git a/tests/integration/sessions/CMakeLists.txt b/tests/integration/sessions/CMakeLists.txt new file mode 100644 index 0000000000..55f70c77d0 --- /dev/null +++ b/tests/integration/sessions/CMakeLists.txt @@ -0,0 +1,12 @@ +add_ydb_test(NAME sessions_it + SOURCES + sessions_it.cpp + LINK_LIBRARIES + yutil + cpp-testing-unittest_main + YDB-CPP-SDK::Driver + YDB-CPP-SDK::Table + api-grpc + LABELS + integration +) \ No newline at end of file diff --git a/tests/integration/sessions/sessions_it.cpp b/tests/integration/sessions/sessions_it.cpp new file mode 100644 index 0000000000..4d2bacfced --- /dev/null +++ b/tests/integration/sessions/sessions_it.cpp @@ -0,0 +1,639 @@ +#include +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace NYdb; +using namespace NYdb::NTable; + +Y_UNIT_TEST_SUITE(YdbSdkSessions) { + Y_UNIT_TEST(TestSessionPool) { + + const std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::unordered_set sids; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); + auto session = sessionResponse.GetSession(); + sids.insert(session.GetId()); + auto result = session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync(); + + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + UNIT_ASSERT_VALUES_EQUAL(result.GetEndpoint(), location); + } + // All requests used one session + UNIT_ASSERT_VALUES_EQUAL(sids.size(), 1); + // No more session captured by client + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + + driver.Stop(true); + } + + Y_UNIT_TEST(TestMultipleSessions) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::vector sids; + std::vector results; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); + auto session = sessionResponse.GetSession(); + sids.push_back(session); + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } + + NThreading::WaitExceptionOrAll(results).Wait(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 10); + + for (auto& result : results) { + UNIT_ASSERT_EQUAL(result.GetValue().GetStatus(), EStatus::SUCCESS); + } + sids.clear(); + results.clear(); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + + driver.Stop(true); + } + + Y_UNIT_TEST(TestActiveSessionCountAfterBadSession) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::vector sids; + std::vector results; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); + auto session = sessionResponse.GetSession(); + sids.push_back(session); + if (count == 0) { + // Force BAD session server response for ExecuteDataQuery + UNIT_ASSERT_EQUAL(session.Close().GetValueSync().GetStatus(), EStatus::SUCCESS); + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } else { + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } + } + + NThreading::WaitExceptionOrAll(results).Wait(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 10); + + for (size_t i = 0; i < results.size(); i++) { + if (i == 9) { + UNIT_ASSERT_EQUAL(results[i].GetValue().GetStatus(), EStatus::BAD_SESSION); + } else { + UNIT_ASSERT_EQUAL(results[i].GetValue().GetStatus(), EStatus::SUCCESS); + } + } + sids.clear(); + results.clear(); + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + + driver.Stop(true); + } + + Y_UNIT_TEST(TestActiveSessionCountAfterTransportError) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 100; + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + auto result = session.ExecuteSchemeQuery(R"___( + CREATE TABLE `/local/t` ( + Key Uint32, + Value String, + PRIMARY KEY (Key) + ); + )___").ExtractValueSync(); + UNIT_ASSERT(result.IsSuccess()); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + } + + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); + auto session = sessionResponse.GetSession(); + + // Assume 10us is too small to execute query and get response + auto res = session.ExecuteDataQuery("SELECT COUNT(*) FROM `/local/t`;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), + NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + } + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + driver.Stop(true); + } + + Y_UNIT_TEST(MultiThreadSync) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + const int nThreads = 10; + const int nRequests = 1000; + auto job = [client]() mutable { + for (int i = 0; i < nRequests; i++) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_EQUAL(sessionResponse.GetStatus(), EStatus::SUCCESS); + } + }; + IThreadFactory* pool = SystemThreadFactory(); + + std::vector> threads; + threads.resize(nThreads); + for (int i = 0; i < nThreads; i++) { + threads[i] = pool->Run(job); + } + for (int i = 0; i < nThreads; i++) { + threads[i]->Join(); + } + UNIT_ASSERT_EQUAL(client.GetActiveSessionCount(), 0); + driver.Stop(true); + } + + void EnsureCanExecQuery(NYdb::NTable::TSession session) { + auto execStatus = session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync().GetStatus(); + UNIT_ASSERT_EQUAL(execStatus, EStatus::SUCCESS); + } + + void EnsureCanExecQuery(NYdb::NQuery::TSession session) { + auto execStatus = session.ExecuteQuery("SELECT 42;", + NYdb::NQuery::TTxControl::BeginTx().CommitTx()).ExtractValueSync().GetStatus(); + UNIT_ASSERT_EQUAL(execStatus, EStatus::SUCCESS); + } + + template + void DoMultiThreadSessionPoolLimitSync() { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + const int maxActiveSessions = 45; + TClient client(driver, + typename TClient::TSettings() + .SessionPoolSettings( + typename TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); + + constexpr int nThreads = 100; + NYdb::EStatus statuses[nThreads]; + std::vector> sessions; + sessions.resize(nThreads); + std::atomic t = 0; + auto job = [client, &t, &statuses, &sessions]() mutable { + auto sessionResponse = client.GetSession().ExtractValueSync(); + int i = ++t; + statuses[--i] = sessionResponse.GetStatus(); + if (statuses[i] == EStatus::SUCCESS) { + EnsureCanExecQuery(sessionResponse.GetSession()); + sessions[i] = sessionResponse.GetSession(); + } + }; + IThreadFactory* pool = SystemThreadFactory(); + + std::vector> threads; + threads.resize(nThreads); + for (int i = 0; i < nThreads; i++) { + threads[i] = pool->Run(job); + } + for (int i = 0; i < nThreads; i++) { + threads[i]->Join(); + } + + sessions.clear(); + + int successCount = 0; + int exhaustedCount = 0; + for (int i = 0; i < nThreads; i++) { + switch (statuses[i]) { + case EStatus::SUCCESS: + successCount++; + break; + case EStatus::CLIENT_RESOURCE_EXHAUSTED: + exhaustedCount++; + break; + default: + UNIT_ASSERT(false); + } + } + + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(successCount, maxActiveSessions); + UNIT_ASSERT_VALUES_EQUAL(exhaustedCount, nThreads - maxActiveSessions); + driver.Stop(true); + } + + Y_UNIT_TEST(MultiThreadSessionPoolLimitSyncTableClient) { + DoMultiThreadSessionPoolLimitSync(); + } + + Y_UNIT_TEST(MultiThreadSessionPoolLimitSyncQueryClient) { + DoMultiThreadSessionPoolLimitSync(); + } + + NYdb::NTable::TAsyncDataQueryResult ExecQueryAsync(NYdb::NTable::TSession session, const std::string q) { + return session.ExecuteDataQuery(q, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); + } + + NYdb::NQuery::TAsyncExecuteQueryResult ExecQueryAsync(NYdb::NQuery::TSession session, const std::string q) { + return session.ExecuteQuery(q, + NYdb::NQuery::TTxControl::BeginTx().CommitTx()); + } + + template + void DoMultiThreadMultipleRequestsOnSharedSessions() { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + const int maxActiveSessions = 10; + typename T::TClient client(driver, + typename T::TClient::TSettings() + .SessionPoolSettings( + typename T::TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); + + constexpr int nThreads = 20; + constexpr int nRequests = 50; + std::array, nThreads> results; + std::atomic t = 0; + std::atomic validSessions = 0; + auto job = [client, &t, &results, &validSessions]() mutable { + auto sessionResponse = client.GetSession().ExtractValueSync(); + + int i = ++t; + std::vector& r = results[--i]; + + if (sessionResponse.GetStatus() != EStatus::SUCCESS) { + return; + } + validSessions.fetch_add(1); + + for (int i = 0; i < nRequests; i++) { + r.push_back(ExecQueryAsync(sessionResponse.GetSession(), "SELECT 42;")); + } + }; + IThreadFactory* pool = SystemThreadFactory(); + + std::vector> threads; + threads.resize(nThreads); + for (int i = 0; i < nThreads; i++) { + threads[i] = pool->Run(job); + } + for (int i = 0; i < nThreads; i++) { + threads[i]->Join(); + } + + for (auto& r : results) { + NThreading::WaitExceptionOrAll(r).Wait(); + } + for (auto& r : results) { + if (!r.empty()) { + for (auto& asyncStatus : r) { + auto res = asyncStatus.GetValue(); + if (!res.IsSuccess()) { + UNIT_ASSERT_VALUES_EQUAL(res.GetStatus(), EStatus::SESSION_BUSY); + } + } + } + } + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), maxActiveSessions); + auto curExpectedActive = maxActiveSessions; + auto empty = 0; + for (auto& r : results) { + if (!r.empty()) { + r.clear(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), --curExpectedActive); + } else { + empty++; + } + } + UNIT_ASSERT_VALUES_EQUAL(empty, nThreads - maxActiveSessions); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + driver.Stop(true); + } + + Y_UNIT_TEST(MultiThreadMultipleRequestsOnSharedSessionsTableClient) { + struct TTypeHelper { + using TClient = NYdb::NTable::TTableClient; + using TResult = NYdb::NTable::TAsyncDataQueryResult; + }; + DoMultiThreadMultipleRequestsOnSharedSessions(); + } + + // Enable after interactive tx support + //Y_UNIT_TEST(MultiThreadMultipleRequestsOnSharedSessionsQueryClient) { + // struct TTypeHelper { + // using TClient = NYdb::NQuery::TQueryClient; + // using TResult = NYdb::NQuery::TAsyncExecuteQueryResult; + // }; + // DoMultiThreadMultipleRequestsOnSharedSessions(); + //} + + // TODO: Enable after accepting a pull request with merging configs + /*Y_UNIT_TEST(SessionsServerLimit) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult = client.CreateSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + + sessionResult = client.CreateSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session2 = sessionResult.GetSession(); + + sessionResult = client.CreateSession().ExtractValueSync(); + sessionResult.GetIssues().PrintTo(std::cerr); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::OVERLOADED); + + auto status = session1.Close().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(status.IsTransportError(), false); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(status.GetStatus(), EStatus::SUCCESS); + + auto result = session2.ExecuteDataQuery(R"___( + SELECT 1; + )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + + sessionResult = client.CreateSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + + sessionResult = client.CreateSession().ExtractValueSync(); + sessionResult.GetIssues().PrintTo(std::cerr); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::OVERLOADED); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + } + + Y_UNIT_TEST(SessionsServerLimitWithSessionPool) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult1 = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::SUCCESS); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + auto session1 = sessionResult1.GetSession(); + + auto sessionResult2 = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult2.GetStatus(), EStatus::SUCCESS); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + auto session2 = sessionResult2.GetSession(); + + { + auto sessionResult3 = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult3.GetStatus(), EStatus::OVERLOADED); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 3); + } + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + + auto status = session1.Close().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(status.IsTransportError(), false); + UNIT_ASSERT_VALUES_EQUAL(status.GetStatus(), EStatus::SUCCESS); + + // Close doesnt free session from user perspective, + // the value of ActiveSessionsCounter will be same after Close() call. + // Probably we want to chenge this contract + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + + auto result = session2.ExecuteDataQuery(R"___( + SELECT 1; + )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + + sessionResult1 = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::SUCCESS); + UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetSession().GetId().empty(), false); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 3); + + auto sessionResult3 = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(sessionResult3.GetStatus(), EStatus::OVERLOADED); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 4); + + auto tmp = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 5); + sessionResult1 = tmp; // here we reset previous created session object, + // so perform close rpc call implicitly and delete it + UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::OVERLOADED); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 4); + }*/ + + Y_UNIT_TEST(CloseSessionAfterDriverDtorWithoutSessionPool) { + std::vector sessionIds; + int iterations = 50; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult = client.CreateSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + UNIT_ASSERT(status.ok()); + auto deferred = response.operation(); + UNIT_ASSERT(deferred.ready() == true); + UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); + } + } + + Y_UNIT_TEST(CloseSessionWithSessionPoolExplicit) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + + if (RandomNumber(10) == 5) { + client.Stop().Apply([client](NThreading::TFuture future){ + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + return future; + }).Wait(); + } else { + client.Stop().Wait(); + } + + if (iterations & 4) { + driver.Stop(true); + } + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + UNIT_ASSERT(status.ok()); + auto deferred = response.operation(); + UNIT_ASSERT(deferred.ready() == true); + UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); + } + } + + Y_UNIT_TEST(CloseSessionWithSessionPoolExplicitDriverStopOnly) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + driver.Stop(true); + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + UNIT_ASSERT(status.ok()); + auto deferred = response.operation(); + UNIT_ASSERT(deferred.ready() == true); + UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); + } + } + + Y_UNIT_TEST(CloseSessionWithSessionPoolFromDtors) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); + UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + UNIT_ASSERT(status.ok()); + auto deferred = response.operation(); + UNIT_ASSERT(deferred.ready() == true); + UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); + } + } +} diff --git a/tests/unit/sessions_pool/CMakeLists.txt b/tests/integration/sessions_pool/CMakeLists.txt similarity index 63% rename from tests/unit/sessions_pool/CMakeLists.txt rename to tests/integration/sessions_pool/CMakeLists.txt index ab01de968f..d302229f4b 100644 --- a/tests/unit/sessions_pool/CMakeLists.txt +++ b/tests/integration/sessions_pool/CMakeLists.txt @@ -1,12 +1,12 @@ -add_ydb_test(NAME sessions_pool_ut +add_ydb_test(NAME sessions_pool_it SPLIT_FACTOR 12 SOURCES - sessions_pool_ut.cpp + sessions_pool_it.cpp LINK_LIBRARIES yutil cpp-testing-unittest_main YDB-CPP-SDK::Table api-grpc LABELS - unit + integration ) \ No newline at end of file diff --git a/tests/unit/sessions_pool/sessions_pool_ut.cpp b/tests/integration/sessions_pool/sessions_pool_it.cpp similarity index 100% rename from tests/unit/sessions_pool/sessions_pool_ut.cpp rename to tests/integration/sessions_pool/sessions_pool_it.cpp diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 96ef94f4d1..d06ff68dbb 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,3 +1,2 @@ add_subdirectory(client) -add_subdirectory(library) -add_subdirectory(sessions_pool) +add_subdirectory(library) \ No newline at end of file diff --git a/tests/unit/sessions_pool/ydb_common_ut.h b/tests/unit/sessions_pool/ydb_common_ut.h deleted file mode 100644 index befad4d787..0000000000 --- a/tests/unit/sessions_pool/ydb_common_ut.h +++ /dev/null @@ -1,367 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ydb_keys_ut.h" - -#include // for WriteCSV() -#include - -#include - -using namespace NKikimr; -namespace NYdb { - -using namespace Tests; -using namespace NYdb; - -struct TKikimrTestSettings { - static constexpr bool SSL = false; - static constexpr bool AUTH = false; - static constexpr bool PrecreatePools = true; - static constexpr bool EnableSystemViews = true; - - static TString GetCaCrt() { return NYdbSslTestData::CaCrt; } - static TString GetServerCrt() { return NYdbSslTestData::ServerCrt; } - static TString GetServerKey() { return NYdbSslTestData::ServerKey; } - - static NKikimr::TCertificateAuthorizationParams GetCertAuthParams() {return {}; } -}; - -struct TKikimrTestWithAuth : TKikimrTestSettings { - static constexpr bool AUTH = true; -}; - -struct TKikimrTestWithAuthAndSsl : TKikimrTestWithAuth { - static constexpr bool SSL = true; -}; - -struct TKikimrTestWithServerCert : TKikimrTestWithAuthAndSsl { - static constexpr bool SSL = true; - - static const TCertAndKey& GetCACertAndKey() { - static const TCertAndKey ca = GenerateCA(TProps::AsCA()); - return ca; - } - - static const TCertAndKey& GetServerCert() { - static const TCertAndKey server = GenerateSignedCert(GetCACertAndKey(), TProps::AsServer()); - return server; - } - - static TString GetCaCrt() { - return GetCACertAndKey().Certificate.c_str(); - } - - static TString GetServerCrt() { - return GetServerCert().Certificate.c_str(); - } - - static TString GetServerKey() { - return GetServerCert().PrivateKey.c_str(); - } -}; - -struct TKikimrTestNoSystemViews : TKikimrTestSettings { - static constexpr bool EnableSystemViews = false; -}; - -template -class TBasicKikimrWithGrpcAndRootSchema { -public: - TBasicKikimrWithGrpcAndRootSchema( - NKikimrConfig::TAppConfig appConfig = {}, - const TVector& kqpSettings = {}, - TAutoPtr logBackend = {}, - bool enableYq = false, - TAppPrepare::TFnReg udfFrFactory = nullptr, - std::function builder = nullptr, - ui16 dynamicNodeCount = 2, ui16 nodeCount = 0) - { - ui16 port = PortManager.GetPort(2134); - ui16 grpc = PortManager.GetPort(2135); - - NKikimrProto::TAuthConfig authConfig = appConfig.GetAuthConfig(); - authConfig.SetUseBuiltinDomain(true); - ServerSettings = new TServerSettings(port, authConfig); - ServerSettings->SetGrpcPort(grpc); - ServerSettings->SetLogBackend(logBackend); - ServerSettings->SetDomainName("Root"); - ServerSettings->SetDynamicNodeCount(dynamicNodeCount); - if (nodeCount > 0) - ServerSettings->SetNodeCount(nodeCount); - if (TestSettings::PrecreatePools) { - ServerSettings->AddStoragePool("ssd"); - ServerSettings->AddStoragePool("hdd"); - ServerSettings->AddStoragePool("hdd1"); - ServerSettings->AddStoragePool("hdd2"); - } else { - ServerSettings->AddStoragePoolType("ssd"); - ServerSettings->AddStoragePoolType("hdd"); - ServerSettings->AddStoragePoolType("hdd1"); - ServerSettings->AddStoragePoolType("hdd2"); - } - ServerSettings->AppConfig->MergeFrom(appConfig); - ServerSettings->FeatureFlags = appConfig.GetFeatureFlags(); - ServerSettings->SetKqpSettings(kqpSettings); - ServerSettings->SetEnableDataColumnForIndexTable(true); - ServerSettings->SetEnableNotNullColumns(true); - ServerSettings->SetEnableSystemViews(TestSettings::EnableSystemViews); - ServerSettings->SetEnableYq(enableYq); - ServerSettings->Formats = new TFormatFactory; - ServerSettings->PQConfig = appConfig.GetPQConfig(); - if (appConfig.HasMeteringConfig() && appConfig.GetMeteringConfig().HasMeteringFilePath()) { - ServerSettings->SetMeteringFilePath(appConfig.GetMeteringConfig().GetMeteringFilePath()); - } - ServerSettings->RegisterGrpcService("dummy"); - if (udfFrFactory) { - ServerSettings->SetFrFactory(udfFrFactory); - } - if (builder) { - builder(*ServerSettings);; - } - - ServerCertificateFile.Write(TestSettings::GetServerCrt().data(), TestSettings::GetServerCrt().size()); - ServerSettings->ServerCertFilePath = ServerCertificateFile.Name(); - - Server_.Reset(new TServer(*ServerSettings)); - Tenants_.Reset(new Tests::TTenants(Server_)); - - //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_PROXY_SCHEME_CACHE, NActors::NLog::PRI_DEBUG); - //Server_->GetRuntime()->SetLogPriority(NKikimrServices::SCHEME_BOARD_REPLICA, NActors::NLog::PRI_DEBUG); - Server_->GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NActors::NLog::PRI_INFO); - //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_PROXY, NActors::NLog::PRI_DEBUG); - //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_OLAPSHARD, NActors::NLog::PRI_DEBUG); - //Server_->GetRuntime()->SetLogPriority(NKikimrServices::TX_COLUMNSHARD, NActors::NLog::PRI_DEBUG); - if (enableYq) { - Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQL_PROXY, NActors::NLog::PRI_DEBUG); - Server_->GetRuntime()->SetLogPriority(NKikimrServices::KQP_COMPUTE, NActors::NLog::PRI_DEBUG); - Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQ_CONTROL_PLANE_STORAGE, NActors::NLog::PRI_DEBUG); - Server_->GetRuntime()->SetLogPriority(NKikimrServices::YQ_CONTROL_PLANE_PROXY, NActors::NLog::PRI_DEBUG); - } - - NYdbGrpc::TServerOptions grpcOption; - if (TestSettings::AUTH) { - grpcOption.SetUseAuth(true); - } - grpcOption.SetPort(grpc); - if (TestSettings::SSL) { - NYdbGrpc::TSslData sslData; - sslData.Cert = TestSettings::GetServerCrt(); - sslData.Key = TestSettings::GetServerKey(); - sslData.Root =TestSettings::GetCaCrt(); - sslData.DoRequestClientCertificate = appConfig.GetClientCertificateAuthorization().GetRequestClientCertificate(); - - grpcOption.SetSslData(sslData); - } - Server_->EnableGRpc(grpcOption); - - TClient annoyingClient(*ServerSettings); - if (ServerSettings->AppConfig->GetDomainsConfig().GetSecurityConfig().GetEnforceUserTokenRequirement()) { - annoyingClient.SetSecurityToken("root@builtin"); - } - annoyingClient.InitRootScheme("Root"); - GRpcPort_ = grpc; - } - - ui16 GetPort() { - return GRpcPort_; - } - - TPortManager& GetPortManager() { - return PortManager; - } - - void ResetSchemeCache(TString path, ui32 nodeIndex = 0) { - TTestActorRuntime* runtime = Server_->GetRuntime(); - TClient annoyingClient(*ServerSettings); - annoyingClient.RefreshPathCache(runtime, path, nodeIndex); - } - - TTestActorRuntime* GetRuntime() { - return Server_->GetRuntime(); - } - - Tests::TServer& GetServer() { - return *Server_; - } - - TServerSettings::TPtr ServerSettings; - Tests::TServer::TPtr Server_; - THolder Tenants_; -private: - TPortManager PortManager; - ui16 GRpcPort_; - TTempFileHandle ServerCertificateFile; -}; - -struct TTestOlap { - static constexpr const char * StoreName = "OlapStore"; - static constexpr const char * TableName = "OlapTable"; - static constexpr const char * TablePath = "/Root/OlapStore/OlapTable"; - - static std::shared_ptr ArrowSchema( - std::shared_ptr tsType = arrow::timestamp(arrow::TimeUnit::TimeUnit::MICRO)) - { - return std::make_shared( - std::vector>{ - arrow::field("timestamp", tsType, false), - arrow::field("resource_type", arrow::utf8()), - arrow::field("resource_id", arrow::utf8()), - arrow::field("uid", arrow::utf8(), false), - arrow::field("level", arrow::int32()), - arrow::field("message", arrow::utf8()), - arrow::field("json_payload", arrow::binary()), - arrow::field("ingested_at", tsType), - arrow::field("saved_at", tsType), - arrow::field("request_id", arrow::utf8()) - }); - } - - static std::vector> PublicSchema(bool sort = false) { - std::vector> schema = { - { "timestamp", NYdb::EPrimitiveType::Timestamp }, - { "resource_type", NYdb::EPrimitiveType::Utf8 }, - { "resource_id", NYdb::EPrimitiveType::Utf8 }, - { "uid", NYdb::EPrimitiveType::Utf8 }, - { "level", NYdb::EPrimitiveType::Int32 }, - { "message", NYdb::EPrimitiveType::Utf8 }, - { "json_payload", NYdb::EPrimitiveType::JsonDocument }, - { "ingested_at", NYdb::EPrimitiveType::Timestamp }, - { "saved_at", NYdb::EPrimitiveType::Timestamp }, - { "request_id", NYdb::EPrimitiveType::Utf8 } - }; - - if (sort) { - std::sort(schema.begin(), schema.end(), [&](const std::pair& x, - const std::pair& y) { - return x.first < y.first; - }); - } - - return schema; - } - - static void CreateTable(const TServerSettings& settings, ui32 shards = 2, - const TString& storeName = StoreName, const TString& tableName = TableName) { - TString tableDescr = Sprintf(R"( - Name: "%s" - ColumnShardCount: 4 - SchemaPresets { - Name: "default" - Schema { - Columns { Name: "timestamp" Type: "Timestamp" NotNull : true } - Columns { Name: "resource_type" Type: "Utf8" } - Columns { Name: "resource_id" Type: "Utf8" } - Columns { Name: "uid" Type: "Utf8" NotNull : true } - Columns { Name: "level" Type: "Int32" } - Columns { Name: "message" Type: "Utf8" } - Columns { Name: "json_payload" Type: "JsonDocument" } - Columns { Name: "ingested_at" Type: "Timestamp" } - Columns { Name: "saved_at" Type: "Timestamp" } - Columns { Name: "request_id" Type: "Utf8" } - KeyColumnNames: "timestamp" - KeyColumnNames: "uid" - Engine: COLUMN_ENGINE_REPLACING_TIMESERIES - } - } - )", storeName.c_str()); - - TClient annoyingClient(settings); - annoyingClient.SetSecurityToken("root@builtin"); - NMsgBusProxy::EResponseStatus status = annoyingClient.CreateOlapStore("/Root", tableDescr); - UNIT_ASSERT_VALUES_EQUAL(status, NMsgBusProxy::EResponseStatus::MSTATUS_OK); - status = annoyingClient.CreateColumnTable("/Root", Sprintf(R"( - Name: "%s/%s" - ColumnShardCount : %d - Sharding { - HashSharding { - Function: HASH_FUNCTION_CLOUD_LOGS - Columns: ["timestamp", "uid"] - } - } - )", storeName.c_str(), tableName.c_str(), shards)); - UNIT_ASSERT_VALUES_EQUAL(status, NMsgBusProxy::EResponseStatus::MSTATUS_OK); - } - - static std::shared_ptr SampleBatch(bool strTimestamps = false, ui32 rowsCount = 100) { - auto schema = ArrowSchema(); - if (strTimestamps) { - schema = ArrowSchema(arrow::binary()); - } - auto builders = NArrow::MakeBuilders(schema, rowsCount); - - for (size_t i = 0; i < rowsCount; ++i) { - std::string s(std::to_string(i)); - - // WriteCSV() does not support Timestamp yet, make CSV via strings - if (strTimestamps) { - std::string us = s; - while (us.size() < 6) { - us = std::string("0") + us; - } - - std::string ts = std::string("1970-01-01T00:00:00.") + std::string(us.data(), us.size()); - - Y_ABORT_UNLESS(NArrow::Append(*builders[0], ts)); - Y_ABORT_UNLESS(NArrow::Append(*builders[7], ts)); - Y_ABORT_UNLESS(NArrow::Append(*builders[8], ts)); - } else { - Y_ABORT_UNLESS(NArrow::Append(*builders[0], i)); - Y_ABORT_UNLESS(NArrow::Append(*builders[7], i)); - Y_ABORT_UNLESS(NArrow::Append(*builders[8], i)); - } - Y_ABORT_UNLESS(NArrow::Append(*builders[1], s)); - Y_ABORT_UNLESS(NArrow::Append(*builders[2], s)); - Y_ABORT_UNLESS(NArrow::Append(*builders[3], s)); - Y_ABORT_UNLESS(NArrow::Append(*builders[4], i)); - Y_ABORT_UNLESS(NArrow::Append(*builders[5], s + "str")); - Y_ABORT_UNLESS(NArrow::Append(*builders[6], "{ \"value\": " + s + " }")); - Y_ABORT_UNLESS(NArrow::Append(*builders[9], s + "str")); - } - - return arrow::RecordBatch::Make(schema, rowsCount, NArrow::Finish(std::move(builders))); - } - - static TString ToCSV(const std::shared_ptr& batch, bool withHeader = false) { - auto res1 = arrow::io::BufferOutputStream::Create(); - Y_ABORT_UNLESS(res1.ok()); - std::shared_ptr outStream = *res1; - - arrow::csv::WriteOptions options = arrow::csv::WriteOptions::Defaults(); - options.include_header = withHeader; - - auto status = arrow::csv::WriteCSV(*batch, options, outStream.get()); - Y_ABORT_UNLESS(status.ok(), "%s", status.ToString().c_str()); - - auto res2 = outStream->Finish(); - Y_ABORT_UNLESS(res2.ok()); - - std::shared_ptr buffer = *res2; - TString out((const char*)buffer->data(), buffer->size()); - //Cerr << out; - return out; - } -}; - -using TKikimrWithGrpcAndRootSchema = TBasicKikimrWithGrpcAndRootSchema; -using TKikimrWithGrpcAndRootSchemaWithAuth = TBasicKikimrWithGrpcAndRootSchema; -using TKikimrWithGrpcAndRootSchemaWithAuthAndSsl = TBasicKikimrWithGrpcAndRootSchema; -using TKikimrWithGrpcAndRootSchemaNoSystemViews = TBasicKikimrWithGrpcAndRootSchema; - -Ydb::StatusIds::StatusCode WaitForStatus( - std::shared_ptr channel, const TString& opId, - TString* error = nullptr, - int retries = 10, - TDuration sleepDuration = NYdb::ITERATION_DURATION -); - -} diff --git a/tests/unit/sessions_pool/ydb_keys_ut.h b/tests/unit/sessions_pool/ydb_keys_ut.h deleted file mode 100644 index af4ad152fc..0000000000 --- a/tests/unit/sessions_pool/ydb_keys_ut.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#include - -// This file contains self signed ca and server cert for -// grpc ut -// Valid for 3560 days -namespace NYdbSslTestData { - -const std::string CaCrt = R"___( ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIJAMrBPGTKCsO/MA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV -BAYTAlJVMQwwCgYDVQQIDANSdXMxCzAJBgNVBAcMAllhMQ0wCwYDVQQKDARUZXN0 -MQ0wCwYDVQQLDARUZXN0MRAwDgYDVQQDDAdSb290IENBMB4XDTE4MDYwNTE0NDMz -MFoXDTI4MDYwMjE0NDMzMFowWDELMAkGA1UEBhMCUlUxDDAKBgNVBAgMA1J1czEL -MAkGA1UEBwwCWWExDTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRlc3QxEDAOBgNV -BAMMB1Jvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDAM9Pi -KaewiIWqcgcWmpedv0hSWRGvMrrwoeab9a9SbvZcJoZaKla11kg4+SFb9uUoxKYk -sa4VCCHeeIwzdVuChjcliFxi+oIoqXehF+ezqqU5qWczyMM1+Gq6Dvoqc0duzBXD -nB6fYfe6mKchx8GRDb8XyGiNVTWXZBeLtDmAPFonr5qfsFNaDLWVtAcNdSYxhJSH -Mj4Ua+MdRK9QuNn2YXc37fp7VJ5VK8kHXyRnUXADMMZUPXj6MRbKp6bpMeKYvqUy -KciXB5WIjRCsD4q53/a08wMChwqxGuBNGGrLqmGRHsb0WrJeuzXPvYkOiEJdFDvH -TS3cLS/V/kSKFmfGJ9VAyFrN59zczoxTRvrNKMDa/2d5vYBgnkmGlRqyLMLld/vL -uA7YilOhDrjrf/8JjtFKxTMwrhUrRg036iWbrkwD76aywnY4cE9FEff6aXqVkVnE -EOCPi2GOh/oO5ZlJgK8D+FPdkf7chdoyGFyHHhjSE5j4FzvSzSIuNA5KqRjFnYhm -6fQYVNvoXO4Q53VBvCof1lzDy7jDAeugcIihd+ccJnHuAkfyI7p/3I1ScdL2mKVs -oifQcagf7V7nqpiOaPgpBl6Bm5pgP1oJgaELrBG5L1SiW9BzjBiUCryOspJb6t6R -S1AB3ni4BBm0QXSWM39GZyt5Iucb9l4cn5lpeQIDAQABo1AwTjAdBgNVHQ4EFgQU -xwtpEVC+iu4iPSnIaXFcVLBpQggwHwYDVR0jBBgwFoAUxwtpEVC+iu4iPSnIaXFc -VLBpQggwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAZDBp3To967TU -jg8wy9V18LeP32Fwfd1DR1Q4R0aujEfyYFNyQlDNPCRqN84n8P4Elt3Q6GFeyvRR -Kfp9Yl3/+Hm37K5YYi4YM/Av+jKknHqkmMDnB530o/Plvh2Rfpsc9WgNjbQT8VQ6 -QtReVS7anre9HY8/PMhN9ZZlozvJlnb0Hn9XfprJ3ozALAqVoUcCUnvq8/FyCRuK -NZW1jLWWxxhHiwluONoBnzEj34muzAVZ/3zB0ZIFl7qoIufq+pIDpuV+5j/GqnU/ -WSBHPOFBlbUOnp6MDmJxo1VXgbRn4Xvn+7Q5yAUId6h6cr8VHpFoFsLKoeRXebkO -BCcIOlLvhVMaDQHdLM9BThxe/s+Cr1QJ8I7WBcs1v/DiGCSBzMDlXxv3P8rN4NAJ -kUPT9s7jUPOzlnsEo0lZb8a5HVHJYx903U3ruJKOtNV0S+DvQLGAdIr9kgIB/nuH -mYqYZPYHgXak1TCtkdS1opmM7Y7LCC/mMLBdZ+CqZ6Xz1be4l55z0VKqh8Eia5/O -dk+s9cl/PCKOW8e77Aw9LlDwCoQV8ObidfCGaCp6kGFka3cChc7hu0/y0e3HVdF6 -q0shVparVk1Gu5b//FkkO21kDBK4soL2uayaOVPrpcryT/UInEgaq1i9cYFDsbVF -3aRN6nfiRZ49DH71I/CnhIy6d5/pwD4= ------END CERTIFICATE----- -)___"; - -const std::string ServerCrt = R"___( ------BEGIN CERTIFICATE----- -MIIFKDCCAxACAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCUlUxDDAKBgNV -BAgMA1J1czELMAkGA1UEBwwCWWExDTALBgNVBAoMBFRlc3QxDTALBgNVBAsMBFRl -c3QxEDAOBgNVBAMMB1Jvb3QgQ0EwHhcNMTgwNjA1MTQ0MzMyWhcNMjgwNjAyMTQ0 -MzMyWjBcMQswCQYDVQQGEwJSVTEMMAoGA1UECAwDUnVzMQswCQYDVQQHDAJZYTEN -MAsGA1UECgwEVGVzdDEPMA0GA1UECwwGU2VydmVyMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDiq7OYi9moBz3SQ8eW -YHK0L47IC+gmYKm87KZpph7CcDLmtqRhLUVGy1DhE6AdcMJqPD02+ddcqO/XnZCg -/8nTv7jbNyN7FkDLASfmRnzeG3XVWJgC2OKbu7zhwNLg48CHdXuwkxB7HzvQptB8 -3pXg7vccTYG7fm+lpUxy4OZRr43qmcPqXvh5f9G72xm1Q6O2/OSibxxab/xcaAIC -h3UGkXaLJykd8LKLF5kbKxM0rXT9TJ4FNQYhtCD99O5VVaRvtnNfDTG28WrhXD5S -tzDau99x7MWfj6DV6pG29j4udcmadhng8qyhIoYpYKkiKzI3Qz1AZTeX7KDQjTAV -yb3HMRZw6U2dxvX4yZLkKAAjWRTSzCJGf8Y8g4wj4s0NCVEOlJ4vx4PvMh39aQye -ha0pMs+Q0NkmKUYNmXqv71DsbWIkJDjvWqC/X+wREaGZykF1o+kGXFDQnObCEaX3 -Ctz92s7zWXsI2q7Bkw50ksS0LPb9ukD3XWSh6L0tTN+C6Lhxv+gwTqJu1XTv9toJ -Jm52RuS2UWZ25noYshUMas2sVM0Ak83R+XW2RSvOibGSoHXSpcW+bXnkwVxIdgNo -o7E4skSbaGUKYUpk2rvJI/D2HdLgYwnD9zGSe09mbqRfBPlb0KO16L4RsWtrH6EX -L60VoFDikVV6Rw1Ec2cP0dKD+QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAehG3o -Axyd7SXlq4QFB1y1062BU2+keI515pRfyAIpaMa8gDA17El6FLYkYhYro6QhfuSD -aNf6l681LiQZr9Xje6dXO77QR5fe1u72ocRzdE9d/XaYeHocHHwBwfAHRt7kSGgt -emYO0mwju9MxgJ3buvUvvJCjyWPEpaE2ot/cIeEU/4DzqVdWtGhvtUD/w1vGUV4d -F6K4k1Q1tl8ZN+mmZKYa9ppHTx92iDC3ZLH0tU8FA1sblmrBZWwDRimUSijuFM/W -+YS0C0+F+4DyDqJ/1oI3Y+BO3BY5A+GPhEbITH+sZLHUAWBIgRKXekmJg/1HuPNu -OsqKsnVMbgtFmitfg5HSUx99maEOlWEjbdFClH22Wpdxudlrq8IV0wCI5ZTvaP+y -pQfeAqYJOSBmFHiF30Rt0jYVRM4CEwqtqP7WCKZlQQQuVW+hnZFDcY0RkynQPJNM -VW9+pdUAXUHlIxn6HxSfZISLJeGihLlnKwnnjIj++oWT8tgQH8pBVmU8PSPLnLxH -zNllxqSFryAgXBmLeVymWu34nEL2MqASDsswDcMiJj0M9w1EHs/seis0PYB/c54g -/Y5Cgk2sRuiRWOr39H0Z49SfxqkMnmbjw0iLlopN4QFBQUqLmitJUoAZiHiwJPJc -4/YLQABjdGmF5lfq8xHflkOa83Wm3H5I5WgWGQ== ------END CERTIFICATE----- -)___"; - -const std::string ServerKey = R"___( ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEA4quzmIvZqAc90kPHlmBytC+OyAvoJmCpvOymaaYewnAy5rak -YS1FRstQ4ROgHXDCajw9NvnXXKjv152QoP/J07+42zcjexZAywEn5kZ83ht11ViY -Atjim7u84cDS4OPAh3V7sJMQex870KbQfN6V4O73HE2Bu35vpaVMcuDmUa+N6pnD -6l74eX/Ru9sZtUOjtvzkom8cWm/8XGgCAod1BpF2iycpHfCyixeZGysTNK10/Uye -BTUGIbQg/fTuVVWkb7ZzXw0xtvFq4Vw+Urcw2rvfcezFn4+g1eqRtvY+LnXJmnYZ -4PKsoSKGKWCpIisyN0M9QGU3l+yg0I0wFcm9xzEWcOlNncb1+MmS5CgAI1kU0swi -Rn/GPIOMI+LNDQlRDpSeL8eD7zId/WkMnoWtKTLPkNDZJilGDZl6r+9Q7G1iJCQ4 -71qgv1/sERGhmcpBdaPpBlxQ0JzmwhGl9wrc/drO81l7CNquwZMOdJLEtCz2/bpA -911koei9LUzfgui4cb/oME6ibtV07/baCSZudkbktlFmduZ6GLIVDGrNrFTNAJPN -0fl1tkUrzomxkqB10qXFvm155MFcSHYDaKOxOLJEm2hlCmFKZNq7ySPw9h3S4GMJ -w/cxkntPZm6kXwT5W9Cjtei+EbFrax+hFy+tFaBQ4pFVekcNRHNnD9HSg/kCAwEA -AQKCAgBOWWU5vFVetCoVTOJnQy1CxRGIaj0zTsQ9DluzNv143glqIAWPpXNFti9d -rUfyBTDeQbYzE4bye15z2/3K+L9Nlv6Rn2x/NkDtKpgdC45Lw5gmR3o7ubYSeIEg -U2NQ4siAygYYEa1nsXMeexqjntiVqGP3/35xTZHP4uQa7UwPPixCxCWpFGy0qo7X -bTNkqV2keaOZ1egqBn1nf3f6YdH8lDkyfjXDKJi+ZUjB0FDSK4a9q0cPq7VT3wxs -W8Yp7vFov9r/JvNhNe9ouFa+hp23basdmObycVX5uxvk7xatPn+SCXKGg7tR8zoG -gWfU7LNt0KsSYCooNF2d2L+fOF2FHuXffsHM090bf0GE5J829CN/H6Wa44NI9LDn -wmtTXoHOWLqKL+iPj12HfMsx5zhcdMBY1qo7vIBZfR1mYyeWcuAAApBJWED9Z5tJ -pEpIVBRgKq6Q/Tr66gTmU5FLRCFKlbIjD2sZdMl/Rkp1rskNdjRLw5cDhn2X+MyM -cRdF0YkJjRph/DnPP/z2aQgx3/NfjXpEhWtd7o0Ipkp4zrPqZ6yyTThY2VYgT2Ce -PzBBy1Ib+avIXU1EExkFahaijFALq4h4c60bstoTfoGq12Y2B512FJKtwB8+80Ih -Jlrtr4vAsWzndTxpIIrBSgQxy/F5lDcxPg0J2xFOr9DbGC34pQKCAQEA/JV3hLCV -GhciZPzsZHb7gBfkC1l2dn1y4QdnaTdVO4n2p/l4k5ucvuHjWqrBzEiO/CxcfRAg -bqUqs/IKwJ4TLvQWAk5zwAe4diuy/jV5VY8ruOiFc56QhieYuog0asPkP2LRucVq -skunljIZNoHLUTfs80ggMmEHBuewv2gnrNuWPp1cNbjCstivpeRuUwKmL2bdQfFA -TyQQHWn9eItNhRqX7MNr4fz1QUUSUt7CWYYoDhJC/Oo45320AjQR/Lms2QYvz6iG -CzVmIJxSqC7Oykz6gVcKu+R+RdO8DLRb+fIaA7QgAX8/lxLlJuKVn5F8WfaAzRQ6 -yKmbI9+HFifGDwKCAQEA5byDrObyWTL4BTeRyFlHRYF6cTe2xBv/Kuk4Ec6zHaaV -E39PfTJn2t0BkDvK9FXtF9sjKTmhsjg3mmgMNOz8Ll2/z4df6/u4zjy1dZ309zyh -yDWcP4tW8Hj6kl8PFStdlRXxIBMt/Gpsf4w45q2cNmqdMU4VBgwhycp87fq7EWJX -0zPDJnzOHo/twG+tsl/ZzQsHyxPC54oG3uFKWnT8azJTBgGeAD+huoONGk44TpOj -uV6LQpIS+q+vwRLmSaMeAiEG2N/IbMEU6l1gbz9ohfcASqFIVmxht2AE1GpD0I1o -XlxeoP0uSP9TtSuhSHfXowjaD/IBxzMJRtsA+X5ddwKCAQEAy5JbpaH8Se7jValT -jRUoVnDq5wrPo2gwMpWZDv/9veLP3Un/mFgO2PmOGAEP+Olx9GR8ln9s5EBSTn2B -lQTSSUGIi4tXVynhzbwioyfOBttBTeJ5zFm7+aPoQE6OkI4ZY8ztY2BtQg4fn7n5 -AClUCL2eR+WVrYTt+O67UUlM0NCaIxUIwHOM2EA0MOwOzvCPqByrrv4V6rMSGeLW -21TKwcBROg224YjS0iwtPIU09ppdphmpy9Wqz0hM0InPBXVQjgmidydIAbij+xyC -sfIn0HyCWcQhbpYV/4lLQqIKj0RFGz8NnKdGRSiBb/mmxdin9Inr/V2Uky2/UAZU -BdNAmQKCAQAYZPcacf+D5zyc2TS6sHg38jK9OOxIUKy8sr0Ibwln+ZtU0azwH10V -yWf0F9VKMqGVaeiG5R69XOjSlX/OUufISJ4ofDh8R2NtStb94UL0ydRn/QFVYgde -S4pX5o4kclFilkzfWgoFBov00z/rhr3SrWl5pc/nr3wbAExZvMkGZIns3E85lAET -D7dwOquYCEOJWUV/k96bVXW7TvLlPgzbmSFlvuA3KIqU0ok2JN4nwdedxGNHM1me -ku83sjkP0qlKEpW2i7Stj6cX58hop7QCnaLDSfLzcljB7wk0QQBoccuGUYqez6ON -jsclsrdSiZ81Kah2Dv2PWGUAyBqHY5qrAoIBAQCtjsqk71UkhTi5LB2ism1aGAme -pKMY459g57wmSEWE/+rBWOBY5CZ7FMV1W+P1bXqydZOr6ptBEUjM4hW6YcsKmDtk -g/LriBtDnPkd/QvrrpOZnj8rrtFyOF/wMx2jFwp6Nes+QtRqQk0RXerWDSbsn9vL -xSN/nSB6IoYs5rlC2TRoxCHqEGPSGImi4hhpLhIrArpqTH8+OhuRRVwtMO8Qo2fS -LEbMU1ARtp9TVPVktwsdNOnCeyynJnQiB6fL6QnpQYFY3xyO1MuBSjruTKLqorPM -pqUJavKfAG3qyVCaGn4msSaPDNnG5QH7eCpbuemWgPDmx/br4fdx/U7VH8HE ------END RSA PRIVATE KEY----- -)___"; - -}; // namespace NYdbSslTestData From cc73f48e5bf6ec76dad8cf05456caa80737e3f85 Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 20:14:47 +0300 Subject: [PATCH 3/9] fix rebase --- tests/integration/CMakeLists.txt | 4 ++-- .../integration/{sessions => sessions_it}/CMakeLists.txt | 0 .../integration/{sessions => sessions_it}/sessions_it.cpp | 6 +++--- .../{sessions_pool => sessions_pool_it}/CMakeLists.txt | 0 .../sessions_pool_it.cpp | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) rename tests/integration/{sessions => sessions_it}/CMakeLists.txt (100%) rename tests/integration/{sessions => sessions_it}/sessions_it.cpp (99%) rename tests/integration/{sessions_pool => sessions_pool_it}/CMakeLists.txt (100%) rename tests/integration/{sessions_pool => sessions_pool_it}/sessions_pool_it.cpp (99%) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 78d656ade6..7943d7494d 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(basic_example) add_subdirectory(bulk_upsert) add_subdirectory(server_restart) -add_subdirectory(sessions) -add_subdirectory(sessions_pool) +add_subdirectory(sessions_it) +add_subdirectory(sessions_pool_it) diff --git a/tests/integration/sessions/CMakeLists.txt b/tests/integration/sessions_it/CMakeLists.txt similarity index 100% rename from tests/integration/sessions/CMakeLists.txt rename to tests/integration/sessions_it/CMakeLists.txt diff --git a/tests/integration/sessions/sessions_it.cpp b/tests/integration/sessions_it/sessions_it.cpp similarity index 99% rename from tests/integration/sessions/sessions_it.cpp rename to tests/integration/sessions_it/sessions_it.cpp index 4d2bacfced..b929384244 100644 --- a/tests/integration/sessions/sessions_it.cpp +++ b/tests/integration/sessions_it/sessions_it.cpp @@ -1,11 +1,11 @@ #include #include -#include -#include +#include +#include #include -#include +#include #include #include diff --git a/tests/integration/sessions_pool/CMakeLists.txt b/tests/integration/sessions_pool_it/CMakeLists.txt similarity index 100% rename from tests/integration/sessions_pool/CMakeLists.txt rename to tests/integration/sessions_pool_it/CMakeLists.txt diff --git a/tests/integration/sessions_pool/sessions_pool_it.cpp b/tests/integration/sessions_pool_it/sessions_pool_it.cpp similarity index 99% rename from tests/integration/sessions_pool/sessions_pool_it.cpp rename to tests/integration/sessions_pool_it/sessions_pool_it.cpp index 14df2a207c..c30c675479 100644 --- a/tests/integration/sessions_pool/sessions_pool_it.cpp +++ b/tests/integration/sessions_pool_it/sessions_pool_it.cpp @@ -1,9 +1,9 @@ #include #include -#include +#include -#include +#include #include #include @@ -242,7 +242,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { CheckPlan(plan); RunPlan(plan, client); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); } @@ -301,7 +301,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { RunStressTestSync(1000, activeSessionsLimit, client); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); From 7a5623b1345d90c2042c0f3611461220ba0de3dd Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 20:45:49 +0300 Subject: [PATCH 4/9] return time --- tests/integration/sessions_pool_it/sessions_pool_it.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/sessions_pool_it/sessions_pool_it.cpp b/tests/integration/sessions_pool_it/sessions_pool_it.cpp index c30c675479..6b55924bf8 100644 --- a/tests/integration/sessions_pool_it/sessions_pool_it.cpp +++ b/tests/integration/sessions_pool_it/sessions_pool_it.cpp @@ -358,7 +358,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { RunStressTestAsync(100, 10, client); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); @@ -410,7 +410,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { sessionFutures.clear(); sessions.clear(); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); } From 2a674f22e585fb5452ef44d6b16f88884dd39c53 Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 21:05:59 +0300 Subject: [PATCH 5/9] added drop table --- tests/integration/sessions_it/sessions_it.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/integration/sessions_it/sessions_it.cpp b/tests/integration/sessions_it/sessions_it.cpp index b929384244..b5b1b88eae 100644 --- a/tests/integration/sessions_it/sessions_it.cpp +++ b/tests/integration/sessions_it/sessions_it.cpp @@ -164,6 +164,10 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { } UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); + auto session = client.GetSession().ExtractValueSync().GetSession(); + session.ExecuteSchemeQuery(R"___( + DROP TABLE `/local/t`; + )___").ExtractValueSync(); driver.Stop(true); } From 8637e8575986fc87d4d16fda10c042101a1b16d0 Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 21:23:56 +0300 Subject: [PATCH 6/9] drop table in basic_example --- tests/integration/basic_example/basic_example.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/integration/basic_example/basic_example.cpp b/tests/integration/basic_example/basic_example.cpp index f712001a55..7d2368e28e 100644 --- a/tests/integration/basic_example/basic_example.cpp +++ b/tests/integration/basic_example/basic_example.cpp @@ -514,3 +514,15 @@ std::vector ScanQuerySelect(TTableClient client, const std::st return resultSets; } + +void DropTables(TTableClient& client, const std::string& path) { + ThrowOnError(client.RetryOperationSync([path](TSession session) { + return session.DropTable(JoinPath(path, "series")).ExtractValueSync(); + })); + ThrowOnError(client.RetryOperationSync([path](TSession session) { + return session.DropTable(JoinPath(path, "seasons")).ExtractValueSync(); + })); + ThrowOnError(client.RetryOperationSync([path](TSession session) { + return session.DropTable(JoinPath(path, "episodes")).ExtractValueSync(); + })); +} From 6cdb675657b56d964054c089f1d9b967a17f54b3 Mon Sep 17 00:00:00 2001 From: st-shchetinin Date: Tue, 6 Aug 2024 21:58:47 +0300 Subject: [PATCH 7/9] return containers --- tests/integration/sessions_it/sessions_it.cpp | 34 +++++++++---------- .../sessions_pool_it/sessions_pool_it.cpp | 20 +++++------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/integration/sessions_it/sessions_it.cpp b/tests/integration/sessions_it/sessions_it.cpp index b5b1b88eae..2f43af456b 100644 --- a/tests/integration/sessions_it/sessions_it.cpp +++ b/tests/integration/sessions_it/sessions_it.cpp @@ -25,7 +25,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { NYdb::NTable::TTableClient client(driver); int count = 10; - std::unordered_set sids; + THashSet sids; while (count--) { auto sessionResponse = client.GetSession().ExtractValueSync(); UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); @@ -55,8 +55,8 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { NYdb::NTable::TTableClient client(driver); int count = 10; - std::vector sids; - std::vector results; + TVector sids; + TVector results; while (count--) { auto sessionResponse = client.GetSession().ExtractValueSync(); UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); @@ -90,8 +90,8 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { NYdb::NTable::TTableClient client(driver); int count = 10; - std::vector sids; - std::vector results; + TVector sids; + TVector results; while (count--) { auto sessionResponse = client.GetSession().ExtractValueSync(); UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); @@ -189,7 +189,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { }; IThreadFactory* pool = SystemThreadFactory(); - std::vector> threads; + TVector> threads; threads.resize(nThreads); for (int i = 0; i < nThreads; i++) { threads[i] = pool->Run(job); @@ -229,12 +229,12 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { constexpr int nThreads = 100; NYdb::EStatus statuses[nThreads]; - std::vector> sessions; + TVector> sessions; sessions.resize(nThreads); - std::atomic t = 0; + TAtomic t = 0; auto job = [client, &t, &statuses, &sessions]() mutable { auto sessionResponse = client.GetSession().ExtractValueSync(); - int i = ++t; + int i = AtomicIncrement(t); statuses[--i] = sessionResponse.GetStatus(); if (statuses[i] == EStatus::SUCCESS) { EnsureCanExecQuery(sessionResponse.GetSession()); @@ -243,7 +243,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { }; IThreadFactory* pool = SystemThreadFactory(); - std::vector> threads; + TVector> threads; threads.resize(nThreads); for (int i = 0; i < nThreads; i++) { threads[i] = pool->Run(job); @@ -309,14 +309,14 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { constexpr int nThreads = 20; constexpr int nRequests = 50; - std::array, nThreads> results; + std::array, nThreads> results; std::atomic t = 0; std::atomic validSessions = 0; auto job = [client, &t, &results, &validSessions]() mutable { auto sessionResponse = client.GetSession().ExtractValueSync(); int i = ++t; - std::vector& r = results[--i]; + TVector& r = results[--i]; if (sessionResponse.GetStatus() != EStatus::SUCCESS) { return; @@ -329,7 +329,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { }; IThreadFactory* pool = SystemThreadFactory(); - std::vector> threads; + TVector> threads; threads.resize(nThreads); for (int i = 0; i < nThreads; i++) { threads[i] = pool->Run(job); @@ -475,7 +475,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { }*/ Y_UNIT_TEST(CloseSessionAfterDriverDtorWithoutSessionPool) { - std::vector sessionIds; + TVector sessionIds; int iterations = 50; while (iterations--) { @@ -505,7 +505,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { } Y_UNIT_TEST(CloseSessionWithSessionPoolExplicit) { - std::vector sessionIds; + TVector sessionIds; int iterations = 100; while (iterations--) { @@ -559,7 +559,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { } Y_UNIT_TEST(CloseSessionWithSessionPoolExplicitDriverStopOnly) { - std::vector sessionIds; + TVector sessionIds; int iterations = 100; while (iterations--) { @@ -601,7 +601,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessions) { } Y_UNIT_TEST(CloseSessionWithSessionPoolFromDtors) { - std::vector sessionIds; + TVector sessionIds; int iterations = 100; while (iterations--) { diff --git a/tests/integration/sessions_pool_it/sessions_pool_it.cpp b/tests/integration/sessions_pool_it/sessions_pool_it.cpp index 6b55924bf8..fadf2aa606 100644 --- a/tests/integration/sessions_pool_it/sessions_pool_it.cpp +++ b/tests/integration/sessions_pool_it/sessions_pool_it.cpp @@ -54,7 +54,7 @@ using TPlan = std::vector>; void CheckPlan(TPlan plan) { - std::unordered_map sessions; + THashMap sessions; for (const auto& [action, sessionId]: plan) { if (action == EAction::CreateFuture) { UNIT_ASSERT(!sessions.contains(sessionId)); @@ -79,8 +79,8 @@ void CheckPlan(TPlan plan) { } void RunPlan(const TPlan& plan, NYdb::NTable::TTableClient& client) { - std::unordered_map> sessionFutures; - std::unordered_map sessions; + THashMap> sessionFutures; + THashMap sessions; ui32 requestedSessions = 0; @@ -162,8 +162,8 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { } void TestWaitQueue(NYdb::NTable::TTableClient& client, ui32 activeSessionsLimit) { - std::vector> sessionFutures; - std::vector sessions; + TVector> sessionFutures; + TVector sessions; // exhaust the pool for (ui32 i = 0; i < activeSessionsLimit; ++i) { @@ -248,8 +248,8 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { } ui32 RunStressTestSync(ui32 n, ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { - std::vector> sessionFutures; - std::vector sessions; + TVector> sessionFutures; + TVector sessions; std::mt19937 rng(0); ui32 successCount = 0; @@ -338,7 +338,7 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { }; IThreadFactory* pool = SystemThreadFactory(); - std::vector> threads; + TVector> threads; threads.resize(nThreads); for (ui32 i = 0; i < nThreads; i++) { threads[i] = pool->Run(job); @@ -379,8 +379,8 @@ Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { } void TestPeriodicTask(ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { - std::vector> sessionFutures; - std::vector sessions; + TVector> sessionFutures; + TVector sessions; for (ui32 i = 0; i < activeSessionsLimit; ++i) { sessions.emplace_back(client.GetSession().ExtractValueSync()); From 91c8cab3183aa515619275a9250d1c1ccb1280d6 Mon Sep 17 00:00:00 2001 From: Bulat Gayazov Date: Mon, 21 Apr 2025 21:06:55 +0000 Subject: [PATCH 8/9] Refactor tests --- CMakeLists.txt | 3 +- CMakePresets.json | 6 +- cmake/external_libs.cmake | 1 - cmake/testing.cmake | 143 +--- scripts/split_unittest.py | 80 -- src/client/draft/CMakeLists.txt | 25 - tests/integration/CMakeLists.txt | 4 +- .../integration/basic_example/CMakeLists.txt | 2 +- .../basic_example/basic_example.cpp | 12 - tests/integration/bulk_upsert/CMakeLists.txt | 2 +- .../integration/server_restart/CMakeLists.txt | 2 +- tests/integration/sessions/CMakeLists.txt | 12 + tests/integration/sessions/main.cpp | 798 ++++++++++++++++++ tests/integration/sessions_it/CMakeLists.txt | 12 - tests/integration/sessions_it/sessions_it.cpp | 643 -------------- .../integration/sessions_pool/CMakeLists.txt | 10 + tests/integration/sessions_pool/main.cpp | 369 ++++++++ .../sessions_pool_it/CMakeLists.txt | 12 - .../sessions_pool_it/sessions_pool_it.cpp | 451 ---------- 19 files changed, 1218 insertions(+), 1369 deletions(-) delete mode 100644 scripts/split_unittest.py delete mode 100644 src/client/draft/CMakeLists.txt create mode 100644 tests/integration/sessions/CMakeLists.txt create mode 100644 tests/integration/sessions/main.cpp delete mode 100644 tests/integration/sessions_it/CMakeLists.txt delete mode 100644 tests/integration/sessions_it/sessions_it.cpp create mode 100644 tests/integration/sessions_pool/CMakeLists.txt create mode 100644 tests/integration/sessions_pool/main.cpp delete mode 100644 tests/integration/sessions_pool_it/CMakeLists.txt delete mode 100644 tests/integration/sessions_pool_it/sessions_pool_it.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9390cc97c5..41f4783ca2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,11 +46,10 @@ include(cmake/install.cmake) include(cmake/common.cmake) include(cmake/ccache.cmake) include(cmake/protobuf.cmake) -include(cmake/testing.cmake) include(cmake/external_libs.cmake) if (YDB_SDK_TESTS) - enable_testing() + include(cmake/testing.cmake) endif() add_subdirectory(tools) diff --git a/CMakePresets.json b/CMakePresets.json index bd0c75cfb1..4638d0b0a9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -102,7 +102,11 @@ "name": "all", "inherits": "common", "displayName": "Run All Tests", - "configurePreset": "base" + "configurePreset": "base", + "environment": { + "YDB_ENDPOINT": "localhost:2136", + "YDB_DATABASE": "/local" + } }, { "name": "unit", diff --git a/cmake/external_libs.cmake b/cmake/external_libs.cmake index c258dea642..22d0603e77 100644 --- a/cmake/external_libs.cmake +++ b/cmake/external_libs.cmake @@ -12,7 +12,6 @@ find_package(Snappy 1.1.8 REQUIRED) find_package(base64 REQUIRED) find_package(Brotli 1.1.0 REQUIRED) find_package(jwt-cpp REQUIRED) -find_package(GTest REQUIRED) find_package(double-conversion REQUIRED) # RapidJSON diff --git a/cmake/testing.cmake b/cmake/testing.cmake index 1608fdfd84..d2a2050e23 100644 --- a/cmake/testing.cmake +++ b/cmake/testing.cmake @@ -1,81 +1,12 @@ -function(add_yunittest) - set(opts "") - set(oneval_args NAME TEST_TARGET WORKING_DIRECTORY) - set(multival_args TEST_ARG) - cmake_parse_arguments(YUNITTEST_ARGS - "${opts}" - "${oneval_args}" - "${multival_args}" - ${ARGN} - ) - - get_property(SPLIT_FACTOR TARGET ${YUNITTEST_ARGS_TEST_TARGET} PROPERTY SPLIT_FACTOR) - get_property(SPLIT_TYPE TARGET ${YUNITTEST_ARGS_TEST_TARGET} PROPERTY SPLIT_TYPE) - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/run_testpack") - add_test(NAME ${YUNITTEST_ARGS_NAME} - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/run_testpack" ${YUNITTEST_ARGS_TEST_ARG} - WORKING_DIRECTORY ${YUNITTEST_ARGS_WORKING_DIRECTORY} - ) - set_property(TEST ${YUNITTEST_ARGS_NAME} PROPERTY ENVIRONMENT "source_root=${YDB_SDK_SOURCE_DIR};build_root=${YDB_SDK_BINARY_DIR};test_split_factor=${SPLIT_FACTOR};test_split_type=${SPLIT_TYPE}") - return() - endif() - - if (${SPLIT_FACTOR} EQUAL 1) - add_test(NAME ${YUNITTEST_ARGS_NAME} - COMMAND ${YUNITTEST_ARGS_TEST_TARGET} ${YUNITTEST_ARGS_TEST_ARG} - WORKING_DIRECTORY ${YUNITTEST_ARGS_WORKING_DIRECTORY} - ) - return() - endif() - - if ("${SPLIT_TYPE}") - set(FORK_MODE_ARG --fork-mode ${SPLIT_TYPE}) - endif() - math(EXPR LastIdx "${SPLIT_FACTOR} - 1") - foreach(Idx RANGE ${LastIdx}) - add_test(NAME ${YUNITTEST_ARGS_NAME}_${Idx} - COMMAND Python3::Interpreter ${YDB_SDK_SOURCE_DIR}/scripts/split_unittest.py - --split-factor ${SPLIT_FACTOR} ${FORK_MODE_ARG} - --shard ${Idx} - $ ${YUNITTEST_ARGS_TEST_ARG} - WORKING_DIRECTORY ${YUNITTEST_ARGS_WORKING_DIRECTORY} - ) - endforeach() -endfunction() - -function(set_yunittest_property) - set(opts "") - set(oneval_args TEST PROPERTY) - set(multival_args ) - cmake_parse_arguments(YUNITTEST_ARGS - "${opts}" - "${oneval_args}" - "${multival_args}" - ${ARGN} - ) - get_property(SPLIT_FACTOR TARGET ${YUNITTEST_ARGS_TEST} PROPERTY SPLIT_FACTOR) - - if ((${SPLIT_FACTOR} EQUAL 1) OR (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/run_testpack")) - set_property(TEST ${YUNITTEST_ARGS_TEST} PROPERTY ${YUNITTEST_ARGS_PROPERTY} ${YUNITTEST_ARGS_UNPARSED_ARGUMENTS}) - return() - endif() +enable_testing() - math(EXPR LastIdx "${SPLIT_FACTOR} - 1") - foreach(Idx RANGE ${LastIdx}) - set_property(TEST ${YUNITTEST_ARGS_TEST}_${Idx} PROPERTY ${YUNITTEST_ARGS_PROPERTY} ${YUNITTEST_ARGS_UNPARSED_ARGUMENTS}) - endforeach() -endfunction() +find_package(GTest REQUIRED) +include(GoogleTest) function(add_ydb_test) set(opts GTEST) -<<<<<<< HEAD set(oneval_args NAME WORKING_DIRECTORY OUTPUT_DIRECTORY) set(multival_args INCLUDE_DIRS SOURCES LINK_LIBRARIES LABELS TEST_ARG) -======= - set(oneval_args NAME) - set(multival_args SPLIT_FACTOR INCLUDE_DIRS SOURCES LINK_LIBRARIES LABELS) ->>>>>>> 273b4fe0c (session_pool_ut) cmake_parse_arguments(YDB_TEST "${opts}" "${oneval_args}" @@ -117,76 +48,38 @@ function(add_ydb_test) ) endif() - if(NOT DEFINED YDB_TEST_SPLIT_FACTOR) - set(YDB_TEST_SPLIT_FACTOR "1") - endif() - - set_property( - TARGET - ${YDB_TEST_NAME} - PROPERTY - SPLIT_FACTOR - ${YDB_TEST_SPLIT_FACTOR} - ) if (YDB_TEST_GTEST) - add_yunittest( - NAME - ${YDB_TEST_NAME} - TEST_TARGET - ${YDB_TEST_NAME} - TEST_ARG - ${YDB_TEST_TEST_ARG} - WORKING_DIRECTORY - ${YDB_TEST_WORKING_DIRECTORY} + gtest_discover_tests(${YDB_TEST_NAME} + EXTRA_ARGS ${YDB_TEST_TEST_ARG} + WORKING_DIRECTORY ${YDB_TEST_WORKING_DIRECTORY} + PROPERTIES LABELS ${YDB_TEST_LABELS} + PROPERTIES PROCESSORS 1 + PROPERTIES TIMEOUT 600 ) + target_link_libraries(${YDB_TEST_NAME} PRIVATE GTest::gtest_main ) else() - add_yunittest( - NAME - ${YDB_TEST_NAME} - TEST_TARGET - ${YDB_TEST_NAME} - TEST_ARG + add_test(NAME ${YDB_TEST_NAME} + WORKING_DIRECTORY ${YDB_TEST_WORKING_DIRECTORY} + COMMAND ${YDB_TEST_NAME} --print-before-suite --print-before-test --fork-tests --print-times --show-fails ${YDB_TEST_TEST_ARG} - WORKING_DIRECTORY - ${YDB_TEST_WORKING_DIRECTORY} ) + target_link_libraries(${YDB_TEST_NAME} PRIVATE cpp-testing-unittest_main ) - endif() - - set_yunittest_property( - TEST - ${YDB_TEST_NAME} - PROPERTY - LABELS - MEDIUM - ${YDB_TEST_LABELS} - ) - set_yunittest_property( - TEST - ${YDB_TEST_NAME} - PROPERTY - PROCESSORS - 1 - ) - - set_yunittest_property( - TEST - ${YDB_TEST_NAME} - PROPERTY - TIMEOUT - 600 - ) + set_tests_properties(${YDB_TEST_NAME} PROPERTIES LABELS ${YDB_TEST_LABELS}) + set_tests_properties(${YDB_TEST_NAME} PROPERTIES PROCESSORS 1) + set_tests_properties(${YDB_TEST_NAME} PROPERTIES TIMEOUT 600) + endif() vcs_info(${YDB_TEST_NAME}) endfunction() diff --git a/scripts/split_unittest.py b/scripts/split_unittest.py deleted file mode 100644 index 8874b8b915..0000000000 --- a/scripts/split_unittest.py +++ /dev/null @@ -1,80 +0,0 @@ -import argparse -import tempfile -import shlex -import subprocess - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("--split-factor", type=int, default=0) - parser.add_argument("--shard", type=int, default=0) - parser.add_argument("--fork-mode", type=str, default="SEQUENTIAL") - parser.add_argument("command", nargs=argparse.REMAINDER) - return parser.parse_args() - - -def get_sequential_chunk(tests, modulo, modulo_index): - chunk_size = len(tests) // modulo - not_used = len(tests) % modulo - shift = chunk_size + (modulo_index < not_used) - start = chunk_size * modulo_index + min(modulo_index, not_used) - end = start + shift - return [] if end > len(tests) else tests[start:end] - - -def get_shuffled_chunk(tests, modulo, modulo_index): - result_tests = [] - for i, test in enumerate(tests): - if i % modulo == modulo_index: - result_tests.append(test) - return result_tests - - -def list_tests(binary): - with tempfile.NamedTemporaryFile() as tmpfile: - cmd = [binary, "--list-verbose", "--list-path", tmpfile.name] - subprocess.check_call(cmd) - - with open(tmpfile.name) as afile: - lines = afile.read().strip().split("\n") - lines = [x.strip() for x in lines] - return [x for x in lines if x] - - -def get_shard_tests(args): - test_names = list_tests(args.command[0]) - test_names = sorted(test_names) - - if args.fork_mode == "MODULO": - return get_shuffled_chunk(test_names, args.split_factor, args.shard) - elif args.fork_mode == "SEQUENTIAL": - return get_sequential_chunk(test_names, args.split_factor, args.shard) - else: - raise ValueError("detected unknown partition mode: {}".format(args.fork_mode)) - - -def get_shard_cmd_args(args): - return ["+{}".format(x) for x in get_shard_tests(args)] - - -def main(): - args = parse_args() - - if args.split_factor: - shard_cmd = get_shard_cmd_args(args) - if shard_cmd: - cmd = args.command + shard_cmd - else: - print("No tests for {} shard".format(args.shard)) - return 0 - else: - cmd = args.command - - rc = subprocess.call(cmd) - if rc: - print("Some tests failed. To reproduce run: {}".format(shlex.join(cmd))) - return rc - - -if __name__ == "__main__": - exit(main()) diff --git a/src/client/draft/CMakeLists.txt b/src/client/draft/CMakeLists.txt deleted file mode 100644 index 988b7706ec..0000000000 --- a/src/client/draft/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -_ydb_sdk_add_library(client-draft) - -target_link_libraries(client-draft PUBLIC - yutil - yql-public-issue - api-grpc-draft - client-ydb_table - client-ydb_types-operation - client-ydb_value -) - -target_sources(client-draft PRIVATE - ydb_dynamic_config.cpp - ydb_replication.cpp - ydb_scripting.cpp - ydb_view.cpp -) - -generate_enum_serilization(client-draft - ${YDB_SDK_SOURCE_DIR}/include/ydb-cpp-sdk/client/draft/ydb_replication.h - INCLUDE_HEADERS - include/ydb-cpp-sdk/client/draft/ydb_replication.h -) - -_ydb_sdk_make_client_component(Draft client-draft) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 7943d7494d..78d656ade6 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(basic_example) add_subdirectory(bulk_upsert) add_subdirectory(server_restart) -add_subdirectory(sessions_it) -add_subdirectory(sessions_pool_it) +add_subdirectory(sessions) +add_subdirectory(sessions_pool) diff --git a/tests/integration/basic_example/CMakeLists.txt b/tests/integration/basic_example/CMakeLists.txt index 6178b80c71..55bdd05341 100644 --- a/tests/integration/basic_example/CMakeLists.txt +++ b/tests/integration/basic_example/CMakeLists.txt @@ -1,4 +1,4 @@ -add_ydb_test(NAME basic-example GTEST +add_ydb_test(NAME basic_example_it GTEST SOURCES main.cpp basic_example_data.cpp diff --git a/tests/integration/basic_example/basic_example.cpp b/tests/integration/basic_example/basic_example.cpp index 7d2368e28e..f712001a55 100644 --- a/tests/integration/basic_example/basic_example.cpp +++ b/tests/integration/basic_example/basic_example.cpp @@ -514,15 +514,3 @@ std::vector ScanQuerySelect(TTableClient client, const std::st return resultSets; } - -void DropTables(TTableClient& client, const std::string& path) { - ThrowOnError(client.RetryOperationSync([path](TSession session) { - return session.DropTable(JoinPath(path, "series")).ExtractValueSync(); - })); - ThrowOnError(client.RetryOperationSync([path](TSession session) { - return session.DropTable(JoinPath(path, "seasons")).ExtractValueSync(); - })); - ThrowOnError(client.RetryOperationSync([path](TSession session) { - return session.DropTable(JoinPath(path, "episodes")).ExtractValueSync(); - })); -} diff --git a/tests/integration/bulk_upsert/CMakeLists.txt b/tests/integration/bulk_upsert/CMakeLists.txt index 05a141963f..46848877c6 100644 --- a/tests/integration/bulk_upsert/CMakeLists.txt +++ b/tests/integration/bulk_upsert/CMakeLists.txt @@ -1,4 +1,4 @@ -add_ydb_test(NAME bulk_upsert GTEST +add_ydb_test(NAME bulk_upsert_it GTEST SOURCES main.cpp bulk_upsert.cpp diff --git a/tests/integration/server_restart/CMakeLists.txt b/tests/integration/server_restart/CMakeLists.txt index 2420bae608..2d485de4e4 100644 --- a/tests/integration/server_restart/CMakeLists.txt +++ b/tests/integration/server_restart/CMakeLists.txt @@ -1,4 +1,4 @@ -add_ydb_test(NAME server_restart GTEST +add_ydb_test(NAME server_restart_it GTEST SOURCES main.cpp LINK_LIBRARIES diff --git a/tests/integration/sessions/CMakeLists.txt b/tests/integration/sessions/CMakeLists.txt new file mode 100644 index 0000000000..100c8ace2b --- /dev/null +++ b/tests/integration/sessions/CMakeLists.txt @@ -0,0 +1,12 @@ +add_ydb_test(NAME sessions_it GTEST + SOURCES + main.cpp + LINK_LIBRARIES + yutil + YDB-CPP-SDK::Table + YDB-CPP-SDK::Query + api-grpc + grpc-client + LABELS + integration +) diff --git a/tests/integration/sessions/main.cpp b/tests/integration/sessions/main.cpp new file mode 100644 index 0000000000..26a126a047 --- /dev/null +++ b/tests/integration/sessions/main.cpp @@ -0,0 +1,798 @@ +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +using namespace NYdb; +using namespace NYdb::NTable; + +namespace { + +void CreateTestTable(NYdb::TDriver& driver) { + NYdb::NTable::TTableClient client(driver); + auto sessionResult = client.CreateSession().ExtractValueSync(); + ASSERT_TRUE(sessionResult.IsSuccess()); + auto session = sessionResult.GetSession(); + auto result = session.ExecuteSchemeQuery(R"___( + CREATE TABLE `/local/t` ( + Key Uint32, + Value String, + PRIMARY KEY (Key) + ); + )___").ExtractValueSync(); + ASSERT_TRUE(result.IsSuccess()); +} + +void WarmPoolCreateSession(NYdb::NQuery::TQueryClient& client, std::string& sessionId) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + auto result = session.ExecuteQuery(R"___( + SELECT 42; + )___", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + ASSERT_TRUE(result.IsSuccess()); + sessionId = session.GetId(); +} + +void WaitForSessionsInPool(NYdb::NQuery::TQueryClient& client, std::int64_t expected) { + while (client.GetActiveSessionCount() != expected) { + Sleep(TDuration::MilliSeconds(100)); + } +} + +} + +void CheckDelete(const NYdbGrpc::TGRpcClientConfig& clientConfig, const std::string& id, int expected, bool& allDoneOk) { + NYdbGrpc::TGRpcClientLow clientLow; + auto connection = clientLow.CreateGRpcServiceConnection(clientConfig); + + Ydb::Query::DeleteSessionRequest request; + request.set_session_id(id); + + NYdbGrpc::TResponseCallback responseCb = + [&allDoneOk, expected](NYdbGrpc::TGrpcStatus&& grpcStatus, Ydb::Query::DeleteSessionResponse&& response) -> void { + ASSERT_FALSE(grpcStatus.InternalError); + ASSERT_EQ(grpcStatus.GRpcStatusCode, 0) << grpcStatus.Msg + " " + grpcStatus.Details; + allDoneOk &= (response.status() == expected); + if (!allDoneOk) { + std::cerr << "Expected status: " << expected << ", got response: " << response.DebugString() << std::endl; + } + }; + + connection->DoRequest(request, std::move(responseCb), &Ydb::Query::V1::QueryService::Stub::AsyncDeleteSession); +} + +TEST(YdbSdkSessions, TestSessionPool) { + const std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::unordered_set sids; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_FALSE(sessionResponse.IsTransportError()); + auto session = sessionResponse.GetSession(); + sids.insert(session.GetId()); + auto result = session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync(); + + ASSERT_EQ(result.GetStatus(), EStatus::SUCCESS); + ASSERT_EQ(result.GetEndpoint(), location); + } + // All requests used one session + ASSERT_EQ(sids.size(), 1); + // No more session captured by client + ASSERT_EQ(client.GetActiveSessionCount(), 0); + + driver.Stop(true); +} + +TEST(YdbSdkSessions, TestMultipleSessions) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::vector sids; + std::vector results; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_FALSE(sessionResponse.IsTransportError()); + auto session = sessionResponse.GetSession(); + sids.push_back(session); + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } + + NThreading::WaitExceptionOrAll(results).Wait(); + ASSERT_EQ(client.GetActiveSessionCount(), 10); + + for (auto& result : results) { + ASSERT_EQ(result.GetValue().GetStatus(), EStatus::SUCCESS); + } + sids.clear(); + results.clear(); + + ASSERT_EQ(client.GetActiveSessionCount(), 0); + + driver.Stop(true); +} + +TEST(YdbSdkSessions, TestActiveSessionCountAfterBadSession) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 10; + + std::vector sids; + std::vector results; + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_FALSE(sessionResponse.IsTransportError()); + auto session = sessionResponse.GetSession(); + sids.push_back(session); + if (count == 0) { + // Force BAD session server response for ExecuteDataQuery + ASSERT_EQ(session.Close().GetValueSync().GetStatus(), EStatus::SUCCESS); + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } else { + results.push_back(session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); + } + } + + NThreading::WaitExceptionOrAll(results).Wait(); + ASSERT_EQ(client.GetActiveSessionCount(), 10); + + for (size_t i = 0; i < results.size(); i++) { + if (i == 9) { + ASSERT_EQ(results[i].GetValue().GetStatus(), EStatus::BAD_SESSION); + } else { + ASSERT_EQ(results[i].GetValue().GetStatus(), EStatus::SUCCESS); + } + } + sids.clear(); + results.clear(); + + ASSERT_EQ(client.GetActiveSessionCount(), 0); + + driver.Stop(true); +} + +TEST(YdbSdkSessions, TestSdkFreeSessionAfterBadSessionQueryService) { + GTEST_SKIP() << "Test is failing right now"; + std::string location = std::getenv("YDB_ENDPOINT"); + auto clientConfig = NYdbGrpc::TGRpcClientConfig(location); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + CreateTestTable(driver); + + NYdb::NQuery::TQueryClient client(driver); + std::string sessionId; + WarmPoolCreateSession(client, sessionId); + WaitForSessionsInPool(client, 1); + + bool allDoneOk = true; + CheckDelete(clientConfig, sessionId, Ydb::StatusIds::SUCCESS, allDoneOk); + ASSERT_TRUE(allDoneOk); + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + ASSERT_EQ(session.GetId(), sessionId); + + auto res = session.ExecuteQuery("SELECT * FROM `/local/t`", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).GetValueSync(); + + ASSERT_EQ(res.GetStatus(), EStatus::BAD_SESSION) << res.GetIssues().ToString(); + } + + WaitForSessionsInPool(client, 0); + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + ASSERT_NE(session.GetId(), sessionId); + auto res = session.ExecuteQuery("SELECT * FROM `/local/t`", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).GetValueSync(); + + ASSERT_EQ(res.GetStatus(), EStatus::SUCCESS) << res.GetIssues().ToString(); + } + + WaitForSessionsInPool(client, 1); + + driver.Stop(true); +} + +TEST(YdbSdkSessions, TestSdkFreeSessionAfterBadSessionQueryServiceStreamCall) { + GTEST_SKIP() << "Test is failing right now"; + std::string location = std::getenv("YDB_ENDPOINT"); + auto clientConfig = NYdbGrpc::TGRpcClientConfig(location); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + CreateTestTable(driver); + + NYdb::NQuery::TQueryClient client(driver); + std::string sessionId; + WarmPoolCreateSession(client, sessionId); + WaitForSessionsInPool(client, 1); + + bool allDoneOk = true; + CheckDelete(clientConfig, sessionId, Ydb::StatusIds::SUCCESS, allDoneOk); + ASSERT_TRUE(allDoneOk); + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + ASSERT_EQ(session.GetId(), sessionId); + + auto it = session.StreamExecuteQuery("SELECT * FROM `/local/t`", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).GetValueSync(); + + ASSERT_EQ(it.GetStatus(), EStatus::SUCCESS) << it.GetIssues().ToString(); + + auto res = it.ReadNext().GetValueSync(); + ASSERT_EQ(res.GetStatus(), EStatus::BAD_SESSION) << res.GetIssues().ToString(); + } + + WaitForSessionsInPool(client, 0); + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + ASSERT_NE(session.GetId(), sessionId); + + auto res = session.ExecuteQuery("SELECT * FROM `/local/t`", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).GetValueSync(); + + ASSERT_EQ(res.GetStatus(), EStatus::SUCCESS) << res.GetIssues().ToString(); + } + + WaitForSessionsInPool(client, 1); + + driver.Stop(true); +} + +TEST(YdbSdkSessions, TestActiveSessionCountAfterTransportError) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + int count = 100; + + { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_TRUE(sessionResponse.IsSuccess()); + auto session = sessionResponse.GetSession(); + auto result = session.ExecuteSchemeQuery(R"___( + CREATE TABLE `/local/t` ( + Key Uint32, + Value String, + PRIMARY KEY (Key) + ); + )___").ExtractValueSync(); + ASSERT_TRUE(result.IsSuccess()); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + } + + while (count--) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_FALSE(sessionResponse.IsTransportError()); + auto session = sessionResponse.GetSession(); + + // Assume 10us is too small to execute query and get response + auto res = session.ExecuteDataQuery("SELECT COUNT(*) FROM `/local/t`;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), + NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + } + + ASSERT_EQ(client.GetActiveSessionCount(), 0); + auto session = client.GetSession().ExtractValueSync().GetSession(); + session.ExecuteSchemeQuery(R"___( + DROP TABLE `/local/t`; + )___").ExtractValueSync(); + driver.Stop(true); +} + +TEST(YdbSdkSessions, MultiThreadSync) { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + NYdb::NTable::TTableClient client(driver); + const int nThreads = 10; + const int nRequests = 1000; + auto job = [client]() mutable { + for (int i = 0; i < nRequests; i++) { + auto sessionResponse = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResponse.GetStatus(), EStatus::SUCCESS); + } + }; + std::vector threads; + for (int i = 0; i < nThreads; i++) { + threads.emplace_back(job); + } + for (auto& thread : threads) { + thread.join(); + } + ASSERT_EQ(client.GetActiveSessionCount(), 0); + driver.Stop(true); +} + +void EnsureCanExecQuery(NYdb::NTable::TSession session) { + auto execStatus = session.ExecuteDataQuery("SELECT 42;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync().GetStatus(); + ASSERT_EQ(execStatus, EStatus::SUCCESS); +} + +void EnsureCanExecQuery(NYdb::NQuery::TSession session) { + auto execStatus = session.ExecuteQuery("SELECT 42;", + NYdb::NQuery::TTxControl::BeginTx().CommitTx()).ExtractValueSync().GetStatus(); + ASSERT_EQ(execStatus, EStatus::SUCCESS); +} + +template +void DoMultiThreadSessionPoolLimitSync() { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + const int maxActiveSessions = 45; + TClient client(driver, + typename TClient::TSettings() + .SessionPoolSettings( + typename TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); + + constexpr int nThreads = 100; + NYdb::EStatus statuses[nThreads]; + std::vector> sessions; + sessions.resize(nThreads); + std::atomic t = 0; + auto job = [client, &t, &statuses, &sessions]() mutable { + auto sessionResponse = client.GetSession().ExtractValueSync(); + int i = ++t; + statuses[--i] = sessionResponse.GetStatus(); + if (statuses[i] == EStatus::SUCCESS) { + EnsureCanExecQuery(sessionResponse.GetSession()); + sessions[i] = sessionResponse.GetSession(); + } + }; + + std::vector threads; + threads.resize(nThreads); + for (int i = 0; i < nThreads; i++) { + threads[i] = std::thread(job); + } + for (int i = 0; i < nThreads; i++) { + threads[i].join(); + } + + sessions.clear(); + + int successCount = 0; + int exhaustedCount = 0; + for (int i = 0; i < nThreads; i++) { + switch (statuses[i]) { + case EStatus::SUCCESS: + successCount++; + break; + case EStatus::CLIENT_RESOURCE_EXHAUSTED: + exhaustedCount++; + break; + default: + FAIL() << "Unexpected status code: " << static_cast(statuses[i]); + } + } + + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(successCount, maxActiveSessions); + ASSERT_EQ(exhaustedCount, nThreads - maxActiveSessions); + driver.Stop(true); +} + +TEST(YdbSdkSessions, MultiThreadSessionPoolLimitSyncTableClient) { + DoMultiThreadSessionPoolLimitSync(); +} + +TEST(YdbSdkSessions, MultiThreadSessionPoolLimitSyncQueryClient) { + DoMultiThreadSessionPoolLimitSync(); +} + +NYdb::NTable::TAsyncDataQueryResult ExecQueryAsync(NYdb::NTable::TSession session, const std::string q) { + return session.ExecuteDataQuery(q, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); +} + +NYdb::NQuery::TAsyncExecuteQueryResult ExecQueryAsync(NYdb::NQuery::TSession session, const std::string q) { + return session.ExecuteQuery(q, + NYdb::NQuery::TTxControl::BeginTx().CommitTx()); +} + +template +void DoMultiThreadMultipleRequestsOnSharedSessions() { + std::string location = std::getenv("YDB_ENDPOINT"); + + auto driver = NYdb::TDriver( + TDriverConfig() + .SetEndpoint(location)); + + const int maxActiveSessions = 10; + typename T::TClient client(driver, + typename T::TClient::TSettings() + .SessionPoolSettings( + typename T::TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); + + constexpr int nThreads = 20; + constexpr int nRequests = 50; + std::array, nThreads> results; + std::atomic t = 0; + std::atomic validSessions = 0; + auto job = [client, &t, &results, &validSessions]() mutable { + auto sessionResponse = client.GetSession().ExtractValueSync(); + + int i = ++t; + std::vector& r = results[--i]; + + if (sessionResponse.GetStatus() != EStatus::SUCCESS) { + return; + } + validSessions.fetch_add(1); + + for (int i = 0; i < nRequests; i++) { + r.push_back(ExecQueryAsync(sessionResponse.GetSession(), "SELECT 42;")); + } + }; + + std::vector threads; + for (int i = 0; i < nThreads; i++) { + threads.emplace_back(job); + } + for (auto& thread : threads) { + thread.join(); + } + + for (auto& r : results) { + NThreading::WaitExceptionOrAll(r).Wait(); + } + for (auto& r : results) { + if (!r.empty()) { + for (auto& asyncStatus : r) { + auto res = asyncStatus.GetValue(); + if (!res.IsSuccess()) { + ASSERT_EQ(res.GetStatus(), EStatus::SESSION_BUSY); + } + } + } + } + ASSERT_EQ(client.GetActiveSessionCount(), maxActiveSessions); + auto curExpectedActive = maxActiveSessions; + auto empty = 0; + for (auto& r : results) { + if (!r.empty()) { + r.clear(); + ASSERT_EQ(client.GetActiveSessionCount(), --curExpectedActive); + } else { + empty++; + } + } + ASSERT_EQ(empty, nThreads - maxActiveSessions); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + driver.Stop(true); +} + +TEST(YdbSdkSessions, MultiThreadMultipleRequestsOnSharedSessionsTableClient) { + struct TTypeHelper { + using TClient = NYdb::NTable::TTableClient; + using TResult = NYdb::NTable::TAsyncDataQueryResult; + }; + DoMultiThreadMultipleRequestsOnSharedSessions(); +} + +TEST(YdbSdkSessions, MultiThreadMultipleRequestsOnSharedSessionsQueryClient) { + GTEST_SKIP() << "Enable after interactive tx support"; + struct TTypeHelper { + using TClient = NYdb::NQuery::TQueryClient; + using TResult = NYdb::NQuery::TAsyncExecuteQueryResult; + }; + DoMultiThreadMultipleRequestsOnSharedSessions(); +} + +TEST(YdbSdkSessions, SessionsServerLimit) { + GTEST_SKIP() << "Enable after accepting a pull request with merging configs"; + + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult = client.CreateSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + + sessionResult = client.CreateSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session2 = sessionResult.GetSession(); + + sessionResult = client.CreateSession().ExtractValueSync(); + sessionResult.GetIssues().PrintTo(Cerr); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::OVERLOADED); + + auto status = session1.Close().ExtractValueSync(); + ASSERT_EQ(status.IsTransportError(), false); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(status.GetStatus(), EStatus::SUCCESS); + + auto result = session2.ExecuteDataQuery(R"___( + SELECT 1; + )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(result.GetStatus(), EStatus::SUCCESS); + + sessionResult = client.CreateSession().ExtractValueSync(); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + + sessionResult = client.CreateSession().ExtractValueSync(); + sessionResult.GetIssues().PrintTo(Cerr); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::OVERLOADED); + ASSERT_EQ(client.GetActiveSessionCount(), 0); +} + +TEST(YdbSdkSessions, SessionsServerLimitWithSessionPool) { + GTEST_SKIP() << "Enable after accepting a pull request with merging configs"; + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult1 = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResult1.GetStatus(), EStatus::SUCCESS); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + auto session1 = sessionResult1.GetSession(); + + auto sessionResult2 = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResult2.GetStatus(), EStatus::SUCCESS); + ASSERT_EQ(client.GetActiveSessionCount(), 2); + auto session2 = sessionResult2.GetSession(); + + { + auto sessionResult3 = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResult3.GetStatus(), EStatus::OVERLOADED); + ASSERT_EQ(client.GetActiveSessionCount(), 3); + } + ASSERT_EQ(client.GetActiveSessionCount(), 2); + + auto status = session1.Close().ExtractValueSync(); + ASSERT_EQ(status.IsTransportError(), false); + ASSERT_EQ(status.GetStatus(), EStatus::SUCCESS); + + // Close doesnt free session from user perspective, + // the value of ActiveSessionsCounter will be same after Close() call. + // Probably we want to chenge this contract + ASSERT_EQ(client.GetActiveSessionCount(), 2); + + auto result = session2.ExecuteDataQuery(R"___( + SELECT 1; + )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + ASSERT_EQ(result.GetStatus(), EStatus::SUCCESS); + + sessionResult1 = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResult1.GetStatus(), EStatus::SUCCESS); + ASSERT_EQ(sessionResult1.GetSession().GetId().empty(), false); + ASSERT_EQ(client.GetActiveSessionCount(), 3); + + auto sessionResult3 = client.GetSession().ExtractValueSync(); + ASSERT_EQ(sessionResult3.GetStatus(), EStatus::OVERLOADED); + ASSERT_EQ(client.GetActiveSessionCount(), 4); + + auto tmp = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 5); + sessionResult1 = tmp; // here we reset previous created session object, + // so perform close rpc call implicitly and delete it + ASSERT_EQ(sessionResult1.GetStatus(), EStatus::OVERLOADED); + ASSERT_EQ(client.GetActiveSessionCount(), 4); +} + +TEST(YdbSdkSessions, CloseSessionAfterDriverDtorWithoutSessionPool) { + std::vector sessionIds; + int iterations = 50; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + auto sessionResult = client.CreateSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + ASSERT_TRUE(status.ok()); + auto deferred = response.operation(); + ASSERT_TRUE(deferred.ready() == true); + ASSERT_EQ(deferred.status(), Ydb::StatusIds::BAD_SESSION); + } +} + +TEST(YdbSdkSessions, CloseSessionWithSessionPoolExplicit) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 2); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 9); + if (dis(gen) == 5) { + auto future = client.Stop(); + future.Apply([](NThreading::TFuture f) { + return f; + }).Wait(); + } else { + client.Stop().Wait(); + } + + if (iterations & 4) { + driver.Stop(true); + } + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + ASSERT_TRUE(status.ok()); + auto deferred = response.operation(); + ASSERT_TRUE(deferred.ready() == true); + ASSERT_EQ(deferred.status(), Ydb::StatusIds::BAD_SESSION); + } +} + +TEST(YdbSdkSessions, CloseSessionWithSessionPoolExplicitDriverStopOnly) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 2); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + driver.Stop(true); + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + ASSERT_TRUE(status.ok()); + auto deferred = response.operation(); + ASSERT_TRUE(deferred.ready() == true); + ASSERT_EQ(deferred.status(), Ydb::StatusIds::BAD_SESSION); + } +} + +TEST(YdbSdkSessions, CloseSessionWithSessionPoolFromDtors) { + std::vector sessionIds; + int iterations = 100; + + while (iterations--) { + NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + NYdb::NTable::TTableClient client(driver); + //TODO: remove this scope after session tracker implementation + { + auto sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + auto session1 = sessionResult.GetSession(); + sessionIds.push_back(session1.GetId()); + + sessionResult = client.GetSession().ExtractValueSync(); + ASSERT_EQ(client.GetActiveSessionCount(), 2); + ASSERT_EQ(sessionResult.GetStatus(), EStatus::SUCCESS); + // Here previous created session will be returnet to session pool + session1 = sessionResult.GetSession(); + ASSERT_EQ(client.GetActiveSessionCount(), 1); + sessionIds.push_back(session1.GetId()); + } + } + + std::shared_ptr channel; + channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); + auto stub = Ydb::Table::V1::TableService::NewStub(channel); + for (const auto& sessionId : sessionIds) { + grpc::ClientContext context; + Ydb::Table::KeepAliveRequest request; + request.set_session_id(sessionId); + Ydb::Table::KeepAliveResponse response; + auto status = stub->KeepAlive(&context, request, &response); + ASSERT_TRUE(status.ok()); + auto deferred = response.operation(); + ASSERT_TRUE(deferred.ready() == true); + ASSERT_EQ(deferred.status(), Ydb::StatusIds::BAD_SESSION); + } +} diff --git a/tests/integration/sessions_it/CMakeLists.txt b/tests/integration/sessions_it/CMakeLists.txt deleted file mode 100644 index 55f70c77d0..0000000000 --- a/tests/integration/sessions_it/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -add_ydb_test(NAME sessions_it - SOURCES - sessions_it.cpp - LINK_LIBRARIES - yutil - cpp-testing-unittest_main - YDB-CPP-SDK::Driver - YDB-CPP-SDK::Table - api-grpc - LABELS - integration -) \ No newline at end of file diff --git a/tests/integration/sessions_it/sessions_it.cpp b/tests/integration/sessions_it/sessions_it.cpp deleted file mode 100644 index 2f43af456b..0000000000 --- a/tests/integration/sessions_it/sessions_it.cpp +++ /dev/null @@ -1,643 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - -#include -#include - -using namespace NYdb; -using namespace NYdb::NTable; - -Y_UNIT_TEST_SUITE(YdbSdkSessions) { - Y_UNIT_TEST(TestSessionPool) { - - const std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - NYdb::NTable::TTableClient client(driver); - int count = 10; - - THashSet sids; - while (count--) { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); - auto session = sessionResponse.GetSession(); - sids.insert(session.GetId()); - auto result = session.ExecuteDataQuery("SELECT 42;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync(); - - UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); - UNIT_ASSERT_VALUES_EQUAL(result.GetEndpoint(), location); - } - // All requests used one session - UNIT_ASSERT_VALUES_EQUAL(sids.size(), 1); - // No more session captured by client - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - - driver.Stop(true); - } - - Y_UNIT_TEST(TestMultipleSessions) { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - NYdb::NTable::TTableClient client(driver); - int count = 10; - - TVector sids; - TVector results; - while (count--) { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); - auto session = sessionResponse.GetSession(); - sids.push_back(session); - results.push_back(session.ExecuteDataQuery("SELECT 42;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); - } - - NThreading::WaitExceptionOrAll(results).Wait(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 10); - - for (auto& result : results) { - UNIT_ASSERT_EQUAL(result.GetValue().GetStatus(), EStatus::SUCCESS); - } - sids.clear(); - results.clear(); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - - driver.Stop(true); - } - - Y_UNIT_TEST(TestActiveSessionCountAfterBadSession) { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - NYdb::NTable::TTableClient client(driver); - int count = 10; - - TVector sids; - TVector results; - while (count--) { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); - auto session = sessionResponse.GetSession(); - sids.push_back(session); - if (count == 0) { - // Force BAD session server response for ExecuteDataQuery - UNIT_ASSERT_EQUAL(session.Close().GetValueSync().GetStatus(), EStatus::SUCCESS); - results.push_back(session.ExecuteDataQuery("SELECT 42;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); - } else { - results.push_back(session.ExecuteDataQuery("SELECT 42;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx())); - } - } - - NThreading::WaitExceptionOrAll(results).Wait(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 10); - - for (size_t i = 0; i < results.size(); i++) { - if (i == 9) { - UNIT_ASSERT_EQUAL(results[i].GetValue().GetStatus(), EStatus::BAD_SESSION); - } else { - UNIT_ASSERT_EQUAL(results[i].GetValue().GetStatus(), EStatus::SUCCESS); - } - } - sids.clear(); - results.clear(); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - - driver.Stop(true); - } - - Y_UNIT_TEST(TestActiveSessionCountAfterTransportError) { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - NYdb::NTable::TTableClient client(driver); - int count = 100; - - { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT(sessionResponse.IsSuccess()); - auto session = sessionResponse.GetSession(); - auto result = session.ExecuteSchemeQuery(R"___( - CREATE TABLE `/local/t` ( - Key Uint32, - Value String, - PRIMARY KEY (Key) - ); - )___").ExtractValueSync(); - UNIT_ASSERT(result.IsSuccess()); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - } - - while (count--) { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_EQUAL(sessionResponse.IsTransportError(), false); - auto session = sessionResponse.GetSession(); - - // Assume 10us is too small to execute query and get response - auto res = session.ExecuteDataQuery("SELECT COUNT(*) FROM `/local/t`;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), - NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - } - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - auto session = client.GetSession().ExtractValueSync().GetSession(); - session.ExecuteSchemeQuery(R"___( - DROP TABLE `/local/t`; - )___").ExtractValueSync(); - driver.Stop(true); - } - - Y_UNIT_TEST(MultiThreadSync) { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - NYdb::NTable::TTableClient client(driver); - const int nThreads = 10; - const int nRequests = 1000; - auto job = [client]() mutable { - for (int i = 0; i < nRequests; i++) { - auto sessionResponse = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_EQUAL(sessionResponse.GetStatus(), EStatus::SUCCESS); - } - }; - IThreadFactory* pool = SystemThreadFactory(); - - TVector> threads; - threads.resize(nThreads); - for (int i = 0; i < nThreads; i++) { - threads[i] = pool->Run(job); - } - for (int i = 0; i < nThreads; i++) { - threads[i]->Join(); - } - UNIT_ASSERT_EQUAL(client.GetActiveSessionCount(), 0); - driver.Stop(true); - } - - void EnsureCanExecQuery(NYdb::NTable::TSession session) { - auto execStatus = session.ExecuteDataQuery("SELECT 42;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()).ExtractValueSync().GetStatus(); - UNIT_ASSERT_EQUAL(execStatus, EStatus::SUCCESS); - } - - void EnsureCanExecQuery(NYdb::NQuery::TSession session) { - auto execStatus = session.ExecuteQuery("SELECT 42;", - NYdb::NQuery::TTxControl::BeginTx().CommitTx()).ExtractValueSync().GetStatus(); - UNIT_ASSERT_EQUAL(execStatus, EStatus::SUCCESS); - } - - template - void DoMultiThreadSessionPoolLimitSync() { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - const int maxActiveSessions = 45; - TClient client(driver, - typename TClient::TSettings() - .SessionPoolSettings( - typename TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); - - constexpr int nThreads = 100; - NYdb::EStatus statuses[nThreads]; - TVector> sessions; - sessions.resize(nThreads); - TAtomic t = 0; - auto job = [client, &t, &statuses, &sessions]() mutable { - auto sessionResponse = client.GetSession().ExtractValueSync(); - int i = AtomicIncrement(t); - statuses[--i] = sessionResponse.GetStatus(); - if (statuses[i] == EStatus::SUCCESS) { - EnsureCanExecQuery(sessionResponse.GetSession()); - sessions[i] = sessionResponse.GetSession(); - } - }; - IThreadFactory* pool = SystemThreadFactory(); - - TVector> threads; - threads.resize(nThreads); - for (int i = 0; i < nThreads; i++) { - threads[i] = pool->Run(job); - } - for (int i = 0; i < nThreads; i++) { - threads[i]->Join(); - } - - sessions.clear(); - - int successCount = 0; - int exhaustedCount = 0; - for (int i = 0; i < nThreads; i++) { - switch (statuses[i]) { - case EStatus::SUCCESS: - successCount++; - break; - case EStatus::CLIENT_RESOURCE_EXHAUSTED: - exhaustedCount++; - break; - default: - UNIT_ASSERT(false); - } - } - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(successCount, maxActiveSessions); - UNIT_ASSERT_VALUES_EQUAL(exhaustedCount, nThreads - maxActiveSessions); - driver.Stop(true); - } - - Y_UNIT_TEST(MultiThreadSessionPoolLimitSyncTableClient) { - DoMultiThreadSessionPoolLimitSync(); - } - - Y_UNIT_TEST(MultiThreadSessionPoolLimitSyncQueryClient) { - DoMultiThreadSessionPoolLimitSync(); - } - - NYdb::NTable::TAsyncDataQueryResult ExecQueryAsync(NYdb::NTable::TSession session, const std::string q) { - return session.ExecuteDataQuery(q, - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); - } - - NYdb::NQuery::TAsyncExecuteQueryResult ExecQueryAsync(NYdb::NQuery::TSession session, const std::string q) { - return session.ExecuteQuery(q, - NYdb::NQuery::TTxControl::BeginTx().CommitTx()); - } - - template - void DoMultiThreadMultipleRequestsOnSharedSessions() { - std::string location = std::getenv("YDB_ENDPOINT"); - - auto driver = NYdb::TDriver( - TDriverConfig() - .SetEndpoint(location)); - - const int maxActiveSessions = 10; - typename T::TClient client(driver, - typename T::TClient::TSettings() - .SessionPoolSettings( - typename T::TClient::TSettings::TSessionPoolSettings().MaxActiveSessions(maxActiveSessions))); - - constexpr int nThreads = 20; - constexpr int nRequests = 50; - std::array, nThreads> results; - std::atomic t = 0; - std::atomic validSessions = 0; - auto job = [client, &t, &results, &validSessions]() mutable { - auto sessionResponse = client.GetSession().ExtractValueSync(); - - int i = ++t; - TVector& r = results[--i]; - - if (sessionResponse.GetStatus() != EStatus::SUCCESS) { - return; - } - validSessions.fetch_add(1); - - for (int i = 0; i < nRequests; i++) { - r.push_back(ExecQueryAsync(sessionResponse.GetSession(), "SELECT 42;")); - } - }; - IThreadFactory* pool = SystemThreadFactory(); - - TVector> threads; - threads.resize(nThreads); - for (int i = 0; i < nThreads; i++) { - threads[i] = pool->Run(job); - } - for (int i = 0; i < nThreads; i++) { - threads[i]->Join(); - } - - for (auto& r : results) { - NThreading::WaitExceptionOrAll(r).Wait(); - } - for (auto& r : results) { - if (!r.empty()) { - for (auto& asyncStatus : r) { - auto res = asyncStatus.GetValue(); - if (!res.IsSuccess()) { - UNIT_ASSERT_VALUES_EQUAL(res.GetStatus(), EStatus::SESSION_BUSY); - } - } - } - } - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), maxActiveSessions); - auto curExpectedActive = maxActiveSessions; - auto empty = 0; - for (auto& r : results) { - if (!r.empty()) { - r.clear(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), --curExpectedActive); - } else { - empty++; - } - } - UNIT_ASSERT_VALUES_EQUAL(empty, nThreads - maxActiveSessions); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - driver.Stop(true); - } - - Y_UNIT_TEST(MultiThreadMultipleRequestsOnSharedSessionsTableClient) { - struct TTypeHelper { - using TClient = NYdb::NTable::TTableClient; - using TResult = NYdb::NTable::TAsyncDataQueryResult; - }; - DoMultiThreadMultipleRequestsOnSharedSessions(); - } - - // Enable after interactive tx support - //Y_UNIT_TEST(MultiThreadMultipleRequestsOnSharedSessionsQueryClient) { - // struct TTypeHelper { - // using TClient = NYdb::NQuery::TQueryClient; - // using TResult = NYdb::NQuery::TAsyncExecuteQueryResult; - // }; - // DoMultiThreadMultipleRequestsOnSharedSessions(); - //} - - // TODO: Enable after accepting a pull request with merging configs - /*Y_UNIT_TEST(SessionsServerLimit) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - auto sessionResult = client.CreateSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session1 = sessionResult.GetSession(); - - sessionResult = client.CreateSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session2 = sessionResult.GetSession(); - - sessionResult = client.CreateSession().ExtractValueSync(); - sessionResult.GetIssues().PrintTo(std::cerr); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::OVERLOADED); - - auto status = session1.Close().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(status.IsTransportError(), false); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(status.GetStatus(), EStatus::SUCCESS); - - auto result = session2.ExecuteDataQuery(R"___( - SELECT 1; - )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); - - sessionResult = client.CreateSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - - sessionResult = client.CreateSession().ExtractValueSync(); - sessionResult.GetIssues().PrintTo(std::cerr); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::OVERLOADED); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - } - - Y_UNIT_TEST(SessionsServerLimitWithSessionPool) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - auto sessionResult1 = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::SUCCESS); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - auto session1 = sessionResult1.GetSession(); - - auto sessionResult2 = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult2.GetStatus(), EStatus::SUCCESS); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - auto session2 = sessionResult2.GetSession(); - - { - auto sessionResult3 = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult3.GetStatus(), EStatus::OVERLOADED); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 3); - } - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - - auto status = session1.Close().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(status.IsTransportError(), false); - UNIT_ASSERT_VALUES_EQUAL(status.GetStatus(), EStatus::SUCCESS); - - // Close doesnt free session from user perspective, - // the value of ActiveSessionsCounter will be same after Close() call. - // Probably we want to chenge this contract - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - - auto result = session2.ExecuteDataQuery(R"___( - SELECT 1; - )___", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); - - sessionResult1 = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::SUCCESS); - UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetSession().GetId().empty(), false); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 3); - - auto sessionResult3 = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(sessionResult3.GetStatus(), EStatus::OVERLOADED); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 4); - - auto tmp = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 5); - sessionResult1 = tmp; // here we reset previous created session object, - // so perform close rpc call implicitly and delete it - UNIT_ASSERT_VALUES_EQUAL(sessionResult1.GetStatus(), EStatus::OVERLOADED); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 4); - }*/ - - Y_UNIT_TEST(CloseSessionAfterDriverDtorWithoutSessionPool) { - TVector sessionIds; - int iterations = 50; - - while (iterations--) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - auto sessionResult = client.CreateSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session1 = sessionResult.GetSession(); - sessionIds.push_back(session1.GetId()); - } - - std::shared_ptr channel; - channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); - auto stub = Ydb::Table::V1::TableService::NewStub(channel); - for (const auto& sessionId : sessionIds) { - grpc::ClientContext context; - Ydb::Table::KeepAliveRequest request; - request.set_session_id(sessionId); - Ydb::Table::KeepAliveResponse response; - auto status = stub->KeepAlive(&context, request, &response); - UNIT_ASSERT(status.ok()); - auto deferred = response.operation(); - UNIT_ASSERT(deferred.ready() == true); - UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); - } - } - - Y_UNIT_TEST(CloseSessionWithSessionPoolExplicit) { - TVector sessionIds; - int iterations = 100; - - while (iterations--) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - //TODO: remove this scope after session tracker implementation - { - auto sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session1 = sessionResult.GetSession(); - sessionIds.push_back(session1.GetId()); - - sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - // Here previous created session will be returnet to session pool - session1 = sessionResult.GetSession(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - sessionIds.push_back(session1.GetId()); - } - - if (RandomNumber(10) == 5) { - client.Stop().Apply([client](NThreading::TFuture future){ - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - return future; - }).Wait(); - } else { - client.Stop().Wait(); - } - - if (iterations & 4) { - driver.Stop(true); - } - } - - std::shared_ptr channel; - channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); - auto stub = Ydb::Table::V1::TableService::NewStub(channel); - for (const auto& sessionId : sessionIds) { - grpc::ClientContext context; - Ydb::Table::KeepAliveRequest request; - request.set_session_id(sessionId); - Ydb::Table::KeepAliveResponse response; - auto status = stub->KeepAlive(&context, request, &response); - UNIT_ASSERT(status.ok()); - auto deferred = response.operation(); - UNIT_ASSERT(deferred.ready() == true); - UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); - } - } - - Y_UNIT_TEST(CloseSessionWithSessionPoolExplicitDriverStopOnly) { - TVector sessionIds; - int iterations = 100; - - while (iterations--) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - //TODO: remove this scope after session tracker implementation - { - auto sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session1 = sessionResult.GetSession(); - sessionIds.push_back(session1.GetId()); - - sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - // Here previous created session will be returnet to session pool - session1 = sessionResult.GetSession(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - sessionIds.push_back(session1.GetId()); - } - driver.Stop(true); - } - - std::shared_ptr channel; - channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); - auto stub = Ydb::Table::V1::TableService::NewStub(channel); - for (const auto& sessionId : sessionIds) { - grpc::ClientContext context; - Ydb::Table::KeepAliveRequest request; - request.set_session_id(sessionId); - Ydb::Table::KeepAliveResponse response; - auto status = stub->KeepAlive(&context, request, &response); - UNIT_ASSERT(status.ok()); - auto deferred = response.operation(); - UNIT_ASSERT(deferred.ready() == true); - UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); - } - } - - Y_UNIT_TEST(CloseSessionWithSessionPoolFromDtors) { - TVector sessionIds; - int iterations = 100; - - while (iterations--) { - NYdb::TDriver driver(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); - NYdb::NTable::TTableClient client(driver); - //TODO: remove this scope after session tracker implementation - { - auto sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - auto session1 = sessionResult.GetSession(); - sessionIds.push_back(session1.GetId()); - - sessionResult = client.GetSession().ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 2); - UNIT_ASSERT_VALUES_EQUAL(sessionResult.GetStatus(), EStatus::SUCCESS); - // Here previous created session will be returnet to session pool - session1 = sessionResult.GetSession(); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - sessionIds.push_back(session1.GetId()); - } - } - - std::shared_ptr channel; - channel = grpc::CreateChannel(std::getenv("YDB_ENDPOINT"), grpc::InsecureChannelCredentials()); - auto stub = Ydb::Table::V1::TableService::NewStub(channel); - for (const auto& sessionId : sessionIds) { - grpc::ClientContext context; - Ydb::Table::KeepAliveRequest request; - request.set_session_id(sessionId); - Ydb::Table::KeepAliveResponse response; - auto status = stub->KeepAlive(&context, request, &response); - UNIT_ASSERT(status.ok()); - auto deferred = response.operation(); - UNIT_ASSERT(deferred.ready() == true); - UNIT_ASSERT(deferred.status() == Ydb::StatusIds::BAD_SESSION); - } - } -} diff --git a/tests/integration/sessions_pool/CMakeLists.txt b/tests/integration/sessions_pool/CMakeLists.txt new file mode 100644 index 0000000000..6e7a6a70ab --- /dev/null +++ b/tests/integration/sessions_pool/CMakeLists.txt @@ -0,0 +1,10 @@ +add_ydb_test(NAME sessions_pool_it GTEST + SOURCES + main.cpp + LINK_LIBRARIES + yutil + YDB-CPP-SDK::Table + api-grpc + LABELS + integration +) diff --git a/tests/integration/sessions_pool/main.cpp b/tests/integration/sessions_pool/main.cpp new file mode 100644 index 0000000000..0779530519 --- /dev/null +++ b/tests/integration/sessions_pool/main.cpp @@ -0,0 +1,369 @@ +#include + +#include + +#include + +#include +#include + +using namespace NYdb; +using namespace NYdb::NTable; + +class YdbSdkSessionsPool : public ::testing::TestWithParam { +protected: + void SetUp() override { + ui32 maxActiveSessions = GetParam(); + Driver = std::make_unique(TDriverConfig().SetEndpoint(std::getenv("YDB_ENDPOINT"))); + + auto clientSettings = TClientSettings().SessionPoolSettings( + TSessionPoolSettings() + .MaxActiveSessions(maxActiveSessions) + .KeepAliveIdleThreshold(TDuration::MilliSeconds(10)) + .CloseIdleThreshold(TDuration::MilliSeconds(10))); + Client = std::make_unique(*Driver, clientSettings); + } + + void TearDown() override { + Driver->Stop(true); + } + +protected: + std::unique_ptr Driver; + std::unique_ptr Client; +}; + +class YdbSdkSessionsPool1Session : public YdbSdkSessionsPool {}; + +enum class EAction: ui8 { + CreateFuture, + ExtractValue, + Return +}; +using TPlan = std::vector>; + + +void CheckPlan(TPlan plan) { + std::unordered_map sessions; + for (const auto& [action, sessionId]: plan) { + if (action == EAction::CreateFuture) { + ASSERT_FALSE(sessions.contains(sessionId)); + } else { + ASSERT_TRUE(sessions.contains(sessionId)); + switch (sessions.at(sessionId)) { + case EAction::CreateFuture: { + ASSERT_EQ(action, EAction::ExtractValue); + break; + } + case EAction::ExtractValue: { + ASSERT_EQ(action, EAction::Return); + break; + } + default: { + ASSERT_TRUE(false); + } + } + } + sessions[sessionId] = action; + } +} + +void RunPlan(const TPlan& plan, NYdb::NTable::TTableClient& client) { + std::unordered_map> sessionFutures; + std::unordered_map sessions; + + ui32 requestedSessions = 0; + + for (const auto& [action, sessionId]: plan) { + switch (action) { + case EAction::CreateFuture: { + sessionFutures.emplace(sessionId, client.GetSession()); + ++requestedSessions; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (requestedSessions > client.GetActiveSessionsLimit()) { + ASSERT_EQ(client.GetActiveSessionCount(), client.GetActiveSessionsLimit()); + } + ASSERT_FALSE(sessionFutures.at(sessionId).HasValue()); + break; + } + case EAction::ExtractValue: { + auto it = sessionFutures.find(sessionId); + auto session = it->second.ExtractValueSync(); + sessionFutures.erase(it); + sessions.emplace(sessionId, std::move(session)); + break; + } + case EAction::Return: { + sessions.erase(sessionId); + --requestedSessions; + break; + } + } + ASSERT_LE(client.GetActiveSessionCount(), client.GetActiveSessionsLimit()); + ASSERT_GE(client.GetActiveSessionCount(), static_cast(sessions.size())); + ASSERT_LE(client.GetActiveSessionCount(), static_cast(sessions.size() + sessionFutures.size())); + } +} + +int GetRand(std::mt19937& rng, int min, int max) { + std::uniform_int_distribution dist(min, max); + return dist(rng); +} + + +TPlan GenerateRandomPlan(ui32 numSessions) { + TPlan plan; + std::random_device dev; + std::mt19937 rng(dev()); + + for (ui32 i = 0; i < numSessions; ++i) { + std::uniform_int_distribution dist(0, plan.size()); + ui32 prevPos = 0; + for (EAction action: {EAction::CreateFuture, EAction::ExtractValue, EAction::Return}) { + int pos = GetRand(rng, prevPos, plan.size()); + plan.emplace(plan.begin() + pos, std::make_pair(action, i)); + prevPos = pos + 1; + } + } + return plan; +} + + +TEST_P(YdbSdkSessionsPool1Session, GetSession) { + ASSERT_EQ(Client->GetActiveSessionsLimit(), 1); + ASSERT_EQ(Client->GetActiveSessionCount(), 0); + ASSERT_EQ(Client->GetCurrentPoolSize(), 0); + + { + //TCreateSessionResult + auto session = Client->GetSession().ExtractValueSync(); + + ASSERT_EQ(session.GetStatus(), EStatus::SUCCESS); + ASSERT_EQ(Client->GetActiveSessionCount(), 1); + ASSERT_EQ(Client->GetCurrentPoolSize(), 0); + } + + ASSERT_EQ(Client->GetActiveSessionCount(), 0); + ASSERT_EQ(Client->GetCurrentPoolSize(), 1); +} + +void TestWaitQueue(NYdb::NTable::TTableClient& client, ui32 activeSessionsLimit) { + std::vector> sessionFutures; + std::vector sessions; + + // exhaust the pool + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessions.emplace_back(client.GetSession().ExtractValueSync()); + } + ASSERT_EQ(client.GetActiveSessionCount(), activeSessionsLimit); + + // next should be in the wait queue + for (ui32 i = 0; i < activeSessionsLimit * 10; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + ASSERT_EQ(client.GetActiveSessionCount(), activeSessionsLimit); + + // next should be a fake session + { + auto brokenSession = client.GetSession().ExtractValueSync(); + ASSERT_FALSE(brokenSession.IsSuccess()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + for (auto& sessionFuture: sessionFutures) { + ASSERT_FALSE(sessionFuture.HasValue()); + } + + for (auto& sessionFuture: sessionFutures) { + sessions.erase(sessions.begin()); + sessions.emplace_back(sessionFuture.ExtractValueSync()); + } + ASSERT_EQ(client.GetActiveSessionCount(), activeSessionsLimit); +} + +TEST_P(YdbSdkSessionsPool, WaitQueue) { + TestWaitQueue(*Client, GetParam()); +} + +TEST_P(YdbSdkSessionsPool1Session, RunSmallPlan) { + TPlan plan{ + {EAction::CreateFuture, 1}, + {EAction::ExtractValue, 1}, + {EAction::CreateFuture, 2}, + {EAction::Return, 1}, + {EAction::ExtractValue, 2}, + {EAction::Return, 2} + }; + CheckPlan(plan); + RunPlan(plan, *Client); + + ASSERT_EQ(Client->GetActiveSessionCount(), 0); + ASSERT_EQ(Client->GetCurrentPoolSize(), 1); +} + +TEST_P(YdbSdkSessionsPool1Session, CustomPlan) { + TPlan plan{ + {EAction::CreateFuture, 1} + }; + CheckPlan(plan); + RunPlan(plan, *Client); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + ASSERT_EQ(Client->GetActiveSessionCount(), 0); +} + +ui32 RunStressTestSync(ui32 n, ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { + std::vector> sessionFutures; + std::vector sessions; + std::mt19937 rng(0); + ui32 successCount = 0; + + for (ui32 i = 0; i < activeSessionsLimit * 12; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + + for (ui32 i = 0; i < n; ++i) { + switch (static_cast(GetRand(rng, 0, 2))) { + case EAction::CreateFuture: { + sessionFutures.emplace_back(client.GetSession()); + break; + } + case EAction::ExtractValue: { + if (sessionFutures.empty()) { + break; + } + auto ind = GetRand(rng, 0, sessionFutures.size() - 1); + auto sessionFuture = sessionFutures[ind]; + if (sessionFuture.HasValue()) { + auto session = sessionFuture.ExtractValueSync(); + if (session.IsSuccess()) { + ++successCount; + } + sessions.emplace_back(std::move(session)); + sessionFutures.erase(sessionFutures.begin() + ind); + break; + } + break; + } + case EAction::Return: { + if (sessions.empty()) { + break; + } + auto ind = GetRand(rng, 0, sessions.size() - 1); + sessions.erase(sessions.begin() + ind); + break; + } + } + } + return successCount; +} + +TEST_P(YdbSdkSessionsPool, StressTestSync) { + ui32 activeSessionsLimit = GetParam(); + + RunStressTestSync(1000, activeSessionsLimit, *Client); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + ASSERT_EQ(Client->GetActiveSessionCount(), 0); + ASSERT_EQ(Client->GetCurrentPoolSize(), activeSessionsLimit); +} + +ui32 RunStressTestAsync(ui32 n, ui32 nThreads, NYdb::NTable::TTableClient& client) { + std::atomic successCount(0); + std::atomic jobIndex(0); + + auto job = [&client, &successCount, &jobIndex, n]() mutable { + std::mt19937 rng(++jobIndex); + for (ui32 i = 0; i < n; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + auto sessionFuture = client.GetSession(); + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + auto session = sessionFuture.ExtractValueSync(); + std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); + successCount += session.IsSuccess(); + } + }; + + std::vector threads; + threads.resize(nThreads); + for (ui32 i = 0; i < nThreads; i++) { + threads[i] = std::thread(job); + } + for (ui32 i = 0; i < nThreads; i++) { + threads[i].join(); + } + + return successCount; +} + +TEST_P(YdbSdkSessionsPool, StressTestAsync) { + ui32 activeSessionsLimit = GetParam(); + ui32 iterations = (activeSessionsLimit == 1) ? 100 : 1000; + + RunStressTestAsync(iterations, 10, *Client); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + ASSERT_EQ(Client->GetActiveSessionCount(), 0); + ASSERT_EQ(Client->GetCurrentPoolSize(), activeSessionsLimit); +} + +void TestPeriodicTask(ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { + std::vector> sessionFutures; + std::vector sessions; + + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessions.emplace_back(client.GetSession().ExtractValueSync()); + ASSERT_TRUE(sessions.back().IsSuccess()); + } + + for (ui32 i = 0; i < activeSessionsLimit; ++i) { + sessionFutures.emplace_back(client.GetSession()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + for (auto& sessionFuture : sessionFutures) { + ASSERT_FALSE(sessionFuture.HasValue()); + } + + // Wait for wait session timeout + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + for (auto& sessionFuture : sessionFutures) { + ASSERT_TRUE(sessionFuture.HasValue()); + ASSERT_FALSE(sessionFuture.ExtractValueSync().IsSuccess()); + } + + ASSERT_EQ(client.GetActiveSessionCount(), activeSessionsLimit); + + sessionFutures.clear(); + sessions.clear(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + ASSERT_EQ(client.GetActiveSessionCount(), 0); + ASSERT_EQ(client.GetCurrentPoolSize(), activeSessionsLimit); +} + +TEST_P(YdbSdkSessionsPool, PeriodicTask) { + TestPeriodicTask(GetParam(), *Client); +} + +TEST_P(YdbSdkSessionsPool1Session, FailTest) { + // This test reproduces bug from KIKIMR-18063 + auto sessionFromPool = Client->GetSession().ExtractValueSync(); + auto futureInWaitPool = Client->GetSession(); + + { + auto standaloneSessionThatWillBeBroken = Client->CreateSession().ExtractValueSync(); + auto res = standaloneSessionThatWillBeBroken.GetSession().ExecuteDataQuery("SELECT COUNT(*) FROM `Root/Test`;", + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), + NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); + } +} + +INSTANTIATE_TEST_SUITE_P(, YdbSdkSessionsPool, ::testing::Values(1, 10)); + +INSTANTIATE_TEST_SUITE_P(, YdbSdkSessionsPool1Session, ::testing::Values(1)); diff --git a/tests/integration/sessions_pool_it/CMakeLists.txt b/tests/integration/sessions_pool_it/CMakeLists.txt deleted file mode 100644 index d302229f4b..0000000000 --- a/tests/integration/sessions_pool_it/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -add_ydb_test(NAME sessions_pool_it - SPLIT_FACTOR 12 - SOURCES - sessions_pool_it.cpp - LINK_LIBRARIES - yutil - cpp-testing-unittest_main - YDB-CPP-SDK::Table - api-grpc - LABELS - integration -) \ No newline at end of file diff --git a/tests/integration/sessions_pool_it/sessions_pool_it.cpp b/tests/integration/sessions_pool_it/sessions_pool_it.cpp deleted file mode 100644 index fadf2aa606..0000000000 --- a/tests/integration/sessions_pool_it/sessions_pool_it.cpp +++ /dev/null @@ -1,451 +0,0 @@ -#include -#include - -#include - -#include - -#include -#include - -using namespace NYdb; -using namespace NYdb::NTable; - -class TDefaultTestSetup { -public: - TDefaultTestSetup(ui32 maxActiveSessions) - : Driver_(NYdb::TDriver( - TDriverConfig().SetEndpoint( - std::getenv("YDB_ENDPOINT") - ) - )) - , Client_( - Driver_, - TClientSettings().SessionPoolSettings( - TSessionPoolSettings() - .MaxActiveSessions(maxActiveSessions) - .KeepAliveIdleThreshold(TDuration::MilliSeconds(10)) - .CloseIdleThreshold(TDuration::MilliSeconds(10)) - ) - ) - { - } - - ~TDefaultTestSetup() { - Driver_.Stop(true); - } - - NYdb::NTable::TTableClient& GetClient() { - return Client_; - } - -private: - NYdb::TDriver Driver_; - NYdb::NTable::TTableClient Client_; -}; - - -enum class EAction: ui8 { - CreateFuture, - ExtractValue, - Return -}; -using TPlan = std::vector>; - - -void CheckPlan(TPlan plan) { - THashMap sessions; - for (const auto& [action, sessionId]: plan) { - if (action == EAction::CreateFuture) { - UNIT_ASSERT(!sessions.contains(sessionId)); - } else { - UNIT_ASSERT(sessions.contains(sessionId)); - switch (sessions.at(sessionId)) { - case EAction::CreateFuture: { - UNIT_ASSERT(action == EAction::ExtractValue); - break; - } - case EAction::ExtractValue: { - UNIT_ASSERT(action == EAction::Return); - break; - } - default: { - UNIT_ASSERT(false); - } - } - } - sessions[sessionId] = action; - } -} - -void RunPlan(const TPlan& plan, NYdb::NTable::TTableClient& client) { - THashMap> sessionFutures; - THashMap sessions; - - ui32 requestedSessions = 0; - - for (const auto& [action, sessionId]: plan) { - switch (action) { - case EAction::CreateFuture: { - sessionFutures.emplace(sessionId, client.GetSession()); - ++requestedSessions; - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - if (requestedSessions > client.GetActiveSessionsLimit()) { - UNIT_ASSERT(client.GetActiveSessionCount() == client.GetActiveSessionsLimit()); - } - UNIT_ASSERT(!sessionFutures.at(sessionId).HasValue()); - break; - } - case EAction::ExtractValue: { - auto it = sessionFutures.find(sessionId); - auto session = it->second.ExtractValueSync(); - sessionFutures.erase(it); - sessions.emplace(sessionId, std::move(session)); - break; - } - case EAction::Return: { - sessions.erase(sessionId); - --requestedSessions; - break; - } - } - UNIT_ASSERT(client.GetActiveSessionCount() <= client.GetActiveSessionsLimit()); - UNIT_ASSERT(client.GetActiveSessionCount() >= static_cast(sessions.size())); - UNIT_ASSERT(client.GetActiveSessionCount() <= static_cast(sessions.size() + sessionFutures.size())); - } -} - -int GetRand(std::mt19937& rng, int min, int max) { - std::uniform_int_distribution dist(min, max); - return dist(rng); -} - - -TPlan GenerateRandomPlan(ui32 numSessions) { - TPlan plan; - std::random_device dev; - std::mt19937 rng(dev()); - - for (ui32 i = 0; i < numSessions; ++i) { - std::uniform_int_distribution dist(0, plan.size()); - ui32 prevPos = 0; - for (EAction action: {EAction::CreateFuture, EAction::ExtractValue, EAction::Return}) { - int pos = GetRand(rng, prevPos, plan.size()); - plan.emplace(plan.begin() + pos, std::make_pair(action, i)); - prevPos = pos + 1; - } - } - return plan; -} - - -Y_UNIT_TEST_SUITE(YdbSdkSessionsPool) { - Y_UNIT_TEST(Get1Session) { - TDefaultTestSetup setup(1); - auto& client = setup.GetClient(); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionsLimit(), 1); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 0); - - { - //TCreateSessionResult - auto session = client.GetSession().ExtractValueSync(); - - UNIT_ASSERT_VALUES_EQUAL(session.GetStatus(), EStatus::SUCCESS); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 1); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 0); - } - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 1); - } - - void TestWaitQueue(NYdb::NTable::TTableClient& client, ui32 activeSessionsLimit) { - TVector> sessionFutures; - TVector sessions; - - // exhaust the pool - for (ui32 i = 0; i < activeSessionsLimit; ++i) { - sessions.emplace_back(client.GetSession().ExtractValueSync()); - } - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); - - // next should be in the wait queue - for (ui32 i = 0; i < activeSessionsLimit * 10; ++i) { - sessionFutures.emplace_back(client.GetSession()); - } - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); - - // next should be a fake session - { - auto brokenSession = client.GetSession().ExtractValueSync(); - UNIT_ASSERT(!brokenSession.IsSuccess()); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - for (auto& sessionFuture: sessionFutures) { - UNIT_ASSERT(!sessionFuture.HasValue()); - } - - for (auto& sessionFuture: sessionFutures) { - sessions.erase(sessions.begin()); - sessions.emplace_back(sessionFuture.ExtractValueSync()); - } - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); - } - - Y_UNIT_TEST(WaitQueue1) { - ui32 activeSessionsLimit = 1; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - TestWaitQueue(client, activeSessionsLimit); - } - - Y_UNIT_TEST(WaitQueue10) { - ui32 activeSessionsLimit = 10; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - TestWaitQueue(client, activeSessionsLimit); - } - - Y_UNIT_TEST(RunSmallPlan) { - TDefaultTestSetup setup(1); - auto& client = setup.GetClient(); - - TPlan plan{ - {EAction::CreateFuture, 1}, - {EAction::ExtractValue, 1}, - {EAction::CreateFuture, 2}, - {EAction::Return, 1}, - {EAction::ExtractValue, 2}, - {EAction::Return, 2} - }; - CheckPlan(plan); - RunPlan(plan, client); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), 1); - } - - Y_UNIT_TEST(CustomPlan) { - TDefaultTestSetup setup(1); - auto& client = setup.GetClient(); - - TPlan plan{ - {EAction::CreateFuture, 1} - }; - CheckPlan(plan); - RunPlan(plan, client); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - } - - ui32 RunStressTestSync(ui32 n, ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { - TVector> sessionFutures; - TVector sessions; - std::mt19937 rng(0); - ui32 successCount = 0; - - for (ui32 i = 0; i < activeSessionsLimit * 12; ++i) { - sessionFutures.emplace_back(client.GetSession()); - } - - for (ui32 i = 0; i < n; ++i) { - switch (static_cast(GetRand(rng, 0, 2))) { - case EAction::CreateFuture: { - sessionFutures.emplace_back(client.GetSession()); - break; - } - case EAction::ExtractValue: { - if (sessionFutures.empty()) { - break; - } - auto ind = GetRand(rng, 0, sessionFutures.size() - 1); - auto sessionFuture = sessionFutures[ind]; - if (sessionFuture.HasValue()) { - auto session = sessionFuture.ExtractValueSync(); - if (session.IsSuccess()) { - ++successCount; - } - sessions.emplace_back(std::move(session)); - sessionFutures.erase(sessionFutures.begin() + ind); - break; - } - break; - } - case EAction::Return: { - if (sessions.empty()) { - break; - } - auto ind = GetRand(rng, 0, sessions.size() - 1); - sessions.erase(sessions.begin() + ind); - break; - } - } - } - return successCount; - } - - Y_UNIT_TEST(StressTestSync1) { - ui32 activeSessionsLimit = 1; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - RunStressTestSync(1000, activeSessionsLimit, client); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); - } - - Y_UNIT_TEST(StressTestSync10) { - ui32 activeSessionsLimit = 10; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - RunStressTestSync(1000, activeSessionsLimit, client); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); - } - - ui32 RunStressTestAsync(ui32 n, ui32 nThreads, NYdb::NTable::TTableClient& client) { - std::atomic successCount(0); - std::atomic jobIndex(0); - - auto job = [&client, &successCount, &jobIndex, n]() mutable { - std::mt19937 rng(++jobIndex); - for (ui32 i = 0; i < n; ++i) { - std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); - auto sessionFuture = client.GetSession(); - std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); - auto session = sessionFuture.ExtractValueSync(); - std::this_thread::sleep_for(std::chrono::milliseconds(GetRand(rng, 1, 10))); - successCount += session.IsSuccess(); - } - }; - - IThreadFactory* pool = SystemThreadFactory(); - TVector> threads; - threads.resize(nThreads); - for (ui32 i = 0; i < nThreads; i++) { - threads[i] = pool->Run(job); - } - for (ui32 i = 0; i < nThreads; i++) { - threads[i]->Join(); - } - - return successCount; - } - - Y_UNIT_TEST(StressTestAsync1) { - ui32 activeSessionsLimit = 1; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - RunStressTestAsync(100, 10, client); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); - } - - Y_UNIT_TEST(StressTestAsync10) { - ui32 activeSessionsLimit = 10; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - RunStressTestAsync(1000, 10, client); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); - } - - void TestPeriodicTask(ui32 activeSessionsLimit, NYdb::NTable::TTableClient& client) { - TVector> sessionFutures; - TVector sessions; - - for (ui32 i = 0; i < activeSessionsLimit; ++i) { - sessions.emplace_back(client.GetSession().ExtractValueSync()); - UNIT_ASSERT_VALUES_EQUAL(sessions.back().IsSuccess(), true); - } - - for (ui32 i = 0; i < activeSessionsLimit; ++i) { - sessionFutures.emplace_back(client.GetSession()); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - for (auto& sessionFuture : sessionFutures) { - UNIT_ASSERT(!sessionFuture.HasValue()); - } - - // Wait for wait session timeout - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - - for (auto& sessionFuture : sessionFutures) { - UNIT_ASSERT(sessionFuture.HasValue()); - UNIT_ASSERT(!sessionFuture.ExtractValueSync().IsSuccess()); - } - - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), activeSessionsLimit); - - sessionFutures.clear(); - sessions.clear(); - - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); - UNIT_ASSERT_VALUES_EQUAL(client.GetActiveSessionCount(), 0); - UNIT_ASSERT_VALUES_EQUAL(client.GetCurrentPoolSize(), activeSessionsLimit); - } - - Y_UNIT_TEST(PeriodicTask1) { - ui32 activeSessionsLimit = 1; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - TestPeriodicTask(activeSessionsLimit, client); - } - - Y_UNIT_TEST(PeriodicTask10) { - ui32 activeSessionsLimit = 10; - - TDefaultTestSetup setup(activeSessionsLimit); - auto& client = setup.GetClient(); - - TestPeriodicTask(activeSessionsLimit, client); - } - - Y_UNIT_TEST(FailTest) { - // This test reproduces bug from KIKIMR-18063 - TDefaultTestSetup setup(1); - auto& client = setup.GetClient(); - - auto sessionFromPool = client.GetSession().ExtractValueSync(); - auto futureInWaitPool = client.GetSession(); - - { - auto standaloneSessionThatWillBeBroken = client.CreateSession().ExtractValueSync(); - auto res = standaloneSessionThatWillBeBroken.GetSession().ExecuteDataQuery("SELECT COUNT(*) FROM `Root/Test`;", - TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx(), - NYdb::NTable::TExecDataQuerySettings().ClientTimeout(TDuration::MicroSeconds(10))).GetValueSync(); - } - } -} From f68505851eab63e3986d1930c1d3d80eda2544ec Mon Sep 17 00:00:00 2001 From: Bulat Gayazov Date: Mon, 21 Apr 2025 21:09:23 +0000 Subject: [PATCH 9/9] fix --- tests/unit/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index d06ff68dbb..1f0962419e 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,2 +1,2 @@ add_subdirectory(client) -add_subdirectory(library) \ No newline at end of file +add_subdirectory(library)