Browse Source

[C++] [userver] Bump userver commit, optimize updates and queries, tweak tracing and metrics (#8768)

* [C++] [userver] Optimize updates and queries

* bump userver commit

* reserve the QueryQueue appropriately

* bump userver commit

* switch to llhttp in userver-bare

* tweak userver-builtin tracing/metrics/logs
itrofimow 1 year ago
parent
commit
f542c4ca97

+ 4 - 3
frameworks/C++/userver/userver-bare.dockerfile

@@ -6,7 +6,7 @@ RUN apt update && \
 
 WORKDIR /src
 RUN git clone https://github.com/userver-framework/userver.git && \
-    cd userver && git checkout fcf0514be560f46740f8a654f2fdce5dc1cd450c
+    cd userver && git checkout c2ca5454f0b0e93dd0a2e082904dedda5cda3052
 
 COPY userver_benchmark/ ./
 RUN mkdir build && cd build && \
@@ -14,8 +14,9 @@ RUN mkdir build && cd build && \
           -DUSERVER_FEATURE_UTEST=0 \
           -DUSERVER_FEATURE_POSTGRESQL=1 \
           -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
-          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \
-          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 -DUSERVER_LTO_CACHE=0 .. && \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \
+          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \
+          -DUSERVER_LTO=0 .. && \
     make -j $(nproc)
 
 FROM builder AS runner

+ 4 - 3
frameworks/C++/userver/userver.dockerfile

@@ -6,7 +6,7 @@ RUN apt update && \
 
 WORKDIR /src
 RUN git clone https://github.com/userver-framework/userver.git && \
-    cd userver && git checkout fcf0514be560f46740f8a654f2fdce5dc1cd450c
+    cd userver && git checkout c2ca5454f0b0e93dd0a2e082904dedda5cda3052
 
 COPY userver_benchmark/ ./
 RUN mkdir build && cd build && \
@@ -14,8 +14,9 @@ RUN mkdir build && cd build && \
           -DUSERVER_FEATURE_UTEST=0 \
           -DUSERVER_FEATURE_POSTGRESQL=1 \
           -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
-          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \
-          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 -DUSERVER_LTO_CACHE=0 .. && \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \
+          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \
+          -DUSERVER_LTO=0 .. && \
     make -j $(nproc)
 
 FROM builder AS runner

+ 1 - 1
frameworks/C++/userver/userver_benchmark/CMakeLists.txt

@@ -15,4 +15,4 @@ add_subdirectory(userver)
 userver_setup_environment()
 
 add_executable(${PROJECT_NAME} ${SOURCES} userver_techempower.cpp)
-target_link_libraries(${PROJECT_NAME} PRIVATE userver-core userver-postgresql)
+target_link_libraries(${PROJECT_NAME} PRIVATE userver-core userver-postgresql userver-llhttp)

+ 13 - 15
frameworks/C++/userver/userver_benchmark/bare/simple_connection.cpp

@@ -3,7 +3,7 @@
 #include <array>
 
 #include <cctz/time_zone.h>
-#include <http_parser.h>
+#include <llhttp.h>
 #include <boost/container/small_vector.hpp>
 
 #include "simple_server.hpp"
@@ -18,8 +18,8 @@ namespace userver_techempower::bare {
 namespace {
 
 struct HttpParser final {
-  http_parser parser{};
-  http_parser_settings parser_settings{};
+  llhttp_t parser{};
+  llhttp_settings_t parser_settings{};
 
   std::function<void(std::string_view)> on_request_cb{};
 
@@ -27,33 +27,32 @@ struct HttpParser final {
 
   explicit HttpParser(std::function<void(std::string_view)> on_request_cb)
       : on_request_cb{std::move(on_request_cb)} {
-    http_parser_init(&parser, HTTP_REQUEST);
-    parser.data = this;
-
-    http_parser_settings_init(&parser_settings);
+    llhttp_settings_init(&parser_settings);
     parser_settings.on_url = HttpOnUrl;
     parser_settings.on_message_begin = HttpOnMessageBegin;
     parser_settings.on_message_complete = HttpOnMessageComplete;
+
+    llhttp_init(&parser, HTTP_REQUEST, &parser_settings);
+    parser.data = this;
   }
 
-  void Execute(const char* data, std::size_t length) {
-    http_parser_execute(&parser, &parser_settings, data, length);
+  auto Execute(const char* data, std::size_t length) {
+    return llhttp_execute(&parser, data, length);
   }
 
-  static int HttpOnUrl(http_parser* parser, const char* data,
-                       std::size_t length) {
+  static int HttpOnUrl(llhttp_t* parser, const char* data, std::size_t length) {
     auto* self = static_cast<HttpParser*>(parser->data);
     self->url.append(std::string_view{data, length});
     return 0;
   }
 
-  static int HttpOnMessageBegin(http_parser* parser) {
+  static int HttpOnMessageBegin(llhttp_t* parser) {
     auto* self = static_cast<HttpParser*>(parser->data);
     self->url.clear();
     return 0;
   }
 
-  static int HttpOnMessageComplete(http_parser* parser) {
+  static int HttpOnMessageComplete(llhttp_t* parser) {
     auto* self = static_cast<HttpParser*>(parser->data);
     self->on_request_cb(static_cast<std::string_view>(self->url));
     return 0;
@@ -192,8 +191,7 @@ void SimpleConnection::Process() {
       break;
     }
 
-    parser.Execute(buffer.data(), last_bytes_read);
-    if (parser.parser.http_errno != 0) {
+    if (parser.Execute(buffer.data(), last_bytes_read) != HPE_OK) {
       break;
     }
 

+ 15 - 15
frameworks/C++/userver/userver_benchmark/controllers/multiple_queries/handler.cpp

@@ -37,24 +37,24 @@ std::string Handler::HandleRequestThrow(
 }
 
 std::string Handler::GetResponse(int queries) const {
-  boost::container::small_vector<db_helpers::WorldTableRow, 20> result(queries);
-  for (auto& value : result) {
-    value.id = db_helpers::GenerateRandomId();
-  }
-
-  {
+  const auto db_result = [this, queries] {
     const auto lock = semaphore_.Acquire();
 
-    auto trx =
-        pg_->Begin(db_helpers::kClusterHostType, {}, db_helpers::kDefaultPgCC);
-    for (auto& value : result) {
-      value.random_number = trx.Execute(db_helpers::kDefaultPgCC,
-                                        db_helpers::kSelectRowQuery, value.id)
-                                .AsSingleRow<db_helpers::WorldTableRow>(
-                                    userver::storages::postgres::kRowTag)
-                                .random_number;
+    auto query_queue = pg_->CreateQueryQueue(db_helpers::kClusterHostType,
+                                             db_helpers::kDefaultPgCC.execute);
+    query_queue.Reserve(queries);
+    for (std::size_t i = 0; i < static_cast<std::size_t>(queries); ++i) {
+      query_queue.Push(db_helpers::kDefaultPgCC, db_helpers::kSelectRowQuery,
+                       db_helpers::GenerateRandomId());
     }
-    trx.Commit();
+
+    return query_queue.Collect(db_helpers::kDefaultPgCC.execute);
+  }();
+
+  boost::container::small_vector<db_helpers::WorldTableRow, 20> result(queries);
+  for (std::size_t i = 0; i < static_cast<std::size_t>(queries); ++i) {
+    result[i] = db_result[i].AsSingleRow<db_helpers::WorldTableRow>(
+        userver::storages::postgres::kRowTag);
   }
 
   userver::formats::json::StringBuilder sb{};

+ 32 - 25
frameworks/C++/userver/userver_benchmark/controllers/updates/handler.cpp

@@ -7,6 +7,15 @@
 
 #include <boost/container/small_vector.hpp>
 
+namespace userver::storages::postgres::io::traits {
+
+// Hijack userver's whitelist of allowed containers
+template <typename T, std::size_t Size>
+struct IsCompatibleContainer<boost::container::small_vector<T, Size>>
+    : std::true_type {};
+
+}  // namespace userver::storages::postgres::io::traits
+
 namespace userver_techempower::updates {
 
 namespace {
@@ -46,41 +55,39 @@ std::string Handler::HandleRequestThrow(
 }
 
 std::string Handler::GetResponse(int queries) const {
-  // userver's PG doesn't accept boost::small_vector as an input, sadly
-  std::vector<db_helpers::WorldTableRow> values(queries);
-  for (auto& value : values) {
-    value.id = db_helpers::GenerateRandomId();
+  boost::container::small_vector<int, 20> ids(queries);
+  for (auto& id : ids) {
+    id = db_helpers::GenerateRandomId();
   }
   // we have to sort ids to not deadlock in update
-  std::sort(values.begin(), values.end(),
-            [](const auto& lhs, const auto& rhs) { return lhs.id < rhs.id; });
+  std::sort(ids.begin(), ids.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs < rhs; });
 
-  boost::container::small_vector<db_helpers::WorldTableRow, 20> result;
+  boost::container::small_vector<int, 20> values(queries);
+  for (auto& value : values) {
+    value = db_helpers::GenerateRandomValue();
+  }
 
-  {
+  const auto db_results = [this, &ids, &values] {
     const auto lock = semaphore_.Acquire();
 
-    auto trx =
-        pg_->Begin(db_helpers::kClusterHostType, {}, db_helpers::kDefaultPgCC);
-    for (auto& value : values) {
-      value.random_number = trx.Execute(db_helpers::kDefaultPgCC,
-                                        db_helpers::kSelectRowQuery, value.id)
-                                .AsSingleRow<db_helpers::WorldTableRow>(
-                                    userver::storages::postgres::kRowTag)
-                                .random_number;
+    auto query_queue = pg_->CreateQueryQueue(db_helpers::kClusterHostType,
+                                             db_helpers::kDefaultPgCC.execute);
+    query_queue.Reserve(ids.size() + 1 /* for the update query */);
+    for (const auto id : ids) {
+      query_queue.Push(db_helpers::kDefaultPgCC, db_helpers::kSelectRowQuery,
+                       id);
     }
 
-    // We copy values here (and hope compiler optimizes it into one memcpy call)
-    // to not serialize into json within transaction
-    result.assign(values.begin(), values.end());
+    query_queue.Push(db_helpers::kDefaultPgCC, update_query_, ids, values);
 
-    for (auto& value : values) {
-      value.random_number = db_helpers::GenerateRandomValue();
-    }
+    return query_queue.Collect(db_helpers::kDefaultPgCC.execute);
+  }();
 
-    trx.ExecuteDecomposeBulk(db_helpers::kDefaultPgCC, update_query_, values,
-                             values.size());
-    trx.Commit();
+  boost::container::small_vector<db_helpers::WorldTableRow, 20> result(queries);
+  for (std::size_t i = 0; i < result.size(); ++i) {
+    result[i] = db_results[i].AsSingleRow<db_helpers::WorldTableRow>(
+        userver::storages::postgres::kRowTag);
   }
 
   userver::formats::json::StringBuilder sb{};

+ 17 - 1
frameworks/C++/userver/userver_benchmark/userver_techempower.cpp

@@ -4,6 +4,7 @@
 
 #include <userver/clients/dns/component.hpp>
 
+#include <userver/server/middlewares/configuration.hpp>
 #include <userver/storages/postgres/component.hpp>
 #include <userver/storages/secdist/component.hpp>
 #include <userver/storages/secdist/provider_component.hpp>
@@ -45,6 +46,20 @@ class NoopTracingManager final
       userver::server::http::HttpResponse&) const final {}
 };
 
+class MinimalMiddlewarePipelineBuilder final
+    : public userver::server::middlewares::PipelineBuilder {
+ public:
+  static constexpr std::string_view kName{
+      "minimal-middleware-pipeline-builder"};
+  using userver::server::middlewares::PipelineBuilder::PipelineBuilder;
+
+ private:
+  userver::server::middlewares::MiddlewaresList BuildPipeline(
+      userver::server::middlewares::MiddlewaresList) const override {
+    return {"userver-unknown-exceptions-handling-middleware"};
+  }
+};
+
 int Main(int argc, char* argv[]) {
   auto component_list =
       userver::components::MinimalServerComponentList()
@@ -63,8 +78,9 @@ int Main(int argc, char* argv[]) {
           .Append<cached_queries::WorldCacheComponent>()  // cache component
           .Append<cached_queries::Handler>()
           .Append<fortunes::Handler>()
-          // tracing tweaks
+          // tracing and metrics tweaks
           .Append<NoopTracingManager>()
+          .Append<MinimalMiddlewarePipelineBuilder>()
           // bare
           .Append<bare::SimpleRouter>()
           .Append<bare::SimpleServer>();

+ 2 - 0
frameworks/C++/userver/userver_configs/static_config.yaml

@@ -29,6 +29,7 @@ components_manager:
                 handler-defaults:
                     set_tracing_headers: false
             server-name: us
+            middleware-pipeline-builder: minimal-middleware-pipeline-builder
         simple-router:
         simple-server:
             port: 8081
@@ -62,6 +63,7 @@ components_manager:
         noop-tracing-manager:
         tracing-manager-locator:
             component-name: noop-tracing-manager
+        minimal-middleware-pipeline-builder:
 
         plaintext-handler:
             path: /plaintext