diff --git a/.clang-tidy b/.clang-tidy index af9b18e704e61c996f61e622faeaf4a8d6ed44e0..8d0b6833f07ac0cea4f92f77b4c5106f3e68b21a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: '-*,clang-diagnostic-*,clang-analyzer-*,-*,modernize*,performance*,readability*,bugprone*,clang-analyzer*,cppcoreguidelines*,misc*,-readability-braces-around-statements,-cppcoreguidelines-pro-bounds-array-to-pointer-decay' +Checks: '-*,clang-diagnostic-*,clang-analyzer-*,-*,modernize*,performance*,readability*,bugprone*,clang-analyzer*,cppcoreguidelines*,misc*,-readability-braces-around-statements,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-modernize-use-trailing-return-type' WarningsAsErrors: '' HeaderFilterRegex: '(src|test)/.*' AnalyzeTemporaryDtors: false diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 14e78c0fa104c7610389436bdcf08bbc0e1c5dc8..61faa9cce135e69fa8cb929a63c03f13b3c75025 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -3,29 +3,60 @@ project(benchmark-tests) set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON) -set(BENCHMARK_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/QueryBuilderTests.cpp) +add_executable(query-builder-tests ${CMAKE_CURRENT_SOURCE_DIR}/QueryBuilderTests.cpp) +target_compile_options(query-builder-tests PRIVATE -Wall -Wextra -g) + +target_link_libraries(query-builder-tests + PRIVATE + libhdbpp_timescale_static_library + TangoInterfaceLibrary + benchmark + benchmark_main + gtest + test-utils) + +target_include_directories(query-builder-tests + PRIVATE ${CMAKE_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}) -add_executable(benchmark-tests ${BENCHMARK_SOURCES}) -target_compile_options(benchmark-tests PRIVATE -Wall -Wextra -g) +target_compile_definitions(query-builder-tests + PRIVATE -DDEBUG_ENABLED) -target_link_libraries(benchmark-tests - PRIVATE libhdbpp_headers libhdbpp_timescale_shared_library TangoInterfaceLibrary benchmark gtest) +set_target_properties(query-builder-tests + PROPERTIES + LINK_FLAGS "-Wl,--no-undefined" + CXX_STANDARD 14) -target_include_directories(benchmark-tests +if(DO_CLANG_TIDY) + set_target_properties(query-builder-tests + PROPERTIES + CXX_CLANG_TIDY ${DO_CLANG_TIDY}) +endif(DO_CLANG_TIDY) + +add_executable(db-insert-tests ${CMAKE_CURRENT_SOURCE_DIR}/DbInsertionTests.cpp) +target_compile_options(db-insert-tests PRIVATE -Wall -Wextra -g) + +target_link_libraries(db-insert-tests + PRIVATE + libhdbpp_timescale_static_library + TangoInterfaceLibrary + benchmark + benchmark_main + gtest + test-utils) + +target_include_directories(db-insert-tests PRIVATE ${CMAKE_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}) -target_compile_definitions(benchmark-tests +target_compile_definitions(db-insert-tests PRIVATE -DDEBUG_ENABLED) -set_target_properties(benchmark-tests +set_target_properties(db-insert-tests PROPERTIES LINK_FLAGS "-Wl,--no-undefined" CXX_STANDARD 14) if(DO_CLANG_TIDY) - set_target_properties(unit-tests + set_target_properties(db-insert-tests PROPERTIES CXX_CLANG_TIDY ${DO_CLANG_TIDY}) endif(DO_CLANG_TIDY) - diff --git a/benchmark/DbInsertionTests.cpp b/benchmark/DbInsertionTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb64d453db377943d6d6328c825cb9a7b4ffdf95 --- /dev/null +++ b/benchmark/DbInsertionTests.cpp @@ -0,0 +1,566 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#include "DbConnection.hpp" +#include "HdbppDefines.hpp" +#include "QueryBuilder.hpp" +#include "TestHelpers.hpp" + +#include <benchmark/benchmark.h> +#include <memory> + +//============================================================================= +//============================================================================= +void clearTables() +{ + auto conn = make_unique<pqxx::connection>(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + + auto traits_array = hdbpp_test::utils::getTraits(); + + { + auto query = string("TRUNCATE "); + + pqxx::work tx {*conn}; + + for (auto &traits : traits_array) + { + query += hdbpp_internal::pqxx_conn::QueryBuilder::tableName(traits); + query += ","; + } + + query += hdbpp_internal::pqxx_conn::schema::ErrTableName + ","; + query += hdbpp_internal::pqxx_conn::schema::ParamTableName + ","; + query += hdbpp_internal::pqxx_conn::schema::HistoryEventTableName + ","; + query += hdbpp_internal::pqxx_conn::schema::HistoryTableName + ","; + query += hdbpp_internal::pqxx_conn::schema::ConfTableName + " RESTART IDENTITY"; + + tx.exec(query); + tx.commit(); + } +} + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmAllocateData(benchmark::State &state) +{ + // TEST - Test data allocation time, so we can subtract it from the db write + // tests + hdbpp_internal::LogConfigurator::initLogging("test"); + hdbpp_internal::AttributeTraits traits {Tango::READ_WRITE, Format, Type}; + + for (auto _ : state) + { + for (int i = 0; i < 1000; i++) + { + if (Format == Tango::SPECTRUM) + { + auto value_r = hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size); + auto value_w = hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size); + } + else + { + auto value_r = hdbpp_test::data_gen::generateData<Type>(traits); + auto value_w = hdbpp_test::data_gen::generateData<Type>(traits); + } + } + } +} + +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_SHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_LONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_LONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_FLOAT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_UCHAR, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_USHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_ULONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_ULONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_STRING, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_STATE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_BOOLEAN, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_LONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_DOUBLE, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_USHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_ULONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_STRING, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmAllocateData, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmDbMultipleSQLInsert(benchmark::State &state) +{ + // TEST - Test the write speed when pushing a single event at a time + // to the database. This version inserts via strings + hdbpp_internal::LogConfigurator::initLogging("test"); + clearTables(); + + hdbpp_internal::AttributeTraits traits {Tango::READ, Format, Type}; + + hdbpp_internal::pqxx_conn::DbConnection conn(hdbpp_internal::pqxx_conn::DbConnection::DbStoreMethod::InsertString); + + conn.connect(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + + conn.storeAttribute(hdbpp_test::attr_name::TestAttrFinalName, + hdbpp_test::attr_name::TestAttrCs, + hdbpp_test::attr_name::TestAttrDomain, + hdbpp_test::attr_name::TestAttrFamily, + hdbpp_test::attr_name::TestAttrMember, + hdbpp_test::attr_name::TestAttrName, + 0, + traits); + + struct timeval tv + {}; + + for (auto _ : state) + { + for (int i = 0; i < 1000; i++) + { + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + if (Format == Tango::SPECTRUM) + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + traits); + } + else + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateData<Type>(traits)), + move(hdbpp_test::data_gen::generateData<Type>(traits)), + traits); + } + } + } + + conn.disconnect(); +} + +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_SHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_LONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_LONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_FLOAT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_UCHAR, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_USHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_ULONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_ULONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_STRING, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_STATE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_BOOLEAN, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_LONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_DOUBLE, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_USHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_ULONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_STRING, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultipleSQLInsert, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmDbMultiplePreparedStatementInsert(benchmark::State &state) +{ + // TEST - Test the write speed when pushing a single event at a time + // to the database. This version inserts via prepared statements + hdbpp_internal::LogConfigurator::initLogging("test"); + clearTables(); + + hdbpp_internal::AttributeTraits traits {Tango::READ, Format, Type}; + + hdbpp_internal::pqxx_conn::DbConnection conn( + hdbpp_internal::pqxx_conn::DbConnection::DbStoreMethod::PreparedStatement); + + conn.connect(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + + conn.storeAttribute(hdbpp_test::attr_name::TestAttrFinalName, + hdbpp_test::attr_name::TestAttrCs, + hdbpp_test::attr_name::TestAttrDomain, + hdbpp_test::attr_name::TestAttrFamily, + hdbpp_test::attr_name::TestAttrMember, + hdbpp_test::attr_name::TestAttrName, + 0, + traits); + + struct timeval tv + {}; + + for (auto _ : state) + { + for (int i = 0; i < 1000; i++) + { + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + if (Format == Tango::SPECTRUM) + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + traits); + } + else + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateData<Type>(traits)), + move(hdbpp_test::data_gen::generateData<Type>(traits)), + traits); + } + } + } + + conn.disconnect(); +} + +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_SHORT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_LONG, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_LONG64, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_FLOAT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_UCHAR, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_USHORT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_ULONG, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_ULONG64, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_STRING, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_STATE, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_BOOLEAN, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_LONG64, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_DOUBLE, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_USHORT, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_ULONG64, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_STRING, Tango::SCALAR) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbMultiplePreparedStatementInsert, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmDbBatchedInsert(benchmark::State &state) +{ + // TEST - Test the write speed when pushing a multiple events at once to + // the db + hdbpp_internal::LogConfigurator::initLogging("test"); + clearTables(); + + hdbpp_internal::AttributeTraits traits {Tango::READ, Format, Type}; + + hdbpp_internal::pqxx_conn::DbConnection conn( + hdbpp_internal::pqxx_conn::DbConnection::DbStoreMethod::PreparedStatement); + + conn.connect(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + conn.buffer(true); + + conn.storeAttribute(hdbpp_test::attr_name::TestAttrFinalName, + hdbpp_test::attr_name::TestAttrCs, + hdbpp_test::attr_name::TestAttrDomain, + hdbpp_test::attr_name::TestAttrFamily, + hdbpp_test::attr_name::TestAttrMember, + hdbpp_test::attr_name::TestAttrName, + 0, + traits); + + struct timeval tv + {}; + + for (auto _ : state) + { + for (int i = 0; i < 1000; i++) + { + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + if (Format == Tango::SPECTRUM) + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + traits); + } + else + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateData<Type>(traits)), + move(hdbpp_test::data_gen::generateData<Type>(traits)), + traits); + } + } + + conn.flush(); + } + + conn.buffer(false); + conn.disconnect(); +} + +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_SHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_LONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_LONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_FLOAT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_UCHAR, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_USHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_ULONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_ULONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_STRING, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_STATE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_BOOLEAN, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_LONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_DOUBLE, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_USHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_ULONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_STRING, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbBatchedInsert, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmDbSingleSQLInsert(benchmark::State &state) +{ + // TEST - Test the write speed when pushing a single event via sql + hdbpp_internal::LogConfigurator::initLogging("test"); + clearTables(); + + hdbpp_internal::AttributeTraits traits {Tango::READ, Format, Type}; + + hdbpp_internal::pqxx_conn::DbConnection conn(hdbpp_internal::pqxx_conn::DbConnection::DbStoreMethod::InsertString); + + conn.connect(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + + conn.storeAttribute(hdbpp_test::attr_name::TestAttrFinalName, + hdbpp_test::attr_name::TestAttrCs, + hdbpp_test::attr_name::TestAttrDomain, + hdbpp_test::attr_name::TestAttrFamily, + hdbpp_test::attr_name::TestAttrMember, + hdbpp_test::attr_name::TestAttrName, + 0, + traits); + + struct timeval tv + {}; + + for (auto _ : state) + { + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + if (Format == Tango::SPECTRUM) + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + traits); + } + else + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateData<Type>(traits)), + move(hdbpp_test::data_gen::generateData<Type>(traits)), + traits); + } + } + + conn.buffer(false); + conn.disconnect(); +} + +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_SHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_LONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_LONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_FLOAT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_UCHAR, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_USHORT, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_ULONG, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_ULONG64, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_STRING, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_STATE, Tango::SPECTRUM, 512)->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_BOOLEAN, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_LONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_DOUBLE, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_USHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_ULONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_STRING, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSingleSQLInsert, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); + +//============================================================================= +//============================================================================= +template<Tango::CmdArgType Type, Tango::AttrDataFormat Format, int Size = 0> +void bmDbSinglePreparedStatementInsert(benchmark::State &state) +{ + // TEST - Test the write speed when pushing a single event via sql + hdbpp_internal::LogConfigurator::initLogging("test"); + clearTables(); + + hdbpp_internal::AttributeTraits traits {Tango::READ, Format, Type}; + + hdbpp_internal::pqxx_conn::DbConnection conn( + hdbpp_internal::pqxx_conn::DbConnection::DbStoreMethod::PreparedStatement); + + conn.connect(hdbpp_test::psql_connection::postgres_db::HdbppConnectionString); + + conn.storeAttribute(hdbpp_test::attr_name::TestAttrFinalName, + hdbpp_test::attr_name::TestAttrCs, + hdbpp_test::attr_name::TestAttrDomain, + hdbpp_test::attr_name::TestAttrFamily, + hdbpp_test::attr_name::TestAttrMember, + hdbpp_test::attr_name::TestAttrName, + 0, + traits); + + struct timeval tv + {}; + + for (auto _ : state) + { + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + if (Format == Tango::SPECTRUM) + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + move(hdbpp_test::data_gen::generateSpectrumData<Type>(false, Size)), + traits); + } + else + { + conn.storeDataEvent(hdbpp_test::attr_name::TestAttrFinalName, + event_time, + 1, + move(hdbpp_test::data_gen::generateData<Type>(traits)), + move(hdbpp_test::data_gen::generateData<Type>(traits)), + traits); + } + } + + conn.buffer(false); + conn.disconnect(); +} + +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_BOOLEAN, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_SHORT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_LONG, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_LONG64, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_FLOAT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_DOUBLE, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_UCHAR, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_USHORT, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_ULONG, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_ULONG64, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_STRING, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_STATE, Tango::SPECTRUM, 512) + ->Unit(benchmark::kMillisecond); + +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_BOOLEAN, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_SHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_LONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_LONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_FLOAT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_DOUBLE, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_UCHAR, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_USHORT, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_ULONG, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_ULONG64, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_STRING, Tango::SCALAR)->Unit(benchmark::kMillisecond); +BENCHMARK_TEMPLATE(bmDbSinglePreparedStatementInsert, Tango::DEV_STATE, Tango::SCALAR)->Unit(benchmark::kMillisecond); \ No newline at end of file diff --git a/benchmark/QueryBuilderTests.cpp b/benchmark/QueryBuilderTests.cpp index a9e31a7c3d2771a0ed350d7fc7221b2535faa73e..0433583841f3821a3ff99d4ea5d31731cad89a71 100644 --- a/benchmark/QueryBuilderTests.cpp +++ b/benchmark/QueryBuilderTests.cpp @@ -18,6 +18,7 @@ along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ #include "QueryBuilder.hpp" + #include <benchmark/benchmark.h> //============================================================================= @@ -67,8 +68,7 @@ void bmTraitsComparator(benchmark::State &state) for (auto &write : write_types) { // add to the cache for future hits - trait_cache.emplace( - hdbpp_internal::AttributeTraits{write, format, type}, + trait_cache.emplace(hdbpp_internal::AttributeTraits {write, format, type}, to_string(write) + to_string(format) + to_string(type)); } } @@ -101,8 +101,8 @@ void bmStoreDataEventQueryNoCache(benchmark::State &state) // an empty cache (this forces the full string to be built) hdbpp_internal::LogConfigurator::initLogging("test"); - hdbpp_internal::AttributeTraits traits - {static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; + hdbpp_internal::AttributeTraits traits { + static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; for (auto _ : state) { @@ -121,8 +121,8 @@ void bmStoreDataEventQueryCache(benchmark::State &state) // map is fully populated hdbpp_internal::LogConfigurator::initLogging("test"); - hdbpp_internal::AttributeTraits traits - {static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; + hdbpp_internal::AttributeTraits traits { + static_cast<Tango::AttrWriteType>(state.range(0)), Tango::SCALAR, Tango::DEV_DOUBLE}; vector<Tango::CmdArgType> types {Tango::DEV_DOUBLE, Tango::DEV_FLOAT, @@ -155,5 +155,3 @@ void bmStoreDataEventQueryCache(benchmark::State &state) BENCHMARK_TEMPLATE(bmStoreDataEventQueryNoCache, bool)->Apply(writeTypeArgs); BENCHMARK_TEMPLATE(bmStoreDataEventQueryCache, bool)->Apply(writeTypeArgs); - -BENCHMARK_MAIN(); diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index a2463ae7b733fc2e4ca393b63e01cb38f2f158f8..73fe18b4f1d6bd590c42c2dc8ebaa881e8fa0caf 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -364,7 +364,7 @@ namespace pqxx_conn checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); - // first ensure the error message has an id inm the database, otherwise + // first ensure the error message has an id in the database, otherwise // we can not store data against it if (!_error_desc_id_cache->valueExists(error_msg)) storeErrorMsg(full_attr_name, error_msg); @@ -383,36 +383,52 @@ namespace pqxx_conn Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } - try + if (_enable_buffering) { - // create and perform a pqxx transaction - pqxx::perform([&, this]() { - pqxx::work tx {(*_conn), StoreDataEventError}; - - if (!tx.prepared(_query_builder.storeDataEventErrorName(traits)).exists()) - { - tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), - _query_builder.storeDataEventErrorStatement(traits)); - - spdlog::trace("Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); - } - - // no result expected - tx.exec_prepared0(_query_builder.storeDataEventErrorName(traits), - _conf_id_cache->value(full_attr_name), - event_time, - quality, - _error_desc_id_cache->value(error_msg)); - - tx.commit(); - }); + auto query = QueryBuilder::storeDataEventErrorString(pqxx::to_string(_conf_id_cache->value(full_attr_name)), + pqxx::to_string(event_time), + pqxx::to_string(quality), + pqxx::to_string(_error_desc_id_cache->value(error_msg)), + traits); + + query += ";"; + _sql_buffer.push_back(query); } - catch (const pqxx::pqxx_exception &ex) + else { - handlePqxxError("The attribute [" + full_attr_name + "] error message [" + error_msg + "] was not saved.", - ex.base().what(), - _query_builder.storeDataEventErrorName(traits), - LOCATION_INFO); + try + { + // create and perform a pqxx transaction + pqxx::perform([&, this]() { + pqxx::work tx {(*_conn), StoreDataEventError}; + + if (!tx.prepared(_query_builder.storeDataEventErrorName(traits)).exists()) + { + tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), + _query_builder.storeDataEventErrorStatement(traits)); + + spdlog::trace( + "Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); + } + + // no result expected + tx.exec_prepared0(_query_builder.storeDataEventErrorName(traits), + _conf_id_cache->value(full_attr_name), + event_time, + quality, + _error_desc_id_cache->value(error_msg)); + + tx.commit(); + }); + } + catch (const pqxx::pqxx_exception &ex) + { + handlePqxxError( + "The attribute [" + full_attr_name + "] error message [" + error_msg + "] was not saved.", + ex.base().what(), + _query_builder.storeDataEventErrorName(traits), + LOCATION_INFO); + } } } @@ -573,6 +589,77 @@ namespace pqxx_conn return traits; } + //============================================================================= + //============================================================================= + void DbConnection::flush() + { + spdlog::debug("Flushing buffer of size: {}", _sql_buffer.size()); + + if (_sql_buffer.empty()) + { + spdlog::warn("Nothing to flush from the buffer, returning"); + return; + } + + try + { + pqxx::perform([&, this]() { + pqxx::work tx {(*_conn), StoreDataEvents}; + + string full_query; + + for (auto const &query : _sql_buffer) + full_query += query; + + tx.exec0(full_query); + + // commit the result + tx.commit(); + }); + } + catch (const pqxx::pqxx_exception &ex) + { + + spdlog::error("Error: An unexpected error occurred when trying to run a multiple event transaction."); + spdlog::error("Caught error at: {} Error: \"{}\"", LOCATION_INFO, ex.base().what()); + spdlog::info("Trying to run multiple event transaction in single bunches."); + + string full_msg = ""; + bool single_error = false; + + for (auto const &query : _sql_buffer) + { + try + { + pqxx::perform([&, this]() { + pqxx::work tx {(*_conn), StoreDataEvents}; + + tx.exec0(query); + tx.commit(); + }); + } + catch (const pqxx::pqxx_exception &ex) + { + single_error = true; + spdlog::error("Error: An unexpected error occurred when trying to run the single query: \"{}\"", query); + spdlog::error("Caught error at: {} Error: \"{}\"", LOCATION_INFO, ex.base().what()); + full_msg += "Could not run query:" + query + "\n"; + } + } + + // we may try the events individually in future + _sql_buffer.clear(); + + if(single_error) + { + spdlog::error("Throwing storage error with message: \"{}\"", full_msg); + Tango::Except::throw_exception("Storage Error", full_msg, LOCATION_INFO); + } + } + + _sql_buffer.clear(); + } + //============================================================================= //============================================================================= void DbConnection::storeEvent(const std::string &full_attr_name, const std::string &event) diff --git a/src/DbConnection.hpp b/src/DbConnection.hpp index 2a1b0351f5964f720856d6e328773da9f24e5994..21adc2e52cb9016d12a9a7d2223f14c1d6b3e2af 100644 --- a/src/DbConnection.hpp +++ b/src/DbConnection.hpp @@ -64,6 +64,12 @@ namespace pqxx_conn bool isOpen() const noexcept override { return _connected; } bool isClosed() const noexcept override { return !isOpen(); } + // this API allows the connection to buffer the event data store + // requests, and send them all at once to the db, this will increase + // insert time. Flush will execute the sql and clear the buffer + void buffer(bool enable) { _enable_buffering = enable; } + void flush(); + // storage API // store a new attribute and its conf data into the database @@ -99,8 +105,8 @@ namespace pqxx_conn void storeDataEvent(const std::string &full_attr_name, double event_time, int quality, - std::unique_ptr<vector<T>> value_r, - std::unique_ptr<vector<T>> value_w, + std::unique_ptr<std::vector<T>> value_r, + std::unique_ptr<std::vector<T>> value_w, const AttributeTraits &traits); // store a data error event in the data tables @@ -160,6 +166,13 @@ namespace pqxx_conn // configured db access method DbStoreMethod _db_store_method; + + // it is possible to buffer store requests as sql strings, then flush + // them all to the database at once, this increases insert speed, since + // multiple statements can be sent across the wire at once. This vector + // holds the preapred sql until the connection is flushed + bool _enable_buffering = false; + std::vector<std::string> _sql_buffer; }; } // namespace pqxx_conn } // namespace hdbpp_internal diff --git a/src/DbConnection.tpp b/src/DbConnection.tpp index 587d51733df444831ac3db1a5a5dd5c25b048ef8..26e5ae3d23bb34444284f53b00e49bee19dce803 100644 --- a/src/DbConnection.tpp +++ b/src/DbConnection.tpp @@ -35,7 +35,7 @@ namespace pqxx_conn template<typename T> struct Store { - static void run(std::unique_ptr<std::vector<T>> &value, + static void run(const std::unique_ptr<std::vector<T>> &value, const AttributeTraits &traits, pqxx::prepare::invocation &inv, pqxx::work & /*unused*/) @@ -53,7 +53,7 @@ namespace pqxx_conn template<> struct Store<std::string> { - static void run(std::unique_ptr<std::vector<std::string>> &value, + static void run(const std::unique_ptr<std::vector<std::string>> &value, const AttributeTraits &traits, pqxx::prepare::invocation &inv, pqxx::work &tx) @@ -77,7 +77,7 @@ namespace pqxx_conn template<> struct Store<bool> { - static void run(std::unique_ptr<std::vector<bool>> &value, + static void run(const std::unique_ptr<std::vector<bool>> &value, const AttributeTraits &traits, pqxx::prepare::invocation &inv, pqxx::work & /*unused*/) @@ -119,85 +119,105 @@ namespace pqxx_conn checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); - try + // if we are buffering the queries, then just save it until the buffer is flushed, + // otherwise execute directly + if (_enable_buffering) { - return pqxx::perform([&, this]() { - pqxx::work tx {(*_conn), StoreDataEvent}; + auto query = QueryBuilder::storeDataEventString<T>(pqxx::to_string(_conf_id_cache->value(full_attr_name)), + pqxx::to_string(event_time), + pqxx::to_string(quality), + value_r, + value_w, + traits); + + query += ";"; + _sql_buffer.push_back(query); + } + else + { + try + { + return pqxx::perform([&, this]() { + pqxx::work tx {(*_conn), StoreDataEvent}; - // there is a single special case here, arrays of strings need a different syntax to store, - // to avoid the quoting. Its likely we will need more for DevEncoded and DevEnum - if (_db_store_method == DbStoreMethod::InsertString || - (traits.isArray() && traits.type() == Tango::DEV_STRING)) - { - auto query = _query_builder.storeDataEventString<T>( - pqxx::to_string(_conf_id_cache->value(full_attr_name)), - pqxx::to_string(event_time), - pqxx::to_string(quality), - value_r, - value_w, - traits); - - tx.exec0(query); - } - else - { - // prepare as a prepared statement, we are going to use these - // queries often - if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) + // there is a single special case here, arrays of strings need a different syntax to store, + // to avoid the quoting. Its likely we will need more for DevEncoded and DevEnum + if (_db_store_method == DbStoreMethod::InsertString || + (traits.isArray() && traits.type() == Tango::DEV_STRING) || _enable_buffering) { - tx.conn().prepare(_query_builder.storeDataEventName(traits), - _query_builder.storeDataEventStatement<T>(traits)); - } - - // get the pqxx prepared statement invocation object to allow us to - // bind each parameter in turn, this gives us the flexibility to bind - // conditional parameters (as long as the query string matches) - auto inv = tx.prepared(_query_builder.storeDataEventName(traits)); - - // this lambda stores the data value correctly into the invocation, - // we must treat scalar/spectrum in different ways, one is a single - // element and the other an array. Further, the unique_ptr may be - // empty and signify a null should be stored in the column instead - auto store_value = [&tx, &traits, &inv](auto &value) { - if (value && !value->empty()) - { - store_data_utils::Store<T>::run(value, traits, inv, tx); - } + auto query = QueryBuilder::storeDataEventString<T>( + pqxx::to_string(_conf_id_cache->value(full_attr_name)), + pqxx::to_string(event_time), + pqxx::to_string(quality), + value_r, + value_w, + traits); + + if (_enable_buffering) + _sql_buffer.push_back(query); else + tx.exec0(query); + } + else + { + // prepare as a prepared statement, we are going to use these + // queries often + if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) { - // no value was given for this field, simply add a null - // instead, this allows invalid quality attributes to be saved - // with no data - inv(); + tx.conn().prepare(_query_builder.storeDataEventName(traits), + _query_builder.storeDataEventStatement<T>(traits)); } - }; - - // bind all the parameters - inv(_conf_id_cache->value(full_attr_name)); - inv(event_time); - - if (traits.hasReadData()) - store_value(value_r); - if (traits.hasWriteData()) - store_value(value_w); - - inv(quality); - - // execute - inv.exec(); - } + // get the pqxx prepared statement invocation object to allow us to + // bind each parameter in turn, this gives us the flexibility to bind + // conditional parameters (as long as the query string matches) + auto inv = tx.prepared(_query_builder.storeDataEventName(traits)); + + // this lambda stores the data value correctly into the invocation, + // we must treat scalar/spectrum in different ways, one is a single + // element and the other an array. Further, the unique_ptr may be + // empty and signify a null should be stored in the column instead + auto store_value = [&tx, &traits, &inv](auto &value) { + if (value && !value->empty()) + { + store_data_utils::Store<T>::run(value, traits, inv, tx); + } + else + { + // no value was given for this field, simply add a null + // instead, this allows invalid quality attributes to be saved + // with no data + inv(); + } + }; + + // bind all the parameters + inv(_conf_id_cache->value(full_attr_name)); + inv(event_time); + + if (traits.hasReadData()) + store_value(value_r); + + if (traits.hasWriteData()) + store_value(value_w); + + inv(quality); + + // execute + inv.exec(); + } - // commit the result - tx.commit(); - }); - } - catch (const pqxx::pqxx_exception &ex) - { - handlePqxxError("The attribute [" + full_attr_name + "] data event was not saved.", - ex.base().what(), - _query_builder.storeDataEventStatement<T>(traits), - LOCATION_INFO); + // commit the result + tx.commit(); + }); + } + catch (const pqxx::pqxx_exception &ex) + { + handlePqxxError("The attribute [" + full_attr_name + "] data event was not saved.", + ex.base().what(), + _query_builder.storeDataEventStatement<T>(traits), + LOCATION_INFO); + } } } } // namespace pqxx_conn diff --git a/src/HdbppTimescaleDbApi.cpp b/src/HdbppTimescaleDbApi.cpp index 5f30247e386bfafcc5779d2f9b728e0f7bcd7271..aadd0989f4cd7603eaade235eb692fa42f2fc1e3 100644 --- a/src/HdbppTimescaleDbApi.cpp +++ b/src/HdbppTimescaleDbApi.cpp @@ -166,56 +166,31 @@ void HdbppTimescaleDbApi::insert_event(Tango::EventData *event_data, const HdbEv assert(event_data->attr_value); spdlog::trace("Insert data event for attribute: {}", event_data->attr_name); - // if there is an error, we store an error, since there will be no data passed in - if (event_data->err) - { - spdlog::trace("Event type is error for attribute: {}", event_data->attr_name); - - // no time data is passed for errors, so make something up - struct timeval tv - {}; + // hand the call to the internal routine + doInsertEvent(event_data, data_type); +} - struct Tango::TimeVal tango_tv - {}; +//============================================================================= +//============================================================================= +void HdbppTimescaleDbApi::insert_events(vector<tuple<Tango::EventData *, HdbEventDataType>> events) +{ + _conn->buffer(true); - gettimeofday(&tv, nullptr); - tango_tv.tv_sec = tv.tv_sec; - tango_tv.tv_usec = tv.tv_usec; - tango_tv.tv_nsec = 0; + try + { + for (auto event : events) + doInsertEvent(get<0>(event), get<1>(event)); - _conn->createTx<HdbppTxDataEventError>() - .withName(event_data->attr_name) - .withTraits(static_cast<Tango::AttrWriteType>(data_type.write_type), - static_cast<Tango::AttrDataFormat>(data_type.data_format), - static_cast<Tango::CmdArgType>(data_type.data_type)) - .withError(string(event_data->errors[0].desc)) - .withEventTime(tango_tv) - .withQuality(event_data->attr_value->get_quality()) - .store(); + _conn->flush(); } - else + catch (Tango::DevFailed &e) { - spdlog::trace("Event type is data for attribute: {}", event_data->attr_name); - - // build a data event request, this will store 0 or more data elements, - // pending on type, format and quality - _conn->createTx<HdbppTxDataEvent>() - .withName(event_data->attr_name) - .withTraits(static_cast<Tango::AttrWriteType>(data_type.write_type), - static_cast<Tango::AttrDataFormat>(data_type.data_format), - static_cast<Tango::CmdArgType>(data_type.data_type)) - .withAttribute(event_data->attr_value) - .withEventTime(event_data->attr_value->get_date()) - .withQuality(event_data->attr_value->get_quality()) - .store(); + // ensure this is disabled on error + _conn->buffer(false); + throw; } -} -//============================================================================= -//============================================================================= -void HdbppTimescaleDbApi::insert_events(vector<tuple<Tango::EventData *, HdbEventDataType>> events) -{ - + _conn->buffer(false); } //============================================================================= @@ -286,4 +261,53 @@ bool HdbppTimescaleDbApi::supported(HdbppFeatures feature) return supported; } +//============================================================================= +//============================================================================= +void HdbppTimescaleDbApi::doInsertEvent(Tango::EventData *event_data, const HdbEventDataType &data_type) +{ + // if there is an error, we store an error, since there will be no data passed in + if (event_data->err) + { + spdlog::trace("Event type is error for attribute: {}", event_data->attr_name); + + // no time data is passed for errors, so make something up + struct timeval tv + {}; + + struct Tango::TimeVal tango_tv + {}; + + gettimeofday(&tv, nullptr); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + _conn->createTx<HdbppTxDataEventError>() + .withName(event_data->attr_name) + .withTraits(static_cast<Tango::AttrWriteType>(data_type.write_type), + static_cast<Tango::AttrDataFormat>(data_type.data_format), + static_cast<Tango::CmdArgType>(data_type.data_type)) + .withError(string(event_data->errors[0].desc)) + .withEventTime(tango_tv) + .withQuality(event_data->attr_value->get_quality()) + .store(); + } + else + { + spdlog::trace("Event type is data for attribute: {}", event_data->attr_name); + + // build a data event request, this will store 0 or more data elements, + // pending on type, format and quality + _conn->createTx<HdbppTxDataEvent>() + .withName(event_data->attr_name) + .withTraits(static_cast<Tango::AttrWriteType>(data_type.write_type), + static_cast<Tango::AttrDataFormat>(data_type.data_format), + static_cast<Tango::CmdArgType>(data_type.data_type)) + .withAttribute(event_data->attr_value) + .withEventTime(event_data->attr_value->get_date()) + .withQuality(event_data->attr_value->get_quality()) + .store(); + } +} + } // namespace hdbpp diff --git a/src/HdbppTimescaleDbApi.hpp b/src/HdbppTimescaleDbApi.hpp index d4b9ecbececf3486a3508f5db354d0a79cd83054..7e632ce7aeb7f0cef7568de7b0f1d0610216dae2 100644 --- a/src/HdbppTimescaleDbApi.hpp +++ b/src/HdbppTimescaleDbApi.hpp @@ -72,6 +72,8 @@ public: bool supported(HdbppFeatures feature) override; private: + void doInsertEvent(Tango::EventData *event_data, const HdbEventDataType &data_type); + std::unique_ptr<hdbpp_internal::pqxx_conn::DbConnection> _conn; std::string _identity; }; diff --git a/src/HdbppTxDataEvent.hpp b/src/HdbppTxDataEvent.hpp index 47f352ea2c20fde3bbf0fa4c6e253fb6e77d11d1..be3c4165ac86add434564c364725a425cebce7c6 100644 --- a/src/HdbppTxDataEvent.hpp +++ b/src/HdbppTxDataEvent.hpp @@ -207,7 +207,7 @@ void HdbppTxDataEvent<Conn>::doStore(ReadFunctor extract_read, WriteFunctor extr } // release ownership of the unique_ptr back to the caller - return std::move(value); + return value; }; // attempt to store the error in the database, any exceptions are left to diff --git a/src/QueryBuilder.cpp b/src/QueryBuilder.cpp index 23a960698dfbaa20776109fbd0e1423f247631be..f4a6dc694ed0c10802dfe738933327485d687e09 100644 --- a/src/QueryBuilder.cpp +++ b/src/QueryBuilder.cpp @@ -261,6 +261,31 @@ namespace pqxx_conn return result->second; } + //============================================================================= + //============================================================================= + string QueryBuilder::storeDataEventErrorString(const string &id, + const string &event_time, + const string &quality, + const string &err_id, + const AttributeTraits &traits) + { + // clang-format off + auto query = "INSERT INTO " + + QueryBuilder::tableName(traits) + " (" + + schema::DatColId + "," + + schema::DatColDataTime + "," + + schema::DatColQuality + "," + + schema::DatColErrorDescId + ") " + + "VALUES (" + + id + "," + + "TO_TIMESTAMP(" + event_time + ")," + + quality + "," + + err_id + ")"; + // clang-format on + + return query; + } + //============================================================================= //============================================================================= const string &QueryBuilder::storeErrorStatement() diff --git a/src/QueryBuilder.hpp b/src/QueryBuilder.hpp index 0dc52f9543a55347826a70a767fcd79068207844..92d4f0fda3cb9407b8f61c7db1023e87b369d685 100644 --- a/src/QueryBuilder.hpp +++ b/src/QueryBuilder.hpp @@ -68,7 +68,7 @@ namespace pqxx_conn template<typename T> struct DataToString { - static std::string run(std::unique_ptr<std::vector<T>> &value, const AttributeTraits &traits) + static std::string run(const std::unique_ptr<std::vector<T>> &value, const AttributeTraits &traits) { if (traits.isScalar()) return pqxx::to_string((*value)[0]); @@ -81,7 +81,7 @@ namespace pqxx_conn template<> struct DataToString<bool> { - static std::string run(std::unique_ptr<std::vector<bool>> &value, const AttributeTraits &traits) + static std::string run(const std::unique_ptr<std::vector<bool>> &value, const AttributeTraits &traits) { // a vector<bool> is not actually a vector<bool>, rather its some kind of bitfield. When // trying to return an element, we appear to get some kind of bitfield reference, @@ -103,7 +103,8 @@ namespace pqxx_conn template<> struct DataToString<std::string> { - static std::string run(std::unique_ptr<std::vector<std::string>> &value, const AttributeTraits &traits) + static std::string run( + const std::unique_ptr<std::vector<std::string>> &value, const AttributeTraits &traits) { // arrays of strings need both the ARRAY keywords and dollar escaping, this is so we // do not have to rely on the postgres escape functions that double and then store @@ -139,6 +140,7 @@ namespace pqxx_conn const string StoreHistoryEvent = "StoreHistoryEvent"; const string StoreParameterEvent = "StoreParameterEvent"; const string StoreDataEvent = "StoreDataEvent"; + const string StoreDataEvents = "StoreDataEvents"; const string StoreDataEventError = "StoreDataEventError"; const string StoreErrorString = "StoreErrorString"; const string StoreTtl = "StoreTtl"; @@ -189,16 +191,24 @@ namespace pqxx_conn // internal caching, so its less efficient, but can be chained in a pipe // to batch data to the database. template<typename T> - const std::string storeDataEventString(const std::string &full_attr_name, + static std::string storeDataEventString(const std::string &id, const std::string &event_time, const std::string &quality, - std::unique_ptr<vector<T>> &value_r, - std::unique_ptr<vector<T>> &value_w, + const std::unique_ptr<vector<T>> &value_r, + const std::unique_ptr<vector<T>> &value_w, const AttributeTraits &traits); // Builds a prepared statement for data event errors const std::string &storeDataEventErrorStatement(const AttributeTraits &traits); + // A vareint of storeDataEventErrorStatement that build and returns a string + // instead of a prepared statement + static std::string storeDataEventErrorString(const std::string &id, + const std::string &event_time, + const std::string &quality, + const std::string &err_id, + const AttributeTraits &traits); + // Utility void print(std::ostream &os) const noexcept; @@ -270,11 +280,11 @@ namespace pqxx_conn } template<typename T> - const std::string QueryBuilder::storeDataEventString(const std::string &full_attr_name, + std::string QueryBuilder::storeDataEventString(const std::string &id, const std::string &event_time, const std::string &quality, - std::unique_ptr<vector<T>> &value_r, - std::unique_ptr<vector<T>> &value_w, + const std::unique_ptr<vector<T>> &value_r, + const std::unique_ptr<vector<T>> &value_w, const AttributeTraits &traits) { auto query = "INSERT INTO " + QueryBuilder::tableName(traits) + " (" + schema::DatColId + "," + @@ -287,7 +297,7 @@ namespace pqxx_conn query = query + "," + schema::DatColValueW; // split to ensure increments are in the correct order - query = query + "," + schema::DatColQuality + ") VALUES ('" + full_attr_name + "'"; + query = query + "," + schema::DatColQuality + ") VALUES ('" + id + "'"; query = query + ",TO_TIMESTAMP(" + event_time + ")"; // add the read parameter with cast diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d839445587955bd08c870cc81bdea70a0ebb113..59417c380409420f2f2f7cca30bf76f528b00b4d 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,7 +6,6 @@ set(CMAKE_COLOR_MAKEFILE ON) # Make test executable set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/TestHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AttributeNameTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/AttributeTraitsTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ColumnCacheTests.cpp @@ -20,11 +19,26 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxUpdateTtlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/QueryBuilderTests.cpp) +add_library(test-utils STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/TestHelpers.cpp) +target_compile_options(test-utils PRIVATE -Wall -Wextra -g) + +target_link_libraries(test-utils + PRIVATE + libhdbpp_timescale_static_library + TangoInterfaceLibrary) + +target_include_directories(test-utils + PUBLIC ${CMAKE_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}) + add_executable(unit-tests ${TEST_SOURCES}) target_compile_options(unit-tests PRIVATE -Wall -Wextra -g) target_link_libraries(unit-tests - PRIVATE libhdbpp_headers libhdbpp_timescale_shared_library Catch2 TangoInterfaceLibrary) + PRIVATE + libhdbpp_timescale_static_library + Catch2 + TangoInterfaceLibrary + test-utils) target_include_directories(unit-tests PRIVATE ${CMAKE_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}) @@ -38,4 +52,8 @@ if(DO_CLANG_TIDY) set_target_properties(unit-tests PROPERTIES CXX_CLANG_TIDY ${DO_CLANG_TIDY}) + + set_target_properties(test-utils + PROPERTIES + CXX_CLANG_TIDY ${DO_CLANG_TIDY}) endif(DO_CLANG_TIDY) \ No newline at end of file diff --git a/test/DbConnectionTests.cpp b/test/DbConnectionTests.cpp index b7cd45188202a4bf8725d2e2ff03539f90e02282..b38f08b18e4cdfef27fd74b5775c7ca9770681e8 100644 --- a/test/DbConnectionTests.cpp +++ b/test/DbConnectionTests.cpp @@ -127,6 +127,12 @@ protected: _db_access = db_access; _test_conn.reset(nullptr); } + + void resetConn() + { + testConn().disconnect(); + testConn().connect(postgres_db::HdbppConnectionString); + } DbConnection &testConn(); pqxx::connection &verifyConn(); @@ -138,14 +144,16 @@ protected: template<Tango::CmdArgType Type> tuple<vector<typename TangoTypeTraits<Type>::type>, vector<typename TangoTypeTraits<Type>::type>> storeTestEventData(const string &att_name, const AttributeTraits &traits, int quality = Tango::ATTR_VALID); + + template<Tango::CmdArgType Type> + tuple<vector<typename TangoTypeTraits<Type>::type>, vector<typename TangoTypeTraits<Type>::type>> + store2EventDataSameTime(const string &att_name, const AttributeTraits &traits, int quality = Tango::ATTR_VALID); template<typename T> void checkStoreTestEventData( const string &att_name, const AttributeTraits &traits, const tuple<vector<T>, vector<T>> &data); QueryBuilder &queryBuilder() { return _query_builder; } - vector<AttributeTraits> getTraits() const; - vector<AttributeTraits> getTraitsImplemented() const; public: DbConnectionTestsFixture() = default; @@ -180,7 +188,7 @@ pqxx::connection &DbConnectionTestsFixture::verifyConn() //============================================================================= void DbConnectionTestsFixture::clearTables() { - vector<AttributeTraits> traits_array = getTraits(); + vector<AttributeTraits> traits_array = utils::getTraits(); { string query = "TRUNCATE "; @@ -239,66 +247,28 @@ string DbConnectionTestsFixture::storeAttributeByTraits(const AttributeTraits &t //============================================================================= //============================================================================= -vector<AttributeTraits> DbConnectionTestsFixture::getTraits() const +template<Tango::CmdArgType Type> +tuple<vector<typename TangoTypeTraits<Type>::type>, vector<typename TangoTypeTraits<Type>::type>> + DbConnectionTestsFixture::store2EventDataSameTime(const string &att_name, const AttributeTraits &traits, int quality) { - vector<AttributeTraits> traits_array {}; - - vector<Tango::CmdArgType> types {Tango::DEV_BOOLEAN, - Tango::DEV_DOUBLE, - Tango::DEV_FLOAT, - Tango::DEV_STRING, - Tango::DEV_LONG, - Tango::DEV_ULONG, - Tango::DEV_LONG64, - Tango::DEV_ULONG64, - Tango::DEV_SHORT, - Tango::DEV_USHORT, - Tango::DEV_UCHAR, - Tango::DEV_STATE, - Tango::DEV_ENCODED, - Tango::DEV_ENUM}; - - vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; - vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM}; - - // loop for every combination of type in Tango - for (auto &type : types) - for (auto &format : format_types) - for (auto &write : write_types) - traits_array.emplace_back(AttributeTraits {write, format, type}); - - return traits_array; -} + struct timeval tv + {}; + gettimeofday(&tv, nullptr); + double event_time = tv.tv_sec + tv.tv_usec / 1.0e6; -//============================================================================= -//============================================================================= -vector<AttributeTraits> DbConnectionTestsFixture::getTraitsImplemented() const -{ - vector<AttributeTraits> traits_array {}; - - vector<Tango::CmdArgType> types {Tango::DEV_BOOLEAN, - Tango::DEV_DOUBLE, - Tango::DEV_FLOAT, - Tango::DEV_STRING, - Tango::DEV_LONG, - Tango::DEV_ULONG, - Tango::DEV_LONG64, - Tango::DEV_ULONG64, - Tango::DEV_SHORT, - Tango::DEV_USHORT, - Tango::DEV_UCHAR, - Tango::DEV_STATE}; + auto r = generateData<Type>(traits, !traits.hasReadData()); + auto w = generateData<Type>(traits, !traits.hasWriteData()); + + auto r2 = generateData<Type>(traits, !traits.hasReadData()); + auto w2 = generateData<Type>(traits, !traits.hasWriteData()); - vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; - vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM}; + // make a copy for the consistency check + auto ret = make_tuple((*r), (*w)); - // loop for every combination of type in Tango - for (auto &type : types) - for (auto &format : format_types) - for (auto &write : write_types) - traits_array.emplace_back(AttributeTraits {write, format, type}); + REQUIRE_NOTHROW(testConn().storeDataEvent(att_name, event_time, quality, move(r), move(w), traits)); + REQUIRE_NOTHROW(testConn().storeDataEvent(att_name, event_time, quality, move(r2), move(w2), traits)); - return traits_array; + return ret; } //============================================================================= @@ -753,10 +723,10 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, } TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, - "Storing event data for all Tango type combinations in the database (prepared statements)", + "Storing single event data for all Tango type combinations in the database (prepared statements)", "[db-access][hdbpp-db-access][db-connection]") { - auto traits_array = getTraitsImplemented(); + auto traits_array = utils::getTraitsImplemented(); REQUIRE_NOTHROW(clearTables()); resetDbAccess(DbConnection::DbStoreMethod::PreparedStatement); @@ -823,10 +793,10 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, } TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, - "Storing event data for all Tango type combinations in the database (insert strings)", + "Storing single event data for all Tango type combinations in the database (insert strings)", "[db-access][hdbpp-db-access][db-connection]") { - auto traits_array = getTraitsImplemented(); + auto traits_array = utils::getTraitsImplemented(); REQUIRE_NOTHROW(clearTables()); resetDbAccess(DbConnection::DbStoreMethod::InsertString); @@ -892,6 +862,159 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, SUCCEED("Passed"); } +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "Storing multiple event data via a buffered sql statement", + "[db-access][hdbpp-db-access][db-connection]") +{ + auto traits_array = utils::getTraitsImplemented(); + REQUIRE_NOTHROW(clearTables()); + resetDbAccess(DbConnection::DbStoreMethod::InsertString); + + testConn().buffer(true); + + for (auto &traits : traits_array) + { + INFO("Inserting data for traits: " << traits); + auto name = storeAttributeByTraits(traits); + + switch (traits.type()) + { + case Tango::DEV_BOOLEAN: storeTestEventData<Tango::DEV_BOOLEAN>(name, traits); break; + + case Tango::DEV_SHORT: storeTestEventData<Tango::DEV_SHORT>(name, traits); break; + + case Tango::DEV_LONG: storeTestEventData<Tango::DEV_LONG>(name, traits); break; + + case Tango::DEV_LONG64: storeTestEventData<Tango::DEV_LONG64>(name, traits); break; + + case Tango::DEV_FLOAT: storeTestEventData<Tango::DEV_FLOAT>(name, traits); break; + + case Tango::DEV_DOUBLE: storeTestEventData<Tango::DEV_DOUBLE>(name, traits); break; + + case Tango::DEV_UCHAR: storeTestEventData<Tango::DEV_UCHAR>(name, traits); break; + + case Tango::DEV_USHORT: storeTestEventData<Tango::DEV_USHORT>(name, traits); break; + + case Tango::DEV_ULONG: storeTestEventData<Tango::DEV_ULONG>(name, traits); break; + + case Tango::DEV_ULONG64: storeTestEventData<Tango::DEV_ULONG64>(name, traits); break; + + case Tango::DEV_STRING: storeTestEventData<Tango::DEV_STRING>(name, traits); break; + + case Tango::DEV_STATE: storeTestEventData<Tango::DEV_STATE>(name, traits); break; + + default: throw "Should not be here!"; + } + } + + REQUIRE_NOTHROW(testConn().flush()); + + // check the rows, should be four in each table + for (auto &traits : traits_array) + { + pqxx::work tx {verifyConn()}; + + // now get the count in the table + auto result(tx.exec1("SELECT COUNT(*) FROM " + QueryBuilder::tableName(traits))); + tx.commit(); + + REQUIRE(result.size() == 1); + REQUIRE(result[0].as<int>() == 4); + } + + testConn().buffer(false); + SUCCEED("Passed"); +} + +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "Storing large number of spectrum event data via a buffered sql statement", + "[db-access][hdbpp-db-access][db-connection]") +{ + REQUIRE_NOTHROW(clearTables()); + resetDbAccess(DbConnection::DbStoreMethod::InsertString); + + testConn().buffer(true); + + AttributeTraits traits {Tango::READ_WRITE, Tango::SPECTRUM, Tango::DEV_DOUBLE}; + auto name = storeAttributeByTraits(traits); + + for (int i = 1; i <= 1000; i++) + storeTestEventData<Tango::DEV_DOUBLE>(name, traits); + + REQUIRE_NOTHROW(testConn().flush()); + + pqxx::work tx {verifyConn()}; + + // now get the count in the table + auto result(tx.exec1("SELECT COUNT(*) FROM " + QueryBuilder::tableName(traits))); + tx.commit(); + + REQUIRE(result.size() == 1); + REQUIRE(result[0].as<int>() == 1000); + + testConn().buffer(false); + SUCCEED("Passed"); +} + +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "Storing 2 scalar event data via a buffered sql statement with one statement causing an error", + "[db-access][hdbpp-db-access][db-connection]") +{ + REQUIRE_NOTHROW(clearTables()); + resetDbAccess(DbConnection::DbStoreMethod::InsertString); + + testConn().buffer(true); + + AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto name = storeAttributeByTraits(traits); + + store2EventDataSameTime<Tango::DEV_DOUBLE>(name, traits); + + REQUIRE_THROWS_AS(testConn().flush(), Tango::DevFailed); + + pqxx::work tx {verifyConn()}; + + // now get the count in the table + auto result(tx.exec1("SELECT COUNT(*) FROM " + QueryBuilder::tableName(traits))); + tx.commit(); + + REQUIRE(result.size() == 1); + REQUIRE(result[0].as<int>() == 1); + + testConn().buffer(false); + SUCCEED("Passed"); +} + +TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, + "Storing large number of scalar event data via a buffered sql statement", + "[db-access][hdbpp-db-access][db-connection]") +{ + REQUIRE_NOTHROW(clearTables()); + resetDbAccess(DbConnection::DbStoreMethod::InsertString); + + testConn().buffer(true); + + AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto name = storeAttributeByTraits(traits); + + for (int i = 0; i < 1000; i++) + storeTestEventData<Tango::DEV_DOUBLE>(name, traits); + + REQUIRE_NOTHROW(testConn().flush()); + + pqxx::work tx {verifyConn()}; + + // now get the count in the table + auto result(tx.exec1("SELECT COUNT(*) FROM " + QueryBuilder::tableName(traits))); + tx.commit(); + + REQUIRE(result.size() == 1); + REQUIRE(result[0].as<int>() == 1000); + + testConn().buffer(false); + SUCCEED("Passed"); +} + TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, "Storing complex arrays of strings containing postgres escape characters", "[db-access][hdbpp-db-access][db-connection]") @@ -1070,6 +1193,9 @@ TEST_CASE_METHOD(pqxx_conn_test::DbConnectionTestsFixture, REQUIRE(error_row.at(schema::ErrColErrorDesc).as<string>() == error_msg); } + //Clear the cache. + resetConn(); + gettimeofday(&tv, nullptr); event_time = tv.tv_sec + tv.tv_usec / 1.0e6; diff --git a/test/QueryBuilderTests.cpp b/test/QueryBuilderTests.cpp index 3daaf18169959df25440bb2fa93e64d507c985ef..6bb32e572ad1626c0b37eaf0432ce5cc3b859ab1 100644 --- a/test/QueryBuilderTests.cpp +++ b/test/QueryBuilderTests.cpp @@ -32,7 +32,6 @@ SCENARIO("storeDataEventString() returns the correct Value fields for the given { GIVEN("A query builder object with nothing cached") { - QueryBuilder query_builder; auto value_r = make_unique<vector<double>>(1.1, 2.2); auto value_r_empty = make_unique<vector<double>>(); auto value_w = make_unique<vector<double>>(3.3, 4.4); @@ -42,7 +41,7 @@ SCENARIO("storeDataEventString() returns the correct Value fields for the given { AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r, value_w_empty, traits); THEN("The result must include the schema::DatColValueR field only") @@ -56,7 +55,7 @@ SCENARIO("storeDataEventString() returns the correct Value fields for the given { AttributeTraits traits {Tango::WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r_empty, value_w, traits); THEN("The result must include the schema::DatColValueW field only") @@ -70,7 +69,7 @@ SCENARIO("storeDataEventString() returns the correct Value fields for the given { AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r, value_w, traits); THEN("The result must include both the schema::DatColValueR and schema::DatColValueW field") @@ -85,7 +84,7 @@ SCENARIO("storeDataEventString() returns the correct Value fields for the given { AttributeTraits traits {Tango::READ_WITH_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r, value_w, traits); THEN("The result must include both the schema::DatColValueR and schema::DatColValueW field") @@ -103,7 +102,6 @@ SCENARIO("storeDataEventString() adds a null when value is size zero", "[query-s { GIVEN("A query builder object with nothing cached") { - QueryBuilder query_builder; auto value_r = make_unique<vector<double>>(1.1, 2.2); auto value_r_empty = make_unique<vector<double>>(); auto value_w = make_unique<vector<double>>(3.3, 4.4); @@ -113,7 +111,7 @@ SCENARIO("storeDataEventString() adds a null when value is size zero", "[query-s { AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r, value_w_empty, traits); THEN("The result must include both the schema::DatColValueR and schema::DatColValueW field") @@ -128,7 +126,7 @@ SCENARIO("storeDataEventString() adds a null when value is size zero", "[query-s { AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; - auto result = query_builder.storeDataEventString<double>( + auto result = QueryBuilder::storeDataEventString<double>( TestAttrFQDName, string("0"), string("1"), value_r_empty, value_w, traits); THEN("The result must include both the schema::DatColValueR and schema::DatColValueW field") @@ -203,21 +201,20 @@ SCENARIO("storeDataEventStatement() returns the correct Value fields for the giv } } -SCENARIO("Creating valid insert queries with storeDataEventErrorQuery()", "[query-string]") +SCENARIO("Creating valid insert queries with storeDataEventErrorString()", "[query-string]") { - QueryBuilder query_builder; - GIVEN("An Attribute traits configured for scalar") { AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; - WHEN("Requesting a table name for the traits") + WHEN("Requesting an error query string") { - auto result = QueryBuilder::tableName(traits); + auto result = QueryBuilder::storeDataEventErrorString( + string("1"), string("0"), string("1"), string("An error message"), traits); - THEN("The result must include the schema::TypeScalar from the schema") + THEN("The result must include the schema::DatColValueR field only") { - REQUIRE_THAT(result, Contains(schema::TypeScalar)); + REQUIRE_THAT(result, Contains(string("An error message"))); } } } diff --git a/test/TestHelpers.cpp b/test/TestHelpers.cpp index 7450bf531916b8689b2ef2ab404d035f410a67e0..b432432f701d815baeff08742af864bb74c38aa8 100644 --- a/test/TestHelpers.cpp +++ b/test/TestHelpers.cpp @@ -39,7 +39,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen)); - return move(value); + return value; } //============================================================================= @@ -78,7 +78,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen)); - return move(value); + return value; } //============================================================================= @@ -93,7 +93,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen)); - return move(value); + return value; } //============================================================================= @@ -108,7 +108,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen)); - return move(value); + return value; } //============================================================================= @@ -216,7 +216,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(strings[experimental::randint(0, ((int)strings.size()) - 1)]); - return move(value); + return value; } //============================================================================= @@ -232,7 +232,74 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen) ? Tango::ON : Tango::OFF); - return move(value); + return value; } } // namespace data_gen + +namespace utils +{ + //============================================================================= + //============================================================================= + vector<AttributeTraits> getTraits() + { + vector<AttributeTraits> traits_array {}; + + vector<Tango::CmdArgType> types {Tango::DEV_BOOLEAN, + Tango::DEV_DOUBLE, + Tango::DEV_FLOAT, + Tango::DEV_STRING, + Tango::DEV_LONG, + Tango::DEV_ULONG, + Tango::DEV_LONG64, + Tango::DEV_ULONG64, + Tango::DEV_SHORT, + Tango::DEV_USHORT, + Tango::DEV_UCHAR, + Tango::DEV_STATE, + Tango::DEV_ENCODED, + Tango::DEV_ENUM}; + + vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; + vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM}; + + // loop for every combination of type in Tango + for (auto &type : types) + for (auto &format : format_types) + for (auto &write : write_types) + traits_array.emplace_back(AttributeTraits {write, format, type}); + + return traits_array; + } + + //============================================================================= + //============================================================================= + vector<AttributeTraits> getTraitsImplemented() + { + vector<AttributeTraits> traits_array {}; + + vector<Tango::CmdArgType> types {Tango::DEV_BOOLEAN, + Tango::DEV_DOUBLE, + Tango::DEV_FLOAT, + Tango::DEV_STRING, + Tango::DEV_LONG, + Tango::DEV_ULONG, + Tango::DEV_LONG64, + Tango::DEV_ULONG64, + Tango::DEV_SHORT, + Tango::DEV_USHORT, + Tango::DEV_UCHAR, + Tango::DEV_STATE}; + + vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; + vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM}; + + // loop for every combination of type in Tango + for (auto &type : types) + for (auto &format : format_types) + for (auto &write : write_types) + traits_array.emplace_back(AttributeTraits {write, format, type}); + + return traits_array; + } +} // namespace utils } // namespace hdbpp_test \ No newline at end of file diff --git a/test/TestHelpers.hpp b/test/TestHelpers.hpp index 3000fceb95a3d98fb6003cea9dd4027d02a19cce..089cb7c0f129d4f23bfc95b3ae77a2f970e26f1f 100644 --- a/test/TestHelpers.hpp +++ b/test/TestHelpers.hpp @@ -28,6 +28,7 @@ #include <memory> #include <random> #include <string> +#include <vector> namespace hdbpp_test { @@ -221,7 +222,7 @@ namespace data_gen for (int i = 0; i < size; i++) value->push_back(d(gen)); - return std::move(value); + return value; } template<Tango::CmdArgType Type> @@ -246,5 +247,11 @@ namespace data_gen return generateScalarData<Type>(empty_data); } } // namespace data_gen + +namespace utils +{ + std::vector<hdbpp_internal::AttributeTraits> getTraits(); + std::vector<hdbpp_internal::AttributeTraits> getTraitsImplemented(); +} // namespace utils } // namespace hdbpp_test #endif // _TEST_HELPERS_HPP