Skip to content

Instantly share code, notes, and snippets.

@marty1885
Last active January 20, 2021 10:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marty1885/3fee810ec743ad874cbefb334abf52fe to your computer and use it in GitHub Desktop.
Save marty1885/3fee810ec743ad874cbefb334abf52fe to your computer and use it in GitHub Desktop.
#include <drogon/drogon.h>
#include <exception>
#include <coroutine>
#include <stdexcept>
using namespace drogon;
int main()
{
app().createDbClient("postgresql", "localhost", 5432, "test", "test", "some legit password 191561", 16)
.addListener("0.0.0.0", 4444)
.setThreadNum(1);
// Asking DB for data then reply (table only as 1 row)
//
// NOTE: Variables must be passed by value. Otherwise (references) can easily go out of scope. Since the function(subrutine) can
// exit while the coroutine is executing.
// NOTE: Must return a `promise_type`. Otherwise C++ doesn't see it as coroutine
//
// Performance: 7244.49 Q/s (98.32%)
// 1 Server thread and 4 client thread. CPU: Cortex-A72 @ 2GHz
// Benchmarked using ApacheBanchmark. Average over 100K requests
//
// It fluctuate a bit. Overall, coroutine is a bit slower than callback.
app().registerHandler("/coro", [](HttpRequestPtr req, std::function<void (const HttpResponsePtr &)> callback) -> AsyncTask {
auto sql = app().getDbClient();
// Coroutines! YAY!!!
auto result = co_await sql->execSqlCoro("SELECT * FROM users;");
//^^^^^ Copy by value kills the just-created coroutine frame. Somehow this made things faster then by reference
auto resp = HttpResponse::newHttpResponse();
callback(resp);
});
// The current solution of using callbacks
//
// Performance: 7367.88Q/s (100%)
app().registerHandler("/cb", [](const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) -> void{
auto sql = app().getDbClient();
sql->execSqlAsync("SELECT * FROM users", [callback](const drogon::orm::Result& result) {
auto resp = HttpResponse::newHttpResponse();
callback(resp);
}, [](const drogon::orm::DrogonDbException& e){
});
});
// ==================================================================================================================
// Misc tests
// Make sure the awaiter is written correctly to propergrate exceptions from callback into coroutine
app().registerHandler("/coro_bad_sql", [](HttpRequestPtr req, std::function<void (const HttpResponsePtr &)> callback) -> AsyncTask {
auto sql = app().getDbClient();
try {
auto result = co_await sql->execSqlCoro("SELECT * FROM users WHERE username ============ ?;");
throw std::logic_error("This line shouldn't run because of exceptions");
}
catch(const drogon::orm::Failure& e) {
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Exception is cought");
callback(resp);
}
});
// Make sure SQL with parameters work
app().registerHandler("/coro_with_param", [](HttpRequestPtr req, std::function<void (const HttpResponsePtr &)> callback) -> AsyncTask {
auto sql = app().getDbClient();
auto result = co_await sql->execSqlCoro("select * from users where username = $1 limit $2;", "the_user", 1);
auto resp = HttpResponse::newHttpResponse();
callback(resp);
});
app().registerHandler("/test_request_coro", [](HttpRequestPtr req, std::function<void (const HttpResponsePtr &)> callback) -> AsyncTask {
auto client = HttpClient::newHttpClient("http://example.com");
auto example_req = HttpRequest::newHttpRequest();
example_req->setPath("/");
try {
auto example_resp = co_await client->sendRequestCoro(example_req);
if(example_resp->getBody().size() == 0)
throw std::runtime_error("Why is there nothing");
}
catch(const std::exception& e) {
std::cerr << "Error in request: " << e.what() << std::endl;
}
auto resp = HttpResponse::newHttpResponse();
callback(resp);
});
app().run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment