benchmark.cpp 10 KB

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