benchmark.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. #include <signal.h>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <string>
  5. #include <vector>
  6. #include <algorithm>
  7. #include <Wt/WServer.h>
  8. #include <Wt/WResource.h>
  9. #include <Wt/Http/Request.h>
  10. #include <Wt/Http/Response.h>
  11. #include <Wt/WTemplate.h>
  12. #include <Wt/Utils.h>
  13. #include <Wt/Dbo/Dbo.h>
  14. #include <Wt/Dbo/Json.h>
  15. #ifndef BENCHMARK_USE_POSTGRES
  16. #include <Wt/Dbo/backend/MySQL.h>
  17. #else
  18. #include <Wt/Dbo/backend/Postgres.h>
  19. #endif
  20. #include <random>
  21. #ifndef WT_WIN32
  22. extern char **environ;
  23. #endif // WT_WIN32
  24. class MyMessage {
  25. public:
  26. std::string message;
  27. template<class Action>
  28. void persist(Action& a) {
  29. Wt::Dbo::field(a, message, "message");
  30. }
  31. };
  32. class World {
  33. public:
  34. int randomNumber;
  35. template<class Action>
  36. void persist(Action& a) {
  37. Wt::Dbo::field(a, randomNumber, "randomnumber");
  38. }
  39. };
  40. class Fortune {
  41. public:
  42. std::string message;
  43. template<class Action>
  44. void persist(Action& a) {
  45. Wt::Dbo::field(a, message, "message");
  46. }
  47. };
  48. namespace Wt {
  49. namespace Dbo {
  50. template<>
  51. struct dbo_traits<World> : public dbo_default_traits {
  52. static const char *versionField() {
  53. return 0;
  54. }
  55. static IdType invalidId() {
  56. return 0;
  57. }
  58. };
  59. template<>
  60. struct dbo_traits<Fortune> : public dbo_default_traits {
  61. static const char *versionField() {
  62. return 0;
  63. }
  64. static IdType invalidId() {
  65. return 0;
  66. }
  67. };
  68. }
  69. }
  70. class JsonResource : public Wt::WResource {
  71. public:
  72. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  73. response.setMimeType("application/json");
  74. response.addHeader("Server", "Wt");
  75. MyMessage message;
  76. message.message = "Hello, World!";
  77. Wt::Dbo::JsonSerializer writer(response.out());
  78. writer.serialize(message);
  79. }
  80. };
  81. class MyConnection : public
  82. #ifdef BENCHMARK_USE_POSTGRES
  83. Wt::Dbo::backend::Postgres
  84. #else
  85. Wt::Dbo::backend::MySQL
  86. #endif
  87. {
  88. public:
  89. #ifdef BENCHMARK_USE_POSTGRES
  90. MyConnection(const std::string &db) :
  91. Wt::Dbo::backend::Postgres(db) {}
  92. #else
  93. MyConnection(const std::string &db, const std::string &dbuser, const std::string &dbpasswd, const std::string &dbhost, unsigned int dbport) :
  94. Wt::Dbo::backend::MySQL(db, dbuser, dbpasswd, dbhost, dbport) {}
  95. #endif
  96. virtual void startTransaction() { }
  97. virtual void commitTransaction() { }
  98. virtual void rollbackTransaction() { }
  99. };
  100. struct DbStruct {
  101. MyConnection *connection;
  102. Wt::Dbo::Session session;
  103. std::default_random_engine rng;
  104. std::uniform_int_distribution<int> distribution;
  105. DbStruct()
  106. : connection(0),
  107. rng(clock()),
  108. distribution(1, 10000) {
  109. std::string dbHostStr = "localhost";
  110. char *dbHost = std::getenv("DBHOST");
  111. if (dbHost)
  112. dbHostStr = std::string(dbHost);
  113. #ifndef BENCHMARK_USE_POSTGRES
  114. auto c = Wt::cpp14::make_unique<MyConnection>("hello_world", "benchmarkdbuser", "benchmarkdbpass", dbHostStr, 3306);
  115. #else
  116. auto connStr = std::string("host=") + dbHostStr + " port=5432 user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world";
  117. auto c = Wt::cpp14::make_unique<MyConnection>(connStr);
  118. #endif
  119. connection = c.get();
  120. session.setConnection(std::move(c));
  121. session.mapClass<World>("world");
  122. session.mapClass<Fortune>("fortune");
  123. }
  124. int rand() {
  125. return distribution(rng);
  126. }
  127. };
  128. namespace {
  129. thread_local DbStruct *dbStruct_;
  130. }
  131. class DbResource : public Wt::WResource {
  132. public:
  133. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  134. response.setMimeType("application/json");
  135. response.addHeader("Server", "Wt");
  136. if (!dbStruct_) {
  137. dbStruct_ = new DbStruct();
  138. }
  139. Wt::Dbo::Transaction transaction(dbStruct_->session);
  140. Wt::Dbo::ptr<World> entry = dbStruct_->session.load<World>(dbStruct_->rand());
  141. Wt::Dbo::JsonSerializer writer(response.out());
  142. writer.serialize(entry);
  143. }
  144. };
  145. class QueriesResource : public Wt::WResource {
  146. public:
  147. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  148. int n;
  149. if (const std::string *queries = request.getParameter("queries")) {
  150. n = atoi(queries->c_str());
  151. if (n < 1)
  152. n = 1;
  153. else if (n > 500)
  154. n = 500;
  155. } else {
  156. n = 1;
  157. }
  158. response.setMimeType("application/json");
  159. response.addHeader("Server", "Wt");
  160. if (!dbStruct_) {
  161. dbStruct_ = new DbStruct();
  162. }
  163. Wt::Dbo::Transaction transaction(dbStruct_->session);
  164. std::vector<Wt::Dbo::ptr<World> > results;
  165. results.reserve(n);
  166. for (int i = 0; i < n; ++i) {
  167. results.push_back(dbStruct_->session.load<World>(dbStruct_->rand()));
  168. }
  169. Wt::Dbo::JsonSerializer writer(response.out());
  170. writer.serialize(results);
  171. }
  172. };
  173. typedef Wt::Dbo::collection< Wt::Dbo::ptr<Fortune> > Fortunes;
  174. typedef std::vector<Wt::Dbo::ptr<Fortune> > VFortunes;
  175. bool fortuneCmp(const Wt::Dbo::ptr<Fortune>& f1, const Wt::Dbo::ptr<Fortune>& f2) {
  176. return strcmp(f1->message.c_str(), f2->message.c_str()) < 0;
  177. }
  178. class FortuneTemplate : public Wt::WTemplate {
  179. private:
  180. const VFortunes *fortunes_;
  181. mutable std::vector<Wt::Dbo::ptr<Fortune> >::const_iterator it_;
  182. public:
  183. FortuneTemplate(const std::vector<Wt::Dbo::ptr<Fortune> >& fortunes)
  184. : Wt::WTemplate(tr("fortunes")),
  185. fortunes_(&fortunes),
  186. it_(fortunes.end())
  187. {
  188. addFunction("while", &Wt::WTemplate::Functions::while_f);
  189. }
  190. virtual bool conditionValue(const std::string& name) const {
  191. if (name == "next-fortune") {
  192. if (it_ == fortunes_->end())
  193. it_ = fortunes_->begin();
  194. else
  195. ++it_;
  196. if (it_ == fortunes_->end())
  197. return false;
  198. return true;
  199. } else
  200. return Wt::WTemplate::conditionValue(name);
  201. }
  202. virtual void resolveString(const std::string& varName, const std::vector<Wt::WString>& vars, std::ostream& result) {
  203. if (varName == "id")
  204. result << it_->id();
  205. else if (varName == "message")
  206. format(result, Wt::WString((*it_)->message));
  207. else
  208. Wt::WTemplate::resolveString(varName, vars, result);
  209. }
  210. };
  211. class FortuneResource : public Wt::WResource {
  212. public:
  213. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  214. response.setMimeType("text/html; charset=utf-8");
  215. response.addHeader("Server", "Wt");
  216. if (!dbStruct_) {
  217. dbStruct_ = new DbStruct();
  218. }
  219. Wt::Dbo::Transaction transaction(dbStruct_->session);
  220. Fortunes fortunes = dbStruct_->session.find<Fortune>();
  221. VFortunes vFortunes;
  222. for (Fortunes::const_iterator i = fortunes.begin(); i != fortunes.end(); ++i)
  223. vFortunes.push_back(*i);
  224. auto additionalFortune = Wt::cpp14::make_unique<Fortune>();
  225. additionalFortune->message = "Additional fortune added at request time.";
  226. vFortunes.push_back(Wt::Dbo::ptr<Fortune>(std::move(additionalFortune)));
  227. std::sort(vFortunes.begin(), vFortunes.end(), fortuneCmp);
  228. FortuneTemplate tpl(vFortunes);
  229. response.out() << "<!DOCTYPE html>";
  230. tpl.renderTemplate(response.out());
  231. }
  232. };
  233. class UpdateResource : public Wt::WResource {
  234. public:
  235. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  236. int n;
  237. if (const std::string *queries = request.getParameter("queries")) {
  238. n = atoi(queries->c_str());
  239. if (n < 1)
  240. n = 1;
  241. else if (n > 500)
  242. n = 500;
  243. } else {
  244. n = 1;
  245. }
  246. response.setMimeType("application/json");
  247. response.addHeader("Server", "Wt");
  248. if (!dbStruct_) {
  249. dbStruct_ = new DbStruct();
  250. }
  251. std::vector<Wt::Dbo::ptr<World> > results;
  252. for (int i = 0; i < n; ++i) {
  253. bool success = false;
  254. while (!success) {
  255. try {
  256. Wt::Dbo::Transaction transaction(dbStruct_->session);
  257. Wt::Dbo::ptr<World> world = dbStruct_->session.load<World>(dbStruct_->rand());
  258. world.modify()->randomNumber = dbStruct_->rand();
  259. transaction.commit();
  260. results.push_back(world);
  261. success = true;
  262. } catch (Wt::Dbo::Exception& e) {
  263. // Retry
  264. }
  265. }
  266. }
  267. Wt::Dbo::JsonSerializer writer(response.out());
  268. writer.serialize(results);
  269. }
  270. };
  271. class PlaintextResource : public Wt::WResource {
  272. virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
  273. response.setMimeType("text/plain");
  274. response.addHeader("Server", "Wt");
  275. response.out() << "Hello, World!";
  276. }
  277. };
  278. int main(int argc, char** argv) {
  279. try {
  280. Wt::WServer server(argv[0]);
  281. server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);
  282. auto bundle = std::make_shared<Wt::WMessageResourceBundle>();
  283. bundle->use(server.appRoot() + "fortunes");
  284. server.setLocalizedStrings(bundle);
  285. JsonResource jsonResource;
  286. server.addResource(&jsonResource, "/json");
  287. DbResource dbResource;
  288. server.addResource(&dbResource, "/db");
  289. QueriesResource queriesResource;
  290. server.addResource(&queriesResource, "/queries");
  291. FortuneResource fortuneResource;
  292. server.addResource(&fortuneResource, "/fortune");
  293. UpdateResource updateResource;
  294. server.addResource(&updateResource, "/updates");
  295. PlaintextResource plaintextResource;
  296. server.addResource(&plaintextResource, "/plaintext");
  297. if (server.start()) {
  298. int sig = Wt::WServer::waitForShutdown();
  299. std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
  300. server.stop();
  301. #ifndef WT_WIN32
  302. if (sig == SIGHUP)
  303. Wt::WServer::restart(argc, argv, environ);
  304. #endif // WT_WIN32
  305. }
  306. } catch (Wt::WServer::Exception& e) {
  307. std::cerr << e.what() << "\n";
  308. return 1;
  309. } catch (std::exception& e) {
  310. std::cerr << "exception: " << e.what() << "\n";
  311. return 1;
  312. }
  313. }