lithium.cc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #include "lithium_http_backend.hh"
  2. #if TFB_MYSQL
  3. #include "lithium_mysql.hh"
  4. #elif TFB_PGSQL
  5. #include "lithium_pgsql.hh"
  6. #endif
  7. #include "symbols.hh"
  8. using namespace li;
  9. template <typename B>
  10. void escape_html_entities(B& buffer, const std::string& data)
  11. {
  12. for(size_t pos = 0; pos != data.size(); ++pos) {
  13. switch(data[pos]) {
  14. case '&': buffer << "&amp;"; break;
  15. case '\"': buffer << "&quot;"; break;
  16. case '\'': buffer << "&apos;"; break;
  17. case '<': buffer << "&lt;"; break;
  18. case '>': buffer << "&gt;"; break;
  19. default: buffer << data[pos]; break;
  20. }
  21. }
  22. }
  23. #ifdef PROFILE_MODE
  24. void siege(int port) {
  25. auto sockets = http_benchmark_connect(512, port);
  26. http_benchmark(sockets, 2, 1000, "GET /json HTTP/1.1\r\n\r\n");
  27. http_benchmark(sockets, 2, 1000, "GET /plaintext HTTP/1.1\r\n\r\n");
  28. http_benchmark(sockets, 2, 1000, "GET /db HTTP/1.1\r\n\r\n");
  29. http_benchmark(sockets, 2, 1000, "GET /queries?N=20 HTTP/1.1\r\n\r\n");
  30. http_benchmark(sockets, 2, 1000, "GET /fortunes HTTP/1.1\r\n\r\n");
  31. http_benchmark(sockets, 2, 1000, "GET /updates?N=20 HTTP/1.1\r\n\r\n");
  32. http_benchmark_close(sockets);
  33. }
  34. #endif
  35. thread_local lru_cache<int, decltype(mmm(s::id = int(), s::randomNumber = int()))> world_cache(10000);
  36. int main(int argc, char* argv[]) {
  37. if (argc != 3)
  38. {
  39. std::cerr << "Usage: " << argv[0] << " sql_host port" << std::endl;
  40. return 1;
  41. }
  42. int port = atoi(argv[2]);
  43. int nprocs = std::thread::hardware_concurrency();
  44. #if MONOTHREAD
  45. int nthreads = 1;
  46. #else
  47. int nthreads = nprocs;
  48. #endif
  49. #if TFB_MYSQL
  50. auto sql_db = mysql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  51. s::password = "benchmarkdbpass", s::port = 3306, s::charset = "utf8");
  52. #elif TFB_PGSQL
  53. auto sql_db = pgsql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  54. s::password = "benchmarkdbpass", s::port = 5432, s::charset = "utf8");
  55. #endif
  56. auto fortunes = sql_orm_schema(sql_db, "Fortune").fields(
  57. s::id(s::auto_increment, s::primary_key) = int(),
  58. s::message = std::string());
  59. auto random_numbers = sql_orm_schema(sql_db, "World").fields(
  60. s::id(s::auto_increment, s::primary_key) = int(),
  61. s::randomNumber = int());
  62. #ifndef N_SQL_CONNECTIONS
  63. #if TFB_MYSQL
  64. int db_nconn = 5;
  65. int queries_nconn = 4;
  66. int fortunes_nconn = 5;
  67. int updates_nconn = 2;
  68. #elif TFB_PGSQL
  69. int db_nconn = 5;
  70. int queries_nconn = 3;
  71. int fortunes_nconn = 7;
  72. int updates_nconn = 3;
  73. #endif
  74. #else
  75. int db_nconn = N_SQL_CONNECTIONS;
  76. int queries_nconn = N_SQL_CONNECTIONS;
  77. int fortunes_nconn = N_SQL_CONNECTIONS;
  78. int updates_nconn = N_SQL_CONNECTIONS;
  79. #endif
  80. http_api my_api;
  81. my_api.get("/plaintext") = [&](http_request& request, http_response& response) {
  82. response.set_header("Content-Type", "text/plain");
  83. response.write("Hello, World!");
  84. };
  85. my_api.get("/json") = [&](http_request& request, http_response& response) {
  86. response.write_json(s::message = "Hello, World!");
  87. };
  88. my_api.get("/db") = [&](http_request& request, http_response& response) {
  89. sql_db.max_async_connections_per_thread_ = db_nconn;
  90. response.write_json(*random_numbers.connect(request.fiber).find_one(s::id = 1 + rand() % 10000));
  91. };
  92. my_api.get("/queries") = [&](http_request& request, http_response& response) {
  93. sql_db.max_async_connections_per_thread_ = queries_nconn;
  94. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  95. int N = atoi(N_str.c_str());
  96. N = std::max(1, std::min(N, 500));
  97. auto c = random_numbers.connect(request.fiber);
  98. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  99. for (int i = 0; i < N; i++)
  100. numbers[i] = *c.find_one(s::id = 1 + rand() % 10000);
  101. response.write_json(numbers);
  102. };
  103. my_api.get("/cached-worlds") = [&](http_request& request, http_response& response) {
  104. sql_db.max_async_connections_per_thread_ = queries_nconn;
  105. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  106. int N = atoi(N_str.c_str());
  107. N = std::max(1, std::min(N, 500));
  108. auto c = random_numbers.connect(request.fiber);
  109. if (world_cache.size() == 0)
  110. c.forall([&] (const auto& number) {
  111. world_cache(number.id, [&] { return metamap_clone(number); });
  112. });
  113. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  114. for (int i = 0; i < N; i++)
  115. {
  116. int id = 1 + rand() % 10000;
  117. numbers[i] = world_cache(id, [&] { return *c.find_one(s::id = id); });
  118. }
  119. response.write_json(numbers);
  120. };
  121. my_api.get("/updates") = [&](http_request& request, http_response& response) {
  122. sql_db.max_async_connections_per_thread_ = updates_nconn;
  123. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  124. int N = atoi(N_str.c_str());
  125. N = std::max(1, std::min(N, 500));
  126. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  127. {
  128. auto c = random_numbers.connect(request.fiber);
  129. auto& raw_c = c.backend_connection();
  130. #if TFB_MYSQL
  131. raw_c("START TRANSACTION");
  132. #endif
  133. for (int i = 0; i < N; i++)
  134. {
  135. numbers[i] = *c.find_one(s::id = 1 + rand() % 10000);
  136. numbers[i].randomNumber = 1 + rand() % 10000;
  137. }
  138. std::sort(numbers.begin(), numbers.end(), [] (auto a, auto b) { return a.id < b.id; });
  139. c.bulk_update(numbers);
  140. #if TFB_MYSQL
  141. raw_c("COMMIT");
  142. #endif
  143. }
  144. response.write_json(numbers);
  145. };
  146. my_api.get("/fortunes") = [&](http_request& request, http_response& response) {
  147. sql_db.max_async_connections_per_thread_ = fortunes_nconn;
  148. typedef decltype(fortunes.all_fields()) fortune;
  149. std::vector<fortune> table;
  150. auto c = fortunes.connect(request.fiber);
  151. c.forall([&] (const auto& f) { table.emplace_back(metamap_clone(f)); });
  152. table.emplace_back(0, "Additional fortune added at request time.");
  153. std::sort(table.begin(), table.end(),
  154. [] (const fortune& a, const fortune& b) { return a.message < b.message; });
  155. char b[100000];
  156. li::output_buffer ss(b, sizeof(b));
  157. ss << "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
  158. for(auto& f : table)
  159. {
  160. ss << "<tr><td>" << f.id << "</td><td>";
  161. escape_html_entities(ss, f.message);
  162. ss << "</td></tr>";
  163. }
  164. ss << "</table></body></html>";
  165. response.set_header("Content-Type", "text/html; charset=utf-8");
  166. response.write(ss.to_string_view());
  167. };
  168. #ifndef PROFILE_MODE
  169. // Start the server for the Techempower benchmark.
  170. http_serve(my_api, port, s::nthreads = nthreads);
  171. #else
  172. std::thread server_thread([&] {
  173. http_serve(my_api, port, s::nthreads = nprocs);
  174. });
  175. usleep(2e6);
  176. siege(port);
  177. li::quit_signal_catched = true;
  178. server_thread.join();
  179. #endif
  180. return 0;
  181. }