11#include < drogon/drogon.h>
22#include < sqlite3.h>
3+ #include < thread>
34#include < dirent.h>
45#include < fstream>
56#include < sstream>
@@ -60,6 +61,11 @@ static sqlite3 *getDb()
6061 return tl_db;
6162}
6263
64+ // ── PostgreSQL (async-db) ──
65+
66+ static bool pg_available = false ;
67+ static drogon::orm::DbClientPtr pgClient;
68+
6369static void loadDataset ()
6470{
6571 const char *path = getenv (" DATASET_PATH" );
@@ -171,6 +177,7 @@ class BenchmarkCtrl : public drogon::HttpController<BenchmarkCtrl>
171177 ADD_METHOD_TO (BenchmarkCtrl::upload, " /upload" , Post);
172178 ADD_METHOD_TO (BenchmarkCtrl::baseline11, " /baseline11" , Get, Post);
173179 ADD_METHOD_TO (BenchmarkCtrl::dbEndpoint, " /db" , Get);
180+ ADD_METHOD_TO (BenchmarkCtrl::asyncDb, " /async-db" , Get);
174181 ADD_METHOD_TO (BenchmarkCtrl::staticFile, " /static/{1}" , Get);
175182 METHOD_LIST_END
176183
@@ -325,6 +332,66 @@ class BenchmarkCtrl : public drogon::HttpController<BenchmarkCtrl>
325332 callback (resp);
326333 }
327334
335+ void asyncDb (const HttpRequestPtr &req,
336+ std::function<void (const HttpResponsePtr &)> &&callback)
337+ {
338+ static const std::string empty_resp = " {\" items\" :[],\" count\" :0}" ;
339+ if (!pg_available || !pgClient) {
340+ auto resp = HttpResponse::newHttpResponse ();
341+ resp->setBody (empty_resp);
342+ resp->setContentTypeCode (CT_APPLICATION_JSON);
343+ callback (resp);
344+ return ;
345+ }
346+ double minPrice = 10.0 , maxPrice = 50.0 ;
347+ for (auto &[k, v] : req->parameters ()) {
348+ if (k == " min" ) { try { minPrice = std::stod (v); } catch (...) {} }
349+ else if (k == " max" ) { try { maxPrice = std::stod (v); } catch (...) {} }
350+ }
351+ pgClient->execSqlAsync (
352+ " SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN $1 AND $2 LIMIT 50" ,
353+ [callback](const drogon::orm::Result &result) {
354+ Json::Value respJson;
355+ Json::Value items (Json::arrayValue);
356+ for (const auto &row : result) {
357+ Json::Value item;
358+ item[" id" ] = static_cast <Json::Int64>(row[" id" ].as <int32_t >());
359+ item[" name" ] = row[" name" ].as <std::string>();
360+ item[" category" ] = row[" category" ].as <std::string>();
361+ item[" price" ] = row[" price" ].as <double >();
362+ item[" quantity" ] = static_cast <Json::Int64>(row[" quantity" ].as <int32_t >());
363+ item[" active" ] = row[" active" ].as <bool >();
364+ Json::CharReaderBuilder rb;
365+ Json::Value tags;
366+ std::string errs;
367+ std::string tagsStr = row[" tags" ].as <std::string>();
368+ std::istringstream tis (tagsStr);
369+ Json::parseFromStream (rb, tis, &tags, &errs);
370+ item[" tags" ] = tags;
371+ Json::Value rating;
372+ rating[" score" ] = row[" rating_score" ].as <double >();
373+ rating[" count" ] = static_cast <Json::Int64>(row[" rating_count" ].as <int32_t >());
374+ item[" rating" ] = rating;
375+ items.append (std::move (item));
376+ }
377+ respJson[" items" ] = std::move (items);
378+ respJson[" count" ] = static_cast <int >(result.size ());
379+ Json::StreamWriterBuilder wb;
380+ wb[" indentation" ] = " " ;
381+ auto resp = HttpResponse::newHttpResponse ();
382+ resp->setBody (Json::writeString (wb, respJson));
383+ resp->setContentTypeCode (CT_APPLICATION_JSON);
384+ callback (resp);
385+ },
386+ [callback](const drogon::orm::DrogonDbException &e) {
387+ auto resp = HttpResponse::newHttpResponse ();
388+ resp->setBody (" {\" items\" :[],\" count\" :0}" );
389+ resp->setContentTypeCode (CT_APPLICATION_JSON);
390+ callback (resp);
391+ },
392+ minPrice, maxPrice);
393+ }
394+
328395 void staticFile (const HttpRequestPtr &req,
329396 std::function<void (const HttpResponsePtr &)> &&callback,
330397 const std::string &filename)
@@ -354,6 +421,37 @@ int main()
354421 sqlite3 *test = openDb ();
355422 if (test) { db_available = true ; sqlite3_close (test); }
356423 }
424+ {
425+ const char *dbUrl = getenv (" DATABASE_URL" );
426+ if (dbUrl) {
427+ // Parse postgres://user:pass@host:port/dbname
428+ std::string s (dbUrl);
429+ auto se = s.find (" ://" );
430+ if (se != std::string::npos) {
431+ s = s.substr (se + 3 );
432+ std::string user, pass, host = " localhost" , port = " 5432" , dbname;
433+ auto at = s.find (' @' );
434+ if (at != std::string::npos) {
435+ auto up = s.substr (0 , at);
436+ s = s.substr (at + 1 );
437+ auto c = up.find (' :' );
438+ if (c != std::string::npos) { user = up.substr (0 , c); pass = up.substr (c + 1 ); }
439+ else user = up;
440+ }
441+ auto sl = s.find (' /' );
442+ if (sl != std::string::npos) { dbname = s.substr (sl + 1 ); s = s.substr (0 , sl); }
443+ auto c = s.find (' :' );
444+ if (c != std::string::npos) { host = s.substr (0 , c); port = s.substr (c + 1 ); }
445+ else host = s;
446+ int nconns = std::thread::hardware_concurrency ();
447+ if (nconns < 4 ) nconns = 4 ;
448+ pgClient = drogon::orm::DbClient::newPgClient (
449+ " host=" + host + " port=" + port + " dbname=" + dbname +
450+ " user=" + user + " password=" + pass, nconns);
451+ pg_available = true ;
452+ }
453+ }
454+ }
357455
358456 app ().setLogLevel (trantor::Logger::kWarn );
359457 app ().setThreadNum (0 );
@@ -372,6 +470,7 @@ int main()
372470 app ().addListener (" 0.0.0.0" , 8443 , true , cert, key);
373471
374472 app ().enableGzip (true );
473+
375474 app ().run ();
376475 return 0 ;
377476}
0 commit comments