Skip to content

Commit

Permalink
[C++] Introduce userver (TechEmpower#7617)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
itrofimow authored Nov 27, 2022
1 parent 9a0a2d9 commit e20a158
Show file tree
Hide file tree
Showing 24 changed files with 762 additions and 0 deletions.
38 changes: 38 additions & 0 deletions frameworks/C++/userver/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 30 additions & 0 deletions frameworks/C++/userver/benchmark_config.json
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions frameworks/C++/userver/userver.dockerfile
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions frameworks/C++/userver/userver_benchmark/configs/secure_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"postgresql_settings": {
"databases": {
"hello_world": [{
"shard_number" : 0,
"hosts": [
"postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world"
]
}]
}
}
}
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions frameworks/C++/userver/userver_benchmark/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 changes: 38 additions & 0 deletions frameworks/C++/userver/userver_benchmark/src/common/db_helpers.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 32 additions & 0 deletions frameworks/C++/userver/userver_benchmark/src/common/db_helpers.hpp
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit e20a158

Please sign in to comment.