Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frameworks/C++/userver/benchmark_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"query_url": "/queries?queries=",
"update_url": "/updates?queries=",
"cached_query_url": "/cached-queries?count=",
"fortune_url": "/fortunes",
"port": 8080,
"approach": "Realistic",
"classification": "Fullstack",
Expand All @@ -32,6 +33,7 @@
"query_url": "/queries?queries=",
"update_url": "/updates?queries=",
"cached_query_url": "/cached-queries?count=",
"fortune_url": "/fortunes",
"port": 8081,
"approach": "Realistic",
"classification": "Micro",
Expand Down
2 changes: 2 additions & 0 deletions frameworks/C++/userver/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ urls.db = "/db"
urls.query = "/queries?queries="
urls.update = "/updates?queries="
urls.cached_query = "/cached-queries?count="
urls.fortune = "/fortunes"
approach = "Realistic"
classification = "Fullstack"
database = "Postgres"
Expand All @@ -25,6 +26,7 @@ urls.db = "/db"
urls.query = "/queries?queries="
urls.update = "/updates?queries="
urls.cached_query = "/cached-queries?count="
urls.fortune = "/fortunes"
approach = "Realistic"
classification = "Micro"
database = "Postgres"
Expand Down
4 changes: 2 additions & 2 deletions frameworks/C++/userver/userver-bare.dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
FROM ghcr.io/userver-framework/docker-userver-build-base:v1a AS builder
WORKDIR /src
RUN git clone https://github.com/userver-framework/userver.git && \
cd userver && git checkout 151bc1e3df01807da9cd27f9677b80f4951b1f25
cd userver && git checkout 5e33f7fe98604080b52208badef0d728c8d4aea0
COPY userver_benchmark/ ./
RUN mkdir build && cd build && \
cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_OPEN_SOURCE_BUILD=1 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -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 \
Expand Down
4 changes: 2 additions & 2 deletions frameworks/C++/userver/userver.dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
FROM ghcr.io/userver-framework/docker-userver-build-base:v1a AS builder
WORKDIR /src
RUN git clone https://github.com/userver-framework/userver.git && \
cd userver && git checkout 151bc1e3df01807da9cd27f9677b80f4951b1f25
cd userver && git checkout 5e33f7fe98604080b52208badef0d728c8d4aea0
COPY userver_benchmark/ ./
RUN mkdir build && cd build && \
cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_OPEN_SOURCE_BUILD=1 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -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 \
Expand Down
3 changes: 3 additions & 0 deletions frameworks/C++/userver/userver_benchmark/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BasedOnStyle: google
DerivePointerAlignment: false
IncludeBlocks: Preserve
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@ class SimpleConnection final {
};

} // namespace userver_techempower::bare

Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ struct SimpleResponse final {
};

} // namespace userver_techempower::bare

17 changes: 13 additions & 4 deletions frameworks/C++/userver/userver_benchmark/bare/simple_router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <userver/components/component_context.hpp>

#include "../controllers/cached_queries/handler.hpp"
#include "../controllers/fortunes/handler.hpp"
#include "../controllers/json/handler.hpp"
#include "../controllers/multiple_queries/handler.hpp"
#include "../controllers/plaintext/handler.hpp"
Expand All @@ -14,16 +15,19 @@ namespace userver_techempower::bare {
namespace {

constexpr std::string_view kPlainTextUrlPrefix{"/plaintext"};
constexpr std::string_view kJsontUrlPrefix{"/json"};
constexpr std::string_view kJsonUrlPrefix{"/json"};
constexpr std::string_view kSingleQueryUrlPrefix{"/db"};
constexpr std::string_view kMultipleQueriesUrlPrefix{"/queries"};
constexpr std::string_view kUpdatesUrlPrefix{"/updates"};
constexpr std::string_view kCachedQueriesUrlPrefix{"/cached-queries"};
constexpr std::string_view kFortunesUrlPrefix{"/fortunes"};

// NOLINTNEXTLINE
const std::string kContentTypePlain{"text/plain"};
// NOLINTNEXTLINE
const std::string kContentTypeJson{"application/json"};
// NOLINTNEXTLINE
const std::string kContentTypeTextHtml{"text/html; charset=utf-8"};

bool StartsWith(std::string_view source, std::string_view pattern) {
return source.substr(0, pattern.length()) == pattern;
Expand All @@ -37,7 +41,8 @@ SimpleRouter::SimpleRouter(const userver::components::ComponentConfig& config,
single_query_{context.FindComponent<single_query::Handler>()},
multiple_queries_{context.FindComponent<multiple_queries::Handler>()},
updates_{context.FindComponent<updates::Handler>()},
cached_queries_{context.FindComponent<cached_queries::Handler>()} {}
cached_queries_{context.FindComponent<cached_queries::Handler>()},
fortunes_{context.FindComponent<fortunes::Handler>()} {}

SimpleRouter::~SimpleRouter() = default;

Expand All @@ -46,7 +51,7 @@ SimpleResponse SimpleRouter::RouteRequest(std::string_view url) const {
return {plaintext::Handler::GetResponse(), kContentTypePlain};
}

if (StartsWith(url, kJsontUrlPrefix)) {
if (StartsWith(url, kJsonUrlPrefix)) {
return {ToString(json::Handler::GetResponse()), kContentTypeJson};
}

Expand All @@ -72,7 +77,11 @@ SimpleResponse SimpleRouter::RouteRequest(std::string_view url) const {
const auto count = db_helpers::ParseParamFromQuery(
url.substr(kCachedQueriesUrlPrefix.size()), "count");

return {ToString(cached_queries_.GetResponse(count)), "application/json"};
return {ToString(cached_queries_.GetResponse(count)), kContentTypeJson};
}

if (StartsWith(url, kFortunesUrlPrefix)) {
return {fortunes_.GetResponse(), kContentTypeTextHtml};
}

throw std::runtime_error{"No handler found for url"};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class Handler;
namespace cached_queries {
class Handler;
}
namespace fortunes {
class Handler;
}

namespace bare {

Expand All @@ -36,8 +39,8 @@ class SimpleRouter final : public userver::components::LoggableComponentBase {
const multiple_queries::Handler& multiple_queries_;
const updates::Handler& updates_;
const cached_queries::Handler& cached_queries_;
const fortunes::Handler& fortunes_;
};

} // namespace bare
} // namespace userver_techempower

Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include "handler.hpp"

#include <vector>

#include "../../common/db_helpers.hpp"

#include <userver/components/component_context.hpp>
#include <userver/storages/postgres/postgres.hpp>

namespace userver_techempower::fortunes {

namespace {

struct Fortune final {
int id;
std::string message;
};

constexpr std::string_view kResultingHtmlHeader{
"<!DOCTYPE "
"html><html><head><title>Fortunes</title></head><body><table><tr><th>id</"
"th><th>message</th></tr>"};
constexpr std::string_view kResultingHtmlFooter{"</table></body></html>"};

constexpr std::string_view kNewRowStart{"<tr><td>"};
constexpr std::string_view kColumnsSeparator{"</td><td>"};
constexpr std::string_view kNewRowEnd{"</td></tr>"};

constexpr std::string_view kEscapedQuote{"&quot;"};
constexpr std::string_view kEscapedAmpersand{"&amp;"};
constexpr std::string_view kEscapedLessThanSign{"&lt;"};
constexpr std::string_view kEscapedMoreThanSign{"&gt;"};

void AppendFortune(std::string& result, const Fortune& fortune) {
{
auto old_size = result.size();
const auto fortune_id = std::to_string(fortune.id);

const auto first_step_size =
kNewRowStart.size() + fortune_id.size() + kColumnsSeparator.size();

result.resize(old_size + first_step_size);
char* append_position = result.data() + old_size;

// this is just faster than std::string::append if we know the resulting
// size upfront, because there are a lot of not inlined calls otherwise
const auto append = [&append_position](std::string_view what) {
std::memcpy(append_position, what.data(), what.size());
append_position += what.size();
};
append(kNewRowStart);
append(fortune_id);
append(kColumnsSeparator);
}

{
std::string_view message{fortune.message};

const auto do_append = [&result](std::string_view unescaped,
std::string_view escaped) {
const auto old_size = result.size();
const auto added_size = unescaped.size() + escaped.size();

result.resize(result.size() + added_size);
char* append_position = result.data() + old_size;
if (!unescaped.empty()) {
std::memcpy(append_position, unescaped.data(), unescaped.size());
append_position += unescaped.size();
}
std::memcpy(append_position, escaped.data(), escaped.size());
};

std::size_t unescaped_len = 0;
const auto append = [&unescaped_len, &message,
&do_append](std::string_view escaped) {
do_append(message.substr(0, unescaped_len), escaped);
message = message.substr(std::exchange(unescaped_len, 0) + 1);
};

while (unescaped_len < message.size()) {
const auto c = message[unescaped_len];
switch (c) {
case '"': {
append(kEscapedQuote);
break;
}
case '&': {
append(kEscapedAmpersand);
break;
}
case '<': {
append(kEscapedLessThanSign);
break;
}
case '>': {
append(kEscapedMoreThanSign);
break;
}
default:
++unescaped_len;
}
}
result.append(message);
}

{ result.append(kNewRowEnd); }
}

std::string FormatFortunes(const std::vector<Fortune>& fortunes) {
std::string result{};
// Wild guess, seems reasonable. Could be the exact value needed, but that
// looks kinda cheating.
result.reserve(2048);

result.append(kResultingHtmlHeader);
for (const auto& fortune : fortunes) {
AppendFortune(result, fortune);
}
result.append(kResultingHtmlFooter);

return result;
}

} // namespace

Handler::Handler(const userver::components::ComponentConfig& config,
const userver::components::ComponentContext& context)
: userver::server::handlers::HttpHandlerBase{config, context},
pg_{context
.FindComponent<userver::components::Postgres>(
db_helpers::kDbComponentName)
.GetCluster()},
select_all_fortunes_query_{"SELECT id, message FROM Fortune"} {}

std::string Handler::HandleRequestThrow(
const userver::server::http::HttpRequest& request,
userver::server::request::RequestContext&) const {
request.GetHttpResponse().SetContentType("text/html; charset=utf-8");
return GetResponse();
}

std::string Handler::GetResponse() const {
auto fortunes =
pg_->Execute(db_helpers::kClusterHostType, select_all_fortunes_query_)
.AsContainer<std::vector<Fortune>>(
userver::storages::postgres::kRowTag);

fortunes.push_back({0, "Additional fortune added at request time."});

std::sort(fortunes.begin(), fortunes.end(),
[](const auto& lhs, const auto& rhs) {
return lhs.message < rhs.message;
});

return FormatFortunes(fortunes);
}

} // namespace userver_techempower::fortunes
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <userver/server/handlers/http_handler_base.hpp>

#include <userver/storages/postgres/postgres_fwd.hpp>
#include <userver/storages/postgres/query.hpp>

namespace userver_techempower::fortunes {

class Handler final : public userver::server::handlers::HttpHandlerBase {
public:
static constexpr std::string_view kName = "fortunes-handler";

Handler(const userver::components::ComponentConfig& config,
const userver::components::ComponentContext& context);

std::string HandleRequestThrow(
const userver::server::http::HttpRequest& request,
userver::server::request::RequestContext&) const final;

std::string GetResponse() const;

private:
const userver::storages::postgres::ClusterPtr pg_;
const userver::storages::postgres::Query select_all_fortunes_query_;
};

} // namespace userver_techempower::fortunes
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <userver/storages/secdist/provider_component.hpp>

#include "controllers/cached_queries/handler.hpp"
#include "controllers/fortunes/handler.hpp"
#include "controllers/json/handler.hpp"
#include "controllers/multiple_queries/handler.hpp"
#include "controllers/plaintext/handler.hpp"
Expand All @@ -35,6 +36,7 @@ int Main(int argc, char* argv[]) {
.Append<updates::Handler>()
.Append<cached_queries::WorldCacheComponent>()
.Append<cached_queries::Handler>()
.Append<fortunes::Handler>()
// bare
.Append<bare::SimpleRouter>()
.Append<bare::SimpleServer>();
Expand Down
7 changes: 6 additions & 1 deletion frameworks/C++/userver/userver_configs/static_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ components_manager:
secdist: {} # Component that stores configuration of hosts and passwords
default-secdist-provider:
config: /app/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
missing-ok: false

plaintext-handler:
path: /plaintext
Expand Down Expand Up @@ -101,3 +101,8 @@ components_manager:
method: GET
task_processor: main-task-processor

fortunes-handler:
path: /fortunes
method: GET
task_processor: main-task-processor