benchmark.cpp 10 KB

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