Quellcode durchsuchen

[C++] Introduce userver (#7617)

* introduce userver: + plaintext

* + json

* + db

* fix README

* lock userver version

* nproc in make -j

* some tweaks

* Userver -> userver

* + march=native

* fix paths in README

* cr from userver crew fixes

* up userver commit

* tune coroutine limits a bit

* Userver db (#3)

* add queries/updates tests

* Restructure project (#4)

* restructure project
* add cached-queries

* fix readme (#5)

* tune postgres connections pool
itrofimow vor 2 Jahren
Ursprung
Commit
e20a158272
24 geänderte Dateien mit 762 neuen und 0 gelöschten Zeilen
  1. 38 0
      frameworks/C++/userver/README.md
  2. 30 0
      frameworks/C++/userver/benchmark_config.json
  3. 17 0
      frameworks/C++/userver/userver.dockerfile
  4. 53 0
      frameworks/C++/userver/userver_benchmark/configs/dynamic_config_fallback.json
  5. 12 0
      frameworks/C++/userver/userver_benchmark/configs/secure_data.json
  6. 90 0
      frameworks/C++/userver/userver_benchmark/configs/static_config.yaml
  7. 17 0
      frameworks/C++/userver/userver_benchmark/src/CMakeLists.txt
  8. 38 0
      frameworks/C++/userver/userver_benchmark/src/common/db_helpers.cpp
  9. 32 0
      frameworks/C++/userver/userver_benchmark/src/common/db_helpers.hpp
  10. 33 0
      frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/handler.cpp
  11. 27 0
      frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/handler.hpp
  12. 21 0
      frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/world_cache_component.cpp
  13. 36 0
      frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/world_cache_component.hpp
  14. 12 0
      frameworks/C++/userver/userver_benchmark/src/controllers/json/handler.cpp
  15. 19 0
      frameworks/C++/userver/userver_benchmark/src/controllers/json/handler.hpp
  16. 44 0
      frameworks/C++/userver/userver_benchmark/src/controllers/multiple_queries/handler.cpp
  17. 26 0
      frameworks/C++/userver/userver_benchmark/src/controllers/multiple_queries/handler.hpp
  18. 12 0
      frameworks/C++/userver/userver_benchmark/src/controllers/plaintext/handler.cpp
  19. 18 0
      frameworks/C++/userver/userver_benchmark/src/controllers/plaintext/handler.hpp
  20. 31 0
      frameworks/C++/userver/userver_benchmark/src/controllers/single_query/handler.cpp
  21. 25 0
      frameworks/C++/userver/userver_benchmark/src/controllers/single_query/handler.hpp
  22. 65 0
      frameworks/C++/userver/userver_benchmark/src/controllers/updates/handler.cpp
  23. 28 0
      frameworks/C++/userver/userver_benchmark/src/controllers/updates/handler.hpp
  24. 38 0
      frameworks/C++/userver/userver_benchmark/src/userver_techempower.cpp

+ 38 - 0
frameworks/C++/userver/README.md

@@ -0,0 +1,38 @@
+# userver Benchmarking Test
+
+This is the [userver](https://github.com/userver-framework/userver) portion of a [benchmarking test suite](https://github.com/TechEmpower/FrameworkBenchmarks) comparing a variety of web development platforms.
+
+### Test Type Implementation Source Code
+
+* [PLAINTEXT](userver_benchmark/src/controllers/plaintext/handler.cpp)
+* [JSON](userver_benchmark/src/controllers/json/handler.cpp)
+* [Single Database Query](userver_benchmark/src/controllers/single_query/handler.cpp)
+* [Multiple Database Queries](userver_benchmark/src/controllers/multiple_queries/handler.cpp)
+* [Database Updates](userver_benchmark/src/controllers/updates/handler.cpp)
+* [Cached Queries](userver_benchmark/src/controllers/cached_queries/handler.cpp)
+
+## Test URLs
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### JSON
+
+http://localhost:8080/json
+
+### Single Database Query
+
+http://localhost:8080/db
+
+### Multiple Database Queries
+
+http://localhost:8080/queries
+
+### Database Updates
+
+http://localhost:8080/updates
+
+### Cached Queries
+
+http://localhost:8080/cached-queries
+

+ 30 - 0
frameworks/C++/userver/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "userver",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-queries?count=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "postgres",
+        "framework": "userver",
+        "language": "C++",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "userver",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 17 - 0
frameworks/C++/userver/userver.dockerfile

@@ -0,0 +1,17 @@
+FROM ghcr.io/userver-framework/docker-userver-build-base:v1a
+WORKDIR src
+COPY userver_benchmark/src/ ./
+RUN git clone https://github.com/userver-framework/userver.git && \
+    cd userver && git checkout 84456123a6a32f68847a791f29ef56d86bb765ad
+RUN mkdir build && cd build && \
+    cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_OPEN_SOURCE_BUILD=1 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
+          -DUSERVER_FEATURE_REDIS=0 -DUSERVER_FEATURE_CLICKHOUSE=0 -DUSERVER_FEATURE_MONGODB=0 -DUSERVER_FEATURE_RABBITMQ=0 -DUSERVER_FEATURE_GRPC=0 \
+          -DUSERVER_FEATURE_UTEST=0 \
+          -DUSERVER_FEATURE_POSTGRESQL=1 \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" .. && \
+    make -j $(nproc)
+
+COPY userver_benchmark/configs/* ./
+
+EXPOSE 8090
+CMD ./build/userver_techempower -c /src/static_config.yaml

+ 53 - 0
frameworks/C++/userver/userver_benchmark/configs/dynamic_config_fallback.json

@@ -0,0 +1,53 @@
+{
+  "USERVER_CACHES": {},
+  "USERVER_CANCEL_HANDLE_REQUEST_BY_DEADLINE": false,
+  "USERVER_CHECK_AUTH_IN_HANDLERS": false,
+  "USERVER_DUMPS": {},
+  "USERVER_HTTP_PROXY": "",
+  "USERVER_LOG_REQUEST": false,
+  "USERVER_LOG_REQUEST_HEADERS": false,
+  "USERVER_LRU_CACHES": {},
+  "USERVER_RPS_CCONTROL_CUSTOM_STATUS": {},
+  "USERVER_TASK_PROCESSOR_PROFILER_DEBUG": {},
+  "HTTP_CLIENT_CONNECTION_POOL_SIZE": 1000,
+  "HTTP_CLIENT_CONNECT_THROTTLE": {
+    "http-limit": 6000,
+    "http-per-second": 1500,
+    "https-limit": 100,
+    "https-per-second": 25,
+    "per-host-limit": 3000,
+    "per-host-per-second": 500
+  },
+  "HTTP_CLIENT_ENFORCE_TASK_DEADLINE": {
+    "cancel-request": false,
+    "update-timeout": false
+  },
+  "USERVER_TASK_PROCESSOR_QOS": {
+    "default-service": {
+      "default-task-processor": {
+        "wait_queue_overload": {
+          "action": "ignore",
+          "length_limit": 16385,
+          "time_limit_us": 30000
+        }
+      }
+    }
+  },
+
+  "POSTGRES_STATEMENT_METRICS_SETTINGS": {},
+  "POSTGRES_CONNECTION_PIPELINE_ENABLED": false,
+  "POSTGRES_CONNECTION_POOL_SETTINGS": {
+    "hello_world": {
+      "max_pool_size": 512,
+      "max_queue_size": 512,
+      "min_pool_size": 256
+    }
+  },
+  "POSTGRES_CONNECTION_SETTINGS": {},
+  "POSTGRES_DEFAULT_COMMAND_CONTROL": {
+    "network_timeout_ms": 1750,
+    "statement_timeout_ms": 1500
+  },
+  "POSTGRES_HANDLERS_COMMAND_CONTROL": {},
+  "POSTGRES_QUERIES_COMMAND_CONTROL": {}
+}

+ 12 - 0
frameworks/C++/userver/userver_benchmark/configs/secure_data.json

@@ -0,0 +1,12 @@
+{
+  "postgresql_settings": {
+    "databases": {
+      "hello_world": [{
+        "shard_number" : 0,
+        "hosts": [
+          "postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world"
+        ]
+      }]
+    }
+  }
+}

+ 90 - 0
frameworks/C++/userver/userver_benchmark/configs/static_config.yaml

@@ -0,0 +1,90 @@
+# yaml
+components_manager:
+    coro_pool:
+        initial_size: 10000             # Preallocate 10000 coroutines at startup.
+        max_size: 65536                 # Do not keep more than 65536 preallocated coroutines.
+        stack_size: 66560               # 64Kb for coroutine stack
+
+    task_processors:                    # Task processor is an executor for coroutine tasks
+
+        main-task-processor:            # Make a task processor for CPU-bound couroutine tasks.
+            thread_name: main-worker    # OS will show the threads of this task processor with 'main-worker' prefix.
+            worker_threads: 26
+            guess-cpu-limit: true
+
+        fs-task-processor:              # Make a separate task processor for filesystem bound tasks.
+            thread_name: fs-worker
+            worker_threads: 4
+
+    default_task_processor: main-task-processor
+
+    components:                         # Configuring components that were registered via component_list
+        server:
+            listener:                   # configuring the main listening socket...
+                port: 8080              # ...to listen on this port and...
+                task_processor: main-task-processor    # ...process incoming requests on this task processor.
+        logging:
+            fs-task-processor: fs-task-processor
+            loggers:
+                default:
+                    file_path: '@stderr'
+                    level: ERROR
+                    overflow_behavior: discard  # Drop logs if the system is too busy to write them down.
+
+        tracer:                                 # Component that helps to trace execution times and requests in logs.
+            service-name: userver-techempower   # "You know. You all know exactly who I am. Say my name. " (c)
+
+        dynamic-config:                      # Dynamic config storage options, do nothing
+            fs-cache-path: ''
+        dynamic-config-fallbacks:            # Load options from file and push them into the dynamic config storage.
+            fallback-path: /src/dynamic_config_fallback.json
+
+        testsuite-support:
+
+        secdist: # Component that stores configuration of hosts and passwords
+            config: /src/secure_data.json  # Values are supposed to be stored in this file
+            missing-ok: false                             # ... but if the file is missing it is still ok
+
+        plaintext-handler:
+            path: /plaintext
+            method: GET
+            task_processor: main-task-processor
+
+        json-handler:
+            path: /json
+            method: GET
+            task_processor: main-task-processor
+
+        hello-world-db:
+            dbalias: hello_world
+            blocking_task_processor: fs-task-processor
+            min_pool_size: 20
+            max_pool_size: 520
+            max_queue_size: 500
+
+        single-query-handler:
+            path: /db
+            method: GET
+            task_processor: main-task-processor
+
+        multiple-queries-handler:
+            path: /queries
+            method: GET
+            task_processor: main-task-processor
+
+        updates-handler:
+            path: /updates
+            method: GET
+            task_processor: main-task-processor
+
+        world-pg-cache:
+            pgcomponent: hello-world-db
+            update-types: only-full
+            update-interval: 1s
+            update-correction: 50ms
+
+        cached-queries-handler:
+            path: /cached-queries
+            method: GET
+            task_processor: main-task-processor
+

+ 17 - 0
frameworks/C++/userver/userver_benchmark/src/CMakeLists.txt

@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.12)
+project(userver_techempower CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+
+file(GLOB_RECURSE SOURCES
+  ${CMAKE_CURRENT_SOURCE_DIR}/controllers/*.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp
+)
+
+include(userver/cmake/SetupEnvironment.cmake)
+include(GNUInstallDirs)
+
+add_subdirectory(userver)
+
+add_executable(${PROJECT_NAME} ${SOURCES} userver_techempower.cpp)
+target_link_libraries(${PROJECT_NAME} PRIVATE userver-core userver-postgresql)

+ 38 - 0
frameworks/C++/userver/userver_benchmark/src/common/db_helpers.cpp

@@ -0,0 +1,38 @@
+#include "db_helpers.hpp"
+
+#include <userver/formats/json/inline.hpp>
+#include <userver/utils/rand.hpp>
+
+namespace userver_techempower::db_helpers {
+
+int GenerateRandomId() {
+  return userver::utils::RandRange(1, kMaxWorldRows + 1);
+}
+
+int GenerateRandomValue() {
+  return userver::utils::RandRange(1, kMaxWorldRows + 1);
+}
+
+userver::formats::json::Value Serialize(
+    const WorldTableRow& value,
+    userver::formats::serialize::To<userver::formats::json::Value>) {
+  return userver::formats::json::MakeObject("id", value.id, "randomNumber",
+                                            value.random_number);
+}
+
+int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
+                        const std::string& name) {
+  const auto& arg_str = request.GetArg(name);
+  if (arg_str.empty()) {
+    return 1;
+  }
+
+  try {
+    int value = std::stoi(arg_str);
+    return std::min(500, std::max(1, value));
+  } catch (const std::invalid_argument&) {
+    return 1;
+  }
+}
+
+}  // namespace userver_techempower::db_helpers

+ 32 - 0
frameworks/C++/userver/userver_benchmark/src/common/db_helpers.hpp

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <userver/formats/json/value.hpp>
+#include <userver/server/http/http_request.hpp>
+#include <userver/storages/postgres/cluster_types.hpp>
+#include <userver/storages/postgres/query.hpp>
+
+namespace userver_techempower::db_helpers {
+
+constexpr int kMaxWorldRows = 10000;
+const userver::storages::postgres::Query kSelectRowQuery{
+    "SELECT id, randomNumber FROM World WHERE id = $1"};
+constexpr auto kClusterHostType =
+    userver::storages::postgres::ClusterHostType::kMaster;
+constexpr std::string_view kDbComponentName = "hello-world-db";
+
+struct WorldTableRow final {
+  int id;
+  int random_number;
+};
+
+int GenerateRandomId();
+int GenerateRandomValue();
+
+userver::formats::json::Value Serialize(
+    const WorldTableRow& value,
+    userver::formats::serialize::To<userver::formats::json::Value>);
+
+int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
+                        const std::string& name);
+
+}  // namespace userver_techempower::db_helpers

+ 33 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/handler.cpp

@@ -0,0 +1,33 @@
+#include "handler.hpp"
+
+#include <userver/formats/serialize/common_containers.hpp>
+
+#include <boost/container/small_vector.hpp>
+
+namespace userver_techempower::cached_queries {
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerJsonBase{config, context},
+      cache_{context.FindComponent<WorldCacheComponent>()},
+      query_arg_name_{"count"} {}
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest& request,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  const auto queries_count =
+      db_helpers::ParseParamFromQuery(request, query_arg_name_);
+
+  boost::container::small_vector<db_helpers::WorldTableRow, 500> result(
+      queries_count);
+
+  const auto cache_ptr = cache_.Get();
+  const auto& cache = *cache_ptr;
+  std::generate(result.begin(), result.end(),
+                [&cache] { return cache.at(db_helpers::GenerateRandomId()); });
+
+  return userver::formats::json::ValueBuilder{result}.ExtractValue();
+}
+
+}  // namespace userver_techempower::cached_queries

+ 27 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/handler.hpp

@@ -0,0 +1,27 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+
+#include "world_cache_component.hpp"
+
+namespace userver_techempower::cached_queries {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "cached-queries-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest& request,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+
+ private:
+  const WorldCacheComponent& cache_;
+
+  const std::string query_arg_name_;
+};
+
+}  // namespace userver_techempower::cached_queries

+ 21 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/world_cache_component.cpp

@@ -0,0 +1,21 @@
+#include "world_cache_component.hpp"
+
+namespace userver_techempower::cached_queries {
+
+WorldCache::WorldCache() { data_.reserve(db_helpers::kMaxWorldRows + 1); }
+
+std::size_t WorldCache::size() const { return data_.size(); }
+
+void WorldCache::insert_or_assign(int key, db_helpers::WorldTableRow&& row) {
+  if (size() <= static_cast<std::size_t>(key)) {
+    data_.resize(key + 1);
+  }
+
+  data_[key] = row;
+}
+
+const db_helpers::WorldTableRow& WorldCache::at(size_t ind) const {
+  return data_[ind];
+}
+
+}  // namespace userver_techempower::cached_queries

+ 36 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/cached_queries/world_cache_component.hpp

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <vector>
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/cache/base_postgres_cache.hpp>
+
+namespace userver_techempower::cached_queries {
+
+class WorldCache final {
+ public:
+  WorldCache();
+
+  std::size_t size() const;
+  void insert_or_assign(int key, db_helpers::WorldTableRow&& row);
+
+  const db_helpers::WorldTableRow& at(size_t ind) const;
+
+ private:
+  std::vector<db_helpers::WorldTableRow> data_;
+};
+
+struct WorldCachePolicy final {
+  static constexpr std::string_view kName = "world-pg-cache";
+  using ValueType = db_helpers::WorldTableRow;
+  using CacheContainer = WorldCache;
+  static constexpr auto kKeyMember = &db_helpers::WorldTableRow::id;
+  static constexpr const char* kQuery = "SELECT id, randomNumber FROM World";
+  static constexpr const char* kUpdatedField = "";
+  static constexpr auto kClusterHostType = db_helpers::kClusterHostType;
+};
+
+using WorldCacheComponent = userver::components::PostgreCache<WorldCachePolicy>;
+
+}  // namespace userver_techempower::cached_queries

+ 12 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/json/handler.cpp

@@ -0,0 +1,12 @@
+#include "handler.hpp"
+
+namespace userver_techempower::json {
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest&,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  return userver::formats::json::MakeObject("message", "Hello, World!");
+}
+
+}  // namespace userver_techempower::json

+ 19 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/json/handler.hpp

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+
+namespace userver_techempower::json {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "json-handler";
+
+  using HttpHandlerJsonBase::HttpHandlerJsonBase;
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest&,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+};
+
+}  // namespace userver_techempower::json

+ 44 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/multiple_queries/handler.cpp

@@ -0,0 +1,44 @@
+#include "handler.hpp"
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/components/component_context.hpp>
+#include <userver/formats/serialize/common_containers.hpp>
+#include <userver/storages/postgres/postgres.hpp>
+
+#include <boost/container/small_vector.hpp>
+
+namespace userver_techempower::multiple_queries {
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerJsonBase{config, context},
+      pg_{context
+              .FindComponent<userver::components::Postgres>(
+                  db_helpers::kDbComponentName)
+              .GetCluster()},
+      query_arg_name_{"queries"} {}
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest& request,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  const auto queries_count =
+      db_helpers::ParseParamFromQuery(request, query_arg_name_);
+
+  boost::container::small_vector<int, 500> random_ids(queries_count);
+  std::generate(random_ids.begin(), random_ids.end(),
+                db_helpers::GenerateRandomId);
+
+  boost::container::small_vector<db_helpers::WorldTableRow, 500> result{};
+  for (auto id : random_ids) {
+    result.push_back(pg_->Execute(db_helpers::kClusterHostType,
+                                  db_helpers::kSelectRowQuery, id)
+                         .AsSingleRow<db_helpers::WorldTableRow>(
+                             userver::storages::postgres::kRowTag));
+  }
+
+  return userver::formats::json::ValueBuilder{result}.ExtractValue();
+}
+
+}  // namespace userver_techempower::multiple_queries

+ 26 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/multiple_queries/handler.hpp

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+#include <userver/storages/postgres/postgres_fwd.hpp>
+
+namespace userver_techempower::multiple_queries {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "multiple-queries-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest& request,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+
+ private:
+  const userver::storages::postgres::ClusterPtr pg_;
+
+  const std::string query_arg_name_;
+};
+
+}  // namespace userver_techempower::multiple_queries

+ 12 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/plaintext/handler.cpp

@@ -0,0 +1,12 @@
+#include "handler.hpp"
+
+namespace userver_techempower::plaintext {
+
+std::string Handler::HandleRequestThrow(
+    const userver::server::http::HttpRequest& request,
+    userver::server::request::RequestContext&) const {
+  request.GetHttpResponse().SetContentType("text/plain");
+  return "Hello, World!";
+}
+
+}  // namespace userver_techempower::plaintext

+ 18 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/plaintext/handler.hpp

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_base.hpp>
+
+namespace userver_techempower::plaintext {
+
+class Handler final : public userver::server::handlers::HttpHandlerBase {
+ public:
+  static constexpr std::string_view kName = "plaintext-handler";
+
+  using HttpHandlerBase::HttpHandlerBase;
+
+  std::string HandleRequestThrow(
+      const userver::server::http::HttpRequest& request,
+      userver::server::request::RequestContext&) const final;
+};
+
+}  // namespace userver_techempower::plaintext

+ 31 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/single_query/handler.cpp

@@ -0,0 +1,31 @@
+#include "handler.hpp"
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/components/component_context.hpp>
+#include <userver/storages/postgres/postgres.hpp>
+
+namespace userver_techempower::single_query {
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerJsonBase{config, context},
+      pg_{context
+              .FindComponent<userver::components::Postgres>(
+                  db_helpers::kDbComponentName)
+              .GetCluster()} {}
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest&,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  const auto row =
+      pg_->Execute(db_helpers::kClusterHostType, db_helpers::kSelectRowQuery,
+                   db_helpers::GenerateRandomId())
+          .AsSingleRow<db_helpers::WorldTableRow>(
+              userver::storages::postgres::kRowTag);
+
+  return db_helpers::Serialize(row, {});
+}
+
+}  // namespace userver_techempower::single_query

+ 25 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/single_query/handler.hpp

@@ -0,0 +1,25 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+
+#include <userver/storages/postgres/postgres_fwd.hpp>
+
+namespace userver_techempower::single_query {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "single-query-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest&,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+
+ private:
+  const userver::storages::postgres::ClusterPtr pg_;
+};
+
+}  // namespace userver_techempower::single_query

+ 65 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/updates/handler.cpp

@@ -0,0 +1,65 @@
+#include "handler.hpp"
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/components/component_context.hpp>
+#include <userver/formats/serialize/common_containers.hpp>
+#include <userver/storages/postgres/postgres.hpp>
+
+#include <boost/container/small_vector.hpp>
+
+namespace userver_techempower::updates {
+
+namespace {
+
+constexpr const char* kUpdateQueryStr{R"(
+UPDATE World w SET
+  randomNumber = new_numbers.randomNumber
+FROM ( SELECT
+  UNNEST($1) as id,
+  UNNEST($2) as randomNumber
+) new_numbers
+WHERE w.id = new_numbers.id
+)"};
+
+}
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerJsonBase{config, context},
+      pg_{context.FindComponent<userver::components::Postgres>("hello-world-db")
+              .GetCluster()},
+      query_arg_name_{"queries"},
+      update_query_{kUpdateQueryStr} {}
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest& request,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  const auto queries_count =
+      db_helpers::ParseParamFromQuery(request, query_arg_name_);
+
+  std::vector<int> random_ids(queries_count);
+  std::generate(random_ids.begin(), random_ids.end(),
+                db_helpers::GenerateRandomId);
+  std::sort(random_ids.begin(), random_ids.end());
+
+  boost::container::small_vector<db_helpers::WorldTableRow, 500> result{};
+  for (auto id : random_ids) {
+    result.push_back(pg_->Execute(db_helpers::kClusterHostType,
+                                  db_helpers::kSelectRowQuery, id)
+                         .AsSingleRow<db_helpers::WorldTableRow>(
+                             userver::storages::postgres::kRowTag));
+  }
+
+  std::vector<int> random_numbers(queries_count);
+  std::generate(random_numbers.begin(), random_numbers.end(),
+                db_helpers::GenerateRandomValue);
+
+  pg_->Execute(db_helpers::kClusterHostType, update_query_, random_ids,
+               random_numbers);
+
+  return userver::formats::json::ValueBuilder{result}.ExtractValue();
+}
+
+}  // namespace userver_techempower::updates

+ 28 - 0
frameworks/C++/userver/userver_benchmark/src/controllers/updates/handler.hpp

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+#include <userver/storages/postgres/postgres_fwd.hpp>
+#include <userver/storages/postgres/query.hpp>
+
+namespace userver_techempower::updates {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "updates-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest& request,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+
+ private:
+  const userver::storages::postgres::ClusterPtr pg_;
+
+  const std::string query_arg_name_;
+  const userver::storages::postgres::Query update_query_;
+};
+
+}  // namespace userver_techempower::updates

+ 38 - 0
frameworks/C++/userver/userver_benchmark/src/userver_techempower.cpp

@@ -0,0 +1,38 @@
+#include <userver/components/minimal_server_component_list.hpp>
+#include <userver/testsuite/testsuite_support.hpp>
+#include <userver/utils/daemon_run.hpp>
+
+#include <userver/storages/postgres/component.hpp>
+#include <userver/storages/secdist/component.hpp>
+
+#include "controllers/cached_queries/handler.hpp"
+#include "controllers/json/handler.hpp"
+#include "controllers/multiple_queries/handler.hpp"
+#include "controllers/plaintext/handler.hpp"
+#include "controllers/single_query/handler.hpp"
+#include "controllers/updates/handler.hpp"
+
+namespace userver_techempower {
+
+int Main(int argc, char* argv[]) {
+  auto component_list =
+      userver::components::MinimalServerComponentList()
+          .Append<userver::components::Secdist>()
+          .Append<userver::components::TestsuiteSupport>()
+          .Append<userver::components::Postgres>("hello-world-db")
+          .Append<plaintext::Handler>()
+          .Append<json::Handler>()
+          .Append<single_query::Handler>()
+          .Append<multiple_queries::Handler>()
+          .Append<updates::Handler>()
+          .Append<cached_queries::WorldCacheComponent>()
+          .Append<cached_queries::Handler>();
+
+  return userver::utils::DaemonMain(argc, argv, component_list);
+}
+
+}  // namespace userver_techempower
+
+int main(int argc, char* argv[]) {
+  return userver_techempower::Main(argc, argv);
+}