Skip to content

Commit e20a158

Browse files
authored
[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
1 parent 9a0a2d9 commit e20a158

24 files changed

+762
-0
lines changed

frameworks/C++/userver/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# userver Benchmarking Test
2+
3+
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.
4+
5+
### Test Type Implementation Source Code
6+
7+
* [PLAINTEXT](userver_benchmark/src/controllers/plaintext/handler.cpp)
8+
* [JSON](userver_benchmark/src/controllers/json/handler.cpp)
9+
* [Single Database Query](userver_benchmark/src/controllers/single_query/handler.cpp)
10+
* [Multiple Database Queries](userver_benchmark/src/controllers/multiple_queries/handler.cpp)
11+
* [Database Updates](userver_benchmark/src/controllers/updates/handler.cpp)
12+
* [Cached Queries](userver_benchmark/src/controllers/cached_queries/handler.cpp)
13+
14+
## Test URLs
15+
### PLAINTEXT
16+
17+
http://localhost:8080/plaintext
18+
19+
### JSON
20+
21+
http://localhost:8080/json
22+
23+
### Single Database Query
24+
25+
http://localhost:8080/db
26+
27+
### Multiple Database Queries
28+
29+
http://localhost:8080/queries
30+
31+
### Database Updates
32+
33+
http://localhost:8080/updates
34+
35+
### Cached Queries
36+
37+
http://localhost:8080/cached-queries
38+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"framework": "userver",
3+
"tests": [
4+
{
5+
"default": {
6+
"json_url": "/json",
7+
"plaintext_url": "/plaintext",
8+
"db_url": "/db",
9+
"query_url": "/queries?queries=",
10+
"update_url": "/updates?queries=",
11+
"cached_query_url": "/cached-queries?count=",
12+
"port": 8080,
13+
"approach": "Realistic",
14+
"classification": "Micro",
15+
"database": "postgres",
16+
"framework": "userver",
17+
"language": "C++",
18+
"flavor": "None",
19+
"orm": "Raw",
20+
"platform": "None",
21+
"webserver": "None",
22+
"os": "Linux",
23+
"database_os": "Linux",
24+
"display_name": "userver",
25+
"notes": "",
26+
"versus": "None"
27+
}
28+
}
29+
]
30+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM ghcr.io/userver-framework/docker-userver-build-base:v1a
2+
WORKDIR src
3+
COPY userver_benchmark/src/ ./
4+
RUN git clone https://github.com/userver-framework/userver.git && \
5+
cd userver && git checkout 84456123a6a32f68847a791f29ef56d86bb765ad
6+
RUN mkdir build && cd build && \
7+
cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_OPEN_SOURCE_BUILD=1 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
8+
-DUSERVER_FEATURE_REDIS=0 -DUSERVER_FEATURE_CLICKHOUSE=0 -DUSERVER_FEATURE_MONGODB=0 -DUSERVER_FEATURE_RABBITMQ=0 -DUSERVER_FEATURE_GRPC=0 \
9+
-DUSERVER_FEATURE_UTEST=0 \
10+
-DUSERVER_FEATURE_POSTGRESQL=1 \
11+
-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" .. && \
12+
make -j $(nproc)
13+
14+
COPY userver_benchmark/configs/* ./
15+
16+
EXPOSE 8090
17+
CMD ./build/userver_techempower -c /src/static_config.yaml
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"USERVER_CACHES": {},
3+
"USERVER_CANCEL_HANDLE_REQUEST_BY_DEADLINE": false,
4+
"USERVER_CHECK_AUTH_IN_HANDLERS": false,
5+
"USERVER_DUMPS": {},
6+
"USERVER_HTTP_PROXY": "",
7+
"USERVER_LOG_REQUEST": false,
8+
"USERVER_LOG_REQUEST_HEADERS": false,
9+
"USERVER_LRU_CACHES": {},
10+
"USERVER_RPS_CCONTROL_CUSTOM_STATUS": {},
11+
"USERVER_TASK_PROCESSOR_PROFILER_DEBUG": {},
12+
"HTTP_CLIENT_CONNECTION_POOL_SIZE": 1000,
13+
"HTTP_CLIENT_CONNECT_THROTTLE": {
14+
"http-limit": 6000,
15+
"http-per-second": 1500,
16+
"https-limit": 100,
17+
"https-per-second": 25,
18+
"per-host-limit": 3000,
19+
"per-host-per-second": 500
20+
},
21+
"HTTP_CLIENT_ENFORCE_TASK_DEADLINE": {
22+
"cancel-request": false,
23+
"update-timeout": false
24+
},
25+
"USERVER_TASK_PROCESSOR_QOS": {
26+
"default-service": {
27+
"default-task-processor": {
28+
"wait_queue_overload": {
29+
"action": "ignore",
30+
"length_limit": 16385,
31+
"time_limit_us": 30000
32+
}
33+
}
34+
}
35+
},
36+
37+
"POSTGRES_STATEMENT_METRICS_SETTINGS": {},
38+
"POSTGRES_CONNECTION_PIPELINE_ENABLED": false,
39+
"POSTGRES_CONNECTION_POOL_SETTINGS": {
40+
"hello_world": {
41+
"max_pool_size": 512,
42+
"max_queue_size": 512,
43+
"min_pool_size": 256
44+
}
45+
},
46+
"POSTGRES_CONNECTION_SETTINGS": {},
47+
"POSTGRES_DEFAULT_COMMAND_CONTROL": {
48+
"network_timeout_ms": 1750,
49+
"statement_timeout_ms": 1500
50+
},
51+
"POSTGRES_HANDLERS_COMMAND_CONTROL": {},
52+
"POSTGRES_QUERIES_COMMAND_CONTROL": {}
53+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"postgresql_settings": {
3+
"databases": {
4+
"hello_world": [{
5+
"shard_number" : 0,
6+
"hosts": [
7+
"postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world"
8+
]
9+
}]
10+
}
11+
}
12+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# yaml
2+
components_manager:
3+
coro_pool:
4+
initial_size: 10000 # Preallocate 10000 coroutines at startup.
5+
max_size: 65536 # Do not keep more than 65536 preallocated coroutines.
6+
stack_size: 66560 # 64Kb for coroutine stack
7+
8+
task_processors: # Task processor is an executor for coroutine tasks
9+
10+
main-task-processor: # Make a task processor for CPU-bound couroutine tasks.
11+
thread_name: main-worker # OS will show the threads of this task processor with 'main-worker' prefix.
12+
worker_threads: 26
13+
guess-cpu-limit: true
14+
15+
fs-task-processor: # Make a separate task processor for filesystem bound tasks.
16+
thread_name: fs-worker
17+
worker_threads: 4
18+
19+
default_task_processor: main-task-processor
20+
21+
components: # Configuring components that were registered via component_list
22+
server:
23+
listener: # configuring the main listening socket...
24+
port: 8080 # ...to listen on this port and...
25+
task_processor: main-task-processor # ...process incoming requests on this task processor.
26+
logging:
27+
fs-task-processor: fs-task-processor
28+
loggers:
29+
default:
30+
file_path: '@stderr'
31+
level: ERROR
32+
overflow_behavior: discard # Drop logs if the system is too busy to write them down.
33+
34+
tracer: # Component that helps to trace execution times and requests in logs.
35+
service-name: userver-techempower # "You know. You all know exactly who I am. Say my name. " (c)
36+
37+
dynamic-config: # Dynamic config storage options, do nothing
38+
fs-cache-path: ''
39+
dynamic-config-fallbacks: # Load options from file and push them into the dynamic config storage.
40+
fallback-path: /src/dynamic_config_fallback.json
41+
42+
testsuite-support:
43+
44+
secdist: # Component that stores configuration of hosts and passwords
45+
config: /src/secure_data.json # Values are supposed to be stored in this file
46+
missing-ok: false # ... but if the file is missing it is still ok
47+
48+
plaintext-handler:
49+
path: /plaintext
50+
method: GET
51+
task_processor: main-task-processor
52+
53+
json-handler:
54+
path: /json
55+
method: GET
56+
task_processor: main-task-processor
57+
58+
hello-world-db:
59+
dbalias: hello_world
60+
blocking_task_processor: fs-task-processor
61+
min_pool_size: 20
62+
max_pool_size: 520
63+
max_queue_size: 500
64+
65+
single-query-handler:
66+
path: /db
67+
method: GET
68+
task_processor: main-task-processor
69+
70+
multiple-queries-handler:
71+
path: /queries
72+
method: GET
73+
task_processor: main-task-processor
74+
75+
updates-handler:
76+
path: /updates
77+
method: GET
78+
task_processor: main-task-processor
79+
80+
world-pg-cache:
81+
pgcomponent: hello-world-db
82+
update-types: only-full
83+
update-interval: 1s
84+
update-correction: 50ms
85+
86+
cached-queries-handler:
87+
path: /cached-queries
88+
method: GET
89+
task_processor: main-task-processor
90+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
cmake_minimum_required(VERSION 3.12)
2+
project(userver_techempower CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
6+
file(GLOB_RECURSE SOURCES
7+
${CMAKE_CURRENT_SOURCE_DIR}/controllers/*.cpp
8+
${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp
9+
)
10+
11+
include(userver/cmake/SetupEnvironment.cmake)
12+
include(GNUInstallDirs)
13+
14+
add_subdirectory(userver)
15+
16+
add_executable(${PROJECT_NAME} ${SOURCES} userver_techempower.cpp)
17+
target_link_libraries(${PROJECT_NAME} PRIVATE userver-core userver-postgresql)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include "db_helpers.hpp"
2+
3+
#include <userver/formats/json/inline.hpp>
4+
#include <userver/utils/rand.hpp>
5+
6+
namespace userver_techempower::db_helpers {
7+
8+
int GenerateRandomId() {
9+
return userver::utils::RandRange(1, kMaxWorldRows + 1);
10+
}
11+
12+
int GenerateRandomValue() {
13+
return userver::utils::RandRange(1, kMaxWorldRows + 1);
14+
}
15+
16+
userver::formats::json::Value Serialize(
17+
const WorldTableRow& value,
18+
userver::formats::serialize::To<userver::formats::json::Value>) {
19+
return userver::formats::json::MakeObject("id", value.id, "randomNumber",
20+
value.random_number);
21+
}
22+
23+
int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
24+
const std::string& name) {
25+
const auto& arg_str = request.GetArg(name);
26+
if (arg_str.empty()) {
27+
return 1;
28+
}
29+
30+
try {
31+
int value = std::stoi(arg_str);
32+
return std::min(500, std::max(1, value));
33+
} catch (const std::invalid_argument&) {
34+
return 1;
35+
}
36+
}
37+
38+
} // namespace userver_techempower::db_helpers
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <userver/formats/json/value.hpp>
4+
#include <userver/server/http/http_request.hpp>
5+
#include <userver/storages/postgres/cluster_types.hpp>
6+
#include <userver/storages/postgres/query.hpp>
7+
8+
namespace userver_techempower::db_helpers {
9+
10+
constexpr int kMaxWorldRows = 10000;
11+
const userver::storages::postgres::Query kSelectRowQuery{
12+
"SELECT id, randomNumber FROM World WHERE id = $1"};
13+
constexpr auto kClusterHostType =
14+
userver::storages::postgres::ClusterHostType::kMaster;
15+
constexpr std::string_view kDbComponentName = "hello-world-db";
16+
17+
struct WorldTableRow final {
18+
int id;
19+
int random_number;
20+
};
21+
22+
int GenerateRandomId();
23+
int GenerateRandomValue();
24+
25+
userver::formats::json::Value Serialize(
26+
const WorldTableRow& value,
27+
userver::formats::serialize::To<userver::formats::json::Value>);
28+
29+
int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
30+
const std::string& name);
31+
32+
} // namespace userver_techempower::db_helpers
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "handler.hpp"
2+
3+
#include <userver/formats/serialize/common_containers.hpp>
4+
5+
#include <boost/container/small_vector.hpp>
6+
7+
namespace userver_techempower::cached_queries {
8+
9+
Handler::Handler(const userver::components::ComponentConfig& config,
10+
const userver::components::ComponentContext& context)
11+
: userver::server::handlers::HttpHandlerJsonBase{config, context},
12+
cache_{context.FindComponent<WorldCacheComponent>()},
13+
query_arg_name_{"count"} {}
14+
15+
userver::formats::json::Value Handler::HandleRequestJsonThrow(
16+
const userver::server::http::HttpRequest& request,
17+
const userver::formats::json::Value&,
18+
userver::server::request::RequestContext&) const {
19+
const auto queries_count =
20+
db_helpers::ParseParamFromQuery(request, query_arg_name_);
21+
22+
boost::container::small_vector<db_helpers::WorldTableRow, 500> result(
23+
queries_count);
24+
25+
const auto cache_ptr = cache_.Get();
26+
const auto& cache = *cache_ptr;
27+
std::generate(result.begin(), result.end(),
28+
[&cache] { return cache.at(db_helpers::GenerateRandomId()); });
29+
30+
return userver::formats::json::ValueBuilder{result}.ExtractValue();
31+
}
32+
33+
} // namespace userver_techempower::cached_queries
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include <userver/server/handlers/http_handler_json_base.hpp>
4+
5+
#include "world_cache_component.hpp"
6+
7+
namespace userver_techempower::cached_queries {
8+
9+
class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
10+
public:
11+
static constexpr std::string_view kName = "cached-queries-handler";
12+
13+
Handler(const userver::components::ComponentConfig& config,
14+
const userver::components::ComponentContext& context);
15+
16+
userver::formats::json::Value HandleRequestJsonThrow(
17+
const userver::server::http::HttpRequest& request,
18+
const userver::formats::json::Value&,
19+
userver::server::request::RequestContext&) const final;
20+
21+
private:
22+
const WorldCacheComponent& cache_;
23+
24+
const std::string query_arg_name_;
25+
};
26+
27+
} // namespace userver_techempower::cached_queries

0 commit comments

Comments
 (0)